7 Commits

3 changed files with 400 additions and 9 deletions

View File

@@ -10,6 +10,7 @@ import TestimonialCardOne from '@/components/sections/testimonial/TestimonialCar
import ContactFaq from '@/components/sections/contact/ContactFaq'; import ContactFaq from '@/components/sections/contact/ContactFaq';
import FooterMedia from '@/components/sections/footer/FooterMedia'; import FooterMedia from '@/components/sections/footer/FooterMedia';
import { Award, Sparkles, Users, Music } from "lucide-react"; import { Award, Sparkles, Users, Music } from "lucide-react";
import BackgroundMusicPlayer from '@/components/BackgroundMusicPlayer';
export default function LandingPage() { export default function LandingPage() {
return ( return (
@@ -25,6 +26,7 @@ export default function LandingPage() {
secondaryButtonStyle="glass" secondaryButtonStyle="glass"
headingFontWeight="normal" headingFontWeight="normal"
> >
<BackgroundMusicPlayer />
<div id="nav" data-section="nav"> <div id="nav" data-section="nav">
<NavbarStyleCentered <NavbarStyleCentered
navItems={[ navItems={[
@@ -42,8 +44,95 @@ export default function LandingPage() {
/> />
</div> </div>
<style>{`
@keyframes scaleHover {
from { transform: scale(1); }
to { transform: scale(1.02); }
}
@keyframes shadowDepth {
from { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); }
to { box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); }
}
@keyframes slideInLeft {
from { transform: translateX(-20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes fadeGlow {
from { filter: drop-shadow(0 0 0px rgba(168, 85, 247, 0)); }
to { filter: drop-shadow(0 0 20px rgba(168, 85, 247, 0.6)); }
}
@keyframes slideInRight {
from { transform: translateX(20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes glowPulse {
from { box-shadow: 0 0 10px rgba(59, 130, 246, 0.5); }
to { box-shadow: 0 0 30px rgba(59, 130, 246, 0.8); }
}
@keyframes floatUp {
from { transform: translateY(10px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
#hero-section {
transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#hero-section:hover {
animation: scaleHover 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
filter: drop-shadow(0 25px 50px rgba(0, 0, 0, 0.25));
}
#about-section {
transition: all 0.6s ease-out;
}
#about-section:hover {
animation: shadowDepth 0.6s ease-out forwards;
transform: translateY(-5px);
}
#features-section {
transition: all 0.5s ease-in-out;
}
#features-section:hover {
animation: slideInLeft 0.5s ease-in-out forwards;
filter: brightness(1.05);
}
#team-section {
transition: all 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
#team-section:hover {
animation: fadeGlow 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
transform: scale(1.01);
}
#testimonials-section {
transition: all 0.6s ease-out;
}
#testimonials-section:hover {
animation: slideInRight 0.6s ease-out forwards;
box-shadow: 0 15px 35px rgba(168, 85, 247, 0.2);
}
#contact-section {
transition: all 0.5s ease-in-out;
}
#contact-section:hover {
animation: glowPulse 0.5s ease-in-out forwards;
transform: translateY(-3px);
}
footer {
transition: all 0.6s ease-out;
}
footer:hover {
animation: floatUp 0.6s ease-out forwards;
box-shadow: 0 -10px 30px rgba(59, 130, 246, 0.3);
}
`}</style>
<div id="hero-section" data-section="hero-section"> <div id="hero-section" data-section="hero-section">
<HeroLogoBillboardSplit <HeroImageOverlay
logoText="Harmony Studios" logoText="Harmony Studios"
description="Where musical excellence meets cutting-edge technology. We craft sonic experiences that resonate with artists and audiences alike." description="Where musical excellence meets cutting-edge technology. We craft sonic experiences that resonate with artists and audiences alike."
background={{ variant: "radial-gradient" }} background={{ variant: "radial-gradient" }}
@@ -52,17 +141,16 @@ export default function LandingPage() {
{ text: "Book a Session", href: "#contact-section" } { text: "Book a Session", href: "#contact-section" }
]} ]}
buttonAnimation="slide-up" buttonAnimation="slide-up"
layoutOrder="default"
imageSrc="https://img.b2bpic.net/free-photo/piano-key-guitar-string-musician-creativity-generated-by-ai_188544-25821.jpg" imageSrc="https://img.b2bpic.net/free-photo/piano-key-guitar-string-musician-creativity-generated-by-ai_188544-25821.jpg"
imageAlt="Elegant recording studio setup with professional audio equipment" imageAlt="Elegant recording studio setup with professional audio equipment"
mediaAnimation="blur-reveal" mediaAnimation="blur-reveal"
frameStyle="card" overlayOpacity={0.4}
ariaLabel="Hero section featuring Harmony Studios branding and studio imagery" ariaLabel="Hero section featuring Harmony Studios branding and studio imagery"
/> />
</div> </div>
<div id="about-section" data-section="about-section"> <div id="about-section" data-section="about-section">
<MetricSplitMediaAbout <TimelineAbout
title="Crafting Soundscapes That Define Artistry" title="Crafting Soundscapes That Define Artistry"
description="With over two decades of experience, our studio has been the creative sanctuary where legendary tracks are born. We blend technical excellence with artistic vision to deliver recordings that resonate beyond the studio walls." description="With over two decades of experience, our studio has been the creative sanctuary where legendary tracks are born. We blend technical excellence with artistic vision to deliver recordings that resonate beyond the studio walls."
metrics={[ metrics={[
@@ -83,7 +171,7 @@ export default function LandingPage() {
</div> </div>
<div id="features-section" data-section="features-section"> <div id="features-section" data-section="features-section">
<FeatureCardThree <FeatureCardTwo
title="Professional Recording Services" title="Professional Recording Services"
description="State-of-the-art facilities and expert engineers to bring your musical vision to life with unparalleled sound quality." description="State-of-the-art facilities and expert engineers to bring your musical vision to life with unparalleled sound quality."
tag="Studio Excellence" tag="Studio Excellence"
@@ -116,7 +204,7 @@ export default function LandingPage() {
</div> </div>
<div id="team-section" data-section="team-section"> <div id="team-section" data-section="team-section">
<TeamCardSix <TeamCardFour
members={[ members={[
{ {
id: "member-1", name: "Alexandra Chen", role: "Lead Producer & Engineer", imageSrc: "https://img.b2bpic.net/free-photo/music-producer-using-his-headphones-mix-master-session_482257-121292.jpg", imageAlt: "Alexandra Chen, Lead Producer & Engineer at Melody Studio" id: "member-1", name: "Alexandra Chen", role: "Lead Producer & Engineer", imageSrc: "https://img.b2bpic.net/free-photo/music-producer-using-his-headphones-mix-master-session_482257-121292.jpg", imageAlt: "Alexandra Chen, Lead Producer & Engineer at Melody Studio"
@@ -155,7 +243,7 @@ export default function LandingPage() {
</div> </div>
<div id="testimonials-section" data-section="testimonials-section"> <div id="testimonials-section" data-section="testimonials-section">
<TestimonialCardOne <TestimonialCardThree
testimonials={[ testimonials={[
{ {
id: "testimonial-1", name: "Alexandra Chen", role: "Lead Vocalist", company: "Midnight Echoes", rating: 5, id: "testimonial-1", name: "Alexandra Chen", role: "Lead Vocalist", company: "Midnight Echoes", rating: 5,
@@ -190,7 +278,7 @@ export default function LandingPage() {
</div> </div>
<div id="contact-section" data-section="contact-section"> <div id="contact-section" data-section="contact-section">
<ContactFaq <ContactForm
faqs={[ faqs={[
{ {
id: "faq-1", title: "What recording packages do you offer?", content: "We provide comprehensive recording packages tailored to different needs, from basic tracking sessions to full production services including mixing, mastering, and artist development." id: "faq-1", title: "What recording packages do you offer?", content: "We provide comprehensive recording packages tailored to different needs, from basic tracking sessions to full production services including mixing, mastering, and artist development."
@@ -220,7 +308,7 @@ export default function LandingPage() {
</div> </div>
<div id="footer-section" data-section="footer-section"> <div id="footer-section" data-section="footer-section">
<FooterMedia <FooterMinimal
imageSrc="https://img.b2bpic.net/free-photo/artist-props-photography_23-2148885625.jpg" imageSrc="https://img.b2bpic.net/free-photo/artist-props-photography_23-2148885625.jpg"
imageAlt="Elegant music studio interior with vintage instruments and warm lighting" imageAlt="Elegant music studio interior with vintage instruments and warm lighting"
columns={[ columns={[

View File

@@ -0,0 +1,161 @@
'use client';
import { useState, useEffect, useRef } from 'react';
import { Play, Pause, Volume2, VolumeX } from 'lucide-react';
interface BackgroundLofiMusicPlayerProps {}
export function BackgroundLofiMusicPlayer({}: BackgroundLofiMusicPlayerProps) {
const audioRef = useRef<HTMLAudioElement>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [volume, setVolume] = useState(0.3);
const [isMuted, setIsMuted] = useState(false);
const [hasUserInteracted, setHasUserInteracted] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
const lofiStreamUrl =
'https://www.chosic.com/wp-content/uploads/2021/07/free-lofi-hip-hop-beats-background-music-for-videos.mp3';
useEffect(() => {
if (audioRef.current) {
audioRef.current.volume = isMuted ? 0 : volume;
}
}, [volume, isMuted]);
useEffect(() => {
const handleUserInteraction = () => {
setHasUserInteracted(true);
document.removeEventListener('click', handleUserInteraction);
document.removeEventListener('keydown', handleUserInteraction);
};
document.addEventListener('click', handleUserInteraction);
document.addEventListener('keydown', handleUserInteraction);
return () => {
document.removeEventListener('click', handleUserInteraction);
document.removeEventListener('keydown', handleUserInteraction);
};
}, []);
const togglePlayPause = () => {
if (!audioRef.current) return;
if (isPlaying) {
audioRef.current.pause();
setIsPlaying(false);
} else {
audioRef.current.play().catch((error) => {
console.error('Autoplay failed:', error);
});
setIsPlaying(true);
}
};
const toggleMute = () => {
setIsMuted(!isMuted);
};
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setVolume(parseFloat(e.target.value));
setIsMuted(false);
};
return (
<div className="fixed bottom-6 right-6 z-50">
<audio
ref={audioRef}
src={lofiStreamUrl}
loop
crossOrigin="anonymous"
onEnded={() => {
if (audioRef.current) {
audioRef.current.currentTime = 0;
audioRef.current.play();
}
}}
/>
<div
className={`bg-gradient-to-br from-slate-800 to-slate-900 rounded-full shadow-lg border border-slate-700 transition-all duration-300 ${
isExpanded ? 'w-64 rounded-2xl' : 'w-16 h-16'
}`}
>
<div className="flex items-center justify-between p-3 h-16">
<button
onClick={togglePlayPause}
className="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-full bg-gradient-to-br from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 transition-all text-white shadow-md hover:shadow-lg"
title={isPlaying ? 'Pause' : 'Play'}
>
{isPlaying ? (
<Pause size={20} fill="currentColor" />
) : (
<Play size={20} fill="currentColor" />
)}
</button>
{isExpanded && (
<div className="flex-1 flex items-center gap-3 ml-3">
<button
onClick={toggleMute}
className="flex-shrink-0 text-slate-300 hover:text-white transition-colors"
title={isMuted ? 'Unmute' : 'Mute'}
>
{isMuted ? <VolumeX size={18} /> : <Volume2 size={18} />}
</button>
<input
type="range"
min="0"
max="1"
step="0.01"
value={isMuted ? 0 : volume}
onChange={handleVolumeChange}
className="flex-1 h-1 bg-slate-700 rounded-lg appearance-none cursor-pointer accent-purple-500"
title="Volume"
/>
<span className="text-xs text-slate-400 w-8 text-right">
{Math.round((isMuted ? 0 : volume) * 100)}%
</span>
</div>
)}
{!isExpanded && (
<button
onClick={() => setIsExpanded(true)}
className="absolute inset-0 rounded-full"
title="Expand player"
/>
)}
</div>
{isExpanded && (
<button
onClick={() => setIsExpanded(false)}
className="absolute top-2 right-2 text-slate-400 hover:text-white transition-colors"
title="Collapse player"
>
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
)}
</div>
<div className="mt-2 text-center text-xs text-slate-400">
<p>🎵 Lofi Music</p>
</div>
</div>
);
}

View File

@@ -0,0 +1,142 @@
'use client';
import { useState, useEffect, useRef } from 'react';
import { Volume2, VolumeX, Play, Pause } from 'lucide-react';
interface Props {}
export default function BackgroundMusicPlayer({}: Props) {
const audioRef = useRef<HTMLAudioElement>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [volume, setVolume] = useState(0.5);
const [isLoaded, setIsLoaded] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const audio = audioRef.current;
if (!audio) return;
audio.volume = volume;
}, [volume]);
useEffect(() => {
const audio = audioRef.current;
if (!audio) return;
const handleCanPlay = () => {
setIsLoaded(true);
setError(null);
};
const handleError = () => {
setError('Failed to load audio');
setIsPlaying(false);
};
const handleEnded = () => {
audio.currentTime = 0;
audio.play().catch(() => {
setIsPlaying(false);
});
};
audio.addEventListener('canplay', handleCanPlay);
audio.addEventListener('error', handleError);
audio.addEventListener('ended', handleEnded);
return () => {
audio.removeEventListener('canplay', handleCanPlay);
audio.removeEventListener('error', handleError);
audio.removeEventListener('ended', handleEnded);
};
}, []);
const togglePlayPause = async () => {
const audio = audioRef.current;
if (!audio) return;
try {
if (isPlaying) {
audio.pause();
setIsPlaying(false);
} else {
await audio.play();
setIsPlaying(true);
}
} catch (err) {
setError('Autoplay policy prevented playback');
setIsPlaying(false);
}
};
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newVolume = parseFloat(e.target.value);
setVolume(newVolume);
};
return (
<div className="fixed bottom-6 right-6 bg-gradient-to-br from-slate-900 to-slate-800 rounded-lg shadow-2xl p-4 w-80 border border-slate-700">
<audio
ref={audioRef}
src="https://www.freepik.com/audio/tune/static"
loop
crossOrigin="anonymous"
/>
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-sm font-semibold text-white">Background Music</h3>
<div className="text-xs text-slate-400">
{isLoaded ? 'Ready' : 'Loading...'}
</div>
</div>
{error && (
<div className="text-xs text-red-400 bg-red-950 bg-opacity-50 rounded px-2 py-1">
{error}
</div>
)}
<div className="flex items-center gap-3">
<button
onClick={togglePlayPause}
disabled={!isLoaded}
className="flex-shrink-0 bg-blue-600 hover:bg-blue-700 disabled:bg-slate-600 disabled:cursor-not-allowed text-white rounded-full p-2 transition-colors duration-200"
aria-label={isPlaying ? 'Pause' : 'Play'}
>
{isPlaying ? (
<Pause size={20} fill="currentColor" />
) : (
<Play size={20} fill="currentColor" />
)}
</button>
<div className="flex-1 flex items-center gap-2">
{volume === 0 ? (
<VolumeX size={18} className="text-slate-400 flex-shrink-0" />
) : (
<Volume2 size={18} className="text-slate-400 flex-shrink-0" />
)}
<input
type="range"
min="0"
max="1"
step="0.05"
value={volume}
onChange={handleVolumeChange}
className="w-full h-2 bg-slate-700 rounded-lg appearance-none cursor-pointer accent-blue-600"
aria-label="Volume"
/>
<span className="text-xs text-slate-400 w-8 text-right">
{Math.round(volume * 100)}%
</span>
</div>
</div>
<div className="text-xs text-slate-500 text-center">
{isPlaying ? 'Now playing' : 'Paused'}
</div>
</div>
</div>
);
}