/* global React */ // ---------- Icons (inline SVG) ---------- const Icon = ({ name, size = 24, stroke = 2 }) => { const paths = { building: <>, home: <>, sparkles: <>, broom: <>, shield: <>, window: <>, phone: <>, mail: <>, location: <>, clock: <>, check: <>, arrow: <>, star: <>, users: <>, award: <>, leaf: <>, heart: <>, zap: <>, chat: <>, menu: <>, close: <>, target: <>, spray: <>, }; return ( ); }; // ---------- SME Logo (real brand mark) ---------- const SMELogo = ({ size = 'md', light = false, vertical = false }) => { const scale = size === 'xl' ? 1.8 : size === 'lg' ? 1.3 : size === 'sm' ? 0.7 : 1; const subColor = light ? 'rgba(255,255,255,0.95)' : '#0891B2'; const tagColor = light ? 'rgba(255,255,255,0.55)' : '#94A3B8'; // Real brand logo (JPEG, white background). On dark surfaces we wrap it // in a small white card so the white background reads as intentional. const markSize = 64 * scale; const mark = (
SME Nettoyage
); if (vertical) { return (
{mark} NETTOYAGE La propreté, notre engagement
); } return (
{mark}
NETTOYAGE La propreté, notre engagement
); }; // ---------- Scroll-triggered fade-in (rect-based for reliability) ---------- const Reveal = ({ children, delay = 0, y = 24, as: As = 'div', style = {}, ...rest }) => { const ref = React.useRef(null); const [visible, setVisible] = React.useState(false); React.useEffect(() => { const el = ref.current; if (!el) return; const check = () => { const rect = el.getBoundingClientRect(); return rect.top < window.innerHeight - 40 && rect.bottom > 0; }; if (check()) { setVisible(true); return; } const onScroll = () => { if (check()) { setVisible(true); cleanup(); } }; // Safety fallback: reveal anyway after 1.2s so content can never stay hidden. const fallback = setTimeout(() => { setVisible(true); cleanup(); }, 1200); const cleanup = () => { window.removeEventListener('scroll', onScroll); clearTimeout(fallback); }; window.addEventListener('scroll', onScroll, { passive: true }); return cleanup; }, []); return ( {children} ); }; // ---------- Animated counter (rect-based) ---------- const Counter = ({ end, suffix = '', prefix = '', duration = 1600 }) => { const ref = React.useRef(null); const [val, setVal] = React.useState(0); const startedRef = React.useRef(false); React.useEffect(() => { const el = ref.current; if (!el) return; let raf; const startAnim = () => { if (startedRef.current) return; startedRef.current = true; const start = performance.now(); const tick = (now) => { const t = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - t, 3); setVal(Math.round(end * eased)); if (t < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); }; const check = () => { const rect = el.getBoundingClientRect(); return rect.top < window.innerHeight - 40 && rect.bottom > 0; }; if (check()) { startAnim(); return () => cancelAnimationFrame(raf); } const onScroll = () => { if (check()) { startAnim(); cleanup(); } }; const fallback = setTimeout(startAnim, 1500); const cleanup = () => { window.removeEventListener('scroll', onScroll); clearTimeout(fallback); }; window.addEventListener('scroll', onScroll, { passive: true }); return () => { cleanup(); cancelAnimationFrame(raf); }; }, [end, duration]); return {prefix}{val.toLocaleString('fr-FR')}{suffix}; }; // ---------- Header (utility topbar + main bar with mega-menu) ---------- const SUB_SERVICES = [ { id: 'entreprises', label: "Nettoyage d'Entreprises", icon: 'building', desc: 'Bureaux, sièges, espaces de travail.' }, { id: 'batiments', label: 'Nettoyage de Bâtiments', icon: 'home', desc: 'Copropriétés, parties communes, halls.' }, { id: 'vitres', label: 'Nettoyage de Vitres', icon: 'window', desc: 'Vitrines, façades, baies vitrées.' }, { id: 'industriel', label: 'Nettoyage Industriel', icon: 'shield', desc: 'Sites de production, entrepôts, ateliers.' }, { id: 'jardins', label: 'Jardins & Parcs', icon: 'leaf', desc: 'Entretien d\'espaces verts et extérieurs.' }, ]; const Header = ({ currentPage, navigate }) => { const [scrolled, setScrolled] = React.useState(false); const [mobileOpen, setMobileOpen] = React.useState(false); const [megaOpen, setMegaOpen] = React.useState(false); const [mobileServicesOpen, setMobileServicesOpen] = React.useState(false); const closeTimer = React.useRef(null); React.useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 12); window.addEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll); }, []); // Close mega-menu on Escape React.useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') setMegaOpen(false); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, []); const pages = [ { id: 'home', label: 'Accueil' }, { id: 'about', label: 'À propos' }, { id: 'services', label: 'Services', hasMega: true }, { id: 'faq', label: 'FAQ' }, { id: 'contact', label: 'Contact' }, ]; const openMega = () => { if (closeTimer.current) { clearTimeout(closeTimer.current); closeTimer.current = null; } setMegaOpen(true); }; const scheduleClose = () => { if (closeTimer.current) clearTimeout(closeTimer.current); closeTimer.current = setTimeout(() => setMegaOpen(false), 180); }; const goService = (subId) => { setMegaOpen(false); setMobileOpen(false); setMobileServicesOpen(false); navigate('services'); // Defer hash so navigate's scroll-to-top finishes first setTimeout(() => { const el = document.getElementById('service-' + subId); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 350); }; const navItem = (p) => { const active = currentPage === p.id; if (p.hasMega) { return (
); } return ( ); }; return ( <> {/* TOP UTILITY BAR */}
07 68 53 65 16 contact@smenettoyage.fr Lun – Sam · 8h–19h
Disponible — Devis 24h
{/* MAIN HEADER */}
{/* MEGA-MENU PANEL — full-width drop under the header */}
{/* Sub-services grid */}
Nos prestations
{SUB_SERVICES.map((s, i) => ( ))}
{/* Featured CTA card */}
{/* MOBILE PANEL */} {mobileOpen && (
{pages.map(p => { const active = currentPage === p.id; if (p.hasMega) { return (
{SUB_SERVICES.map(s => ( ))}
); } return ( ); })}
07 68 53 65 16 contact@smenettoyage.fr
)}
); }; // ---------- Footer ---------- const Footer = ({ navigate }) => (

Entreprise de nettoyage professionnel pour particuliers et professionnels. Des espaces propres, sains et accueillants — avec rigueur et confiance.

Navigation

    {['home', 'about', 'services', 'faq', 'contact'].map((p, i) => (
  • ))}

Services

  • Nettoyage d'Entreprises
  • Nettoyage de Bâtiments
  • Nettoyage de Vitres
  • Nettoyage Industriel
  • Jardins & Parcs

Contact

  • 07 68 53 65 16
  • contact@smenettoyage.fr
  • Île-de-France
    & alentours
© 2026 SME Nettoyage — Tous droits réservés. Mentions légales · Politique de confidentialité
); // ---------- Reusable: section header ---------- const SectionHeader = ({ eyebrow, title, intro, center = false, eyebrowGreen = false }) => (
{eyebrow}

{title}

{intro &&

{intro}

}
); // Export to window for cross-file babel script access Object.assign(window, { Icon, SMELogo, Header, Footer, SectionHeader, Reveal, Counter });