diff --git a/img/hero-bg.webp b/img/hero-bg.webp index 2df8311..25bea10 100644 Binary files a/img/hero-bg.webp and b/img/hero-bg.webp differ diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..2830860 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,34 @@ +# ─── Désactiver le listing des répertoires ────────────────────────────────── +Options -Indexes + +# ─── Bloquer les fichiers sensibles ───────────────────────────────────────── + + Order allow,deny + Deny from all + + +# ─── Bloquer l'accès direct aux fichiers cachés (.htaccess, .env, etc.) ───── + + Order allow,deny + Deny from all + + +# ─── Headers de sécurité ───────────────────────────────────────────────────── + + Header always set X-Content-Type-Options "nosniff" + Header always set X-Frame-Options "SAMEORIGIN" + Header always set Referrer-Policy "strict-origin-when-cross-origin" + + +# ─── SPA routing (React Router / Vite) ────────────────────────────────────── + + RewriteEngine On + RewriteBase / + + # Ne pas réécrire les fichiers et dossiers existants + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + # Tout le reste → index.html + RewriteRule ^ index.html [L] + diff --git a/public/img/favicon.ico b/public/img/favicon.ico new file mode 100644 index 0000000..7a337d1 Binary files /dev/null and b/public/img/favicon.ico differ diff --git a/public/img/hero-bg.webp b/public/img/hero-bg.webp new file mode 100644 index 0000000..25bea10 Binary files /dev/null and b/public/img/hero-bg.webp differ diff --git a/public/img/ico-180.png b/public/img/ico-180.png new file mode 100644 index 0000000..1f6b138 Binary files /dev/null and b/public/img/ico-180.png differ diff --git a/public/img/ico.png b/public/img/ico.png new file mode 100644 index 0000000..ed04538 Binary files /dev/null and b/public/img/ico.png differ diff --git a/public/img/ico.webp b/public/img/ico.webp new file mode 100644 index 0000000..ce4faf7 Binary files /dev/null and b/public/img/ico.webp differ diff --git a/src/App.jsx b/src/App.jsx index 6ecd2a3..ed84e0d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,19 +1,27 @@ +import { useState, useEffect } from 'react' import Navbar from './components/Navbar' import Hero from './components/Hero' import ComparisonSection from './components/ComparisonSection' -import Testimonials from './components/Testimonials' import GuideSection from './components/GuideSection' import Footer from './components/Footer' export default function App() { + const [bgSrc, setBgSrc] = useState(null) + + useEffect(() => { + fetch('/config.json') + .then(r => r.json()) + .then(data => setBgSrc(data.heroBg ?? null)) + .catch(() => {}) + }, []) + return ( -
+
- + - - +
diff --git a/src/components/AnimatedBrandName.jsx b/src/components/AnimatedBrandName.jsx new file mode 100644 index 0000000..59612d7 --- /dev/null +++ b/src/components/AnimatedBrandName.jsx @@ -0,0 +1,50 @@ +import { useEffect } from 'react' +import { motion, useAnimation } from 'framer-motion' + +const LETTERS_1 = 'Assurez'.split('') +const LETTERS_2 = 'Moi'.split('') + +export default function AnimatedBrandName({ className = '', lightText = false }) { + const controls = useAnimation() + + useEffect(() => { + const play = () => + controls.start(i => ({ + y: [0, -9, 0], + transition: { + delay: i * 0.055, + duration: 0.38, + ease: 'easeInOut', + }, + })) + + play() + const interval = setInterval(play, 5000) + return () => clearInterval(interval) + }, [controls]) + + return ( + + {LETTERS_1.map((letter, i) => ( + + {letter} + + ))} + {LETTERS_2.map((letter, i) => ( + + {letter} + + ))} + + ) +} diff --git a/src/components/ComparisonSection.jsx b/src/components/ComparisonSection.jsx index 9eb31e9..ab8278e 100644 --- a/src/components/ComparisonSection.jsx +++ b/src/components/ComparisonSection.jsx @@ -98,11 +98,17 @@ export default function ComparisonSection() { const { mutuelles, loading, error } = useMutuelles() return ( -
+
{/* Header */} -
+
Classement officiel 2026 @@ -114,7 +120,7 @@ export default function ComparisonSection() { Classement mis à jour chaque mois — basé sur les garanties, les tarifs et les avis clients de plus de 186 mutuelles analysées.

-
+
{/* Erreur */} {error && ( @@ -134,7 +140,7 @@ export default function ComparisonSection() { key={m.rang} initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} - viewport={{ once: true }} + viewport={{ once: false }} transition={{ delay: i * 0.1, duration: 0.45 }} > @@ -143,12 +149,10 @@ export default function ComparisonSection() {
{/* Séparateur */} -
-
+
Classement complet -
{/* Liste complète */} @@ -158,7 +162,7 @@ export default function ComparisonSection() { key={m.rang} initial={{ opacity: 0, x: -16 }} whileInView={{ opacity: 1, x: 0 }} - viewport={{ once: true }} + viewport={{ once: false }} transition={{ delay: i * 0.06, duration: 0.4 }} > diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 5c96393..031cac5 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -1,11 +1,6 @@ -import { - FiFacebook, - FiTwitter, - FiInstagram, - FiLinkedin, - FiYoutube, - FiArrowUpRight, -} from 'react-icons/fi' +import { motion } from 'framer-motion' +import AnimatedBrandName from './AnimatedBrandName' +import { FiArrowUpRight } from 'react-icons/fi' const colResources = ['FAQ', 'Guides', 'Lexique', 'Documents'] @@ -25,58 +20,60 @@ const colLegal = [ "Conditions générales d'utilisation", ] -const socials = [ - { Icon: FiFacebook, label: 'Facebook' }, - { Icon: FiTwitter, label: 'Twitter / X' }, - { Icon: FiInstagram, label: 'Instagram' }, - { Icon: FiLinkedin, label: 'LinkedIn' }, - { Icon: FiYoutube, label: 'YouTube' }, -] - export default function Footer() { return ( -