// ---------- Sticky nav ---------- const siteNav = document.querySelector(".site-nav"); function onScroll() { if (!siteNav) return; siteNav.classList.toggle("scrolled", window.scrollY > 30); } document.addEventListener("scroll", onScroll, { passive: true }); onScroll(); // ---------- Scroll reveal ---------- const revealEls = document.querySelectorAll(".reveal"); if ("IntersectionObserver" in window && revealEls.length) { const io = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add("in-view"); io.unobserve(entry.target); } }); }, { threshold: 0.15, rootMargin: "0px 0px -40px 0px" } ); revealEls.forEach((el) => io.observe(el)); } else { revealEls.forEach((el) => el.classList.add("in-view")); } // ---------- Countdown ---------- const LAUNCH_DATE = new Date("2026-07-23T18:00:00Z").getTime(); const countdownEl = document.querySelector("[data-countdown]"); function tickCell(el, value) { const formatted = String(value).padStart(2, "0"); if (el.textContent !== formatted) { el.textContent = formatted; const cell = el.closest(".countdown-cell"); if (cell) { cell.classList.remove("tick"); // eslint-disable-next-line no-unused-expressions void cell.offsetWidth; cell.classList.add("tick"); } } } function updateCountdown() { if (!countdownEl) return; const now = Date.now(); let diff = Math.max(0, LAUNCH_DATE - now); const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff / (1000 * 60 * 60)) % 24); const minutes = Math.floor((diff / (1000 * 60)) % 60); const seconds = Math.floor((diff / 1000) % 60); tickCell(countdownEl.querySelector('[data-unit="days"]'), days); tickCell(countdownEl.querySelector('[data-unit="hours"]'), hours); tickCell(countdownEl.querySelector('[data-unit="minutes"]'), minutes); tickCell(countdownEl.querySelector('[data-unit="seconds"]'), seconds); } if (countdownEl) { updateCountdown(); setInterval(updateCountdown, 1000); } // ---------- Lite YouTube embed ---------- document.querySelectorAll("[data-youtube-id]").forEach((frame) => { const videoId = frame.getAttribute("data-youtube-id"); const thumb = frame.querySelector("img.video-thumb"); const overlay = frame.querySelector(".play-overlay"); if (thumb) { thumb.src = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`; thumb.addEventListener( "error", () => { thumb.src = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`; }, { once: true } ); } const play = () => { const iframe = document.createElement("iframe"); iframe.src = `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1&rel=0`; iframe.title = "High Times trailer"; iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"; iframe.allowFullscreen = true; frame.innerHTML = ""; frame.appendChild(iframe); }; frame.addEventListener("click", play); frame.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); play(); } }); }); // ---------- Hero parallax ---------- const heroVisual = document.querySelector(".hero"); const parallaxItems = document.querySelectorAll("[data-parallax]"); if (heroVisual && parallaxItems.length && window.matchMedia("(min-width: 981px)").matches) { let rafId = null; heroVisual.addEventListener("mousemove", (e) => { const rect = heroVisual.getBoundingClientRect(); const x = (e.clientX - rect.left) / rect.width - 0.5; const y = (e.clientY - rect.top) / rect.height - 0.5; if (rafId) cancelAnimationFrame(rafId); rafId = requestAnimationFrame(() => { parallaxItems.forEach((el) => { const depth = parseFloat(el.getAttribute("data-parallax")) || 10; el.style.setProperty("--px", `${x * depth}px`); el.style.setProperty("--py", `${y * depth}px`); }); }); }); heroVisual.addEventListener("mouseleave", () => { parallaxItems.forEach((el) => { el.style.setProperty("--px", "0px"); el.style.setProperty("--py", "0px"); }); }); } // ---------- Smooth-follow newsletter card ---------- const stickyCard = document.querySelector(".hero-newsletter"); const stickyParent = document.querySelector(".hero-aside"); const reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); if (stickyCard && stickyParent && window.matchMedia("(min-width: 981px)").matches && !reducedMotionQuery.matches) { const topOffset = 100; const ease = 0.1; let currentY = 0; let targetY = 0; function computeTarget() { const cardRect = stickyCard.getBoundingClientRect(); const naturalTop = cardRect.top - currentY; const parentRect = stickyParent.getBoundingClientRect(); const cardHeight = stickyCard.offsetHeight; let desired = Math.max(0, topOffset - naturalTop); const maxDesired = Math.max(0, parentRect.bottom - cardHeight - naturalTop); targetY = Math.min(desired, maxDesired); } function tick() { computeTarget(); currentY += (targetY - currentY) * ease; if (Math.abs(targetY - currentY) < 0.05) currentY = targetY; stickyCard.style.transform = `translateY(${currentY}px)`; requestAnimationFrame(tick); } requestAnimationFrame(tick); } // ---------- Cast carousel ---------- document.querySelectorAll(".cast-carousel").forEach((carousel) => { const scroller = carousel.querySelector(".cast-scroll"); const prevBtn = carousel.querySelector(".cast-nav.prev"); const nextBtn = carousel.querySelector(".cast-nav.next"); if (!scroller) return; const scrollByAmount = () => Math.max(240, scroller.clientWidth * 0.7); if (prevBtn) prevBtn.addEventListener("click", () => scroller.scrollBy({ left: -scrollByAmount(), behavior: "smooth" })); if (nextBtn) nextBtn.addEventListener("click", () => scroller.scrollBy({ left: scrollByAmount(), behavior: "smooth" })); let isDown = false; let startX = 0; let startScroll = 0; let moved = false; scroller.addEventListener("mousedown", (e) => { isDown = true; moved = false; startX = e.pageX; startScroll = scroller.scrollLeft; scroller.classList.add("dragging"); }); window.addEventListener("mouseup", () => { isDown = false; scroller.classList.remove("dragging"); }); window.addEventListener("mousemove", (e) => { if (!isDown) return; const delta = e.pageX - startX; if (Math.abs(delta) > 4) moved = true; scroller.scrollLeft = startScroll - delta; }); scroller.addEventListener( "click", (e) => { if (moved) { e.preventDefault(); e.stopPropagation(); } }, true ); }); // ---------- Cast voice-line playback ---------- const castAudioPlayer = new Audio(); let activeCastChip = null; function stopCastAudio() { castAudioPlayer.pause(); castAudioPlayer.currentTime = 0; if (activeCastChip) activeCastChip.classList.remove("playing"); activeCastChip = null; } function playCastChip(chip) { const src = chip.getAttribute("data-audio"); if (!src) return; if (activeCastChip === chip) { stopCastAudio(); return; } stopCastAudio(); castAudioPlayer.src = src; castAudioPlayer.play().catch(() => {}); chip.classList.add("playing"); activeCastChip = chip; } castAudioPlayer.addEventListener("ended", stopCastAudio); document.querySelectorAll(".cast-chip[data-audio]").forEach((chip) => { chip.addEventListener("click", () => playCastChip(chip)); chip.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); playCastChip(chip); } }); }); // ---------- Feature video banners ---------- const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const videoCards = document.querySelectorAll(".feature-media video[data-src]"); if (!reducedMotion && "IntersectionObserver" in window && videoCards.length) { const vio = new IntersectionObserver( (entries) => { entries.forEach((entry) => { const video = entry.target; if (entry.isIntersecting) { if (!video.src) video.src = video.getAttribute("data-src"); video.play().catch(() => {}); } else { video.pause(); } }); }, { threshold: 0.3, rootMargin: "80px 0px" } ); videoCards.forEach((video) => vio.observe(video)); }