let mm = gsap.matchMedia(); CustomEase.create( "loaderEase", "M0,0,C0,0,0.10,0.34,0.238,0.442,0.305,0.506,0.322,0.514,0.396,0.54,0.478,0.568,0.468,0.56,0.522,0.584,0.572,0.606,0.61,0.719,0.714,0.826,0.798,0.912,1,1,1,1" ); CustomEase.create("ease-1", "M0,0 C0.15,0 0.15,1 1,1"); CustomEase.create( "ease-2", "M0,0 C0.071,0.505 0.192,0.726 0.318,0.852 0.45,0.984 0.504,1 1,1" ); CustomEase.create("ease-3", "0.65, 0.01, 0.05, 0.99"); const lenis = new Lenis(); lenis.on("scroll", ScrollTrigger.update); gsap.ticker.add((time) => { lenis.raf(time * 1000); }); gsap.ticker.lagSmoothing(0); function resetWebflow() { Webflow.destroy(); Webflow.ready(); Webflow.require("ix2").init(); } function solutionsPage() { $(".about-hero").each(function () { let aboutHero = $(this); let aboutImg = aboutHero.find(".about-hero_img"); let aboutImgEl = aboutImg.find(".img-fill"); let aboutTitle = aboutHero.find(".about-hero_above-wrap"); let aboutClipTitle = aboutHero.find(".about-hero_above"); let aboutEndTrig = aboutHero.closest(".philosophy-section"); gsap.set(aboutClipTitle, { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", }); let aboutIntroTl = gsap.timeline({ scrollTrigger: { trigger: aboutHero, start: "clamp(top top)", end: "bottom top", scrub: true, pin: aboutHero, pinSpacing: true, onUpdate: function (self) { let imgRect = aboutImg[0].getBoundingClientRect(); let titleRect = aboutTitle[0].getBoundingClientRect(); let imgTop = imgRect.top; let titleBottom = titleRect.bottom; let titleTop = titleRect.top; let clipPath = "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)"; if (imgTop <= titleBottom && imgTop >= titleTop) { let progress = (titleBottom - imgTop) / (titleBottom - titleTop); let clipPercent = 100 - progress * 100; clipPath = `polygon(0% 100%, 100% 100%, 100% ${clipPercent}%, 0% ${clipPercent}%)`; } else if (imgTop < titleTop) { clipPath = "polygon(0% 100%, 100% 100%, 100% 0%, 0% 0%)"; } gsap.set(aboutClipTitle, { clipPath: clipPath }); }, }, }); mm.add("(min-width: 992px) and (pointer: fine)", () => { aboutIntroTl.fromTo( aboutImg, { yPercent: -20, clipPath: "polygon(30% 0%, 70% 0%, 70% 100%, 30% 100%)", }, { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", yPercent: -100, immediateRender: false, ease: "linear", } ); }); mm.add("(max-width: 991px) and (pointer: coarse)", () => { aboutIntroTl.fromTo( aboutImg, { yPercent: -20, clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", }, { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", yPercent: -100, immediateRender: false, ease: "linear", } ); }); aboutIntroTl.fromTo( aboutImgEl, { scale: 1.4, }, { scale: 1, immediateRender: false, ease: "linear", }, "<" ); mm.add("(min-width: 992px) and (pointer: fine)", () => { aboutIntroTl.fromTo( aboutClipTitle, { yPercent: 0, }, { scrollTrigger: { trigger: aboutImg, start: "clamp(top center)", end: "clamp(bottom top)", scrub: true, }, immediateRender: false, yPercent: -200, ease: "linear", } ); aboutIntroTl.to([aboutImg, aboutClipTitle], { scrollTrigger: { trigger: aboutEndTrig, start: "clamp(top bottom)", end: "clamp(top top)", scrub: true, }, y: "100vh", ease: "linear", }); }); }); $(".services-section").each(function () { let servicesSection = $(this); let compSection = servicesSection.find(".comp_section"); let compTriggers = compSection.find(".comp_item"); let compTargets = compSection.find(".comp-logo_item"); let compWrap = compSection.find(".comp_wrap"); let compBg = compSection.find("[comp-bg]"); let compOverlay = servicesSection.find(".services-bg_overlay"); let waysSection = servicesSection.find(".way_section"); let bgAlternative = servicesSection.find(".services-bg_alt"); let overlayEndTrig = servicesSection.find(".services-list_wrap"); let overlayStartTrig = servicesSection.find(".services-header_wrap"); let cropTrigger = servicesSection.find(".comp_wrap"); function makeItemsActive(index) { compTriggers.add(compTargets).removeClass("show-logo"); compTriggers.eq(index).addClass("show-logo"); compTargets.eq(index).addClass("show-logo"); } compTriggers.on("mouseenter", function () { let itemIndex = $(this).index(); makeItemsActive(itemIndex); compSection.addClass("hide-static"); }); compWrap.on("mouseleave", function () { compTargets.removeClass("show-logo"); compSection.removeClass("hide-static"); }); let compBgTl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: overlayStartTrig, start: "top top", end: "top top", endTrigger: compSection, scrub: true, }, }); compBgTl.fromTo( compOverlay, { opacity: 0, }, { opacity: 1, onComplete: () => { gsap.set(bgAlternative, { opacity: 1 }); }, } ); compBgTl.fromTo( compBg, { opacity: 0, }, { opacity: 1, scrollTrigger: { trigger: compSection, start: "top +15px", end: "top top", scrub: true, }, onReverseComplete: () => { gsap.set(bgAlternative, { opacity: 0 }); }, } ); compBgTl.fromTo( compBg, { opacity: 1, }, { scrollTrigger: { trigger: compSection, start: "bottom bottom-=15px", end: "bottom bottom", scrub: true, }, opacity: 0, immediateRender: false, ease: "linear", } ); compBgTl.fromTo( compOverlay, { opacity: 1, }, { scrollTrigger: { trigger: waysSection, start: "top bottom", end: "top top", scrub: true, }, opacity: 0, immediateRender: false, ease: "linear", } ); }); $(".philosophy-section").each(function () { let philSection = $(this); let philTriggers = philSection.find(".philosophy-trig"); let philPanels = philSection.find(".philosophy-panel_left"); let philStickyOuter = philSection.find(".philosophy-panels"); let philEndTrig = philSection.find(".philosophy-triggers"); let philImg = philSection.find(".philosophy-img_item"); mm.add("(min-width: 992px) and (pointer: fine)", () => { let pinPhilOuter = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: philStickyOuter, start: "top top", end: "bottom bottom", endTrigger: philEndTrig, pin: philStickyOuter, pinSpacing: false, scrub: true, }, }); philTriggers.each(function (index) { let currentPanel = philPanels.eq(index); let currentImg = philImg.eq(index); let philTl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: $(this), start: "top bottom", end: "top top", scrub: true, }, }); philTl.to(currentPanel, { top: "0%", }); ScrollTrigger.create({ trigger: $(this), start: "top 20%", end: "bottom 20%", onEnter: () => { currentImg.addClass("current-phil"); }, onLeave: () => { currentImg.removeClass("current-phil"); }, onEnterBack: () => { currentImg.addClass("current-phil"); }, onLeaveBack: () => { currentImg.removeClass("current-phil"); }, }); }); }); }); function setSolutionPositions() { $(".solutions-section").each(function () { let solutionsSection = $(this); let solutionItems = solutionsSection.find(".solutions-item"); let solutionTopHeight = 0; let solutionsIntro = solutionsSection.find(".solutions-intro_item"); let solutionsIntroText = solutionsIntro.find(".solutions-intro"); mm.add("(min-width: 992px) and (pointer: fine)", () => { let solutionsIntroTl = gsap.timeline({ scrollTrigger: { trigger: solutionsSection, start: "top top", end: "+1000px", scrub: true, }, }); solutionsIntroTl.to(solutionsIntro, { yPercent: 150, ease: "power1.in", }); solutionsIntroTl.to( solutionsIntroText, { scale: 1.2, ease: "linear", scrollTrigger: { trigger: solutionsSection, start: "top center", end: "top top", scrub: true, }, }, "<" ); let firstSolutionTop = solutionItems.first().find(".solutions-top"); if (firstSolutionTop.length) { solutionTopHeight = firstSolutionTop.outerHeight(); } solutionItems.each(function (index) { let solutionItem = $(this); let pinPosition = index * solutionTopHeight; ScrollTrigger.getAll().forEach((trigger) => { if (trigger.trigger === solutionItem[0]) { trigger.kill(); } }); let solutionPinTl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: solutionItem, start: `top top+=${pinPosition}`, end: "bottom bottom", endTrigger: solutionsSection, pin: solutionItem, pinSpacing: false, scrub: true, }, }); }); }); }); } function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } mm.add("(min-width: 992px) and (pointer: fine)", () => { setSolutionPositions(); }); $(window).on( "resize", debounce(() => { mm.add("(min-width: 992px) and (pointer: fine)", () => { setSolutionPositions(); }); }, 250) ); } function workPage() { $(".page-main").removeClass("nav-hidden"); $(".work-page").each(function () { let workPage = $(this); let workLogo = workPage.find(".logo-wrap"); let workTriggers = workPage.find(".work-triggers_item"); let workLargeItems = workPage.find(".work-large_item"); let workLargeitemImg = workPage.find(".work-large_item"); let workTitleWrap = workPage.find(".work-content_item"); let workBg = workPage.find(".work-large_bg"); let workSmallItems = workPage.find(".work-small_item"); let filterTrig = workPage.find("[filter-popup_trig]"); let closeFilter = workPage.find("[filter-popup_close]"); let filterPopup = workPage.find("[filter-popup]"); let filterBtnText = workPage.find(".filter-indicator"); let currentFilter = { type: null, value: null, }; let originalFilterText = filterBtnText.text(); let originalElements = { triggers: [], largeItems: [], contentItems: [], smallItems: [], }; let scrollAnimations = []; let snapObserver = null; let triggerElements = []; let inViewObserver = null; let backgroundObserver = null; function storeOriginalElements() { workTriggers.each(function (index) { originalElements.triggers.push($(this).clone(true)); originalElements.largeItems.push(workLargeItems.eq(index).clone(true)); originalElements.contentItems.push(workTitleWrap.eq(index).clone(true)); originalElements.smallItems.push(workSmallItems.eq(index).clone(true)); }); } function updateElementReferences() { workTriggers = workPage.find(".work-triggers_item"); workLargeItems = workPage.find(".work-large_item"); workTitleWrap = workPage.find(".work-content_item"); workSmallItems = workPage.find(".work-small_item"); triggerElements = []; workTriggers.each(function () { triggerElements.push(this); }); } function clearTransforms(element) { element.css("transform", ""); element.find("*").css("transform", ""); gsap.set(element[0], { clearProps: "all" }); element.find("*").each(function () { gsap.set(this, { clearProps: "all" }); }); } function clearAllContainers() { workPage.find(".work-triggers_list").empty(); workPage.find(".work-large_list").empty(); workPage.find(".work-content_list").empty(); workPage.find(".work-small_list").empty(); } function appendElements(elements) { elements.forEach((element) => { let freshTrigger = element.trigger.clone(true); let freshLarge = element.large.clone(true); let freshContent = element.content.clone(true); let freshSmall = element.small.clone(true); [freshLarge, freshContent, freshSmall].forEach(clearTransforms); workPage.find(".work-triggers_list").append(freshTrigger); workPage.find(".work-large_list").append(freshLarge); workPage.find(".work-content_list").append(freshContent); workPage.find(".work-small_list").append(freshSmall); }); } function getImageSrcFromLargeItem(index) { if (index >= 0 && index < workLargeItems.length) { let largeItem = workLargeItems.eq(index); let img = largeItem.find("img"); if (img.length > 0) { return img.attr("src"); } let bgImage = largeItem.css("background-image"); if (bgImage && bgImage !== "none") { let match = bgImage.match(/url\(["']?(.*?)["']?\)/); if (match) { return match[1]; } } } return null; } function updateBackgroundImage(index) { let imageSrc = getImageSrcFromLargeItem(index); if (imageSrc) { gsap.to(workPage, { duration: 0.01, ease: "linear", onComplete: function () { workBg.attr("src", imageSrc); }, }); } } function createBackgroundObserver() { if (backgroundObserver) { backgroundObserver.forEach((trigger) => { if (trigger.kill) trigger.kill(); }); backgroundObserver = null; } if (workTriggers.length === 0) return; backgroundObserver = []; workTriggers.each(function (index) { let trigger = workTriggers.eq(index)[0]; let bgTrigger = ScrollTrigger.create({ trigger: trigger, start: "top center", end: "bottom center", onEnter: () => updateBackgroundImage(index), onEnterBack: () => updateBackgroundImage(index), refreshPriority: -3, }); backgroundObserver.push(bgTrigger); scrollAnimations.push(bgTrigger); }); setTimeout(() => { let activeIndex = getCurrentActiveIndex(); if (activeIndex >= 0) { updateBackgroundImage(activeIndex); } }, 50); } function clearInViewClasses() { workSmallItems.removeClass("in-view"); } function setInViewClass(index) { clearInViewClasses(); if (index >= 0 && index < workSmallItems.length) { workSmallItems.eq(index).addClass("in-view"); } } function createInViewObserver() { if (inViewObserver && inViewObserver.kill) { inViewObserver.kill(); inViewObserver = null; } if (workTriggers.length === 0) return; workTriggers.each(function (index) { let trigger = workTriggers.eq(index)[0]; let inViewTrigger = ScrollTrigger.create({ trigger: trigger, start: "top center", end: "bottom center", onEnter: () => setInViewClass(index), onEnterBack: () => setInViewClass(index), refreshPriority: -2, }); scrollAnimations.push(inViewTrigger); }); setTimeout(() => { let activeIndex = getCurrentActiveIndex(); if (activeIndex >= 0) { setInViewClass(activeIndex); } }, 50); } function killScrollAnimations() { ScrollTrigger.getAll().forEach((trigger) => { if (trigger.trigger && workPage[0].contains(trigger.trigger)) { if (trigger.vars && trigger.vars.id === "nav-animation") { return; } trigger.kill(); } }); scrollAnimations.forEach((animation) => { if (animation.kill) { animation.kill(); } }); scrollAnimations = []; if (snapObserver && snapObserver.kill) { snapObserver.kill(); snapObserver = null; } if (inViewObserver && inViewObserver.kill) { inViewObserver.kill(); inViewObserver = null; } if (backgroundObserver) { backgroundObserver.forEach((trigger) => { if (trigger.kill) trigger.kill(); }); backgroundObserver = null; } } function forceResetImageTransforms() { workLargeItems.each(function (index) { let largeImg = $(this).find(".work-large_img"); if (largeImg.length > 0) { gsap.killTweensOf(largeImg[0]); gsap.set(largeImg[0], { clearProps: "all" }); largeImg[0].style.transform = ""; largeImg[0].style.removeProperty("transform"); if (index === 0) { gsap.set(largeImg[0], { yPercent: -5 }); } else { gsap.set(largeImg[0], { yPercent: 0 }); } } }); } function resetElementTransforms() { forceResetImageTransforms(); workLargeItems.each(function () { gsap.killTweensOf(this); gsap.set(this, { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", clearProps: "all", }); }); workTitleWrap.each(function () { let workTitle = $(this).find("[work-title]"); if (workTitle.length > 0) { gsap.killTweensOf(workTitle[0]); gsap.set(workTitle, { yPercent: 100, clearProps: "transform", }); } }); if (workSmallItems.length > 0) { workSmallItems.each(function () { gsap.killTweensOf(this); gsap.set(this, { yPercent: 0, clearProps: "transform", }); }); } clearInViewClasses(); } function createScrollAnimations() { resetElementTransforms(); workTriggers.each(function (index) { let trigger = workTriggers.eq(index)[0]; let correspondingItem = workLargeItems.eq(index)[0]; let correspondingLargeImg = $(correspondingItem).find(".work-large_img"); let correspondingTitleWrap = workTitleWrap.eq(index); let workTitle = correspondingTitleWrap.find("[work-title]"); let largeItemAnimation = gsap.fromTo( correspondingItem, { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", }, { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", ease: "none", scrollTrigger: { trigger: trigger, start: "top bottom", end: "top top", scrub: true, refreshPriority: -1, }, } ); scrollAnimations.push(largeItemAnimation); if (correspondingLargeImg.length > 0) { let imageMovementAnimation = gsap.fromTo( correspondingLargeImg, { yPercent: 0, }, { yPercent: -5, ease: "none", scrollTrigger: { trigger: trigger, start: "top bottom", end: "top top", scrub: true, refreshPriority: -1, }, } ); scrollAnimations.push(imageMovementAnimation); } if (index > 0) { let previousItem = workLargeItems.eq(index - 1)[0]; let previousLargeImg = $(previousItem).find(".work-large_img"); if (previousLargeImg.length > 0) { let previousImageMovementAnimation = gsap.fromTo( previousLargeImg, { yPercent: -5, }, { yPercent: -10, ease: "none", scrollTrigger: { trigger: trigger, start: "top bottom", end: "top top", scrub: true, refreshPriority: -1, }, } ); scrollAnimations.push(previousImageMovementAnimation); } } if (workTitle.length > 0) { let titleAnimation = gsap.fromTo( workTitle, { yPercent: 100, }, { yPercent: -100, ease: "none", scrollTrigger: { trigger: trigger, start: "top bottom", end: "bottom top", scrub: true, refreshPriority: -1, }, } ); scrollAnimations.push(titleAnimation); } }); if (workSmallItems.length > 0 && workTriggers.length > 0) { let firstTrigger = workTriggers.eq(0)[0]; let lastTrigger = workTriggers.eq(workTriggers.length - 1)[0]; let smallItemsAnimation = gsap.fromTo( workSmallItems, { yPercent: 0, }, { yPercent: -100 * (workTriggers.length - 1), ease: "none", scrollTrigger: { trigger: firstTrigger, endTrigger: lastTrigger, start: "top top", end: "top top", scrub: true, refreshPriority: -1, }, } ); scrollAnimations.push(smallItemsAnimation); } } function createSnapping() { if (workTriggers.length === 0) return; let isSnapping = false; let snapTimeoutId; function resetSnapping() { clearTimeout(snapTimeoutId); isSnapping = false; } snapObserver = ScrollTrigger.observe({ target: window, type: "scroll", debounce: true, onStop: () => { clearTimeout(snapTimeoutId); if (isSnapping) return; let bestTrigger = null; let bestScore = 0; triggerElements.forEach((trigger) => { const rect = trigger.getBoundingClientRect(); const viewportHeight = window.innerHeight; const visibleHeight = Math.max( 0, Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0) ); const visibilityRatio = visibleHeight / rect.height; const triggerCenter = rect.top + rect.height / 2; const viewportCenter = viewportHeight / 2; const distanceFromCenter = Math.abs(triggerCenter - viewportCenter); const proximityScore = Math.max( 0, 1 - distanceFromCenter / viewportHeight ); const score = visibilityRatio * 0.6 + proximityScore * 0.4; if (score > bestScore) { bestScore = score; bestTrigger = trigger; } }); if (bestTrigger) { const currentScroll = window.pageYOffset; const targetScroll = bestTrigger.offsetTop; const distance = Math.abs(currentScroll - targetScroll); if (distance > 10) { isSnapping = true; snapTimeoutId = setTimeout(() => { resetSnapping(); }, 100); lenis.scrollTo(targetScroll, { duration: 0.4, easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), onComplete: () => { resetSnapping(); }, }); } } }, onStart: () => { if (isSnapping) { resetSnapping(); } }, }); } function rebuildScrollSystem() { lenis.stop(); setTimeout(() => { createScrollAnimations(); createInViewObserver(); createBackgroundObserver(); setupNavAnimation(); ScrollTrigger.refresh(true); if (lenis && lenis.resize) { lenis.resize(); } lenis.start(); setTimeout(() => { createSnapping(); }, 50); }, 500); } function getCurrentActiveIndex() { let activeIndex = -1; let bestScore = 0; workTriggers.each(function (index) { let trigger = this; const rect = trigger.getBoundingClientRect(); const viewportHeight = window.innerHeight; const viewportCenter = viewportHeight / 2; if (rect.top <= viewportCenter && rect.bottom >= viewportCenter) { const triggerCenter = rect.top + rect.height / 2; const distanceFromCenter = Math.abs(triggerCenter - viewportCenter); const score = Math.max(0, 1 - distanceFromCenter / viewportHeight); if (score > bestScore) { bestScore = score; activeIndex = index; } } }); if (activeIndex === -1) { let closestDistance = Infinity; workTriggers.each(function (index) { const rect = this.getBoundingClientRect(); const triggerCenter = rect.top + rect.height / 2; const viewportCenter = window.innerHeight / 2; const distance = Math.abs(triggerCenter - viewportCenter); if (distance < closestDistance) { closestDistance = distance; activeIndex = index; } }); } return activeIndex; } function animateFilterTransition(callback) { let activeIndex = getCurrentActiveIndex(); let currentLargeItems = workLargeItems; let currentTitleWraps = workTitleWrap; lenis.stop(); let filterTl = gsap.timeline({ onComplete: () => { setTimeout(() => { lenis.start(); }, 50); }, }); filterTl.call( () => { callback(); }, null, 0 ); filterTl.call( () => { let newLargeItems = workLargeItems; let newTitleWraps = workTitleWrap; setTimeout(() => { updateBackgroundImage(0); }, 200); if (newLargeItems.length > 0) { gsap.set(newLargeItems.eq(0), { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", }); } if (newLargeItems.length > 0) { let firstNewLargeItem = newLargeItems.eq(0); gsap.fromTo( firstNewLargeItem, { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", }, { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", duration: 0.4, ease: "expo.out", } ); gsap.to(workBg, { yPercent: -5, duration: 0.4, clearProps: "all", ease: "expo.out", }); } if (newTitleWraps.length > 0) { let firstTitleWrap = newTitleWraps.eq(0); let firstWorkTitle = firstTitleWrap.find("[work-title]"); if (firstWorkTitle.length > 0) { gsap.set(firstWorkTitle, { yPercent: 100 }); gsap.to(firstWorkTitle, { yPercent: 0, duration: 0.4, ease: "power2.inOut", }); } } }, null, 0 ); } function showAllItems() { currentFilter.type = null; currentFilter.value = null; filterTrig.removeClass("filter-visible"); filterBtnText.text(originalFilterText); animateFilterTransition(() => { restoreAllElements(); }); } function filterItems(filterType, filterValue, filterDisplayText) { currentFilter.type = filterType; currentFilter.value = filterValue; filterTrig.addClass("filter-visible"); filterBtnText.text(filterDisplayText); animateFilterTransition(() => { rebuildDOMWithFilter(filterType, filterValue); }); } function restoreAllElements() { killScrollAnimations(); clearAllContainers(); let elementsToAppend = originalElements.triggers.map( (trigger, index) => ({ trigger: trigger, large: originalElements.largeItems[index], content: originalElements.contentItems[index], small: originalElements.smallItems[index], }) ); appendElements(elementsToAppend); updateElementReferences(); animateNewElementsIn(() => { setTimeout(() => { lenis.scrollTo(0, { immediate: true, }); }, 160); rebuildScrollSystem(); }); } function rebuildDOMWithFilter(filterType, filterValue) { killScrollAnimations(); let attributeName = "filter-" + filterType; let matchingElements = []; originalElements.triggers.forEach((trigger, index) => { let itemValue = trigger.attr(attributeName); if (itemValue === filterValue) { matchingElements.push({ trigger: trigger, large: originalElements.largeItems[index], content: originalElements.contentItems[index], small: originalElements.smallItems[index], }); } }); clearAllContainers(); appendElements(matchingElements); updateElementReferences(); animateNewElementsIn(() => { setTimeout(() => { lenis.scrollTo(0, { immediate: true, }); }, 160); rebuildScrollSystem(); }); } function animateNewElementsIn(callback) { workLargeItems.each(function (index) { gsap.set(this, { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", }); let largeImg = $(this).find(".work-large_img"); if (largeImg.length > 0) { if (index === 0) { gsap.set(largeImg, { yPercent: -5 }); } else { gsap.set(largeImg, { yPercent: 0 }); } } }); workTitleWrap.each(function () { let workTitle = $(this).find("[work-title]"); if (workTitle.length > 0) { gsap.set(workTitle, { yPercent: 100, }); } }); callback(); } function setupPopupHandlers() { filterTrig.on("click", function (e) { e.preventDefault(); e.stopPropagation(); if (filterTrig.hasClass("filter-visible")) { showAllItems(); lenis.start(); } else { workPage.toggleClass("show-popup"); lenis.stop(); } }); closeFilter.on("click", function (e) { e.preventDefault(); e.stopPropagation(); workPage.removeClass("show-popup"); lenis.start(); }); filterPopup.on("click", function (e) { e.stopPropagation(); }); workPage.on("click", function (e) { workPage.removeClass("show-popup"); lenis.start(); }); } function getAvailableFilterValues() { let availableCapabilities = new Set(); let availableCompanies = new Set(); originalElements.triggers.forEach((trigger) => { let capabilityValue = trigger.attr("filter-capability"); let companyValue = trigger.attr("filter-company"); if (capabilityValue) { availableCapabilities.add(capabilityValue); } if (companyValue) { availableCompanies.add(companyValue); } }); return { capabilities: availableCapabilities, companies: availableCompanies, }; } function updateFilterVisibility() { let availableValues = getAvailableFilterValues(); filterPopup.find("[filter-capability_trigger]").each(function () { let filterValue = $(this).attr("filter-capability_trigger"); let filterButton = $(this); if (availableValues.capabilities.has(filterValue)) { filterButton.show(); } else { filterButton.hide(); } }); filterPopup.find("[filter-company_trigger]").each(function () { let filterValue = $(this).attr("filter-company_trigger"); let filterButton = $(this); if (availableValues.companies.has(filterValue)) { filterButton.show(); } else { filterButton.hide(); } }); let visibleCapabilityFilters = filterPopup.find( "[filter-capability_trigger]:visible" ); let visibleCompanyFilters = filterPopup.find( "[filter-company_trigger]:visible" ); let capabilitySection = filterPopup.find("[filter-capability_section]"); if (capabilitySection.length && visibleCapabilityFilters.length === 0) { capabilitySection.hide(); } else if (capabilitySection.length) { capabilitySection.show(); } let companySection = filterPopup.find("[filter-company_section]"); if (companySection.length && visibleCompanyFilters.length === 0) { companySection.hide(); } else if (companySection.length) { companySection.show(); } } function setupFilterHandlers() { filterPopup.find("[filter-capability_trigger]").on("click", function (e) { e.preventDefault(); e.stopPropagation(); let filterValue = $(this).attr("filter-capability_trigger"); let filterDisplayText = $(this).text().trim(); filterItems("capability", filterValue, filterDisplayText); setTimeout(() => { workPage.removeClass("show-popup"); lenis.start(); }, 150); }); filterPopup.find("[filter-company_trigger]").on("click", function (e) { e.preventDefault(); e.stopPropagation(); let filterValue = $(this).attr("filter-company_trigger"); let filterDisplayText = $(this).text().trim(); filterItems("company", filterValue, filterDisplayText); setTimeout(() => { workPage.removeClass("show-popup"); lenis.start(); }, 150); }); filterPopup.find("[filter-clear]").on("click", function (e) { e.preventDefault(); e.stopPropagation(); showAllItems(); setTimeout(() => { workPage.removeClass("show-popup"); lenis.start(); }, 150); }); } function setupNavAnimation() { let workNavTl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { id: "nav-animation", trigger: workPage, start: "clamp(top top)", end: "+400px", scrub: true, }, }); workNavTl.fromTo( workLogo, { y: "0rem", }, { y: "-6rem", } ); } function init() { storeOriginalElements(); updateElementReferences(); updateFilterVisibility(); setupPopupHandlers(); setupFilterHandlers(); setupNavAnimation(); createScrollAnimations(); createInViewObserver(); createBackgroundObserver(); createSnapping(); } init(); }); } function globalScripts() { $("img").attr("loading", "auto"); $(window).scrollTop(0); lenis.resize(); lenis.start(); ScrollTrigger.refresh(); $("[line-expand_el]").on("mouseenter", function () { $(this).addClass("expanded"); }); $("[line-expand_el]").on("mouseleave", function () { $(this).removeClass("expanded"); }); $(".commitments-item").on("click", function () { if ($(this).hasClass("expanded")) { $(this).removeClass("expanded"); setTimeout(function () { lenis.resize(); }, 400); } else { $(".commitments-item").removeClass("expanded"); $(this).addClass("expanded"); setTimeout(function () { lenis.resize(); }, 400); } }); $("[title-loop]").each(function () { let titleSection = $(this); let startTrigger = titleSection; let titleItems = titleSection.find("[titles-item]"); titleItems.each(function () { let titleItemEl = $(this); let endTrigger = titleItemEl.find("[titles-end]"); let hiddenTitles = titleItemEl.find(".looping-words_hidden"); let titleInnerItems = titleItemEl.find(".looping-words__p"); let itemCount = titleInnerItems.length; let endPercentage = -(itemCount - 1) * 100 + "%"; let titleScrubTl = gsap.timeline({ defaults: { ease: "sine.inOut", }, scrollTrigger: { trigger: startTrigger, start: "clamp(top top)", end: "clamp(top 5%)", endTrigger: endTrigger, scrub: 1, }, }); titleScrubTl.fromTo( hiddenTitles, { top: "0%", }, { top: endPercentage, } ); }); }); $(".page_wrap").each(function (index) { let pageEl = $(this); let pMain = pageEl.find(".page-main"); let fadeUpItems = pageEl.find("[pl-fade_up]"); let shiftUpItems = pageEl.find("[pl-shift_up]"); ScrollTrigger.create({ trigger: $(this), start: "top top", end: "+400px", onLeave: ({ self }) => { pMain.addClass("nav-scrolled"); }, onEnterBack: ({ self }) => { pMain.removeClass("nav-scrolled"); }, }); let ctaCursorWrap = pageEl.find("[cta-cursor_wrap]"); mm.add("(min-width: 992px) and (pointer: fine)", () => { ctaCursorWrap.each(function () { let $wrap = $(this); let $cursor = $wrap.find("[cta-cursor_el]"); let cursorHeight = $cursor.outerHeight(); let cursorYTl = gsap.quickTo($cursor[0], "y", { duration: 0.4, ease: "power3", }); let lastMouseY = null; const updateCursor = (clientY) => { let rect = $wrap[0].getBoundingClientRect(); relativeY = clientY - rect.top - cursorHeight / 2; relativeY = Math.max( 0, Math.min(relativeY, rect.height - cursorHeight) ); cursorYTl(relativeY); }; $wrap.on("mousemove mouseover", function (e) { lastMouseY = e.clientY; updateCursor(e.clientY); }); $(window).on("scroll", function () { if (lastMouseY !== null) { updateCursor(lastMouseY); } }); }); }); $("[share-el]").each(function () { const shareElement = $(this); const textElement = shareElement.find("[share-el_text]"); const originalText = textElement.text(); shareElement.on("click", function () { navigator.clipboard .writeText(window.location.href) .then(function () { textElement.text("Link Copied"); setTimeout(function () { textElement.text(originalText); }, 1000); }) .catch(function (err) { console.error("Failed to copy URL: ", err); textElement.text("Copy failed"); setTimeout(function () { textElement.text(originalText); }, 1000); }); }); }); $("[static-count_el]").each(function () { var itemCount = $(this).find("[static-count_item]").length; $(this).find("[static-count_text]").text(itemCount); }); }); $(".img-switch_component").each(function () { let $root = $(this); let $listElement = $root.find(".client-list"); let $rows = $listElement.find(".client-item_layout"); let $mediaContainer = $root.find(".media-container"); if ($listElement.length === 0 || $mediaContainer.length === 0) { console.warn("Required elements for img-switch component not found"); return; } let mediasUrl = []; $root.find(".medias img").each(function () { let src = $(this).attr("src"); if (src) { mediasUrl.push(src); } }); if (mediasUrl.length === 0) { console.warn("No media images found for img-switch component"); return; } mm.add("(min-width: 992px) and (pointer: fine)", () => { $listElement.on("mouseenter", function () { $mediaContainer.addClass("on"); }); $listElement.on("mouseleave", function () { $mediaContainer.removeClass("on"); $mediaContainer.empty(); }); $rows.each(function (index) { $(this).on("mouseenter", function () { if (index < mediasUrl.length) { createMedia(index); } }); }); }); mm.add("(max-width: 991px) and (pointer: coarse)", () => { $mediaContainer.addClass("on"); $rows.each(function (index) { let $row = $(this); ScrollTrigger.create({ trigger: $row[0], start: "top center", end: "bottom center", onEnter: () => { if (index < mediasUrl.length) { createMedia(index); } }, onEnterBack: () => { if (index < mediasUrl.length) { createMedia(index); } }, }); }); ScrollTrigger.create({ trigger: $listElement[0], start: "top center", end: "bottom top", onEnter: () => { $mediaContainer.addClass("on"); $root.addClass("on"); }, onEnterBack: () => { $mediaContainer.addClass("on"); $root.addClass("on"); }, onLeaveBack: () => { $mediaContainer.removeClass("on"); $root.removeClass("on"); $mediaContainer.empty(); }, onLeave: () => { $mediaContainer.removeClass("on"); $root.removeClass("on"); $mediaContainer.empty(); }, }); }); function createMedia(index) { if (index >= mediasUrl.length) { console.warn("Media index out of bounds:", index); return; } let $div = $("