Outiref

Code source de l'URL : http://lukalive.fr

<!DOCTYPE html><html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Luka Live | Location Sono & Lumière à Amiens (Somme, Hauts-de-France)</title>
  <meta name="description" content="Luka Live, association basée à Amiens (80), propose la location de matériel de sonorisation et d’éclairage à tarifs associatifs : enceintes, caissons, micros, consoles, projecteurs LED, lyres, structures, avec livraison, installation et accompagnement technique pour concerts, événements, conférences et spectacles." />
  <link rel="canonical" href="https://lukalive.fr/" />
  <link rel="alternate" hreflang="fr-FR" href="https://lukalive.fr/" />
  <link rel="alternate" hreflang="x-default" href="https://lukalive.fr/" />
  <meta name="robots" content="index,follow,max-snippet:-1,max-image-preview:large,max-video-preview:-1" />
  <meta property="og:type" content="website" />
  <meta property="og:url" content="https://lukalive.fr/" />
  <meta property="og:site_name" content="Luka Live" />
  <meta property="og:locale" content="fr_FR" />
  <meta property="og:title" content="Luka Live | Location Sono & Lumière à Amiens (80)" />
  <meta property="og:description" content="Association à Amiens spécialisée en location de sonorisation et d’éclairage à tarifs associatifs, avec livraison, installation et accompagnement technique pour tous vos événements." />
  <meta property="og:image" content="https://lukalive.fr/logo.png" />
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Luka Live | Location Sono & Lumière à Amiens (80)" />
  <meta name="twitter:description" content="Association à Amiens spécialisée en location de sonorisation et d’éclairage à tarifs associatifs, avec livraison, installation et accompagnement technique pour tous vos événements." />
  <meta name="twitter:image" content="https://lukalive.fr/logo.png" />
  <meta property="og:image:secure_url" content="https://lukalive.fr/logo.png" />
  <meta property="og:image:type" content="image/png" />
  <meta property="og:image:alt" content="Luka Live — location sonorisation et éclairage événementiel à Amiens" />
  <meta name="twitter:image:alt" content="Luka Live — location sonorisation et éclairage événementiel à Amiens" />
  <meta name="author" content="Luka Live" />
  <meta name="geo.region" content="FR-80" />
  <meta name="geo.placename" content="Amiens" />
  <meta name="geo.position" content="49.8943;2.2957" />
  <meta name="ICBM" content="49.8943, 2.2957" />
  <meta name="apple-mobile-web-app-title" content="Luka Live" />
  <meta name="application-name" content="Luka Live" />
  <link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
  <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@graph": [
      {
        "@type": [
          "NGO",
          "LocalBusiness"
        ],
        "@id": "https://lukalive.fr/#entity",
        "name": "Luka Live",
        "legalName": "Association LUKA LIVE",
        "url": "https://lukalive.fr/",
        "logo": "https://lukalive.fr/logo.png",
        "image": "https://lukalive.fr/logo.png",
        "description": "Association loi 1901 basée à Amiens, Luka Live propose la location de matériel de sonorisation et d’éclairage à tarifs associatifs, avec livraison, installation et accompagnement technique pour concerts, conférences, spectacles et événements locaux.",
        "email": "lukalive.assos@gmail.com",
        "sameAs": [
          "https://luka-live.assoconnect.com/collect/description/492945-n-membre-membre-artiste-adhesion-annuelle"
        ],
        "contactPoint": [
          {
            "@type": "ContactPoint",
            "contactType": "customer support",
            "email": "lukalive.assos@gmail.com",
            "availableLanguage": ["French"],
            "areaServed": "FR"
          }
        ],
        "geo": {
          "@type": "GeoCoordinates",
          "latitude": 49.8943,
          "longitude": 2.2957
        },
        "address": {
          "@type": "PostalAddress",
          "streetAddress": "6 Place Gambetta",
          "addressLocality": "Amiens",
          "postalCode": "80000",
          "addressRegion": "Somme",
          "addressCountry": "FR"
        },
        "areaServed": [
          {
            "@type": "City",
            "name": "Amiens"
          },
          {
            "@type": "AdministrativeArea",
            "name": "Somme"
          },
          {
            "@type": "AdministrativeArea",
            "name": "Hauts-de-France"
          }
        ],
        "hasOfferCatalog": {
          "@type": "OfferCatalog",
          "name": "Catalogue de location son et lumière",
          "url": "https://lukalive.fr/#catalogue",
          "itemListElement": [
            {
              "@type": "Offer",
              "name": "Location Enceinte Active Yamaha DZR15",
              "description": "Location d’enceinte active Yamaha DZR15 pour sonorisation d’événements, concerts, soirées associatives et prestations live à Amiens et dans la Somme.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Yamaha DZR15",
                "category": "Sonorisation / Enceinte active",
                "brand": {
                  "@type": "Brand",
                  "name": "Yamaha"
                }
              }
            },
            {
              "@type": "Offer",
              "name": "Location Caisson de Basse",
              "description": "Location de caisson de basse puissant pour renforcer les basses fréquences lors de concerts, DJ sets, spectacles et événements culturels.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Caisson de basse (ex. Yamaha DXS18XLF)",
                "category": "Sonorisation / Subwoofer",
                "brand": {
                  "@type": "Brand",
                  "name": "Yamaha"
                }
              }
            },
            {
              "@type": "Offer",
              "name": "Location Console de Mixage",
              "description": "Location de console de mixage analogique et numérique pour gestion des sources micro/instruments et diffusion événementielle.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Console de mixage (ex. Yamaha MG12XU)",
                "category": "Sonorisation / Mixage",
                "brand": {
                  "@type": "Brand",
                  "name": "Yamaha"
                }
              }
            },
            {
              "@type": "Offer",
              "name": "Location Micros Filaires",
              "description": "Location de micros filaires pour voix, animation, conférences et prises de parole sur scène avec rendu clair et fiable.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Micros scène",
                "category": "Sonorisation / Microphonie"
              }
            },
            {
              "@type": "Offer",
              "name": "Location Projecteurs LED",
              "description": "Location de projecteurs LED RGBW/RGBWA+UV pour éclairage de scène, mise en ambiance, valorisation d’artistes et scénographies événementielles.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Projecteurs LED (PAR24 / PAR36)",
                "category": "Éclairage / Projecteur LED"
              }
            },
            {
              "@type": "Offer",
              "name": "Location Lyres et Effets Lumière",
              "description": "Location de lyres motorisées, effets beam et blinder pour créer une ambiance scénique dynamique lors de concerts et événements festifs.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Lyres LED / Blinder / Effets",
                "category": "Éclairage / Effets scéniques"
              }
            },
            {
              "@type": "Offer",
              "name": "Location Machine à Fumée",
              "description": "Location de machine à fumée pour renforcer les faisceaux lumineux et l’impact visuel des effets de scène en conditions événementielles.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Machine à fumée",
                "category": "Effets spéciaux / Fumée"
              }
            },
            {
              "@type": "Offer",
              "name": "Location Structures Événementielles",
              "description": "Location de structures de scène et ponts d’éclairage (traverses, angles, pieds) pour installation sécurisée de dispositifs son et lumière.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Structures aluminium et pieds",
                "category": "Structure scénique"
              }
            },
            {
              "@type": "Offer",
              "name": "Location Câblage Audio et DMX",
              "description": "Location de câblage et adaptateurs (XLR, DMX, accessoires) pour fiabiliser le montage technique de vos prestations.",
              "availability": "https://schema.org/InStock",
              "priceSpecification": {
                "@type": "PriceSpecification",
                "priceCurrency": "EUR",
                "description": "Tarif associatif sur devis"
              },
              "itemOffered": {
                "@type": "Product",
                "name": "Câblage XLR / DMX / Adaptateurs",
                "category": "Accessoires techniques"
              }
            }
          ]
        }
      },
      {
        "@type": "ItemList",
        "@id": "https://lukalive.fr/#catalogue-list",
        "name": "Produits majeurs en location",
        "numberOfItems": 9,
        "itemListOrder": "https://schema.org/ItemListOrderAscending",
        "itemListElement": [
          {
            "@type": "ListItem",
            "position": 1,
            "name": "Enceintes actives (Yamaha DZR15)"
          },
          {
            "@type": "ListItem",
            "position": 2,
            "name": "Caissons de basse"
          },
          {
            "@type": "ListItem",
            "position": 3,
            "name": "Consoles de mixage"
          },
          {
            "@type": "ListItem",
            "position": 4,
            "name": "Micros filaires"
          },
          {
            "@type": "ListItem",
            "position": 5,
            "name": "Projecteurs LED"
          },
          {
            "@type": "ListItem",
            "position": 6,
            "name": "Lyres et effets lumière"
          },
          {
            "@type": "ListItem",
            "position": 7,
            "name": "Machines à fumée"
          },
          {
            "@type": "ListItem",
            "position": 8,
            "name": "Structures de scène et ponts"
          },
          {
            "@type": "ListItem",
            "position": 9,
            "name": "Câblage et adaptateurs (XLR/DMX)"
          }
        ]
      },
      {
        "@type": "WebSite",
        "@id": "https://lukalive.fr/#website",
        "url": "https://lukalive.fr/",
        "name": "Luka Live",
        "inLanguage": "fr-FR",
        "description": "Location de matériel de sonorisation et d'éclairage à Amiens et dans les Hauts-de-France : sono, lumière, structures, livraison et installation.",
        "publisher": { "@id": "https://lukalive.fr/#entity" }
      },
      {
        "@type": "FAQPage",
        "@id": "https://lukalive.fr/#faq-schema",
        "url": "https://lukalive.fr/#faq",
        "mainEntity": [
          {
            "@type": "Question",
            "name": "Peut-on louer uniquement une partie du matériel ?",
            "acceptedAnswer": {
              "@type": "Answer",
              "text": "Oui. Vous pouvez louer un pack complet ou seulement quelques éléments selon vos besoins : son, lumière, accessoires ou renfort technique spécifique."
            }
          },
          {
            "@type": "Question",
            "name": "Proposez-vous la livraison ou l'installation ?",
            "acceptedAnswer": {
              "@type": "Answer",
              "text": "Oui, selon la nature de l'événement et la zone géographique. Vous pouvez l'indiquer dans votre demande de devis pour recevoir une proposition adaptée."
            }
          },
          {
            "@type": "Question",
            "name": "Les tarifs affichés sont-ils fixes ?",
            "acceptedAnswer": {
              "@type": "Answer",
              "text": "Ce sont des tarifs de base indicatifs. Le devis final dépend du volume de matériel, de la durée, de la logistique et des éventuelles prestations complémentaires."
            }
          },
          {
            "@type": "Question",
            "name": "Travaillez-vous avec les associations et les particuliers ?",
            "acceptedAnswer": {
              "@type": "Answer",
              "text": "Oui, en priorité avec les associations, artistes et structures culturelles, mais aussi selon les projets et la faisabilité technique."
            }
          }
        ]
      }
    ]
  }
  </script>
  <link rel="shortcut icon" href="favicon_io/favicon.ico" type="image/x-icon" />
  <link rel="icon" type="image/png" sizes="16x16" href="favicon_io/favicon-16x16.png" />
  <link rel="icon" type="image/png" sizes="32x32" href="favicon_io/favicon-32x32.png" />
  <link rel="icon" type="image/png" sizes="192x192" href="favicon_io/android-chrome-192x192.png" />
  <link rel="icon" type="image/png" sizes="512x512" href="favicon_io/android-chrome-512x512.png" />
  <link rel="apple-touch-icon" sizes="180x180" href="favicon_io/apple-touch-icon.png" />
  <link rel="manifest" href="site.webmanifest" />
  <meta name="theme-color" content="#11151d" />
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/dmuy/MDTimePicker@v2.0.3/dist/mdtimepicker.min.css" />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css" />
  <link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;700&family=Manrope:wght@400;800&display=swap" rel="stylesheet" />
  <link rel="stylesheet" href="style.css" />
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@emailjs/browser@4/dist/email.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/dmuy/MDTimePicker@v2.0.3/dist/mdtimepicker.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</head>
<body>
  <div id="loading-screen" aria-label="Bienvenue sur notre nouveau site !">
    <div class="splash-content">
      <img src="logo-blanc.png" alt="Logo LUKA LIVE" />
      <p class="splash-title">LUKA LIVE</p>
      <p class="splash-subtitle">Location Son & Lumière</p>
    </div>
  </div>
  <nav id="site-nav">
    <div class="container nav-inner">
      <a href="#accueil" class="nav-brand route-link" data-page="accueil" aria-label="Retour à  l'accueil">
        <img src="logo.png" alt="Logo LUKA LIVE" />
        <div>
          <div class="nav-brand-title">LUKA LIVE</div>
          <div class="nav-brand-sub">Son & Lumière</div>
        </div>
  </a><ul class="nav-links">
    <li><a href="#accueil" class="route-link active" data-page="accueil">Accueil</a></li>
    <li><a href="#catalogue" class="route-link" data-page="catalogue">Catalogue</a></li>
    <li><a href="https://luka-live.assoconnect.com/collect/description/492945-n-membre-membre-artiste-adhesion-annuelle" target="_blank" rel="noopener noreferrer">Devenir membre</a></li>
    <li><a href="#services" class="route-link" data-page="services">Services</a></li>
    <li><a href="#association" class="route-link" data-page="association">Association</a></li>
    <li><a href="#faq" class="route-link" data-page="faq">FAQ</a></li>
  </ul>
  <button type="button" class="nav-cta route-link" id="nav-quote-button" data-page="devis">Demander mon devis</button>
  <button type="button" class="mobile-toggle" id="mobile-toggle" aria-label="Ouvrir le menu">
    <span class="mobile-toggle-pulse"></span>
    <span></span>
  </button>
  <div class="nav-menu" id="nav-menu">
    <div class="menu-watermark">
      <img src="logo.png" alt="LUKA LIVE" class="menu-watermark-logo">
    </div>
    <ul>
      <li><a href="#accueil" class="route-link" data-page="accueil">Accueil</a></li>
      <li><button type="button" class="route-link menu-primary" data-page="devis" id="mobile-quote-button">Demander mon devis</button></li>
      <li><a href="#catalogue" class="route-link" data-page="catalogue">Catalogue</a></li>
      <li><a href="https://luka-live.assoconnect.com/collect/description/492945-n-membre-membre-artiste-adhesion-annuelle" target="_blank" rel="noopener noreferrer">Devenir membre</a></li>
      <li><a href="#services" class="route-link" data-page="services">Services</a></li>
      <li><a href="#association" class="route-link" data-page="association">Association</a></li>
      <li><a href="#faq" class="route-link" data-page="faq">FAQ</a></li>
    </ul>
  </div>
</div>
  </nav> <main>
    <section class="page active" id="page-accueil" data-page="accueil">
      <div class="hero grid-overlay">
        <div class="container hero-layout">
          <div class="hero-content">
          <div class="hero-badge"><span class="hero-badge-dot"></span>AMIENS • SOMME • 80</div>
            <h1>Location sono et éclairage à Amiens</h1>
            <p class="hero-sub">Luka Live accompagne vos concerts, événements, conférences, spectacles et tout type de rassemblements avec du matériel son et lumière professionnel, comprenant la livraison, l'installation et une gestion intégrée dans les tarifs.</p>
            <div class="hero-actions">
              <button type="button" class="btn-primary route-link" data-page="catalogue">VOIR LE CATALOGUE</button>
              <button type="button" class="btn-secondary route-link" data-page="devis">OBTENIR UN DEVIS</button>
            </div>
            <div class="hero-note">
              <span>Installation comprise</span>
              <span>Location courte ou longue durée</span>
            </div>
          </div><div class="hero-visual brand-section">
        <div class="orb"></div>
        <div class="hero-panel">
          <img src="logo.png" alt="Luka Live — logo, location sono et éclairage à Amiens" class="hero-brand-image" width="2338" height="2338" fetchpriority="high" decoding="async" loading="eager" onerror="this.classList.add('is-error-hidden'); const fb=this.nextElementSibling; if(fb) fb.classList.add('is-visible');" />
          <div class="hero-brand-fallback">LUKA LIVE</div>
        </div>
      </div>
    </div>
  </div>
  <div class="marquee-section">
    <div class="marquee-track">
      <span class="marquee-item">Son Professionnel <span class="marquee-dot"></span></span>
      <span class="marquee-item">éclairage Scénique <span class="marquee-dot"></span></span>
      <span class="marquee-item">Tarifs Associatifs <span class="marquee-dot"></span></span>
      <span class="marquee-item">Livraison Possible <span class="marquee-dot"></span></span>
      <span class="marquee-item">Installation Sur Demande <span class="marquee-dot"></span></span>
      <span class="marquee-item">Concerts • Conférences • Spectacles <span class="marquee-dot"></span></span>
      <span class="marquee-item">Son Professionnel <span class="marquee-dot"></span></span>
      <span class="marquee-item">éclairage Scénique <span class="marquee-dot"></span></span>
      <span class="marquee-item">Tarifs Associatifs <span class="marquee-dot"></span></span>
      <span class="marquee-item">Livraison Possible <span class="marquee-dot"></span></span>
      <span class="marquee-item">Installation Sur Demande <span class="marquee-dot"></span></span>
      <span class="marquee-item">Concerts • Conférences • Spectacles <span class="marquee-dot"></span></span>
    </div>
  </div>
</section>
<section class="page" id="page-services" data-page="services">
  <div class="page-section">
    <div class="container">
      <div class="section-header reveal">
        <div>
          <h2>Des services clairs,<br>pensés pour l'événementiel</h2>
        </div>
        <p class="section-text services-intro">LUKA LIVE accompagne vos concerts, spectacles et événements culturels en levant les freins matériels. De la simple location à la formule complète, nous proposons un matériel fiable et des tarifs associatifs pour soutenir la création musicale dans la région d'Amiens.</p>
      </div>
      <div class="services-grid">
        <article class="service-card reveal">
          <div class="service-number">01 / 03</div>
          <svg class="service-icon" viewbox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="6" y="6" width="24" height="36" rx="2" stroke="white" stroke-width="2" fill="none"/><circle cx="18" cy="30" r="8" stroke="white" stroke-width="2" fill="none"/><circle cx="18" cy="30" r="3" fill="white" opacity="0.6"/><circle cx="18" cy="16" r="5" stroke="white" stroke-width="2" fill="none"/><line x1="32" y1="12" x2="44" y2="12" stroke="white" stroke-width="2"/><line x1="32" y1="20" x2="44" y2="20" stroke="white" stroke-width="2"/><line x1="32" y1="28" x2="44" y2="28" stroke="white" stroke-width="2"/></svg>
          <h3>Location Son</h3>
          <p>Enceintes actives, caissons de basse, tables de mixage et micros : tout le nécessaire pour assurer une diffusion sonore puissante et claire, adaptée aux jauges intimes comme aux grandes salles.</p>
        </article>
        <article class="service-card reveal">
          <div class="service-number">02 / 03</div>
          <svg class="service-icon" viewbox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="24" cy="24" r="10" stroke="white" stroke-width="2" fill="none"/><line x1="24" y1="4" x2="24" y2="10" stroke="white" stroke-width="2"/><line x1="24" y1="38" x2="24" y2="44" stroke="white" stroke-width="2"/><line x1="4" y1="24" x2="10" y2="24" stroke="white" stroke-width="2"/><line x1="38" y1="24" x2="44" y2="24" stroke="white" stroke-width="2"/><line x1="9" y1="9" x2="14" y2="14" stroke="white" stroke-width="2"/><line x1="34" y1="34" x2="39" y2="39" stroke="white" stroke-width="2"/><line x1="39" y1="9" x2="34" y2="14" stroke="white" stroke-width="2"/><line x1="14" y1="34" x2="9" y2="39" stroke="white" stroke-width="2"/><circle cx="24" cy="24" r="4" fill="white" opacity="0.5"/></svg>
          <h3>Location Lumière</h3>
          <p>PAR LED, lyres (moving heads) et effets visuels pour construire une véritable ambiance scénique, mettre en valeur les artistes et dynamiser votre événement.</p>
        </article>
        <article class="service-card reveal">
          <div class="service-number">03 / 03</div>
          <svg class="service-icon" viewbox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="6" y="18" width="36" height="24" rx="2" stroke="white" stroke-width="2" fill="none"/><path d="M14 18V14C14 9.58 17.58 6 22 6H26C30.42 6 34 9.58 34 14V18" stroke="white" stroke-width="2"/><circle cx="24" cy="30" r="4" stroke="white" stroke-width="2" fill="none"/><line x1="24" y1="34" x2="24" y2="38" stroke="white" stroke-width="2"/></svg>
          <h3>Pack événement</h3>
          <p>Une solution clé en main incluant la préparation, la livraison, l'installation sur site et la gestion de l'événement avec conseil technique pour permettre une bonne organisation de votre événement.</p>
        </article> </div>
    </div>
  </div>
