templates/Parent/DetailsSejour.html.twig line 1

Open in your IDE?
  1. {% extends "Parent/LayoutParent.html.twig" %} {% block LinksCss %}
  2. {{ parent() }}
  3. <script src="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.js" defer></script>
  4. <link rel="stylesheet" href="{{ '/css/Parent/css/premiercnx.css' }}" />
  5. <link href="{{ asset('css/Parent/css/detailsejour.css') }}" type="text/css" rel="stylesheet" />
  6. <link rel="stylesheet" href="{{ '/css/Accompagnateur/imgzoom.css' }}" />
  7. <link rel="stylesheet" href="{{ asset('Plugins/css/dropzone.css') }}" />
  8. <link rel="stylesheet" href="{{ asset('css/splide.min.css') }}" />
  9. <link rel="stylesheet" href="{{ asset('css/favorites-sidebar.css') }}" />
  10. {% set destination = "detailsejour" %}
  11. <style>
  12. .slide img {
  13.   max-width: 100%;
  14.   max-height: 100%;
  15.   object-fit: contain;
  16.   border-radius: 8px;
  17.   box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
  18.   transition: transform 0.3s ease;
  19.   cursor: pointer;
  20. }
  21. .slide img.zoomed {
  22.   transform: scale(1.5);
  23.   cursor: zoom-in;
  24. }
  25. </style>
  26. {% endblock %} {% set pageMenu = app.session.get('pageMenu') %} {% block Content
  27. %}
  28. <!-- Alerte pour inciter à acheter -->
  29. <div id="purchase-alert" class="purchase-alert hidden" style="display: none;">
  30.   <button class="close-btn" onclick="closePurchaseAlert()">&times;</button>
  31.   <div id="purchase-alert-content">
  32.     <!-- Le contenu de l'alerte sera mis à jour dynamiquement en fonction du nombre de favoris -->
  33.   </div>
  34. </div>
  35. <div
  36.   id="verifImg"
  37.   class="modal fade"
  38.   role="dialog"
  39.   style="background-color: rgba(112, 112, 112, 0.56); z-index: 1000000"
  40. >
  41. </div>
  42. <div class="main-content">
  43.   <div class="row no-margin">
  44.     <!-- Bouton cadeau attractif e-commerce -->
  45.     <div
  46.       class="gift-button"
  47.       id="gift-button-trigger"
  48.       style="cursor: pointer;"
  49.     >
  50.       <i id="gift-icon-payment" class="bi bi-gift gift-pulse"></i>
  51.       <label
  52.         id="giftCount"
  53.         class="labelGiftCount gift-count-bounce"
  54.         style="background-color: #f56040"
  55.       >
  56.         {{ likes | length }}
  57.       </label>
  58.       <!-- Message flottant attractif -->
  59.       <div class="gift-tooltip" id="giftTooltip">
  60.         <div class="gift-tooltip-content">
  61.           <div class="gift-tooltip-header">
  62.             <i class="bi bi-clock-history"></i>
  63.             <strong>Offre limitée !</strong>
  64.           </div>
  65.           <p>Commandez vite ! Les photos seront disponibles seulement pendant <strong>6 semaines</strong> après la fin du séjour.</p>
  66.           <div class="gift-tooltip-cta">
  67.             <i class="bi bi-lightning-charge"></i>
  68.             Commander maintenant
  69.           </div>
  70.         </div>
  71.       </div>
  72.     </div>
  73.     <!-- Sidebar E-commerce -->
  74.     <div id="ecommerce-sidebar">
  75.       <div class="ecommerce-header">
  76.         <h2 class="ecommerce-title" id="ecommerce-title">
  77.           🎉 Vos souvenirs favoris sont prêts !
  78.         </h2>
  79.         <p class="ecommerce-subtitle" id="ecommerce-subtitle">
  80.           {{ likes | length }} photos sélectionnées avec amour ❤️
  81.         </p>
  82.         <button class="ecommerce-close" onclick="closeEcommerceSidebar()">
  83.           <i class="bi bi-x"></i>
  84.         </button>
  85.       </div>
  86.       <div class="ecommerce-content">
  87.         <!-- Bannière d'urgence -->
  88.         <div class="urgency-banner" id="urgency-banner">
  89.           <div>
  90.             <i class="bi bi-clock-history"></i>
  91.             <strong>⏳ Commandez vite !</strong>
  92.           </div>
  93.           <div>Vos photos seront disponibles seulement <strong>6 semaines</strong> après la fin du séjour</div>
  94.           <div class="countdown" id="countdown-timer" style="display: none;">
  95.             Plus que <span id="days-left">0</span> jours !
  96.           </div>
  97.         </div>
  98.         <!-- Produits recommandés -->
  99.         <div id="products-container">
  100.           <!-- Album Photo -->
  101.           <div class="product-card popular">
  102.             <div class="product-header">
  103.               <div class="product-icon">
  104.                 <img src="/images/produit/Album5sur5-3.jpg" alt="Album Photo Premium" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px;">
  105.               </div>
  106.               <div class="product-info">
  107.                 <h4>Album Photo Premium</h4>
  108.                 <div class="product-pricing">
  109.                   <span class="current-price">24,90€</span>
  110.                   <span class="original-price">28,90€</span>
  111.                   <span class="savings">-14%</span>
  112.                 </div>
  113.               </div>
  114.               <div class="product-favorites-indicator" title="Photos favorites sélectionnées">
  115.                 <i class="bi bi-heart-fill" style="color: #f56040"></i>
  116.                 <span id="album-fav-count">{{ likes | length }}</span>
  117.               </div>
  118.             </div>
  119.             <p class="product-description">
  120.               Album photo personnalisé avec vos <strong id="album-count">{{ likes | length }}</strong> photos favorites. Papier de qualité supérieure, reliure rigide.
  121.             </p>
  122.             <button class="product-cta" onclick="orderProduct('album')" 
  123.                     title="Commander l'album photo avec vos photos favorites">
  124.               <i class="bi bi-cart-plus"></i>
  125.               Commander l'album
  126.             </button>
  127.           </div>
  128.           <!-- Pack Numérique -->
  129.           <div class="product-card">
  130.             <div class="product-header">
  131.               <div class="product-icon">
  132.                 <img src="/images/produit/photoNumerique.jpg" alt="Pack Numérique HD" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px;">
  133.               </div>
  134.               <div class="product-info">
  135.                 <h4>Pack Numérique HD</h4>
  136.                 <p class="price">À partir de 3,90€</p>
  137.               </div>
  138.                 <div class="product-favorites-indicator" title="Photos favorites sélectionnées">
  139.                   <i class="bi bi-heart-fill" style="color: #f56040"></i>
  140.                   <span id="digital-fav-count">{{ likes | length }}</span>
  141.                 </div>
  142.             </div>
  143.             <p class="product-description">
  144.               Téléchargement immédiat de vos <strong id="digital-count">{{ likes | length }}</strong> photos en haute définition. Qualité professionnelle garantie.
  145.             </p>
  146.             <button class="product-cta" onclick="orderProduct('digital')" 
  147.                     title="Télécharger vos photos en haute définition">
  148.               <i class="bi bi-download"></i>
  149.               Télécharger HD
  150.             </button>
  151.           </div>
  152.           <!-- Pochette Tirages -->
  153.           <div class="product-card" id="prints-card" style="display: none;">
  154.             <div class="product-header">
  155.               <div class="product-icon">
  156.                 <i class="bi bi-images"></i>
  157.               </div>
  158.               <div class="product-info">
  159.                 <h4>Pochette Tirages</h4>
  160.                 <p class="price">À partir de 9,90€</p>
  161.               </div>
  162.             </div>
  163.             <p class="product-description">
  164.               <strong id="prints-count">12</strong> tirages photo 10x15cm de vos favoris. Papier brillant professionnel, envoi sous 48h.
  165.             </p>
  166.             <button class="product-cta" onclick="orderProduct('prints')" 
  167.                     title="Commander des tirages photo de vos favoris">
  168.               <i class="bi bi-printer"></i>
  169.               Commander les tirages
  170.             </button>
  171.           </div>
  172.         </div>
  173.         <!-- Réassurance -->
  174.         <div class="reassurance">
  175.           <i class="bi bi-shield-check" style="color: #10b981; margin-right: 8px;"></i>
  176.           <strong>Imprimé par l'équipe 5sur5 – satisfaction garantie ✅</strong>
  177.         </div>
  178.         
  179.       </div>
  180.     </div>
  181.     <div class="selection-popover" id="selectionPopover">
  182.       <h4>Votre sélection</h4>
  183.       <p>Tirages : {{ likes | length }} / 12</p>
  184.       <p>Numériques : {{ likes | length }} / 15</p>
  185.       <p>Album : {{ likes | length }} / 20</p>
  186.       <button class="finalize-button" onclick="openEcommerceSidebar()" 
  187.               title="Voir les produits disponibles avec vos favoris">
  188.         <i class="bi bi-gift"></i>
  189.         Finaliser ma commande
  190.       </button>
  191.     </div>
  192.     
  193.     <style>
  194.       /* Amélioration des boutons de produits */
  195.       .product-cta {
  196.         background: #3a8d95;
  197.         border: none;
  198.         border-radius: 25px;
  199.         color: white;
  200.         padding: 12px 24px;
  201.         font-weight: 600;
  202.         cursor: pointer;
  203.         transition: all 0.3s ease;
  204.         display: flex;
  205.         align-items: center;
  206.         gap: 8px;
  207.         width: 100%;
  208.         justify-content: center;
  209.         margin-top: 10px;
  210.       }
  211.       
  212.       .product-cta:hover {
  213.         transform: translateY(-2px);
  214.         box-shadow: 0 5px 15px rgba(58, 141, 149, 0.4);
  215.         background: #2f7a82;
  216.       }
  217.       
  218.       .product-cta:active {
  219.         transform: translateY(0);
  220.       }
  221.       
  222.       .product-cta:disabled {
  223.         background: #ccc;
  224.         cursor: not-allowed;
  225.         transform: none;
  226.         box-shadow: none;
  227.       }
  228.       
  229.       .finalize-button {
  230.         background: linear-gradient(135deg, #e91e63 0%, #ad1457 100%);
  231.         border: none;
  232.         border-radius: 25px;
  233.         color: white;
  234.         padding: 12px 24px;
  235.         font-weight: 600;
  236.         cursor: pointer;
  237.         transition: all 0.3s ease;
  238.         display: flex;
  239.         align-items: center;
  240.         gap: 8px;
  241.         width: 100%;
  242.         justify-content: center;
  243.         margin-top: 15px;
  244.       }
  245.       
  246.       .finalize-button:hover {
  247.         transform: translateY(-2px);
  248.         box-shadow: 0 5px 15px rgba(233, 30, 99, 0.4);
  249.         background: linear-gradient(135deg, #d81b60 0%, #9c1450 100%);
  250.       }
  251.       
  252.       /* Amélioration des cartes de produits */
  253.       .product-card {
  254.         border-radius: 12px;
  255.         box-shadow: 0 4px 15px rgba(0,0,0,0.1);
  256.         transition: all 0.3s ease;
  257.         margin-bottom: 20px;
  258.         overflow: hidden;
  259.       }
  260.       
  261.       .product-card:hover {
  262.         transform: translateY(-5px);
  263.         box-shadow: 0 8px 25px rgba(0,0,0,0.15);
  264.       }
  265.       
  266.       .product-header {
  267.         display: flex;
  268.         align-items: center;
  269.         gap: 15px;
  270.         padding: 20px;
  271.         background: #f8f9fa;
  272.       }
  273.       
  274.       .product-icon {
  275.         width: 60px;
  276.         height: 60px;
  277.         border-radius: 8px;
  278.         overflow: hidden;
  279.         flex-shrink: 0;
  280.       }
  281.       
  282.       .product-info h4 {
  283.         margin: 0 0 5px 0;
  284.         color: #333;
  285.         font-weight: 600;
  286.       }
  287.       
  288.       .product-pricing {
  289.         display: flex;
  290.         align-items: center;
  291.         gap: 10px;
  292.         margin: 5px 0;
  293.       }
  294.       
  295.       .current-price {
  296.         font-size: 1.2rem;
  297.         font-weight: 700;
  298.         color: #e91e63;
  299.       }
  300.       
  301.       .original-price {
  302.         text-decoration: line-through;
  303.         color: #666;
  304.         font-size: 0.9rem;
  305.       }
  306.       
  307.       .savings {
  308.         background: #4caf50;
  309.         color: white;
  310.         padding: 2px 8px;
  311.         border-radius: 12px;
  312.         font-size: 0.8rem;
  313.         font-weight: 600;
  314.       }
  315.       
  316.       .product-description {
  317.         padding: 0 20px 20px 20px;
  318.         color: #666;
  319.         line-height: 1.5;
  320.       }
  321.       
  322.       .product-favorites-indicator {
  323.         display: flex;
  324.         align-items: center;
  325.         gap: 5px;
  326.         background: rgba(245, 96, 64, 0.1);
  327.         padding: 5px 10px;
  328.         border-radius: 15px;
  329.         font-size: 0.9rem;
  330.         color: #f56040;
  331.         font-weight: 600;
  332.       }
  333.     </style>
  334.   </div>
  335.   <div class="divSliderModern">
  336.     <input type="hidden" id="nbFavCurrent" value="{{ nblikes }}" />
  337.    <input  id="likeCount" type="hidden" value="{{ nblikes }}" />
  338.     <div
  339.       class="splide no-padding no-margin"
  340.       id="imageSlider"
  341.       style="max-height: 200px"
  342.     >
  343.       <div class="splide__track">
  344.         <ul class="splide__list">
  345.           <!-- Slide 1 -->
  346.           <li class="splide__slide">
  347.             <div class="slider-content" style="background: white">
  348.               <div class="namePRD" style="display: block">
  349.                 <h4
  350.                   class="titleProdbienvenu titleProdbienvenu1"
  351.                   style="color: #41a2aa"
  352.                 >
  353.                   Ajoutez vos favoris dès maintenant
  354.                 </h4>
  355.                 <h4
  356.                   class="titleProdbienvenu titleProdbienvenu2"
  357.                   style="color: #f09e7a"
  358.                 >
  359.                   et profitez de nos produits souvenirs personnalisés !
  360.                 </h4>
  361.               </div>
  362.               <img
  363.                 src="{{ asset('/images/imgSliderEmpty2.png') }}"
  364.                 class="imgslider"
  365.                 alt="Image 1"
  366.               />
  367.             </div>
  368.           </li>
  369.           <!-- Slide 2 -->
  370.           <li class="splide__slide">
  371.             <div class="slider-content" style="background: white">
  372.               <div class="namePRD" style="display: block">
  373.                 <h4
  374.                   class="titleProdbienvenu titleProdbienvenu1"
  375.                   style="color: #f09e7a"
  376.                 >
  377.                   Pensez à commander le livre du séjour
  378.                 </h4>
  379.                 <h4
  380.                   class="titleProdbienvenu titleProdbienvenu2"
  381.                   style="color: #41a2aa"
  382.                 >
  383.                   et offrez lui le plus beau des cadeaux !
  384.                 </h4>
  385.               </div>
  386.               <img
  387.                 src="{{ asset('/images/imgSliderEmpty1.png') }}"
  388.                 class="imgslider"
  389.                 alt="Image 2"
  390.               />
  391.             </div>
  392.           </li>
  393.         </ul>
  394.       </div>
  395.     </div>
  396.   </div>
  397.   <!-- Section de contenu à atteindre après le scroll -->
  398.   <div
  399.     class="no-margin"
  400.     id="scrollTarget"
  401.     style="width: 100%; background: #f9f9f9; padding-top: 30px"
  402.   >
  403.     <div class="no-margin" id="scrollTarget" style="width: 100%">
  404.       <!-- Header compact 2 bandes -->
  405.       <div class="header-restructured">
  406.         <!-- Ligne 1: Icône + Titre + Stats (alignés) -->
  407.         <div class="header-line-1">
  408.           <div class="header-content">
  409.             <img src="/Accueil/imagesAccueil/sejour.png" alt="Icône séjour" width="40" height="40">
  410.             <h1 class="titreDetailSej">Séjour {{ sejour.themSejour }}</h1>
  411.             <nav class="header-pills" aria-label="Statistiques du séjour">
  412.              
  413.                
  414.               <button id="filtre_photos_voir" class="pill stat-pill" data-filter="photos" aria-label="Voir {{ attachementsCount }} photos et vidéos partagées">
  415.                 <i class="bi bi-images"></i> {{ attachementsCount }}
  416.               </button>
  417.               <button class="pill stat-pill" data-filter="audios" aria-label="Voir {{ nbmessages }} messages audio enregistrés">
  418.                 <i class="bi bi-mic-fill"></i> {{ nbmessages }}
  419.               </button>
  420.               <button class="pill stat-pill" data-filter="favoris" aria-label="Voir {{ nblikes }} contenus marqués en favoris">
  421.                 <i class="bi bi-heart-fill"></i> {{ nblikes }}
  422.               </button>
  423.             </nav>
  424.           </div>
  425.         </div>
  426.         <!-- Ligne 2: Dates (gris clair) -->
  427.         <div class="header-line-2">
  428.           du {{ sejour.dateDebutSejour|date("d M Y")|replace({"Jan": "janv.", "Feb": "févr.", "Mar": "mars", "Apr": "avr.", "May": "mai", "Jun": "juin", "Jul": "juil.", "Aug": "août", "Sep": "sept.", "Oct.": "oct.", "Nov.": "nov.", "Dec.": "déc."}) }}
  429.           au {{ sejour.dateFinSejour|date("d M Y")|replace({"Jan": "janv.", "Feb": "févr.", "Mar": "mars", "Apr": "avr.", "May": "mai", "Jun": "juin", "Jul": "juil.", "Aug": "août", "Sep": "sept.", "Oct.": "oct.", "Nov.": "nov.", "Dec.": "déc."}) }}
  430.           · Code&nbsp;: {{sejour.codeSejour}}
  431.         </div>
  432.         <!-- 🎯 MEDIA TOOLBAR CAPSULE (Senior UX) -->
  433.         <nav class="media-toolbar" aria-label="Filtres médias">
  434.           <div class="mtb-inner" role="group">
  435.             <!-- Tout -->
  436.             <button class="mtb-btn active" data-filter="all" title="Tout">
  437.               <i class="bi bi-grid-3x3-gap-fill" aria-hidden="true"></i>
  438.               <span class="mtb-count">Tout {{ attachementsCount }}</span>
  439.             </button>
  440.         
  441.             <!-- Audios -->
  442.             <button class="mtb-btn" data-filter="audio" title="Audios">
  443.              
  444.        
  445. <span class="icon-telephone-voicemail" >
  446.   <i class="bi bi-telephone-fill tel text-secandary" style="color:#ffd700"></i>
  447.   <i class="bi bi-voicemail vm text-secandary" style="color:#ffd700"></i>
  448. </span>
  449.               <span class="mtb-count" data-bind="audio">0</span>
  450.             </button>
  451.           
  452.    
  453.             <!-- Favoris -->
  454.             <button class="mtb-btn" data-filter="favoris" title="Favoris">
  455.               <i class="bi bi-heart-fill" style="color:#f56040" aria-hidden="true"></i>
  456.               <span class="mtb-count" data-bind="fav" id="mesFavCount">{{ nblikes }}</span>
  457.             </button>
  458.                <!-- Photos -->
  459.             <button class="mtb-btn" data-filter="photos" aria-pressed="false" title="Voir toutes les photos">
  460.               <i class="bi bi-eye-fill" aria-hidden="true"></i>
  461.           
  462.             </button>
  463.           </div>
  464.         </nav>
  465.         <!-- Navigation par jours -->
  466.         <div class="section-days">
  467.           <div class="date-navigation">
  468.           
  469.      
  470.             <div class="date-container">
  471.               {% for x, groupAttach in listeattach %}
  472.                 {% set xDate = date(x) %}
  473.                 {% set finDate = date(sejour.dateFinSejour) %}
  474.                 {% if xDate <= finDate %}
  475.                   <div
  476.                     class="date-card modern-card {% if loop.last and xDate <= finDate %} active {% endif %}"
  477.                     data-aos="fade-up"
  478.                     data-bs-toggle="collapse"
  479.                     data-day-id="{{ xDate|date('Y-m-d') }}"
  480.                     data-bs-target="#demP{{ loop.index }}"
  481.                     id="iconedemoP{{ loop.index }}"
  482.                     role="button"
  483.                     aria-label="Contenu du {{ xDate|date("d F Y")|replace({
  484.                       "Jan": "janvier", "Feb": "février", "Mar": "mars", "Apr": "avril",
  485.                       "May": "mai", "Jun": "juin", "Jul": "juillet", "Aug": "août",
  486.                       "Sep": "septembre", "Oct": "octobre", "Nov": "novembre", "Dec": "décembre"
  487.                     }) }} : {{ groupAttach.countPhotos }} photos, {{ groupAttach.countAudio }} audios, {{ groupAttach.countVideos }} vidéos"
  488.                     tabindex="0"
  489.                   >
  490.                     <div class="card-content">
  491.                       <span class="title-line">
  492.                       
  493.                           <span class="full-date">
  494.                             {{ xDate|date("D")|replace({
  495.                                 "Mon": "lun.","Tue": "mar.","Wed": "mer.","Thu": "jeu.","Fri": "ven.","Sat": "sam.","Sun": "dim."
  496.                           }) }}
  497.                         
  498.                             {{ xDate|date("d M")|replace({
  499.                               "Jan": "janv.","Feb": "févr.","Mar": "mars","Apr": "avr.","May": "mai","Jun": "juin",
  500.                               "Jul": "juil.","Aug": "août","Sep": "sept.","Oct.": "oct.","Nov.": "nov.","Dec.": "déc."
  501.                             }) }}
  502.                              {% if groupAttach.isFirstDay == "yes" %}
  503.                               <span class="day-badge first">Début</span>
  504.                             {% elseif groupAttach.isLastDay == "yes" %}
  505.                               <span class="day-badge last">Dernier</span>
  506.                      
  507.                          
  508.                         {% endif %}
  509.                       </span>
  510.                       
  511.                      
  512.                           <span class="badge-new d-none" aria-label="Nouveau contenu"></span>
  513.                     
  514.                       </span>
  515.                       <ul class="media-list-horizontal">
  516.                         {% if groupAttach.countPhotos > 0 %}
  517.                         <li><i class="bi bi-images"></i>{{ groupAttach.countPhotos }}</li>
  518.                         {% endif %}
  519.                         {% if groupAttach.countAudio > 0 %}
  520.                         <li><span class="icon-telephone-voicemail">
  521.   <i class="bi bi-telephone-fill tel text-secandary" style="color:#ffd700!important"></i>
  522.   <i class="bi bi-voicemail vm text-secandary" style="color:#ffd700!important;  transform: translate(-8%, -46%);font-size:0.4rem!important"></i>
  523. </span>{{ groupAttach.countAudio }}</li>
  524.                         {% endif %}
  525.                         {% if groupAttach.countVideos > 0 %}
  526.                         <li><i class="bi bi-camera-video-fill"></i>{{ groupAttach.countVideos }}</li>
  527.                         {% endif %}
  528.                       </ul>
  529.                     </div>
  530.                   </div>
  531.                 {% endif %}
  532.               {% endfor %}
  533.       </div>
  534.     </div>
  535.             </div>
  536.           </div>
  537.         </div>
  538.     <!-- Carte produit flottante -->
  539.     <div id="dynamic-card" class="dynamic-card" style="display:none">
  540.       <div id="dynamic-card-content" class="dynamic-card-content" >
  541.         <!-- Le contenu dynamique (album, pochette, montage vidéo) sera injecté ici -->
  542.       </div>
  543.     </div>
  544.     <!-- Descriptions and Attachments -->
  545.     <div class="container--gallery modern">
  546.       {% set lastValidIndex = 0 %}
  547.       {% set hasAttachments = false %}
  548.       {% for x, groupAttach in listeattach %}
  549.       {% set xDate = date(x) %}
  550.       {% set finDate = date(sejour.dateFinSejour) %}
  551.       {% if xDate <= finDate %}
  552.       {% set lastValidIndex = loop.index %}
  553.       {% set hasAttachments = true %}
  554.       <div
  555.         id="demP{{ loop.index }}"
  556.         class="collapse {% if loop.last and xDate <= finDate %}show{% endif %}"
  557.         style="padding: 2%; padding-top: 0%"
  558.       >
  559.         <div class="journal-entry">
  560.         <div class="entry-header" style="
  561.     display: none;
  562.     align-items: center;
  563.     justify-content: space-between;
  564.     background: #ffffff;
  565.     padding: 8px 15px;
  566.     border-radius: 10px;
  567.     margin-bottom: 20px;
  568.     box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  569.     border-bottom: 1px solid #eee;
  570.     flex-wrap: wrap;
  571. ">
  572.  
  573.  
  574.   <!-- Centre : Date + Médias -->
  575.   <div style="
  576.       flex-grow: 1;
  577.       display: flex;
  578.       align-items: center;
  579.       justify-content: center;
  580.       flex-wrap: wrap;
  581.       gap: 12px;
  582.   ">
  583.     <!-- Date -->
  584.     <div style="font-weight: bold; font-size: 16px; color: #333;">
  585.       {{ x|date("l d F Y")|replace({
  586.           "Monday": "Lundi", "Tuesday": "Mardi", "Wednesday": "Mercredi",
  587.           "Thursday": "Jeudi", "Friday": "Vendredi", "Saturday": "Samedi",
  588.           "Sunday": "Dimanche",
  589.           "January": "Janvier", "February": "Février", "March": "Mars",
  590.           "April": "Avril", "May": "Mai", "June": "Juin",
  591.           "July": "Juillet", "August": "Août", "September": "Septembre",
  592.           "October": "Octobre", "November": "Novembre", "December": "Décembre"
  593.       }) }}
  594.     </div>
  595.   <!-- Filtres Médias -->
  596. <div class="filter-icons" style="display: flex; gap: 8px; flex-wrap: wrap;">
  597.     <span class="filter-badge active" data-filter="all" title="Afficher tout">
  598.         <i class="bi bi-grid-3x3-gap-fill" style="margin-right: 5px; font-size: 14px;"></i> Tout
  599.     </span>
  600.     {% if groupAttach.countPhotos > 0 %}
  601.     <span class="filter-badge" data-filter="photo" title="Filtrer les photos">
  602.         <i class="bi bi-image" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countPhotos }}
  603.     </span>
  604.     {% endif %}
  605.     {% if groupAttach.countVideos > 0 %}
  606.     <span class="filter-badge" data-filter="video" title="Filtrer les vidéos">
  607.         <i class="bi bi-camera-video-fill" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countVideos }}
  608.     </span>
  609.     {% endif %}
  610.     {% if groupAttach.countAudio > 0 %}
  611.     <span class="filter-badge" data-filter="audio" title="Filtrer les messages audio">
  612.         <i class="bi bi-mic-fill" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countAudio }}
  613.     </span>
  614.     {% endif %}
  615. </div>
  616.   </div>
  617.    
  618.    
  619.         </div>
  620.   <!-- Bouton Jour Suivant -->
  621.         <!-- Contenu -->
  622.         <div class="entry-content" id="TourContent">
  623.           <!-- Dropdown de filtrage par jour -->
  624.           <div class="day-filter-dropdown" data-day-index="{{ loop.index }}">
  625.             <button class="filter-toggle" type="button" aria-label="Options de filtrage">
  626.               <i class="bi bi-three-dots"></i>
  627.             </button>
  628.             <div class="filter-dropdown-menu">
  629.               <div class="filter-option active" data-filter="all">
  630.                 <i class="bi bi-grid-3x3-gap-fill"></i>
  631.                 <span>Tout</span>
  632.                 <span class="count" data-count-all>0</span>
  633.               </div>
  634.               <div class="filter-option" data-filter="photo">
  635.                 <i class="bi bi-image"></i>
  636.                 <span>Photos</span>
  637.                 <span class="count" data-count-photo>{{ groupAttach.countPhotos|default(0) }}</span>
  638.               </div>
  639.               <div class="filter-option" data-filter="video">
  640.                 <i class="bi bi-camera-video-fill"></i>
  641.                 <span>Vidéos</span>
  642.                 <span class="count" data-count-video>{{ groupAttach.countVideos|default(0) }}</span>
  643.               </div>
  644.               <div class="filter-option" data-filter="audio">
  645.                 <i class="bi bi-mic-fill"></i>
  646.                 <span>Messages</span>
  647.                 <span class="count" data-count-audio>{{ groupAttach.countAudio|default(0) }}</span>
  648.               </div>
  649.             </div>
  650.           </div>
  651.       
  652.           <p class="description" style="margin-left:2%;width:95%;margin-top:1%;margin-bottom:1%;text-align:left">
  653.             {% for description in sejour.jourdescripdate %} {% if
  654.             description.datejourphoto|date("m/d/Y") == x|date("m/d/Y") %}
  655.             {{ description.description | nl2br }}
  656.             {% endif %} {% endfor %}
  657.           </p>
  658.  
  659.           <!-- Conteneur des photos et vidéos -->
  660.           <div
  661.             class="rowimag no-margin"
  662.             style="
  663.               width: 100%;
  664.               display: flex;
  665.               flex-wrap: wrap;
  666.               margin: 0;
  667.               box-sizing: border-box;
  668.             "
  669.           >
  670.             <!-- Afficher les Photos et Vidéos -->
  671.             {% for attach in groupAttach.attachments %}
  672.               {% if attach.libiller == 'photo' %}
  673.            
  674.             <div class="column" data-type="{{ attach.libiller }}">
  675.             
  676.               <div class="photo-zoom photo-item">
  677.                 <!-- Image sans lien -->
  678.                 <img src="{{ attach.path }}" alt="{{ attach.descreption }}" loading="lazy" decoding="async" />
  679.                 <!-- Icône "voir" pour ouvrir le slider -->
  680.                 <div class="view-icon" onclick="viewImage('{{ attach.id_attchment }}', this)" title="Voir en plein écran">
  681.                   <i class="bi bi-eye-fill"></i>
  682.                 </div>
  683.                 <!-- Icône du cœur avec logique existante -->
  684.                 <div
  685.                   class="heart-icon"
  686.                   id="coeur{{ attach.id_attchment }}"
  687.                   data-id="{{ attach.id_attchment }}"
  688.                   data-sejour-id="{{ sejour.id }}"
  689.                   data-path="{{ attach.path }}"
  690.                   data-description="{{ attach.descreption }}"
  691.                 >
  692.                   {% if app.user %} {% if attach.is_favorite %}
  693.                   <i
  694.                     class="bi bi-heart-fill"
  695.                     title="Sélectionnée"
  696.                     style="color: #f56040"
  697.                   ></i>
  698.                   {% else %}
  699.                   <i class="bi bi-heart" title="Ajouter à ma sélection"></i>
  700.                   {% endif %} {% endif %}
  701.                 </div>
  702.                 <div class="photo-actions" style="display: none">
  703.                   <button class="menu-btn">⋮</button>
  704.                   <div class="menu-options">
  705.                     <button onclick="addToPack('tirage')">
  706.                       🖨️ Ajouter au tirage
  707.                     </button>
  708.                     <button onclick="addToPack('numerique')">
  709.                       💾 Ajouter au numérique
  710.                     </button>
  711.                   </div>
  712.                 </div>
  713.               
  714.               </div>
  715.               {% if attach.descreption != "" %}
  716.               <h4 class="description">{{ attach.descreption }}</h4>
  717.               {% endif %}
  718.             </div>
  719.             {% endif %} 
  720.              {% if attach.libiller == 'video' %}
  721.            
  722.             <div class="column" data-type="{{ attach.libiller }}">
  723.                  
  724.             
  725.                  <div class="video-container" style="position: relative; display: inline-block; width: 100%; border-radius: 8px; overflow: hidden;">
  726.                         <video class="photo-zoom" controls controlslist="nodownload noplaybackrate" style="width: 100%;">
  727.                           <source src="{{ attach.path }}" type="video/mp4" />
  728.                           Votre navigateur ne supporte pas la lecture vidéo.
  729.                         </video>
  730.                       
  731.                       </div>
  732.               {% if attach.descreption != "" %}
  733.               <h4 class="description">{{ attach.descreption }}</h4>
  734.               {% endif %}
  735.             </div>
  736.             {% endif %} 
  737.             
  738.             {% endfor %}
  739.           </div>
  740.           <!-- Section séparée pour les messages audio -->
  741.           {% if groupAttach.countAudio > 0 %}
  742.           <div class="audio-messages-section" style="margin-top:15px; border-top: 1px solid #eee; padding: 30px;">
  743.             <h4 style="margin-bottom: 15px; color: #555">
  744.             
  745.               <i class="bi bi-mic-fill" style="margin-right: 8px; color: #ffa500"></i>
  746.               Messages vocaux ({{ groupAttach.countAudio }})
  747.             </h4>
  748.             
  749.             {% if sejour.codeSejour|slice(0, 2) == 'PF' %}
  750.               {# Les séjours commençant par PF ont toujours accès #}
  751.               <div class="audio-messages-container" data-type="audio" style="display: flex; flex-wrap: wrap; gap: 15px">
  752.                 {% for attach in groupAttach.attachments %}
  753.                   {% if attach.libiller == 'message' %}
  754.                     <div class="audio-message-item" data-type="audio" style="background: #f9f9f9; border-radius: 8px; padding: 12px; display: flex; flex-direction: column; width: calc(33.33% - 10px); min-width: 250px; flex-grow: 1;">
  755.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  756.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  757.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1">
  758.                           <source src="{{ attach.path }}" type="audio/mp3" />
  759.                           Votre navigateur ne supporte pas la lecture audio.
  760.                         </audio>
  761.                       </div>
  762.                       {% if attach.descreption != "" %}
  763.                         <div class="audio-description" style="padding-left: 30px">
  764.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  765.                             {{ attach.descreption }}
  766.                           </p>
  767.                         </div>
  768.                       {% endif %}
  769.                     </div>
  770.                   {% endif %}
  771.                 {% endfor %}
  772.               </div>
  773.             
  774.             {% elseif sejour.codeSejour|slice(0, 2) == 'PP' and parentsejour.payment  == 1 or sejour.codeSejour|slice(0, 2) == 'EF' and parentsejour.payment  == 1 %}
  775.               {# PP ou EF avec paiement effectué #}
  776.               <div class="audio-messages-container" data-type="audio" style="display: flex; flex-wrap: wrap; gap: 15px">
  777.                 {% for attach in groupAttach.attachments %}
  778.                   {% if attach.libiller == 'message' %}
  779.                     <div class="audio-message-item" data-type="audio" style="background: #f9f9f9; border-radius: 8px; padding: 12px; display: flex; flex-direction: column; width: calc(33.33% - 10px); min-width: 250px; flex-grow: 1;">
  780.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  781.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  782.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1">
  783.                           <source src="{{ attach.path }}" type="audio/mp3" />
  784.                           Votre navigateur ne supporte pas la lecture audio.
  785.                         </audio>
  786.                       </div>
  787.                       {% if attach.descreption != "" %}
  788.                         <div class="audio-description" style="padding-left: 30px">
  789.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  790.                             {{ attach.descreption }}
  791.                           </p>
  792.                         </div>
  793.                       {% endif %}
  794.                     </div>
  795.                   {% endif %}
  796.                 {% endfor %}
  797.               </div>
  798.             
  799.             {% elseif sejour.codeSejour|slice(0, 2) == 'PP' and parentsejour.payment == 0 or sejour.codeSejour|slice(0, 2) == 'EF' and parentsejour.payment == 0 %}
  800.               {# PP ou EF sans paiement - message d'accès limité et audio désactivés #}
  801.               <div class="audio-messages-container" style="display: flex; flex-wrap: wrap; gap: 15px; opacity: 0.5; pointer-events: none; filter: grayscale(100%);">
  802.                 <div class="audio-messages-restricted" data-type="audio" style="padding: 20px; background: #f0f0f0; border-radius: 8px; text-align: center; margin-bottom: 15px; width: 100%;">
  803.                   <i class="bi bi-lock-fill" style="font-size: 24px; color: #808080; margin-bottom: 10px;"></i>
  804.                   <p style="margin: 0; color: #555;">📢 Les messages vocaux sont disponibles via la boîte vocale premium. Un paiement est requis pour l’activer et y accéder.</p>
  805.                 </div>
  806.                 {% for attach in groupAttach.attachments %}
  807.                   {% if attach.libiller == 'message' %}
  808.                     <div class="audio-message-item" data-type="audio" style="background: #f9f9f9; border-radius: 8px; padding: 12px; display: flex; flex-direction: column; width: calc(33.33% - 10px); min-width: 250px; flex-grow: 1;">
  809.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  810.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  811.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1" disabled>
  812.                           <source src="{{ attach.path }}" type="audio/mp3" />
  813.                           Votre navigateur ne supporte pas la lecture audio.
  814.                         </audio>
  815.                       </div>
  816.                       {% if attach.descreption != "" %}
  817.                         <div class="audio-description" style="padding-left: 30px">
  818.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  819.                             {{ attach.descreption }}
  820.                           </p>
  821.                         </div>
  822.                       {% endif %}
  823.                     </div>
  824.                   {% endif %}
  825.                 {% endfor %}
  826.               </div>
  827.             {% endif %}
  828.           </div>
  829.         {% endif %}
  830.         </div>
  831.       </div>
  832.     </div>
  833.     {% endif %}
  834.     {% endfor %}
  835.     {% if not hasAttachments %}
  836.     <div class="welcome-message" style="padding: 50px 20px; text-align: center; background: #f9f9f9; border-radius: 15px; margin: 30px auto; max-width: 800px; box-shadow: 0 5px 15px rgba(0,0,0,0.05);">
  837.       <img src="/images/welcome-icon.svg" alt="Bienvenue" style="width: 80px; margin-bottom: 20px;" loading="lazy" decoding="async" fetchpriority="low" onerror="this.src='/images/Accompagnateur/Picto5sur5.svg'; this.style.width='120px';">
  838.       
  839.       <h2 style="color: #41a2aa; margin-bottom: 20px; font-size: 24px;">Bienvenue sur votre espace séjour !</h2>
  840.       
  841.       <p style="font-size: 18px; color: #555; margin-bottom: 15px;">
  842.         Aucun contenu n'a encore été partagé pour ce séjour.
  843.       </p>
  844.       
  845.       <p style="font-size: 16px; color: #666; margin-bottom: 25px;">
  846.         L'accompagnateur partagera bientôt des photos, vidéos et messages vocaux.
  847.         <br>Revenez consulter cette page régulièrement pour suivre les aventures du séjour !
  848.       </p>
  849.       
  850.       <div style="display: flex; justify-content: center; gap: 15px; flex-wrap: wrap;">
  851.         <div style="display: flex; align-items: center; background: #fff; padding: 15px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
  852.           <i class="bi bi-images" style="font-size: 24px; color: #3a8d95; margin-right: 10px;"></i>
  853.           <span>Photos du séjour</span>
  854.         </div>
  855.         
  856.         <div style="display: flex; align-items: center; background: #fff; padding: 15px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
  857.           <i class="bi bi-camera-video-fill" style="font-size: 24px; color: #41a2aa; margin-right: 10px;"></i>
  858.           <span>Vidéos des activités</span>
  859.         </div>
  860.         
  861.         <div style="display: flex; align-items: center; background: #fff; padding: 15px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
  862.           <i class="bi bi-mic-fill" style="font-size: 24px; color: #ffa500; margin-right: 10px;"></i>
  863.           <span>Messages vocaux</span>
  864.         </div>
  865.       </div>
  866.     </div>
  867.     
  868.     <!-- Espace supplémentaire pour déplacer le footer vers le bas -->
  869.     <div style="height: 300px;"></div>
  870.     {% endif %}
  871.     
  872.     <!-- JavaScript pour les pills et la gestion du nouveau contenu -->
  873.     <script>
  874.       document.addEventListener('DOMContentLoaded', function() {
  875.         // Valider les liens de produits au chargement
  876.         setTimeout(() => {
  877.           if (typeof window.validateProductLinks === 'function') {
  878.             window.validateProductLinks();
  879.           }
  880.         }, 1000);
  881.         
  882.         // ⚡ OPTIMISATION: Event delegation avec throttling pour éviter les clics multiples
  883.         let clickThrottleTimeout = null;
  884.         document.addEventListener('click', function(e) {
  885.           // Throttling pour éviter les clics multiples rapides
  886.           if (clickThrottleTimeout) return;
  887.           
  888.           const pill = e.target.closest('.stat-pill');
  889.           if (pill) {
  890.             clickThrottleTimeout = setTimeout(() => {
  891.               clickThrottleTimeout = null;
  892.             }, 100);
  893.             
  894.             // ⚡ OPTIMISÉ: Une seule boucle pour gérer les états actifs
  895.             const statPills = document.querySelectorAll('.stat-pill');
  896.             for (let i = 0; i < statPills.length; i++) {
  897.               const p = statPills[i];
  898.               if (p && p.classList) {
  899.                 if (p === pill) {
  900.                   p.classList.add('active');
  901.                 } else {
  902.                   p.classList.remove('active');
  903.                 }
  904.               }
  905.             }
  906.             
  907.             // Déclencher le filtrage du contenu
  908.             const filter = pill.getAttribute('data-filter');
  909.             if (typeof window.filterContent === 'function') {
  910.               window.filterContent(filter);
  911.             }
  912.           }
  913.         });
  914.         // Fonction de filtrage du contenu - globale
  915.         window.filterContent = function(filter) {
  916.           // ⚡ OPTIMISÉ: Suppression des console.log en production
  917.           if (window.location.hostname === 'localhost' || window.location.hostname.includes('dev')) {
  918.             console.log('Filtrage par:', filter);
  919.           }
  920.           
  921.           const dateNavigation = document.querySelector('.date-navigation');
  922.           const sectionDays = document.querySelector('.section-days');
  923.           
  924.           if (filter === 'favoris') {
  925.             // Afficher la vue des favoris
  926.             showFavoritesView();
  927.           } else if (filter === 'audio') {
  928.             // Afficher la vue des messages audio
  929.             showAudiosView();
  930.           } else if (filter === 'photos') {
  931.             // Ouvrir le carrousel du jour actif ou du dernier jour
  932.             openCurrentDayCarousel();
  933.           } else {
  934.             // Restaurer la vue normale des jours
  935.             showDaysView();
  936.           }
  937.         }
  938.         // Fonction pour afficher la vue des favoris - globale
  939.         window.showFavoritesView = function() {
  940.           const sectionDays = document.querySelector('.section-days');
  941.           const containerGallery = document.querySelector('.container--gallery');
  942.           
  943.           // Masquer le container gallery
  944.           if (containerGallery) {
  945.             containerGallery.style.display = 'none';
  946.           }
  947.           
  948.           // Mettre à jour l'état des boutons
  949.           updatePhotosButtonState('favorites');
  950.           updateAudioButtonState('favorites');
  951.           updateFavoritesButtonState('favorites');
  952.           
  953.           const favoriteCount = getCurrentFavoriteCount();
  954.           
  955.           // sla vue des favoris avec e-commerce
  956.           const favoritesHTML = `
  957.             <div class="favorites-ecommerce-view">
  958.               <!-- Header avec progression -->
  959.               <div class="favorites-header">
  960.                 <div class="favorites-title">
  961.                   <h3>Vos souvenirs sélectionnés</h3>
  962.                   <span class="favorites-count" id="favorites-count">${favoriteCount} photos</span>
  963.                 </div>
  964.                 <button class="btn-back-to-days" onclick="window.showDaysView()">
  965.                   <i class="bi bi-arrow-left"></i>
  966.                   Retour aux jours
  967.                 </button>
  968.               </div>
  969.             
  970.               <!-- Grille des favoris -->
  971.               <div class="favorites-content">
  972.                 <div class="favorites-grid" id="favoritesGrid">
  973.                   ${generateFavoritesGrid()}
  974.                 </div>
  975.                 
  976.                 <!-- Propositions de produits -->
  977.                 <div class="product-suggestions">
  978.                   ${generateProductSuggestions(favoriteCount)}
  979.                 </div>
  980.               </div>
  981.             </div>
  982.           `;
  983.           
  984.           sectionDays.innerHTML = favoritesHTML;
  985.         }
  986.         // Fonction pour afficher la vue des messages audio - globale
  987.         window.showAudiosView = function() {
  988.           const sectionDays = document.querySelector('.section-days');
  989.           const containerGallery = document.querySelector('.container--gallery');
  990.           
  991.           // Masquer le container gallery
  992.           if (containerGallery) {
  993.             containerGallery.style.display = 'none';
  994.           }
  995.           
  996.           // Mettre à jour l'état des boutons
  997.           updatePhotosButtonState('audios');
  998.           updateAudioButtonState('audios');
  999.           updateFavoritesButtonState('audios');
  1000.           
  1001.           // Collecter tous les messages audio de tous les jours
  1002.           const allAudioMessages = [];
  1003.           const audioItems = document.querySelectorAll('.audio-message-item[data-type="audio"]');
  1004.           
  1005.           audioItems.forEach(item => {
  1006.             const audio = item.querySelector('audio source');
  1007.             const description = item.querySelector('.audio-description p');
  1008.             const dayContainer = item.closest('[id^="demP"]');
  1009.             let dayTitle = 'Jour inconnu';
  1010.             
  1011.             if (dayContainer) {
  1012.               // Trouver le titre du jour
  1013.               const dayIndex = dayContainer.id.replace('demP', '');
  1014.               const dayCard = document.querySelector(`[data-target="#demP${dayIndex}"]`);
  1015.               if (dayCard) {
  1016.                 dayTitle = dayCard.textContent.trim();
  1017.               } else {
  1018.                 // Essayer de trouver dans les cartes de date
  1019.                 const dateCard = document.querySelector(`.date-card[data-bs-target="#demP${dayIndex}"], .date-card[data-target="#demP${dayIndex}"]`);
  1020.                 if (dateCard) {
  1021.                   const dateText = dateCard.querySelector('.date-text, .card-title, h5, .title-line');
  1022.                   if (dateText) {
  1023.                     dayTitle = dateText.textContent.trim();
  1024.                   }
  1025.                 }
  1026.               }
  1027.             }
  1028.             
  1029.             if (audio) {
  1030.               allAudioMessages.push({
  1031.                 src: audio.src,
  1032.                 description: description ? description.textContent.trim() : '',
  1033.                 day: dayTitle,
  1034.                 isRestricted: item.closest('.audio-messages-container[style*="opacity: 0.5"]') !== null
  1035.               });
  1036.             }
  1037.           });
  1038.           
  1039.           // Créer la vue des messages audio
  1040.           const audiosHTML = `
  1041.             <div class="audios-view">
  1042.               <!-- Header -->
  1043.               <div class="audios-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding: 20px; background: #f8f9fa; border-radius: 10px;">
  1044.                 <div class="audios-title">
  1045.                   <h3><span class="icon-telephone-voicemail">
  1046.   <i class="bi bi-telephone-fill tel"  style="color: #ffd700;font-size:1.5em"></i>
  1047.   <i class="bi bi-voicemail vm" style="color: #ffd700;font-size: 0.9rem;
  1048.     transform: translate(-40%, -46%);"></i>
  1049. </span>
  1050.                   <span class="audios-count">${allAudioMessages.length} messages</span>
  1051.                 </div>
  1052.                 <button class="btn-back-to-days" onclick="window.showDaysView()" style="background: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;">
  1053.                   <i class="bi bi-arrow-left"></i>
  1054.                   Retour aux jours
  1055.                 </button>
  1056.               </div>
  1057.               <!-- Grille des messages audio -->
  1058.               <div class="audios-content" style="display: flex; flex-wrap: wrap; gap: 20px;">
  1059.                 ${allAudioMessages.map(msg => `
  1060.                   <div class="audio-message-card" style="background: #f9f9f9; border-radius: 12px; padding: 20px; width: calc(50% - 10px); min-width: 300px; ${msg.isRestricted ? 'opacity: 0.5; filter: grayscale(100%);' : ''}">
  1061.                     ${msg.day && msg.day !== 'Jour inconnu' ? `
  1062.                       <div class="audio-day-tag" style="background: #e9ecef; color: #495057; padding: 4px 8px; border-radius: 15px; font-size: 12px; margin-bottom: 12px; display: inline-block;">
  1063.                         ${msg.day}
  1064.                       </div>
  1065.                     ` : ''}
  1066.                     <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 12px;">
  1067.                       <i class="bi bi-mic-fill" style="font-size: 24px; margin-right: 15px; color: #ffa500;"></i>
  1068.                       <audio controls controlslist="nodownload noplaybackrate" style="flex: 1; border-radius: 5px;" ${msg.isRestricted ? 'disabled' : ''}>
  1069.                         <source src="${msg.src}" type="audio/mp3" />
  1070.                         Votre navigateur ne supporte pas la lecture audio.
  1071.                       </audio>
  1072.                     </div>
  1073.                     ${msg.description ? `
  1074.                       <div class="audio-description" style="padding-left: 39px;">
  1075.                         <p style="margin: 0; font-size: 14px; color: #666; line-height: 1.4;">${msg.description}</p>
  1076.                       </div>
  1077.                     ` : ''}
  1078.                     ${msg.isRestricted ? `
  1079.                       <div class="audio-restricted-notice" style="text-align: center; margin-top: 10px; padding: 8px; background: #fff3cd; border-radius: 5px; border: 1px solid #ffeaa7;">
  1080.                         <i class="bi bi-lock-fill" style="color: #856404; margin-right: 5px;"></i>
  1081.                         <small style="color: #856404;">Accès premium requis</small>
  1082.                       </div>
  1083.                     ` : ''}
  1084.                   </div>
  1085.                 `).join('')}
  1086.               </div>
  1087.               
  1088.               ${allAudioMessages.length === 0 ? `
  1089.                 <div class="no-audios" style="text-align: center; padding: 60px 20px; color: #6c757d;">
  1090.                   <i class="bi bi-mic" style="font-size: 4rem; margin-bottom: 20px; opacity: 0.3;"></i>
  1091.                   <h4>Aucun message vocal</h4>
  1092.                   <p>Il n'y a pas encore de messages vocaux dans ce séjour.</p>
  1093.                 </div>
  1094.               ` : ''}
  1095.             </div>
  1096.           `;
  1097.           
  1098.           sectionDays.innerHTML = audiosHTML;
  1099.         }
  1100.         
  1101.         // Variables pour sauvegarder le contenu original
  1102.         let originalDaysContent = null;
  1103.         
  1104.         // Sauvegarder le contenu original au chargement
  1105.         document.addEventListener('DOMContentLoaded', function() {
  1106.           setTimeout(() => {
  1107.             const containerGallery = document.querySelector('.container--gallery');
  1108.             if (containerGallery) {
  1109.               originalDaysContent = containerGallery.outerHTML;
  1110.             }
  1111.           }, 1000);
  1112.         });
  1113.         
  1114.       
  1115.         // Fonction pour ouvrir le carrousel contextuel - globale
  1116.         window.openCurrentDayCarousel = function() {
  1117.           // Détecter la vue active
  1118.           const currentView = detectCurrentView();
  1119.           
  1120.           switch (currentView) {
  1121.             case 'favorites':
  1122.               openFavoritesCarousel();
  1123.               break;
  1124.             case 'audios':
  1125.               // Dans la vue audios, pas de carrousel photos pertinent
  1126.               console.log('Carrousel photos non disponible dans la vue audios');
  1127.               return;
  1128.             case 'days':
  1129.             default:
  1130.               openActiveDayCarousel();
  1131.               break;
  1132.           }
  1133.         }
  1134.         
  1135.         // Fonction pour mettre à jour l'état du bouton photos selon le contexte
  1136.         function updatePhotosButtonState(currentView) {
  1137.           const photosBtn = document.querySelector('.mtb-btn[data-filter="photos"]');
  1138.           if (!photosBtn) return;
  1139.           
  1140.           const btnIcon = photosBtn.querySelector('i');
  1141.           const btnCount = photosBtn.querySelector('.mtb-count');
  1142.           
  1143.           switch (currentView) {
  1144.             case 'favorites':
  1145.               // Dans les favoris : bouton actif, ouvre le carrousel des favoris
  1146.               photosBtn.style.opacity = '1';
  1147.               photosBtn.style.pointerEvents = 'auto';
  1148.               photosBtn.title = 'Voir le carrousel des favoris';
  1149.               if (btnIcon) btnIcon.style.color = '#007bff';
  1150.               if (btnCount) btnCount.style.display = 'none'; // Cacher le compteur
  1151.               break;
  1152.               
  1153.             case 'audios':
  1154.               // Dans les audios : bouton désactivé
  1155.               photosBtn.style.opacity = '0.4';
  1156.               photosBtn.style.pointerEvents = 'none';
  1157.               photosBtn.title = 'Carrousel non disponible dans la vue audios';
  1158.               if (btnIcon) btnIcon.style.color = '#6c757d';
  1159.               if (btnCount) btnCount.style.display = 'none';
  1160.               break;
  1161.               
  1162.             case 'days':
  1163.             default:
  1164.               // Vue normale : utiliser la logique existante du carrousel
  1165.               photosBtn.style.opacity = '1';
  1166.               photosBtn.style.pointerEvents = 'auto';
  1167.               photosBtn.title = 'Voir les photos du jour actif';
  1168.               if (btnIcon) btnIcon.style.color = '';
  1169.               if (btnCount) btnCount.style.display = 'none'; // Cacher le compteur pour éviter la confusion
  1170.               break;
  1171.           }
  1172.         }
  1173.         
  1174.         // Fonction pour mettre à jour l'état du bouton audio selon le contexte
  1175.         window.updateAudioButtonState = function updateAudioButtonState(currentView) {
  1176.           const audioBtn = document.querySelector('.mtb-btn[data-filter="audio"]');
  1177.           if (!audioBtn) return;
  1178.           
  1179.           const btnCount = audioBtn.querySelector('.mtb-count');
  1180.           
  1181.           switch (currentView) {
  1182.             case 'favorites':
  1183.               // Dans les favoris : pas d'action sur le compteur audio
  1184.               break;
  1185.               
  1186.             case 'audios':
  1187.               // Dans les audios : afficher le nombre total calculé par le frontend
  1188.               const totalAudioCount = document.querySelectorAll('.audio-message-item[data-type="audio"]').length;
  1189.               if (btnCount) {
  1190.                 btnCount.textContent = totalAudioCount;
  1191.               }
  1192.               break;
  1193.               
  1194.             case 'days':
  1195.             default:
  1196.               // Vue normale : compter les audios du jour actif
  1197.               const activeDayAudioCount = countActiveDayAudios();
  1198.               if (btnCount) {
  1199.                 btnCount.textContent = activeDayAudioCount;
  1200.               }
  1201.               break;
  1202.           }
  1203.         }
  1204.         
  1205.         // Fonction pour mettre à jour l'état du bouton favoris selon le contexte
  1206.         window.updateFavoritesButtonState = function updateFavoritesButtonState(currentView) {
  1207.           const favoritesBtn = document.querySelector('.mtb-btn[data-filter="favoris"]');
  1208.           if (!favoritesBtn) return;
  1209.           
  1210.           const btnIcon = favoritesBtn.querySelector('i');
  1211.           const btnCount = favoritesBtn.querySelector('.mtb-count');
  1212.           
  1213.           switch (currentView) {
  1214.             case 'favorites':
  1215.               // Dans les favoris : bouton actif
  1216.               favoritesBtn.style.opacity = '1';
  1217.               favoritesBtn.style.pointerEvents = 'auto';
  1218.               favoritesBtn.title = 'Vous êtes dans la vue favoris';
  1219.               if (btnIcon) btnIcon.style.color = '#f56040';
  1220.               if (btnCount) btnCount.style.display = 'inline';
  1221.               favoritesBtn.classList.add('active');
  1222.               break;
  1223.               
  1224.             case 'audios':
  1225.               // Dans les audios : bouton désactivé
  1226.               favoritesBtn.style.opacity = '0.4';
  1227.               favoritesBtn.style.pointerEvents = 'none';
  1228.               favoritesBtn.title = 'Favoris non disponibles dans la vue audios';
  1229.               if (btnIcon) btnIcon.style.color = '#6c757d';
  1230.               if (btnCount) btnCount.style.display = 'none';
  1231.               favoritesBtn.classList.remove('active');
  1232.               break;
  1233.               
  1234.             case 'days':
  1235.             default:
  1236.               // Vue normale : bouton normal
  1237.               favoritesBtn.style.opacity = '1';
  1238.               favoritesBtn.style.pointerEvents = 'auto';
  1239.               favoritesBtn.title = 'Voir les favoris';
  1240.               if (btnIcon) btnIcon.style.color = '#f56040';
  1241.               if (btnCount) btnCount.style.display = 'inline';
  1242.               favoritesBtn.classList.remove('active');
  1243.               break;
  1244.           }
  1245.         }
  1246.         
  1247.         // Fonction pour compter les audios du jour actif (réutilise la même logique)
  1248.         // ⚡ OPTIMISÉ: Cache des éléments DOM pour éviter les requêtes répétitives
  1249.         let cachedActiveDay = null;
  1250.         let cachedAudioCount = 0;
  1251.         let lastCacheTime = 0;
  1252.         const CACHE_DURATION = 1000; // 1 seconde
  1253.         
  1254.         function countActiveDayAudios() {
  1255.           const now = Date.now();
  1256.           
  1257.           // Utiliser le cache si récent
  1258.           if (cachedActiveDay && (now - lastCacheTime) < CACHE_DURATION) {
  1259.             return cachedAudioCount;
  1260.           }
  1261.           
  1262.           // Trouver le jour actuellement ouvert
  1263.           let activeDay = document.querySelector('.collapse.show[id^="demP"]');
  1264.           
  1265.           // Si aucun jour ouvert, prendre le dernier jour
  1266.           if (!activeDay) {
  1267.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  1268.             activeDay = allDays[allDays.length - 1];
  1269.           }
  1270.           
  1271.           if (!activeDay) {
  1272.             cachedActiveDay = null;
  1273.             cachedAudioCount = 0;
  1274.             lastCacheTime = now;
  1275.             return 0;
  1276.           }
  1277.           
  1278.           // Compter les messages audio dans ce jour
  1279.           const audiosInDay = activeDay.querySelectorAll('.audio-message-item[data-type="audio"]');
  1280.           const count = audiosInDay.length;
  1281.           
  1282.           // Mettre à jour le cache
  1283.           cachedActiveDay = activeDay;
  1284.           cachedAudioCount = count;
  1285.           lastCacheTime = now;
  1286.           
  1287.           return count;
  1288.         }
  1289.         
  1290.         // Fonction pour détecter la vue active
  1291.         // ⚡ OPTIMISÉ: Cache de la vue active pour éviter les requêtes DOM répétitives
  1292.         let cachedCurrentView = null;
  1293.         let lastViewCheck = 0;
  1294.         const VIEW_CACHE_DURATION = 500; // 500ms
  1295.         
  1296.         function detectCurrentView() {
  1297.           const now = Date.now();
  1298.           
  1299.           // Utiliser le cache si récent
  1300.           if (cachedCurrentView && (now - lastViewCheck) < VIEW_CACHE_DURATION) {
  1301.             return cachedCurrentView;
  1302.           }
  1303.           
  1304.           let view = 'days';
  1305.           if (document.querySelector('.favorites-ecommerce-view')) {
  1306.             view = 'favorites';
  1307.           } else if (document.querySelector('.audios-view')) {
  1308.             view = 'audios';
  1309.           }
  1310.           
  1311.           // Mettre à jour le cache
  1312.           cachedCurrentView = view;
  1313.           lastViewCheck = now;
  1314.           
  1315.           return view;
  1316.         }
  1317.         
  1318.         // ⚡ OPTIMISÉ: Cache global des éléments DOM fréquemment utilisés
  1319.         const domCache = {
  1320.           favoritesGrid: null,
  1321.           lastFavoritesUpdate: 0,
  1322.           CACHE_DURATION: 2000 // 2 secondes
  1323.         };
  1324.         
  1325.         function getCachedFavorites() {
  1326.           const now = Date.now();
  1327.           if (!domCache.favoritesGrid || (now - domCache.lastFavoritesUpdate) > domCache.CACHE_DURATION) {
  1328.             domCache.favoritesGrid = document.querySelector('.favorites-grid');
  1329.             domCache.lastFavoritesUpdate = now;
  1330.           }
  1331.           return domCache.favoritesGrid;
  1332.         }
  1333.         
  1334.         // Fonction pour ouvrir le carrousel des favoris
  1335.         function openFavoritesCarousel() {
  1336.           const favoritesGrid = getCachedFavorites();
  1337.           const favoriteItems = favoritesGrid ? favoritesGrid.querySelectorAll('.favorite-item') : [];
  1338.           if (favoriteItems.length > 0) {
  1339.             // Trouver le premier favori avec une action de vue
  1340.             const firstFavorite = favoriteItems[0];
  1341.             const viewBtn = firstFavorite.querySelector('.btn-view-favorite');
  1342.             if (viewBtn && viewBtn.onclick) {
  1343.               viewBtn.click();
  1344.             } else {
  1345.               // Fallback : extraire l'ID du data-id et utiliser viewImage
  1346.               const favoriteId = firstFavorite.dataset.id;
  1347.               if (favoriteId && typeof window.viewImage === 'function') {
  1348.                 window.viewImage(favoriteId, firstFavorite);
  1349.               }
  1350.             }
  1351.           } else {
  1352.             console.log('Aucun favori trouvé pour ouvrir le carrousel');
  1353.           }
  1354.         }
  1355.         
  1356.         // Fonction pour ouvrir le carrousel du jour actif
  1357.         function openActiveDayCarousel() {
  1358.           // Trouver le jour actuellement ouvert (avec classe 'show')
  1359.           let activeDay = document.querySelector('.collapse.show[id^="demP"]');
  1360.           
  1361.           // Si aucun jour n'est ouvert, prendre le dernier jour
  1362.           if (!activeDay) {
  1363.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  1364.             activeDay = allDays[allDays.length - 1]; // Dernier jour
  1365.           }
  1366.           
  1367.           if (!activeDay) {
  1368.             console.warn('Aucun jour trouvé pour ouvrir le carrousel');
  1369.             return;
  1370.           }
  1371.           
  1372.           // Trouver la première photo de ce jour
  1373.           const viewIcon = activeDay.querySelector('.view-icon[onclick*="viewImage"]');
  1374.           if (viewIcon) {
  1375.             // Extraire l'ID de la photo du onclick
  1376.             const onclickAttr = viewIcon.getAttribute('onclick');
  1377.             const match = onclickAttr.match(/viewImage\('(\d+)'/);
  1378.             if (match) {
  1379.               const imageId = match[1];
  1380.               // Ouvrir le slider avec cette image
  1381.               if (typeof window.viewImage === 'function') {
  1382.                 window.viewImage(imageId, viewIcon);
  1383.               }
  1384.             }
  1385.           } else {
  1386.             // Fallback : chercher dans tous les jours
  1387.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  1388.             for (let day of allDays) {
  1389.               const photoInDay = day.querySelector('.view-icon[onclick*="viewImage"]');
  1390.               if (photoInDay) {
  1391.                 photoInDay.click();
  1392.                 break;
  1393.               }
  1394.             }
  1395.           }
  1396.         }
  1397.         // Fonction pour afficher la vue normale des jours - globale
  1398.         window.showDaysView = function() {
  1399.           const sectionDays = document.querySelector('.section-days');
  1400.           const containerGallery = document.querySelector('.container--gallery');
  1401.           
  1402.           // Si la section days contient des vues spéciales (favoris/audios), les supprimer
  1403.           if (sectionDays && (sectionDays.innerHTML.includes('favorites-ecommerce-view') || sectionDays.innerHTML.includes('audios-view'))) {
  1404.             // Restaurer le contenu original si disponible
  1405.             if (originalDaysContent) {
  1406.               sectionDays.innerHTML = originalDaysContent;
  1407.             } else {
  1408.               // Fallback : recharger la page si pas de sauvegarde
  1409.               location.reload();
  1410.               return;
  1411.             }
  1412.           }
  1413.           
  1414.           // Restaurer la visibilité du container gallery
  1415.           if (containerGallery) {
  1416.             containerGallery.style.display = 'block';
  1417.           }
  1418.           
  1419.           // Appeler le filtre "all" pour restaurer l'affichage complet
  1420.           if (typeof window.filterContent === 'function') {
  1421.             window.filterContent('toutVoir');
  1422.           }
  1423.           
  1424.           // Mettre à jour l'état des boutons
  1425.           updatePhotosButtonState('days');
  1426.           updateAudioButtonState('days');
  1427.           updateFavoritesButtonState('days');
  1428.         }
  1429.         // Fonction pour générer les cartes de jours (alternative simple)
  1430.         function generateDaysCards() {
  1431.           // Cette fonction pourrait régénérer les cartes, mais pour simplifier
  1432.           // on recharge la page dans showDaysView()
  1433.           return '';
  1434.         }
  1435.         // Fonction pour revenir à la vue des jours
  1436.      
  1437.         // Fonction pour générer la grille des favoris
  1438.         function generateFavoritesGrid() {
  1439.           const favorites = getFavoriteItems();
  1440.           
  1441.           if (favorites.length === 0) {
  1442.             return `
  1443.               <div class="no-favorites">
  1444.                 <i class="bi bi-heart" style="font-size: 3rem; color: #ccc; margin-bottom: 1rem;"></i>
  1445.                 <h4>Aucun favori sélectionné</h4>
  1446.                 <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos photos préférées pour les ajouter ici.</p>
  1447.               </div>
  1448.             `;
  1449.           }
  1450.           
  1451.           return favorites.map(item => `
  1452.             <div class="favorite-item" data-id="${item.id}">
  1453.               ${item.type === 'image' ? 
  1454.                 `<img src="${item.url}" alt="Photo favorite" loading="lazy">` :
  1455.                 item.type === 'video' ? 
  1456.                 `<video src="${item.url}" poster="${item.thumbnail}"></video>
  1457.                  <div class="video-overlay"><i class="bi bi-play-circle"></i></div>` :
  1458.                 `<div class="audio-item">
  1459.                    <i class="bi bi-mic-fill"></i>
  1460.                    <span>Audio ${item.duration || '00:00'}</span>
  1461.                  </div>`
  1462.               }
  1463.               <!-- Actions overlay -->
  1464.               <div class="favorite-actions">
  1465.                 <button class="btn-view-favorite" onclick="viewFavorite('${item.id}')" title="Voir en grand">
  1466.                   <i class="bi bi-eye"></i>
  1467.                   <span>Voir</span>
  1468.                 </button>
  1469.                 <button class="btn-remove-favorite" onclick="removeFavorite('${item.id}')" title="Retirer des favoris">
  1470.                   <i class="bi bi-heart-fill"></i>
  1471.                   <span>Retirer</span>
  1472.                 </button>
  1473.               </div>
  1474.             </div>
  1475.           `).join('');
  1476.         }
  1477.         // Fonction pour réorganiser la grille des favoris
  1478.         function reorganizeFavoritesGrid() {
  1479.           const favoritesGrid = document.getElementById('favoritesGrid');
  1480.           if (!favoritesGrid) return;
  1481.           
  1482.           const remainingItems = favoritesGrid.querySelectorAll('.favorite-item');
  1483.           if (remainingItems.length === 0) {
  1484.             // Afficher le message "Aucun favori"
  1485.             favoritesGrid.innerHTML = `
  1486.               <div class="no-favorites">
  1487.                 <i class="bi bi-heart" style="font-size: 3rem; color: #ccc; margin-bottom: 1rem;"></i>
  1488.                 <h4>Aucun favori sélectionné</h4>
  1489.                 <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos photos préférées pour les ajouter ici.</p>
  1490.               </div>
  1491.             `;
  1492.           } else {
  1493.             // Réorganiser les éléments restants avec une animation douce
  1494.             remainingItems.forEach((item, index) => {
  1495.               item.style.transition = 'all 0.3s ease';
  1496.               item.style.order = index;
  1497.             });
  1498.           }
  1499.         }
  1500.         // Fonction pour récupérer les éléments favoris
  1501.         function getFavoriteItems() {
  1502.           const favorites = [];
  1503.           
  1504.           // ⚡ OPTIMISÉ: Cache et parcours plus efficace des favoris
  1505.           const heartIcons = document.querySelectorAll('.heart-icon');
  1506.           for (let i = 0; i < heartIcons.length; i++) {
  1507.             const heartIcon = heartIcons[i];
  1508.             const heartFill = heartIcon.querySelector('.bi-heart-fill');
  1509.             if (heartFill) {
  1510.               const id = heartIcon.getAttribute('data-id');
  1511.               const path = heartIcon.getAttribute('data-path');
  1512.               const description = heartIcon.getAttribute('data-description');
  1513.               
  1514.               if (id && path) {
  1515.                 // Déterminer le type de média
  1516.                 let type = 'image';
  1517.                 let url = path;
  1518.                 let thumbnail = path;
  1519.                 
  1520.                 if (path.includes('.mp4') || path.includes('.mov') || path.includes('.avi')) {
  1521.                   type = 'video';
  1522.                   thumbnail = path.replace(/\.(mp4|mov|avi)$/i, '_thumb.jpg');
  1523.                 } else if (path.includes('.mp3') || path.includes('.wav') || path.includes('.m4a')) {
  1524.                   type = 'audio';
  1525.                   url = path;
  1526.                 }
  1527.                 
  1528.                 // Récupérer la date depuis le conteneur parent
  1529.                 const dateCard = heartIcon.closest('[id^="demP"]');
  1530.                 let date = 'Date inconnue';
  1531.                 if (dateCard) {
  1532.                   const dateElement = dateCard.querySelector('.full-date, .day-title');
  1533.                   if (dateElement) {
  1534.                     date = dateElement.textContent.trim();
  1535.                   }
  1536.                 }
  1537.                 
  1538.                 favorites.push({
  1539.                   id: id,
  1540.                   url: url,
  1541.                   thumbnail: thumbnail,
  1542.                   type: type,
  1543.                   date: date,
  1544.                   description: description || ''
  1545.                 });
  1546.               }
  1547.             }
  1548.           }
  1549.           
  1550.           // ⚡ OPTIMISÉ: Log conditionnel pour réduire l'impact performance
  1551.           if (favorites.length > 0) {
  1552.             console.log('Favoris trouvés:', favorites.length);
  1553.           }
  1554.           return favorites;
  1555.         }
  1556.         // Rendre les fonctions accessibles globalement
  1557.         window.viewFavorite = viewFavorite;
  1558.         window.removeFavorite = removeFavorite;
  1559.         window.getFavoriteItems = getFavoriteItems;
  1560.         // Fonction pour supprimer un favori
  1561.         function removeFavorite(id) {
  1562.          
  1563.           const heartIcon = document.querySelector(`[data-id="${id}"]`);
  1564.          
  1565.           if (heartIcon) {
  1566.             const heartFill = heartIcon.querySelector('.bi-heart-fill');
  1567.             if (heartFill && heartFill.classList) {
  1568.               heartFill.classList.remove('bi-heart-fill');
  1569.               heartFill.classList.add('bi-heart');
  1570.               heartFill.style.color = '';
  1571.             }
  1572.           }
  1573.           const favoriteItem = document.querySelector(`.favorite-item[data-id="${id}"]`);
  1574.         
  1575.           if (favoriteItem) {
  1576.             // Animation de disparition puis suppression complète
  1577.             favoriteItem.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
  1578.             favoriteItem.style.opacity = '0';
  1579.             favoriteItem.style.transform = 'scale(0.8)';
  1580.             
  1581.             setTimeout(() => {
  1582.               favoriteItem.remove();
  1583.               
  1584.               // Appeler la fonction métier si présente
  1585.               const heartIcon = document.querySelector(`#coeur${id}`);
  1586.               const sejourId = heartIcon && heartIcon.dataset.sejourId ? heartIcon.dataset.sejourId : '';
  1587.               supprimerFavoris(id, sejourId);
  1588.               
  1589.               // Mettre à jour les compteurs
  1590.               updateAllFavoriteCounters();
  1591.               
  1592.               // Réorganiser la grille si nécessaire
  1593.               reorganizeFavoritesGrid();
  1594.             }, 300);
  1595.       
  1596.      
  1597.    
  1598.   }
  1599.           
  1600.         }
  1601.         // Slider plein écran pour les favoris
  1602.         class FavoritesSlider {
  1603.           constructor() {
  1604.             this.currentIndex = 0;
  1605.             this.favorites = [];
  1606.             this.isOpen = false;
  1607.             this.touchStartX = 0;
  1608.             this.touchEndX = 0;
  1609.           }
  1610.           open(favoriteId) {
  1611.             console.log('Ouverture du slider pour:', favoriteId);
  1612.             
  1613.             // Récupérer tous les favoris images
  1614.             this.favorites = getFavoriteItems().filter(item => item.type === 'image');
  1615.             
  1616.             if (this.favorites.length === 0) {
  1617.               console.error('Aucun favori image trouvé');
  1618.               return;
  1619.             }
  1620.             // Trouver l'index de l'image courante
  1621.             this.currentIndex = this.favorites.findIndex(fav => fav.id === favoriteId);
  1622.             if (this.currentIndex === -1) this.currentIndex = 0;
  1623.             console.log('Index courant:', this.currentIndex, 'Total:', this.favorites.length);
  1624.             this.createSlider();
  1625.             this.showSlide(this.currentIndex);
  1626.             this.attachEvents();
  1627.             this.isOpen = true;
  1628.             // Animation d'ouverture immédiate pour favoris avec diagnostic
  1629.             setTimeout(() => {
  1630.               if (window.diagnoseSliderDisplay) {
  1631.                 window.diagnoseSliderDisplay('#favoritesSlider');
  1632.               }
  1633.             }, 50);
  1634.           }
  1635.           createSlider() {
  1636.             // ⚡ OPTIMISATION: Créer le slider seulement quand nécessaire
  1637.             if (document.querySelector('#favoritesSlider')) {
  1638.               return; // Déjà créé, ne pas recréer
  1639.             }
  1640.             
  1641.             // Structure HTML minimale et optimisée
  1642.             const sliderHTML = `
  1643.               <div class="favorites-slider" id="favoritesSlider" style="
  1644.                 position: fixed !important; 
  1645.                 top: 0 !important; 
  1646.                 left: 0 !important; 
  1647.                 width: 100% !important; 
  1648.                 height: 100% !important; 
  1649.                 background: rgba(0,0,0,0.95) !important; 
  1650.                 z-index: 9999 !important; 
  1651.                 display: flex !important; 
  1652.                 align-items: center !important; 
  1653.                 justify-content: center !important; 
  1654.                 opacity: 1 !important; 
  1655.                 visibility: visible !important;
  1656.               ">
  1657.                 <div class="slider-overlay"></div>
  1658.                 
  1659.                 <!-- Header avec contrôles -->
  1660.                 <div class="slider-header">
  1661.                   <div class="slider-controls">
  1662.                     <button class="slider-btn favorite-btn" title="Retirer des favoris">
  1663.                       <i class="bi bi-heart-fill"></i>
  1664.                     </button>
  1665.                     <button class="slider-btn zoom-btn" title="Zoom">
  1666.                       <i class="bi bi-zoom-in"></i>
  1667.                     </button>
  1668.                     <button class="slider-btn close-btn" title="Fermer">
  1669.                       <i class="bi bi-x-lg"></i>
  1670.                     </button>
  1671.                   </div>
  1672.                 </div>
  1673.                 <!-- Navigation -->
  1674.                 <button class="slider-nav prev-btn" title="Précédent">
  1675.                   <i class="bi bi-arrow-left-circle-fill"></i>
  1676.                 </button>
  1677.                 <button class="slider-nav next-btn" title="Suivant">
  1678.                   <i class="bi bi-arrow-right-circle-fill"></i>
  1679.                 </button>
  1680.                 <!-- Container des slides -->
  1681.                 <div class="slider-container">
  1682.                   <div class="slider-track" id="sliderTrack">
  1683.                     ${this.favorites.map((fav, index) => `
  1684.                       <div class="slide" data-index="${index}">
  1685.                         <div class="slide-content">
  1686.                           <img src="${fav.url}" alt="${fav.description || 'Photo favorite'}" 
  1687.                                loading="${index <= 2 ? 'eager' : 'lazy'}"
  1688.                                draggable="false">
  1689.                         </div>
  1690.                         <div class="slide-info">
  1691.                           <h4>${fav.description || 'Photo favorite'}</h4>
  1692.                           <p>${fav.date || 'Date inconnue'}</p>
  1693.                         </div>
  1694.                       </div>
  1695.                     `).join('')}
  1696.                   </div>
  1697.                 </div>
  1698.                 <!-- Thumbnails -->
  1699.                 <div class="slider-thumbnails">
  1700.                   ${this.favorites.map((fav, index) => `
  1701.                     <div class="thumbnail ${index === this.currentIndex ? 'active' : ''}" 
  1702.                          data-index="${index}">
  1703.                       <img src="${fav.url}" alt="Thumbnail ${index + 1}">
  1704.                     </div>
  1705.                   `).join('')}
  1706.                 </div>
  1707.               </div>
  1708.             `;
  1709.             // Injecter dans le DOM
  1710.             document.body.insertAdjacentHTML('beforeend', sliderHTML);
  1711.           }
  1712.           showSlide(index) {
  1713.             if (index < 0 || index >= this.favorites.length) return;
  1714.             this.currentIndex = index;
  1715.             const track = document.getElementById('sliderTrack');
  1716.             const translateX = -index * 100;
  1717.             
  1718.             track.style.transform = `translateX(${translateX}%)`;
  1719.             // ⚡ OPTIMISÉ: Éviter forEach sur querySelectorAll (mémoire)
  1720.             const thumbnails = document.querySelectorAll('.thumbnail');
  1721.             for (let i = 0; i < thumbnails.length; i++) {
  1722.               const thumb = thumbnails[i];
  1723.               if (thumb?.classList) {
  1724.                 thumb.classList.toggle('active', i === index);
  1725.               }
  1726.             }
  1727.             // Mettre à jour le bouton favori
  1728.             const currentFav = this.favorites[index];
  1729.             const favoriteBtn = document.querySelector('.slider-controls .favorite-btn');
  1730.             if (favoriteBtn && currentFav) {
  1731.               favoriteBtn.setAttribute('data-id', currentFav.id);
  1732.             }
  1733.           }
  1734.           nextSlide() {
  1735.             const nextIndex = (this.currentIndex + 1) % this.favorites.length;
  1736.             this.showSlide(nextIndex);
  1737.           }
  1738.           prevSlide() {
  1739.             const prevIndex = (this.currentIndex - 1 + this.favorites.length) % this.favorites.length;
  1740.             this.showSlide(prevIndex);
  1741.           }
  1742.           close() {
  1743.             if (!this.isOpen) return;
  1744.             const slider = document.querySelector('.favorites-slider');
  1745.             if (slider && slider.classList) {
  1746.               slider.classList.remove('active');
  1747.               
  1748.               setTimeout(() => {
  1749.                 slider.remove();
  1750.                 this.isOpen = false;
  1751.               }, 300);
  1752.             }
  1753.           }
  1754.           attachEvents() {
  1755.             const slider = document.getElementById('favoritesSlider');
  1756.             
  1757.             // Bouton fermer
  1758.             slider.querySelector('.close-btn').addEventListener('click', () => this.close());
  1759.             
  1760.             // Navigation
  1761.             slider.querySelector('.prev-btn').addEventListener('click', () => this.prevSlide());
  1762.             slider.querySelector('.next-btn').addEventListener('click', () => this.nextSlide());
  1763.             
  1764.             // ⚡ OPTIMISÉ: Event delegation au lieu de listeners multiples
  1765.             const thumbContainer = slider.querySelector('.thumbnails-container') || slider;
  1766.             if (thumbContainer && !thumbContainer.hasAttribute('data-thumb-delegated')) {
  1767.               thumbContainer.addEventListener('click', (e) => {
  1768.                 const thumb = e.target.closest('.thumbnail');
  1769.                 if (thumb) {
  1770.                   const thumbnails = slider.querySelectorAll('.thumbnail');
  1771.                   const index = Array.from(thumbnails).indexOf(thumb);
  1772.                   if (index !== -1) this.showSlide(index);
  1773.                 }
  1774.               });
  1775.               thumbContainer.setAttribute('data-thumb-delegated', 'true');
  1776.             }
  1777.             // Bouton favori
  1778.             slider.querySelector('.favorite-btn').addEventListener('click', (e) => {
  1779.               const favoriteId = e.currentTarget.getAttribute('data-id');
  1780.               if (favoriteId) {
  1781.                 removeFavorite(favoriteId);
  1782.                 // Recharger le slider avec les favoris mis à jour
  1783.                 setTimeout(() => {
  1784.                   this.close();
  1785.                   if (getFavoriteItems().filter(item => item.type === 'image').length > 0) {
  1786.                     this.open(this.favorites[0]?.id);
  1787.                   }
  1788.                 }, 100);
  1789.               }
  1790.             });
  1791.             // Clavier
  1792.             document.addEventListener('keydown', this.handleKeyboard.bind(this));
  1793.             
  1794.             // Clic sur overlay pour fermer
  1795.             slider.querySelector('.slider-overlay').addEventListener('click', () => this.close());
  1796.             // Touch/swipe sur mobile
  1797.             const track = slider.querySelector('.slider-track');
  1798.             track.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: true });
  1799.             track.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: true });
  1800.             // Zoom sur clic simple (5 niveaux) + double-clic (legacy)
  1801.             slider.querySelectorAll('.slide img').forEach(img => {
  1802.               let zoomLevel = 1;
  1803.               let isDragging = false;
  1804.               let startX, startY, translateX = 0, translateY = 0;
  1805.               
  1806.               // Zoom sur clic simple (nouveau système 5 niveaux)
  1807.               img.addEventListener('click', (e) => {
  1808.                 e.preventDefault();
  1809.                 e.stopPropagation();
  1810.                 
  1811.                 // Cycle à travers 5 niveaux de zoom
  1812.                 switch(zoomLevel) {
  1813.                   case 1: zoomLevel = 1.5; break;  // 1x → 1.5x
  1814.                   case 1.5: zoomLevel = 2; break;  // 1.5x → 2x
  1815.                   case 2: zoomLevel = 3; break;    // 2x → 3x
  1816.                   case 3: zoomLevel = 4; break;    // 3x → 4x
  1817.                   case 4: zoomLevel = 5; break;    // 4x → 5x
  1818.                   case 5: 
  1819.                     zoomLevel = 1;                 // 5x → 1x (reset)
  1820.                     translateX = 0;
  1821.                     translateY = 0;
  1822.                     break;
  1823.                 }
  1824.                 console.log('FavoritesSlider - Zoom niveau:', zoomLevel);
  1825.                 img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  1826.                 
  1827.                 // Curseur selon le niveau
  1828.                 if (zoomLevel === 1) {
  1829.                   img.style.cursor = 'zoom-in';
  1830.                   img.removeAttribute('data-zoomed');
  1831.                 } else if (zoomLevel === 5) {
  1832.                   img.style.cursor = 'zoom-out';
  1833.                   img.setAttribute('data-zoomed', zoomLevel);
  1834.                 } else {
  1835.                   img.style.cursor = 'zoom-in';
  1836.                   img.setAttribute('data-zoomed', zoomLevel);
  1837.                 }
  1838.               });
  1839.               
  1840.               // Drag pour déplacer l'image zoomée
  1841.               img.addEventListener('mousedown', (e) => {
  1842.                 if (zoomLevel > 1) {
  1843.                   isDragging = true;
  1844.                   startX = e.clientX - translateX;
  1845.                   startY = e.clientY - translateY;
  1846.                   img.style.cursor = 'grabbing';
  1847.                   e.preventDefault();
  1848.                   e.stopPropagation();
  1849.                 }
  1850.               });
  1851.               const handleMouseMove = (e) => {
  1852.                 if (isDragging && zoomLevel > 1) {
  1853.                   translateX = e.clientX - startX;
  1854.                   translateY = e.clientY - startY;
  1855.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  1856.                 }
  1857.               };
  1858.               const handleMouseUp = () => {
  1859.                 if (isDragging) {
  1860.                   isDragging = false;
  1861.                   if (zoomLevel === 1) {
  1862.                     img.style.cursor = 'zoom-in';
  1863.                   } else if (zoomLevel === 5) {
  1864.                     img.style.cursor = 'zoom-out';
  1865.                   } else {
  1866.                     img.style.cursor = 'zoom-in';
  1867.                   }
  1868.                 }
  1869.               };
  1870.               document.addEventListener('mousemove', handleMouseMove);
  1871.               document.addEventListener('mouseup', handleMouseUp);
  1872.               // Support tactile pour mobile
  1873.               img.addEventListener('touchstart', (e) => {
  1874.                 if (zoomLevel > 1 && e.touches.length === 1) {
  1875.                   isDragging = true;
  1876.                   const touch = e.touches[0];
  1877.                   startX = touch.clientX - translateX;
  1878.                   startY = touch.clientY - translateY;
  1879.                   e.preventDefault();
  1880.                 }
  1881.               });
  1882.               img.addEventListener('touchmove', (e) => {
  1883.                 if (isDragging && zoomLevel > 1 && e.touches.length === 1) {
  1884.                   const touch = e.touches[0];
  1885.                   translateX = touch.clientX - startX;
  1886.                   translateY = touch.clientY - startY;
  1887.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  1888.                   e.preventDefault();
  1889.                 }
  1890.               });
  1891.               img.addEventListener('touchend', () => {
  1892.                 isDragging = false;
  1893.               });
  1894.               // Initialiser le curseur
  1895.               img.style.cursor = 'zoom-in';
  1896.               
  1897.               // Garder aussi le double-clic pour compatibilité (legacy)
  1898.               img.addEventListener('dblclick', this.handleZoom.bind(this));
  1899.             });
  1900.           }
  1901.           handleKeyboard(e) {
  1902.             if (!this.isOpen) return;
  1903.             
  1904.             switch(e.key) {
  1905.               case 'Escape':
  1906.                 this.close();
  1907.                 break;
  1908.               case 'ArrowLeft':
  1909.                 e.preventDefault();
  1910.                 this.prevSlide();
  1911.                 break;
  1912.               case 'ArrowRight':
  1913.                 e.preventDefault();
  1914.                 this.nextSlide();
  1915.                 break;
  1916.             }
  1917.           }
  1918.           handleTouchStart(e) {
  1919.             this.touchStartX = e.changedTouches[0].screenX;
  1920.           }
  1921.           handleTouchEnd(e) {
  1922.             this.touchEndX = e.changedTouches[0].screenX;
  1923.             this.handleSwipe();
  1924.           }
  1925.           handleSwipe() {
  1926.             const swipeThreshold = 50;
  1927.             const diff = this.touchStartX - this.touchEndX;
  1928.             if (Math.abs(diff) > swipeThreshold) {
  1929.               if (diff > 0) {
  1930.                 this.nextSlide(); // Swipe left -> next
  1931.               } else {
  1932.                 this.prevSlide(); // Swipe right -> prev
  1933.               }
  1934.             }
  1935.           }
  1936.           handleZoom(e) {
  1937.             const img = e.target;
  1938.             
  1939.             // Vérifier que l'élément et ses propriétés existent
  1940.             if (!img || !img.classList) {
  1941.               console.warn('Élément image invalide pour le zoom');
  1942.               return;
  1943.             }
  1944.             
  1945.             // Gérer les différents niveaux de zoom
  1946.             if (img.classList.contains('zoom-4x')) {
  1947.               // Retour à la taille normale
  1948.               img.classList.remove('zoom-4x', 'zoom-3x', 'zoom-2x', 'zoomed');
  1949.               img.style.cursor = 'pointer';
  1950.               img.style.transform = 'scale(1)'; // Réinitialiser la position
  1951.             } else if (img.classList.contains('zoom-3x')) {
  1952.               // Passer au zoom 4x
  1953.               img.classList.remove('zoom-3x');
  1954.               img.classList.add('zoom-4x');
  1955.               img.style.cursor = 'zoom-out';
  1956.               img.style.transform = 'scale(4)'; // Réinitialiser la position
  1957.             } else if (img.classList.contains('zoom-2x')) {
  1958.               // Passer au zoom 3x
  1959.               img.classList.remove('zoom-2x');
  1960.               img.classList.add('zoom-3x');
  1961.               img.style.cursor = 'zoom-in';
  1962.               img.style.transform = 'scale(3)'; // Réinitialiser la position
  1963.             } else if (img.classList.contains('zoomed')) {
  1964.               // Passer au zoom 2x
  1965.               img.classList.remove('zoomed');
  1966.               img.classList.add('zoom-2x');
  1967.               img.style.cursor = 'zoom-in';
  1968.               img.style.transform = 'scale(2)'; // Réinitialiser la position
  1969.             } else {
  1970.               // Premier zoom (1.5x)
  1971.               img.classList.add('zoomed');
  1972.               img.style.cursor = 'zoom-in';
  1973.               img.style.transform = 'scale(1.5)'; // Réinitialiser la position
  1974.             }
  1975.           }
  1976.         }
  1977.         // Slider universel pour toutes les images (favoris + galerie normale)
  1978.         class UniversalImageSlider {
  1979.           constructor() {
  1980.             this.currentIndex = 0;
  1981.             this.images = [];
  1982.             this.isOpen = false;
  1983.             this.touchStartX = 0;
  1984.             this.touchEndX = 0;
  1985.           }
  1986.           // Ouvrir avec une image spécifique depuis les favoris
  1987.           openFromFavorites(favoriteId) {
  1988.             console.log('Ouverture du slider depuis favoris:', favoriteId);
  1989.             
  1990.             // Récupérer tous les favoris images
  1991.             this.images = getFavoriteItems().filter(item => item.type === 'image');
  1992.             
  1993.             if (this.images.length === 0) {
  1994.               console.error('Aucun favori image trouvé');
  1995.               return;
  1996.             }
  1997.             // Trouver l'index de l'image courante
  1998.             this.currentIndex = this.images.findIndex(img => img.id === favoriteId);
  1999.             if (this.currentIndex === -1) this.currentIndex = 0;
  2000.             this.openSlider();
  2001.           }
  2002.           // Ouvrir avec une image spécifique depuis la galerie normale
  2003.           openFromGallery(imageId, dayContainer) {
  2004.             console.log('Ouverture du slider depuis galerie:', imageId, dayContainer);
  2005.             
  2006.             // Récupérer toutes les images du jour courant
  2007.             const dayImages = [];
  2008.             
  2009.             // Chercher dans le container du jour (peut être .dynamic-card ou autre)
  2010.             let photoItems;
  2011.             if (dayContainer) {
  2012.               photoItems = dayContainer.querySelectorAll('.photo-item');
  2013.             } else {
  2014.               // Fallback: chercher dans tout le document
  2015.               photoItems = document.querySelectorAll('.photo-item');
  2016.             }
  2017.             
  2018.             console.log('Photo items trouvés:', photoItems.length);
  2019.             
  2020.             photoItems.forEach(item => {
  2021.               const img = item.querySelector('img');
  2022.               const heartIcon = item.querySelector('.heart-icon');
  2023.               
  2024.               console.log('Processing item:', {
  2025.                 img: !!img,
  2026.                 heartIcon: !!heartIcon,
  2027.                 imgSrc: img ? img.src : null,
  2028.                 heartIconId: heartIcon ? heartIcon.getAttribute('data-id') : null
  2029.               });
  2030.               
  2031.               if (img && heartIcon) {
  2032.                 dayImages.push({
  2033.                   id: heartIcon.getAttribute('data-id'),
  2034.                   url: heartIcon.getAttribute('data-path') || img.src,
  2035.                   description: heartIcon.getAttribute('data-description') || img.alt || 'Photo',
  2036.                   date: '', // Pas de date spécifique pour les images de galerie
  2037.                   type: 'image'
  2038.                 });
  2039.               }
  2040.             });
  2041.             console.log('Images trouvées:', dayImages);
  2042.             this.images = dayImages;
  2043.             
  2044.             if (this.images.length === 0) {
  2045.               console.error('Aucune image trouvée dans ce jour');
  2046.               // Essayer de créer au moins l'image courante
  2047.               const currentHeartIcon = document.querySelector(`[data-id="${imageId}"]`);
  2048.               if (currentHeartIcon) {
  2049.                 const currentImg = currentHeartIcon.closest('.photo-item').querySelector('img');
  2050.                 if (currentImg) {
  2051.                   this.images = [{
  2052.                     id: imageId,
  2053.                     url: currentHeartIcon.getAttribute('data-path') || currentImg.src,
  2054.                     description: currentHeartIcon.getAttribute('data-description') || currentImg.alt || 'Photo',
  2055.                     date: '',
  2056.                     type: 'image'
  2057.                   }];
  2058.                   console.log('Image de fallback créée:', this.images);
  2059.                 }
  2060.               }
  2061.               
  2062.               if (this.images.length === 0) {
  2063.                 return;
  2064.               }
  2065.             }
  2066.             // Trouver l'index de l'image courante
  2067.             this.currentIndex = this.images.findIndex(img => img.id === imageId);
  2068.             if (this.currentIndex === -1) this.currentIndex = 0;
  2069.             console.log('Index trouvé:', this.currentIndex);
  2070.             this.openSlider();
  2071.           }
  2072.           openSlider() {
  2073.             console.log('=== openSlider appelée ===');
  2074.             console.log('Index courant:', this.currentIndex, 'Total:', this.images.length);
  2075.             console.log('Images à afficher:', this.images);
  2076.             // Supprimer tout slider existant
  2077.             const existingSlider = document.querySelector('.universal-slider');
  2078.             if (existingSlider) {
  2079.               console.log('Suppression du slider existant');
  2080.               existingSlider.remove();
  2081.             }
  2082.             this.createSlider();
  2083.             this.showSlide(this.currentIndex);
  2084.             this.attachEvents();
  2085.             this.isOpen = true;
  2086.               // Animation d'ouverture immédiate et garantie avec diagnostic
  2087.               setTimeout(() => {
  2088.                 if (window.diagnoseSliderDisplay) {
  2089.                   window.diagnoseSliderDisplay('#universalSlider');
  2090.                 }
  2091.               }, 50);
  2092.           }
  2093.           createSlider() {
  2094.             // ⚡ OPTIMISATION: Créer seulement si nécessaire
  2095.             if (document.querySelector('#universalSlider')) {
  2096.               return; // Déjà créé
  2097.             }
  2098.             
  2099.             console.log('Création slider pour', this.images.length, 'images');
  2100.             
  2101.             // HTML optimisé et minimal
  2102.             const sliderHTML = `
  2103.               <div class="universal-slider" id="universalSlider" style="
  2104.                 position: fixed !important; 
  2105.                 top: 0 !important; 
  2106.                 left: 0 !important; 
  2107.                 width: 100% !important; 
  2108.                 height: 100% !important; 
  2109.                 background: rgba(0,0,0,0.95) !important; 
  2110.                 z-index: 9999 !important; 
  2111.                 display: flex !important; 
  2112.                 align-items: center !important; 
  2113.                 justify-content: center !important; 
  2114.                 opacity: 1 !important; 
  2115.                 visibility: visible !important;
  2116.               ">
  2117.                 <div class="slider-overlay" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: pointer;"></div>
  2118.                 
  2119.                 <!-- Header avec contrôles -->
  2120.                 <div class="slider-header" style="
  2121.                   position: absolute; 
  2122.                   top: 20px; 
  2123.                   right: 20px; 
  2124.                   display: flex; 
  2125.                   align-items: center; 
  2126.                   gap: 15px; 
  2127.                   z-index: 10001;
  2128.                 ">
  2129.                   <button class="slider-btn close-btn" title="Fermer" style="
  2130.                     background: rgba(255,255,255,0.2); 
  2131.                     border: none; 
  2132.                     color: white; 
  2133.                     width: 40px; 
  2134.                     height: 40px; 
  2135.                     border-radius: 50%; 
  2136.                     cursor: pointer; 
  2137.                     display: flex; 
  2138.                     align-items: center; 
  2139.                     justify-content: center;
  2140.                     transition: background 0.2s ease;
  2141.                   ">
  2142.                     <i class="bi bi-x-lg"></i>
  2143.                   </button>
  2144.                 </div>
  2145.                 <!-- Navigation -->
  2146.                 <button class="slider-nav prev-btn" title="Précédent" style="
  2147.                   position: absolute; 
  2148.                   left: 20px; 
  2149.                   top: 50%; 
  2150.                   transform: translateY(-50%); 
  2151.                   background: rgba(255,255,255,0.2); 
  2152.                   border: none; 
  2153.                   color: white; 
  2154.                   width: 50px; 
  2155.                   height: 50px; 
  2156.                   border-radius: 50%; 
  2157.                   cursor: pointer; 
  2158.                   display: ${this.images.length > 1 ? 'flex' : 'none'}; 
  2159.                   align-items: center; 
  2160.                   justify-content: center; 
  2161.                   z-index: 10001;
  2162.                   transition: background 0.2s ease;
  2163.                 ">
  2164.                   <i class="bi bi-arrow-left-circle-fill" style="font-size: 20px;"></i>
  2165.                 </button>
  2166.                 <button class="slider-nav next-btn" title="Suivant" style="
  2167.                   position: absolute; 
  2168.                   right: 20px; 
  2169.                   top: 50%; 
  2170.                   transform: translateY(-50%); 
  2171.                   background: rgba(255,255,255,0.2); 
  2172.                   border: none; 
  2173.                   color: white; 
  2174.                   width: 50px; 
  2175.                   height: 50px; 
  2176.                   border-radius: 50%; 
  2177.                   cursor: pointer; 
  2178.                   display: ${this.images.length > 1 ? 'flex' : 'none'}; 
  2179.                   align-items: center; 
  2180.                   justify-content: center; 
  2181.                   z-index: 10001;
  2182.                   transition: background 0.2s ease;
  2183.                 ">
  2184.                   <i class="bi bi-arrow-right-circle-fill" style="font-size: 20px;"></i>
  2185.                 </button>
  2186.                 <!-- Container des slides -->
  2187.                 <div class="slider-container" style="
  2188.                   position: relative; 
  2189.                   width: 90%; 
  2190.                   height: 90%; 
  2191.                   display: flex; 
  2192.                   align-items: center; 
  2193.                   justify-content: center; 
  2194.                   z-index: 10000;
  2195.                 ">
  2196.                   <div class="slider-track" id="universalSliderTrack" style="
  2197.                     position: relative; 
  2198.                     width: 100%; 
  2199.                     height: 100%; 
  2200.                     display: flex; 
  2201.                     align-items: center; 
  2202.                     justify-content: center;
  2203.                     overflow: hidden;
  2204.                   ">
  2205.                     ${this.images.map((img, index) => `
  2206.                       <div class="slide" data-index="${index}" style="
  2207.                         position: absolute; 
  2208.                         width: 100%; 
  2209.                         height: 100%; 
  2210.                         display: ${index === 0 ? 'flex' : 'none'}; 
  2211.                         align-items: center; 
  2212.                         justify-content: center; 
  2213.                         flex-direction: column;
  2214.                       ">
  2215.                         <div class="slide-content" style="
  2216.                           position: relative; 
  2217.                           max-width: 100%; 
  2218.                           max-height: 90%; 
  2219.                           display: flex; 
  2220.                           align-items: center; 
  2221.                           justify-content: center;
  2222.                         ">
  2223.                           <img src="${img.url}" alt="${img.description || 'Photo'}" 
  2224.                                loading="${index <= 2 ? 'eager' : 'lazy'}"
  2225.                                draggable="false"
  2226.                                style="
  2227.                                  max-width: 100%; 
  2228.                                  max-height: 100%; 
  2229.                                  object-fit: contain; 
  2230.                                  cursor: zoom-in;
  2231.                                  transition: transform 0.3s ease;
  2232.                                  user-select: none;
  2233.                                ">
  2234.                         </div>
  2235.                         ${img.description ? `
  2236.                         <div class="slide-info" style="
  2237.                           position: absolute; 
  2238.                           bottom: 60px; 
  2239.                           left: 50%; 
  2240.                           transform: translateX(-50%); 
  2241.                           text-align: center; 
  2242.                           color: white; 
  2243.                           background: rgba(0,0,0,0.6); 
  2244.                           padding: 8px 16px; 
  2245.                           border-radius: 20px; 
  2246.                           max-width: 80%;
  2247.                           font-size: 14px;
  2248.                         ">
  2249.                           ${img.description}
  2250.                         </div>
  2251.                         ` : ''}
  2252.                       </div>
  2253.                     `).join('')}
  2254.                   </div>
  2255.                 </div>
  2256.                 <!-- Galerie de thumbnails révolutionnaire -->
  2257.                 <div class="cinema-gallery" style="
  2258.                   position: absolute; 
  2259.                   bottom: 0; 
  2260.                   left: 0; 
  2261.                   right: 0;
  2262.                   height: 180px;
  2263.                   background: linear-gradient(0deg, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.7) 50%, transparent 100%);
  2264.                   z-index: 10001;
  2265.                   display: flex;
  2266.                   flex-direction: column;
  2267.                   justify-content: flex-end;
  2268.                   padding: 0 20px 20px;
  2269.                 ">
  2270.                   
  2271.                   <!-- Info header avec compteur élégant -->
  2272.                   <div class="gallery-header" style="
  2273.                     display: flex;
  2274.                     justify-content: space-between;
  2275.                     align-items: center;
  2276.                     margin-bottom: 15px;
  2277.                   ">
  2278.                     <div class="photo-counter" style="
  2279.                       color: white;
  2280.                       font-size: 16px;
  2281.                       font-weight: 600;
  2282.                       display: flex;
  2283.                       align-items: center;
  2284.                       gap: 8px;
  2285.                     ">
  2286.                       <div style="
  2287.                         width: 8px;
  2288.                         height: 8px;
  2289.                         background: #10b981;
  2290.                         border-radius: 50%;
  2291.                         animation: pulse 2s infinite;
  2292.                       "></div>
  2293.                       <span>${(this.currentIndex || 0) + 1}</span>
  2294.                       <span style="opacity: 0.6;">sur</span>
  2295.                       <span>${this.images.length}</span>
  2296.                     </div>
  2297.                     <div class="gallery-controls" style="
  2298.                       display: flex;
  2299.                       gap: 12px;
  2300.                       align-items: center;
  2301.                     ">
  2302.                       <div style="color: rgba(255,255,255,0.7); font-size: 12px;">
  2303.                         Clic = Zoom 5x • Drag = Déplacer
  2304.                       </div>
  2305.                     </div>
  2306.                   </div>
  2307.                   
  2308.                   <!-- Carrousel de thumbnails cinématographique -->
  2309.                   <div class="cinema-carousel" style="
  2310.                     position: relative;
  2311.                     height: 80px;
  2312.                     overflow: hidden;
  2313.                   ">
  2314.                     <!-- Container scrollable -->
  2315.                     <div class="carousel-track" id="carouselTrack" style="
  2316.                       display: flex;
  2317.                       gap: 12px;
  2318.                       height: 100%;
  2319.                       overflow-x: auto;
  2320.                       overflow-y: hidden;
  2321.                       scroll-behavior: smooth;
  2322.                       scrollbar-width: none;
  2323.                       -ms-overflow-style: none;
  2324.                       padding: 0 50px;
  2325.                     ">
  2326.                       ${this.images.map((img, index) => `
  2327.                         <div class="cinema-thumb ${index === 0 ? 'active' : ''}" 
  2328.                              data-index="${index}" 
  2329.                              style="
  2330.                                flex: 0 0 auto;
  2331.                                width: ${index === 0 ? '100px' : '70px'};
  2332.                                height: 70px;
  2333.                                border-radius: 8px;
  2334.                                overflow: hidden;
  2335.                                cursor: pointer;
  2336.                                position: relative;
  2337.                                transition: transform 0.2s ease, opacity 0.2s ease;
  2338.                                transform: ${index === 0 ? 'translateY(-4px)' : 'translateY(0)'};
  2339.                                opacity: ${index === 0 ? '1' : '0.7'};
  2340.                                border: ${index === 0 ? '2px solid #10b981' : '1px solid rgba(255,255,255,0.3)'};
  2341.                              "
  2342.                              onmouseenter="
  2343.                                if (${index} !== (window.universalSlider?.currentIndex || 0)) {
  2344.                                  this.style.opacity = '0.9';
  2345.                                }
  2346.                              "
  2347.                              onmouseleave="
  2348.                                if (${index} !== (window.universalSlider?.currentIndex || 0)) {
  2349.                                  this.style.opacity = '0.7';
  2350.                                }
  2351.                              ">
  2352.                           
  2353.                           <!-- Image principale -->
  2354.                           <img src="${img.url}" 
  2355.                                alt="${img.description || 'Photo'}" 
  2356.                                loading="lazy" 
  2357.                                decoding="async"
  2358.                                style="
  2359.                                  width: 100%; 
  2360.                                  height: 100%; 
  2361.                                  object-fit: cover;
  2362.                                ">
  2363.                           
  2364.                           <!-- Numéro simplifié -->
  2365.                           <div class="thumb-number" style="
  2366.                             position: absolute;
  2367.                             top: 4px;
  2368.                             right: 4px;
  2369.                             background: ${index === 0 ? '#10b981' : 'rgba(0,0,0,0.8)'};
  2370.                             color: white;
  2371.                             font-size: 10px;
  2372.                             font-weight: 500;
  2373.                             padding: 2px 5px;
  2374.                             border-radius: 4px;
  2375.                             min-width: 16px;
  2376.                             text-align: center;
  2377.                           ">
  2378.                             ${index + 1}
  2379.                           </div>
  2380.                           
  2381.                           <!-- Indicateur actif simplifié -->
  2382.                           ${index === 0 ? `
  2383.                           <div class="active-indicator" style="
  2384.                             position: absolute;
  2385.                             bottom: -1px;
  2386.                             left: 50%;
  2387.                             transform: translateX(-50%);
  2388.                             width: 16px;
  2389.                             height: 2px;
  2390.                             background: #10b981;
  2391.                             border-radius: 1px;
  2392.                           "></div>
  2393.                           ` : ''}
  2394.                           
  2395.                           <!-- Preview au hover pour les non-actifs -->
  2396.                           ${index !== 0 ? `
  2397.                           <div class="hover-preview" style="
  2398.                             position: absolute;
  2399.                             bottom: -40px;
  2400.                             left: 50%;
  2401.                             transform: translateX(-50%);
  2402.                             background: rgba(0,0,0,0.9);
  2403.                             color: white;
  2404.                             font-size: 11px;
  2405.                             padding: 4px 8px;
  2406.                             border-radius: 6px;
  2407.                             opacity: 0;
  2408.                             pointer-events: none;
  2409.                             transition: all 0.3s ease;
  2410.                             white-space: nowrap;
  2411.                             z-index: 1000;
  2412.                           ">
  2413.                             Photo ${index + 1}
  2414.                           </div>
  2415.                           ` : ''}
  2416.                         </div>
  2417.                       `).join('')}
  2418.                     </div>
  2419.                     
  2420.                     <!-- Gradients de fade sur les côtés -->
  2421.                     <div style="
  2422.                       position: absolute;
  2423.                       left: 0;
  2424.                       top: 0;
  2425.                       bottom: 0;
  2426.                       width: 50px;
  2427.                       background: linear-gradient(90deg, rgba(0,0,0,0.8), transparent);
  2428.                       pointer-events: none;
  2429.                       z-index: 1;
  2430.                     "></div>
  2431.                     <div style="
  2432.                       position: absolute;
  2433.                       right: 0;
  2434.                       top: 0;
  2435.                       bottom: 0;
  2436.                       width: 50px;
  2437.                       background: linear-gradient(-90deg, rgba(0,0,0,0.8), transparent);
  2438.                       pointer-events: none;
  2439.                       z-index: 1;
  2440.                     "></div>
  2441.                   </div>
  2442.                 </div>
  2443.                 <!-- Animations CSS Optimisées -->
  2444.                 <style>
  2445.                   /* Optimisation: animations réduites et ciblées */
  2446.                   @keyframes pulse {
  2447.                     0%, 100% { opacity: 1; }
  2448.                     50% { opacity: 0.7; }
  2449.                   }
  2450.                   
  2451.                   /* Suppression du glow coûteux - remplacé par border simple */
  2452.                   .cinema-thumb.active .active-indicator {
  2453.                     background: #10b981;
  2454.                     animation: none; /* Suppression de l'animation glow */
  2455.                   }
  2456.                   
  2457.                   /* Hover optimisé - moins d'effets */
  2458.                   .cinema-thumb:hover .hover-preview {
  2459.                     opacity: 1 !important;
  2460.                     bottom: -35px !important;
  2461.                   }
  2462.                   
  2463.                   /* Masquer scrollbar */
  2464.                   .carousel-track::-webkit-scrollbar {
  2465.                     display: none;
  2466.                   }
  2467.                   
  2468.                   /* Optimisation: will-change pour les éléments animés */
  2469.                   .cinema-thumb {
  2470.                     will-change: transform, filter;
  2471.                   }
  2472.                   
  2473.                   .progress-bar {
  2474.                     will-change: width;
  2475.                   }
  2476.                 </style>
  2477.               </div>
  2478.             `;
  2479.             // Injecter dans le DOM
  2480.             console.log('Ajout du slider au DOM');
  2481.             document.body.insertAdjacentHTML('beforeend', sliderHTML);
  2482.             
  2483.             // Vérifier que le slider a été ajouté
  2484.             const addedSlider = document.querySelector('#universalSlider');
  2485.             console.log('Slider ajouté:', !!addedSlider);
  2486.             if (addedSlider) {
  2487.               console.log('Slider trouvé dans le DOM');
  2488.               console.log('Styles du slider:', {
  2489.                 display: addedSlider.style.display,
  2490.                 opacity: addedSlider.style.opacity,
  2491.                 visibility: addedSlider.style.visibility,
  2492.                 zIndex: addedSlider.style.zIndex,
  2493.                 position: addedSlider.style.position
  2494.               });
  2495.               console.log('Slider dans viewport:', addedSlider.getBoundingClientRect());
  2496.             } else {
  2497.               console.error('Erreur: slider non trouvé après ajout');
  2498.             }
  2499.           }
  2500.           showSlide(index) {
  2501.             console.log('=== showSlide appelée ===', 'index:', index, 'total:', this.images.length);
  2502.             if (index < 0 || index >= this.images.length) {
  2503.               console.log('Index invalide, arrêt');
  2504.               return;
  2505.             }
  2506.             this.currentIndex = index;
  2507.             // Masquer toutes les slides et afficher seulement la courante
  2508.             const slides = document.querySelectorAll('#universalSlider .slide');
  2509.             console.log('Slides trouvées:', slides.length);
  2510.             slides.forEach((slide, i) => {
  2511.               slide.style.display = i === index ? 'flex' : 'none';
  2512.               console.log(`Slide ${i}:`, i === index ? 'visible' : 'cachée');
  2513.             });
  2514.             // Mettre à jour le compteur
  2515.             const currentSlideSpan = document.querySelector('#universalSlider .current-slide');
  2516.             if (currentSlideSpan) {
  2517.               currentSlideSpan.textContent = index + 1;
  2518.               console.log('Compteur mis à jour:', index + 1);
  2519.             }
  2520.             // Mettre à jour les boutons de navigation
  2521.             const prevBtn = document.querySelector('#universalSlider .prev-btn');
  2522.             const nextBtn = document.querySelector('#universalSlider .next-btn');
  2523.             
  2524.             if (prevBtn) {
  2525.               prevBtn.style.opacity = index > 0 ? '1' : '0.5';
  2526.             }
  2527.             if (nextBtn) {
  2528.               nextBtn.style.opacity = index < this.images.length - 1 ? '1' : '0.5';
  2529.             }
  2530.             // Mettre à jour la galerie cinématographique
  2531.             this.updateCinemaGallery(index);
  2532.             // Mettre à jour le bouton favori
  2533.             this.updateFavoriteButton();
  2534.           }
  2535.           updateCinemaGallery(activeIndex) {
  2536.             // Mettre à jour la progress bar (optimisé)
  2537.             const progressBar = document.querySelector('.progress-bar');
  2538.             if (progressBar) {
  2539.               progressBar.style.width = `${(activeIndex + 1) / this.images.length * 100}%`;
  2540.             }
  2541.             // Mettre à jour le compteur (optimisé)
  2542.             const counter = document.querySelector('.photo-counter span');
  2543.             if (counter) {
  2544.               counter.textContent = activeIndex + 1;
  2545.             }
  2546.             // Mettre à jour les thumbnails - optimisé avec moins de calculs
  2547.             const cinemaThumbs = document.querySelectorAll('.cinema-thumb');
  2548.             cinemaThumbs.forEach((thumb, i) => {
  2549.               const isActive = i === activeIndex;
  2550.               
  2551.               // Optimisation: changements minimaux
  2552.               if (isActive) {
  2553.                 thumb.style.width = '100px';
  2554.                 thumb.style.transform = 'translateY(-4px)';
  2555.                 thumb.style.opacity = '1';
  2556.                 thumb.style.borderColor = '#10b981';
  2557.                 thumb.style.borderWidth = '2px';
  2558.                 
  2559.                 // Numéro actif - simplifié
  2560.                 const number = thumb.querySelector('.thumb-number');
  2561.                 if (number) {
  2562.                   number.style.background = '#10b981';
  2563.                 }
  2564.               } else {
  2565.                 thumb.style.width = '70px';
  2566.                 thumb.style.transform = 'translateY(0)';
  2567.                 thumb.style.opacity = '0.7';
  2568.                 thumb.style.borderColor = 'rgba(255,255,255,0.3)';
  2569.                 thumb.style.borderWidth = '1px';
  2570.                 
  2571.                 // Numéro inactif - simplifié
  2572.                 const number = thumb.querySelector('.thumb-number');
  2573.                 if (number) {
  2574.                   number.style.background = 'rgba(0,0,0,0.8)';
  2575.                 }
  2576.               }
  2577.             });
  2578.             // Auto-scroll optimisé avec throttling
  2579.             if (!this.scrollTimeout) {
  2580.               this.scrollTimeout = setTimeout(() => {
  2581.                 const track = document.getElementById('carouselTrack');
  2582.                 const activeThumb = document.querySelector(`.cinema-thumb[data-index="${activeIndex}"]`);
  2583.                 if (track && activeThumb) {
  2584.                   const scrollLeft = activeThumb.offsetLeft - (track.clientWidth / 2) + (activeThumb.clientWidth / 2);
  2585.                   track.scrollTo({
  2586.                     left: scrollLeft,
  2587.                     behavior: 'smooth'
  2588.                   });
  2589.                 }
  2590.                 this.scrollTimeout = null;
  2591.               }, 50); // Throttle à 50ms
  2592.             }
  2593.           }
  2594.           updateFavoriteButton() {
  2595.             const currentImage = this.images[this.currentIndex];
  2596.             const favoriteBtn = document.querySelector('.slider-controls .favorite-btn');
  2597.             
  2598.             if (favoriteBtn && currentImage) {
  2599.               favoriteBtn.setAttribute('data-id', currentImage.id);
  2600.               
  2601.               // Vérifier si l'image est déjà en favoris
  2602.               const heartIcon = document.querySelector(`[data-id="${currentImage.id}"]`);
  2603.               const isFavorite = heartIcon && heartIcon.querySelector('i.bi-heart-fill');
  2604.               
  2605.               const icon = favoriteBtn.querySelector('i');
  2606.               if (isFavorite) {
  2607.                 icon.className = 'bi bi-heart-fill';
  2608.                 favoriteBtn.style.color = '#e91e63';
  2609.               } else {
  2610.                 icon.className = 'bi bi-heart';
  2611.                 favoriteBtn.style.color = 'white';
  2612.               }
  2613.             }
  2614.           }
  2615.           nextSlide() {
  2616.             const nextIndex = (this.currentIndex + 1) % this.images.length;
  2617.             this.showSlide(nextIndex);
  2618.           }
  2619.           prevSlide() {
  2620.             const prevIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
  2621.             this.showSlide(prevIndex);
  2622.           }
  2623.           close() {
  2624.             if (!this.isOpen) return;
  2625.             const slider = document.querySelector('.universal-slider');
  2626.             if (slider) {
  2627.               slider.classList.remove('active');
  2628.               
  2629.               setTimeout(() => {
  2630.                 slider.remove();
  2631.                 this.isOpen = false;
  2632.               }, 300);
  2633.             }
  2634.           }
  2635.           attachEvents() {
  2636.             console.log('=== attachEvents appelée ===');
  2637.             const slider = document.getElementById('universalSlider');
  2638.             if (!slider) {
  2639.               console.error('Slider non trouvé pour attachEvents');
  2640.               return;
  2641.             }
  2642.             console.log('Slider trouvé pour attachEvents');
  2643.             
  2644.             // Bouton fermer
  2645.             const closeBtn = slider.querySelector('.close-btn');
  2646.             if (closeBtn) {
  2647.               console.log('Bouton fermer trouvé');
  2648.               closeBtn.addEventListener('click', () => {
  2649.                 console.log('Clic fermer');
  2650.                 this.close();
  2651.               });
  2652.             }
  2653.             
  2654.             // Navigation
  2655.             const prevBtn = slider.querySelector('.prev-btn');
  2656.             const nextBtn = slider.querySelector('.next-btn');
  2657.             if (prevBtn) {
  2658.               console.log('Bouton précédent trouvé');
  2659.               prevBtn.addEventListener('click', () => {
  2660.                 console.log('Clic précédent');
  2661.                 this.prevSlide();
  2662.               });
  2663.             }
  2664.             if (nextBtn) {
  2665.               console.log('Bouton suivant trouvé');
  2666.               nextBtn.addEventListener('click', () => {
  2667.                 console.log('Clic suivant');
  2668.                 this.nextSlide();
  2669.               });
  2670.             }
  2671.             // Navigation cinématographique des thumbnails
  2672.             const cinemaThumbs = slider.querySelectorAll('.cinema-thumb');
  2673.             cinemaThumbs.forEach((thumb, index) => {
  2674.               thumb.addEventListener('click', () => {
  2675.                 console.log('Clic cinema thumbnail:', index);
  2676.                 this.showSlide(index);
  2677.                 this.updateCinemaGallery(index);
  2678.               });
  2679.             });
  2680.             console.log('Cinema thumbnails cliquables:', cinemaThumbs.length);
  2681.             // Clic sur overlay pour fermer
  2682.             const overlay = slider.querySelector('.slider-overlay');
  2683.             if (overlay) {
  2684.               overlay.addEventListener('click', () => {
  2685.                 console.log('Clic overlay');
  2686.                 this.close();
  2687.               });
  2688.             }
  2689.             // Clavier
  2690.             this.keyboardHandler = this.handleKeyboard.bind(this);
  2691.             document.addEventListener('keydown', this.keyboardHandler);
  2692.             console.log('Événements clavier attachés');
  2693.             // Zoom sur les images
  2694.             this.attachZoomEvents(slider);
  2695.             console.log('Événements zoom attachés');
  2696.             console.log('Événements attachés avec succès');
  2697.           }
  2698.           close() {
  2699.             console.log('=== close appelée ===');
  2700.             const slider = document.querySelector('.universal-slider');
  2701.             if (slider) {
  2702.               console.log('Fermeture du slider');
  2703.               // Animation de fermeture
  2704.               slider.style.opacity = '0';
  2705.               
  2706.               setTimeout(() => {
  2707.                 slider.remove();
  2708.                 console.log('Slider supprimé du DOM');
  2709.                 
  2710.                 // Nettoyer les événements
  2711.                 if (this.keyboardHandler) {
  2712.                   document.removeEventListener('keydown', this.keyboardHandler);
  2713.                   this.keyboardHandler = null;
  2714.                 }
  2715.                 
  2716.                 this.isOpen = false;
  2717.               }, 300);
  2718.             }
  2719.           }
  2720.           nextSlide() {
  2721.             if (this.currentIndex < this.images.length - 1) {
  2722.               this.showSlide(this.currentIndex + 1);
  2723.             }
  2724.           }
  2725.           prevSlide() {
  2726.             if (this.currentIndex > 0) {
  2727.               this.showSlide(this.currentIndex - 1);
  2728.             }
  2729.           }
  2730.           handleKeyboard(e) {
  2731.             if (!this.isOpen) return;
  2732.             
  2733.             switch (e.key) {
  2734.               case 'Escape':
  2735.                 this.close();
  2736.                 break;
  2737.               case 'ArrowLeft':
  2738.                 this.prevSlide();
  2739.                 break;
  2740.               case 'ArrowRight':
  2741.                 this.nextSlide();
  2742.                 break;
  2743.             }
  2744.           }
  2745.           attachZoomEvents(slider) {
  2746.             // Fonctionnalité de zoom à 5 niveaux pour les images
  2747.             const images = slider.querySelectorAll('.slide img');
  2748.             images.forEach(img => {
  2749.               let zoomLevel = 1; // 1x, 1.5x, 2x, 3x, 4x, 5x
  2750.               let isDragging = false;
  2751.               let startX = 0;
  2752.               let startY = 0;
  2753.               let translateX = 0;
  2754.               let translateY = 0;
  2755.               // Clic simple pour zoom progressif (5 niveaux)
  2756.               img.addEventListener('click', (e) => {
  2757.                 e.preventDefault();
  2758.                 e.stopPropagation();
  2759.                 
  2760.                 // Cycle à travers 5 niveaux de zoom
  2761.                 switch(zoomLevel) {
  2762.                   case 1: zoomLevel = 1.5; break;  // 1x → 1.5x
  2763.                   case 1.5: zoomLevel = 2; break;  // 1.5x → 2x
  2764.                   case 2: zoomLevel = 3; break;    // 2x → 3x
  2765.                   case 3: zoomLevel = 4; break;    // 3x → 4x
  2766.                   case 4: zoomLevel = 5; break;    // 4x → 5x
  2767.                   case 5: 
  2768.                     zoomLevel = 1;                 // 5x → 1x (reset)
  2769.                     translateX = 0;
  2770.                     translateY = 0;
  2771.                     break;
  2772.                 }
  2773.                 console.log('Zoom niveau:', zoomLevel);
  2774.                 img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  2775.                 
  2776.                 // Curseur selon le niveau
  2777.                 if (zoomLevel === 1) {
  2778.                   img.style.cursor = 'zoom-in';
  2779.                   img.removeAttribute('data-zoomed');
  2780.                 } else if (zoomLevel === 5) {
  2781.                   img.style.cursor = 'zoom-out';
  2782.                   img.setAttribute('data-zoomed', zoomLevel);
  2783.                 } else {
  2784.                   img.style.cursor = 'zoom-in';
  2785.                   img.setAttribute('data-zoomed', zoomLevel);
  2786.                 }
  2787.               });
  2788.               // Drag pour déplacer l'image zoomée
  2789.               img.addEventListener('mousedown', (e) => {
  2790.                 if (zoomLevel > 1) {
  2791.                   isDragging = true;
  2792.                   startX = e.clientX - translateX;
  2793.                   startY = e.clientY - translateY;
  2794.                   img.style.cursor = 'grabbing';
  2795.                   e.preventDefault();
  2796.                   e.stopPropagation();
  2797.                 }
  2798.               });
  2799.               // Mousemove global pour le drag
  2800.               const handleMouseMove = (e) => {
  2801.                 if (isDragging && zoomLevel > 1) {
  2802.                   translateX = e.clientX - startX;
  2803.                   translateY = e.clientY - startY;
  2804.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  2805.                 }
  2806.               };
  2807.               // Mouseup global pour arrêter le drag
  2808.               const handleMouseUp = () => {
  2809.                 if (isDragging) {
  2810.                   isDragging = false;
  2811.                   if (zoomLevel === 1) {
  2812.                     img.style.cursor = 'zoom-in';
  2813.                   } else if (zoomLevel === 5) {
  2814.                     img.style.cursor = 'zoom-out';
  2815.                   } else {
  2816.                     img.style.cursor = 'zoom-in';
  2817.                   }
  2818.                 }
  2819.               };
  2820.               document.addEventListener('mousemove', handleMouseMove);
  2821.               document.addEventListener('mouseup', handleMouseUp);
  2822.               // Touch support pour mobile
  2823.               img.addEventListener('touchstart', (e) => {
  2824.                 if (zoomLevel > 1 && e.touches.length === 1) {
  2825.                   isDragging = true;
  2826.                   const touch = e.touches[0];
  2827.                   startX = touch.clientX - translateX;
  2828.                   startY = touch.clientY - translateY;
  2829.                   e.preventDefault();
  2830.                 }
  2831.               });
  2832.               img.addEventListener('touchmove', (e) => {
  2833.                 if (isDragging && zoomLevel > 1 && e.touches.length === 1) {
  2834.                   const touch = e.touches[0];
  2835.                   translateX = touch.clientX - startX;
  2836.                   translateY = touch.clientY - startY;
  2837.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  2838.                   e.preventDefault();
  2839.                 }
  2840.               });
  2841.               img.addEventListener('touchend', () => {
  2842.                 isDragging = false;
  2843.               });
  2844.               // Initialiser le curseur
  2845.               img.style.cursor = 'zoom-in';
  2846.             });
  2847.           }
  2848.         }
  2849.         // ⚡ OPTIMISATION MAJEURE: Sliders lazy (créés seulement quand utilisés)
  2850.         let favoritesSlider = null;
  2851.         let universalSlider = null;
  2852.         
  2853.         // Fonctions pour créer les sliders à la demande
  2854.         function getFavoritesSlider() {
  2855.           if (!favoritesSlider) {
  2856.             favoritesSlider = new FavoritesSlider();
  2857.           }
  2858.           return favoritesSlider;
  2859.         }
  2860.         
  2861.         function getUniversalSlider() {
  2862.           if (!universalSlider) {
  2863.             universalSlider = new UniversalImageSlider();
  2864.           }
  2865.           return universalSlider;
  2866.         }
  2867.         // Fonction pour voir un favori avec le slider plein écran
  2868.         function viewFavorite(id) {
  2869.           console.log('Ouverture du slider pour favori:', id);
  2870.           // ⚡ OPTIMISÉ: Utiliser le slider lazy
  2871.           const slider = getUniversalSlider();
  2872.           if (slider && slider.openFromFavorites) {
  2873.             slider.openFromFavorites(id);
  2874.           } else {
  2875.             console.error('Erreur slider favoris');
  2876.           }
  2877.         }
  2878.         // Fonction pour voir une image depuis la galerie normale
  2879.         function viewImage(imageId, clickedElement) {
  2880.           console.log('=== viewImage appelée ===');
  2881.           console.log('imageId:', imageId);
  2882.           console.log('clickedElement:', clickedElement);
  2883.           
  2884.           // Trouver le container du jour - peut être plusieurs niveaux au-dessus
  2885.           let dayContainer = clickedElement;
  2886.           
  2887.           // Remonter jusqu'à trouver un container approprié
  2888.           while (dayContainer && dayContainer !== document.body) {
  2889.             console.log('Checking element:', dayContainer.tagName, dayContainer.className, dayContainer.id);
  2890.             if (dayContainer && dayContainer.classList && (
  2891.                 dayContainer.classList.contains('dynamic-card') || 
  2892.                 dayContainer.classList.contains('collapse') ||
  2893.                 dayContainer.id && dayContainer.id.startsWith('demP')
  2894.             )) {
  2895.               break;
  2896.             }
  2897.             dayContainer = dayContainer.parentElement;
  2898.           }
  2899.           
  2900.           // ⚡ OPTIMISÉ: Logs réduits pour performance
  2901.           
  2902.           // ⚡ OPTIMISÉ: Utiliser le slider lazy
  2903.           const slider = getUniversalSlider();
  2904.           if (slider && slider.openFromGallery) {
  2905.             slider.openFromGallery(imageId, dayContainer);
  2906.           } else {
  2907.             console.error('Erreur slider galerie');
  2908.           }
  2909.         }
  2910.         // ⚡ OPTIMISÉ: Fonctions globales avec sliders lazy
  2911.         window.viewImage = viewImage;
  2912.         window.viewFavorite = viewFavorite;
  2913.         window.getUniversalSlider = getUniversalSlider; // Fonction lazy au lieu de l'instance
  2914.         // Variables globales pour éviter les ReferenceError
  2915.         if (typeof favoriteCount === 'undefined') {
  2916.           window.favoriteCount = 0;
  2917.         }
  2918.         if (typeof favButton === 'undefined') {
  2919.           window.favButton = null;
  2920.         }
  2921.         if (typeof favoriteButton === 'undefined') {
  2922.           window.favoriteButton = null;
  2923.         }
  2924.         if (typeof purchaseAlertTimeout === 'undefined') {
  2925.           window.purchaseAlertTimeout = null;
  2926.         }
  2927.         
  2928.         // Définir favoriteCount globalement pour éviter les ReferenceError
  2929.         let favoriteCount = window.favoriteCount || 0;
  2930.         // Fonction de sécurité pour vérifier les éléments DOM
  2931.         function safeAddEventListener(selector, event, callback) {
  2932.           const element = document.querySelector(selector);
  2933.           if (element) {
  2934.             element.addEventListener(event, callback);
  2935.           } else {
  2936.             console.warn(`Élément non trouvé: ${selector}`);
  2937.           }
  2938.         }
  2939.         // Fonction utilitaire pour manipuler classList de manière sécurisée
  2940.         function safeClassList(element, action, ...classes) {
  2941.           if (!element || !element.classList) {
  2942.             console.warn('Élément ou classList invalide:', element);
  2943.             return false;
  2944.           }
  2945.           
  2946.           try {
  2947.             switch(action) {
  2948.               case 'add':
  2949.                 element.classList.add(...classes);
  2950.                 break;
  2951.               case 'remove':
  2952.                 element.classList.remove(...classes);
  2953.                 break;
  2954.               case 'toggle':
  2955.                 return element.classList.toggle(classes[0], classes[1]);
  2956.               case 'contains':
  2957.                 return element.classList.contains(classes[0]);
  2958.               default:
  2959.                 console.warn('Action classList inconnue:', action);
  2960.                 return false;
  2961.             }
  2962.             return true;
  2963.           } catch (error) {
  2964.             console.warn('Erreur lors de la manipulation de classList:', error);
  2965.             return false;
  2966.           }
  2967.         }
  2968.         // Rendre la fonction utilitaire globale
  2969.         window.safeClassList = safeClassList;
  2970.         // Fonction pour sécuriser automatiquement tous les accès à classList
  2971.         function secureClassListAccess() {
  2972.           // Intercepter les erreurs classList globalement
  2973.           window.addEventListener('error', function(e) {
  2974.             if (e.message && (e.message.includes('classList') || e.message.includes('Cannot read properties of null'))) {
  2975.               console.warn('Erreur DOM interceptée et corrigée:', e.message, 'Ligne:', e.lineno, 'Fichier:', e.filename);
  2976.               e.preventDefault(); // Empêcher l'erreur de se propager
  2977.               return true;
  2978.             }
  2979.           });
  2980.           
  2981.           // Intercepter les erreurs non catchées
  2982.           window.addEventListener('unhandledrejection', function(e) {
  2983.             if (e.reason && e.reason.message && e.reason.message.includes('classList')) {
  2984.               console.warn('Promise rejection classList interceptée:', e.reason.message);
  2985.               e.preventDefault();
  2986.               return true;
  2987.             }
  2988.           });
  2989.           
  2990.           console.log('Système de sécurisation DOM/classList activé');
  2991.         }
  2992.         // Activer la sécurisation
  2993.         secureClassListAccess();
  2994.         // Fonction de diagnostic pour vérifier l'affichage du slider
  2995.         function diagnoseSliderDisplay(sliderId) {
  2996.           const slider = document.querySelector(sliderId);
  2997.           if (!slider) {
  2998.             console.error('Diagnostic: Slider non trouvé -', sliderId);
  2999.             return false;
  3000.           }
  3001.           
  3002.           const styles = window.getComputedStyle(slider);
  3003.           const rect = slider.getBoundingClientRect();
  3004.           
  3005.           console.log('=== DIAGNOSTIC SLIDER ===', sliderId);
  3006.           console.log('Élément présent:', !!slider);
  3007.           console.log('Styles calculés:', {
  3008.             display: styles.display,
  3009.             opacity: styles.opacity,
  3010.             visibility: styles.visibility,
  3011.             zIndex: styles.zIndex,
  3012.             position: styles.position
  3013.           });
  3014.           console.log('Position/Taille:', rect);
  3015.           console.log('Dans le viewport:', rect.width > 0 && rect.height > 0);
  3016.           console.log('Parent:', slider.parentElement?.tagName);
  3017.           
  3018.           // Vérifier les règles CSS qui pourraient masquer le slider
  3019.           const hiddenReasons = [];
  3020.           if (styles.display === 'none') hiddenReasons.push('display: none');
  3021.           if (styles.opacity === '0') hiddenReasons.push('opacity: 0');
  3022.           if (styles.visibility === 'hidden') hiddenReasons.push('visibility: hidden');
  3023.           if (parseInt(styles.zIndex) < 0) hiddenReasons.push('z-index négatif');
  3024.           
  3025.           if (hiddenReasons.length > 0) {
  3026.             console.warn('Raisons du masquage:', hiddenReasons);
  3027.             
  3028.             // Forcer l'affichage
  3029.             console.log('Forçage de l\'affichage...');
  3030.             slider.style.display = 'flex';
  3031.             slider.style.opacity = '1';
  3032.             slider.style.visibility = 'visible';
  3033.             slider.style.zIndex = '9999';
  3034.             slider.style.position = 'fixed';
  3035.             slider.style.top = '0';
  3036.             slider.style.left = '0';
  3037.             slider.style.width = '100%';
  3038.             slider.style.height = '100%';
  3039.             
  3040.             console.log('Slider forcé visible');
  3041.           } else {
  3042.             console.log('✅ Slider devrait être visible');
  3043.           }
  3044.           
  3045.           return true;
  3046.         }
  3047.         // Rendre la fonction globale
  3048.         window.diagnoseSliderDisplay = diagnoseSliderDisplay;
  3049.         // Fonction de sécurité pour checkFavorites
  3050.         function checkFavorites() {
  3051.           try {
  3052.             let count = 0;
  3053.             if (typeof getFavoriteItems === 'function') {
  3054.               count = getFavoriteItems().length;
  3055.             } else {
  3056.               // Fallback : compter depuis le DOM
  3057.               const giftCount = document.getElementById('giftCount');
  3058.               if (giftCount && giftCount.textContent) {
  3059.                 count = parseInt(giftCount.textContent.trim()) || 0;
  3060.               }
  3061.             }
  3062.             
  3063.             // Mettre à jour les variables globales
  3064.             window.favoriteCount = count;
  3065.             favoriteCount = count;
  3066.             
  3067.             console.log('Favoris vérifiés:', count);
  3068.             return count;
  3069.           } catch (error) {
  3070.             console.warn('Erreur lors de la vérification des favoris:', error);
  3071.             return 0;
  3072.           }
  3073.         }
  3074.         // Rendre checkFavorites globale si elle n'existe pas
  3075.         if (typeof window.checkFavorites === 'undefined') {
  3076.           window.checkFavorites = checkFavorites;
  3077.         }
  3078.         // Initialisation sécurisée après chargement du DOM
  3079.         document.addEventListener('DOMContentLoaded', function() {
  3080.           console.log('=== DOM chargé, initialisation des sliders ===');
  3081.           console.log('viewImage disponible:', typeof window.viewImage);
  3082.           console.log('viewFavorite disponible:', typeof window.viewFavorite);
  3083.           console.log('universalSlider disponible:', typeof window.universalSlider);
  3084.           
  3085.           // Vérifier que les éléments critiques existent
  3086.           const criticalElements = [
  3087.             'filtre_photos_voir',
  3088.             // Ajoutez d'autres IDs critiques ici si nécessaire
  3089.           ];
  3090.           
  3091.           criticalElements.forEach(id => {
  3092.             const element = document.getElementById(id);
  3093.             if (element) {
  3094.               console.log(`✓ Élément trouvé: ${id}`);
  3095.             } else {
  3096.               console.warn(`⚠ Élément manquant: ${id}`);
  3097.             }
  3098.           });
  3099.           
  3100.           // Initialiser checkFavorites si la fonction existe
  3101.           if (typeof window.checkFavorites === 'function') {
  3102.             window.checkFavorites();
  3103.           }
  3104.         });
  3105.       
  3106.         // Fonction pour obtenir l'image d'un produit selon son type
  3107.         function getProductImage(productId) {
  3108.           const productImages = {
  3109.             'album': '/images/produit/Album5sur5-3.jpg',
  3110.             'digital': '/images/produit/photoNumerique.jpg', 
  3111.             'prints': '/images/produit/PochettePhoto5sur5-2.jpg',
  3112.             'calendrier': '/images/produit/Calendrier5sur5-1.jpg',
  3113.             'livre': '/images/produit/LivreSouvenir5sur5-1.jpg',
  3114.             'coffret': '/images/produit/CoffretCadeau5sur5-2.jpg',
  3115.             'retro': '/images/produit/PochetteRetro5sur5-1.jpg'
  3116.           };
  3117.           
  3118.           return productImages[productId] || '/images/produit/albumm.PNG';
  3119.         }
  3120.         
  3121.         // Fonction pour générer les suggestions de produits (synchronisées avec ecommerce-sidebar)
  3122.         function generateProductSuggestions(favoriteCount) {
  3123.           if (favoriteCount === 0) {
  3124.             return `
  3125.               <div class="no-favorites-cta">
  3126.                 <div class="cta-content">
  3127.                   <i class="bi bi-heart" style="font-size: 4rem; color: #e91e63; margin-bottom: 1rem;"></i>
  3128.                   <h3>Commencez par sélectionner vos photos préférées</h3>
  3129.                   <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos plus beaux souvenirs pour débloquer nos produits personnalisés.</p>
  3130.                   <button class="btn-start-selection" onclick="window.showDaysView()">
  3131.                     <i class="bi bi-images"></i>
  3132.                     Parcourir les photos
  3133.                   </button>
  3134.                 </div>
  3135.               </div>
  3136.             `;
  3137.           }
  3138.           let suggestionsHTML = '<div class="suggestions-header"><h4><i class="bi bi-gift"></i> Produits recommandés pour vous</h4></div>';
  3139.           
  3140.           // Utiliser les mêmes produits que dans ecommerce-sidebar
  3141.           const products = [];
  3142.           
  3143.           // Album Photo Premium (toujours disponible)
  3144.           products.push({
  3145.             id: 'album',
  3146.             name: 'Album Photo Premium',
  3147.             description: `Album photo personnalisé avec vos ${favoriteCount} photos favorites. Papier de qualité supérieure, reliure rigide.`,
  3148.             currentPrice: '24,90€',
  3149.             originalPrice: '28,90€',
  3150.             savings: '-14%',
  3151.             badge: 'Populaire',
  3152.             available: favoriteCount > 0,
  3153.             disabled: favoriteCount === 0
  3154.           });
  3155.           // Pack Numérique HD (toujours disponible)
  3156.           products.push({
  3157.             id: 'digital',
  3158.             name: 'Pack Numérique HD',
  3159.             description: `Téléchargement immédiat de vos photos en haute définition. Qualité professionnelle garantie.`,
  3160.             currentPrice: '3,90€',
  3161.             originalPrice: '',
  3162.             savings: '',
  3163.             badge: 'Nouveau',
  3164.             available: favoriteCount > 0,
  3165.             disabled: favoriteCount === 0
  3166.           });
  3167.           // Tirages Premium (disponible si >= 12 favoris)
  3168.          
  3169.             products.push({
  3170.               id: 'prints',
  3171.               name: 'Tirages Premium',
  3172.               description: `${Math.min(favoriteCount, 12)} tirages photo 10x15cm de vos favoris. Papier brillant professionnel, livraison gratuite.`,
  3173.               currentPrice: '9.9€',
  3174.               originalPrice: '',
  3175.               savings: '',
  3176.               badge: 'Débloqué !',
  3177.               available: true,
  3178.               disabled: false
  3179.             });
  3180.           
  3181.           suggestionsHTML += '<div class="products-grid">';
  3182.           
  3183.           products.forEach(product => {
  3184.             suggestionsHTML += `
  3185.               <div class="product-card ${product.disabled ? 'disabled' : ''}" data-product="${product.id}">
  3186.                 <div class="product-badge ${product.disabled ? 'disabled' : ''}">${product.badge}</div>
  3187.                 <div class="product-image">
  3188.                   <img src="${getProductImage(product.id)}" alt="${product.name}" onerror="this.src='/images/produit/albumm.PNG'">
  3189.                 </div>
  3190.                 <div class="product-info">
  3191.                   <h5>${product.name}</h5>
  3192.                   <p>${product.description}</p>
  3193.                   <div class="product-pricing">
  3194.                     <span class="current-price">${product.currentPrice}</span>
  3195.                     ${product.originalPrice ? `<span class="original-price">${product.originalPrice}</span>` : ''}
  3196.                     ${product.savings ? `<span class="savings">${product.savings}</span>` : ''}
  3197.                   </div>
  3198.                   <button class="btn-add-to-cart ${product.disabled ? 'disabled' : ''}" 
  3199.                           onclick="orderProduct('${product.id}')" 
  3200.                           ${product.disabled ? 'disabled title="Ajoutez des favoris pour commander"' : ''}>
  3201.                     <i class="bi bi-cart-plus"></i>
  3202.                     ${product.disabled ? 'Ajoutez des favoris' : 'Commander'}
  3203.                   </button>
  3204.                 </div>
  3205.               </div>
  3206.             `;
  3207.           });
  3208.           
  3209.           suggestionsHTML += '</div>';
  3210.           // Message d'encouragement
  3211.           if (favoriteCount < 12) {
  3212.             const remaining = 12 - favoriteCount;
  3213.             suggestionsHTML += `
  3214.               <div class="encouragement-card">
  3215.                 <div class="encouragement-content">
  3216.                   <div class="encouragement-icon">🎯</div>
  3217.                   <div class="encouragement-text">
  3218.                     <h5>Encore ${remaining} photo${remaining > 1 ? 's' : ''} pour débloquer les Tirages Premium !</h5>
  3219.                     <p>Sélectionnez ${remaining} photo${remaining > 1 ? 's' : ''} supplémentaire${remaining > 1 ? 's' : ''} pour accéder à tous nos produits</p>
  3220.                   </div>
  3221.                 </div>
  3222.               </div>
  3223.             `;
  3224.           }
  3225.           return suggestionsHTML;
  3226.         }
  3227.         // Fonction pour ajouter au panier
  3228.         function addToCart(productId) {
  3229.           // Animation du bouton cadeau
  3230.           const giftButton = document.querySelector('.gift-button');
  3231.           if (giftButton) {
  3232.             if (giftButton.classList) {
  3233.               giftButton.classList.add('active');
  3234.               setTimeout(() => {
  3235.                 if (giftButton.classList) {
  3236.                   giftButton.classList.remove('active');
  3237.                 }
  3238.               }, 600);
  3239.             }
  3240.           }
  3241.           // Afficher une notification
  3242.           showNotification(`Produit ajouté au panier !`, 'success');
  3243.           
  3244.           // Ici vous pourriez ajouter la logique d'ajout au panier réel
  3245.           console.log('Ajout au panier:', productId);
  3246.         }
  3247.         // Fonction pour afficher une notification
  3248.         function showNotification(message, type = 'info') {
  3249.           const notification = document.createElement('div');
  3250.           notification.className = `notification ${type}`;
  3251.           notification.innerHTML = `
  3252.             <i class="bi bi-check-circle"></i>
  3253.             <span>${message}</span>
  3254.           `;
  3255.           
  3256.           document.body.appendChild(notification);
  3257.           
  3258.           setTimeout(() => {
  3259.             if (notification && notification.classList) {
  3260.               notification.classList.add('show');
  3261.             }
  3262.           }, 100);
  3263.           
  3264.           setTimeout(() => {
  3265.             if (notification && notification.classList) {
  3266.               notification.classList.remove('show');
  3267.               setTimeout(() => {
  3268.                 if (document.body.contains(notification)) {
  3269.                   document.body.removeChild(notification);
  3270.                 }
  3271.               }, 300);
  3272.             }
  3273.           }, 3000);
  3274.         }
  3275.         // Fonction pour obtenir le nombre actuel de favoris
  3276.         function getCurrentFavoriteCount() {
  3277.           return getFavoriteItems().length;
  3278.         }
  3279.         // Gestion du localStorage pour l'indicateur "nouveau contenu"
  3280.         const lastVisitKey = 'lastVisitISO';
  3281.         const currentVisit = new Date().toISOString();
  3282.         
  3283.         // Sauvegarder la visite actuelle
  3284.         localStorage.setItem(lastVisitKey, currentVisit);
  3285.         
  3286.         // Vérifier et marquer les jours avec nouveau contenu
  3287.         const dateCards = document.querySelectorAll('.date-card.modern-card');
  3288.         dateCards.forEach(card => {
  3289.           // Ici vous devriez comparer avec updatedAt du backend
  3290.           // Pour l'instant, on simule avec un attribut data-updated
  3291.           const updatedAt = card.getAttribute('data-updated');
  3292.           if (updatedAt) {
  3293.             const lastVisit = localStorage.getItem(lastVisitKey);
  3294.             if (new Date(updatedAt) > new Date(lastVisit)) {
  3295.               if (card && card.classList) {
  3296.                 card.classList.add('has-new-content');
  3297.               }
  3298.               // Ajouter le badge "nouveau" si pas déjà présent
  3299.               if (!card.querySelector('.badge-new')) {
  3300.                 const badge = document.createElement('span');
  3301.                 badge.className = 'badge-new';
  3302.                 badge.setAttribute('aria-label', 'Nouveau contenu');
  3303.                 card.querySelector('.title-line').appendChild(badge);
  3304.               }
  3305.             }
  3306.           }
  3307.         });
  3308.         // Gestion des aria-label dynamiques pour les cartes de jours
  3309.         dateCards.forEach(card => {
  3310.           const dayText = card.querySelector('.day').textContent.trim();
  3311.           const dateText = card.querySelector('.full-date').textContent.trim();
  3312.           const photos = card.querySelector('.media-list-horizontal li:nth-child(1)')?.textContent.trim() || '0';
  3313.           const audios = card.querySelector('.media-list-horizontal li:nth-child(2)')?.textContent.trim() || '0';
  3314.           const videos = card.querySelector('.media-list-horizontal li:nth-child(3)')?.textContent.trim() || '0';
  3315.           
  3316.           const ariaLabel = `Contenu du ${dateText} : ${photos} photos, ${audios} audios, ${videos} vidéos`;
  3317.           card.setAttribute('aria-label', ariaLabel);
  3318.         });
  3319.       });
  3320.     </script>
  3321.     
  3322.     <!-- Make sure we're showing the right content by default if no valid content is marked 'show' -->
  3323.     {% if lastValidIndex > 0 %}
  3324.     <script>
  3325.       document.addEventListener('DOMContentLoaded', function() {
  3326.         // Check if no content is showing
  3327.         if (document.querySelectorAll('.container--gallery .collapse.show').length === 0) {
  3328.           // Show the content for the last valid date
  3329.           var lastValidContent = document.getElementById('demP{{ lastValidIndex }}');
  3330.           if (lastValidContent) {
  3331.             if (lastValidContent.classList) {
  3332.               lastValidContent.classList.add('show');
  3333.             }
  3334.             
  3335.             // Also mark the corresponding date card as active
  3336.             var dateCards = document.querySelectorAll('.date-card');
  3337.             if (dateCards.length >= {{ lastValidIndex }}) {
  3338.               dateCards.forEach(card => {
  3339.                 if (card && card.classList) {
  3340.                   card.classList.remove('active');
  3341.                 }
  3342.               });
  3343.               const targetCard = dateCards[{{ lastValidIndex - 1 }}];
  3344.               if (targetCard && targetCard.classList) {
  3345.                 targetCard.classList.add('active');
  3346.               }
  3347.             }
  3348.           }
  3349.         }
  3350.       });
  3351.     </script>
  3352.     {% endif %}
  3353.   </div>
  3354. </div>
  3355.   {% endblock %} {% block Javascript %}
  3356.   {{ parent() }}
  3357.   <script>// Gestion de la sidebar des favoris
  3358. document.addEventListener('DOMContentLoaded', function() {
  3359.     const sidebar = document.getElementById('favorites-sidebar');
  3360.     const openBtn = document.getElementById('openFavoritesSidebar');
  3361.     const closeBtn = document.querySelector('.favorites-close');
  3362.     const giftButton = document.querySelector('.gift-button');
  3363.     function openSidebar() {
  3364.         if (sidebar && sidebar.classList) {
  3365.           sidebar.classList.add('active');
  3366.           updateFavoritesSidebar();
  3367.         }
  3368.     }
  3369.     function closeSidebar() {
  3370.         if (sidebar && sidebar.classList) {
  3371.           sidebar.classList.remove('active');
  3372.         }
  3373.     }
  3374.     function updateFavoritesSidebar() {
  3375.         const grid = document.getElementById('favorites-grid');
  3376.         const counter = document.getElementById('favorites-counter');
  3377.         const emptyState = document.getElementById('favorites-empty-state');
  3378.         const progress = document.getElementById('favorites-progress');
  3379.         
  3380.         const count = parseInt(document.getElementById('likeCount').textContent);
  3381.         counter.textContent = count +1;
  3382.         
  3383.         if (count === 0) {
  3384.             emptyState.style.display = 'flex';
  3385.             grid.style.display = 'none';
  3386.         } else {
  3387.             emptyState.style.display = 'none';
  3388.             grid.style.display = 'grid';
  3389.             const percentage = (count / 20) * 100;
  3390.             progress.style.width = `${percentage}%`;
  3391.         }
  3392.     }
  3393.     if (openBtn) {
  3394.         openBtn.addEventListener('click', openSidebar);
  3395.     }
  3396.     if (favButton) {
  3397.         favButton.addEventListener('click', openSidebar);
  3398.     }
  3399.     if (closeBtn) {
  3400.         closeBtn.addEventListener('click', closeSidebar);
  3401.     }
  3402.     document.addEventListener('click', (e) => {
  3403.         if (sidebar && sidebar.classList && sidebar.classList.contains('active') && 
  3404.             !sidebar.contains(e.target) && 
  3405.             (!favButton || !favButton.contains(e.target)) && 
  3406.             (!openBtn || !openBtn.contains(e.target))) {
  3407.             closeSidebar();
  3408.         }
  3409.     });
  3410. });
  3411. // Modification des fonctions existantes
  3412. const originalAddFavoris = window.AddFavoris;
  3413. window.AddFavoris = function($id, $idSejour, $urlimg, $description) {
  3414.     if (originalAddFavoris) {
  3415.         originalAddFavoris($id, $idSejour, $urlimg, $description);
  3416.     }
  3417.     
  3418.   
  3419.     
  3420.     // Vérifier si la sidebar est ouverte - noter que nous vérifions la valeur CSS right: 0
  3421.     if ($("#favorites-sidebar").css("right") === "0px") {
  3422.         // Recharger les favoris
  3423.         loadFavorites();
  3424.     }
  3425. };
  3426. const originalSupprimerFavoris = window.supprimerFavoris;
  3427. window.supprimerFavoris = function($id, $idSejour) {
  3428.     if (originalSupprimerFavoris) {
  3429.         originalSupprimerFavoris($id, $idSejour);
  3430.     }
  3431.     
  3432.     // Mettre à jour tous les compteurs de favoris
  3433.     updateAllFavoriteCounters();
  3434.     
  3435.     // Vérifier si la sidebar est ouverte
  3436.     if ($("#favorites-sidebar").css("right") === "0px") {
  3437.         // Recharger les favoris
  3438.         loadFavorites();
  3439.     }
  3440. };
  3441. // Variables globales
  3442. let selectedFavorites = [];
  3443. let allFavorites = [];
  3444. // Fonction pour mettre à jour la sidebar
  3445. function loadFavorites() {
  3446.     $("#favorites-grid").html("<div style='text-align:center;padding:20px;'>Chargement...</div>");
  3447.     
  3448.     $.ajax({
  3449.         url: "/Parent/mes-favoris",
  3450.         type: "GET",
  3451.         dataType: "json",
  3452.         success: function(data) {
  3453.             $("#favorites-grid").empty();
  3454.             allFavorites = data.data || [];
  3455.             
  3456.             if (allFavorites.length > 0) {
  3457.                 $("#favorites-empty-state").hide();
  3458.                 
  3459.                 $.each(allFavorites, function(i, fav) {
  3460.                     var isSelected = selectedFavorites.includes(fav.id);
  3461.                     
  3462.                     var item = $("<div class='favorite-item' style='position:relative;border-radius:8px;overflow:hidden;aspect-ratio:1;cursor:pointer;'></div>");
  3463.                     var img = $("<img style='width:100%;height:100%;object-fit:cover;transition:transform 0.3s ease;'>").attr("src", fav.path).attr("alt", fav.descreption || "Photo favorite");
  3464.                     
  3465.                     // Overlay de sélection
  3466.                     var selectionOverlay = $("<div class='selection-overlay' style='position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;'></div>");
  3467.                     
  3468.                     // Icône de sélection
  3469.                     var checkIcon = $("<div style='width:25px;height:25px;border-radius:50%;border:2px solid white;display:flex;align-items:center;justify-content:center;background:" + (isSelected ? "#F56040" : "transparent") + ";transition:background 0.2s;'></div>");
  3470.                     if (isSelected) {
  3471.                         checkIcon.append("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  3472.                     }
  3473.                     
  3474.                     selectionOverlay.append(checkIcon);
  3475.                     
  3476.                     // Overlay d'action (bouton supprimer)
  3477.                     var actionOverlay = $("<div class='action-overlay' style='position:absolute;top:5px;right:5px;opacity:0;transition:opacity 0.2s;'></div>");
  3478.                   
  3479.                     actionOverlay.append(deleteBtn);
  3480.                     
  3481.                     // Ajouter les événements
  3482.                     item.hover(
  3483.                         function() { $(this).find(".action-overlay").css("opacity", "1"); },
  3484.                         function() { $(this).find(".action-overlay").css("opacity", "0"); }
  3485.                     );
  3486.                     
  3487.                     item.click(function() {
  3488.                         toggleSelection(fav.id, $(this).find(".selection-overlay > div"));
  3489.                     });
  3490.                     
  3491.                     item.append(img).append(selectionOverlay).append(actionOverlay);
  3492.                     $("#favorites-grid").append(item);
  3493.                 });
  3494.                 
  3495.                 $("#favorites-counter").text(allFavorites.length);
  3496.                 
  3497.                 // Mettre à jour le compteur sur le bouton également
  3498.                 $("#openFavoritesSidebar span").text(allFavorites.length);
  3499.                 
  3500.                 // Mettre à jour l'interface produits
  3501.                 updateProductsView();
  3502.                 
  3503.             } else {
  3504.                 $("#favorites-empty-state").show();
  3505.                 $("#favorites-counter").text("0");
  3506.                 $("#openFavoritesSidebar span").text("0");
  3507.                 $("#selection-count").text("0");
  3508.                 updateProductsView();
  3509.             }
  3510.         },
  3511.         error: function() {
  3512.             $("#favorites-grid").html("<div style='color:red;text-align:center;padding:20px;'>Erreur de chargement</div>");
  3513.         }
  3514.     });
  3515. }
  3516. // Fonction pour supprimer un favori
  3517. function removeFavorite(id) {
  3518.     $.ajax({
  3519.         url: "/Parent/remove-favorite/" + id,
  3520.         type: "POST",
  3521.         success: function() {
  3522.             // Retirer de la sélection si présent
  3523.             selectedFavorites = selectedFavorites.filter(favId => favId != id);
  3524.             
  3525.             // Mettre à jour le compteur de sélection
  3526.             $("#selection-count").text(selectedFavorites.length);
  3527.             
  3528.             // Recharger les favoris
  3529.             loadFavorites();
  3530.             
  3531.             // Mettre à jour tous les compteurs de favoris
  3532.             updateAllFavoriteCounters();
  3533.             
  3534.             // Mettre à jour l'interface produits
  3535.             updateProductsView();
  3536.         },
  3537.         error: function() {
  3538.             alert("Erreur lors de la suppression du favori");
  3539.         }
  3540.     });
  3541. }
  3542. // Fonction pour basculer la sélection d'une photo
  3543. function toggleSelection(id, checkElement) {
  3544.     const index = selectedFavorites.indexOf(id);
  3545.     
  3546.     if (index === -1) {
  3547.         // Ajouter à la sélection
  3548.         selectedFavorites.push(id);
  3549.         checkElement.css("background", "#F56040");
  3550.         checkElement.html("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  3551.     } else {
  3552.         // Retirer de la sélection
  3553.         selectedFavorites.splice(index, 1);
  3554.         checkElement.css("background", "transparent");
  3555.         checkElement.html("");
  3556.     }
  3557.     
  3558.     // Mettre à jour le compteur
  3559.     $("#selection-count").text(selectedFavorites.length);
  3560.     
  3561.     // Mettre à jour l'interface produits
  3562.     updateProductsView();
  3563. }
  3564. // Fonction pour mettre à jour la vue des produits
  3565. function updateProductsView() {
  3566.     const current = selectedFavorites.length;
  3567.     $("#product-photo-count").text(current);
  3568.     
  3569.     let remainingForAlbum = Math.max(0, 20 - current);
  3570.     let remainingForPochette = Math.max(0, 12 - current);
  3571.     let remainingForPack = Math.max(0, 12 - current);
  3572.     const progressBar = (count, total, color) => `
  3573.         <div style="margin: 5px 0;">
  3574.             <div style="background-color: #e9ecef; border-radius: 5px; overflow: hidden; height: 8px;">
  3575.                 <div style="width: ${(count / total) * 100}%; background-color: ${color}; height: 100%;"></div>
  3576.             </div>
  3577.             <small style="font-size: 12px;">${count}/${total} photos</small>
  3578.         </div>
  3579.     `;
  3580.     // Liste des produits
  3581.     const products = [
  3582.         {
  3583.             name: "Pack numérique (20 photos)",
  3584.             required: 20,
  3585.             remaining: Math.max(0, 20 - current),
  3586.             image: "/images/produit/photoNumerique.jpg",
  3587.             color: "#4caf50",
  3588.             link: "{{ path('PackPhotosNumerique_Favoris', {'nbr':20}) }}",
  3589.         },
  3590.        +
  3591.         {
  3592.             name: "Pochette photo (12 photos)",
  3593.             required: 12,
  3594.             remaining: Math.max(0, 12 - current),
  3595.             image: "/images/produit/PochettePhoto5sur5-2.jpg",
  3596.             color: "#2196f3",
  3597.             link: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}",
  3598.         },
  3599.     ].sort((a, b) => a.remaining - b.remaining);
  3600.     const productList = products
  3601.         .map((product) => {
  3602.             const count = current;
  3603.             const total = product.required;
  3604.             const remaining = product.remaining;
  3605.             return `
  3606.                 <li style="margin-bottom: 20px;">
  3607.                     <div style="display: flex; align-items: center; gap: 10px;">
  3608.                         <img src="${product.image}" alt="${product.name}" style="width: 70px; height: 70px; border-radius: 5px; object-fit: cover;" />
  3609.                         <div style="flex: 1;">
  3610.                             <strong style="font-size: 14px;">${product.name}</strong>
  3611.                             ${progressBar(count, total, product.color)}
  3612.                             ${
  3613.                                 `<small style="color: ${product.color}; font-size: 12px;">
  3614.                                     Encore ${remaining} photos pour compléter ${product.name.toLowerCase()}
  3615.                                 </small>
  3616.                                 <button
  3617.                                     style="
  3618.                                         margin-top: 5px;
  3619.                                         padding: 6px 12px;
  3620.                                         background-color: ${product.color};
  3621.                                         color: white;
  3622.                                         border: none;
  3623.                                         border-radius: 5px;
  3624.                                         font-size: 13px;
  3625.                                         cursor: pointer;
  3626.                                     "
  3627.                                     onclick="window.location.href='${product.link}'"
  3628.                                 >
  3629.                                     Commander
  3630.                                 </button>`
  3631.                             }
  3632.                         </div>
  3633.                     </div>
  3634.                 </li>
  3635.             `;
  3636.         })
  3637.         .join("");
  3638.     const boutiqueButton = `
  3639.         <li style="margin-top: 25px; text-align: center;">
  3640.             <button
  3641.                 style="
  3642.                     padding: 8px 15px;
  3643.                     background-color: #F56040;
  3644.                     color: white;
  3645.                     border: none;
  3646.                     border-radius: 5px;
  3647.                     font-size: 14px;
  3648.                     width: 170px;
  3649.                     height: 40px;
  3650.                     cursor: pointer;
  3651.                 "
  3652.                 onclick="window.location.href='{{ path('boutique5sur5') }}'"
  3653.             >
  3654.                 Voir toute la boutique
  3655.             </button>
  3656.         </li>
  3657.     `;
  3658.     $("#product-list").html(productList + boutiqueButton);
  3659. }
  3660. // Modifications au script existant
  3661. $(document).ready(function() {
  3662.     // Fonctions pour ouvrir/fermer la sidebar
  3663.     $("#openFavoritesSidebar").click(function() {
  3664.         $("#favorites-sidebar").css("right", "0");
  3665.         loadFavorites();
  3666.     });
  3667.     
  3668.     $("#close-favorites-btn").click(function() {
  3669.         closeFavoritesSidebar();
  3670.     });
  3671.     
  3672.     // Fonction pour fermer la sidebar
  3673.     window.closeFavoritesSidebar = function() {
  3674.         $("#favorites-sidebar").css("right", "-500px");
  3675.     };
  3676.     
  3677.     // Tout sélectionner / Tout désélectionner
  3678.     $("#select-all-btn").click(function() {
  3679.         if (selectedFavorites.length === allFavorites.length) {
  3680.             // Tout désélectionner
  3681.             selectedFavorites = [];
  3682.             $(".selection-overlay > div").css("background", "transparent").html("");
  3683.             $(this).text("Tout sélectionner");
  3684.         } else {
  3685.             // Tout sélectionner
  3686.             selectedFavorites = allFavorites.map(fav => fav.id);
  3687.             $(".selection-overlay > div").css("background", "#F56040").html("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  3688.             $(this).text("Tout désélectionner");
  3689.         }
  3690.         
  3691.         // Mettre à jour le compteur
  3692.         $("#selection-count").text(selectedFavorites.length);
  3693.         
  3694.         // Mettre à jour l'interface produits
  3695.         updateProductsView();
  3696.     });
  3697.     
  3698.     // Gestion des onglets
  3699.     $(".sidebar-tab").click(function() {
  3700.         $(".sidebar-tab").removeClass("active").css({
  3701.             "color": "#666",
  3702.             "border-bottom": "none"
  3703.         });
  3704.         $(this).addClass("active").css({
  3705.             "color": "#F56040",
  3706.             "border-bottom": "2px solid #F56040"
  3707.         });
  3708.         
  3709.         const tabId = $(this).attr("id");
  3710.         $(".tab-content").hide();
  3711.         
  3712.         if (tabId === "tab-photos") {
  3713.             $("#photos-content").show();
  3714.         } else if (tabId === "tab-products") {
  3715.             $("#products-content").show();
  3716.         }
  3717.     });
  3718.     
  3719.     // Modifier les fonctions existantes pour intercepter l'ajout/suppression de favoris
  3720.     const originalAddFavoris = window.AddFavoris;
  3721.     window.AddFavoris = function($id, $idSejour, $urlimg, $description) {
  3722.         if (originalAddFavoris) {
  3723.             originalAddFavoris($id, $idSejour, $urlimg, $description);
  3724.         }
  3725.         
  3726.         // Mettre à jour tous les compteurs de favoris
  3727.         updateAllFavoriteCounters();
  3728.         
  3729.         // Recharger les favoris si la sidebar est ouverte
  3730.         if ($("#favorites-sidebar").css("right") === "0px") {
  3731.             loadFavorites();
  3732.         }
  3733.     };
  3734.     const originalSupprimerFavoris = window.supprimerFavoris;
  3735.     window.supprimerFavoris = function($id, $idSejour) {
  3736.         if (originalSupprimerFavoris) {
  3737.             originalSupprimerFavoris($id, $idSejour);
  3738.         }
  3739.         
  3740.         // Mettre à jour tous les compteurs de favoris
  3741.         updateAllFavoriteCounters();
  3742.         
  3743.         // Recharger les favoris si la sidebar est ouverte
  3744.         if ($("#favorites-sidebar").css("right") === "0px") {
  3745.             // Retirer de la sélection si présent
  3746.             selectedFavorites = selectedFavorites.filter(favId => favId != $id);
  3747.             loadFavorites();
  3748.         }
  3749.     };
  3750.     
  3751.     // Fermer en cliquant en dehors
  3752.     $(document).click(function(event) {
  3753.         if (!$(event.target).closest("#favorites-sidebar").length && 
  3754.             !$(event.target).closest("#openFavoritesSidebar").length && 
  3755.             $("#favorites-sidebar").css("right") === "0px") {
  3756.             closeFavoritesSidebar();
  3757.         }
  3758.     });
  3759. });
  3760. // Initialisation de la sidebar
  3761. $(document).ready(function() {
  3762.     // Fonctions pour ouvrir/fermer la sidebar
  3763.     $("#openFavoritesSidebar").click(function() {
  3764.         $("#favorites-sidebar").css("right", "0");
  3765.         loadFavorites();
  3766.     });
  3767.     
  3768.     $("#close-favorites-btn").click(function() {
  3769.         $("#favorites-sidebar").css("right", "-500px");
  3770.     });
  3771.     
  3772.     // Fermer en cliquant en dehors
  3773.     $(document).click(function(event) {
  3774.         if (!$(event.target).closest("#favorites-sidebar").length && 
  3775.             !$(event.target).closest("#openFavoritesSidebar").length && 
  3776.             $("#favorites-sidebar").css("right") === "0px") {
  3777.             $("#favorites-sidebar").css("right", "-500px");
  3778.         }
  3779.     });
  3780. });
  3781.   </script>
  3782.   <script src="{{ asset('js/splide.min.js') }}" defer></script>
  3783.    
  3784. <script>
  3785.   document.addEventListener('DOMContentLoaded', function () {
  3786.     const openBtn = document.getElementById('openFavoritesSidebar');
  3787.     const sidebar = document.getElementById('favorites-sidebar');
  3788.     const closeBtn = document.getElementById('favorites-close');
  3789.     if (openBtn && sidebar) {
  3790.       openBtn.addEventListener('click', () => {
  3791.         if (sidebar && sidebar.classList) {
  3792.           sidebar.classList.add('active');
  3793.         }
  3794.       });
  3795.     }
  3796.     if (closeBtn && sidebar) {
  3797.       closeBtn.addEventListener('click', () => {
  3798.         if (sidebar && sidebar.classList) {
  3799.           sidebar.classList.remove('active');
  3800.         }
  3801.       });
  3802.     }
  3803.   });
  3804. </script>
  3805.  
  3806. <script>
  3807. document.addEventListener('DOMContentLoaded', function() {
  3808.   const filterBadges = document.querySelectorAll('.filter-badge');
  3809.   filterBadges.forEach(badge => {
  3810.     badge.addEventListener('click', function() {
  3811.       // Désactiver tous les badges
  3812.       filterBadges.forEach(b => {
  3813.         if (b && b.classList) {
  3814.           b.classList.remove('active');
  3815.         }
  3816.       });
  3817.       // Activer le badge cliqué
  3818.       if (this && this.classList) {
  3819.         this.classList.add('active');
  3820.       }
  3821.     });
  3822.   });
  3823. });
  3824. </script>
  3825.   <script
  3826.     type="text/javascript"
  3827.     src="{{ asset('Accueil/js/jquery.magnific-popup.min.js') }}"
  3828.   ></script>
  3829.   <script>
  3830. document.addEventListener('DOMContentLoaded', function () {
  3831.   const prevButtons = document.querySelectorAll('.btn-prev-day');
  3832.   const nextButtons = document.querySelectorAll('.btn-next-day');
  3833.   const dateCards = document.querySelectorAll('.date-card.modern-card');
  3834.   const collapseSections = document.querySelectorAll('.collapse');
  3835.   // 🎯 MEDIA TOOLBAR CAPSULE - État global et logique (Senior UX)
  3836.   (() => {
  3837.     // État global de filtre
  3838.     const MediaFilter = { current: 'all' };
  3839.     // Compteurs dynamiques
  3840.     const counters = { photo: 0, audio: 0, video: 0, fav: 0 };
  3841.     
  3842.     const updateCounts = () => {
  3843.       // Compter les médias dans le DOM
  3844.       counters.photo = document.querySelectorAll('.photo-item, [data-media-type="photo"]').length;
  3845.       counters.video = document.querySelectorAll('[data-media-type="video"]').length;
  3846.       counters.audio = document.querySelectorAll('.audio-message-item[data-type="audio"]').length;
  3847.       // NE PAS recalculer seulement les favoris - ils utilisent {{ nblikes }}
  3848.       
  3849.       // Mettre à jour l'affichage (sauf favoris)
  3850.       Object.entries(counters).forEach(([k, v]) => {
  3851.         if (k !== 'fav') { // Garder seulement le compteur favoris intact
  3852.           document.querySelectorAll(`.mtb-count[data-bind="${k}"]`).forEach(el => el.textContent = v);
  3853.         }
  3854.       });
  3855.     };
  3856.     // Simple fonction pour le filtre actuel
  3857.     function setActive(filter) {
  3858.       MediaFilter.current = filter;
  3859.       
  3860.       // Exception : ne pas ajouter de classe active au bouton photos
  3861.       if (filter === 'photos') {
  3862.         const photosBtn = document.querySelector('.mtb-btn[data-filter="photos"]');
  3863.         if (photosBtn) {
  3864.           photosBtn.classList.remove('active', 'is-active');
  3865.         }
  3866.       }
  3867.     }
  3868.     // Fonction simplifiée qui mappe et appelle filterContent
  3869.     function applyFilter(filter) {
  3870.       // Mapper les filtres de la capsule vers les filtres existants si nécessaire
  3871.       const filterMap = {
  3872.         'all': 'toutVoir',
  3873.         'photos': 'photos', 
  3874.         'audio': 'audio',
  3875.         'videos': 'videos',
  3876.         'favoris': 'favoris'
  3877.       };
  3878.       
  3879.       const mappedFilter = filterMap[filter] || filter;
  3880.       
  3881.       // Pour le filtre "all", s'assurer que la vue des jours est restaurée
  3882.       if (filter === 'all') {
  3883.         const containerGallery = document.querySelector('.container--gallery');
  3884.         if (containerGallery) {
  3885.           containerGallery.style.display = 'block';
  3886.         }
  3887.       }
  3888.       
  3889.       if (typeof window.filterContent === 'function') {
  3890.         window.filterContent(mappedFilter);
  3891.       } else {
  3892.         console.error('window.filterContent n\'est pas disponible');
  3893.       }
  3894.     }
  3895.     // Click handlers - exactement la même logique que stat-pill
  3896.     document.addEventListener('click', (e) => {
  3897.       const btn = e.target.closest('.mtb-btn');
  3898.       if (!btn) return;
  3899.       // Bouton fermer
  3900.       if (btn.classList.contains('mtb-close')) {
  3901.         document.querySelector('.media-toolbar')?.remove();
  3902.         return;
  3903.       }
  3904.       const filter = btn.dataset.filter;
  3905.       if (!filter) return;
  3906.       // Debug spécifique pour le bouton favoris
  3907.       if (filter === 'favoris') {
  3908.         console.log('🔍 Bouton favoris cliqué - Debug:', {
  3909.           btn: btn,
  3910.           filter: filter,
  3911.           hasFilterContent: typeof window.filterContent === 'function',
  3912.           mesFavCount: document.getElementById('mesFavCount')?.textContent
  3913.         });
  3914.         
  3915.         // Protection : empêcher la décrémentation du compteur pour les clics de filtre
  3916.         e.stopPropagation();
  3917.         e.preventDefault();
  3918.         
  3919.         // Appeler uniquement la fonction de filtrage
  3920.         if (typeof window.filterContent === 'function') {
  3921.           window.filterContent('favoris');
  3922.         } else {
  3923.           console.error('❌ window.filterContent n\'est pas disponible');
  3924.         }
  3925.         return; // Sortir de la fonction pour éviter le traitement normal
  3926.       }
  3927.       // Exception pour le bouton photos : ne pas modifier les états actifs
  3928.       if (btn.dataset.filter === 'photos') {
  3929.         // Pour le bouton photos, ne pas toucher aux autres boutons
  3930.         // Juste exécuter l'action sans changer les états
  3931.       } else {
  3932.         // Pour les autres boutons : gérer les états actifs normalement
  3933.         const mtbBtns = document.querySelectorAll('.mtb-btn[data-filter]');
  3934.         for (let i = 0; i < mtbBtns.length; i++) {
  3935.           const b = mtbBtns[i];
  3936.           if (b && b.classList) {
  3937.             if (b === btn) {
  3938.               b.classList.add('active');
  3939.             } else {
  3940.               b.classList.remove('active');
  3941.             }
  3942.           }
  3943.         }
  3944.       }
  3945.       
  3946.       // Déclencher le filtrage du contenu - même logique que stat-pill
  3947.       console.log('mtb-btn click:', filter, 'filterContent type:', typeof window.filterContent);
  3948.       if (typeof window.filterContent === 'function') {
  3949.         window.filterContent(filter);
  3950.       } else {
  3951.         console.error('window.filterContent n\'est pas une fonction!');
  3952.       }
  3953.     });
  3954.     // Expose un setter global
  3955.     window.setMediaFilter = (filter) => {
  3956.       // Pour le bouton photos : ne pas modifier les états
  3957.       if (filter !== 'photos') {
  3958.         setActive(filter);
  3959.       }
  3960.       applyFilter(filter);
  3961.     };
  3962.     // Mise à jour des compteurs quand les favoris changent
  3963.     window.addEventListener('favorites:updated', updateCounts);
  3964.     
  3965.     // Initialisation
  3966.     setTimeout(() => {
  3967.       updateCounts();
  3968.       setActive('all');
  3969.     }, 100);
  3970.     // Mise à jour périodique des compteurs (sauf favoris qui reste fixe)
  3971.     setInterval(updateCounts, 2000);
  3972.   })();
  3973.   function navigateToDay(index) {
  3974.      if (index >= 0 && index < collapseSections.length) {
  3975.       // Fermer tous les jours
  3976.       collapseSections.forEach(s => {
  3977.         if (s && s.classList) {
  3978.           s.classList.remove('show');
  3979.         }
  3980.       });
  3981.       dateCards.forEach(c => {
  3982.         if (c && c.classList) {
  3983.           c.classList.remove('active');
  3984.         }
  3985.       });
  3986.       // Ouvrir le bon jour
  3987.       const targetCollapse = collapseSections[index];
  3988.       const targetCard = dateCards[index];
  3989.       if (targetCollapse && targetCard) {
  3990.         if (targetCollapse.classList) {
  3991.           targetCollapse.classList.add('show');
  3992.         }
  3993.         if (targetCard.classList) {
  3994.           targetCard.classList.add('active');
  3995.         }
  3996.       }
  3997.     }
  3998.   }
  3999.   prevButtons.forEach(button => {
  4000.     button.addEventListener('click', function () {
  4001.       const targetIndex = parseInt(this.dataset.target, 10);
  4002.       navigateToDay(targetIndex);
  4003.     });
  4004.   });
  4005.   nextButtons.forEach(button => {
  4006.     button.addEventListener('click', function () {
  4007.       const targetIndex = parseInt(this.dataset.target, 10);
  4008.       navigateToDay(targetIndex);
  4009.     });
  4010.   });
  4011. });
  4012. </script>
  4013.   <script>
  4014.     document.addEventListener('DOMContentLoaded', function() {
  4015.   const style = document.createElement('style');
  4016.   style.textContent = `
  4017.     .hidden {
  4018.       display: none !important;
  4019.     }
  4020.   `;
  4021.   document.head.appendChild(style);
  4022. });
  4023.     document.addEventListener('DOMContentLoaded', function() {
  4024.     // Get the gift button
  4025.     const giftButton = document.querySelector('.gift-button');
  4026.     if (giftButton) {
  4027.       // Keep it clickable for the gift functionality
  4028.       giftButton.style.pointerEvents = 'auto';
  4029.       
  4030.       // Add click handler for gift action
  4031.       giftButton.onclick = function(e) {
  4032.         e.preventDefault();
  4033.         e.stopPropagation();
  4034.         return false;
  4035.       };
  4036.       
  4037.       // Make sure hover still works
  4038.       favoriteButton.addEventListener('mouseover', function() {
  4039.         document.getElementById('purchase-alert').style.display = 'block';
  4040.       });
  4041.       
  4042.       // Keep any existing hover functionality
  4043.       if (typeof showSelection === 'function') {
  4044.         favoriteButton.onmouseover = showSelection;
  4045.       }
  4046.     }
  4047.     
  4048.     // Make sure the purchase alert remains interactive
  4049.     const purchaseAlert = document.getElementById('purchase-alert');
  4050.     if (purchaseAlert) {
  4051.       purchaseAlert.style.pointerEvents = 'auto';
  4052.     }
  4053.   });
  4054.     // Sélection des éléments
  4055.     const purchaseAlert = document.getElementById("purchase-alert");
  4056.     const alertContent = document.getElementById("purchase-alert-content");
  4057.     const likeCountLabel = document.getElementById("likeCount");
  4058.     
  4059.     // Fonction pour obtenir le nombre actuel de favoris
  4060.     function getCurrentFavoriteCount() {
  4061.         const likeCountLabel = document.getElementById("likeCount");
  4062.         return parseInt(likeCountLabel?.textContent.trim(), 10) || 0;
  4063.     }
  4064.     // Fonction pour mettre à jour le contenu de l'alerte
  4065.     function updatePurchaseAlert(current) {
  4066.   let remainingForAlbum = Math.max(0, 20 - current);
  4067.   let remainingForPochette = Math.max(0, 12 - current);
  4068.   let remainingForPack = Math.max(0, 12 - current);
  4069.   const progressBar = (count, total, color) => `
  4070.   <div style="margin: 5px 0;">
  4071.     <div style="background-color: #e9ecef; border-radius: 5px; overflow: hidden; height: 8px;">
  4072.       <div style="width: ${
  4073.         (count / total) * 100
  4074.       }%; background-color: ${color}; height: 100%;"></div>
  4075.     </div>
  4076.     <small style="font-size: 12px;">${count}/${total} photos</small>
  4077.   </div>
  4078. `;
  4079.   // Use Twig paths here:
  4080.   const products = [
  4081.       {
  4082.       name: "Pochette photo (12 photos)",
  4083.       required: 12,
  4084.       remaining: remainingForPochette,
  4085.       image: "/images/produit/PochettePhoto5sur5-2.jpg",
  4086.       color: "#2196f3",
  4087.       link: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}",
  4088.     },
  4089.        {
  4090.       name: "Pack numérique (20 photos)",
  4091.       required: 20,
  4092.       remaining: remainingForAlbum,
  4093.       image: "/images/produit/photoNumerique.jpg",
  4094.       color: "#4caf50",
  4095.       link: "{{ path('PackPhotosNumerique_Favoris', {'nbr': 20}) }}",
  4096.     }
  4097.   
  4098.   
  4099.     
  4100.   ].sort((a, b) => a.remaining - b.remaining);
  4101.   const productList = products
  4102.     .map((product) => {
  4103.       const count = current;
  4104.       const total = product.required;
  4105.       const remaining = product.remaining;
  4106.       return `
  4107.     <li style="margin-bottom: 15px;">
  4108.       <div style="display: flex; align-items: center; gap: 10px;">
  4109.         <img
  4110.           src="${product.image}"
  4111.           alt="${product.name}"
  4112.           style="width: 65px; height: 65px; border-radius: 5px; margin-top:-19px"
  4113.         />
  4114.         <div style="flex: 1;">
  4115.           <strong style="font-size: 14px;">${product.name}</strong>
  4116.           ${progressBar(count, total, product.color)}
  4117.           ${
  4118.             remaining > 0
  4119.               ? `<small style="color: ${product.color}; font-size: 12px;">
  4120.                    Encore ${remaining} photos ❤️ pour compléter ${product.name.toLowerCase()}
  4121.                  </small>`
  4122.               : `<button
  4123.                   style="
  4124.                     margin-top: 5px;
  4125.                     padding: 5px 8px;
  4126.                     background-color: ${product.color};
  4127.                     color: white;
  4128.                     border: none;
  4129.                     border-radius: 5px;
  4130.                     font-size: 12px;
  4131.                     cursor: pointer;
  4132.                   "
  4133.                   onclick="window.location.href='${product.link}'"
  4134.                 >
  4135.                   Commander
  4136.                 </button>`
  4137.           }
  4138.         </div>
  4139.       </div>
  4140.     </li>
  4141.   `;
  4142.     })
  4143.     .join("");
  4144.   const plusButton = `
  4145.     <li style="margin-bottom: 15px; text-align: center;">
  4146.       <button
  4147.         style="
  4148.           padding: 5px 8px;
  4149.           background-color: #F56040;
  4150.           color: white;
  4151.           border: none;
  4152.           border-radius: 5px;
  4153.           font-size: 14px;
  4154.           line-height: 1;
  4155.           width: 150px;
  4156.           height: 40px;
  4157.           cursor: pointer;
  4158.         "
  4159.         onclick="window.location.href='{{ path('boutique5sur5') }}'"
  4160.       >
  4161.         Aller à la boutique
  4162.       </button>
  4163.     </li>
  4164.   `;
  4165.   if (current === 0) {
  4166.     alertContent.innerHTML = `
  4167.     <p style="font-size: 16px; font-weight: bold; color: #333;">
  4168.       Vous n'avez pas encore de photos favorites !
  4169.     </p>
  4170.     <p style="margin-bottom: 20px; color: #555;">
  4171.       Commencez à ajouter vos moments préférés pour profiter de nos offres.
  4172.     </p>
  4173.     <ul style="list-style-type: none; padding: 0;">${productList}${plusButton}</ul>
  4174.   `;
  4175.   } else {
  4176.     alertContent.innerHTML = `
  4177.     <p style="font-size: 16px; font-weight: bold; color: #333;">
  4178.       Vous avez atteint ${current} photos favorites !
  4179.     </p>
  4180.     <p style="margin-bottom: 20px; color: #555;">
  4181.       Profitez de nos offres spéciales :
  4182.     </p>
  4183.     <ul style="list-style-type: none; padding: 0;">${productList}${plusButton}</ul>
  4184.   `;
  4185.   }
  4186.   if (purchaseAlert && purchaseAlert.classList) {
  4187.     purchaseAlert.classList.remove("hidden");
  4188.   }
  4189.   clearTimeout(window.purchaseAlertTimeout);
  4190.   window.purchaseAlertTimeout = setTimeout(() => {
  4191.     if (!purchaseAlert.matches(":hover")) {
  4192.       closePurchaseAlert();
  4193.     }
  4194.   }, 5000);
  4195. }
  4196.     // Fonction pour fermer l'alerte
  4197.     function closePurchaseAlert() {
  4198.       if (purchaseAlert && purchaseAlert.classList) {
  4199.         purchaseAlert.classList.add("hidden");
  4200.       }
  4201.     }
  4202.     // Événement pour mettre à jour le contenu et afficher la popover dynamiquement au hover
  4203.     document.querySelector(".gift-button").addEventListener("mouseover", () => {
  4204.       const currentCount = getCurrentFavoriteCount();
  4205.       updatePurchaseAlert(currentCount);
  4206.       if (purchaseAlert && purchaseAlert.classList) {
  4207.         purchaseAlert.classList.remove("cachee"); // Réaffiche la popover
  4208.       }
  4209.     });
  4210.     // Nouvelles fonctions pour le bouton cadeau
  4211.     function showGiftMessage() {
  4212.       const tooltip = document.getElementById("giftTooltip");
  4213.       if (tooltip) {
  4214.         if (tooltip && tooltip.classList) {
  4215.           tooltip.classList.add("show");
  4216.         }
  4217.       }
  4218.     }
  4219.     function hideGiftMessage() {
  4220.       const tooltip = document.getElementById("giftTooltip");
  4221.       if (tooltip) {
  4222.         if (tooltip && tooltip.classList) {
  4223.           tooltip.classList.remove("show");
  4224.         }
  4225.       }
  4226.     }
  4227.     function showSelection() {
  4228.       document.getElementById("purchase-alert").style.display = "block";
  4229.     }
  4230.     function hideSelection() {
  4231.       document.getElementById("selectionPopover").style.display = "none";
  4232.     }
  4233.     document.addEventListener("DOMContentLoaded", function () {
  4234.       const container = document.querySelector(".date-container");
  4235.       // Vérifie si le conteneur existe pour éviter les erreurs
  4236.       if (container) {
  4237.         container.scrollTo({
  4238.           left: container.scrollWidth, // Scroll directement à la position maximale
  4239.           behavior: "smooth", // Défilement fluide
  4240.         });
  4241.       }
  4242.     });
  4243.     document.addEventListener("DOMContentLoaded", function () {
  4244.       const container = document.querySelector(".date-container");
  4245.       const leftArrow = document.querySelector(".scroll-btn.left");
  4246.       const rightArrow = document.querySelector(".scroll-btn.right");
  4247.       // Fonction pour vérifier le débordement et activer/désactiver les flèches
  4248.       function updateArrowsVisibility() {
  4249.         const isOverflowing = container.scrollWidth > container.clientWidth; // Vérifie si débordement
  4250.         leftArrow.style.display = isOverflowing ? "flex" : "none";
  4251.         rightArrow.style.display = isOverflowing ? "flex" : "none";
  4252.       }
  4253.       // Fonction pour défiler
  4254.       function scrollContainer(direction) {
  4255.         container.scrollBy({
  4256.           left: direction === "left" ? -200 : 200, // Défiler à gauche ou à droite
  4257.           behavior: "smooth",
  4258.         });
  4259.       }
  4260.       // Ajout des événements de clic pour les flèches
  4261.       leftArrow.addEventListener("click", () => scrollContainer("left"));
  4262.       rightArrow.addEventListener("click", () => scrollContainer("right"));
  4263.       // ⚡ OPTIMISÉ: Debounce resize pour éviter surcharge
  4264.       let resizeTimeout;
  4265.       const debouncedResize = () => {
  4266.         clearTimeout(resizeTimeout);
  4267.         resizeTimeout = setTimeout(updateArrowsVisibility, 150);
  4268.       };
  4269.       
  4270.       updateArrowsVisibility();
  4271.       window.addEventListener("resize", debouncedResize, { passive: true });
  4272.     });
  4273.     document.addEventListener("DOMContentLoaded", function () {
  4274.       const container = document.querySelector(".date-container");
  4275.       const leftBtn = document.querySelector(".scroll-btn.left");
  4276.       const rightBtn = document.querySelector(".scroll-btn.right");
  4277.       leftBtn.addEventListener("click", () => {
  4278.         container.scrollBy({
  4279.           left: -200, // Défile vers la gauche
  4280.           behavior: "smooth",
  4281.         });
  4282.       });
  4283.       rightBtn.addEventListener("click", () => {
  4284.         container.scrollBy({
  4285.           left: 200, // Défile vers la droite
  4286.           behavior: "smooth",
  4287.         });
  4288.       });
  4289.     });
  4290.  document.addEventListener("DOMContentLoaded", function () {
  4291.     // Sélectionnez tous les badges de filtre
  4292.     const filterBadges = document.querySelectorAll(".filter-badge");
  4293.     // Sélectionnez tous les éléments de la galerie
  4294.     const galleryItems = document.querySelectorAll(".column");
  4295.     // Sélectionnez tous les jours
  4296.     const days = document.querySelectorAll(".collapse");
  4297.     // Fonction pour réinitialiser les filtres
  4298.     function resetFilters() {
  4299.         // Réinitialisez tous les éléments de la galerie
  4300.         galleryItems.forEach((item) => {
  4301.             item.style.display = "block";
  4302.         });
  4303.         // Réinitialisez les états des badges
  4304.         filterBadges.forEach((badge) => {
  4305.           if (badge && badge.classList) {
  4306.             badge.classList.remove("active");
  4307.           }
  4308.         });
  4309.     }
  4310.     // Ajoutez un gestionnaire d'événements pour chaque badge
  4311.     filterBadges.forEach((badge) => {
  4312.         badge.addEventListener("click", function () {
  4313.             const filter = this.getAttribute("data-filter");
  4314.             // Réinitialisez l'état actif pour tous les badges
  4315.             filterBadges.forEach((btn) => {
  4316.               if (btn && btn.classList) {
  4317.                 btn.classList.remove("active");
  4318.               }
  4319.             });
  4320.             // Ajoutez l'état actif au badge cliqué
  4321.             if (this && this.classList) {
  4322.               this.classList.add("active");
  4323.             }
  4324.             // Affichez ou masquez les éléments de la galerie
  4325.             galleryItems.forEach((item) => {
  4326.                 if (filter === "all") {
  4327.                     item.style.display = "block";
  4328.                 } else if (filter === "photo" && item.querySelector("img")) {
  4329.                     item.style.display = "block";
  4330.                 } else if (filter === "video" && item.querySelector("video")) {
  4331.                     item.style.display = "block";
  4332.                 } else if (filter === "audio" && item.classList && item.classList.contains("audio-message-item")) {
  4333.                     item.style.display = "block";
  4334.                 } else {
  4335.                     item.style.display = "none";
  4336.                 }
  4337.             });
  4338.         });
  4339.     });
  4340.     // Réinitialiser les filtres lors du changement de jour
  4341.     days.forEach((day) => {
  4342.         day.addEventListener("show.bs.collapse", function () {
  4343.             resetFilters();
  4344.         });
  4345.     });
  4346. });
  4347.     $(document).ready(function () {
  4348.       let zoomCounter = 0; // Initialize zoom counter
  4349.       let currentImageSrc = ""; // Track current image source
  4350.       let lastClickPosition = { x: 50, y: 50 }; // Default to center of image
  4351.       $(".container--gallery").magnificPopup({
  4352.         delegate: "a",
  4353.         type: "image",
  4354.         mainClass: "mfp-with-zoom mfp-img-mobile",
  4355.         image: {
  4356.           verticalFit: true,
  4357.         },
  4358.         gallery: {
  4359.           enabled: true,
  4360.           tPrev: "Previous (Left arrow key)", // Alt text on left arrow
  4361.           tNext: "Next (Right arrow key)", // Alt text on right arrow
  4362.           tCounter: "%curr% of %total%", // Markup for "1 of 7" counter
  4363.         },
  4364.         zoom: {
  4365.           enabled: true,
  4366.           duration: 300,
  4367.           opener: function (element) {
  4368.             return element.find("img");
  4369.           },
  4370.         },
  4371.         callbacks: {
  4372.           open: function () {
  4373.             // Get current image data from the link that was clicked
  4374.             const currentLink = this.currItem.el;
  4375.             const imageId =
  4376.               currentLink
  4377.                 .closest(".photo-zoom")
  4378.                 .find(".heart-icon")
  4379.                 .data("id") || "";
  4380.             const sejourId =
  4381.               currentLink
  4382.                 .closest(".photo-zoom")
  4383.                 .find(".heart-icon")
  4384.                 .data("sejour-id") || "";
  4385.             const imagePath =
  4386.               currentLink
  4387.                 .closest(".photo-zoom")
  4388.                 .find(".heart-icon")
  4389.                 .data("path") || "";
  4390.             const imageDesc =
  4391.               currentLink
  4392.                 .closest(".photo-zoom")
  4393.                 .find(".heart-icon")
  4394.                 .data("description") || "";
  4395.             const isFavorite = currentLink
  4396.               .closest(".photo-zoom")
  4397.               .find(".heart-icon i")
  4398.               .hasClass("bi-heart-fill");
  4399.             const favoriteIconClass = isFavorite ? "bi-heart-fill" : "bi-heart";
  4400.             const favoriteIconColor = isFavorite ? "#f56040" : "white";
  4401.             const favoriteTooltip = isFavorite
  4402.               ? "Retirer des favoris"
  4403.               : "Ajouter aux favoris";
  4404.             const zoomControls = `
  4405.           <div class="mfp-zoom-controls">
  4406.             <button class="zoom-btn zoom-out" title="Zoom Out"><i class="fa fa-search-minus"></i></button>
  4407.             <button class="zoom-btn zoom-in" title="Zoom In"><i class="fa fa-search-plus"></i></button>
  4408.           </div>
  4409.           <div class="mfp-favorite">
  4410.             <button class="favorite-btn" 
  4411.                     data-id="${imageId}" 
  4412.                     data-sejour-id="${sejourId}" 
  4413.                     data-path="${imagePath}" 
  4414.                     data-description="${imageDesc}"
  4415.                     title="${favoriteTooltip}">
  4416.               <i class="bi ${favoriteIconClass}" style="color: ${favoriteIconColor}; text-shadow: 0px 0px 3px rgba(0,0,0,0.5);"></i>
  4417.             </button>
  4418.           </div>
  4419.           <div class="mfp-counter"></div>
  4420.         `;
  4421.             $(".mfp-content").append(zoomControls);
  4422.             initializeZoomControls();
  4423.             initializeFavoriteButton();
  4424.             const intervalId = setInterval(() => {
  4425.               const newImageSrc = $(".mfp-img").attr("src");
  4426.               if (newImageSrc !== currentImageSrc) {
  4427.                 currentImageSrc = newImageSrc;
  4428.                 zoomCounter = 0;
  4429.                 lastClickPosition = { x: 50, y: 50 }; // Reset to center
  4430.                 attachZoomHandler(); // Reattach zoom handler to new image
  4431.                 $(".mfp-img").css({
  4432.                   "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4433.                   transform: `scale(1)`,
  4434.                 });
  4435.                 // Update favorite button for the new image
  4436.                 updateFavoriteButton();
  4437.                 initializeZoomControls();
  4438.                 updateCounter();
  4439.               }
  4440.             }, 100);
  4441.             this.content.on("mfpClose", function () {
  4442.               clearInterval(intervalId);
  4443.             });
  4444.             attachZoomHandler();
  4445.           },
  4446.           close: function () {
  4447.             $(".mfp-zoom-controls").remove();
  4448.             $(".mfp-favorite").remove();
  4449.             $(".mfp-counter").remove();
  4450.             zoomCounter = 0;
  4451.           },
  4452.         },
  4453.       });
  4454.       function attachZoomHandler() {
  4455.         $(".mfp-img")
  4456.           .off("click")
  4457.           .on("click", function (event) {
  4458.             event.stopPropagation(); // Prevent default navigation behavior
  4459.             // Calculate click coordinates relative to the image
  4460.             const imgOffset = $(this).offset();
  4461.             const clickX = event.pageX - imgOffset.left;
  4462.             const clickY = event.pageY - imgOffset.top;
  4463.             const imgWidth = $(this).width();
  4464.             const imgHeight = $(this).height();
  4465.             // Calculate transform-origin based on click position
  4466.             lastClickPosition = {
  4467.               x: (clickX / imgWidth) * 100,
  4468.               y: (clickY / imgHeight) * 100,
  4469.             };
  4470.             // Cycle through zoom levels: 1x, 1.5x, 2x
  4471.             zoomCounter = (zoomCounter + 1) % 3;
  4472.             const zoomLevels = [1, 1.5, 2];
  4473.             const zoomLevel = zoomLevels[zoomCounter];
  4474.             $(this).css({
  4475.               "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4476.               transform: `scale(${zoomLevel})`,
  4477.             });
  4478.             updateZoomButtonState();
  4479.           });
  4480.       }
  4481.       function initializeZoomControls() {
  4482.         $(".mfp-zoom-controls .zoom-in")
  4483.           .off("click")
  4484.           .on("click", function (event) {
  4485.             event.stopPropagation();
  4486.             zoomCounter = (zoomCounter + 1) % 3;
  4487.             const zoomLevels = [1, 1.5, 2];
  4488.             const zoomLevel = zoomLevels[zoomCounter];
  4489.             $(".mfp-img").css({
  4490.               "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4491.               transform: `scale(${zoomLevel})`,
  4492.             });
  4493.             updateZoomButtonState();
  4494.           });
  4495.         $(".mfp-zoom-controls .zoom-out")
  4496.           .off("click")
  4497.           .on("click", function (event) {
  4498.             event.stopPropagation();
  4499.             if (zoomCounter > 0) {
  4500.               zoomCounter -= 1;
  4501.               const zoomLevels = [1, 1.5, 2];
  4502.               const zoomLevel = zoomLevels[zoomCounter];
  4503.               $(".mfp-img").css({
  4504.                 "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4505.                 transform: `scale(${zoomLevel})`,
  4506.               });
  4507.               updateZoomButtonState();
  4508.             } else {
  4509.               $.magnificPopup.close();
  4510.             }
  4511.           });
  4512.       }
  4513.       function initializeFavoriteButton() {
  4514.         $(".mfp-favorite .favorite-btn")
  4515.           .off("click")
  4516.           .on("click", function (event) {
  4517.             event.stopPropagation();
  4518.             const $this = $(this);
  4519.             const imageId = $this.data("id");
  4520.             const sejourId = $this.data("sejour-id");
  4521.             // Toggle favorite status
  4522.             const isFavorite = $this.find("i").hasClass("bi-heart-fill");
  4523.             // Update the button appearance
  4524.             if (isFavorite) {
  4525.               $this
  4526.                 .find("i")
  4527.                 .removeClass("bi-heart-fill")
  4528.                 .addClass("bi-heart")
  4529.                 .css("color", "white");
  4530.               $this.attr("title", "Ajouter aux favoris");
  4531.             } else {
  4532.               $this
  4533.                 .find("i")
  4534.                 .removeClass("bi-heart")
  4535.                 .addClass("bi-heart-fill")
  4536.                 .css("color", "#f56040");
  4537.               $this.attr("title", "Retirer des favoris");
  4538.             }
  4539.             // Update the original heart icon in the gallery
  4540.             const originalHeartIcon = $(
  4541.               `.heart-icon[data-id="${imageId}"]`
  4542.             ).find("i");
  4543.             if (isFavorite) {
  4544.               originalHeartIcon
  4545.                 .removeClass("bi-heart-fill")
  4546.                 .addClass("bi-heart")
  4547.                 .css("color", "");
  4548.             } else {
  4549.               originalHeartIcon
  4550.                 .removeClass("bi-heart")
  4551.                 .addClass("bi-heart-fill")
  4552.                 .css("color", "#f56040");
  4553.             }
  4554.             // Make AJAX call to update favorite status in the backend using Parent routes
  4555.             $.ajax({
  4556.               url: isFavorite ? "/Parent/aSupprimerFav" : "/Parent/ajouterFav",
  4557.               type: "POST",
  4558.               data: {
  4559.                 id: imageId,
  4560.                 idSejour: sejourId,
  4561.               },
  4562.               success: function (response) {
  4563.                 // Optional: Show a success message or handle response
  4564.                 console.log("Favorite status updated", response);
  4565.               },
  4566.               error: function (error) {
  4567.                 console.error("Error updating favorite status", error);
  4568.                 // Revert the icon change on error
  4569.                 if (isFavorite) {
  4570.                   $this
  4571.                     .find("i")
  4572.                     .removeClass("bi-heart")
  4573.                     .addClass("bi-heart-fill")
  4574.                     .css("color", "#f56040");
  4575.                   originalHeartIcon
  4576.                     .removeClass("bi-heart")
  4577.                     .addClass("bi-heart-fill")
  4578.                     .css("color", "#f56040");
  4579.                 } else {
  4580.                   $this
  4581.                     .find("i")
  4582.                     .removeClass("bi-heart-fill")
  4583.                     .addClass("bi-heart")
  4584.                     .css("color", "white");
  4585.                   originalHeartIcon
  4586.                     .removeClass("bi-heart-fill")
  4587.                     .addClass("bi-heart")
  4588.                     .css("color", "");
  4589.                 }
  4590.               },
  4591.             });
  4592.           });
  4593.       }
  4594.       function updateFavoriteButton() {
  4595.         // Get current image data from the current slide
  4596.         const currentSlide = $.magnificPopup.instance.currItem.el;
  4597.         const photoZoom = currentSlide.closest(".photo-zoom");
  4598.         if (photoZoom.length) {
  4599.           const heartIcon = photoZoom.find(".heart-icon");
  4600.           const imageId = heartIcon.data("id") || "";
  4601.           const sejourId = heartIcon.data("sejour-id") || "";
  4602.           const imagePath = heartIcon.data("path") || "";
  4603.           const imageDesc = heartIcon.data("description") || "";
  4604.           const isFavorite = heartIcon.find("i").hasClass("bi-heart-fill");
  4605.           const favoriteIconClass = isFavorite ? "bi-heart-fill" : "bi-heart";
  4606.           const favoriteIconColor = isFavorite ? "#f56040" : "white";
  4607.           const favoriteTooltip = isFavorite
  4608.             ? "Retirer des favoris"
  4609.             : "Ajouter aux favoris";
  4610.           // Update the favorite button
  4611.           const $favoriteBtn = $(".mfp-favorite .favorite-btn");
  4612.           $favoriteBtn.data("id", imageId);
  4613.           $favoriteBtn.data("sejour-id", sejourId);
  4614.           $favoriteBtn.data("path", imagePath);
  4615.           $favoriteBtn.data("description", imageDesc);
  4616.           $favoriteBtn.attr("title", favoriteTooltip);
  4617.           $favoriteBtn
  4618.             .find("i")
  4619.             .removeClass("bi-heart bi-heart-fill")
  4620.             .addClass(favoriteIconClass)
  4621.             .css("color", favoriteIconColor);
  4622.         }
  4623.       }
  4624.       function updateZoomButtonState() {
  4625.         const zoomLevels = [1, 1.5, 2];
  4626.         const currentZoom = zoomLevels[zoomCounter];
  4627.         $(".zoom-in").prop("disabled", currentZoom === 2);
  4628.         $(".zoom-out").prop("disabled", currentZoom === 1);
  4629.       }
  4630.       function updateCounter() {
  4631.         const counterText = $(".mfp-counter")
  4632.           .closest(".mfp-content")
  4633.           .find(".mfp-counter")
  4634.           .text();
  4635.         const matches = counterText.match(/(\d+) of (\d+)/);
  4636.         if (matches) {
  4637.           const currentIndex = matches[1];
  4638.           const totalImages = matches[2];
  4639.           $(".mfp-counter").text(`${currentIndex} of ${totalImages}`);
  4640.         }
  4641.       }
  4642.       // Add CSS for the favorite button and rounded image corners
  4643.       $("<style>")
  4644.         .prop("type", "text/css")
  4645.         .html(
  4646.           `
  4647.       .mfp-favorite {
  4648.         position: absolute;
  4649.         top: 15px;
  4650.         left: 15px;
  4651.         z-index: 1046;
  4652.       }
  4653.       .favorite-btn {
  4654.         background: transparent;
  4655.         border: none;
  4656.         font-size: 24px;
  4657.         padding: 5px;
  4658.         cursor: pointer;
  4659.         outline: none;
  4660.       }
  4661.       .favorite-btn i {
  4662.         transition: all 0.3s ease;
  4663.       }
  4664.       .favorite-btn:hover i {
  4665.         transform: scale(1.2);
  4666.       }
  4667.       /* Rounded corners for zoomed images */
  4668.       .mfp-img {
  4669.         border-radius: 8px;
  4670.       }
  4671.       /* Make sure the container doesn't clip the rounded corners */
  4672.       .mfp-figure:after {
  4673.         border-radius: 8px;
  4674.       }
  4675.     `
  4676.         )
  4677.         .appendTo("head");
  4678.     });
  4679.   </script>
  4680. <script>
  4681. document.addEventListener('DOMContentLoaded', function () {
  4682.   const openBtn = document.getElementById('openFavoritesBtn');
  4683.   const closeBtn = document.getElementById('closeSidebarBtn');
  4684.   const sidebar = document.getElementById('favoritesSidebar');
  4685.   const tbody = document.querySelector('#favoritesTable tbody');
  4686.   openBtn.addEventListener('click', async () => {
  4687.     try {
  4688.       const response = await fetch('/Parent/mes-favoris', {
  4689.         headers: {
  4690.           'Accept': 'application/json'
  4691.         }
  4692.       });
  4693.       const result = await response.json();
  4694.       if (!result.success || !Array.isArray(result.data)) {
  4695.         alert('Erreur lors du chargement des favoris.');
  4696.         return;
  4697.       }
  4698.       tbody.innerHTML = '';
  4699.       result.data.forEach((fav, index) => {
  4700.         const row = document.createElement('tr');
  4701.         row.innerHTML = `
  4702.           <td>${index + 1}</td>
  4703.           <td><img src="${fav.path}" alt="favori"></td>
  4704.           <td>${fav.descreption || '—'}</td>
  4705.           <td>${fav.created_at}</td>
  4706.         `;
  4707.         tbody.appendChild(row);
  4708.       });
  4709.       sidebar.classList.add('active');
  4710.     } catch (e) {
  4711.       console.error('Erreur réseau:', e);
  4712.       alert('Impossible de charger les favoris.');
  4713.     }
  4714.   });
  4715.   closeBtn.addEventListener('click', () => {
  4716.     sidebar.classList.remove('active');
  4717.   });
  4718. });
  4719. </script>
  4720.   <script>
  4721.         // Fonction pour vérifier et afficher l'alerte
  4722.         function checkFavoritesAlert() {
  4723.             const currentCount = window.favoriteCount || 0;
  4724.             if (currentCount >= 10) {
  4725.                 const purchaseAlert = document.getElementById('purchase-alert');
  4726.                 if (purchaseAlert) {
  4727.                     purchaseAlert.style.display = 'block'; // Affiche l'alerte
  4728.                 }
  4729.             } else {
  4730.                 purchaseAlert.style.display = 'none'; // Cache l'alerte si le nombre est réduit
  4731.             }
  4732.         }
  4733.     
  4734.         
  4735.         document.addEventListener('DOMContentLoaded', () => {
  4736.     const favoriteCount = {{ nblikes }};
  4737.     updateCardContent(favoriteCount);
  4738. });
  4739. function updateCardContent(favoriteCount) {
  4740.     const card = document.getElementById('dynamic-card');
  4741.     const cardContent = document.getElementById('dynamic-card-content');
  4742.     let produits = [];
  4743.     if (favoriteCount >= 20) {
  4744.         produits.push({
  4745.             titre: "Album débloqué !",
  4746.             bouton: "Commander",
  4747.             image: "/images/produit/Album5sur5-3.jpg",
  4748.             lien: "{{ path('EditionAlbum') }}"
  4749.         });
  4750.     }
  4751.     if (favoriteCount >= 12) {
  4752.         produits.push({
  4753.             titre: "Pochette débloquée !",
  4754.             bouton: "Commander",
  4755.             image: "/images/produit/PochettePhoto5sur5-2.jpg",
  4756.             lien: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}"
  4757.         });
  4758.     }
  4759.     if (favoriteCount >= 5) {
  4760.         produits.push({
  4761.             titre: "Pack numérique débloqué !",
  4762.             bouton: "Commander",
  4763.             image: "/images/produit/photoNumerique.jpg",
  4764.             lien: "{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}"
  4765.         });
  4766.     }
  4767. if (produits.length === 0) {
  4768.   cardContent.innerHTML = `
  4769.     <div style="position: relative; width: 100%; height: 140px; border-radius: 15px; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,0.1);">
  4770.       <div style="background-image: url('/images/produit/CoffretCadeau5sur5-2.jpg'); background-size: cover; background-position: center; width: 100%; height: 100%;">
  4771.         <div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; padding: 10px;">
  4772.           <div style="
  4773.     margin-top: 35px;
  4774.     color: white;
  4775.     font-size: 15px;
  4776.     font-weight: bold;
  4777.     text-align: center;
  4778.     line-height: 1;
  4779.     text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);">
  4780.             Ajoutez des favoris ❤️<br><span style="font-size: 16px; font-weight: normal;">pour débloquer un souvenir à offrir 🎁</span>
  4781.           </div>
  4782.         </div>
  4783.       </div>
  4784.     </div>
  4785.   `;
  4786.         return;
  4787.     }
  4788.     cardContent.innerHTML = `
  4789.     <div class="splide" id="dynamicSplide">
  4790.       <div class="splide__track">
  4791.         <ul class="splide__list">
  4792.           ${produits.map(produit => `
  4793.             <li class="splide__slide" style="position: relative;">
  4794.               <img src="${produit.image}" alt="${produit.titre}" style="width: 100%; height: 150px; object-fit: cover; border-radius: 8px;">
  4795.               <div style="position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.1); color: white; padding: 10px; text-align: center; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
  4796.                 <div style="font-weight: bold; font-size: 14px;">${produit.titre}</div>
  4797.                 <button style="margin-top: 5px; padding: 5px 8px; background-color: #F56040; color: white; border: none; border-radius: 4px; font-size: 12px; cursor: pointer;" onclick="window.location.href='${produit.lien}'">
  4798.                   ${produit.bouton}
  4799.                 </button>
  4800.               </div>
  4801.             </li>
  4802.           `).join('')}
  4803.         </ul>
  4804.       </div>
  4805.     </div>
  4806.     `;
  4807.     // Monte le carrousel
  4808.     new Splide('#dynamicSplide', {
  4809.         type: 'loop',
  4810.         arrows: true,
  4811.         pagination: false,
  4812.         autoplay: true,
  4813.         interval: 4000,
  4814.         speed: 800,
  4815.     }).mount();
  4816. }
  4817.         function supprimerFavoris($id, $idSejour) {
  4818.             // Vider l'élément coeur pour ce favori
  4819.             const coeurElement = $('#coeur' + $id);
  4820.             coeurElement.empty();
  4821.             // Ajout d'une animation sur le bouton cadeau
  4822.             const giftButton = document.querySelector('.gift-button');
  4823.             if (giftButton) {
  4824.                 giftButton.classList.add('active');
  4825.                 // Retirer l'animation après qu'elle soit jouée
  4826.                 setTimeout(() => {
  4827.                     giftButton.classList.remove('active');
  4828.                 }, 600); // La durée doit correspondre à celle de l'animation
  4829.             }
  4830.             // Mise à jour de l'icône coeur
  4831.             const clas = $('.IconImag6').hasClass('active') ? "IconDelete IconDeletesix" : "IconDelete";
  4832.             coeurElement.html(
  4833.                 `<i class="bi bi-heart ${clas}" ></i>`
  4834.             );
  4835.             // Mettre à jour le compteur des favoris
  4836.             const likeCountLabel = document.getElementById('likeCount');
  4837.             const likeMesFavLabel = document.getElementById('mesFavCount');
  4838.             
  4839.             let newCount = 0;
  4840.             
  4841.             if (likeCountLabel) {
  4842.                 // Utiliser la fonction utilitaire robuste
  4843.                 let currentCount = window.getFavoriteCount();
  4844.                 newCount = Math.max(0, currentCount - 1); // Empêche le compteur d'aller en dessous de 0
  4845.                 window.setFavoriteCount(newCount);
  4846.             
  4847.                 // Mettre à jour la valeur dans l'input hidden
  4848.                 const nbFavCurrentInput = $('#nbFavCurrent');
  4849.                 if (nbFavCurrentInput.length) {
  4850.                     nbFavCurrentInput.val(newCount);
  4851.                 }
  4852.             }
  4853.             
  4854.             // Synchroniser mesFavCount avec likeCount
  4855.             if (likeMesFavLabel) {
  4856.                 if (likeCountLabel) {
  4857.                     // Utiliser la valeur de likeCount pour synchroniser
  4858.                     likeMesFavLabel.textContent = newCount;
  4859.                 } else {
  4860.                     // Si likeCount n'existe pas, décrémenter directement mesFavCount
  4861.                     let currentFavCount = parseInt(likeMesFavLabel.textContent.trim(), 10) || 0;
  4862.                     newCount = Math.max(0, currentFavCount - 1);
  4863.                                          likeMesFavLabel.textContent = newCount;
  4864.                  }
  4865.              }
  4866.              
  4867.              // Mettre à jour l'alerte immédiatement
  4868.              setTimeout(() => {
  4869.                  const finalCount = getCurrentFavoriteCount();
  4870.                  if (typeof updatePurchaseAlert === 'function') {
  4871.                      updatePurchaseAlert(finalCount);
  4872.                  }
  4873.              }, 50);
  4874.             // Préparation des données pour l'Ajax
  4875.             const $_data = { 'id': $id, 'idSejour': $idSejour };
  4876.             // Appel Ajax pour supprimer le favori
  4877.             $.ajax({
  4878.                 type: "POST",
  4879.                 url: "{{ path('Supprimer_fav') }}",
  4880.                 data: $_data,
  4881.                 success: function () {
  4882.                     // Réactiver les icônes après succès
  4883.                     $('.IconDelete').each(function () {
  4884.                         $(this).css('pointer-events', '');
  4885.                     });
  4886.                     
  4887.                     // Mettre à jour l'alerte avec le nouveau nombre
  4888.                     const finalCount = getCurrentFavoriteCount();
  4889.                     if (typeof updatePurchaseAlert === 'function') {
  4890.                         updatePurchaseAlert(finalCount);
  4891.                     }
  4892.                 },
  4893.                 error: function (xhr, status, error) {
  4894.                     console.error('Erreur lors de la suppression du favori :', error);
  4895.                 }
  4896.             });
  4897.         }
  4898.         function AddFavoris($id, $idSejour, $urlimg, $description) {
  4899.             // Update heart icon
  4900.             $('#coeur' + $id).empty();
  4901.             var clas = $('.IconImag6').hasClass('active') ? "IconDelete IconDeletesix" : "IconDelete";
  4902.             $('#coeur' + $id).html("<i class=\"bi bi-heart-fill favSelect " + clas + "\" )\"></i>");
  4903.             
  4904.             // Update counters UNIQUEMENT
  4905.             const likeCountLabel = document.getElementById('likeCount');
  4906.             const likeMesFavLabel = document.getElementById('mesFavCount');
  4907.             const  giftCountLabel = document.getElementById('giftCount');
  4908.             
  4909.             if (likeCountLabel) {
  4910.                 // Utiliser la fonction utilitaire robuste
  4911.                 let currentCount = window.getFavoriteCount();
  4912.                 currentCount++;
  4913.                 window.setFavoriteCount(currentCount);
  4914.                
  4915.                 
  4916.                 // Mettre à jour le compteur des favoris dans le titre
  4917.                 if (likeMesFavLabel) {
  4918.                     likeMesFavLabel.textContent = currentCount;
  4919.              
  4920.                 }
  4921.                   if (giftCountLabel) {
  4922.                     giftCountLabel.textContent = currentCount;
  4923.              
  4924.                 }
  4925.                 
  4926.                 // Mettre à jour l'input hidden
  4927.                 const nbFavCurrentInput = document.getElementById('nbFavCurrent');
  4928.                 if (nbFavCurrentInput) {
  4929.                     nbFavCurrentInput.value = currentCount;
  4930.                 }
  4931.             }
  4932.             
  4933.             // Update other counters
  4934.             var $total = parseInt($("#totalLike").html()) + 1;
  4935.             $("#totalLike").html($total);
  4936.             $("#totalLikeTitle").html($total);
  4937.             $("#totalLikeMobile").html($total);
  4938.             
  4939.             // Add gift button animation
  4940.             const giftButton = document.querySelector('.gift-button');
  4941.             if (giftButton) {
  4942.                 giftButton.classList.add('active');
  4943.                 setTimeout(() => {
  4944.                     giftButton.classList.remove('active');
  4945.                 }, 600);
  4946.             }
  4947.             var $data = { 'id': $id, 'idSejour': $idSejour };
  4948.             $.ajax({
  4949.                 type: "POST",
  4950.                 url: "{{ path('Ajouter_fav') }}",
  4951.                 data: $data,
  4952.                 success: function () {
  4953.                     $('.IconDelete').each(function () {
  4954.                         $(this).css('pointer-events', '');
  4955.                     });
  4956.                     if ($description === undefined) {
  4957.                         $description = ''; // Set it to an empty string
  4958.                     }
  4959.                     $('.rowMaselection').append(
  4960.                         '<div class="column" id="column-' + $id + '">'+
  4961.                         '<a style="position: relative;" title="Enlever de ma sélection" onclick="supprimerFavoris(' + $id + ',' + $idSejour + ')" class="iconeSuppImg"><i class="bi bi-x" style="font-size:17px;cursor:pointer;color:#d30909;float:right;margin-top:-3%;margin-right:2%"></i></a>'+
  4962.                         '<a class="photo-zoom">'+
  4963.                         '<img data-idAtach="'+$id+'" id="'+$idSejour+'" src="'+$urlimg+'"></a>'+
  4964.                         ($description ? '<h4 id="commint" class="titleHeadPhoto">'+$description+'</h4>' : '')+ // Only add the <h4> if $description is not empty
  4965.                         '</div>'
  4966.                     );
  4967.                     // Directly update nbLikes count in the header
  4968.                     var currentNbLikes = parseInt($('#favoris-link-Accueilpayment .nbrpanier').text());
  4969.                     var newNbLikes = currentNbLikes + 1;
  4970.                     $('#favoris-link-Accueilpayment .nbrpanier').text(newNbLikes);
  4971.                     
  4972.                     // Mettre à jour l'alerte avec le nouveau nombre
  4973.                     const finalCount = getCurrentFavoriteCount();
  4974.                     if (typeof updatePurchaseAlert === 'function') {
  4975.                         updatePurchaseAlert(finalCount);
  4976.                     }
  4977.                 },
  4978.                 error: function (xhr, status, error) {
  4979.                     console.error('Error:', error);
  4980.                 }
  4981.             });
  4982.         }
  4983.         $(document).on('click', '.bi-heart, .bi-heart-fill', function () {
  4984.         const heartIcon       = $(this);
  4985.         const heartContainer  = heartIcon.closest('.heart-icon');
  4986.         // Extract data attributes
  4987.         const attachmentId    = heartContainer.data('id');
  4988.         const sejourId        = heartContainer.data('sejour-id');
  4989.         const path            = heartContainer.data('path');
  4990.         const description     = heartContainer.data('description');
  4991.         const isFavorite      = heartIcon.hasClass('bi-heart-fill');
  4992.         if (isFavorite) {
  4993.             // Remove from favorites
  4994.             supprimerFavoris(attachmentId, sejourId);
  4995.         } else {
  4996.             // Add to favorites
  4997.             AddFavoris(attachmentId, sejourId, path, description);
  4998.         }
  4999.         // Update UI components after the action (sans double comptage)
  5000.         setTimeout(function() {
  5001.             const likeCountLabel = document.getElementById('likeCount');
  5002.             
  5003.             if (likeCountLabel) {
  5004.                 const currentCount = parseInt(likeCountLabel.textContent, 10) || 0;
  5005.                 
  5006.                 // Update UI components seulement
  5007.                 updateCardContent(currentCount);
  5008.                 updateFavoritesSidebar();
  5009.                 $("#close-favorites-btn").click();
  5010.             }
  5011.         }, 50);
  5012.     });
  5013.         // Ajoutez les événements sur les icônes de cœur
  5014.         document.querySelectorAll('.IconDelete').forEach((icon) => {
  5015.             icon.addEventListener('click', (event) => {
  5016.                 const isFavorite = icon && icon.classList && icon.classList.contains('bi-heart-fill');
  5017.                 if (isFavorite) {
  5018.                     removeFavorite();
  5019.                     if (icon.classList) {
  5020.                       icon.classList.remove('bi-heart-fill');
  5021.                       icon.classList.add('bi-heart');
  5022.                     }
  5023.                 } else {
  5024.                     addFavorite();
  5025.                     if (icon.classList) {
  5026.                       icon.classList.remove('bi-heart');
  5027.                       icon.classList.add('bi-heart-fill');
  5028.                     }
  5029.                 }
  5030.             });
  5031.         });
  5032.         // Vérifie l'état initial
  5033.         checkFavoritesAlert();
  5034.         
  5035.         
  5036.         // ⚡ OPTIMISÉ: Réduction du délai d'initialisation
  5037.       
  5038.   </script>
  5039.   <!-- Initialisation -->
  5040.   <script>
  5041.     // ⚡ OPTIMISATION: Différer l'initialisation d'AOS pour ne pas bloquer le chargement
  5042.     setTimeout(function() {
  5043.       AOS.init({
  5044.         duration: 800,
  5045.         easing: "ease-in-out"
  5046.       });
  5047.     }, 100);
  5048.     // 🎯 DAY FILTER DROPDOWN LOGIC (Senior UX)
  5049.     document.addEventListener('DOMContentLoaded', function() {
  5050.       initializeDayFilters();
  5051.       
  5052.       // Initialiser le compteur audio avec la valeur correcte
  5053.       setTimeout(function() {
  5054.         updateAudioButtonState('days');
  5055.       }, 500);
  5056.     });
  5057.     function initializeDayFilters() {
  5058.       const dropdowns = document.querySelectorAll('.day-filter-dropdown');
  5059.       
  5060.       dropdowns.forEach(dropdown => {
  5061.         const toggle = dropdown.querySelector('.filter-toggle');
  5062.         const menu = dropdown.querySelector('.filter-dropdown-menu');
  5063.         const options = dropdown.querySelectorAll('.filter-option');
  5064.         const dayIndex = dropdown.dataset.dayIndex;
  5065.         const dayContainer = document.getElementById(`demP${dayIndex}`);
  5066.         
  5067.         if (!toggle || !menu || !dayContainer) return;
  5068.         
  5069.         // Calculer et mettre à jour les compteurs initiaux
  5070.         updateDayFilterCounts(dropdown, dayContainer);
  5071.         
  5072.         // Toggle dropdown
  5073.         toggle.addEventListener('click', (e) => {
  5074.           e.stopPropagation();
  5075.           const isOpen = dropdown.classList.contains('open');
  5076.           
  5077.           // Fermer tous les autres dropdowns
  5078.           document.querySelectorAll('.day-filter-dropdown.open').forEach(d => {
  5079.             if (d !== dropdown) d.classList.remove('open');
  5080.           });
  5081.           
  5082.           // Toggle le dropdown actuel
  5083.           dropdown.classList.toggle('open', !isOpen);
  5084.         });
  5085.         
  5086.         // Gérer les clics sur les options
  5087.         options.forEach(option => {
  5088.           option.addEventListener('click', (e) => {
  5089.             e.stopPropagation();
  5090.             const filter = option.dataset.filter;
  5091.             
  5092.             // Mettre à jour l'état actif
  5093.             options.forEach(opt => opt.classList.remove('active'));
  5094.             option.classList.add('active');
  5095.             
  5096.             // Appliquer le filtre
  5097.             applyDayFilter(dayContainer, filter);
  5098.             
  5099.             // Fermer le dropdown
  5100.             dropdown.classList.remove('open');
  5101.           });
  5102.         });
  5103.       });
  5104.       
  5105.       // Fermer les dropdowns en cliquant ailleurs
  5106.       document.addEventListener('click', () => {
  5107.         document.querySelectorAll('.day-filter-dropdown.open').forEach(dropdown => {
  5108.           dropdown.classList.remove('open');
  5109.         });
  5110.       });
  5111.     }
  5112.     function updateDayFilterCounts(dropdown, dayContainer) {
  5113.       const photoItems = dayContainer.querySelectorAll('[data-type="photo"]');
  5114.       const videoItems = dayContainer.querySelectorAll('[data-type="video"]');
  5115.       // Compter les messages audio individuels plutôt que les conteneurs
  5116.       const audioMessageItems = dayContainer.querySelectorAll('.audio-message-item[data-type="audio"]');
  5117.       const audioContainers = dayContainer.querySelectorAll('.audio-messages-container[data-type="audio"]');
  5118.       const audioRestricted = dayContainer.querySelectorAll('.audio-messages-restricted[data-type="audio"]');
  5119.       
  5120.       const allItems = dayContainer.querySelectorAll('[data-type]');
  5121.       
  5122.       const countAll = dropdown.querySelector('[data-count-all]');
  5123.       const countPhoto = dropdown.querySelector('[data-count-photo]');
  5124.       const countVideo = dropdown.querySelector('[data-count-video]');
  5125.       const countAudio = dropdown.querySelector('[data-count-audio]');
  5126.       
  5127.       // Compter les messages audio individuels + conteneurs + sections restreintes
  5128.       const totalAudio = audioMessageItems.length + audioContainers.length + audioRestricted.length;
  5129.       
  5130.       if (countAll) countAll.textContent = allItems.length;
  5131.       if (countPhoto) countPhoto.textContent = photoItems.length;
  5132.       if (countVideo) countVideo.textContent = videoItems.length;
  5133.       if (countAudio) countAudio.textContent = totalAudio;
  5134.       
  5135.       // Masquer les options sans contenu
  5136.       const photoOption = dropdown.querySelector('[data-filter="photo"]');
  5137.       const videoOption = dropdown.querySelector('[data-filter="video"]');
  5138.       const audioOption = dropdown.querySelector('[data-filter="audio"]');
  5139.       
  5140.       if (photoOption) photoOption.style.display = photoItems.length > 0 ? 'flex' : 'none';
  5141.       if (videoOption) videoOption.style.display = videoItems.length > 0 ? 'flex' : 'none';
  5142.       if (audioOption) audioOption.style.display = totalAudio > 0 ? 'flex' : 'none';
  5143.     }
  5144.     function applyDayFilter(dayContainer, filter) {
  5145.       const items = dayContainer.querySelectorAll('[data-type]');
  5146.       
  5147.       items.forEach(item => {
  5148.         if (filter === 'all' || item.dataset.type === filter) {
  5149.           item.style.display = '';
  5150.           item.classList.remove('filtered-out');
  5151.         } else {
  5152.           item.style.display = 'none';
  5153.           item.classList.add('filtered-out');
  5154.         }
  5155.       });
  5156.       
  5157.       // Animation fluide pour les éléments visibles
  5158.       requestAnimationFrame(() => {
  5159.         const visibleItems = dayContainer.querySelectorAll('[data-type]:not(.filtered-out)');
  5160.         visibleItems.forEach((item, index) => {
  5161.           item.style.animation = `fadeInUp 0.3s ease forwards ${index * 0.05}s`;
  5162.         });
  5163.       });
  5164.     }
  5165.     // Animation CSS pour fadeInUp
  5166.     if (!document.querySelector('#dayFilterAnimations')) {
  5167.       const style = document.createElement('style');
  5168.       style.id = 'dayFilterAnimations';
  5169.       style.textContent = `
  5170.         @keyframes fadeInUp {
  5171.           from {
  5172.             opacity: 0;
  5173.             transform: translateY(20px);
  5174.           }
  5175.           to {
  5176.             opacity: 1;
  5177.             transform: translateY(0);
  5178.           }
  5179.         }
  5180.       `;
  5181.       document.head.appendChild(style);
  5182.     }
  5183.     document.addEventListener("DOMContentLoaded", function () {
  5184.       const dateCards = document.querySelectorAll(".date-card");
  5185.       const sections = document.querySelectorAll(".collapse");
  5186.       dateCards.forEach((card) => {
  5187.         card.addEventListener("click", function () {
  5188.           // Supprimer les classes actives des autres cartes et sections
  5189.           dateCards.forEach((c) => c.classList.remove("active"));
  5190.           sections.forEach((s) => s.classList.remove("show"));
  5191.           // Ajouter la classe active à la carte cliquée
  5192.           this.classList.add("active");
  5193.           // Récupérer la cible et afficher la bonne section
  5194.           const targetId = this.getAttribute("data-bs-target");
  5195.           const targetSection = document.querySelector(targetId);
  5196.           if (targetSection) {
  5197.             targetSection.classList.add("show");
  5198.           }
  5199.         });
  5200.       });
  5201.     });
  5202.     document.addEventListener("DOMContentLoaded", function () {
  5203.       // Initialisation du carrousel Splide
  5204.       var splide = new Splide("#imageSlider", {
  5205.         type: "loop",
  5206.         perPage: 1,
  5207.         autoplay: true,
  5208.         interval: 6000,
  5209.         pauseOnHover: false,
  5210.         pauseOnFocus: false,
  5211.         pagination: false, // Désactive la pagination
  5212.         arrows: false,
  5213.       });
  5214.       splide.mount();
  5215.       // Fonction pour faire défiler automatiquement vers la section suivante avec un ajustement personnalisé de la hauteur
  5216.       function scrollToNextSection() {
  5217.         const targetSection = document.getElementById("scrollTarget");
  5218.         if (targetSection) {
  5219.           const targetPosition =
  5220.             targetSection.getBoundingClientRect().top + window.scrollY; // Position de la section
  5221.           const adjustedPosition = targetPosition - 50; // Réduit la hauteur du scroll de 150px (ajustez selon vos besoins)
  5222.           // Scroll vers la position ajustée
  5223.           window.scrollTo({
  5224.             top: adjustedPosition,
  5225.             behavior: "smooth",
  5226.           });
  5227.         }
  5228.       }
  5229.       // Démarrer le timer pour le scroll automatique après 10 secondes
  5230.       setTimeout(scrollToNextSection, 5000); // 10 secondes
  5231.     });
  5232.   </script>
  5233.   <script>
  5234.    
  5235.     const giftButton = document.querySelector('.gift-button');
  5236.     if (giftButton) {
  5237.         giftButton.addEventListener('click', () => {
  5238.         // Ajouter la classe 'active' pour déclencher l'éclat
  5239.             giftButton.classList.add('active');
  5240.         // Retirer l'animation après qu'elle soit jouée
  5241.         setTimeout(() => {
  5242.                 giftButton.classList.remove('active');
  5243.         }, 600); // La durée doit correspondre à celle de l'animation
  5244.             
  5245.             // Optionnel : rediriger vers la page de commande
  5246.             // window.location.href = '/commande';
  5247.     });
  5248.     }
  5249.     
  5250.     //const HeartAddButton = document.querySelector('.IconDelete');
  5251.     $(".IconDelete").on('click', () => {
  5252.         // Ajouter la classe 'active' pour déclencher l'éclat
  5253.         favoriteButton.classList.add('active');
  5254.         // Retirer l'animation après qu'elle soit jouée
  5255.         setTimeout(() => {
  5256.             favoriteButton.classList.remove('active');
  5257.         }, 600); // La durée doit correspondre à celle de l'animation
  5258.     });
  5259.     $(document).ready(function() {
  5260.         // Attach click event to collapse triggers
  5261.         const lastCard = $('.date-card.modern-card.active');
  5262.         const lastTargetId = lastCard.attr('data-bs-target');
  5263.         if (lastTargetId) {
  5264.             $(lastTargetId).collapse('show'); // Expand the last collapse section
  5265.             LoadImagesCloud($(lastTargetId)); // Load images for the last day
  5266.         }
  5267.         $('[data-bs-toggle="collapse"]').on('click', function() {
  5268.             var targetId = $(this).attr('data-bs-target'); // Get the target ID
  5269.             $('.date-card.modern-card').removeClass('active'); // Remove 'active' class from all cards
  5270.             $(this).addClass('active'); // Add 'active' class to the clicked card
  5271.             LoadImagesCloud($(targetId)); // Ensure this function works as expected
  5272.                // Hide all other collapses except the one clicked
  5273.                $('[data-bs-target]').each(function() {
  5274.                 var currentTargetId = $(this).attr('data-bs-target');
  5275.                 // If the current collapse is not the one clicked, hide it
  5276.                 if (currentTargetId !== targetId) {
  5277.                     $(currentTargetId).collapse('hide');
  5278.                     //$('[data-bs-toggle="collapse"]').removeClass('active'); // Remove active class from all cards
  5279.                     //Modifier leurs style en non active aussi
  5280.                 }
  5281.             });
  5282.         });
  5283.     });
  5284.   
  5285.             $(document).ready(function () {
  5286.               
  5287.                 {% if app.session.get("paymentmoniteco") %}
  5288.                 {% if app.session.get("paymentmoniteco") == "succses" %}
  5289.                 Swal.fire({
  5290.                     icon: 'success',
  5291.                     title: ' succès ',
  5292.                     text: 'votre commande est validée'
  5293.                 });
  5294.                 {% endif %}
  5295.                 {% endif %}
  5296.                 if ($total1 > 0) {
  5297.                     $('.iconeFleche').first().click();
  5298.                     //  $([document.documentElement, document.body]).animate({
  5299.                     //  scrollTop: $('.iconeFleche').last().offset().top
  5300.                     //  }, );
  5301.                 }
  5302.                 else {
  5303.                     $(window).scrollTop(0);
  5304.                 }
  5305.                 var slider = $('.responsive').slick({
  5306.                     infinite: true,
  5307.                     slidesToShow: 1,
  5308.                     slidesToScroll: 1,
  5309.                     autoplay: true,
  5310.                     autoplaySpeed: 4000,
  5311.                     pauseOnFocus: false,
  5312.                     pauseOnHover: false,
  5313.                     draggable: false,
  5314.                     fade: true
  5315.                 });
  5316.                 $('.responsive').css('display', 'block');
  5317.                 $('.namePRD').css('display', 'block');
  5318.                 var currSlide = 0;
  5319.                 var nextSlide = 0;
  5320.                 slider.on('afterChange', function (event, slick, currentSlide) {
  5321.                     console.log(typeof ($('.slick-active .slick-current').find('.imgproduit2')) != "undefined");
  5322.                     if (typeof ($('.slick-active .slick-current').find('.imgproduit2')) != "undefined") {
  5323.                         setTimeout(function () {
  5324.                             $('.slick-active .imgproduit1').removeClass('animated fadeIn');
  5325.                             $('.slick-active .imgproduit1').addClass('animated fadeOut');
  5326.                             $('.slick-active .imgproduit1').css('display', 'none');
  5327.                             $('.slick-active .imgproduit2').css('display', 'block');
  5328.                             $('.slick-active .imgproduit2').removeClass('animated fadeOut');
  5329.                             $('.slick-active .imgproduit2').addClass('animated fadeIn');
  5330.                         }, 2000);
  5331.                     }
  5332.                 });
  5333.                 slider.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
  5334.                     currSlide = currentSlide;
  5335.                     $('.imgproduit2').each(function () {
  5336.                         $(this).removeClass('animated fadeIn');
  5337.                         $(this).addClass('animated fadeOut');
  5338.                         $(this).css('display', 'none');
  5339.                     });
  5340.                     $('.imgproduit1').each(function () {
  5341.                         $(this).css('display', 'block');
  5342.                         $(this).removeClass('animated fadeOut');
  5343.                         $(this).addClass('animated fadeIn');
  5344.                     });
  5345.                 });
  5346.                 $('.columnPub').each(function () {
  5347.                     $(this).slick({
  5348.                         infinite: true,
  5349.                         speed: 50,
  5350.                         fade: true,
  5351.                         slidesToShow: 1,
  5352.                         slidesToScroll: 1,
  5353.                         autoplay: true,
  5354.                         pauseOnFocus: false,
  5355.                         pauseOnHover: false,
  5356.                         draggable: false
  5357.                     });
  5358.                     $(this).css('display', 'block');
  5359.                 });
  5360.                 $("#offrePack").click();
  5361.                 {%if app.user.showpubprod != 'false' %}
  5362.                 $('#btnPubProd').click();
  5363.                 $('.modal-backdrop').css('background-color', 'rgba(0, 0, 0, 0.2)');
  5364.                 {% endif %}
  5365.             });
  5366.             $("#closeImage").click(function () {
  5367.                 $('#myModalImage').css('display', "none");
  5368.             });
  5369.             $.ajax({
  5370.                 type: "POST",
  5371.                 url: "{{ path("delateSession_parent") }}",
  5372.                 success: function () { }
  5373.             });
  5374.             function afficheDiv(elem) {
  5375.                 $('.nav-link').each(function () {
  5376.                     $(this).removeClass('active');
  5377.                 });
  5378.                 elem.addClass('active');
  5379.                 if (elem.attr('id') === "esphoto" || elem.attr('id') === "esphotoMobile") {
  5380.                     $("#espacphoto").show();
  5381.                     $("#espacemessage").hide();
  5382.                     $("#espaceMa_selection").hide();
  5383.                     pageMenu = 'MonSejour'
  5384.                     $(this).addClass('active');
  5385.                    $('#imageActifphoto').css('display', 'block');
  5386.                      $('#imagenoActifphoto').css('display', 'none');
  5387.                    $('#VocalActivee').css('display', 'none');
  5388.                      $('#noActifVocal').css('display', 'block');
  5389.                 }
  5390.                 if (elem.attr('id') === "esmessage" || elem.attr('id') === "esmessageMobile") {
  5391.                     $("#espacphoto").hide();
  5392.                     $("#espaceMa_selection").hide();
  5393.                     $("#espacemessage").show();
  5394.                     pageMenu = 'BoiteVocale'
  5395.                     $("#espaceMa_selection").hide();
  5396.                     $(this).addClass('active');
  5397.                   $('#imageActifphoto').css('display', 'none');
  5398.                      $('#imagenoActifphoto').css('display', 'block');
  5399.                    $('#VocalActivee').css('display', 'block');
  5400.                      $('#noActifVocal').css('display', 'none');
  5401.                 }
  5402.                 if (elem.attr('id') === "esselection" || elem.attr('id') === "esselectionMobile") {
  5403.                     $("#espacphoto").hide();
  5404.                     $("#espacemessage").hide();
  5405.                     $("#espaceMa_selection").show();
  5406.                     $(homeNavmob).removeClass('bi bi-house-door-fill');
  5407.                     $(homeNavmob).addClass('bi bi-house-door');
  5408.                     $(micromob).removeClass('bi bi-mic-fill');
  5409.                     $(micromob).addClass('bi bi-mic');
  5410.                     $(selecNavmob).removeClass('bi bi-heart');
  5411.                     $(selecNavmob).addClass('bi bi-heart-fill');
  5412.                 }
  5413.             }
  5414.             function LoadImagesCloud($element) {
  5415.                 $element.find('.photo-zoom img').each(function ($this) {
  5416.                     if ($(this).attr('data-src') != $(this).attr('src')) {
  5417.                         $(this).attr('src', $(this).attr('data-src'));
  5418.                     }
  5419.                 });
  5420.             }
  5421.   </script>
  5422.   <script>
  5423.     // ⚡ OPTIMISATION: Regroupement des initialisations jQuery
  5424.     $(document).ready(function () {
  5425.       // Modal PubProd
  5426.       $("#PubProd").on("hidden.bs.modal", function () {
  5427.         $(document).trigger("modalClosed");
  5428.       });
  5429.       
  5430.       // NoShow checkbox
  5431.       $("#noShow").on("change", function () {
  5432.         if ($(this).is(":checked")) {
  5433.           $.ajax({
  5434.             url: "/Parent/showpub",
  5435.             type: "POST",
  5436.             dataType: "json",
  5437.             success: function (response) {
  5438.               if (response.status === "success") {
  5439.                 console.log("User showpubprod updated successfully.");
  5440.               } else {
  5441.                 console.log("Error:", response.message);
  5442.               }
  5443.             },
  5444.             error: function (xhr, status, error) {
  5445.               console.log("AJAX Error:", error);
  5446.             },
  5447.           });
  5448.         }
  5449.       });
  5450.     });
  5451.   </script>
  5452. </div>
  5453. <!-- Script pour la sidebar des favoris -->
  5454. <script>
  5455.   // ⚡ OPTIMISATION: Utiliser requestIdleCallback pour ne pas bloquer le thread principal
  5456.  
  5457.   
  5458.     
  5459.     // Charger les favoris
  5460.     function loadFavorites() {
  5461.       $.ajax({
  5462.         url: "/Parent/mes-favoris",
  5463.         type: "GET",
  5464.         dataType: "json",
  5465.         beforeSend: function() {
  5466.           $("#favorites-grid").html("<div style='text-align:center'>Chargement...</div>");
  5467.         },
  5468.         success: function(data) {
  5469.           $("#favorites-grid").empty();
  5470.           
  5471.           if (data.data && data.data.length > 0) {
  5472.             $("#favorites-empty-state").hide();
  5473.             
  5474.             $.each(data.data, function(i, fav) {
  5475.               var item = $("<div class='favorite-item'></div>");
  5476.               var img = $("<img>").attr("src", fav.path).attr("alt", fav.descreption || "Photo favorite");
  5477.               var overlay = $("<div class='favorite-overlay'></div>");
  5478.         
  5479.               
  5480.               btn.click(function(e) {
  5481.                 e.preventDefault();
  5482.                 e.stopPropagation();
  5483.                 removeFavorite(fav.id);
  5484.               });
  5485.               
  5486.               overlay.append(btn);
  5487.               item.append(img).append(overlay);
  5488.               $("#favorites-grid").append(item);
  5489.             });
  5490.             
  5491.             $("#favorites-counter").text(data.data.length);
  5492.             var percentage = (data.data.length / 10) * 100;
  5493.             $("#favorites-progress").css("width", percentage + "%");
  5494.             
  5495.           } else {
  5496.             $("#favorites-empty-state").show();
  5497.             $("#favorites-counter").text("0");
  5498.             $("#favorites-progress").css("width", "0%");
  5499.           }
  5500.         },
  5501.         error: function() {
  5502.           $("#favorites-grid").html("<div style='color:red;text-align:center'>Erreur de chargement</div>");
  5503.         }
  5504.       });
  5505.     }
  5506.     
  5507.     // Supprimer un favori
  5508.     function removeFavorite(id) {
  5509.       $.ajax({
  5510.         url: "/Parent/remove-favorite/" + id,
  5511.         type: "POST",
  5512.         success: function() {
  5513.           loadFavorites();
  5514.           
  5515.           // Mettre à jour tous les compteurs de favoris
  5516.           updateAllFavoriteCounters();
  5517.         },
  5518.         error: function() {
  5519.           alert("Erreur lors de la suppression du favori");
  5520.         }
  5521.       });
  5522.     }
  5523.   });
  5524. </script>
  5525. <!-- === E-COMMERCE SIDEBAR JAVASCRIPT === -->
  5526. <script>
  5527. // Configuration UX_VARIANT pour A/B testing
  5528. let UX_VARIANT = 'EMOTION'; // ou 'URGENCY'
  5529. // Variables globales e-commerce
  5530. let sejourEndDate = null; // À définir avec la vraie date de fin du séjour
  5531. // Fonction utilitaire robuste pour gérer les compteurs de favoris
  5532. window.getFavoriteCount = function getFavoriteCount() {
  5533.   const likeCountInput = document.getElementById('likeCount');
  5534.   if (likeCountInput) {
  5535.     // Priorité à la valeur de l'input
  5536.     const value = likeCountInput.value || likeCountInput.textContent || 0;
  5537.     return parseInt(value, 10) || 0;
  5538.   }
  5539.   
  5540.   // Fallback sur giftCount
  5541.   const giftCount = document.getElementById('giftCount');
  5542.   if (giftCount && giftCount.textContent) {
  5543.     return parseInt(giftCount.textContent.trim(), 10) || 0;
  5544.   }
  5545.   
  5546.   return 0;
  5547. };
  5548. window.setFavoriteCount = function setFavoriteCount(count) {
  5549.   const likeCountInput = document.getElementById('likeCount');
  5550.   if (likeCountInput) {
  5551.     // Mettre à jour les deux propriétés pour être sûr
  5552.     likeCountInput.value = count;
  5553.     likeCountInput.textContent = count;
  5554.   }
  5555.   
  5556.   // Mettre à jour les autres compteurs
  5557.   const giftCount = document.getElementById('giftCount');
  5558.   if (giftCount) {
  5559.     giftCount.textContent = count;
  5560.   }
  5561.   
  5562.   const mesFavCount = document.getElementById('mesFavCount');
  5563.   if (mesFavCount) {
  5564.     mesFavCount.textContent = count;
  5565.   }
  5566.   
  5567.   // Mettre à jour l'input hidden
  5568.   const nbFavCurrentInput = document.getElementById('nbFavCurrent');
  5569.   if (nbFavCurrentInput) {
  5570.     nbFavCurrentInput.value = count;
  5571.   }
  5572. };
  5573. // Fonction pour définir la date de fin du séjour
  5574. window.setSejourEndDate = function setSejourEndDate(dateString) {
  5575.   sejourEndDate = dateString;
  5576.   console.log('🎯 Sejour end date set to:', sejourEndDate);
  5577.   
  5578.   // Mettre à jour le countdown si le sidebar est ouvert
  5579.   updateCountdownTimer();
  5580. };
  5581. // Fonctions d'ouverture/fermeture du sidebar
  5582. window.openEcommerceSidebar = function openEcommerceSidebar() {
  5583.   const sidebar = document.getElementById('ecommerce-sidebar');
  5584.   if (!sidebar) {
  5585.     console.error('❌ E-commerce sidebar element not found!');
  5586.     return false;
  5587.   }
  5588.   
  5589.   // Ajouter la classe active
  5590.   sidebar.classList.add('active');
  5591.   
  5592.     // Mettre à jour le contenu avec le nombre actuel de favoris
  5593.     let favoriteCount = window.getFavoriteCount();
  5594.     console.log('🎯 Opening sidebar with favoriteCount:', favoriteCount);
  5595.   
  5596.   // Mettre à jour le contenu du sidebar
  5597.   if (typeof window.updateEcommerceSidebarContent === 'function') {
  5598.     window.updateEcommerceSidebarContent(favoriteCount);
  5599.   }
  5600.   
  5601.   return true;
  5602. };
  5603. window.closeEcommerceSidebar = function closeEcommerceSidebar() {
  5604.   const sidebar = document.getElementById('ecommerce-sidebar');
  5605.   if (sidebar) {
  5606.     sidebar.classList.remove('active');
  5607.     console.log('🎯 E-commerce sidebar closed');
  5608.   } else {
  5609.     console.error('❌ E-commerce sidebar element not found!');
  5610.   }
  5611. };
  5612. // Fonction pour mettre à jour le contenu du sidebar
  5613. window.updateEcommerceSidebarContent = function updateEcommerceSidebarContent(favoriteCount) {
  5614.   const title = document.getElementById('ecommerce-title');
  5615.   const subtitle = document.getElementById('ecommerce-subtitle');
  5616.   const albumCount = document.getElementById('album-count');
  5617.   const digitalCount = document.getElementById('digital-count');
  5618.   const printsCount = document.getElementById('prints-count');
  5619.   const printsCard = document.getElementById('prints-card');
  5620.   const productsContainer = document.getElementById('products-container');
  5621.   
  5622.   // Gérer l'état vide vs rempli
  5623.   const isEmpty = favoriteCount === 0;
  5624.   
  5625.   // Titre émotionnel basé sur le nombre de favoris et la variante UX
  5626.   if (title) {
  5627.     if (UX_VARIANT === 'URGENCY') {
  5628.       // Variante urgence
  5629.       if (isEmpty) {
  5630.         title.innerHTML = `⏳ Commencez par sélectionner vos favoris ❤️`;
  5631.       } else if (favoriteCount < 5) {
  5632.         title.innerHTML = `⏳ ${favoriteCount} souvenirs - Commandez vite !`;
  5633.       } else if (favoriteCount < 12) {
  5634.         title.innerHTML = `⏳ ${favoriteCount} magnifiques souvenirs - Plus que X jours !`;
  5635.       } else {
  5636.         title.innerHTML = `⏳ Superbe collection de ${favoriteCount} photos - Dernière chance !`;
  5637.       }
  5638.     } else {
  5639.       // Variante émotion (par défaut)
  5640.       if (isEmpty) {
  5641.         title.innerHTML = `Commencez par sélectionner vos favoris ❤️`;
  5642.       } else if (favoriteCount < 5) {
  5643.         title.innerHTML = `✨ ${favoriteCount} souvenirs sélectionnés !`;
  5644.       } else if (favoriteCount < 12) {
  5645.         title.innerHTML = `🎉 Vous avez ${favoriteCount} souvenirs favoris`;
  5646.       } else {
  5647.         title.innerHTML = `🎊 Superbe collection de ${favoriteCount} photos !`;
  5648.       }
  5649.     }
  5650.   }
  5651.   
  5652.   // Sous-titre dynamique
  5653.   if (subtitle) {
  5654.     if (isEmpty) {
  5655.       subtitle.innerHTML = `Cliquez sur ❤️ pour ajouter des photos à vos favoris`;
  5656.     } else if (favoriteCount === 1) {
  5657.       subtitle.innerHTML = `1 photo sélectionnée avec amour ❤️`;
  5658.     } else {
  5659.       subtitle.innerHTML = `${favoriteCount} photos sélectionnées avec amour ❤️`;
  5660.     }
  5661.   }
  5662.   
  5663.   // Mettre à jour les compteurs produits
  5664.   console.log('🔄 Updating sidebar counters:', { favoriteCount, albumCount, digitalCount, printsCount });
  5665.   if (albumCount) {
  5666.     albumCount.textContent = favoriteCount;
  5667.     console.log('✅ Album count updated to:', favoriteCount);
  5668.   }
  5669.   if (digitalCount) {
  5670.     digitalCount.textContent = favoriteCount;
  5671.     console.log('✅ Digital count updated to:', favoriteCount);
  5672.   }
  5673.   
  5674.   // Afficher la pochette tirages seulement si >= 12 favoris
  5675.   if (printsCard) {
  5676.     if (favoriteCount >= 12) {
  5677.       printsCard.style.display = 'block';
  5678.       if (printsCount) printsCount.textContent = Math.min(favoriteCount, 12);
  5679.     } else {
  5680.       printsCard.style.display = 'none';
  5681.     }
  5682.   }
  5683.   
  5684.   // Gérer l'état des CTA produits
  5685.   updateProductCTAs(favoriteCount);
  5686.   
  5687.   // Gérer le countdown si proche de l'expiration
  5688.   updateCountdownTimer();
  5689. }
  5690. // Fonction pour gérer l'état des CTA produits
  5691. window.updateProductCTAs = function updateProductCTAs(favoriteCount) {
  5692.   const isEmpty = favoriteCount === 0;
  5693.   const productCTAs = document.querySelectorAll('.product-cta');
  5694.   const productDescriptions = document.querySelectorAll('.product-description');
  5695.   
  5696.   productCTAs.forEach((cta, index) => {
  5697.     if (isEmpty) {
  5698.       // État désactivé
  5699.       cta.disabled = true;
  5700.       cta.setAttribute('aria-disabled', 'true');
  5701.       cta.style.cursor = 'not-allowed';
  5702.       cta.style.opacity = '0.6';
  5703.       cta.title = 'Ajoutez des favoris pour commander';
  5704.     } else {
  5705.       // État activé
  5706.       cta.disabled = false;
  5707.       cta.removeAttribute('aria-disabled');
  5708.       cta.style.cursor = 'pointer';
  5709.       cta.style.opacity = '1';
  5710.       cta.title = '';
  5711.     }
  5712.   });
  5713.   
  5714.   // Mettre à jour les descriptions des produits
  5715.   productDescriptions.forEach((desc, index) => {
  5716.     if (isEmpty) {
  5717.       // Texte générique sans compteur
  5718.       if (index === 0) { // Album
  5719.         desc.innerHTML = 'Personnalisez votre album avec vos photos favorites. Papier de qualité supérieure, reliure rigide.';
  5720.       } else if (index === 1) { // Pack numérique
  5721.         desc.innerHTML = 'Téléchargement immédiat de vos photos en haute définition. Qualité professionnelle garantie.';
  5722.       } else if (index === 2) { // Tirages
  5723.         desc.innerHTML = 'Tirages photo 10x15cm de vos favoris. Papier brillant professionnel, livraison gratuite.';
  5724.       }
  5725.     } else {
  5726.       // Texte avec compteur dynamique
  5727.       if (index === 0) { // Album
  5728.         desc.innerHTML = `Album photo personnalisé avec vos <strong>${favoriteCount}</strong> photos favorites. Papier de qualité supérieure, reliure rigide.`;
  5729.       } else if (index === 1) { // Pack numérique
  5730.         desc.innerHTML = `Téléchargement immédiat de vos <strong>${favoriteCount}</strong> photos en haute définition. Qualité professionnelle garantie.`;
  5731.       } else if (index === 2) { // Tirages
  5732.         const printsCount = Math.min(favoriteCount, 12);
  5733.         desc.innerHTML = `<strong>${printsCount}</strong> tirages photo 10x15cm de vos favoris. Papier brillant professionnel, envoi sous 48h.`;
  5734.       }
  5735.     }
  5736.   });
  5737.   
  5738.   // Mettre à jour les icônes de favoris dans les produits
  5739.   const albumFavCount = document.getElementById('album-fav-count');
  5740.   const digitalFavCount = document.getElementById('digital-fav-count');
  5741.   const printsFavCount = document.getElementById('prints-fav-count');
  5742.   
  5743.   if (albumFavCount) albumFavCount.textContent = favoriteCount;
  5744.   if (digitalFavCount) digitalFavCount.textContent = favoriteCount;
  5745.   if (printsFavCount) printsFavCount.textContent = Math.min(favoriteCount, 12);
  5746. }
  5747. // Fonction pour gérer le countdown
  5748. window.updateCountdownTimer = function updateCountdownTimer() {
  5749.   const countdownTimer = document.getElementById('countdown-timer');
  5750.   const daysLeftSpan = document.getElementById('days-left');
  5751.   
  5752.   if (!countdownTimer || !daysLeftSpan) return;
  5753.   
  5754.   // Si pas de date définie, utiliser une date par défaut (6 semaines à partir d'aujourd'hui)
  5755.   let expirationDate;
  5756.   if (sejourEndDate) {
  5757.     const endDate = new Date(sejourEndDate);
  5758.     expirationDate = new Date(endDate.getTime() + (6 * 7 * 24 * 60 * 60 * 1000)); // +6 semaines
  5759.   } else {
  5760.     // Date par défaut : 6 semaines à partir d'aujourd'hui
  5761.     expirationDate = new Date();
  5762.     expirationDate.setDate(expirationDate.getDate() + (6 * 7));
  5763.   }
  5764.   
  5765.   const now = new Date();
  5766.   const daysLeft = Math.ceil((expirationDate - now) / (24 * 60 * 60 * 1000));
  5767.   
  5768.   if (daysLeft <= 10 && daysLeft > 0) {
  5769.     countdownTimer.style.display = 'block';
  5770.     daysLeftSpan.textContent = daysLeft;
  5771.     
  5772.     // Mettre à jour les titres avec le countdown si variante URGENCY
  5773.     if (UX_VARIANT === 'URGENCY') {
  5774.       const title = document.getElementById('ecommerce-title');
  5775.       if (title) {
  5776.         const favoriteCount = parseInt(document.getElementById('giftCount')?.textContent || '0');
  5777.         if (favoriteCount > 0) {
  5778.           if (favoriteCount < 5) {
  5779.             title.innerHTML = `⏳ ${favoriteCount} souvenirs - Plus que ${daysLeft} jours !`;
  5780.           } else if (favoriteCount < 12) {
  5781.             title.innerHTML = `⏳ ${favoriteCount} magnifiques souvenirs - Plus que ${daysLeft} jours !`;
  5782.           } else {
  5783.             title.innerHTML = `⏳ Superbe collection de ${favoriteCount} photos - Plus que ${daysLeft} jours !`;
  5784.           }
  5785.         }
  5786.       }
  5787.     }
  5788.   } else {
  5789.     countdownTimer.style.display = 'none';
  5790.   }
  5791. }
  5792. // Fonction pour commander un produit
  5793. window.orderProduct = function orderProduct(productType) {
  5794.   let favoriteCount = 0;
  5795.   
  5796.   // Méthode 1: Fonction getCurrentFavoriteCount
  5797.   try {
  5798.     if (typeof getCurrentFavoriteCount === 'function') {
  5799.       favoriteCount = getCurrentFavoriteCount();
  5800.     }
  5801.   } catch (e) {
  5802.     console.log('⚠️ getCurrentFavoriteCount non disponible:', e);
  5803.   }
  5804.   
  5805.   // Méthode 2: Element giftCount
  5806.   if (favoriteCount === 0) {
  5807.     const giftCount = document.getElementById('giftCount');
  5808.     if (giftCount && giftCount.textContent) {
  5809.       favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  5810.     }
  5811.   }
  5812.   
  5813.   // Méthode 3: Récupérer depuis les éléments qui affichent le nombre de favoris
  5814.   if (favoriteCount === 0) {
  5815.     const albumFavCount = document.getElementById('album-fav-count');
  5816.     const digitalFavCount = document.getElementById('digital-fav-count');
  5817.     
  5818.     if (albumFavCount && albumFavCount.textContent) {
  5819.       favoriteCount = parseInt(albumFavCount.textContent.trim()) || 0;
  5820.     } else if (digitalFavCount && digitalFavCount.textContent) {
  5821.       favoriteCount = parseInt(digitalFavCount.textContent.trim()) || 0;
  5822.     }
  5823.   }
  5824.   
  5825.   // Méthode 4: Variable globale likes
  5826.   if (favoriteCount === 0 && typeof likes !== 'undefined' && likes) {
  5827.     favoriteCount = likes.length || 0;
  5828.   }
  5829.   
  5830.   // Méthode 5: Compter les éléments liked dans le DOM
  5831.   if (favoriteCount === 0) {
  5832.     const likedElements = document.querySelectorAll('.liked, .photo.liked, [data-liked="true"]');
  5833.     favoriteCount = likedElements.length;
  5834.   }
  5835.   
  5836.   // Debug pour voir la valeur récupérée
  5837.   console.log('🔍 Nombre de favoris détecté:', favoriteCount);
  5838.   
  5839.   if (favoriteCount === 0) {
  5840.     // Afficher une notification plus élégante
  5841.     const notification = document.createElement('div');
  5842.     notification.className = 'favorite-notification';
  5843.     notification.innerHTML = `
  5844.       <div class="notification-content">
  5845.         <i class="bi bi-heart" style="color: #e91e63; font-size: 1.5rem; margin-right: 10px;"></i>
  5846.         <span>Veuillez d'abord sélectionner des photos favorites !</span>
  5847.         <button onclick="this.parentElement.parentElement.remove()" style="background: none; border: none; color: #666; font-size: 1.2rem; margin-left: 10px;">&times;</button>
  5848.       </div>
  5849.     `;
  5850.     
  5851.     // Styles pour la notification
  5852.     notification.style.cssText = `
  5853.       position: fixed;
  5854.       top: 20px;
  5855.       right: 20px;
  5856.       background: #fff;
  5857.       border: 2px solid #e91e63;
  5858.       border-radius: 8px;
  5859.       padding: 15px;
  5860.       box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  5861.       z-index: 10000;
  5862.       animation: slideInRight 0.3s ease;
  5863.     `;
  5864.     
  5865.     // Ajouter l'animation CSS si elle n'existe pas
  5866.     if (!document.getElementById('notification-styles')) {
  5867.       const style = document.createElement('style');
  5868.       style.id = 'notification-styles';
  5869.       style.textContent = `
  5870.         @keyframes slideInRight {
  5871.           from { transform: translateX(100%); opacity: 0; }
  5872.           to { transform: translateX(0); opacity: 1; }
  5873.         }
  5874.         .notification-content {
  5875.           display: flex;
  5876.           align-items: center;
  5877.         }
  5878.       `;
  5879.       document.head.appendChild(style);
  5880.     }
  5881.     
  5882.     document.body.appendChild(notification);
  5883.     
  5884.     // Supprimer automatiquement après 5 secondes
  5885.     setTimeout(() => {
  5886.       if (notification.parentElement) {
  5887.         notification.remove();
  5888.       }
  5889.     }, 5000);
  5890.     
  5891.     return;
  5892.   }
  5893.   
  5894.   // Construire l'URL avec les favoris pré-sélectionnés
  5895.   let orderUrl = '';
  5896.   
  5897.   switch (productType) {
  5898.     case 'album':
  5899.       orderUrl = `{{ path('EditionAlbum') }}?favorites=${favoriteCount}`;
  5900.       break;
  5901.     case 'digital':
  5902.       orderUrl = `{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}?favorites=${favoriteCount}`;
  5903.       break;
  5904.     case 'prints':
  5905.       orderUrl = `{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}?favorites=${favoriteCount}`;
  5906.       break;
  5907.     default:
  5908.       console.error('Type de produit non reconnu:', productType);
  5909.       alert('Erreur: Type de produit non reconnu');
  5910.       return;
  5911.   }
  5912.   
  5913.   // Analytics/tracking
  5914.   console.log('Product ordered', { productType, favoriteCount, url: orderUrl });
  5915.   
  5916.   // Redirection vers la commande
  5917.   if (orderUrl) {
  5918.     // Vérifier que l'URL est valide
  5919.     try {
  5920.       new URL(orderUrl, window.location.origin);
  5921.     window.location.href = orderUrl;
  5922.     } catch (error) {
  5923.       console.error('URL invalide générée:', orderUrl, error);
  5924.       alert('Erreur: Impossible de générer le lien de commande');
  5925.     }
  5926.   } else {
  5927.     console.error('Aucune URL générée pour le produit:', productType);
  5928.     alert('Erreur: Impossible de générer le lien de commande');
  5929.   }
  5930. }
  5931. // Fonction pour tester tous les liens de produits
  5932. window.testProductLinks = function() {
  5933.   const products = ['album', 'digital', 'prints'];
  5934.   const favoriteCount = window.getFavoriteCount() || 0;
  5935.   
  5936.   console.log('🧪 Test des liens de produits avec', favoriteCount, 'favoris');
  5937.   
  5938.   products.forEach(productType => {
  5939.     try {
  5940.       let testUrl = '';
  5941.       switch (productType) {
  5942.         case 'album':
  5943.           testUrl = `{{ path('EditionAlbum') }}?favorites=${favoriteCount}`;
  5944.           break;
  5945.         case 'digital':
  5946.           testUrl = `{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}?favorites=${favoriteCount}`;
  5947.           break;
  5948.         case 'prints':
  5949.           testUrl = `{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}?favorites=${favoriteCount}`;
  5950.           break;
  5951.       }
  5952.       
  5953.       // Vérifier que l'URL est valide
  5954.       const url = new URL(testUrl, window.location.origin);
  5955.       console.log(`✅ ${productType}: ${url.href}`);
  5956.     } catch (error) {
  5957.       console.error(`❌ ${productType}: Erreur URL`, error);
  5958.     }
  5959.   });
  5960. };
  5961. // Fonction pour valider les liens au chargement de la page
  5962. window.validateProductLinks = function() {
  5963.   // Vérifier que tous les boutons de produits existent
  5964.   const albumBtn = document.querySelector('button[onclick*="orderProduct(\'album\')"]');
  5965.   const digitalBtn = document.querySelector('button[onclick*="orderProduct(\'digital\')"]');
  5966.   const printsBtn = document.querySelector('button[onclick*="orderProduct(\'prints\')"]');
  5967.   
  5968.   if (!albumBtn) console.warn('⚠️ Bouton album non trouvé');
  5969.   if (!digitalBtn) console.warn('⚠️ Bouton digital non trouvé');
  5970.   if (!printsBtn) console.warn('⚠️ Bouton prints non trouvé');
  5971.   
  5972.   // Tester les liens
  5973.   window.testProductLinks();
  5974. };
  5975. // Améliorer la fonction updateAllFavoriteCounters existante
  5976. const originalUpdateAllFavoriteCounters = window.updateAllFavoriteCounters || function() {};
  5977. window.updateAllFavoriteCounters = function() {
  5978.   // Appeler la fonction originale
  5979.   originalUpdateAllFavoriteCounters();
  5980.   
  5981.   // Animation rebond du compteur favoris
  5982.   const giftCount = document.getElementById('giftCount');
  5983.   if (giftCount) {
  5984.     giftCount.classList.remove('gift-count-bounce');
  5985.     setTimeout(() => giftCount.classList.add('gift-count-bounce'), 10);
  5986.   }
  5987.   
  5988.   // Animation rebond du compteur dans la bulle cadeau
  5989.   const likeCount = document.getElementById('likeCount');
  5990.   if (likeCount) {
  5991.     likeCount.classList.remove('gift-count-bounce');
  5992.     setTimeout(() => likeCount.classList.add('gift-count-bounce'), 10);
  5993.   }
  5994.   
  5995.   // Récupérer le nombre de favoris actuel avec la fonction utilitaire robuste
  5996.   let favoriteCount = window.getFavoriteCount();
  5997.   
  5998.   // Mettre à jour les compteurs dans le sidebar même s'il n'est pas ouvert
  5999.   const albumCount = document.getElementById('album-count');
  6000.   const digitalCount = document.getElementById('digital-count');
  6001.   const printsCount = document.getElementById('prints-count');
  6002.   
  6003.   if (albumCount) albumCount.textContent = favoriteCount;
  6004.   if (digitalCount) digitalCount.textContent = favoriteCount;
  6005.   if (printsCount) printsCount.textContent = Math.min(favoriteCount, 12);
  6006.   
  6007.   // Mettre à jour les icônes de favoris dans les produits
  6008.   const albumFavCount = document.getElementById('album-fav-count');
  6009.   const digitalFavCount = document.getElementById('digital-fav-count');
  6010.   const printsFavCount = document.getElementById('prints-fav-count');
  6011.   
  6012.   if (albumFavCount) albumFavCount.textContent = favoriteCount;
  6013.   if (digitalFavCount) digitalFavCount.textContent = favoriteCount;
  6014.   if (printsFavCount) printsFavCount.textContent = Math.min(favoriteCount, 12);
  6015.   
  6016.   // Mettre à jour le sidebar si ouvert
  6017.   const sidebar = document.getElementById('ecommerce-sidebar');
  6018.   if (sidebar && sidebar.classList.contains('active')) {
  6019.     console.log('🔄 Sidebar is open, updating content with favoriteCount:', favoriteCount);
  6020.     window.updateEcommerceSidebarContent(favoriteCount);
  6021.   } else {
  6022.     console.log('ℹ️ Sidebar is closed, but updating content anyway for consistency');
  6023.     // Mettre à jour le contenu même si le sidebar n'est pas ouvert pour la cohérence
  6024.     window.updateEcommerceSidebarContent(favoriteCount);
  6025.   }
  6026. };
  6027. // Fonction pour retirer un favori de la grille et exécuter supprimerFavoris
  6028. window.removeFavorite = function removeFavorite(itemId) {
  6029.   // Animation simple de disparition puis suppression
  6030.   const favoriteItem = document.querySelector(`.favorite-item[data-id="${itemId}"]`);
  6031.  
  6032.   if (favoriteItem) {
  6033.     favoriteItem.classList.add('fade-out');
  6034.     setTimeout(() => {
  6035.       favoriteItem.remove();
  6036.       // Appeler la fonction métier si présente
  6037.       if (typeof window.supprimerFavoris === 'function') {
  6038.         const heartIcon = document.querySelector(`#coeur${itemId}`);
  6039.         const sejourId = heartIcon && heartIcon.dataset.sejourId ? heartIcon.dataset.sejourId : '';
  6040.         window.supprimerFavoris(itemId, sejourId);
  6041.       }
  6042.       // Mettre à jour les compteurs
  6043.       updateAllFavoriteCounters();
  6044.     }, 200);
  6045.   }
  6046. };
  6047. // Vérifier que la fonction est bien définie
  6048. console.log('🔍 removeFavorite function defined:', typeof window.removeFavorite);
  6049. // (Nettoyé) Pas de délégation globale ici; les boutons utilisent des onclick simples
  6050. // Fonction pour voir un favori en grand
  6051. window.viewFavorite = function viewFavorite(itemId) {
  6052.   console.log('👁️ Viewing favorite:', itemId);
  6053.   
  6054.   // Essayer d'utiliser la fonction viewImage existante
  6055.   if (typeof window.viewImage === 'function') {
  6056.     window.viewImage(itemId, null);
  6057.   } else {
  6058.     console.log('ℹ️ viewImage function not available, opening in new tab');
  6059.     // Fallback: ouvrir dans un nouvel onglet
  6060.     const favoriteItem = document.querySelector(`.favorite-item[data-id="${itemId}"] img`);
  6061.     if (favoriteItem && favoriteItem.src) {
  6062.       window.open(favoriteItem.src, '_blank');
  6063.     }
  6064.   }
  6065. };
  6066. // Fonction de debug pour tester les compteurs
  6067. window.debugFavoriteCounters = function() {
  6068.   console.log('🔍 Debug Favorite Counters:');
  6069.   console.log('- getFavoriteCount():', window.getFavoriteCount());
  6070.   console.log('- likeCount input value:', document.getElementById('likeCount')?.value);
  6071.   console.log('- likeCount textContent:', document.getElementById('likeCount')?.textContent);
  6072.   console.log('- giftCount textContent:', document.getElementById('giftCount')?.textContent);
  6073.   console.log('- album-count textContent:', document.getElementById('album-count')?.textContent);
  6074.   console.log('- digital-count textContent:', document.getElementById('digital-count')?.textContent);
  6075.   console.log('- prints-count textContent:', document.getElementById('prints-count')?.textContent);
  6076.   console.log('- Sidebar active:', document.getElementById('ecommerce-sidebar')?.classList.contains('active'));
  6077. };
  6078. // Fonction de test pour les boutons de favoris
  6079. window.testFavoriteButtons = function() {
  6080.   console.log('🧪 Testing favorite buttons:');
  6081.   const removeButtons = document.querySelectorAll('.btn-remove-favorite');
  6082.   const viewButtons = document.querySelectorAll('.btn-view-favorite');
  6083.   
  6084.   console.log('- Remove buttons found:', removeButtons.length);
  6085.   console.log('- View buttons found:', viewButtons.length);
  6086.   
  6087.   removeButtons.forEach((btn, index) => {
  6088.     console.log(`- Remove button ${index}:`, {
  6089.       itemId: btn.dataset.itemId,
  6090.       hasClickListener: btn.onclick !== null
  6091.     });
  6092.   });
  6093.   
  6094.   viewButtons.forEach((btn, index) => {
  6095.     console.log(`- View button ${index}:`, {
  6096.       itemId: btn.dataset.itemId,
  6097.       hasClickListener: btn.onclick !== null
  6098.     });
  6099.   });
  6100. };
  6101. // Fermer le sidebar en cliquant en dehors
  6102. document.addEventListener('click', function(e) {
  6103.   const sidebar = document.getElementById('ecommerce-sidebar');
  6104.   const giftButton = document.querySelector('.gift-button');
  6105.   
  6106.   if (sidebar && sidebar.classList.contains('active') && 
  6107.       !sidebar.contains(e.target) && 
  6108.       !giftButton.contains(e.target)) {
  6109.     window.closeEcommerceSidebar();
  6110.   }
  6111. });
  6112. // Fermer avec Escape
  6113. document.addEventListener('keydown', function(e) {
  6114.   if (e.key === 'Escape') {
  6115.     window.closeEcommerceSidebar();
  6116.   }
  6117. });
  6118. // Initialisation au chargement
  6119. document.addEventListener('DOMContentLoaded', function() {
  6120.   // Vérifier que les éléments existent
  6121.   const sidebar = document.getElementById('ecommerce-sidebar');
  6122.   const giftButton = document.getElementById('gift-button-trigger');
  6123.   
  6124.   // Event listener propre pour le bouton cadeau
  6125.   if (giftButton) {
  6126.     giftButton.addEventListener('click', function(e) {
  6127.       e.preventDefault();
  6128.       e.stopPropagation();
  6129.       window.openEcommerceSidebar();
  6130.     });
  6131.   }
  6132.   
  6133.   // Définir la date de fin du séjour si disponible
  6134.   {% if sejour and sejour.dateFinSejour is defined %}
  6135.     window.setSejourEndDate('{{ sejour.dateFinSejour|date("Y-m-d") }}');
  6136.   {% endif %}
  6137.   
  6138.   // Mise à jour initiale du contenu
  6139.   let favoriteCount = 0;
  6140.   try {
  6141.     if (typeof getCurrentFavoriteCount === 'function') {
  6142.       favoriteCount = getCurrentFavoriteCount();
  6143.     } else {
  6144.       // Essayer d'abord l'input likeCount
  6145.       const likeCountInput = document.getElementById('likeCount');
  6146.       if (likeCountInput && likeCountInput.value) {
  6147.         favoriteCount = parseInt(likeCountInput.value, 10) || 0;
  6148.       } else {
  6149.         // Fallback sur giftCount
  6150.         const giftCount = document.getElementById('giftCount');
  6151.         if (giftCount && giftCount.textContent) {
  6152.           favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  6153.         }
  6154.       }
  6155.     }
  6156.   } catch (e) {
  6157.     console.log('⚠️ Fallback pour favoriteCount initial:', e);
  6158.     // Fallback sur likeCount input
  6159.     const likeCountInput = document.getElementById('likeCount');
  6160.     if (likeCountInput && likeCountInput.value) {
  6161.       favoriteCount = parseInt(likeCountInput.value, 10) || 0;
  6162.     } else {
  6163.       const giftCount = document.getElementById('giftCount');
  6164.       if (giftCount && giftCount.textContent) {
  6165.         favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  6166.       }
  6167.     }
  6168.   }
  6169.   
  6170.   window.updateEcommerceSidebarContent(favoriteCount);
  6171.   
  6172. });
  6173. </script>
  6174. <!-- 🎯 DAY FILTER POPOVER (instancié une seule fois) -->
  6175. <div id="dayFilterPopover" class="day-popover" role="dialog" aria-modal="true" aria-hidden="true">
  6176.   <div class="dp-arrow" aria-hidden="true"></div>
  6177.   <div class="dp-content" role="group" aria-label="Filtres du jour">
  6178.     <button class="dp-btn" data-filter="all" title="Tout" aria-pressed="true">
  6179.       <i class="bi bi-grid-3x3-gap-fill"></i><span class="dp-count dp-label">Tout</span>
  6180.     </button>
  6181.     <button class="dp-btn" data-filter="photos" title="Photos" aria-pressed="false">
  6182.       <i class="bi bi-images"></i><span class="dp-count" data-bind="photo">0</span>
  6183.     </button>
  6184.     <button class="dp-btn" data-filter="audio" title="Audios" aria-pressed="false">
  6185.       <i class="bi bi-mic-fill"></i><span class="dp-count" data-bind="audio">0</span>
  6186.     </button>
  6187.     <button class="dp-btn" data-filter="videos" title="Vidéos" aria-pressed="false">
  6188.       <i class="bi bi-camera-video-fill"></i><span class="dp-count" data-bind="video">0</span>
  6189.     </button>
  6190.     <button class="dp-btn" data-filter="favoris" title="Favoris" aria-pressed="false">
  6191.       <i class="bi bi-heart-fill" style="color:#f56040"></i><span class="dp-count" data-bind="fav">0</span>
  6192.     </button>
  6193.     <button class="dp-btn dp-close" title="Fermer" aria-label="Fermer">
  6194.       <i class="bi bi-x-lg"></i>
  6195.     </button>
  6196.   </div>
  6197. </div>
  6198. {% endblock %}