'use client'; import { useState, useEffect, useRef } from 'react'; import Image from 'next/image'; import { useMediaQuery } from '@react-hook/media-query'; import { ChevronDown, Phone, Mail } from 'lucide-react'; import { z } from 'zod'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { Button } from '@/components/ui/button'; import { Form, FormControl, FormField, FormItem, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; const formSchema = z.object({ firstName: z.string().min(1, 'First name is required'), lastName: z.string().min(1, 'Last name is required'), email: z.string().email('Invalid email address'), phone: z.string().min(1, 'Phone is required'), message: z.string().optional(), }); export default function ContactPage() { const [mounted, setMounted] = useState(false); const [logoPosition, setLogoPosition] = useState('center'); const [logoStyle, setLogoStyle] = useState({}); const [buttonOpacity, setButtonOpacity] = useState(1); const [chevronOpacity, setChevronOpacity] = useState(1); const [contactTop, setContactTop] = useState(0); const [windowHeight, setWindowHeight] = useState(0); const contactSectionRef = useRef(null); const logoRef = useRef(null); const animationFrameRef = useRef(null); const lastScrollY = useRef(0); const isMobile = useMediaQuery("(max-width: 768px)"); const isDesktop = useMediaQuery("(min-width: 1280px)"); // Manage form state const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { firstName: "", lastName: "", email: "", phone: "", message: "", }, }); async function onSubmit(values: z.infer) { console.log(values); } // Logo dimensions based on screen size const logoWidth = isMobile ? 240 : isDesktop ? 316 : 280; const logoHeight = isMobile ? 115 : isDesktop ? 151 : 134; useEffect(() => { setMounted(true); // Set initial dimensions on mount setWindowHeight(window.innerHeight); if (contactSectionRef.current) { setContactTop(contactSectionRef.current.offsetTop); } }, []); const updateLogoPosition = () => { if (!contactSectionRef.current) return; const scrollY = window.scrollY; const currentWindowHeight = window.innerHeight; const currentContactTop = contactSectionRef.current.offsetTop; // Update cached values if needed if (currentContactTop !== contactTop) { setContactTop(currentContactTop); } if (currentWindowHeight !== windowHeight) { setWindowHeight(currentWindowHeight); } // Calculate positions - adjusted for scaled logo const targetTopPosition = isMobile ? 10 : 20; // Adjusted for smaller scaled logo if (isMobile) { // For mobile, calculate where the logo should end up const startY = currentWindowHeight * 0.35; // Logo starts higher - 35% from top const endY = targetTopPosition + (logoHeight * 0.5) / 2; // Account for 50% scale const totalDistance = startY - endY; // Keep animation ending at the full contact section position const animationEndScroll = currentContactTop; if (scrollY >= animationEndScroll) { // Logo has reached destination - keep it fixed but move with scroll setLogoPosition('top'); // Calculate position to simulate being part of the page const scrollPastEnd = scrollY - animationEndScroll; const fixedY = endY - scrollPastEnd; const mobileScale = 0.5; // Final scale for mobile setLogoStyle({ position: 'fixed', top: `${fixedY}px`, left: '50%', transform: `translate3d(-50%, 0, 0) scale3d(${mobileScale}, ${mobileScale}, 1)`, transformOrigin: 'center', willChange: 'transform', transition: 'none', zIndex: 50 }); } else if (scrollY > 0) { // Animate logo from center to destination - starts immediately at any scroll const progress = scrollY / animationEndScroll; const currentY = startY - (totalDistance * progress); const mobileScale = 1 - (0.5 * progress); // Scale from 1.0 to 0.5 setLogoPosition('animating'); setLogoStyle({ position: 'fixed', top: `${currentY}px`, left: '50%', transform: `translate3d(-50%, 0, 0) scale3d(${mobileScale}, ${mobileScale}, 1)`, transformOrigin: 'center', willChange: 'transform', transition: 'none', zIndex: 50 }); } else { // At the top - logo at starting position setLogoPosition('center'); setLogoStyle({ position: 'fixed', top: `${startY}px`, left: '50%', transform: 'translate3d(-50%, 0, 0) scale3d(1, 1, 1)', transformOrigin: 'center', willChange: 'transform', transition: 'none', zIndex: 50 }); } } else { // Desktop - standard animation with scaling const logoSpeed = 0.4; const centerY = currentWindowHeight / 2; const targetTopPosition = 20; // Reduced from 100px to account for smaller logo const totalDistance = centerY - targetTopPosition - logoHeight / 2; const logoYPosition = -(scrollY * logoSpeed); const maxUpwardMovement = -totalDistance; const animatedY = Math.max(logoYPosition, maxUpwardMovement); // Calculate scale based on scroll progress const maxScroll = 500; // Scroll distance at which scaling completes const scrollProgress = Math.min(scrollY / maxScroll, 1); const scale = 1 - (0.6 * scrollProgress); // Scale from 1.0 to 0.4 // Update state based on scroll if (scrollY > 10) { setLogoPosition('animating'); } else { setLogoPosition('center'); } // Fixed positioning with animation and scaling setLogoStyle({ position: 'fixed', top: '50%', left: '50%', transform: `translate3d(-50%, calc(-50% + ${animatedY}px), 0) scale3d(${scale}, ${scale}, 1)`, transformOrigin: 'center', willChange: 'transform', transition: 'none', zIndex: 50 }); } // Hide button and chevron - faster on mobile const fadeThreshold = isMobile ? 5 : 10; // Fade out at just 5px scroll on mobile if (scrollY > fadeThreshold) { setButtonOpacity(0); setChevronOpacity(0); } else { setButtonOpacity(1); setChevronOpacity(1); } }; // Animated scroll to form const scrollToForm = () => { if (!contactSectionRef.current) { console.error('Contact section ref not found'); return; } console.log('Starting scroll animation to:', contactSectionRef.current.offsetTop); // Use native smooth scrolling window.scrollTo({ top: contactSectionRef.current.offsetTop, behavior: 'smooth' }); }; // Add scroll listener for bidirectional animation with RAF useEffect(() => { let ticking = false; const handleScroll = () => { lastScrollY.current = window.scrollY; if (!ticking) { animationFrameRef.current = requestAnimationFrame(() => { updateLogoPosition(); ticking = false; }); ticking = true; } }; const handleResize = () => { setWindowHeight(window.innerHeight); if (contactSectionRef.current) { setContactTop(contactSectionRef.current.offsetTop); } updateLogoPosition(); }; // Initial position update updateLogoPosition(); window.addEventListener('scroll', handleScroll, { passive: true }); window.addEventListener('resize', handleResize); return () => { window.removeEventListener('scroll', handleScroll); window.removeEventListener('resize', handleResize); if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); } }; }, [isMobile, isDesktop, logoHeight, windowHeight]); // Don't render until mounted to avoid hydration mismatch if (!mounted) { return null; } return (
{/* Hero Section - Full Viewport Height */}
{/* Single Port Amador Logo with dynamic positioning - Always rendered */}
Port Amador
{/* Button with fade out on scroll */} {/* Chevron Down - with fade out on scroll */}
{/* Contact Section - Desktop Layout with Marina Image */}
{isMobile ? ( // Mobile Layout - Stacked with Image
{/* Form Section */}

Connect with us

( )} /> ( )} /> ( )} /> ( )} /> (