Migration vers Astro et refonte du hero

- Remplacement de React/Vite par Astro avec Tailwind CSS
- Nouvelle accroche : « Rêver grand. Jouer vrai. »
- Ajout d'un sélecteur de thèmes temporaire (4 palettes)
- Mise à jour du .gitignore (dist/, .astro/, .clever.json)
This commit is contained in:
Jalil Arfaoui 2026-03-05 19:35:02 +01:00
parent 457779e829
commit 4ed41c370d
32 changed files with 6201 additions and 4248 deletions

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
node_modules/ node_modules/
.direnv/ .direnv/
dist/
.astro/
.clever.json

10
astro.config.mjs Normal file
View file

@ -0,0 +1,10 @@
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
import icon from 'astro-icon';
export default defineConfig({
integrations: [icon()],
vite: {
plugins: [tailwindcss()],
},
});

View file

@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Google AI Studio App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

7487
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,35 +1,19 @@
{ {
"name": "react-example", "name": "aspireves-org",
"private": true, "private": true,
"version": "0.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --port=3000 --host=0.0.0.0", "dev": "astro dev --port 3030 --host 0.0.0.0",
"build": "vite build", "build": "astro build",
"preview": "vite preview", "preview": "astro preview",
"clean": "rm -rf dist", "clean": "rm -rf dist"
"lint": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@google/genai": "^1.29.0", "astro": "^5.5.0",
"astro-icon": "^1.1.5",
"@iconify-json/lucide": "^1.2.40",
"@tailwindcss/vite": "^4.1.14", "@tailwindcss/vite": "^4.1.14",
"@vitejs/plugin-react": "^5.0.4", "tailwindcss": "^4.1.14"
"lucide-react": "^0.546.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"vite": "^6.2.0",
"express": "^4.21.2",
"dotenv": "^17.2.3",
"better-sqlite3": "^12.4.1",
"motion": "^12.23.24"
},
"devDependencies": {
"@types/node": "^22.14.0",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.0",
"@types/express": "^4.17.21"
} }
} }

View file