</section>
<section class="page" id="page-catalogue" data-page="catalogue">
  <div class="page-section">
    <div class="container">
      <div class="section-header reveal">
        <div>
          <h2>Votre matériel de sonorisation dans la Somme (80)</h2>
        </div>
        <p class="section-text">Cliquez sur <strong class="text-strong">Ajouter au devis</strong> pour construire votre demande sans quitter la page.</p>
      </div>
      <div class="catalogue-top">
        <div class="catalogue-filters">
          <button type="button" class="filter-btn active" data-filter="tous">Tout</button>
          <button type="button" class="filter-btn" data-filter="son">Son</button>
          <button type="button" class="filter-btn" data-filter="lumiere">Lumière</button>
          <button type="button" class="filter-btn" data-filter="structure">Structures</button>
          <button type="button" class="filter-btn" data-filter="accessoires">Accessoires</button>
        </div>
        <label class="catalogue-search" for="catalogue-search">
          <svg class="catalogue-search-icon" viewbox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="1.8"/><path d="M20 20L16.65 16.65" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
          <input type="search" id="catalogue-search" class="catalogue-search-input" placeholder="Rechercher un produit, une marque, une fonction..." autocomplete="off" />
        </label>
        <div class="catalogue-view-switch" aria-label="Mode d'affichage du catalogue" role="toolbar">
          <button type="button" class="catalogue-view-btn" data-catalogue-view="duo" aria-label="Affichage deux colonnes">
            <svg viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="4" y="5" width="6.5" height="14" rx="1.5" stroke-width="1.8"/><rect x="13.5" y="5" width="6.5" height="14" rx="1.5" stroke-width="1.8"/></svg>
          </button>
          <button type="button" class="catalogue-view-btn active" data-catalogue-view="grid" aria-label="Affichage grille compacte">
            <svg viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="4" y="4" width="6" height="6" rx="1.4" stroke-width="1.8"/><rect x="14" y="4" width="6" height="6" rx="1.4" stroke-width="1.8"/><rect x="4" y="14" width="6" height="6" rx="1.4" stroke-width="1.8"/><rect x="14" y="14" width="6" height="6" rx="1.4" stroke-width="1.8"/></svg>
          </button>
          <button type="button" class="catalogue-view-btn" data-catalogue-view="list" aria-label="Affichage liste pleine largeur">
            <svg viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="4" y="5" width="16" height="3.2" rx="1.6" stroke-width="1.5"/><rect x="4" y="10.4" width="16" height="3.2" rx="1.6" stroke-width="1.5"/><rect x="4" y="15.8" width="16" height="3.2" rx="1.6" stroke-width="1.5"/></svg>
          </button>
        </div>
        <div class="page-actions">
          <button type="button" class="btn-ghost route-link" data-page="devis">Voir mon devis</button>
          <button type="button" class="btn-secondary" id="clear-quote">Vider la sélection</button>
        </div>
      </div>
      <div class="catalogue-grid" id="catalogue-grid" data-view="grid">
<article class="catalogue-card reveal" data-category="son" data-product="son-dzr15" data-images="FOLDER-IMAGES/SON/DZR15/enceinte1.webp|FOLDER-IMAGES/SON/DZR15/enceinte2.webp|FOLDER-IMAGES/SON/DZR15/enceinte3.webp|FOLDER-IMAGES/SON/DZR15/enceinte4.webp|FOLDER-IMAGES/SON/DZR15/enceinte5.webp|FOLDER-IMAGES/SON/DZR15/enceinte6.webp|FOLDER-IMAGES/SON/DZR15/enceinte7.webp|FOLDER-IMAGES/SON/DZR15/enceinte8.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/SON/DZR15/enceinte1.webp" alt="Enceinte Yamaha 2000W" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">YAMAHA DZR15</p>
            <h3>Enceinte Yamaha 2000W</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="son-dzr15">
                  <button type="button" class="qty-btn qty-minus" data-product="son-dzr15" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="son-dzr15" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="son-dzr15" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="son-dzr15" data-item="Enceinte Yamaha 2000W" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="son-dzr15">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="son" data-product="son-dxs18xlf" data-images="FOLDER-IMAGES/SON/DXS18xlf/caisson_de_basse1.webp|FOLDER-IMAGES/SON/DXS18xlf/caisson_de_basse2.webp|FOLDER-IMAGES/SON/DXS18xlf/caisson_de_basse3.webp|FOLDER-IMAGES/SON/DXS18xlf/caisson_de_basse4.webp|FOLDER-IMAGES/SON/DXS18xlf/caisson_de_basse5.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/SON/DXS18xlf/caisson_de_basse1.webp" alt="Caisson de basse 1600W" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">YAMAHA DXS18xlf</p>
            <h3>Caisson de basse 1600W</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="son-dxs18xlf">
                  <button type="button" class="qty-btn qty-minus" data-product="son-dxs18xlf" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="son-dxs18xlf" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="son-dxs18xlf" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="son-dxs18xlf" data-item="Caisson de basse 1600W" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="son-dxs18xlf">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="son" data-product="son-mg12xu" data-images="FOLDER-IMAGES/SON/MG12XU/table_mixage1.webp|FOLDER-IMAGES/SON/MG12XU/table_mixage2.webp|FOLDER-IMAGES/SON/MG12XU/table_mixage3.webp|FOLDER-IMAGES/SON/MG12XU/table_mixage4.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/SON/MG12XU/table_mixage1.webp" alt="Console de mixage" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">YAMAHA MG12XU</p>
            <h3>Console de mixage</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="son-mg12xu">
                  <button type="button" class="qty-btn qty-minus" data-product="son-mg12xu" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="son-mg12xu" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="son-mg12xu" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="son-mg12xu" data-item="Console de mixage" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="son-mg12xu">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="son" data-product="son-pieds_enceintes" data-images="FOLDER-IMAGES/SON/pieds_enceintes/pieds1.webp|FOLDER-IMAGES/SON/pieds_enceintes/pieds2.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/SON/pieds_enceintes/pieds1.webp" alt="Pieds d&#x27;enceinte" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">K&M 21449</p>
            <h3>Pieds d&#x27;enceinte</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="son-pieds_enceintes">
                  <button type="button" class="qty-btn qty-minus" data-product="son-pieds_enceintes" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="son-pieds_enceintes" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="son-pieds_enceintes" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="son-pieds_enceintes" data-item="Pieds d&#x27;enceinte" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="son-pieds_enceintes">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="son" data-product="son-micro1" data-images="FOLDER-IMAGES/SON/Micro1/micro1_1.webp|FOLDER-IMAGES/SON/Micro1/micro1_2.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/SON/Micro1/micro1_1.webp" alt="Micro Prodipe" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">Prodipe TT1-LANEN</p>
            <h3>Micro Prodipe</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="son-micro1">
                  <button type="button" class="qty-btn qty-minus" data-product="son-micro1" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="son-micro1" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="son-micro1" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="son-micro1" data-item="Micro Prodipe" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="son-micro1">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="son" data-product="son-micro2" data-images="FOLDER-IMAGES/SON/Micro2/micro2_1.webp|FOLDER-IMAGES/SON/Micro2/micro2_2.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/SON/Micro2/micro2_1.webp" alt="Micro Stagg" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">Stagg SDM50</p>
            <h3>Micro Stagg</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="son-micro2">
                  <button type="button" class="qty-btn qty-minus" data-product="son-micro2" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="son-micro2" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="son-micro2" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="son-micro2" data-item="Micro Stagg" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="son-micro2">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="lumiere" data-product="lumiere-lyre1" data-images="FOLDER-IMAGES/lumiere/lyre1/Lyre1.webp|FOLDER-IMAGES/lumiere/lyre1/Lyre1.webp|FOLDER-IMAGES/lumiere/lyre1/lyre2.webp|FOLDER-IMAGES/lumiere/lyre1/Lyre3.webp|FOLDER-IMAGES/lumiere/lyre1/Lyre4.webp|FOLDER-IMAGES/lumiere/lyre1/Lyre5.webp|FOLDER-IMAGES/lumiere/lyre1/Lyre6.webp|FOLDER-IMAGES/lumiere/lyre1/Lyre7.webp|FOLDER-IMAGES/lumiere/lyre1/Lyre8.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/lumiere/lyre1/Lyre1.webp" alt="Lyre 760W RGBW" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">LYRE 760W</p>
            <h3>Lyre 760W RGBW</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="lumiere-lyre1">
                  <button type="button" class="qty-btn qty-minus" data-product="lumiere-lyre1" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="lumiere-lyre1" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="lumiere-lyre1" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="lumiere-lyre1" data-item="Lyre 760W RGBW" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="lumiere-lyre1">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="lumiere" data-product="lumiere-lyre2" data-images="FOLDER-IMAGES/lumiere/Lyre2/robot1.webp|FOLDER-IMAGES/lumiere/Lyre2/robot2.webp|FOLDER-IMAGES/lumiere/Lyre2/robot3.webp|FOLDER-IMAGES/lumiere/Lyre2/robot4.webp|FOLDER-IMAGES/lumiere/Lyre2/robot5.webp|FOLDER-IMAGES/lumiere/Lyre2/robot6.webp|FOLDER-IMAGES/lumiere/Lyre2/robot7.webp|FOLDER-IMAGES/lumiere/Lyre2/robot8.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/lumiere/Lyre2/robot1.webp" alt="Lyre LED 7x40W RGBW" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">LYRE LED</p>
            <h3>Lyre LED 7x40W RGBW</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="lumiere-lyre2">
                  <button type="button" class="qty-btn qty-minus" data-product="lumiere-lyre2" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="lumiere-lyre2" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="lumiere-lyre2" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="lumiere-lyre2" data-item="Lyre LED 7x40W RGBW" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="lumiere-lyre2">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="lumiere" data-product="lumiere-blinder" data-images="FOLDER-IMAGES/lumiere/BLINDER/blinder1.webp|FOLDER-IMAGES/lumiere/BLINDER/blinder2.webp|FOLDER-IMAGES/lumiere/BLINDER/blinder3.webp|FOLDER-IMAGES/lumiere/BLINDER/blinder4.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/lumiere/BLINDER/blinder1.webp" alt="Blinder 200W" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">BLINDER LED</p>
            <h3>Blinder 200W</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="lumiere-blinder">
                  <button type="button" class="qty-btn qty-minus" data-product="lumiere-blinder" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="lumiere-blinder" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="lumiere-blinder" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="lumiere-blinder" data-item="Blinder 200W" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="lumiere-blinder">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="lumiere" data-product="lumiere-bar" data-images="FOLDER-IMAGES/lumiere/bar/bar1.webp|FOLDER-IMAGES/lumiere/bar/bar2.webp|FOLDER-IMAGES/lumiere/bar/bar3.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/lumiere/bar/bar1.webp" alt="Barre Motorisée Moving Beam 250W" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">BARRE MOTORISÉE</p>
            <h3>Barre Motorisée Moving Beam 250W</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="lumiere-bar">
                  <button type="button" class="qty-btn qty-minus" data-product="lumiere-bar" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="lumiere-bar" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="lumiere-bar" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="lumiere-bar" data-item="Barre Motorisée Moving Beam 250W" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="lumiere-bar">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="lumiere" data-product="lumiere-par36" data-images="FOLDER-IMAGES/lumiere/PAR36/projo1.webp|FOLDER-IMAGES/lumiere/PAR36/projo2.webp|FOLDER-IMAGES/lumiere/PAR36/projo3.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/lumiere/PAR36/projo1.webp" alt="Projecteur LED 200W RGBWA+UV" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">PROJECTEUR LED 200W</p>
            <h3>Projecteur LED 200W RGBWA+UV</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="lumiere-par36">
                  <button type="button" class="qty-btn qty-minus" data-product="lumiere-par36" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="lumiere-par36" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="lumiere-par36" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="lumiere-par36" data-item="Projecteur LED 200W RGBWA+UV" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="lumiere-par36">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="lumiere" data-product="lumiere-par24" data-images="FOLDER-IMAGES/lumiere/PAR24/par1.png|FOLDER-IMAGES/lumiere/PAR24/par2.png|FOLDER-IMAGES/lumiere/PAR24/par3.png">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/lumiere/PAR24/par1.png" alt="Projecteur LED 80W RGBW" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">PROJECTEUR LED 80W</p>
            <h3>Projecteur LED 80W RGBW</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="lumiere-par24">
                  <button type="button" class="qty-btn qty-minus" data-product="lumiere-par24" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="lumiere-par24" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="lumiere-par24" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="lumiere-par24" data-item="Projecteur LED 80W RGBW" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="lumiere-par24">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="structure" data-product="structure-strcuture1" data-images="FOLDER-IMAGES/STRUCTURE/STRCUTURE1/structure.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/STRUCTURE/STRCUTURE1/structure.webp" alt="Ensemble Pont d'éclairage 3m" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">PONT ÉCLAIRAGE</p>
            <h3>Ensemble Pont d'éclairage 3m</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="structure-strcuture1">
                  <button type="button" class="qty-btn qty-minus" data-product="structure-strcuture1" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="structure-strcuture1" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="structure-strcuture1" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="structure-strcuture1" data-item="Ensemble Pont d'éclairage 3m" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="structure-strcuture1">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="structure" data-product="structure-structure2" data-images="FOLDER-IMAGES/STRUCTURE/STRUCTURE2/structure2.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/STRUCTURE/STRUCTURE2/structure2.webp" alt="Traverse de pont (1,5 m)" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">TRAVERSE</p>
            <h3>Traverse de pont (1,5 m)</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="structure-structure2">
                  <button type="button" class="qty-btn qty-minus" data-product="structure-structure2" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="structure-structure2" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="structure-structure2" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="structure-structure2" data-item="Traverse de pont (1,5 m)" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="structure-structure2">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="structure" data-product="structure-structure3" data-images="FOLDER-IMAGES/STRUCTURE/STRUCTURE3/structure3.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/STRUCTURE/STRUCTURE3/structure3.webp" alt="Traverse d'éclairage - Angle 90°" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">ANGLE</p>
            <h3>Traverse d'éclairage - Angle 90°</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="structure-structure3">
                  <button type="button" class="qty-btn qty-minus" data-product="structure-structure3" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="structure-structure3" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="structure-structure3" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="structure-structure3" data-item="Traverse d'éclairage - Angle 90°" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="structure-structure3">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="structure" data-product="structure-structure4" data-images="FOLDER-IMAGES/STRUCTURE/STRUCTURE4/structure4.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/STRUCTURE/STRUCTURE4/structure4.webp" alt="Pied d&#x27;éclairage / structure" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag">PIED DE STRUCTURE</p>
            <h3>Pied d&#x27;éclairage / structure</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="structure-structure4">
                  <button type="button" class="qty-btn qty-minus" data-product="structure-structure4" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="structure-structure4" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="structure-structure4" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="structure-structure4" data-item="Pied d&#x27;éclairage / structure" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="structure-structure4">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="accessoires" data-product="son-cable_xlr" data-images="FOLDER-IMAGES/SON/cable_xlr/xlr.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/SON/cable_xlr/xlr.webp" alt="Câblage et Adaptateurs" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag"></p>
            <h3>Câblage et Adaptateurs</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="son-cable_xlr">
                  <button type="button" class="qty-btn qty-minus" data-product="son-cable_xlr" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="son-cable_xlr" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="son-cable_xlr" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="son-cable_xlr" data-item="Câblage et Adaptateurs" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="son-cable_xlr">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
<article class="catalogue-card reveal" data-category="accessoires" data-product="lumiere-cable_dmx" data-images="FOLDER-IMAGES/lumiere/cable_dmx/dmx.webp">
          <div class="catalogue-card-img"><img src="FOLDER-IMAGES/lumiere/cable_dmx/dmx.webp" alt="Câblage et Adaptateurs Lumière / DMX" loading="lazy" /></div>
          <div class="catalogue-card-body">
            <p class="catalogue-tag"></p>
            <h3>Câblage et Adaptateurs Lumière / DMX</h3>
            <div class="catalogue-card-footer">
              <div class="catalogue-card-meta">
                <div class="catalogue-price">Sur Devis</div>
                <div class="qty-picker" data-product="lumiere-cable_dmx">
                  <button type="button" class="qty-btn qty-minus" data-product="lumiere-cable_dmx" data-context="catalogue">-</button>
                  <span class="qty-value" data-product="lumiere-cable_dmx" data-context="catalogue">1</span>
                  <button type="button" class="qty-btn qty-plus" data-product="lumiere-cable_dmx" data-context="catalogue">+</button>
                </div>
              </div>
              <div class="catalogue-card-actions">
                <button type="button" class="btn-reserve add-to-quote" data-product="lumiere-cable_dmx" data-item="Câblage et Adaptateurs Lumière / DMX" data-price="Sur Devis">Ajouter au devis</button>
                <button type="button" class="btn-detail product-detail" data-product="lumiere-cable_dmx">Détails du produit</button>
              </div>
            </div>
          </div>
        </article>
      </div>
      <div class="catalogue-results-empty" id="catalogue-results-empty">Aucun produit ne correspond à votre recherche. Essayez un autre mot-clé ou changez de catégorie.</div>
    </div>
  </div>
