diff --git a/skiply/skiply-connected-systems.css b/skiply/skiply-connected-systems.css index 0ca3973..eacdef4 100644 --- a/skiply/skiply-connected-systems.css +++ b/skiply/skiply-connected-systems.css @@ -112,6 +112,21 @@ body { height: 100%; object-fit: contain; filter: drop-shadow(0 0 14px rgba(110, 165, 255, 0.8)) drop-shadow(0 0 36px rgba(70, 120, 255, 0.4)); + animation: shoeGlow 2s ease-in-out infinite; +} + +@keyframes shoeGlow { + + 0%, + 100% { + filter: + drop-shadow(rgba(110, 165, 255, 0.5) 0px 0px 10px) drop-shadow(rgba(70, 120, 255, 0.25) 0px 0px 25px); + } + + 50% { + filter: + drop-shadow(rgba(110, 165, 255, 1) 0px 0px 20px) drop-shadow(rgba(70, 120, 255, 0.65) 0px 0px 50px); + } } /* Outer layer: a plain rectangular clip that reveals left-to-right in @@ -158,10 +173,12 @@ body { top: 45%; height: 3px; margin-top: -1.5px; + opacity: 0; transform: scaleX(0); transform-origin: center; + z-index: 3; pointer-events: none; border-top: 2px solid; @@ -174,6 +191,55 @@ body { filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.9)) drop-shadow(0 0 12px rgba(110, 165, 255, 0.8)) drop-shadow(0 0 30px rgba(70, 120, 255, 0.5)); + + overflow: visible; +} + +/* Moving wave */ +.shoe-stage__horizon::after { + content: ""; + position: absolute; + top: -2px; + left: -10%; + width: 20%; + height: 5px; + + background: linear-gradient(90deg, + transparent, + rgba(255, 255, 255, 0.9), + rgba(110, 165, 255, 0.8), + transparent); + + filter: blur(5px); + opacity: 0; + + animation: horizonWave 2.5s ease-in-out infinite; +} + +@keyframes horizonWave { + 0% { + left: -10%; + opacity: 0; + transform: scaleY(0.5); + } + + 20% { + opacity: 1; + } + + 50% { + transform: scaleY(1.5); + } + + 80% { + opacity: 1; + } + + 100% { + left: 90%; + opacity: 0; + transform: scaleY(0.5); + } } .shoe-stage__particles { diff --git a/skiply/skiply-connected-systems.js b/skiply/skiply-connected-systems.js index 012316c..cec1ef4 100644 --- a/skiply/skiply-connected-systems.js +++ b/skiply/skiply-connected-systems.js @@ -43,7 +43,7 @@ document.addEventListener('DOMContentLoaded', () => { /* ── Ambient dust: persistent, independent of the scroll timeline ── */ const ambientHost = document.getElementById('ambientParticles'); - const AMBIENT_COUNT = 22; + const AMBIENT_COUNT = 38; for (let i = 0; i < AMBIENT_COUNT; i++) { const p = document.createElement('span'); p.className = 'ambient-particle'; @@ -70,12 +70,14 @@ document.addEventListener('DOMContentLoaded', () => { gsap.set(treeSvg, { opacity: 1 }); gsap.set(branchGroups, { clipPath: 'inset(0% 0% 0% 0%)' }); gsap.set(nodes, { opacity: 1, scale: 1 }); + gsap.set(routes, { opacity: 1, y: -4 }); routes.forEach((el) => el.classList.add('is-lit')); return; } gsap.set(shoeStage, { opacity: 0 }); gsap.set(labels, { opacity: 0, y: 10, scale: 0.9, transformOrigin: '50% 50%' }); + gsap.set(routes, { opacity: 0, y: 20 }); function playIntro() { const tl = gsap.timeline({ defaults: { ease: 'power2.out' } }); @@ -112,8 +114,8 @@ document.addEventListener('DOMContentLoaded', () => { // 6. The tree becomes visible, then each branch draws itself in // center-out: the middle line first, then the two adjacent, then - // the two outer ones — each followed by its node flashing and - // its pill lighting up as the light "arrives". + // the two outer ones — each followed by its node simply appearing + // and its pill lighting up as the light "arrives". const centerIndex = Math.floor(branchGroups.length / 2); const STEP = 0.22; const BRANCH_DUR = 0.9; @@ -125,17 +127,26 @@ document.addEventListener('DOMContentLoaded', () => { const pos = `branches+=${(Math.abs(i - centerIndex) * STEP).toFixed(2)}`; tl.fromTo(group, { clipPath: 'inset(0% 0% 100% 0%)' }, - { clipPath: 'inset(0% 0% 0% 0%)', duration: BRANCH_DUR, ease: 'power2.out' }, + { + clipPath: 'inset(0% 0% 0% 0%)', + duration: BRANCH_DUR, + ease: 'power2.out', + // Fires exactly when this branch's line finishes drawing, so + // the node and its route card never appear before the line + // has actually reached them. + onComplete: () => { + gsap.to(nodes[i], { opacity: 1, duration: 0.12 }); + gsap.to(routes[i], { + opacity: 1, + y: -4, // matches .is-lit's lift so there's no transform conflict + duration: 0.4, + ease: 'power2.out', + onComplete: () => routes[i].classList.add('is-lit'), + }); + }, + }, pos ); - // Opacity reveal is permanent; the scale punch alone yoyos back down. - tl.to(nodes[i], { opacity: 1, duration: 0.15 }, `${pos}+=${BRANCH_DUR - 0.15}`); - tl.fromTo(nodes[i], - { scale: 1 }, - { scale: 1.5, duration: 0.18, yoyo: true, repeat: 1, ease: 'power2.out' }, - `${pos}+=${BRANCH_DUR - 0.15}` - ); - tl.call(() => routes[i].classList.add('is-lit'), null, `${pos}+=${(BRANCH_DUR - 0.05).toFixed(2)}`); }); return tl;