diff --git a/web/src/components/HeroPlayerSilhouette.jsx b/web/src/components/HeroPlayerSilhouette.jsx
new file mode 100644
index 0000000..6ada6f6
--- /dev/null
+++ b/web/src/components/HeroPlayerSilhouette.jsx
@@ -0,0 +1,60 @@
+// Purely decorative basketball-player silhouette animated behind the hero
+// stats row. An original flat-illustration silhouette (not a photo/video),
+// built from rounded-capsule limb segments — the same construction as
+// classic flat sports pictograms (NBA/Jordan-style marks) — in a duotone
+// matching the site theme. Avoids any droit-à-l'image concern since it
+// does not depict a real identifiable person.
+export default function HeroPlayerSilhouette({ className = '' }) {
+ return (
+
+
+
+ {/* trailing arm, swung back for balance */}
+
+
+ {/* dribbling arm, reaching down to the ball */}
+
+
+ {/* torso */}
+
+
+ {/* planted / front leg */}
+
+
+
+ {/* trailing leg, extended back */}
+
+
+
+
+ {/* joint fillers smoothing the width change between segments */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/web/src/pages/Home.jsx b/web/src/pages/Home.jsx
index c9abb92..9ea7819 100644
--- a/web/src/pages/Home.jsx
+++ b/web/src/pages/Home.jsx
@@ -7,6 +7,7 @@ import Modal from '../components/Modal.jsx';
import PastEventCard from '../components/PastEventCard.jsx';
import { AdhesionForm, AdhesionModal } from '../components/AdhesionForm.jsx';
import { associationSettings } from '../lib/siteSettings.js';
+import HeroPlayerSilhouette from '../components/HeroPlayerSilhouette.jsx';
export function InscriptionModal({ rencontre, onClose, onDone }) {
const { user, login } = useAuth();
@@ -265,13 +266,16 @@ export default function Home() {
Découvrir l'association
-
- {[[String(members.length), 'entreprises membres'], [String(passees.length), 'rencontres organisées'], ['30 ans', 'de partenariat']].map(([n, l]) => (
-
- ))}
+
+
+
+ {[[String(members.length), 'entreprises membres'], [String(passees.length), 'rencontres organisées'], ['30 ans', 'de partenariat']].map(([n, l]) => (
+
+ ))}
+
diff --git a/web/src/styles.css b/web/src/styles.css
index c7c3c9e..d02f9ca 100644
--- a/web/src/styles.css
+++ b/web/src/styles.css
@@ -43,6 +43,60 @@ input:focus, textarea:focus, select:focus { outline: none; border-color: var(--r
.marquee-fade { width: 40px; }
}
+/* Hero stats background: an animated basketball-player silhouette behind
+ the "entreprises membres / rencontres organisées / X ans" row. Original
+ illustration (not a photo), decorative only — see .hspl-* below. */
+.hero-stats-wrap { overflow: visible; }
+.hero-stats-silhouette {
+ position: absolute; right: -20px; bottom: -34px; width: 300px; height: auto;
+ pointer-events: none;
+ /* Fades the silhouette out over the stat text (left) and lets it show at
+ full (still very low) opacity only over the clear whitespace (right),
+ so it never degrades the existing text contrast. */
+ mask-image: linear-gradient(100deg, transparent 0%, transparent 24%, black 48%, black 100%);
+ -webkit-mask-image: linear-gradient(100deg, transparent 0%, transparent 24%, black 48%, black 100%);
+}
+.hspl-figure {
+ opacity: .08; transform-origin: 50% 100%;
+ animation: hspl-drift 7s ease-in-out infinite alternate, hspl-glow 5s ease-in-out infinite;
+}
+.hspl-ball {
+ opacity: .14; transform-box: fill-box; transform-origin: 50% 100%;
+ animation: hspl-ball-bounce 1.4s infinite;
+}
+.hspl-speedline {
+ opacity: .3;
+ animation: hspl-speedline 2.4s ease-out infinite;
+}
+.hspl-speedline:nth-child(2) { animation-delay: .3s; }
+.hspl-speedline:nth-child(3) { animation-delay: .6s; }
+
+@keyframes hspl-drift {
+ from { transform: translateY(0) rotate(0deg); }
+ to { transform: translateY(-10px) rotate(-1.4deg); }
+}
+@keyframes hspl-glow {
+ 0%, 100% { filter: drop-shadow(2px 0 0 rgba(193, 39, 45, 0)); }
+ 50% { filter: drop-shadow(2px 0 0 rgba(193, 39, 45, .18)); }
+}
+@keyframes hspl-ball-bounce {
+ 0% { transform: translateY(0); animation-timing-function: cubic-bezier(.55, 0, 1, .45); }
+ 18% { transform: translateY(38px) scaleY(.82) scaleX(1.1); animation-timing-function: cubic-bezier(0, .55, .45, 1); }
+ 50% { transform: translateY(0); animation-timing-function: cubic-bezier(.55, 0, 1, .45); }
+ 68% { transform: translateY(38px) scaleY(.82) scaleX(1.1); animation-timing-function: cubic-bezier(0, .55, .45, 1); }
+ 100% { transform: translateY(0); }
+}
+@keyframes hspl-speedline {
+ 0% { opacity: 0; transform: translateX(6px); }
+ 30% { opacity: 1; transform: translateX(0); }
+ 70% { opacity: 1; transform: translateX(-4px); }
+ 100% { opacity: 0; transform: translateX(-10px); }
+}
+@media (prefers-reduced-motion: reduce) {
+ .hspl-figure, .hspl-ball { animation: none !important; }
+ .hspl-speedline { display: none !important; }
+}
+
.container { max-width: 1180px; margin: 0 auto; padding-left: 32px; padding-right: 32px; }
.serif { font-family: var(--serif); }
@@ -248,6 +302,7 @@ input:focus, textarea:focus, select:focus { outline: none; border-color: var(--r
@media (max-width: 960px) {
.grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr !important; }
.hero-grid { grid-template-columns: 1fr !important; }
+ .hero-stats-silhouette { width: 190px; right: -10px; bottom: -10px; }
}
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
@@ -274,6 +329,7 @@ input:focus, textarea:focus, select:focus { outline: none; border-color: var(--r
@media (max-width: 640px) {
.container { padding-left: 20px; padding-right: 20px; }
+ .hero-stats-silhouette { display: none; }
.admin-nav { grid-template-columns: 1fr; gap: 20px; }
.admin-sidebar-footer { align-items: stretch; flex-direction: column; }
.admin-sidebar-action { width: 100%; }