</section>
<section class="page" id="page-produit" data-page="produit">
  <div class="page-section">
    <div class="container">
      <div class="section-header reveal">
        <div>
          <h2 id="product-page-title">Produit</h2>
        </div>
        <div class="product-actions">
          <button type="button" class="product-back route-link" data-page="catalogue">Retour au catalogue</button>
          <div class="qty-picker" data-product="">
            <button type="button" class="qty-btn qty-minus" id="product-page-qty-minus" data-context="product">-</button>
            <span class="qty-value" id="product-page-qty-value" data-context="product">1</span>
            <button type="button" class="qty-btn qty-plus" id="product-page-qty-plus" data-context="product">+</button>
          </div>
          <button type="button" class="btn-primary add-to-quote" id="product-page-add" data-product="" data-item="" data-price="">Ajouter au devis</button>
        </div>
      </div>
      <div class="product-shell">
        <div class="product-gallery-panel">
          <div class="product-images-grid" id="product-images-collage"></div>
          <div class="product-gallery-thumbs" id="product-gallery-thumbs"></div>
        </div>
        <div class="product-info-card">
          <h3 id="product-page-name" class="product-page-name-heading">Nom du produit</h3>
          <p class="product-reference" id="product-page-reference"><strong>Référence</strong><span>-</span></p>
          <p class="product-description" id="product-page-description"></p>
          <div class="product-meta-grid">
            <div class="product-meta-item">
              <strong>Tarif indicatif</strong>
              <span id="product-page-price">-</span>
            </div>
</div>
          <div class="product-detail-block">
            <h4>Détails</h4>
            <ul class="product-detail-list" id="product-page-details"></ul>
          </div>
                  </div> </div>
    </div>
  </div>
</section>
<section class="page" id="page-association" data-page="association">
  <div class="page-section">
    <div class="container about-grid">
      <div class="about-text reveal">
        <h2 class="about-title">L'association<br>LUKA LIVE</h2>
        <p class="section-text">Fondée en 2024 à Amiens, Luka Live est une association loi 1901 dédiée à la promotion d'événements musicaux locaux. Notre mission est simple : au-delà de la location de matériel à bas coût, avec installation et livraison comprises, nous organisons de grands rassemblements artistiques dans la région.</p>
        <p>Notre mission est simple : offrir un espace d’échange collaboratif où les artistes, groupes et passionnés peuvent se rencontrer, et surtout, se produire dans des conditions professionnelles sans que le budget ne soit un obstacle.</p>
        <p>Au-delà de la location de matériel à bas coût, nous organisons de grands rassemblements artistiques dont les bénéfices soutiennent des causes nobles, comme l'aide aux enfants malades.</p>
        <div class="page-actions page-actions-mt-15">
          <button type="button" class="btn-primary route-link" data-page="devis">Parler de votre projet</button>
        </div>
      </div>
      <div class="about-panel reveal">
        <div class="about-visual"><svg viewbox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="100" cy="100" r="72" stroke="white" stroke-width="2" opacity="0.25"/><circle cx="100" cy="100" r="44" stroke="white" stroke-width="2" opacity="0.45"/><circle cx="100" cy="100" r="12" fill="white" opacity="0.7"/><path d="M100 28V0" stroke="white" stroke-width="2" opacity="0.35"/><path d="M100 200v-28" stroke="white" stroke-width="2" opacity="0.35"/><path d="M28 100H0" stroke="white" stroke-width="2" opacity="0.35"/><path d="M200 100h-28" stroke="white" stroke-width="2" opacity="0.35"/></svg></div>
        <div class="about-values">
          <div class="about-value"><div class="about-value-title">Accessibilité</div><p>Des tarifs associatifs très réduits pour aider concrètement les projets culturels et les particuliers à exister.</p></div>
          <div class="about-value"><div class="about-value-title">Qualité</div><p>Un parc de matériel de sonorisation et d’éclairage professionnel, soigneusement entretenu et préparé.</p></div>
          <div class="about-value"><div class="about-value-title">Accompagnement</div><p>Un soutien humain, des conseils techniques et une aide à  la mise en réseau pour les artistes locaux.</p></div>
          <div class="about-value"><div class="about-value-title">Solidarité</div><p>Des événements et une dynamique d'entraide entre les musiciens, bénévoles et organisateurs.</p></div>
        </div> </div>
    </div>
  </div>
</section>
<section class="page" id="page-faq" data-page="faq">
  <div class="page-section">
    <div class="container">
      <div class="section-header reveal">
        <div>
          <div class="section-eyebrow">Questions fréquentes</div>
          <h2>FAQ</h2>
        </div>
      </div>
      <div class="faq-list">
        <div class="faq-item reveal">
          <button class="faq-question" type="button"><span>Peut-on louer uniquement une partie du matériel ?</span><span>+</span></button>
          <div class="faq-answer-wrap"><div class="faq-answer">Oui. Vous pouvez louer un pack complet ou seulement quelques éléments selon vos besoins : son, lumière, accessoires ou renfort technique spécifique.</div></div>
        </div>
        <div class="faq-item reveal">
          <button class="faq-question" type="button"><span>Proposez-vous la livraison ou l'installation ? </span><span>+</span></button>
          <div class="faq-answer-wrap"><div class="faq-answer">Oui, selon la nature de l'événement et la zone géographique. Vous pouvez l'indiquer dans votre demande de devis pour recevoir une proposition adaptée.</div></div>
        </div>
        <div class="faq-item reveal">
          <button class="faq-question" type="button"><span>Les tarifs affichés sont-ils fixes ?</span><span>+</span></button>
          <div class="faq-answer-wrap"><div class="faq-answer">Ce sont des tarifs de base indicatifs. Le devis final dépend du volume de matériel, de la durée, de la logistique et des éventuelles prestations complémentaires.</div></div>
        </div>
        <div class="faq-item reveal">
          <button class="faq-question" type="button"><span>Travaillez-vous avec les associations et les particuliers ?</span><span>+</span></button>
          <div class="faq-answer-wrap"><div class="faq-answer">Oui, en priorité avec les associations, artistes et structures culturelles, mais aussi selon les projets et la faisabilité technique.</div></div>
        </div>
      </div>
      <div class="form-card faq-support-card reveal">
        <div class="section-header section-header-mb-11">
          <div>
            <div class="section-eyebrow">Une autre question ?</div>
            <h3>Envoyez-nous un message</h3>
          </div>
        </div>
        <p class="quote-builder-note faq-support-copy">Si votre réponse n'est pas ici, écrivez-nous sans hésiter. Nous vous répondrons à <strong class="text-strong">lukalive.assos@gmail.com</strong>.</p>
        <form class="contact-form" id="faq-contact-form">
          <div class="form-group">
            <label for="faq-email">Votre email</label>
            <input type="email" id="faq-email" name="faq_email" placeholder="contact@email.fr" required />
          </div>
          <div class="form-group">
            <label for="faq-message">Votre message</label>
            <textarea id="faq-message" name="faq_message" placeholder="Écrivez votre question ici..." required></textarea>
          </div>
          <div class="inline-flex-wrap-row">
            <button type="submit" class="btn-submit" id="faq-submit">Envoyer ma question</button>
            <div class="form-status" id="faq-form-status"></div>
          </div>
        </form> </div>
    </div>
  </div>
</section>
<section class="page" id="page-devis" data-page="devis">
  <div class="page-hero">
    <div class="container devis-grid">
      <aside class="quote-summary-card">
        <div class="quote-summary-head">
          <strong>Mon devis en cours</strong>
          <span class="quote-count" id="quote-count">0 article</span>
        </div>
        <p class="quote-builder-note quote-builder-note-emphasis">
          Parcourez notre catalogue pour faire votre sélection de matériel. Retrouvez-la ensuite ici pour finaliser et envoyer votre demande.
        </p>
        <div class="page-actions page-actions-mt-12-mb-1">
          <button type="button" class="btn-primary route-link" data-page="catalogue">Voir le catalogue</button>
          <button type="button" id="clear-quote-top" class="btn-secondary">Vider la sélection</button>
        </div>
        <div class="quote-list" id="quote-list-main">
          <div class="quote-empty" id="quote-empty-main">Aucun matériel sélectionné pour le moment.</div>
        </div>
      </aside>
      <div class="form-card">
        <div class="section-header section-header-mb-2">
          <div>
          <h2 class="devis-title">Finaliser le devis</h2>
          </div>
        </div>
        <form class="contact-form" id="contact-form">
          <div class="form-row">
            <div class="form-group">
              <label for="nom">Nom / Organisation</label>
              <input type="text" id="nom" name="nom" placeholder="Jean Dupont" required />
            </div>
            <div class="form-group">
              <label for="email">Email</label>
              <input type="email" id="email" name="email" placeholder="contact@email.fr" required />
            </div>
          </div>
          <div class="form-row">
            <div class="form-group">
              <label for="telephone">Téléphone</label>
              <input type="tel" id="telephone" name="telephone" placeholder="06 XX XX XX XX" />
            </div>
            <div class="form-group">
              <label for="ville">Ville / lieu</label>
              <div class="autocomplete-wrapper">
                <input type="text" id="ville" name="ville" placeholder="Ex : 12 rue des Prés, 80330 Cagny" autocomplete="off" />
                <ul id="ville-suggestions" class="suggestions-list"></ul>
              </div>
            </div>
          </div>
          <div class="form-row">
            <div class="form-group">
              <label for="jauge">Jauge estimée</label>
              <div class="slider-container">
                <div class="slider-value-display"><span id="jauge-val">25</span> personnes</div>
                <input type="range" id="jauge" name="jauge" min="25" max="2000" step="10" value="25" class="jauge-slider" />
              </div>
            </div>
            <div class="form-group">
              <label for="type">Type d'événement</label>
              <select id="type" name="type">
                <option value="">Sélectionner</option>
                <option value="prive">événement privé / fêtes</option>
                <option value="concert">Concert</option>
                <option value="conference">Conférence</option>
                <option value="spectacle">Spectacle</option>
                <option value="communal">événement communal</option>
                <option value="festival">Annexe festival</option>
              </select>
            </div>
          </div>
          <div class="form-group">
            <label>Date souhaitée</label>
            <button type="button" class="date-range-trigger" id="open-calendar">
              <span class="date-range-trigger-main">
                <svg class="date-range-icon" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
                  <path d="M7 2V5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
                  <path d="M17 2V5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
                  <rect x="3" y="4" width="18" height="17" rx="3" stroke="currentColor" stroke-width="1.8"/>
                  <path d="M3 9H21" stroke="currentColor" stroke-width="1.8"/>
                </svg>
                <span class="date-range-trigger-text">
                  <span class="date-range-trigger-label">Plage date + heure</span>
                  <span class="date-range-trigger-value" id="date-range-trigger-value">Choisir une première et une seconde date avec l'heure</span>
                </span>
              </span>
              <span></span>
            </button>
            <input type="hidden" id="date-debut" name="date_debut" />
            <input type="hidden" id="date-fin" name="date_fin" />
          </div>
          <div class="form-group">
            <label>Matériel sélectionné</label>
            <div class="quote-builder">
              <div class="quote-builder-head">
                <strong>Résumé catalogue</strong>
                <button type="button" class="btn-ghost route-link" data-page="catalogue">Voir le catalogue</button>
              </div>
              <div class="quote-list" id="quote-list-form">
                <div class="quote-empty" id="quote-empty-form">Aucun matériel sélectionné pour le moment.</div>
              </div>
              <input type="hidden" id="quote-items-input" name="selection_catalogue" />
            </div>
          </div>
          <div class="form-group">
            <label for="message">Message / détails</label>
            <textarea id="message" name="message" placeholder="Décrivez brièvement le type d'événement (ex: Mariage, Anniversaire, soirée d'entreprise) ou toute autre information utile..."></textarea>
          </div>
          <div class="inline-flex-wrap-row">
            <button type="submit" class="btn-submit">Envoyer la demande</button>
            <div class="form-status" id="form-status"></div>
          </div>
        </form> </div>
    </div>
  </div>
</section>
<section class="page" id="page-mentions" data-page="mentions">
  <div class="page-section">
    <div class="container">
      <div class="section-header">
        <div>
          <div class="section-eyebrow">Cadre Juridique</div>
          <h2>Mentions Légales & Droits Réservés</h2>
        </div>
        <button type="button" class="product-back route-link" data-page="catalogue">Retour au catalogue</button>
      </div>
      <div class="page-card">
        <div class="product-detail-block">
          <h4>1. ÉDITION ET PUBLICATION</h4>
          <p class="product-description">
            Le présent site est édité par l'<strong>Association LUKA LIVE</strong>, association loi 1901 déclarée.<br>
            <strong>Siège social :</strong> 6 PLACE GAMBETTA, 80000 AMIENS, France.<br>
            <strong>N° SIREN :</strong> 932 584 139<br>
            <strong>N° SIRET :</strong> 932 584 139 00012<br>
            <strong>N° RNA :</strong> W802 018 908<br>
            <strong>Email :</strong> lukalive.assos@gmail.com<br>
            <strong>Directeur de la publication :</strong> Lucas HOMBECQ - Président de l'association LUKA LIVE.
          </p>
        </div>
        <div class="product-detail-block">
          <h4>2. HÉBERGEMENT & DROIT APPLICABLE</h4>
          <p class="product-description">
            <strong>Hébergeur :</strong> GitHub Inc.<br>
            <strong>Adresse :</strong> 88 Colin P. Kelly Jr. St, San Francisco, CA 94107, USA.<br>
            <strong>Téléphone :</strong> +1 415-448-6673<br>
            <strong>Droit applicable :</strong> Le présent site est soumis au <strong>droit français</strong>.
          </p>
        </div>
        <div class="product-detail-block">
          <h4>3. PROTECTION DES DONNÉES (RGPD)</h4>
          <p class="product-description">
            <strong>Responsable du traitement :</strong> LUKA LIVE (lukalive.assos@gmail.com).<br>
            <strong>Finalité :</strong> Les données collectées via le formulaire sont destinées exclusivement au traitement de votre demande de devis.<br>
            <strong>Base légale :</strong> Intérêt légitime / Exécution d'une demande.<br>
            <strong>Conservation :</strong> Les données sont conservées pendant une durée maximale de <strong>24 mois</strong> après traitement.<br>
            <strong>Vos droits :</strong> Vous disposez d'un droit d'accès, de rectification et de suppression de vos données via l'email ci-dessus.
          </p>
        </div>
        <div class="product-detail-block">
          <h4>4. PROPRIÉTÉ INTELLECTUELLE & COOKIES</h4>
          <p class="product-description">
            <strong>Propriété :</strong> © 2026 LUKA LIVE - Tous droits réservés. Toute reproduction sans accord écrit est interdite.<br>
            <strong>Cookies :</strong> Le site utilise uniquement des cookies techniques nécessaires à son bon fonctionnement et ne pratique aucun traçage publicitaire.
          </p>
        </div> </div>
    </div>
  </div>
