PageSourceSearch

https://www.dallasinternationalschool.org/Templates/DallasInternational/js/custom.js?v=1.161.6.13

js dallasinternationalschool.org collected 2026-10-06 19:29:34 UTC 137,634 bytes, 3,368 lines download raw bytes

1/*
2=====================================
3Add website specific custom fucntions
4This JS reference is given to site-script.js
5=====================================
6*/
7
8var $notEditMode = !$('.cms-page-is-in-edit-mode').length;
9var $editMode = $('.cms-page-is-in-edit-mode').length;
10var $notPublishedMode = !$('.cms-page-is-published').length;
11
12var $isHome = $('#home-page').length;
13var $notHome = !$isHome;
14const $window = $(window);
15
16// gsap.registerPlugin(ScrollTrigger);
17
18gsap.registerPlugin(SplitText, ScrollTrigger);
19
20
21$(document).ready(function () {
22    initializeStickyHeader();
23    initializeUnslick();
24    initializeHeroFunctionality();
25    initializeFooterSocialWrapper();
26    initializeImageThumbnailGallery();
27    initializeStackedGallery();
28    // initializeStackedGallerySlider();
29    initializeThemeToggleButton();
30    // initializeCustomSlider();
31    initializeCustomGallerySlider();
32    initializeCustomCardSlider();
33    initializeCustomTabbedSlider();
34    initializeCustomDisplaySlider();
35    initializeInfographicChart();   
36    initializeSiteBookmark();
37    initializeCustomPreloader();
38    initializeLanguageToggle();
39    initializeVideoPopup();
40    initializeSgDivisionFilter(); 
41    initializeStackedDisplaySlider();
42    initializeBBDirectory();
43    initializeContentIntroCheck();
44    initializeVideoControls();
45    removeAriaHiddenFromVideoSlides();
46    // initializeAlternateHomeVersionLoader();
47    initializeSiteClearGridWrapper();
48    initializeAnchorOffset();
49    cloneGalleryButton();
50    utilityLinksClone();
51
52    // blockquoteParallax();
53
54    // initializefedCustomSlider();
55
56
57});
58
59// ================================
60// Site Clear Grid Wrapper
61// ================================
62function initializeSiteClearGridWrapper() {
63    $("#site-main-content-wrapper").append('<div class="site-clear-grid-wrapper"></div>');
64}
65
66// ================================
67// Anchor Offset Function
68// ================================
69function initializeAnchorOffset() {
70    if (!$notEditMode) return;
71    
72    const offset = 75; 
73    
74    function scrollToTarget(targetId) {
75        const element = document.getElementById(targetId);
76        if (element) {
77            const targetPosition = element.getBoundingClientRect().top + window.pageYOffset - offset;
78            $('html, body').animate({ scrollTop: targetPosition }, 800);
79        }
80    }
81    
82    // Handle anchor clicks
83    $(document).on('click', 'a[href*="#"]', function(e) {
84        const href = $(this).attr('href');
85        if (href.startsWith('#') || (href.includes('#') && !href.startsWith('http'))) {
86            e.preventDefault();
87            scrollToTarget(href.split('#')[1]);
88        }
89    });
90    
91    // Handle URL hash on page load
92    if (window.location.hash) {
93        setTimeout(() => scrollToTarget(window.location.hash.substring(1)), 100);
94    }
95}
96
97// ================================
98// Sticky Header
99// ================================
100function initializeStickyHeader() {
101    if ($notEditMode) {
102        let ticking = false;
103        let lastScrollTop = 0;
104        const fsHeaderHeight = 0;
105        const $body = $('body');
106        
107        function updateStickyHeader() {
108            const scroll = window.pageYOffset || document.documentElement.scrollTop;
109            
110            // Handle sticky header activation
111            if (scroll > fsHeaderHeight) {
112                $body.addClass("sticky-header-active");
113            } else {
114                $body.removeClass("sticky-header-active");
115            }
116            
117            // Handle scroll direction for banner visibility
118            if (scroll > lastScrollTop && scroll > 100) {
119                // Scrolling down - hide banner
120                $body.removeClass('scrolled-up');
121            } else if (scroll < lastScrollTop) {
122                // Scrolling up - show banner
123                $body.addClass('scrolled-up');
124            }
125            
126            lastScrollTop = scroll <= 0 ? 0 : scroll; // For Mobile or negative scrolling
127            ticking = false;
128        }
129        
130        function requestTick() {
131            if (!ticking) {
132                requestAnimationFrame(updateStickyHeader);
133                ticking = true;
134            }
135        }
136        
137        // Use passive event listener for better performance
138        window.addEventListener('scroll', requestTick, { passive: true });
139        
140        // Initial check
141        updateStickyHeader();
142    }
143}
144
145
146
147
148// Custom Unslick Features
149// ================================
150function initializeUnslick() {
151    
152    // Check if cms-gallery-feature exists on the page before running unslick
153    if ($('.unslick-wrapper').length) {
154        $(".unslick").each(function () {
155            var $slickWrapper = $(this).find('.site-slick-wrapper');
156            
157            // Check if element exists, has slick function, and is initialized
158            if ($slickWrapper.length && 
159                typeof $slickWrapper.slick === 'function' && 
160                $slickWrapper.hasClass('slick-initialized')) {
161                $slickWrapper.slick('unslick');
162            }
163        });
164    }
165}
166
167// ================================
168// Hero
169// ================================
170function initializeHeroFunctionality() {
171    if ($notEditMode) {
172        const $heroElement = $('#site-hero-wrapper');
173    
174        if (!$($heroElement).hasClass("no-features")) {
175            $('body').addClass('has-hero');
176        }
177
178        const $landingHero = $('.landing-hero-wrapper');
179
180        // Add scroll down button to landing hero
181        if ($landingHero.length) {
182            $landingHero.each(function() {
183                const $scrollDownButton = $('<button class="scroll-down-button">Scroll Down</button>');
184                const $image = $(this).find('.site-slider-image-block').prepend('<div class="lines"><span class="line1"></span><span class="line2"></span><span class="line3"></span></div>');
185                $(this).append($scrollDownButton);
186            });
187        }
188
189        //Scroll down button
190        $(document).on("click", ".scroll-down-button", function (event) {
191            event.preventDefault();
192      
193            // Use modern smooth scrolling with fallback
194            const targetElement = document.getElementById("site-main-grid-block");
195            if (targetElement) {
196                targetElement.scrollIntoView({
197                    behavior: 'smooth',
198                    block: 'start'
199                });
200            } else {
201                // Fallback to jQuery animate if element not found
202                $('html, body').animate({
203                    scrollTop: $("#site-main-grid-block").offset().top
204                }, 800);
205            }
206        });
207
208    }
209}
210
211
212// ================================
213// Footer Social Wrapper
214// ================================
215
216function initializeFooterSocialWrapper() {
217    // Function to move social wrapper based on screen size
218    function moveFooterSocialWrapper() {
219        const socialWrapper = $('.site-footer-social-wrapper');
220        const footerColumn1 = $('#site-footer-column-1');
221        const footerBottom = $('.site-footer-bottom');
222        
223        if (socialWrapper.length && footerColumn1.length && footerBottom.length) {
224            if (window.innerWidth < 1024) {
225                // Move to footer bottom for screens under 1024px
226                if (socialWrapper.parent().is(footerColumn1)) {
227                    socialWrapper.prependTo(footerBottom);
228                }
229            } else {
230                // Move back to footer column 1 for screens 1024px and above
231                if (socialWrapper.parent().is(footerBottom)) {
232                    socialWrapper.appendTo(footerColumn1);
233                }
234            }
235        }
236    }
237    
238    // Initial call
239    moveFooterSocialWrapper();
240    
241    // Call on window resize with debounce
242    let resizeTimer;
243    $(window).on('resize', function() {
244        clearTimeout(resizeTimer);
245        resizeTimer = setTimeout(function() {
246            moveFooterSocialWrapper();
247        }, 250);
248    });
249}
250
251
252
253// ================================
254// Image thumbnail Gallery
255// ================================
256function initializeImageThumbnailGallery() {
257
258    const $imageThumbnailGallery = $('.site-image-gallery-thumbnails-wrapper .site-slick-wrapper');
259
260    // Check if image thumbnail gallery elements exist
261    if ($imageThumbnailGallery.length === 0) {
262        return;
263    }
264
265    if (!$imageThumbnailGallery.hasClass('slick-initialized')) {
266
267        // Declare caption container variable in higher scope
268        let $captionContainer;
269
270        $imageThumbnailGallery.on('init', function(event, slick) {
271
272            // Create a wrapper container for captions and dots
273            const $captionDotsWrapper = $('<div class="site-image-gallery-caption-dots-wrapper"></div>');
274            
275            // Move the dots into the wrapper
276            const $dots = $imageThumbnailGallery.find('.slick-dots');
277            $dots.appendTo($captionDotsWrapper);
278            
279            // Create caption container inside the wrapper
280            $captionContainer = $('<div class="site-image-gallery-caption-container"></div>');
281            $captionDotsWrapper.prepend($captionContainer);
282            
283            // Insert the wrapper after the slick list
284            $imageThumbnailGallery.find('.slick-list').after($captionDotsWrapper);
285
286            var $imageThumbnailGalleryDots = $imageThumbnailGallery.find('.slick-dots li');
287
288            // Batch DOM reads and writes to prevent layout thrashing
289            const slides = $('.site-image-gallery-thumbnails-wrapper .site-slick-wrapper .slick-slide:not(.slick-cloned)');
290            const dotButtons = [];
291            
292            // First, collect all data without DOM writes
293            slides.each(function(i) {
294              var slideImage = $(this).find('img').attr('src');
295              var slideCaption = $(this).find('.site-slider-caption').html();
296              
297              // Store dot button for later batch update
298              const $dotButton = $imageThumbnailGalleryDots.eq(i).find('button');
299              if ($dotButton.length && slideImage) {
300                  dotButtons.push({ button: $dotButton, image: slideImage });
301              }
302              
303              // Create caption elements for the synced slider
304              if (slideCaption) {
305                  var $captionSlide = $('<div class="caption-slide" data-slide-index="' + i + '">' + slideCaption + '</div>');
306                  $captionContainer.append($captionSlide);
307              }
308            });
309            
310            // Batch all background-image updates in a single operation
311            if (dotButtons.length > 0) {
312                requestAnimationFrame(function() {
313                    dotButtons.forEach(function(item) {
314                        item.button.css('background-image', 'url(' + item.image + ')');
315                    });
316                });
317            }
318            
319            // Show first caption initially
320            $captionContainer.find('.caption-slide').first().addClass('active');
321            
322            // Calculate and set min-height based on the tallest caption slide
323            // Use requestAnimationFrame to defer calculation and batch DOM reads/writes
324            // This prevents layout thrashing that causes scroll lag on macOS
325            requestAnimationFrame(function() {
326                let maxHeight = 0;
327                const $captionSlides = $captionContainer.find('.caption-slide');
328                
329                // Batch all DOM reads: first show all slides, then measure
330                $captionSlides.removeClass('active');
331                $captionSlides.addClass('active');
332                
333                // Force a single layout calculation by reading all heights at once
334                $captionSlides.each(function() {
335                    const slideHeight = $(this).outerHeight();
336                    if (slideHeight > maxHeight) {
337                        maxHeight = slideHeight;
338                    }
339                });
340                
341                // Batch DOM writes: hide all except first, then set min-height
342                $captionSlides.removeClass('active');
343                $captionContainer.find('.caption-slide').first().addClass('active');
344                
345                // Set the min-height on the caption container
346                if (maxHeight > 0) {
347                    $captionContainer.css('min-height', maxHeight + 'px');
348                }
349            });
350           
351        });
352        
353        // Handle slide changes to sync captions
354        $imageThumbnailGallery.on('afterChange', function(event, slick, currentSlide) {
355            if ($captionContainer) {
356                $captionContainer.find('.caption-slide').removeClass('active');
357                $captionContainer.find('.caption-slide[data-slide-index="' + currentSlide + '"]').addClass('active');
358            }
359        });
360        
361        // Handle dot clicks to sync captions
362        $imageThumbnailGallery.on('click', '.slick-dots li button', function() {
363            if ($captionContainer) {
364                var slideIndex = $(this).parent().index();
365                $captionContainer.find('.caption-slide').removeClass('active');
366                $captionContainer.find('.caption-slide[data-slide-index="' + slideIndex + '"]').addClass('active');
367            }
368        });
369               
370        $imageThumbnailGallery.slick({
371            infinite: false,
372            slidesToShow: 1,
373            slidesToScroll: 1,
374            arrows: false,
375            dots: true,
376            fade: true,
377        
378        });
379    }
380    
381}
382
383
384/*
385=====================================
386Stacked Gallery
387=====================================
388*/
389function initializeStackedGallery() {
390    //if ($notEditMode) {
391        // Only run on screen sizes 1024px and upwards
392        if (window.innerWidth >= 1024 && $('.site-row-block-stacked-cards-container').length) {
393
394            $( ".site-row-block-stacked-cards-container" ).wrapInner( "<div class='site-row-block-stacked-cards-inner'></div>");
395
396            // Check if site-row-block-stacked-top exists and prepend to inner container
397            const $stackedTop = $('.site-row-block-stacked-top');
398            if ($stackedTop.length) {
399                $stackedTop.prependTo('.site-row-block-stacked-cards-inner');
400            }
401
402            $('.site-stacked-gallery-wrapper').each(function() {
403                const $container = $(this);
404                const $slickWrapper = $container.find('.site-slick-wrapper');
405                
406                // Create caption container and append to .site-row-block-stacked-cards-container
407                const $captionContainer = $('<div class="site-stacked-gallery-caption-container"><div class="site-stacked-gallery-caption-inner"></div></div>');
408                $container.closest('.site-row-block-stacked-cards-container .site-row-block-stacked-cards-inner').append($captionContainer);
409                
410                // Loop through and find site-slider-caption and append each to the new caption container
411                $slickWrapper.find('.site-slide').each(function(index) {
412                    const $captionBlock = $(this).find('.site-slider-caption');
413                    if ($captionBlock.length) {
414                        const $captionElement = $('<div class="caption-element" data-slide-index="' + index + '">' + $captionBlock.html() + '</div>');
415                        $captionContainer.find('.site-stacked-gallery-caption-inner').append($captionElement);
416                    }
417                });
418                
419                // Set the first slide as active by default
420                const $firstSlide = $slickWrapper.find('.site-slide').first();
421                const $firstCaption = $captionContainer.find('.caption-element').first();
422                $firstSlide.addClass('active');
423                $firstCaption.addClass('active');
424                
425                // Add hover functionality to caption elements
426                $captionContainer.find('.caption-element').on('mouseenter', function() {
427                    const slideIndex = $(this).data('slide-index');
428                    const $slides = $slickWrapper.find('.site-slide');
429                    
430                    // Clear any existing timeouts to prevent conflicts
431                    if (window.stackedGalleryTimeout) {
432                        clearTimeout(window.stackedGalleryTimeout);
433                    }
434                    
435                    // Remove active class from all captions and slides
436                    $captionContainer.find('.caption-element').removeClass('active');
437                    $slides.removeClass('active');
438                    
439                    // Add active class to current caption and corresponding slide
440                    $(this).addClass('active');
441                    $slides.eq(slideIndex).addClass('active');
442                });
443                
444                // Reset to default state when mouse leaves caption
445                $captionContainer.find('.caption-element').on('mouseleave', function() {
446                    const $slides = $slickWrapper.find('.site-slide');
447                    const $firstSlide = $slides.first();
448                    const $firstCaption = $captionContainer.find('.caption-element').first();
449                    
450                    // Remove active class from caption immediately
451                    $captionContainer.find('.caption-element').removeClass('active');
452                    
453                    // Add a small delay before removing slide active class for smoother transition
454                    window.stackedGalleryTimeout = setTimeout(function() {
455                        $slides.removeClass('active');
456                        
457                        // Return active state to first slide and caption
458                        $firstSlide.addClass('active');
459                        $firstCaption.addClass('active');
460                    }, 200);
461                });
462            });
463        }
464    //}
465}
466
467/*
468=====================================
469Stacked Gallery Slider version not in use
470=====================================
471*/
472// function initializeStackedGallerySlider() {
473
474//     if ($('.site-row-block-stacked-cards-slider-container').length) {
475
476//         //$( ".site-row-block-stacked-cards-slider-container" ).wrapInner( "<div class='site-row-block-stacked-cards-slider-inner'></div>");
477
478//         const $stackedGallerySliderContainer = $('.site-stacked-gallery-slider-wrapper');
479//         const $customStackedGallerySlider = $stackedGallerySliderContainer.find('.site-slick-wrapper');
480      
481
482//         const $customSliderArrows = $('<div class="stacked-slider-arrow-container"><button class="slick-arrow slick-prev">Previous</button><button class="slick-arrow slick-next">Next</button></div>');
483//         $customSliderArrows.appendTo($stackedGallerySliderContainer);
484
485//         // Create caption container and append to .site-row-block-stacked-cards-container
486//         const $sliderCaptionContainer = $('<div class="site-stacked-display-caption-container"></div>');
487     
488//         $sliderCaptionContainer.appendTo($stackedGallerySliderContainer);
489        
490//         // Loop through and find site-slider-caption and append each to the new caption container
491//         $customStackedGallerySlider.find('.site-slide').each(function(index) {
492//             const $captionBlock = $(this).find('.site-slider-caption');
493//             if ($captionBlock.length) {
494//                 const $captionElement = $('<div class="caption-element" data-slide-index="' + index + '">' + $captionBlock.html() + '</div>');
495//                 $sliderCaptionContainer.append($captionElement);
496//             }
497//         });
498
499       
500
501//         if ($customStackedGallerySlider.length) {
502
503//             $customStackedGallerySlider.each(function() {
504//                 var $stackedSlider = $(this);
505            
506//                 $stackedSlider.slick({
507//                     slidesToShow: 1,
508//                     slidesToScroll: 1,
509//                     dots: false,
510//                     arrows: true,
511//                     infinite: true,
512//                     fade: true,
513//                     prevArrow: $stackedGallerySliderContainer.find('.stacked-slider-arrow-container .slick-prev'),
514//                     nextArrow: $stackedGallerySliderContainer.find('.stacked-slider-arrow-container .slick-next'),
515//                     asNavFor: $sliderCaptionContainer,
516//                 });
517
518//                 // Initialize synchronized caption slider
519              
520//                 $sliderCaptionContainer.slick({
521//                     slidesToShow: 1,
522//                     slidesToScroll: 1,
523//                     dots: false,
524//                     arrows: false,
525//                     infinite: true,
526//                     fade: true,
527//                     speed: 600,
528//                     asNavFor: $stackedSlider,
529//                 });
530                
531                
532//             });
533                
534//         }
535//     }
536// }
537
538
539/*
540=====================================
541Stacked Gallery Slider version
542=====================================
543*/
544function initializeStackedDisplaySlider() {
545
546    if ($('.site-row-block-stacked-cards-display-container').length) {
547
548        $( ".site-row-block-stacked-cards-display-container" ).wrapInner( "<div class='site-row-block-stacked-cards-slider-inner'></div>");
549
550        const $stackedDisplaySliderContainer = $('.site-display-stacked-cards-wrapper');
551        const $customStackedDisplaySlider = $stackedDisplaySliderContainer.find('.site-display-list');
552      
553
554         // Create caption container and append to .site-row-block-stacked-cards-container
555        const $sliderCaptionContainer = $('<div class="site-stacked-slider-caption-container"></div>');
556     
557        $sliderCaptionContainer.appendTo($stackedDisplaySliderContainer);
558        
559        const $displayCustomSliderArrows = $('<div class="stacked-slider-arrow-container"><button class="slick-arrow slick-prev">Previous</button><button class="slick-arrow slick-next">Next</button></div>');
560        $displayCustomSliderArrows.appendTo($stackedDisplaySliderContainer);
561        
562        // Create separate mobile arrows container (just arrows, no dots)
563        const $displayCustomSliderArrowsMobile = $('<div class="mobile-stacked-arrow-container"><button class="slick-arrow slick-prev">Previous</button><button class="slick-arrow slick-next">Next</button></div>');
564
565       
566        // Loop through and find site-slider-caption and append each to the new caption container
567        $customStackedDisplaySlider.find('.site-display-wrapper-listitem').each(function(index) {
568            const $captionBlock = $(this).find('.site-display-data-block');
569            if ($captionBlock.length) {
570                const $captionElement = $('<div class="caption-element" data-slide-index="' + index + '"><div class="caption-element-inner">' + $captionBlock.html() + '</div></div>');
571                $sliderCaptionContainer.append($captionElement);
572                
573                // Now find and wrap the bookmark button inside the caption element
574                const $displayBookmarkButtons = $captionElement.find('.site-bookmark-button');
575                $displayBookmarkButtons.each(function() {
576                    const $displayBookmarkButton = $(this);
577                    
578                    // Only process if not already processed (check for data attribute)
579                    if (!$displayBookmarkButton.data('collection-text-added')) {
580                        // Only wrap if not already wrapped
581                        if (!$displayBookmarkButton.find('.site-bookmark-button-inner').length) {
582                            $displayBookmarkButton.wrapInner('<div class="site-bookmark-button-inner"></div>');
583                        }
584                        
585                        // Update bookmark button text with subtext based on button class
586                        const $innerDiv = $displayBookmarkButton.find('.site-bookmark-button-inner');
587                        if ($innerDiv.length) {
588                            if ($displayBookmarkButton.hasClass("site-bookmark-display-button-add")) {
589                                $innerDiv.html('Add <small >to my collection</small>');
590                            } else if ($displayBookmarkButton.hasClass("site-bookmark-display-button-remove")) {
591                                $innerDiv.html('Remove <small>from my collection</small>');
592                            } else {
593                                // Default to add if no specific class is found
594                                $innerDiv.html('Add <small>to my collection</small>');
595                            }
596                            
597                            // Mark as processed to prevent duplicate processing
598                            $displayBookmarkButton.data('collection-text-added', true);
599                        }
600                    }
601                });
602            }
603        });
604
605       
606        if ($customStackedDisplaySlider.length) {
607
608            $customStackedDisplaySlider.each(function() {
609                var $stackedSlider = $(this);
610            
611                $stackedSlider.slick({
612                    slidesToShow: 1,
613                    slidesToScroll: 1,
614                    dots: false,
615                    arrows: true,
616                    infinite: false,
617                    fade: true,
618                    prevArrow: $stackedDisplaySliderContainer.find('.stacked-slider-arrow-container .slick-prev'),
619                    nextArrow: $stackedDisplaySliderContainer.find('.stacked-slider-arrow-container .slick-next'),
620                    asNavFor: $sliderCaptionContainer,
621                });
622
623                
624               // Initialize synchronized caption slider
625                $sliderCaptionContainer.slick({
626                    slidesToShow: 1,
627                    slidesToScroll: 1,
628                    dots: false,
629                    arrows: false,
630                    infinite: false,
631                    fade: true,
632                    speed: 600,
633                    asNavFor: $stackedSlider,
634                    responsive: [
635                        {
636                          breakpoint: 1024,
637                          settings: {
638                            slidesToShow: 1,
639                            slidesToScroll: 1,
640                            fade: false,
641                            adaptiveHeight: true,
642                          }
643                        }
644                   
645                      ]
646                });
647                
648                // Add mobile arrows to the caption container 
649                var $mobileArrows = $displayCustomSliderArrowsMobile.clone();
650                $mobileArrows.addClass('mobile-stacked-slide-arrows');
651                $sliderCaptionContainer.prepend($mobileArrows);
652                
653                // Set up click handlers for mobile arrows
654                $mobileArrows.find('.slick-prev').on('click', function() {
655                    $stackedSlider.slick('slickPrev');
656                });
657                
658                $mobileArrows.find('.slick-next').on('click', function() {
659                    $stackedSlider.slick('slickNext');
660                });
661                
662                //Function to update disabled state on mobile arrows
663                function updateMobileArrowsDisabledState() {
664                    var currentSlide = $stackedSlider.slick('slickCurrentSlide');
665                    var slideCount = $stackedSlider.slick('getSlick').slideCount;
666                    
667                    // Get the mobile arrow buttons
668                    var $mobilePrevButton = $mobileArrows.find('.slick-prev');
669                    var $mobileNextButton = $mobileArrows.find('.slick-next');
670                    
671                    // Remove disabled class first
672                    $mobilePrevButton.removeClass('slick-disabled');
673                    $mobileNextButton.removeClass('slick-disabled');
674                    
675                    // Add disabled class based on current position
676                    if (currentSlide === 0) {
677                        // On first slide - disable prev button
678                        $mobilePrevButton.addClass('slick-disabled');
679                    }
680                    
681                    if (currentSlide >= slideCount - 1) {
682                        // On last slide - disable next button
683                        $mobileNextButton.addClass('slick-disabled');
684                    }
685                }
686                
687                // Update disabled state on init
688                $sliderCaptionContainer.on('init reInit', function(event, slick) {
689                    updateMobileArrowsDisabledState();
690                });
691                
692                // Update disabled state on slide change
693                $stackedSlider.on('afterChange', function(event, slick, currentSlide) {
694                    updateMobileArrowsDisabledState();
695                });
696                
697                // Initial update
698                setTimeout(updateMobileArrowsDisabledState, 100);
699                
700                // Handle resize to reinitialize sliders when crossing breakpoint
701                var lastWidth = $(window).width();
702                var resizeTimer;
703                
704                $(window).on('resize', function() {
705                    clearTimeout(resizeTimer);
706                    resizeTimer = setTimeout(function() {
707                        var currentWidth = $(window).width();
708                        
709                        // Check if we crossed the 1024px breakpoint
710                        if ((lastWidth >= 1024 && currentWidth < 1024) || (lastWidth < 1024 && currentWidth >= 1024)) {
711                            
712                            try {
713                                // Save current slide index
714                                var currentSlideIndex = $stackedSlider.slick('slickCurrentSlide');
715                                
716                                // Destroy both sliders
717                                $sliderCaptionContainer.slick('unslick');
718                                $stackedSlider.slick('unslick');
719                                
720                                // Remove old mobile arrows
721                                $mobileArrows.remove();
722                                
723                                // Reinitialize main slider
724                                $stackedSlider.slick({
725                                    slidesToShow: 1,
726                                    slidesToScroll: 1,
727                                    dots: false,
728                                    arrows: true,
729                                    infinite: false,
730                                    fade: true,
731                                    initialSlide: currentSlideIndex,
732                                    prevArrow: $stackedDisplaySliderContainer.find('.stacked-slider-arrow-container .slick-prev'),
733                                    nextArrow: $stackedDisplaySliderContainer.find('.stacked-slider-arrow-container .slick-next'),
734                                    asNavFor: $sliderCaptionContainer,
735                                });
736                                
737                                // Reinitialize caption slider
738                                $sliderCaptionContainer.slick({
739                                    slidesToShow: 1,
740                                    slidesToScroll: 1,
741                                    dots: false,
742                                    arrows: false,
743                                    infinite: false,
744                                    fade: true,
745                                    speed: 600,
746                                    initialSlide: currentSlideIndex,
747                                    asNavFor: $stackedSlider,
748                                    responsive: [
749                                        {
750                                          breakpoint: 1024,
751                                          settings: {
752                                            slidesToShow: 1,
753                                            slidesToScroll: 1,
754                                            fade: false,
755                                            adaptiveHeight: true,
756                                          }
757                                        }
758                                    ]
759                                });
760                                
761                                // Re-add mobile arrows
762                                $mobileArrows = $displayCustomSliderArrowsMobile.clone();
763                                $mobileArrows.addClass('mobile-stacked-slide-arrows');
764                                $sliderCaptionContainer.prepend($mobileArrows);
765                                
766                                // Re-attach click handlers
767                                $mobileArrows.find('.slick-prev').on('click', function() {
768                                    $stackedSlider.slick('slickPrev');
769                                });
770                                
771                                $mobileArrows.find('.slick-next').on('click', function() {
772                                    $stackedSlider.slick('slickNext');
773                                });
774                                
775                                // Update disabled state
776                                setTimeout(updateMobileArrowsDisabledState, 100);
777                                
778                            } catch(e) {
779                                console.error('Error reinitializing sliders:', e);
780                            }
781                        }
782                        
783                        lastWidth = currentWidth;
784                    }, 300);
785                });
786                
787               
788            });
789                
790        }
791    }
792}
793
794/*
795=====================================
796Theme Toggle Button
797=====================================
798*/
799function initializeThemeToggleButton() {
800    if ($notEditMode) {
801        // Check if we're on home or landing page
802        const isHomePage = $('.home-page').length > 0;
803        const isLandingPage = $('.landing-page').length > 0;
804        
805        if (isHomePage || isLandingPage) {
806            const $heroWrapper = $('#site-hero-wrapper');
807            
808            if ($heroWrapper.length) {
809                // Create toggle button container
810                const $toggleContainer = $('<div class="theme-toggle-container"></div>');
811                
812                // Create toggle button
813                const $toggleButton = $('<button class="theme-toggle-button" title="Toggle Theme"></button>');
814                
815                // Append button to container, then container to hero wrapper
816                $toggleContainer.append($toggleButton);
817                $heroWrapper.append($toggleContainer);
818                
819                // Add click event to toggle body class
820                $toggleButton.on('click', function(e) {
821                    e.preventDefault(); // Prevent any default button behavior
822                    
823                    const $body = $('body');
824                    const $button = $(this);
825                    
826                    // Add a small delay to ensure smooth transition
827                    if ($body.hasClass('custom-primary-theme')) {
828                        $body.removeClass('custom-primary-theme');
829                        // $button.text('🌙').attr('title', 'Toggle Theme');
830                    } else {
831                        $body.addClass('custom-primary-theme');
832                        // $button.text('☀️').attr('title', 'Toggle Theme');
833                    }
834                });
835            }
836        }
837    }
838
839}
840
841
842/*
843=====================================
844Custom Slider using Swiper.js 
845=====================================
846*/
847// function initializeCustomSlider() {
848
849//     const $customSliderContainer = $('.site-gallery-slider-wrapper');
850//     const $customSlider = $customSliderContainer.find('.site-slick-wrapper');
851
852//     // Check if custom slider elements exist
853//     if ($customSlider.length === 0) {
854//         return;
855//     }
856
857//     $customSlider.each(function() {
858
859//         var $customSliderItem = $(this);
860        
861//         // Check if Swiper is already initialized on this element
862//         if ($customSliderItem.find('.swiper-container').hasClass('swiper-initialized')) {
863            
864//             return;
865//         }
866        
867//         // First, destroy Slick slider if it exists
868//         if ($customSliderItem.hasClass('slick-initialized')) {
869            
870//             $customSliderItem.slick('unslick');
871//         }
872        
873//         // Remove any Slick-specific classes and data
874//         $customSliderItem.removeClass('slick-initialized slick-slider');
875//         $customSliderItem.find('.slick-slide').removeClass('slick-slide slick-active slick-center slick-current slick-cloned');
876//         $customSliderItem.find('.slick-list, .slick-track').remove();
877        
878//         // Create a completely new Swiper structure
879//         const $swiperContainer = $('<div class="swiper-container"></div>');
880//         const $swiperWrapper = $('<div class="swiper-wrapper"></div>');
881        
882//         // Move all slides to the new Swiper structure
883//         const slides = $customSliderItem.find('.site-slide');
884        
885//         // Clone slides to ensure enough slides for smooth looping
886//         const totalSlides = slides.length;
887//         const minSlidesForLoop = 8; // Minimum slides needed for smooth loop
888        
889//         // If we have fewer slides than needed for smooth looping, clone them
890//         if (totalSlides < minSlidesForLoop) {
891//             const clonesNeeded = minSlidesForLoop - totalSlides;
892            
893//             // Add original slides
894//             slides.each(function(index) {
895//                 var $slide = $(this).clone();
896//                 $slide.addClass('swiper-slide');
897//                 $slide.attr('data-swiper-slide-index', index);
898//                 $swiperWrapper.append($slide);
899//             });
900            
901//             // Add cloned slides to reach minimum count
902//             for (let i = 0; i < clonesNeeded; i++) {
903//                 const originalIndex = i % totalSlides;
904//                 const $originalSlide = slides.eq(originalIndex);
905//                 var $clonedSlide = $originalSlide.clone();
906//                 $clonedSlide.addClass('swiper-slide swiper-slide-cloned');
907//                 $clonedSlide.attr('data-swiper-slide-index', totalSlides + i);
908//                 $swiperWrapper.append($clonedSlide);
909//             }
910//         } else {
911//             // If we have enough slides, just add them normally
912//             slides.each(function(index) {
913//                 var $slide = $(this).clone();
914//                 $slide.addClass('swiper-slide');
915//                 $slide.attr('data-swiper-slide-index', index);
916//                 $swiperWrapper.append($slide);
917//             });
918//         }
919        
920//         // Replace the old structure with the new Swiper structure
921//         $swiperContainer.append($swiperWrapper);
922//         $customSliderItem.empty().append($swiperContainer);
923        
924//         // Add navigation arrows to the slider container
925//         const $navigationContainer = $('<div class="swiper-navigation"></div>');
926//         const $prevButton = $('<div class="swiper-button-prev"></div>');
927//         const $nextButton = $('<div class="swiper-button-next"></div>');
928//         $navigationContainer.append($prevButton, $nextButton);
929        
930//         $customSliderContainer.append($navigationContainer);
931    
932//         // Initialize Swiper after ensuring Slick is destroyed
933//         // Use the specific swiper container for this slider, not document.querySelector
934//         const swiperElement = $swiperContainer[0];
935            
936//         // Check the structure
937//         const swiperWrapper = swiperElement.querySelector('.swiper-wrapper');
938//         const swiperSlides = swiperWrapper.querySelectorAll('.swiper-slide');
939        
940//         // Only initialize if we have slides
941//         if (swiperSlides.length > 0) {
942//             // const swiper = new Swiper(swiperElement, {
943//             //     slidesPerView: 'auto',
944//             //     spaceBetween: 32,
945//             //     loop: true,
946//             //     // autoplay: {
947//             //     //     delay: 3000,
948//             //     //     disableOnInteraction: false,
949//             //     //     pauseOnMouseEnter: false,
950//             //     // },
951//             //     centeredSlides: true,
952//             //     speed: 1000,
953//             //     effect: 'slide',
954//             //     navigation: {
955//             //         nextEl: '.swiper-button-next',
956//             //         prevEl: '.swiper-button-prev',
957//             //     },
958//             // });
959
960//             const swiper = new Swiper($swiperContainer[0], {
961//                 slidesPerView: 'auto',
962//                 spaceBetween: 32,
963//                 loop: true,
964//                 centeredSlides: true,
965//                 speed: 1000,
966//                 loopedSlides: slides.length, 
967//                 slidesOffsetBefore: 340, 
968//                 navigation: {
969//                     nextEl: '.swiper-button-next',
970//                     prevEl: '.swiper-button-prev',
971//                 },
972//             });
973            
974//             // Start autoplay only if swiper and autoplay are properly initialized
975//             // if (swiper && swiper.autoplay) {
976//             //     swiper.autoplay.start();
977//             // }
978//         }
979        
980//     });
981// }
982
983
984   
985/*
986=====================================
987Custom Sliders Gallery
988=====================================
989*/
990function initializeCustomGallerySlider() {
991
992    const $customGallerySliderContainer = $('.custom-gallery-slider-wrapper');
993    const $customGallerySlider = $customGallerySliderContainer.find('.site-slick-wrapper');
994
995    // Check if custom gallery slider elements exist
996    if ($customGallerySlider.length === 0) {
997        return;
998    }
999
1000    const $customSliderArrows = $('<div class="slider-arrow-container"><button class="slick-arrow slick-prev">Previous</button><button class="slick-arrow slick-next">Next</button></div>');
1001    $customSliderArrows.appendTo($customGallerySliderContainer);
1002    
1003    // Create separate mobile arrows container (just arrows, no dots)
1004    const $customSliderArrowsMobile = $('<div class="mobile-arrow-container"><button class="slick-arrow slick-prev">Previous</button><button class="slick-arrow slick-next">Next</button></div>');
1005
1006    $customGallerySlider.each(function() {
1007        var $gallerySliderItem = $(this);
1008        var $gallerySliderPanel = $gallerySliderItem.closest('.custom-gallery-slider-wrapper');
1009        
1010        // Extract caption text for custom dots
1011        var $slides = $gallerySliderItem.find('.site-slide');
1012        var customDots = [];
1013        
1014        
1015        $slides.each(function(index) {
1016            var $caption = $(this).find('.site-slider-caption .site-container .site-slider-caption-data h4');
1017            
1018            // Try alternative selectors if the first one doesn't work
1019            if (!$caption.length) {
1020                $caption = $(this).find('.site-slider-caption h4');
1021            }
1022            
1023            var captionText = $caption.length ? $caption.text().trim() : 'Slide ' + (index + 1);
1024            customDots.push(captionText);
1025        });
1026        
1027        
1028        $gallerySliderItem.slick({
1029            slidesToShow: 1,
1030            slidesToScroll: 1,
1031            dots: true,
1032            arrows: true,
1033            infinite: true,
1034            fade: true,
1035            prevArrow: $gallerySliderPanel.find('.slider-arrow-container .slick-prev'),
1036            nextArrow: $gallerySliderPanel.find('.slider-arrow-container .slick-next'),
1037            customPaging: function(slider, i) {
1038                return '<button type="button" class="custom-dot" data-sli
1038de="' + i + '">' + customDots[i] + '</button>';
1039            },
1040            responsive: [
1041                {
1042                  breakpoint: 1024,
1043                  settings: {
1044                    slidesToShow: 1,
1045                    slidesToScroll: 1,
1046                    adaptiveHeight: true,
1047                  }
1048                }
1049              ]
1050        });
1051        
1052        // Move the slick dots into the slider-arrow-container
1053        var $dotsContainer = $gallerySliderPanel.find('.slick-dots');
1054        var $arrowContainer = $gallerySliderPanel.find('.slider-arrow-container');
1055        
1056        // if ($dotsContainer.length && $arrowContainer.length) {
1057        //     $dotsContainer.prependTo($arrowContainer);
1058            
1059        //     // Add custom styling for the custom dots
1060        //     $dotsContainer.addClass('custom-dots-container');
1061        // }
1062
1063        $dotsContainer.prependTo($gallerySliderPanel);
1064         // Add custom styling for the custom dots
1065        $dotsContainer.addClass('custom-dots-container');
1066        
1067        // Simple mobile arrow setup - clone mobile arrows under each slide image
1068        $slides.each(function() {
1069            var $slide = $(this);
1070            var $imageBlock = $slide.find('.site-slider-image-block');
1071            
1072            if ($imageBlock.length) {
1073                // Clone the mobile arrows (no dots) and add them under the image
1074                var $mobileArrows = $customSliderArrowsMobile.clone();
1075                $mobileArrows.addClass('mobile-slide-arrows');
1076                $mobileArrows.insertAfter($imageBlock);
1077                
1078                // Add click handlers to make mobile arrows work
1079                $mobileArrows.find('.slick-prev').on('click', function() {
1080                    $gallerySliderItem.slick('slickPrev');
1081                });
1082                
1083                $mobileArrows.find('.slick-next').on('click', function() {
1084                    $gallerySliderItem.slick('slickNext');
1085                });
1086            }
1087        });
1088        
1089    });
1090            
1091}
1092
1093/*
1094=====================================
1095Custom Card Slider 
1096=====================================
1097*/
1098function initializeCustomCardSlider() {
1099
1100    const $customCardSliderContainer = $('.site-card-slider-wrapper');
1101    const $customCardSlider = $customCardSliderContainer.find('.site-slick-wrapper');
1102
1103    // Check if custom card slider elements exist
1104    if ($customCardSlider.length === 0) {
1105        return;
1106    }
1107
1108    const $customCardSliderArrows = $('<div class="slider-arrow-container"><button class="slick-arrow slick-prev">Previous</button><button class="slick-arrow slick-next">Next</button></div>');
1109    $customCardSliderArrows.appendTo($customCardSliderContainer);
1110      
1111    $customCardSlider.each(function() {
1112        var $slider = $(this);
1113        
1114        // var $sliderPanel = $slider.closest('.custom-slider-wrapper');
1115        $slider.slick({
1116            slidesToShow: 1,
1117            slidesToScroll: 1,
1118            dots: false,
1119            arrows: true,
1120            infinite: true,
1121            centerMode: true,
1122            centerPadding: '0px',
1123            variableWidth: true,
1124            prevArrow: $customCardSliderContainer.find('.slider-arrow-container .slick-prev'),
1125            nextArrow: $customCardSliderContainer.find('.slider-arrow-container .slick-next'),
1126    
1127        });
1128
1129        //Add custom class to next or previous slide depending on change direction
1130        $slider.on('beforeChange', (event, slick, currentSlide, nextSlide) => {
1131            let nextIndex = currentSlide + 1; // assume moving right
1132        
1133            if ((currentSlide > nextSlide && (nextSlide !== 0 || currentSlide === 1)) || (currentSlide === 0 && nextSlide === slick.slideCount - 1)) {
1134                // console.log('previous');
1135                nextIndex = currentSlide - 1; // nope, moving left
1136            }
1137        
1138            $(`[data-slick-index="${nextIndex}"]`).addClass('slick-target');
1139        });
1140
1141        // clear custom class after transition
1142        $slider.on('afterChange', () => {
1143            $('.slick-slide').removeClass('slick-target');
1144        });
1145
1146    
1147    });
1148
1149      
1150}
1151    
1152
1153
1154/*
1155=====================================
1156Custom Tabbed Gallery Slider
1157=====================================
1158*/
1159
1160function initializeCustomTabbedSlider() {
1161
1162    const $customSliderContainer = $('.custom-tabbed-autoscroll-gallery-wrapper');
1163    const $customSlider = $customSliderContainer.find('.site-slick-wrapper');
1164
1165    if ($customSlider.length === 0) return;
1166
1167    $customSlider.each(function () {
1168        const $slider = $(this);
1169
1170        // Destroy Slick if initialized
1171        if ($slider.hasClass('slick-initialized')) {
1172            $slider.slick('unslick');
1173        }
1174        $slider.removeClass('slick-initialized slick-slider');
1175        $slider.find('.slick-slide').removeClass('slick-slide slick-active slick-center slick-current slick-cloned');
1176        $slider.find('.slick-list, .slick-track').remove();
1177
1178        // Clone slides to ensure enough slides for smooth looping
1179        let slides = $slider.find('.site-slide');
1180        const totalSlides = slides.length;
1181        const minSlidesForLoop = 8; // Minimum slides needed for smooth loop
1182        
1183        // If we have fewer slides than needed for smooth looping, clone them
1184        if (totalSlides < minSlidesForLoop) {
1185            const clonesNeeded = minSlidesForLoop - totalSlides;
1186            
1187            // Add cloned slides to reach minimum count
1188            for (let i = 0; i < clonesNeeded; i++) {
1189                const originalIndex = i % totalSlides;
1190                const $originalSlide = slides.eq(originalIndex);
1191                const $clonedSlide = $originalSlide.clone();
1192                $clonedSlide.addClass('swiper-slide-cloned');
1193                $slider.append($clonedSlide);
1194            }
1195            slides = $slider.find('.site-slide'); // refresh after cloning
1196        }
1197
1198        // Build main Swiper structure
1199        const $swiperContainer = $('<div class="swiper-container"></div>');
1200        const $swiperWrapper = $('<div class="swiper-wrapper"></div>');
1201
1202        slides.each(function (index) {
1203            const $slide = $(this).clone();
1204            $slide.addClass('swiper-slide');
1205            $slide.attr('data-swiper-slide-index', index);
1206            $swiperWrapper.append($slide);
1207        });
1208
1209        $swiperContainer.append($swiperWrapper);
1210        $slider.empty().append($swiperContainer);
1211        
1212        // Add navigation arrows to the main container
1213        const $navigationContainer = $('<div class="swiper-navigation"></div>');
1214        const $prevButton = $('<div class="swiper-button-prev"></div>');
1215        const $nextButton = $('<div class="swiper-button-next"></div>');
1216        $navigationContainer.append($prevButton, $nextButton);
1217        
1218        $customSliderContainer.find('.custom-tabbed-autoscroll-gallery').append($navigationContainer);
1219
1220        // Build caption Swiper
1221        const $captionContainer = $('<
1221div class="custom-tabbed-slider-caption-container"></div>');
1222        const $captionSwiperContainer = $('<div class="swiper-container caption-swiper"></div>');
1223        const $captionSwiperWrapper = $('<div class="swiper-wrapper"></div>');
1224
1225        slides.each(function (index) {
1226            const $captionBlock = $(this).find('.site-slider-caption');
1227            const $captionSlide = $('<div class="swiper-slide caption-slide"></div>');
1228
1229            if ($captionBlock.length) {
1230                $captionSlide.append($captionBlock.clone());
1231            } else {
1232                $captionSlide.html('<div class="empty-caption"></div>');
1233            }
1234            $captionSwiperWrapper.append($captionSlide);
1235        });
1236
1237        $captionSwiperContainer.append($captionSwiperWrapper);
1238        $captionContainer.append($captionSwiperContainer);
1239        $customSliderContainer.append($captionContainer);
1240
1241        // Initialize main Swiper
1242        const mainSwiper = new Swiper($swiperContainer[0], {
1243            slidesPerView: 'auto',
1244            spaceBetween: 32,
1245            loop: true,
1246            centeredSlides: true,
1247            speed: 1000,
1248            loopedSlides: slides.length, 
1249            slidesOffsetBefore: window.innerWidth < 1024 ? 0 : 340, 
1250            navigation: {
1251                nextEl: '.swiper-button-next',
1252                prevEl: '.swiper-button-prev',
1253            },
1254            autoplay: {
1255                delay: 10000, // 10 seconds
1256                disableOnInteraction: false,
1257                pauseOnMouseEnter: true,
1258            },
1259        });
1260
1261    
1262        // Initialize caption Swiper with fade effect
1263        const captionSwiper = new Swiper($captionSwiperContainer[0], {
1264            slidesPerView: 1,
1265            loop: false,
1266            allowTouchMove: false,
1267            speed: 500,
1268            effect: 'fade',
1269            fadeEffect: { crossFade: true },
1270            autoHeight: true, // Automatically adjust height based on active slide content
1271        });
1272
1273        // Sync captions with main slider
1274        mainSwiper.on('slideChange', function () {
1275            captionSwiper.slideTo(mainSwiper.realIndex);
1276        });
1277
1278    });
1279}
1280
1281
1282
1283
1284    
1285
1286/*
1287=====================================
1288Custom Card Slider 
1289=====================================
1290*/
1291// function initializeCustomDisplaySlider() {
1292
1293//     const $customDisplaySliderContainer = $('.site-display-slider-wrapper');
1294    
1295//     // Check if container exists
1296//     if ($customDisplaySliderContainer.length === 0) {
1297//         return;
1298//     }
1299    
1300//     const $customDisplaySlider = $customDisplaySliderContainer.find('.site-display-list');
1301
1302//     // Check if custom card slider elements exist
1303//     if ($customDisplaySlider.length === 0) {
1304//         return;
1305//     }       
1306
1307//     const $customDisplaySliderArrows = $('<div class="display-slider-arrow-container"><button class="slick-arrow slick-prev">Previous</button><button class="slick-arrow slick-next">Next</button></div>');
1308    
1309//     // Check if site-display-filters exists, otherwise append to customDisplaySlider
1310//     const $siteDisplayFilters = $customDisplaySliderContainer.find('.site-display-filters');
1311//     if ($siteDisplayFilters.length > 0) {
1312//         $customDisplaySliderArrows.appendTo($siteDisplayFilters);
1313//     } else {
1314//         $customDisplaySliderArrows.appendTo($customDisplaySliderContainer);
1315//     }
1316    
1317//     // Store references to arrow elements for later use
1318//     const $prevArrow = $customDisplaySliderContainer.find('.display-slider-arrow-container .slick-prev');
1319//     const $nextArrow = $customDisplaySliderContainer.find('.display-slider-arrow-container .slick-next');
1320
1321//     // Remove 'By' text from site-display-filter-title elements
1322//     $customDisplaySliderContainer.find('.site-display-filter-title').each(function() {
1323//         var $title = $(this);
1324//         var text = $title.text();
1325//         if (text.includes('By')) {
1326//             $title.text(text.replace(/By\s*/g, '').trim());
1327//         }
1328//         // Remove space before colon
1329//         var currentText = $title.text();
1330//         $title.text(currentText.replace(/\s+:/g, ':'));
1331//     });
1332      
1333//     $customDisplaySlider.each(function() {
1334//         var $displayslider = $(this);
1335        
1336//         // Check if slider has slides before initializing
1337//         var $slides = $displayslider.find('> *');
1338//         if ($slides.length === 0) {
1339           
1340//             return;
1341//         }
1342        
1343//         // Check if slider is already initialized to prevent double initialization
1344//         if ($displayslider.hasClass('slick-initialized')) {
1345//             return;
1346//         }
1347        
1348//         // var $sliderPanel = $slider.closest('.custom-slider-wrapper');
1349//                                                                                                                                                                         try {
1350//             $displayslider.slick({
1351//                 slidesToShow: 1,
1352//                 slidesToScroll: 1,
1353//                 dots: false,
1354//                 arrows: $prevArrow.length > 0 && $nextArrow.length > 0, // Only enable arrows if they exist
1355//                 infinite: $slides.length > 1, // Only enable infinite if there are multiple slides
1356//                 centerMode: true,
1357//                 centerPadding: '0px',
1358//                 variableWidth: true,
1359//                 prevArrow: $prevArrow.length > 0 ? $prevArrow : null,
1360//                 nextArrow: $nextArrow.length > 0 ? $nextArrow : null,
1361//                 accessibility: true, // Explicitly enable accessibility
1362//                 adaptiveHeight: false,
1363//                 lazyLoad: 'ondemand' // Add lazy loading to prevent issues with missing images
1364//             });
1365//         } catch (error) {
1366//             // console.error('Error initializing custom display slider:', error);
1367//         }
1368
1369//     });
1370    
1371//     // Add click event for filter data block labels to refresh slider
1372//     $customDisplaySliderContainer.find('.site-display-filters .site-display-filter-desc .site-display-filter-wrapper .site-display-filter-data-block label').on('click', function() {
1373//         // Refresh all sliders in the container
1374//         $customDisplaySliderContainer.find('.site-display-list').each(function() {
1375//             $(this).slick('refresh');
1376//         });
1377//     });
1378    
1379// }
1380
1381
1382
1383/*
1384=====================================
1385Custom Card Slider New Version
1386=====================================
1387*/
1388function initializeCustomDisplaySlider() {
1389
1390    const $customDisplaySliderContainer = $('.site-display-slider-wrapper');
1391    
1392    // Check if container exists
1393    if ($customDisplaySliderContainer.length === 0) {
1394        return;
1395    }
1396    
1397    const $customDisplaySlider = $customDisplaySliderContainer.find('.slick-slider');
1398
1399
1400    // const $siteDisplayFilters = $customDisplaySliderContainer.find('.site-display-filters');
1401    if ($customDisplaySliderContainer.find('.site-display-filters').length > 0) {
1402        $customDisplaySliderContainer.addClass('has-filters');
1403    } else {
1404        $customDisplaySliderContainer.removeClass('has-filters');
1405    }
1406    
1407  
1408    // Remove 'By' text from site-display-filter-title elements
1409    $customDisplaySliderContainer.find('.site-display-filter-title').each(function() {
1410        var $title = $(this);
1411        var text = $title.text();
1412        if (text.includes('By')) {
1413            $title.text(text.replace(/By\s*/g, '').trim());
1414        }
1415        // Remove space before colon
1416        var currentText = $title.text();
1417        $title.text(currentText.replace(/\s+:/g, ':'));
1418    });
1419      
1420    $customDisplaySlider.each(function() {
1421        var $displayslider = $(this);
1422        
1423        // Initialize the slider with proper configuration
1424        $displayslider.slick("slickSetOption",{
1425            slidesToShow: 1,
1426            slidesToScroll: 1,
1427            variableWidth: true,
1428        });
1429
1430        // Refresh slick slider to handle any dynamic content changes
1431        $displayslider.slick('refresh');
1432          
1433    });
1434
1435   
1436    
1437}
1438
1439
1440  
1441  
1442  
1443  
1444  
1445  
1446
1447/*
1448=====================================
1449Custom Infographic
1450=====================================
1451*/
1452
1453// create gradient based on canvas size
1454function createGradient(ctx, size) {
1455    const gradient = ctx.createLinearGradient(0, 0, 0, size);
1456    const isCustomTheme = document.body.classList.contains('custom-primary-theme');
1457    
1458    if (isCustomTheme) {
1459     
1460        gradient.addColorStop(0, 'rgba(255, 100, 100, 0.4)'); // Brightened red for plus-lighter effect
1461        gradient.addColorStop(1, 'rgba(100, 150, 255, 0.4)'); // Brightened blue for plus-lighter effect
1462    } else {
1463        gradient.addColorStop(0, 'rgba(213, 28, 41, 0.3)');
1464        gradient.addColorStop(1, 'rgba(43, 60, 115, 0.3)');
1465    }
1466    return gradient;
1467}
1468
1469function initializeInfographicChart() {
1470    if (typeof Chart === 'undefined') {
1471        return;
1472    }
1473
1474    var $container = $('.infographic-wrapper.cms-feature-wrapper .site-slide .site-slider-caption .site-slider-caption-block .site-container');
1475    if (!$container.length) {
1476        return;
1477    }
1478
1479    $container.each(function(index) {
1480        var $thisContainer = $(this);
1481        var $h2 = $thisContainer.find('h2').first();
1482        if (!$h2.length) {
1483            return;
1484        }
1485
1486        // Extract just the percentage number
1487        var match = $h2.text().match(/(\d+)%/);
1488        if (!match) {
1489            return;
1490        }
1491        var percent = parseInt(match[1], 10);
1492        percent = Math.max(0, Math.min(100, percent)); // clamp between 0–100
1493
1494        // Check if chart wrapper already exists and clean it up
1495        var $existingWrapper = $thisContainer.find('.circle-progress-wrapper-chartjs');
1496        if ($existingWrapper.length) {
1497            // Destroy existing chart if it exists
1498            var existingChart = $existingWrapper.data('chart');
1499            if (existingChart) {
1500                existingChart.destroy();
1501            }
1502            $existingWrapper.remove();
1503        }
1504
1505        // Responsive canvas wrapper
1506        var isCustomTheme = document.body.classList.contains('custom-primary-theme');
1507        
1508        var canvasMarkup = `
1509            <div class="circle-progress-wrapper-chartjs" style="position:relative; width:100%; max-width:500px; aspect-ratio:1/1; margin:0 auto;">
1510                <canvas id="doughnut-chart-${index}"></canvas>
1511            </div>
1512        `;
1513
1514        $thisContainer.append(canvasMarkup);
1515        
1516        // Ensure the container is properly sized before proceeding
1517        setTimeout(function() {
1518            initializeChartForContainer($thisContainer, index, percent);
1519        }, 50);
1520    });
1521
1522    //Redraw gradients on window resize
1523    window.addEventListener('resize', function() {
1524        $('.circle-progress-wrapper-chartjs').each(function() {
1525            var chart = $(this).data('chart');
1526            if (chart) {
1527                var ctx = chart.ctx;
1528                var size = ctx.canvas.offsetHeight || ctx.canvas.offsetWidth;
1529                var newGradient = createGradient(ctx, size);
1530
1531                chart.data.datasets[0].backgroundColor[0] = newGradient;
1532                chart.update();
1533            }
1534        });
1535    });
1536
1537    // If inside slider, set up accordingly
1538    var $infographicSlider = $('.infographic-slider-wrapper .site-slick-wrapper');
1539    if ($infographicSlider.length) {
1540        setupInfographicSlider();
1541    }
1542
1543    // Listen for theme changes and re-initialize charts
1544    $(document).on('click', '.theme-toggle-button', function() {
1545        setTimeout(function() {
1546            // Re-initialize charts after theme change
1547            $('.circle-progress-wrapper-chartjs').each(function() {
1548                var $wrapper = $(this);
1549                var chart = $wrapper.data('chart');
1550                var percent = $wrapper.data('percent');
1551                
1552                if (chart && percent !== undefined) {
1553                    // Check current theme state
1554                    var isCustomTheme = document.body.classList.contains('custom-primary-theme');
1555                    
1556                    // Update gradient with new theme colors
1557                    var ctx = chart.ctx;
1558                    var size = ctx.canvas.offsetHeight || ctx.canvas.offsetWidth;
1559                    var newGradient = createGradient(ctx, size);
1560                    chart.data.datasets[0].backgroundColor[0] = newGradient;
1561                    
1562                    // Update chart
1563                    chart.update();
1564                }
1565            });
1566        }, 100); // Small delay to ensure theme class is applied
1567    });
1568}
1569
1570
1571function initializeChartForContainer($thisContainer, index, percent) {
1572    var $wrapper = $thisContainer.find('.circle-progress-wrapper-chartjs');
1573    if (!$wrapper.length) return;
1574  
1575    try {
1576      var canvas = document.getElementById(`doughnut-chart-${index}`);
1577      var ctx = canvas.getContext('2d');
1578  
1579      // --- Simplified, predictable sizing ---
1580      var browserWidth = window.innerWidth;
1581      var targetSize = browserWidth <= 768 ? 300 : 500; // mobile vs desktop
1582  
1583        // Apply consistent explicit sizing
1584        canvas.width = targetSize;
1585        canvas.height = targetSize;
1586        canvas.style.width = targetSize + 'px';
1587        canvas.style.height = targetSize + 'px';
1588        
1589        // Completely disable pointer events to prevent any tooltips
1590        canvas.style.pointerEvents = 'none';
1591  
1592      var size = targetSize;
1593      var gradient = createGradient(ctx, size);
1594  
1595      var chart = new Chart(ctx, {
1596        type: 'doughnut',
1597        data: {
1598          datasets: [{
1599            data: [0, 100], // start at 0%
1600            backgroundColor: [gradient, 'transparent'],
1601            borderWidth: 0,
1602            cutout: '50%',
1603          }],
1604        },
1605        options: {
1606          responsive: false, // now we control the size manually
1607          maintainAspectRatio: false,
1608          animation: {
1609            animateRotate: false,
1610            animateScale: false,
1611          },
1612          plugins: {
1613            legend: { display: false },
1614            tooltip: { 
1615              enabled: false,
1616              external: null,
1617              filter: function() { return false; },
1618              callbacks: {}
1619            },
1620          },
1621          elements: {
1622            arc: { borderWidth: 0 },
1623          },
1624        },
1625      });
1626  
1627      // Store chart + percent
1628      $wrapper.data('chart', chart);
1629      $wrapper.data('percent', percent);
1630  
1631      // Animate when scrolled into view
1632      $wrapper.waypoint(
1633        function (direction) {
1634          var chart = $wrapper.data('chart');
1635          var percent = $wrapper.data('percent');
1636          if (chart && percent !== undefined) {
1637            chart.data.datasets[0].data = [percent, 100 - percent];
1638            chart.update();
1639          }
1640          this.destroy();
1641        },
1642        { offset: '80%' }
1643      );
1644    } catch (error) {
1645      console.error('Chart.js error:', error);
1646    }
1647}
1648  
1649
1650function setupInfographicSlider() {
1651    var $infographicSlider = $('.infographic-slider-wrapper .site-slick-wrapper');
1652    var $infographicSliderText = $('.infographic-text-wrapper.cms-feature-wrapper table.site-table-content tbody');
1653    
1654    if ($infographicSlider.length) {
1655        if (!$infographicSlider.hasClass('slick-initialized')) {
1656            $infographicSlider.slick({
1657                slidesToShow: 1,
1658                slidesToScroll: 1,
1659                dots: true,
1660                arrows: false,
1661                fade: true,
1662                speed: 600,
1663                asNavFor: $infographicSliderText
1664            });
1665        }
1666
1667        if (!$infographicSliderText.hasClass('slick-initialized')) {
1668            $infographicSliderText.slick({
1669                slidesToShow: 1,
1670                slidesToScroll: 1,
1671                dots: false,
1672                arrows: false,
1673                fade: true,
1674                speed: 600,
1675                asNavFor: $infographicSlider
1676            });
1677        }
1678        
1679        // Listen for slide changes
1680        $infographicSlider.off('afterChange').on('afterChange', function(event, slick, currentSlide) {
1681            // Reset all charts to 0% immediately
1682            $('.circle-progress-wrapper-chartjs').each(function() {
1683                var $wrapper = $(this);
1684                var chart = $wrapper.data('chart');
1685                if (chart) {
1686                    chart.data.datasets[0].data = [0, 100];
1687                    chart.update('none');
1688                }
1689            });
1690            
1691            // Animate the active slide's chart using currentSlide index
1692            setTimeout(function() {
1693                var $activeSlide = $(slick.$slides[currentSlide]);
1694                var $activeContainer = $activeSlide.find('.circle-progress-wrapper-chartjs');
1695                
1696                if ($activeContainer.length) {
1697                    var chart = $activeContainer.data('chart');
1698                    var percent = $activeContainer.data('percent');
1699                    
1700                    if (chart && percent !== undefined) {
1701                        chart.data.datasets[0].data = [percent, 100 - percent];
1702                        chart.update();
1703                    }
1704                }
1705            }, 100); // Reduced timeout to 100ms
1706        });
1707        
1708        // Also listen for beforeChange to ensure charts are reset
1709        $infographicSlider.off('beforeChange').on('beforeChange', function(event, slick, currentSlide, nextSlide) {
1710            // Reset all charts before the slide changes
1711            $('.circle-progress-wrapper-chartjs').each(function() {
1712                var $wrapper = $(this);
1713                var chart = $wrapper.data('chart');
1714                if (chart) {
1715                    chart.data.datasets[0].data = [0, 100];
1716                    chart.update('none');
1717                }
1718            });
1719        });
1720    }
1721}
1722
1723
1724/*
1725=====================================
1726Video Popup Function
1727=====================================
1728*/
1729function initializeVideoPopup() {
1730    $("a.video-popup-button, a.custom-popup, .custom-popup .site-slide-link").click(function (event) {
1731        event.preventDefault();
1732
1733        $(".video-popup-button").removeClass("video-popup-button-active");
1734        $(".video-popup-section").remove();
1735
1736        $(this).addClass("video-popup-button-active");
1737
1738        $('<div>', {
1739            id: 'video-popup-section',
1740            class: 'video-popup-section video-popup-wrapper video-popup-wrapper-active',
1741        }).insertAfter("#page-wrapper");
1742
1743        $(".video-popup-section").append("<div class='video-popup'></div>");
1744        $(".video-popup").append("<div class='remove-video-popup'></div>");
1745
1746        const videoUrl = $(this).attr('href');
1747        const videoId = getVideoIdFromURL(videoUrl);
1748
1749        if (isVimeoVideo(videoUrl)) {
1750            const videoSrc = `https://player.vimeo.com/video/${videoId}?autoplay=1`;
1751            $('.video-popup-section .video-popup').append(`<iframe width="100%" height="500" src="${videoSrc}" frameborder="0" allow="autoplay" allowfullscreen></iframe>`);
1752        } else if (isYouTubeVideo(videoUrl)) {
1753            const videoSrc = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
1754            $('.video-popup-section .video-popup').append(`<iframe width="100%" height="500" src="${videoSrc}" frameborder="0" allow="autoplay" allowfullscreen></iframe>`);
1755        } else if (!isYouTubeVideo(videoUrl) && !isVimeoVideo(videoUrl) && !videoUrl.endsWith(".mp4") && !videoUrl.endsWith(".webm")) {
1756            $('.video-popup-section .video-popup').append(`<iframe class="has-page-link" width="100%" height="500" src="${videoUrl}" frameborder="0" allow="autoplay" allowfullscreen></iframe>`);
1757        } else {
1758            $('.video-popup-section .video-popup').append(`
1759              <video width="100%" height="auto" controls autoplay>
1760                  <source src="${videoUrl}" type="video/mp4">
1761                  Your browser does not support the video tag.
1762              </video>
1763          `);
1764        }
1765    });
1766
1767    function getVideoIdFromURL(url) {
1768        const youtubeRegex = /(?:youtube\.com\/(?:[^/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?/ ]{11})/;
1769        const vimeoRegex = /vimeo\.com\/(?:channels\/|groups\/[^\/]*\/videos\/|video\/|)([0-9]+)/;
1770
1771        const youtubeMatch = url.match(youtubeRegex);
1772        const vimeoMatch = url.match(vimeoRegex);
1773
1774        if (youtubeMatch) {
1775            return youtubeMatch[1];
1776        } else if (vimeoMatch) {
1777            return vimeoMatch[1];
1778        } else {
1779            return null;
1780        }
1781    }
1782
1783    function isYouTubeVideo(url) {
1784        return /(?:youtube\.com\/(?:[^/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)/.test(url);
1785    }
1786
1787    function isVimeoVideo(url) {
1788        return /vimeo\.com/.test(url);
1789    }
1790
1791    $(function () {
1792        $(document).on('click', '.remove-video-popup', function () {
1793            var $this = $(this).closest(".video-popup-wrapper");
1794
1795            $this.css('transition', 'opacity 500ms');
1796            $this.css('opacity', 0);
1797
1798            // After the transition, remove the element
1799            setTimeout(function () {
1800                $this.remove();
1801            }, 500);
1802        });
1803    });
1804}
1805
1806/*
1807=====================================
1808Style Guide Example Age Grade Filtering
1809=====================================
1810*/
1811function initializeSgDivisionFilter() {
1812    if ($notEditMode) {
1813        // Check if grade selection elements exist
1814        const $gradeSelectionLinks = $('.site-grade-selection-wrapper.cms-feature-wrapper .cms-feature-datablock ul li a');
1815        
1816        if ($gradeSelectionLinks.length === 0) {
1817            return;
1818        }
1819
1820        // Load All Grades content on page load
1821        // if(window.location.pathname === "/sg-landing-academic-division") {
1822        //     sgloadContentFromUrl("/ec-elementary-division");
1823        // }
1824        
1825
1826        $gradeSelectionLinks.on('click', function(e) {
1827            e.preventDefault();
1828
1829            // Remove active class from all anchors
1830            $('.site-grade-selection-wrapper.cms-feature-wrapper .cms-feature-datablock ul li a').removeClass('active');
1831            // Add active class to the clicked anchor
1832            $(this).addClass('active');
1833
1834            var pageUrl = $(this).attr('href');
1835            sgloadContentFromUrl(pageUrl);
1836            loadContentFromUrl(pageUrl);
1837
1838            $('html, body').animate({scrollTop: $('.site-grade-selection-wrapper').offset().top - 200 }, 'slow');
1839        });
1840    }
1841}
1842
1843
1844//Example version
1845function sgloadContentFromUrl(pageUrl = "/ec-elementary-division"){
1846    if(window.location.pathname !== "/sg-landing-academic-division"){
1847        return;
1848    }
1849
1850    $.ajax({
1851        url: pageUrl,
1852        method: 'GET',
1853        success(response) {
1854            const $wrapper = $('.site-grade-selection-wrapper');
1855            const newContent = $(response).find('#site-main-content-wrapper').html();
1856            
1857            // Remove any previously loaded content
1858            $wrapper.nextAll().remove();
1859            
1860            // Insert new content after the wrapper
1861            $wrapper.after(newContent);
1862            
1863            window.dispatchEvent(new Event('resize'));
1864            initializeUnslick();
1865            // initializeCustomSlider();
1866            initializeCustomGallerySlider();
1867            initializeVideoPopup(); 
1868        },
1869        complete() {
1870            // Call the function immediately
1871            initializeAjaxGridSystem();
1872        }
1873    });
1874}
1875
1876//Site version
1877function loadContentFromUrl(pageUrl = "/early-childhood-division"){
1878    if(window.location.pathname !== "/academics"){
1879        return;
1880    }
1881
1882    $.ajax({
1883        url: pageUrl,
1884        method: 'GET',
1885        success(response) {
1886            const $wrapper = $('.site-grade-selection-wrapper');
1887            const newContent = $(response).find('#site-main-content-wrapper').html();
1888            
1889            // Remove any previously loaded content
1890            $wrapper.nextAll().remove();
1891            
1892            // Insert new content after the wrapper
1893            $wrapper.after(newContent);
1894            
1895            window.dispatchEvent(new Event('resize'));
1896            initializeUnslick();
1897            // initializeCustomSlider();
1898            initializeCustomGallerySlider();
1899            initializeVideoPopup(); 
1900            initializeCustomTabbedSlider();
1901        },
1902        complete() {
1903            // Call the function immediately
1904            initializeAjaxGridSystem();
1905        }
1906    });
1907}
1908
1909// Move this function outside the AJAX call so it's accessible
1910function initializeAjaxGridSystem() {
1911    $('.site-row-wrapper').each(function () {
1912        var $nextGroup = $(this).nextUntil('.site-row-wrapper').add(this);
1913        $nextGroup.add($nextGroup).wrapAll('<div class="site-row-container">');
1914    });
1915
1916    var classNames = [];
1917    $('div[class*="site-row-block-"]').each(function (i, el) {
1918        var allClasses = Array.from(el.classList);
1919        
1920        var nonWrapperClasses = allClasses.filter(function (c) {
1921            return c.startsWith('site-row-block-') && !c.endsWith('-wrapper');
1922        });
1923        
1924        nonWrapperClasses.forEach(function (className) {
1925            $(el).removeClass(className);
1926        });
1927        
1928        if (nonWrapperClasses.length > 0) {
1929            $(el).parents(".site-row-container").addClass(nonWrapperClasses.join(' '));
1930        }
1931    });
1932}
1933
1934
1935/*
1936=====================================
1937GSAP Fill Text
1938=====================================
1939*/
1940
1941// function initializeFillText() {
1942//     const headings = document.querySelectorAll(
1943//       ".fill-text-wrapper .cms-text-feature-data .cms-feature-datablock h1, " +
1944//       ".fill-text-wrapper .cms-text-feature-data .cms-feature-datablock h2"
1945//     );
1946
1947//     // Check if fill-text-wrapper elements exist
1948//     if (headings.length === 0) {
1949//         return;
1950//     }
1951
1952//     if ($editMode) {
1953//         return;
1954//     }
1955  
1956//     headings.forEach((heading) => {
1957//       let split, tl;
1958  
1959//       const createSplit = () => {
1960//         // Clean up previous
1961//         split && split.revert();
1962//         tl && tl.kill();
1963  
1964//         // Split text into chars
1965//         split = new SplitText(heading, {
1966//           type: "words,chars",
1967//           wordsClass: "split-word",
1968//           charsClass: "split-char"
1969//         });
1970//         heading._split = split;
1971  
1972//         // Base color is always red, fill color by theme
1973//         const isCustomTheme = document.body.classList.contains("custom-primary-theme");
1974//         const fillColor = isCustomTheme ? "#ffffff" : "#2B3C73";
1975//         const baseColor = "#E90010";
1976
1977//         // Init chars with base color (red)
1978//         split.chars.forEach((char) => {
1979//           char.dataset.fillActive = "0";
1980//           char.dataset.baseColor = baseColor;
1981//           char.dataset.fillColor = fillColor;
1982//           gsap.set(char, { color: baseColor });
1983//         });
1984  
1985//         // Build timeline
1986//         tl = gsap.timeline({
1987//           scrollTrigger: {
1988//             trigger: heading,
1989//             start: "top 90%",
1990//             end: "bottom 20%",
1991//             scrub: 1.2,
1992//             markers: false
1993//           }
1994//         });
1995//         heading._st = tl;
1996  
1997//         // Smooth transition per char with easing
1998//         split.chars.forEach((char, i) => {
1999//           tl.to(
2000//             char,
2001//             {
2002//               duration: 0.3,
2003//               ease: "power2.out",
2004//               color: char.dataset.fillColor,
2005//               onStart: () => { char.dataset.fillActive = "1"; },
2006//               onReverseComplete: () => { 
2007//                 char.dataset.fillActive = "0"; 
2008//                 gsap.set(char, { color: char.dataset.baseColor }); 
2009//               }
2010//             },
2011//             (i * 0.05) + 0.001 
2012//           );
2013//         });
2014  
2015//         tl.progress(0, false); // force to start state
2016//       };
2017  
2018//       createSplit();
2019  
2020//       // Debounce resize
2021//       const debouncer = gsap.delayedCall(0.2, createSplit).pause();
2022//       window.addEventListener("resize", () => debouncer.restart(true));
2023//     });
2024  
2025//     // Update theme colors when toggled
2026//     const updateThemeColor = () => {
2027//       // Recreate all splits with new theme colors
2028//       headings.forEach((heading) => {
2029//         if (heading._split) {
2030//           // Store current scroll progress
2031//           const currentProgress = heading._st ? heading._st.progress() : 0;
2032          
2033//           // Recreate the split with new colors
2034//           const createSplit = () => {
2035//             const split = heading._split;
2036//             const tl = heading._st;
2037            
2038//             // Clean up previous
2039//             split && split.revert();
2040//             tl && tl.kill();
2041            
2042//             // Split text into chars
2043//             const newSplit = new SplitText(heading, {
2044//               type: "words,chars",
2045//               wordsClass: "split-word",
2046//               charsClass: "split-char"
2047//             });
2048//             heading._split = newSplit;
2049            
2050//             // Base color is always red, fill color by theme
2051//             const isCustomTheme = document.body.classList.contains("custom-primary-theme");
2052//             const fillColor = isCustomTheme ? "#ffffff" : "#2B3C73";
2053//             const baseColor = "#E90010";
2054            
2055//             // Init chars with base color (red)
2056//             newSplit.chars.forEach((char) => {
2057//               char.dataset.fillActive = "0";
2058//               char.dataset.baseColor = baseColor;
2059//               char.dataset.fillColor = fillColor;
2060//               gsap.set(char, { color: baseColor });
2061//             });
2062            
2063//             // Build timeline
2064//             const newTl = gsap.timeline({
2065//               scrollTrigger: {
2066//                 trigger: heading,
2067//                 start: "top 90%",
2068//                 end: "bottom 20%",
2069//                 scrub: 1.2,
2070//                 markers: false
2071//               }
2072//             });
2073//             heading._st = newTl;
2074            
2075//             // Instant switch per char (no interpolation)
2076//             newSplit.chars.forEach((char, i) => {
2077//               newTl.to(
2078//                 char,
2079//                 {
2080//                   duration: 0.3,
2081//                   ease: "power2.out",
2082//                   color: char.dataset.fillColor,
2083//                   onStart: () => { char.dataset.fillActive = "1"; },
2084//                   onReverseComplete: () => { 
2085//                     char.dataset.fillActive = "0"; 
2086//                     gsap.set(char, { color: char.dataset.baseColor }); 
2087//                   }
2088//                 },
2089//                 (i * 0.05) + 0.001 
2090//               );
2091//             });
2092            
2093//             // Restore scroll progress
2094//             newTl.progress(currentProgress, false);
2095//           };
2096          
2097//           createSplit();
2098//         }
2099//       });
2100
2101//       ScrollTrigger.refresh();
2102//     };
2103  
2104//     // Theme toggle button listener
2105//     document.addEventListener("click", (e) => {
2106//       if (e.target.classList.contains("theme-toggle-button")) {
2107//         setTimeout(updateThemeColor, 50);
2108//       }
2109//     });
2110// }
2111  
2112// // Wait until fonts are loaded before initializing
2113// document.fonts.ready.then(() => {
2114//     initializeFillText();
2115//     ScrollTrigger.refresh();
2116// });
2117
2118function initializeFillText() {
2119    const headings = document.querySelectorAll(
2120      ".fill-text-wrapper .cms-text-feature-data .cms-feature-datablock h1, " +
2121      ".fill-text-wrapper .cms-text-feature-data .cms-feature-datablock h2"
2122    );
2123  
2124    if (headings.length === 0 || $editMode) return;
2125  
2126    headings.forEach((heading) => {
2127      let split, tl;
2128  
2129      const createSplit = () => {
2130        // Always wait for fonts before creating SplitText
2131        document.fonts.ready.then(() => {
2132          // Clean up previous
2133          split && split.revert();
2134          tl && tl.kill();
2135  
2136          // Split text into chars
2137          split = new SplitText(heading, {
2138            type: "words,chars",
2139            wordsClass: "split-word",
2140            charsClass: "split-char"
2141          });
2142          heading._split = split;
2143  
2144          // Determine colors based on theme
2145          const isCustomTheme = document.body.classList.contains("custom-primary-theme");
2146          const fillColor = isCustomTheme ? "#ffffff" : "#2B3C73";
2147          const baseColor = "#E90010";
2148  
2149          // Initialize chars with base color (red)
2150          split.chars.forEach((char) => {
2151            char.dataset.fillActive = "0";
2152            char.dataset.baseColor = baseColor;
2153            char.dataset.fillColor = fillColor;
2154            gsap.set(char, { color: baseColor });
2155          });
2156  
2157          // Build ScrollTrigger timeline
2158          tl = gsap.timeline({
2159            scrollTrigger: {
2160              trigger: heading,
2161              start: "top 90%",
2162              end: "bottom 20%",
2163              scrub: 1.2,
2164              markers: false
2165            }
2166          });
2167          heading._st = tl;
2168  
2169          // Animate each char's color transition smoothly
2170          split.chars.forEach((char, i) => {
2171            tl.to(
2172              char,
2173              {
2174                duration: 0.3,
2175                ease: "power2.out",
2176                color: char.dataset.fillColor,
2177                onStart: () => { char.dataset.fillActive = "1"; },
2178                onReverseComplete: () => {
2179                  char.dataset.fillActive = "0";
2180                  gsap.set(char, { color: char.dataset.baseColor });
2181                }
2182              },
2183              (i * 0.05) + 0.001
2184            );
2185          });
2186  
2187          tl.progress(0, false);
2188        });
2189      };
2190  
2191      // Initial Split
2192      createSplit();
2193  
2194      // Debounced resize re-split (safe with fonts)
2195      document.fonts.ready.then(() => {
2196        const debouncer = gsap.delayedCall(0.2, createSplit).pause();
2197        window.addEventListener("resize", () => debouncer.restart(true));
2198      });
2199    });
2200  
2201    // Theme color update function
2202    const updateThemeColor = () => {
2203      headings.forEach((heading) => {
2204        if (!heading._split) return;
2205  
2206        // Preserve scroll progress
2207        const currentProgress = heading._st ? heading._st.progress() : 0;
2208  
2209        // Wait for fonts before recreating split
2210        document.fonts.ready.then(() => {
2211          const oldSplit = heading._split;
2212          const oldTl = heading._st;
2213          oldSplit && oldSplit.revert();
2214          oldTl && oldTl.kill();
2215  
2216          const newSplit = new SplitText(heading, {
2217            type: "words,chars",
2218            wordsClass: "split-word",
2219            charsClass: "split-char"
2220          });
2221          heading._split = newSplit;
2222  
2223          const isCustomTheme = document.body.classList.contains("custom-primary-theme");
2224          const fillColor = isCustomTheme ? "#ffffff" : "#2B3C73";
2225          const baseColor = "#E90010";
2226  
2227          newSplit.chars.forEach((char) => {
2228            char.dataset.fillActive = "0";
2229            char.dataset.baseColor = baseColor;
2230            char.dataset.fillColor = fillColor;
2231            gsap.set(char, { color: baseColor });
2232          });
2233  
2234          const newTl = gsap.timeline({
2235            scrollTrigger: {
2236              trigger: heading,
2237              start: "top 90%",
2238              end: "bottom 20%",
2239              scrub: 1.2,
2240              markers: false
2241            }
2242          });
2243          heading._st = newTl;
2244  
2245          newSplit.chars.forEach((char, i) => {
2246            newTl.to(
2247              char,
2248              {
2249                duration: 0.3,
2250                ease: "power2.out",
2251                color: char.dataset.fillColor,
2252                onStart: () => { char.dataset.fillActive = "1"; },
2253                onReverseComplete: () => {
2254                  char.dataset.fillActive = "0";
2255                  gsap.set(char, { color: char.dataset.baseColor });
2256                }
2257              },
2258              (i * 0.05) + 0.001
2259            );
2260          });
2261  
2262          // Restore progress
2263          newTl.progress(currentProgress, false);
2264        });
2265      });
2266  
2267      ScrollTrigger.refresh();
2268    };
2269  
2270    // Theme toggle listener
2271    document.addEventListener("click", (e) => {
2272      if (e.target.classList.contains("theme-toggle-button")) {
2273        setTimeout(updateThemeColor, 50);
2274      }
2275    });
2276  }
2277  
2278  // Initialize only after fonts load initially
2279  document.fonts.ready.then(() => {
2280    initializeFillText();
2281    ScrollTrigger.refresh();
2282  });
2283  
2284/*
2285=====================================
2286Cookie Utilities
2287=====================================
2288*/
2289
2290// Cookie utility functions for preloader control
2291function setCookie(name, value, days) {
2292    const expires = new Date();
2293    expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
2294    document.cookie = name + '=' + value + ';expires=' + expires.toUTCString() + ';path=/';
2295}
2296
2297function getCookie(name) {
2298    const nameEQ = name + "=";
2299    const ca = document.cookie.split(';');
2300    for (let i = 0; i < ca.length; i++) {
2301        let c = ca[i];
2302        while (c.charAt(0) === ' ') c = c.substring(1, c.length);
2303        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
2304    }
2305    return null;
2306}
2307
2308function shouldShowPreloader() {
2309    const preloaderCookie = getCookie('preloader_shown');
2310    if (!preloaderCookie) return true;
2311    
2312    const lastShown = new Date(preloaderCookie);
2313    const now = new Date();
2314    const daysSinceLastShown = (now - lastShown) / (1000 * 60 * 60 * 24);
2315    
2316    // Show preloader if it's been more than 1 day since last shown
2317    return daysSinceLastShown >= 1;
2318}
2319
2320function markPreloaderAsShown() {
2321    const now = new Date().toISOString();
2322    setCookie('preloader_shown', now, 1); // Cookie expires in 1 day
2323}
2324
2325/*
2326=====================================
2327Custom Preloader
2328=====================================
2329*/
2330
2331function initializeCustomPreloader() {
2332    if ($editMode) return;
2333
2334    const $preloaderContainer = $('.site-preloader-wrapper');
2335    if (!$preloaderContainer.length) return;
2336
2337    // Check if preloader should be shown based on cookie
2338    if (!shouldShowPreloader()) {
2339        // Hide preloader if it shouldn't be shown
2340        $preloaderContainer.hide();
2341        return;
2342    }
2343
2344    // Show preloader by adding the CSS class
2345    $preloaderContainer.addClass('preloader-should-show');
2346    $('body').addClass('preloader-active');
2347
2348    const $enterButton = $('.site-preloader-wrapper .site-slider-more-desc .site-slider-more-desc-data .site-button-icon');
2349    $enterButton.addClass('preloader-enter-btn');
2350    $preloaderContainer.append($enterButton);
2351    const $preloaderLogo = $('.site-preloader-wrapper .preloder-logo');
2352    $preloaderContainer.prepend($preloaderLogo);
2353
2354    $preloaderContainer.off('click.preloader').on('click.preloader', '.preloader-enter-btn', function (e) {
2355        e.preventDefault();
2356        e.stopPropagation();
2357
2358        let bg = '#f7f4f6';
2359        try {
2360            const cs = getComputedStyle($preloaderContainer[0]);
2361            bg = cs.backgroundColor || bg;
2362        } catch (_) {}
2363
2364        const uid = 'pre-mask-' + Date.now();
2365        const maskId = `preloader-hole-mask-${uid}`;
2366        const circleId = `preloader-hole-${uid}`;
2367
2368        const $svgOverlay = $(`
2369          <div class="preloader-reveal-svg" style="position:fixed;inset:0;z-index:10000;pointer-events:none;">
2370            <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" aria-hidden="true">
2371              <defs>
2372                <mask id="${maskId}" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse">
2373                  <rect x="0" y="0" width="100" height="100" fill="#fff"/>
2374                  <circle id="${circleId}" cx="50" cy="50" r="0" fill="#000"/>
2375                </mask>
2376              </defs>
2377              <rect x="0" y="0" width="100" height="100" fill="${bg}" mask="url(#${maskId})"/>
2378            </svg>
2379          </div>
2380        `).appendTo('body');
2381
2382        const hole = document.getElementById(circleId);
2383
2384     
2385        const targetR = 80;
2386
2387        gsap.to(hole, {
2388            duration: 1.8,         
2389            ease: 'power1.out',
2390            attr: { r: targetR },
2391            onStart() {
2392                // Hide preloader content so the hole reveals the page
2393                gsap.set($preloaderContainer, { opacity: 0 });
2394            },
2395            onComplete() {
2396                // Mark preloader as shown so it won't appear again for 1 day
2397                markPreloaderAsShown();
2398                
2399                $('body').removeClass('preloader-active');
2400                $preloaderContainer.remove();
2401                $svgOverlay.remove();
2402            }
2403        });
2404    });
2405}
2406
2407
2408
2409
2410/*
2411=====================================
2412Site Bookmark
2413=====================================
2414*/
2415
2416function initializeSiteBookmark() {
2417    if ($editMode) return;
2418
2419    const $siteBookmarkWrapper = $('.site-bookmark-wrapper');
2420    if (!$siteBookmarkWrapper.length) return;
2421
2422    const $bookmarkButton = $('.site-bookmark-button');
2423    const $siteDisplayDataBlock = $('.site-display-bookmark-wrapper .site-display-data-block');
2424
2425
2426    const $customBookmarkButton = $('.custom-bookmark-button-wrapper');
2427
2428    // Update custom bookmark button text based on current page URL
2429    if ($customBookmarkButton.length) {
2430        const $customBookmarkButtonElement = $customBookmarkButton.find('.site-bookmark-button');
2431        if ($customBookmarkButtonElement.length) {
2432            // Get current page URL path
2433            const currentPath = window.location.pathname;
2434            // Remove leading slash, replace hyphens with spaces, and capitalize first letter, or use 'Home' for root
2435            const pageName = currentPath === '/' ? 'Home' : currentPath.substring(1).replace(/-/g, ' ').charAt(0).toUpperCase() + currentPath.substring(1).replace(/-/g, ' ').slice(1);
2436            
2437            // Update button text with subtext based on bookmark state
2438            if ($customBookmarkButtonElement.hasClass("site-bookmark-single-button-add")) {
2439                $customBookmarkButtonElement.html(pageName + '<small> Add to my collection</small>');
2440            } else if ($customBookmarkButtonElement.hasClass("site-bookmark-single-button-remove")) {
2441                $customBookmarkButtonElement.html(pageName + '<small> Remove from my collection</small>');
2442            } else {
2443                // Default to add if no specific class is found
2444                $customBookmarkButtonElement.html(pageName + '<small> Add to my collection</small>');
2445            }
2446            $customBookmarkButtonElement.wrapInner('<div class="site-bookmark-button-inner"></div>');
2447        }
2448    }
2449
2450    // Function to update custom bookmark button text
2451    function updateCustomBookmarkButtonText() {
2452        // console.log('updateCustomBookmarkButtonText called');
2453        const $customBookmarkButton = $('.custom-bookmark-button-wrapper');
2454        // console.log('Custom bookmark button found:', $customBookmarkButton.length);
2455        if ($customBookmarkButton.length) {
2456            const $customBookmarkButtonElement = $customBookmarkButton.find('.site-bookmark-button').first();
2457            // console.log('Custom bookmark button element found:', $customBookmarkButtonElement.length);
2458            if ($customBookmarkButtonElement.length) {
2459                // console.log('Current classes:', $customBookmarkButtonElement.attr('class'));
2460                // Get current page URL path
2461                const currentPath = window.location.pathname;
2462                const pageName = currentPath === '/' ? 'Home' : currentPath.substring(1).replace(/-/g, ' ').charAt(0).toUpperCase() + currentPath.substring(1).replace(/-/g, ' ').slice(1);
2463                // console.log('Page name:', pageName);
2464                
2465                // Check if inner div exists, if not create it
2466                let $innerDiv = $customBookmarkButtonElement.find('.site-bookmark-button-inner');
2467                // console.log('Inner div found:', $innerDiv.length);
2468                if (!$innerDiv.length) {
2469                    // console.log('Creating inner div');
2470                    $customBookmarkButtonElement.wrapInner('<div class="site-bookmark-button-inner"></div>');
2471                    $innerDiv = $customBookmarkButtonElement.find('.site-bookmark-button-inner');
2472                }
2473                
2474                // Update text content inside the inner div based on current state
2475                if ($customBookmarkButtonElement.hasClass("site-bookmark-single-button-add")) {
2476                    // console.log('Setting text to ADD');
2477                    $innerDiv.html(pageName + '<small> Add to my collection</small>');
2478                } else if ($customBookmarkButtonElement.hasClass("site-bookmark-single-button-remove")) {
2479                    // console.log('Setting text to REMOVE');
2480                    $innerDiv.html(pageName + '<small> Remove from my collection</small>');
2481                } else {
2482                    // console.log('Setting text to DEFAULT ADD');
2483                    // Default to add if no specific class is found
2484                    $innerDiv.html(pageName + '<small> Add to my collection</small>');
2485                }
2486                // console.log('Text update completed');
2487            }
2488        }
2489    }
2490
2491    if ($customBookmarkButton.length && $('#site-hero-wrapper .site-hero-wrapper').length) {
2492        $customBookmarkButton.appendTo('#site-hero-wrapper .site-hero-wrapper .site-slider');
2493    }
2494
2495    // Force update bookmark button text on page load
2496    setTimeout(function() {
2497        updateCustomBookmarkButtonText();
2498    }, 100);
2499
2500    if ($bookmarkButton.length && $siteDisplayDataBlock.length) {
2501        // Try to move the button to the first display data block
2502        //$bookmarkButton.prependTo($siteDisplayDataBlock.first());
2503        
2504        // Update the text content of the moved button
2505        $bookmarkButton.each(function () {
2506            if ($(this).hasClass("site-bookmark-display-button-add")) {
2507                $(this).html("Explorer<small> Add to my collection</small> ");
2508            } else if ($(this).hasClass("site-bookmark-display-button-remove")) {
2509                $(this).html("Remove<small>from your collection </small>");
2510            }
2511            $(this).wrapInner('<div class="site-bookmark-button-inner"></div>');
2512        });
2513    }
2514
2515
2516    // Find and move site-bookmark-icon after #site-header
2517    const $bookmarkIcon = $('.site-bookmark-icon');
2518    if ($bookmarkIcon.length) {
2519        $bookmarkIcon.insertAfter("#site-header");
2520        
2521        // Initially hide the bookmark icon
2522        $bookmarkIcon.hide();
2523        
2524        // Function to check scroll position and show/hide bookmark icon (mobile only)
2525        function handleBookmarkIconVisibility() {
2526            // Only run scroll-based logic on mobile devices (screen width <= 768px)
2527            const isMobile = $(window).width() <= 768;
2528            if (!isMobile) {
2529                // On desktop, always show the icon
2530                $bookmarkIcon.fadeIn(300);
2531                return;
2532            }
2533            
2534            // Mobile: show/hide based on scroll position
2535            const windowHeight = $(window).height();
2536            const scrollTop = $(window).scrollTop();
2537            
2538            // Show bookmark icon when user has scrolled a quarter of the screen height
2539            if (scrollTop >= windowHeight / 4) {
2540                $bookmarkIcon.fadeIn(300);
2541            } else {
2542                $bookmarkIcon.fadeOut(300);
2543            }
2544        }
2545        
2546        // Initial check
2547        handleBookmarkIconVisibility();
2548        
2549        // Listen for scroll events
2550        $(window).on('scroll', handleBookmarkIconVisibility);
2551        
2552        // Listen for resize events to recalculate on window resize
2553        $(window).on('resize', handleBookmarkIconVisibility);
2554    }
2555
2556    const $bookmarkPanel = $("<div class='site-bookmark-panel-wrapper'><div class='bookmark-panel-inner'></div></div>");
2557    $bookmarkPanel.insertAfter("#site-header");
2558    
2559    // Add close button to the panel
2560    const $closeButton = $('<button class="site-bookmark-panel-close"></button>');
2561    $bookmarkPanel.prepend($closeButton);
2562    
2563    // Add click event to bookmark icon to toggle panel
2564    $bookmarkIcon.on('click', function() {
2565        $bookmarkPanel.addClass('active');
2566        $bookmarkIcon.addClass('panel-active');
2567    });
2568    
2569    // Add click event to close button
2570    $closeButton.on('click', function() {
2571        $bookmarkPanel.removeClass('active');
2572        $bookmarkIcon.removeClass('panel-active');
2573    });
2574    
2575    // Append the site bookmark wrapper to the bookmark panel inner
2576    $siteBookmarkWrapper.appendTo($bookmarkPanel.find('.bookmark-panel-inner'));
2577
2578    // Append site-bookmark-text-feature-wrapper to site-bookmark-block
2579    const $textFeatureWrapper = $('.site-bookmark-text-feature-wrapper');
2580    if ($textFeatureWrapper.length) {
2581        $textFeatureWrapper.appendTo($siteBookmarkWrapper.find('.site-bookmark-block'));
2582    }
2583
2584    // Add placeholder items to fill the 3x3 grid (9 total slots)
2585    const $bookmarkList = $siteBookmarkWrapper.find('.site-bookmark-list');
2586    const existingItems = $bookmarkList.find('.site-bookmark-wrapper-listitem').length;
2587    const totalSlots = 9; // 3x3 grid
2588    const placeholdersNeeded = totalSlots - existingItems;
2589
2590    // Add placeholder items for empty slots
2591    for (let i = 0; i < placeholdersNeeded; i++) {
2592        $bookmarkList.append('<div class="site-bookmark-placeholder-item"></div>');
2593    }
2594    
2595
2596    // Function to remove a placeholder when a bookmark is added
2597    window.removeBookmarkPlaceholder = function() {
2598        const $placeholder = $bookmarkList.find('.site-bookmark-placeholder-item').first();
2599        if ($placeholder.length) {
2600            $placeholder.remove();
2601        }
2602    };
2603
2604    // Function to add a placeholder when a bookmark is removed
2605    window.addBookmarkPlaceholder = function() {
2606        // Add placeholder at the end to maintain grid order
2607        $bookmarkList.append('<div class="site-bookmark-placeholder-item"></div>');
2608    };
2609
2610    // Override the calculateBookmarkCounter function to ensure correct counting
2611    const originalCalculateBookmarkCounter = window.calculateBookmarkCounter;
2612    
2613    window.calculateBookmarkCounter = function() {
2614        const bookmarkCount = $(".site-bookmark-wrapper .site-bookmark-wrapper-listitem").length;
2615        
2616        // Call the original function first
2617        if (originalCalculateBookmarkCounter) {
2618            originalCalculateBookmarkCounter();
2619        }
2620        
2621        // Now override the counter display with the correct count
2622        const $counter = $(".site-bookmark-icon .site-bookmark-count");
2623        if ($counter.length) {
2624            if (bookmarkCount > 0) {
2625                $counter.text(bookmarkCount).show();
2626            } else {
2627                $counter.hide();
2628            }
2629        }
2630    };
2631
2632    // Listen for bookmark addition/removal events - only manage placeholders
2633    $(document).on("amais:bookmark:added", function() {
2634        // console.log('amais:bookmark:added event fired');
2635        // Remove a placeholder when a bookmark is added and move the new bookmark to the top
2636        setTimeout(function() {
2637            // Remove the first placeholder (from the top)
2638            window.removeBookmarkPlaceholder();
2639            
2640            // Move the last added bookmark to the beginning of the list (top position)
2641            const $lastBookmark = $bookmarkList.find('.site-bookmark-wrapper-listitem').last();
2642            if ($lastBookmark.length) {
2643                $lastBookmark.prependTo($bookmarkList);
2644            }
2645            
2646            // Update custom bookmark button text when bookmark is added
2647            updateCustomBookmarkButtonText();
2648        }, 100); 
2649    });
2650
2651    $(document).on("amais:bookmark:removed", function() {
2652        // console.log('amais:bookmark:removed event fired');
2653        // Add a placeholder when a bookmark is removed
2654        setTimeout(function() {
2655            window.addBookmarkPlaceholder();
2656            
2657            // Update custom bookmark button text when bookmark is removed
2658            updateCustomBookmarkButtonText();
2659        }, 100); 
2660    });
2661
2662    // Check for site-bookmark-button in site-display-bookmark-wrapper and add class
2663    $('.site-display-bookmark-wrapper').each(function () {
2664        const $wrapper = $(this);
2665        const $bookmarkButtonInWrapper = $wrapper.find('.site-bookmark-button');
2666        
2667        if ($bookmarkButtonInWrapper.length > 0) {
2668            $wrapper.addClass('has-bookmark-button');
2669        }
2670    });
2671
2672    // Check for has-prompt-wrapper class and prepend site-card-prompt-wrapper
2673    $('.site-display-bookmark-wrapper').each(function () {
2674        const $wrapper = $(this);
2675        const $wrapperInner = $wrapper.find('.site-display-wrapper');
2676        
2677        if ($wrapper.hasClass('has-prompt-wrapper')) {
2678            const $promptWrapper = $('.site-card-prompt-wrapper');
2679            if ($promptWrapper.length) {
2680                $promptWrapper.prependTo($wrapperInner);
2681            }
2682        }
2683    });
2684
2685
2686    
2687    // Create popup overlay for bookmark details
2688    const $popupOverlay = $('<div class="site-bookmark-popup-overlay"><div class="site-bookmark-popup-content"><div class="site-bookmark-popup-content-inner"><button class="site-bookmark-popup-go-back">Go Back</button><div class="site-bookmark-popup-body"></div></div></div></div>');
2689    const $popupOverlaycloseButton = $('<button class="site-bookmark-popup-close">&times;</button>');
2690    $popupOverlaycloseButton.prependTo($popupOverlay);
2691    $popupOverlay.insertAfter($bookmarkPanel);
2692
2693    // Use event delegation to handle clicks on bookmark items (including dynamically added ones)
2694    $bookmarkList.on('click', '.site-bookmark-wrapper-listitem', function(e) {
2695        e.preventDefault();
2696        
2697        const $listItem = $(this);
2698        
2699        // Find the image and data block within this item
2700        const $bookmarkImage = $listItem.find('.site-bookmark-image');
2701        const $bookmarkDataBlock = $listItem.find('.site-bookmark-data-block');
2702        
2703        // Clone the content for the popup
2704        const $clonedImage = $bookmarkImage.clone();
2705        const $clonedDataBlock = $bookmarkDataBlock.clone();
2706        
2707        // Clear previous content and add new content to popup
2708        const $popupBody = $popupOverlay.find('.site-bookmark-popup-body');
2709        $popupBody.empty();
2710        $popupBody.append($clonedImage);
2711        $popupBody.append($clonedDataBlock);
2712        
2713        // Show the popup
2714        $popupOverlay.addClass('active');
2715    });
2716
2717    // Add click handler to close popup
2718    $popupOverlay.find('.site-bookmark-popup-close').on('click', function() {
2719        $popupOverlay.removeClass('active');
2720        $bookmarkPanel.removeClass('active');
2721        $bookmarkIcon.removeClass('panel-active');
2722    });
2723
2724    // Add click handler to close popup
2725    $popupOverlay.find('.site-bookmark-popup-go-back').on('click', function() {
2726        $popupOverlay.removeClass('active');
2727    });
2728
2729
2730   
2731
2732    // Add click handler to close popup when clicking overlay background
2733    $popupOverlay.on('click', function(e) {
2734        if (e.target === this) {
2735            $popupOverlay.removeClass('active');
2736        }
2737    });
2738}
2739
2740
2741
2742
2743/*
2744=====================================
2745Language Toggle
2746=====================================
2747*/
2748function initializeLanguageToggle() {
2749    if ($notEditMode) {
2750        const $languageContainer = $('.site-language-translation');
2751        const $englishTrigger = $('.english-trigger');
2752        const $frenchTrigger = $('.french-trigger');
2753        const $germanTrigger = $('.german-trigger');
2754        const $spanishTrigger = $('.spanish-trigger');
2755        const $chineseTrigger = $('.mandarin-trigger');
2756        const $englishContent = $('.site-row-block-english-translation');
2757        const $frenchContent = $('.site-row-block-french-translation');
2758        const $germanContent = $('.site-row-block-german-translation');
2759        const $spanishContent = $('.site-row-block-spanish-translation');
2760        const $chineseContent = $('.site-row-block-mandarin-translation');
2761
2762        // Check if language toggle elements exist
2763        if ($languageContainer.length === 0 || $englishTrigger.length === 0) {
2764            return;
2765        }
2766
2767        // Set initial state - English visible, others hidden
2768        $englishContent.addClass('language-active').removeClass('language-hidden');
2769        $frenchContent.addClass('language-hidden').removeClass('language-active');
2770        $germanContent.addClass('language-hidden').removeClass('language-active');
2771        $spanishContent.addClass('language-hidden').removeClass('language-active');
2772        $chineseContent.addClass('language-hidden').removeClass('language-active');
2773        $englishTrigger.addClass('active');
2774        $frenchTrigger.removeClass('active');
2775        $germanTrigger.removeClass('active');
2776        $spanishTrigger.removeClass('active');
2777        $chineseTrigger.removeClass('active');
2778
2779        // Helper function to switch language
2780        function switchLanguage(activeTrigger, activeContent) {
2781            // Remove active class from all triggers
2782            $englishTrigger.removeClass('active');
2783            $frenchTrigger.removeClass('active');
2784            $germanTrigger.removeClass('active');
2785            $spanishTrigger.removeClass('active');
2786            $chineseTrigger.removeClass('active');
2787            
2788            // Hide all content
2789            $englishContent.removeClass('language-active').addClass('language-hidden');
2790            $frenchContent.removeClass('language-active').addClass('language-hidden');
2791            $germanContent.removeClass('language-active').addClass('language-hidden');
2792            $spanishContent.removeClass('language-active').addClass('language-hidden');
2793            $chineseContent.removeClass('language-active').addClass('language-hidden');
2794            
2795            // Show active content and trigger
2796            activeTrigger.addClass('active');
2797            activeContent.removeClass('language-hidden').addClass('language-active');
2798            
2799            // Refresh slick sliders
2800            if (typeof $.fn.slick !== 'undefined') {
2801                $('.slick-initialized').slick('refresh');
2802            }
2803            
2804            // Fix dots container positioning after refresh
2805            setTimeout(function() {
2806                $('.custom-gallery-slider-wrapper').each(function() {
2807                    var $sliderPanel = $(this);
2808                    var $dotsContainer = $sliderPanel.find('.slick-dots');
2809                    var $arrowContainer = $sliderPanel.find('.slider-arrow-container');
2810                    
2811                    if ($dotsContainer.length && $arrowContainer.length && !$dotsContainer.parent().is($arrowContainer)) {
2812                        $dotsContainer.prependTo($arrowContainer);
2813                        $dotsContainer.addClass('custom-dots-container');
2814                    }
2815                });
2816            }, 150);
2817        }
2818
2819        // English trigger click handler
2820        $englishTrigger.on('click', function(e) {
2821            e.preventDefault();
2822            switchLanguage($(this), $englishContent);
2823        });
2824
2825        // French trigger click handler
2826        $frenchTrigger.on('click', function(e) {
2827            e.preventDefault();
2828            switchLanguage($(this), $frenchContent);
2829        });
2830
2831        // German trigger click handler
2832        $germanTrigger.on('click', function(e) {
2833            e.preventDefault();
2834            switchLanguage($(this), $germanContent);
2835        });
2836
2837        // Spanish trigger click handler
2838        $spanishTrigger.on('click', function(e) {
2839            e.preventDefault();
2840            switchLanguage($(this), $spanishContent);
2841        });
2842
2843        // Chinese trigger click handler
2844        $chineseTrigger.on('click', function(e) {
2845            e.preventDefault();
2846            switchLanguage($(this), $chineseContent);
2847        });
2848    }
2849}
2850
2851/*
2852=====================================
2853Alternate Home Version Loader
2854=====================================
2855*/
2856// function initializeAlternateHomeVersionLoader() {
2857//     // Check if we're on the home page - do this inside the function to ensure DOM is ready
2858//     if (!$('#home-page').length) {
2859//         return;
2860//     }
2861    
2862//     // Get the last version shown from localStorage
2863//     let lastVersion = localStorage.getItem('pageVersion');
2864    
2865//     // If no version stored, start with version 1 (0)
2866//     if (lastVersion === null) {
2867//         lastVersion = 0;
2868//     } else {
2869//         lastVersion = parseInt(lastVersion);
2870//     }
2871    
2872//     // Alternate to the other version
2873//     const currentVersion = lastVersion === 0 ? 1 : 0;
2874    
2875//     // Store the current version for next time
2876//     localStorage.setItem('pageVersion', currentVersion.toString());
2877    
2878//     // Load the appropriate version
2879//     if (currentVersion === 1) {
2880//         loadAlternateVersion();
2881//     }
2882
2883//     function loadAlternateVersion() {
2884//         // Replace this with your actual alternate page URL
2885//         const alternatePageUrl = '/home-alternative-version';
2886        
2887//         fetch(alternatePageUrl)
2888//             .then(response => {
2889//                 if (!response.ok) {
2890//                     throw new Error(`HTTP error! status: ${response.status}`);
2891//                 }
2892//                 return response.text();
2893//             })
2894//             .then(html => {
2895//                 // Create a temporary div to parse the HTML
2896//                 const tempDiv = document.createElement('div');
2897//                 tempDiv.innerHTML = html;
2898                
2899//                 // Extract only the content from the site-main-grid-block in the response
2900//                 const alternateContent = tempDiv.querySelector('#site-main-grid-block');
2901                
2902//                 if (alternateContent) {
2903//                     // Get the inner content of the alternate page's site-main-grid-block
2904//                     const alternateInnerHTML = alternateContent.innerHTML;
2905                    
2906//                     // Check if the target element exists
2907//                     const targetElement = document.getElementById('site-main-grid-block');
2908//                     if (targetElement) {
2909//                         targetElement.innerHTML = alternateInnerHTML;
2910                        
2911//                         // Reinitialize functions after content replacement
2912//                         window.dispatchEvent(new Event('resize'));
2913//                         initializeUnslick();
2914//                          initializeAjaxGridSystem();
2915//                         initializeInfographicChart();
2916//                         initializeStackedDisplaySlider();
2917//                         initializeSiteBookmark();
2918//                         initializeCustomPreloader();
2919                        
2920//                         // Reinitialize fill text with ScrollTrigger refresh
2921//                         initializeFillText();
2922//                         ScrollTrigger.refresh();
2923//                     }
2924//                 }
2925//             })
2926//             .catch(error => {
2927//                 console.error('Error loading alternate version:', error);
2928//             });
2929//     }
2930// }
2931
2932/*
2933=====================================
2934Directory
2935=====================================
2936*/
2937function initializeBBDirectory() {
2938    $(".site-bb-directory-grid").each(function () {
2939      
2940        // Check for cms-bb-parent-image-block and prepend if missing
2941        $(this)
2942            .find(".cms-bb-result-item-data")
2943            .each(function () {
2944                if ($(this).find(".cms-bb-parent-image-block, .placeholder-image-block").length === 0) {
2945                    $(this).prepend("<div class='placeholder-image-block'></div>");
2946                }
2947            });
2948    });
2949}
2950
2951/*
2952=====================================
2953Content Intro Check
2954=====================================
2955*/
2956function initializeContentIntroCheck() {
2957    if ($notEditMode) {
2958        // Check if .site-row-block-intro-wrapper exists on the page
2959        if ($('.site-row-block-intro-wrapper').length > 0) {
2960            $('body').addClass('has-content-intro');
2961            
2962            // Function to check and apply height adjustment
2963            function checkAndAdjustHeights() {
2964                const $customSidebarWrapper = $('.custom-sidebar-wrapper .cms-text-feature-data');
2965                const $introWrapper = $('.site-row-block-intro-wrapper .cms-text-feature-data');
2966                
2967                if ($customSidebarWrapper.length > 0 && $introWrapper.length > 0) {
2968                    const sidebarHeight = $customSidebarWrapper.outerHeight();
2969                    
2970                    // Get the natural height of intro wrapper by temporarily removing min-height
2971                    const currentMinHeight = $introWrapper.css('min-height');
2972                    $introWrapper.css('min-height', '');
2973                    const introHeight = $introWrapper.outerHeight();
2974                    // Restore min-height if it existed
2975                    if (currentMinHeight && currentMinHeight !== '0px' && currentMinHeight !== 'none') {
2976                        $introWrapper.css('min-height', currentMinHeight);
2977                    }
2978                    
2979                    // console.log('Sidebar height:', sidebarHeight);
2980                    // console.log('Intro height:', introHeight);
2981                    
2982                    // Only proceed if both elements have actual height (not 0)
2983                    if (sidebarHeight > 0 && introHeight > 0) {
2984                        if (sidebarHeight > introHeight) {
2985                            const adjustedHeight = sidebarHeight + 60;
2986                            // console.log('Applying min-height:', adjustedHeight + 'px');
2987                            $introWrapper.css('min-height', adjustedHeight + 'px');
2988                        } else {
2989                            // Clear min-height if intro is naturally taller than sidebar
2990                            // console.log('Clearing min-height - intro is taller');
2991                            $introWrapper.css('min-height', '');
2992                        }
2993                    } else {
2994                        // console.log('Elements have no height yet, will retry...');
2995                        // Retry after a short delay if elements don't have height yet
2996                        setTimeout(checkAndAdjustHeights, 200);
2997                    }
2998                } else {
2999                    //console.log('Elements not found - Sidebar:', $customSidebarWrapper.length, 'Intro:', $introWrapper.length);
3000                }
3001            }
3002            
3003            // Run immediately
3004            checkAndAdjustHeights();
3005            
3006            // Run after a short delay to ensure elements are fully rendered
3007            setTimeout(checkAndAdjustHeights, 100);
3008            setTimeout(checkAndAdjustHeights, 500);
3009            setTimeout(checkAndAdjustHeights, 1000);
3010            
3011            // Run after images are loaded
3012            $(window).on('load', checkAndAdjustHeights);
3013            
3014            // Run on window resize with debounce
3015            let resizeTimer;
3016            $(window).on('resize', function() {
3017                clearTimeout(resizeTimer);
3018                resizeTimer = setTimeout(checkAndAdjustHeights, 250);
3019            });
3020        }
3021    }
3022}
3023
3024
3025
3026
3027
3028/*
3029=====================================
3030Remove aria-hidden from cloned slides with video elements
3031=====================================
3032*/
3033function removeAriaHiddenFromVideoSlides() {
3034    if ($notEditMode) {
3035        // Find all cloned slides that have video elements
3036        $('.custom-tabbed-autoscroll-gallery .site-slide.slick-cloned').each(function() {
3037            const $slide = $(this);
3038            const $video = $slide.find('.site-slider-video-block video');
3039            
3040            // If this cloned slide has a video, remove aria-hidden
3041            if ($video.length > 0) {
3042                $slide.removeAttr('aria-hidden');
3043                // console.log('Removed aria-hidden from cloned slide with video');
3044            }
3045        });
3046        
3047        // Also set up a MutationObserver to watch for new cloned slides
3048        const observer = new MutationObserver(function(mutations) {
3049            mutations.forEach(function(mutation) {
3050                if (mutation.type === 'childList') {
3051                    mutation.addedNodes.forEach(function(node) {
3052                        if (node.nodeType === 1) { // Element node
3053                            const $node = $(node);
3054                            if ($node.hasClass('slick-cloned')) {
3055                                const $video = $node.find('.site-slider-video-block video');
3056                                if ($video.length > 0) {
3057                                    $node.removeAttr('aria-hidden');
3058                                    // console.log('Removed aria-hidden from newly created cloned slide with video');
3059                                }
3060                            }
3061                        }
3062                    });
3063                }
3064            });
3065        });
3066        
3067        // Observe the gallery container for changes
3068        const galleryContainer = document.querySelector('.custom-tabbed-autoscroll-gallery');
3069        if (galleryContainer) {
3070            observer.observe(galleryContainer, {
3071                childList: true,
3072                subtree: true
3073            });
3074        }
3075    }
3076}
3077
3078/*
3079=====================================
3080Video Controls with Play/Pause Button
3081=====================================
3082*/
3083function initializeVideoControls() {
3084    if ($notEditMode) {
3085        // Find all video elements that need custom controls (including cloned slides)
3086        $('.custom-tabbed-autoscroll-gallery .site-slide, .site-gallery-slider-wrapper .site-slide').each(function() {
3087            // console.log('Processing video slide:', $(this).hasClass('slick-cloned') ? 'CLONED' : 'ORIGINAL');
3088
3089            const $frameVideo = $(this).find('.site-slider-video-block video');
3090            
3091            // Check if video element exists
3092            if ($frameVideo.length === 0) {
3093                return; // Skip if no video found
3094            }
3095
3096            
3097            const $frameVideoContainer = $frameVideo.closest('.site-slider-video-block');
3098            
3099            // Only add controls if they don't already exist
3100            if (!$frameVideoContainer.find('.custom-video-controls').length) {
3101                // Create custom controls container
3102                const $frameControlsContainer = $('<div class="custom-video-controls"></div>');
3103                
3104                // Create progress bar container
3105                const $frameProgressContainer = $('<div class="video-progress-container"></div>');
3106                const $frameProgressBar = $('<div class="video-progress-bar"></div>');
3107                const $frameProgressFill = $('<div class="video-progress-fill"></div>');
3108                
3109                // Create play/pause button
3110                const $framePlayPauseBtn = $('<button class="video-play-pause-btn" aria-label="Play video"></button>');
3111                
3112                // Assemble progress bar
3113                $frameProgressBar.append($frameProgressFill);
3114                $frameProgressContainer.append($frameProgressBar);
3115                
3116                // Add elements to controls container
3117                $frameControlsContainer.append($frameProgressContainer);
3118                $frameControlsContainer.append($framePlayPauseBtn);
3119                
3120                // Add controls to video container
3121                $frameVideoContainer.append($frameControlsContainer);
3122                
3123                // Set initial state
3124                $frameVideo.prop('muted', true); // Start muted
3125                $frameVideo.prop('controls', false); // Hide default controls
3126                $frameVideo.prop('autoplay', false); // Disable autoplay
3127                $frameVideo.prop('loop', false); // Disable loop
3128                
3129                // Ensure video is paused on load (with safety check)
3130                if ($frameVideo[0]) {
3131                    $frameVideo[0].pause();
3132                }
3133                
3134                // Update progress bar function
3135                function updateFrameProgress() {
3136                    if ($frameVideo[0] && !isNaN($frameVideo[0].duration)) {
3137                        const progress = ($frameVideo[0].currentTime / $frameVideo[0].duration) * 100;
3138                        $frameProgressFill.css('width', progress + '%');
3139                    }
3140                }
3141                
3142                // Handle play/pause button click
3143                $framePlayPauseBtn.on('click', function(e) {
3144                    e.preventDefault();
3145                    e.stopPropagation();
3146                    
3147                    // console.log('Video play button clicked'); // Debug log
3148                    
3149                    // Safety check to ensure video element exists
3150                    if (!$frameVideo[0]) {
3151                        console.error('Video element not found');
3152                        return;
3153                    }
3154                    
3155                    // Check if video has a source
3156                    if (!$frameVideo[0].src && !$frameVideo[0].currentSrc) {
3157                        console.error('Video has no source');
3158                        return;
3159                    }
3160                    
3161                    if ($frameVideo[0].paused) {
3162                        // Stop all audio clips before playing video
3163                        $('audio').each(function() {
3164                            if (!this.paused) {
3165                                this.pause();
3166                                this.currentTime = 0;
3167                            }
3168                        });
3169                        
3170                        // Reset all audio button states
3171                        $('.audio-button').removeClass('playing').addClass('paused');
3172                        
3173                        // Pause the Slick slider when video starts playing
3174                        const $slider = $(this).closest('.slick-initialized');
3175                        if ($slider.length > 0) {
3176                            $slider.slick('slickPause');
3177                            // console.log('Slider paused for video playback');
3178                        }
3179                        
3180                        // Play video - keep muted initially to avoid autoplay policy issues
3181                        $frameVideo[0].play().then(() => {
3182                            $framePlayPauseBtn.addClass('playing');
3183                            $framePlayPauseBtn.attr('aria-label', 'Pause video');
3184                            // Try to unmute after successful play
3185                            $frameVideo.prop('muted', false);
3186                        }).catch(error => {
3187                            console.error('Error playing video:', error);
3188                            // If play fails, try with muted
3189                            $frameVideo.prop('muted', true);
3190                            $frameVideo[0].play().then(() => {
3191                                $framePlayPauseBtn.addClass('playing');
3192                                $framePlayPauseBtn.attr('aria-label', 'Pause video');
3193                            }).catch(mutedError => {
3194                                console.error('Error playing muted video:', mutedError);
3195                            });
3196                        });
3197                    } else {
3198                        // Pause video
3199                        $frameVideo[0].pause();
3200                        $framePlayPauseBtn.removeClass('playing');
3201                        $framePlayPauseBtn.attr('aria-label', 'Play video');
3202                        
3203                        // Resume the Slick slider when video is paused
3204                        const $slider = $(this).closest('.slick-initialized');
3205                        if ($slider.length > 0) {
3206                            $slider.slick('slickPlay');
3207                            // console.log('Slider resumed after video pause');
3208                        }
3209                    }
3210                });
3211                
3212                // Handle progress bar click for seeking
3213                $frameProgressBar.on('click', function(e) {
3214                    e.preventDefault();
3215                    e.stopPropagation();
3216                    
3217                    if ($frameVideo[0] && !isNaN($frameVideo[0].duration)) {
3218                        const rect = this.getBoundingClientRect();
3219                        const clickX = e.clientX - rect.left;
3220                        const progressBarWidth = rect.width;
3221                        const seekTime = (clickX / progressBarWidth) * $frameVideo[0].duration;
3222                        
3223                        $frameVideo[0].currentTime = seekTime;
3224                    }
3225                });
3226                
3227                // Handle video timeupdate event for progress bar
3228                $frameVideo.on('timeupdate', updateFrameProgress);
3229                
3230                // Handle video loadedmetadata event
3231                $frameVideo.on('loadedmetadata', function() {
3232                    updateFrameProgress();
3233                });
3234                
3235                // Handle video ended event
3236                $frameVideo.on('ended', function() {
3237                    $framePlayPauseBtn.removeClass('playing');
3238                    $framePlayPauseBtn.attr('aria-label', 'Play video');
3239                    updateFrameProgress(); // Ensure progress bar shows 100%
3240                    
3241                    // Resume the Slick slider when video ends
3242                    const $slider = $(this).closest('.slick-initialized');
3243                    if ($slider.length > 0) {
3244                        $slider.slick('slickPlay');
3245                        // console.log('Slider resumed after video ended');
3246                    }
3247                });
3248                
3249                // Handle video play event
3250                $frameVideo.on('play', function() {
3251                    $framePlayPauseBtn.addClass('playing');
3252                    $framePlayPauseBtn.attr('aria-label', 'Pause video');
3253                });
3254                
3255                // Handle video pause event
3256                $frameVideo.on('pause', function() {
3257                    $framePlayPauseBtn.removeClass('playing');
3258                    $framePlayPauseBtn.attr('aria-label', 'Play video');
3259                });
3260            }
3261        });
3262    }
3263}
3264
3265/*
3266=====================================
3267Search Placeholder Text Function
3268=====================================
3269*/
3270
3271// Set the search placeholder text
3272document.addEventListener('DOMContentLoaded', function() {
3273    const searchInput = document.querySelector('.site-search-wrapper .site-search-block.site-search-option3 .site-search-data .site-search-input');
3274    if (searchInput) {
3275        searchInput.placeholder = 'Search this website...';
3276    }
3277});
3278
3279
3280
3281
3282// function blockquoteParallax() {
3283//     console.log('blockquoteParallax function called');
3284//     const parallaxSections = document.querySelectorAll('.site-blockquote-wrapper');
3285//     console.log('Found parallax sections:', parallaxSections.length);
3286    
3287//     if (parallaxSections.length === 0) {
3288//         console.log('No parallax sections found, returning');
3289//         return;
3290//     }
3291
3292//     function updateParallax() {
3293//         const scrollY = window.scrollY;
3294//         parallaxSections.forEach((section, index) => {
3295//             const rect = section.getBoundingClientRect();
3296//             const sectionTop = rect.top + window.scrollY;
3297//             const sectionHeight = rect.height;
3298//             const windowHeight = window.innerHeight;
3299            
3300//             // Calculate how much of the section is visible (0 to 1)
3301//             const scrolled = scrollY - sectionTop;
3302//             const progress = Math.max(0, Math.min(1, scrolled / (sectionHeight + windowHeight)));
3303            
3304//             // Different speeds for different elements - more visible for testing
3305//             const speedBefore = 1.5; // globe moves slower
3306//             const speedAfter = 2.0;  // outer rings move faster
3307            
3308//             // Apply parallax transforms - start moving immediately when section comes into view
3309//             const beforeOffset = progress * speedBefore * 100; // Start at 0, move as section scrolls
3310//             const afterOffset = progress * speedAfter * 100;
3311            
3312//             // Debug logging for first section
3313//             if (index === 0) {
3314//                 console.log('Section 0 - Progress:', progress, 'Before offset:', beforeOffset, 'After offset:', afterOffset);
3315//             }
3316            
3317//             section.style.setProperty('--before-transform', `${beforeOffset}px`);
3318//             section.style.setProperty('--after-transform', `${afterOffset}px`);
3319            
3320//             // Temporary test - add a visible indicator that the function is working
3321//             if (index === 0) {
3322//                 section.style.border = `5px solid red`;
3323//                 section.style.borderColor = `hsl(${progress * 360}, 70%, 50%)`;
3324//                 section.style.backgroundColor = `rgba(255, 0, 0, 0.1)`;
3325//                 section.style.position = 'relative';
3326//                 section.style.zIndex = '1000';
3327//             }
3328//         });
3329//         requestAnimationFrame(updateParallax);
3330//     }
3331
3332//     updateParallax();
3333// }
3334
3335function cloneGalleryButton() {
3336    $('.site-gallery-image-wrapper').each(function() {
3337        const $wrapper = $(this);
3338        const $slide = $wrapper.find('.site-slide');
3339        
3340        if ($slide.length > 0) {
3341            const $buttonContainer = $slide.find('.site-slider-more-desc');
3342            
3343            if ($buttonContainer.length > 0) {
3344                $wrapper.addClass('has-mobile-button');
3345                const $clonedButton = $buttonContainer.clone();
3346                $clonedButton.addClass('gallery-button-container');
3347                $clonedButton.insertAfter($wrapper);
3348            }
3349        }
3350    });
3351}
3352
3353
3354
3355function utilityLinksClone() {
3356    $('#site-header-utility-links').each(function() {
3357        const $utilityLinks = $(this);
3358        const $targetContainer = $('.site-header-hamburger-wrapper .site-navigation-container .site-navigation-wrapper');
3359        
3360        // Check if clone already exists to avoid duplicates
3361        if ($targetContainer.length && !$targetContainer.find('.site-header-utility-links').length) {
3362            const $clonedLinks = $utilityLinks.clone();
3363            $targetContainer.append($clonedLinks);
3364        }
3365    });
3366}
3367
3368

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.