"use client" import { useState, useRef, useEffect, useCallback } from "react" import { ArrowLeft, ArrowRight } from "lucide-react" import { CoverSlide } from "@/components/slides/cover-slide" import { ServiceSlide } from "@/components/slides/service-slide" import { ClientsSlide } from "@/components/slides/clients-slide" import { ContactSlide } from "@/components/slides/contact-slide" import { services } from "@/lib/brochure-data" import { cn } from "@/lib/utils" // Desktop: cover + services + contact // Mobile: cover + services + clients + contact const DESKTOP_SLIDES = 2 + services.length const TOTAL_SLIDES = DESKTOP_SLIDES // used for desktop nav dots; mobile scrolls freely export function Brochure() { const [active, setActive] = useState(0) const [hasMounted, setHasMounted] = useState(false) const [headerSolid, setHeaderSolid] = useState(false) const scrollerRef = useRef(null) useEffect(() => { setHasMounted(true) }, []) const isDesktop = () => typeof window !== "undefined" && window.matchMedia("(min-width: 768px)").matches const goTo = useCallback((index: number) => { const scroller = scrollerRef.current if (!scroller) return const clamped = Math.max(0, Math.min(TOTAL_SLIDES - 1, index)) if (isDesktop()) { scroller.scrollTo({ left: clamped * scroller.clientWidth, behavior: "smooth" }) } else { scroller.children[clamped]?.scrollIntoView({ behavior: "smooth" }) } }, []) // Keep the active index in sync with the real scroll position. useEffect(() => { const scroller = scrollerRef.current if (!scroller) return let raf = 0 const onScroll = () => { cancelAnimationFrame(raf) raf = requestAnimationFrame(() => { if (isDesktop()) { const index = Math.round(scroller.scrollLeft / scroller.clientWidth) setActive(index) setHeaderSolid(false) } else { // Solid header once user scrolls past 60px on mobile setHeaderSolid(scroller.scrollTop > 60) const children = Array.from(scroller.children) as HTMLElement[] let closest = 0 let minDist = Infinity children.forEach((child, i) => { const dist = Math.abs(child.offsetTop - scroller.scrollTop) if (dist < minDist) { minDist = dist closest = i } }) setActive(closest) } }) } scroller.addEventListener("scroll", onScroll, { passive: true }) return () => { scroller.removeEventListener("scroll", onScroll) cancelAnimationFrame(raf) } }, []) // Translate vertical wheel into horizontal navigation on desktop so a normal // mouse wheel moves the brochure sideways, one slide per gesture. useEffect(() => { const scroller = scrollerRef.current if (!scroller) return let lock = false const onWheel = (e: WheelEvent) => { if (!isDesktop()) return // Let native horizontal wheels (trackpads) scroll naturally. if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) return e.preventDefault() if (lock) return if (Math.abs(e.deltaY) < 8) return lock = true const current = Math.round(scroller.scrollLeft / scroller.clientWidth) goTo(current + (e.deltaY > 0 ? 1 : -1)) window.setTimeout(() => { lock = false }, 450) } scroller.addEventListener("wheel", onWheel, { passive: false }) return () => scroller.removeEventListener("wheel", onWheel) }, [goTo]) // Keyboard navigation on desktop. useEffect(() => { const onKey = (e: KeyboardEvent) => { if (!isDesktop()) return if (e.key === "ArrowRight" || e.key === "ArrowDown") { e.preventDefault() goTo(active + 1) } if (e.key === "ArrowLeft" || e.key === "ArrowUp") { e.preventDefault() goTo(active - 1) } } window.addEventListener("keydown", onKey) return () => window.removeEventListener("keydown", onKey) }, [active, goTo]) return (
{/* Fixed brand header */}
{ if (isDesktop()) { e.preventDefault() goTo(TOTAL_SLIDES - 1) } }} className="pointer-events-auto rounded-full border border-border bg-card/40 px-4 py-1.5 text-xs font-medium uppercase tracking-wider text-foreground backdrop-blur transition-colors hover:bg-card" > Contacto
{/* Slides scroller: native horizontal scroll-snap on desktop, vertical on mobile */}
goTo(1)} /> {services.map((service, i) => ( ))} {/* Mobile-only: all clients consolidated in one slide */}
{/* Desktop nav controls */} {hasMounted && (
{Array.from({ length: TOTAL_SLIDES }).map((_, i) => (
)}
) } function Slide({ children, id, }: { children: React.ReactNode id?: string }) { return (
{children}
) }