PageSourceSearch

https://www.scientology.ru/js-static/year-in-review-2026/main.js?_=

js scientology.ru collected 2026-09-25 00:03:36 UTC 210,508 bytes, 5,721 lines download raw bytes

1var YEAR2026 = (function () {
2    'use strict';
3
4    function throttle(func, waitMs) {
5        var lastTime = 0;
6        var timeoutId = null;
7        var lastArgs = null;
8        var lastThis = null;
9
10        return function () {
11            var now = Date.now();
12            var remaining = waitMs - (now - lastTime);
13            lastArgs = arguments;
14            lastThis = this;
15
16            if (remaining <= 0) {
17                if (timeoutId) {
18                    clearTimeout(timeoutId);
19                    timeoutId = null;
20                }
21                lastTime = now;
22                func.apply(lastThis, lastArgs);
23                lastArgs = null;
24                lastThis = null;
25                return;
26            }
27
28            if (timeoutId) {
29                return;
30            }
31
32            timeoutId = setTimeout(function () {
33                timeoutId = null;
34                lastTime = Date.now();
35                func.apply(lastThis, lastArgs);
36                lastArgs = null;
37                lastThis = null;
38            }, remaining);
39        };
40    }
41
42    function YEAR2026() {
43        // enforces new
44        if (!(this instanceof YEAR2026)) {
45            var a = new YEAR2026();
46            return a;
47        }
48    }
49
50    YEAR2026.prototype.clearUrlHash = function () {
51        if (!window.location.hash) {
52            return;
53        }
54        // Preserve path + query, remove hash so refresh won't jump.
55        window.history.replaceState({}, '', window.location.href.split('#')[0]);
56    }
57
58    YEAR2026.prototype.clearUrlHashIfPageAnchor = function () {
59        if (!window.location.hash) {
60            return;
61        }
62
63        // Don't interfere with modal/history hashes.
64        if (window.location.hash === '#modal-open' || window.location.hash === '#') {
65            return;
66        }
67
68        var hashId = window.location.hash.replace('#', '');
69        if (!hashId) {
70            return;
71        }
72
73        // Only clear hashes that point to an in-page element.
74        if (document.getElementById(hashId)) {
75            this.clearUrlHash();
76        }
77    }
78
79    YEAR2026.prototype.clearUrlHashIfAnchorNotNearViewportTop = function () {
80        if (!window.location.hash) {
81            return;
82        }
83
84        // Don't interfere with modal/history hashes.
85        if (window.location.hash === '#modal-open' || window.location.hash === '#') {
86            return;
87        }
88
89        var hashId = window.location.hash.replace('#', '');
90        if (!hashId) {
91            return;
92        }
93
94        var el = document.getElementById(hashId);
95        if (!el) {
96            return;
97        }
98
99        // If the anchored element is no longer near the viewport top, clear the hash.
100        // This covers "subsection" anchor navigation: once the user scrolls to another
101        // subsection, the old hash should not persist across refresh.
102        var rect = el.getBoundingClientRect();
103        if (rect.top < -300 || rect.top > 300) {
104            this.clearUrlHash();
105        }
106    }
107
108    YEAR2026.prototype.Init = function () {
109        var self = this;
110
111        // anti bot protection
112        if (lazySizesConfig.disabled) {
113            return;
114        }
115
116        this.globalTimelines = {};
117
118
119        hasWebP().then(function () {
120            $('body').addClass('webp-support');
121        }, function () {
122            $('body').addClass('no-webp-support');
123        });
124
125        this.initLazyload();
126        this.fadeInBubble();
127
128        setTimeout(function () {
129            EventTracker.trackScroll();
130        }, 1000);
131
132
133        
134        this.initIntro();
135        this.initMutedAutoplayLoopVidoes();
136        this.init4DAutoplayBehindLogos();
137        this.initVideosModal('.year-in-review-2026-video__modal');
138
139        this.initLoopAutoPlay();
140        this.initVideos();
141        this.initVideoAutoplay();
142        this.stickyVideoAutoplay();
143        this.initMutedAutoplayVidoes();
144        this.initImageCompareTrigger();
145
146        this.initLandmarkSlideShow();
147        
148        if (!this.isIosVersionAtMost17() && !globalGetParams.screencap) {
149            this.initSliderImages();
150            
151            this.initMainMenu()
152            this.initScrollPosition();
153            this.initIntroScroll();
154            
155            this.initButtonFadein();
156            if(!this.isMobile()) {
157                this.initFloats();
158                this.initGrowingLines();
159            }
160            
161            this.initNav();
162            this.initControls();
163            
164            
165            
166            this.initTechnologyForLife();
167            this.initLRHHall();
168            this.initLRHHallTeaser();
169
170            this.initIASStats();
171            
172            this.initFEBCSlamTitles();
173            
174
175            this.initHouseWipe();
176            this.initParallax();
177
178            
179            this.initBdGameWinners();
180
181            this.initRotateImages();
182
183            this.initEndCurious();
184
185            this.initNetworkSection();
186
187            // this.initIASCCHRtransition();
188            
189            // this.initBackgroundVideo();
190            this.initMosaicRows();
191
192            this.sectionReveal();
193
194            this.initAwardsScroller();
195            this.initUnstoppablePower();
196
197            this.initScnNWBackground();
198            Digital.Init('.digital-matrix');
199
200            // year-in-review-2026-globe-inner
201            if(this.isChrome119OrLower() || this.isMobile()) {
202                $('.year-in-review-2026-globe-static').removeClass('d-none')
203                $('.year-in-review-2026-globe-wrapper').addClass('year-in-review-2026-globe-wrapper__static');
204
205                gsap.timeline({
206                    scrollTrigger: {
207                        trigger: '.year-in-review-2026-globe-inner',
208                        start: '25% center',
209                        endTrigger: '.year-in-review-2026--febc-end-trigger',
210                        end: 'bottom bottom',
211                        toggleActions: "play none resume reverse",
212                        pin: true,
213                        pinSpacing: false,
214                        invalidateOnRefresh: true,
215                        anticipatePin: 1,
216                    }
217                });
218            } else {
219                this.initLazyloadTHREEJS();
220            }
221
222            loadjs([
223                "/js-static/year-in-review-2026/stars.js?_=" + cacheId,
224            ]);
225            
226        } else {
227
228            $('.year-in-review-2026-title--gradient').addClass("year-in-review-2026-title--gradient__disabled");
229            gsap.set('.year-in-review-2026-first-images', {
230                autoAlpha: 1,
231                y: 0,
232            });
233
234            $('.year-in-review-2026-lrh-hall-teaser, .year-in-review-2026-winners, .year-in-review-2026--lrh-hall-wrapper').css('height', 'auto');
235
236            $('.year-in-review-2026--video-autoplay--lrh-hall, .year-in-review-2026--video-autoplay--lrh-hall, .year-in-review-2026-lrh-line-4-wrapper').css('position', 'relative');
237
238
239            $('.year-in-review-2026-slam-wrapper').addClass('year-in-review-2026-slam-wrapper__disabled');
240            $('.year-in-review-2026-slam-inner').css('height', 'auto');
241
242            $('.year-in-review-2026-bd-game-winner').addClass('d-none');
243
244            $('.year-in-review-2026-lrh-house').css({
245                'margin-bottom': '40px',
246            });
247
248            $('.year-in-review-2026--autoplay-video-replay-animation').remove();
249
250            $('.year-in-review-2026--video-autoplay--lrh-hall, .year-in-review-2025-ias-stat, .year-2026-our-message-wrapper, .year-in-review-2026--our-tech-intro').css('position', 'relative');
251            $('.digital-matrix, .year-2026-our-message-video').css('margin-top', '0');
252
253            $('.year-2025-awards-wall-wrapper').css('height', 'auto');
254            $('.year-2025--awards-row').css('display', 'block');
255
256            $('.year-in-review-2026-4d-mosaic--image__featured').each(function (index, el) {
257                gsap.set(el, {
258                    scale: 2.5,
259                    zIndex: 1000,
260                });
261            });
262
263            $('.year-in-review-2026--video-autoplay-element').each(function (_, el) {
264                $(el).css('margin-top', '0px');
265            });
266
267            $('.year-in-review-2026-slam-in-sequence').each(function (_, el) {
268                gsap.set(el, {
269                    autoAlpha: 1,
270                    position: 'relative',
271                    height: 'auto',
272                });
273            });
274
275            $('.year-in-review-2026-slam-wrapper').css('margin-top', '0px');
276
277            $('.year-in-review-2026--intro-titles > div').each(function (_, el) {
278
279                gsap.set(el.querySelector('span'), {
280                    autoAlpha: 1,
281                });
282            });
283
284            this.stickyVideoLastFrame();
285            
286        }
287
288        if(globalGetParams.stoppoints) {
289            var style = document.createElement('style');
290            style.textContent = [
291                ".navigation-step {",
292                "    width: 100%;",
293                "    height: 1px;",
294                "    background: red;",
295                "    z-index: 9999;",
296                "}"
297            ].join('\n');
298            document.head.appendChild(style);
299        }
300
301
302        if (globalGetParams.dev) {
303            // load js  "/plugins/gsapv3/GSDevTools.min.js",
304            loadjs([
305                "/plugins/gsapv3/GSDevTools.min.js",
306            ], 'gsap-dev', {
307                success: function () {
308                    GSDevTools.create();
309                }
310            });
311        }
312
313        if ($('.show-codes-button').length) {
314            $('.show-codes-button:not(.copyversion)').click(function (event) {
315                $('.image-code').toggleClass('hidden');
316            });
317            $('.mag-image-lazyload__abs').each(function (index, el) {
318                // console.log( $(el).find('img').attr('data-src') );
319                if ($(el).find('source').length > 0) {
320                    var src = $(el).find('source').attr('data-srcset');
321                    if (!src) {
322                        return;
323                    }
324                    // most are _XX9999.jpg
325                    var parts = src.match(/[-_](([0-9]+[a-zA-Z]+|[a-zA-Z]+[0-9]+)[0-9a-zA-Z]*)(-(inet|1))?(-r)?(_en)?.jpg/);
326                    if (parts === null) {
327                        // Some are DJI_0242
328                        parts = src.match(/[-_]([A-Z]+_[0-9]+).jpg/);
329                    }
330                    if (parts && parts.length > 0) {
331                        var code = parts[1];
332                        $(el).append('<div class="image-code hidden">' + code + '</div>')
333                    }
334                }
335            });
336            loadjs(['/plugins/bootstrap/js/bootstrap.bundle.min.js?_=' + cacheId], 'bootstrapjs', { async: false });
337            loadjs.ready('bootstrapjs', function () {
338                $('.image-code').tooltip({
339                    html: true,
340                    title: '<span class="icon-zoom-in"></span> VIEW FULL SCREEN',
341                    placement: 'bottom',
342                });
343            });
344
345            // load up photoswipe
346            loadjs('/plugins/photoswipe/photoswipe.min.js?_=' + new Date().getTime());
347            loadjs('/plugins/photoswipe/photoswipe-ui-default.min.js?_=' + new Date().getTime());
348
349            loadjs(['css!/plugins/photoswipe/photoswipe.css?_=' + cacheId, 'css!/plugins/photoswipe/default-skin/default-skin.css?_=' + cacheId]);
350
351            $('.pswp__container').html('<div class="pswp__item"></div><div class="pswp__item"></div><div class="pswp__item"></div>');
352
353            $(document).on('click', '.image-code', function (e) {
354                e.stopPropagation();
355                e.stopImmediatePropagation();
356                e.preventDefault();
357
358                var $imageItem = $(this).closest('.mag-image-lazyload__abs');
359                var src = $imageItem.find('img')[0].currentSrc.replace(/@w=[0-9]*/, '');
360                // if(src[0] != "/") {
361                //     src = "/" + src;
362                // }
363                // photoswipe zoom in
364                // console.log('src',src);
365                if (src) {
366                    // var url = new URL(window.location.href);
367                    // url.pathname = src;
368                    var pswpElement = document.querySelectorAll('.pswp')[0];
369                    // build items array
370                    var items = [
371                        {
372                            // src: unescape(url.toString()),
373                            src: src,
374                            w: 0,
375                            h: 0,
376                        },
377                    ];
378
379                    // define options (if needed)
380                    var options = {
381                        index: 0,
382                        shareEl: false,
383                        history: false
384                    };
385                    var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
386
387                    // dynamicly set W & H
388                    gallery.listen('gettingData', function (index, item) {
389                        if (item.w < 1 || item.h < 1) {
390                            var img = new Image();
391                            img.onload = function () {
392                                item.w = this.width;
393                                item.h = this.height;
394                                gallery.updateSize(true);
395                            };
396                            img.src = item.src;
397                        }
398                    });
399
400                    gallery.init();
401                }
402            });
403
404        }
405
406        var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
407        if (isSafari) {
408            $('body').addClass('safari');
409        }
410
411        if (globalGetParams.id) {
412            window.scrollTo(0, $("#" + globalGetParams.id).offset().top - 150);
413        }
414
415        // on mouse wheel move
416        window.addEventListener('wheel', function (event) {
417            if (self.lastScrollAnimation) {
418                self.lastScrollAnimation.pause();
419            }
420        }, { passive: false });
421    }
422
423    YEAR2026.prototype.init4DAutoplayBehindLogos = function () {
424        var update = function () {
425            var containers = document.querySelectorAll('.bs-extender__4d-background > .relative');
426            if (!containers || !containers.length) {
427                return;
428            }
429
430            var isMobile = window.matchMedia && window.matchMedia('(max-width: 575.98px)').matches;
431            var scale = isMobile ? 1.2 : 1.5;
432            var padding = isMobile ? 30 : 60;
433            var minSize = isMobile ? 280 : 420;
434            var maxSize = isMobile ? 440 : 760;
435
436            Array.prototype.forEach.call(containers, function (container) {
437                var autoplayEl = container.querySelector('.year-in-review-2026-autoplay--4d');
438                if (!autoplayEl) {
439                    return;
440                }
441
442                var logos = container.querySelectorAll('.year-in-review-2026-image-grid-logo');
443                if (!logos || !logos.length) {
444                    return;
445                }
446
447                var containerRect = container.getBoundingClientRect();
448                if (!containerRect.width || !containerRect.height) {
449                    return;
450                }
451
452                var autoplayRect = autoplayEl.getBoundingClientRect();
453                var autoplayCx = autoplayRect.left + autoplayRect.width / 2;
454                var autoplayCy = autoplayRect.top + autoplayRect.height / 2;
455
456                var closestLogo = logos[0];
457                var closestDist = Infinity;
458                Array.prototype.forEach.call(logos, function (logoEl) {
459                    var r = logoEl.getBoundingClientRect();
460                    var cx = r.left + r.width / 2;
461                    var cy = r.top + r.height / 2;
462                    var dx = cx - autoplayCx;
463                    var dy = cy - autoplayCy;
464                    var dist = dx * dx + dy * dy;
465                    if (dist < closestDist) {
466                        closestDist = dist;
467                        closestLogo = logoEl;
468                    }
469                });
470
471                var logoRect = closestLogo.getBoundingClientRect();
472                var x = (logoRect.left + logoRect.width / 2) - containerRect.left;
473                var y = (logoRect.top + logoRect.height / 2) - containerRect.top;
474                var size = Math.max(logoRect.width, logoRect.height) * scale + padding;
475                size = Math.max(minSize, Math.min(maxSize, size));
476
477                container.style.setProperty('--year-in-review-2026-4d-x', x + 'px');
478                container.style.setProperty('--year-in-review-2026-4d-y', y + 'px');
479                container.style.setProperty('--year-in-review-2026-4d-size', size + 'px');
480            });
481        };
482
483        var updateThrottled = throttle(update, 120);
484        updateThrottled();
485
486        window.addEventListener('resize', updateThrottled, { passive: true });
487        window.addEventListener('load', updateThrottled, { passive: true });
488    }
489
490
491    YEAR2026.prototype.initLazyloadTHREEJS = function () {
492        let hasExecuted = false;
493
494        // Function to load scripts and initialize ThreeJSGlobe
495        const initializeThreeJS = () => {
496            if (hasExecuted) return; // Prevent further execution
497            hasExecuted = true;
498
499            loadjs.ready('threejs', function () {
500                new ThreeJSGlobe();
501            });
502
503            if (!loadjs.isDefined('threejs')) {
504                loadjs([
505                    'module!/plugins/threejs/threejs-wrapper.js?_=' + cacheId,
506                    // '/plugins/threejs/three-nebula.min.js?_=' + cacheId,
507                    '/plugins/threejs/globe-2.34.2.gl.min.js?_=' + cacheId,
508                    '/js-static/year-in-review-2026/threejs.js?_=' + cacheId,
509                ], 'threejs', {
510                    async: false,
511                });
512            }
513    
514        };
515
516        const observerOptions = {
517            root: null, // Use the viewport as the root
518            rootMargin: '0px 0px 500px 0px', // Adjusts the viewport margin
519            threshold: 0,
520        };
521    
522        // Create IntersectionObserver
523        const observer = new IntersectionObserver((entries, observer) => {
524            entries.forEach(entry => {
525                if (entry.isIntersecting) {
526                    initializeThreeJS(); // Trigger initialization
527                    observer.disconnect(); // Stop observing after the first trigger
528                }
529            });
530        }, observerOptions);
531    
532        // Elements to observe
533        const elementsToObserve = ['.year-in-review-2026-globe-wrapper'];
534    
535        elementsToObserve.forEach(selector => {
536            const element = document.querySelector(selector);
537            if (element) {
538                observer.observe(element);
539            }
540        });
541    }
542
543    YEAR2026.prototype.initImageCompareTrigger = function () {
544        // Guard if the slider initializer is missing
545        if (typeof initComparison !== 'function') {
546            return;
547        }
548
549
550        initComparison();
551
552        var containers = document.querySelectorAll('.cd-image-container');
553        if (!containers.length) {
554            return;
555        }
556
557        containers.forEach(function (container) {
558            ScrollTrigger.create({
559                trigger: container,
560                start: 'top 50%', // run when this container is roughly half visible
561                once: true,
562                onEnter: function () {
563                    container.click();
564                }
565            });
566        });
567    }
568
569    YEAR2026.prototype.resizeAnimations = function () {
570        var resizeEvent = new CustomEvent('resize');
571        // Dispatch the event
572        window.dispatchEvent(resizeEvent);
573    }
574
575    YEAR2026.prototype.isChrome119OrLower = function () {
576        var userAgent = navigator.userAgent;
577        var chromeAgent = userAgent.indexOf("Chrome") > -1;
578    
579        if (chromeAgent) {
580            var chromeVersion = userAgent.match(/Chrome\/(\d+)/);
581            if (chromeVersion && chromeVersion.length > 1) {
582                var versionNumber = parseInt(chromeVersion[1], 10);
583                return versionNumber <= 119;
584            }
585        }
586        return false;
587    }
588
589    YEAR2026.prototype.initFloats = function () {
590        var self = this;
591
592        $('.year-in-review-2026--float-bottom').each(function () {
593            var $el = $(this);
594            var yOffset = 50;
595            var xOffset = 0;
596            var offset = 100;
597            var duration = 0.5;
598            var delay = 0;
599            var triggerElement = this;
600
601            if (typeof $el.data('trigger') != "undefined") {
602                triggerElement = $el.data('trigger');
603            }
604            if (typeof $el.data('yoffset') != "undefined") {
605                yOffset = $el.data('yoffset');
606            }
607            if (typeof $el.data('xoffset') != "undefined") {
608                xOffset = $el.data('xoffset');
609            }
610            if (typeof $el.data('offset') != "undefined") {
611                offset = $el.data('offset');
612            }
613            if (typeof $el.data('duration') != "undefined") {
614                duration = $el.data('duration');
615            }
616            if (typeof $el.data('delay') != "undefined") {
617                delay = $el.data('delay');
618            }
619
620            gsap.from(this, {
621                duration: duration,
622                delay: delay,
623                y: yOffset,
624                x: xOffset,
625                autoAlpha: 0,
626                ease: Power4.easeOut,
627                scrollTrigger: {
628                    trigger: this,
629                    toggleActions: "play pause resume reverse",
630                    start: "+=" + offset + " bottom",
631                }
632            });
633            // new ScrollMagic.Scene({ triggerElement: triggerElement, triggerHook: 1, reverse: true, offset: offset, duration: 0 })
634            //     .setTween(tween)
635            //     .setClassToggle(this, 'visible')
636            //     // .addIndicators() // add indicators (requires plugin)
637            // .addTo(self.controller);
638        });
639
640        $('.year-2026--text-scale').each(function () {
641            var $el = $(this);
642            var duration = 0.5;
643            var delay = 0;
644            var offset = 100;
645            var scale = 3;
646            var triggerElement = this;
647
648            if (typeof $el.data('trigger') != "undefined") {
649                triggerElement = $el.data('trigger');
650            }
651            if (typeof $el.data('duration') != "undefined") {
652                duration = $el.data('duration');
653            }
654            if (typeof $el.data('offset') != "undefined") {
655                offset = $el.data('offset');
656            }
657            if (typeof $el.data('delay') != "undefined") {
658                delay = $el.data('delay');
659            }
660            if (typeof $el.data('scale') != "undefined") {
661                scale = $el.data('scale');
662            }
663
664            gsap.from(this.querySelector('.year-2026--text-scale--inner'), {
665                duration: duration,
666                delay: delay,
667                scale: scale,
668                autoAlpha: 0,
669                ease: Power4.easeOut,
670                scrollTrigger: {
671                    trigger: this,
672                    toggleActions: "play pause resume reverse",
673                    start: "+=" + offset + " bottom",
674                }
675            });
676            // new ScrollMagic.Scene({ triggerElement: triggerElement, triggerHook: 1, reverse: true, offset: offset, duration: 0 })
677            //     .setTween(tween)
678            //     .setClassToggle(this, 'visible')
679            //     // .addIndicators() // add indicators (requires plugin)
680            // .addTo(self.controller);
681        });
682
683        $('.year-in-review-2026--scale-in').each(function () {
684            var options = {
685                scale: 2,
686                duration: 2,
687                ease: "Power4.easeOut",
688                scrollTrigger: {
689                    trigger: this,
690                    toggleActions: "play pause resume reverse",
691                }
692            };
693            if($(this).hasClass('year-in-review-2026--scale-in__center')) {
694                options.transformOrigin = "center";
695            }
696            gsap.from(this.querySelector('img'), options);
697            gsap.from(this,
698                {
699                    scale: 0.8,
700                    duration: 2,
701                    autoAlpha: 0,
702                    y: 20,
703                    ease: "Power4.easeOut",
704                    scrollTrigger: {
705                        trigger: this,
706                        toggleActions: "play pause resume reverse",
707                    }
708                });
709        });
710
711        $('.year-in-review-2026--scale-from').each(function () {
712            var options = {
713                scale: 3,
714                duration: 1,
715                autoAlpha: 0,
716                ease: "Power4.easeOut",
717                scrollTrigger: {
718                    trigger: this,
719                    toggleActions: "play pause resume reverse",
720                }
721            };
722            gsap.from(this.querySelector('img'), options);
723        });
724    }
725
726    YEAR2026.prototype.initHouseWipe = function () {
727        if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
728            return;
729        }
730
731        var section = document.querySelector('.year-in-review-2025--lrh-landmarks');
732        if (!section) {
733            return;
734        }
735
736        var wrapper = section.querySelector('.year-in-review-2025--lrh-landmark-images-wrapper');
737        var images = wrapper ? wrapper.querySelectorAll('.year-in-review-2025--lrh-landmark-image') : [];
738        var columns = section.querySelectorAll('.year-in-review-2025--lrh-landmark-column');
739
740        if (!wrapper || images.length < 3 || columns.length < 3) {
741            return;
742        }
743
744        ScrollTrigger.create({
745            trigger: '.year-in-review-2025--lrh-landmark-image__first',
746            start: 'center center',
747            endTrigger: columns[2],
748            end: 'center center',
749            pin: wrapper,
750            anticipatePin: 1,
751            invalidateOnRefresh: true,
752        });
753
754        images.forEach(function (img, index) {
755            gsap.set(img, { zIndex: index + 1 });
756        });
757
758        var wipes = [
759            { column: columns[1], image: images[1] },
760            { column: columns[2], image: images[2] },
761        ];
762
763        wipes.forEach(function (item) {
764            if (!item.column || !item.image) {
765                return;
766            }
767
768            gsap.fromTo(item.image,
769                {
770                    clipPath: 'polygon(0 100%, 100% 100%, 100% 100%, 0 100%)',
771                },
772                {
773                    clipPath: 'polygon(0 0%, 100% 0%, 100% 100%, 0 100%)',
774                    ease: 'none',
775                    scrollTrigger: {
776                        trigger: item.column,
777                        start: 'top center+=30%',
778                        end: 'center center+=10%',
779                        scrub: true,
780                    }
781                });
782        });
783    }
784
785    YEAR2026.prototype.initLandmarkSlideShow = function () {
786        gsap.timeline({
787            scrollTrigger: {
788                trigger: ".year-in-review-2026-lrh-house-wrapper",
789                start: "top center+=20%",
790                toggleActions: "play none resume reverse",
791            }
792        })
793        .from('.year-in-review-2026-lrh-house--inner', {
794            duration: 1,
795            autoAlpha: 0,
796            stagger: {
797                amount: 2,
798                from: 0
799            }
800        })
801
802        var wrapper = document.querySelector('.year-in-review-2026-lrh-houses');
803        if (!wrapper) {
804            return;
805        }
806
807        var slides = wrapper.querySelectorAll('.year-in-review-2026-lrh-house');
808        if (!slides || !slides.length) {
809            return;
810        }
811
812        var nav = document.querySelector('.year-2026-lrh-houses-nav');
813        if (!nav) {
814            return;
815        }
816
817        var prevBtn = nav.querySelector('.year-2026-lrh-houses--arrow__prev');
818        var nextBtn = nav.querySelector('.year-2026-lrh-houses--arrow__next');
819        if (!prevBtn || !nextBtn) {
820            return;
821        }
822
823        var isLikelyVisible = function (el) {
824            if (!el) {
825                return false;
826            }
827            // quick style checks
828            try {
829                var cs = window.getComputedStyle(el);
830                if (!cs || cs.display === 'none' || cs.visibility === 'hidden' || parseFloat(cs.opacity || '1') <= 0) {
831                    return false;
832                }
833            } catch (e) { }
834
835            // viewport intersection check
836            var rect = el.getBoundingClientRect();
837            if (!rect || (!rect.width && !rect.height)) {
838                return false;
839            }
840            var vw = window.innerWidth || document.documentElement.clientWidth || 0;
841            var vh = window.innerHeight || document.documentElement.clientHeight || 0;
842            // require at least a small portion visible
843            var threshold = 8;
844            var inX = rect.right > threshold && rect.left < (vw - threshold);
845            var inY = rect.bottom > threshold && rect.top < (vh - threshold);
846            return inX && inY;
847        };
848
849        var shouldHandleKeys = function () {
850            // Only handle keys when the nav is on-screen.
851            if (!isLikelyVisible(nav)) {
852                return false;
853            }
854            // Don't steal arrow keys when typing/using form elements.
855            var ae = document.activeElement;
856            if (ae) {
857                var tag = (ae.tagName || '').toLowerCase();
858                if (tag === 'input' || tag === 'textarea' || tag === 'select' || ae.isContentEditable) {
859                    return false;
860                }
861            }
862            return true;
863        };
864
865        // Prevent duplicate listeners if Init runs more than once.
866        if (wrapper._lrhCarousel && typeof wrapper._lrhCarousel.destroy === 'function') {
867            wrapper._lrhCarousel.destroy();
868        }
869
870        var rtl = document.documentElement && document.documentElement.getAttribute('dir') === 'rtl';
871        var viewport = wrapper.parentElement || document.body;
872
873        var state = {
874            index: 0,
875            step: 0,
876            maxIndex: 0,
877            bounds: { min: 0, max: 0 },
878        };
879
880        var getCurrentX = function () {
881            // All slides share the same x transform; read from the first.
882            try {
883                var v = gsap.getProperty(slides[0], 'x');
884                return (typeof v === 'number' && !isNaN(v)) ? v : 0;
885            } catch (e) {
886                return 0;
887            }
888        };
889
890        var setXImmediate = function (x) {
891            gsap.set(slides, { x: x, willChange: 'transform' });
892        };
893
894        var updateNavState = function () {
895            var atStart = state.index <= 0;
896            var atEnd = state.index >= state.maxIndex - 1;
897
898            if (prevBtn) {
899                prevBtn.classList.toggle('disabled', atStart);
900                prevBtn.setAttribute('aria-disabled', atStart ? 'true' : 'false');
901            }
902            if (nextBtn) {
903                nextBtn.classList.toggle('disabled', atEnd);
904                nextBtn.setAttribute('aria-disabled', atEnd ? 'true' : 'false');
905            }
906        };
907
908        var computeMetrics = function () {
909            // Step = slide width + horizontal margins.
910            var first = slides[0];
911            if (!first) {
912                return;
913            }
914
915            var style = window.getComputedStyle(first);
916            var marginLeft = parseFloat(style.marginLeft) || 0;
917            var marginRight = parseFloat(style.marginRight) || 0;
918
919            state.step = (first.offsetWidth || 0) + marginLeft + marginRight;
920
921            // Bounds based on scrollable overflow.
922            var viewportWidth = viewport.clientWidth || window.innerWidth || 0;
923            // var maxOffset = Math.max(0, (wrapper.scrollWidth || 0) - viewportWidth);
924            var maxOffset = Math.max(0, state.step * (slides.length - 1));
925
926            state.bounds.min = -maxOffset;
927            state.bounds.max = 0;
928
929            if (!state.step || state.step <= 0) {
930                state.maxIndex = 0;
931            } else {
932                state.maxIndex = Math.max(0, Math.ceil(maxOffset / state.step));
933            }
934
935            // Keep current index valid.
936            state.index = Math.max(0, Math.min(state.maxIndex, state.index));
937
938            // Clamp current visual position (do NOT snap to state.index; that causes jumps when
939            // rapid clicks interrupt an in-flight tween).
940            var currentX = getCurrentX();
941            var clampedX = gsap.utils.clamp(state.bounds.min, state.bounds.max, currentX);
942            if (clampedX !== currentX) {
943                setXImmediate(clampedX);
944            }
945
946            updateNavState();
947        };
948
949        // Ensure a deterministic starting position.
950        setXImmediate(0);
951        computeMetrics();
952
953        var gotoIndex = function (targetIndex) {
954            computeMetrics();
955
956            targetIndex = Math.max(0, Math.min(state.maxIndex, targetIndex));
957            state.index = targetIndex;
958
959            updateNavState();
960
961            var targetX = -1 * state.index * state.step;
962            targetX = gsap.utils.clamp(state.bounds.min, state.bounds.max, targetX);
963
964            var startX = getCurrentX();
965            var dist = Math.abs(targetX - startX);
966            var isInterrupt = !!(wrapper._lrhCarouselTween && typeof wrapper._lrhCarouselTween.isActive === 'function' && wrapper._lrhCarouselTween.isActive());
967            var perStep = isInterrupt ? 0.35 : 0.55;
968            var duration = state.step > 0 ? (dist / state.step) * perStep : 0.45;
969            duration = gsap.utils.clamp(0.2, 0.85, duration);
970
971            if (wrapper._lrhCarouselTween) {
972                try { wrapper._lrhCarouselTween.kill(); } catch (e) { }
973                wrapper._lrhCarouselTween = null;
974            }
975
976            wrapper._lrhCarouselTween = gsap.to(slides, {
977                x: targetX,
978                duration: duration,
979                ease: 'power3.inOut',
980                overwrite: true,
981            });
982        };
983
984        var onPrev = function (e) {
985            e && e.preventDefault && e.preventDefault();
986            if (prevBtn && prevBtn.classList && prevBtn.classList.contains('disabled')) {
987                return;
988            }
989            gotoIndex(state.index - 1);
990        };
991
992        var onNext = function (e) {
993            e && e.preventDefault && e.preventDefault();
994            if (nextBtn && nextBtn.classList && nextBtn.classList.contains('disabled')) {
995                return;
996            }
997            gotoIndex(state.index + 1);
998        };
999
1000        var onKeyDown = function (e) {
1001            if (!e) {
1002                return;
1003            }
1004            if (e.defaultPrevented) {
1005                return;
1006            }
1007            if (e.altKey || e.ctrlKey || e.metaKey) {
1008                return;
1009            }
1010            if (!shouldHandleKeys()) {
1011                return;
1012            }
1013
1014            // Support modern + older key values.
1015            var key = e.key || e.code || e.keyCode;
1016            if (key === 'ArrowLeft' || key === 'Left' || key === 37) {
1017                e.preventDefault();
1018                // In RTL, ArrowLeft should advance visually to the "next" item.
1019                if (rtl) {
1020                    onNext(e);
1021                } else {
1022                    onPrev(e);
1023                }
1024            }
1025            if (key === 'ArrowRight' || key === 'Right' || key === 39) {
1026                e.preventDefault();
1027                // In RTL, ArrowRight should go to the "previous" item.
1028                if (rtl) {
1029                    onPrev(e);
1030                } else {
1031                    onNext(e);
1032                }
1033            }
1034        };
1035
1036        prevBtn.addEventListener('click', onPrev);
1037        nextBtn.addEventListener('click', onNext);
1038        window.addEventListener('keydown', onKeyDown, { passive: false });
1039
1040        var onResize = throttle(function () {
1041            computeMetrics();
1042        }, 150);
1043
1044        window.addEventListener('resize', onResize, { passive: true });
1045        window.addEventListener('load', onResize, { passive: true });
1046
1047        wrapper._lrhCarousel = {
1048            destroy: function () {
1049                try { prevBtn.removeEventListener('click', onPrev); } catch (e) { }
1050                try { nextBtn.removeEventListener('click', onNext); } catch (e) { }
1051                try { window.removeEventListener('keydown', onKeyDown); } catch (e) { }
1052                try { window.removeEventListener('resize', onResize); } catch (e) { }
1053                try { window.removeEventListener('load', onResize); } catch (e) { }
1054                if (wrapper._lrhCarouselTween) {
1055                    try { wrapper._lrhCarouselTween.kill(); } catch (e) { }
1056                    wrapper._lrhCarouselTween = null;
1057                }
1058            }
1059        };
1060    }
1061
1062    YEAR2026.prototype.initLandmarkTimeline = function () {
1063        var section = document.querySelector('.year-in-review-2026-lrh-houses');
1064        if (!section || typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
1065            return;
1066        }
1067
1068
1069        return;
1070
1071        var houses = section.querySelectorAll('.year-in-review-2026-lrh-house');
1072        if (!houses.length) {
1073            return;
1074        }
1075
1076        // Calculate offsets so each house centers in the viewport.
1077        var getMetrics = function () {
1078            var viewportWidth = window.innerWidth;
1079            var first = houses[0];
1080            var last = houses[houses.length - 1];
1081            var firstCenter = first.offsetLeft + (first.offsetWidth / 2);
1082            var lastCenter = last.offsetLeft + (last.offsetWidth / 2);
1083
1084            var startX = (viewportWidth / 2) - firstCenter;
1085            var endX = (viewportWidth / 2) - lastCenter;
1086
1087            var denominator = (endX - startX) || 1;
1087 // avoid divide-by-zero
1088
1089            var snapPoints = Array.from(houses).map(function (house) {
1090                var center = house.offsetLeft + (house.offsetWidth / 2);
1091                var targetX = (viewportWidth / 2) - center;
1092                return (targetX - startX) / denominator;
1093            });
1094
1095            return {
1096                startX: startX,
1097                endX: endX,
1098                snapPoints: snapPoints
1099            };
1100        };
1101
1102        var timelines = section.querySelectorAll('.year-in-review-2026-lrh-house-timeline');
1103
1104        gsap.timeline({
1105            scrollTrigger: {
1106                trigger: ".year-in-review-2026-lrh-house-wrapper",
1107                start: "top center+=20%",
1108                toggleActions: "play none resume reverse",
1109            }
1110        })
1111        .from('.year-in-review-2026-lrh-house-wrapper', {
1112            x: 500,
1113            duration: 2,
1114            ease: 'power4.out',
1115            autoAlpha: 0,
1116        })
1117
1118        var timeline = gsap.timeline({
1119            defaults: {
1120                ease: 'none'
1121            },
1122            scrollTrigger: {
1123                trigger: ".year-in-review-2026-lrh-house-trigger",
1124                start: "top top",
1125                // duration: "100%",
1126                end: "bottom bottom",
1127                pin: '.year-in-review-2026-lrh-houses-pin',
1128                scrub: true,
1129                anticipatePin: 1,
1130                invalidateOnRefresh: true,
1131                pinSpacing: false,
1132                snap: {
1133                    snapTo: function (value) {
1134                        var metrics = getMetrics();
1135                        var snapper = gsap.utils.snap(metrics.snapPoints);
1136                        return snapper(value);
1137                    },
1138                    duration: 1,
1139                    ease: "power1.inOut"
1140                }
1141            }
1142        });
1143        
1144
1145        timeline.fromTo(
1146            section,
1147            { 
1148                x: function () {
1149                    var metrics = getMetrics();
1150                    return metrics.startX;
1151                },
1152            },
1153            {
1154                x: function () {
1155                    var metrics = getMetrics();
1156                    return metrics.endX;
1157                },
1158            },
1159            0
1160        );
1161
1162        // Move timeline graphics at half the distance for a parallax-style offset.
1163        if (timelines.length) {
1164            timeline.fromTo(
1165                timelines,
1166                {
1167                    x: function () {
1168                        var metrics = getMetrics();
1169                        return -metrics.startX;
1170                    },
1171                },
1172                {
1173                    x: function () {
1174                        var metrics = getMetrics();
1175                        return -0.5 * (metrics.endX - metrics.startX);
1176                    },
1177                },
1178                0
1179            );
1180        }
1181
1182        // Keep the first house centered on refresh/resizes before scrubbing begins.
1183        ScrollTrigger.addEventListener('refreshInit', function () {
1184            var metrics = getMetrics();
1185            gsap.set(section, { x: metrics.startX });
1186        });
1187    }
1188
1189    YEAR2026.prototype.initBdGameWinners = function () {
1190        if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
1191            return;
1192        }
1193
1194        var section = document.querySelector('.year-in-review-2026-winners');
1195        if (!section) {
1196            return;
1197        }
1198
1199        var pinEl = section.querySelector('.year-in-review-2026-winners-inner') || section;
1200
1201        var imagesWrapper = section.querySelector('.year-in-review-2026-images');
1202        var images = imagesWrapper ? Array.prototype.slice.call(imagesWrapper.querySelectorAll('.year-in-review-2026-bd-game-winner')) : [];
1203
1204        var featuredWrapper = section.querySelector('.year-in-review-2026-featured-image');
1205        var title = section.querySelector('.year-in-review-2026-title');
1206        var subtitle = section.querySelector('.year-in-review-2026-subtitle');
1207
1208        if (!featuredWrapper || !images.length) {
1209            return;
1210        }
1211
1212        // Ensure only one instance exists.
1213        if (section._bdWinnersTl) {
1214            try { section._bdWinnersTl.scrollTrigger && section._bdWinnersTl.scrollTrigger.kill(true); } catch (e) { }
1215            try { section._bdWinnersTl.kill(); } catch (e) { }
1216            section._bdWinnersTl = null;
1217        }
1218
1219        // Kill any existing per-element ScrollTriggers created earlier (initFloats / scale-in triggers)
1220        // that would otherwise fight with this scrubbed timeline.
1221        var killTriggersForEl = function (el) {
1222            if (!el) {
1223                return;
1224            }
1225            try {
1226                gsap.killTweensOf(el);
1227            } catch (e) { }
1228            try {
1229                ScrollTrigger.getAll().forEach(function (st) {
1230                    if (st && st.trigger === el) {
1231                        st.kill(true);
1232                    }
1233                });
1234            } catch (e) { }
1235        };
1236
1237        if (title) {
1238            killTriggersForEl(title);
1239            title.classList.remove('year-in-review-2026--float-bottom');
1240        }
1241        if (subtitle) {
1242            killTriggersForEl(subtitle);
1243            subtitle.classList.remove('year-in-review-2026--float-bottom');
1244        }
1245
1246        // Featured image often has `.year-in-review-2026--scale-in` on a nested wrapper.
1247        // Remove it within this section to avoid the generic scale-in ScrollTrigger.
1248        var featuredScaleIn = featuredWrapper.querySelector('.year-in-review-2026--scale-in');
1249        if (featuredScaleIn) {
1250            killTriggersForEl(featuredScaleIn);
1251            featuredScaleIn.classList.remove('year-in-review-2026--scale-in');
1252        }
1253
1254        // Deterministic pseudo-random dispersal: emit from center, fling in varied directions.
1255        var rand01 = function (index, salt) {
1256            var x = Math.sin((index + 1) * 999 + salt * 1013) * 10000;
1257            return x - Math.floor(x);
1258        };
1259
1260        var getDisperse = function (globalIndex, localIndex, waveSize, waveNumber, multiplier) {
1261            var w = window.innerWidth || 1200;
1262            var h = window.innerHeight || 800;
1263
1264            // Keep images mostly within the viewport regardless of device size.
1265            // These clamps are conservative to avoid cropping (cards are ~300px wide).
1266            var clampX = gsap.utils.clamp(-w * 0.28, w * 0.28);
1267            var clampY = gsap.utils.clamp(-h * 0.20, h * 0.20);
1268
1269            var base = Math.min(w, h) * 0.34 * multiplier;
1270
1271            // Evenly spaced angles within each wave to avoid clustering/overlap.
1272            // Add a small deterministic jitter so it doesn't look too geometric.
1273            var twoPi = Math.PI * 2;
1274            var jitter = (rand01(globalIndex, 7) - 0.5) * (Math.PI / 10);
1275            var waveRot = (waveNumber * 0.9) + (rand01(waveNumber, 9) * 0.6);
1276            var angle = ((localIndex / Math.max(waveSize, 1)) * twoPi) + waveRot + jitter;
1277
1278            // Radius varies by localIndex so items in a wave don't sit on top of each other.
1279            var localSpread = 0.55 + (localIndex * 0.22);
1280            var radius = base * localSpread;
1281
1282            // Slight vertical bias so it feels more "thrown".
1283            var yBias = 0.85 + (rand01(globalIndex, 3) * 0.25);
1284
1285            var x = Math.cos(angle) * radius;
1286            var y = Math.sin(angle) * radius * yBias;
1287
1288            return {
1289                x: clampX(x),
1290                y: clampY(y),
1291            };
1292        };
1293
1294        // Helper: treat the stream as groups of 4 so only 4 positions are active at a time.
1295        // The group number rotates the scatter so it doesn't feel repetitive.
1296        var getGroupMetrics = function (i) {
1297            var groupIndex = Math.floor(i / 4);
1298            var groupStart = groupIndex * 4;
1299            var waveSize = Math.min(4, Math.max(1, images.length - groupStart));
1300            var localIndex = i - groupStart;
1301            return {
1302                groupIndex: groupIndex,
1303                groupStart: groupStart,
1304                waveSize: waveSize,
1305                localIndex: localIndex,
1306                waveNumber: groupIndex + 1,
1307            };
1308        };
1309
1310        // Per-item offset so the stream doesn't look like repeating "waves of 4".
1311        // We still keep the 4-slot safety (via waveSize/localIndex), but add deterministic
1312        // jitter to angle/rotation so each new item lands slightly differently.
1313        var getStreamDisperse = function (i, multiplier) {
1314            var m = getGroupMetrics(i);
1315            var slotJitter = (rand01(i, 21) - 0.5) * 0.65; // breaks obvious 0-1-2-3 repetition
1316            var rotJitter = (rand01(i, 22) - 0.5) * 0.55;
1317            return getDisperse(i, m.localIndex + slotJitter, m.waveSize, m.waveNumber + rotJitter, multiplier);
1318        };
1319
1320        // Set starting states.
1321        // Place winner cards already dispersed (near their final positions) s
1321o they
1322        // don't crash into each other while scaling in.
1323        var disperseMult = 1.45;
1324        // Start closer together so the cards travel more during the stream-in,
1325        // while still avoiding a single stacked center point.
1326        var disperseStartScale = 0.55;
1327
1328        var setInitialStates = function () {
1329            gsap.set(pinEl, { willChange: 'transform' });
1330            gsap.set(featuredWrapper, { transformOrigin: '50% 50%', scale: 0.4, autoAlpha: 0 });
1331
1332            gsap.set(images, {
1333                transformOrigin: '50% 50%',
1334                scale: 0.4,
1335                x: function (i) {
1336                    var d = getStreamDisperse(i, disperseMult);
1337                    return d.x * disperseStartScale;
1338                },
1339                y: function (i) {
1340                    var d = getStreamDisperse(i, disperseMult);
1341                    return d.y * disperseStartScale;
1342                },
1343                autoAlpha: 0,
1344            });
1345            if (title) {
1346                gsap.set(title, { autoAlpha: 0, y: 20 });
1347            }
1348            if (subtitle) {
1349                gsap.set(subtitle, { autoAlpha: 0, y: 20 });
1350            }
1351        };
1352
1353        setInitialStates();
1354
1355        gsap.timeline({
1356            defaults: { ease: 'none' },
1357            scrollTrigger: {
1358                trigger: '.year-in-review-2026-winners',
1359                start: 'top top',
1360                end: 'bottom bottom',
1361                scrub: true,
1362                pin: '.year-2025-lrh-bd-winners-bg',
1363                anticipatePin: 1,
1364                invalidateOnRefresh: true,
1365            }
1366        });
1367
1368        var tl = gsap.timeline({
1369            defaults: { ease: 'none' },
1370            scrollTrigger: {
1371                trigger: section,
1372                start: 'top top',
1373                end: 'bottom bottom',
1374                scrub: true,
1375                pin: pinEl,
1376                anticipatePin: 1,
1377                invalidateOnRefresh: true,
1378                onRefreshInit: function () {
1379                    setInitialStates();
1380                },
1381            }
1382        });
1383
1384        // Continuous stream: reveal one-by-one, keeping at most 4 visible.
1385        // Timings are in "timeline seconds" which map to scroll distance via scrub.
1386        var itemInDur = 0.34;
1387        // Faster fades for a slicker handoff.
1388        var fadeInDur = 0.08;
1389        var fadeOutDur = 0.10;
1390        var scaleEnd = 1.12;
1391
1392        // Spacing between the start of each card entering.
1393        // Clamp to guarantee no more than 4 visible at once.
1394        var maxVisible = 4;
1395        // Keep this close to the theoretical minimum so multiple cards overlap (1–4 visible).
1396        // Minimum is roughly itemInDur/maxVisible; keep a tiny cushion for float math.
1397        var streamStep = (itemInDur / maxVisible) + 0.006;
1398
1399        var startTimes = images.map(function (_, i) {
1400            return i * streamStep;
1401        });
1402
1403        // Collect all inner images once so we can fade them with their cards.
1404        var cardInnerImages = images.map(function (card) {
1405            var imgs = card ? card.querySelectorAll('img') : null;
1406            return (imgs && imgs.length) ? Array.prototype.slice.call(imgs) : [];
1407        });
1408
1409        images.forEach(function (card, i) {
1410            if (!card) {
1411                return;
1412            }
1413
1414            var startTime = startTimes[i] || 0;
1415            var m = getGroupMetrics(i);
1416
1417            // Fade in fast while scale continues.
1418            tl.to(card, {
1419                autoAlpha: 1,
1420                duration: fadeInDur,
1421                ease: 'linear',
1422            }, startTime);
1423
1424            // Scale + move into its dispersed position.
1425            tl.to(card, {
1426                scale: scaleEnd,
1427                duration: itemInDur,
1428                ease: 'linear',
1429                x: function () {
1430                    var d = getStreamDisperse(i, disperseMult);
1431                    return d.x;
1432                },
1433                y: function () {
1434                    var d = getStreamDisperse(i, disperseMult);
1435                    return d.y;
1436                },
1437            }, startTime);
1438
1439            // Fade out quickly so the card is gone by the time it finishes scaling.
1440            var fadeOutAt = startTime + itemInDur - fadeOutDur;
1441            tl.to(card, {
1442                autoAlpha: 0,
1443                duration: fadeOutDur,
1444                ease: 'linear',
1445            }, fadeOutAt);
1446
1447            var imgs = cardInnerImages[i];
1448            if (imgs && imgs.length) {
1449                tl.to(imgs, {
1450                    autoAlpha: 0,
1451                    duration: fadeOutDur,
1452                    ease: 'linear',
1453                }, fadeOutAt);
1454            }
1455        });
1456
1457        // Featured image comes in after the stream clears.
1458        var lastStart = startTimes.length ? startTimes[startTimes.length - 1] : 0;
1459        var streamEnd = lastStart + itemInDur;
1460        var featuredIn = streamEnd - 0.2;
1461        tl.to(featuredWrapper, {
1462            autoAlpha: 1,
1463            duration: 0.1,
1464            ease: 'none',
1465        }, featuredIn);
1466        tl.to(featuredWrapper, {
1467            scale: 1,
1468            duration: 0.5,
1469            ease: 'power4.out',
1470        }, featuredIn);
1471
1472        // 3) Finally title + subtitle fade in after the animation is over.
1473        if (title) {
1474            tl.to(title, {
1475                autoAlpha: 1,
1476                y: 0,
1477                duration: 0.08,
1478                ease: 'power2.out',
1479            }, featuredIn + 0.1);
1480        }
1481        if (subtitle) {
1482            tl.to(subtitle, {
1483                autoAlpha: 1,
1484                y: 0,
1485                duration: 0.08,
1486                ease: 'power2.out',
1487            }, featuredIn + 0.15);
1488        }
1489
1490        section._bdWinnersTl = tl;
1491    }
1492
1493    YEAR2026.prototype.initRotateImages = function () {
1494        var wrappers = document.querySelectorAll('.year-in-review-2026-rotate-wrapper');
1495        if (!wrappers.length) {
1496            return;
1497        }
1498
1499        wrappers.forEach(function (wrapper) {
1500
1501            if($(wrapper).closest('.year-in-review-2026-ao-row').length > 0) {
1502                wrapper = $(wrapper).closest('.year-in-review-2026-ao-row')[0];
1503            }
1504            
1505            var rotateContainers = wrapper.querySelectorAll('.year-in-review-2026-rotate-in-outer');
1506            if (!rotateContainers.length) {
1507                return;
1508            }
1509
1510            var innerItems = [];
1511            rotateContainers.forEach(function (container, containerIndex) {
1512                gsap.set(container, { zIndex: containerIndex + 1 });
1513                var inner = container.querySelector('.year-in-review-2026-rotate-in-inner');
1514                if (inner) {
1515                    innerItems.push(inner);
1516                }
1517            });
1518
1519            var replayButtons = wrapper.querySelectorAll('.year-in-review-2026--autoplay-video-replay-animation');
1520            gsap.set(replayButtons, { autoAlpha: 0 });
1521
1522            var tl = gsap.timeline({
1523                defaults: { ease: 'power4.out' },
1524                scrollTrigger: {
1525                    trigger: wrapper,
1526                    start: 'top center+=20%',
1527                    toggleActions: "play none resume reverse",
1528                },
1529                onComplete: function () {
1530                    gsap.to(replayButtons, { autoAlpha: 1, duration: 0.3 });
1531                }
1532            });
1533
1534            // Important: previous runs may have left some containers hidden.
1535            // Ensure a clean starting state so replay + reverse work reliably.
1536            tl.set(rotateContainers, { autoAlpha: 1 }, 0);
1537
1538            tl.from(rotateContainers, {
1539                rotation: 20,
1540                scale: 0,
1541                transformOrigin: '50% 50%',
1542                duration: 1,
1543                stagger: 1.1,
1544            }, 0);
1545
1546            if (innerItems.length) {
1547                tl.from(innerItems, {
1548                    scale: 1.7,
1549                    transformOrigin: '50% 50%',
1550                    duration: 1.1,
1551                    stagger: 1.1,
1552                }, 0);
1553            }
1554
1555            // After each next image finishes animating on top, hide the previous one.
1556            // Use the longer of the outer/inner durations so the handoff happens after the full "pop".
1557            if (rotateContainers.length > 1) {
1558                var rotateStagger = 1.1;
1559                var rotateEndDur = innerItems.length ? 1.1 : 1;
1560
1561                rotateContainers.forEach(function (_, i) {
1562                    if (i === 0) {
1563                        return;
1564                    }
1565                    var hideAt = (i * rotateStagger) + rotateEndDur;
1566                    tl.to(rotateContainers[i - 1], {
1567                        autoAlpha: 0,
1568                        duration: 0.01,
1569                        ease: 'none',
1570                    }, hideAt);
1571                });
1572            }
1573
1574            tl.from(wrapper.querySelectorAll('.year-in-review-2026-ao-row--image__left'), {
1575                x: -100,
1576                autoAlpha: 0,
1577                duration: 0.8,
1578            }, "-=0.5");
1579
1580            tl.from(wrapper.querySelectorAll('.year-in-review-2026-ao-row--image__right'), {
1581                x: 100,
1582                autoAlpha: 0,
1583                duration: 0.8,
1584            }, "-=0.5");
1585
1586
1587            replayButtons.forEach(function (btn) {
1588                btn.addEventListener('click', function (e) {
1589                    e.preventDefault && e.preventDefault();
1590                    gsap.set(replayButtons, { autoAlpha: 0 });
1591                    gsap.set(rotateContainers, { autoAlpha: 1 });
1592                    tl && tl.restart(true, false);
1593                });
1594            });
1595        });
1596    }
1597
1598    YEAR2026.prototype.initIASStats = function () {
1599
1600        var stats = document.querySelectorAll('.year-in-review-2025-ias-stats .year-in-review-2025-ias-stat--inner');
1601
1602        gsap.timeline({
1603            scrollTrigger: {
1604                trigger: '.year-in-review-2025-ias-accomplishments',
1605                start: 'top top',
1606                toggleActions: "play none resume reverse",
1607                pin: '.year-in-review-ias-bg-wing',
1608                end: 'bottom bottom',
1609                pinSpacing: false,
1610            },
1611        })
1612        .from(stats[0].parentElement, {
1613            autoAlpha: 0,
1614            scale: 8,
1615            duration: 0.25,
1616        });
1617
1618        gsap.timeline({
1619            scrollTrigger: {
1620                trigger: '.year-in-review-2025-our-help-stats-trigger-1',
1621                start: 'top top',
1622                toggleActions: "play none resume reverse",
1623                pinSpacing: false,
1624            },
1625        })
1626        .to(stats[0], {
1627            autoAlpha: 0,
1628            duration: 0.1,
1629        })
1630        .from(stats[1], {
1631            autoAlpha: 0,
1632            scale: 8,
1633            duration: 0.25,
1634        }, "<")
1635
1636        // pin .year-in-review-2025-our-help-lead-in
1637        gsap.timeline({
1638            scrollTrigger: {
1639                trigger: '.year-in-review-2025-ias-accomplishments',
1640                start: 'top top',
1641                toggleActions: "play none resume reverse",
1642                pin: '.year-in-review-2025-our-help-stats',
1643                end: 'bottom bottom',
1644                pinSpacing: false,
1645            },
1646        });
1647    }
1648
1649    YEAR2026.prototype.initEndCurious = function () {
1650
1651         gsap.timeline({
1652            scrollTrigger: {
1653                trigger: '.year-in-review-2026-bottom-image--bg',
1654                start: 'bottom bottom',
1655                endTrigger: '.year-in-review-2026-bottom-wrapper',
1656                end: 'bottom bottom',
1657                scrub: true,
1658                toggleActions: "play none resume reverse",
1659                duration: "100%",
1660                pin: true,
1661                pinSpacing: false,
1662                anticipatePin: 1,
1663                invalidateOnRefresh: true,
1664            },
1665        });
1666
1667         gsap.timeline({
1668            scrollTrigger: {
1669                trigger: '.year-in-review-2026-bottom-stars-wrapper',
1670                start: 'bottom bottom',
1671                endTrigger: '.year-in-review-2026-bottom-wrapper',
1672                end: 'bottom bottom',
1673                scrub: true,
1674                toggleActions: "play none resume reverse",
1675                duration: "100%",
1676                pin: true,
1677                pinSpacing: false,
1678                anticipatePin: 1,
1679                invalidateOnRefresh: true,
1680            },
1681        });
1682        
1683        var tl = gsap.timeline({
1684            scrollTrigger: {
1685                trigger: '.year-in-review-2026-bottom-image--man',
1686                start: 'bottom bottom',
1687                endTrigger: '.year-in-review-2026-bottom-wrapper',
1688                end: 'bottom bottom',
1689                scrub: true,
1690                toggleActions: "play none resume reverse",
1691                duration: "100%",
1692                pin: true,
1693                pinSpacing: false,
1694                anticipatePin: 1,
1695                invalidateOnRefresh: true,
1696            },
1697        });
1698
1699        tl.fromTo('.year-in-review-2026-bottom-image--man', {
1700            yPercent: 5,
1701        }, {
1702            yPercent: -10,
1703        });
1704
1705        var bottomTexts = document.querySelectorAll('.year-in-review-2026-bottom-text');
1706
1707        bottomTexts.forEach(function (title) {
1708            var split = title._splitTextInstance;
1709            if (!split) {
1710                split = new SplitText(title, {
1711                    type: 'words,chars',
1712                    wordsClass: 'word',
1713                    charsClass: 'char'
1714                });
1715                title._splitTextInstance = split;
1716            }
1717
1718            var words = (split && split.words) ? split.words : [];
1719            if (!words.length) {
1720                return;
1721            }
1722
1723            var wordTimeline = gsap.timeline({
1724                defaults: {
1725                    ease: 'power1.inOut'
1726                },
1727                scrollTrigger: {
1728                    trigger: title,
1729                    start: 'top center',
1730                    end: 'bottom center',
1731                    toggleActions: 'play reverse play reverse',
1732                    // markers: true,
1733                },
1734            });
1735
1736            var allChars = [];
1737            words.forEach(function (word) {
1738                var chars = word.querySelectorAll('.char');
1739                chars.forEach(function (char) {
1740                    if (char.parentNode) {
1741                        gsap.set(char.parentNode, { perspective: 2000 });
1742                    }
1743                    allChars.push(char);
1744                });
1745            });
1746
1747            if (allChars.length) {
1748                wordTimeline.fromTo(allChars, {
1749                    'will-change': 'opacity, transform',
1750                    opacity: 0,
1751                    rotationX: -90,
1752                    yPercent: 50
1753                }, {
1754                    duration: 0.25,
1755                    opacity: 1,
1756                    rotationX: 0,
1757                    yPercent: 0,
1758                    stagger: {
1759                        amount: 0.25,
1760                        from: 0
1761                    }
1762                }, 0);
1763            }
1764        });
1765    }
1766
1767    YEAR2026.prototype.initParallax = function () {
1768
1769        _.each($('.year-review-2026--top-bg-parallax'), function (element, index) {
1770            var offset = element.getAttribute('data-parallax-offset');
1771
1772            gsap.to(element, {
1773                scrollTrigger: {
1774                    trigger: '.year-in-review-2026--hero-section',
1775                    scrub: true,
1776                    start: "top top",
1777                    end: "bottom top",
1778                },
1779                y: offset,
1780                ease: 'none'
1781            });
1782        });
1783
1784        _.each($('.year-review-2025--parallax'), function (element, index) {
1785            var offset = element.getAttribute('data-parallax-offset');
1786
1787            var triggerOptions = {
1788                trigger: element,
1789                scrub: true,
1790                start: "top bottom",
1791                end: "bottom top",
1792            };
1793
1794            if(element.getAttribute('data-trigger')) {
1795                triggerOptions.trigger = element.getAttribute('data-trigger');
1796            }
1797
1798            if(element.getAttribute('data-end-trigger')) {
1799                triggerOptions.endTrigger = element.getAttribute('data-end-trigger');
1800            }
1801
1802            if(element.getAttribute('data-end')) {
1803                triggerOptions.end = element.getAttribute('data-end');
1804            }
1805
1806            gsap.from(element.querySelector('img'), {
1807                scrollTrigger: triggerOptions,
1808                y: offset,
1809                ease: 'none'
1810            });
1811        });
1812    }
1813
1814
1815    YEAR2026.prototype.initAwardsScroller = function () {
1816        let sections = gsap.utils.toArray(".year-2025--awards-item");
1817
1818        var rtl = $('html').attr('dir') == 'rtl';
1819
1820        gsap.timeline({
1821            scrollTrigger: {
1822                trigger: ".year-2025-awards-wall-pin-trigger",
1823                start: "top top",
1824                endTrigger: ".year-2025-awards-wall-wrapper",
1825                end: "bottom bottom",
1826                pin: '.year-2026-awards-wall-container',
1827                anticipatePin: true,
1828                pinSpacing: false,
1829                scrub: true,
1830            }
1831        }).to('.year-2025-awards-wall', {
1832            x: "-30%",
1833        });
1834
1835
1836        gsap.to(sections, {
1837            xPercent: rtl ? 100 * (sections.length - 1) : -100 * (sections.length - 1),
1838            ease: "none",
1839            scrollTrigger: {
1840                trigger: ".year-2025--awards-row",
1841                pin: true,
1842                pinSpacing: false,
1843                scrub: 1,
1844                snap: {
1845                    snapTo: function(value) {
1846                        var interval = 1 / (sections.length - 1);
1847                        return Math.round(value / interval) * interval;
1848                    },
1849                    duration: { min: 0.2, max: 0.5 },
1850                    ease: "power2.inOut"
1851                },
1852                end: "100%",
1853            }
1854        });
1855
1856    }
1857
1858    YEAR2026.prototype.isIosVersionAtMost17 = function () {
1859        if(globalGetParams.iphone == "1") {
1860            return true;
1861        }
1862        var iosMatch = navigator.userAgent.match(/(?:iPhone|iPad|iPod).*OS\s+(\d+)_/i);
1863        if (!iosMatch) {
1864            return false;
1865        }
1866        var majorVersion = parseInt(iosMatch[1], 10);
1867        return !isNaN(majorVersion) && majorVersion <= 17;
1868    };
1869
1870    YEAR2026.prototype.initMosaicRows = function () {
1871        var mosaics = document.querySelectorAll('.year-in-review-2026-4d-mosaic--row');
1872
1873        if (!mosaics.length || typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
1874            return;
1875        }
1876
1877        // Responsive columns:
1878        // - < 768px: 2 columns
1879        // - >= 992px: 3 columns
1880        // (768–785 keeps the 2-col behavior to match authored breakpoint intent)
1881        var getColumnCount = function () {
1882            return (window.innerWidth >= 992) ? 3 : 2;
1883        };
1884
1885        var killTimeline = function (tl) {
1886            if (!tl) {
1887                return;
1888            }
1889            try {
1890                if (tl.scrollTrigger) {
1891                    tl.scrollTrigger.kill(true);
1892                }
1893            } catch (e) { }
1894            try {
1895                tl.kill();
1896            } catch (e) { }
1897        };
1898
1899        var buildParallax = function (mosaic, columnCount) {
1900            if (!mosaic) {
1901                return;
1902            }
1903
1904            // Ensure only one parallax timeline exists per mosaic.
1905            if (mosaic._mosaicParallaxTl) {
1906                killTimeline(mosaic._mosaicParallaxTl);
1907                mosaic._mosaicParallaxTl = null;
1908            }
1909
1910            var colsSelector1 = (columnCount === 2) ? '2n+1' : '3n+1';
1911            var colsSelector2 = (columnCount === 2) ? '2n+2' : '3n+2';
1912            var colsSelector3 = (columnCount === 2) ? null : '3n+3';
1913
1914            var col1 = mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--columns > :nth-child(' + colsSelector1 + ')');
1915            var col2 = mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--columns > :nth-child(' + colsSelector2 + ')');
1916            var col3 = colsSelector3 ? mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--columns > :nth-child(' + colsSelector3 + ')') : [];
1917
1918            var endTriggerEl = mosaic.closest('.bs-extender__4d-background') || mosaic;
1919
1920            var parallaxTl = gsap.timeline({
1921                scrollTrigger: {
1922                    trigger: mosaic,
1923                    start: 'top bottom',
1924                    end: 'bottom+=25% bottom',
1925                    endTrigger: endTriggerEl,
1926                    scrub: true,
1927                    invalidateOnRefresh: true,
1928                }
1929            });
1930
1931            if (col1.length) parallaxTl.to(col1, { y: -50, ease: 'none', duration: 1, }, 0);
1932            if (col2.length) parallaxTl.to(col2, { y: -100, ease: 'none', duration: 1, }, 0);
1933            if (col3.length) parallaxTl.to(col3, { y: -80, ease: 'none', duration: 1, }, 0);
1934
1935            parallaxTl.to(mosaic, {
1936                autoAlpha: 0,
1937                duration: 0.2,
1938            }, 0.8);
1939
1940            mosaic._mosaicParallaxTl = parallaxTl;
1941        };
1942
1943        mosaics.forEach(function (mosaic) {
1944            var tl = gsap.timeline({
1945                scrollTrigger: {
1946                    trigger: mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--image')[0],
1947                    start: 'bottom bottom',
1948                    toggleActions: "play none resume reverse",
1949                }
1950            });
1951
1952            tl.fromTo(mosaic.querySelector('.year-in-review-2026-4d-mosaic--desc-inner'), {
1953                y: 50,
1954                scale: 5,
1955                autoAlpha: 0,
1956            }, {
1957                y: 0,
1958                scale: 1,
1959                autoAlpha: 1,
1960                duration: 0.4,
1961                ease: 'power2.out',
1962            });
1963            
1964            // Pin the desc, and during the pin: pop the featured image + dim the rest.
1965            var allImages = mosaic.querySelectorAll('.year-in-review-2026-4d-mosaic--image');
1966
1967            var pinTl = gsap.timeline({
1968                scrollTrigger: {
1969                    trigger: mosaic.querySelector('.year-in-review-2026-rotate-wrapper__4d'),
1970                    start: 'center center',
1971                    toggleActions: "play none resume reverse",
1972                    pin: true,
1973                    pinSpacing: false,
1974                    invalidateOnRefresh: true,
1975                    anticipatePin: 1,
1976                    endTrigger: mosaic,
1977                    end: 'bottom bottom',
1978                }
1979            });
1980
1981            gsap.timeline({
1982                scrollTrigger: {
1983                    trigger: mosaic.querySelector('.year-in-review-2026-4d-mosaic--desc-wrapper'),
1984                    start: 'center center',
1985                    toggleActions: "play none resume reverse",
1986                    pin: true,
1987                    pinSpacing: false,
1988                    invalidateOnRefresh: true,
1989                    endTrigger: mosaic,
1990                    end: 'bottom-=50% center',
1991                }
1992            });
1993
1994            buildParallax(mosaic, getColumnCount());
1995        });
1996
1997        // Rebuild parallax timelines on breakpoint changes so nth-child 
1997selectors
1998        // always match the active column layout (prevents odd gaps on mobile/tablet).
1999        var self = this;
2000        if (self._mosaicRowsResizeHandler) {
2001            try { window.removeEventListener('resize', self._mosaicRowsResizeHandler); } catch (e) { }
2002            self._mosaicRowsResizeHandler = null;
2003        }
2004
2005        var lastColumnCount = getColumnCount();
2006        self._mosaicRowsResizeHandler = function () {
2007            // Cheap debounce: coalesce multiple resize events.
2008            if (self._mosaicRowsResizeRaf) {
2009                return;
2010            }
2011            self._mosaicRowsResizeRaf = window.requestAnimationFrame(function () {
2012                self._mosaicRowsResizeRaf = null;
2013                var nextCount = getColumnCount();
2014                if (nextCount === lastColumnCount) {
2015                    return;
2016                }
2017                lastColumnCount = nextCount;
2018                mosaics.forEach(function (mosaic) {
2019                    buildParallax(mosaic, nextCount);
2020                });
2021                try {
2022                    ScrollTrigger.refresh(true);
2023                } catch (e) { }
2024            });
2025        };
2026        window.addEventListener('resize', self._mosaicRowsResizeHandler);
2027
2028        var wrappers = document.querySelectorAll('.bs-extender__4d-background');
2029        wrappers.forEach(function (wrapper) {
2030            gsap.timeline({
2031                scrollTrigger: {
2032                    trigger: wrapper,
2033                    start: 'top top',
2034                    end: 'bottom bottom',
2035                    scrub: true,
2036                    pin: wrapper.querySelector('.year-in-review-2026-vignette'),
2037                    pinSpacing: false,
2038                    invalidateOnRefresh: true,
2039                }
2040            });
2041        });
2042    }
2043
2044    YEAR2026.prototype.initIASCCHRtransition = function () {
2045        if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
2046            return;
2047        }
2048
2049        var logo = document.querySelector('.year-in-review-2026-4d-logo-row--item--cchr-image');
2050        var background = document.querySelector('.bs-extender__cchr-background');
2051
2052        if (!logo) {
2053            return;
2054        }
2055
2056        // Helpers to center the logo and scale it to cover the viewport.
2057        var coverScale = 50;
2058        var centerOffset = { x: 0, y: 0 };
2059        var isRefreshing = false;
2060
2061        var tl; // timeline defined later; hoisted reference so recalc can invalidate safely
2062
2063        var recalcCover = function () {
2064            // cache current transform so measuring does not snap the animation
2065            var prevTransform = {
2066                x: gsap.getProperty(logo, 'x'),
2067                y: gsap.getProperty(logo, 'y'),
2068                scale: gsap.getProperty(logo, 'scale'),
2069                autoAlpha: gsap.getProperty(logo, 'autoAlpha'),
2070            };
2071
2072            // reset transforms so measurements are accurate
2073            gsap.set(logo, { x: 0, y: 0, scale: 1 });
2074
2075            var rect = logo.getBoundingClientRect();
2076            var vw = window.innerWidth || 1;
2077            var vh = window.innerHeight || 1;
2078
2079            if (rect.width && rect.height) {
2080                // scale large enough to cover viewport; keeps animation consistent after resize/refresh
2081                // coverScale = Math.max(vw / rect.width, vh / rect.height) * 1.2;
2082            }
2083
2084            var cx = rect.left + rect.width / 2;
2085            var cy = rect.top + rect.height / 2;
2086            centerOffset = {
2087                x: vw / 2 - cx,
2088                y: vh / 2 - cy,
2089            };
2090
2091            // restore previous transform so scrubbed timelines do not jump
2092            gsap.set(logo, prevTransform);
2093
2094            // keep any attached timeline in sync with the new measurements
2095            if (tl && tl.scrollTrigger) {
2096                var st = tl.scrollTrigger;
2097                var currentProgress = st.progress;
2098                var currentScrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
2099                tl.invalidate();
2100
2101                // refresh start/end/pin bounds once per recalc to keep reverse scrub aligned
2102                if (!isRefreshing) {
2103                    isRefreshing = true;
2104                    st.refresh();
2105                    isRefreshing = false;
2106                }
2107
2108                // restoring scroll position prevents refresh-induced jumps to the top
2109                window.scrollTo(0, currentScrollY);
2110                tl.progress(currentProgress);
2111            }
2112        };
2113
2114        recalcCover();
2115        ScrollTrigger.addEventListener('refreshInit', recalcCover);
2116        ScrollTrigger.addEventListener('refresh', recalcCover);
2117
2118        gsap.set(logo, {
2119            transformOrigin: '50% 50%',
2120            zIndex: 50,
2121            willChange: 'transform, opacity',
2122        });
2123
2124        tl = gsap.timeline({
2125            defaults: { ease: 'power1.inOut' },
2126            scrollTrigger: {
2127                trigger: logo,
2128                start: 'center center',
2129                endTrigger: background || logo,
2130                end: 'top+=15% top',
2131                scrub: true,
2132                pin: true,
2133                pinSpacing: true,
2134                anticipatePin: 1,
2135                invalidateOnRefresh: true,
2136                onEnter: recalcCover,
2137                onEnterBack: recalcCover,
2138                onRefresh: recalcCover,
2139            }
2140        });
2141
2142
2143        tl.fromTo(logo, {
2144            x: 0,
2145            y: 0,
2146            scale: 1,
2147            autoAlpha: 1,
2148        }, {
2149            x: function () { return centerOffset.x; },
2150            y: function () { return centerOffset.y; },
2151            scale: function () { return coverScale; },
2152            autoAlpha: 1,
2153            duration: 0.7,
2154        }, 0);
2155
2156
2157        tl.to(logo, {
2158            autoAlpha: 0,
2159            duration: 0.1,
2160        }, 0.7);
2161
2162        tl.fromTo('.year-in-review-ias-cchr-bg', {
2163            autoAlpha: 0,
2164        }, {
2165            autoAlpha: 1,
2166            duration: 0.2,
2167        }, 0.3);
2168
2169        if (background) {
2170            tl.fromTo(background, {
2171                autoAlpha: 0,
2172            }, {
2173                autoAlpha: 1,
2174                duration: 0.2,
2175            }, 0.3);
2176        }
2177    }
2178
2179    YEAR2026.prototype.sectionReveal = function () {
2180        if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
2181            return;
2182        }
2183
2184        var topSections = document.querySelectorAll('.video-section-reveal-animation');
2185
2186        var zIndex = 1000;
2187
2188        topSections.forEach(function (topSection) {
2189            var bottomSection = topSection.nextElementSibling;
2190
2191            if (bottomSection && bottomSection.classList.contains('video-section-reveal-animation')) {
2192                gsap.set(topSection, { zIndex: zIndex, position: 'relative' });
2193                gsap.set(bottomSection, { zIndex: zIndex - 1, position: 'relative' });
2194                zIndex -= 2;
2195
2196                gsap.fromTo(bottomSection,
2197                    {
2198                        y: "-100vh",
2199                    },
2200                    {
2201                        y: 0,
2202                        ease: 'none',
2203                        scrollTrigger: {
2204                            trigger: topSection,
2205                            start: 'bottom bottom',
2206                            end: 'bottom top+=10%',
2207                            scrub: true
2208                        }
2209                    }
2210                );
2211            }
2212        });
2213    }
2214
2215    YEAR2026.prototype.initSliderImages = function () {
2216        var self = this;
2217    
2218        $('.year-end-2026--carousel-images').each(function () {
2219            var $row = $(this);
2220
2221            // Note: When the row is narrower than the viewport, translating it will *create* visible gaps.
2222            // We keep it centered (no scroll movement) in that case, and clamp movement when wider.
2223            var getAttrNumber = function (name, fallback) {
2224                var raw = $row.attr('data-' + name);
2225                if (raw === undefined || raw === null || raw === '') {
2226                    return fallback;
2227                }
2228                var n = parseFloat(raw);
2229                return isNaN(n) ? fallback : n;
2230            };
2231
2232            var clamp = function (value, min, max) {
2233                if (value < min) {
2234                    return min;
2235                }
2236                if (value > max) {
2237                    return max;
2238                }
2239                return value;
2240            };
2241
2242            var calcRowWidth = function () {
2243                var totalWidth = 0;
2244                $row.children().each(function (_, child) {
2245                    totalWidth += $(child).outerWidth(true) || 0;
2246                });
2247                return totalWidth;
2248            };
2249
2250            var calcPositions = function () {
2251                var windowWidth = $(window).width() || 0;
2252                var rowWidth = calcRowWidth();
2253
2254                // Compute bounds based on where the row naturally sits in the viewport.
2255                // This makes the slider work for both LTR and RTL layouts (right-aligned rows).
2256                var rowEl = $row[0];
2257                var currentX = 0;
2258                try {
2259                    currentX = parseFloat(gsap.getProperty(rowEl, 'x')) || 0;
2260                } catch (e) {
2261                    currentX = 0;
2262                }
2263
2264                var rowRect = rowEl ? rowEl.getBoundingClientRect() : { left: 0 };
2265                // baseLeft = the row's viewport-left position when x === 0
2266                var baseLeft = (rowRect.left || 0) - currentX;
2267
2268                // Bounds that prevent showing empty space at either edge.
2269                // Solve for x such that: (baseLeft + x) <= 0 AND (baseLeft + x + rowWidth) >= windowWidth
2270                // => x in [windowWidth - rowWidth - baseLeft, -baseLeft]
2271                var minX = (windowWidth - rowWidth) - baseLeft;
2272                var maxX = -baseLeft;
2273
2274                // Centered position (x that makes left == (windowWidth-rowWidth)/2)
2275                var centerX = ((windowWidth - rowWidth) / 2) - baseLeft;
2276
2277                var startX = getAttrNumber('start-x', 0);
2278                var endX = getAttrNumber('end-x', 0);
2279
2280                // Determine intended direction from authored values (or previous computed values).
2281                // Used only when the row is narrower than the viewport.
2282                var authoredStart = startX;
2283                var authoredEnd = endX;
2284                var directionIsIncreasing = authoredStart <= authoredEnd;
2285
2286                // If the content doesn't span the viewport, keep it centered and static.
2287                if (rowWidth <= windowWidth) {
2288                    // We *cannot* translate freely without revealing empty space.
2289                    // Instead, do a subtle parallax within a small range around center.
2290                    var availableGap = (windowWidth - rowWidth) / 2;
2291                    // Range is a fraction of the available gap, capped to a reasonable max.
2292                    // (If there is barely any gap, this becomes ~0 and effectively locks.)
2293                    var parallaxRange = Math.min(availableGap * 0.35, 60);
2294                    if (parallaxRange < 1) {
2295                        parallaxRange = 0;
2296                    }
2297
2298                    var narrowStart = centerX - parallaxRange;
2299                    var narrowEnd = centerX + parallaxRange;
2300
2301                    startX = directionIsIncreasing ? narrowStart : narrowEnd;
2302                    endX = directionIsIncreasing ? narrowEnd : narrowStart;
2303                } else {
2304                    // Clamp to ensure we never translate beyond the edges.
2305                    startX = clamp(startX, minX, maxX);
2306                    endX = clamp(endX, minX, maxX);
2307                }
2308
2309                // Special-case: large images row centers the 2nd card at scroll midpoint,
2310                // then continues moving as user scrolls out.
2311                if ($row.hasClass('year-end-2026--carousel-images__large-images')) {
2312                    var $parent = $row.parent();
2313                    var parentWidth = $parent.width() || windowWidth;
2314                    var parentRect = $parent.length ? $parent[0].getBoundingClientRect() : { left: 0 };
2315                    var targetChild = $row.children().eq(1);
2316                    if (targetChild.length > 0) {
2317                        var targetOffset = targetChild.position().left + (targetChild.outerWidth(true) / 2);
2318                        // This offset centers the 2nd image in its parent viewport.
2319                        // targetOffset is relative to the row's left edge; baseLeft/parentRect.left anchor it in viewport coords.
2320                        var desiredCenter = (parentRect.left || 0) + (parentWidth / 2);
2321                        var targetCenterAtX0 = baseLeft + targetOffset;
2322                        var centeringOffset = desiredCenter - targetCenterAtX0;
2323
2324                        if (rowWidth <= windowWidth) {
2325                            // Narrow: subtle parallax around center.
2326                            var availableGapNarrow = (windowWidth - rowWidth) / 2;
2327                            var parallaxRangeNarrow = Math.min(availableGapNarrow * 0.35, 60);
2328                            if (parallaxRangeNarrow < 1) {
2329                                parallaxRangeNarrow = 0;
2330                            }
2331
2332                            // Preserve authored direction even in the subtle parallax case.
2333                            var narrowA = centerX - parallaxRangeNarrow;
2334                            var narrowB = centerX + parallaxRangeNarrow;
2335                            startX = directionIsIncreasing ? narrowA : narrowB;
2336                            endX = directionIsIncreasing ? narrowB : narrowA;
2337                        } else {
2338                            // Wide: start before centered, pass through centered at midpoint, end past centered.
2339                            // Increase travel so it doesn't feel like it "sticks" at center.
2340                            var centered = clamp(centeringOffset, minX, maxX);
2341
2342                            // Desired travel before/after center. Bigger = faster feel.
2343                            // Cap to avoid overly long slides on ultra-wide screens.
2344                            var desiredTravel = Math.min(rowWidth * 0.28, 420);
2345
2346                            // Never exceed bounds: centered +/- travel must stay within [minX, maxX].
2347                            var maxTravel = Math.max(0, Math.min(maxX - centered, centered - minX));
2348                            var travel = Math.min(desiredTravel, maxTravel);
2349
2350                            var wideA = centered - travel;
2351                            var wideB = centered + travel;
2352                            startX = directionIsIncreasing ? wideA : wideB;
2353                            endX = directionIsIncreasing ? wideB : wideA;
2354                        }
2355                    }
2356
2357                    // Respect bounds after applying the centering offset.
2358                    if (rowWidth <= windowWidth) {
2359                        // Keep the already-computed narrow parallax values.
2360                    } else {
2361                        startX = clamp(startX, minX, maxX);
2362                        endX = clamp(endX, minX, maxX);
2363                    }
2364
2365                    if ($row.data('reversed') === true || $row.data('reversed') === "true") {
2366                        var tmp = startX;
2367                        startX = endX;
2368                        endX = tmp;
2369                    }
2370                }
2371
2372                // Keep attributes updated for debugging/inspection (avoid jQuery .data cache issues).
2373                $row.attr('data-start-x', startX);
2374                $row.attr('data-end-x', endX);
2375
2376                return { startX: startX, endX: endX };
2377            };
2378    
2379            var tl = gsap.timeline({
2380                scrollTrigger: {
2381                    trigger: this,
2382                    start: 'top bottom',
2383                    end: function () {
2384                        return self.getWindowHeight() + "px";
2385                    },
2386                    scrub: true,
2387                    invalidateOnRefresh: true, // invalidate on resize
2388                },
2389            });
2390    
2391            tl.fromTo(this, {
2392                x: function () {
2393                    return calcPositions().startX;
2394                },
2395            }, {
2396                x: function () {
2397                    return calcPositions().endX;
2398                },
2399            });
2400        });
2401    }
2402
2403    YEAR2026.prototype.initVideosModal = function (selector) {
2404        var self = this;
2405
2406        $('.year-review-2022--back').click(function () {
2407            $('.modal-full-viewport .close').click();
2408        });
2409
2410        $(selector).click(function () {
2411            var $modal = $('.modal-full-viewport__video').first();
2412            $modal.addClass('visible');
2413
2414            for (var player in videojs.players) {
2415                if (videojs.players.hasOwnProperty(player) && videojs.players[player]) {
2416                    // try {
2417                    //     videojs.players[player].pause();
2418                    // } catch (error) {
2419                    // }
2420                    if ($(videojs.players[player].el_).closest('.modal-full-viewport').length > 0) {
2421                        videojs.players[player].dispose();
2422                    }
2423                }
2424            }
2425
2426            var videoId = $(this).data('videoid');
2427            self.currentVideoId = videoId;
2428
2429            TweenMax.from($modal, 0.75, { scale: 0.9, autoAlpha: 0, ease: Power4.easeOut, clearProps: 'all' });
2430
2431            // self.initModalCarousel();
2432
2433            if ($(this).attr('id')) {
2434                window.history.replaceState({}, '', '#' + $(this).attr('id'));
2435                trackEvent("Video", "Play", $(this).attr('id'));
2436            }
2437
2438            var $el = $(this);
2439
2440            var showyoutube = false;
2441            var scntvlink = $el.data('scntv');
2442
2443            if ($el.data('youtube')) {
2444                showyoutube = true;
2445            }
2446
2447            $('.modal-youtube-embed--inited').remove();
2448
2449            if (scntvlink) {
2450                $modal.find('.modal-scntv-video').clone().removeClass("d-none").prependTo($modal.find('.modal-video-wrapper')).addClass('modal-scntv-video--inited');
2451                var src = $('.modal-scntv-video--inited iframe').attr('data-src').replace(/YOUTUBE_ID/g, $el.data('youtube'));
2452                $('.modal-scntv-video--inited').removeClass('d-none');
2453                var src = $('.modal-scntv-video--inited iframe').data('src').replace("URL", scntvlink);
2454                $('.modal-scntv-video--inited iframe').attr('src', src);
2455                return;
2456            } else if (showyoutube) {
2457                $modal.find('.modal-youtube-embed').clone().prependTo($modal.find('.modal-video-wrapper')).addClass('modal-youtube-embed--inited');
2458                var src = $('.modal-youtube-embed--inited iframe').attr('data-src').replace(/YOUTUBE_ID/g, $el.data('youtube'));
2459                $('.modal-youtube-embed--inited').removeClass('d-none');
2460                $('.modal-youtube-embed--inited iframe').attr('src', src);
2461                return;
2462            }
2463            // https://www.youtube.com/watch?v=FiW5kejelns
2464
2465            $('.year-review-2022--primary-menu-bg, .banner-wrapper, .external-links').hide();
2466            // $('.year-review-2021-nav').removeClass('year-review-2022-nav__visible');
2467
2468
2469            var options = jQuery.extend({}, videoOptions);
2470            options.controls = true;
2471            options.poster = $(this).data('videoposter');
2472            options.sources = $(this).data('videosrc');
2473
2474            self.initModalVideo($modal, options, videoId);
2475
2476            // var url = new URL(window.location.href);
2477            // url.search = "?shareanchor=" + $(this).attr('id');
2478            // url.hash = "";
2479            // var link = url.href;
2480            // var title = $('[href="#'+$(this).attr('id')+'"]').first().text().trim();
2481            // self.updateSharingButtons(title, link)
2482        });
2483
2484
2485        window.onpopstate = function (event) {
2486            if ($('.modal-full-viewport').hasClass('visible')) {
2487                history.replaceState({}, '');
2488                event.preventDefault();
2489                event.stopPropagation();
2490                $('.modal-full-viewport .close').click();
2491            }
2492        }
2493
2494        $('.modal-full-viewport .close, .modal-full-viewport .year-review-2021--back__underlined').click(function () {
2495            $('.show-codes-button').removeClass('d-none');
2496            TweenMax.to($('.modal-full-viewport'), 0.75, {
2497                scale: 0.9, autoAlpha: 0, clearProps: 'all', ease: Power4.easeOut, onComplete: function () {
2498                    $('.modal-full-viewport').removeClass('visible');
2499                }
2500            });
2501            $('.modal-video-wrapper-up-next--button__cancel').click();
2502            window.history.replaceState({}, '', '');
2503            if (window.videojs) {
2504                for (var player in videojs.players) {
2505                    if (videojs.players[player] && $(videojs.players[player].el_).closest('.modal-full-viewport').length > 0) {
2506                        if (videojs.players.hasOwnProperty(player) && videojs.players[player]) {
2507                            try {
2508                                videojs.players[player].pause();
2509                            } catch (error) {
2510                            }
2511                            videojs.players[player].dispose();
2512                        }
2513                    }
2514                }
2515            }
2516            _.each(self.videoPlayers, function (player) {
2517                if (player && videojs.players[player] && $(videojs.players[player].el_).closest('.modal-full-viewport').length > 0) {
2518                    player.dispose();
2519                }
2520            });
2521            self.videoPlayers = [];
2522            $('.modal-youtube-embed--inited').remove();
2523            $('.modal-scntv-video--inited').remove();
2524            VideoPlayerHotkeys.player = null;
2525            $('.year-review-2022--primary-menu-bg, .banner-wrapper, .external-links').show();
2526            // $('.year-review-2021-nav').addClass('year-review-2022-nav__visible');
2527
2528            _.each($('.year-end-2022--playbutton-area'), function (el, index) {
2529                var $el = $(el);
2530                if (isFullyVisible($el) && $el.find('video').muted) {
2531                    $el.find('video')[0].play();
2532                }
2533            });
2534
2535            history.replaceState(null, null, "#");
2536        });
2537    }
2538
2539    YEAR2026.prototype.initModalVideo = function ($modal, options, videoId) {
2540        $modal.find('.modal-player__model').clone().prependTo($modal.find('.modal-video-wrapper')).removeClass('d-none').removeClass('modal-player__model');
2541        var videoEl = $('[data-videosrc][data-videoid="' + videoId + '"]');
2542        var self = this;
2543        videojs($modal.find('.video-js')[0], options).ready(function () {
2544            if (VideoPlayerHotkeys) {
2545                VideoPlayerHotkeys.player = this;
2546            }
2547            EventTracker.trackVideo(this);
2548            this.on('play', function () {
2549                $('.modal-video-wrapper-up-next-wrapper').addClass('d-none');
2550            });
2551            this.play();
2552            self.videoPlayers = self.videoPlayers || [];
2553            self.videoPlayers.push(this);
2554
2555            var player = this;
2556
2557
2558            this.on("ended", function () {
2559                $('.modal-full-viewport .close').click();
2560                // player.poster('');
2561                // var currentVideo = $('.modal-full-viewport .swiper-slide__modal-video[data-id="' + self.currentVideoId + '"]');
2562                // var $next = currentVideo.next();
2563                // if ($next.length == 0) {
2564                //     $next = $('.swiper-slide__modal-video').first();
2565                // }
2566                // $('.modal-video-wrapper-up-next-wrapper').removeClass('d-none');
2567                // $('.modal-video-wrapper-up-next-wrapper .modal-video-wrapper-up-next--title').html($next.find('.swiper-slide__modal-video--title').html());
2568                // $('.modal-video-wrapper-up-next-wrapper img').attr('srcset', $next.find('source').attr('srcset'));
2569                // $('.modal-video-wrapper-up-next-wrapper img').attr('data-srcset', $next.find('source').attr('data-srcset'));
2570                // $('.modal-video-wrapper-up-next-wrapper img').addClass('lazyload');
2571                // self.setUpNext($next);
2572            });
2573        });
2574    }
2575
2576    YEAR2026.prototype.initIntro = function () {
2577        var tl = gsap.timeline({
2578            // id: "initIntro",
2579            onComplete: () => {
2580            },
2581        });
2582        if (!globalGetParams.screencap) {
2583            this.initIntroTitles();
2584        }
2585
2586        tl.from('.year-in-review-2026--background-static', {
2587            autoAlpha: 0,
2588            duration: 1,
2589            ease: Power4.easeOut,
2590        });
2591
2592        tl.from('.year-in-review-2026--hero-text__text-wrapper, .year-in-review-2026--hero-text__span', {
2593            autoAlpha: 0,
2594            duration: 0.75,
2595            scale: 3,
2596            ease: Power4.easeOut,
2597            stagger: 0.1,
2598        });
2599
2600        tl.from('.year-in-review-2026--hero-subtext span', {
2601            autoAlpha: 0,
2602            duration: 0.75,
2603            y: 20,
2604            ease: Power4.easeOut,
2605            stagger: 0.1,
2606        }, "-=0.5");
2607
2608        tl.from('.year-2025--bounce', {
2609            autoAlpha: 0,
2610            duration: 0.75,
2611            y: 50,
2612            ease: Power4.easeOut,
2613        }, "-=0.5");
2614
2615
2616        gsap.timeline({
2617            scrollTrigger: {
2618                trigger: '.year-in-review-2026--hero-header-top-spacer',
2619                start: 'top top',
2620                pin: '.year-in-review-2026--hero-text',
2621                end: "+=100%",
2622                pinSpacing: false,
2623                scrub: true,
2624                invalidateOnRefresh: true,
2625            }
2626        }).to('.year-in-review-2026--hero-text', {
2627            y: function () {
2628                return -window.innerHeight / 2;
2629            },
2630            ease: "none"
2631        });
2632    }
2633
2634    YEAR2026.prototype.initIntroTitles = function () {
2635        $('.year-in-review-2026--intro-titles > div').each(function (index, el) {
2636
2637            gsap.from(el.querySelector('span'), {
2638                scrollTrigger: {
2639                    trigger: el,
2640                    start: 'top bottom-=100',
2641                    toggleActions: "play none resume reverse",
2642                },
2643
2644                autoAlpha: 0,
2645                duration: 0.75,
2646                scale: 5,
2647                y: 20,
2648                ease: Power4.easeOut,
2649            });
2650        });
2651    }
2652
2653    YEAR2026.prototype.initMainMenu = function () {
2654        var self = this;
2655        var anchors = [];
2656        var lastHashId = "";
2657
2658
2659        $('.ny-2024--menuitem, .ny-2024--primary-menu-logo').each(function (index) {
2660            var $link = $(this).find('a');
2661            if ($(this)[0].tagName == "A") {
2662                $link = $(this);
2663            }
2664            var id = $link.attr('href').replace('#', '');
2665            var $el = $('#' + id);
2666            if ($el.length == 0) {
2667                return;
2668            }
2669            var triggerElement = $el[0];
2670            anchors.push(id);
2671
2672            self.activeShareImageIndex = 0;
2673
2674            ScrollTrigger.create({
2675                trigger: triggerElement,
2676                start: "top center",
2677                end: "bottom center",
2678                onEnter: function () {
2679                    var id = $el.attr('id');
2680                    var $parent = $('.ny-2024--menuitem [href="#' + id + '"]').parent();
2681                    if ($parent.hasClass('ny-2024--menuitem')) {
2682                        $('.ny-2024--menuitem__active').removeClass('ny-2024--menuitem__active');
2683                        $parent.addClass('ny-2024--menuitem__active');
2684                        $('.year-review-2021--mobile-menuitem-link[href="#' + id + '"]').addClass('ny-2024--menuitem__active');
2685                    }
2686                    // Clear the hash in the URL when the active section changes.
2687                    self.clearUrlHashIfPageAnchor();
2688                },
2689                onLeaveBack: function () {
2690                    var id = $el.attr('id');
2691                    var $parent = $('.ny-2024--menuitem [href="#' + id + '"]').parent()
2692                    var $prev = $parent.prev();
2693                    $('.ny-2024--menuitem__active').removeClass('ny-2024--menuitem__active');
2694                    if ($prev.length > 0 && $prev.hasClass('ny-2024--menuitem')) {
2695                        $prev.addClass('ny-2024--menuitem__active');
2696                        $('.year-review-2021--mobile-menuitem-link[href="#' + id + '"]').parent().prev().find('a').addClass('ny-2024--menuitem__active');
2697                    }
2698                    // Clear the hash in the URL when the active section changes.
2699                    self.clearUrlHashIfPageAnchor();
2700                }
2701            });
2702        });
2703
2704        // When a user clicks a menu/submenu anchor, let the page scroll/jump,
2705        // then immediately remove the hash so refresh won't re-jump.
2706        $(document).on('click', '.ny-2024--menuitem a[href^="#"], .year-review-2021--mobile-menuitem-link[href^="#"]', function () {
2707            setTimeout(function () {
2708                self.clearUrlHashIfPageAnchor();
2709            }, 0);
2710        });
2711        // if(self.videoSelected()) {
2712        //     self.updateUrlHash(window.location.hash.replace("#", ""));
2713        // } else if(lastHashId) {
2714        //     self.updateUrlHash(lastHashId);
2715        // }
2716
2717        $('.year-review-2021--mobile-menuitem').click(function () {
2718            $('.year-review-2021-mobile-menu').removeClass('year-review-2021-mobile-menu__visible');
2719        });
2720
2721        $('.ny-2024--menuitem-link__mobile-overlay').click(function (event) {
2722            if ($(window).width() > 992) {
2723                return;
2724            }
2725
2726            event.preventDefault();
2727            event.stopPropagation();
2728
2729            trackEvent("Menu", "Opened");
2730
2731            $('.year-review-2021-mobile-menu').toggleClass('year-review-2021-mobile-menu__visible');
2732        });
2733
2734        $('.year-review-2021-mobile-menu--close').click(function () {
2735            $('.year-review-2021-mobile-menu').removeClass('year-review-2021-mobile-menu__visible');
2736        });
2737    }
2738
2739
2740    YEAR2026.prototype.initScrollPosition = function () {
2741        var self = this;
2742        self.calculateSectionSize();
2743        var halfScreen = $(window).height() * 0.5;
2744        $(window).resize(function () {
2745            self.calculateSectionSize();
2746            halfScreen = $(window).height() * 0.5;
2747        });
2748
2749        $(window).scroll(function () {
2750            var scrollTop = window.scrollY + halfScreen;
2751            if(window.scrollY == 0) {
2752                scrollTop = 0;
2753            }
2754            var activeSection = self.sections[self.sections.length - 1];
2755            var foundSection = false;
2756            var acttiveSectionIndex = 0;
2757            for (var index = 0; index < self.sections.length; index++) {
2758                var section = self.sections[index];
2759                if (section.start <= scrollTop && scrollTop < section.end) {
2760                    foundSection = true;
2761                    activeSection = self.sections[index];
2762                    acttiveSectionIndex = index;
2763                    break;
2764                }
2765            }
2766
2767            if (!foundSection) {
2768                TweenMax.set('.ny-2024--primary-menu__progress', { scaleX: 0 });
2769                return;
2770            }
2771            
2772            // if window.pixelRatio is fractional, we need to scale the progress bar
2773            var windowScaleFactor = 1;
2774            if (window.devicePixelRatio != Math.round(window.devicePixelRatio)) {
2775                windowScaleFactor = window.devicePixelRatio / Math.floor(window.devicePixelRatio);
2776            }
2777
2778            var activeSectionProgress = (scrollTop - activeSection.start) / (activeSection.end - activeSection.start);
2779
2780            var totalProgress;
2781            var totalProgressW = 0;
2782            if (self.isMobile()) {
2783                totalProgress = activeSectionProgress;
2784            } else {
2785                for (var index = 0; index < self.sections.length; index++) {
2786                    var section = self.sections[index];
2787                    if (section.start < activeSection.start) {
2788                        totalProgressW += section.width;
2789                    }
2790                }
2791
2792                totalProgressW += activeSectionProgress * activeSection.width * windowScaleFactor;
2793                totalProgress = totalProgressW / ($('.ny-2024--primary-menu').width() * windowScaleFactor);
2794            }
2795
2796            if(totalProgress > 1) {
2797                totalProgress = 1;
2798            }
2799
2800            TweenMax.set('.ny-2024--primary-menu__progress', { scaleX: totalProgress });
2801
2802            // Clear any lingering in-page anchor hash when the user scrolls away from it.
2803            self.clearUrlHashIfAnchorNotNearViewportTop();
2804        });
2805    }
2806
2807    YEAR2026.prototype.calculateSectionSize = function () {
2808        var sections = [];
2809        var start = $('.page-menu-start').offset().top;
2810        var isMobile = this.isMobile();
2811        // if window.pixelRatio is fractional, we need to scale the progress bar
2812        var windowScaleFactor = 1;
2813        if (window.devicePixelRatio != Math.round(window.devicePixelRatio)) {
2814            windowScaleFactor = window.devicePixelRatio / Math.floor(window.devicePixelRatio);
2815        }
2816
2817        $('.ny-2024--menuitem').each(function (index) {
2818            var $el = $(this);
2819            // var margin = parseInt($el.css("paddingRight").replace('px', '')) * 2;
2820            // if (isNaN(margin)) {
2821            //     margin = 0;
2822            // }
2823            // calcualte outerWidth
2824            var width = this.offsetWidth * windowScaleFactor;
2825            // length = $($el.find('a').attr('href')).offset().top;
2826            var end = 0;
2827            if ($el.next().length > 0 && $el.next().find('a').length > 0) {
2828                end = $($el.next().find('a').attr('href')).offset().top;
2829            } else {
2830                end = $('.end-of-article').offset().top;
2831            }
2832            if (isMobile) {
2833                width = $(window).width();
2834            }
2835            sections.push({
2836                start: start,
2837                end: end,
2838                width: width,
2839            });
2840            start = end;
2841        });
2842        this.sections = sections;
2843    }
2844
2845
2846
2847
2848    YEAR2026.prototype.fadeInBubble = function () {
2849        setTimeout(function () {
2850            $('.enlightenment-launcher-container').addClass('enlightenment-launcher-container__opened');
2851            TweenLite.from($('.enlightenment-launcher-container'), 0.75, { width: 24, ease: Power4.easeInOut });
2852            TweenLite.to($('.enlightenment-launcher-container'), 0.75, { opacity: 1, clearProps: 'width', ease: Power4.easeInOut });
2853
2854            setTimeout(function () {
2855                $('.enlightenment-launcher-container').removeClass('enlightenment-launcher-container__opened');
2856            }, 10000);
2857        }, 10000);
2858
2859        var self = this;
2860
2861        $('.oca-form-button, .oca-start--button, .who-we-are-cta-button__oca').click(function (event) {
2862            event.preventDefault();
2863            event.stopPropagation();
2864            
2865            var $modal = $('.modal-full-viewport__who-we-are-oca').first();
2866            $modal.addClass('visible');
2867            history.pushState({}, '', '#modal-open');
2868            self.disableBodyScroll();
2869            setTimeout(function () {
2870                self.checkModalScroll();
2871            }, 500);
2872
2873            TweenMax.from($modal, 0.25, { autoAlpha: 0, ease: Power4.easeOut, clearProps: 'all' });
2874            TweenMax.from($modal.find('.who-we-are-oca__content-wrapper'), 0.75, { y: "100%", ease: Power4.easeOut });
2875
2876        });
2877    }
2878
2879    YEAR2026.prototype.initLazyload = function () {
2880        //add simple support for background images:
2881        document.addEventListener('lazybeforeunveil', function (e) {
2882            var bg = e.target.getAttribute('data-bg');
2883            if (bg) {
2884                e.target.style.backgroundImage = 'url(' + bg + ')';
2885            }
2886        });
2887        setTimeout(() => {
2888            $('[data-bg]').each(function () {
2889                if ($(this).hasClass('lazyloaded')) {
2890                    var bg = this.getAttribute('data-bg');
2891                    if (bg) {
2892                        this.style.backgroundImage = 'url(' + bg + ')';
2893                    }
2894                }
2895            });
2896        }, 500);
2897
2898        $('slide [data-src]').each(function () {
2899            var $el = $(this);
2900            if (!$el.hasClass('lazyload')) {
2901                $el.attr('data-srcset', $el.attr('data-src') + " 1x, " + $el.attr('data-src2x') + " 2x");
2902                $el.addClass('lazyload');
2903            }
2904        });
2905
2906
2907        var self = this;
2908        $(window).resize(function () {
2909            self.updateCache();
2910        });
2911
2912        $(document).ready(function () {
2913            self.updateCache();
2914        });
2915    }
2916
2917    YEAR2026.prototype.updateCache = function () {
2918        window.YEAR2026.cachedPageHeight = $(document).height();
2919        window.YEAR2026.cachedWindowHeight = $(window).height();
2920        window.YEAR2026.cachedOuterWindowWidth = $(window).outerWidth();
2921        window.YEAR2026.cachedOuterWindowHeight = $(window).outerHeight();
2922    }
2923
2924    YEAR2026.prototype.initNav = function () {
2925        var self = this;
2926
2927        // new ScrollMagic.Scene({ triggerElement: ".year-review-2022--top-wrapper", triggerHook: "onLeave", reverse: true })
2928        //     .setClassToggle('.year-review-2021-nav', 'year-review-2022-nav__visible')
2929        //     .addTo(self.controller);
2930
2931        $('.year-review-2021-nav__up').click(function () {
2932            if (self.scrollToPrevInset()) {
2933                return;
2934            }
2935
2936            if (typeof self.activeStepIndex == "undefined") {
2937                var index = 0;
2938            } else {
2939                var index = self.activeStepIndex - 1;
2940            }
2941            if (self.scrollToIndex > -1) {
2942                index = self.scrollToIndex - 1;
2943            }
2944
2945            var duration = $(this).data('duration');
2946
2947            var $lastInset = self.getLastInsetFor(index);
2948            if ($lastInset) {
2949                self.scrollToElement($lastInset, duration, { forceEase: Power4.easeOut });
2950            } else {
2951                self.lastScrollAnimation = gsap.to(window, {
2952                    duration: 2,
2953                    scrollTo: {
2954                        y: 0,
2955                    },
2956                    ease: Power4.easeOut,
2957                });
2958            }
2959
2960        });
2961
2962        $('.year-review-2021-nav__down, .year-2025--bounce').click(function () {
2963            if (self.scrollToNextInset()) {
2964                return;
2965            }
2966            if (typeof self.activeStepIndex == "undefined") {
2967                var index = 0;
2968            } else {
2969                var index = self.activeStepIndex + 1;
2970            }
2971            if (self.scrollToIndex > -1) {
2972                index = self.scrollToIndex + 1;
2973            }
2974        });
2975    }
2976
2977    YEAR2026.prototype.initControls = function () {
2978        var self = this;
2979
2980        $('.navigation-step').each(function (index) {
2981            var $el = $(this);
2982            $el.attr('data-index', index);
2983        });
2984
2985        $(document).keydown(function (e) {
2986            var arrow = { left: 37, up: 38, right: 39, down: 40, pageUp: 33, pageDown: 34 };
2987
2988            switch (e.which) {
2989                //   case arrow.left:
2990                //     for (var index = 0; index < self.swipers.length; index++) {
2991                //         var swiper = self.swipers[index];
2992                //         if(isInViewportVisible(swiper.$el)) {
2993                //             var index = swiper.$el.attr('data-swiper-index');
2994                //             self.swipers[parseInt(index)].slidePrev();
2995                //         }
2996                //     }
2997                //     break;
2998                case arrow.up:
2999                case arrow.pageUp:
3000                    if (self.scrollToPrevInset()) {
3001                        e.preventDefault();
3002                        e.stopPropagation();
3003                        $('.year-review-2021-nav__up').addClass('year-review-2021-nav__up-pressed');
3004                        setTimeout(function () {
3005                            $('.year-review-2021-nav__up').removeClass('year-review-2021-nav__up-pressed');
3006                        }, 1100);
3007                        return;
3008                    }
3009                    break;
3010                //   case arrow.right:
3011                //     for (var index = 0; index < self.swipers.length; index++) {
3012                //         var swiper = self.swipers[index];
3013                //         if(isInViewportVisible(swiper.$el)) {
3014                //             var index = swiper.$el.attr('data-swiper-index');
3015                //             self.swipers[parseInt(index)].slideNext();
3016                //         }
3017                //     }
3018                //     break;
3019                case arrow.down:
3020                case arrow.pageDown:
3021                    if (self.scrollToNextInset()) {
3022                        e.preventDefault();
3023                        e.stopPropagation();
3024                        $('.year-review-2021-nav__down').addClass('year-review-2021-nav__down__pressed');
3025                        setTimeout(function () {
3026                            $('.year-review-2021-nav__down').removeClass('year-review-2021-nav__down__pressed');
3027                        }, 1100);
3028                        return;
3029                    }
3030                    break;
3031                // case 32:
3032                //     for (var index in videojs.players) {
3033                //         if (Object.hasOwnProperty.call(videojs.players, index)) {
3034                //             var video = videojs.players[index];
3035                //             var $el = $(video.el());
3036                //             if(isInViewportVisible($el) && self.qualifiedAutoplay($el)) {
3037                //                 var $wrapper = $el.closest('.show-on-the-road--playbutton-area');
3038                //                 var videoPlaying = $wrapper.hasClass('show-on-the-road-videoautoplay__inline');
3039                //                 if(!videoPlaying) {
3040                //                     $wrapper.click();
3041                //                 } else if(video.paused()) {
3042                //                     video.play();
3043                //                 } else {
3044                //                     video.pause();
3045                //                 }
3046
3047                //                 e.preventDefault();
3048                //                 e.stopPropagation();
3049                //             }
3050                //         }
3051                //     }
3052                //     break;
3053            }
3054        });
3055    }
3056
3057
3058
3059
3060    YEAR2026.prototype.getLastInsetFor = function (index) {
3061        var $container = $('.navigation-step[data-id=' + index + ']');
3062        if ($container.length == 0) {
3063            return false;
3064        }
3065        return $container.last();
3066    }
3067
3068    YEAR2026.prototype.getPrevInset = function () {
3069        var self = this;
3070        var $insets = $('.navigation-step');
3071        if ($insets.length == 0) {
3072            self._prevInsetActiveIndex = null;
3073            return false;
3074        }
3075
3076        var activeInsetIndex = -1;
3077        var scrollTop = $(window).scrollTop();
3078        var windowHeight = $(window).height();
3079        var $activeInset;
3080
3081        $insets.each(function (index, inset) {
3082            var $inset = $(inset);
3083            var alignBottom = $inset.data('align-bottom');
3084            var offset = 200;
3085            if ($inset.data("offset")) {
3086                offset = parseInt($inset.data("offset"));
3087            }
3088
3089            if($inset.closest('.who-we-are-iaas-grid-active').length > 0) {
3090                return;
3091            }
3092
3093            if (self.getScrollPosition($inset) + 100 < scrollTop) {
3094                $activeInset = $inset;
3095                activeInsetIndex = index;
3096            }
3097        });
3098
3099        var $target;
3100
3101        // did not scroll past first inset, go to it
3102        if (activeInsetIndex == -1) {
3103            // $target = $($insets[0]);
3104            self._prevInsetActiveIndex = null;
3105            return false;
3106        } else if (activeInsetIndex == 0) {
3107            $target = $('.navigation-step').first();
3108        } else if (activeInsetIndex - 1 < 0) {
3109            self._prevInsetActiveIndex = null;
3110            return false;
3111        } else {
3112            $target = $($insets[activeInsetIndex]);
3113        }
3114
3115        // Expose the computed active index so scrollToPrevInset can derive the
3116        // "from" (current) inset and use its duration.
3117        self._prevInsetActiveIndex = activeInsetIndex;
3118
3119        return $target;
3120    }
3121
3122    YEAR2026.prototype.scrollToPrevInset = function () {
3123        var $target = this.getPrevInset();
3124
3125        if (!$target || !$target.length) {
3126            return false;
3127        }
3128
3129        // For reverse navigation we want the same timing as the forward move
3130        // that brought us here. Durations are authored per *destination* inset,
3131        // so when going up we read duration from the inset we're leaving.
3132        var duration;
3133        var activeIndex = (typeof this._prevInsetActiveIndex === 'number') ? this._prevInsetActiveIndex : null;
3134        var $from;
3135        if (activeIndex !== null) {
3136            var $insets = $('.navigation-step');
3137            if (activeIndex + 1 < $insets.length) {
3138                $from = $($insets[activeIndex + 1]);
3139            }
3140        }
3141
3142        duration = ($from && $from.length) ? $from.data('duration') : $target.data('duration');
3143
3144        if (typeof duration == "undefined") {
3145            duration = 1000;
3146        }
3147        
3148        this._lastInsetScrollTime = 0;
3149
3150        // Always use ease-out when navigating backwards (up arrow / back button).
3151        this.scrollToElement($target, duration, { forceEase: Power4.easeOut });
3152
3153        this.clearUrlHashIfPageAnchor();
3154
3155        return true;
3156    }
3157
3158    YEAR2026.prototype.getScrollPosition = function (el) {
3159        var self = this;
3160        if (self.lastScrollAnimation) {
3161            self.lastScrollAnimation.pause();
3162        }
3163
3164        var id = el.attr('id');
3165        var alignBottom = el.data('align-bottom');
3166        var ease = Power4.easeInOut;
3167
3168        var topOffset = el.offset().top;
3169
3170        if (el.data('align-center')) {
3171            topOffset = topOffset - $(window).height() / 2 + el.height() / 2;
3172        }
3173
3174        var scrollingDown = topOffset - window.scrollY > 0;
3175
3176        var topSpace = 66;
3177        var bottomAligner = 20;
3178        if (typeof el.data("top") != "undefined") {
3179            topSpace = parseInt(el.data("top"));
3180            bottomAligner = parseInt(el.data("top"));
3181        }
3182
3183        if (typeof el.data("ease") != "undefined") {
3184            ease = Power4.easeOut;
3185        }
3186
3187        if (typeof el.data("easein") != "undefined") {
3188            ease = Power4.easeInOut;
3189        }
3190
3191
3192        if (alignBottom) {
3193            return topOffset + bottomAligner - $(window).height();
3194        }
3195        return topOffset - topSpace;
3196    }
3197
3198    YEAR2026.prototype.scrollToNextInset = function () {
3199
3200        var $target = this.getNextInset();
3201
3202        this._lastInsetScrollTime = new Date().getTime();
3203
3204        var duration = $target.data('duration');
3205        this.scrollToElement($target, duration);
3206
3207        this.clearUrlHashIfPageAnchor();
3208
3209        return true;
3210    }
3211
3212    YEAR2026.prototype.getNextInset = function () {
3213        var self = this;
3214        var $insets = $('.navigation-step');
3215        if ($insets.length == 0) {
3216            return false;
3217        }
3218        var $activeInset;
3219        var activeInsetIndex = 0;
3220        var scrollTop = $(window).scrollTop();
3221
3222        var windowHeight = $(window).height();
3223
3224
3225        $insets.each(function (index, inset) {
3226            if ($activeInset) {
3227                return;
3228            }
3229            var $inset = $(inset);
3230            var offset = 100;
3231
3232            if($inset.closest('.who-we-are-iaas-grid-active').length > 0) {
3233                return;
3234            }
3235
3236            var alignBottom = $inset.data('align-bottom');
3237            if (typeof $inset.data('offset') != "undefined") {
3238                offset = parseInt($inset.data('offset'));
3239            }
3240
3241            if (self.getScrollPosition($inset) - offset > scrollTop) {
3242                $activeInset = $inset;
3243                activeInsetIndex = index;
3244            }
3245        });
3246
3247        var $target;
3248
3249        // did not scroll past first inset, go to it
3250        if (!$activeInset) {
3251            if (window.scrollY > 2000) {
3252                return;
3253            }
3254            $target = $($insets[0]);
3255        } else {
3256            $target = $activeInset;
3257        }
3258
3259        return $target;
3260    }
3261
3262
3263    YEAR2026.prototype.getScrollTopOffset = function (el) {
3264        var alignBottom = el.data('align-bottom');
3265
3266        var topSpace = 75;
3267        var bottomAligner = 20;
3268        if (typeof el.data("top") != "undefined") {
3269            topSpace = parseInt(el.data("top"));
3270            bottomAligner = 0;
3271        } else if (alignBottom) {
3272            topSpace = 20;
3273            bottomAligner = 0;
3274
3275            if (typeof el.data("top") != "undefined") {
3276                topSpace = parseInt(el.data("top"));
3277            }
3278        }
3279
3280
3281        var topOffset = el.offset().top;
3282        if (el.data('align-center')) {
3283            topOffset = topOffset - $(window).height() / 2 + el.height() / 2;
3284        } else if (alignBottom) {
3285            topOffset = topOffset + topSpace + bottomAligner - $(window).height();
3286        } else {
3287            topOffset = topOffset - topSpace;
3288        }
3289
3290        return topOffset;
3291    }
3292
3293    YEAR2026.prototype.scrollToElement = function (el, duration, options) {
3294        var self = this;
3295        if (self.lastScrollAnimation) {
3296            self.lastScrollAnimation.pause();
3297        }
3298
3299        var topOffset = self.getScrollTopOffset(el);
3300
3301        if(globalGetParams.stoppoints) {
3302            console.log("Scrolling to element: ", el);
3303        }
3304
3305        var dur = 2;
3306        var ease = Power4.easeInOut;
3307
3308        // Optional override for callers (e.g. back button) that must force an ease.
3309        if (options && options.forceEase) {
3310            ease = options.forceEase;
3311        } else {
3312            if (typeof el.data("ease") != "undefined") {
3313                ease = el.data("ease");
3314            }
3315
3316            if (typeof el.data("ease-curve") != "undefined") {
3317                ease = el.data("ease-curve");
3318            }
3319
3320            if (typeof el.data("ease-curve-custom") != "undefined") {
3321                ease = CustomEase.create("custom", el.data("ease-curve-custom"));
3322            }
3323        }
3324
3325        if (duration) {
3326            dur = duration / 1000;
3327        }
3328
3329
3330        var scrollForward = topOffset - window.scrollY > 0;
3331
3332        if (scrollForward) {
3333            var $prevInset;
3334            var currentIndex = parseInt(el.attr('data-index'));
3335            if (currentIndex > 0) {
3336                $prevInset = $('.navigation-step[data-index="' + (currentIndex - 1) + '"]');
3337            }
3338
3339
3340            if ($prevInset) {
3341                var currentPosition = self.getScrollTopOffset($prevInset);
3342                var totalScrollDistance = Math.abs(currentPosition - topOffset);
3343                var remainingScrollDistance = Math.abs(topOffset - window.scrollY);
3344
3345                if(typeof el.data('dynamic-duration') == 'undefined') {
3346                    dur = dur * remainingScrollDistance / totalScrollDistance;
3347                }
3348            }
3349        } else {
3350            var $nextInset;
3351            var currentIndex = parseInt(el.attr('data-index'));
3352            if (currentIndex < $('.navigation-step').length - 1) {
3353                $nextInset = $('.navigation-step[data-index="' + (currentIndex + 1) + '"]');
3354            }
3355
3356
3357            if ($nextInset) {
3358                var currentPosition = self.getScrollTopOffset($nextInset);
3359                var totalScrollDistance = Math.abs(currentPosition - topOffset);
3360                var remainingScrollDistance = Math.abs(topOffset - window.scrollY);
3361
3362                dur = dur * remainingScrollDistance / totalScrollDistance;
3363            }
3364        }
3365
3366        if (dur < 0.5) {
3367            dur = 0.5;
3368        }
3369
3370        self.lastScrollAnimation = gsap.to(window, {
3371            duration: dur,
3372            scrollTo: {
3373                y: topOffset,
3374            },
3375            ease: ease,
3376        });
3377
3378    }
3379
3380
3381    YEAR2026.prototype.getPageHeight = function () {
3382        // called from ScrollMagic
3383        var self = window.YEAR2026 || this;
3384        if (!self.cachedPageHeight) {
3385            self.cachedPageHeight = $(document).height();
3386        }
3387        return self.cachedPageHeight;
3388    }
3389
3390    YEAR2026.prototype.getWindowHeight = function () {
3391        // called from ScrollMagic
3392        var self = window.YEAR2026 || this;
3393        if (!self.cachedWindowHeight) {
3394            self.cachedWindowHeight = $(window).height();
3395        }
3396        return self.cachedWindowHeight;
3397    }
3398
3399    YEAR2026.prototype.getWindowOuterWidth = function () {
3400        // called from ScrollMagic
3401        var self = window.YEAR2026 || this;
3402        if (!self.cachedOuterWindowWidth) {
3403            self.cachedOuterWindowWidth = $(window).outerWidth();
3404        }
3405        return self.cachedOuterWindowWidth;
3406    }
3407
3408    YEAR2026.prototype.isMobile = function () {
3409        return $(window).width() < 767;
3410    }
3411
3412    YEAR2026.prototype.isMobileAgent = function () {
3413        return isMobile.any;
3414    }
3415
3416
3417    YEAR2026.prototype.isTablet = function () {
3418        return  $(window).width() >= 768 && $(window).width() < 992;
3419    }
3420
3421    YEAR2026.prototype.isAndroid = function () {
3422        var ua = navigator.userAgent.toLowerCase();
3423        var isAndroid = ua.indexOf("android") > -1;
3424        return isAndroid;
3425    }
3426
3427    YEAR2026.prototype.isIpod = function () {
3428        var ua = navigator.userAgent.toLowerCase();
3429        var isIpod = ua.indexOf("iPod") > -1;
3430        return isIpod;
3431    }
3432
3433    YEAR2026.prototype.initButtonFadein = function () {
3434        var self = this;
3435        $('.year-in-review-2026-button-wrapper').each(function () {
3436            var $button = $(this);
3437            var paused = $button.data('paused');
3438            var options = {};
3439
3440            if (!paused) {
3441                options.scrollTrigger = {
3442                    trigger: this,
3443                    start: 'bottom bottom+=5%',
3444                    end: "100%",
3445                    toggleActions: "play none resume reverse",
3446                };
3447            }
3448
3449            var timeline = gsap.timeline(options);
3450            if (paused) {
3451                timeline.pause();
3452            }
3453            if ($(this).attr('id')) {
3454                self.globalTimelines[$(this).attr('id')] = timeline;
3455            }
3456            timeline.fromTo(this.querySelector('.year-in-review-2026-button-inner'), {
3457                scale: 0,
3458            },{
3459                duration: 0.25,
3460                scale: 1,
3461                autoAlpha: 1,
3462            });
3463            timeline.fromTo(this.querySelector('.year-in-review-2026-button-outer-circle'), {
3464                scale: 0,
3465            },{
3466                duration: 0.5,
3467                scale: 1.5,
3468                autoAlpha: 1,
3469            }, "<");
3470            
3471            timeline.fromTo(this.querySelector('.year-in-review-2026-button-outer-circle'), {
3472                scale: 1.5,
3473            },{
3474                duration: 0.5,
3475                scale: 0,
3476            });
3477            timeline.from(this.querySelector('.year-in-review-2026-button-inner'), {
3478                duration: 0.75,
3479                width: 50,
3480                height: 50,
3481                clearProps: 'width, height',
3482            }, "-=0.3");
3483            timeline.from(this.querySelectorAll('.year-in-review-2026-button-inner-text'), {
3484                duration: 0.25,
3485                autoAlpha: 0, 
3486            });
3487        });
3488
3489        // Fade in LRH houses nav (same overall "pop" feel as buttons: scale + alpha).
3490        $('.year-2026-lrh-houses-nav').each(function () {
3491            var navEl = this;
3492
3493            // Prevent duplicate ScrollTriggers/timelines if Init runs more than once.
3494            if (navEl._fadeinTl) {
3495                try { navEl._fadeinTl.scrollTrigger && navEl._fadeinTl.scrollTrigger.kill(true); } catch (e) { }
3496                try { navEl._fadeinTl.kill(); } catch (e) { }
3497                navEl._fadeinTl = null;
3498            }
3499            var tl = gsap.timeline({
3500                scrollTrigger: {
3501                    trigger: navEl,
3502                    start: 'bottom bottom+=5%',
3503                    end: '100%',
3504                    toggleActions: 'play none resume reverse',
3505                },
3506                delay: 0.5
3507            });
3508
3509            tl.fromTo(navEl, {
3510                autoAlpha: 0,
3511                scale: 0,
3512                transformOrigin: '50% 50%'
3513            }, {
3514                duration: 0.25,
3515                autoAlpha: 1,
3516                scale: 1,
3517                clearProps: 'transformOrigin'
3518            });
3519
3520            var arrows = navEl.querySelectorAll('.year-2026-lrh-houses--arrow');
3521            if (arrows && arrows.length) {
3522                tl.fromTo(arrows, {
3523                    autoAlpha: 0,
3524                    scale: 0.85,
3525                    transformOrigin: '50% 50%'
3526                }, {
3527                    duration: 0.25,
3528                    autoAlpha: 1,
3529                    scale: 1,
3530                    stagger: 0.08,
3531                    clearProps: 'transformOrigin'
3532                }, '<');
3533            }
3534
3535            navEl._fadeinTl = tl;
3536        });
3537    }
3538
3539
3540
3541    YEAR2026.prototype.initIntroScroll = function () {
3542        gsap.fromTo('.year-in-review-2026-first-images', {
3543            autoAlpha: 0,
3544            y: 50,
3545        }, {
3546            autoAlpha: 1,
3547            y: 0,
3548            scrollTrigger: {
3549                // trigger: '.year-2025-top-inner',
3550                // start: "bottom center+=10%",
3551                trigger: '.year-in-review-2026-first-images',
3552                start: "center bottom",
3553            }
3554        })
3555    }
3556
3557    YEAR2026.prototype.initGrowingLines = function () {
3558        var self = this;
3559
3560        $('.year-in-review-2026-vertical-line').each(function () {
3561            gsap.from(this.querySelector('.year-in-review-2026-glow-line'), {
3562                height: 0,
3563                scrollTrigger: {
3564                    start: 'top bottom',
3565                    end: 'top center',
3566                    duration: 1,
3567                    // scrub: true,
3568                    trigger: this,
3569                    toggleClass: 'year-in-review-2026-glow-line__visible',
3570                    toggleActions: "play none resume reverse",
3571                }
3572            });
3573
3574        });
3575    }
3576
3577
3578    YEAR2026.prototype.playBackgroundVideo = function () {
3579        var self = this;
3580        // Select the video elements
3581        var startVideoElement = document.querySelector('.who-we-are--bg-video-start');
3582        var loopVideoElement = document.querySelector('.who-we-are--bg-video');
3583    
3584        // Initialize the start video player
3585        var startPlayer = videojs(startVideoElement, { 
3586            controls: false, 
3587            muted: true, 
3588            loop: false, 
3589            autoplay: true 
3590        });
3591    
3592        // Initialize the loop video player
3593        var loopPlayer = videojs(loopVideoElement, { 
3594            controls: false, 
3595            muted: true, 
3596            loop: true, 
3597            autoplay: false,
3598        });
3599    
3600        // Initially hide the loop video
3601        $(loopPlayer.el()).hide();
3602    
3603        // Set source for start video
3604        startPlayer.ready(function () {
3605            var src = $(this.el_).data('src');
3606            gsap.set(this.el_, { autoAlpha: 1 });
3607            if (src) {
3608                this.src(src);
3609                this.load();
3610                this.play();
3611                var inited = false;
3612                this.one('play', function () {
3613                    setTimeout(function(){
3614                        if(!inited) {
3615                            inited = true;
3616                            // self.initTopHeader();
3617                        }
3618                    }, 2000);
3619                });
3620                this.on('error', function () {
3621                    if(!inited) {
3622                        inited = true;
3623                        // self.initTopHeader();
3624                    }
3625                });
3626            }
3627        });
3628    
3629        // Set source for loop video
3630        loopPlayer.ready(function () {
3631            var src = $(this.el_).data('src');
3632            gsap.set(this.el_, { autoAlpha: 1 });
3633            if (src) {
3634                this.src(src);
3635                this.load();
3636            }
3637        });
3638    
3639        // When the start video ends, switch to the loop video
3640        startPlayer.on('ended', function () {
3641            gsap.set(this.el_, { autoAlpha: 0 });
3642            loopPlayer.play();
3643        });
3644    
3645        // Optional: Ensure the loop video is ready before hiding
3646        loopPlayer.on('loadedmetadata', function() {
3647            // Optionally seek to the first frame if needed
3648            gsap.set(this.el_, { autoAlpha: 1 });
3649            $(loopPlayer.el()).show();
3650            this.currentTime(0);
3651        });
3652    };
3653
3654    YEAR2026.prototype.initFireworksBackgroundVideo = function () {
3655        var self = this;
3656        var elements = document.querySelectorAll('.year-in-review-2026-fireworks-video-autoplay');
3657        elements.forEach(function (element) {
3658            var videoInitied = false;
3659            var video = element.querySelector('.year-in-review-2026-fireworks-video video');
3660            gsap.timeline({
3661                scrollTrigger: {
3662                    trigger: element,
3663                    start: 'top top',
3664                    pin: element.querySelector('.year-in-review-2026-fireworks-wrapper'),
3665                    end: "+=100%",
3666                    pinSpacing: false,
3667                    onEnter: function (progress, direction, isActive) {
3668                        if (!video.muted) {
3669                            return;
3670                        }
3671
3672                        video.currentTime = 0;
3673
3674                        gsap.fromTo( element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video .video-js, .year-in-review-2026-fireworks-video-overlay'), {
3675                            autoAlpha: 0,
3676                        }, {
3677                            autoAlpha: 1,
3678                        });
3679
3680                        if(!videoInitied) {
3681                            videoInitied = true;
3682                            self.playBackgroundFireworksVideo(video);
3683                            return;
3684                        }
3685
3686                        console.log('video', video);
3687
3688                        video.controls = false;
3689                        video.play().catch((error) => {
3690                            if (error.name === 'AbortError') {
3691                                console.log('Playback was interrupted:', error);
3692                            } else {
3693                                console.error('An unexpected error occurred:', error);
3694                                window.sentry && Sentry.captureException(error);
3695                            }
3696                        });
3697                    },
3698                    onEnterBack: function (progress, direction, isActive) {
3699                        if (!video.muted) {
3700                            return;
3701                        }
3702
3703                        video.currentTime = 0;
3704
3705                        gsap.fromTo(element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video-overlay'), {
3706                            autoAlpha: 0,
3707                        }, {
3708                            autoAlpha: 1,
3709                        });
3710
3711                        if(!videoInitied) {
3712                            videoInitied = true;
3713                            self.playBackgroundFireworksVideo(video);
3714                            return;
3715                        }
3716
3717                        video.controls = false;
3718                        video.play().catch((error) => {
3719                            if (error.name === 'AbortError') {
3720                                console.log('Playback was interrupted:', error);
3721                            } else {
3722                                console.error('An unexpected error occurred:', error);
3723                                window.sentry && Sentry.captureException(error);
3724                            }
3725                        });
3726                    },
3727                    onLeave: function (progress, direction, isActive) {
3728                        // video started playing by user interaction
3729                        if (!video.muted) {
3730                            return;
3731                        }
3732                        video.pause();
3733                        gsap.to(element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video-overlay'), {
3734                            autoAlpha: 0,
3735                        });
3736                    },
3737                    onLeaveBack: function (progress, direction, isActive) {
3738                        // video started playing by user interaction
3739                        if (!video.muted) {
3740                            return;
3741                        }
3742                        if (direction === "reverse") {
3743                            video.pause();
3744                        }
3745                        gsap.to(element.querySelectorAll('.year-in-review-2026-fireworks-video, .year-in-review-2026-fireworks-video-overlay'), {
3746                            autoAlpha: 0,
3747                        });
3748                    }
3749                },
3750            });
3751        });
3752    }
3753    
3754    YEAR2026.prototype.playBackgroundFireworksVideo = function (startVideoElement) {
3755        var self = this;
3756        // Select the video elements
3757    
3758        // Initialize the start video player
3759        var startPlayer = videojs(startVideoElement, { 
3760            controls: false, 
3761            muted: true, 
3762            loop: false, 
3763            autoplay: true 
3764        });
3765    
3766        // Set source for start video
3767        startPlayer.ready(function () {
3768            var src = $(this.el_).data('src');
3769            gsap.set(this.el_, { autoAlpha: 1 });
3770            if (src) {
3771                this.src(src);
3772                this.load();
3773                this.play();
3774            }
3775        });
3776    
3777    };
3778
3779    YEAR2026.prototype.initLoopAutoPlay = function () {
3780        var self = this;
3781
3782        if (typeof videojs === 'undefined') {
3783            return;
3784        }
3785
3786        var players = [];
3787
3788        var parseVideoSrc = function (videoEl) {
3789            var src = $(videoEl).data('videosrc');
3790            if (!src) {
3791                src = videoEl.getAttribute('data-videosrc');
3792            }
3793            if (!src) {
3794                return null;
3795            }
3796            if (typeof src === 'string') {
3797                try {
3798                    return JSON.parse(src);
3799                } catch (e) {
3800                    return null;
3801                }
3802            }
3803            return src;
3804        };
3805
3806        var fadeInEl = function (el) {
3807            if (typeof gsap === 'undefined' || !el) {
3808                return;
3809            }
3810            gsap.to(el, { autoAlpha: 1, duration: 0.25, overwrite: true });
3811        };
3812
3813        var fadeOutEl = function (el) {
3814            if (typeof gsap === 'undefined' || !el) {
3815                return;
3816            }
3817            gsap.to(el, { autoAlpha: 0, duration: 0.25, overwrite: true });
3818        };
3819
3820        var attemptPlay = function (player, videoEl) {
3821            try {
3822                player.muted(true);
3823                player.loop(true);
3824                player.controls(false);
3825                var p = player.play();
3826                if (p && typeof p.then === 'function') {
3827                    p.then(function () {
3828                        fadeInEl(videoEl);
3829                    });
3830                }
3831                if (p && typeof p.catch === 'function') {
3832                    p.catch(function (error) {
3833                        if (error && error.name === 'AbortError') {
3834                            return;
3835                        }
3836                        console.error('Autoplay error:', error);
3837                        window.sentry && Sentry.captureException(error);
3838                    });
3839                }
3840            } catch (e) {
3841                console.error('Autoplay exception:', e);
3842                window.sentry && Sentry.captureException(e);
3843            }
3844        };
3845
3846        $('.year-2026-our-voice--video').each(function () {
3847            var videoEl = this.querySelector('video');
3848            var videoElWrapper = this;
3849            var videoSrc = parseVideoSrc(videoEl);
3850            if (!videoSrc) {
3851                return;
3852            }
3853
3854            // Start hidden; fade in on successful autoplay.
3855            fadeOutEl(videoElWrapper);
3856
3857            videoEl.muted = true;
3858            videoEl.loop = true;
3859            videoEl.controls = false;
3860            videoEl.setAttribute('muted', '');
3861            videoEl.setAttribute('playsinline', '');
3862            videoEl.setAttribute('webkit-playsinline', '');
3863            videoEl.setAttribute('loop', '');
3864
3865            videojs(videoEl, { controls: false, muted: true, loop: true, preload: 'none', autoplay: false }).ready(function () {
3866                var player = this;
3867                players.push(player);
3868
3869                player.src(videoSrc);
3870                player.pause();
3871
3872                player.on('play', function () {
3873                    fadeInEl(videoElWrapper);
3874                });
3875                player.on('pause', function () {
3876                    fadeOutEl(videoElWrapper);
3877                });
3878
3879                if (typeof ScrollTrigger !== 'undefined') {
3880                    ScrollTrigger.create({
3881                        trigger: '.year-2026-our-message-video',
3882                        start: 'top bottom',
3883                        end: 'bottom top',
3884                        toggleActions: 'play none resume reverse',
3885                        onEnter: function () {
3886                            attemptPlay(player, videoElWrapper);
3887                        },
3888                        onEnterBack: function () {
3889                            attemptPlay(player, videoElWrapper);
3890                        },
3891                        onLeave: function () {
3892                            player.pause();
3893                        },
3894                        onLeaveBack: function () {
3895                            player.pause();
3896                        },
3897                    });
3898                } else {
3899                    attemptPlay(player, videoElWrapper);
3900                }
3901            });
3902        });
3903
3904        // Fallback: some browsers require a user gesture even for muted autoplay.
3905        if (!self._loopAutoplayUnlockBound && players.length > 0) {
3906            self._loopAutoplayUnlockBound = true;
3907            var unlock = function () {
3908                for (var i = 0; i < players.length; i++) {
3909                    attemptPlay(players[i], players[i] && players[i].el() ? players[i].el() : null);
3910                }
3911                document.removeEventListener('click', unlock);
3912                document.removeEventListener('touchstart', unlock);
3913                document.removeEventListener('keydown', unlock);
3914            };
3915            document.addEventListener('click', unlock, { passive: true, once: true });
3916            document.addEventListener('touchstart', unlock, { passive: true, once: true });
3917            document.addEventListener('keydown', unlock, { passive: true, once: true });
3918        }
3919    }
3920
3921    YEAR2026.prototype.initNetworkSection = function () {
3922
3923        // pin 150 awards section
3924        gsap.timeline({
3925            scrollTrigger: {
3926                trigger: '.year-in-review-2024-150-awards-wrapper',
3927                start: 'top top',
3928                endTrigger: '.year-in-review-2025-awards-image-wrapper',
3929                end: 'bottom bottom',
3930                pin: '.year-in-review-2024-150-awards',
3931                pinSpacing: false,
3932                anticipatePin: 1,
3933                // markers: true,
3934            }
3935        });
3936        
3937        // pin awawrds section
3938        gsap.timeline({
3939            scrollTrigger: {
3940                trigger: '.year-review-2025--tv-awards__wrapper',
3941                start: 'top top',
3942                endTrigger: '.year-in-review-2024-150-awards-wrapper',
3943                end: 'bottom bottom',
3944                pin: true,
3945                pinSpacing: false,
3946                anticipatePin: 1,
3947                // markers: true,
3948            }
3949        });
3950
3951        gsap.timeline({
3952            scrollTrigger: {
3953                trigger: '.year-in-review-2024-150-awards__trigger',
3954                start: 'top 30%',
3955                toggleActions: "play none resume reverse",
3956            }
3957        })
3958        .from('.year-in-review-2024-150-awards .year-2025--text-scale--inner', {
3959            autoAlpha: 0,
3960            scale: 10,
3961            duration: 0.25,
3962        });
3963
3964        gsap.timeline({
3965            scrollTrigger: {
3966                trigger: '.year-in-review-2025-awards-image-wrapper',
3967                start: 'center bottom',
3968                toggleActions: "play none resume reverse",
3969            }
3970        })
3971        .fromTo('.year-in-review-2025-awards-over-trophies-bg', {
3972            autoAlpha: 0,
3973            duration: 0.5,
3974            ease: Power4.easeOut,
3975        }, {
3976            autoAlpha: 0.6,
3977            duration: 0.5,
3978            ease: Power4.easeOut,
3979        });
3980
3981        // gsap.timeline({
3982        //     scrollTrigger: {
3983        //         trigger: '.year-in-review-2025-awards-image-wrapper',
3984        //         start: 'bottom bottom',
3985        //         end: 'bottom bottom',
3986        //         endTrigger: '.year-in-review-2024-150-awards-wrapper',
3987        //         pin: '.year-in-review-2025-awards-image-wrapper',
3988        //         toggleActions: "play none resume reverse",
3989        //         pinSpacing: false,
3990        //     }
3991        // });
3992
3993    }
3994
3995    
3996
3997    YEAR2026.prototype.initScnNWBackground = function () {
3998        var target = '.year-review-2025-network-section-bg';
3999        ScrollTrigger.create({
4000            trigger: '.year-2026-tv-section-wrapper',
4001            start: 'top top',
4002            end: 'bottom bottom',
4003            pin: target,
4004            pinSpacing: false,
4005            toggleClass: {
4006                targets: target,
4007                className: 'year-review-2025-network-section-bg__visible'
4008            },
4009            toggleActions: 'play reverse play reverse'
4010        });
4011
4012        // ScrollTrigger.create({
4013        //     trigger: '.year-review-2022-network',
4014        //     start: 'bottom top',
4015        //     toggleClass: {
4016        //         targets: target,
4017        //         className: 'year-review-2025-network-section-bg__hidden'
4018        //     },
4019        //     toggleActions: 'play reverse play reverse'
4020        // });
4021    }
4022
4023    YEAR2026.prototype.initFEBCSlamTitles = function () {
4024        if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
4025            return;
4026        }
4027
4028        var wrappers = document.querySelectorAll('.year-in-review-2026-slam-wrapper');
4029        if (!wrappers || wrappers.length === 0) {
4030            return;
4031        }
4032
4033        wrappers.forEach(function (wrapper) {
4034            var titles = wrapper.querySelectorAll('.year-in-review-2026-slam-in-sequence');
4035            if (!titles || titles.length === 0) {
4036                return;
4037            }
4038
4039            var saluteBg = wrapper.querySelector('.year-in-review-2026-febc-salute') || document.querySelector('.year-in-review-2026-febc-salute');
4040            if (saluteBg) {
4041                gsap.set(saluteBg, {
4042                    autoAlpha: 0,
4043                    scale: 0,
4044                    rotate: 0,
4045                    transformOrigin: '50% 50%',
4046                    willChange: 'transform, opacity',
4047                });
4048            }
4049
4050            gsap.set(titles, {
4051                autoAlpha: 0,
4052                scale: 10,
4053                transformOrigin: '50% 50%',
4054                willChange: 'transform, opacity',
4055            });
4056
4057            var activeIndex = -1;
4058
4059            var slamIn = function (el) {
4060                gsap.killTweensOf(el);
4061                return gsap.fromTo(el, {
4062                    autoAlpha: 0,
4063                    scale: 10,
4064                }, {
4065                    autoAlpha: 1,
4066                    scale: 1,
4067                    duration: 0.35,
4068                    ease: Power4.easeOut,
4069                    overwrite: true,
4070                });
4071            };
4072
4073            var fadeOut = function (el) {
4074                if (!el) {
4075                    return;
4076                }
4077                gsap.killTweensOf(el);
4078                gsap.to(el, {
4079                    autoAlpha: 0,
4080                    duration: 0.2,
4081                    overwrite: true,
4082                });
4083            };
4084
4085            var showIndex = function (nextIndex) {
4086                if (nextIndex < 0 || nextIndex >= titles.length) {
4087                    return;
4088                }
4089                if (nextIndex === activeIndex) {
4090                    return;
4091                }
4092
4093                var prevIndex = activeIndex;
4094                activeIndex = nextIndex;
4095
4096                for (var i = 0; i < titles.length; i++) {
4097                    if (i !== nextIndex && i !== prevIndex) {
4098                        gsap.set(titles[i], { autoAlpha: 0 });
4099                    }
4100                }
4101
4102                if (prevIndex > -1 && titles[prevIndex]) {
4103                    fadeOut(titles[prevIndex]);
4104                }
4105
4106                // Add a subtle shake to the salute background on each slam.
4107                if (saluteBg) {
4108                    var rand = function (min, max) {
4109                        return min + Math.random() * (max - min);
4110                    };
4111                    gsap.killTweensOf(saluteBg, 'x,y');
4112                    gsap.fromTo(saluteBg, {
4113                        x: 0,
4114                        y: 0,
4115                    }, {
4116                        x: rand(-10, 10),
4117                        y: rand(-8, 8),
4118                        duration: 0.06,
4119                        repeat: 6,
4120                        yoyo: true,
4121                        ease: 'none',
4122                        overwrite: 'auto',
4123                        onComplete: function () {
4124                            gsap.set(saluteBg, { x: 0, y: 0 });
4125                        }
4126                    });
4127                }
4128
4129                slamIn(titles[nextIndex]);
4130            };
4131
4132            // Scroll controls *when* each slam runs; the slam animation itself is time-based (no scrub).
4133            var pinEl = wrapper.querySelector('.year-in-review-2026-slam-inner') || wrapper;
4134
4135            // Background salute: intro bounce (time-based) + rotation (scrubbed).
4136            if (saluteBg) {
4137                ScrollTrigger.create({
4138                    trigger: wrapper,
4139                    start: 'top top',
4140                    end: 'bottom bottom',
4141                    invalidateOnRefresh: true,
4142                    onEnter: function () {
4143                        // Don't kill the scroll-driven rotation tween.
4144                        gsap.killTweensOf(saluteBg, 'autoAlpha,scale');
4145                        gsap.fromTo(saluteBg, {
4146                            autoAlpha: 0,
4147                            scale: 0,
4148                        }, {
4149                            autoAlpha: 1,
4150                            scale: 1.6,
4151                            duration: 0.8,
4152                            ease: 'back.out(1.6)',
4153                            overwrite: 'auto',
4154                        });
4155                    },
4156                    onEnterBack: function () {
4157                        gsap.set(saluteBg, { autoAlpha: 1, scale: 1.6 });
4158                    },
4159                    onLeave: function () {
4160                        gsap.to(saluteBg, { autoAlpha: 0, duration: 0.35, overwrite: 'auto' });
4161                    },
4162                    onLeaveBack: function () {
4163                        gsap.to(saluteBg, { autoAlpha: 0, duration: 0.25, overwrite: 'auto' });
4164                    },
4165                });
4166
4167                gsap.to(saluteBg, {
4168                    rotate: 18,
4169                    ease: 'none',
4170                    scrollTrigger: {
4171                        trigger: wrapper,
4172                        start: 'top top',
4173                        end: 'bottom bottom',
4174                        scrub: true,
4175                        invalidateOnRefresh: true,
4176                    }
4177                });
4178            }
4179
4180            ScrollTrigger.create({
4181                trigger: wrapper,
4182                start: 'top top',
4183                end: 'bottom bottom',
4184                pin: pinEl,
4185                pinSpacing: false,
4186                anticipatePin: 1,
4187                invalidateOnRefresh: true,
4188                onEnter: function () {
4189                    showIndex(0);
4190                },
4191                onEnterBack: function () {
4192                    if (activeIndex === -1) {
4193                        showIndex(0);
4194                    }
4195                },
4196                onUpdate: function (self) {
4197                    // Map scroll progress to a discrete title index.
4198                    var nextIndex = Math.min(titles.length - 1, Math.floor(self.progress * titles.length));
4199                    showIndex(nextIndex);
4200                },
4201                onLeave: function () {
4202                    // Keep the final title visible after the sequence completes.
4203                    var lastIndex = titles.length - 1;
4204                    for (var i = 0; i < titles.length; i++) {
4205                        if (i === lastIndex) {
4206                            gsap.set(titles[i], { autoAlpha: 1, scale: 1 });
4207                        } else {
4208                            gsap.set(titles[i], { autoAlpha: 0 });
4209                        }
4210                    }
4211                    activeIndex = lastIndex;
4212                },
4213            });
4214        });
4215    }
4216
4217    YEAR2026.prototype.initUnstoppablePower = function () {
4218        var self = this;
4219
4220        gsap.timeline({
4221            scrollTrigger: {
4222                trigger: '.year-in-review-2026-fireworks--stratosphere',
4223                start: 'top top',
4224                pin: '.year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-wrapper',
4225                end: 'bottom center',
4226                endTrigger: '.year-in-review-2026-stratosphere-line4',
4227            },
4228        });
4229
4230        var videoInitied = false;
4231
4232        var video = document.querySelector('.year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video video');
4233
4234        // Fade out exactly 1s before the video ends
4235        var bindFadeOutBeforeEnd = function (vid) {
4236            if (!vid || vid._fadeOutBeforeEndBound) {
4237                return;
4238            }
4239            vid._fadeOutBeforeEndBound = true;
4240
4241            var fadeTargets = '.year-in-review-2026-fireworks--stratosphere, .year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video-overlay';
4242            var hasFaded = false;
4243
4244            var resetFadeState = function () {
4245                hasFaded = false;
4246                gsap.to(fadeTargets, { autoAlpha: 1, duration: 0.15, overwrite: true });
4247            };
4248
4249            var onTimeUpdate = function () {
4250                if (hasFaded) return;
4251
4252                var dur = vid.duration;
4253                if (!dur || !isFinite(dur)) return;
4254
4255                if ((dur - vid.currentTime) <= 1) {
4256                    hasFaded = true;
4257                    gsap.to(fadeTargets, { autoAlpha: 0, duration: 1, ease: 'power1.out', overwrite: true });
4258                }
4259            };
4260
4261            vid.addEventListener('play', resetFadeState);
4262            vid.addEventListener('timeupdate', onTimeUpdate);
4263
4264            // Safety: ensure it's hidden at the end too.
4265            vid.addEventListener('ended', function () {
4266                gsap.to(fadeTargets, { autoAlpha: 0, duration: 0.1, overwrite: true });
4267            });
4268        };
4269
4270        if (video) {
4271            bindFadeOutBeforeEnd(video);
4272        }
4273
4274        gsap.timeline({
4275            scrollTrigger: {
4276                trigger: '.year-in-review-2026-fireworks--stratosphere',
4277                start: 'center center',
4278                toggleActions: "play none resume reverse",
4279            },
4280            onComplete: function () {
4281                gsap.to('.year-in-review-2026-fireworks--stratosphere, .year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video-overlay', {
4282                    autoAlpha: 1,
4283                });
4284
4285                if (!videoInitied) {
4286                    videoInitied = true;
4287                    self.playBackgroundFireworksVideo(video);
4288
4289                    // Re-bind after videojs init (safe no-op if already bound)
4290                    bindFadeOutBeforeEnd(video);
4291                    return;
4292                }
4293
4294                // Ensure it’s visible when replaying and let the timeupdate handler fade it out near the end.
4295                gsap.to('.year-in-review-2026-fireworks--stratosphere, .year-in-review-2026-fireworks--stratosphere .year-in-review-2026-fireworks-video-overlay', {
4296                    autoAlpha: 1,
4297                    duration: 0.15,
4298                    overwrite: true,
4299                });
4300
4301                video.play();
4302            }
4303        })
4304            .from('.year-in-review-2026-stratosphere-line1', {
4305                duration: 0.25,
4306                autoAlpha: 0,
4307                y: 50,
4308            })
4309            .from('.year-in-review-2026-stratosphere-line2 .year-2026--text-scale--inner', {
4310                duration: 0.25,
4311                autoAlpha: 0,
4312                scale: 10
4313            }, "-=0.15")
4314            .from('.year-in-review-2026-stratosphere-line3', {
4315                duration: 0.25,
4316                autoAlpha: 0,
4317                y: 50,
4318            }, "-=0.15")
4319            .from('.year-in-review-2026-stratosphere-line4 .year-2026--text-scale--inner', {
4320                duration: 0.25,
4321                autoAlpha: 0,
4322                scale: 10
4323            }, "-=0.15")
4324    }
4325
4326    YEAR2026.prototype.initMutedAutoplayVidoes = function () {
4327        var self = this;
4328
4329        $('.year-in-review-2026--autoplay-video-block').each(function () {
4330            var $el = $(this);
4331            var videoSrc = $el.find('video').data('videosrc');
4332            videojs($el.find('video')[0], { controls: false, muted: true, loop: false }).ready(function () {
4333                this.src(videoSrc);
4334                this.pause();
4335                var player = this;
4336
4337                ScrollTrigger.create({
4338                    trigger: $el[0],
4339                    start: "center bottom",
4340                    end: "bottom top",
4341                    toggleActions: "play none resume reverse",
4342                    onEnter: function (progress, direction, isActive) {
4343                        var vid = player.el().querySelector('video');
4344                        vid.muted = true;
4345                        vid.controls = false;
4346                        player.play();
4347                    },
4348                    onEnterBack: function (progress, direction, isActive) {
4349                        player.play();
4350                    },
4351                    onLeave: function (progress, direction, isActive) {
4352                        player.pause();
4353                    },
4354                    onLeaveBack: function (progress, direction, isActive) {
4355                        player.pause();
4356                    }
4357                });
4358
4359                $el.find('.year-in-review-2026--autoplay-video-replay').click(function () {
4360                    player.currentTime(0);
4361                    player.play();
4362                });
4363            });
4364        });
4365    }
4366
4367    YEAR2026.prototype.initMutedAutoplayLoopVidoes = function () {
4368        var self = this;
4369        $('.year-in-review-2026--autoplay-video-loop').each(function () {
4370            var $el = $(this);
4371            var videoSrc = $el.find('video').data('videosrc');
4372            videojs($el.find('video')[0], { controls: false, muted: true, loop: true }).ready(function () {
4373                this.src(videoSrc);
4374                this.pause();
4375                var player = this;
4376
4377                $el.removeClass('d-none');
4378
4379                var options = {
4380                    trigger: $el[0],
4381                    start: "center bottom",
4382                    end: "bottom top",
4383                    toggleActions: "play none resume reverse",
4384                }
4385
4386                if($el.hasClass('year-in-review-2026--lrh-hall-stars__video')) {
4387                    options.trigger = ".year-in-review-2026--lrh-hall-stars";
4388                    options.start = "top-=50px top";
4389                }
4390
4391                ScrollTrigger.create({
4392                    trigger: options.trigger,
4393                    start: options.start,
4394                    end: options.end,
4395                    toggleActions: "play none resume reverse",
4396                    onEnter: function (progress, direction, isActive) {
4397                        var vid = player.el().querySelector('video');
4398                        vid.muted = true;
4399                        vid.controls = false;
4400                        player.play();
4401                    },
4402                    onEnterBack: function (progress, direction, isActive) {
4403                        player.play();
4404                    },
4405                    onLeave: function (progress, direction, isActive) {
4406                        player.pause();
4407                    },
4408                    onLeaveBack: function (progress, direction, isActive) {
4409                        player.pause();
4410                    }
4411                });
4412            });
4413        });
4414    }
4415
4416
4417    YEAR2026.prototype.initBackgroundVideo = function () {
4418        var self = this;
4419        var videoInitied = false;
4420        var video = document.querySelector('.who-we-are--bg-video');
4421
4422        gsap.timeline({
4423            scrollTrigger: {
4424                trigger: '.year-in-review-2026--triptych',
4425                start: 'top top',
4426                end: "bottom top",
4427                pin: '.who-we-are--bg-video-inner',
4428                pinSpacing: false,
4429                onEnter: function (progress, direction, isActive) {
4430                    if (!video.muted) {
4431                        return;
4432                    }
4433
4434                    if(!videoInitied) {
4435                        videoInitied = true;
4436                        self.playBackgroundVideo();
4437                        return;
4438                    }
4439
4440                    video.controls = false;
4441                    video.play().catch((error) => {
4442                        if (error.name === 'AbortError') {
4443                            console.log('Playback was interrupted:', error);
4444                        } else {
4445                            console.error('An unexpected error occurred:', error);
4446                            window.sentry && Sentry.captureException(error);
4447                        }
4448                    });
4449                    gsap.fromTo('.who-we-are--bg-video-inner', {
4450                        autoAlpha: 0,
4451                    }, {
4452                        autoAlpha: 1,
4453                    });
4454                },
4455                onEnterBack: function (progress, direction, isActive) {
4456                    if (!video.muted) {
4457                        return;
4458                    }
4459
4460                    if(!videoInitied) {
4461                        videoInitied = true;
4462                        self.playBackgroundVideo();
4463                        return;
4464                    }
4465
4466                    video.controls = false;
4467                    video.play().catch((error) => {
4468                        if (error.name === 'AbortError') {
4469                            console.log('Playback was interrupted:', error);
4470                        } else {
4471                            console.error('An unexpected error occurred:', error);
4472                            window.sentry && Sentry.captureException(error);
4473                        }
4474                    });
4475                    gsap.fromTo('.who-we-are--bg-video-inner', {
4476                        autoAlpha: 0,
4477                    }, {
4478                        autoAlpha: 1,
4479                    });
4480                },
4481                onLeave: function (progress, direction, isActive) {
4482                    // video started playing by user interaction
4483                    if (!video.muted) {
4484                        return;
4485                    }
4486                    video.pause();
4487                    gsap.to('.who-we-are--bg-video-inner', {
4488                        autoAlpha: 0,
4489                    });
4490                },
4491                onLeaveBack: function (progress, direction, isActive) {
4492                    // video started playing by user interaction
4493                    if (!video.muted) {
4494                        return;
4495                    }
4496                    if (direction === "reverse") {
4497                        video.pause();
4498                    }
4499                    gsap.to('.who-we-are--bg-video-inner', {
4500                        autoAlpha: 0,
4501                    });
4502                }
4503            },
4504        });
4505    }
4506
4507
4508
4509    YEAR2026.prototype.initVideos = function () {
4510        var self = this;
4511        $('.year-end-2022--playbutton-area').click(function () {
4512            var $el = $(this);
4513            var videoElId = $el.find('.video-js').attr('id');
4514            var video = videojs.players[videoElId];
4515
4516            var scntvlink = $el.data('scntv');
4517            if (scntvlink) {
4518            }
4519
4520            if (!video) {
4521                return;
4522            }
4523
4524            var videoId = $(this).data('videoid');
4525            if (!video.controls()) {
4526                for (var player in videojs.players) {
4527                    if (videojs.players.hasOwnProperty(player) && videojs.players[player] && $(videojs.players[player].el_).closest('.year-end-2022--playbutton-area').length > 0) {
4528                        videojs.players[player].pause();
4529                    }
4530                }
4531
4532                video.controls(true);
4533                video.muted(false);
4534                video.currentTime(0);
4535                video.loop(false);
4536                video.poster($el.data('videoposter'));
4537                $el.addClass('year-end-2022-videoautoplay__inline');
4538                EventTracker.trackVideo(video);
4539                video.one('timeupdate', function () {
4540                    self.addCaptionsIfNeeded(video, videoId);
4541                });
4542                video.play();
4543                trackEvent("Video", "Play", videoId);
4544            } else if (video.paused()) {
4545                trackEvent("Video", "Play", videoId);
4546            }
4547        });
4548    }
4549    YEAR2026.prototype.stickyVideoAutoplay = function () {
4550        var isMobile = this.isMobile();
4551        var self = this;
4552        $('.year-in-review-2026--video-autoplay').each(function (index, wrapper) {
4553            var skipPin = wrapper.getAttribute('data-skip-pin') === 'true';
4554            if (!isMobile && !skipPin) {
4555                ScrollTrigger.create({
4556                    trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'),
4557                    start: 'top top',
4558                    end: 'bottom top',
4559                    scrub: true,
4560                    pin: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'),
4561                    pinSpacing: false,
4562                });
4563            }
4564
4565            gsap.to(wrapper.querySelector('.year-in-review-2026--intro-video-dim'), {
4566                scrollTrigger: {
4567                    trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'),
4568                    start: 'top top',
4569                    toggleActions: "play none resume reverse",
4570                },
4571                autoAlpha: 0.7,
4572                duration: 0.5,
4573                ease: Power4.easeOut,
4574            });
4575
4576            var videoEl = wrapper.querySelector('.video-js');
4577            if (!videoEl || typeof videojs === 'undefined') {
4578                return;
4579            }
4580
4581            var parseVideoSrcAttr = function (attrName) {
4582                var raw = videoEl.getAttribute(attrName);
4583                if (!raw) {
4584                    return null;
4585                }
4586                if (typeof raw !== 'string') {
4587                    return raw;
4588                }
4589                try {
4590                    return JSON.parse(raw);
4591                } catch (e) {
4592                    console.error('Invalid JSON for ' + attrName, e);
4593                    window.sentry && Sentry.captureException(e);
4594                    return null;
4595                }
4596            };
4597
4598            var useDesktopSrc = window.innerWidth > 1200;
4599            var videoSource = (useDesktopSrc && parseVideoSrcAttr('data-desktop-videosrc')) || parseVideoSrcAttr('data-videosrc');
4600            if (!videoSource) {
4601                return;
4602            }
4603
4604            videojs(videoEl, { controls: false, muted: true, loop: false }).ready(function () {
4605                var player = this;
4606                this.src(videoSource);
4607                player.muted(true);
4608
4609                player.on('ended', function () {
4610                    var now = new Date().getTime();
4611                    if(self._lastInsetScrollTime && (now - self._lastInsetScrollTime) < 15000 && wrapper.getAttribute('data-auto-scroll') === 'true') {
4612                        self.scrollToNextInset();
4613                    }
4614                });
4615
4616
4617                gsap.timeline({
4618                    scrollTrigger: {
4619                        trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay--fade-trigger'),
4620                        start: 'center center',
4621                        toggleActions: "play none resume reverse",
4622                        onToggle: (self) => {
4623                            var shouldPlay = self.direction == 1 || player.currentTime() < 0.5;
4624                            if (self.isActive && shouldPlay) {
4625                                player.play();
4626                            }
4627                        },
4628                    },
4629                }).fromTo(wrapper.querySelector('.year-in-review-2026--videoautoplay'), {
4630                    autoAlpha: 0,
4631                }, {
4632                    autoAlpha: 1,
4633                    duration: 0.25,
4634                });
4635            });
4636
4637        });
4638    }
4639
4640    YEAR2026.prototype.initTechnologyForLife = function () {
4641        if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
4642            return;
4643        }
4644
4645        var section = document.querySelector('.year-2026-our-message-video');
4646        if (!section) {
4647            return;
4648        }
4649
4650        var intro = section.querySelector('.year-in-review-2026--our-tech-intro');
4651        var message = section.querySelector('.year-2026-our-message-wrapper');
4652        var videoLayer = section.querySelector('.year-2026-our-voice--video');
4653        var videoEl = videoLayer ? videoLayer.querySelector('video') : null;
4654
4655        if (!intro || !message || !videoLayer) {
4656            return;
4657        }
4658
4659        // Ensure predictable initial state (prevents flashes).
4660        gsap.set(videoLayer, { autoAlpha: 0, visibility: 'hidden' });
4661        gsap.set(intro, { autoAlpha: 0, y: 60, willChange: 'transform,opacity', pointerEvents: 'auto' });
4662        gsap.set(message, { autoAlpha: 0, y: 60, willChange: 'transform,opacity', pointerEvents: 'none' });
4663
4664        // Optional: autoplay the background video while this section is active.
4665        var player = null;
4666        var ensurePlayer = function () {
4667            if (!videoEl || typeof videojs === 'undefined') {
4668                return null;
4669            }
4670            if (player) {
4671                return player;
4672            }
4673
4674            var parseVideoSrcAttr = function (attrName) {
4675                var raw = videoEl.getAttribute(attrName);
4676                if (!raw) {
4677                    return null;
4678                }
4679                if (typeof raw !== 'string') {
4680                    return raw;
4681                }
4682                try {
4683                    return JSON.parse(raw);
4684                } catch (e) {
4685                    console.error('Invalid JSON for ' + attrName, e);
4686                    window.sentry && Sentry.captureException(e);
4687                    return null;
4688                }
4689            };
4690
4691            var videoSource = parseVideoSrcAttr('data-videosrc');
4692            if (!videoSource) {
4693                return null;
4694            }
4695
4696            player = videojs(videoEl, { controls: false, muted: true, loop: true, preload: 'none' });
4697            try {
4698                player.src(videoSource);
4699                player.muted(true);
4700            } catch (e) {
4701                console.error('Video init failed', e);
4702                window.sentry && Sentry.captureException(e);
4703            }
4704            return player;
4705        };
4706
4707        var playVideo = function () {
4708            var p = ensurePlayer();
4709            if (!p) {
4710                return;
4711            }
4712            try {
4713                var vid = p.el() && p.el().querySelector('video');
4714                if (vid) {
4715                    vid.muted = true;
4716                    vid.controls = false;
4717                }
4718                // Keep it subtle: restart only when entering from outside.
4719                p.currentTime(0);
4720                p.play();
4721            } catch (e) {
4722                // Autoplay may be blocked in some contexts.
4723            }
4724        };
4725
4726        var pauseVideo = function () {
4727            if (!player) {
4728                return;
4729            }
4730            try {
4731                player.pause();
4732            } catch (e) {
4733                // ignore
4734            }
4735        };
4736
4737        // 1) Sticky/pinned video for the entire section scroll.
4738        // Use a safer approach on mobile (pinning can be unstable on iOS).
4739        var isMobile = this.isMobile && this.isMobile();
4740
4741        ScrollTrigger.create({
4742            trigger: section,
4743            start: 'top top',
4744            end: 'bottom top',
4745            pin: videoLayer,
4746            pinSpacing: false,
4747            anticipatePin: 1,
4748            invalidateOnRefresh: true,
4749            onEnter: function () {
4750                gsap.to(videoLayer, { autoAlpha: 1, visibility: 'visible', duration: 0.25, ease: 'power2.out', overwrite: true });
4751                playVideo();
4752            },
4753            onEnterBack: function () {
4754                gsap.to(videoLayer, { autoAlpha: 1, visibility: 'visible', duration: 0.25, ease: 'power2.out', overwrite: true });
4755                playVideo();
4756            },
4757            onLeave: function () {
4758                pauseVideo();
4759                gsap.to(videoLayer, { autoAlpha: 0, duration: 0.2, ease: 'power2.out', overwrite: true });
4760            },
4761            onLeaveBack: function () {
4762                pauseVideo();
4763                gsap.to(videoLayer, { autoAlpha: 0, duration: 0.2, ease: 'power2.out', overwrite: true });
4764            },
4765        });
4766
4767        ScrollTrigger.create({
4768            trigger: '.year-2026-our-message-video',
4769            start: 'top top',
4770            end: "bottom bottom",
4771            pin: '.year-2026-our-message-inner',
4772            pinSpacing: false,
4773            toggleActions: "play pause resume reverse",
4774            anticipatePin: 1,
4775            invalidateOnRefresh: true,
4776        });
4777
4778        // 2) 50/50 crossfade between the two text panels, each fading up from the bottom.
4779        // Timeline duration is 2 "units" so the switch point is exactly at 50% (t=1).
4780        gsap.timeline({
4781            // defaults: { ease: 'none' },
4782            scrollTrigger: {
4783                // trigger: section,
4784                trigger: '.year-2026-our-message-trigger',
4785                start: 'top top',
4786                endTrigger: section,
4787                end: 'bottom bottom',
4788                scrub: true,
4789                toggleActions: "play pause resume reverse",
4790                invalidateOnRefresh: true,
4791            },
4792        })
4793            // Intro fades in early
4794            .to(intro, { autoAlpha: 1, y: 0, duration: 0.2, ease: 'power3.out' }, 0)
4795            // Crossfade around the midpoint (t=1)
4796            .to(intro.querySelector('.year-2026-inner-content'), { autoAlpha: 0, y: -40, duration: 0.2, ease: 'power2.inOut' })
4797            .fromTo(message, {
4798                autoAlpha: 0,
4799                y: 50,
4800                duration: 0.4,
4801                ease: 'power3.out',
4802            }, {
4803                autoAlpha: 1,
4804                y: 0,
4805            }, "<");
4806
4807            // Keep message stable through the second half
4808            // .to(message, { autoAlpha: 1, y: 0, duration: 0.85 }, 1.15);
4809    }
4810
4811    YEAR2026.prototype.initLRHHallTeaser = function () {
4812
4813        var teaser = document.querySelector('.year-in-review-2026-lrh-hall-teaser');
4814        if (!teaser) {
4815            return;
4816        }
4817
4818        var titles = Array.prototype.slice.call(teaser.querySelectorAll('.year-in-review-2026-lrh-hall-teaser-title'));
4819        if (!titles || titles.length === 0) {
4820            return;
4821        }
4822
4823        // Overlay titles so we can sequence them (similar to FEBC slam, but different motion).
4824        gsap.set(teaser, {
4825            position: 'relative',
4826            overflow: 'hidden',
4827        });
4828
4829        gsap.set(titles, {
4830            position: 'absolute',
4831            top: 0,
4832            left: 0,
4833            right: 0,
4834            autoAlpha: 0,
4835        });
4836
4837        var hasSplitText = (typeof SplitText !== 'undefined');
4838        var splitByIndex = {};
4839
4840        var getTextEl = function (titleEl) {
4841            return titleEl.querySelector('.year-in-review-2026-subheading__medium') || titleEl;
4842        };
4843
4844        var getTextEls = function (titleEl) {
4845            return titleEl.querySelectorAll('.year-in-review-2026-subheading__medium') || titleEl;
4846        };
4847
4848        var ensureSplit = function (idx) {
4849            if (!hasSplitText) {
4850                return null;
4851            }
4852            if (splitByIndex[idx]) {
4853                return splitByIndex[idx];
4854            }
4855
4856            var el = getTextEl(titles[idx]);
4857            if (!el) {
4858                return null;
4859            }
4860
4861            // Split once; keep reference for subsequent plays.
4862            try {
4863                splitByIndex[idx] = new SplitText(el, {
4864                    type: 'lines,words,chars',
4865                    linesClass: 'line',
4866                    wordsClass: 'word',
4867                    charsClass: 'char'
4868                });
4869            } catch (e) {
4870                console.error('SplitText init failed:', e);
4871                window.sentry && Sentry.captureException(e);
4872                splitByIndex[idx] = null;
4873            }
4874            return splitByIndex[idx];
4875        };
4876
4877        var animateTitleIn = function (idx) {
4878            var titleEl = titles[idx];
4879            if (!titleEl) {
4880                return;
4881            }
4882
4883            var textEl = getTextEl(titleEl);
4884            var tl = gsap.timeline({
4885                id: 'lrh-hall-teaser-title-' + idx,
4886                overwrite: true,
4887            });
4888
4889            // Fallback if SplitText isn't present.
4890            if (!hasSplitText) {
4891                tl.fromTo(textEl, {
4892                    autoAlpha: 0,
4893                    y: 30,
4894                    scale: 0.98,
4895                }, {
4896                    autoAlpha: 1,
4897                    y: 0,
4898                    scale: 1,
4899                    duration: 0.5,
4900                    ease: Power3.easeOut,
4901                    overwrite: true,
4902                });
4903                return tl;
4904            }
4905
4906            ensureSplit(idx);
4907            addSlamTextInToTimeline(tl, textEl, {
4908                duration: 1.1,
4909                ease: 'power2.inOut',
4910                fallbackDuration: 0.5,
4911                autoSplit: false, // already split + cached by ensureSplit()
4912            });
4913
4914            return tl;
4915        };
4916
4917        var fadeTitleIn = function (idx) {
4918            var titleEl = titles[idx];
4919            if (!titleEl) {
4920                return;
4921            }
4922
4923            var tl = gsap.timeline({
4924                id: 'lrh-hall-teaser-title-fade-' + idx,
4925                overwrite: true,
4926            });
4927
4928            // Animate each text element (line) letter-by-letter.
4929            var staggerAmount = 1;
4930            var charDuration = 1.5;
4931
4932            var nodeList = titleEl.querySelectorAll('.year-in-review-2026-subheading__medium');
4933            var textEls = (nodeList && nodeList.length) ? Array.prototype.slice.call(nodeList) : [getTextEl(titleEl)];
4934
4935            for (var i = 0; i < textEls.length; i++) {
4936                var el = textEls[i];
4937                if (!el) {
4938                    continue;
4939                }
4940
4941                // Ensure the container is visible; chars will be animated in.
4942                gsap.set(el, { autoAlpha: 1, y: 0, overwrite: true });
4943
4944                // Cache SplitText per element to avoid double-wrapping on replays.
4945                if (!el._lrhFadeSplit) {
4946                    try {
4947                        el._lrhFadeSplit = new SplitText(el, {
4948                            type: 'words,chars',
4949                            wordsClass: 'word',
4950                            charsClass: 'char'
4951                        });
4952                    } catch (e) {
4953                        console.error('SplitText init failed:', e);
4954                        window.sentry && Sentry.captureException(e);
4955                        el._lrhFadeSplit = null;
4956                    }
4957                }
4958
4959                var chars = el.querySelectorAll ? el.querySelectorAll('.char') : null;
4960                gsap.set(chars, { autoAlpha: 0, overwrite: true });
4961                tl.to(chars, {
4962                    autoAlpha: 1,
4963                    y: 0,
4964                    duration: charDuration,
4965                    ease: Power2.easeOut,
4966                    overwrite: true,
4967                    stagger: { amount: staggerAmount },
4968                }, i > 0 ? "<+=1.25" : 0);
4969            }
4970
4971            return tl;
4972        };
4973
4974        var activeIndex = -1;
4975        var killTitleTweens = function (idx) {
4976            var titleEl = titles[idx];
4977            if (!titleEl) {
4978                return;
4979            }
4980            var textEl = getTextEl(titleEl);
4981            gsap.killTweensOf(titleEl);
4982            if (textEl) {
4983                gsap.killTweensOf(textEl);
4984                var chars = textEl.querySelectorAll && textEl.querySelectorAll('.char');
4985                if (chars && chars.length) {
4986                    gsap.killTweensOf(chars);
4987                }
4988            }
4989        };
4990
4991        var resetAllTitles = function () {
4992            activeIndex = -1;
4993            for (var i = 0; i < titles.length; i++) {
4994                killTitleTweens(i);
4995                gsap.set(titles[i], { autoAlpha: 0, y: 0, scale: 1 });
4996            }
4997        };
4998
4999        var showIndex = function (nextIndex, forceReplay) {
5000            if (nextIndex < 0 || nextIndex >= titles.length) {
5001                return;
5002            }
5003            if (!forceReplay && nextIndex === activeIndex) {
5004                return;
5005            }
5006
5007            var prevIndex = activeIndex;
5008            activeIndex = nextIndex;
5009
5010            if (prevIndex > -1 && titles[prevIndex]) {
5011                killTitleTweens(prevIndex);
5012                gsap.set(titles[prevIndex], { autoAlpha: 0, y: 0, scale: 1 });
5013            }
5014
5015            // Ensure a clean state before replaying.
5016            killTitleTweens(nextIndex);
5017            gsap.set(titles[nextIndex], { autoAlpha: 1, y: 0, scale: 1 });
5018            if(nextIndex == 0) {
5019                animateTitleIn(nextIndex);
5020            } else {
5021                fadeTitleIn(nextIndex);
5022            }
5023        };
5024
5025        // Provide continuous visual feedback while scrubbing.
5026        var applyScrubFeedback = function (st, idx) {
5027            if (!st || idx < 0 || idx >= titles.length) {
5028                return;
5029            }
5030
5031            var segSize = 1 / titles.length;
5032            var local = (st.progress - (idx * segSize)) / segSize;
5033            local = Math.max(0, Math.min(1, local));
5034
5035            // Subtle parallax/scale as you scroll (reverses naturally when scrolling back).
5036            var y = gsap.utils.interpolate(14, -14, local);
5037            var scale = gsap.utils.interpolate(1.02, 0.98, local);
5038
5039            gsap.set(titles[idx], {
5040                y: y,
5041                scale: scale,
5042                overwrite: true,
5043            });
5044        };
5045
5046        var fadeTween;
5047        var fadeOutTeaser = function () {
5048            if (fadeTween) {
5049                fadeTween.kill();
5050            }
5051            fadeTween = gsap.to('.year-in-review-2026--lrh-hall-stars, .year-in-review-2026-lrh-hall-teaser', {
5052                autoAlpha: 0,
5053                duration: 0.35,
5054                ease: Power2.easeOut,
5055                overwrite: true,
5056            });
5057        };
5058
5059        var fadeInTeaser = function () {
5060            if (fadeTween) {
5061                fadeTween.kill();
5062            }
5063            fadeTween = gsap.to('.year-in-review-2026--lrh-hall-stars, .year-in-review-2026-lrh-hall-teaser', {
5064                autoAlpha: 1,
5065                duration: 0.25,
5066                ease: Power2.easeOut,
5067                overwrite: true,
5068            });
5069        };
5070
5071        // Scroll controls *when* each title animates.
5072        ScrollTrigger.create({
5073            trigger: teaser,
5074            start: 'top top',
5075            // Give each title enough scroll distance (otherwise small scroll instantly advances).
5076            end: "bottom top",
5077            pin: true,
5078            pinSpacing: false,
5079            scrub: true,
5080            toggleActions: "play pause resume reverse",
5081            anticipatePin: 1,
5082            invalidateOnRefresh: true,
5083            onEnter: function () {
5084                // Always replay the first line when entering from the top.
5085                fadeInTeaser();
5086                showIndex(0, true);
5087            },
5088            onEnterBack: function () {
5089                // When coming back up into the teaser, ensure title is visible.
5090                fadeInTeaser();
5091                if (activeIndex === -1) {
5092                    showIndex(0, true);
5093                }
5094            },
5095            onUpdate: function (st) {
5096                var idx = Math.min(titles.length - 1, Math.floor(st.progress * titles.length));
5097                showIndex(idx);
5098                applyScrubFeedback(st, idx);
5099            },
5100            onLeave: function () {
5101                // Keep final title visible.
5102                showIndex(titles.length - 1);
5103                fadeOutTeaser();
5104            },
5105            onLeaveBack: function () {
5106                // Reset state so the first title can replay next time you scroll back in.
5107                resetAllTitles();
5108                fadeInTeaser();
5109            }
5110        });
5111    }
5112
5113    YEAR2026.prototype.updateLRHStopPoint = function () {
5114        $('.navigation-step__abs-lrh-hall').css('bottom', $('.year-in-review-2026--lrh-hall-image-wrapper').height() + 20 + 'px');
5115    }
5116    YEAR2026.prototype.initLRHHall = function () {
5117        var self = this;
5118        var isMobile = this.isMobile();
5119
5120        this.updateLRHStopPoint();
5121        var updateLRHStopPointThrottled = _.throttle(function () {
5122            self.updateLRHStopPoint();
5123            ScrollTrigger.refresh();
5124        }, 250);
5125        window.addEventListener('resize', updateLRHStopPointThrottled);
5126        window.addEventListener('orientationchange', updateLRHStopPointThrottled);
5127
5128        var lrhStarsTimeLine = gsap.timeline({
5129            scrollTrigger: {
5130                trigger: ".year-in-review-2026--lrh-hall-stars",
5131                start: 'top-=50px top',
5132                pin: ".year-in-review-2026--lrh-hall-stars__video",
5133                end: "bottom top",
5134                pinSpacing: false,
5135                toggleActions: "play none resume reverse",
5136            }
5137        });
5138
5139        lrhStarsTimeLine.from(".year-in-review-2026--lrh-hall-stars__video", {
5140            autoAlpha: 0,
5141            duration: 1,
5142        });
5143
5144        
5145        $('.year-in-review-2026--video-autoplay--lrh-hall').each(function (index, wrapper) {
5146
5147            gsap.to(wrapper.querySelector('.year-in-review-2026--intro-video-dim'), {
5148                scrollTrigger: {
5149                    trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay-element'),
5150                    start: 'top top',
5151                    toggleActions: "play none resume reverse",
5152                },
5153                autoAlpha: 0.7,
5154                duration: 0.5,
5155                ease: Power4.easeOut,
5156            });
5157
5158            var videoEl = wrapper.querySelector('.video-js');
5159            if (!videoEl || typeof videojs === 'undefined') {
5160                return;
5161            }
5162
5163            var parseVideoSrcAttr = function (attrName) {
5164                var raw = videoEl.getAttribute(attrName);
5165                if (!raw) {
5166                    return null;
5167                }
5168                if (typeof raw !== 'string') {
5169                    return raw;
5170                }
5171                try {
5172                    return JSON.parse(raw);
5173                } catch (e) {
5174                    console.error('Invalid JSON for ' + attrName, e);
5175                    window.sentry && Sentry.captureException(e);
5176                    return null;
5177                }
5178            };
5179            
5180            var useDesktopSrc = window.innerWidth > 1200;
5181            var videoSource = (useDesktopSrc && parseVideoSrcAttr('data-desktop-videosrc')) || parseVideoSrcAttr('data-videosrc');
5182            if (!videoSource) {
5183                return;
5184            }
5185
5186            gsap.timeline({
5187                scrollTrigger: {
5188                    trigger: '.year-in-review-2026--lrh-hall-wrapper',
5189                    start: 'top top',
5190                    pin: '.year-in-review-2026--lrh-hall-wrapper-inner',
5191                    pinSpacing: false,
5192                    // endTrigger: '.year-in-review-2026--lrh-hall-end',
5193                    // end: 'bottom bottom',
5194                    endTrigger: '.navigation-step__abs-lrh-hall',
5195                    end: 'top top',
5196                    toggleActions: "play none resume reverse",
5197                    invalidateOnRefresh: true,
5198                }
5199            });
5200
5201            var lrhHallTl = gsap.timeline({
5202                scrollTrigger: {
5203                    trigger: '.year-in-review-2026--lrh-hall-end-animation',
5204                    start: 'top top',
5205                    toggleActions: "play none resume reverse",
5206                }
5207            });
5208
5209            lrhHallTl.to(videoEl, {
5210                autoAlpha: 0,
5211                duration: 0.5,
5212                ease: Power4.easeOut,
5213            })
5214
5215            var imgEl = document.querySelector('.year-in-review-2026--lrh-hall-img');
5216            lrhHallTl.fromTo('.year-in-review-2026--lrh-masterpiece-title', {
5217                autoAlpha: 1,
5218            }, {
5219                autoAlpha: 0,
5220                duration: 0.25,
5221            }, "<");
5222
5223            var hallNameEl = document.querySelector('.year-in-review-2026--lrh-hall-name .year-2026--text-scale--inner');
5224            var hallNameTl = gsap.timeline({ overwrite: true });
5225            addSlamTextInToTimeline(hallNameTl, hallNameEl, {
5226                duration: 1.1,
5227                ease: 'power2.inOut',
5228                fallbackDuration: 0.5,
5229                autoSplit: true,
5230                delay: 1,
5231            });
5232            lrhHallTl.add(hallNameTl, "-=0.5");
5233
5234            lrhHallTl.to('.year-in-review-2026-lrh-line-4', {
5235                autoAlpha: 0,
5236            }, "<")
5237
5238            lrhHallTl.fromTo(imgEl.querySelector('img'), {
5239                scale: 5,
5240                transformOrigin: "center",
5241            }, {
5242                scale: 1,
5243                duration: 4,
5244                ease: "Power4.easeOut",
5245            }, "<");
5246
5247            lrhHallTl.fromTo(imgEl, {
5248                scale: 0.8,
5249                autoAlpha: 0,
5250                y: 20,
5251            }, {
5252                scale: 1,
5253                autoAlpha: 1,
5254                y: 0,
5255                duration: 4,
5256                ease: "Power4.easeOut",
5257            }, "<");
5258
5259                        
5260            lrhHallTl.from('.year-in-review-2026--lrh-hall-image-wrapper .year-in-review-2026-subtitle', {
5261                autoAlpha: 0,
5262                y: 50,
5263                duration: 0.25,
5264            }, "-=1");
5265
5266            // The button timeline may be created in a paused state (data-paused).
5267            // Explicitly restart it when this end-card sequence reaches this point.
5268            var m13BtnTl = self.globalTimelines && self.globalTimelines['m13-2025-button'];
5269            if (m13BtnTl) {
5270                lrhHallTl.add(function () {
5271                    if (lrhHallTl.reversed()) {
5272                        m13BtnTl.pause(0);
5273                        return;
5274                    }
5275                    try { m13BtnTl.restart(); } catch (e) { }
5276                }, "-=0.5");
5277            }
5278
5279
5280            videojs(videoEl, { controls: false, muted: true, loop: false }).ready(function () {
5281                var player = this;
5282                this.src(videoSource);
5283                player.muted(true);
5284
5285                var autoScrollTriggered = false;
5286                var tryScrollToNextInset = function () {
5287                    var now = Date.now();
5288                    if (self._lastInsetScrollTime && (now - self._lastInsetScrollTime) < 15000 && wrapper.getAttribute('data-auto-scroll') === 'true') {
5289                        self.scrollToNextInset();
5290                    }
5291                };
5292
5293                player.on('timeupdate', function () {
5294                    if (autoScrollTriggered) {
5295                        return;
5296                    }
5297                    var duration = player.duration();
5298                    if (!duration || !isFinite(duration)) {
5299                        return;
5300                    }
5301                    if (duration - player.currentTime() <= 1) {
5302                        autoScrollTriggered = true;
5303                        tryScrollToNextInset();
5304                    }
5305                });
5306
5307                player.on('play', function () {
5308                    autoScrollTriggered = false;
5309                });
5310
5311                player.on('ended', function () {
5312                    if (autoScrollTriggered) {
5313                        return;
5314                    }
5315                    tryScrollToNextInset();
5316                });
5317
5318                gsap.timeline({
5319                    scrollTrigger: {
5320                        trigger: wrapper.querySelector('.year-in-review-2026--video-autoplay--fade-trigger'),
5321                        start: 'center center',
5322                        end: 'bottom top',
5323                        toggleActions: "play none resume reverse",
5324                        onToggle: (st) => {
5325                            var shouldPlay = st.direction == 1 || player.currentTime() < 0.5;
5326                            if (st.isActive && shouldPlay) {
5327                                player.play();
5328                            }
5329                        },
5330                        onLeaveBack: function () {
5331                            // Scrolling back out: reset video + lrhHall end-card animation
5332                            try { player.pause(); } catch (e) { }
5333                            try { player.currentTime(0); } catch (e) { }
5334                            m13BtnTl && m13BtnTl.pause(0);
5335
5336                            var autoplayEl = wrapper.querySelector('.year-in-review-2026--videoautoplay');
5337                            if (autoplayEl) {
5338                                gsap.set(autoplayEl, { autoAlpha: 0 });
5339                            }
5340                        },
5341                    },
5342                }).fromTo(wrapper.querySelector('.year-in-review-2026--videoautoplay'), {
5343                    autoAlpha: 0,
5344                }, {
5345                    autoAlpha: 1,
5346                    duration: 0.25,
5347                });
5348            });
5349
5350        });
5351    }
5352    
5353    YEAR2026.prototype.stickyVideoLastFrame = function () {
5354        $('.year-in-review-2026--video-autoplay, .year-in-review-2026--video-autoplay--lrh-hall').each(function (index, wrapper) {
5355
5356            var videoEl = wrapper.querySelector('.video-js');
5357            if (!videoEl || typeof videojs === 'undefined') {
5358                return;
5359            }
5360
5361            var parseVideoSrcAttr = function (attrName) {
5362                var raw = videoEl.getAttribute(attrName);
5363                if (!raw) {
5364                    return null;
5365                }
5366                if (typeof raw !== 'string') {
5367                    return raw;
5368                }
5369                try {
5370                    return JSON.parse(raw);
5371                } catch (e) {
5372                    console.error('Invalid JSON for ' + attrName, e);
5373                    window.sentry && Sentry.captureException(e);
5374                    return null;
5375                }
5376            };
5377
5378            var useDesktopSrc = window.innerWidth > 1200;
5379            var videoSource = (useDesktopSrc && parseVideoSrcAttr('data-desktop-videosrc')) || parseVideoSrcAttr('data-videosrc');
5380            if (!videoSource) {
5381                return;
5382            }
5383
5384            videojs(videoEl, { controls: false, muted: true, loop: false }).ready(function () {
5385                var player = this;
5386                this.src(videoSource);
5387                player.muted(true);
5388
5389                // Seek to last frame and pause on init
5390                player.one('loadedmetadata', function () {
5391                    var lastFrameTime = Math.max(player.duration() - 0.05, 0);
5392                    player.currentTime(lastFrameTime);
5393                });
5394                gsap.set(wrapper.querySelector('.year-in-review-2026--videoautoplay'), { autoAlpha: 1 });
5395
5396                player.play();
5397            });
5398        });
5399    }
5400
5401
5402
5403    YEAR2026.prototype.initVideoAutoplay = function () {
5404        var self = this;
5405        _.each($('.year-end-2022--playbutton-area'), function (el, index) {
5406            var $el = $(el);
5407
5408            var videoId = $el.data('videoid');
5409
5410            var options = jQuery.extend({}, videoOptions);
5411            options.preload = "none";
5412            options.controls = false;
5413            options.autoplay = false;
5414            options.sources = $el.data('videosrc');
5415            options.loop = true;
5416            var videos = $el.find('.video-js');
5417            if (videos.length == 0) {
5418                return;
5419            }
5420            var video = videos[0];
5421            videojs(videos[0], options).ready(function () {
5422                var tween;
5423
5424                var player = this;
5425
5426                gsap.timeline({
5427                    scrollTrigger: {
5428                        trigger: $el,
5429                        start: 'top bottom',
5430                        end: "bottom top",
5431                        onEnter: function (progress, direction, isActive) {
5432                            if (!video.muted) {
5433                                return;
5434                            }
5435                            player.currentTime(0);
5436                            player.one('play', function () {
5437                                tween && tween.pause();
5438                                tween = gsap.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 1, ease: Power4.easeOut });
5439                            });
5440                            if (!$el.hasClass('year-end-2022-videoautoplay__inline')) {
5441                                var vid = player.el().querySelector('video');
5442                                if (vid.currentTime < 3 && $(window).width() < 768) {
5443                                    player.src(player.currentSources());
5444                                }
5445                                vid.controls = false;
5446                                player.play();
5447                            }
5448                            trackEvent("Video", "Visible", videoId);
5449                        },
5450                        onEnterBack: function (progress, direction, isActive) {
5451                            if (!video.muted) {
5452                                return;
5453                            }
5454                            player.currentTime(0);
5455                            player.one('play', function () {
5456                                tween && tween.pause();
5457                                tween = gsap.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 1, ease: Power4.easeOut });
5458                            });
5459                            if (!$el.hasClass('year-end-2022-videoautoplay__inline')) {
5460                                var vid = player.el().querySelector('video');
5461                                if (vid.currentTime < 3 && $(window).width() < 768) {
5462                                    player.src(player.currentSources());
5463                                }
5464                                vid.controls = false;
5465                                player.play();
5466                            }
5467                            trackEvent("Video", "Visible", videoId);
5468                        },
5469                        onLeave: function (progress, direction, isActive) {
5470                            // video started playing by user interaction
5471                            if (!video.muted) {
5472                                return;
5473                            }
5474                            tween && tween.pause();
5475                            tween = TweenMax.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 0, ease: Power4.easeOut });
5476                            video.pause();
5477                        },
5478                        onLeaveBack: function (progress, direction, isActive) {
5479                            // video started playing by user interaction
5480                            if (!video.muted) {
5481                                return;
5482                            }
5483                            if (direction === "reverse") {
5484                                tween && tween.pause();
5485                                tween = TweenMax.to($el.find('.year-end-2022-videoautoplay'), 0.5, { autoAlpha: 0, ease: Power4.easeOut });
5486                                video.pause();
5487                            }
5488                        }
5489                    },
5490                });
5491            });
5492
5493        });
5494    }
5495
5496
5497    
5498    /**
5499     * Calculates the minimum scale factor to rotate a background without revealing edges.
5500     *
5501     * @param {number} width - The width of the viewport (e.g., screen width).
5502     * @param {number} height - The height of the viewport (e.g., screen height).
5503     * @param {number} angle - The rotation angle in degrees.
5504     * @returns {number} - The minimum scale factor.
5505     */
5506
5507    function addSlamTextInToTimeline(tl, textEl, options) {
5508        if (typeof gsap === 'undefined') {
5509            return tl;
5510        }
5511
5512        options = options || {};
5513        var duration = typeof options.duration === 'number' ? options.duration : 1.1;
5514        var ease = options.ease || 'power2.inOut';
5515        var fallbackDuration = typeof options.fallbackDuration === 'number' ? options.fallbackDuration : 0.5;
5516        var autoSplit = (typeof options.autoSplit === 'boolean') ? options.autoSplit : true;
5517
5518        if (!tl) {
5519            tl = gsap.timeline({ overwrite: true });
5520        }
5521        if (!textEl) {
5522            return tl;
5523        }
5524
5525        var hasSplitText = (typeof SplitText !== 'undefined');
5526
5527        // Split (once) if requested and available.
5528        if (hasSplitText && autoSplit) {
5529            var alreadySplit = textEl.querySelector && (textEl.querySelector('.char') || textEl.querySelector('.line'));
5530            if (!alreadySplit && !textEl._slamSplitText) {
5531                try {
5532                    textEl._slamSplitText = new SplitText(textEl, {
5533                        type: 'lines,chars',
5534                        linesClass: 'line',
5535                        charsClass: 'char'
5536                    });
5537                } catch (e) {
5538                    console.error('SplitText init failed:', e);
5539                    window.sentry && Sentry.captureException(e);
5540                }
5541            }
5542        }
5543
5544        // Fallback if SplitText isn't present / failed.
5545        if (!hasSplitText) {
5546            tl.fromTo(textEl, {
5547                autoAlpha: 0,
5548                y: 30,
5549                scale: 0.98,
5550            }, {
5551                autoAlpha: 1,
5552                y: 0,
5553                scale: 1,
5554                duration: fallbackDuration,
5555                ease: Power3.easeOut,
5556                overwrite: true,
5557            });
5558            return tl;
5559        }
5560
5561        var lines = textEl.querySelectorAll ? textEl.querySelectorAll('.line') : null;
5562        if (!lines || lines.length === 0) {
5563            // SplitText may have produced chars without lines, or not at all.
5564            var charsFallback = textEl.querySelectorAll ? textEl.querySelectorAll('.char') : null;
5565            if (!charsFallback || charsFallback.length === 0) {
5566                tl.fromTo(textEl, {
5567                    autoAlpha: 0,
5568                    y: 30,
5569                }, {
5570                    autoAlpha: 1,
5571                    y: 0,
5572                    duration: fallbackDuration,
5573                    ease: Power3.easeOut,
5574                    overwrite: true,
5575                });
5576                return tl;
5577            }
5578            // Treat as a single “line”.
5579            lines = [{ querySelectorAll: function () { return charsFallback; } }];
5580        }
5581
5582        Array.prototype.forEach.call(lines, function (line) {
5583            var chars = line && line.querySelectorAll ? line.querySelectorAll('.char') : null;
5584            if (!chars || chars.length === 0) {
5585                tl.fromTo(textEl, {
5586                    autoAlpha: 0,
5587                    y: 30,
5588                }, {
5589                    autoAlpha: 1,
5590                    y: 0,
5591                    duration: fallbackDuration,
5592                    ease: Power3.easeOut,
5593                    overwrite: true,
5594                });
5595                return;
5596            }
5597
5598            var charsArr = Array.prototype.slice.call(chars);
5599            var charsTotal = charsArr.length;
5600
5601            charsArr.forEach(function (char) {
5602                if (char && char.parentNode) {
5603                    gsap.set(char.parentNode, { perspective: 1000 });
5604                }
5605            });
5606
5607            tl.fromTo(charsArr, {
5608                willChange: 'transform,opacity',
5609                x: function (position) {
5610                    var center = Math.ceil(charsTotal / 2);
5611                    var factor = position < center ? position : center - Math.abs(Math.floor(charsTotal / 2) - position) - 1;
5612                    var dist = (charsTotal % 2 ? Math.abs(center - 1 - factor) : Math.abs(center - factor)) * 45;
5613                    return dist * (position < charsTotal / 2 ? -1 : 1);
5614                },
5615                y: 12,
5616                rotationY: -180,
5617                autoAlpha: 0,
5618                y: position => {
5619                    const factor = position < Math.ceil(charsTotal / 2) ? position : Math.ceil(charsTotal / 2) - Math.abs(Math.floor(charsTotal / 2) - position) - 1;
5620                    return factor * 3;
5621                },
5622                rotationZ: position => {
5623                    const factor = position < Math.ceil(charsTotal / 2) ? position : Math.ceil(charsTotal / 2) - Math.abs(Math.floor(charsTotal / 2) - position) - 1;
5624                    return position < charsTotal / 2 ? Math.abs(factor - charsTotal / 2) * 8 : -1 * Math.abs(factor - charsTotal / 2) * 8;
5625                }
5626            }, {
5627                x: 0,
5628                y: 0,
5629                rotationY: 0,
5630                rotationZ: 0,
5631                autoAlpha: 1,
5632                duration: duration,
5633                ease: ease,
5634                overwrite: true,
5635            }, options.delay || 0);
5636        });
5637
5638        return tl;
5639    }
5640
5641    function calculateScale(width, height, angle) {
5642        // Convert angle from degrees to radians
5643        const theta = angle * (Math.PI / 180);
5644        
5645        const cosTheta = Math.cos(theta);
5646        const sinTheta = Math.sin(theta);
5647        
5648        // Calculate scale factors for width and height
5649        const scaleX = (width * cosTheta + height * sinTheta) / width;
5650        const scaleY = (width * sinTheta + height * cosTheta) / height;
5651        
5652        // Return the maximum scale factor to ensure full coverage
5653        return Math.max(scaleX, scaleY);
5654    }
5655
5656
5657    // Helper function that lets you dynamically figure out a grid's rows/columns as well as further refine those with "odd" or "even" ones
5658    // https://greensock.com/forums/topic/34808-how-can-i-animate-the-odd-and-even-columns-rows-of-a-grid-with-gsapto/?do=findComment&comment=174346
5659    const getGrid = selector => {
5660        let elements = gsap.utils.toArray(selector),
5661            bounds,
5662            getSubset = (axis, dimension, alternating, merge) => {
5663                let a = [],
5664                    subsets = {},
5665                    onlyEven = alternating === "even",
5666                    p;
5667                bounds.forEach((b, i) => {
5668                    let position = Math.round(b[axis] + b[dimension] / 2),
5669                        subset = subsets[position];
5670                    subset || (subsets[position] = subset = []);
5671                    subset.push(elements[i]);
5672                });
5673                for (p in subsets) {
5674                    a.push(subsets[p]);
5675                }
5676                if (onlyEven || alternating === "odd") {
5677                    a = a.filter((el, i) => !(i % 2) === onlyEven);
5678                }
5679                if (merge) {
5680                    let a2 = [];
5681                    a.forEach(subset => a2.push(...subset));
5682                    return a2;
5683                }
5684                return a;
5685            };
5686        elements.refresh = () => bounds = elements.map(el => el.getBoundingClientRect());
5687        elements.columns = (alternating, merge) => getSubset("left", "width", alternating, merge);
5688        elements.rows = (alternating, merge) => getSubset("top", "height", alternating, merge);
5689        elements.refresh();
5690
5691        return elements;
5692    }
5693
5694    var hasWebP = (function () {
5695        // some small (2x1 px) test images for each feature
5696        var images = {
5697            basic: "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==",
5698            lossless: "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA="
5699        };
5700
5701        return function (feature) {
5702            var deferred = $.Deferred();
5703
5704            $("<img>").on("load", function () {
5705                // the images should have these dimensions
5706                if (this.width === 2 && this.height === 1) {
5707                    deferred.resolve();
5708                } else {
5709                    deferred.reject();
5710                }
5711            }).on("error", function () {
5712                deferred.reject();
5713            }).attr("src", images[feature || "basic"]);
5714
5715            return deferred.promise();
5716        }
5717    })();
5718
5719
5720    return YEAR2026;
5721}())();

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.