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 = $("
"); let $image = $("").attr("src", mediasUrl[index]); $div.append($image); $mediaContainer.append($div); if (typeof gsap !== "undefined") { gsap.to([$div[0], $image[0]], { y: 0, duration: 0.3, ease: "power3.out", }); } else { console.warn("GSAP not loaded, skipping animation"); } if ($mediaContainer.children().length > 20) { $mediaContainer.children().first().remove(); } } }); $(".clients_section").each(function () { let clientsSection = $(this); let pinEl = clientsSection.find(".clients-header_img-wrap"); let imgFloat1 = clientsSection.find(".featured-img_wrap-large"); let imgFloat2 = clientsSection.find(".featured-img_wrap"); mm.add("(min-width: 992px) and (pointer: fine)", () => { let pinClientsDesktopTl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: pinEl, start: "center center", end: "bottom top", endTrigger: clientsSection, pin: true, scrub: true, }, }); }); mm.add("(max-width: 991px) and (pointer: coarse)", () => { let pinClientsMobileTl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: pinEl, start: "top 64px", end: "bottom top", endTrigger: clientsSection, pin: pinEl, scrub: true, }, }); }); let imgFloat1Tl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: clientsSection, start: "top bottom", end: "bottom bottom", scrub: true, }, }); imgFloat1Tl.fromTo( imgFloat1, { y: "0vh", }, { y: "-80vh", } ); let imgFloat2Tl = gsap.timeline({ defaults: { ease: "none", }, scrollTrigger: { trigger: clientsSection, start: "top bottom", end: "bottom bottom", scrub: true, }, }); imgFloat2Tl.fromTo( imgFloat2, { y: "0vh", }, { y: "-20vh", } ); }); $(".agencies-items_contain").each(function () { const $container = $(this); const $flipEl = $container.find(".agency-flip_el"); const $agencyItems = $container.find(".agencies-item"); const $agencyScrollWrap = $container.find(".agencies-scroll_wrap"); const $hiddenHeight = $container.find(".agencies-name_contain"); const $isMoving = $container.find(".agencies-item.is-moving"); const $flipTrigger = $container.find(".agencies-list"); ScrollTrigger.create({ trigger: $flipEl[0], start: "center center", end: () => `bottom center+=${$hiddenHeight.outerHeight() / 2}`, endTrigger: $agencyScrollWrap, pin: $flipEl[0], pinSpacing: false, }); const $agencyTriggers = $container.find( ".agencies-scroll_item:not(:first)" ); $agencyTriggers.each(function (index) { const tl = gsap.timeline({ paused: true }); const yStart = -100 * index; const yEnd = -100 * (index + 1); tl.set($isMoving, { yPercent: yStart, }); tl.to($isMoving, { yPercent: yEnd, ease: "none", }); ScrollTrigger.create({ trigger: this, start: () => `top center+=${$hiddenHeight.outerHeight() / 2}`, end: () => `bottom center+=${$hiddenHeight.outerHeight() / 2}`, scrub: true, animation: tl, }); }); function addClassToFirstItem() { $(".is-static .agencies-static_list").each(function () { $(this).find(".agencies-static_item:first").addClass("in-view"); }); } addClassToFirstItem(); mm.add("(min-width: 992px) and (pointer: fine)", () => { const $agencyTriggersText = $container.find(".agencies-scroll_item"); $agencyTriggersText.each(function (index) { const $item = $(this); ScrollTrigger.create({ trigger: $item[0], start: "top center", end: "bottom center", toggleClass: "in-view", onToggle: (self) => { if (self.isActive) { $agencyItems.not($item).removeClass("in-view"); $(".agencies-static_list .agencies-static_item").removeClass( "in-view" ); $(".agencies-static_list").each(function () { $(this) .find(".agencies-static_item") .eq(index) .addClass("in-view"); }); } }, }); }); }); gsap.set(".is-moving", { yPercent: 0 }); }); $(".footer-component").each(function () { let footerEl = $(this); let footerTop = footerEl.find(".footer-top_layout"); let footerBot = footerEl.find(".footer-bottom"); let footerBlocks = footerEl.find(".footer-bottom_col"); let footerBlocksFade = footerBlocks.find("[footer-block_content]"); let footerReveal = gsap.timeline({ defaults: { ease: "expo", duration: 1.2, }, scrollTrigger: { trigger: this, start: "top bottom", end: "top top", toggleActions: "play none none none", }, }); footerReveal.fromTo( footerEl, { width: "80%", }, { width: "100%", } ); footerReveal.fromTo( footerTop, { opacity: 0, }, { opacity: 1, ease: "sine.out", }, "<20%" ); footerReveal.fromTo( footerTop, { y: "5rem", }, { y: "0rem", ease: "expo.out", duration: 1.8, }, "<" ); footerReveal.fromTo( footerBlocks, { scaleY: 0, }, { ease: "expo.out", duration: 1.8, scaleY: 1, stagger: function (index) { const delays = [0.1, 0, 0.2]; return delays[index]; }, }, "<" ); footerReveal.fromTo( footerBlocksFade, { opacity: 0, }, { opacity: 1, ease: "sine.out", }, "<30%" ); ScrollTrigger.create({ trigger: $(this), start: "clamp(top bottom)", onEnter: ({ self }) => { $(".page-main").addClass("nav-hidden"); }, onLeaveBack: ({ self }) => { $(".page-main").removeClass("nav-hidden"); }, }); }); mm.add("(min-width: 992px) and (pointer: fine)", () => { $(".agencies-header_layout").each(function () { let agenciesEl = $(this); let agenciesSlow = agenciesEl.find(".agencies-header_wrap"); let agenciesImgWrap = agenciesEl.find(".agencies-header_img_contain"); let agenciesItemContain = agenciesEl.closest(".agencies-items_contain"); let agencyParallax = gsap.timeline({ defaults: { ease: "linear", }, scrollTrigger: { trigger: agenciesEl, start: "top bottom", end: "top 20%", endTrigger: agenciesItemContain, scrub: 1, }, }); agencyParallax.fromTo( agenciesSlow, { y: "0vh", }, { y: "50vh", } ); agencyParallax.fromTo( agenciesImgWrap, { y: "0vh", }, { y: "-30vh", scrollTrigger: { trigger: agenciesEl, start: "top bottom", end: "top 20%", endTrigger: agenciesItemContain, scrub: true, }, }, "<" ); }); }); $(".agencies_section").each(function () { let agenciesWrap = $(this); let squishedWork = agenciesWrap.find(".featured-work_section"); let colorTrigger = agenciesWrap.find(".agencies-items_contain"); let squishedWorkTl = gsap.timeline({ scrollTrigger: { trigger: squishedWork, start: "top bottom", end: "top center", onEnter: () => { squishedWork.removeClass("is-squished"); }, }, }); squishedWorkTl.to( agenciesWrap, { scrollTrigger: { trigger: colorTrigger, start: "bottom top", end: "top top", endTrigger: squishedWork, scrub: true, }, backgroundColor: "#191919", ease: "linear", }, "<" ); }); mm.add("(max-width: 992px)", () => { $(".clients-list_layout").each(function () { let clientsList = $(this); let clientItems = clientsList.find(".client-item_layout"); clientItems.each(function () { let clientInView = gsap.timeline({ scrollTrigger: { trigger: this, start: "top center", end: "bottom center", toggleClass: "in-view", }, }); }); }); }); $("[video-player]").each(function (index) { let playVidBtn = $(this).find("[video-trig]"); let videoPopupEl = $(this).find("[video-popup]"); let closeVidBtn = $(this).find("[video-close]"); let videoItem = $(this).find("[video-el]"); let videoBg = $(this).find("[video-bg]"); let thisComponent = $(this); const controls = `
00:00
Sound On Sound Off
`; let player = new Plyr(thisComponent.find(".plyr_video")[0], { controls, resetOnEnd: true, }); let videoPopupTl = gsap.timeline({ paused: true, defaults: { ease: "primary-ease", duration: 0.5, }, }); videoPopupTl.set(videoPopupEl, { display: "flex", }); videoPopupTl.set( videoBg, { opacity: 1, }, 0 ); videoPopupTl.set( videoItem, { opacity: 1, }, 0 ); videoPopupTl.set( closeVidBtn, { opacity: 1, }, 0 ); videoPopupTl.set( videoBg, { pointerEvents: "auto", }, 0 ); playVidBtn.on("click", function () { videoPopupTl.timeScale(1).restart(); player.play(); lenis.stop(); $("body").addClass("video-playing"); }); closeVidBtn.on("click", function () { videoPopupTl.timeScale(2).reverse(); player.pause(); lenis.start(); $("body").removeClass("video-playing"); }); player.on("ended", (event) => { videoPopupTl.timeScale(2).reverse(); lenis.start(); $("body").removeClass("video-playing"); }); }); function initMenu() { let navWrap = document.querySelector(".nav"); let state = navWrap.getAttribute("data-nav"); let menu = navWrap.querySelector(".menu"); let overlay = navWrap.querySelector(".overlay"); let menuLogoBg = navWrap.querySelector(".bg-panel_full"); let menuBg = navWrap.querySelectorAll(".bg-panel"); let menuToggles = document.querySelectorAll("[data-menu-toggle]"); let menuLinks = navWrap.querySelectorAll(".menu-list_wrap"); let fadeTargets = navWrap.querySelectorAll("[data-menu-fade]"); let whiteBg = navWrap.querySelectorAll(".nav-white"); let menuImgs = navWrap.querySelectorAll(".menu-img"); let menuListItems = navWrap.querySelectorAll(".menu-list-item"); function hideAllImages() { menuImgs.forEach((img) => { img.classList.remove("show"); }); } menuListItems.forEach((item, index) => { item.addEventListener("mouseenter", () => { hideAllImages(); if (menuImgs[index]) { menuImgs[index].classList.add("show"); } }); item.addEventListener("mouseleave", () => { hideAllImages(); }); }); let tl = gsap.timeline({}); const openNav = () => { lenis.stop(); navWrap.setAttribute("data-nav", "open"); tl.clear() .set(navWrap, { visibility: "visible" }) .set(menu, { yPercent: 0 }, "<") .fromTo( overlay, { autoAlpha: 0 }, { duration: 0.8, autoAlpha: 1, ease: "sine" }, "<" ) .fromTo( menuBg, { yPercent: 101, scaleX: 0.9 }, { scaleX: 1, yPercent: 0, ease: "ease-2", duration: 0.8, }, "<" ) .fromTo( menuBg, { clipPath: "inset(0 0 0 0 round 0.875rem)" }, { clipPath: "inset(0 0 0 0 round 0rem)", ease: "power3.in", duration: 0.8, }, "<" ) .fromTo( menuLogoBg, { yPercent: 101 }, { yPercent: 0, duration: 1.2, ease: "ease-2" }, "<30%" ) .fromTo( menuLinks, { y: "8rem" }, { y: "0rem", opacity: 1, duration: 1.2, ease: "ease-2", }, "<" ) .fromTo( menuLinks, { opacity: 0 }, { opacity: 1, duration: 1.2, ease: "sine", }, "<" ) .fromTo( fadeTargets, { autoAlpha: 0 }, { autoAlpha: 1, duration: 0.575, ease: "power3.out", }, "<50%" ); }; const closeNav = () => { lenis.start(); navWrap.setAttribute("data-nav", "closed"); tl.clear() .set(whiteBg, { visibility: "visible", yPercent: 0 }) .to(fadeTargets, { autoAlpha: 0, duration: 0.8, ease: "power3" }) .to(menu, { yPercent: -101, duration: 0.8, ease: "power3" }, "<") .to(whiteBg, { yPercent: -101, duration: 0.8, ease: "power3" }, "<10%") .to(overlay, { autoAlpha: 0, duration: 0.8, ease: "sine" }, "<") .set(navWrap, { visibility: "hidden" }); }; menuToggles.forEach((toggle) => { toggle.addEventListener("click", () => { state = navWrap.getAttribute("data-nav"); if (state === "open") { closeNav(); } else { openNav(); } }); }); document.addEventListener("keydown", (e) => { if (e.key === "Escape" && navWrap.getAttribute("data-nav") === "open") { closeNav(); } }); } initMenu(); function popupHandlers() { $("[popup-component]").each(function () { const $component = $(this); const $trigger = $component.find("[popup-trig]"); const $closeBtn = $component.find("[popup-close]"); const $popupEl = $component.find("[popup-el]"); $trigger.on("click", function (e) { e.preventDefault(); e.stopPropagation(); if ($trigger.hasClass("project-menu-toggle")) { lenis.stop(); } $component.toggleClass("show-popup"); }); $closeBtn.on("click", function (e) { e.preventDefault(); e.stopPropagation(); $component.removeClass("show-popup"); if ($trigger.hasClass("project-menu-toggle")) { lenis.start(); } }); $popupEl.on("click", function (e) { e.stopPropagation(); }); $component.on("click", function (e) { if (e.target === this) { $component.removeClass("show-popup"); if ($trigger.hasClass("project-menu-toggle")) { lenis.start(); } } }); }); $(document).on("click", function (e) { if (!$(e.target).closest("[popup-component]").length) { const $openPopups = $("[popup-component].show-popup"); $openPopups.each(function () { const $trigger = $(this).find("[popup-trig]"); if ($trigger.hasClass("project-menu-toggle")) { lenis.start(); } }); $openPopups.removeClass("show-popup"); } }); $(document).on("keydown", function (e) { if (e.key === "Escape") { const $openPopups = $("[popup-component].show-popup"); $openPopups.each(function () { const $trigger = $(this).find("[popup-trig]"); if ($trigger.hasClass("project-menu-toggle")) { lenis.start(); } }); $openPopups.removeClass("show-popup"); } }); } popupHandlers(); function splitLines() { $("[l-split]").each(function () { const text = this; SplitText.create(text, { type: "lines", autoSplit: true, linesClass: "line", onSplit(self) { gsap.from(self.lines, { scrollTrigger: { trigger: text, start: "top 70%", toggleActions: "play none none none", }, opacity: 0, duration: 1, stagger: 0.085, ease: "sine", }); }, }); gsap.set(text, { visibility: "visible" }); }); } splitLines(); $("[soft-pin]").each(function () { let softPinItem = $(this); let softPinOverlay = softPinItem.find("[softpin-overlay]"); let softPinItemTl = gsap.timeline({ scrollTrigger: { trigger: softPinItem, start: "top top", end: "bottom top", scrub: true, }, }); mm.add("(min-width: 992px) and (pointer: fine)", () => { softPinItemTl.to(softPinItem, { yPercent: 90, ease: "linear", }); }); mm.add("(max-width: 991px) and (pointer: coarse)", () => { softPinItemTl.to(softPinItem, { scrollTrigger: { trigger: softPinItem, start: "top top", end: "bottom top", scrub: true, pin: true, pinSpacing: false, }, }); }); softPinItemTl.to( softPinOverlay, { opacity: 0.5, ease: "linear", }, "<" ); }); $(".beliefs-outer").each(function () { let beliefsPin = $(this); let beliefsImgWrap = beliefsPin.find(".beliefs-img_wrap"); let beliefsImgs = beliefsImgWrap.find(".beliefs-img"); let beliefsTriggers = beliefsPin.find(".belief-item_wrap"); mm.add("(min-width: 992px) and (pointer: fine)", () => { let beliefsPinTl = gsap.timeline({ defaults: { ease: "linear", }, scrollTrigger: { trigger: beliefsPin, start: "top top", end: "bottom bottom", pin: beliefsImgWrap, scrub: true, }, }); }); beliefsTriggers.each(function (index) { ScrollTrigger.create({ trigger: this, start: "top center", end: "bottom center", onEnter: function () { beliefsImgs.removeClass("show"); beliefsImgs.eq(index).addClass("show"); }, onEnterBack: function () { beliefsImgs.removeClass("show"); beliefsImgs.eq(index).addClass("show"); }, }); }); }); $(".beliefs-component").each(function () { let beliefsWrap = $(this); let beliefsOuter = beliefsWrap.find(".beliefs-outer"); let beliefsEndWrap = beliefsWrap.find(".beliefs-end"); let beliefsTextWrap = beliefsWrap.find(".beliefs-text_wrap"); let beliefsSplit = beliefsWrap.find("[caption-split]"); let beliefsEndContent = beliefsWrap.find(".beliefs-end_content"); let beliefsEndTrigger = beliefsWrap.find(".beliefs-sticky_spacer"); let beliefsEndingTl = gsap.timeline({ scrollTrigger: { trigger: beliefsOuter, start: "bottom bottom", end: "bottom top", scrub: true, }, }); beliefsEndingTl.fromTo( beliefsEndContent, { yPercent: 20, }, { yPercent: -20, ease: "linear", } ); }); $(".parallax-layout").each(function () { let parallaxSection = $(this); let parallaxItems = parallaxSection.find(".parallax-wrap"); parallaxItems.each(function (index) { let parallaxItem = $(this); let speeds = [0.5, 0.7, 0.8, 0.6, 0.4]; let speed = speeds[index % speeds.length]; gsap.to(parallaxItem, { yPercent: -100 * speed, ease: "none", scrollTrigger: { trigger: parallaxSection, start: "top bottom", end: "bottom top", scrub: 1.2, }, }); }); }); $(".thought-page").each(function () { let thoughtPage = $(this); let filterTrig = thoughtPage.find("[filter-popup_trig]"); let closeFilter = thoughtPage.find("[filter-popup_close]"); let filterPopup = thoughtPage.find("[filter-popup]"); let filterBtnText = thoughtPage.find(".filter-indicator"); let totalText = thoughtPage.find(".thoughts-total_count"); let filteredThought = thoughtPage.find("[filtered-thought]"); let currentFilter = { type: null, value: null, }; let originalFilterText = filterBtnText.text(); let originalFilteredThoughtText = filteredThought.text(); let originalElements = []; let isAnimating = false; let thoughtItems = thoughtPage.find(".thought-item"); function storeOriginalElements() { thoughtItems.each(function () { originalElements.push($(this).clone(true)); }); } function updateElementReferences() { thoughtItems = thoughtPage.find(".thought-item"); } function updateTotalCount() { let count = thoughtItems.length; totalText.text(count); } function scrollToTop() { if (typeof lenis !== "undefined" && lenis.scrollTo && !lenis.isStopped) { lenis.scrollTo(0, { immediate: true }); } else { window.scrollTo({ top: 0, behavior: "smooth" }); } } function clearAllContainers() { thoughtPage.find(".thought-list").empty(); } function appendElements(elements, startHidden = false) { elements.forEach((element) => { let freshElement = element.clone(true); if (startHidden) { gsap.set(freshElement, { opacity: 0 }); } thoughtPage.find(".thought-list").append(freshElement); }); } function fadeOutCurrentItems() { return new Promise((resolve) => { if (thoughtItems.length === 0) { resolve(); return; } gsap.to(thoughtItems, { opacity: 0, duration: 0.6, ease: "expo.out", onComplete: resolve, }); }); } function fadeInNewItems() { updateElementReferences(); if (thoughtItems.length === 0) { return Promise.resolve(); } return new Promise((resolve) => { gsap.fromTo( thoughtItems, { opacity: 0 }, { opacity: 1, duration: 0.6, ease: "sine", stagger: 0.1, } ); gsap.fromTo( thoughtItems, { y: "8rem" }, { y: "0rem", duration: 0.6, ease: "expo.out", stagger: 0.1, onComplete: () => { gsap.set(thoughtItems, { clearProps: "all" }); resolve(); }, } ); }); } function refreshScrollSystems() { if (typeof lenis !== "undefined" && lenis.resize) { lenis.resize(); } if (typeof ScrollTrigger !== "undefined") { ScrollTrigger.refresh(); } } async function showAllItems() { if (isAnimating) return; isAnimating = true; currentFilter.type = null; currentFilter.value = null; filterTrig.removeClass("filter-visible"); filterBtnText.text(originalFilterText); filteredThought.text(originalFilteredThoughtText); await fadeOutCurrentItems(); restoreAllElements(); scrollToTop(); await fadeInNewItems(); updateTotalCount(); refreshScrollSystems(); isAnimating = false; } async function filterItems(filterType, filterValue, filterDisplayText) { if (isAnimating) return; isAnimating = true; currentFilter.type = filterType; currentFilter.value = filterValue; filterTrig.addClass("filter-visible"); filterBtnText.text(filterDisplayText); filteredThought.text(filterDisplayText); await fadeOutCurrentItems(); rebuildDOMWithFilter(filterType, filterValue); scrollToTop(); await fadeInNewItems(); updateTotalCount(); refreshScrollSystems(); isAnimating = false; } function restoreAllElements() { clearAllContainers(); appendElements(originalElements, true); updateElementReferences(); } function rebuildDOMWithFilter(filterType, filterValue) { let attributeName = "filter-" + filterType; let matchingElements = []; originalElements.forEach((element) => { let itemValue = element.attr(attributeName); if (itemValue === filterValue) { matchingElements.push(element); } }); clearAllContainers(); appendElements(matchingElements, true); updateElementReferences(); } function setupPopupHandlers() { filterTrig.on("click", function (e) { e.preventDefault(); e.stopPropagation(); if (isAnimating) return; if (filterTrig.hasClass("filter-visible")) { showAllItems(); lenis.start(); } else { thoughtPage.toggleClass("show-popup"); lenis.stop(); } }); closeFilter.on("click", function (e) { e.preventDefault(); e.stopPropagation(); lenis.start(); thoughtPage.removeClass("show-popup"); }); filterPopup.on("click", function (e) { e.stopPropagation(); }); thoughtPage.on("click", function (e) { thoughtPage.removeClass("show-popup"); lenis.start(); }); } function setupFilterHandlers() { filterPopup.find("[filter-company_trigger]").on("click", function (e) { e.preventDefault(); e.stopPropagation(); if (isAnimating) return; lenis.start(); let filterValue = $(this).attr("filter-company_trigger"); let filterDisplayText = $(this).text().trim(); filterItems("company", filterValue, filterDisplayText); thoughtPage.removeClass("show-popup"); }); filterPopup.find("[filter-clear]").on("click", function (e) { e.preventDefault(); e.stopPropagation(); if (isAnimating) return; showAllItems(); setTimeout(() => { thoughtPage.removeClass("show-popup"); }, 150); }); } function init() { storeOriginalElements(); updateElementReferences(); updateTotalCount(); setupPopupHandlers(); setupFilterHandlers(); } init(); }); function initNextProjectNavigation() { return new Promise((resolve) => { $(".single-work_page").each(function () { let $currentPage = $(this); let config = { wrapSelector: ".project-next_wrap", listSelector: ".project-next_list", itemSelector: ".project-next_item", linkSelector: ".project-next_link", }; let currentUrl = window.location.pathname; let $projectItems = $currentPage.find(config.itemSelector); if ($projectItems.length === 0) { return true; } let currentIndex = -1; $projectItems.each(function (index) { let $link = $(this).find(config.linkSelector); if ($link.length > 0) { let linkUrl = $link.attr("href"); if ( linkUrl && (linkUrl === currentUrl || linkUrl.endsWith(currentUrl)) ) { currentIndex = index; return false; } } }); if (currentIndex === -1) { currentIndex = findCurrentBySlug($projectItems, config.linkSelector); } let nextIndex = 0; if (currentIndex !== -1) { nextIndex = (currentIndex + 1) % $projectItems.length; } $projectItems.each(function (index) { if (index !== nextIndex) { $(this).remove(); } }); let $nextItem = $projectItems.eq(nextIndex); $nextItem.attr("data-next-project", "true"); $nextItem.attr("data-current-index", currentIndex); $nextItem.attr("data-next-index", nextIndex); $nextItem.addClass("is-next-project"); }); resolve(); }); } function findCurrentBySlug($items, linkSelector) { let currentSlug = getCurrentSlug(); let foundIndex = -1; if (!currentSlug) return -1; $items.each(function (index) { let $link = $(this).find(linkSelector); if ($link.length > 0) { let linkHref = $link.attr("href") || ""; let linkSlug = linkHref.split("/").pop().split("?")[0]; if (linkSlug === currentSlug) { foundIndex = index; return false; } } }); return foundIndex; } function getCurrentSlug() { let path = window.location.pathname; let segments = path.split("/").filter((segment) => segment.length > 0); return segments.length > 0 ? segments[segments.length - 1] : null; } function initWithUrlParam(paramName = "project") { return new Promise((resolve) => { let urlParams = new URLSearchParams(window.location.search); let currentProject = urlParams.get(paramName); if (!currentProject) { initNextProjectNavigation().then(() => { resolve(); }); return; } resolve(); }); } initNextProjectNavigation().then(() => { lenis.resize(); }); let emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; let phoneRegex = /^[\+]?[1-9][\d]{0,15}$/; function validateForm($form) { let isValid = true; $form .find("input[required], textarea[required], select[required]") .each(function () { let $field = $(this); let fieldType = $field.attr("type") || $field.prop("tagName").toLowerCase(); let fieldValue = $field.val().trim(); $field.removeClass("error"); if (!fieldValue) { isValid = false; $field.addClass("error"); return; } if ( fieldType === "email" || $field.hasClass("email") || $field.attr("name")?.includes("email") ) { if (!emailRegex.test(fieldValue)) { isValid = false; $field.addClass("error"); } } if ( fieldType === "tel" || $field.hasClass("phone") || $field.attr("name")?.includes("phone") ) { if (!phoneRegex.test(fieldValue.replace(/[\s\-\(\)]/g, ""))) { isValid = false; $field.addClass("error"); } } let minLength = $field.attr("minlength"); if (minLength && fieldValue.length < parseInt(minLength)) { isValid = false; $field.addClass("error"); } if (fieldType === "url") { try { new URL(fieldValue); } catch { isValid = false; $field.addClass("error"); } } }); $form.find('input[type="checkbox"][required]').each(function () { if (!$(this).is(":checked")) { isValid = false; $(this).addClass("error"); } else { $(this).removeClass("error"); } }); $form.find('input[type="radio"][required]').each(function () { let name = $(this).attr("name"); let $radioGroup = $form.find(`input[type="radio"][name="${name}"]`); if (!$radioGroup.is(":checked")) { isValid = false; $radioGroup.addClass("error"); } else { $radioGroup.removeClass("error"); } }); if (isValid) { $form.addClass("form-valid"); } else { $form.removeClass("form-valid"); } return isValid; } $("form").each(function () { let $form = $(this); validateForm($form); $form.on("input change blur", "input, textarea, select", function () { validateForm($form); }); $form.on( "change", 'input[type="checkbox"], input[type="radio"]', function () { validateForm($form); } ); }); } globalScripts(); function preloader() { let loader = $(".preloader"); let loaderLogo = $(".preloader-logo_wrap"); let loaderProgressWrap = $(".preloader-progress_el"); let loaderProgressText = $(".preloader-text"); let counter = { value: 0, }; let loaderDuration = 1.8; function updateLoaderText() { let progress = Math.round(counter.value); let formattedProgress = progress < 10 ? `0${progress}%` : `${progress}%`; loaderProgressText.text(formattedProgress); } let preloaderTl = gsap.timeline({ defaults: { ease: "expo", duration: 1.2, }, onStart: () => { lenis.stop(); }, onComplete: () => { lenis.start(); $("body").removeClass("show-preloader"); }, }); preloaderTl.fromTo( loaderLogo, { opacity: 0, }, { opacity: 1, ease: "sine.out", } ); preloaderTl.fromTo( loaderLogo, { y: "8rem", }, { y: "0rem", ease: "expo.out", duration: loaderDuration, }, "<" ); preloaderTl.fromTo( loaderProgressWrap, { opacity: 0, }, { opacity: 1, duration: 0.4, ease: "expo.out", }, "<20%" ); preloaderTl.to( loaderProgressWrap, { marginTop: "0px", duration: loaderDuration, ease: "expo.inOut", }, "<" ); preloaderTl.to( counter, { value: 100, onUpdate: updateLoaderText, duration: loaderDuration, ease: "expo.inOut", }, "<" ); preloaderTl.to(loader, { yPercent: -101, ease: "expo.out", duration: loaderDuration / 1.5, }); preloaderTl.fromTo( "[pl-fade_up]", { opacity: 0, }, { opacity: 1, ease: "sine.out", }, "<" ); preloaderTl.fromTo( "[pl-fade_up]", { y: "5rem", }, { y: "0rem", ease: "expo.out", duration: 1.8, }, "<" ); preloaderTl.fromTo( "[pl-shift_up]", { opacity: 0, yPercent: 100, }, { yPercent: 0, opacity: 1, ease: "expo.out", duration: 1.8, }, "<" ); let solutionsIntroImgEl = $(".about-hero_img"); if (solutionsIntroImgEl.length) { preloaderTl.fromTo( solutionsIntroImgEl[0], { yPercent: 0, }, { yPercent: -20, ease: "expo.out", duration: 1.8, }, "<" ); } } function checkPreloader() { if (sessionStorage.getItem("visited") !== null) { $("body").removeClass("show-preloader"); } else { preloader(); } sessionStorage.setItem("visited", "true"); } barba.hooks.enter((data) => { gsap.set(data.next.container, { position: "fixed", top: 0, left: 0, width: "100%", clearProps: "auto", }); }); barba.hooks.before((data) => { lenis.stop(); }); barba.hooks.beforeEnter((data) => { checkPreloader(); }); barba.hooks.after((data) => { gsap.set(data.next.container, { position: "static" }); globalScripts(); resetWebflow(); lenis.resize(); }); barba.init({ preventRunning: true, cacheFirstPage: true, timeout: 10000, views: [ { namespace: "work-page", beforeEnter(data) { lenis.options.syncTouch = true; lenis.options.syncTouchLerp = 0.075; lenis.options.touchInertiaExponent = 1.7; lenis.options.touchMultiplier = 1; workPage(); }, afterEnter(data) { let pageLoad = gsap.timeline({ defaults: { ease: "expo", duration: 1.2, }, onComplete: () => { $("html").removeClass("lenis-scrolling"); }, }); pageLoad.fromTo( ".work-content_item", { opacity: 0, }, { opacity: 1, ease: "sine.out", } ); pageLoad.fromTo( ".work-content_item", { y: "5rem", }, { y: "0rem", ease: "expo.out", duration: 1.8, }, "<" ); }, beforeLeave(data) { lenis.options.syncTouch = false; lenis.options.syncTouchLerp = 0.1; lenis.options.touchInertiaExponent = 1.4; lenis.options.touchMultiplier = 1; }, }, { namespace: "solutions-page", beforeEnter(data) { gsap.set(".about-hero_above", { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", }); mm.add("(min-width: 992px) and (pointer: fine)", () => { gsap.set(".about-hero_img", { clipPath: "polygon(30% 0%, 70% 0%, 70% 100%, 30% 100%)", }); }); gsap.set(".about-hero_img .img-fill", { scale: 1.4, }); }, afterEnter(data) { solutionsPage(); }, }, ], transitions: [ { name: "page-reload", once: (data) => { let pageLoad = gsap.timeline({ defaults: { ease: "expo", duration: 1.2, }, onComplete: () => { $("html").removeClass("lenis-scrolling"); }, }); pageLoad.fromTo( "[pl-fade_up]", { opacity: 0, }, { opacity: 1, ease: "sine.out", } ); pageLoad.fromTo( "[pl-fade_up]", { y: "5rem", }, { y: "0rem", ease: "expo.out", duration: 1.8, }, "<" ); pageLoad.fromTo( "[pl-shift_up]", { opacity: 0, yPercent: 100, }, { yPercent: 0, opacity: 1, ease: "expo.out", duration: 1.8, }, "<" ); const solutionsIntroImg = data.next.container.querySelectorAll(".about-hero_img"); if (solutionsIntroImg.length > 0) { pageLoad.fromTo( solutionsIntroImg, { yPercent: 0, }, { yPercent: -20, ease: "expo.out", duration: 1.8, }, "<" ); } }, }, { sync: true, enter(data) { let barbaTransitionTl = gsap.timeline({ defaults: { duration: 1, ease: "ease-3", }, }); barbaTransitionTl.fromTo( data.current.container, { opacity: 1, }, { opacity: 0.2, } ); barbaTransitionTl.fromTo( data.next.container, { clipPath: "polygon(0% 100vh, 100% 100vh, 100% 100%, 0% 100%)", }, { clipPath: "polygon(0% 0vh, 100% 0vh, 100% 100%, 0% 100%)", }, "<" ); const fadeUpElements = data.next.container.querySelectorAll("[pl-fade_up]"); if (fadeUpElements.length > 0) { barbaTransitionTl.fromTo( fadeUpElements, { opacity: 0, }, { opacity: 1, ease: "sine.out", }, "<50%" ); barbaTransitionTl.fromTo( fadeUpElements, { y: "5rem", }, { y: "0rem", ease: "expo.out", duration: 1.8, }, "<" ); const solutionsIntroImg = data.next.container.querySelectorAll(".about-hero_img"); if (solutionsIntroImg.length > 0) { mm.add("(min-width: 992px) and (pointer: fine)", () => { barbaTransitionTl.fromTo( solutionsIntroImg, { yPercent: 0, }, { yPercent: -20, ease: "expo.out", duration: 1.8, }, "<" ); }); } } const shiftUpElements = data.next.container.querySelectorAll("[pl-shift_up]"); if (shiftUpElements.length > 0) { barbaTransitionTl.fromTo( shiftUpElements, { opacity: 0, yPercent: 100, }, { yPercent: 0, opacity: 1, ease: "expo.out", duration: 1.8, }, "<" ); } return barbaTransitionTl; }, }, ], });