Files
New-Optic/components/HeroSection.tsx
2026-05-16 00:04:02 +01:00

88 lines
5.3 KiB
TypeScript

"use client";
import { motion, useMotionTemplate, useMotionValue, useSpring, useTransform } from "framer-motion";
import { ArrowRight, ShieldCheck } from "lucide-react";
import Image from "next/image";
import { useEffect, useState } from "react";
import { business } from "@/config/business";
import type { Messages } from "@/messages";
import PhysicsButton from "./PhysicsButton";
export default function HeroSection({ t, whatsappUrl }: { t: Messages; whatsappUrl: string }) {
const [canAnimateIntro, setCanAnimateIntro] = useState(false);
const pointerX = useMotionValue(0);
const pointerY = useMotionValue(0);
const smoothX = useSpring(pointerX, { stiffness: 90, damping: 22, mass: 0.4 });
const smoothY = useSpring(pointerY, { stiffness: 90, damping: 22, mass: 0.4 });
const rotateX = useTransform(smoothY, [-0.5, 0.5], [4, -4]);
const rotateY = useTransform(smoothX, [-0.5, 0.5], [-5, 5]);
const imageTransform = useMotionTemplate`perspective(1200px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
useEffect(() => {
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const desktop = window.matchMedia("(min-width: 1024px)").matches;
setCanAnimateIntro(desktop && !reduced);
}, []);
return (
<section id="home" className="relative px-4 pb-20 pt-32 sm:px-6 lg:pb-28 lg:pt-40">
<motion.div className="absolute left-1/2 top-24 hidden h-72 w-72 -translate-x-1/2 rounded-full bg-optical/10 blur-3xl lg:block" animate={canAnimateIntro ? { scale: [1, 1.16, 1], opacity: [0.55, 0.88, 0.55] } : undefined} transition={{ duration: 8, repeat: Infinity, ease: "easeInOut" }} />
<div className="mx-auto grid max-w-7xl items-center gap-12 lg:grid-cols-[1fr_0.92fr]">
<motion.div initial={canAnimateIntro ? { opacity: 0, y: 24 } : false} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }} className="relative z-10 text-center lg:text-start">
<div className="mx-auto mb-6 inline-flex items-center gap-2 rounded-full border border-ink/10 bg-white/65 px-4 py-2 text-xs font-semibold uppercase tracking-[0.18em] text-ink/60 shadow-sm backdrop-blur lg:mx-0">
<ShieldCheck size={15} className="text-optical" />
{t.hero.eyebrow}
</div>
<h1 className="mx-auto max-w-4xl text-5xl font-semibold tracking-[-0.075em] text-ink sm:text-7xl lg:mx-0 lg:text-8xl">{t.hero.title}</h1>
<p className="mx-auto mt-6 max-w-2xl text-lg leading-8 text-ink/64 sm:text-xl lg:mx-0">{t.hero.subtitle}</p>
<div className="mt-9 flex flex-col justify-center gap-3 sm:flex-row lg:justify-start">
<PhysicsButton href={whatsappUrl} external className="rounded-full bg-ink px-7 py-4 text-sm font-semibold text-white shadow-soft transition-colors hover:bg-optical">
{t.hero.primary}
<ArrowRight size={16} className="transition group-hover:translate-x-0.5 rtl:rotate-180" />
</PhysicsButton>
<PhysicsButton href="#services" className="rounded-full border border-ink/10 bg-white/65 px-7 py-4 text-sm font-semibold text-ink shadow-sm backdrop-blur transition-colors hover:bg-white">
{t.hero.secondary}
</PhysicsButton>
</div>
<p className="mt-7 text-sm text-ink/48">{t.hero.note}</p>
</motion.div>
<motion.div
initial={canAnimateIntro ? { opacity: 0, scale: 0.96, y: 24 } : false}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{ duration: 1, delay: 0.1, ease: [0.22, 1, 0.36, 1] }}
onPointerMove={(event) => {
if (!canAnimateIntro) return;
const bounds = event.currentTarget.getBoundingClientRect();
pointerX.set((event.clientX - bounds.left) / bounds.width - 0.5);
pointerY.set((event.clientY - bounds.top) / bounds.height - 0.5);
}}
onPointerLeave={() => {
pointerX.set(0);
pointerY.set(0);
}}
style={canAnimateIntro ? { transform: imageTransform } : undefined}
className="relative mx-auto w-full max-w-xl lg:max-w-none lg:will-change-transform"
>
<div className="absolute -inset-6 hidden rounded-[3rem] bg-gradient-to-br from-optical/14 via-white/0 to-silver/45 blur-2xl lg:block" />
<div className="glass relative overflow-hidden rounded-[2.6rem] p-3">
<span className="floating-sheen" />
<div className="relative aspect-[4/3] overflow-hidden rounded-[2rem] bg-silver/40">
<Image src={business.assets.hero} alt={t.hero.imageAlt} fill sizes="(min-width: 1024px) 45vw, 92vw" className="object-cover" priority />
</div>
<div className="absolute bottom-7 left-7 right-7 rounded-[1.7rem] border border-white/70 bg-white/72 p-4 shadow-glass backdrop-blur-xl sm:p-5">
<div className="flex items-center justify-between gap-4">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-optical/80">{business.name}</p>
<p className="mt-1 text-sm font-semibold text-ink">Temara, Morocco</p>
</div>
<div className="grid size-12 place-items-center rounded-full bg-ink text-xs font-semibold text-white">2011</div>
</div>
</div>
</div>
</motion.div>
</div>
</section>
);
}