</section>
  </main> <div class="calendar-modal" id="calendar-modal" aria-hidden="true">
    <div class="calendar-panel">
      <div class="calendar-topbar">
        <div class="calendar-topbar-left">
          <button type="button" class="calendar-reset" id="calendar-reset">Réinitialiser</button>
        </div>
        <div class="calendar-range-preview">
          <div class="calendar-preview-pill placeholder" id="calendar-preview-start">
            <span class="calendar-preview-main">Première date</span>
            <span class="calendar-preview-sub">Choisir le début</span>
          </div>
          <div class="calendar-preview-pill placeholder" id="calendar-preview-end">
            <span class="calendar-preview-main">Seconde date</span>
            <span class="calendar-preview-sub">Choisir la fin</span>
          </div>
        </div>
        <div class="calendar-topbar-right">
          <button type="button" class="calendar-nav-btn" id="calendar-prev">&#10094;</button>
          <span class="calendar-current-month" id="calendar-current-month">-</span>
          <button type="button" class="calendar-nav-btn" id="calendar-next">&#10095;</button>
        </div>
      </div>
      <div class="calendar-months" id="calendar-months"></div>
      <div class="calendar-time-grid">
        <div class="calendar-time-card">
          <strong>Heure de début</strong>
          <input type="time" id="time-debut" value="18:00" step="900" />
        </div>
        <div class="calendar-time-card">
          <strong>Heure de fin</strong>
          <input type="time" id="time-fin" value="23:00" step="900" />
        </div>
      </div>
      <div class="calendar-footer">
        <button type="button" class="calendar-confirm" id="calendar-confirm">OK</button>
      </div>
    </div>
  </div>
  <div class="product-image-modal" id="product-image-modal" aria-hidden="true">
    <div class="product-image-modal-panel">
      <div class="product-image-modal-topbar">
        <div class="product-image-modal-meta">
          <strong class="product-image-modal-title" id="product-image-modal-title">Image produit</strong>
          <span class="product-image-modal-subtitle" id="product-image-modal-subtitle">Cliquez en dehors pour fermer</span>
        </div>
        <button type="button" class="product-image-close" id="product-image-close" aria-label="Fermer l'image">&times;</button>
      </div>
      <h2 class="lightbox-header-title viewer-title" id="lightbox-title"></h2>
      <div class="product-image-stage" id="product-image-stage"></div>
      <div class="lightbox-zoom-container">
        <svg viewbox="0 0 24 24" width="20" height="20" stroke="currentColor" fill="none"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>
        <input type="range" id="lightbox-zoom-slider" min="1" max="4" step="0.1" value="1" />
        <svg viewbox="0 0 24 24" width="20" height="20" stroke="currentColor" fill="none"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>
      </div>
    </div>
  </div>
  <footer>
    <div class="container footer-inner">
      <div class="footer-brand">
        <img src="logo.png" alt="Logo LUKA LIVE" />
        <span>LUKA LIVE</span>
      </div>
      <ul class="footer-links">
        <li><a href="#accueil" class="route-link" data-page="accueil">Accueil</a></li>
        <li><a href="#services" class="route-link" data-page="services">Services</a></li>
        <li><a href="#catalogue" class="route-link" data-page="catalogue">Catalogue</a></li>
        <li><a href="#association" class="route-link" data-page="association">Association</a></li>
        <li><a href="#faq" class="route-link" data-page="faq">FAQ</a></li>
        <li><a href="https://luka-live.assoconnect.com/collect/description/492945-n-membre-membre-artiste-adhesion-annuelle" target="_blank" rel="noopener noreferrer">Devenir membre</a></li>
      </ul>
      <p class="footer-copy">
        © 2026 LUKA LIVE | Tous droits réservés |
        <a href="#mentions" class="route-link legal-link" data-page="mentions">Mentions Légales</a>
      </p>
    </div>
  </footer>
  <button id="floating-quote-btn" class="floating-quote-btn" type="button">
    Mon devis <span class="floating-quote-badge" id="floating-quote-badge">0</span>
  </button>
  <div id="toast-container" class="toast-container"></div>
  <script>
    const mobileToggle = document.getElementById('mobile-toggle');
    const nav = document.getElementById('site-nav');
    const routeLinks = document.querySelectorAll('.route-link:not(#floating-quote-btn)');
    const pages = document.querySelectorAll('.page');
    const navLinks = document.querySelectorAll('.nav-links .route-link');
    const heroPanels = document.querySelectorAll('.hero-panel');
    const faqItems = document.querySelectorAll('.faq-item');
    const filterButtons = document.querySelectorAll('.filter-btn');
    const catalogueViewButtons = document.querySelectorAll('.catalogue-view-btn, .view-btn');
    const catalogueGrid = document.getElementById('catalogue-grid');
    const catalogueCards = document.querySelectorAll('.catalogue-card');
    const addToQuoteButtons = document.querySelectorAll('.add-to-quote');
    const productDetailButtons = document.querySelectorAll('.product-detail');
    const quoteListMain = document.getElementById('quote-list-main');
    const quoteListForm = document.getElementById('quote-list-form');
    const quoteEmptyMain = document.getElementById('quote-empty-main');
    const quoteEmptyForm = document.getElementById('quote-empty-form');
    const quoteInput = document.getElementById('quote-items-input');
    const quoteCount = document.getElementById('quote-count');
    const clearQuoteButton = document.getElementById('clear-quote');
    const clearQuoteTopButton = document.getElementById('clear-quote-top');
    const navQuoteButton = document.getElementById('nav-quote-button');
    const mobileQuoteButton = document.getElementById('mobile-quote-button');
    const floatingQuoteBtn = document.getElementById('floating-quote-btn');
    const floatingQuoteBadge = document.getElementById('floating-quote-badge');
    let quickBtn = null;
    let quickPanel = null;
    let closeQuickBtn = null;
    let quickForm = null;
    const form = document.getElementById('contact-form');
    const formStatus = document.getElementById('form-status');
    const faqContactForm = document.getElementById('faq-contact-form');
    const faqFormStatus = document.getElementById('faq-form-status');
    const jaugeSlider = document.getElementById('jauge');
    const jaugeValDisplay = document.getElementById('jauge-val');
    const villeInput = document.getElementById('ville');
    const suggestionsList = document.getElementById('ville-suggestions');
    const dateStartInput = document.getElementById('date-debut');
    const dateEndInput = document.getElementById('date-fin');
    const dateRangeTrigger = document.getElementById('open-calendar');
    const dateRangeTriggerValue = document.getElementById('date-range-trigger-value');
    const calendarModal = document.getElementById('calendar-modal');
    const calendarMonths = document.getElementById('calendar-months');
    const calendarPreviewStart = document.getElementById('calendar-preview-start');
    const calendarPreviewEnd = document.getElementById('calendar-preview-end');
    const calendarPrev = document.getElementById('calendar-prev');
    const calendarNext = document.getElementById('calendar-next');
    const calendarCurrentMonth = document.getElementById('calendar-current-month');
    const calendarReset = document.getElementById('calendar-reset');
    const calendarConfirm = document.getElementById('calendar-confirm');
    const timeStartInput = document.getElementById('time-debut');
    const timeEndInput = document.getElementById('time-fin');
    const productPageTitle = document.getElementById('product-page-title');
    const productPageName = document.getElementById('product-page-name');
    const productPageReference = document.getElementById('product-page-reference');
    const productPageDescription = document.getElementById('product-page-description');
    const productPagePrice = document.getElementById('product-page-price');
    const productPageDetails = document.getElementById('product-page-details');
    const productPageAdd = document.getElementById('product-page-add');
    const productPageQtyValue = document.getElementById('product-page-qty-value');
    const productPageQtyMinus = document.getElementById('product-page-qty-minus');
    const productPageQtyPlus = document.getElementById('product-page-qty-plus');
    const productGalleryMain = document.getElementById('product-images-collage');
    const productGalleryThumbs = document.getElementById('product-gallery-thumbs');
    const catalogueSearchInput = document.getElementById('catalogue-search');
    const catalogueResultsEmpty = document.getElementById('catalogue-results-empty');
    const productImageModal = document.getElementById('product-image-modal');
    const productImageModalTitle = document.getElementById('product-image-modal-title');
    const productImageModalSubtitle = document.getElementById('product-image-modal-subtitle');
    const productImageClose = document.getElementById('product-image-close');
    const productImageStage = document.getElementById('product-image-stage');
    const lightboxTitle = document.getElementById('lightbox-title');
    const lightboxZoomSlider = document.getElementById('lightbox-zoom-slider');
    let quoteItems = [];
    let calendarVisibleMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
    const productMap = {};
    const catalogueQuantities = {};
    let lastJaugeValue = jaugeSlider ? jaugeSlider.value : 25;
    let lightboxScale = 1;
    let lightboxPanning = false;
    let lightboxPointX = 0;
    let lightboxPointY = 0;
    let lightboxStartX = 0;
    let lightboxStartY = 0;
    const productDetailsContent = {
      'lumiere-bar': {
        reference: 'BARRE MOTORISéE',
        overview: 'Puissance et Optique | Source lumineuse : 10 LED de 25W RGBW (Rouge, Vert, Bleu, Blanc) | Puissance totale : 250W | Angle d\'ouverture : 2° (faisceaux ultra-serrés pour un effet "sabre laser") | Luminosité (Lux à  5m) : Blanc, Vert, Rouge, Bleu | Mouvement et Effets | Mouvement Tilt (Axe Y) : 120° motorisé | Gradateur (Dimmer) : Linéaire 0 à  100% | Stroboscope : Fréquence réglable de 1 à  16 Hz | Contrôle et Programmation | Modes de fonctionnement : DMX512, Automatique, Activation vocale, Maître-Esclave | Canaux DMX : 7, 13 ou 43 canaux (le mode 43CH permet le contrôle individuel de chaque LED) | Interface : écran numérique à  4 chiffres | Données Physiques | Châssis : Alliage d\'aluminium | Alimentation : AC 110V-240V / 50-60Hz | Dimensions (L x l x H) : 104,00 - 12,00 - 22,00 cm | Poids net : 7,1 kg',
        details: [
          'Puissance et Optique',
          'Source lumineuse : 10 LED de 25W RGBW (Rouge, Vert, Bleu, Blanc)',
          'Puissance totale : 250W',
          'Angle d\'ouverture : 2° (faisceaux ultra-serrés pour un effet "sabre laser")',
          'Luminosité (Lux à  5m) : Blanc, Vert, Rouge, Bleu',
          'Mouvement et Effets',
          'Mouvement Tilt (Axe Y) : 120° motorisé',
          'Gradateur (Dimmer) : Linéaire 0 à  100%',
          'Stroboscope : Fréquence réglable de 1 à  16 Hz',
          'Contrôle et Programmation',
          'Modes de fonctionnement : DMX512, Automatique, Activation vocale, Maître-Esclave',
          'Canaux DMX : 7, 13 ou 43 canaux (le mode 43CH permet le contrôle individuel de chaque LED)',
          'Interface : écran numérique à  4 chiffres',
          'Données Physiques',
          'Châssis : Alliage d\'aluminium',
          'Alimentation : AC 110V-240V / 50-60Hz',
          'Dimensions (L x l x H) : 104,00 - 12,00 - 22,00 cm',
          'Poids net : 7,1 kg'
        ]
      },
      'lumiere-blinder': {
        reference: 'BLINDER LED',
        overview: 'Puissance et Optique | Source lumineuse : 2 LED COB (Blanc Froid + Blanc Chaud) | Puissance totale : 200W | Mouvement et Effets | Gradateur (Dimmer) : 0 à  100% | Stroboscope | Fonctions clés : Effet blinder (éblouissement), mixage manuel des blancs, effets de saut, dégradé, pulsation et changement sec | Contrôle et Programmation | Modes de fonctionnement : DMX512, Automatique, Sound, Maître-Esclave | Canaux DMX : 8 canaux | Interface : écran numérique | Données Physiques | Châssis : Fonte d\'aluminium et fer haute résistance | Alimentation : AC 90V-220V / 50-60Hz | Dimensions (L x l x H) : 34,50 - 21,00 - 18,00 cm | Poids net : 3,1 kg',
        details: [
          'Puissance et Optique',
          'Source lumineuse : 2 LED COB (Blanc Froid + Blanc Chaud)',
          'Puissance totale : 200W',
          'Mouvement et Effets',
          'Gradateur (Dimmer) : 0 à  100%',
          'Stroboscope',
          'Fonctions clés : Effet blinder (éblouissement), mixage manuel des blancs, effets de saut, dégradé, pulsation et changement sec',
          'Contrôle et Programmation',
          'Modes de fonctionnement : DMX512, Automatique, Sound, Maître-Esclave',
          'Canaux DMX : 8 canaux',
          'Interface : écran numérique',
          'Données Physiques',
          'Châssis : Fonte d\'aluminium et fer haute résistance',
          'Alimentation : AC 90V-220V / 50-60Hz',
          'Dimensions (L x l x H) : 34,50 - 21,00 - 18,00 cm',
          'Poids net : 3,1 kg'
        ]
      },
      'lumiere-cable_dmx': {
        reference: 'CABLAGE LUMIERE',
        overview: 'Sur demande de devis. | Toutes tailles possibles : | Câble DMX (Femelle / Mâle) | Câble d\'alimentation PowerCON | Câble d\'alimentation IEC | Rallonge / Prolongateur électrique | Adaptateurs : | Tout type d\'adaptateur',
        details: [
          'Toutes tailles possibles :',
          'Câble DMX (Femelle / Mà¢le)',
          'Câble d\'alimentation PowerCON',
          'Câble d\'alimentation IEC',
          'Rallonge / Prolongateur électrique',
          'Adaptateurs :',
          'Tout type d\'adaptateur'
        ]
      },
      'lumiere-lyre1': {
        reference: 'LYRE 760W',
        overview: 'Puissance et Optique | Source lumineuse : 7 LED de 40W (RGBW) | Optique : Lentille de 145 mm | Zoom : Système optique x8 avec angle réglable de 7° (faisceau Beam) à  34° (Wash) | Température de couleur : 2500K à  8000K | Luminosité : 2150 lux | Mouvement et Effets | Mouvement : Pan et Tilt à  vitesse réglable | Gradateur (Dimmer) : 0 à  100% linéaire | Stroboscope | Contrôle et Programmation | Modes de fonctionnement : DMX512, Automatique, Sound, Manuel | Canaux DMX : 23, 35 ou 51 canaux | Interface : écran LCD | Données Physiques | Alimentation : AC 100-240V / 50-60Hz | Connectique électrique : Entrée PowerCON | Dimensions (L x l x H) : 28,00 - 17,00 - 37,00 cm | Poids net : 6,16 kg',
        details: [
          'Puissance et Optique',
          'Source lumineuse : 7 LED de 40W (RGBW)',
          'Optique : Lentille de 145 mm',
          'Zoom : Système optique x8 avec angle réglable de 7° (faisceau Beam) à  34° (Wash)',
          'Température de couleur : 2500K à  8000K',
          'Luminosité : 2150 lux',
          'Mouvement et Effets',
          'Mouvement : Pan et Tilt à  vitesse réglable',
          'Gradateur (Dimmer) : 0 à  100% linéaire',
          'Stroboscope',
          'Contrôle et Programmation',
          'Modes de fonctionnement : DMX512, Automatique, Sound, Manuel',
          'Canaux DMX : 23, 35 ou 51 canaux',
          'Interface : écran LCD',
          'Données Physiques',
          'Alimentation : AC 100-240V / 50-60Hz',
          'Connectique électrique : Entrée PowerCON',
          'Dimensions (L x l x H) : 28,00 - 17,00 - 37,00 cm',
          'Poids net : 6,16 kg'
        ]
      },
      'lumiere-lyre2': {
        reference: 'LYRE LED',
        overview: 'Puissance et Optique | Source lumineuse : 7 LED de 40W (RGBW) | Optique : Lentille de 145 mm | Zoom : Système optique x8 avec angle réglable de 7° (faisceau Beam) à  34° (Wash) | Température de couleur : 2500K à  8000K | Luminosité : 2150 lux | Mouvement et Effets | Mouvement : Pan et Tilt avec fonction d\'inversion et vitesse réglable (Ajustement fin disponible) | Gradateur (Dimmer) : 0 à  100% linéaire | Stroboscope | Contrôle et Programmation | Modes de fonctionnement : DMX512, RDM, Automatique, Sound, Manuel | Canaux DMX : 23, 35 ou 51 canaux | Interface : écran graphique avec navigation simplifiée | Données Physiques | Alimentation : AC 100-240V / 50-60Hz | Connectique électrique : Entrée PowerCON | Dimensions (L x l x H) : 28,00 - 17,00 - 37,00 cm | Poids net : 6,16 kg | IP20',
        details: [
          'Puissance et Optique',
          'Source lumineuse : 7 LED de 40W (RGBW)',
          'Optique : Lentille de 145 mm',
          'Zoom : Système optique x8 avec angle réglable de 7° (faisceau Beam) à  34° (Wash)',
          'Température de couleur : 2500K à  8000K',
          'Luminosité : 2150 lux',
          'Mouvement et Effets',
          'Mouvement : Pan et Tilt avec fonction d\'inversion et vitesse réglable (Ajustement fin disponible)',
          'Gradateur (Dimmer) : 0 à  100% linéaire',
          'Stroboscope',
          'Contrôle et Programmation',
          'Modes de fonctionnement : DMX512, RDM, Automatique, Sound, Manuel',
          'Canaux DMX : 23, 35 ou 51 canaux',
          'Interface : écran graphique avec navigation simplifiée',
          'Données Physiques',
          'Alimentation : AC 100-240V / 50-60Hz',
          'Connectique électrique : Entrée PowerCON',
          'Dimensions (L x l x H) : 28,00 - 17,00 - 37,00 cm',
          'Poids net : 6,16 kg',
          'IP20'
        ]
      },
      'lumiere-par24': {
        reference: 'PROJECTEUR LED 80W',
        overview: 'Puissance et Optique | Source lumineuse : 14 LED RGBW | Puissance totale : 80W | Angle d\'ouverture : 25° | Mouvement et Effets | Gradateur (Dimmer) : 0 à  100% linéaire | Fonctions clés : Mixage de couleurs infini | Contrôle et Programmation | Modes de fonctionnement : DMX512, Automatique, Sound, Maître-Esclave, | Canaux DMX : 4 ou 8 canaux | Données Physiques | Alimentation : AC 90V-220V / 50-60Hz | Installation : Double lyre d\'accroche incluse (structure ou pose au sol)',
        details: [
          'Puissance et Optique',
          'Source lumineuse : 14 LED RGBW',
          'Puissance totale : 80W',
          'Angle d\'ouverture : 25°',
          'Mouvement et Effets',
          'Gradateur (Dimmer) : 0 à  100% linéaire',
          'Fonctions clés : Mixage de couleurs infini',
          'Contrôle et Programmation',
          'Modes de fonctionnement : DMX512, Automatique, Sound, Maître-Esclave,',
          'Canaux DMX : 4 ou 8 canaux',
          'Données Physiques',
          'Alimentation : AC 90V-220V / 50-60Hz',
          'Installation : Double lyre d\'accroche incluse (structure ou pose au sol)'
        ]
      },
      'lumiere-par36': {
        reference: 'PROJECTEUR LED 200W',
        overview: 'Puissance et Optique | Source lumineuse : 18 LED 18W RGBW + Ambre +UV | Puissance totale : 200W | faisceau: 40° | Mouvement et Effets | Gradateur (Dimmer) : 0 à  100% linéaire | Stroboscope : Fréquence 0 à  25 Hz | Contrôle et Programmation | Modes de fonctionnement : DMX512, Automatique, Sound, Manuel | Canaux DMX : 6 ou 10 canaux | Données Physiques | Châssis : Alliage d\'aluminium haute résistance | Refroidissement : Système de dissipation thermique + ventilo efficace avec protection contre la surchauffe | Alimentation : AC 100-240V / 50-60Hz | Connectique électrique : Connecteur PowerCON | Dimensions (L x l x P) : 20,00 - 20,00 - 8,00 cm | Poids net : 2,6 kg | IP20',
        details: [
          'Puissance et Optique',
          'Source lumineuse : 18 LED 18W RGBW + Ambre +UV',
          'Puissance totale : 200W',
          'faisceau: 40°',
          'Mouvement et Effets',
          'Gradateur (Dimmer) : 0 à  100% linéaire',
          'Stroboscope : Fréquence 0 à  25 Hz',
          'Contrôle et Programmation',
          'Modes de fonctionnement : DMX512, Automatique, Sound, Manuel',
          'Canaux DMX : 6 ou 10 canaux',
          'Données Physiques',
          'Châssis : Alliage d\'aluminium haute résistance',
          'Refroidissement : Système de dissipation thermique + ventilo efficace avec protection contre la surchauffe',
          'Alimentation : AC 100-240V / 50-60Hz',
          'Connectique électrique : Connecteur PowerCON',
          'Dimensions (L x l x P) : 20,00 - 20,00 - 8,00 cm',
          'Poids net : 2,6 kg',
          'IP20'
        ]
      },
      'son-cable_xlr': {
        reference: 'CÂBLAGE SON',
        overview: 'Sur demande de devis. | Câbles, toutes tailles possibles : | Câble XLR Femelle / XLR Mà¢le (Microphone/Signal) | Câble Jack 6.35mm | Câble Mini-Jack | Câble Speakon | Câble RCA | Câble MIDI (DIN 5 broches) | Adaptateurs : | Tout type d\'adaptateur',
        details: [
          'Câbles, toutes tailles possibles :',
          'Câble XLR Femelle / XLR Mà¢le (Microphone/Signal)',
          'Câble Jack 6.35mm',
          'Câble Mini-Jack',
          'Câble Speakon',
          'Câble RCA',
          'Câble MIDI (DIN 5 broches)',
          'Adaptateurs :',
          'Tout type d\'adaptateur'
        ]
      },
      'son-dxs18xlf': {
        reference: 'YAMAHA DXS18XLF',
        overview: 'Référence YAMAHA DXS18XLF | Détails | Caisson de graves 18" avec bobine 4" | Niveau de pression acoustique maximal 136 dB SPL | Réponse en fréquence de 30Hz à  150Hz | Amplification classe D 1600W | DSP 96kHz pour un son haute résolution avec une latence ultra faible | Nombreuses fonctions DSP, chargement de préréglages et égalisation paramétrique via l\'écran LCD intuitif | Traitement D-XSUB et mode cardioïde | Coffret en multiplis résistant avec revêtement Polyurea | Housse et roulettes | Finition noire',
        details: [
          'Caisson de graves 18" avec bobine 4"',
          'Niveau de pression acoustique maximal 136 dB SPL',
          'Réponse en fréquence de 30Hz à  150Hz',
          'Amplification classe D 1600W',
          'DSP 96kHz pour un son haute résolution avec une latence ultra faible',
          'Nombreuses fonctions DSP, chargement de préréglages et égalisation paramétrique via l\'écran LCD intuitif',
          'Traitement D-XSUB et mode cardioïde',
          'Coffret en multiplis résistant avec revêtement Polyurea',
          'Housse et roulettes',
          'Finition noire'
        ]
      },
      'son-dzr15': {
        reference: 'YAMAHA DZR-15',
        overview: 'Référence YAMAHA DZR-15 | Détails | Enceinte 2 voies avec tweeter de 2" et boomer de 15" | Niveau de pression acoustique maximum de 139 dB SPL et réponse en fréquence de 34Hz à  20 kHz | Amplification classe D de 2000 W | DSP 96kHz et filtre FIR pour une très haute résolution avec une latence extrêmement faible | Technologie Advanced FIR-X pour minimiser la distorsion de phase | Réglages DSP précis via l\'écran LCD intuitif | D-Contour et circuits de protection très fiables | Préréglage retour de scène pour une configuration en miroir | Coffret en multiplis léger avec revêtement polyurée | Pavillon rotatif',
        details: [
          'Enceinte 2 voies avec tweeter de 2" et boomer de 15"',
          'Niveau de pression acoustique maximum de 139 dB SPL et réponse en fréquence de 34Hz à  20 kHz',
          'Amplification classe D de 2000 W',
          'DSP 96kHz et filtre FIR pour une très haute résolution avec une latence extrêmement faible',
          'Technologie Advanced FIR-X pour minimiser la distorsion de phase',
          'Réglages DSP précis via l\'écran LCD intuitif',
          'D-Contour et circuits de protection très fiables',
          'Préréglage retour de scène pour une configuration en miroir',
          'Coffret en multiplis léger avec revêtement polyurée',
          'Pavillon rotatif'
        ]
      },
      'son-mg12xu': {
        reference: 'YAMAHA MG12XU',
        overview: 'Référence YAMAHA - MG12XU | 6 entrées micro, 12 entrées ligne (4 mono + 4 stéréo) | 2 bus GROUP, 1 bus stéréo | 2 Aux (dont FX) | Préamplis micro D-PRE avec circuit Darlington inversé | 4 compresseurs 1-Knob | Multieffet SPX haute qualité 24 programmes | Interface audio USB 24bits/192kHz | Atténuateur PAD sur les entrées mono | Alimentation fantôme +48V | Sorties XLR et Jack symétriques | Châssis métallique résistant | Dimensions (LxHxP): 308 mm x 118 mm x 422 mm | Poids net: 4.2 kg',
        details: [
          '6 entrées micro, 12 entrées ligne (4 mono + 4 stéréo)',
          '2 bus GROUP, 1 bus stéréo',
          '2 Aux (dont FX)',
          'Préamplis micro D-PRE avec circuit Darlington inversé',
          '4 compresseurs 1-Knob',
          'Multieffet SPX haute qualité 24 programmes',
          'Interface audio USB 24bits/192kHz',
          'Atténuateur PAD sur les entrées mono',
          'Alimentation fantôme +48V',
          'Sorties XLR et Jack symétriques',
          'Châssis métallique résistant',
          'Dimensions (LxHxP): 308 mm x 118 mm x 422 mm',
          'Poids net: 4.2 kg'
        ]
      },
      'son-micro1': {
        reference: 'Prodipe TT1-LANEN',
        overview: 'Référence : Prodipe TT1-LANEN | Micro chant dynamique avec interrupteur | Directivité cardioïde | Réponse en fréquence de 50Hz à  15kHz | Sensibilité de -49dB ±3dB | Impédance de 600 Ω | Poids 350g (dimensions à¸48 x 185mm)',
        details: [
          'Micro chant dynamique avec interrupteur',
          'Directivité cardioïde',
          'Réponse en fréquence de 50Hz à  15kHz',
          'Sensibilité de -49dB ±3dB',
          'Impédance de 600 Ω',
          'Poids 350g (dimensions Ø48 x 185mm)'
        ]
      },
      'son-micro2': {
        reference: 'Stagg SDM50',
        overview: 'Référence : Stagg SDM50 | Microphone dynamique cardioïde, | Utilisations : Voix et instruments | Sensibilité : -54 dB +/- 3 dB (0 dB = 1 V / Pa à  1 KHz) | Bande passante : 50 Hz à  15 KHz | Impédance de sortie : 600 Ω ± 30 % (à  1 KHz) | Corps : Alliage de zinc | Interrupteur marche/arrêt',
        details: [
          'Microphone dynamique cardioïde,',
          'Utilisations : Voix et instruments',
          'Sensibilité : -54 dB +/- 3 dB (0 dB = 1 V / Pa à  1 KHz)',
          'Bande passante : 50 Hz à  15 KHz',
          'Impédance de sortie : 600 Ω ± 30 % (à  1 KHz)',
          'Corps : Alliage de zinc',
          'Interrupteur marche/arrêt'
        ]
      },
      'son-pieds_enceintes': {
        reference: 'K&M 21449',
        overview: 'Référence : K&M 21449 | Kit de 2 pieds d\'enceinte avec housse de transport | Matériau : Aluminium | Hauteur réglable de 127 cm à  193 cm | Réglage en hauteur par système de verrouillage à  bouton-poussoir et vis de sécurité | Diamètre de l\'embout (tube) : 35 mm (standard) | Capacité de charge maximale : 50 kg par pied | Poids total de l\'ensemble : ~6 kg',
        details: [
          'Kit de 2 pieds d\'enceinte avec housse de transport',
          'Matériau : Aluminium',
          'Hauteur réglable de 127 cm à  193 cm',
          'Réglage en hauteur par système de verrouillage à  bouton-poussoir et vis de sécurité',
          'Diamètre de l\'embout (tube) : 35 mm (standard)',
          'Capacité de charge maximale : 50 kg par pied',
          'Poids total de l\'ensemble : ~6 kg'
        ]
      },
      'structure-strcuture1': {
        reference: 'PONT ÉCLAIRAGE',
        overview: 'Type : Ensemble complet | Matériau : Acier et magnésium | Hauteur réglable : de 1,50 m à  3,10 m | Largeur totale : 3,00 m | Capacité d\'accroche : Permet de monter jusqu\'à  8 projecteurs | Base des trépieds : Empreinte au sol de ~ 1,20 m | Installation : Montage ultra-rapide et simple | Poids total de l\'ensemble : 21,0 kg | Inclus : 2 pieds de lumière, 2 traverses de 1,5 m et 2 supports de traverse',
        details: [
          'Type : Ensemble complet',
          'Matériau : Acier et magnésium',
          'Hauteur réglable : de 1,50 m à  3,10 m',
          'Largeur totale : 3,00 m',
          'Capacité d\'accroche : Permet de monter jusqu\'à  8 projecteurs',
          'Base des trépieds : Empreinte au sol de ~ 1,20 m',
          'Installation : Montage ultra-rapide et simple',
          'Poids total de l\'ensemble : 21,0 kg',
          'Inclus : 2 pieds de lumière, 2 traverses de 1,5 m et 2 supports de traverse'
        ]
      },
      'structure-structure2': {
        reference: 'TRAVERSE',
        overview: 'Dimensions : 1500 x 210 mm | Capacité de charge maximale : 100 kg | Tubes longitudinaux : ~ 18 x 1 mm | Poids : 4,5 kg',
        details: [
          'Dimensions : 1500 x 210 mm',
          'Capacité de charge maximale : 100 kg',
          'Tubes longitudinaux : ~ 18 x 1 mm',
          'Poids : 4,5 kg'
        ]
      },
      'structure-structure3': {
        reference: 'ANGLE',
        overview: 'Dimensions : 350 x 350 mm | Section extérieure (largeur) : ~210 mm | Capacité de charge maximale : 100 kg | Tubes longitudinaux : ~ 18 x 1 mm | Poids : 2,5 kg',
        details: [
          'Dimensions : 350 x 350 mm',
          'Section extérieure (largeur) : ~210 mm',
          'Capacité de charge maximale : 100 kg',
          'Tubes longitudinaux : ~ 18 x 1 mm',
          'Poids : 2,5 kg'
        ]
      },
      'structure-structure4': {
        reference: 'PIED DE STRUCTURE',
        overview: 'Type : Pied d\'éclairage stable | Matériau : Acier et magnésium | Hauteur réglable : de 150 cm à  315 cm | Base : Pieds renforcés individuellement avec une grande surface au sol (~ 120 cm) | Fixation : Adaptateur 35 - 36 mm | transport : Longueur repliée de 124 cm | Poids : 5,6 kg',
        details: [
          'Type : Pied d\'éclairage stable',
          'Matériau : Acier et magnésium',
          'Hauteur réglable : de 150 cm à  315 cm',
          'Base : Pieds renforcés individuellement avec une grande surface au sol (~ 120 cm)',
          'Fixation : Adaptateur 35 - 36 mm',
          'transport : Longueur repliée de 124 cm',
          'Poids : 5,6 kg'
        ]
      }
    };
    const productSearchKeywords = {
      'son-dzr15': ['enceinte', 'yamaha', 'dzr15', 'dzr-15', 'sono', 'haut-parleur', 'speaker', 'retour', 'façade', '2000w', 'puissance', 'diffusion'],
      'son-dxs18xlf': ['caisson', 'basse', 'sub', 'subwoofer', 'yamaha', 'dxs18xlf', '18 pouces', 'grave', '1600w', 'cardioide'],
      'son-mg12xu': ['console', 'mixage', 'table', 'yamaha', 'mg12xu', 'usb', 'mixer', 'audio', 'effets', 'micro', 'preamp', 'spx'],
      'son-pieds_enceintes': ['pied', 'pieds', 'enceinte', 'stand', 'support', 'km', 'k&m', '21449', 'tripod', 'hauteur'],
      'son-micro1': ['micro', 'microphone', 'prodipe', 'tt1', 'chant', 'voix', 'dynamique', 'cardioide', 'scene'],
      'son-micro2': ['micro', 'microphone', 'stagg', 'sdm50', 'chant', 'voix', 'instrument', 'dynamique', 'cardioide'],
      'lumiere-lyre1': ['lyre', 'moving head', 'beam', 'wash', 'rgbw', '760w', 'automatique', 'dmx', 'scene', 'eclairage'],
      'lumiere-lyre2': ['lyre', 'moving head', 'led', '7x40w', 'rgbw', 'dmx', 'rdm', 'beam', 'wash', 'eclairage'],
      'lumiere-blinder': ['blinder', 'led', 'cob', '200w', 'stroboscope', 'scene', 'eclairage', 'public', 'warm white', 'cold white'],
      'lumiere-bar': ['barre', 'motorisee', 'moving beam', '250w', 'sabre laser', 'tilt', 'led', 'rgbw', 'eclairage'],
      'lumiere-par36': ['projecteur', 'par', 'par36', 'led', '200w', 'rgbwa', 'uv', 'uplight', 'wash', 'dmx', 'scene'],
      'lumiere-par24': ['projecteur', 'par', 'par24', 'led', '80w', 'rgbw', 'wash', 'uplight', 'couleur', 'dmx'],
      'structure-strcuture1': ['pont', 'structure', 'portique', 'eclairage', 'scene', '3m', 'truss', 'ensemble', 'support'],
      'structure-structure2': ['traverse', 'pont', 'structure', '1,5m', '1.5m', 'truss', 'barre', 'support'],
      'structure-structure3': ['traverse', 'angle', '90', '90°', 'coude', 'pont', 'structure', 'truss'],
      'structure-structure4': ['pied', 'structure', 'eclairage', 'support', 'trepied', 'truss', '35mm', 'hauteur'],
      'son-cable_xlr': ['cable', 'cablage', 'adaptateur', 'xlr', 'jack', 'speakon', 'rca', 'midi', 'audio', 'son'],
      'lumiere-cable_dmx': ['cable', 'cablage', 'adaptateur', 'dmx', 'powercon', 'iec', 'rallonge', 'lumiere', 'eclairage']
    };
    function normalizeCategory(value) {
      const labels = {
        son: 'Son',
        lumiere: 'Lumière',
        structure: 'Structures',
        accessoires: 'Accessoires'
      };
      return labels[value] || value;
    }
    const revealOptions = {
      root: null,
      rootMargin: '0px',
      threshold: 0.15
    };
    const revealObserver = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          entry.target.classList.add('reveal-visible');
          observer.unobserve(entry.target);
        }
      });
    }, revealOptions);
    function initReveal() {
      document.querySelectorAll('.page.active .reveal').forEach(el => {
        el.classList.remove('reveal-visible');
        revealObserver.observe(el);
      });
    }
    function showToast(message) {
      const container = document.getElementById('toast-container');
      if (!container) return;
      const toast = document.createElement('div');
      toast.className = 'toast';
      toast.innerHTML = message;
      container.appendChild(toast);
      setTimeout(() => toast.remove(), 3000);
    }
    function showNotification(message, type = 'success') {
      const oldNotify = document.querySelector('.notification');
      if (oldNotify) oldNotify.remove();
      const notification = document.createElement('div');
      notification.className = `notification ${type}`;
      const icon = type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle';
      notification.innerHTML = `
        <i class="fas ${icon}"></i>
        <span>${message}</span>
      `;
      document.body.appendChild(notification);
      setTimeout(() => notification.classList.add('show'), 100);
      setTimeout(() => {
        notification.classList.remove('show');
        setTimeout(() => notification.remove(), 500);
      }, 3000);
    }
    function buildGalleryMarkup(slides, currentIndex) {
      return slides.map((slide, index) => `
        <div class="catalogue-slide${index === currentIndex ? ' active' : ''}" data-slide-index="${index}">
          ${slide.html}
          <div class="catalogue-slide-caption">
            <span>${slide.label}</span>
            <span class="catalogue-gallery-counter">${index + 1} / ${slides.length}</span>
          </div>
        </div>
      `).join('');
    }
    function updateGallery(gallery, nextIndex) {
      const slides = [...gallery.querySelectorAll('.catalogue-slide')];
      if (!slides.length) return;
      const total = slides.length;
      const index = (nextIndex + total) % total;
      gallery.dataset.index = String(index);
      slides.forEach((slide, slideIndex) => {
        slide.classList.toggle('active', slideIndex === index);
      });
    }
    function setupCatalogueGalleries() {
      catalogueCards.forEach(card => {
        const imageBox = card.querySelector('.catalogue-card-img');
        const imageList = (card.dataset.images || '').split('|').map(item => item.trim()).filter(Boolean);
        const baseMarkup = imageBox.innerHTML.trim();
        const name = card.querySelector('h3').textContent.trim() || 'Produit';
        const slides = imageList.length ?
           imageList.map((imageName, index) => ({
              label: `Photo ${index + 1}`,
              html: `<img src="${imageName}" alt="${name} - photo ${index + 1}" />`
            }))
          : ['Vue principale', 'Autre angle', 'En situation'].map(label => ({ label, html: baseMarkup }));
        imageBox.dataset.index = '0';
        imageBox.innerHTML = `
          ${buildGalleryMarkup(slides, 0)}
          <button type="button" class="gallery-nav gallery-prev" aria-label="Photo précédente">&#10094;</button>
          <button type="button" class="gallery-nav gallery-next" aria-label="Photo suivante">&#10095;</button>
        `;
        imageBox.dataset.galleryName = name;
      });
    }
    function buildProductMap() {
      catalogueCards.forEach(card => {
        const id = card.dataset.product;
        const title = card.querySelector('h3').textContent.trim() || '';
        const tag = card.querySelector('.catalogue-tag')?.textContent.trim() || '';
        const descElem = card.querySelector('.catalogue-card-body > p:not(.catalogue-tag)');
        const description = descElem ? descElem.textContent.trim() : '';
        const price = card.querySelector('.catalogue-price').textContent.replace(/\s+/g, ' ').trim() || '';
        const category = card.dataset.category || '';
        const extraDetails = productDetailsContent[id] || null;
        const keywordParts = [
          title,
          tag,
          description,
          normalizeCategory(category),
          extraDetails?.reference || '',
          extraDetails?.overview || '',
          ...(extraDetails?.details || []),
          ...(productSearchKeywords[id] || [])
        ];
        const keywordText = keywordParts.join(' | ');
        const slides = [...card.querySelectorAll('.catalogue-slide')].map(slide => ({
          label: slide.querySelector('.catalogue-slide-caption span').textContent.trim() || 'Vue',
          html: slide.querySelector('svg, img').outerHTML || ''
        }));
        productMap[id] = {
          id,
          title,
          tag,
          description,
          price,
          category,
          slides,
          reference: extraDetails.reference || title,
          overview: extraDetails.overview || description,
          details: extraDetails.details || [],
          searchKeywords: keywordText,
        };
      });
    }
    function renderProductGallery(product, activeIndex = 0) {
      productGalleryMain.innerHTML = product.slides.map((slide, index) => `
        <button type="button" class="grid-item${index === activeIndex ? ' active' : ''}" data-index="${index}" aria-label="Ouvrir ${product.title} - photo ${index + 1}">
          ${slide.html}
        </button>
      `).join('');
      productGalleryMain.dataset.index = String(activeIndex);
      productGalleryThumbs.innerHTML = '';
    }
    function syncProductThumbs(index) {
      [...productGalleryThumbs.querySelectorAll('.product-thumb')].forEach((thumb, thumbIndex) => {
        thumb.classList.toggle('active', thumbIndex === index);
      });
      [...productGalleryMain.querySelectorAll('.grid-item')].forEach((item, itemIndex) => {
        item.classList.toggle('active', itemIndex === index);
      });
    }
    function syncProductImageModalMeta(product, index) {
      if (!product) return;
      productImageModalTitle.textContent = `${index + 1}/${product.slides.length}`;
      productImageModalSubtitle.textContent = '';
      if (lightboxTitle) {
        const categoryName = product.title || 'Produit';
        const productRef = product.reference || '';
        lightboxTitle.innerHTML = `${categoryName} <span class="viewer-ref">- ${productRef}</span>`;
      }
    }
    function buildLightboxGalleryMarkup(slides, currentIndex) {
      return slides.map((slide, index) => `
        <div class="catalogue-slide${index === currentIndex ? ' active' : ''}" data-slide-index="${index}">
          <div class="lightbox-img-wrapper">
            ${slide.html}
          </div>
          <div class="catalogue-slide-caption">
            <span>${slide.label}</span>
            <span class="catalogue-gallery-counter">${index + 1} / ${slides.length}</span>
          </div>
        </div>
      `).join('');
    }
    function getActiveLightboxMedia() {
      return productImageStage.querySelector('.catalogue-slide.active .lightbox-img-wrapper > img, .catalogue-slide.active .lightbox-img-wrapper > svg') || null;
    }
    function setLightboxTransform() {
      const activeMedia = getActiveLightboxMedia();
      if (!activeMedia) return;
      activeMedia.style.transform = `translate(${lightboxPointX}px, ${lightboxPointY}px) scale(${lightboxScale})`;
    }
    function resetLightboxZoom() {
      lightboxScale = 1;
      lightboxPointX = 0;
      lightboxPointY = 0;
      lightboxPanning = false;
      if (lightboxZoomSlider) lightboxZoomSlider.value = '1';
      const activeMedia = getActiveLightboxMedia();
      if (activeMedia) {
        activeMedia.style.transform = 'translate(0px, 0px) scale(1)';
      }
    }
    function renderProductImageModal(product, activeIndex = 0) {
      if (!productImageStage || !product.slides.length) return;
      productImageStage.innerHTML = `
        ${buildLightboxGalleryMarkup(product.slides, activeIndex)}
        <button type="button" class="gallery-nav gallery-prev" aria-label="Image précédente">&#10094;</button>
        <button type="button" class="gallery-nav gallery-next" aria-label="Image suivante">&#10095;</button>
      `;
      productImageStage.dataset.index = String(activeIndex);
      productImageStage.dataset.product = product.id;
      syncProductImageModalMeta(product, activeIndex);
      resetLightboxZoom();
    }
    function syncProductGallery(index) {
      productGalleryMain.dataset.index = String(index);
      syncProductThumbs(index);
    }
    function openProductImageModal(index = Number(productGalleryMain.dataset.index || 0), options = {}) {
      const { pushHistory = true } = options;
      const productId = productPageAdd.dataset.product;
      const product = productMap[productId];
     
      if (!product || !product.slides || !product.slides.length) return;
      renderProductImageModal(product, index);
      productImageModal.classList.add('open');
      productImageModal.setAttribute('aria-hidden', 'false');
      document.body.classList.add('product-image-modal-open');
      if (pushHistory) {
        try {
          const activePage = document.querySelector('.page.active').dataset.page || 'produit';
          history.pushState({ page: activePage, productImageModal: true, productId, imageIndex: index }, '', `#${activePage}`);
        } catch (error) {
          console.warn("Navigation locale bloquee, mais la modale s'ouvre quand meme.");
        }
      }
    }
    function closeProductImageModal(options = {}) {
      const { useHistoryBack = false } = options;
      if (useHistoryBack && history.state.productImageModal) {
        history.back();
        return;
      }
      productImageModal.classList.remove('open');
      productImageModal.setAttribute('aria-hidden', 'true');
      document.body.classList.remove('product-image-modal-open');
      resetLightboxZoom();
    }
    function navigateProductImageModal(direction) {
      const productId = productImageStage.dataset.product;
      const product = productMap[productId];
      if (!product.slides.length) return;
      const currentIndex = Number(productImageStage.dataset.index || 0);
      const nextIndex = (currentIndex + direction + product.slides.length) % product.slides.length;
      updateGallery(productImageStage, nextIndex);
      const normalizedIndex = Number(productImageStage.dataset.index || 0);
      syncProductGallery(normalizedIndex);
      syncProductImageModalMeta(product, normalizedIndex);
      resetLightboxZoom();
    }
    function getCatalogueQuantity(productId) {
      return catalogueQuantities[productId] || 1;
    }
    function setCatalogueQuantity(productId, quantity) {
      const safeQuantity = Math.max(1, quantity);
      catalogueQuantities[productId] = safeQuantity;
      document.querySelectorAll(`.qty-value[data-product="${productId}"][data-context="catalogue"]`).forEach(node => {
        node.textContent = String(safeQuantity);
      });
    }
    function getProductPageQuantity() {
      return Math.max(1, Number(productPageQtyValue.textContent) || 1);
    }
    function setProductPageQuantity(quantity) {
      productPageQtyValue.textContent = String(Math.max(1, quantity));
    }
    function normalizeProductToken(value) {
      return String(value || '')
        .toLowerCase()
        .normalize('NFD')
        .replace(/[\u0300-\u036f]/g, '')
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-+|-+$/g, '');
    }
    function getFallbackProductId() {
      if (window.__lastProductId && productMap[window.__lastProductId]) return window.__lastProductId;
      const ids = Object.keys(productMap);
      return ids.length ? ids[0] : '';
    }
    function resolveProductToken(token) {
      const raw = String(token || '').trim();
      if (!raw) return getFallbackProductId();
      if (productMap[raw]) return raw;
      const norm = normalizeProductToken(raw);
      let partialMatch = '';
      for (const product of Object.values(productMap)) {
        const idNorm = normalizeProductToken(product.id);
        const titleNorm = normalizeProductToken(product.title);
        const refNorm = normalizeProductToken(product.reference || '');
        if (norm === idNorm || norm === titleNorm || norm === refNorm) return product.id;
        if (!partialMatch && (idNorm.includes(norm) || titleNorm.includes(norm) || refNorm.includes(norm))) {
          partialMatch = product.id;
        }
      }
      return partialMatch || getFallbackProductId();
    }
    function triggerRangeFeedback() {
      if (!jaugeSlider) return;
      if (navigator.vibrate) navigator.vibrate(50);
      jaugeSlider.classList.remove('range-vibrate');
      void jaugeSlider.offsetWidth;
      jaugeSlider.classList.add('range-vibrate');
      setTimeout(() => jaugeSlider.classList.remove('range-vibrate'), 400);
    }
    function showProductDetails(productId, options = {}) {
      const { isBack = false } = options;
      const product = productMap[productId];
      if (!product) return;
      closeProductImageModal();
      productPageTitle.textContent = product.title;
      productPageName.textContent = product.title;
      const reference = (product.reference || '').trim();
      if (!reference || reference === '-') {
        productPageReference.style.display = 'none';
      } else {
        productPageReference.style.display = '';
        productPageReference.innerHTML = `<strong>Référence</strong><span>${reference}</span>`;
      }
      productPageDescription.textContent = product.description;
      productPagePrice.textContent = product.price;
      const detailSubcategories = new Set([
        'Puissance et Optique',
        'Mouvement et Effets',
        'Contrôle et Programmation',
        'Données Physiques'
      ]);
      productPageDetails.innerHTML = product.details.map((d) => {
        const value = d.trim();
        const className = detailSubcategories.has(value) ? ' class="detail-subcategory"' : '';
        return `<li${className}>${value}</li>`;
      }).join('');
      productPageAdd.dataset.product = product.id;
      productPageAdd.dataset.item = product.title;
      productPageAdd.dataset.price = product.price;
      window.__lastProductId = product.id;
      setProductPageQuantity(getCatalogueQuantity(product.id));
      renderProductGallery(product, 0);
      showPage('produit', isBack);
      if (!isBack) {
        const slug = normalizeProductToken(product.title || product.id);
        history.replaceState({ page: 'produit', productId: product.id }, '', `#produit/${encodeURIComponent(slug)}`);
      }
    }
    window.showProductDetails = showProductDetails;
    let navOpenedAt = 0;
    let navLockScrollY = 0;
    function setNavOpen(isOpen) {
      nav.classList.toggle('nav-open', isOpen);
      mobileToggle.classList.toggle('active', isOpen);
      document.body.classList.toggle('nav-open', isOpen);
      if (isOpen) {
        navLockScrollY = window.scrollY || window.pageYOffset || 0;
        document.body.style.position = 'fixed';
        document.body.style.top = `-${navLockScrollY}px`;
        document.body.style.left = '0';
        document.body.style.right = '0';
        document.body.style.width = '100%';
        document.body.style.overflow = 'hidden';
        navOpenedAt = Date.now();
      } else {
        document.body.style.position = '';
        document.body.style.top = '';
        document.body.style.left = '';
        document.body.style.right = '';
        document.body.style.width = '';
        document.body.style.overflow = '';
        window.scrollTo({ top: navLockScrollY, behavior: 'auto' });
      }
    }
    mobileToggle.addEventListener('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      if (navigator.vibrate) navigator.vibrate(200);
      this.classList.remove('pulse-active');
      void this.offsetWidth;
      this.classList.add('pulse-active');
      setTimeout(() => {
        this.classList.remove('pulse-active');
      }, 600);
      setNavOpen(!nav.classList.contains('nav-open'));
    });
    jaugeSlider.addEventListener('touchstart', triggerRangeFeedback, { passive: true });
    jaugeSlider.addEventListener('touchend', triggerRangeFeedback, { passive: true });
    jaugeSlider.addEventListener('mousedown', triggerRangeFeedback);
    jaugeSlider.addEventListener('mouseup', triggerRangeFeedback);
    function setActiveNav(page) {
      navLinks.forEach(link => link.classList.toggle('active', link.dataset.page === page));
    }
    const scrollMemory = {};
    function showPage(pageName, isBack = false) {
      const activePage = document.querySelector('.page.active');
      if (activePage) {
        scrollMemory[activePage.dataset.page] = window.scrollY;
      }
      if (productImageModal.classList.contains('open')) {
        closeProductImageModal();
      }
      if (pageName === 'catalogue') {
        setCatalogueView(window.innerWidth <= 640 ? 'list' : 'grid');
      }
      pages.forEach(page => {
        const isTarget = page.dataset.page === pageName;
        page.classList.toggle('active', isTarget);
        page.classList.remove('page-enter');
        if (isTarget && !isBack) {
          void page.offsetWidth;
          page.classList.add('page-enter');
        }
      });
      setActiveNav(pageName);
      setNavOpen(false);
      if (isBack && scrollMemory[pageName] !== undefined) {
        window.scrollTo({ top: scrollMemory[pageName], behavior: 'auto' });
      } else {
        window.scrollTo({ top: 0, behavior: 'smooth' });
      }
      if (!isBack) {
        history.pushState({ page: pageName }, '', `#${pageName}`);
      }
      setTimeout(initReveal, 100);
    }
    routeLinks.forEach(link => {
      link.addEventListener('click', (e) => {
        if (nav.classList.contains('nav-open') && (Date.now() - navOpenedAt) < 350) {
          e.preventDefault();
          return;
        }
        e.preventDefault();
        const targetPage = link.dataset.page;
        if (targetPage) showPage(targetPage);
      });
    });
    floatingQuoteBtn.addEventListener('click', () => showPage('devis'));
    if (!Object.keys(productMap).length) {
      buildProductMap();
    }
    window.productMap = productMap;
    function handleHashRoute(rawHash = window.location.hash.replace('#', '')) {
      const hashParts = String(rawHash || '').split('/');
      const hashPage = hashParts[0];
      const hashProductToken = decodeURIComponent(hashParts.slice(1).join('/') || '');
      const hashProductId = resolveProductToken(hashProductToken);
      if (hashPage === 'produit' && hashProductId && productMap[hashProductId]) {
        showProductDetails(hashProductId, { isBack: true });
        return true;
      }
      if (pageNames.includes(hashPage)) {
        showPage(hashPage, true);
        return true;
      }
      showPage('accueil', true);
      return false;
    }
    const initialHashRaw = window.location.hash.replace('#', '');
    const initialHashParts = initialHashRaw.split('/');
    const initialHash = initialHashParts[0];
    const initialProductToken = decodeURIComponent(initialHashParts.slice(1).join('/') || '');
    const initialProductId = resolveProductToken(initialProductToken);
    const pageNames = [...pages].map(page => page.dataset.page);
    const startPage = pageNames.includes(initialHash) ? initialHash : 'accueil';
    if (startPage === 'produit' && initialProductId && productMap[initialProductId]) {
      const initialSlug = encodeURIComponent(initialProductToken || normalizeProductToken(productMap[initialProductId].title || initialProductId));
      history.replaceState({ page: 'produit', productId: initialProductId }, '', `#produit/${initialSlug}`);
      showProductDetails(initialProductId, { isBack: true });
    } else {
      history.replaceState({ page: startPage }, '', `#${startPage}`);
      showPage(startPage, true);
    }
    window.addEventListener('popstate', (e) => {
      if (e.state.productImageModal && e.state.productId) {
        showProductDetails(e.state.productId, { isBack: true });
        openProductImageModal(e.state.imageIndex || 0, { pushHistory: false });
        return;
      }
      if (productImageModal.classList.contains('open')) {
        closeProductImageModal();
        if (e.state.page) {
          const currentPage = document.querySelector('.page.active').dataset.page;
          if (currentPage !== e.state.page) {
            showPage(e.state.page, true);
          }
        }
        return;
      }
      if (e.state && e.state.page) {
        if (e.state.page === 'produit' && e.state.productId && productMap[e.state.productId]) {
          showProductDetails(e.state.productId, { isBack: true });
        } else {
          showPage(e.state.page, true);
        }
      } else {
        const hashRaw = window.location.hash.replace('#', '');
        const hashParts = hashRaw.split('/');
        const hashPage = hashParts[0];
        const hashProductToken = decodeURIComponent(hashParts.slice(1).join('/') || '');
        const hashProductId = resolveProductToken(hashProductToken);
        if (hashPage === 'produit' && hashProductId && productMap[hashProductId]) {
          showProductDetails(hashProductId, { isBack: true });
        } else {
          showPage(pageNames.includes(hashPage) ? hashPage : 'accueil', true);
        }
      }
    });
    window.addEventListener('hashchange', () => {
      handleHashRoute();
    });
    window.__routeFallbackInit = true;
    faqItems.forEach(item => {
      const button = item.querySelector('.faq-question');
      const wrap = item.querySelector('.faq-answer-wrap');
      button.addEventListener('click', () => {
        const isActive = item.classList.contains('active');
        faqItems.forEach(other => {
          other.classList.remove('active');
          const otherWrap = other.querySelector('.faq-answer-wrap');
          if (otherWrap) otherWrap.style.maxHeight = null;
        });
        if (!isActive && wrap) {
          item.classList.add('active');
          wrap.style.maxHeight = wrap.scrollHeight + 'px';
        }
      });
    });
    function setCatalogueView(view) {
      if (!catalogueGrid || !['duo', 'grid', 'list'].includes(view)) return;
      catalogueGrid.dataset.view = view;
      catalogueViewButtons.forEach(button => {
        button.classList.toggle('active', button.dataset.catalogueView === view);
      });
    }
    function tokenizeSearchQuery(value) {
      const normalized = normalizeProductToken(value).trim();
      return normalized ? normalized.split('-').filter(Boolean) : [];
    }
    function applyCatalogueFilters() {
      const activeFilter = document.querySelector('.filter-btn.active')?.dataset.filter || 'tous';
      const query = catalogueSearchInput ? catalogueSearchInput.value.trim() : '';
      const searchTokens = tokenizeSearchQuery(query);
      let visibleCount = 0;
      catalogueCards.forEach(card => {
        const categoryMatch = activeFilter === 'tous' || card.dataset.category === activeFilter;
        const product = productMap[card.dataset.product];
        const keywordBank = normalizeProductToken(product?.searchKeywords || '');
        const searchMatch = !searchTokens.length || searchTokens.every(token => keywordBank.includes(token));
        const isVisible = categoryMatch && searchMatch;
        card.style.display = isVisible ? '' : 'none';
        if (isVisible) visibleCount += 1;
      });
      if (catalogueResultsEmpty) {
        catalogueResultsEmpty.classList.toggle('is-visible', visibleCount === 0);
      }
    }
    catalogueViewButtons.forEach(button => {
      button.addEventListener('click', () => {
        setCatalogueView(button.dataset.catalogueView);
      });
    });
    const viewButtons = document.querySelectorAll('.catalogue-view-btn');
    if (viewButtons.length > 0 && catalogueGrid) {
      viewButtons.forEach(btn => {
        btn.addEventListener('click', () => {
          const view = btn.getAttribute('data-catalogue-view');
          catalogueGrid.setAttribute('data-view', view);
          viewButtons.forEach(b => b.classList.remove('active'));
          btn.classList.add('active');
        });
      });
    }
    setCatalogueView(window.innerWidth <= 640 ? 'list' : 'grid');
    filterButtons.forEach(button => {
      button.addEventListener('click', () => {
        filterButtons.forEach(btn => btn.classList.remove('active'));
        button.classList.add('active');
        applyCatalogueFilters();
      });
    });
    if (catalogueSearchInput) {
      catalogueSearchInput.addEventListener('input', () => {
        applyCatalogueFilters();
      });
      catalogueSearchInput.addEventListener('search', () => {
        applyCatalogueFilters();
      });
    }
    applyCatalogueFilters();
    function updateQuoteButtons() {
      const text = quoteItems.length ? 'Voir mon devis en cours' : 'Demander mon devis';
      navQuoteButton.textContent = text;
      mobileQuoteButton.textContent = text;
      const totalQuantity = quoteItems.reduce((sum, item) => sum + item.quantity, 0);
      quoteCount.textContent = `${totalQuantity} article${totalQuantity > 1 ? 's' : ''}`;
      if (floatingQuoteBadge) floatingQuoteBadge.textContent = totalQuantity;
      if (floatingQuoteBtn) {
        if (totalQuantity > 0) floatingQuoteBtn.classList.add('visible');
        else floatingQuoteBtn.classList.remove('visible');
      }
    }
    function renderQuoteList(listElement, emptyElement) {
      listElement.innerHTML = '';
      if (!quoteItems.length) {
        listElement.appendChild(emptyElement);
        return;
      }
      quoteItems.forEach((item, index) => {
        const row = document.createElement('div');
        row.className = 'quote-item';
        row.innerHTML = `<div class="quote-item-main"><strong>${item.name}</strong><span>${item.price} € Quantité ${item.quantity}</span></div><div class="quote-item-actions"><div class="quote-item-qty"><button type="button" class="qty-btn quote-qty-minus" data-index="${index}">-</button><span class="qty-value">${item.quantity}</span><button type="button" class="qty-btn quote-qty-plus" data-index="${index}">+</button></div><button type="button" class="quote-detail" data-product="${item.id}">Détails du produit</button><button type="button" class="quote-remove" data-index="${index}">Retirer</button></div>`;
        listElement.appendChild(row);
      });
    }
    function syncQuoteUI() {
      renderQuoteList(quoteListMain, quoteEmptyMain);
      renderQuoteList(quoteListForm, quoteEmptyForm);
      quoteInput.value = quoteItems.map(item => `${item.name} x${item.quantity} é ${item.price}`).join(' | ');
      updateQuoteButtons();
    }
    function addItemToQuote(id, name, price, quantity = 1) {
      const safeQuantity = Math.max(1, quantity);
      const existingItem = quoteItems.find(item => item.id === id);
      if (!existingItem) {
        quoteItems.push({ id, name, price, quantity: safeQuantity });
      } else {
        existingItem.quantity += safeQuantity;
      }
      syncQuoteUI();
      showToast(`<span class="toast-mobile-text"> Article ajouté au devis</span><span class="toast-desktop-text"> <strong>${name}</strong><br><span class="toast-subtext">ajouté au devis</span></span>`);
    }
    addToQuoteButtons.forEach(button => {
      button.addEventListener('click', () => {
        if (!button.dataset.product) return;
        const quantity = button.id === 'product-page-add' ? getProductPageQuantity() : getCatalogueQuantity(button.dataset.product);
        addItemToQuote(button.dataset.product, button.dataset.item, button.dataset.price, quantity);
      });
    });
    productDetailButtons.forEach(button => {
      button.addEventListener('click', () => {
        showProductDetails(button.dataset.product);
      });
    });
    [quoteListMain, quoteListForm].forEach(list => {
      list.addEventListener('click', (e) => {
        const target = e.target;
        if (target.classList.contains('quote-remove')) {
          const index = Number(target.dataset.index);
          quoteItems.splice(index, 1);
          syncQuoteUI();
          formStatus.textContent = 'Un article a été retiré du devis.';
        }
        if (target.classList.contains('quote-qty-minus')) {
          const index = Number(target.dataset.index);
          if (quoteItems[index]) {
            quoteItems[index].quantity = Math.max(1, quoteItems[index].quantity - 1);
            syncQuoteUI();
          }
        }
        if (target.classList.contains('quote-qty-plus')) {
          const index = Number(target.dataset.index);
          if (quoteItems[index]) {
            quoteItems[index].quantity += 1;
            syncQuoteUI();
          }
        }
        if (target.classList.contains('quote-detail')) {
          showProductDetails(target.dataset.product);
        }
      });
    });
    catalogueGrid.addEventListener('click', (e) => {
      const qtyButton = e.target.closest('.qty-btn');
      if (qtyButton && qtyButton.dataset.context === 'catalogue') {
        const productId = qtyButton.dataset.product;
        const current = getCatalogueQuantity(productId);
        setCatalogueQuantity(productId, current + (qtyButton.classList.contains('qty-plus') ? 1 : -1));
        return;
      }
      const target = e.target;
      const navButton = target.closest('.gallery-nav');
      if (navButton) {
        const gallery = navButton.closest('.catalogue-card-img');
        const currentIndex = Number(gallery.dataset.index || 0);
        updateGallery(gallery, currentIndex + (navButton.classList.contains('gallery-next') ? 1 : -1));
        return;
      }
      const slideClick = target.closest('.catalogue-slide');
      if (slideClick) {
        const card = slideClick.closest('.catalogue-card');
        if (card && card.dataset.product) {
          showProductDetails(card.dataset.product);
        }
        return;
      }
      const imageClick = target.closest('.catalogue-card-img img, .catalogue-card-img');
      if (imageClick) {
        const card = imageClick.closest('.catalogue-card');
        if (card && card.dataset.product) {
          showProductDetails(card.dataset.product);
        }
      }
    });
    document.addEventListener('click', (e) => {
      const detailBtn = e.target.closest('.product-detail');
      if (!detailBtn) return;
      const productId = detailBtn.dataset.product;
      if (!productId) return;
      e.preventDefault();
      showProductDetails(productId);
    });
    productPageQtyMinus.addEventListener('click', () => {
      setProductPageQuantity(getProductPageQuantity() - 1);
    });
    productPageQtyPlus.addEventListener('click', () => {
      setProductPageQuantity(getProductPageQuantity() + 1);
    });
    if (jaugeSlider && jaugeValDisplay) {
      jaugeSlider.addEventListener('input', (e) => {
        jaugeValDisplay.textContent = e.target.value;
        if (e.target.value !== lastJaugeValue) {
          if ('vibrate' in navigator) {
            navigator.vibrate(15);
          }
          lastJaugeValue = e.target.value;
        }
      });
    }
    if (villeInput && suggestionsList) {
      let addressSearchTimeout;
      villeInput.addEventListener('input', (e) => {
        clearTimeout(addressSearchTimeout);
        const query = e.target.value.trim();
        if (query.length < 3) {
          suggestionsList.classList.remove('active');
          suggestionsList.innerHTML = '';
          return;
        }
        addressSearchTimeout = setTimeout(async () => {
          try {
            const response = await fetch(`https://api-adresse.data.gouv.fr/search/?q=${encodeURIComponent(query)}&limit=5`);
            const data = await response.json();
            const addresses = Array.isArray(data.features) ? data.features : [];
            suggestionsList.innerHTML = '';
            if (!addresses.length) {
              suggestionsList.classList.remove('active');
              return;
            }
            addresses.forEach((feature) => {
              const item = document.createElement('li');
              const fullLabel = feature.properties.label || '';
              item.className = 'suggestion-item';
              item.textContent = fullLabel;
              item.addEventListener('click', () => {
                villeInput.value = fullLabel;
                suggestionsList.classList.remove('active');
                suggestionsList.innerHTML = '';
              });
              suggestionsList.appendChild(item);
            });
            suggestionsList.classList.add('active');
          } catch (error) {
            console.error('Erreur avec la recherche d adresse :', error);
            suggestionsList.classList.remove('active');
          }
        }, 300);
      });
      document.addEventListener('click', (e) => {
        if (!villeInput.contains(e.target) && !suggestionsList.contains(e.target)) {
          suggestionsList.classList.remove('active');
        }
      });
    }
    function clearQuote() {
      quoteItems = [];
      syncQuoteUI();
      formStatus.textContent = 'La sélection du devis a été vidée.';
    }
    clearQuoteButton.addEventListener('click', clearQuote);
    clearQuoteTopButton.addEventListener('click', clearQuote);
    function formatDateFr(value) {
      if (!value) return '';
      const d = new Date(value + 'T00:00:00');
      return new Intl.DateTimeFormat('fr-FR', { day: '2-digit', month: 'short', year: 'numeric' }).format(d);
    }
    function parseDateTimeValue(value) {
      if (!value) return { date: '', time: '' };
      const normalized = value.replace(' ', 'T');
      const [date = '', time = ''] = normalized.split('T');
      return { date, time: time.slice(0, 5) };
    }
    function combineDateTimeValue(dateValue, timeValue) {
      if (!dateValue) return '';
      return `${dateValue}T${timeValue || '00:00'}`;
    }
    function formatTimeFr(value) {
      if (!value) return '--:--';
      return value.slice(0, 5);
    }
    function formatDateTimeFr(value) {
      if (!value) return '';
      const { date, time } = parseDateTimeValue(value);
      if (!date) return '';
      return `${formatDateFr(date)} à  ${formatTimeFr(time)}`;
    }
    function ensureChronologicalDateRange() {
      if (!dateStartInput.value || !dateEndInput.value) return;
      const startDate = new Date(dateStartInput.value);
      const endDate = new Date(dateEndInput.value);
      if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) return;
      if (endDate < startDate) {
        dateEndInput.value = dateStartInput.value;
        timeEndInput.value = parseDateTimeValue(dateEndInput.value).time || timeStartInput.value || '18:00';
      }
    }
    function toInputDate(date) {
      const y = date.getFullYear();
      const m = String(date.getMonth() + 1).padStart(2, '0');
      const d = String(date.getDate()).padStart(2, '0');
      return `${y}-${m}-${d}`;
    }
    function parseInputDate(value) {
      if (!value) return null;
      const [y, m, d] = value.split('-').map(Number);
      return new Date(y, m - 1, d);
    }
    function isSameDay(a, b) {
      return a && b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
    }
    function syncDateRangeUI() {
      const start = dateStartInput.value;
      const end = dateEndInput.value;
      const startParts = parseDateTimeValue(start);
      const endParts = parseDateTimeValue(end);
      dateRangeTriggerValue.textContent = start && end
        ? `${formatDateTimeFr(start)} ${formatDateTimeFr(end)}`
        : start
          ? `${formatDateTimeFr(start)} choisir la fin`
          : 'Choisir une première et une seconde date avec l\'heure';
      calendarPreviewStart.innerHTML = start
        ? `<span class="calendar-preview-main">${formatDateFr(startParts.date)}</span><span class="calendar-preview-sub">${formatTimeFr(startParts.time)}</span>`
        : `<span class="calendar-preview-main">Première date</span><span class="calendar-preview-sub">Choisir le début</span>`;
      calendarPreviewEnd.innerHTML = end
        ? `<span class="calendar-preview-main">${formatDateFr(endParts.date)}</span><span class="calendar-preview-sub">${formatTimeFr(endParts.time)}</span>`
        : `<span class="calendar-preview-main">Seconde date</span><span class="calendar-preview-sub">Choisir la fin</span>`;
      calendarPreviewStart.classList.toggle('placeholder', !start);
      calendarPreviewEnd.classList.toggle('placeholder', !end);
    }
    function renderMonth(baseDate) {
      const wrapper = document.createElement('div');
      const title = document.createElement('div');
      title.className = 'calendar-month-title';
      title.textContent = new Intl.DateTimeFormat('fr-FR', { month: 'long', year: 'numeric' }).format(baseDate);
      wrapper.appendChild(title);
      const weekdays = document.createElement('div');
      weekdays.className = 'calendar-weekdays';
      ['l', 'm', 'm', 'j', 'v', 's', 'd'].forEach(day => {
        const span = document.createElement('span');
        span.textContent = day;
        weekdays.appendChild(span);
      });
      wrapper.appendChild(weekdays);
      const grid = document.createElement('div');
      grid.className = 'calendar-days';
      const year = baseDate.getFullYear();
      const month = baseDate.getMonth();
      const firstDay = new Date(year, month, 1);
      const firstWeekday = (firstDay.getDay() + 6) % 7;
      const daysInMonth = new Date(year, month + 1, 0).getDate();
      const start = parseInputDate(parseDateTimeValue(dateStartInput.value).date);
      const end = parseInputDate(parseDateTimeValue(dateEndInput.value).date);
      for (let i = 0; i < firstWeekday; i++) {
        const empty = document.createElement('div');
        empty.className = 'calendar-day-empty';
        grid.appendChild(empty);
      }
      for (let day = 1; day <= daysInMonth; day++) {
        const date = new Date(year, month, day);
        const btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'calendar-day';
        btn.textContent = day;
        if (start && end && date >= start && date <= end) btn.classList.add('in-range');
        if (start && isSameDay(date, start)) btn.classList.add('range-start');
        if (end && isSameDay(date, end)) btn.classList.add('range-end');
        btn.addEventListener('click', () => {
          const value = toInputDate(date);
          const currentStart = parseDateTimeValue(dateStartInput.value).date;
          const currentEnd = parseDateTimeValue(dateEndInput.value).date;
          if (!currentStart || currentEnd) {
            dateStartInput.value = combineDateTimeValue(value, timeStartInput.value);
            dateEndInput.value = '';
          } else if (value < currentStart) {
            dateEndInput.value = combineDateTimeValue(currentStart, timeEndInput.value);
            dateStartInput.value = combineDateTimeValue(value, timeStartInput.value);
          } else {
            dateEndInput.value = combineDateTimeValue(value, timeEndInput.value);
          }
          ensureChronologicalDateRange();
          syncDateRangeUI();
          renderCalendar();
        });
        grid.appendChild(btn);
      }
      wrapper.appendChild(grid);
      return wrapper;
    }
    function renderCalendar() {
      calendarMonths.innerHTML = '';
      const first = new Date(calendarVisibleMonth.getFullYear(), calendarVisibleMonth.getMonth(), 1);
      const second = new Date(calendarVisibleMonth.getFullYear(), calendarVisibleMonth.getMonth() + 1, 1);
      if (calendarCurrentMonth) {
        calendarCurrentMonth.textContent = new Intl.DateTimeFormat('fr-FR', { month: 'long', year: 'numeric' }).format(first);
      }
      calendarMonths.appendChild(renderMonth(first));
      calendarMonths.appendChild(renderMonth(second));
    }
    function openCalendar() {
      const startParts = parseDateTimeValue(dateStartInput.value);
      const endParts = parseDateTimeValue(dateEndInput.value);
      timeStartInput.value = startParts.time || '18:00';
      timeEndInput.value = endParts.time || '23:00';
      calendarModal.classList.add('open');
      document.body.style.overflow = 'hidden';
      renderCalendar();
      syncDateRangeUI();
    }
    function closeCalendar() {
      calendarModal.classList.remove('open');
      document.body.style.overflow = '';
    }
    dateRangeTrigger.addEventListener('click', openCalendar);
    calendarPrev.addEventListener('click', () => {
      calendarVisibleMonth = new Date(calendarVisibleMonth.getFullYear(), calendarVisibleMonth.getMonth() - 1, 1);
      renderCalendar();
    });
    calendarNext.addEventListener('click', () => {
      calendarVisibleMonth = new Date(calendarVisibleMonth.getFullYear(), calendarVisibleMonth.getMonth() + 1, 1);
      renderCalendar();
    });
    calendarReset.addEventListener('click', () => {
      dateStartInput.value = '';
      dateEndInput.value = '';
      timeStartInput.value = '18:00';
      timeEndInput.value = '23:00';
      syncDateRangeUI();
      renderCalendar();
    });
    timeStartInput.addEventListener('input', () => {
      const startDate = parseDateTimeValue(dateStartInput.value).date;
      if (startDate) {
        dateStartInput.value = combineDateTimeValue(startDate, timeStartInput.value);
        ensureChronologicalDateRange();
        syncDateRangeUI();
      }
    });
    timeEndInput.addEventListener('input', () => {
      const endDate = parseDateTimeValue(dateEndInput.value).date;
      if (endDate) {
        dateEndInput.value = combineDateTimeValue(endDate, timeEndInput.value);
        ensureChronologicalDateRange();
        syncDateRangeUI();
      }
    });
    calendarConfirm.addEventListener('click', closeCalendar);
    calendarModal.addEventListener('click', (e) => {
      if (e.target === calendarModal) closeCalendar();
    });
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape' && calendarModal.classList.contains('open')) closeCalendar();
    });
    productGalleryMain.addEventListener('click', (e) => {
      const gridItem = e.target.closest('.grid-item');
      if (!gridItem) return;
      const index = Number(gridItem.dataset.index || 0);
      syncProductGallery(index);
      openProductImageModal(index);
    });
    productGalleryThumbs.addEventListener('click', (e) => {
      const thumb = e.target.closest('.product-thumb');
      if (!thumb) return;
      const index = Number(thumb.dataset.index);
      updateGallery(productGalleryMain, index);
      syncProductThumbs(index);
    });
    lightboxZoomSlider.addEventListener('input', (e) => {
      lightboxScale = Number(e.target.value || 1);
      if (lightboxScale <= 1) {
        lightboxPointX = 0;
        lightboxPointY = 0;
      }
      setLightboxTransform();
    });
    const startLightboxPan = (e) => {
      const activeMedia = getActiveLightboxMedia();
      if (!activeMedia || lightboxScale <= 1) return;
      if (!e.target.closest('.lightbox-img-wrapper')) return;
      e.preventDefault();
      lightboxPanning = true;
      const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
      const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
      lightboxStartX = clientX - lightboxPointX;
      lightboxStartY = clientY - lightboxPointY;
    };
    const moveLightboxPan = (e) => {
      if (!lightboxPanning || lightboxScale <= 1) return;
      e.preventDefault();
      const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
      const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
      lightboxPointX = clientX - lightboxStartX;
      lightboxPointY = clientY - lightboxStartY;
      setLightboxTransform();
    };
    const endLightboxPan = () => {
      lightboxPanning = false;
    };
    productImageStage.addEventListener('mousedown', startLightboxPan);
    productImageStage.addEventListener('touchstart', startLightboxPan, { passive: false });
    window.addEventListener('mousemove', moveLightboxPan);
    window.addEventListener('mouseup', endLightboxPan);
    window.addEventListener('touchmove', moveLightboxPan, { passive: false });
    window.addEventListener('touchend', endLightboxPan);
    productImageClose.addEventListener('click', () => closeProductImageModal({ useHistoryBack: true }));
    productImageModal.addEventListener('click', (e) => {
      if (e.target === productImageModal) {
        closeProductImageModal({ useHistoryBack: true });
        return;
      }
      const navButton = e.target.closest('.gallery-nav');
      if (!navButton) return;
      navigateProductImageModal(navButton.classList.contains('gallery-next') ? 1 : -1);
    });
    document.addEventListener('keydown', (e) => {
      if (!productImageModal.classList.contains('open')) return;
      if (e.key === 'Escape') closeProductImageModal({ useHistoryBack: true });
      if (e.key === 'ArrowRight') navigateProductImageModal(1);
      if (e.key === 'ArrowLeft') navigateProductImageModal(-1);
    });
    (function() {
      emailjs.init('vJcHzXRcAsb9FTvgb');
    })();
    if (window.innerWidth > 1024 && typeof mdtimepicker !== 'undefined') {
      mdtimepicker("input[type='time']", {
        format: 'hh:mm',
        theme: 'dark',
        clearBtn: true,
        is24hour: true
      });
    }
    const typeEventSelect = document.querySelector('#type');
    if (typeEventSelect && typeof Choices !== 'undefined') {
      new Choices(typeEventSelect, {
        searchEnabled: false,
        itemSelectText: '',
        shouldSort: false
      });
    }
    form.addEventListener('submit', (e) => {
      e.preventDefault();
      const submitBtn = form.querySelector('.btn-submit');
      submitBtn.disabled = true;
      submitBtn.textContent = 'Envoi en cours...';
      formStatus.classList.remove('is-success', 'is-error');
      formStatus.textContent = '';
      const templateParams = {
        nom: document.getElementById('nom').value,
        email: document.getElementById('email').value,
        telephone: document.getElementById('telephone').value,
        ville: document.getElementById('ville').value,
        jauge: document.getElementById('jauge').value,
        type_event: document.getElementById('type').value,
        date_debut: formatDateTimeFr(document.getElementById('date-debut').value),
        date_fin: formatDateTimeFr(document.getElementById('date-fin').value),
        selection_catalogue: document.getElementById('quote-items-input').value,
        message: document.getElementById('message').value
      };
      const sendToAdmin = emailjs.send('service_4j6v6k3', 'template_8zck8wg', templateParams);
      const sendToClient = emailjs.send('service_4j6v6k3', 'template_0kg4ccr', templateParams);
      Promise.all([sendToAdmin, sendToClient])
        .then(() => {
          formStatus.classList.remove('is-error');
          formStatus.classList.add('is-success');
          formStatus.textContent = "Merci ! Votre demande et l'email de confirmation ont été envoyés.";
          if (typeof Swal !== 'undefined') {
            Swal.fire({
              title: 'Demande envoyée !',
              text: "L'équipe LUKA LIVE a bien reçu votre demande. Nous vous recontactons sous 24h avec votre devis.",
              icon: 'success',
              confirmButtonText: 'Super, merci !',
              customClass: { popup: 'luka-swal-popup' }
            });
          }
          setTimeout(() => {
            form.reset();
            dateStartInput.value = '';
            dateEndInput.value = '';
            clearQuote();
            syncDateRangeUI();
            submitBtn.disabled = false;
            submitBtn.textContent = 'Envoyer la demande';
            formStatus.textContent = '';
            formStatus.classList.remove('is-success', 'is-error');
          }, 3000);
        })
        .catch((error) => {
          formStatus.classList.remove('is-success');
          formStatus.classList.add('is-error');
          formStatus.textContent = "Erreur lors de l'envoi. Veuillez réessayer.";
          submitBtn.disabled = false;
          submitBtn.textContent = 'Réessayer';
          console.error('Erreur double envoi:', error);
        });
    });
    faqContactForm.addEventListener('submit', (e) => {
      e.preventDefault();
      const submitBtn = faqContactForm.querySelector('.btn-submit');
      submitBtn.disabled = true;
      submitBtn.textContent = 'Envoi en cours...';
      faqFormStatus.classList.remove('is-success', 'is-error');
      faqFormStatus.textContent = '';
      const templateParams = {
        nom: 'Question FAQ',
        email: document.getElementById('faq-email').value,
        telephone: '',
        ville: '',
        jauge: '',
        type_event: 'question-faq',
        date_debut: '',
        date_fin: '',
        selection_catalogue: '',
        message: document.getElementById('faq-message').value
      };
      emailjs.send('service_4j6v6k3', 'template_8zck8wg', templateParams)
        .then(() => {
          faqFormStatus.classList.remove('is-error');
          faqFormStatus.classList.add('is-success');
          faqFormStatus.textContent = 'Merci ! Votre question a bien été envoyée.';
          setTimeout(() => {
            faqContactForm.reset();
            submitBtn.disabled = false;
            submitBtn.textContent = 'Envoyer ma question';
            faqFormStatus.textContent = '';
            faqFormStatus.classList.remove('is-success', 'is-error');
          }, 3000);
        })
        .catch((error) => {
          faqFormStatus.classList.remove('is-success');
          faqFormStatus.classList.add('is-error');
          faqFormStatus.textContent = "Erreur lors de l'envoi. Veuillez réessayer.";
          submitBtn.disabled = false;
          submitBtn.textContent = 'Réessayer';
          console.error('Erreur formulaire FAQ:', error);
        });
    });
    function openQuickContact() {
      if (!quickBtn || !quickPanel) return;
      quickPanel.classList.add('is-open');
      quickBtn.setAttribute('aria-expanded', 'true');
    }
    function closeQuickContact() {
      if (!quickBtn || !quickPanel) return;
      quickPanel.classList.remove('is-open');
      quickBtn.setAttribute('aria-expanded', 'false');
    }
    function initHeroPanelTilt() {
      if (!heroPanels.length) return;
      const canTilt = window.matchMedia('(hover: hover) and (pointer: fine)').matches && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
      heroPanels.forEach((panel) => {
        if (panel.dataset.tiltInit === '1') return;
        panel.dataset.tiltInit = '1';
        const visual = panel.closest('.hero-visual');
        if (!canTilt) {
          panel.style.setProperty('--panel-rotate-x', '0deg');
          panel.style.setProperty('--panel-rotate-y', '0deg');
          panel.style.setProperty('--panel-shift-x', '0px');
          panel.style.setProperty('--panel-shift-y', '0px');
          panel.style.setProperty('--pointer-x', '50%');
          panel.style.setProperty('--pointer-y', '50%');
          panel.style.setProperty('--shine-angle', '160deg');
          panel.style.setProperty('--shine-opacity', '0.5');
          if (visual) visual.style.setProperty('--hero-halo-boost', '0');
          return;
        }
        const state = {
          currentRotateX: 0,
          currentRotateY: 0,
          currentShiftX: 0,
          currentShiftY: 0,
          currentPointerX: 50,
          currentPointerY: 50,
          currentShineOpacity: 0.5,
          currentShineAngle: 160,
          currentHaloBoost: 0,
          targetRotateX: 0,
          targetRotateY: 0,
          targetShiftX: 0,
          targetShiftY: 0,
          targetPointerX: 50,
          targetPointerY: 50,
          targetShineOpacity: 0.5,
          targetShineAngle: 160,
          targetHaloBoost: 0,
          frame: null,
          isHovering: false
        };
        const render = () => {
          const tiltEase = state.isHovering ? 0.11 : 0.08;
          const shiftEase = state.isHovering ? 0.1 : 0.075;
          const pointerEase = state.isHovering ? 0.13 : 0.09;
          const shineEase = state.isHovering ? 0.1 : 0.075;
          const haloEase = state.isHovering ? 0.085 : 0.06;
          state.currentRotateX += (state.targetRotateX - state.currentRotateX) * tiltEase;
          state.currentRotateY += (state.targetRotateY - state.currentRotateY) * tiltEase;
          state.currentShiftX += (state.targetShiftX - state.currentShiftX) * shiftEase;
          state.currentShiftY += (state.targetShiftY - state.currentShiftY) * shiftEase;
          state.currentPointerX += (state.targetPointerX - state.currentPointerX) * pointerEase;
          state.currentPointerY += (state.targetPointerY - state.currentPointerY) * pointerEase;
          state.currentShineOpacity += (state.targetShineOpacity - state.currentShineOpacity) * shineEase;
          state.currentShineAngle += (state.targetShineAngle - state.currentShineAngle) * shineEase;
          state.currentHaloBoost += (state.targetHaloBoost - state.currentHaloBoost) * haloEase;
          panel.style.setProperty('--panel-rotate-x', `${state.currentRotateX.toFixed(2)}deg`);
          panel.style.setProperty('--panel-rotate-y', `${state.currentRotateY.toFixed(2)}deg`);
          panel.style.setProperty('--panel-shift-x', `${state.currentShiftX.toFixed(2)}px`);
          panel.style.setProperty('--panel-shift-y', `${state.currentShiftY.toFixed(2)}px`);
          panel.style.setProperty('--pointer-x', `${state.currentPointerX.toFixed(2)}%`);
          panel.style.setProperty('--pointer-y', `${state.currentPointerY.toFixed(2)}%`);
          panel.style.setProperty('--shine-opacity', state.currentShineOpacity.toFixed(3));
          panel.style.setProperty('--shine-angle', `${state.currentShineAngle.toFixed(2)}deg`);
          if (visual) visual.style.setProperty('--hero-halo-boost', state.currentHaloBoost.toFixed(3));
          const settled =
            Math.abs(state.targetRotateX - state.currentRotateX) < 0.02 &&
            Math.abs(state.targetRotateY - state.currentRotateY) < 0.02 &&
            Math.abs(state.targetShiftX - state.currentShiftX) < 0.05 &&
            Math.abs(state.targetShiftY - state.currentShiftY) < 0.05 &&
            Math.abs(state.targetPointerX - state.currentPointerX) < 0.08 &&
            Math.abs(state.targetPointerY - state.currentPointerY) < 0.08 &&
            Math.abs(state.targetShineOpacity - state.currentShineOpacity) < 0.01 &&
            Math.abs(state.targetShineAngle - state.currentShineAngle) < 0.2 &&
            Math.abs(state.targetHaloBoost - state.currentHaloBoost) < 0.01;
          if (settled) {
            state.frame = null;
            return;
          }
          state.frame = window.requestAnimationFrame(render);
        };
        const requestRender = () => {
          if (state.frame !== null) return;
          state.frame = window.requestAnimationFrame(render);
        };
        const updateFromPointer = (event) => {
          const rect = panel.getBoundingClientRect();
          const pointerX = clamp((event.clientX - rect.left) / rect.width, 0, 1);
          const pointerY = clamp((event.clientY - rect.top) / rect.height, 0, 1);
          const normalizedX = (pointerX - 0.5) * 2;
          const normalizedY = (pointerY - 0.5) * 2;
          const energy = Math.min(1, Math.hypot(normalizedX, normalizedY));
          state.targetRotateX = -normalizedY * 8;
          state.targetRotateY = normalizedX * 10;
          state.targetShiftX = normalizedX * 18;
          state.targetShiftY = normalizedY * 14;
          state.targetPointerX = clamp(pointerX * 100, 6, 94);
          state.targetPointerY = clamp(pointerY * 100, 8, 92);
          state.targetShineOpacity = 0.42 + energy * 0.2;
          state.targetShineAngle = 160 + normalizedX * 32 - normalizedY * 10;
          state.targetHaloBoost = 1.35 + energy * 1.85;
          state.isHovering = true;
          panel.classList.add('is-tilting');
          requestRender();
        };
        const resetTilt = () => {
          state.targetRotateX = 0;
          state.targetRotateY = 0;
          state.targetShiftX = 0;
          state.targetShiftY = 0;
          state.targetPointerX = 50;
          state.targetPointerY = 50;
          state.targetShineOpacity = 0.5;
          state.targetShineAngle = 160;
          state.targetHaloBoost = 0;
          state.isHovering = false;
          panel.classList.remove('is-tilting');
          requestRender();
        };
        panel.addEventListener('pointerenter', updateFromPointer);
        panel.addEventListener('pointermove', updateFromPointer);
        panel.addEventListener('pointerleave', resetTilt);
      });
    }
    function centerAccueilHeroPanelOnLoad() {
      if (window.innerWidth <= 860) return;
      const accueilPage = document.getElementById('page-accueil');
      if (!accueilPage || !accueilPage.classList.contains('active')) return;
      if (window.__heroLogoCenteredOnce) return;
      const visual = accueilPage.querySelector('.hero-visual.brand-section');
      const panel = accueilPage.querySelector('.hero-panel');
      const content = accueilPage.querySelector('.hero-content');
      const contentWrapper = accueilPage.querySelector('.hero-content-wrapper');
      const logo = accueilPage.querySelector('.hero-brand-image');
      if (!visual || !panel) return;
      visual.style.setProperty('transform', 'translate3d(0, 0, 0)', 'important');
      if (content) content.style.setProperty('transform', 'translate3d(0, 0, 0)', 'important');
      if (contentWrapper) contentWrapper.style.setProperty('transform', 'translate3d(0, 0, 0)', 'important');
      const measureAndCenter = () => {
        const targetRect = (logo && logo.offsetHeight > 0) ? logo.getBoundingClientRect() : panel.getBoundingClientRect();
        if (!targetRect.height) return;
        const viewportCenterY = window.innerHeight * 0.5;
        const targetCenterY = targetRect.top + (targetRect.height * 0.5);
        const deltaY = Math.round(viewportCenterY - targetCenterY);
        visual.style.setProperty('transform', `translate3d(0, ${deltaY}px, 0)`, 'important');
        if (content) content.style.setProperty('transform', `translate3d(0, ${deltaY}px, 0)`, 'important');
        if (contentWrapper) contentWrapper.style.setProperty('transform', `translate3d(0, ${deltaY}px, 0)`, 'important');
        window.__heroLogoCenteredOnce = true;
      };
      requestAnimationFrame(measureAndCenter);
    }
    function toggleMarqueeAtPageBottom() {
      const doc = document.documentElement;
      const threshold = 2;
      const atBottom = (window.innerHeight + window.scrollY) >= (doc.scrollHeight - threshold);
      document.body.classList.toggle('hide-marquee-at-bottom', atBottom);
    }
    function initQuickContact() {
      quickBtn = document.getElementById('quickContactBtn');
      quickPanel = document.getElementById('quickContactPanel');
      closeQuickBtn = document.getElementById('closeContactPanel');
      quickForm = document.getElementById('quickContactForm');
      if (!quickBtn || !quickPanel || !closeQuickBtn || !quickForm || quickBtn.dataset.quickInit === '1') return;
      quickBtn.dataset.quickInit = '1';
      quickBtn.addEventListener('click', () => {
        if (quickPanel.classList.contains('is-open')) {
          closeQuickContact();
        } else {
          openQuickContact();
        }
      });
      closeQuickBtn.addEventListener('click', () => {
        closeQuickContact();
      });
      document.getElementById('quickContactForm').addEventListener('submit', function(e) {
        e.preventDefault();
        const form = this;
        const btn = form.querySelector('button[type="submit"]');
        const originalText = btn.textContent;
        btn.disabled = true;
        btn.textContent = 'Envoi...';
        fetch('https://api.web3forms.com/submit', {
          method: 'POST',
          body: new FormData(form)
        })
          .then(async (response) => {
            if (response.status === 200) {
              showNotification("Message envoyé avec succès ! ✅", "success");
              form.reset();
              setTimeout(() => {
                document.getElementById('quickContactPanel').classList.remove('active');
                btn.textContent = originalText;
                btn.disabled = false;
              }, 1000);
            } else {
              showNotification("Erreur lors de l'envoi... ❌", "error");
              btn.textContent = originalText;
              btn.disabled = false;
            }
          })
          .catch(() => {
            showNotification("Erreur réseau ❌", "error");
            btn.disabled = false;
            btn.textContent = originalText;
          });
      });
      document.addEventListener('click', (e) => {
        if (!quickPanel.contains(e.target) && !quickBtn.contains(e.target)) {
          quickPanel.classList.remove('is-open');
          quickBtn.setAttribute('aria-expanded', 'false');
        }
      });
    }
    initQuickContact();
    window.addEventListener('load', initQuickContact);
    initHeroPanelTilt();
    window.addEventListener('load', centerAccueilHeroPanelOnLoad);
    setTimeout(centerAccueilHeroPanelOnLoad, 2200);
    toggleMarqueeAtPageBottom();
    window.addEventListener('load', toggleMarqueeAtPageBottom);
    window.addEventListener('scroll', toggleMarqueeAtPageBottom, { passive: true });
    window.addEventListener('resize', toggleMarqueeAtPageBottom);
    setupCatalogueGalleries();
    buildProductMap();
    Object.keys(productMap).forEach(productId => setCatalogueQuantity(productId, 1));
    syncQuoteUI();
    syncDateRangeUI();
    initReveal();
    window.addEventListener('load', () => {
      setTimeout(() => {
        const splash = document.getElementById('loading-screen') || document.getElementById('splash-screen');
        if (splash) splash.classList.add('hidden');
        document.body.classList.add('splash-done');
      }, 2000);
    });
  </script>
  <div class="quick-contact-wrapper desktop-only">
    <button class="quick-contact-btn" id="quickContactBtn" aria-label="Ouvrir le contact" aria-expanded="false" type="button">
        <svg viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path></svg>
    </button>
    <div class="quick-contact-panel" id="quickContactPanel">
        <div class="panel-header">
            <h3>Une question ?</h3>
            <button class="close-panel" id="closeContactPanel" type="button">&times;</button>
        </div>
        <p class="panel-subtitle">Envoyez-nous un message rapide et nous vous répondrons au plus vite.</p>
        <form id="quickContactForm" action="https://api.web3forms.com/submit" method="POST">
            <input type="hidden" name="access_key" value="ec1dcb9f-02aa-4344-b24a-8b1ea9a99e0f">
            <input type="hidden" name="name" value="Contact rapide site">
            <input type="checkbox" name="botcheck" class="hidden hidden-display-none" />
            <label for="quick-email">Email</label>
            <input type="email" id="quick-email" name="email" placeholder="jeandupont@gmail.com" required>
            <label for="quick-message">Message</label>
            <textarea id="quick-message" name="message" placeholder="Votre message..." required rows="4"></textarea>
            <button type="submit" class="submit-btn">Envoyer</button>
        </form>
    </div>