@ -1,52 +0,0 @@
import { useState } from 'react';
import Navbar from './components/Navbar';
import Home from './components/Home';
import Spectacles from './components/Spectacles';
import SpectacleDetail from './components/SpectacleDetail';
import Agenda from './components/Agenda';
import Ateliers from './components/Ateliers';
import Contact from './components/Contact';
import Footer from './components/Footer';
export default function App() {
const [currentPage, setCurrentPage] = useState('home');
const handleNavigate = (page: string) => {
setCurrentPage(page);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const renderPage = () => {
if (currentPage.startsWith('spectacle-')) {
const spectacleId = currentPage.replace('spectacle-', '');
return <SpectacleDetail spectacleId={spectacleId} onNavigate={handleNavigate} />;
}
switch (currentPage) {
case 'home':
return <Home onNavigate={handleNavigate} />;
case 'spectacles':
return <Spectacles onNavigate={handleNavigate} />;
case 'agenda':
return <Agenda />;
case 'ateliers':
return <Ateliers />;
case 'contact':
return <Contact />;
default:
return <Home onNavigate={handleNavigate} />;
}
};
return (
<div className="bg-cloud min-h-screen flex flex-col">
<Navbar currentPage={currentPage} onNavigate={handleNavigate} />
<main className="flex-grow">
{renderPage()}
</main>
<Footer />
</div>
);
}

View file

@ -1,107 +0,0 @@
import { motion } from 'motion/react';
import { agenda, spectacles } from '../data';
import { MapPin, Ticket, Info, Sparkles, Calendar as CalendarIcon } from 'lucide-react';
export default function Agenda() {
const sortedAgenda = [...agenda].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
return (
<div className="pt-32 pb-24 min-h-screen bg-cloud relative overflow-hidden">
{/* Background Magic */}
<div className="absolute top-1/2 left-0 w-96 h-96 bg-dream-purple/10 rounded-full filter blur-[120px] -translate-x-1/2"></div>
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center mb-12 md:mb-20"
>
<div className="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<CalendarIcon size={16} />
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Calendrier</span>
</div>
<h1 className="font-display text-4xl sm:text-6xl md:text-8xl text-night mb-8">L'<span className="text-dream-coral italic">Agenda</span></h1>
<p className="font-sans text-night/60 max-w-2xl mx-auto text-lg md:text-xl leading-relaxed">
Venez nous voir sur scène ! <br className="hidden sm:block" />Chaque date est une nouvelle aventure.
</p>
</motion.div>
<div className="space-y-6 md:space-y-10">
{sortedAgenda.map((event, index) => {
const spectacle = spectacles.find(s => s.id === event.spectacleId);
const dateObj = new Date(event.date);
const isPast = dateObj < new Date();
return (
<motion.div
key={event.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className={`group relative flex flex-col md:flex-row gap-6 md:gap-10 p-6 md:p-10 rounded-[32px] md:rounded-[40px] border transition-all ${
isPast ? 'opacity-40 grayscale bg-night/5 border-transparent' : 'bg-white border-white shadow-xl shadow-night/5 hover:shadow-dream-purple/10 hover:scale-[1.02]'
}`}
>
{/* Date Block */}
<div className="flex-shrink-0 flex flex-col items-center justify-center w-24 h-24 md:w-36 md:h-36 rounded-[24px] md:rounded-[32px] bg-gradient-to-br from-dream-purple to-dream-blue text-white shadow-lg mx-auto md:mx-0">
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest opacity-80 mb-1">
{dateObj.toLocaleString('fr-FR', { month: 'short' })}
</span>
<span className="font-display text-4xl md:text-6xl">
{dateObj.getDate()}
</span>
<span className="font-sans text-[10px] md:text-xs font-bold opacity-60 mt-1">
{dateObj.getFullYear()}
</span>
</div>
{/* Info Block */}
<div className="flex-grow flex flex-col justify-center space-y-3 md:space-y-4 text-center md:text-left">
<div className="flex items-center justify-center md:justify-start gap-3">
<Sparkles size={14} className="text-dream-coral" />
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest text-dream-coral">
{spectacle?.title}
</span>
{isPast && <span className="text-[10px] uppercase tracking-tighter bg-night/10 px-2 py-0.5 rounded text-night/50 font-bold">Passé</span>}
</div>
<h3 className="font-display text-2xl md:text-4xl text-night">{spectacle?.title}</h3>
<div className="flex items-center justify-center md:justify-start gap-3 text-night/50 font-sans font-bold text-[10px] md:text-sm uppercase tracking-widest">
<MapPin size={16} className="text-dream-blue" />
<span>{event.location}</span>
</div>
</div>
{/* Action Block */}
<div className="flex-shrink-0 flex items-center">
{event.bookingLink ? (
<a
href={event.bookingLink}
target="_blank"
rel="noreferrer"
className="w-full md:w-auto flex items-center justify-center gap-3 bg-night text-cloud px-8 py-4 rounded-full font-sans font-bold text-sm uppercase tracking-widest hover:bg-night/80 transition-all hover:scale-105 shadow-lg shadow-night/20"
>
<Ticket size={20} />
Réserver
</a>
) : (
<div className="w-full md:w-auto flex items-center justify-center gap-2 border-2 border-dashed border-night/10 text-night/30 px-8 py-4 rounded-full font-sans text-sm font-bold uppercase tracking-widest italic">
<Info size={18} />
Sur place
</div>
)}
</div>
</motion.div>
);
})}
</div>
{sortedAgenda.length === 0 && (
<div className="text-center py-32 bg-white/50 rounded-[60px] border-4 border-dashed border-dream-blue/20">
<p className="font-display text-3xl text-night/30 italic">Le nuage se prépare pour de nouveaux voyages...</p>
</div>
)}
</div>
</div>
);
}

View file

@ -1,102 +0,0 @@
import { motion } from 'motion/react';
import { Mail, Clock, MapPin, Users, Sparkles, Star } from 'lucide-react';
import { companyInfo } from '../data';
export default function Ateliers() {
return (
<div className="pt-32 pb-24 min-h-screen bg-cloud relative overflow-hidden">
{/* Background Magic */}
<div className="absolute top-1/4 right-0 w-[500px] h-[500px] bg-dream-pink/20 rounded-full filter blur-[120px] translate-x-1/2"></div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-20 items-center">
<motion.div
initial={{ opacity: 0, x: -30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
className="text-center lg:text-left"
>
<div className="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<Sparkles size={16} />
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Partage & Transmission</span>
</div>
<h1 className="font-display text-4xl sm:text-6xl md:text-8xl text-night mb-8 md:mb-10 leading-tight">Ateliers <br /><span className="text-dream-purple italic">Théâtre</span></h1>
<div className="space-y-6 md:space-y-8 font-sans text-lg md:text-xl text-night/70 leading-relaxed">
<p>
La Compagnie AspiRêves propose des ateliers pour petits et grands enfants, pour que vous aussi vous voliez sur votre petit nuage.
</p>
<p>
Nos ateliers sont basés sur le jeu, l'expression corporelle, la découverte du clown et la pratique théâtrale dans la bienveillance et le plaisir.
</p>
</div>
<div className="mt-12 md:mt-16 grid grid-cols-1 sm:grid-cols-2 gap-6 md:gap-8">
<div className="flex items-start gap-4 md:gap-5 p-5 md:p-6 rounded-2xl md:rounded-3xl bg-white shadow-xl shadow-night/5 border border-white text-left">
<div className="w-12 h-12 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-dream-purple/20 flex items-center justify-center text-dream-purple flex-shrink-0">
<Users size={24} />
</div>
<div>
<h3 className="font-display text-lg md:text-xl text-night mb-1">Pour tous</h3>
<p className="font-sans text-[10px] md:text-sm text-night/50 font-bold uppercase tracking-widest">Enfants & Adultes</p>
</div>
</div>
<div className="flex items-start gap-4 md:gap-5 p-5 md:p-6 rounded-2xl md:rounded-3xl bg-white shadow-xl shadow-night/5 border border-white text-left">
<div className="w-12 h-12 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-dream-blue/20 flex items-center justify-center text-dream-blue flex-shrink-0">
<Clock size={24} />
</div>
<div>
<h3 className="font-display text-lg md:text-xl text-night mb-1">Plaquette</h3>
<p className="font-sans text-[10px] md:text-sm text-night/50 font-bold uppercase tracking-widest">Sur demande</p>
</div>
</div>
<div className="flex items-start gap-4 md:gap-5 p-5 md:p-6 rounded-2xl md:rounded-3xl bg-white shadow-xl shadow-night/5 border border-white sm:col-span-2 text-left">
<div className="w-12 h-12 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-dream-coral/20 flex items-center justify-center text-dream-coral flex-shrink-0">
<MapPin size={24} />
</div>
<div>
<h3 className="font-display text-lg md:text-xl text-night mb-1">Lieux d'intervention</h3>
<p className="font-sans text-[10px] md:text-sm text-night/50 font-bold uppercase tracking-widest">Tarn (81) & Hérault (34)</p>
</div>
</div>
</div>
<div className="mt-12 md:mt-16">
<a
href={`mailto:${companyInfo.emails.ateliers}`}
className="inline-flex items-center gap-4 bg-night text-cloud px-8 py-4 md:px-10 md:py-5 rounded-full font-sans font-bold tracking-widest uppercase text-sm hover:bg-night/90 transition-all hover:scale-105 shadow-xl shadow-night/20"
>
<Mail size={20} />
Nous contacter
</a>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
className="relative px-4 md:px-0"
>
{/* Whimsical Image Frame */}
<div className="relative z-10 aspect-square rounded-[40px] md:rounded-[80px] overflow-hidden border-4 md:border-[12px] border-white shadow-2xl rotate-3 hover:rotate-0 transition-transform duration-700">
<img
src="https://picsum.photos/seed/theater-workshop/800/800"
alt="Atelier Théâtre"
className="w-full h-full object-cover"
referrerPolicy="no-referrer"
/>
</div>
{/* Floating Decorations */}
<div className="absolute -top-10 -right-10 w-48 h-48 bg-dream-purple rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob"></div>
<div className="absolute -bottom-10 -left-10 w-48 h-48 bg-dream-blue rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob animation-delay-2000"></div>
<div className="absolute top-1/2 -right-12 animate-float text-star"><Star size={48} fill="currentColor" /></div>
</motion.div>
</div>
</div>
</div>
);
}

View file

@ -1,118 +0,0 @@
import { motion } from 'motion/react';
import { Mail, Instagram, Facebook, Youtube, Send, Sparkles, Star } from 'lucide-react';
import { companyInfo } from '../data';
export default function Contact() {
return (
<div className="pt-32 pb-24 min-h-screen bg-cloud relative overflow-hidden">
{/* Background Magic */}
<div className="absolute top-0 right-0 w-96 h-96 bg-dream-purple/10 rounded-full filter blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center mb-16 md:mb-24"
>
<div className="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<Sparkles size={16} />
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Contact</span>
</div>
<h1 className="font-display text-5xl md:text-8xl text-night mb-8">Nous <span className="text-dream-coral italic">Joindre</span></h1>
<p className="font-sans text-night/60 max-w-2xl mx-auto text-lg md:text-xl leading-relaxed">
Une question ? Un projet ? N'hésitez pas à nous contacter, <br className="hidden sm:block" />nous vous répondrons avec plaisir.
</p>
</motion.div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10 md:gap-16">
{/* Email Cards */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="space-y-6 md:space-y-8"
>
<div className="bg-white p-8 md:p-10 rounded-[32px] md:rounded-[40px] border border-white shadow-xl shadow-night/5 hover:shadow-dream-purple/10 transition-all group">
<div className="w-14 h-14 rounded-2xl bg-night text-cloud flex items-center justify-center mb-8 group-hover:scale-110 transition-transform">
<Mail size={28} />
</div>
<h3 className="font-display text-2xl md:text-3xl text-night mb-3">Diffusion & Programmation</h3>
<p className="font-sans text-night/50 font-bold uppercase tracking-widest text-xs mb-8">Pour toute demande concernant nos spectacles.</p>
<a
href={`mailto:${companyInfo.emails.pro}`}
className="inline-flex items-center gap-3 text-night font-sans font-bold hover:text-dream-coral transition-colors break-all"
>
{companyInfo.emails.pro}
<Send size={16} className="text-dream-coral" />
</a>
</div>
<div className="bg-white p-8 md:p-10 rounded-[32px] md:rounded-[40px] border border-white shadow-xl shadow-night/5 hover:shadow-dream-blue/10 transition-all group">
<div className="w-14 h-14 rounded-2xl bg-dream-blue text-white flex items-center justify-center mb-8 group-hover:scale-110 transition-transform">
<Mail size={28} />
</div>
<h3 className="font-display text-2xl md:text-3xl text-night mb-3">Ateliers & Pédagogie</h3>
<p className="font-sans text-night/50 font-bold uppercase tracking-widest text-xs mb-8">Pour les inscriptions et renseignements ateliers.</p>
<a
href={`mailto:${companyInfo.emails.ateliers}`}
className="inline-flex items-center gap-3 text-night font-sans font-bold hover:text-dream-blue transition-colors break-all"
>
{companyInfo.emails.ateliers}
<Send size={16} className="text-dream-blue" />
</a>
</div>
</motion.div>
{/* Social & Info */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="bg-night text-cloud p-10 md:p-16 rounded-[40px] md:rounded-[60px] flex flex-col justify-between relative overflow-hidden"
>
<div className="absolute top-0 right-0 w-64 h-64 bg-dream-purple/20 rounded-full filter blur-[80px] -translate-y-1/2 translate-x-1/2"></div>
<div className="relative z-10">
<h3 className="font-display text-4xl md:text-5xl mb-8">Suivez nos <br /><span className="text-dream-coral">aventures</span></h3>
<p className="font-sans text-cloud/60 text-lg md:text-xl mb-12 leading-relaxed">
Rejoignez-nous sur les réseaux sociaux pour découvrir les coulisses, les photos de tournée et les actualités de la compagnie.
</p>
<div className="space-y-6">
<a href={companyInfo.socials.instagram} target="_blank" rel="noreferrer" className="flex items-center gap-6 group">
<div className="w-14 h-14 rounded-2xl border-2 border-cloud/10 flex items-center justify-center group-hover:border-dream-pink group-hover:text-dream-pink transition-all">
<Instagram size={24} />
</div>
<span className="font-sans text-xl font-bold tracking-wide group-hover:text-dream-pink transition-colors">Instagram</span>
</a>
<a href={companyInfo.socials.facebook} target="_blank" rel="noreferrer" className="flex items-center gap-6 group">
<div className="w-14 h-14 rounded-2xl border-2 border-cloud/10 flex items-center justify-center group-hover:border-dream-blue group-hover:text-dream-blue transition-all">
<Facebook size={24} />
</div>
<span className="font-sans text-xl font-bold tracking-wide group-hover:text-dream-blue transition-colors">Facebook</span>
</a>
<a href={companyInfo.socials.youtube} target="_blank" rel="noreferrer" className="flex items-center gap-6 group">
<div className="w-14 h-14 rounded-2xl border-2 border-cloud/10 flex items-center justify-center group-hover:border-dream-coral group-hover:text-dream-coral transition-all">
<Youtube size={24} />
</div>
<span className="font-sans text-xl font-bold tracking-wide group-hover:text-dream-coral transition-colors">YouTube</span>
</a>
</div>
</div>
<div className="mt-16 pt-12 border-t border-cloud/10 relative z-10">
<p className="font-display text-2xl italic text-cloud/40 leading-relaxed">
"Le théâtre est une nourriture aussi indispensable que le pain et le vin."
</p>
</div>
<div className="absolute bottom-10 right-10 text-star opacity-20 animate-float">
<Star size={48} fill="currentColor" />
</div>
</motion.div>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,76 @@
---
import { Icon } from 'astro-icon/components';
import StarFilled from './icons/StarFilled.astro';
import { companyInfo } from '../data';
---
<footer class="bg-night text-cloud py-20 border-t border-cloud/10 relative overflow-hidden">
<!-- Background -->
<div class="absolute top-0 left-0 w-64 h-64 bg-dream-purple/10 rounded-full filter blur-[100px] -translate-y-1/2 -translate-x-1/2"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<div class="grid grid-cols-1 md:grid-cols-4 gap-16 md:gap-12">
<!-- Brand -->
<div class="col-span-1 md:col-span-2">
<div class="flex items-center gap-4 mb-8">
<div class="w-12 h-12 rounded-2xl bg-cloud flex items-center justify-center overflow-hidden shadow-lg">
<span class="text-night font-display text-2xl">A</span>
</div>
<span class="font-display text-3xl font-bold tracking-tight text-cloud">
AspiRêves
</span>
</div>
<p class="font-sans text-cloud/50 text-lg max-w-sm leading-relaxed mb-10">
{companyInfo.description}
</p>
<div class="flex gap-5">
<a href={companyInfo.socials.instagram} target="_blank" rel="noreferrer" class="w-12 h-12 rounded-2xl border-2 border-cloud/10 flex items-center justify-center text-cloud/40 hover:text-dream-pink hover:border-dream-pink transition-all">
<span class="sr-only">Instagram</span>
<Icon name="lucide:instagram" size={20} />
</a>
<a href={companyInfo.socials.facebook} target="_blank" rel="noreferrer" class="w-12 h-12 rounded-2xl border-2 border-cloud/10 flex items-center justify-center text-cloud/40 hover:text-dream-blue hover:border-dream-blue transition-all">
<span class="sr-only">Facebook</span>
<Icon name="lucide:facebook" size={20} />
</a>
</div>
</div>
<!-- Contact -->
<div>
<h4 class="font-sans text-xs uppercase tracking-[0.2em] text-cloud/30 font-bold mb-8">Contact</h4>
<ul class="space-y-8 font-sans">
<li>
<span class="block text-[10px] text-cloud/20 uppercase tracking-widest font-bold mb-2">Professionnels</span>
<a href={`mailto:${companyInfo.emails.pro}`} class="text-cloud/70 hover:text-dream-coral transition-colors flex items-center gap-3 font-bold break-all">
<Icon name="lucide:mail" size={16} class="text-dream-coral" />
{companyInfo.emails.pro}
</a>
</li>
<li>
<span class="block text-[10px] text-cloud/20 uppercase tracking-widest font-bold mb-2">Ateliers Théâtre</span>
<a href={`mailto:${companyInfo.emails.ateliers}`} class="text-cloud/70 hover:text-dream-blue transition-colors flex items-center gap-3 font-bold break-all">
<Icon name="lucide:mail" size={16} class="text-dream-blue" />
{companyInfo.emails.ateliers}
</a>
</li>
</ul>
</div>
<!-- Legal -->
<div>
<h4 class="font-sans text-xs uppercase tracking-[0.2em] text-cloud/30 font-bold mb-8">Informations</h4>
<ul class="space-y-4 font-sans text-cloud/50 text-sm">
<li><a href="#" class="hover:text-cloud transition-colors">Mentions légales</a></li>
<li><a href="#" class="hover:text-cloud transition-colors">Politique de confidentialité</a></li>
<li class="pt-8 text-cloud/20 text-xs font-bold uppercase tracking-widest">
&copy; {new Date().getFullYear()} Compagnie AspiRêves
</li>
</ul>
</div>
</div>
</div>
<div class="absolute bottom-10 left-1/2 -translate-x-1/2 text-cloud/5 animate-pulse">
<StarFilled size={120} />
</div>
</footer>

View file

@ -1,83 +0,0 @@
import { Instagram, Facebook, Youtube, Mail, Star } from 'lucide-react';
import { companyInfo } from '../data';
export default function Footer() {
return (
<footer className="bg-night text-cloud py-20 border-t border-cloud/10 relative overflow-hidden">
{/* Background Magic */}
<div className="absolute top-0 left-0 w-64 h-64 bg-dream-purple/10 rounded-full filter blur-[100px] -translate-y-1/2 -translate-x-1/2"></div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<div className="grid grid-cols-1 md:grid-cols-4 gap-16 md:gap-12">
{/* Brand */}
<div className="col-span-1 md:col-span-2">
<div className="flex items-center gap-4 mb-8">
<div className="w-12 h-12 rounded-2xl bg-cloud flex items-center justify-center overflow-hidden shadow-lg">
<span className="text-night font-display text-2xl">A</span>
</div>
<span className="font-display text-3xl font-bold tracking-tight text-cloud">
AspiRêves
</span>
</div>
<p className="font-sans text-cloud/50 text-lg max-w-sm leading-relaxed mb-10">
{companyInfo.description}
</p>
<div className="flex gap-5">
<a href={companyInfo.socials.instagram} target="_blank" rel="noreferrer" className="w-12 h-12 rounded-2xl border-2 border-cloud/10 flex items-center justify-center text-cloud/40 hover:text-dream-pink hover:border-dream-pink transition-all">
<span className="sr-only">Instagram</span>
<Instagram size={20} />
</a>
<a href={companyInfo.socials.facebook} target="_blank" rel="noreferrer" className="w-12 h-12 rounded-2xl border-2 border-cloud/10 flex items-center justify-center text-cloud/40 hover:text-dream-blue hover:border-dream-blue transition-all">
<span className="sr-only">Facebook</span>
<Facebook size={20} />
</a>
<a href={companyInfo.socials.youtube} target="_blank" rel="noreferrer" className="w-12 h-12 rounded-2xl border-2 border-cloud/10 flex items-center justify-center text-cloud/40 hover:text-dream-coral hover:border-dream-coral transition-all">
<span className="sr-only">YouTube</span>
<Youtube size={20} />
</a>
</div>
</div>
{/* Contact */}
<div>
<h4 className="font-sans text-xs uppercase tracking-[0.2em] text-cloud/30 font-bold mb-8">Contact</h4>
<ul className="space-y-8 font-sans">
<li>
<span className="block text-[10px] text-cloud/20 uppercase tracking-widest font-bold mb-2">Professionnels</span>
<a href={`mailto:${companyInfo.emails.pro}`} className="text-cloud/70 hover:text-dream-coral transition-colors flex items-center gap-3 font-bold break-all">
<Mail size={16} className="text-dream-coral" />
{companyInfo.emails.pro}
</a>
</li>
<li>
<span className="block text-[10px] text-cloud/20 uppercase tracking-widest font-bold mb-2">Ateliers Théâtre</span>
<a href={`mailto:${companyInfo.emails.ateliers}`} className="text-cloud/70 hover:text-dream-blue transition-colors flex items-center gap-3 font-bold break-all">
<Mail size={16} className="text-dream-blue" />
{companyInfo.emails.ateliers}
</a>
</li>
</ul>
</div>
{/* Legal */}
<div>
<h4 className="font-sans text-xs uppercase tracking-[0.2em] text-cloud/30 font-bold mb-8">Informations</h4>
<ul className="space-y-4 font-sans text-cloud/50 text-sm">
<li><a href="#" className="hover:text-cloud transition-colors">Mentions légales</a></li>
<li><a href="#" className="hover:text-cloud transition-colors">Politique de confidentialité</a></li>
<li className="pt-8 text-cloud/20 text-xs font-bold uppercase tracking-widest">
© {new Date().getFullYear()} Compagnie AspiRêves
</li>
</ul>
</div>
</div>
</div>
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 text-cloud/5 animate-pulse">
<Star size={120} fill="currentColor" />
</div>
</footer>
);
}

View file

@ -1,192 +0,0 @@
import { motion } from 'motion/react';
import { Play, Calendar, ArrowRight, Star, Sparkles, Moon, MapPin } from 'lucide-react';
import { companyInfo, spectacles, agenda } from '../data';
interface HomeProps {
onNavigate: (page: string) => void;
}
export default function Home({ onNavigate }: HomeProps) {
// Get the next 2 upcoming shows
const upcomingShows = agenda
.filter(event => new Date(event.date) >= new Date())
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
.slice(0, 2);
return (
<div className="min-h-screen bg-cloud">
{/* Hero Section */}
<section className="relative min-h-screen flex items-center justify-center overflow-hidden pt-24 md:pt-32">
{/* Whimsical Background Blobs */}
<div className="absolute top-1/4 -left-20 w-[500px] h-[500px] bg-dream-purple/30 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-blob"></div>
<div className="absolute top-1/3 -right-20 w-[600px] h-[600px] bg-dream-blue/40 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-blob animation-delay-2000"></div>
<div className="absolute -bottom-32 left-1/3 w-[550px] h-[550px] bg-dream-pink/40 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-blob animation-delay-4000"></div>
{/* Floating Stars/Sparkles */}
<div className="absolute top-20 left-20 animate-float text-star opacity-40"><Star size={40} fill="currentColor" /></div>
<div className="absolute bottom-40 right-20 animate-float animation-delay-2000 text-dream-coral opacity-40"><Sparkles size={32} /></div>
<div className="absolute top-1/2 right-1/4 animate-float animation-delay-4000 text-dream-purple opacity-30"><Moon size={24} fill="currentColor" /></div>
<div className="relative z-10 max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 1, ease: "easeOut" }}
>
<div className="inline-block mb-6 px-4 py-2 rounded-full bg-white/50 backdrop-blur-sm border border-white/20 shadow-sm">
<span className="font-sans text-[10px] sm:text-sm font-semibold text-dream-coral uppercase tracking-widest">Compagnie de Théâtre Jeune Public</span>
</div>
<h1 className="font-display text-4xl sm:text-6xl md:text-8xl lg:text-9xl text-night leading-tight mb-8">
Rêver en <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-dream-purple via-dream-coral to-dream-blue">grand ensemble</span>
</h1>
<p className="font-sans text-lg md:text-2xl text-night/70 max-w-3xl mx-auto leading-relaxed mb-12">
{companyInfo.description}
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-8">
<button
onClick={() => onNavigate('spectacles')}
className="group relative flex items-center gap-3 bg-night text-cloud px-10 py-5 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-xl shadow-night/20"
>
Nos Spectacles
<ArrowRight size={20} className="group-hover:translate-x-2 transition-transform" />
</button>
<a
href={`https://www.youtube.com/watch?v=${companyInfo.teaserVideoId}`}
target="_blank"
rel="noreferrer"
className="group flex items-center gap-4 text-night font-sans font-bold hover:text-dream-coral transition-colors"
>
<div className="w-14 h-14 rounded-full bg-white flex items-center justify-center shadow-lg group-hover:shadow-dream-coral/20 group-hover:scale-110 transition-all">
<Play size={22} className="ml-1 fill-night" />
</div>
Voir le teaser
</a>
</div>
</motion.div>
</div>
{/* Wave Divider */}
<div className="absolute bottom-0 left-0 w-full overflow-hidden leading-none">
<svg className="relative block w-full h-[100px]" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 120" preserveAspectRatio="none">
<path d="M321.39,56.44c58-10.79,114.16-30.13,172-41.86,82.39-16.72,168.19-17.73,250.45-.39C823.78,31,906.67,72,985.66,92.83c70.05,18.48,146.53,26.09,214.34,3V120H0V95.8C57.23,103.19,115.6,115.59,173.45,108a512.21,512.21,0,0,0,147.94-51.56Z" fill="#ffffff"></path>
</svg>
</div>
</section>
{/* Featured Spectacles Section */}
<section className="py-20 md:py-32 bg-white relative">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12 md:mb-20">
<h2 className="font-display text-4xl md:text-6xl text-night mb-6">Nos Créations</h2>
<div className="w-16 md:w-24 h-2 bg-gradient-to-r from-dream-purple to-dream-blue mx-auto rounded-full"></div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-12">
{spectacles.map((spectacle, index) => (
<motion.div
key={spectacle.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.2, duration: 0.6 }}
className="group cursor-pointer"
onClick={() => onNavigate(`spectacle-${spectacle.id}`)}
>
<div className="relative aspect-[4/5] overflow-hidden rounded-[32px] md:rounded-[40px] mb-6 md:mb-8 shadow-2xl shadow-night/5 group-hover:shadow-dream-purple/20 transition-all duration-500">
<img
src={spectacle.image}
alt={spectacle.title}
className="object-cover w-full h-full transform group-hover:scale-110 transition-transform duration-1000 ease-in-out"
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-gradient-to-t from-night/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 flex items-end p-8">
<span className="text-cloud font-sans font-bold uppercase tracking-widest bg-white/20 backdrop-blur-md rounded-full px-6 py-3 border border-white/30">
Découvrir
</span>
</div>
</div>
<h3 className="font-display text-3xl text-night mb-3 group-hover:text-dream-coral transition-colors">{spectacle.title}</h3>
<div className="flex items-center gap-4 font-sans text-sm font-bold text-night/40 uppercase tracking-widest">
<span className="px-3 py-1 bg-dream-blue/20 rounded-lg text-dream-blue-dark">{spectacle.age}</span>
<span className="w-1.5 h-1.5 rounded-full bg-dream-coral"></span>
<span>{spectacle.duration}</span>
</div>
</motion.div>
))}
</div>
</div>
</section>
{/* Quick Agenda Section with a softer look */}
<section className="py-20 md:py-32 bg-cloud relative overflow-hidden">
<div className="absolute top-0 left-0 w-full h-full opacity-30 pointer-events-none">
<div className="absolute top-1/4 left-1/4 w-64 h-64 bg-dream-purple rounded-full filter blur-[100px] animate-pulse"></div>
<div className="absolute bottom-1/4 right-1/4 w-64 h-64 bg-dream-blue rounded-full filter blur-[100px] animate-pulse animation-delay-2000"></div>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<div className="bg-white rounded-[40px] md:rounded-[60px] p-8 md:p-20 shadow-2xl shadow-night/5 border border-white">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 md:gap-20 items-center">
<div className="text-center lg:text-left">
<h2 className="font-display text-4xl md:text-6xl text-night mb-6 md:mb-8 leading-tight">Prochaines <br /><span className="text-dream-coral">Rencontres</span></h2>
<p className="font-sans text-night/60 text-lg mb-8 md:mb-12 max-w-md mx-auto lg:mx-0 leading-relaxed">
Le théâtre est un voyage qui se partage. Retrouvez-nous pour une parenthèse enchantée.
</p>
<button
onClick={() => onNavigate('agenda')}
className="inline-flex items-center gap-4 bg-night text-cloud px-8 py-4 md:px-10 md:py-5 rounded-full font-sans font-bold uppercase tracking-widest text-sm transition-all hover:scale-105 shadow-xl shadow-night/20"
>
Tout l'agenda
</button>
</div>
<div className="space-y-6 md:space-y-8">
{upcomingShows.length > 0 ? (
upcomingShows.map((event, index) => {
const spec = spectacles.find(s => s.id === event.spectacleId);
const dateObj = new Date(event.date);
return (
<motion.div
key={event.id}
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.2 }}
className="group flex flex-col sm:flex-row gap-6 md:gap-8 p-6 md:p-8 rounded-[24px] md:rounded-[32px] bg-cloud/50 hover:bg-white hover:shadow-xl hover:shadow-night/5 transition-all cursor-pointer border border-transparent hover:border-dream-purple/20 items-center sm:items-stretch"
onClick={() => onNavigate('agenda')}
>
<div className="flex-shrink-0 flex flex-col items-center justify-center w-24 h-24 md:w-28 md:h-28 rounded-2xl md:rounded-3xl bg-gradient-to-br from-dream-purple to-dream-blue text-white shadow-lg">
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest opacity-80 mb-1">
{dateObj.toLocaleString('fr-FR', { month: 'short' })}
</span>
<span className="font-display text-4xl md:text-5xl">
{dateObj.getDate()}
</span>
</div>
<div className="flex-grow flex flex-col justify-center text-center sm:text-left">
<h4 className="font-display text-xl md:text-2xl text-night mb-2 group-hover:text-dream-coral transition-colors">{spec?.title}</h4>
<div className="flex items-center justify-center sm:justify-start gap-2 font-sans text-[10px] md:text-sm font-bold text-night/40 uppercase tracking-widest">
<MapPin size={14} className="text-dream-coral" />
<span>{event.location}</span>
</div>
</div>
</motion.div>
);
})
) : (
<div className="p-10 md:p-12 border-4 border-dashed border-dream-blue/20 rounded-[32px] md:rounded-[40px] text-center">
<p className="font-sans text-night/40 font-bold uppercase tracking-widest text-sm">De nouveaux rêves arrivent bientôt...</p>
</div>
)}
</div>
</div>
</div>
</div>
</section>
</div>
);
}

138
src/components/Navbar.astro Normal file
View file

@ -0,0 +1,138 @@
---
import { Icon } from 'astro-icon/components';
import StarFilled from './icons/StarFilled.astro';
import { companyInfo } from '../data';
const currentPath = Astro.url.pathname;
const navLinks = [
{ href: '/', label: 'Accueil' },
{ href: '/spectacles/', label: 'Spectacles' },
{ href: '/agenda/', label: 'Agenda' },
{ href: '/ateliers/', label: 'Ateliers Théâtre' },
{ href: '/contact/', label: 'Contact' },
];
function isActive(href: string): boolean {
if (href === '/') return currentPath === '/';
return currentPath.startsWith(href);
}
---
<nav
id="navbar"
class="fixed top-0 w-full z-50 transition-all duration-500 bg-transparent py-8"
>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center">
<!-- Logo -->
<a href="/" class="flex items-center gap-4 group">
<div class="relative">
<div class="w-12 h-12 rounded-2xl bg-night flex items-center justify-center overflow-hidden transition-all group-hover:rotate-12 group-hover:scale-110 shadow-lg">
<span class="text-cloud font-display text-2xl">A</span>
</div>
<div class="absolute -top-2 -right-2 text-star animate-pulse">
<StarFilled size={16} />
</div>
</div>
<span class="font-display text-2xl font-bold tracking-tight text-night group-hover:text-dream-coral transition-colors">
AspiRêves
</span>
</a>
<!-- Desktop Navigation -->
<div class="hidden md:flex items-center space-x-10">
{navLinks.map((link) => (
<a
href={link.href}
class:list={[
'text-sm font-bold uppercase tracking-widest transition-all relative group',
isActive(link.href) ? 'text-dream-coral' : 'text-night/60 hover:text-night',
]}
>
{link.label}
<span
class:list={[
'absolute -bottom-2 left-1/2 -translate-x-1/2 h-1.5 w-1.5 rounded-full bg-dream-coral transition-all duration-300',
isActive(link.href) ? 'opacity-100 scale-100' : 'opacity-0 scale-0 group-hover:opacity-100 group-hover:scale-100',
]}
/>
</a>
))}
</div>
<!-- Mobile Menu Button -->
<div class="md:hidden flex items-center">
<button
id="mobile-menu-btn"
class="text-night p-2 focus:outline-none"
aria-label="Menu"
>
<Icon name="lucide:menu" size={24} id="icon-menu" />
<Icon name="lucide:x" size={24} id="icon-close" class="hidden" />
</button>
</div>
</div>
</div>
<!-- Mobile Menu -->
<div
id="mobile-menu"
class="md:hidden absolute top-full left-0 w-full bg-white/95 backdrop-blur-xl shadow-2xl border-t border-night/5 hidden animate-slide-down"
>
<div class="px-6 py-8 space-y-2">
{navLinks.map((link) => (
<a
href={link.href}
class:list={[
'block w-full text-left px-4 py-4 text-lg font-display tracking-wide rounded-2xl transition-all',
isActive(link.href) ? 'text-dream-coral bg-dream-coral/5 font-bold' : 'text-night/70 hover:bg-night/5',
]}
>
{link.label}
</a>
))}
<div class="flex gap-6 px-4 pt-8 border-t border-night/5">
<a href={companyInfo.socials.instagram} target="_blank" rel="noreferrer" class="text-night/40 hover:text-dream-pink transition-colors">
<Icon name="lucide:instagram" size={24} />
</a>
<a href={companyInfo.socials.facebook} target="_blank" rel="noreferrer" class="text-night/40 hover:text-dream-blue transition-colors">
<Icon name="lucide:facebook" size={24} />
</a>
</div>
</div>
</div>
</nav>
<script>
const navbar = document.getElementById('navbar')!;
const menuBtn = document.getElementById('mobile-menu-btn')!;
const mobileMenu = document.getElementById('mobile-menu')!;
const iconMenu = document.getElementById('icon-menu')!;
const iconClose = document.getElementById('icon-close')!;
// Scroll detection
window.addEventListener('scroll', () => {
if (window.scrollY > 20) {
navbar.classList.remove('bg-transparent', 'py-8');
navbar.classList.add('bg-white/80', 'backdrop-blur-lg', 'shadow-xl', 'shadow-night/5', 'py-4');
} else {
navbar.classList.remove('bg-white/80', 'backdrop-blur-lg', 'shadow-xl', 'shadow-night/5', 'py-4');
navbar.classList.add('bg-transparent', 'py-8');
}
});
// Mobile menu toggle
menuBtn.addEventListener('click', () => {
const isOpen = !mobileMenu.classList.contains('hidden');
if (isOpen) {
mobileMenu.classList.add('hidden');
iconMenu.classList.remove('hidden');
iconClose.classList.add('hidden');
} else {
mobileMenu.classList.remove('hidden');
iconMenu.classList.add('hidden');
iconClose.classList.remove('hidden');
}
});
</script>

View file

@ -1,125 +0,0 @@
import { useState, useEffect } from 'react';
import { Menu, X, Instagram, Facebook, Youtube, Star } from 'lucide-react';
import { companyInfo } from '../data';
interface NavbarProps {
currentPage: string;
onNavigate: (page: string) => void;
}
export default function Navbar({ currentPage, onNavigate }: NavbarProps) {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const navLinks = [
{ id: 'home', label: 'Accueil' },
{ id: 'spectacles', label: 'Spectacles' },
{ id: 'agenda', label: 'Agenda' },
{ id: 'ateliers', label: 'Ateliers Théâtre' },
{ id: 'contact', label: 'Contact' },
];
const handleNavClick = (id: string) => {
onNavigate(id);
setIsMobileMenuOpen(false);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return (
<nav
className={`fixed top-0 w-full z-50 transition-all duration-500 ${
isScrolled ? 'bg-white/80 backdrop-blur-lg shadow-xl shadow-night/5 py-4' : 'bg-transparent py-8'
}`}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center">
{/* Logo Area */}
<div
className="flex items-center gap-4 cursor-pointer group"
onClick={() => handleNavClick('home')}
>
<div className="relative">
<div className="w-12 h-12 rounded-2xl bg-night flex items-center justify-center overflow-hidden transition-all group-hover:rotate-12 group-hover:scale-110 shadow-lg">
<span className="text-cloud font-display text-2xl">A</span>
</div>
<div className="absolute -top-2 -right-2 text-star animate-pulse">
<Star size={16} fill="currentColor" />
</div>
</div>
<span className="font-display text-2xl font-bold tracking-tight text-night group-hover:text-dream-coral transition-colors">
AspiRêves
</span>
</div>
{/* Desktop Navigation */}
<div className="hidden md:flex items-center space-x-10">
{navLinks.map((link) => (
<button
key={link.id}
onClick={() => handleNavClick(link.id)}
className={`text-sm font-bold uppercase tracking-widest transition-all relative group ${
currentPage === link.id ? 'text-dream-coral' : 'text-night/60 hover:text-night'
}`}
>
{link.label}
<span
className={`absolute -bottom-2 left-1/2 -translate-x-1/2 h-1.5 w-1.5 rounded-full bg-dream-coral transition-all duration-300 ${
currentPage === link.id ? 'opacity-100 scale-100' : 'opacity-0 scale-0 group-hover:opacity-100 group-hover:scale-100'
}`}
/>
</button>
))}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center">
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="text-night p-2 focus:outline-none"
>
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
</div>
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<div className="md:hidden absolute top-full left-0 w-full bg-white/95 backdrop-blur-xl shadow-2xl border-t border-night/5 animate-in fade-in slide-in-from-top-4 duration-300">
<div className="px-6 py-8 space-y-2">
{navLinks.map((link) => (
<button
key={link.id}
onClick={() => handleNavClick(link.id)}
className={`block w-full text-left px-4 py-4 text-lg font-display tracking-wide rounded-2xl transition-all ${
currentPage === link.id ? 'text-dream-coral bg-dream-coral/5 font-bold' : 'text-night/70 hover:bg-night/5'
}`}
>
{link.label}
</button>
))}
<div className="flex gap-6 px-4 pt-8 border-t border-night/5">
<a href={companyInfo.socials.instagram} target="_blank" rel="noreferrer" className="text-night/40 hover:text-dream-pink transition-colors">
<Instagram size={24} />
</a>
<a href={companyInfo.socials.facebook} target="_blank" rel="noreferrer" className="text-night/40 hover:text-dream-blue transition-colors">
<Facebook size={24} />
</a>
<a href={companyInfo.socials.youtube} target="_blank" rel="noreferrer" className="text-night/40 hover:text-dream-coral transition-colors">
<Youtube size={24} />
</a>
</div>
</div>
</div>
)}
</nav>
);
}

View file

@ -1,183 +0,0 @@
import { motion } from 'motion/react';
import { ArrowLeft, Clock, Users, Download, MapPin, Calendar } from 'lucide-react';
import { spectacles, agenda } from '../data';
interface SpectacleDetailProps {
spectacleId: string;
onNavigate: (page: string) => void;
}
export default function SpectacleDetail({ spectacleId, onNavigate }: SpectacleDetailProps) {
const spectacle = spectacles.find(s => s.id === spectacleId);
if (!spectacle) {
return (
<div className="pt-32 pb-24 min-h-screen bg-cloud text-center">
<p className="font-sans text-night/60 text-xl">Spectacle introuvable.</p>
<button onClick={() => onNavigate('spectacles')} className="mt-8 text-dream-purple font-sans font-bold">
Retour aux spectacles
</button>
</div>
);
}
const upcomingDates = agenda
.filter(event => event.spectacleId === spectacleId)
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
return (
<div className="pt-24 md:pt-32 pb-24 min-h-screen bg-cloud overflow-hidden relative">
{/* Background */}
<div className="absolute top-0 right-0 w-96 h-96 bg-dream-purple/20 rounded-full filter blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
<div className="absolute bottom-0 left-0 w-96 h-96 bg-dream-blue/20 rounded-full filter blur-[100px] translate-y-1/2 -translate-x-1/2"></div>
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
{/* Back button */}
<motion.button
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
onClick={() => onNavigate('spectacles')}
className="group flex items-center gap-3 mb-10 font-sans font-bold text-night/60 hover:text-dream-purple transition-colors"
>
<ArrowLeft size={20} className="group-hover:-translate-x-1 transition-transform" />
Tous les spectacles
</motion.button>
{/* Hero */}
<div className="flex flex-col md:flex-row gap-10 md:gap-16 items-center mb-16 md:mb-24">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6 }}
className="w-full md:w-1/2"
>
<div className="relative aspect-[4/3] overflow-hidden rounded-[32px] md:rounded-[60px] shadow-2xl shadow-night/10 border-4 md:border-8 border-white">
<img
src={spectacle.image}
alt={spectacle.title}
className="object-cover w-full h-full"
referrerPolicy="no-referrer"
/>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="w-full md:w-1/2 space-y-6 text-center md:text-left"
>
<h1 className="font-display text-4xl sm:text-6xl md:text-7xl text-night">{spectacle.title}</h1>
<div className="flex flex-wrap justify-center md:justify-start gap-3">
<div className="flex items-center gap-2 bg-dream-blue/20 px-5 py-2 rounded-2xl text-sm font-sans font-bold text-dream-blue-dark">
<Users size={16} className="text-dream-blue" />
<span>{spectacle.age}</span>
</div>
<div className="flex items-center gap-2 bg-dream-pink/20 px-5 py-2 rounded-2xl text-sm font-sans font-bold text-dream-pink-dark">
<Clock size={16} className="text-dream-pink" />
<span>{spectacle.duration}</span>
</div>
</div>
<p className="font-sans text-lg md:text-xl text-night/70 leading-relaxed">
{spectacle.summary}
</p>
{spectacle.dossierPro && spectacle.dossierPro !== '#' && (
<a
href={spectacle.dossierPro}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-3 bg-night text-cloud px-8 py-4 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-lg shadow-night/20"
>
<Download size={18} />
Dossier Pédagogique
</a>
)}
</motion.div>
</div>
{/* Gallery */}
{spectacle.gallery.length > 0 && (
<motion.section
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="mb-16 md:mb-24"
>
<h2 className="font-display text-3xl md:text-5xl text-night mb-8 md:mb-12 text-center">Galerie</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6 md:gap-8">
{spectacle.gallery.map((img, index) => (
<div
key={index}
className="aspect-[4/3] overflow-hidden rounded-[24px] md:rounded-[40px] shadow-xl shadow-night/5 border-4 border-white"
>
<img
src={img}
alt={`${spectacle.title} - photo ${index + 1}`}
className="object-cover w-full h-full"
referrerPolicy="no-referrer"
/>
</div>
))}
</div>
</motion.section>
)}
{/* Upcoming dates */}
{upcomingDates.length > 0 && (
<motion.section
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
<h2 className="font-display text-3xl md:text-5xl text-night mb-8 md:mb-12 text-center">Prochaines dates</h2>
<div className="space-y-4 max-w-2xl mx-auto">
{upcomingDates.map(event => {
const dateObj = new Date(event.date);
return (
<div
key={event.id}
className="flex flex-col sm:flex-row gap-6 p-6 md:p-8 rounded-[24px] bg-white shadow-lg shadow-night/5 border border-dream-purple/10 items-center"
>
<div className="flex-shrink-0 flex flex-col items-center justify-center w-20 h-20 md:w-24 md:h-24 rounded-2xl bg-gradient-to-br from-dream-purple to-dream-blue text-white shadow-md">
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest opacity-80">
{dateObj.toLocaleString('fr-FR', { month: 'short' })}
</span>
<span className="font-display text-3xl md:text-4xl">
{dateObj.getDate()}
</span>
</div>
<div className="flex-grow text-center sm:text-left">
<div className="flex items-center justify-center sm:justify-start gap-2 font-sans text-sm text-night/50 mb-1">
<Calendar size={14} />
<span>{dateObj.toLocaleString('fr-FR', { weekday: 'long', hour: '2-digit', minute: '2-digit' })}</span>
</div>
<div className="flex items-center justify-center sm:justify-start gap-2 font-sans font-bold text-night">
<MapPin size={14} className="text-dream-coral" />
<span>{event.location}</span>
</div>
</div>
{event.bookingLink && (
<a
href={event.bookingLink}
target="_blank"
rel="noreferrer"
className="bg-dream-coral text-white px-6 py-3 rounded-full font-sans font-bold text-sm hover:scale-105 transition-transform shadow-md"
>
Réserver
</a>
)}
</div>
);
})}
</div>
</motion.section>
)}
</div>
</div>
);
}

View file

@ -1,102 +0,0 @@
import { motion } from 'motion/react';
import { spectacles } from '../data';
import { Clock, Users, Star, Sparkles } from 'lucide-react';
interface SpectaclesProps {
onNavigate: (page: string) => void;
}
export default function Spectacles({ onNavigate }: SpectaclesProps) {
return (
<div className="pt-32 pb-24 min-h-screen bg-cloud overflow-hidden relative">
{/* Background Magic */}
<div className="absolute top-0 right-0 w-96 h-96 bg-dream-purple/20 rounded-full filter blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
<div className="absolute bottom-0 left-0 w-96 h-96 bg-dream-blue/20 rounded-full filter blur-[100px] translate-y-1/2 -translate-x-1/2"></div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center mb-16 md:mb-24"
>
<div className="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<Sparkles size={16} />
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Le Répertoire</span>
</div>
<h1 className="font-display text-4xl sm:text-6xl md:text-8xl text-night mb-8">Nos <span className="text-dream-purple italic">Spectacles</span></h1>
<p className="font-sans text-night/60 max-w-2xl mx-auto text-lg md:text-xl leading-relaxed">
Des bulles de poésie et d'humour pour s'évader, <br className="hidden sm:block" />apprendre et grandir ensemble.
</p>
</motion.div>
<div className="space-y-24 md:space-y-40">
{spectacles.map((spectacle, index) => (
<motion.div
key={spectacle.id}
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className={`flex flex-col ${index % 2 === 1 ? 'md:flex-row-reverse' : 'md:flex-row'} gap-10 md:gap-16 items-center`}
>
{/* Image Side - Dream Bubble Look */}
<div className="w-full md:w-1/2 relative cursor-pointer" onClick={() => onNavigate(`spectacle-${spectacle.id}`)}>
<div className="absolute -top-4 -left-4 md:-top-6 md:-left-6 w-16 h-16 md:w-24 md:h-24 bg-star/20 rounded-full filter blur-xl md:blur-2xl animate-pulse"></div>
<div className="absolute -bottom-4 -right-4 md:-bottom-6 md:-right-6 w-20 h-20 md:w-32 md:h-32 bg-dream-blue/30 rounded-full filter blur-xl md:blur-2xl animate-pulse animation-delay-2000"></div>
<div className="relative aspect-[4/3] overflow-hidden rounded-[32px] md:rounded-[60px] shadow-2xl shadow-night/10 group border-4 md:border-8 border-white">
<img
src={spectacle.image}
alt={spectacle.title}
className="object-cover w-full h-full transition-transform duration-1000 group-hover:scale-110"
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-gradient-to-tr from-dream-purple/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
</div>
</div>
{/* Content Side */}
<div className="w-full md:w-1/2 space-y-6 md:space-y-8 text-center md:text-left">
<div className="flex items-center justify-center md:justify-start gap-3">
<div className="h-px w-8 md:w-12 bg-dream-coral"></div>
<span className="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest text-dream-coral">Création Originale</span>
</div>
<h2 className="font-display text-4xl md:text-6xl text-night group-hover:text-dream-purple transition-colors">{spectacle.title}</h2>
<div className="flex flex-wrap justify-center md:justify-start gap-3 md:gap-4">
<div className="flex items-center gap-2 bg-dream-blue/20 px-4 py-1.5 md:px-5 md:py-2 rounded-xl md:rounded-2xl text-xs md:text-sm font-sans font-bold text-dream-blue-dark">
<Users size={16} className="text-dream-blue" />
<span>{spectacle.age}</span>
</div>
<div className="flex items-center gap-2 bg-dream-pink/20 px-4 py-1.5 md:px-5 md:py-2 rounded-xl md:rounded-2xl text-xs md:text-sm font-sans font-bold text-dream-pink-dark">
<Clock size={16} className="text-dream-pink" />
<span>{spectacle.duration}</span>
</div>
</div>
<p className="font-sans text-lg md:text-xl text-night/70 leading-relaxed">
{spectacle.summary}
</p>
<div className="pt-4 md:pt-8 flex flex-wrap justify-center md:justify-start gap-4 md:gap-6">
<button
onClick={() => onNavigate(`spectacle-${spectacle.id}`)}
className="w-full sm:w-auto bg-night text-cloud px-8 py-4 md:px-10 md:py-4 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-lg shadow-night/20"
>
Découvrir
</button>
</div>
</div>
</motion.div>
))}
</div>
</div>
{/* Decorative Floating Elements */}
<div className="absolute top-1/4 left-10 text-dream-pink opacity-20 animate-float"><Star size={60} fill="currentColor" /></div>
<div className="absolute bottom-1/4 right-10 text-dream-blue opacity-20 animate-float animation-delay-2000"><Star size={40} fill="currentColor" /></div>
</div>
);
}

View file

@ -0,0 +1,198 @@
---
// Composant temporaire pour prévisualiser les palettes de couleurs.
// À supprimer une fois le thème choisi.
---
<div id="theme-switcher">
<button id="theme-toggle" aria-label="Changer de thème">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"/>
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
</svg>
</button>
<div id="theme-panel" class="hidden">
<span class="panel-title">Palette</span>
<button data-set-theme="" class="theme-btn active" title="Rêverie (actuel)">
<span class="swatch" style="background: linear-gradient(135deg, #E0C3FC, #FF9F89)"></span>
<span class="label">Rêverie</span>
<span class="desc">Pastels actuels</span>
</button>
<button data-set-theme="crepuscule" class="theme-btn" title="Crépuscule & Cuivre">
<span class="swatch" style="background: linear-gradient(135deg, #1A1A2E, #C9956B)"></span>
<span class="label">Crépuscule</span>
<span class="desc">Cuivre & nuit</span>
</button>
<button data-set-theme="foret" class="theme-btn" title="Forêt enchantée">
<span class="swatch" style="background: linear-gradient(135deg, #3D5A47, #D4A843)"></span>
<span class="label">Forêt</span>
<span class="desc">Mousse & terre</span>
</button>
<button data-set-theme="nuit" class="theme-btn" title="Nuit étoilée">
<span class="swatch" style="background: linear-gradient(135deg, #1E1E3F, #C8A961)"></span>
<span class="label">Nuit</span>
<span class="desc">Sombre & or</span>
</button>
<button data-set-theme="aube" class="theme-btn" title="Aube rosée">
<span class="swatch" style="background: linear-gradient(135deg, #6B3A5A, #C0785A)"></span>
<span class="label">Aube</span>
<span class="desc">Prune & terracotta</span>
</button>
</div>
</div>
<style>
#theme-switcher {
position: fixed;
bottom: 1.5rem;
right: 1.5rem;
z-index: 9999;
font-family: system-ui, sans-serif;
}
#theme-toggle {
width: 3rem;
height: 3rem;
border-radius: 50%;
background: rgba(0, 0, 0, 0.85);
color: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
transition: transform 0.2s;
}
#theme-toggle:hover {
transform: scale(1.1);
}
#theme-panel {
position: absolute;
bottom: calc(100% + 0.75rem);
right: 0;
background: rgba(255, 255, 255, 0.97);
backdrop-filter: blur(20px);
border-radius: 1rem;
padding: 1rem;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05);
min-width: 220px;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.panel-title {
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.15em;
color: rgba(0, 0, 0, 0.3);
padding: 0 0.5rem 0.5rem;
}
.theme-btn {
display: grid;
grid-template-columns: 1.75rem 1fr;
grid-template-rows: auto auto;
column-gap: 0.625rem;
row-gap: 0;
align-items: center;
padding: 0.5rem;
border-radius: 0.625rem;
border: 2px solid transparent;
background: transparent;
cursor: pointer;
transition: all 0.15s;
text-align: left;
}
.theme-btn:hover {
background: rgba(0, 0, 0, 0.04);
}
.theme-btn.active {
background: rgba(0, 0, 0, 0.06);
border-color: rgba(0, 0, 0, 0.15);
}
.swatch {
grid-row: 1 / 3;
width: 1.75rem;
height: 1.75rem;
border-radius: 0.5rem;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
}
.label {
font-size: 0.8rem;
font-weight: 600;
color: #1a1a1a;
line-height: 1.2;
}
.desc {
font-size: 0.65rem;
color: rgba(0, 0, 0, 0.4);
line-height: 1.2;
}
.hidden {
display: none !important;
}
</style>
<script>
const toggle = document.getElementById('theme-toggle')!;
const panel = document.getElementById('theme-panel')!;
const buttons = panel.querySelectorAll<HTMLButtonElement>('[data-set-theme]');
// Appliquer le thème sauvegardé
const saved = localStorage.getItem('aspireves-theme');
if (saved) {
document.documentElement.setAttribute('data-theme', saved);
}
function updateActive() {
const current = document.documentElement.getAttribute('data-theme') || '';
buttons.forEach((btn) => {
btn.classList.toggle('active', btn.dataset.setTheme === current);
});
}
updateActive();
// Toggle du panel
toggle.addEventListener('click', () => {
panel.classList.toggle('hidden');
});
// Fermer si clic en dehors
document.addEventListener('click', (e) => {
const switcher = document.getElementById('theme-switcher')!;
if (!switcher.contains(e.target as Node)) {
panel.classList.add('hidden');
}
});
// Changement de thème
buttons.forEach((btn) => {
btn.addEventListener('click', () => {
const theme = btn.dataset.setTheme!;
if (theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('aspireves-theme', theme);
} else {
document.documentElement.removeAttribute('data-theme');
localStorage.removeItem('aspireves-theme');
}
updateActive();
});
});
</script>

