const loader = document.querySelector(".loader"); const glow = document.querySelector(".mouse-glow"); const navToggle = document.querySelector(".nav-toggle"); const navlinks = document.querySelector(".navlinks"); const modal = document.querySelector(".trailer-modal"); const modalVideo = modal?.querySelector("video"); const endingModal = document.querySelector(".ending-modal"); const endingModalVideo = endingModal?.querySelector("video"); const trailerVideo = document.querySelector(".trailer__video"); const bioModal = document.querySelector(".bio-modal"); const bioTitle = bioModal?.querySelector("h2"); const bioText = bioModal?.querySelector(".bio-modal__text"); const biographies = { walter: { title: "Уолтер Уайт", text: "Школьный учитель химии узнает о смертельном диагнозе и начинает варить метамфетамин, чтобы оставить деньги семье. Постепенно мотивация меняется: Уолт уже не просто выживает, а строит империю, уничтожает конкурентов, манипулирует Джесси и становится Хайзенбергом. Его главный вклад в сюжет — превращение обычного человека в центр преступной системы, которую он сам же и разрушает.", }, jesse: { title: "Джесси Пинкман", text: "Бывший ученик Уолта и его первый партнер в производстве. Джесси проходит путь от мелкого дилера до человека, который видит настоящую цену империи Хайзенберга. Через него сериал показывает совесть, зависимость, вину и попытку вырваться из мира, где взрослые используют его как расходный материал.", }, gus: { title: "Густаво Фринг", text: "Владелец Los Pollos Hermanos и один из самых опасных игроков наркорынка. Гус дает Уолту промышленный масштаб, лабораторию и систему сбыта, но требует абсолютной дисциплины. Его противостояние с Уолтом превращает сериал из истории выживания в войну интеллектов.", }, saul: { title: "Сол Гудман", text: "Криминальный адвокат, который легализует хаос вокруг Уолта и Джесси: связи, отмывание денег, фальшивые легенды, исчезновение людей. Сол делает преступную империю управляемой с юридической стороны и постоянно напоминает, что в этом мире у всего есть цена.", }, mike: { title: "Майк Эрмантраут", text: "Бывший полицейский, fixer и правая рука Гуса. Майк отвечает за порядок, безопасность и грязную работу, но живет по собственному кодексу. Его конфликт с Уолтом показывает разницу между профессиональной преступностью и эго человека, которому всегда нужно побеждать.", }, }; window.addEventListener("load", () => { if (loader) setTimeout(() => loader.classList.add("is-hidden"), 900); }); document.addEventListener("mousemove", (event) => { if (!glow) return; glow.style.left = `${event.clientX}px`; glow.style.top = `${event.clientY}px`; }); if (navToggle && navlinks) { navToggle.addEventListener("click", () => { navlinks.classList.toggle("is-open"); document.body.classList.toggle("menu-open", navlinks.classList.contains("is-open")); }); } document.querySelectorAll(".navlinks a").forEach((link) => { link.addEventListener("click", () => { navlinks.classList.remove("is-open"); document.body.classList.remove("menu-open"); }); }); const openTrailer = () => { if (!modal || !modalVideo) return; modal.classList.add("is-open"); modal.setAttribute("aria-hidden", "false"); modalVideo.currentTime = 0; modalVideo.play(); }; const closeTrailer = () => { if (!modal || !modalVideo) return; modal.classList.remove("is-open"); modal.setAttribute("aria-hidden", "true"); modalVideo.pause(); }; const openEnding = () => { if (!endingModal || !endingModalVideo) return; endingModal.classList.add("is-open"); endingModal.setAttribute("aria-hidden", "false"); endingModalVideo.currentTime = 0; endingModalVideo.play(); }; const closeEnding = () => { if (!endingModal || !endingModalVideo) return; endingModal.classList.remove("is-open"); endingModal.setAttribute("aria-hidden", "true"); endingModalVideo.pause(); }; const openBio = (key) => { const bio = biographies[key]; if (!bio || !bioModal || !bioTitle || !bioText) return; bioTitle.textContent = bio.title; bioText.textContent = bio.text; bioModal.classList.add("is-open"); bioModal.setAttribute("aria-hidden", "false"); }; const closeBio = () => { if (!bioModal) return; bioModal.classList.remove("is-open"); bioModal.setAttribute("aria-hidden", "true"); }; document.querySelectorAll("[data-open-trailer], [data-play-trailer]").forEach((button) => { button.addEventListener("click", openTrailer); }); document.querySelectorAll("[data-open-ending]").forEach((button) => { button.addEventListener("click", openEnding); }); modal?.querySelector(".modal-close")?.addEventListener("click", closeTrailer); document.querySelector(".ending-close")?.addEventListener("click", closeEnding); document.querySelector(".bio-close")?.addEventListener("click", closeBio); modal?.addEventListener("click", (event) => { if (event.target === modal) closeTrailer(); }); endingModal?.addEventListener("click", (event) => { if (event.target === endingModal) closeEnding(); }); bioModal?.addEventListener("click", (event) => { if (event.target === bioModal) closeBio(); }); document.querySelectorAll("[data-bio]").forEach((button) => { button.addEventListener("click", () => openBio(button.dataset.bio)); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape" && modal?.classList.contains("is-open")) closeTrailer(); if (event.key === "Escape" && endingModal?.classList.contains("is-open")) closeEnding(); if (event.key === "Escape" && bioModal?.classList.contains("is-open")) closeBio(); }); if (window.Lenis) { const lenis = new Lenis({ lerp: 0.08, wheelMultiplier: 0.85 }); const raf = (time) => { lenis.raf(time); requestAnimationFrame(raf); }; requestAnimationFrame(raf); } if (window.Swiper) { new Swiper(".character-swiper", { slidesPerView: 1.08, spaceBetween: 18, centeredSlides: false, pagination: { el: ".swiper-pagination", clickable: true }, breakpoints: { 760: { slidesPerView: 2.15, spaceBetween: 22 }, 1120: { slidesPerView: 3.18, spaceBetween: 26 }, }, }); } if (window.gsap) { gsap.registerPlugin(ScrollTrigger); gsap.from(".reveal", { y: 42, opacity: 0, duration: 1.15, stagger: 0.16, ease: "power3.out", delay: 0.55, }); gsap.utils.toArray(".section__head, .glass-card, .support-card, .timeline__item, .dea__grid, .ending__caption, .legacy__image, .legacy__copy").forEach((element) => { gsap.from(element, { scrollTrigger: { trigger: element, start: "top 82%" }, y: 70, opacity: 0, duration: 0.95, ease: "power3.out", }); }); gsap.to(".hero__video", { scrollTrigger: { trigger: ".hero", start: "top top", end: "bottom top", scrub: true }, scale: 1.18, yPercent: 10, }); gsap.to(".trailer__video", { scrollTrigger: { trigger: ".trailer", start: "top 65%", onEnter: () => trailerVideo?.play(), onLeaveBack: () => trailerVideo?.pause(), }, }); gsap.utils.toArray(".formula").forEach((formula, index) => { gsap.to(formula, { scrollTrigger: { trigger: ".lab", start: "top bottom", end: "bottom top", scrub: true }, xPercent: index % 2 ? -24 : 24, yPercent: index % 2 ? 40 : -35, }); }); } const canvas = document.getElementById("particles"); const ctx = canvas.getContext("2d"); let particles = []; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; particles = Array.from({ length: Math.min(90, Math.floor(window.innerWidth / 16)) }, () => ({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, r: Math.random() * 1.7 + 0.35, vx: (Math.random() - 0.5) * 0.25, vy: Math.random() * -0.35 - 0.05, a: Math.random() * 0.5 + 0.15, })); } function drawParticles() { ctx.clearRect(0, 0, canvas.width, canvas.height); particles.forEach((particle) => { particle.x += particle.vx; particle.y += particle.vy; if (particle.y < -10) particle.y = canvas.height + 10; if (particle.x < -10) particle.x = canvas.width + 10; if (particle.x > canvas.width + 10) particle.x = -10; ctx.beginPath(); ctx.arc(particle.x, particle.y, particle.r, 0, Math.PI * 2); ctx.fillStyle = `rgba(215, 255, 54, ${particle.a})`; ctx.shadowBlur = 12; ctx.shadowColor = "rgba(21, 255, 128, 0.8)"; ctx.fill(); }); requestAnimationFrame(drawParticles); } resizeCanvas(); drawParticles(); window.addEventListener("resize", resizeCanvas);