</div>
<script>
  (function initRouteFallback() {
    if (window.__routeFallbackInit) return;
    window.__routeFallbackInit = true;
    const pages = Array.from(document.querySelectorAll('.page'));
    if (!pages.length) return;
    const pageNames = pages.map((p) => p.dataset.page).filter(Boolean);
    const normalizeToken = (value) => String(value || '')
      .toLowerCase()
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '')
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/^-+|-+$/g, '');
    const resolveTokenToProductId = (token) => {
      const raw = String(token || '').trim();
      if (!raw || typeof window.showProductDetails !== 'function') return (window.__lastProductId || '');
      if (window.productMap && window.productMap[raw]) return raw;
      const cards = Array.from(document.querySelectorAll('.catalogue-card[data-product]'));
      const norm = normalizeToken(raw);
      let partial = '';
      for (const card of cards) {
        const id = card.dataset.product || '';
        const title = (card.querySelector('h3')?.textContent || '').trim();
        if (!id) continue;
        if (norm === normalizeToken(id) || norm === normalizeToken(title)) return id;
        if (!partial && (normalizeToken(id).includes(norm) || normalizeToken(title).includes(norm))) partial = id;
      }
      return partial || window.__lastProductId || (cards[0]?.dataset.product || '');
    };
    const applyPage = (name, productId = '') => {
      const target = pageNames.includes(name) ? name : 'accueil';
      pages.forEach((page) => {
        const isTarget = page.dataset.page === target;
        page.classList.toggle('active', isTarget);
        page.style.display = isTarget ? 'block' : 'none';
      });
      if (target === 'produit' && productId && typeof window.showProductDetails === 'function') {
        const resolvedId = resolveTokenToProductId(productId);
        window.showProductDetails(resolvedId, { isBack: true });
      }
    };
    document.addEventListener('click', (e) => {
      const link = e.target.closest('.route-link[data-page]');
      if (!link) return;
      const page = link.getAttribute('data-page');
      if (!page) return;
      e.preventDefault();
      applyPage(page);
      if (window.location.hash !== `#${page}`) {
        history.pushState({ page }, '', `#${page}`);
      }
      window.scrollTo({ top: 0, behavior: 'auto' });
    }, true);
    window.addEventListener('hashchange', () => {
      const raw = window.location.hash.replace('#', '');
      const parts = raw.split('/');
      const page = parts[0] || '';
      const productId = decodeURIComponent(parts.slice(1).join('/') || '');
      applyPage(page, productId);
    });
    const initRaw = window.location.hash.replace('#', '');
    const initParts = initRaw.split('/');
    const initPage = initParts[0] || '';
    const initProductId = decodeURIComponent(initParts.slice(1).join('/') || '');
    applyPage(initPage, initProductId);
  })();
