"use client"; import { useState, useRef, useCallback, useEffect } from "react"; import Image from "next/image"; import { Instagram } from "lucide-react"; import { siteContent } from "@/data/content"; import { SectionHeading } from "@/components/ui/SectionHeading"; import { Reveal } from "@/components/ui/Reveal"; const AUTO_PLAY_MS = 4500; const PAUSE_MS = 12000; const CARD_SPACING = 260; // px between card centers function wrapIndex(i: number, total: number) { return ((i % total) + total) % total; } function getDiff(index: number, active: number, total: number) { let diff = index - active; if (diff > total / 2) diff -= total; if (diff < -total / 2) diff += total; return diff; } // Interpolation helpers function lerp(a: number, b: number, t: number) { return a + (b - a) * t; } function clamp(v: number, min: number, max: number) { return Math.max(min, Math.min(max, v)); } // Slot properties for each position (0=center, 1=near, 2=mid, 3=far, 4=hidden) const SLOTS = [ { w: 280, h: 400, opacity: 1, scale: 1, x: 0, brightness: 1, grayscale: 0, z: 10, border: true }, { w: 220, h: 340, opacity: 0.8, scale: 0.97, x: 260, brightness: 0.6, grayscale: 0.2, z: 5, border: false }, { w: 180, h: 280, opacity: 0.6, scale: 0.93, x: 470, brightness: 0.45, grayscale: 0.35, z: 3, border: false }, { w: 150, h: 230, opacity: 0.35, scale: 0.88, x: 640, brightness: 0.3, grayscale: 0.5, z: 2, border: false }, { w: 120, h: 180, opacity: 0, scale: 0.83, x: 780, brightness: 0.2, grayscale: 0.8, z: 1, border: false }, ]; export function Team() { const { team } = siteContent; const total = team.members.length; const [activeIndex, setActiveIndex] = useState(0); const [dragOffset, setDragOffset] = useState(0); const isDraggingRef = useRef(false); const pausedUntilRef = useRef(0); const dragStartRef = useRef<{ x: number; startIndex: number } | null>(null); const member = team.members[activeIndex]; const goTo = useCallback( (i: number) => { setActiveIndex(wrapIndex(i, total)); setDragOffset(0); pausedUntilRef.current = Date.now() + PAUSE_MS; }, [total] ); // Auto-rotate — completely skip while dragging useEffect(() => { const id = setInterval(() => { if (isDraggingRef.current) return; if (Date.now() < pausedUntilRef.current) return; setActiveIndex((i) => (i + 1) % total); }, AUTO_PLAY_MS); return () => clearInterval(id); }, [total]); // Pointer handlers const onPointerDown = useCallback( (e: React.PointerEvent) => { (e.target as HTMLElement).setPointerCapture(e.pointerId); isDraggingRef.current = true; setActiveIndex((cur) => { dragStartRef.current = { x: e.clientX, startIndex: cur }; return cur; }); setDragOffset(0); }, [] ); const onPointerMove = useCallback( (e: React.PointerEvent) => { if (!dragStartRef.current) return; const dx = e.clientX - dragStartRef.current.x; setDragOffset(dx); }, [] ); const onPointerUp = useCallback(() => { if (!dragStartRef.current) return; const startIdx = dragStartRef.current.startIndex; // Read current dragOffset from state via functional update trick setDragOffset((currentOffset) => { const wasDrag = Math.abs(currentOffset) > 10; const steps = wasDrag ? Math.round(currentOffset / CARD_SPACING) : 0; if (steps !== 0) { const newIndex = wrapIndex(startIdx - steps, total); setActiveIndex(newIndex); } return 0; // reset offset }); dragStartRef.current = null; isDraggingRef.current = false; pausedUntilRef.current = Date.now() + PAUSE_MS; }, [total]); // Compute interpolated style for each card // During drag, base position is startIndex; otherwise activeIndex const baseIndex = dragStartRef.current ? dragStartRef.current.startIndex : activeIndex; function getCardStyle(index: number) { const baseDiff = getDiff(index, baseIndex, total); const fractionalShift = dragOffset / CARD_SPACING; const continuousDiff = baseDiff + fractionalShift; const absDiff = Math.abs(continuousDiff); if (absDiff > 4) return null; // Interpolate between the two nearest slot positions const lowerSlot = Math.floor(absDiff); const upperSlot = Math.ceil(absDiff); const t = absDiff - lowerSlot; const s0 = SLOTS[clamp(lowerSlot, 0, 4)]; const s1 = SLOTS[clamp(upperSlot, 0, 4)]; const sign = continuousDiff >= 0 ? 1 : -1; const x = sign * lerp(s0.x, s1.x, t); const w = lerp(s0.w, s1.w, t); const h = lerp(s0.h, s1.h, t); const opacity = lerp(s0.opacity, s1.opacity, t); const scale = lerp(s0.scale, s1.scale, t); const brightness = lerp(s0.brightness, s1.brightness, t); const grayscale = lerp(s0.grayscale, s1.grayscale, t); const z = Math.round(lerp(s0.z, s1.z, t)); const showBorder = absDiff < 0.5; if (opacity < 0.02) return null; return { width: w, height: h, opacity, zIndex: z, transform: `translateX(${x}px) scale(${scale})`, filter: `brightness(${brightness}) grayscale(${grayscale})`, borderColor: showBorder ? "rgba(201,169,110,0.3)" : "transparent", boxShadow: showBorder ? "0 0 60px rgba(201,169,110,0.12)" : "none", transition: isDraggingRef.current ? "none" : "all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94)", isCenter: absDiff < 0.5, }; } return (
{/* Stage spotlight glow */}
{team.title}
{/* Stage */}
{/* Spotlight cone */}
{/* Cards */} {team.members.map((m, i) => { const style = getCardStyle(i); if (!style) return null; return (
{m.name} {style.isCenter && ( <>

{m.name}

{m.role}

)}
); })}
{/* Member info */}
{member.instagram && ( {member.instagram.split("/").filter(Boolean).pop()} )} {member.description && (

{member.description}

)} {/* Progress dots */}
{team.members.map((_, i) => (
); }