'use client'; import React, { useLayoutEffect, useEffect, useRef, useState } from 'react'; import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; import { DEFAULT_EASE, DEFAULT_DURATION } from '@/utils/animation'; import AnimatedDarkBg from './AnimatedDarkBg'; if (typeof window !== 'undefined') { gsap.registerPlugin(ScrollTrigger); } const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; interface Stage { title: string; tagline: string; detail: string; icon: React.ReactNode; } const STAGES: Stage[] = [ { title: 'Understand', tagline: 'See exactly who walks in, when, and why they buy.', detail: 'Real-time footfall & behavior insights', icon: ( ), }, { title: 'Engage', tagline: 'Playable daily Lyts turn one visit into a habit.', detail: 'Daily streaks, challenges & mini games', icon: ( ), }, { title: 'Convert', tagline: 'Customers play to unlock the deal, then buy on the spot.', detail: 'Higher-converting, game-based offers', icon: ( ), }, { title: 'Retain', tagline: 'Every redemption resets the loop for the next visit.', detail: 'More repeat visits, compounding loyalty', icon: ( ), }, ]; function StageCard({ stage, index, active, onHover, }: { stage: Stage; index: number; active: boolean; onHover: (i: number | null) => void; }) { const cardRef = useRef(null); const iconRef = useRef(null); const onMouseMove = (e: React.MouseEvent) => { const el = cardRef.current; if (!el) return; const r = el.getBoundingClientRect(); const px = (e.clientX - r.left) / r.width - 0.5; const py = (e.clientY - r.top) / r.height - 0.5; gsap.to(el, { '--mx': `${((px + 0.5) * 100).toFixed(1)}%`, '--my': `${((py + 0.5) * 100).toFixed(1)}%`, duration: 0.3, overwrite: 'auto' } as gsap.TweenVars); }; const onMouseEnter = () => { onHover(index); if (iconRef.current) gsap.to(iconRef.current, { rotate: -8, scale: 1.08, duration: 0.4, ease: 'back.out(2)' }); }; const onMouseLeave = () => { onHover(null); if (iconRef.current) gsap.to(iconRef.current, { rotate: 0, scale: 1, duration: 0.4, ease: 'power3.out' }); }; const isYellow = index % 2 === 1; return ( {/* Cursor-following glow */} {/* Icon */} {stage.icon} {/* Title */} {stage.title} {/* Tagline — single concise line */} {stage.tagline} {/* Detail pill — quick supporting fact */} {stage.detail} ); } interface WhatWeDoProps { isLoaded?: boolean; } export default function WhatWeDo({ isLoaded = false }: WhatWeDoProps) { const containerRef = useRef(null); const [active, setActive] = useState(null); useIsomorphicLayoutEffect(() => { if (!isLoaded) return; if (typeof window === 'undefined') return; const ctx = gsap.context(() => { const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; // Header: clean fade-up with a refined blur transition gsap.fromTo( '.wwd-header-el', { opacity: 0, y: prefersReducedMotion ? 0 : 30, filter: prefersReducedMotion ? 'none' : 'blur(8px)' }, { opacity: 1, y: 0, filter: 'blur(0px)', duration: DEFAULT_DURATION, stagger: 0.15, ease: DEFAULT_EASE, scrollTrigger: { trigger: '.wwd-header', start: 'top 85%', toggleActions: 'play none none reset' }, } ); // The cards have no reveal animation by design — they render in place. // Hover interactions live in StageCard. }, containerRef); return () => ctx.revert(); }, [isLoaded]); return ( {/* Header */} One Platform Every visit, a loyal customer. {/* 4-up grid */} {STAGES.map((stage, i) => ( ))} ); }
{stage.tagline}