</script>
<div class="cookie-consent" id="cookie-consent" aria-live="polite" role="dialog" aria-label="Gestion des cookies">
  <div class="cookie-consent-row">
    <div class="cookie-consent-copy">
      <strong>Ce site utilise des cookies</strong>
      <p>Nous utilisons des cookies nécessaires au fonctionnement du site et, avec votre accord, des cookies optionnels pour améliorer l'expérience.</p>
    </div>
    <div class="cookie-actions">
      <button class="cookie-btn" type="button" id="cookie-customize">Personnaliser</button>
      <button class="cookie-btn" type="button" id="cookie-reject">Refuser</button>
      <button class="cookie-btn primary" type="button" id="cookie-accept">Accepter</button>
    </div>
  </div>
  <div class="cookie-panel" id="cookie-panel">
    <div class="cookie-option">
      <label>
        Cookies nécessaires
        <small>Indispensables au fonctionnement du site.</small>
      </label>
      <input type="checkbox" checked disabled />
    </div>
    <div class="cookie-option">
      <label for="cookie-analytics">
        Cookies de mesure d'audience
        <small>Nous aident à comprendre la fréquentation du site.</small>
      </label>
      <input type="checkbox" id="cookie-analytics" />
    </div>
    <div class="cookie-option">
      <label for="cookie-marketing">
        Cookies marketing
        <small>Permettent des contenus et campagnes personnalisés.</small>
      </label>
      <input type="checkbox" id="cookie-marketing" />
    </div>
    <div class="cookie-actions cookie-actions-mt-06">
      <button class="cookie-btn primary" type="button" id="cookie-save">Enregistrer mes choix</button>
    </div>
  </div>
