diff --git a/assets/custom/css/app.css b/assets/custom/css/app.css index 4a992e6..e93dddc 100644 --- a/assets/custom/css/app.css +++ b/assets/custom/css/app.css @@ -3,3 +3,4 @@ .project-video-detail .project-video-detail-copy{position:relative;z-index:2}.project-video-detail .project-video-detail-copy-bottom{margin-top:2.5rem}.project-video-detail .project-video-detail-copy-bottom .main-h{margin-bottom:0}@media screen and (max-width:767px){.project-video-detail .project-video-detail-hgroup{flex-flow:row;gap:1.25rem}.project-video-detail .project-video-detail-hgroup.flip{flex-flow:row-reverse}.project-video-detail .project-video-detail-hgroup .main-h.h1{font-size:2.35rem}.project-video-detail .project-video-detail-hgroup .h-txt{max-width:22ch}.project-video-detail .project-video-detail-copy-bottom{margin-top:2rem}}@media screen and (max-width:479px){.project-video-detail .project-video-detail-copy{width:86%}.project-video-detail .project-video-detail-hgroup{gap:1rem}.project-video-detail .project-video-detail-hgroup .main-h.h1{font-size:2rem}.project-video-detail .project-video-detail-hgroup .h-txt{max-width:17ch;font-size:.86rem;line-height:1.28em}} .project-composition .project-composition-copy{position:relative;z-index:2}.project-composition .project-composition-copy-bottom{margin-top:2.5rem}.project-composition .project-composition-copy-bottom .main-h{margin-bottom:0}@media screen and (max-width:767px){.project-composition .project-composition-hgroup{flex-flow:row;gap:1.25rem}.project-composition .project-composition-hgroup.flip{flex-flow:row-reverse}.project-composition .project-composition-hgroup .main-h.h2{font-size:2.35rem}.project-composition .project-composition-hgroup .h-txt{max-width:22ch}.project-composition .project-composition-copy-bottom{margin-top:2rem}}@media screen and (max-width:479px){.project-composition .project-composition-copy{width:86%}.project-composition .project-composition-hgroup{gap:1rem}.project-composition .project-composition-hgroup .main-h.h2{font-size:2rem}.project-composition .project-composition-hgroup .h-txt{max-width:17ch;font-size:.86rem;line-height:1.28em}} .pricing-table-section .pricing-table-shell{padding-top:12svh;padding-bottom:12svh}@media screen and (max-width:767px){.pricing-table-section .pricing-table-shell{padding-top:8svh;padding-bottom:8svh}} +[data-module=notification-w][data-notification-stack-ready]{height:auto;max-height:calc(100svh - 3.2rem);overflow:visible;scrollbar-width:none;overscroll-behavior:contain}[data-module=notification-w][data-notification-stack-ready]::-webkit-scrollbar{display:none}[data-module=notification-w][data-notification-stack-ready]:not(:hover):not([data-notification-expanded=true]){height:var(--notification-collapsed-height,6.6rem);max-height:var(--notification-collapsed-height,6.6rem)}[data-module=notification-w][data-notification-stack-ready]:is(:hover,[data-notification-expanded=true]){overflow-x:visible;overflow-y:auto;padding-bottom:.2rem}[data-module=notification-w][data-notification-stack-ready]>[data-module=notification]{position:relative;z-index:var(--notification-z,1);transform:translateY(var(--notification-stack-y,0));transition:transform .3s cubic-bezier(.22,1,.36,1)}[data-module=notification-w][data-notification-stack-ready]:is(:hover,[data-notification-expanded=true])>[data-module=notification]{transform:translateY(0)} diff --git a/assets/custom/css/out.css b/assets/custom/css/out.css index 9639c85..1e8684b 100644 --- a/assets/custom/css/out.css +++ b/assets/custom/css/out.css @@ -3,3 +3,4 @@ .project-video-detail .project-video-detail-copy{position:relative;z-index:2}.project-video-detail .project-video-detail-copy-bottom{margin-top:2.5rem}.project-video-detail .project-video-detail-copy-bottom .main-h{margin-bottom:0}@media screen and (max-width:767px){.project-video-detail .project-video-detail-hgroup{flex-flow:row;gap:1.25rem}.project-video-detail .project-video-detail-hgroup.flip{flex-flow:row-reverse}.project-video-detail .project-video-detail-hgroup .main-h.h1{font-size:2.35rem}.project-video-detail .project-video-detail-hgroup .h-txt{max-width:22ch}.project-video-detail .project-video-detail-copy-bottom{margin-top:2rem}}@media screen and (max-width:479px){.project-video-detail .project-video-detail-copy{width:86%}.project-video-detail .project-video-detail-hgroup{gap:1rem}.project-video-detail .project-video-detail-hgroup .main-h.h1{font-size:2rem}.project-video-detail .project-video-detail-hgroup .h-txt{max-width:17ch;font-size:.86rem;line-height:1.28em}} .project-composition .project-composition-copy{position:relative;z-index:2}.project-composition .project-composition-copy-bottom{margin-top:2.5rem}.project-composition .project-composition-copy-bottom .main-h{margin-bottom:0}@media screen and (max-width:767px){.project-composition .project-composition-hgroup{flex-flow:row;gap:1.25rem}.project-composition .project-composition-hgroup.flip{flex-flow:row-reverse}.project-composition .project-composition-hgroup .main-h.h2{font-size:2.35rem}.project-composition .project-composition-hgroup .h-txt{max-width:22ch}.project-composition .project-composition-copy-bottom{margin-top:2rem}}@media screen and (max-width:479px){.project-composition .project-composition-copy{width:86%}.project-composition .project-composition-hgroup{gap:1rem}.project-composition .project-composition-hgroup .main-h.h2{font-size:2rem}.project-composition .project-composition-hgroup .h-txt{max-width:17ch;font-size:.86rem;line-height:1.28em}} .pricing-table-section .pricing-table-shell{padding-top:12svh;padding-bottom:12svh}@media screen and (max-width:767px){.pricing-table-section .pricing-table-shell{padding-top:8svh;padding-bottom:8svh}} +[data-module=notification-w][data-notification-stack-ready]{height:auto;max-height:calc(100svh - 3.2rem);overflow:visible;scrollbar-width:none;overscroll-behavior:contain}[data-module=notification-w][data-notification-stack-ready]::-webkit-scrollbar{display:none}[data-module=notification-w][data-notification-stack-ready]:not(:hover):not([data-notification-expanded=true]){height:var(--notification-collapsed-height,6.6rem);max-height:var(--notification-collapsed-height,6.6rem)}[data-module=notification-w][data-notification-stack-ready]:is(:hover,[data-notification-expanded=true]){overflow-x:visible;overflow-y:auto;padding-bottom:.2rem}[data-module=notification-w][data-notification-stack-ready]>[data-module=notification]{position:relative;z-index:var(--notification-z,1);transform:translateY(var(--notification-stack-y,0));transition:transform .3s cubic-bezier(.22,1,.36,1)}[data-module=notification-w][data-notification-stack-ready]:is(:hover,[data-notification-expanded=true])>[data-module=notification]{transform:translateY(0)} diff --git a/assets/custom/js/interaction-overrides.js b/assets/custom/js/interaction-overrides.js index f75baf7..3e945cd 100644 --- a/assets/custom/js/interaction-overrides.js +++ b/assets/custom/js/interaction-overrides.js @@ -12,7 +12,9 @@ const MEDIA_HANDLE_BOUND_FLAG = "__nodedcMediaHandleBound"; const DOCK_LINK_BOUND_FLAG = "__nodedcDockLinkBound"; const ORDERED_SLIDER_BOUND_FLAG = "__nodedcOrderedSliderBound"; + const LAZY_MEDIA_VIDEO_BOUND_FLAG = "__nodedcLazyMediaVideoBound"; const SCROLL_HEIGHT_SYNC_FLAG = "__nodedcScrollHeightSyncBound"; + const NOTIFICATION_STACK_BOUND_FLAG = "__nodedcNotificationStackBound"; const SCROLL_HEIGHT_SYNC_WARMUP_MS = 14000; const SCROLL_HEIGHT_SYNC_INTERVAL_MS = 90; const IMAGE_EXTENSIONS = new Set(["avif", "gif", "jpeg", "jpg", "png", "svg", "webp"]); @@ -384,6 +386,75 @@ document.querySelectorAll('[data-module="dock"] a[data-app]').forEach(bindDockLink); } + function refreshNotificationStack(wrapper) { + const notifications = Array.from(wrapper.querySelectorAll(':scope > [data-module="notification"]')); + const firstOffset = notifications[0]?.offsetTop || 0; + const collapsedHeight = (notifications[0]?.offsetHeight || 0) + Math.min(Math.max(notifications.length - 1, 0), 3) * 5; + + wrapper.dataset.notificationStackReady = "true"; + wrapper.style.setProperty("--notification-count", String(notifications.length)); + wrapper.style.setProperty("--notification-collapsed-height", `${collapsedHeight}px`); + + notifications.forEach((notification, index) => { + const naturalOffset = notification.offsetTop - firstOffset; + const deckOffset = Math.min(index, 3) * 5; + + notification.style.setProperty("--notification-stack-y", `${deckOffset - naturalOffset}px`); + notification.style.setProperty("--notification-z", String(index + 1)); + }); + } + + function scheduleNotificationStackRefresh(wrapper) { + if (wrapper.__nodedcNotificationStackFrame) return; + + wrapper.__nodedcNotificationStackFrame = window.requestAnimationFrame(() => { + wrapper.__nodedcNotificationStackFrame = 0; + refreshNotificationStack(wrapper); + }); + } + + function bindNotificationStack(wrapper) { + if (!wrapper || wrapper[NOTIFICATION_STACK_BOUND_FLAG]) return; + + wrapper[NOTIFICATION_STACK_BOUND_FLAG] = true; + scheduleNotificationStackRefresh(wrapper); + + const mutationObserver = + typeof MutationObserver === "function" + ? new MutationObserver(() => scheduleNotificationStackRefresh(wrapper)) + : null; + + mutationObserver?.observe(wrapper, { childList: true }); + + wrapper.addEventListener("pointerdown", (event) => { + if (event.target?.closest?.("[data-close]")) return; + wrapper.dataset.notificationExpanded = "true"; + }); + + wrapper.addEventListener("mouseleave", () => { + delete wrapper.dataset.notificationExpanded; + wrapper.scrollTop = 0; + scheduleNotificationStackRefresh(wrapper); + }); + + document.addEventListener( + "pointerdown", + (event) => { + if (wrapper.contains(event.target)) return; + delete wrapper.dataset.notificationExpanded; + wrapper.scrollTop = 0; + }, + { capture: true }, + ); + + window.addEventListener("resize", () => scheduleNotificationStackRefresh(wrapper)); + window.setTimeout(() => scheduleNotificationStackRefresh(wrapper), 120); + } + + function bindNotificationStacks() { + document.querySelectorAll('[data-module="notification-w"]').forEach(bindNotificationStack); + } + function mediaExtension(src) { const cleanSrc = String(src || "").split(/[?#]/)[0]; const match = /\.([a-z0-9]+)$/i.exec(cleanSrc); @@ -407,7 +478,7 @@ const video = document.createElement("video"); video.className = "video-el"; - video.dataset.module = "video-handle"; + video.dataset.lazyVideo = ""; video.src = src; video.muted = true; video.loop = true; @@ -458,8 +529,53 @@ document.querySelectorAll("[data-adaptive-video] video").forEach(bindAdaptiveVideo); } + function playLazyVideo(video) { + video.preload = "auto"; + const playPromise = video.play(); + + if (playPromise && typeof playPromise.catch === "function") { + playPromise.catch(() => {}); + } + } + + function bindLazyMediaVideo(video) { + if (!video || video[LAZY_MEDIA_VIDEO_BOUND_FLAG]) return; + + video[LAZY_MEDIA_VIDEO_BOUND_FLAG] = true; + video.muted = true; + video.loop = true; + video.playsInline = true; + video.preload = "none"; + video.removeAttribute("autoplay"); + + if (typeof IntersectionObserver !== "function") { + playLazyVideo(video); + return; + } + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + playLazyVideo(video); + } else { + video.pause(); + } + }); + }, + { rootMargin: "80px 0px", threshold: 0.01 }, + ); + + observer.observe(video); + } + + function bindLazyMediaVideos() { + document.querySelectorAll("video[data-lazy-video]").forEach(bindLazyMediaVideo); + } + bindScrollHeightSync(); hydrateMediaHandles(); + bindNotificationStacks(); if (document.readyState === "loading") { document.addEventListener( @@ -470,7 +586,9 @@ bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); + bindNotificationStacks(); bindAdaptiveVideos(); + bindLazyMediaVideos(); scheduleScrollHeightSync(true); }, { once: true }, @@ -481,7 +599,9 @@ bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); + bindNotificationStacks(); bindAdaptiveVideos(); + bindLazyMediaVideos(); scheduleScrollHeightSync(true); } @@ -491,14 +611,18 @@ bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); + bindNotificationStacks(); bindAdaptiveVideos(); + bindLazyMediaVideos(); scheduleScrollHeightSync(true); window.setTimeout(() => { hydrateMediaHandles(); bindInteractiveScroll(); bindOrderedSliders(); bindDockLinks(); + bindNotificationStacks(); bindAdaptiveVideos(); + bindLazyMediaVideos(); scheduleScrollHeightSync(true); }, 500); }); diff --git a/templates/pages/home/blocks/project-composition.html b/templates/pages/home/blocks/project-composition.html index f470ef4..f431915 100644 --- a/templates/pages/home/blocks/project-composition.html +++ b/templates/pages/home/blocks/project-composition.html @@ -16,7 +16,7 @@