Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94c8df1328 | |||
| 615b4d2859 | |||
| a34b39ff5e | |||
| 9183f66f50 | |||
| 604104c875 | |||
| 54497158b0 |
@@ -4,9 +4,6 @@ import { Inter } from "next/font/google";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { ServiceWrapper } from "@/components/ServiceWrapper";
|
import { ServiceWrapper } from "@/components/ServiceWrapper";
|
||||||
import Tag from "@/tag/Tag";
|
import Tag from "@/tag/Tag";
|
||||||
import { BackgroundLofiMusicPlayer } from "@/components/BackgroundLofiMusicPlayer";
|
|
||||||
|
|
||||||
// Create the BackgroundLofiMusicPlayer component file at src/components/BackgroundLofiMusicPlayer.tsx
|
|
||||||
|
|
||||||
const notoSans = Noto_Sans({
|
const notoSans = Noto_Sans({
|
||||||
variable: "--font-noto-sans", subsets: ["latin"],
|
variable: "--font-noto-sans", subsets: ["latin"],
|
||||||
@@ -31,7 +28,6 @@ export default function RootLayout({
|
|||||||
<body
|
<body
|
||||||
className={`${notoSans.variable} ${inter.variable} antialiased`}
|
className={`${notoSans.variable} ${inter.variable} antialiased`}
|
||||||
>
|
>
|
||||||
<BackgroundLofiMusicPlayer />
|
|
||||||
<Tag />
|
<Tag />
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
|
|||||||
106
src/app/page.tsx
106
src/app/page.tsx
@@ -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={[
|
||||||
|
|||||||
142
src/components/BackgroundMusicPlayer.tsx
Normal file
142
src/components/BackgroundMusicPlayer.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user