View file

@ -0,0 +1,19 @@
---
interface Props {
size?: number;
class?: string;
}
const { size = 24, class: className = '' } = Astro.props;
---
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="currentColor"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class={className}
><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.12 2.12 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.12 2.12 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.12 2.12 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.12 2.12 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.12 2.12 0 0 0 1.597-1.16z"/></svg>

View file

@ -1,26 +1,32 @@
export const companyInfo = { export const companyInfo = {
name: "Compagnie AspiRêves", name: "Compagnie AspiRêves",
description: "La Compagnie AspiRêves vous embarque sur son petit nuage. Un petit nuage tout doux, poétique, drôle et burlesque. Mais attention, ça tangue ! C'est du rêve qui secoue, qui trébuche, des songes d'adultes, des jeux d'enfants... c'est du spectacle vivant.", description: "La Compagnie AspiRêves vous embarque sur son petit nuage. Un petit nuage tout doux, poétique, drôle et burlesque. Mais attention, ça tangue ! C'est du rêve qui secoue, qui trébuche, des songes d'adultes, des jeux d'enfants... c'est du spectacle vivant.",
founder: "Éléonore Astruc",
founderRole: "Autrice, comédienne & metteuse en scène",
address: "14 rue Gisclard, 81160 Saint-Juéry",
phone: "06 84 26 36 92",
emails: { emails: {
pro: "aspireves@gmail.com", pro: "aspireves@gmail.com",
ateliers: "compagnieaspireves@gmail.com" ateliers: "compagnieaspireves@gmail.com"
}, },
socials: { socials: {
instagram: "https://www.instagram.com/compagnieaspireves", instagram: "https://www.instagram.com/compagnieaspireves",
facebook: "https://www.facebook.com/compagnieaspireves", facebook: "https://www.facebook.com/compagnieaspireves"
youtube: "https://www.youtube.com/@aspireves"
}, },
teaserVideoId: "fImFdHPvXa8" // Extrait de https://www.youtube.com/watch?v=fImFdHPvXa8 teaserVideoId: "fImFdHPvXa8"
}; };
export const spectacles = [ export const spectacles = [
{ {
id: "bassibulle", id: "bassibulle",
title: "BASSIBULLE", title: "BASSIBULLE",
age: "De 6 mois à 5 ans", category: "jeune-public" as const,
retired: false,
age: "Dès 6 mois",
duration: "30 min", duration: "30 min",
summary: "Un voyage sensoriel et poétique où les bulles de savon racontent des histoires. Une expérience immersive pour les tout-petits.", summary: "Une bassine, un personnage... Que va-t-il se passer entre ces deux-là ? Au fil des découvertes et des explorations, c'est une véritable aventure pleine d'émotions qui se construit sous les yeux des enfants. La bassine, tantôt docile, tantôt rebelle, n'en finit pas d'embêter ce personnage bien décidé à créer le lien avec cet objet.",
image: "https://picsum.photos/seed/bassibulle/800/600?blur=2", credits: "Création et interprétation : Éléonore Astruc",
image: "https://picsum.photos/seed/bassibulle/800/600",
gallery: [ gallery: [
"https://picsum.photos/seed/bassi1/400/300", "https://picsum.photos/seed/bassi1/400/300",
"https://picsum.photos/seed/bassi2/400/300" "https://picsum.photos/seed/bassi2/400/300"
@ -30,9 +36,12 @@ export const spectacles = [
{ {
id: "minus", id: "minus",
title: "MiNUS", title: "MiNUS",
age: "À partir de 3 ans", category: "jeune-public" as const,
retired: false,
age: "À partir de 4 ans",
duration: "45 min", duration: "45 min",
summary: "L'histoire d'un petit être qui découvre le monde. Un spectacle clownesque et tendre sur la différence et l'acceptation.", summary: "Minus est un garçon pas comme les autres. Dans son village, dans sa famille, tout le monde l'appelle MiNUS, si bien qu'il finit par croire que c'est son vrai nom. Rien n'est à sa taille, il ne voit jamais rien, n'entend jamais rien... Mais un jour, grâce à de nouveaux arrivants, sa vie bascule dans une aventure héroïque et poétique.",
credits: "Création et interprétation : Éléonore Astruc",
image: "https://picsum.photos/seed/minus/800/600", image: "https://picsum.photos/seed/minus/800/600",
gallery: [ gallery: [
"https://picsum.photos/seed/minus1/400/300", "https://picsum.photos/seed/minus1/400/300",
@ -43,13 +52,68 @@ export const spectacles = [
{ {
id: "cadeau-surprise", id: "cadeau-surprise",
title: "Le Cadeau Surprise !", title: "Le Cadeau Surprise !",
age: "À partir de 4 ans", category: "jeune-public" as const,
duration: "50 min", retired: false,
summary: "Que se cache-t-il dans cette boîte géante ? Une aventure rocambolesque pleine de rebondissements et de magie.", age: "De 6 mois à 6 ans",
image: "https://picsum.photos/seed/cadeau/800/600", duration: "30 min",
summary: "Un spectacle de théâtre visuel et sensoriel ponctué de chansons, pensé pour émerveiller les tout-petits.",
credits: "Création et interprétation : Éléonore Astruc",
image: "https://picsum.photos/seed/cadeau-surprise/800/600",
gallery: [],
dossierPro: "#"
},
{
id: "la-piece",
title: "La Pièce",
category: "tout-public" as const,
retired: false,
age: "Tout public",
duration: "",
summary: "Un drame sur la résistance, écrit par Michel Cals.",
credits: "Texte : Michel Cals — Avec : Jalil Arfaoui, Éléonore Astruc, Cathy Giovannini, Arthur Placet, Jean-Louis Reynes",
image: "https://picsum.photos/seed/la-piece/800/600",
gallery: [],
dossierPro: "#"
},
{
id: "fulgurante-envie",
title: "Une fulgurante envie de s'asseoir",
category: "tout-public" as const,
retired: false,
age: "Tout public",
duration: "",
summary: "Un duo burlesque et poétique.",
credits: "Écriture et interprétation : Éléonore Astruc & Cathy Giovannini",
image: "https://picsum.photos/seed/fulgurante/800/600",
gallery: [],
dossierPro: "#"
},
{
id: "au-pied-de-mon-arbre",
title: "Au pied de mon arbre",
category: "tout-public" as const,
retired: true,
age: "Tout public",
duration: "",
summary: "",
credits: "",
image: "https://picsum.photos/seed/arbre/800/600",
gallery: [],
dossierPro: "#"
},
{
id: "planete-des-songes",
title: "Planète des Songes",
category: "tout-public" as const,
retired: true,
age: "Tout public",
duration: "Duo burlesque gestuel",
summary: "Les aventures sans parole d'Éléonore et Fred nous transportent de la jungle des employés de bureau à la moiteur d'une chambre à coucher. Un univers poétique et absurde qui nous emmène avec un grain de folie sur la Planète des Songes.",
credits: "Écriture et interprétation : Éléonore Astruc & Frédéric Pillet",
image: "https://picsum.photos/seed/planete-songes/800/600",
gallery: [ gallery: [
"https://picsum.photos/seed/cadeau1/400/300", "https://picsum.photos/seed/songes1/400/300",
"https://picsum.photos/seed/cadeau2/400/300" "https://picsum.photos/seed/songes2/400/300"
], ],
dossierPro: "#" dossierPro: "#"
} }
@ -58,23 +122,9 @@ export const spectacles = [
export const agenda = [ export const agenda = [
{ {
id: "1", id: "1",
date: "2024-05-15T10:30:00", date: "2026-03-18T15:00:00",
location: "Théâtre de la Plume, Montpellier", location: "La Manufacture, Villefranche-de-Rouergue",
spectacleId: "bassibulle",
bookingLink: "https://example.com/book"
},
{
id: "2",
date: "2024-06-02T15:00:00",
location: "Médiathèque Émile Zola, Montpellier",
spectacleId: "minus", spectacleId: "minus",
bookingLink: null // Entrée libre bookingLink: null // Entrée libre
},
{
id: "3",
date: "2024-07-10T16:00:00",
location: "Festival d'Avignon Off",
spectacleId: "cadeau-surprise",
bookingLink: "https://example.com/book-avignon"
} }
]; ];

1
src/env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference path="../.astro/types.d.ts" />

View file

@ -1,69 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Outfit:wght@300;400;500;600&family=Fredoka:wght@300..700&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Outfit", ui-sans-serif, system-ui, sans-serif;
--font-serif: "Cormorant Garamond", ui-serif, Georgia, serif;
--font-display: "Fredoka", sans-serif;
--color-cloud: #F8F7FF;
--color-night: #2D2D3A;
--color-star: #FFD700;
--color-dream-pink: #FFD6E0;
--color-dream-blue: #C1D3FE;
--color-dream-purple: #E0C3FC;
--color-dream-coral: #FF9F89;
}
body {
background-color: var(--color-cloud);
color: var(--color-night);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
@keyframes float {
0% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-20px) rotate(2deg); }
100% { transform: translateY(0px) rotate(0deg); }
}
.animate-float {
animation: float 6s ease-in-out infinite;
}
@keyframes blob {
0% { transform: scale(1); }
33% { transform: scale(1.1) translate(10px, -10px); }
66% { transform: scale(0.9) translate(-10px, 10px); }
100% { transform: scale(1); }
}
.animate-blob {
animation: blob 10s infinite alternate;
}
.animation-delay-2000 {
animation-delay: 2s;
}
.animation-delay-4000 {
animation-delay: 4s;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: var(--color-cloud);
}
::-webkit-scrollbar-thumb {
background: var(--color-dream-purple);
border-radius: 10px;
border: 3px solid var(--color-cloud);
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-dream-coral);
}

47
src/layouts/Layout.astro Normal file
View file

@ -0,0 +1,47 @@
---
import Navbar from '../components/Navbar.astro';
import Footer from '../components/Footer.astro';
import ThemeSwitcher from '../components/ThemeSwitcher.astro';
import '../styles/global.css';
interface Props {
title?: string;
}
const { title = 'Compagnie AspiRêves' } = Astro.props;
---
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
</head>
<body class="bg-cloud min-h-screen flex flex-col">
<Navbar />
<main class="flex-grow">
<slot />
</main>
<Footer />
<ThemeSwitcher />
<script>
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1, rootMargin: '-50px' }
);
document.querySelectorAll('.fade-up, .fade-right, .fade-scale').forEach((el) => {
observer.observe(el);
});
</script>
</body>
</html>

View file

@ -1,10 +0,0 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

104
src/pages/agenda.astro Normal file
View file

@ -0,0 +1,104 @@
---
import Layout from '../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import { agenda, spectacles } from '../data';
const sortedAgenda = [...agenda].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
const now = new Date();
---
<Layout title="Agenda — Compagnie AspiRêves">
<div class="pt-32 pb-24 min-h-screen bg-cloud relative overflow-hidden">
<!-- Background -->
<div class="absolute top-1/2 left-0 w-96 h-96 bg-dream-purple/10 rounded-full filter blur-[120px] -translate-x-1/2"></div>
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<!-- Header -->
<div class="animate-entrance text-center mb-12 md:mb-20">
<div class="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<Icon name="lucide:calendar" size={16} />
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Calendrier</span>
</div>
<h1 class="font-display text-4xl sm:text-6xl md:text-8xl text-night mb-8">L'<span class="text-dream-coral italic">Agenda</span></h1>
<p class="font-sans text-night/60 max-w-2xl mx-auto text-lg md:text-xl leading-relaxed">
Venez nous voir sur scène ! <br class="hidden sm:block" />Chaque date est une nouvelle aventure.
</p>
</div>
<div class="space-y-6 md:space-y-10">
{sortedAgenda.map((event, index) => {
const spectacle = spectacles.find(s => s.id === event.spectacleId);
const dateObj = new Date(event.date);
const isPast = dateObj < now;
return (
<div
class:list={[
'fade-up group relative flex flex-col md:flex-row gap-6 md:gap-10 p-6 md:p-10 rounded-[32px] md:rounded-[40px] border transition-all',
isPast
? 'opacity-40 grayscale bg-night/5 border-transparent'
: 'bg-white border-white shadow-xl shadow-night/5 hover:shadow-dream-purple/10 hover:scale-[1.02]',
]}
style={`transition-delay: ${index * 0.1}s`}
>
<!-- Date Block -->
<div class="flex-shrink-0 flex flex-col items-center justify-center w-24 h-24 md:w-36 md:h-36 rounded-[24px] md:rounded-[32px] bg-gradient-to-br from-dream-purple to-dream-blue text-white shadow-lg mx-auto md:mx-0">
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest opacity-80 mb-1">
{dateObj.toLocaleString('fr-FR', { month: 'short' })}
</span>
<span class="font-display text-4xl md:text-6xl">
{dateObj.getDate()}
</span>
<span class="font-sans text-[10px] md:text-xs font-bold opacity-60 mt-1">
{dateObj.getFullYear()}
</span>
</div>
<!-- Info Block -->
<div class="flex-grow flex flex-col justify-center space-y-3 md:space-y-4 text-center md:text-left">
<div class="flex items-center justify-center md:justify-start gap-3">
<Icon name="lucide:sparkles" size={14} class="text-dream-coral" />
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest text-dream-coral">
{spectacle?.title}
</span>
{isPast && <span class="text-[10px] uppercase tracking-tighter bg-night/10 px-2 py-0.5 rounded text-night/50 font-bold">Passé</span>}
</div>
<h3 class="font-display text-2xl md:text-4xl text-night">{spectacle?.title}</h3>
<div class="flex items-center justify-center md:justify-start gap-3 text-night/50 font-sans font-bold text-[10px] md:text-sm uppercase tracking-widest">
<Icon name="lucide:map-pin" size={16} class="text-dream-blue" />
<span>{event.location}</span>
</div>
</div>
<!-- Action Block -->
<div class="flex-shrink-0 flex items-center">
{event.bookingLink ? (
<a
href={event.bookingLink}
target="_blank"
rel="noreferrer"
class="w-full md:w-auto flex items-center justify-center gap-3 bg-night text-cloud px-8 py-4 rounded-full font-sans font-bold text-sm uppercase tracking-widest hover:bg-night/80 transition-all hover:scale-105 shadow-lg shadow-night/20"
>
<Icon name="lucide:ticket" size={20} />
Réserver
</a>
) : (
<div class="w-full md:w-auto flex items-center justify-center gap-2 border-2 border-dashed border-night/10 text-night/30 px-8 py-4 rounded-full font-sans text-sm font-bold uppercase tracking-widest italic">
<Icon name="lucide:info" size={18} />
Sur place
</div>
)}
</div>
</div>
);
})}
</div>
{sortedAgenda.length === 0 && (
<div class="text-center py-32 bg-white/50 rounded-[60px] border-4 border-dashed border-dream-blue/20">
<p class="font-display text-3xl text-night/30 italic">Le nuage se prépare pour de nouveaux voyages...</p>
</div>
)}
</div>
</div>
</Layout>

118
src/pages/ateliers.astro Normal file
View file

@ -0,0 +1,118 @@
---
import Layout from '../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import StarFilled from '../components/icons/StarFilled.astro';
import { companyInfo } from '../data';
---
<Layout title="Ateliers Théâtre — Compagnie AspiRêves">
<div class="pt-32 pb-24 min-h-screen bg-cloud relative overflow-hidden">
<!-- Background -->
<div class="absolute top-1/4 right-0 w-[500px] h-[500px] bg-dream-pink/20 rounded-full filter blur-[120px] translate-x-1/2"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<!-- Header -->
<div class="animate-entrance text-center mb-16 md:mb-24">
<div class="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<Icon name="lucide:sparkles" size={16} />
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Partage & Transmission</span>
</div>
<h1 class="font-display text-4xl sm:text-6xl md:text-8xl text-night mb-8 leading-tight">Ateliers <span class="text-dream-purple italic">Théâtre</span></h1>
<p class="font-sans text-night/60 max-w-2xl mx-auto text-lg md:text-xl leading-relaxed">
La Compagnie AspiRêves propose des ateliers pour petits et grands, pour que vous aussi vous voliez sur votre petit nuage.
</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-10 md:gap-16">
<!-- Enfants & Ados -->
<div class="fade-up bg-white p-8 md:p-12 rounded-[32px] md:rounded-[40px] border border-white shadow-xl shadow-night/5">
<div class="w-14 h-14 rounded-2xl bg-dream-purple/20 flex items-center justify-center text-dream-purple mb-8">
<Icon name="lucide:users" size={28} />
</div>
<h2 class="font-display text-3xl md:text-4xl text-night mb-4">Enfants & Ados</h2>
<p class="font-sans text-night/50 font-bold uppercase tracking-widest text-xs mb-8">Jeu, expression & créativité</p>
<ul class="space-y-4 font-sans text-night/70 text-lg leading-relaxed">
<li class="flex items-start gap-3">
<span class="w-2 h-2 rounded-full bg-dream-purple mt-2.5 flex-shrink-0"></span>
Développement de l'imagination
</li>
<li class="flex items-start gap-3">
<span class="w-2 h-2 rounded-full bg-dream-purple mt-2.5 flex-shrink-0"></span>
Expression corporelle et vocale
</li>
<li class="flex items-start gap-3">
<span class="w-2 h-2 rounded-full bg-dream-purple mt-2.5 flex-shrink-0"></span>
Confiance en soi et écoute
</li>
<li class="flex items-start gap-3">
<span class="w-2 h-2 rounded-full bg-dream-purple mt-2.5 flex-shrink-0"></span>
Jeux d'improvisation
</li>
</ul>
</div>
<!-- Adultes -->
<div class="fade-up bg-white p-8 md:p-12 rounded-[32px] md:rounded-[40px] border border-white shadow-xl shadow-night/5" style="transition-delay: 0.2s">
<div class="w-14 h-14 rounded-2xl bg-dream-coral/20 flex items-center justify-center text-dream-coral mb-8">
<Icon name="lucide:drama" size={28} />
</div>
<h2 class="font-display text-3xl md:text-4xl text-night mb-4">Adultes</h2>
<p class="font-sans text-night/50 font-bold uppercase tracking-widest text-xs mb-8">Bienveillance & plaisir</p>
<ul class="space-y-4 font-sans text-night/70 text-lg leading-relaxed">
<li class="flex items-start gap-3">
<span class="w-2 h-2 rounded-full bg-dream-coral mt-2.5 flex-shrink-0"></span>
Mise en scène et jeu théâtral
</li>
<li class="flex items-start gap-3">
<span class="w-2 h-2 rounded-full bg-dream-coral mt-2.5 flex-shrink-0"></span>
Découverte du clown
</li>
<li class="flex items-start gap-3">
<span class="w-2 h-2 rounded-full bg-dream-coral mt-2.5 flex-shrink-0"></span>
Expression corporelle
</li>
</ul>
</div>
</div>
<!-- Info cards -->
<div class="fade-up mt-12 md:mt-16 grid grid-cols-1 sm:grid-cols-2 gap-6 md:gap-8 max-w-3xl mx-auto">
<div class="flex items-start gap-4 md:gap-5 p-5 md:p-6 rounded-2xl md:rounded-3xl bg-white shadow-xl shadow-night/5 border border-white">
<div class="w-12 h-12 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-dream-blue/20 flex items-center justify-center text-dream-blue flex-shrink-0">
<Icon name="lucide:map-pin" size={24} />
</div>
<div>
<h3 class="font-display text-lg md:text-xl text-night mb-1">Lieux d'intervention</h3>
<p class="font-sans text-[10px] md:text-sm text-night/50 font-bold uppercase tracking-widest">Tarn (81) & Hérault (34)</p>
</div>
</div>
<div class="flex items-start gap-4 md:gap-5 p-5 md:p-6 rounded-2xl md:rounded-3xl bg-white shadow-xl shadow-night/5 border border-white">
<div class="w-12 h-12 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-dream-pink/20 flex items-center justify-center text-dream-pink flex-shrink-0">
<Icon name="lucide:hand" size={24} />
</div>
<div>
<h3 class="font-display text-lg md:text-xl text-night mb-1">Plaquette</h3>
<p class="font-sans text-[10px] md:text-sm text-night/50 font-bold uppercase tracking-widest">Sur demande</p>
</div>
</div>
</div>
<!-- CTA -->
<div class="mt-12 md:mt-16 text-center">
<a
href={`mailto:${companyInfo.emails.ateliers}`}
class="inline-flex items-center gap-4 bg-night text-cloud px-8 py-4 md:px-10 md:py-5 rounded-full font-sans font-bold tracking-widest uppercase text-sm hover:bg-night/90 transition-all hover:scale-105 shadow-xl shadow-night/20"
>
<Icon name="lucide:mail" size={20} />
Nous contacter
</a>
</div>
</div>
<!-- Floating decoration -->
<div class="absolute bottom-20 left-10 text-dream-purple opacity-20 animate-float">
<StarFilled size={48} />
</div>
</div>
</Layout>

104
src/pages/contact.astro Normal file
View file

@ -0,0 +1,104 @@
---
import Layout from '../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import StarFilled from '../components/icons/StarFilled.astro';
import { companyInfo } from '../data';
---
<Layout title="Contact — Compagnie AspiRêves">
<div class="pt-32 pb-24 min-h-screen bg-cloud relative overflow-hidden">
<!-- Background -->
<div class="absolute top-0 right-0 w-96 h-96 bg-dream-purple/10 rounded-full filter blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<!-- Header -->
<div class="animate-entrance text-center mb-16 md:mb-24">
<div class="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<Icon name="lucide:sparkles" size={16} />
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Contact</span>
</div>
<h1 class="font-display text-5xl md:text-8xl text-night mb-8">Nous <span class="text-dream-coral italic">Joindre</span></h1>
<p class="font-sans text-night/60 max-w-2xl mx-auto text-lg md:text-xl leading-relaxed">
Une question ? Un projet ? N'hésitez pas à nous contacter, <br class="hidden sm:block" />nous vous répondrons avec plaisir.
</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-10 md:gap-16">
<!-- Email Cards -->
<div class="fade-up space-y-6 md:space-y-8">
<div class="bg-white p-8 md:p-10 rounded-[32px] md:rounded-[40px] border border-white shadow-xl shadow-night/5 hover:shadow-dream-purple/10 transition-all group">
<div class="w-14 h-14 rounded-2xl bg-night text-cloud flex items-center justify-center mb-8 group-hover:scale-110 transition-transform">
<Icon name="lucide:mail" size={28} />
</div>
<h3 class="font-display text-2xl md:text-3xl text-night mb-3">Diffusion & Programmation</h3>
<p class="font-sans text-night/50 font-bold uppercase tracking-widest text-xs mb-8">Pour toute demande concernant nos spectacles.</p>
<a
href={`mailto:${companyInfo.emails.pro}`}
class="inline-flex items-center gap-3 text-night font-sans font-bold hover:text-dream-coral transition-colors break-all"
>
{companyInfo.emails.pro}
<Icon name="lucide:send" size={16} class="text-dream-coral" />
</a>
</div>
<div class="bg-white p-8 md:p-10 rounded-[32px] md:rounded-[40px] border border-white shadow-xl shadow-night/5 hover:shadow-dream-blue/10 transition-all group">
<div class="w-14 h-14 rounded-2xl bg-dream-blue text-white flex items-center justify-center mb-8 group-hover:scale-110 transition-transform">
<Icon name="lucide:mail" size={28} />
</div>
<h3 class="font-display text-2xl md:text-3xl text-night mb-3">Ateliers & Pédagogie</h3>
<p class="font-sans text-night/50 font-bold uppercase tracking-widest text-xs mb-8">Pour les inscriptions et renseignements ateliers.</p>
<a
href={`mailto:${companyInfo.emails.ateliers}`}
class="inline-flex items-center gap-3 text-night font-sans font-bold hover:text-dream-blue transition-colors break-all"
>
{companyInfo.emails.ateliers}
<Icon name="lucide:send" size={16} class="text-dream-blue" />
</a>
</div>
</div>
<!-- Social & Info -->
<div class="fade-up bg-night text-cloud p-10 md:p-16 rounded-[40px] md:rounded-[60px] flex flex-col justify-between relative overflow-hidden" style="transition-delay: 0.2s">
<div class="absolute top-0 right-0 w-64 h-64 bg-dream-purple/20 rounded-full filter blur-[80px] -translate-y-1/2 translate-x-1/2"></div>
<div class="relative z-10">
<h3 class="font-display text-4xl md:text-5xl mb-8">Suivez nos <br /><span class="text-dream-coral">aventures</span></h3>
<p class="font-sans text-cloud/60 text-lg md:text-xl mb-12 leading-relaxed">
Rejoignez-nous sur les réseaux sociaux pour découvrir les coulisses, les photos de tournée et les actualités de la compagnie.
</p>
<div class="space-y-6">
<a href={companyInfo.socials.instagram} target="_blank" rel="noreferrer" class="flex items-center gap-6 group">
<div class="w-14 h-14 rounded-2xl border-2 border-cloud/10 flex items-center justify-center group-hover:border-dream-pink group-hover:text-dream-pink transition-all">
<Icon name="lucide:instagram" size={24} />
</div>
<span class="font-sans text-xl font-bold tracking-wide group-hover:text-dream-pink transition-colors">Instagram</span>
</a>
<a href={companyInfo.socials.facebook} target="_blank" rel="noreferrer" class="flex items-center gap-6 group">
<div class="w-14 h-14 rounded-2xl border-2 border-cloud/10 flex items-center justify-center group-hover:border-dream-blue group-hover:text-dream-blue transition-all">
<Icon name="lucide:facebook" size={24} />
</div>
<span class="font-sans text-xl font-bold tracking-wide group-hover:text-dream-blue transition-colors">Facebook</span>
</a>
</div>
</div>
<div class="mt-16 pt-12 border-t border-cloud/10 relative z-10 space-y-4">
<div class="flex items-center gap-4">
<Icon name="lucide:phone" size={18} class="text-dream-coral" />
<a href={`tel:${companyInfo.phone}`} class="font-sans text-lg font-bold hover:text-dream-coral transition-colors">{companyInfo.phone}</a>
</div>
<div class="flex items-start gap-4">
<Icon name="lucide:map-pin" size={18} class="text-dream-coral flex-shrink-0 mt-1" />
<span class="font-sans text-cloud/60">{companyInfo.address}</span>
</div>
</div>
<div class="absolute bottom-10 right-10 text-star opacity-20 animate-float">
<StarFilled size={48} />
</div>
</div>
</div>
</div>
</div>
</Layout>

184
src/pages/index.astro Normal file
View file

@ -0,0 +1,184 @@
---
import Layout from '../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import StarFilled from '../components/icons/StarFilled.astro';
import { companyInfo, spectacles, agenda } from '../data';
const activeSpectacles = spectacles.filter(s => !s.retired);
const upcomingShows = agenda
.filter(event => new Date(event.date) >= new Date())
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
.slice(0, 2);
---
<Layout>
<!-- Hero Section -->
<section class="relative min-h-screen flex items-center justify-center overflow-hidden pt-24 md:pt-32">
<!-- Background Blobs -->
<div class="absolute top-1/4 -left-20 w-[500px] h-[500px] bg-dream-purple/30 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-blob"></div>
<div class="absolute top-1/3 -right-20 w-[600px] h-[600px] bg-dream-blue/40 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-blob animation-delay-2000"></div>
<div class="absolute -bottom-32 left-1/3 w-[550px] h-[550px] bg-dream-pink/40 rounded-full mix-blend-multiply filter blur-3xl opacity-70 animate-blob animation-delay-4000"></div>
<!-- Floating decorations -->
<div class="absolute top-20 left-20 animate-float text-star opacity-40">
<StarFilled size={40} />
</div>
<div class="absolute bottom-40 right-20 animate-float animation-delay-2000 text-dream-coral opacity-40">
<Icon name="lucide:sparkles" size={32} />
</div>
<div class="absolute top-1/2 right-1/4 animate-float animation-delay-4000 text-dream-purple opacity-30">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>
</div>
<div class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<div class="animate-entrance-scale">
<div class="inline-block mb-6 px-4 py-2 rounded-full bg-white/50 backdrop-blur-sm border border-white/20 shadow-sm">
<span class="font-sans text-[10px] sm:text-sm font-semibold text-dream-coral uppercase tracking-widest">Spectacle vivant · Tarn & Occitanie</span>
</div>
<h1 class="font-display text-5xl sm:text-6xl md:text-7xl lg:text-8xl text-night leading-tight mb-8">
Rêver grand. <br class="md:hidden" /><span class="text-transparent bg-clip-text bg-gradient-to-r from-dream-purple via-dream-coral to-dream-blue">Jouer vrai.</span>
</h1>
<p class="font-sans text-lg md:text-2xl text-night/70 max-w-3xl mx-auto leading-relaxed mb-12">
Des spectacles poétiques et burlesques pour petits et grands, et des ateliers où l'on cultive l'imaginaire.
</p>
<div class="flex flex-col sm:flex-row items-center justify-center gap-8">
<a
href="/spectacles/"
class="group relative flex items-center gap-3 bg-night text-cloud px-10 py-5 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-xl shadow-night/20"
>
Nos Spectacles
<Icon name="lucide:arrow-right" size={20} class="group-hover:translate-x-2 transition-transform" />
</a>
<a
href={`https://www.youtube.com/watch?v=${companyInfo.teaserVideoId}`}
target="_blank"
rel="noreferrer"
class="group flex items-center gap-4 text-night font-sans font-bold hover:text-dream-coral transition-colors"
>
<div class="w-14 h-14 rounded-full bg-white flex items-center justify-center shadow-lg group-hover:shadow-dream-coral/20 group-hover:scale-110 transition-all">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ml-1"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"/></svg>
</div>
Voir le teaser
</a>
</div>
</div>
</div>
<!-- Wave Divider -->
<div class="absolute bottom-0 left-0 w-full overflow-hidden leading-none">
<svg class="relative block w-full h-[100px]" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 120" preserveAspectRatio="none">
<path d="M321.39,56.44c58-10.79,114.16-30.13,172-41.86,82.39-16.72,168.19-17.73,250.45-.39C823.78,31,906.67,72,985.66,92.83c70.05,18.48,146.53,26.09,214.34,3V120H0V95.8C57.23,103.19,115.6,115.59,173.45,108a512.21,512.21,0,0,0,147.94-51.56Z" fill="#ffffff"></path>
</svg>
</div>
</section>
<!-- Featured Spectacles -->
<section class="py-20 md:py-32 bg-white relative">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-12 md:mb-20">
<h2 class="font-display text-4xl md:text-6xl text-night mb-6">Nos Créations</h2>
<div class="w-16 md:w-24 h-2 bg-gradient-to-r from-dream-purple to-dream-blue mx-auto rounded-full"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-12">
{activeSpectacles.map((spectacle, index) => (
<a
href={`/spectacles/${spectacle.id}/`}
class="fade-up group cursor-pointer block"
style={`transition-delay: ${index * 0.15}s`}
>
<div class="relative aspect-[4/5] overflow-hidden rounded-[32px] md:rounded-[40px] mb-6 md:mb-8 shadow-2xl shadow-night/5 group-hover:shadow-dream-purple/20 transition-all duration-500">
<img
src={spectacle.image}
alt={spectacle.title}
class="object-cover w-full h-full transform group-hover:scale-110 transition-transform duration-1000 ease-in-out"
referrerpolicy="no-referrer"
/>
<div class="absolute inset-0 bg-gradient-to-t from-night/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 flex items-end p-8">
<span class="text-cloud font-sans font-bold uppercase tracking-widest bg-white/20 backdrop-blur-md rounded-full px-6 py-3 border border-white/30">
Découvrir
</span>
</div>
</div>
<h3 class="font-display text-3xl text-night mb-3 group-hover:text-dream-coral transition-colors">{spectacle.title}</h3>
<div class="flex items-center gap-4 font-sans text-sm font-bold text-night/40 uppercase tracking-widest">
<span class="px-3 py-1 bg-dream-blue/20 rounded-lg">{spectacle.age}</span>
{spectacle.duration && (
<>
<span class="w-1.5 h-1.5 rounded-full bg-dream-coral"></span>
<span>{spectacle.duration}</span>
</>
)}
</div>
</a>
))}
</div>
</div>
</section>
<!-- Quick Agenda -->
<section class="py-20 md:py-32 bg-cloud relative overflow-hidden">
<div class="absolute top-0 left-0 w-full h-full opacity-30 pointer-events-none">
<div class="absolute top-1/4 left-1/4 w-64 h-64 bg-dream-purple rounded-full filter blur-[100px] animate-pulse"></div>
<div class="absolute bottom-1/4 right-1/4 w-64 h-64 bg-dream-blue rounded-full filter blur-[100px] animate-pulse animation-delay-2000"></div>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<div class="bg-white rounded-[40px] md:rounded-[60px] p-8 md:p-20 shadow-2xl shadow-night/5 border border-white">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 md:gap-20 items-center">
<div class="text-center lg:text-left">
<h2 class="font-display text-4xl md:text-6xl text-night mb-6 md:mb-8 leading-tight">Prochaines <br /><span class="text-dream-coral">Rencontres</span></h2>
<p class="font-sans text-night/60 text-lg mb-8 md:mb-12 max-w-md mx-auto lg:mx-0 leading-relaxed">
Le théâtre est un voyage qui se partage. Retrouvez-nous pour une parenthèse enchantée.
</p>
<a
href="/agenda/"
class="inline-flex items-center gap-4 bg-night text-cloud px-8 py-4 md:px-10 md:py-5 rounded-full font-sans font-bold uppercase tracking-widest text-sm transition-all hover:scale-105 shadow-xl shadow-night/20"
>
Tout l'agenda
</a>
</div>
<div class="space-y-6 md:space-y-8">
{upcomingShows.length > 0 ? (
upcomingShows.map((event, index) => {
const spec = spectacles.find(s => s.id === event.spectacleId);
const dateObj = new Date(event.date);
return (
<a
href="/agenda/"
class="fade-right group flex flex-col sm:flex-row gap-6 md:gap-8 p-6 md:p-8 rounded-[24px] md:rounded-[32px] bg-cloud/50 hover:bg-white hover:shadow-xl hover:shadow-night/5 transition-all cursor-pointer border border-transparent hover:border-dream-purple/20 items-center sm:items-stretch block"
style={`transition-delay: ${index * 0.2}s`}
>
<div class="flex-shrink-0 flex flex-col items-center justify-center w-24 h-24 md:w-28 md:h-28 rounded-2xl md:rounded-3xl bg-gradient-to-br from-dream-purple to-dream-blue text-white shadow-lg">
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest opacity-80 mb-1">
{dateObj.toLocaleString('fr-FR', { month: 'short' })}
</span>
<span class="font-display text-4xl md:text-5xl">
{dateObj.getDate()}
</span>
</div>
<div class="flex-grow flex flex-col justify-center text-center sm:text-left">
<h4 class="font-display text-xl md:text-2xl text-night mb-2 group-hover:text-dream-coral transition-colors">{spec?.title}</h4>
<div class="flex items-center justify-center sm:justify-start gap-2 font-sans text-[10px] md:text-sm font-bold text-night/40 uppercase tracking-widest">
<Icon name="lucide:map-pin" size={14} class="text-dream-coral" />
<span>{event.location}</span>
</div>
</div>
</a>
);
})
) : (
<div class="p-10 md:p-12 border-4 border-dashed border-dream-blue/20 rounded-[32px] md:rounded-[40px] text-center">
<p class="font-sans text-night/40 font-bold uppercase tracking-widest text-sm">De nouveaux rêves arrivent bientôt...</p>
</div>
)}
</div>
</div>
</div>
</div>
</section>
</Layout>

View file

@ -0,0 +1,157 @@
---
import Layout from '../../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import { spectacles, agenda } from '../../data';
export function getStaticPaths() {
return spectacles.map(s => ({
params: { id: s.id },
}));
}
const { id } = Astro.params;
const spectacle = spectacles.find(s => s.id === id)!;
const upcomingDates = agenda
.filter(event => event.spectacleId === id)
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
---
<Layout title={`${spectacle.title} — Compagnie AspiRêves`}>
<div class="pt-24 md:pt-32 pb-24 min-h-screen bg-cloud overflow-hidden relative">
<!-- Background -->
<div class="absolute top-0 right-0 w-96 h-96 bg-dream-purple/20 rounded-full filter blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
<div class="absolute bottom-0 left-0 w-96 h-96 bg-dream-blue/20 rounded-full filter blur-[100px] translate-y-1/2 -translate-x-1/2"></div>
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<!-- Back button -->
<a
href="/spectacles/"
class="animate-entrance group flex items-center gap-3 mb-10 font-sans font-bold text-night/60 hover:text-dream-purple transition-colors"
>
<Icon name="lucide:arrow-left" size={20} class="group-hover:-translate-x-1 transition-transform" />
Tous les spectacles
</a>
<!-- Hero -->
<div class="flex flex-col md:flex-row gap-10 md:gap-16 items-center mb-16 md:mb-24">
<div class="fade-scale w-full md:w-1/2">
<div class="relative aspect-[4/3] overflow-hidden rounded-[32px] md:rounded-[60px] shadow-2xl shadow-night/10 border-4 md:border-8 border-white">
<img
src={spectacle.image}
alt={spectacle.title}
class="object-cover w-full h-full"
referrerpolicy="no-referrer"
/>
</div>
</div>
<div class="fade-up w-full md:w-1/2 space-y-6 text-center md:text-left" style="transition-delay: 0.2s">
{spectacle.retired && (
<div class="inline-block px-4 py-1 rounded-full bg-night/10 text-night/50 font-sans text-xs font-bold uppercase tracking-widest">
Spectacle retraité
</div>
)}
<h1 class="font-display text-4xl sm:text-6xl md:text-7xl text-night">{spectacle.title}</h1>
<div class="flex flex-wrap justify-center md:justify-start gap-3">
<div class="flex items-center gap-2 bg-dream-blue/20 px-5 py-2 rounded-2xl text-sm font-sans font-bold">
<Icon name="lucide:users" size={16} class="text-dream-blue" />
<span>{spectacle.age}</span>
</div>
{spectacle.duration && (
<div class="flex items-center gap-2 bg-dream-pink/20 px-5 py-2 rounded-2xl text-sm font-sans font-bold">
<Icon name="lucide:clock" size={16} class="text-dream-pink" />
<span>{spectacle.duration}</span>
</div>
)}
</div>
{spectacle.summary && (
<p class="font-sans text-lg md:text-xl text-night/70 leading-relaxed">{spectacle.summary}</p>
)}
{spectacle.credits && (
<p class="font-sans text-sm text-night/50 font-bold uppercase tracking-widest">{spectacle.credits}</p>
)}
{spectacle.dossierPro && spectacle.dossierPro !== '#' && (
<a
href={spectacle.dossierPro}
target="_blank"
rel="noreferrer"
class="inline-flex items-center gap-3 bg-night text-cloud px-8 py-4 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-lg shadow-night/20"
>
<Icon name="lucide:download" size={18} />
Dossier Pédagogique
</a>
)}
</div>
</div>
<!-- Gallery -->
{spectacle.gallery.length > 0 && (
<section class="fade-up mb-16 md:mb-24">
<h2 class="font-display text-3xl md:text-5xl text-night mb-8 md:mb-12 text-center">Galerie</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6 md:gap-8">
{spectacle.gallery.map((img, index) => (
<div class="aspect-[4/3] overflow-hidden rounded-[24px] md:rounded-[40px] shadow-xl shadow-night/5 border-4 border-white">
<img
src={img}
alt={`${spectacle.title} - photo ${index + 1}`}
class="object-cover w-full h-full"
referrerpolicy="no-referrer"
/>
</div>
))}
</div>
</section>
)}
<!-- Upcoming dates -->
{upcomingDates.length > 0 && (
<section class="fade-up">
<h2 class="font-display text-3xl md:text-5xl text-night mb-8 md:mb-12 text-center">Prochaines dates</h2>
<div class="space-y-4 max-w-2xl mx-auto">
{upcomingDates.map(event => {
const dateObj = new Date(event.date);
return (
<div class="flex flex-col sm:flex-row gap-6 p-6 md:p-8 rounded-[24px] bg-white shadow-lg shadow-night/5 border border-dream-purple/10 items-center">
<div class="flex-shrink-0 flex flex-col items-center justify-center w-20 h-20 md:w-24 md:h-24 rounded-2xl bg-gradient-to-br from-dream-purple to-dream-blue text-white shadow-md">
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest opacity-80">
{dateObj.toLocaleString('fr-FR', { month: 'short' })}
</span>
<span class="font-display text-3xl md:text-4xl">
{dateObj.getDate()}
</span>
</div>
<div class="flex-grow text-center sm:text-left">
<div class="flex items-center justify-center sm:justify-start gap-2 font-sans text-sm text-night/50 mb-1">
<Icon name="lucide:calendar" size={14} />
<span>{dateObj.toLocaleString('fr-FR', { weekday: 'long', hour: '2-digit', minute: '2-digit' })}</span>
</div>
<div class="flex items-center justify-center sm:justify-start gap-2 font-sans font-bold text-night">
<Icon name="lucide:map-pin" size={14} class="text-dream-coral" />
<span>{event.location}</span>
</div>
</div>
{event.bookingLink && (
<a
href={event.bookingLink}
target="_blank"
rel="noreferrer"
class="bg-dream-coral text-white px-6 py-3 rounded-full font-sans font-bold text-sm hover:scale-105 transition-transform shadow-md"
>
Réserver
</a>
)}
</div>
);
})}
</div>
</section>
)}
</div>
</div>
</Layout>

View file

@ -0,0 +1,235 @@
---
import Layout from '../../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import StarFilled from '../../components/icons/StarFilled.astro';
import { spectacles } from '../../data';
const jeunePublic = spectacles.filter(s => s.category === 'jeune-public' && !s.retired);
const toutPublic = spectacles.filter(s => s.category === 'tout-public' && !s.retired);
const retraites = spectacles.filter(s => s.retired);
---
<Layout title="Spectacles — Compagnie AspiRêves">
<div class="pt-32 pb-24 min-h-screen bg-cloud overflow-hidden relative">
<!-- Background -->
<div class="absolute top-0 right-0 w-96 h-96 bg-dream-purple/20 rounded-full filter blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
<div class="absolute bottom-0 left-0 w-96 h-96 bg-dream-blue/20 rounded-full filter blur-[100px] translate-y-1/2 -translate-x-1/2"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<!-- Header -->
<div class="animate-entrance text-center mb-16 md:mb-24">
<div class="inline-flex items-center gap-2 mb-6 px-4 py-1 rounded-full bg-white/50 border border-white/20 text-dream-coral">
<Icon name="lucide:sparkles" size={16} />
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest">Le Répertoire</span>
</div>
<h1 class="font-display text-4xl sm:text-6xl md:text-8xl text-night mb-8">Nos <span class="text-dream-purple italic">Spectacles</span></h1>
<p class="font-sans text-night/60 max-w-2xl mx-auto text-lg md:text-xl leading-relaxed">
Des bulles de poésie et d'humour pour s'évader, <br class="hidden sm:block" />apprendre et grandir ensemble.
</p>
</div>
<!-- Jeune Public -->
{jeunePublic.length > 0 && (
<div class="mb-24 md:mb-40">
<div class="text-center mb-12 md:mb-16">
<h2 class="font-display text-3xl md:text-5xl text-night">Jeune Public</h2>
<div class="w-16 h-1.5 bg-gradient-to-r from-dream-purple to-dream-blue mx-auto mt-4 rounded-full"></div>
</div>
<div class="space-y-24 md:space-y-40">
{jeunePublic.map((spectacle, index) => (
<div
class:list={[
'fade-up flex flex-col items-center gap-10 md:gap-16',
index % 2 === 1 ? 'md:flex-row-reverse' : 'md:flex-row',
]}
>
<!-- Image -->
<a href={`/spectacles/${spectacle.id}/`} class="w-full md:w-1/2 relative cursor-pointer">
<div class="absolute -top-4 -left-4 md:-top-6 md:-left-6 w-16 h-16 md:w-24 md:h-24 bg-star/20 rounded-full filter blur-xl md:blur-2xl animate-pulse"></div>
<div class="absolute -bottom-4 -right-4 md:-bottom-6 md:-right-6 w-20 h-20 md:w-32 md:h-32 bg-dream-blue/30 rounded-full filter blur-xl md:blur-2xl animate-pulse animation-delay-2000"></div>
<div class="relative aspect-[4/3] overflow-hidden rounded-[32px] md:rounded-[60px] shadow-2xl shadow-night/10 group border-4 md:border-8 border-white">
<img
src={spectacle.image}
alt={spectacle.title}
class="object-cover w-full h-full transition-transform duration-1000 group-hover:scale-110"
referrerpolicy="no-referrer"
/>
<div class="absolute inset-0 bg-gradient-to-tr from-dream-purple/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
</div>
</a>
<!-- Content -->
<div class="w-full md:w-1/2 space-y-6 md:space-y-8 text-center md:text-left">
<div class="flex items-center justify-center md:justify-start gap-3">
<div class="h-px w-8 md:w-12 bg-dream-coral"></div>
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest text-dream-coral">Création Originale</span>
</div>
<h2 class="font-display text-4xl md:text-6xl text-night">{spectacle.title}</h2>
<div class="flex flex-wrap justify-center md:justify-start gap-3 md:gap-4">
<div class="flex items-center gap-2 bg-dream-blue/20 px-4 py-1.5 md:px-5 md:py-2 rounded-xl md:rounded-2xl text-xs md:text-sm font-sans font-bold">
<Icon name="lucide:users" size={16} class="text-dream-blue" />
<span>{spectacle.age}</span>
</div>
{spectacle.duration && (
<div class="flex items-center gap-2 bg-dream-pink/20 px-4 py-1.5 md:px-5 md:py-2 rounded-xl md:rounded-2xl text-xs md:text-sm font-sans font-bold">
<Icon name="lucide:clock" size={16} class="text-dream-pink" />
<span>{spectacle.duration}</span>
</div>
)}
</div>
{spectacle.summary && (
<p class="font-sans text-lg md:text-xl text-night/70 leading-relaxed">{spectacle.summary}</p>
)}
<div class="pt-4 md:pt-8 flex flex-wrap justify-center md:justify-start gap-4 md:gap-6">
<a
href={`/spectacles/${spectacle.id}/`}
class="w-full sm:w-auto bg-night text-cloud px-8 py-4 md:px-10 md:py-4 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-lg shadow-night/20 text-center"
>
Découvrir
</a>
</div>
</div>
</div>
))}
</div>
</div>
)}
<!-- Tout Public -->
{toutPublic.length > 0 && (
<div class="mb-24 md:mb-40">
<div class="text-center mb-12 md:mb-16">
<h2 class="font-display text-3xl md:text-5xl text-night">Tout Public</h2>
<div class="w-16 h-1.5 bg-gradient-to-r from-dream-purple to-dream-blue mx-auto mt-4 rounded-full"></div>
</div>
<div class="space-y-24 md:space-y-40">
{toutPublic.map((spectacle, index) => {
const globalIndex = jeunePublic.length + index;
return (
<div
class:list={[
'fade-up flex flex-col items-center gap-10 md:gap-16',
globalIndex % 2 === 1 ? 'md:flex-row-reverse' : 'md:flex-row',
]}
>
<!-- Image -->
<a href={`/spectacles/${spectacle.id}/`} class="w-full md:w-1/2 relative cursor-pointer">
<div class="absolute -top-4 -left-4 md:-top-6 md:-left-6 w-16 h-16 md:w-24 md:h-24 bg-star/20 rounded-full filter blur-xl md:blur-2xl animate-pulse"></div>
<div class="absolute -bottom-4 -right-4 md:-bottom-6 md:-right-6 w-20 h-20 md:w-32 md:h-32 bg-dream-blue/30 rounded-full filter blur-xl md:blur-2xl animate-pulse animation-delay-2000"></div>
<div class="relative aspect-[4/3] overflow-hidden rounded-[32px] md:rounded-[60px] shadow-2xl shadow-night/10 group border-4 md:border-8 border-white">
<img
src={spectacle.image}
alt={spectacle.title}
class="object-cover w-full h-full transition-transform duration-1000 group-hover:scale-110"
referrerpolicy="no-referrer"
/>
<div class="absolute inset-0 bg-gradient-to-tr from-dream-purple/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
</div>
</a>
<!-- Content -->
<div class="w-full md:w-1/2 space-y-6 md:space-y-8 text-center md:text-left">
<div class="flex items-center justify-center md:justify-start gap-3">
<div class="h-px w-8 md:w-12 bg-dream-coral"></div>
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest text-dream-coral">Création Originale</span>
</div>
<h2 class="font-display text-4xl md:text-6xl text-night">{spectacle.title}</h2>
<div class="flex flex-wrap justify-center md:justify-start gap-3 md:gap-4">
<div class="flex items-center gap-2 bg-dream-blue/20 px-4 py-1.5 md:px-5 md:py-2 rounded-xl md:rounded-2xl text-xs md:text-sm font-sans font-bold">
<Icon name="lucide:users" size={16} class="text-dream-blue" />
<span>{spectacle.age}</span>
</div>
{spectacle.duration && (
<div class="flex items-center gap-2 bg-dream-pink/20 px-4 py-1.5 md:px-5 md:py-2 rounded-xl md:rounded-2xl text-xs md:text-sm font-sans font-bold">
<Icon name="lucide:clock" size={16} class="text-dream-pink" />
<span>{spectacle.duration}</span>
</div>
)}
</div>
{spectacle.summary && (
<p class="font-sans text-lg md:text-xl text-night/70 leading-relaxed">{spectacle.summary}</p>
)}
<div class="pt-4 md:pt-8 flex flex-wrap justify-center md:justify-start gap-4 md:gap-6">
<a
href={`/spectacles/${spectacle.id}/`}
class="w-full sm:w-auto bg-night text-cloud px-8 py-4 md:px-10 md:py-4 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-lg shadow-night/20 text-center"
>
Découvrir
</a>
</div>
</div>
</div>
);
})}
</div>
</div>
)}
<!-- Retraités -->
{retraites.length > 0 && (
<div class="mt-24 md:mt-40">
<div class="text-center mb-12 md:mb-16">
<h2 class="font-display text-2xl md:text-4xl text-night/40">Spectacles Retraités</h2>
<div class="w-12 h-1 bg-night/10 mx-auto mt-4 rounded-full"></div>
</div>
<div class="space-y-16 md:space-y-24 opacity-60">
{retraites.map((spectacle, index) => (
<div
class:list={[
'fade-up flex flex-col items-center gap-10 md:gap-16',
index % 2 === 1 ? 'md:flex-row-reverse' : 'md:flex-row',
]}
>
<a href={`/spectacles/${spectacle.id}/`} class="w-full md:w-1/2 relative cursor-pointer">
<div class="absolute -top-4 -left-4 md:-top-6 md:-left-6 w-16 h-16 md:w-24 md:h-24 bg-star/20 rounded-full filter blur-xl md:blur-2xl animate-pulse"></div>
<div class="absolute -bottom-4 -right-4 md:-bottom-6 md:-right-6 w-20 h-20 md:w-32 md:h-32 bg-dream-blue/30 rounded-full filter blur-xl md:blur-2xl animate-pulse animation-delay-2000"></div>
<div class="relative aspect-[4/3] overflow-hidden rounded-[32px] md:rounded-[60px] shadow-2xl shadow-night/10 group border-4 md:border-8 border-white">
<img
src={spectacle.image}
alt={spectacle.title}
class="object-cover w-full h-full transition-transform duration-1000 group-hover:scale-110"
referrerpolicy="no-referrer"
/>
<div class="absolute inset-0 bg-gradient-to-tr from-dream-purple/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
</div>
</a>
<div class="w-full md:w-1/2 space-y-6 md:space-y-8 text-center md:text-left">
<div class="flex items-center justify-center md:justify-start gap-3">
<div class="h-px w-8 md:w-12 bg-dream-coral"></div>
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest text-dream-coral">Création Originale</span>
</div>
<h2 class="font-display text-4xl md:text-6xl text-night">{spectacle.title}</h2>
<div class="flex flex-wrap justify-center md:justify-start gap-3 md:gap-4">
<div class="flex items-center gap-2 bg-dream-blue/20 px-4 py-1.5 md:px-5 md:py-2 rounded-xl md:rounded-2xl text-xs md:text-sm font-sans font-bold">
<Icon name="lucide:users" size={16} class="text-dream-blue" />
<span>{spectacle.age}</span>
</div>
</div>
{spectacle.summary && (
<p class="font-sans text-lg md:text-xl text-night/70 leading-relaxed">{spectacle.summary}</p>
)}
<div class="pt-4 md:pt-8 flex flex-wrap justify-center md:justify-start gap-4 md:gap-6">
<a
href={`/spectacles/${spectacle.id}/`}
class="w-full sm:w-auto bg-night text-cloud px-8 py-4 md:px-10 md:py-4 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-lg shadow-night/20 text-center"
>
Découvrir
</a>
</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
<!-- Floating decorations -->
<div class="absolute top-1/4 left-10 text-dream-pink opacity-20 animate-float">
<StarFilled size={60} />
</div>
<div class="absolute bottom-1/4 right-10 text-dream-blue opacity-20 animate-float animation-delay-2000">
<StarFilled size={40} />
</div>
</div>
</Layout>

199
src/styles/global.css Normal file
View file

@ -0,0 +1,199 @@
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Outfit:wght@300;400;500;600&family=Fredoka:wght@300..700&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Outfit", ui-sans-serif, system-ui, sans-serif;
--font-serif: "Cormorant Garamond", ui-serif, Georgia, serif;
--font-display: "Fredoka", sans-serif;
--color-cloud: #F8F7FF;
--color-night: #2D2D3A;
--color-star: #FFD700;
--color-dream-pink: #FFD6E0;
--color-dream-blue: #C1D3FE;
--color-dream-purple: #E0C3FC;
--color-dream-coral: #FF9F89;
}
body {
background-color: var(--color-cloud);
color: var(--color-night);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
/* Keyframe animations */
@keyframes float {
0% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-20px) rotate(2deg); }
100% { transform: translateY(0px) rotate(0deg); }
}
.animate-float {
animation: float 6s ease-in-out infinite;
}
@keyframes blob {
0% { transform: scale(1); }
33% { transform: scale(1.1) translate(10px, -10px); }
66% { transform: scale(0.9) translate(-10px, 10px); }
100% { transform: scale(1); }
}
.animate-blob {
animation: blob 10s infinite alternate;
}
.animation-delay-2000 {
animation-delay: 2s;
}
.animation-delay-4000 {
animation-delay: 4s;
}
/* Scroll-triggered animations (Intersection Observer) */
.fade-up {
opacity: 0;
transform: translateY(2rem);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.fade-up.visible {
opacity: 1;
transform: translateY(0);
}
.fade-right {
opacity: 0;
transform: translateX(2rem);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.fade-right.visible {
opacity: 1;
transform: translateX(0);
}
.fade-scale {
opacity: 0;
transform: scale(0.9);
transition: opacity 1s ease, transform 1s ease;
}
.fade-scale.visible {
opacity: 1;
transform: scale(1);
}
/* Page entrance animation */
@keyframes entrance {
from { opacity: 0; transform: translateY(1.5rem); }
to { opacity: 1; transform: translateY(0); }
}
.animate-entrance {
animation: entrance 0.6s ease forwards;
}
@keyframes entrance-scale {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}
.animate-entrance-scale {
animation: entrance-scale 1s ease forwards;
}
/* Mobile menu animation */
@keyframes slideDown {
from { opacity: 0; transform: translateY(-1rem); }
to { opacity: 1; transform: translateY(0); }
}
.animate-slide-down {
animation: slideDown 0.3s ease forwards;
}
/*
THÈMES - Sélecteur temporaire de palette
*/
/* Crépuscule & Cuivre — Théâtre classique, velours, projecteurs */
[data-theme="crepuscule"] {
--color-cloud: #F5EFE6;
--color-night: #1A1A2E;
--color-star: #C9956B;
--color-dream-pink: #E8D5B7;
--color-dream-blue: #3A506B;
--color-dream-purple: #C9956B;
--color-dream-coral: #D4443B;
}
/* Forêt enchantée — Conte, nature mystérieuse, rêve enraciné */
[data-theme="foret"] {
--color-cloud: #F2EDE4;
--color-night: #1B2D2A;
--color-star: #D4A843;
--color-dream-pink: #D4A843;
--color-dream-blue: #3D5A47;
--color-dream-purple: #7A9E6B;
--color-dream-coral: #C17F3E;
}
/* Nuit étoilée — Scène dans le noir, dramatique, onirique */
[data-theme="nuit"] {
--color-cloud: #0D0D1A;
--color-night: #F0ECE2;
--color-star: #C8A961;
--color-dream-pink: #C8A961;
--color-dream-blue: #6B5FB5;
--color-dream-purple: #4A3F8A;
--color-dream-coral: #C8A961;
}
/* Nuit étoilée — Overrides pour les bg-white hardcodés */
[data-theme="nuit"] nav.bg-white\/80,
[data-theme="nuit"] nav[class*="bg-white"] {
background-color: rgba(13, 13, 26, 0.85) !important;
}
[data-theme="nuit"] #mobile-menu {
background-color: rgba(13, 13, 26, 0.95) !important;
}
/* Aube rosée — Chaleur, poésie, douceur mature */
[data-theme="aube"] {
--color-cloud: #FAF4F0;
--color-night: #2C1A2E;
--color-star: #C0785A;
--color-dream-pink: #D4A88C;
--color-dream-blue: #7A4A6A;
--color-dream-purple: #6B3A5A;
--color-dream-coral: #C0785A;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: var(--color-cloud);
}
::-webkit-scrollbar-thumb {
background: var(--color-dream-purple);
border-radius: 10px;
border: 3px solid var(--color-cloud);
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-dream-coral);
}

View file

@ -1,26 +1,3 @@
{ {
"compilerOptions": { "extends": "astro/tsconfigs/strict"
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
} }

View file

@ -1,24 +0,0 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});