</div>
<script>
  (function initCookieConsent() {
    const KEY = 'luka_cookie_consent_v1';
    const banner = document.getElementById('cookie-consent');
    const panel = document.getElementById('cookie-panel');
    const btnAccept = document.getElementById('cookie-accept');
    const btnReject = document.getElementById('cookie-reject');
    const btnCustomize = document.getElementById('cookie-customize');
    const btnSave = document.getElementById('cookie-save');
    const chkAnalytics = document.getElementById('cookie-analytics');
    const chkMarketing = document.getElementById('cookie-marketing');
    if (!banner || !btnAccept || !btnReject || !btnCustomize || !btnSave || !chkAnalytics || !chkMarketing) return;

    const readConsent = () => {
      try {
        const raw = localStorage.getItem(KEY);
        if (!raw) return null;
        const parsed = JSON.parse(raw);
        if (!parsed || typeof parsed !== 'object') return null;
        return parsed;
      } catch (_) {
        return null;
      }
    };

    const writeConsent = (consent) => {
      const payload = {
        necessary: true,
        analytics: !!consent.analytics,
        marketing: !!consent.marketing,
        updatedAt: new Date().toISOString()
      };
      localStorage.setItem(KEY, JSON.stringify(payload));
      window.cookieConsent = payload;
    };

    const openBanner = () => banner.classList.add('is-open');
    const closeBanner = () => banner.classList.remove('is-open');
    const openPanel = () => panel.classList.add('is-open');
    const closePanel = () => panel.classList.remove('is-open');

    const existing = readConsent();
    if (existing) {
      window.cookieConsent = existing;
      closeBanner();
    } else {
      openBanner();
    }

    btnAccept.addEventListener('click', () => {
      writeConsent({ analytics: true, marketing: true });
      closeBanner();
    });

    btnReject.addEventListener('click', () => {
      writeConsent({ analytics: false, marketing: false });
      closeBanner();
    });

    btnCustomize.addEventListener('click', () => {
      if (panel.classList.contains('is-open')) {
        closePanel();
      } else {
        const current = readConsent();
        chkAnalytics.checked = !!(current && current.analytics);
        chkMarketing.checked = !!(current && current.marketing);
        openPanel();
      }
    });

    btnSave.addEventListener('click', () => {
      writeConsent({ analytics: chkAnalytics.checked, marketing: chkMarketing.checked });
      closeBanner();
      closePanel();
    });
  })();
</script>
</body>
</html>