10 Commits

2 changed files with 160 additions and 5 deletions

View File

@@ -2,6 +2,9 @@
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple';
'use client';
import { useState, useEffect } from 'react';
import HeroOverlay from '@/components/sections/hero/HeroOverlay';
import InlineImageSplitTextAbout from '@/components/sections/about/InlineImageSplitTextAbout';
import FeatureBento from '@/components/sections/feature/FeatureBento';
@@ -9,9 +12,37 @@ import MetricCardSeven from '@/components/sections/metrics/MetricCardSeven';
import TestimonialCardSixteen from '@/components/sections/testimonial/TestimonialCardSixteen';
import ContactSplitForm from '@/components/sections/contact/ContactSplitForm';
import FooterBaseCard from '@/components/sections/footer/FooterBaseCard';
import AnimatedCursor from '@/components/AnimatedCursor';
import LofiMusicPlayer from '@/components/LofiMusicPlayer';
import { Sparkles, TrendingUp } from "lucide-react";
export default function LandingPage() {
useEffect(() => {
const playClickSound = () => {
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800;
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.1);
};
const handleClick = () => {
playClickSound();
};
document.addEventListener('click', handleClick);
return () => document.removeEventListener('click', handleClick);
}, []);
return (
<ThemeProvider
defaultButtonVariant="hover-bubble"
@@ -25,6 +56,8 @@ export default function LandingPage() {
secondaryButtonStyle="radial-glow"
headingFontWeight="medium"
>
<AnimatedCursor />
<LofiMusicPlayer />
<div id="nav" data-section="nav">
<NavbarStyleApple
brandName="Webild"

View File

@@ -1,6 +1,8 @@
"use client";
import { memo } from "react";
"use client"
import { memo, useState, useRef, useEffect } from "react";
import CardStack from "@/components/cardStack/CardStack";
import MediaContent from "@/components/shared/MediaContent";
import { cls } from "@/lib/utils";
@@ -83,14 +85,134 @@ const TestimonialCard = memo(({
overlayClassName = "",
ratingClassName = "",
nameClassName = "",
roleClassName = "",
roleClassName = "",
companyClassName = "",
}: TestimonialCardProps) => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [scrollY, setScrollY] = useState(0);
const cardRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleScroll = () => setScrollY(window.scrollY);
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
setMousePosition({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
};
const parallaxOffset = scrollY * 0.05;
return (
<div className={cls("relative h-full aspect-[8/10] rounded-theme-capped overflow-hidden group perspective", cardClassName)} style={{ perspective: "1000px" }}>
<div className="relative w-full h-full transition-transform duration-500 ease-out group-hover:[transform:rotateY(180deg)]" style={{ transformStyle: "preserve-3d" }}>
<div
ref={cardRef}
className={cls("relative h-full aspect-[8/10] rounded-theme-capped overflow-hidden group", cardClassName)}
style={{animation: "cardEntrance 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards"}}
onMouseMove={handleMouseMove}
>
<style>{`
@keyframes cardEntrance {
from {
opacity: 0;
transform: translateY(30px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes glowPulse {
0%, 100% {
box-shadow: 0 0 20px rgba(var(--accent-rgb), 0.3), 0 20px 40px rgba(0, 0, 0, 0.15);
}
50% {
box-shadow: 0 0 40px rgba(var(--accent-rgb), 0.5), 0 25px 50px rgba(0, 0, 0, 0.2);
}
}
@keyframes rotateBorder {
from {
background-position: 0% 0%;
}
to {
background-position: 360% 360%;
}
}
@keyframes colorShift {
0%, 100% {
filter: hue-rotate(0deg);
}
50% {
filter: hue-rotate(15deg);
}
}
@keyframes sparkle {
0%, 100% {
opacity: 0;
transform: scale(0);
}
50% {
opacity: 1;
transform: scale(1);
}
}
@keyframes subtleBounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-4px);
}
}
.group:hover .testimonial-inner {
animation: glowPulse 2s ease-in-out infinite, colorShift 3s ease-in-out infinite, subtleBounce 0.6s ease-in-out infinite;
}
.group:hover .rotating-border {
animation: rotateBorder 3s linear infinite;
}
.sparkle-particle {
position: absolute;
pointer-events: none;
}
.sparkle-particle.active {
animation: sparkle 0.8s ease-out forwards;
}
`}</style>
<div
className="testimonial-inner relative w-full h-full transition-all duration-500 ease-out group-hover:shadow-2xl group-hover:-translate-y-12 group-hover:scale-105 group-hover:[transform:perspective(1000px)_rotateX(2deg)_rotateY(-3deg)_translateY(-48px)_scale(1.05)]"
style={{transform: `translateY(${parallaxOffset}px)`}}
>
{/* Rotating border effect */}
<div
className="rotating-border absolute inset-0 rounded-theme-capped pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-300"
style={{
background: "conic-gradient(from 0deg, rgba(var(--accent-rgb), 0.5), rgba(var(--accent-rgb), 0.1), rgba(var(--accent-rgb), 0.5))",
backgroundSize: "200% 200%",
}}
/>
{/* Sparkle particles */}
{[...Array(6)].map((_, i) => (
<div
key={i}
className="sparkle-particle active"
style={{
left: `${mousePosition.x}px`,
top: `${mousePosition.y}px`,
width: "8px",
height: "8px",
background: "radial-gradient(circle, rgba(var(--accent-rgb), 0.8), transparent)",
borderRadius: "50%",
transform: `translate(-50%, -50%) rotate(${(i / 6) * 360}deg) translateX(20px)`,
}}
/>
))}
{/* Front of card */}
<div style={{ backfaceVisibility: "hidden" }} className="absolute w-full h-full">
<div className="absolute w-full h-full">
<MediaContent
imageSrc={testimonial.imageSrc}
videoSrc={testimonial.videoSrc}