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. <link rel="stylesheet" href="{{ asset('css/sidebar-ecommerce-pro.css') }}">
  11. {% set destination = "detailsejour" %}
  12. {# ==================== Configuration B2C Conversion ==================== #}
  13. {% set nowTs = 'now'|date('U') %}
  14. {% set debutTs = sejour.dateDebutSejour|date('U') %}
  15. {% set finTs = sejour.dateFinSejour|date('U') %}
  16. {% set closeTs = sejour.dateFinCode ? sejour.dateFinCode|date('U') : finTs %}
  17. {% set secondsPerDay = 86400 %}
  18. {% set daysSinceStart = ((nowTs - debutTs) / secondsPerDay)|round(0, 'floor') %}
  19. {% set daysUntilEnd = ((finTs - nowTs) / secondsPerDay)|round(0, 'ceil') %}
  20. {% set daysAfterEnd = ((nowTs - finTs) / secondsPerDay)|round(0, 'floor') %}
  21. {% set daysUntilClose = ((closeTs - nowTs) / secondsPerDay)|round(0, 'ceil') %}
  22. <style>
  23. /* ============================================
  24.    PREMIUM DAY CARDS - Style Accompagnateur
  25.    ============================================ */
  26. /* Base card styles */
  27. /* ============================================
  28.    PREMIUM DAY CARDS - Timeline ultra-premium
  29.    Identique à l'espace accompagnateur
  30.    ============================================ */
  31. .date-card {
  32.   flex: 0 0 auto;
  33.   width: 110px;
  34.   background: #fff;
  35.   border-radius: 14px;
  36.   padding: 8px 6px;
  37.   text-align: center;
  38.   transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  39.   overflow: visible;
  40.   scroll-snap-align: center;
  41.   position: relative;
  42.   min-height: 56px;
  43.   max-height: 68px;
  44.   border: 1px solid transparent;
  45.   cursor: pointer;
  46. }
  47. .date-card .day {
  48.   font-weight: 600;
  49.   font-size: 0.70rem;
  50.   color: #2c3e50;
  51.   margin-bottom: 1px;
  52.   letter-spacing: -0.01em;
  53.   line-height: 1.15;
  54. }
  55. .date-card .full-date {
  56.   font-size: 0.60rem;
  57.   color: #6b7280;
  58.   font-weight: 500;
  59.   line-height: 1.1;
  60.   margin-top: 0px;
  61.   padding-bottom: 5px;
  62. }
  63. .date-card ul {
  64.   margin: 4px 0 0 0;
  65.   padding: 0;
  66.   list-style: none;
  67.   display: flex;
  68.   gap: 4px;
  69.   justify-content: center;
  70.   flex-wrap: wrap;
  71.   align-items: center;
  72. }
  73. .date-card ul li {
  74.   font-size: 0.60rem;
  75.   color: #6b7280;
  76.   display: flex;
  77.   align-items: center;
  78.   gap: 2px;
  79.   line-height: 1;
  80.   font-weight: 500;
  81. }
  82. .date-card ul li i {
  83.   font-size: 0.65rem;
  84. }
  85. .premium-day-card {
  86.   flex: 0 0 auto;
  87.   width: 130px;
  88.   background: #fff;
  89.   border-radius: 14px;
  90.   padding: 8px 6px;
  91.   text-align: center;
  92.   transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  93.   overflow: visible;
  94.   scroll-snap-align: center;
  95.   position: relative;
  96.   min-height: 56px;
  97.   max-height: 68px;
  98.   border: 1px solid transparent;
  99.   cursor: pointer;
  100.   will-change: transform;
  101.   contain: layout style;
  102. }
  103. .premium-day-card .day {
  104.   font-weight: 600;
  105.   font-size: 0.9rem;
  106.   color: #2c3e50;
  107.   margin-bottom: 1px;
  108.   letter-spacing: -0.01em;
  109.   line-height: 1.15;
  110. }
  111. .premium-day-card .full-date {
  112.   font-size: 0.75rem;
  113.   color: #6b7280;
  114.   font-weight: 500;
  115.   line-height: 1.1;
  116.   margin-top: 0;
  117. }
  118. /* Compteurs de médias - Points colorés minimalistes */
  119. .premium-day-card ul,
  120. .date-card ul,
  121. .media-list-horizontal {
  122.   margin: 4px 0 0 0;
  123.   padding: 0;
  124.   list-style: none;
  125.   display: flex;
  126.   gap: 4px;
  127.   justify-content: center;
  128.   flex-wrap: wrap;
  129.   align-items: center;
  130. }
  131. .premium-day-card ul li,
  132. .date-card ul li {
  133.   font-size: 0.60rem;
  134.   color: #6b7280;
  135.   display: flex;
  136.   align-items: center;
  137.   gap: 2px;
  138.   line-height: 1;
  139.   font-weight: 500;
  140. }
  141. .premium-day-card ul li i,
  142. .date-card ul li i {
  143.   font-size: 0.65rem;
  144. }
  145. /* ============================================
  146.    COULEURS PAR TYPE DE MÉDIA - Uniforme pour toutes les cartes
  147.    Photo = Orange, Vocal = Bleu, Vidéo = Orange clair
  148.    ============================================ */
  149. /* Photo = Orange 🟠 */
  150. .premium-day-card ul li i.bi-camera,
  151. .premium-day-card ul li i.bi-camera-fill,
  152. .premium-day-card ul li i.bi-image,
  153. .premium-day-card ul li i.bi-images,
  154. .premium-day-past ul li i.bi-camera,
  155. .premium-day-past ul li i.bi-camera-fill,
  156. .premium-day-past ul li i.bi-image,
  157. .premium-day-past ul li i.bi-images,
  158. .premium-day-current ul li i.bi-camera,
  159. .premium-day-current ul li i.bi-camera-fill,
  160. .premium-day-current ul li i.bi-image,
  161. .premium-day-current ul li i.bi-images,
  162. .premium-day-future ul li i.bi-camera,
  163. .premium-day-future ul li i.bi-camera-fill,
  164. .premium-day-future ul li i.bi-image,
  165. .premium-day-future ul li i.bi-images,
  166. .date-card ul li i.bi-camera,
  167. .date-card ul li i.bi-camera-fill,
  168. .date-card ul li i.bi-image,
  169. .date-card ul li i.bi-images {
  170.   color: #F56040 !important;
  171.   opacity: 1 !important;
  172. }
  173. /* Vocal/Audio = Bleu Mint 🔵 */
  174. .premium-day-card ul li i.bi-mic-fill,
  175. .premium-day-card ul li i.bi-mic,
  176. .premium-day-past ul li i.bi-mic-fill,
  177. .premium-day-past ul li i.bi-mic,
  178. .premium-day-current ul li i.bi-mic-fill,
  179. .premium-day-current ul li i.bi-mic,
  180. .premium-day-future ul li i.bi-mic-fill,
  181. .premium-day-future ul li i.bi-mic,
  182. .date-card ul li i.bi-mic-fill,
  183. .date-card ul li i.bi-mic {
  184.   color: #41A2AA !important;
  185.   opacity: 1 !important;
  186. }
  187. /* Vidéo = Orange clair 🟠 */
  188. .premium-day-card ul li i.bi-camera-video,
  189. .premium-day-card ul li i.bi-camera-video-fill,
  190. .premium-day-card ul li i.bi-film,
  191. .premium-day-past ul li i.bi-camera-video,
  192. .premium-day-past ul li i.bi-camera-video-fill,
  193. .premium-day-past ul li i.bi-film,
  194. .premium-day-current ul li i.bi-camera-video,
  195. .premium-day-current ul li i.bi-camera-video-fill,
  196. .premium-day-current ul li i.bi-film,
  197. .premium-day-future ul li i.bi-camera-video,
  198. .premium-day-future ul li i.bi-camera-video-fill,
  199. .premium-day-future ul li i.bi-film,
  200. .date-card ul li i.bi-camera-video,
  201. .date-card ul li i.bi-camera-video-fill,
  202. .date-card ul li i.bi-film {
  203.   color: #FF9F7A !important;
  204.   opacity: 1 !important;
  205. }
  206. /* État PASSÉ - Cohérent avec charte 5sur5 */
  207. .premium-day-past {
  208.   opacity: 0.85;
  209.   background: rgba(65, 162, 170, 0.05);
  210.   border: 1px solid rgba(65, 162, 170, 0.25);
  211.   cursor: pointer;
  212.   pointer-events: auto;
  213. }
  214. .premium-day-past:hover {
  215.   transform: translateY(-2px);
  216.   box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
  217.   opacity: 1;
  218. }
  219. /* État AUJOURD'HUI - Premium mis en avant clairement */
  220. .premium-day-current {
  221.   width: 110px !important;
  222.   border: 2px solid #41A2AA !important;
  223.   background: #fff !important;
  224.   box-shadow: 0 6px 16px rgba(65, 162, 170, 0.12) !important;
  225.   transform: scale(1.04);
  226.   z-index: 10;
  227.   cursor: pointer;
  228.   opacity: 1 !important;
  229.   position: relative;
  230. }
  231. /* Point animé "Aujourd'hui" - style calendrier moderne */
  232. .premium-day-current::after {
  233.   content: '';
  234.   position: absolute;
  235.   top: 8px;
  236.   right: 8px;
  237.   width: 8px;
  238.   height: 8px;
  239.   background: #41A2AA;
  240.   border-radius: 50%;
  241.   box-shadow: 0 0 0 0 rgba(65, 162, 170, 0.7);
  242.   animation: todayPulse 2s ease-in-out infinite;
  243.   z-index: 5;
  244. }
  245. @keyframes todayPulse {
  246.   0% {
  247.     box-shadow: 0 0 0 0 rgba(65, 162, 170, 0.7);
  248.   }
  249.   50% {
  250.     box-shadow: 0 0 0 6px rgba(65, 162, 170, 0);
  251.   }
  252.   100% {
  253.     box-shadow: 0 0 0 0 rgba(65, 162, 170, 0);
  254.   }
  255. }
  256. .premium-day-current:hover {
  257.   transform: scale(1.04);
  258.   box-shadow: 0 6px 16px rgba(65, 162, 170, 0.12) !important;
  259. }
  260. .premium-day-current:focus-visible {
  261.   outline: 3px solid rgba(65, 162, 170, 0.3);
  262.   outline-offset: 2px;
  263. }
  264. .premium-day-current .day,
  265. .premium-day-current .full-date {
  266.   color: #137F86 !important;
  267.   font-weight: 600 !important;
  268. }
  269. .premium-day-current .full-date {
  270.   padding-bottom: 5px !important;
  271. }
  272. .premium-day-current ul li {
  273.   color: #137F86 !important;
  274. }
  275. /* État ACTIF - Carte sélectionnée */
  276. .date-card.active,
  277. .premium-day-card.active {
  278.   width: 140px !important;
  279.   border: 2px solid #41A2AA !important;
  280.   background: #EAF7F7 !important;
  281.   box-shadow: 0 2px 8px rgba(65, 162, 170, 0.15) !important;
  282.   transform: scale(1.02);
  283.   z-index: 10;
  284. }
  285. .date-card.active .day,
  286. .premium-day-card.active .day,
  287. .date-card.active .day-number,
  288. .premium-day-card.active .day-number {
  289.   color: #137F86 !important;
  290.   font-weight: 700;
  291. }
  292. .date-card.active .full-date,
  293. .premium-day-card.active .full-date,
  294. .date-card.active .day-name,
  295. .premium-day-card.active .day-name {
  296.   color: #137F86 !important;
  297.   font-weight: 700;
  298. }
  299. .date-card.active ul li,
  300. .premium-day-card.active ul li {
  301.   color: #137F86 !important;
  302. }
  303. .date-card.active ul li i,
  304. .premium-day-card.active ul li i {
  305.   color: #41A2AA !important;
  306. }
  307. .date-card:hover,
  308. .premium-day-card:hover {
  309.   background-color: #f9f9f9;
  310.   box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
  311. }
  312. /* État FUTUR - Semi-caché, réduit, verrouillé */
  313. .premium-day-future {
  314.   opacity: 0.45 !important;
  315.   transform: scale(0.92);
  316.   cursor: not-allowed !important;
  317.   pointer-events: none !important;
  318.   border: 1px solid rgba(188, 196, 206, 0.2) !important;
  319.   background: #fff !important;
  320.   box-shadow: none !important;
  321. }
  322. .premium-day-future:hover {
  323.   transform: scale(0.92) !important;
  324.   box-shadow: none !important;
  325. }
  326. .premium-day-future .day,
  327. .premium-day-future .full-date {
  328.   color: #BCC4CE !important;
  329. }
  330. .premium-day-future ul {
  331.   display: none !important;
  332. }
  333. /* Icône cadenas sur jours futurs */
  334. .premium-day-future::before {
  335.   content: '\F4C0';
  336.   font-family: 'bootstrap-icons';
  337.   position: absolute;
  338.   top: 4px;
  339.   right: 4px;
  340.   font-size: 9px;
  341.   color: #BCC4CE;
  342.   opacity: 0.25;
  343. }
  344. /* Tooltip pour jours futurs */
  345. .premium-day-future::after {
  346.   content: attr(data-tooltip);
  347.   position: absolute;
  348.   bottom: -35px;
  349.   left: 50%;
  350.   transform: translateX(-50%);
  351.   background: rgba(0, 0, 0, 0.85);
  352.   color: white;
  353.   padding: 6px 10px;
  354.   border-radius: 6px;
  355.   font-size: 11px;
  356.   white-space: nowrap;
  357.   opacity: 0;
  358.   pointer-events: none;
  359.   transition: opacity 0.2s ease;
  360.   z-index: 1000;
  361. }
  362. /* Date container scroll */
  363. .date-container {
  364.   display: flex;
  365.   gap: 12px;
  366.   overflow-x: auto;
  367.   scrollbar-width: none;
  368.   -ms-overflow-style: none;
  369.   padding: 8px 4px;
  370. }
  371. .date-container::-webkit-scrollbar {
  372.   display: none;
  373. }
  374. /* Section days wrapper */
  375. .section-days {
  376.   background: #f8fafb;
  377.   border-radius: 12px;
  378.   padding: 10px 12px;
  379.   margin-bottom: 16px;
  380.   border: 1px solid rgba(65, 162, 170, 0.1);
  381.   box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
  382. }
  383. /* Bouton favoris désactivé (0 favoris) */
  384. .mtb-btn.favoris-empty,
  385. .mtb-btn[data-filter="favoris"]:disabled {
  386.   opacity: 0.6;
  387.   cursor: not-allowed;
  388.   pointer-events: none;
  389.   background: rgba(0, 0, 0, 0.02) !important;
  390.   border-color: rgba(0, 0, 0, 0.08) !important;
  391. }
  392. .mtb-btn.favoris-empty:hover,
  393. .mtb-btn[data-filter="favoris"]:disabled:hover {
  394.   transform: none;
  395.   box-shadow: none;
  396. }
  397. .favoris-empty-text {
  398.   font-size: 11px;
  399.   font-family: inherit;
  400.   color: #94a3b8;
  401.   font-style: italic;
  402.   font-weight: 500;
  403.   line-height: 1.2;
  404. }
  405. /* Styles premium pour les filtres */
  406. .premium-5sur5-actions-group {
  407.   display: flex;
  408.   gap: 8px;
  409.   align-items: center;
  410.   flex-wrap: wrap;
  411. }
  412. .premium-5sur5-action-btn {
  413.   display: inline-flex;
  414.   align-items: center;
  415.   gap: 6px;
  416.   padding: 7px 12px;
  417.   background: #F7FBFC;
  418.   border: 1px solid rgba(65, 162, 170, 0.12);
  419.   border-radius: 14px;
  420.   color: #41A2AA;
  421.   font-size: 13px;
  422.   font-weight: 600;
  423.   font-family: inherit;
  424.   cursor: pointer;
  425.   transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  426.   white-space: nowrap;
  427.   position: relative;
  428.   line-height: 1.4;
  429. }
  430. .premium-5sur5-action-btn:hover:not(:disabled) {
  431.   background: rgba(65, 162, 170, 0.08);
  432.   border-color: rgba(65, 162, 170, 0.25);
  433.   transform: translateY(-1px);
  434. }
  435. .premium-5sur5-action-btn:active:not(:disabled) {
  436.   transform: translateY(0);
  437. }
  438. .premium-5sur5-action-btn i {
  439.   font-size: 14px;
  440.   opacity: 0.85;
  441.   line-height: 1;
  442. }
  443. .premium-5sur5-action-btn span:not(.premium-5sur5-badge):not(.favoris-empty-text) {
  444.   font-size: 13px;
  445.   font-weight: 600;
  446.   letter-spacing: 0.2px;
  447.   line-height: 1.4;
  448.   font-family: inherit;
  449. }
  450. .premium-5sur5-badge {
  451.   display: inline-block;
  452.   font-size: 11px;
  453.   font-weight: 600;
  454.   font-family: inherit;
  455.   padding: 2px 6px;
  456.   border-radius: 6px;
  457.   letter-spacing: 0.2px;
  458.   margin-left: 2px;
  459.   line-height: 1.2;
  460. }
  461. /* État actif pour les boutons premium */
  462. .premium-5sur5-action-btn.mtb-btn.active,
  463. .premium-5sur5-action-btn.mtb-btn.is-active {
  464.   background: rgba(65, 162, 170, 0.15) !important;
  465.   border-color: #41A2AA !important;
  466.   color: #137F86 !important;
  467.   box-shadow: 0 2px 8px rgba(65, 162, 170, 0.2);
  468. }
  469. .premium-5sur5-action-btn.mtb-btn[data-filter="favoris"].active {
  470.   background: rgba(245, 96, 64, 0.15) !important;
  471.   border-color: #f56040 !important;
  472.   color: #d43e1f !important;
  473. }
  474. .premium-5sur5-action-btn.mtb-btn[data-filter="audio"].active {
  475.   background: rgba(255, 215, 0, 0.15) !important;
  476.   border-color: #ffd700 !important;
  477.   color: #b8860b !important;
  478. }
  479. /* Bouton favoris désactivé */
  480. .premium-5sur5-action-btn.favoris-empty,
  481. .premium-5sur5-action-btn[data-filter="favoris"]:disabled {
  482.   opacity: 0.6;
  483.   cursor: not-allowed;
  484.   pointer-events: none;
  485.   background: rgba(0, 0, 0, 0.02) !important;
  486.   border-color: rgba(0, 0, 0, 0.08) !important;
  487. }
  488. .premium-5sur5-action-btn.favoris-empty:hover,
  489. .premium-5sur5-action-btn[data-filter="favoris"]:disabled:hover {
  490.   transform: none;
  491.   box-shadow: none;
  492. }
  493. /* Styles pour la section favoris */
  494. .favorites-ecommerce-view {
  495.   width: 100%;
  496.   max-width: 100%;
  497.   padding: 24px;
  498.   box-sizing: border-box;
  499. }
  500. .favorites-header {
  501.   display: flex;
  502.   justify-content: space-between;
  503.   align-items: center;
  504.   margin-bottom: 24px;
  505.   padding: 20px;
  506.   background: #f8fafb;
  507.   border-radius: 12px;
  508.   border: 1px solid rgba(65, 162, 170, 0.1);
  509. }
  510. .favorites-title {
  511.   display: flex;
  512.   align-items: center;
  513.   gap: 12px;
  514.   flex-wrap: wrap;
  515. }
  516. .favorites-title h3 {
  517.   font-size: 20px;
  518.   font-weight: 700;
  519.   color: #1a1a1a;
  520.   margin: 0;
  521.   display: flex;
  522.   align-items: center;
  523.   gap: 8px;
  524. }
  525. .favorites-title h3 i {
  526.   color: #f56040;
  527.   font-size: 20px;
  528. }
  529. .favorites-count {
  530.   display: inline-flex;
  531.   align-items: center;
  532.   padding: 6px 12px;
  533.   background: rgba(245, 96, 64, 0.1);
  534.   color: #f56040;
  535.   border-radius: 20px;
  536.   font-size: 14px;
  537.   font-weight: 600;
  538. }
  539. .btn-back-to-days {
  540.   display: flex;
  541.   align-items: center;
  542.   gap: 8px;
  543.   padding: 10px 20px;
  544.   background: #ffffff;
  545.   border: 1px solid rgba(65, 162, 170, 0.2);
  546.   border-radius: 10px;
  547.   color: #6b7280;
  548.   font-size: 14px;
  549.   font-weight: 600;
  550.   cursor: pointer;
  551.   transition: all 0.2s ease;
  552. }
  553. .btn-back-to-days:hover {
  554.   background: rgba(65, 162, 170, 0.05);
  555.   border-color: #41A2AA;
  556.   color: #41A2AA;
  557. }
  558. .favorites-content {
  559.   width: 100%;
  560. }
  561. .favorites-grid {
  562.   display: grid;
  563.   grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  564.   gap: 16px;
  565.   width: 100%;
  566.   padding: 0;
  567. }
  568. .favorites-grid .favorite-item,
  569. .favorites-grid .photo-item {
  570.   position: relative;
  571.   width: 100%;
  572.   aspect-ratio: 1;
  573.   border-radius: 12px;
  574.   overflow: hidden;
  575.   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  576.   transition: all 0.3s ease;
  577.   cursor: pointer;
  578. }
  579. .favorites-grid .favorite-item:hover,
  580. .favorites-grid .photo-item:hover {
  581.   transform: translateY(-4px);
  582.   box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  583. }
  584. .favorites-grid .favorite-item:hover .favorite-actions,
  585. .favorites-grid .photo-item:hover .favorite-actions {
  586.   opacity: 1;
  587. }
  588. .favorites-grid img {
  589.   width: 100%;
  590.   height: 100%;
  591.   object-fit: cover;
  592.   display: block;
  593. }
  594. .favorites-grid .favorite-item {
  595.   position: relative;
  596. }
  597. .favorites-grid .favorite-actions {
  598.   position: absolute;
  599.   top: 8px;
  600.   right: 8px;
  601.   display: flex;
  602.   gap: 6px;
  603.   opacity: 0;
  604.   transition: opacity 0.3s ease;
  605.   z-index: 10;
  606. }
  607. /* CTA discret et premium */
  608. .premium-cta-subtle {
  609.   margin: 20px 0;
  610.   padding: 16px 20px;
  611.   background: rgba(65, 162, 170, 0.04);
  612.   border: 1px solid rgba(65, 162, 170, 0.15);
  613.   border-radius: 12px;
  614.   cursor: pointer;
  615.   transition: all 0.3s ease;
  616.   display: flex;
  617.   align-items: center;
  618.   justify-content: space-between;
  619.   gap: 12px;
  620.   text-decoration: none;
  621.   color: inherit;
  622. }
  623. .premium-cta-subtle:hover {
  624.   background: rgba(65, 162, 170, 0.08);
  625.   border-color: rgba(65, 162, 170, 0.25);
  626.   transform: translateX(2px);
  627.   text-decoration: none;
  628.   color: inherit;
  629. }
  630. .premium-cta-subtle > div:first-child {
  631.   display: flex;
  632.   align-items: center;
  633.   gap: 12px;
  634.   flex: 1;
  635. }
  636. .premium-cta-subtle > div:first-child > div:first-child {
  637.   width: 40px;
  638.   height: 40px;
  639.   background: rgba(65, 162, 170, 0.1);
  640.   border-radius: 10px;
  641.   display: flex;
  642.   align-items: center;
  643.   justify-content: center;
  644.   flex-shrink: 0;
  645. }
  646. .premium-cta-subtle > div:first-child > div:first-child i {
  647.   font-size: 18px;
  648.   color: #41A2AA;
  649. }
  650. .premium-cta-subtle > div:first-child > div:last-child {
  651.   flex: 1;
  652.   min-width: 0;
  653. }
  654. .premium-cta-subtle > div:first-child > div:last-child p:first-child {
  655.   margin: 0;
  656.   font-size: 14px;
  657.   font-weight: 600;
  658.   color: #1a1a1a;
  659.   line-height: 1.4;
  660. }
  661. .premium-cta-subtle > div:first-child > div:last-child p:last-child {
  662.   margin: 4px 0 0 0;
  663.   font-size: 12px;
  664.   color: #6b7280;
  665.   line-height: 1.4;
  666. }
  667. .premium-cta-subtle > i:last-child {
  668.   font-size: 18px;
  669.   color: #41A2AA;
  670.   flex-shrink: 0;
  671. }
  672. .premium-5sur5-day-title {
  673.   font-size: 1.35rem;
  674.   font-weight: 700;
  675.   color: #2c3e50;
  676.   margin: 0 0 4px 0;
  677.   letter-spacing: -0.02em;
  678.   line-height: 1.3;
  679. }
  680. .premium-5sur5-day-subtitle {
  681.   font-size: 0.9rem;
  682.   color: #6b7280;
  683.   margin: 0;
  684.   font-weight: 500;
  685.   line-height: 1.5;
  686. }
  687. @media (max-width: 768px) {
  688.   .premium-5sur5-day-header {
  689.     padding: 12px 16px;
  690.     margin-bottom: 16px;
  691.   }
  692.   .premium-5sur5-day-title {
  693.     font-size: 1.15rem;
  694.   }
  695.   .premium-5sur5-day-subtitle {
  696.     font-size: 0.8rem;
  697.   }
  698.   /* Menu sticky en bas - Override des styles précédents */
  699.   .premium-5sur5-actions-group {
  700.     position: fixed !important;
  701.     bottom: 0 !important;
  702.     left: 0 !important;
  703.     right: 0 !important;
  704.     width: 100% !important;
  705.     background: #ffffff !important;
  706.     border-top: 1px solid rgba(65, 162, 170, 0.15) !important;
  707.     padding: 12px 16px !important;
  708.     z-index: 1050 !important;
  709.     box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.08) !important;
  710.     justify-content: space-around !important;
  711.     gap: 0 !important;
  712.     margin: 0 !important;
  713.     margin-top: 0 !important;
  714.     /* Safe area pour iPhone avec encoche */
  715.     padding-bottom: max(12px, env(safe-area-inset-bottom)) !important;
  716.   }
  717.   .premium-5sur5-action-btn {
  718.     flex: 1 !important;
  719.     justify-content: center !important;
  720.     min-width: 0 !important;
  721.     padding: 10px 8px !important;
  722.     border-radius: 12px !important;
  723.     font-size: 12px !important;
  724.   }
  725.   .premium-5sur5-action-btn i {
  726.     font-size: 16px !important;
  727.   }
  728.   
  729.   .premium-5sur5-action-btn span:not(.premium-5sur5-badge):not(.favoris-empty-text) {
  730.     font-size: 12px !important;
  731.   }
  732.   
  733.   .premium-5sur5-badge {
  734.     font-size: 10px !important;
  735.     padding: 2px 5px !important;
  736.   }
  737.   /* Ajouter un padding-bottom au contenu pour éviter que le menu sticky ne cache le contenu */
  738.   #scrollTarget {
  739.     padding-bottom: 80px !important;
  740.   }
  741.   /* Section favoris responsive */
  742.   .favorites-ecommerce-view {
  743.     padding: 16px !important;
  744.   }
  745.   .favorites-header {
  746.     flex-direction: column !important;
  747.     gap: 12px !important;
  748.     align-items: flex-start !important;
  749.     padding: 16px !important;
  750.     margin-bottom: 16px !important;
  751.   }
  752.   .favorites-title {
  753.     width: 100% !important;
  754.   }
  755.   .favorites-title h3 {
  756.     font-size: 18px !important;
  757.     margin-bottom: 8px !important;
  758.   }
  759.   .favorites-count {
  760.     font-size: 13px !important;
  761.     padding: 4px 10px !important;
  762.   }
  763.   .btn-back-to-days {
  764.     width: 100% !important;
  765.     justify-content: center !important;
  766.     padding: 10px 16px !important;
  767.     font-size: 14px !important;
  768.   }
  769.   .favorites-content {
  770.     padding: 0 !important;
  771.   }
  772.   .favorites-grid {
  773.     display: grid !important;
  774.     grid-template-columns: repeat(2, 1fr) !important;
  775.     gap: 12px !important;
  776.     padding: 0 !important;
  777.   }
  778.   .favorites-grid .favorite-item,
  779.   .favorites-grid .photo-item {
  780.     width: 100% !important;
  781.     height: auto !important;
  782.     min-height: 150px !important;
  783.     aspect-ratio: 1 !important;
  784.     position: relative !important;
  785.     display: block !important;
  786.     border-radius: 12px !important;
  787.     overflow: hidden !important;
  788.     background: #f0f0f0 !important;
  789.   }
  790.   .favorites-grid .favorite-item img,
  791.   .favorites-grid .photo-item img {
  792.     width: 100% !important;
  793.     height: 100% !important;
  794.     min-height: 150px !important;
  795.     object-fit: cover !important;
  796.     display: block !important;
  797.     position: absolute !important;
  798.     top: 0 !important;
  799.     left: 0 !important;
  800.     border-radius: 12px !important;
  801.   }
  802.   .favorites-grid video {
  803.     width: 100% !important;
  804.     height: 100% !important;
  805.     min-height: 150px !important;
  806.     object-fit: cover !important;
  807.     display: block !important;
  808.     position: absolute !important;
  809.     top: 0 !important;
  810.     left: 0 !important;
  811.     border-radius: 12px !important;
  812.   }
  813.   /* CTA discret responsive */
  814.   .premium-cta-subtle {
  815.     padding: 12px 16px !important;
  816.     margin: 16px 0 !important;
  817.   }
  818.   .premium-cta-subtle > div:first-child > div:first-child {
  819.     width: 36px !important;
  820.     height: 36px !important;
  821.   }
  822.   .premium-cta-subtle > div:first-child > div:first-child i {
  823.     font-size: 16px !important;
  824.   }
  825.   .premium-cta-subtle > div:first-child > div:last-child p:first-child {
  826.     font-size: 13px !important;
  827.   }
  828.   .premium-cta-subtle > div:first-child > div:last-child p:last-child {
  829.     font-size: 11px !important;
  830.   }
  831.   .premium-cta-subtle:hover {
  832.     background: rgba(65, 162, 170, 0.08) !important;
  833.     border-color: rgba(65, 162, 170, 0.25) !important;
  834.     transform: translateX(2px) !important;
  835.   }
  836. }
  837. .date-navigation {
  838.   width: 100%;
  839. }
  840. /* Legacy compatibility */
  841. .date-card .card-content {
  842.   display: block;
  843.   text-align: center;
  844. }
  845. .date-card .media-list-horizontal {
  846.   margin: 0 !important;
  847.   padding: 0 !important;
  848.   display: flex;
  849.   gap: 6px;
  850.   justify-content: center;
  851. }
  852. .date-card .media-list-horizontal li {
  853.   font-size: 0.65rem !important;
  854. }
  855. /* Dots minimalistes pour les indicateurs de jour */
  856. .dot {
  857.   display: inline-block;
  858.   width: 6px;
  859.   height: 6px;
  860.   border-radius: 50%;
  861.   margin-left: 4px;
  862. }
  863. .dot.blue {
  864.   background: #41a2aa;
  865. }
  866. .dot.green {
  867.   background: #8ed081;
  868. }
  869. .dot.orange {
  870.   background: #ff9c57;
  871. }
  872. /* Dot multi-couleur pour aujourd'hui + premier/dernier jour */
  873. .status-dot.multi {
  874.   position: absolute;
  875.   top: 6px;
  876.   right: 6px;
  877.   width: 10px;
  878.   height: 10px;
  879.   border-radius: 50%;
  880.   background: linear-gradient(90deg, #41a2aa 50%, #ff9c57 50%);
  881.   z-index: 5;
  882. }
  883. /* Variante pour premier jour + aujourd'hui */
  884. .status-dot.multi.green-orange {
  885.   background: linear-gradient(90deg, #8ed081 50%, #ff9c57 50%);
  886. }
  887. /* ==================== TOASTS & ANIMATIONS ==================== */
  888. @keyframes slideUpFromGift {
  889.   0% {
  890.     opacity: 0;
  891.     transform: translateY(60px) scale(0.9);
  892.   }
  893.   100% {
  894.     opacity: 1;
  895.     transform: translateY(0) scale(1);
  896.   }
  897. }
  898. @keyframes fadeOut {
  899.   to {
  900.     opacity: 0;
  901.     transform: translateY(-20px);
  902.   }
  903. }
  904. /* ========== BANDEAU PROMO SÉJOUR TERMINÉ ========== */
  905. .promo-banner-termine {
  906.   background: linear-gradient(90deg, #d32f2f 0%, #e65100 50%, #ff5722 100%);
  907.   color: white;
  908.   padding: 0;
  909.   height: 38px;
  910.   width: 100%;
  911.   position: relative;
  912.   z-index: 100;
  913.   box-shadow: 0 2px 6px rgba(211, 47, 47, 0.2);
  914. }
  915. .promo-banner-content {
  916.   display: flex;
  917.   align-items: center;
  918.   justify-content: center;
  919.   gap: 10px;
  920.   max-width: 1200px;
  921.   margin: 0 auto;
  922.   padding: 0 12px;
  923.   height: 100%;
  924. }
  925. .promo-banner-icon {
  926.   font-size: 16px;
  927.   flex-shrink: 0;
  928. }
  929. .promo-banner-text {
  930.   font-size: 12px;
  931.   line-height: 1.2;
  932. }
  933. .promo-banner-text strong {
  934.   font-weight: 700;
  935.   display: inline;
  936. }
  937. .promo-days {
  938.   color: #fff;
  939.   font-weight: 700;
  940.   background: rgba(255,255,255,0.2);
  941.   padding: 1px 5px;
  942.   border-radius: 3px;
  943.   font-size: 11px;
  944. }
  945. .promo-banner-cta {
  946.   background: #fff;
  947.   color: #d32f2f;
  948.   font-weight: 600;
  949.   font-size: 11px;
  950.   padding: 4px 12px;
  951.   border-radius: 12px;
  952.   text-decoration: none;
  953.   white-space: nowrap;
  954.   transition: all 0.2s;
  955. }
  956. .promo-banner-cta:hover {
  957.   background: rgba(255,255,255,0.9);
  958.   transform: scale(1.02);
  959.   color: #d32f2f;
  960. }
  961. .promo-banner-close {
  962.   background: none;
  963.   border: none;
  964.   color: white;
  965.   font-size: 18px;
  966.   cursor: pointer;
  967.   opacity: 0.7;
  968.   padding: 0 2px;
  969.   margin-left: 2px;
  970.   line-height: 1;
  971. }
  972. .promo-banner-close:hover {
  973.   opacity: 1;
  974. }
  975. @media (max-width: 600px) {
  976.   .promo-banner-termine {
  977.     height: 34px;
  978.   }
  979.   .promo-banner-content {
  980.     gap: 6px;
  981.     padding: 0 8px;
  982.   }
  983.   .promo-banner-text {
  984.     font-size: 11px;
  985.   }
  986.   .promo-banner-icon {
  987.     font-size: 14px;
  988.   }
  989. }
  990. /* ========== SLIDER COMPACT PREMIUM ========== */
  991. #heroPromo {
  992.   margin: 0;
  993.   padding: 0;
  994.   width: 100%;
  995.   overflow: hidden;
  996.   background: #fff;
  997. }
  998. #heroPromo .divSliderModern {
  999.   width: 100% !important;
  1000.   margin: 0 !important;
  1001.   padding: 0 !important;
  1002. }
  1003. #imageSlider {
  1004.   max-height: 70px !important;
  1005.   height: 70px !important;
  1006.   overflow: hidden !important;
  1007.   width: 100% !important;
  1008.   margin: 0 !important;
  1009.   padding: 0 !important;
  1010. }
  1011. #imageSlider .splide__track {
  1012.   height: 70px !important;
  1013.   width: 100% !important;
  1014.   margin: 0 !important;
  1015.   padding: 0 !important;
  1016. }
  1017. #imageSlider .splide__list {
  1018.   width: 100% !important;
  1019.   margin: 0 !important;
  1020.   padding: 0 !important;
  1021. }
  1022. #imageSlider .splide__slide {
  1023.   height: 70px !important;
  1024.   width: 100% !important;
  1025.   margin: 0 !important;
  1026.   padding: 0 !important;
  1027. }
  1028. #imageSlider .splide__slide img {
  1029.   object-fit: contain !important;
  1030.   height: auto !important;
  1031.   width: auto !important;
  1032. }
  1033. .slider-content-compact {
  1034.   display: flex;
  1035.   align-items: center;
  1036.   justify-content: flex-start;
  1037.   gap: 0;
  1038.   background: linear-gradient(135deg, #f7fcfc 0%, #ffffff 30%, #fef8f5 100%);
  1039.   height: 70px !important;
  1040.   padding: 0;
  1041.   width: 100%;
  1042.   box-sizing: border-box;
  1043.   overflow: hidden;
  1044.   position: relative;
  1045. }
  1046. .imgslider-compact {
  1047.   height: 100% !important;
  1048.   width: 70% !important;
  1049.   object-fit: cover;
  1050.   flex-shrink: 0;
  1051.   display: block;
  1052. }
  1053. .slider-text-compact {
  1054.   display: flex;
  1055.   flex-direction: column;
  1056.   gap: 4px;
  1057.   align-items: flex-start;
  1058.   justify-content: center;
  1059.   flex: 1;
  1060.   min-width: 0;
  1061.   overflow: hidden;
  1062.   padding: 12px 24px;
  1063.   height: 100%;
  1064. }
  1065. .slider-title-compact {
  1066.   font-size: 14px;
  1067.   font-weight: 700;
  1068.   line-height: 1.3;
  1069.   color: inherit;
  1070.   white-space: normal;
  1071.   word-wrap: break-word;
  1072.   max-width: 100%;
  1073. }
  1074. .slider-subtitle-compact {
  1075.   font-size: 12px;
  1076.   font-weight: 500;
  1077.   line-height: 1.3;
  1078.   color: inherit;
  1079.   white-space: normal;
  1080.   word-wrap: break-word;
  1081.   max-width: 100%;
  1082. }
  1083. @media (max-width: 768px) {
  1084.   #imageSlider, #imageSlider .splide__track, #imageSlider .splide__slide, .slider-content-compact {
  1085.     height: 65px !important;
  1086.   }
  1087.   .slider-content-compact {
  1088.     gap: 0;
  1089.   }
  1090.   .imgslider-compact {
  1091.     height: 100% !important;
  1092.     width: 70% !important;
  1093.   }
  1094.   .slider-text-compact {
  1095.     padding: 10px 16px;
  1096.   }
  1097.   .slider-title-compact {
  1098.     font-size: 13px;
  1099.     line-height: 1.2;
  1100.   }
  1101.   .slider-subtitle-compact {
  1102.     font-size: 11px;
  1103.     line-height: 1.2;
  1104.   }
  1105. }
  1106. @media (max-width: 480px) {
  1107.   #imageSlider, #imageSlider .splide__track, #imageSlider .splide__slide, .slider-content-compact {
  1108.     height: 60px !important;
  1109.   }
  1110.   .slider-content-compact {
  1111.     gap: 0;
  1112.   }
  1113.   .imgslider-compact {
  1114.     height: 100% !important;
  1115.     width: 70% !important;
  1116.   }
  1117.   .slider-text-compact {
  1118.     padding: 8px 12px;
  1119.   }
  1120.   .slider-title-compact {
  1121.     font-size: 12px;
  1122.     line-height: 1.2;
  1123.   }
  1124.   .slider-subtitle-compact {
  1125.     font-size: 10px;
  1126.     line-height: 1.2;
  1127.   }
  1128. }
  1129. /* ========== EMPTY STATE ========== */
  1130. .stay-empty-wrapper {
  1131.   padding: 50px 20px 60px;
  1132.   display: flex;
  1133.   justify-content: center;
  1134. }
  1135. .stay-empty-card {
  1136.   max-width: 720px;
  1137.   width: 100%;
  1138.   background: #ffffff;
  1139.   border-radius: 24px;
  1140.   padding: 44px 40px 40px;
  1141.   box-shadow: 0 16px 50px rgba(5, 45, 60, 0.06);
  1142.   margin: 0 auto;
  1143. }
  1144. .stay-empty-title {
  1145.   font-size: 1.5rem;
  1146.   font-weight: 700;
  1147.   color: #1A1A2E;
  1148.   margin-bottom: 20px;
  1149.   text-align: center;
  1150. }
  1151. .stay-empty-intro {
  1152.   font-size: 0.98rem;
  1153.   line-height: 1.7;
  1154.   color: #5A6178;
  1155.   max-width: 580px;
  1156.   margin: 0 auto 32px;
  1157.   text-align: center;
  1158. }
  1159. /* Section "Ce qui va arriver" */
  1160. .stay-empty-expect {
  1161.   display: flex;
  1162.   gap: 36px;
  1163.   align-items: flex-start;
  1164.   margin-bottom: 28px;
  1165. }
  1166. .stay-empty-expect-content {
  1167.   flex: 1;
  1168. }
  1169. .stay-empty-expect-label {
  1170.   font-size: 0.92rem;
  1171.   font-weight: 600;
  1172.   color: #1A1A2E;
  1173.   margin-bottom: 16px;
  1174. }
  1175. .stay-empty-list {
  1176.   list-style: none;
  1177.   padding: 0;
  1178.   margin: 0;
  1179. }
  1180. .stay-empty-list li {
  1181.   display: flex;
  1182.   align-items: flex-start;
  1183.   gap: 12px;
  1184.   padding: 10px 0;
  1185.   font-size: 0.92rem;
  1186.   color: #4B5563;
  1187.   line-height: 1.5;
  1188. }
  1189. .stay-empty-list-icon {
  1190.   width: 28px;
  1191.   height: 28px;
  1192.   border-radius: 8px;
  1193.   display: flex;
  1194.   align-items: center;
  1195.   justify-content: center;
  1196.   flex-shrink: 0;
  1197.   margin-top: 1px;
  1198. }
  1199. .stay-empty-list-icon svg {
  1200.   width: 14px;
  1201.   height: 14px;
  1202. }
  1203. .stay-empty-list-icon--check {
  1204.   background: rgba(65, 162, 170, 0.12);
  1205.   color: #41A2AA;
  1206. }
  1207. .stay-empty-list-icon--photos {
  1208.   background: rgba(240, 158, 122, 0.12);
  1209.   color: #E8865E;
  1210. }
  1211. .stay-empty-list-icon--vocal {
  1212.   background: rgba(155, 139, 244, 0.12);
  1213.   color: #7B6BE0;
  1214. }
  1215. /* Illustration compacte */
  1216. .stay-empty-illustration {
  1217.   position: relative;
  1218.   width: 180px;
  1219.   height: 200px;
  1220.   flex-shrink: 0;
  1221. }
  1222. .stay-empty-phone {
  1223.   position: absolute;
  1224.   left: 50%;
  1225.   top: 50%;
  1226.   transform: translate(-50%, -50%);
  1227.   width: 90px;
  1228.   filter: drop-shadow(0 8px 20px rgba(0,0,0,0.1));
  1229.   animation: phoneFloat 4s ease-in-out infinite;
  1230. }
  1231. @keyframes phoneFloat {
  1232.   0%, 100% { transform: translate(-50%, -50%) translateY(0); }
  1233.   50% { transform: translate(-50%, -50%) translateY(-5px); }
  1234. }
  1235. .stay-empty-bubble {
  1236.   position: absolute;
  1237.   display: flex;
  1238.   align-items: center;
  1239.   gap: 6px;
  1240.   padding: 6px 10px;
  1241.   background: #ffffff;
  1242.   border-radius: 10px;
  1243.   box-shadow: 0 4px 14px rgba(0,0,0,0.08);
  1244.   font-size: 0.72rem;
  1245.   font-weight: 600;
  1246.   color: #1A1A2E;
  1247.   white-space: nowrap;
  1248.   opacity: 0;
  1249.   animation: bubbleFadeIn 0.5s ease forwards;
  1250. }
  1251. .stay-empty-bubble-icon {
  1252.   width: 18px;
  1253.   height: 18px;
  1254.   border-radius: 5px;
  1255.   display: flex;
  1256.   align-items: center;
  1257.   justify-content: center;
  1258.   flex-shrink: 0;
  1259. }
  1260. .stay-empty-bubble-icon svg {
  1261.   width: 10px;
  1262.   height: 10px;
  1263. }
  1264. .stay-empty-bubble--arrived {
  1265.   top: 20px;
  1266.   left: -10px;
  1267.   animation-delay: 0.4s;
  1268. }
  1269. .stay-empty-bubble--arrived .stay-empty-bubble-icon {
  1270.   background: linear-gradient(135deg, #41A2AA, #359BA3);
  1271.   color: white;
  1272. }
  1273. .stay-empty-bubble--photos {
  1274.   top: 70px;
  1275.   right: -5px;
  1276.   animation-delay: 0.9s;
  1277. }
  1278. .stay-empty-bubble--photos .stay-empty-bubble-icon {
  1279.   background: linear-gradient(135deg, #F09E7A, #E8865E);
  1280.   color: white;
  1281. }
  1282. @keyframes bubbleFadeIn {
  1283.   0% { opacity: 0; transform: translateY(6px) scale(0.95); }
  1284.   100% { opacity: 1; transform: translateY(0) scale(1); }
  1285. }
  1286. .stay-empty-illustration-label {
  1287.   position: absolute;
  1288.   bottom: 0;
  1289.   left: 50%;
  1290.   transform: translateX(-50%);
  1291.   font-size: 0.7rem;
  1292.   color: #8E95A9;
  1293.   white-space: nowrap;
  1294.   text-align: center;
  1295. }
  1296. /* Note rassurante */
  1297. .stay-empty-reassurance {
  1298.   background: #F8FAFB;
  1299.   border-radius: 12px;
  1300.   padding: 14px 20px;
  1301.   margin-bottom: 24px;
  1302.   display: flex;
  1303.   align-items: flex-start;
  1304.   gap: 12px;
  1305. }
  1306. .stay-empty-reassurance-icon {
  1307.   width: 20px;
  1308.   height: 20px;
  1309.   color: #41A2AA;
  1310.   flex-shrink: 0;
  1311.   margin-top: 2px;
  1312. }
  1313. .stay-empty-reassurance p {
  1314.   margin: 0;
  1315.   font-size: 0.85rem;
  1316.   color: #5A6178;
  1317.   line-height: 1.6;
  1318. }
  1319. /* CTA */
  1320. .stay-empty-cta {
  1321.   display: inline-flex;
  1322.   align-items: center;
  1323.   gap: 8px;
  1324.   padding: 11px 22px;
  1325.   font-size: 0.88rem;
  1326.   font-weight: 600;
  1327.   color: #41A2AA;
  1328.   background: transparent;
  1329.   border: 1.5px solid #E5E9F0;
  1330.   border-radius: 10px;
  1331.   text-decoration: none;
  1332.   transition: all 0.2s ease;
  1333. }
  1334. .stay-empty-cta:hover {
  1335.   background: #F0F9FA;
  1336.   border-color: #41A2AA;
  1337.   color: #359BA3;
  1338.   text-decoration: none;
  1339. }
  1340. .stay-empty-cta svg {
  1341.   width: 16px;
  1342.   height: 16px;
  1343. }
  1344. .stay-empty-footer {
  1345.   text-align: center;
  1346. }
  1347. /* Responsive */
  1348. @media (max-width: 700px) {
  1349.   .stay-empty-card {
  1350.     padding: 32px 24px 32px;
  1351.   }
  1352.   .stay-empty-title {
  1353.     font-size: 1.3rem;
  1354.   }
  1355.   .stay-empty-expect {
  1356.     flex-direction: column;
  1357.     gap: 24px;
  1358.   }
  1359.   .stay-empty-illustration {
  1360.     width: 100%;
  1361.     height: 160px;
  1362.     order: -1;
  1363.   }
  1364.   .stay-empty-phone {
  1365.     width: 80px;
  1366.   }
  1367.   .stay-empty-bubble {
  1368.     font-size: 0.68rem;
  1369.     padding: 5px 8px;
  1370.   }
  1371.   .stay-empty-bubble--arrived {
  1372.     left: calc(50% - 85px);
  1373.   }
  1374.   .stay-empty-bubble--photos {
  1375.     right: calc(50% - 85px);
  1376.   }
  1377.   .stay-empty-reassurance {
  1378.     flex-direction: column;
  1379.     gap: 8px;
  1380.     text-align: center;
  1381.   }
  1382.   .stay-empty-reassurance-icon {
  1383.     margin: 0 auto;
  1384.   }
  1385. }
  1386. </style>
  1387. <script>
  1388.   window.__FEATURE_PARENT_CONVERSION__ = true;
  1389.   // Configuration runtime pour le système B2C
  1390.   {# Calcul des dates pour le JS - on calcule tout en Twig #}
  1391.   {% set nowTimestamp = 'now'|date('U') %}
  1392.   {% set debutTimestamp = sejour.dateDebutSejour|date('U') %}
  1393.   {% set finTimestamp = sejour.dateFinSejour|date('U') %}
  1394.   {% set finCodeDateJS = sejour.dateFinCode is defined and sejour.dateFinCode is not null ? sejour.dateFinCode : sejour.dateFinSejour|date_modify('+42 days') %}
  1395.   {% set closeTimestamp = finCodeDateJS|date('U') %}
  1396.   {% set daysSinceStartCalc = ((nowTimestamp - debutTimestamp) / 86400)|round(0, 'floor') %}
  1397.   {% set daysUntilEndCalc = ((finTimestamp - nowTimestamp) / 86400)|round(0, 'ceil') %}
  1398.   {% set daysAfterEndCalc = ((nowTimestamp - finTimestamp) / 86400)|round(0, 'floor') %}
  1399.   {% set daysUntilCloseCalc = ((closeTimestamp - nowTimestamp) / 86400)|round(0, 'ceil') %}
  1400.   
  1401.   window.PARENT_CONVERSION_CONFIG = {
  1402.     user: {
  1403.       prenom: "{{ app.user.prenom|default('Parent')|e('js') }}"
  1404.     },
  1405.     sejour: {
  1406.       id: {{ sejour.id }},
  1407.       dateDebut: "{{ sejour.dateDebutSejour|date('Y-m-d') }}",
  1408.       dateFin: "{{ sejour.dateFinSejour|date('Y-m-d') }}",
  1409.       dateFinCode: "{{ sejour.dateFinCode ? sejour.dateFinCode|date('Y-m-d') : '' }}",
  1410.       isTermine: {{ (sejour.dateFinSejour < date()) ? 'true' : 'false' }}
  1411.     },
  1412.     dates: {
  1413.       aujourdhui: "{{ 'now'|date('Y-m-d') }}",
  1414.       isFirstDay: {{ (sejour.dateDebutSejour|date('Y-m-d') == 'now'|date('Y-m-d')) ? 'true' : 'false' }},
  1415.       isWithin: {{ (sejour.dateDebutSejour <= date() and sejour.dateFinSejour >= date()) ? 'true' : 'false' }},
  1416.       daysSinceStart: {{ daysSinceStartCalc|default(0) }},
  1417.       daysUntilEnd: {{ daysUntilEndCalc|default(0) }},
  1418.       daysAfterEnd: {{ daysAfterEndCalc|default(0) }},
  1419.       daysUntilClose: {{ daysUntilCloseCalc|default(0) }}
  1420.     },
  1421.     stats: {
  1422.       totalDays: {{ sejour.dateFinSejour.diff(sejour.dateDebutSejour).days }},
  1423.       totalPhotos: {{ attachementsCount|default(0) }},
  1424.       totalMessages: {{ nbmessages|default(0) }},
  1425.       todayPhotos: {{ photosDuJour|default(0) }}
  1426.     },
  1427.     parent: {
  1428.       favorites: {{ nblikes|default(0) }},
  1429.       cartCount: 0,
  1430.       lastVisitAt: "{{ 'now'|date('Y-m-d H:i:s') }}"
  1431.     }
  1432.   };
  1433.   // Catalogue produits (routes réelles 5sur5)
  1434.   window.PARENT_PRODUCT_CATALOG = {
  1435.     digi10: { title:"Pack numérique 10 photos", badge:"Petit budget", url:"{{ path('boutique5sur5') }}" },
  1436.     digi25: { title:"Pack numérique 25 photos", badge:"Immédiat", url:"{{ path('boutique5sur5') }}" },
  1437.     retro12: { title:"Pochette rétro 12 tirages", badge:"Cadeau", url:"{{ path('boutique5sur5') }}" },
  1438.     prints20: { title:"Pochette 20 tirages", badge:"Best seller", url:"{{ path('boutique5sur5') }}" },
  1439.     album24: { title:"Mini-album 24 pages", badge:"Parfait début", url:"{{ path('Album_du_Sejour') }}" },
  1440.     album48: { title:"Album Premium 48 pages", badge:"Meilleure vente", url:"{{ path('Album_du_Sejour') }}" },
  1441.     mix10Poster: { title:"Pack mixte 10 tirages + poster", badge:"Combo", url:"{{ path('boutique5sur5') }}" },
  1442.     bundleAlbumPrints: { title:"Album + tirages (-15%)", badge:"Éco", url:"{{ path('boutique5sur5') }}" }
  1443.   };
  1444. </script>
  1445. <style>
  1446. .btn-close {
  1447.   font-size: 1.2rem;
  1448.   border: none;
  1449.   background: transparent;
  1450. }
  1451. .slide img {
  1452.   max-width: 100%;
  1453.   max-height: 100%;
  1454.   object-fit: contain;
  1455.   border-radius: 8px;
  1456.   box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
  1457.   transition: transform 0.3s ease;
  1458.   cursor: pointer;
  1459. }
  1460. .slide img.zoomed {
  1461.   transform: scale(1.5);
  1462.   cursor: zoom-in;
  1463. }
  1464. /* === Badges discrets des cartes jours (style accompagnateur avec Bootstrap) === */
  1465. /* Les badges utilisent directement les classes Bootstrap: badge, bg-success, bg-info, position-absolute */
  1466. /* === Affichage des dates (style accompagnateur exact) === */
  1467. .container--gallery.modern {
  1468.   background: #ffffff;
  1469.   border-radius: 32px;
  1470.   padding: 10px;
  1471.   box-shadow: 0 30px 70px rgba(11, 36, 71, 0.08);
  1472.   border: 1px solid rgba(65, 162, 170, 0.08);
  1473.   min-height:1000px;
  1474. }
  1475. @media (max-width: 768px) {
  1476.   .container--gallery.modern {
  1477.     padding: 24px;
  1478.     min-height:500px;
  1479.   }
  1480. }
  1481. .pro-journal {
  1482.   background: linear-gradient(145deg, #f8fbff 0%, #ffffff 100%);
  1483.   border-radius: 24px;
  1484.   padding: 28px;
  1485.   margin-bottom: 28px;
  1486.   border: 1px solid rgba(65, 162, 170, 0.08);
  1487.   box-shadow: 0 18px 45px rgba(11, 36, 71, 0.05);
  1488. }
  1489. .pro-entry-header {
  1490.   display: flex;
  1491.   align-items: center;
  1492.   justify-content: space-between;
  1493.   flex-wrap: wrap;
  1494.   gap: 18px;
  1495.   margin-bottom: 20px;
  1496. }
  1497. .pro-entry-header .date-chip {
  1498.   display: inline-flex;
  1499.   align-items: center;
  1500.   gap: 10px;
  1501.   padding: 10px 18px;
  1502.   border-radius: 999px;
  1503.   background: rgba(65, 162, 170, 0.08);
  1504.   color: #1f2933;
  1505.   font-weight: 600;
  1506.   font-size: 0.95rem;
  1507. }
  1508. .gallery-meta-chips {
  1509.   display: flex;
  1510.   flex-wrap: wrap;
  1511.   gap: 10px;
  1512.   align-items: center;
  1513. }
  1514. .gallery-meta-chips .filter-badge {
  1515.   background: #ffffff;
  1516.   border: 1px solid rgba(15, 40, 77, 0.08);
  1517.   border-radius: 999px;
  1518.   padding: 8px 14px;
  1519.   font-size: 0.85rem;
  1520.   font-weight: 600;
  1521.   color: #405064;
  1522.   display: inline-flex;
  1523.   align-items: center;
  1524.   gap: 6px;
  1525.   transition: all 0.2s ease;
  1526. }
  1527. .gallery-meta-chips .filter-badge.active {
  1528.   background: #41a2aa;
  1529.   color: #fff;
  1530.   border-color: #41a2aa;
  1531.   box-shadow: 0 8px 20px rgba(65, 162, 170, 0.25);
  1532. }
  1533. .gallery-grid {
  1534.   width: 100%;
  1535.   display: grid !important;
  1536.   grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  1537.   gap: 18px;
  1538. }
  1539. .gallery-grid .column {
  1540.   margin: 0 !important;
  1541. }
  1542. .gallery-grid .photo-item,
  1543. .gallery-grid .video-item,
  1544. .gallery-grid .audio-message-item {
  1545.   border-radius: 18px;
  1546.   background: #fff;
  1547.   border: 1px solid rgba(15, 40, 77, 0.08);
  1548.   box-shadow: 0 12px 30px rgba(11, 36, 71, 0.08);
  1549. }
  1550. .gallery-grid .photo-item img {
  1551.   border-radius: 16px;
  1552.   height: 220px;
  1553.   object-fit: cover;
  1554. }
  1555. .gallery-grid .video-item video,
  1556. .gallery-grid .audio-message-item audio {
  1557.   border-radius: 12px;
  1558. }
  1559. </style>
  1560. {% endblock %} {% set pageMenu = app.session.get('pageMenu') %} {% block Content
  1561. %}
  1562. <div class="main-content">
  1563.   <div class="row no-margin">
  1564.     <!-- Bouton cadeau attractif e-commerce -->
  1565.     {% set showGiftButton = (likes|default([]))|length > 0 %}
  1566.     <div
  1567.       class="gift-button"
  1568.       id="gift-button-trigger"
  1569.       data-has-favorites="{{ showGiftButton ? '1' : '0' }}"
  1570.       style="cursor: pointer; {{ showGiftButton ? '' : 'display:none;' }}"
  1571.     >
  1572.       <i id="gift-icon-payment" class="bi bi-gift gift-pulse"></i>
  1573.       <label
  1574.         id="giftCount"
  1575.         class="labelGiftCount gift-count-bounce"
  1576.         style="background-color: #f56040"
  1577.       >
  1578.         {{ (likes|default([]))|length }}
  1579.       </label>
  1580.       <!-- Message flottant attractif -->
  1581.       <div class="gift-tooltip" id="giftTooltip">
  1582.         <div class="gift-tooltip-content">
  1583.           <div class="gift-tooltip-header">
  1584.             <i class="bi bi-clock-history"></i>
  1585.             <strong>Offre limitée !</strong>
  1586.           </div>
  1587.           <p>Commandez vite ! Les photos seront disponibles seulement pendant <strong>6 semaines</strong> après la fin du séjour.</p>
  1588.           <div class="gift-tooltip-cta">
  1589.             <i class="bi bi-lightning-charge"></i>
  1590.             Commander maintenant
  1591.           </div>
  1592.         </div>
  1593.       </div>
  1594.     </div>
  1595.     <!-- Sidebar E-commerce -->
  1596.     <div id="ecommerce-sidebar">
  1597.       <div class="ecommerce-header">
  1598.         <h2 class="ecommerce-title" id="ecommerce-title">
  1599.           🎉 Vos souvenirs favoris sont prêts !
  1600.         </h2>
  1601.         <p class="ecommerce-subtitle" id="ecommerce-subtitle">
  1602.           {{ (likes|default([]))|length }} photos sélectionnées avec amour ❤️
  1603.         </p>
  1604.         <button class="ecommerce-close" onclick="closeEcommerceSidebar()">
  1605.           <i class="bi bi-x"></i>
  1606.         </button>
  1607.       </div>
  1608.       <div class="ecommerce-content">
  1609.         <!-- Bannière d'urgence -->
  1610.         <div class="urgency-banner" id="urgency-banner">
  1611.           <div>
  1612.             <i class="bi bi-clock-history"></i>
  1613.             <strong>⏳ Commandez vite !</strong>
  1614.           </div>
  1615.           <div> Les photos seront disponibles seulement <strong>6 semaines</strong> après la fin du séjour</div>
  1616.           <div class="countdown" id="countdown-timer" style="display: none;">
  1617.             Plus que <span id="days-left">0</span> jours !
  1618.           </div>
  1619.         </div>
  1620.         <!-- Produits recommandés -->
  1621.         <div id="products-container">
  1622.           <!-- Album Photo -->
  1623.           <div class="product-card popular" id="album-card">
  1624.             <div class="product-header">
  1625.               <div class="product-icon">
  1626.                 <img src="/images/produit/Album5sur5-3.jpg" alt="Album Photo Premium" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px;">
  1627.               </div>
  1628.               <div class="product-info">
  1629.                 <h4>Album Photo Premium</h4>
  1630.                 <div class="product-pricing">
  1631.                   <span class="current-price">24,90€</span>
  1632.                   <span class="original-price">28,90€</span>
  1633.                   <span class="savings">-14%</span>
  1634.                 </div>
  1635.               </div>
  1636.               <div class="product-favorites-indicator" title="Photos favorites sélectionnées">
  1637.                 <i class="bi bi-heart-fill" style="color: #f56040"></i>
  1638.                 <span id="album-fav-count">{{ (likes|default([]))|length }}</span>
  1639.               </div>
  1640.             </div>
  1641.             <p class="product-description">
  1642.               Album photo personnalisé avec vos <strong id="album-count">{{ (likes|default([]))|length }}</strong> photos favorites. Papier de qualité supérieure, reliure rigide.
  1643.             </p>
  1644.             <button class="product-cta" onclick="orderProduct('album')" 
  1645.                     title="Commander l'album photo avec vos photos favorites">
  1646.               <i class="bi bi-cart-plus"></i>
  1647.               Commander l'album
  1648.             </button>
  1649.           </div>
  1650.           <!-- Pack Numérique -->
  1651.           <div class="product-card" id="digital-card">
  1652.             <div class="product-header">
  1653.               <div class="product-icon">
  1654.                 <img src="/images/produit/photoNumerique.jpg" alt="Pack Numérique HD" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px;">
  1655.               </div>
  1656.               <div class="product-info">
  1657.                 <h4>Pack Numérique HD</h4>
  1658.                 <p class="price">À partir de 3,90€</p>
  1659.               </div>
  1660.                 <div class="product-favorites-indicator" title="Photos favorites sélectionnées">
  1661.                   <i class="bi bi-heart-fill" style="color: #f56040"></i>
  1662.                   <span id="digital-fav-count">{{ (likes|default([]))|length }}</span>
  1663.                 </div>
  1664.             </div>
  1665.             <p class="product-description">
  1666.               Téléchargement immédiat de vos <strong id="digital-count">{{ (likes|default([]))|length }}</strong> photos en haute définition. Qualité professionnelle garantie.
  1667.             </p>
  1668.             <button class="product-cta" onclick="orderProduct('digital')" 
  1669.                     title="Télécharger vos photos en haute définition">
  1670.               <i class="bi bi-download"></i>
  1671.               Télécharger HD
  1672.             </button>
  1673.           </div>
  1674.           <!-- Pochette Tirages -->
  1675.           <div class="product-card" id="prints-card" style="display: none;">
  1676.             <div class="product-header">
  1677.               <div class="product-icon">
  1678.                 <i class="bi bi-images"></i>
  1679.               </div>
  1680.               <div class="product-info">
  1681.                 <h4>Pochette Tirages</h4>
  1682.                 <p class="price">À partir de 9,90€</p>
  1683.               </div>
  1684.               <div class="product-favorites-indicator" title="Photos favorites sélectionnées">
  1685.                 <i class="bi bi-heart-fill" style="color: #f56040"></i>
  1686.                 <span>{{ (likes|default([]))|length }}</span>
  1687.               </div>
  1688.             </div>
  1689.             <p class="product-description">
  1690.               <strong id="prints-count">12</strong> tirages photo 10x15cm de vos favoris. Papier brillant professionnel, envoi sous 48h.
  1691.             </p>
  1692.             <button class="product-cta" onclick="orderProduct('prints')" 
  1693.                     title="Commander des tirages photo de vos favoris">
  1694.               <i class="bi bi-printer"></i>
  1695.               Commander les tirages
  1696.             </button>
  1697.           </div>
  1698.         </div>
  1699.         <!-- Réassurance -->
  1700.         <div class="reassurance">
  1701.           <i class="bi bi-shield-check" style="color: #10b981; margin-right: 8px;"></i>
  1702.           <strong>Imprimé en France – satisfaction garantie ✅</strong>
  1703.         </div>
  1704.         
  1705.       </div>
  1706.     </div>
  1707.     <div class="selection-popover" id="selectionPopover">
  1708.       <h4>Votre sélection</h4>
  1709.       <p>Tirages : {{ (likes|default([]))|length }} / 12</p>
  1710.       <p>Numériques : {{ (likes|default([]))|length }} / 15</p>
  1711.       <p>Album : {{ (likes|default([]))|length }} / 20</p>
  1712.       <button class="finalize-button" onclick="openEcommerceSidebar()" 
  1713.               title="Voir les produits disponibles avec vos favoris">
  1714.         <i class="bi bi-gift"></i>
  1715.         Finaliser ma commande
  1716.       </button>
  1717.     </div>
  1718.     
  1719.     <style>
  1720.       /* Amélioration des boutons de produits */
  1721.       .product-cta {
  1722.         background: #3a8d95;
  1723.         border: none;
  1724.         border-radius: 25px;
  1725.         color: white;
  1726.         padding: 12px 24px;
  1727.         font-weight: 600;
  1728.         cursor: pointer;
  1729.         transition: all 0.3s ease;
  1730.         display: flex;
  1731.         align-items: center;
  1732.         gap: 8px;
  1733.         width: 100%;
  1734.         justify-content: center;
  1735.         margin-top: 10px;
  1736.       }
  1737.       
  1738.       .product-cta:hover {
  1739.         transform: translateY(-2px);
  1740.         box-shadow: 0 5px 15px rgba(58, 141, 149, 0.4);
  1741.         background: #2f7a82;
  1742.       }
  1743.       
  1744.       .product-cta:active {
  1745.         transform: translateY(0);
  1746.       }
  1747.       
  1748.       .product-cta:disabled {
  1749.         background: #ccc;
  1750.         cursor: not-allowed;
  1751.         transform: none;
  1752.         box-shadow: none;
  1753.       }
  1754.       
  1755.       .finalize-button {
  1756.         background: linear-gradient(135deg, #e91e63 0%, #ad1457 100%);
  1757.         border: none;
  1758.         border-radius: 25px;
  1759.         color: white;
  1760.         padding: 12px 24px;
  1761.         font-weight: 600;
  1762.         cursor: pointer;
  1763.         transition: all 0.3s ease;
  1764.         display: flex;
  1765.         align-items: center;
  1766.         gap: 8px;
  1767.         width: 100%;
  1768.         justify-content: center;
  1769.         margin-top: 15px;
  1770.       }
  1771.       
  1772.       .finalize-button:hover {
  1773.         transform: translateY(-2px);
  1774.         box-shadow: 0 5px 15px rgba(233, 30, 99, 0.4);
  1775.         background: linear-gradient(135deg, #d81b60 0%, #9c1450 100%);
  1776.       }
  1777.       
  1778.       /* Amélioration des cartes de produits */
  1779.       .product-card {
  1780.         border-radius: 12px;
  1781.         box-shadow: 0 4px 15px rgba(0,0,0,0.1);
  1782.         transition: all 0.3s ease;
  1783.         margin-bottom: 20px;
  1784.         overflow: hidden;
  1785.       }
  1786.       
  1787.       .product-card:hover {
  1788.         transform: translateY(-5px);
  1789.         box-shadow: 0 8px 25px rgba(0,0,0,0.15);
  1790.       }
  1791.       
  1792.       .product-header {
  1793.         display: flex;
  1794.         align-items: center;
  1795.         gap: 15px;
  1796.         padding: 20px;
  1797.         background: #f8f9fa;
  1798.       }
  1799.       
  1800.       .product-icon {
  1801.         width: 60px;
  1802.         height: 60px;
  1803.         border-radius: 8px;
  1804.         overflow: hidden;
  1805.         flex-shrink: 0;
  1806.       }
  1807.       
  1808.       .product-info h4 {
  1809.         margin: 0 0 5px 0;
  1810.         color: #333;
  1811.         font-weight: 600;
  1812.       }
  1813.       
  1814.       .product-pricing {
  1815.         display: flex;
  1816.         align-items: center;
  1817.         gap: 10px;
  1818.         margin: 5px 0;
  1819.       }
  1820.       
  1821.       .current-price {
  1822.         font-size: 1.2rem;
  1823.         font-weight: 700;
  1824.         color: #e91e63;
  1825.       }
  1826.       
  1827.       .original-price {
  1828.         text-decoration: line-through;
  1829.         color: #666;
  1830.         font-size: 0.9rem;
  1831.       }
  1832.       
  1833.       .savings {
  1834.         background: #4caf50;
  1835.         color: white;
  1836.         padding: 2px 8px;
  1837.         border-radius: 12px;
  1838.         font-size: 0.8rem;
  1839.         font-weight: 600;
  1840.       }
  1841.       
  1842.       .product-description {
  1843.         padding: 0 20px 20px 20px;
  1844.         color: #666;
  1845.         line-height: 1.5;
  1846.       }
  1847.       
  1848.       .product-favorites-indicator {
  1849.         display: flex;
  1850.         align-items: center;
  1851.         gap: 5px;
  1852.         background: rgba(245, 96, 64, 0.1);
  1853.         padding: 5px 10px;
  1854.         border-radius: 15px;
  1855.         font-size: 0.9rem;
  1856.         color: #f56040;
  1857.         font-weight: 600;
  1858.       }
  1859.     </style>
  1860.   </div>
  1861.   {# Zone promo - Masquée si pas de photos #}
  1862.   {% if attachementsCount|default(0) > 0 %}
  1863.   
  1864.   {# Calculer si séjour terminé #}
  1865.   {% set isSejourTermine = sejour.dateFinSejour < date() %}
  1866.   {% set finCodeDate = sejour.dateFinCode is defined and sejour.dateFinCode is not null ? sejour.dateFinCode : sejour.dateFinSejour|date_modify('+42 days') %}
  1867.   {% set daysUntilClose = ((finCodeDate|date('U') - 'now'|date('U')) / 86400)|round(0, 'ceil') %}
  1868.   
  1869.   <section id="heroPromo" style="margin: 0; padding: 0;">
  1870.     {# BANDEAU SÉJOUR TERMINÉ - S'affiche pour tout séjour passé #}
  1871.     {% if isSejourTermine %}
  1872.     <div class="promo-banner-termine" id="promoBannerTermine">
  1873.       <div class="promo-banner-content">
  1874.         <span class="promo-banner-icon">🎬</span>
  1875.         <div class="promo-banner-text">
  1876.           {% if daysUntilClose > 0 %}
  1877.             <strong>Séjour terminé — Dernière chance !</strong>
  1878.             Plus que <span class="promo-days">{{ daysUntilClose }}</span> jour{{ daysUntilClose > 1 ? 's' : '' }} pour commander vos souvenirs.
  1879.           {% else %}
  1880.             <strong>Séjour terminé</strong>
  1881.             Créez vos souvenirs : albums, tirages, photos numériques...
  1882.           {% endif %}
  1883.         </div>
  1884.         <a href="{{ path('boutique5sur5') }}" class="promo-banner-cta">Commander</a>
  1885.         <button class="promo-banner-close" onclick="this.parentElement.parentElement.style.display='none'" aria-label="Fermer">×</button>
  1886.       </div>
  1887.     </div>
  1888.     {% else %}
  1889.     {# SLIDER PROMO PREMIUM - Séjour en cours #}
  1890.     <div class="divSliderModern" style="margin: 0; padding: 0; width: 100%; overflow: hidden;">
  1891.       <div class="splide no-padding no-margin" id="imageSlider" style="width: 100%;">
  1892.         <div class="splide__track" style="width: 100%;">
  1893.           <ul class="splide__list" style="width: 100%;">
  1894.             <li class="splide__slide" style="width: 100%;">
  1895.               <div class="slider-content-compact">
  1896.                 <img src="{{ asset('/images/imgSliderEmpty2.png') }}" class="imgslider-compact" alt="Produits souvenirs" />
  1897.                 <div class="slider-text-compact">
  1898.                   <span class="slider-title-compact" style="color: #41a2aa">Ajoutez vos favoris</span>
  1899.                   <span class="slider-subtitle-compact" style="color: #f09e7a">et créez vos souvenirs personnalisés !</span>
  1900.                 </div>
  1901.             </div>
  1902.           </li>
  1903.             <li class="splide__slide" style="width: 100%;">
  1904.               <div class="slider-content-compact">
  1905.                 <img src="{{ asset('/images/imgSliderEmpty1.png') }}" class="imgslider-compact" alt="Livre du séjour" />
  1906.                 <div class="slider-text-compact">
  1907.                   <span class="slider-title-compact" style="color: #f09e7a">Pensez à commander le livre</span>
  1908.                   <span class="slider-subtitle-compact" style="color: #41a2aa">et offrez le plus beau des cadeaux !</span>
  1909.               </div>
  1910.             </div>
  1911.           </li>
  1912.         </ul>
  1913.       </div>
  1914.     </div>
  1915.   </div>
  1916.     {% endif %}
  1917.   </section>
  1918.   {% endif %}
  1919.   {# Fin heroPromo #}
  1920.   
  1921.   <!-- Section de contenu à atteindre après le scroll -->
  1922.   <div
  1923.     class="no-margin"
  1924.     id="scrollTarget"
  1925.     style="width: 100%; background: #ffffff; padding-top: 16px"
  1926.   >
  1927.     <div class="no-margin" id="scrollTarget" style="width: 100%">
  1928.       <!-- Header premium 5sur5 -->
  1929.       <header class="premium-5sur5-day-header" style="padding: 5px 25px; background: #ffffff; border-bottom: 1px solid rgba(65, 162, 170, 0.1); margin-bottom: -15px;">
  1930.         <div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
  1931.           <div class="flex-grow-1">
  1932.             <h5 class="premium-5sur5-day-title" style="margin: 0 0 6px 0; font-size: 1.3rem; font-weight: 700; color: #1a1a1a;">
  1933.               {{ sejour.themSejour }}
  1934.             </h5>
  1935.             <p class="premium-5sur5-day-subtitle" style="margin: 0; font-size: 0.9rem; color: #6b7280; font-weight: 500;">
  1936.                    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."}) }}
  1937.           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."}) }}
  1938.               · Code : <span style="color: #41a2aa; font-weight: 600;">{{sejour.codeSejour}}</span>
  1939.             </p>
  1940.         </div>
  1941.           <!-- 🎯 MEDIA TOOLBAR PREMIUM - Style 5sur5 -->
  1942.           <div class="premium-5sur5-actions-group" style="gap: 8px;">
  1943.             <!-- Tout -->
  1944.           <button class="premium-5sur5-action-btn mtb-btn {% if attachementsCount|default(0) > 0 %}active{% endif %}" data-filter="all" title="Tout">
  1945.             <i class="bi bi-grid-3x3-gap-fill"></i>
  1946.             <span>Tout</span>
  1947.             <span class="premium-5sur5-badge" style="background: rgba(65, 162, 170, 0.1); color: #41A2AA;">{{ attachementsCount|default(0) }}</span>
  1948.             </button>
  1949.           <!-- Favoris -->
  1950.           <button 
  1951.             class="premium-5sur5-action-btn mtb-btn {% if nblikes|default(0) == 0 %}favoris-empty{% endif %}" 
  1952.             data-filter="favoris" 
  1953.             title="{% if nblikes|default(0) == 0 %}Pas de favoris pour l'instant{% else %}Mes favoris{% endif %}"
  1954.             {% if nblikes|default(0) == 0 %}disabled{% endif %}
  1955.           >
  1956.             <i class="bi bi-heart-fill" style="color:{% if nblikes|default(0) == 0 %}#cbd5e1{% else %}#f56040{% endif %};"></i>
  1957.             <span>Favoris</span>
  1958.             <span class="favoris-count" id="mesFavCount" data-bind="fav" data-empty-text="Pas de favoris">
  1959.               {% if nblikes|default(0) == 0 %}
  1960.                 <span class="favoris-empty-text" style="color: #94a3b8; font-style: italic; font-size: 11px;">Pas de favoris</span>
  1961.               {% else %}
  1962.                 <span class="premium-5sur5-badge" style="background: rgba(245, 96, 64, 0.1); color: #F56040;">{{ nblikes }}</span>
  1963.               {% endif %}
  1964. </span>
  1965.             </button>
  1966.           <!-- Vocal -->
  1967.           <button class="premium-5sur5-action-btn mtb-btn" data-filter="audio" title="Messages vocaux">
  1968.             <span class="icon-telephone-voicemail" style="display: inline-flex; align-items: center; gap: 0;">
  1969.               <i class="bi bi-telephone-fill" style="color:#ffd700; font-size: 14px;"></i>
  1970.               <i class="bi bi-voicemail" style="color:#ffd700; font-size: 14px; margin-left: -4px;"></i>
  1971.             </span>
  1972.             <span>Vocal</span>
  1973.             <span class="mtb-count premium-5sur5-badge" data-bind="audio" style="background: rgba(255, 215, 0, 0.15); color: #ffa500;">0</span>
  1974.             </button>
  1975.           </div>
  1976.         </div>
  1977.       </header>
  1978.         <!-- Navigation par jours - Style Premium compact -->
  1979.         <div class="section-days" style="background: #f8fafb; border-radius: 0px; padding: 10px 12px; margin: 12px 0 16px 0; border: 1px solid rgba(65, 162, 170, 0.1);">
  1980.           <div class="date-navigation d-flex align-items-center justify-content-center" style="gap: 0;">
  1981.             <div class="date-container d-flex" style="overflow-x: auto; overflow-y: hidden; gap: 10px; padding: 4px 0; scrollbar-width: thin; scrollbar-color: #41a2aa #f0f0f0;">
  1982.               {% for x, groupAttach in listeattach %}
  1983.                 {% set xDate = date(x) %}
  1984.                 {% set finDate = date(sejour.dateFinSejour) %}
  1985.                 {% set todayStr = 'now'|date('Y-m-d') %}
  1986.                 {% set currentDateStr = xDate|date('Y-m-d') %}
  1987.                 
  1988.                 {% if xDate <= finDate %}
  1989.                   {# États du jour : passé / courant / futur #}
  1990.                   {% set isPast = currentDateStr < todayStr %}
  1991.                   {% set isCurrent = currentDateStr == todayStr %}
  1992.                   {% set isFuture = currentDateStr > todayStr %}
  1993.                   {% set isFirstDay = groupAttach.isFirstDay == "yes" %}
  1994.                   {% set isLastDay = groupAttach.isLastDay == "yes" %}
  1995.                   
  1996.                   {# Classes premium #}
  1997.                   {% set cardClasses = ['date-card', 'premium-day-card'] %}
  1998.                   {% if isFuture %}
  1999.                     {% set cardClasses = cardClasses|merge(['premium-day-future', 'notclickable']) %}
  2000.                   {% elseif isCurrent %}
  2001.                     {% set cardClasses = cardClasses|merge(['premium-day-current', 'active']) %}
  2002.                   {% else %}
  2003.                     {% set cardClasses = cardClasses|merge(['premium-day-past']) %}
  2004.                     {% if loop.last and not isCurrent %}
  2005.                       {% set cardClasses = cardClasses|merge(['active']) %}
  2006.                     {% endif %}
  2007.                   {% endif %}
  2008.                   <div
  2009.                     class="{{ cardClasses|join(' ') }} position-relative"
  2010.                     id="iconedemoP{{ loop.index }}"
  2011.                     {% if isFuture %}
  2012.                       data-tooltip="Ce jour n'est pas encore accessible"
  2013.                       aria-disabled="true"
  2014.                     {% else %}
  2015.                     data-bs-toggle="collapse"
  2016.                     data-bs-target="#demP{{ loop.index }}"
  2017.                       data-day-id="{{ currentDateStr }}"
  2018.                       aria-expanded="false"
  2019.                       aria-controls="demP{{ loop.index }}"
  2020.                     tabindex="0"
  2021.                     {% endif %}
  2022.                   >
  2023.                     {# Badge pour premier/dernier jour #}
  2024.                     {% if groupAttach.isFirstDay == "yes" %}
  2025.                       <span class="badge bg-success text-white position-absolute" style="top: 5px; left: 5px; font-size: 0.5rem; padding: 0.2rem 0.2rem;">
  2026.                         <i class="bi bi-play-fill me-1"></i>
  2027.                       </span>
  2028.                     {% elseif groupAttach.isLastDay == "yes" %}
  2029.                       <span class="badge bg-info text-white position-absolute" style="top: 5px; left: 5px; font-size: 0.5rem; padding: 0.2rem 0.2rem;">
  2030.                         <i class="bi bi-flag-fill ms-1"></i>
  2031.                       </span>
  2032.                     {% endif %}
  2033.                     
  2034.                     <div class="card-content text-center">
  2035.                       <span class="day">
  2036.                        {{ xDate|date("D")|replace({
  2037.                           "Mon": "lun.", "Tue": "mar.", "Wed": "mer.",
  2038.                           "Thu": "jeu.", "Fri": "ven.", "Sat": "sam.",
  2039.                           "Sun": "dim."
  2040.                         }) }}  
  2041.                       </span>
  2042.                       <span class="full-date">
  2043.                         {{ xDate|date("d M Y")|replace({
  2044.                           "Jan": "janv.", "Feb": "fév.", "Mar": "mars",
  2045.                           "Apr": "avr.",  "May": "mai",   "Jun": "juin",
  2046.                           "Jul": "juil.", "Aug": "août",  "Sep": "sept.",
  2047.                           "Oct": "oct.",  "Nov": "nov.",  "Dec": "déc."
  2048.                         }) }}
  2049.                       </span>
  2050.                       <ul class="media-list-horizontal">
  2051.                         {% if groupAttach.countPhotos > 0 %}
  2052.                           <li><i class="bi bi-camera"></i> {{ groupAttach.countPhotos }}</li>
  2053.                         {% endif %}
  2054.                         {% if groupAttach.countAudio > 0 %}
  2055.                           <li><i class="bi bi-mic-fill"></i> {{ groupAttach.countAudio }}</li>
  2056.                         {% endif %}
  2057.                         {% if groupAttach.countVideos > 0 %}
  2058.                           <li><i class="bi bi-camera-video"></i> {{ groupAttach.countVideos }}</li>
  2059.                         {% endif %}
  2060.                       </ul>
  2061.                     </div>
  2062.                   </div>
  2063.                 {% endif %}
  2064.               {% endfor %}
  2065.             </div>
  2066.           </div>
  2067.         </div>
  2068.     <!-- Carte produit flottante -->
  2069.     <div id="dynamic-card" class="dynamic-card" style="display:none">
  2070.       <div id="dynamic-card-content" class="dynamic-card-content" >
  2071.         <!-- Le contenu dynamique (album, pochette, montage vidéo) sera injecté ici -->
  2072.       </div>
  2073.     </div>
  2074.     <!-- Descriptions and Attachments avec sidebar B2C -->
  2075.     <div class="container-xxl" style="padding: 0 25px;  margin: 0 auto;">
  2076.       <div class="row g-2 col-md-12" style="margin: 0;">
  2077.         <div class="col-lg-12 col-md-12" style="padding: 0;">
  2078.           {# Galerie principale #}
  2079.           <div class="container--gallery modern" data-anchor="gallery" style="padding: 0;">
  2080.             {% set lastValidIndex = 0 %}
  2081.             {% set hasAttachments = false %}
  2082.       {% for x, groupAttach in listeattach %}
  2083.       {% set xDate = date(x) %}
  2084.       {% set finDate = date(sejour.dateFinSejour) %}
  2085.       {% if xDate <= finDate %}
  2086.       {% set lastValidIndex = loop.index %}
  2087.       {% set hasAttachments = true %}
  2088.       <div
  2089.         id="demP{{ loop.index }}"
  2090.         class="collapse {% if loop.last and xDate <= finDate %}show{% endif %}"
  2091.         style="padding: 12px 0;"
  2092.       >
  2093.         <div >
  2094.         <div class="entry-header" style="
  2095.     display: none;
  2096.     align-items: center;
  2097.     justify-content: space-between;
  2098.     background: #ffffff;
  2099.     padding: 8px 15px;
  2100.     border-radius: 10px;
  2101.     margin-bottom: 20px;
  2102.     box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  2103.     border-bottom: 1px solid #eee;
  2104.     flex-wrap: wrap;
  2105. ">
  2106.  
  2107.  
  2108.   <!-- Centre : Date + Médias -->
  2109.   <div style="
  2110.       flex-grow: 1;
  2111.       display: flex;
  2112.       align-items: center;
  2113.       justify-content: center;
  2114.       flex-wrap: wrap;
  2115.       gap: 12px;
  2116.   ">
  2117.     <!-- Date -->
  2118.     <div style="font-weight: bold; font-size: 16px; color: #333;">
  2119.       {{ x|date("l d F Y")|replace({
  2120.           "Monday": "Lundi", "Tuesday": "Mardi", "Wednesday": "Mercredi",
  2121.           "Thursday": "Jeudi", "Friday": "Vendredi", "Saturday": "Samedi",
  2122.           "Sunday": "Dimanche",
  2123.           "January": "Janvier", "February": "Février", "March": "Mars",
  2124.           "April": "Avril", "May": "Mai", "June": "Juin",
  2125.           "July": "Juillet", "August": "Août", "September": "Septembre",
  2126.           "October": "Octobre", "November": "Novembre", "December": "Décembre"
  2127.       }) }}
  2128.     </div>
  2129.   <!-- Filtres Médias -->
  2130. <div class="filter-icons" style="display: flex; gap: 8px; flex-wrap: wrap;">
  2131.     <span class="filter-badge active" data-filter="all" title="Afficher tout">
  2132.         <i class="bi bi-grid-3x3-gap-fill" style="margin-right: 5px; font-size: 14px;"></i> Tout
  2133.     </span>
  2134.     {% if groupAttach.countPhotos > 0 %}
  2135.     <span class="filter-badge" data-filter="photo" title="Filtrer les photos">
  2136.         <i class="bi bi-image" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countPhotos }}
  2137.     </span>
  2138.     {% endif %}
  2139.     {% if groupAttach.countVideos > 0 %}
  2140.     <span class="filter-badge" data-filter="video" title="Filtrer les vidéos">
  2141.         <i class="bi bi-camera-video-fill" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countVideos }}
  2142.     </span>
  2143.     {% endif %}
  2144.     {% if groupAttach.countAudio > 0 %}
  2145.     <span class="filter-badge" data-filter="audio" title="Filtrer les messages audio">
  2146.         <i class="bi bi-mic-fill" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countAudio }}
  2147.     </span>
  2148.     {% endif %}
  2149. </div>
  2150.   </div>
  2151.    
  2152.    
  2153.         </div>
  2154.   <!-- Bouton Jour Suivant -->
  2155.         <!-- Contenu -->
  2156.         <div class="entry-content" id="TourContent">
  2157.           <!-- Dropdown de filtrage par jour -->
  2158.           <div class="day-filter-dropdown" data-day-index="{{ loop.index }}">
  2159.             <button class="filter-toggle" type="button" aria-label="Options de filtrage">
  2160.               <i class="bi bi-three-dots"></i>
  2161.             </button>
  2162.             <div class="filter-dropdown-menu">
  2163.               <div class="filter-option active" data-filter="all">
  2164.                 <i class="bi bi-grid-3x3-gap-fill"></i>
  2165.                 <span>Tout</span>
  2166.                 <span class="count" data-count-all>0</span>
  2167.               </div>
  2168.               <div class="filter-option" data-filter="photo">
  2169.                 <i class="bi bi-image"></i>
  2170.                 <span>Photos</span>
  2171.                 <span class="count" data-count-photo>{{ groupAttach.countPhotos|default(0) }}</span>
  2172.               </div>
  2173.               <div class="filter-option" data-filter="video">
  2174.                 <i class="bi bi-camera-video-fill"></i>
  2175.                 <span>Vidéos</span>
  2176.                 <span class="count" data-count-video>{{ groupAttach.countVideos|default(0) }}</span>
  2177.               </div>
  2178.               <div class="filter-option" data-filter="audio">
  2179.                 <i class="bi bi-mic-fill"></i>
  2180.                 <span>Messages</span>
  2181.                 <span class="count" data-count-audio>{{ groupAttach.countAudio|default(0) }}</span>
  2182.               </div>
  2183.             </div>
  2184.           </div>
  2185.                {% for description in sejour.jourdescripdate %}
  2186.       {%if   description.datejourphoto|date("m/d/Y") !=""%}{% if
  2187.             description.datejourphoto|date("m/d/Y") == x|date("m/d/Y") %}
  2188.           <p class="description" style="margin-left:2%;width:95%;margin-top:1%;margin-bottom:1%;text-align:left">
  2189.         
  2190.             {{ description.description | nl2br }}
  2191.          
  2192.           </p>   {% endif %} 
  2193.        {%endif%}{% endfor %}
  2194.  
  2195.           <!-- Conteneur des photos et vidéos -->
  2196.           <div
  2197.             class="rowimag no-margin"
  2198.             style="
  2199.               width: 100%;
  2200.               display: flex;
  2201.               flex-wrap: wrap;
  2202.               margin: 0;
  2203.               box-sizing: border-box;
  2204.             "
  2205.           >
  2206.             <!-- Afficher les Photos et Vidéos -->
  2207.             {% for attach in groupAttach.attachments %}
  2208.               {% if attach.libiller == 'photo' %}
  2209.            
  2210.             <div class="column" data-type="{{ attach.libiller }}">
  2211.             
  2212.               <div class="photo-zoom photo-item">
  2213.                 <!-- Image sans lien -->
  2214.                 <img src="{{ attach.path }}" alt="{{ attach.descreption }}" loading="lazy" decoding="async" />
  2215.                 <!-- Icône "voir" pour ouvrir le slider -->
  2216.                 <div class="view-icon" onclick="viewImage('{{ attach.id_attchment }}', this)" title="Voir en plein écran">
  2217.                   <i class="bi bi-eye-fill"></i>
  2218.                 </div>
  2219.                 <!-- Icône du cœur avec logique existante -->
  2220.                 <div
  2221.                   class="heart-icon"
  2222.                   id="coeur{{ attach.id_attchment }}"
  2223.                   data-id="{{ attach.id_attchment }}"
  2224.                   data-sejour-id="{{ sejour.id }}"
  2225.                   data-path="{{ attach.path }}"
  2226.                   data-description="{{ attach.descreption }}"
  2227.                 >
  2228.                   {% if app.user %} {% if attach.is_favorite %}
  2229.                   <i
  2230.                     class="bi bi-heart-fill"
  2231.                     title="Sélectionnée"
  2232.                     style="color: #f56040"
  2233.                   ></i>
  2234.                   {% else %}
  2235.                   <i class="bi bi-heart" title="Ajouter à ma sélection"></i>
  2236.                   {% endif %} {% endif %}
  2237.                 </div>
  2238.                 <div class="photo-actions" style="display: none">
  2239.                   <button class="menu-btn">⋮</button>
  2240.                   <div class="menu-options">
  2241.                     <button onclick="addToPack('tirage')">
  2242.                       🖨️ Ajouter au tirage
  2243.                     </button>
  2244.                     <button onclick="addToPack('numerique')">
  2245.                       💾 Ajouter au numérique
  2246.                     </button>
  2247.                   </div>
  2248.                 </div>
  2249.               
  2250.               </div>
  2251.               {% if attach.descreption != "" %}
  2252.               <h4 class="description">{{ attach.descreption }}</h4>
  2253.               {% endif %}
  2254.             </div>
  2255.             {% endif %} 
  2256.              {% if attach.libiller == 'video' %}
  2257.            
  2258.             <div class="column" data-type="{{ attach.libiller }}">
  2259.                  
  2260.             
  2261.                  <div class="video-container" style="position: relative; display: inline-block; width: 100%; border-radius: 8px; overflow: hidden;">
  2262.                         <video class="photo-zoom" controls controlslist="nodownload noplaybackrate" style="width: 100%;">
  2263.                           <source src="{{ attach.path }}" type="video/mp4" />
  2264.                           Votre navigateur ne supporte pas la lecture vidéo.
  2265.                         </video>
  2266.                       
  2267.                       </div>
  2268.               {% if attach.descreption != "" %}
  2269.               <h4 class="description">{{ attach.descreption }}</h4>
  2270.               {% endif %}
  2271.             </div>
  2272.             {% endif %} 
  2273.             
  2274.             {% endfor %}
  2275.           </div>
  2276.           <!-- Section séparée pour les messages audio -->
  2277.           {% if groupAttach.countAudio > 0 %}
  2278.           <div class="audio-messages-section" style="margin-top:15px; border-top: 1px solid #eee; padding: 30px;">
  2279.             <h4 style="margin-bottom: 15px; color: #555">
  2280.             
  2281.               <i class="bi bi-mic-fill" style="margin-right: 8px; color: #ffa500"></i>
  2282.               Messages vocaux ({{ groupAttach.countAudio }})
  2283.             </h4>
  2284.             
  2285.             {% if sejour.codeSejour|slice(0, 2) == 'PF' %}
  2286.               {# Les séjours commençant par PF ont toujours accès #}
  2287.               <div class="audio-messages-container" data-type="audio" style="display: flex; flex-wrap: wrap; gap: 15px">
  2288.                 {% for attach in groupAttach.attachments %}
  2289.                   {% if attach.libiller == 'message' %}
  2290.                     <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;">
  2291.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  2292.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  2293.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1">
  2294.                           <source src="{{ attach.path }}" type="audio/mp3" />
  2295.                           Votre navigateur ne supporte pas la lecture audio.
  2296.                         </audio>
  2297.                       </div>
  2298.                       {% if attach.descreption != "" %}
  2299.                         <div class="audio-description" style="padding-left: 30px">
  2300.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  2301.                             {{ attach.descreption }}
  2302.                           </p>
  2303.                         </div>
  2304.                       {% endif %}
  2305.                     </div>
  2306.                   {% endif %}
  2307.                 {% endfor %}
  2308.               </div>
  2309.             
  2310.             {% elseif sejour.codeSejour|slice(0, 2) == 'PP' and parentsejour.payment  == 1 or sejour.codeSejour|slice(0, 2) == 'EF' and parentsejour.payment  == 1 %}
  2311.               {# PP ou EF avec paiement effectué #}
  2312.               <div class="audio-messages-container" data-type="audio" style="display: flex; flex-wrap: wrap; gap: 15px">
  2313.                 {% for attach in groupAttach.attachments %}
  2314.                   {% if attach.libiller == 'message' %}
  2315.                     <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;">
  2316.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  2317.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  2318.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1">
  2319.                           <source src="{{ attach.path }}" type="audio/mp3" />
  2320.                           Votre navigateur ne supporte pas la lecture audio.
  2321.                         </audio>
  2322.                       </div>
  2323.                       {% if attach.descreption != "" %}
  2324.                         <div class="audio-description" style="padding-left: 30px">
  2325.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  2326.                             {{ attach.descreption }}
  2327.                           </p>
  2328.                         </div>
  2329.                       {% endif %}
  2330.                     </div>
  2331.                   {% endif %}
  2332.                 {% endfor %}
  2333.               </div>
  2334.             
  2335.             {% elseif sejour.codeSejour|slice(0, 2) == 'PP' and parentsejour.payment == 0 or sejour.codeSejour|slice(0, 2) == 'EF' and parentsejour.payment == 0 %}
  2336.               {# PP ou EF sans paiement - message d'accès limité et audio désactivés #}
  2337.               <div class="audio-messages-container" style="display: flex; flex-wrap: wrap; gap: 15px; opacity: 0.5; pointer-events: none; filter: grayscale(100%);">
  2338.                 <div class="audio-messages-restricted" data-type="audio" style="padding: 20px; background: #f0f0f0; border-radius: 8px; text-align: center; margin-bottom: 15px; width: 100%;">
  2339.                   <i class="bi bi-lock-fill" style="font-size: 24px; color: #808080; margin-bottom: 10px;"></i>
  2340.                   <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>
  2341.                 </div>
  2342.                 {% for attach in groupAttach.attachments %}
  2343.                   {% if attach.libiller == 'message' %}
  2344.                     <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;">
  2345.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  2346.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  2347.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1" disabled>
  2348.                           <source src="{{ attach.path }}" type="audio/mp3" />
  2349.                           Votre navigateur ne supporte pas la lecture audio.
  2350.                         </audio>
  2351.                       </div>
  2352.                       {% if attach.descreption != "" %}
  2353.                         <div class="audio-description" style="padding-left: 30px">
  2354.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  2355.                             {{ attach.descreption }}
  2356.                           </p>
  2357.                         </div>
  2358.                       {% endif %}
  2359.                     </div>
  2360.                   {% endif %}
  2361.                 {% endfor %}
  2362.               </div>
  2363.             {% endif %}
  2364.           </div>
  2365.         {% endif %}
  2366.         </div>
  2367.       </div>
  2368.     </div>
  2369.     {% endif %}
  2370.     {% endfor %}
  2371.     {% if not hasAttachments %}
  2372.   <section class="stay-empty-wrapper">
  2373.   <div class="stay-empty-card">
  2374.       <h2 class="stay-empty-title">Aucune photo pour le moment, c'est normal.</h2>
  2375.     <p class="stay-empty-intro">
  2376.         Le séjour démarre et l'équipe est pleinement avec les enfants : installation, découverte des lieux, premiers temps de vie de groupe.<br>
  2377.         Les premières photos ou messages vocaux sont généralement publiés en fin de journée d'arrivée ou le lendemain matin.
  2378.       </p>
  2379.       <!-- Bloc "Ce qui va arriver" + illustration -->
  2380.       <div class="stay-empty-expect">
  2381.         <div class="stay-empty-expect-content">
  2382.           <p class="stay-empty-expect-label">Ce qui va arriver dans les prochaines heures :</p>
  2383.       <ul class="stay-empty-list">
  2384.         <li>
  2385.               <span class="stay-empty-list-icon stay-empty-list-icon--check">
  2386.                 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
  2387.                   <polyline points="20 6 9 17 4 12"/>
  2388.                 </svg>
  2389.               </span>
  2390.               <span>1 photo ou message « Bien arrivés »</span>
  2391.         </li>
  2392.         <li>
  2393.               <span class="stay-empty-list-icon stay-empty-list-icon--photos">
  2394.                 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  2395.                   <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
  2396.                   <circle cx="8.5" cy="8.5" r="1.5"/>
  2397.                   <polyline points="21 15 16 10 5 21"/>
  2398.                 </svg>
  2399.               </span>
  2400.               <span>Quelques photos du lieu et du groupe</span>
  2401.         </li>
  2402.         <li>
  2403.               <span class="stay-empty-list-icon stay-empty-list-icon--vocal">
  2404.                 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  2405.                   <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
  2406.                   <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
  2407.                 </svg>
  2408.               </span>
  2409.               <span>Un message vocal dès que le programme le permet</span>
  2410.         </li>
  2411.       </ul>
  2412.         </div>
  2413.         <!-- Illustration compacte -->
  2414.         <div class="stay-empty-illustration">
  2415.           <svg class="stay-empty-phone" viewBox="0 0 120 200" fill="none" xmlns="http://www.w3.org/2000/svg">
  2416.             <rect x="0" y="0" width="120" height="200" rx="18" fill="#1A1A2E"/>
  2417.             <rect x="6" y="10" width="108" height="180" rx="14" fill="#fff"/>
  2418.             <rect x="12" y="22" width="44" height="44" rx="8" fill="#F09E7A" opacity="0.2"/>
  2419.             <rect x="62" y="22" width="46" height="44" rx="8" fill="#41A2AA" opacity="0.2"/>
  2420.             <rect x="12" y="72" width="96" height="56" rx="8" fill="#9B8BF4" opacity="0.15"/>
  2421.             <circle cx="34" cy="44" r="10" fill="#F09E7A"/>
  2422.             <path d="M29 47l4-5 3 3 4-5 5 7H29z" fill="#fff"/>
  2423.             <circle cx="85" cy="44" r="10" fill="#41A2AA"/>
  2424.             <path d="M82 41v6M85 38v9M88 42v4" stroke="#fff" stroke-width="2" stroke-linecap="round"/>
  2425.             <circle cx="60" cy="100" r="12" fill="rgba(255,255,255,0.9)"/>
  2426.             <path d="M56 94l10 6-10 6V94z" fill="#9B8BF4"/>
  2427.             <rect x="12" y="138" width="96" height="44" rx="8" fill="#F8FAFB"/>
  2428.             <circle cx="34" cy="160" r="8" fill="#E5E9F0"/>
  2429.             <circle cx="60" cy="160" r="8" fill="#41A2AA"/>
  2430.             <circle cx="86" cy="160" r="8" fill="#E5E9F0"/>
  2431.             <rect x="42" y="4" width="36" height="5" rx="2.5" fill="#1A1A2E"/>
  2432.           </svg>
  2433.           <div class="stay-empty-bubble stay-empty-bubble--arrived">
  2434.             <span class="stay-empty-bubble-icon">
  2435.               <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
  2436.                 <polyline points="20 6 9 17 4 12"/>
  2437.               </svg>
  2438.             </span>
  2439.             Bien arrivés !
  2440.           </div>
  2441.           <div class="stay-empty-bubble stay-empty-bubble--photos">
  2442.             <span class="stay-empty-bubble-icon">
  2443.               <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  2444.                 <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
  2445.                 <circle cx="8.5" cy="8.5" r="1.5"/>
  2446.                 <polyline points="21 15 16 10 5 21"/>
  2447.               </svg>
  2448.             </span>
  2449.             +12 photos
  2450.           </div>
  2451.           <span class="stay-empty-illustration-label">Exemple de nouvelles à venir</span>
  2452.         </div>
  2453.       </div>
  2454.       <!-- Note rassurante -->
  2455.       <div class="stay-empty-reassurance">
  2456.         <svg class="stay-empty-reassurance-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  2457.           <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
  2458.         </svg>
  2459.         <p>En cas de besoin important, l'école ou l'organisateur vous contactera directement, comme d'habitude.</p>
  2460.       </div>
  2461.       <!-- CTA -->
  2462.       <div class="stay-empty-footer">
  2463.         <a href="{{ path('ParametresParent') }}" class="stay-empty-cta">
  2464.           <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  2465.             <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
  2466.             <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
  2467.           </svg>
  2468.           Gérer mes notifications
  2469.         </a>
  2470.     </div>
  2471.   </div>
  2472. </section>
  2473.   <div style="height: 200px;"></div>
  2474.     {% endif %}
  2475.     
  2476.     <!-- JavaScript pour les pills et la gestion du nouveau contenu -->
  2477.     <script>
  2478.       document.addEventListener('DOMContentLoaded', function() {
  2479.         // Valider les liens de produits au chargement
  2480.         setTimeout(() => {
  2481.           if (typeof window.validateProductLinks === 'function') {
  2482.             window.validateProductLinks();
  2483.           }
  2484.         }, 1000);
  2485.         
  2486.         // ⚡ OPTIMISATION: Event delegation avec throttling pour éviter les clics multiples
  2487.         let clickThrottleTimeout = null;
  2488.         document.addEventListener('click', function(e) {
  2489.           // Throttling pour éviter les clics multiples rapides
  2490.           if (clickThrottleTimeout) return;
  2491.           
  2492.           const pill = e.target.closest('.stat-pill');
  2493.           if (pill) {
  2494.             clickThrottleTimeout = setTimeout(() => {
  2495.               clickThrottleTimeout = null;
  2496.             }, 100);
  2497.             
  2498.             // ⚡ OPTIMISÉ: Une seule boucle pour gérer les états actifs
  2499.             const statPills = document.querySelectorAll('.stat-pill');
  2500.             for (let i = 0; i < statPills.length; i++) {
  2501.               const p = statPills[i];
  2502.               if (p && p.classList) {
  2503.                 if (p === pill) {
  2504.                   p.classList.add('active');
  2505.                 } else {
  2506.                   p.classList.remove('active');
  2507.                 }
  2508.               }
  2509.             }
  2510.             
  2511.             // Déclencher le filtrage du contenu
  2512.             const filter = pill.getAttribute('data-filter');
  2513.             if (typeof window.filterContent === 'function') {
  2514.               window.filterContent(filter);
  2515.             }
  2516.           }
  2517.         });
  2518.         // Fonction de filtrage du contenu - globale
  2519.         window.filterContent = function(filter) {
  2520.           // ⚡ OPTIMISÉ: Suppression des console.log en production
  2521.           if (window.location.hostname === 'localhost' || window.location.hostname.includes('dev')) {
  2522.             console.log('Filtrage par:', filter);
  2523.           }
  2524.           
  2525.           const dateNavigation = document.querySelector('.date-navigation');
  2526.           const sectionDays = document.querySelector('.section-days');
  2527.           
  2528.           if (filter === 'favoris') {
  2529.             // Afficher la vue des favoris
  2530.             showFavoritesView();
  2531.           } else if (filter === 'audio') {
  2532.             // Afficher la vue des messages audio
  2533.             showAudiosView();
  2534.           } else if (filter === 'photos') {
  2535.             // Ouvrir le carrousel du jour actif ou du dernier jour
  2536.             openCurrentDayCarousel();
  2537.           } else {
  2538.             // Restaurer la vue normale des jours
  2539.             showDaysView();
  2540.           }
  2541.         }
  2542.         // Fonction pour afficher la vue des favoris - globale
  2543.         window.showFavoritesView = function() {
  2544.           const sectionDays = document.querySelector('.section-days');
  2545.           const containerGallery = document.querySelector('.container--gallery');
  2546.           
  2547.           // Masquer le container gallery
  2548.           if (containerGallery) {
  2549.             containerGallery.style.display = 'none';
  2550.           }
  2551.           
  2552.           // Mettre à jour l'état des boutons
  2553.           updatePhotosButtonState('favorites');
  2554.           updateAudioButtonState('favorites');
  2555.           updateFavoritesButtonState('favorites');
  2556.           
  2557.           const favoriteCount = getCurrentFavoriteCount();
  2558.           
  2559.           // sla vue des favoris avec e-commerce
  2560.           const favoritesHTML = `
  2561.             <div class="favorites-ecommerce-view">
  2562.               <!-- Header avec progression -->
  2563.               <div class="favorites-header">
  2564.                 <div class="favorites-title">
  2565.                   <h3>Vos souvenirs sélectionnés</h3>
  2566.                   <span class="favorites-count" id="favorites-count">${favoriteCount} photos</span>
  2567.                 </div>
  2568.                 <button class="btn-back-to-days" onclick="window.showDaysView()">
  2569.                   <i class="bi bi-arrow-left"></i>
  2570.                   Retour aux jours
  2571.                 </button>
  2572.               </div>
  2573.             
  2574.               <!-- Grille des favoris -->
  2575.               <div class="favorites-content">
  2576.                 <div class="favorites-grid" id="favoritesGrid">
  2577.                   ${generateFavoritesGrid()}
  2578.                 </div>
  2579.                 
  2580.                 <!-- CTA discret et premium pour rediriger vers la boutique -->
  2581.                 <a href="{{ path('boutique5sur5') }}" class="premium-cta-subtle" style="margin: 20px 0; padding: 16px 20px; background: rgba(65, 162, 170, 0.04); border: 1px solid rgba(65, 162, 170, 0.15); border-radius: 12px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; justify-content: space-between; gap: 12px; text-decoration: none;">
  2582.                   <div style="display: flex; align-items: center; gap: 12px; flex: 1;">
  2583.                     <div style="width: 40px; height: 40px; background: rgba(65, 162, 170, 0.1); border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-shrink: 0;">
  2584.                       <i class="bi bi-gift" style="font-size: 18px; color: #41A2AA;"></i>
  2585.                     </div>
  2586.                     <div style="flex: 1; min-width: 0;">
  2587.                       <p style="margin: 0; font-size: 14px; font-weight: 600; color: #1a1a1a; line-height: 1.4;">Créer des souvenirs avec vos ${favoriteCount} photos</p>
  2588.                       <p style="margin: 4px 0 0 0; font-size: 12px; color: #6b7280; line-height: 1.4;">Albums, tirages et livres personnalisés</p>
  2589.                     </div>
  2590.                   </div>
  2591.                   <i class="bi bi-chevron-right" style="font-size: 18px; color: #41A2AA; flex-shrink: 0;"></i>
  2592.                 </a>
  2593.               </div>
  2594.             </div>
  2595.           `;
  2596.           
  2597.           sectionDays.innerHTML = favoritesHTML;
  2598.         }
  2599.         // Fonction pour afficher la vue des messages audio - globale
  2600.         window.showAudiosView = function() {
  2601.           const sectionDays = document.querySelector('.section-days');
  2602.           const containerGallery = document.querySelector('.container--gallery');
  2603.           
  2604.           // Masquer le container gallery
  2605.           if (containerGallery) {
  2606.             containerGallery.style.display = 'none';
  2607.           }
  2608.           
  2609.           // Mettre à jour l'état des boutons
  2610.           updatePhotosButtonState('audios');
  2611.           updateAudioButtonState('audios');
  2612.           updateFavoritesButtonState('audios');
  2613.           
  2614.           // Collecter tous les messages audio de tous les jours
  2615.           const allAudioMessages = [];
  2616.           const audioItems = document.querySelectorAll('.audio-message-item[data-type="audio"]');
  2617.           
  2618.           audioItems.forEach(item => {
  2619.             const audio = item.querySelector('audio source');
  2620.             const description = item.querySelector('.audio-description p');
  2621.             const dayContainer = item.closest('[id^="demP"]');
  2622.             let dayTitle = 'Jour inconnu';
  2623.             
  2624.             if (dayContainer) {
  2625.               // Trouver le titre du jour
  2626.               const dayIndex = dayContainer.id.replace('demP', '');
  2627.               const dayCard = document.querySelector(`[data-target="#demP${dayIndex}"]`);
  2628.               if (dayCard) {
  2629.                 dayTitle = dayCard.textContent.trim();
  2630.               } else {
  2631.                 // Essayer de trouver dans les cartes de date
  2632.                 const dateCard = document.querySelector(`.date-card[data-bs-target="#demP${dayIndex}"], .date-card[data-target="#demP${dayIndex}"]`);
  2633.                 if (dateCard) {
  2634.                   const dateText = dateCard.querySelector('.date-text, .card-title, h5, .title-line');
  2635.                   if (dateText) {
  2636.                     dayTitle = dateText.textContent.trim();
  2637.                   }
  2638.                 }
  2639.               }
  2640.             }
  2641.             
  2642.             if (audio) {
  2643.               allAudioMessages.push({
  2644.                 src: audio.src,
  2645.                 description: description ? description.textContent.trim() : '',
  2646.                 day: dayTitle,
  2647.                 isRestricted: item.closest('.audio-messages-container[style*="opacity: 0.5"]') !== null
  2648.               });
  2649.             }
  2650.           });
  2651.           
  2652.           // Créer la vue des messages audio
  2653.           const audiosHTML = `
  2654.             <div class="audios-view">
  2655.               <!-- Header -->
  2656.               <div class="audios-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding: 20px; background: #f8f9fa; border-radius: 10px;">
  2657.                 <div class="audios-title">
  2658.                   <h3><span class="icon-telephone-voicemail">
  2659.   <i class="bi bi-telephone-fill tel"  style="color: #ffd700;font-size:1.5em"></i>
  2660.   <i class="bi bi-voicemail vm" style="color: #ffd700;font-size: 0.9rem;
  2661.     transform: translate(-40%, -46%);"></i>
  2662. </span>
  2663.                   <span class="audios-count">${allAudioMessages.length} messages</span>
  2664.                 </div>
  2665.                 <button class="btn-back-to-days" onclick="window.showDaysView()" style="background: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;">
  2666.                   <i class="bi bi-arrow-left"></i>
  2667.                   Retour aux jours
  2668.                 </button>
  2669.               </div>
  2670.               <!-- Grille des messages audio -->
  2671.               <div class="audios-content" style="display: flex; flex-wrap: wrap; gap: 20px;">
  2672.                 ${allAudioMessages.map(msg => `
  2673.                   <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%);' : ''}">
  2674.                     ${msg.day && msg.day !== 'Jour inconnu' ? `
  2675.                       <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;">
  2676.                         ${msg.day}
  2677.                       </div>
  2678.                     ` : ''}
  2679.                     <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 12px;">
  2680.                       <i class="bi bi-mic-fill" style="font-size: 24px; margin-right: 15px; color: #ffa500;"></i>
  2681.                       <audio controls controlslist="nodownload noplaybackrate" style="flex: 1; border-radius: 5px;" ${msg.isRestricted ? 'disabled' : ''}>
  2682.                         <source src="${msg.src}" type="audio/mp3" />
  2683.                         Votre navigateur ne supporte pas la lecture audio.
  2684.                       </audio>
  2685.                     </div>
  2686.                     ${msg.description ? `
  2687.                       <div class="audio-description" style="padding-left: 39px;">
  2688.                         <p style="margin: 0; font-size: 14px; color: #666; line-height: 1.4;">${msg.description}</p>
  2689.                       </div>
  2690.                     ` : ''}
  2691.                     ${msg.isRestricted ? `
  2692.                       <div class="audio-restricted-notice" style="text-align: center; margin-top: 10px; padding: 8px; background: #fff3cd; border-radius: 5px; border: 1px solid #ffeaa7;">
  2693.                         <i class="bi bi-lock-fill" style="color: #856404; margin-right: 5px;"></i>
  2694.                         <small style="color: #856404;">Accès premium requis</small>
  2695.                       </div>
  2696.                     ` : ''}
  2697.                   </div>
  2698.                 `).join('')}
  2699.               </div>
  2700.               
  2701.               ${allAudioMessages.length === 0 ? `
  2702.                 <div class="no-audios" style="text-align: center; padding: 60px 20px; color: #6c757d;">
  2703.                   <i class="bi bi-mic" style="font-size: 4rem; margin-bottom: 20px; opacity: 0.3;"></i>
  2704.                   <h4>Aucun message vocal</h4>
  2705.                   <p>Il n'y a pas encore de messages vocaux dans ce séjour.</p>
  2706.                 </div>
  2707.               ` : ''}
  2708.             </div>
  2709.           `;
  2710.           
  2711.           sectionDays.innerHTML = audiosHTML;
  2712.         }
  2713.         
  2714.         // Variables pour sauvegarder le contenu original
  2715.         let originalDaysContent = null;
  2716.         
  2717.         // Sauvegarder le contenu original au chargement
  2718.         document.addEventListener('DOMContentLoaded', function() {
  2719.           setTimeout(() => {
  2720.             const containerGallery = document.querySelector('.container--gallery');
  2721.             if (containerGallery) {
  2722.               originalDaysContent = containerGallery.outerHTML;
  2723.             }
  2724.           }, 1000);
  2725.         });
  2726.         
  2727.       
  2728.         // Fonction pour ouvrir le carrousel contextuel - globale
  2729.         window.openCurrentDayCarousel = function() {
  2730.           // Détecter la vue active
  2731.           const currentView = detectCurrentView();
  2732.           
  2733.           switch (currentView) {
  2734.             case 'favorites':
  2735.               openFavoritesCarousel();
  2736.               break;
  2737.             case 'audios':
  2738.               // Dans la vue audios, pas de carrousel photos pertinent
  2739.               console.log('Carrousel photos non disponible dans la vue audios');
  2740.               return;
  2741.             case 'days':
  2742.             default:
  2743.               openActiveDayCarousel();
  2744.               break;
  2745.           }
  2746.         }
  2747.         
  2748.         // Fonction pour mettre à jour l'état du bouton photos selon le contexte
  2749.         function updatePhotosButtonState(currentView) {
  2750.           const photosBtn = document.querySelector('.mtb-btn[data-filter="photos"]');
  2751.           if (!photosBtn) return;
  2752.           
  2753.           const btnIcon = photosBtn.querySelector('i');
  2754.           const btnCount = photosBtn.querySelector('.mtb-count');
  2755.           
  2756.           switch (currentView) {
  2757.             case 'favorites':
  2758.               // Dans les favoris : bouton actif, ouvre le carrousel des favoris
  2759.               photosBtn.style.opacity = '1';
  2760.               photosBtn.style.pointerEvents = 'auto';
  2761.               photosBtn.title = 'Voir le carrousel des favoris';
  2762.               if (btnIcon) btnIcon.style.color = '#007bff';
  2763.               if (btnCount) btnCount.style.display = 'none'; // Cacher le compteur
  2764.               break;
  2765.               
  2766.             case 'audios':
  2767.               // Dans les audios : bouton désactivé
  2768.               photosBtn.style.opacity = '0.4';
  2769.               photosBtn.style.pointerEvents = 'none';
  2770.               photosBtn.title = 'Carrousel non disponible dans la vue audios';
  2771.               if (btnIcon) btnIcon.style.color = '#6c757d';
  2772.               if (btnCount) btnCount.style.display = 'none';
  2773.               break;
  2774.               
  2775.             case 'days':
  2776.             default:
  2777.               // Vue normale : utiliser la logique existante du carrousel
  2778.               photosBtn.style.opacity = '1';
  2779.               photosBtn.style.pointerEvents = 'auto';
  2780.               photosBtn.title = 'Voir les photos du jour actif';
  2781.               if (btnIcon) btnIcon.style.color = '';
  2782.               if (btnCount) btnCount.style.display = 'none'; // Cacher le compteur pour éviter la confusion
  2783.               break;
  2784.           }
  2785.         }
  2786.         
  2787.         // Fonction pour mettre à jour l'état du bouton audio selon le contexte
  2788.         window.updateAudioButtonState = function updateAudioButtonState(currentView) {
  2789.           const audioBtn = document.querySelector('.mtb-btn[data-filter="audio"]');
  2790.           if (!audioBtn) return;
  2791.           
  2792.           const btnCount = audioBtn.querySelector('.mtb-count');
  2793.           
  2794.           switch (currentView) {
  2795.             case 'favorites':
  2796.               // Dans les favoris : pas d'action sur le compteur audio
  2797.               break;
  2798.               
  2799.             case 'audios':
  2800.               // Dans les audios : afficher le nombre total calculé par le frontend
  2801.               const totalAudioCount = document.querySelectorAll('.audio-message-item[data-type="audio"]').length;
  2802.               if (btnCount) {
  2803.                 btnCount.textContent = totalAudioCount;
  2804.               }
  2805.               break;
  2806.               
  2807.             case 'days':
  2808.             default:
  2809.               // Vue normale : compter les audios du jour actif
  2810.               const activeDayAudioCount = countActiveDayAudios();
  2811.               if (btnCount) {
  2812.                 btnCount.textContent = activeDayAudioCount;
  2813.               }
  2814.               break;
  2815.           }
  2816.         }
  2817.         
  2818.         // Fonction pour mettre à jour l'état du bouton favoris selon le contexte
  2819.         window.updateFavoritesButtonState = function updateFavoritesButtonState(currentView) {
  2820.           const favoritesBtn = document.querySelector('.mtb-btn[data-filter="favoris"]');
  2821.           if (!favoritesBtn) return;
  2822.           
  2823.           const btnIcon = favoritesBtn.querySelector('i');
  2824.           const btnCount = favoritesBtn.querySelector('.mtb-count');
  2825.           
  2826.           // ✅ IMPORTANT : Le bouton favoris reste TOUJOURS accessible, peu importe la vue
  2827.           switch (currentView) {
  2828.             case 'favorites':
  2829.               // Dans les favoris : bouton actif marqué visuellement
  2830.               favoritesBtn.style.opacity = '1';
  2831.               favoritesBtn.style.pointerEvents = 'auto';
  2832.               favoritesBtn.title = 'Vous êtes dans la vue favoris';
  2833.               if (btnIcon) btnIcon.style.color = '#f56040';
  2834.               if (btnCount) btnCount.style.display = 'inline';
  2835.               favoritesBtn.classList.add('active');
  2836.               break;
  2837.               
  2838.             case 'audios':
  2839.               // Dans les audios : bouton reste actif et accessible ✅
  2840.               favoritesBtn.style.opacity = '1';
  2841.               favoritesBtn.style.pointerEvents = 'auto';
  2842.               favoritesBtn.title = 'Voir les favoris';
  2843.               if (btnIcon) btnIcon.style.color = '#f56040';
  2844.               if (btnCount) btnCount.style.display = 'inline';
  2845.               favoritesBtn.classList.remove('active');
  2846.               break;
  2847.               
  2848.             case 'days':
  2849.             default:
  2850.               // Vue normale : bouton normal et accessible
  2851.               favoritesBtn.style.opacity = '1';
  2852.               favoritesBtn.style.pointerEvents = 'auto';
  2853.               favoritesBtn.title = 'Voir les favoris';
  2854.               if (btnIcon) btnIcon.style.color = '#f56040';
  2855.               if (btnCount) btnCount.style.display = 'inline';
  2856.               favoritesBtn.classList.remove('active');
  2857.               break;
  2858.           }
  2859.         }
  2860.         
  2861.         // Fonction pour compter les audios du jour actif (réutilise la même logique)
  2862.         // ⚡ OPTIMISÉ: Cache des éléments DOM pour éviter les requêtes répétitives
  2863.         let cachedActiveDay = null;
  2864.         let cachedAudioCount = 0;
  2865.         let lastCacheTime = 0;
  2866.         const CACHE_DURATION = 1000; // 1 seconde
  2867.         
  2868.         function countActiveDayAudios() {
  2869.           const now = Date.now();
  2870.           
  2871.           // Utiliser le cache si récent
  2872.           if (cachedActiveDay && (now - lastCacheTime) < CACHE_DURATION) {
  2873.             return cachedAudioCount;
  2874.           }
  2875.           
  2876.           // Trouver le jour actuellement ouvert
  2877.           let activeDay = document.querySelector('.collapse.show[id^="demP"]');
  2878.           
  2879.           // Si aucun jour ouvert, prendre le dernier jour
  2880.           if (!activeDay) {
  2881.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  2882.             activeDay = allDays[allDays.length - 1];
  2883.           }
  2884.           
  2885.           if (!activeDay) {
  2886.             cachedActiveDay = null;
  2887.             cachedAudioCount = 0;
  2888.             lastCacheTime = now;
  2889.             return 0;
  2890.           }
  2891.           
  2892.           // Compter les messages audio dans ce jour
  2893.           const audiosInDay = activeDay.querySelectorAll('.audio-message-item[data-type="audio"]');
  2894.           const count = audiosInDay.length;
  2895.           
  2896.           // Mettre à jour le cache
  2897.           cachedActiveDay = activeDay;
  2898.           cachedAudioCount = count;
  2899.           lastCacheTime = now;
  2900.           
  2901.           return count;
  2902.         }
  2903.         
  2904.         // Fonction pour détecter la vue active
  2905.         // ⚡ OPTIMISÉ: Cache de la vue active pour éviter les requêtes DOM répétitives
  2906.         let cachedCurrentView = null;
  2907.         let lastViewCheck = 0;
  2908.         const VIEW_CACHE_DURATION = 500; // 500ms
  2909.         
  2910.         function detectCurrentView() {
  2911.           const now = Date.now();
  2912.           
  2913.           // Utiliser le cache si récent
  2914.           if (cachedCurrentView && (now - lastViewCheck) < VIEW_CACHE_DURATION) {
  2915.             return cachedCurrentView;
  2916.           }
  2917.           
  2918.           let view = 'days';
  2919.           if (document.querySelector('.favorites-ecommerce-view')) {
  2920.             view = 'favorites';
  2921.           } else if (document.querySelector('.audios-view')) {
  2922.             view = 'audios';
  2923.           }
  2924.           
  2925.           // Mettre à jour le cache
  2926.           cachedCurrentView = view;
  2927.           lastViewCheck = now;
  2928.           
  2929.           return view;
  2930.         }
  2931.         
  2932.         // ⚡ OPTIMISÉ: Cache global des éléments DOM fréquemment utilisés
  2933.         const domCache = {
  2934.           favoritesGrid: null,
  2935.           lastFavoritesUpdate: 0,
  2936.           CACHE_DURATION: 2000 // 2 secondes
  2937.         };
  2938.         
  2939.         function getCachedFavorites() {
  2940.           const now = Date.now();
  2941.           if (!domCache.favoritesGrid || (now - domCache.lastFavoritesUpdate) > domCache.CACHE_DURATION) {
  2942.             domCache.favoritesGrid = document.querySelector('.favorites-grid');
  2943.             domCache.lastFavoritesUpdate = now;
  2944.           }
  2945.           return domCache.favoritesGrid;
  2946.         }
  2947.         
  2948.         // Fonction pour ouvrir le carrousel des favoris
  2949.         function openFavoritesCarousel() {
  2950.           const favoritesGrid = getCachedFavorites();
  2951.           const favoriteItems = favoritesGrid ? favoritesGrid.querySelectorAll('.favorite-item') : [];
  2952.           if (favoriteItems.length > 0) {
  2953.             // Trouver le premier favori avec une action de vue
  2954.             const firstFavorite = favoriteItems[0];
  2955.             const viewBtn = firstFavorite.querySelector('.btn-view-favorite');
  2956.             if (viewBtn && viewBtn.onclick) {
  2957.               viewBtn.click();
  2958.             } else {
  2959.               // Fallback : extraire l'ID du data-id et utiliser viewImage
  2960.               const favoriteId = firstFavorite.dataset.id;
  2961.               if (favoriteId && typeof window.viewImage === 'function') {
  2962.                 window.viewImage(favoriteId, firstFavorite);
  2963.               }
  2964.             }
  2965.           } else {
  2966.             console.log('Aucun favori trouvé pour ouvrir le carrousel');
  2967.           }
  2968.         }
  2969.         
  2970.         // Fonction pour ouvrir le carrousel du jour actif
  2971.         function openActiveDayCarousel() {
  2972.           // Trouver le jour actuellement ouvert (avec classe 'show')
  2973.           let activeDay = document.querySelector('.collapse.show[id^="demP"]');
  2974.           
  2975.           // Si aucun jour n'est ouvert, prendre le dernier jour
  2976.           if (!activeDay) {
  2977.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  2978.             activeDay = allDays[allDays.length - 1]; // Dernier jour
  2979.           }
  2980.           
  2981.           if (!activeDay) {
  2982.             console.warn('Aucun jour trouvé pour ouvrir le carrousel');
  2983.             return;
  2984.           }
  2985.           
  2986.           // Trouver la première photo de ce jour
  2987.           const viewIcon = activeDay.querySelector('.view-icon[onclick*="viewImage"]');
  2988.           if (viewIcon) {
  2989.             // Extraire l'ID de la photo du onclick
  2990.             const onclickAttr = viewIcon.getAttribute('onclick');
  2991.             const match = onclickAttr.match(/viewImage\('(\d+)'/);
  2992.             if (match) {
  2993.               const imageId = match[1];
  2994.               // Ouvrir le slider avec cette image
  2995.               if (typeof window.viewImage === 'function') {
  2996.                 window.viewImage(imageId, viewIcon);
  2997.               }
  2998.             }
  2999.           } else {
  3000.             // Fallback : chercher dans tous les jours
  3001.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  3002.             for (let day of allDays) {
  3003.               const photoInDay = day.querySelector('.view-icon[onclick*="viewImage"]');
  3004.               if (photoInDay) {
  3005.                 photoInDay.click();
  3006.                 break;
  3007.               }
  3008.             }
  3009.           }
  3010.         }
  3011.         // Fonction pour afficher la vue normale des jours - globale
  3012.         window.showDaysView = function() {
  3013.           const sectionDays = document.querySelector('.section-days');
  3014.           const containerGallery = document.querySelector('.container--gallery');
  3015.           
  3016.           // Si la section days contient des vues spéciales (favoris/audios), les supprimer
  3017.           if (sectionDays && (sectionDays.innerHTML.includes('favorites-ecommerce-view') || sectionDays.innerHTML.includes('audios-view'))) {
  3018.             // Restaurer le contenu original si disponible
  3019.             if (originalDaysContent) {
  3020.               sectionDays.innerHTML = originalDaysContent;
  3021.             } else {
  3022.               // Fallback : recharger la page si pas de sauvegarde
  3023.               location.reload();
  3024.               return;
  3025.             }
  3026.           }
  3027.           
  3028.           // Restaurer la visibilité du container gallery
  3029.           if (containerGallery) {
  3030.             containerGallery.style.display = 'block';
  3031.           }
  3032.           
  3033.           // Appeler le filtre "all" pour restaurer l'affichage complet
  3034.           if (typeof window.filterContent === 'function') {
  3035.             window.filterContent('toutVoir');
  3036.           }
  3037.           
  3038.           // Mettre à jour l'état des boutons
  3039.           updatePhotosButtonState('days');
  3040.           updateAudioButtonState('days');
  3041.           updateFavoritesButtonState('days');
  3042.         }
  3043.         // Fonction pour générer les cartes de jours (alternative simple)
  3044.         function generateDaysCards() {
  3045.           // Cette fonction pourrait régénérer les cartes, mais pour simplifier
  3046.           // on recharge la page dans showDaysView()
  3047.           return '';
  3048.         }
  3049.         // Fonction pour revenir à la vue des jours
  3050.      
  3051.         // Fonction pour générer la grille des favoris
  3052.         function generateFavoritesGrid() {
  3053.           const favorites = getFavoriteItems();
  3054.           
  3055.           if (favorites.length === 0) {
  3056.             return `
  3057.               <div class="no-favorites">
  3058.                 <i class="bi bi-heart" style="font-size: 3rem; color: #ccc; margin-bottom: 1rem;"></i>
  3059.                 <h4>Aucun favori sélectionné</h4>
  3060.                 <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos photos préférées pour les ajouter ici.</p>
  3061.               </div>
  3062.             `;
  3063.           }
  3064.           
  3065.           return favorites.map(item => `
  3066.             <div class="favorite-item photo-item" data-id="${item.id}" style="position: relative; width: 100%; aspect-ratio: 1; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); background: #f0f0f0;">
  3067.               ${item.type === 'image' ? 
  3068.                 `<img src="${item.url}" alt="Photo favorite" loading="lazy" style="width: 100%; height: 100%; object-fit: cover; display: block;">` :
  3069.                 item.type === 'video' ? 
  3070.                 `<video src="${item.url}" poster="${item.thumbnail}" style="width: 100%; height: 100%; object-fit: cover; display: block;"></video>
  3071.                  <div class="video-overlay" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.3);"><i class="bi bi-play-circle" style="font-size: 48px; color: white;"></i></div>` :
  3072.                 `<div class="audio-item" style="width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background: linear-gradient(135deg, rgba(65, 162, 170, 0.1) 0%, rgba(245, 96, 64, 0.1) 100%);">
  3073.                    <i class="bi bi-mic-fill" style="font-size: 32px; color: #41A2AA; margin-bottom: 8px;"></i>
  3074.                    <span style="font-size: 14px; color: #6b7280; font-weight: 600;">Audio ${item.duration || '00:00'}</span>
  3075.                  </div>`
  3076.               }
  3077.               <!-- Actions overlay -->
  3078.               <div class="favorite-actions" style="position: absolute; top: 8px; right: 8px; display: flex; gap: 6px; opacity: 0; transition: opacity 0.3s ease;">
  3079.                 <button class="btn-view-favorite" onclick="viewFavorite('${item.id}')" title="Voir en grand" style="width: 32px; height: 32px; border-radius: 8px; background: rgba(255,255,255,0.9); border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
  3080.                   <i class="bi bi-eye" style="font-size: 14px; color: #41A2AA;"></i>
  3081.                 </button>
  3082.                 <button class="btn-remove-favorite" onclick="removeFavorite('${item.id}')" title="Retirer des favoris" style="width: 32px; height: 32px; border-radius: 8px; background: rgba(255,255,255,0.9); border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
  3083.                   <i class="bi bi-heart-fill" style="font-size: 14px; color: #f56040;"></i>
  3084.                 </button>
  3085.               </div>
  3086.             </div>
  3087.           `).join('');
  3088.         }
  3089.         // Fonction pour réorganiser la grille des favoris
  3090.         function reorganizeFavoritesGrid() {
  3091.           const favoritesGrid = document.getElementById('favoritesGrid');
  3092.           if (!favoritesGrid) return;
  3093.           
  3094.           const remainingItems = favoritesGrid.querySelectorAll('.favorite-item');
  3095.           if (remainingItems.length === 0) {
  3096.             // Afficher le message "Aucun favori"
  3097.             favoritesGrid.innerHTML = `
  3098.               <div class="no-favorites">
  3099.                 <i class="bi bi-heart" style="font-size: 3rem; color: #ccc; margin-bottom: 1rem;"></i>
  3100.                 <h4>Aucun favori sélectionné</h4>
  3101.                 <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos photos préférées pour les ajouter ici.</p>
  3102.               </div>
  3103.             `;
  3104.           } else {
  3105.             // Réorganiser les éléments restants avec une animation douce
  3106.             remainingItems.forEach((item, index) => {
  3107.               item.style.transition = 'all 0.3s ease';
  3108.               item.style.order = index;
  3109.             });
  3110.           }
  3111.         }
  3112.         // Fonction pour récupérer les éléments favoris
  3113.         function getFavoriteItems() {
  3114.           const favorites = [];
  3115.           
  3116.           // ⚡ OPTIMISÉ: Cache et parcours plus efficace des favoris
  3117.           const heartIcons = document.querySelectorAll('.heart-icon');
  3118.           for (let i = 0; i < heartIcons.length; i++) {
  3119.             const heartIcon = heartIcons[i];
  3120.             const heartFill = heartIcon.querySelector('.bi-heart-fill');
  3121.             if (heartFill) {
  3122.               const id = heartIcon.getAttribute('data-id');
  3123.               const path = heartIcon.getAttribute('data-path');
  3124.               const description = heartIcon.getAttribute('data-description');
  3125.               
  3126.               if (id && path) {
  3127.                 // Déterminer le type de média
  3128.                 let type = 'image';
  3129.                 let url = path;
  3130.                 let thumbnail = path;
  3131.                 
  3132.                 if (path.includes('.mp4') || path.includes('.mov') || path.includes('.avi')) {
  3133.                   type = 'video';
  3134.                   thumbnail = path.replace(/\.(mp4|mov|avi)$/i, '_thumb.jpg');
  3135.                 } else if (path.includes('.mp3') || path.includes('.wav') || path.includes('.m4a')) {
  3136.                   type = 'audio';
  3137.                   url = path;
  3138.                 }
  3139.                 
  3140.                 // Récupérer la date depuis le conteneur parent
  3141.                 const dateCard = heartIcon.closest('[id^="demP"]');
  3142.                 let date = 'Date inconnue';
  3143.                 if (dateCard) {
  3144.                   const dateElement = dateCard.querySelector('.full-date, .day-title');
  3145.                   if (dateElement) {
  3146.                     date = dateElement.textContent.trim();
  3147.                   }
  3148.                 }
  3149.                 
  3150.                 favorites.push({
  3151.                   id: id,
  3152.                   url: url,
  3153.                   thumbnail: thumbnail,
  3154.                   type: type,
  3155.                   date: date,
  3156.                   description: description || ''
  3157.                 });
  3158.               }
  3159.             }
  3160.           }
  3161.           
  3162.           // ⚡ OPTIMISÉ: Log conditionnel pour réduire l'impact performance
  3163.           if (favorites.length > 0) {
  3164.             console.log('Favoris trouvés:', favorites.length);
  3165.           }
  3166.           return favorites;
  3167.         }
  3168.         // Rendre les fonctions accessibles globalement
  3169.         window.viewFavorite = viewFavorite;
  3170.         window.removeFavorite = removeFavorite;
  3171.         window.getFavoriteItems = getFavoriteItems;
  3172.         // Fonction pour supprimer un favori
  3173.         function removeFavorite(id) {
  3174.          
  3175.           const heartIcon = document.querySelector(`[data-id="${id}"]`);
  3176.          
  3177.           if (heartIcon) {
  3178.             const heartFill = heartIcon.querySelector('.bi-heart-fill');
  3179.             if (heartFill && heartFill.classList) {
  3180.               heartFill.classList.remove('bi-heart-fill');
  3181.               heartFill.classList.add('bi-heart');
  3182.               heartFill.style.color = '';
  3183.             }
  3184.           }
  3185.           const favoriteItem = document.querySelector(`.favorite-item[data-id="${id}"]`);
  3186.         
  3187.           if (favoriteItem) {
  3188.             // Animation de disparition puis suppression complète
  3189.             favoriteItem.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
  3190.             favoriteItem.style.opacity = '0';
  3191.             favoriteItem.style.transform = 'scale(0.8)';
  3192.             
  3193.             setTimeout(() => {
  3194.               favoriteItem.remove();
  3195.               
  3196.               // Appeler la fonction métier si présente
  3197.               const heartIcon = document.querySelector(`#coeur${id}`);
  3198.               const sejourId = heartIcon && heartIcon.dataset.sejourId ? heartIcon.dataset.sejourId : '';
  3199.               supprimerFavoris(id, sejourId);
  3200.               
  3201.               // Mettre à jour les compteurs
  3202.               updateAllFavoriteCounters();
  3203.               
  3204.               // Réorganiser la grille si nécessaire
  3205.               reorganizeFavoritesGrid();
  3206.             }, 300);
  3207.       
  3208.      
  3209.    
  3210.   }
  3211.           
  3212.         }
  3213.         // Slider plein écran pour les favoris
  3214.         class FavoritesSlider {
  3215.           constructor() {
  3216.             this.currentIndex = 0;
  3217.             this.favorites = [];
  3218.             this.isOpen = false;
  3219.             this.touchStartX = 0;
  3220.             this.touchEndX = 0;
  3221.           }
  3222.           open(favoriteId) {
  3223.             console.log('Ouverture du slider pour:', favoriteId);
  3224.             
  3225.             // Récupérer tous les favoris images
  3226.             this.favorites = getFavoriteItems().filter(item => item.type === 'image');
  3227.             
  3228.             if (this.favorites.length === 0) {
  3229.               console.error('Aucun favori image trouvé');
  3230.               return;
  3231.             }
  3232.             // Trouver l'index de l'image courante
  3233.             this.currentIndex = this.favorites.findIndex(fav => fav.id === favoriteId);
  3234.             if (this.currentIndex === -1) this.currentIndex = 0;
  3235.             console.log('Index courant:', this.currentIndex, 'Total:', this.favorites.length);
  3236.             this.createSlider();
  3237.             this.showSlide(this.currentIndex);
  3238.             this.attachEvents();
  3239.             this.isOpen = true;
  3240.             // Animation d'ouverture immédiate pour favoris avec diagnostic
  3241.             setTimeout(() => {
  3242.               if (window.diagnoseSliderDisplay) {
  3243.                 window.diagnoseSliderDisplay('#favoritesSlider');
  3244.               }
  3245.             }, 50);
  3246.           }
  3247.           createSlider() {
  3248.             // ⚡ OPTIMISATION: Créer le slider seulement quand nécessaire
  3249.             if (document.querySelector('#favoritesSlider')) {
  3250.               return; // Déjà créé, ne pas recréer
  3251.             }
  3252.             
  3253.             // Structure HTML minimale et optimisée
  3254.             const sliderHTML = `
  3255.               <div class="favorites-slider" id="favoritesSlider" style="
  3256.                 position: fixed !important; 
  3257.                 top: 0 !important; 
  3258.                 left: 0 !important; 
  3259.                 width: 100% !important; 
  3260.                 height: 100% !important; 
  3261.                 background: rgba(0,0,0,0.95) !important; 
  3262.                 z-index: 9999 !important; 
  3263.                 display: flex !important; 
  3264.                 align-items: center !important; 
  3265.                 justify-content: center !important; 
  3266.                 opacity: 1 !important; 
  3267.                 visibility: visible !important;
  3268.               ">
  3269.                 <div class="slider-overlay"></div>
  3270.                 
  3271.                 <!-- Header avec contrôles -->
  3272.                 <div class="slider-header">
  3273.                   <div class="slider-controls">
  3274.                     <button class="slider-btn favorite-btn" title="Retirer des favoris">
  3275.                       <i class="bi bi-heart-fill"></i>
  3276.                     </button>
  3277.                     <button class="slider-btn zoom-btn" title="Zoom">
  3278.                       <i class="bi bi-zoom-in"></i>
  3279.                     </button>
  3280.                     <button class="slider-btn close-btn" title="Fermer">
  3281.                       <i class="bi bi-x-lg"></i>
  3282.                     </button>
  3283.                   </div>
  3284.                 </div>
  3285.                 <!-- Navigation -->
  3286.                 <button class="slider-nav prev-btn" title="Précédent">
  3287.                   <i class="bi bi-arrow-left-circle-fill"></i>
  3288.                 </button>
  3289.                 <button class="slider-nav next-btn" title="Suivant">
  3290.                   <i class="bi bi-arrow-right-circle-fill"></i>
  3291.                 </button>
  3292.                 <!-- Container des slides -->
  3293.                 <div class="slider-container">
  3294.                   <div class="slider-track" id="sliderTrack">
  3295.                     ${this.favorites.map((fav, index) => `
  3296.                       <div class="slide" data-index="${index}">
  3297.                         <div class="slide-content">
  3298.                           <img src="${fav.url}" alt="${fav.description || 'Photo favorite'}" 
  3299.                                loading="${index <= 2 ? 'eager' : 'lazy'}"
  3300.                                draggable="false">
  3301.                         </div>
  3302.                         <div class="slide-info">
  3303.                           <h4>${fav.description || 'Photo favorite'}</h4>
  3304.                           <p>${fav.date || 'Date inconnue'}</p>
  3305.                         </div>
  3306.                       </div>
  3307.                     `).join('')}
  3308.                   </div>
  3309.                 </div>
  3310.                 <!-- Thumbnails -->
  3311.                 <div class="slider-thumbnails">
  3312.                   ${this.favorites.map((fav, index) => `
  3313.                     <div class="thumbnail ${index === this.currentIndex ? 'active' : ''}" 
  3314.                          data-index="${index}">
  3315.                       <img src="${fav.url}" alt="Thumbnail ${index + 1}">
  3316.                     </div>
  3317.                   `).join('')}
  3318.                 </div>
  3319.               </div>
  3320.             `;
  3321.             // Injecter dans le DOM
  3322.             document.body.insertAdjacentHTML('beforeend', sliderHTML);
  3323.           }
  3324.           showSlide(index) {
  3325.             if (index < 0 || index >= this.favorites.length) return;
  3326.             this.currentIndex = index;
  3327.             const track = document.getElementById('sliderTrack');
  3328.             const translateX = -index * 100;
  3329.             
  3330.             track.style.transform = `translateX(${translateX}%)`;
  3331.             // ⚡ OPTIMISÉ: Éviter forEach sur querySelectorAll (mémoire)
  3332.             const thumbnails = document.querySelectorAll('.thumbnail');
  3333.             for (let i = 0; i < thumbnails.length; i++) {
  3334.               const thumb = thumbnails[i];
  3335.               if (thumb?.classList) {
  3336.                 thumb.classList.toggle('active', i === index);
  3337.               }
  3338.             }
  3339.             // Mettre à jour le bouton favori
  3340.             const currentFav = this.favorites[index];
  3341.             const favoriteBtn = document.querySelector('.slider-controls .favorite-btn');
  3342.             if (favoriteBtn && currentFav) {
  3343.               favoriteBtn.setAttribute('data-id', currentFav.id);
  3344.             }
  3345.           }
  3346.           nextSlide() {
  3347.             const nextIndex = (this.currentIndex + 1) % this.favorites.length;
  3348.             this.showSlide(nextIndex);
  3349.           }
  3350.           prevSlide() {
  3351.             const prevIndex = (this.currentIndex - 1 + this.favorites.length) % this.favorites.length;
  3352.             this.showSlide(prevIndex);
  3353.           }
  3354.           close() {
  3355.             if (!this.isOpen) return;
  3356.             const slider = document.querySelector('.favorites-slider');
  3357.             if (slider && slider.classList) {
  3358.               slider.classList.remove('active');
  3359.               
  3360.               setTimeout(() => {
  3361.                 slider.remove();
  3362.                 this.isOpen = false;
  3363.               }, 300);
  3364.             }
  3365.           }
  3366.           attachEvents() {
  3367.             const slider = document.getElementById('favoritesSlider');
  3368.             
  3369.             // Bouton fermer
  3370.             slider.querySelector('.close-btn').addEventListener('click', () => this.close());
  3371.             
  3372.             // Navigation
  3373.             slider.querySelector('.prev-btn').addEventListener('click', () => this.prevSlide());
  3374.             slider.querySelector('.next-btn').addEventListener('click', () => this.nextSlide());
  3375.             
  3376.             // ⚡ OPTIMISÉ: Event delegation au lieu de listeners multiples
  3377.             const thumbContainer = slider.querySelector('.thumbnails-container') || slider;
  3378.             if (thumbContainer && !thumbContainer.hasAttribute('data-thumb-delegated')) {
  3379.               thumbContainer.addEventListener('click', (e) => {
  3380.                 const thumb = e.target.closest('.thumbnail');
  3381.                 if (thumb) {
  3382.                   const thumbnails = slider.querySelectorAll('.thumbnail');
  3383.                   const index = Array.from(thumbnails).indexOf(thumb);
  3384.                   if (index !== -1) this.showSlide(index);
  3385.                 }
  3386.               });
  3387.               thumbContainer.setAttribute('data-thumb-delegated', 'true');
  3388.             }
  3389.             // Bouton favori
  3390.             slider.querySelector('.favorite-btn').addEventListener('click', (e) => {
  3391.               const favoriteId = e.currentTarget.getAttribute('data-id');
  3392.               if (favoriteId) {
  3393.                 removeFavorite(favoriteId);
  3394.                 // Recharger le slider avec les favoris mis à jour
  3395.                 setTimeout(() => {
  3396.                   this.close();
  3397.                   if (getFavoriteItems().filter(item => item.type === 'image').length > 0) {
  3398.                     this.open(this.favorites[0]?.id);
  3399.                   }
  3400.                 }, 100);
  3401.               }
  3402.             });
  3403.             // Clavier
  3404.             document.addEventListener('keydown', this.handleKeyboard.bind(this));
  3405.             
  3406.             // Clic sur overlay pour fermer
  3407.             slider.querySelector('.slider-overlay').addEventListener('click', () => this.close());
  3408.             // Touch/swipe sur mobile
  3409.             const track = slider.querySelector('.slider-track');
  3410.             track.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: true });
  3411.             track.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: true });
  3412.             // Zoom sur clic simple (5 niveaux) + double-clic (legacy)
  3413.             slider.querySelectorAll('.slide img').forEach(img => {
  3414.               let zoomLevel = 1;
  3415.               let isDragging = false;
  3416.               let startX, startY, translateX = 0, translateY = 0;
  3417.               
  3418.               // Zoom sur clic simple (nouveau système 5 niveaux)
  3419.               img.addEventListener('click', (e) => {
  3420.                 e.preventDefault();
  3421.                 e.stopPropagation();
  3422.                 
  3423.                 // Cycle à travers 5 niveaux de zoom
  3424.                 switch(zoomLevel) {
  3425.                   case 1: zoomLevel = 1.5; break;  // 1x → 1.5x
  3426.                   case 1.5: zoomLevel = 2; break;  // 1.5x → 2x
  3427.                   case 2: zoomLevel = 3; break;    // 2x → 3x
  3428.                   case 3: zoomLevel = 4; break;    // 3x → 4x
  3429.                   case 4: zoomLevel = 5; break;    // 4x → 5x
  3430.                   case 5: 
  3431.                     zoomLevel = 1;                 // 5x → 1x (reset)
  3432.                     translateX = 0;
  3433.                     translateY = 0;
  3434.                     break;
  3435.                 }
  3436.                 console.log('FavoritesSlider - Zoom niveau:', zoomLevel);
  3437.                 img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  3438.                 
  3439.                 // Curseur selon le niveau
  3440.                 if (zoomLevel === 1) {
  3441.                   img.style.cursor = 'zoom-in';
  3442.                   img.removeAttribute('data-zoomed');
  3443.                 } else if (zoomLevel === 5) {
  3444.                   img.style.cursor = 'zoom-out';
  3445.                   img.setAttribute('data-zoomed', zoomLevel);
  3446.                 } else {
  3447.                   img.style.cursor = 'zoom-in';
  3448.                   img.setAttribute('data-zoomed', zoomLevel);
  3449.                 }
  3450.               });
  3451.               
  3452.               // Drag pour déplacer l'image zoomée
  3453.               img.addEventListener('mousedown', (e) => {
  3454.                 if (zoomLevel > 1) {
  3455.                   isDragging = true;
  3456.                   startX = e.clientX - translateX;
  3457.                   startY = e.clientY - translateY;
  3458.                   img.style.cursor = 'grabbing';
  3459.                   e.preventDefault();
  3460.                   e.stopPropagation();
  3461.                 }
  3462.               });
  3463.               const handleMouseMove = (e) => {
  3464.                 if (isDragging && zoomLevel > 1) {
  3465.                   translateX = e.clientX - startX;
  3466.                   translateY = e.clientY - startY;
  3467.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  3468.                 }
  3469.               };
  3470.               const handleMouseUp = () => {
  3471.                 if (isDragging) {
  3472.                   isDragging = false;
  3473.                   if (zoomLevel === 1) {
  3474.                     img.style.cursor = 'zoom-in';
  3475.                   } else if (zoomLevel === 5) {
  3476.                     img.style.cursor = 'zoom-out';
  3477.                   } else {
  3478.                     img.style.cursor = 'zoom-in';
  3479.                   }
  3480.                 }
  3481.               };
  3482.               document.addEventListener('mousemove', handleMouseMove);
  3483.               document.addEventListener('mouseup', handleMouseUp);
  3484.               // Support tactile pour mobile
  3485.               img.addEventListener('touchstart', (e) => {
  3486.                 if (zoomLevel > 1 && e.touches.length === 1) {
  3487.                   isDragging = true;
  3488.                   const touch = e.touches[0];
  3489.                   startX = touch.clientX - translateX;
  3490.                   startY = touch.clientY - translateY;
  3491.                   e.preventDefault();
  3492.                 }
  3493.               });
  3494.               img.addEventListener('touchmove', (e) => {
  3495.                 if (isDragging && zoomLevel > 1 && e.touches.length === 1) {
  3496.                   const touch = e.touches[0];
  3497.                   translateX = touch.clientX - startX;
  3498.                   translateY = touch.clientY - startY;
  3499.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  3500.                   e.preventDefault();
  3501.                 }
  3502.               });
  3503.               img.addEventListener('touchend', () => {
  3504.                 isDragging = false;
  3505.               });
  3506.               // Initialiser le curseur
  3507.               img.style.cursor = 'zoom-in';
  3508.               
  3509.               // Garder aussi le double-clic pour compatibilité (legacy)
  3510.               img.addEventListener('dblclick', this.handleZoom.bind(this));
  3511.             });
  3512.           }
  3513.           handleKeyboard(e) {
  3514.             if (!this.isOpen) return;
  3515.             
  3516.             switch(e.key) {
  3517.               case 'Escape':
  3518.                 this.close();
  3519.                 break;
  3520.               case 'ArrowLeft':
  3521.                 e.preventDefault();
  3522.                 this.prevSlide();
  3523.                 break;
  3524.               case 'ArrowRight':
  3525.                 e.preventDefault();
  3526.                 this.nextSlide();
  3527.                 break;
  3528.             }
  3529.           }
  3530.           handleTouchStart(e) {
  3531.             this.touchStartX = e.changedTouches[0].screenX;
  3532.           }
  3533.           handleTouchEnd(e) {
  3534.             this.touchEndX = e.changedTouches[0].screenX;
  3535.             this.handleSwipe();
  3536.           }
  3537.           handleSwipe() {
  3538.             const swipeThreshold = 50;
  3539.             const diff = this.touchStartX - this.touchEndX;
  3540.             if (Math.abs(diff) > swipeThreshold) {
  3541.               if (diff > 0) {
  3542.                 this.nextSlide(); // Swipe left -> next
  3543.               } else {
  3544.                 this.prevSlide(); // Swipe right -> prev
  3545.               }
  3546.             }
  3547.           }
  3548.           handleZoom(e) {
  3549.             const img = e.target;
  3550.             
  3551.             // Vérifier que l'élément et ses propriétés existent
  3552.             if (!img || !img.classList) {
  3553.               console.warn('Élément image invalide pour le zoom');
  3554.               return;
  3555.             }
  3556.             
  3557.             // Gérer les différents niveaux de zoom
  3558.             if (img.classList.contains('zoom-4x')) {
  3559.               // Retour à la taille normale
  3560.               img.classList.remove('zoom-4x', 'zoom-3x', 'zoom-2x', 'zoomed');
  3561.               img.style.cursor = 'pointer';
  3562.               img.style.transform = 'scale(1)'; // Réinitialiser la position
  3563.             } else if (img.classList.contains('zoom-3x')) {
  3564.               // Passer au zoom 4x
  3565.               img.classList.remove('zoom-3x');
  3566.               img.classList.add('zoom-4x');
  3567.               img.style.cursor = 'zoom-out';
  3568.               img.style.transform = 'scale(4)'; // Réinitialiser la position
  3569.             } else if (img.classList.contains('zoom-2x')) {
  3570.               // Passer au zoom 3x
  3571.               img.classList.remove('zoom-2x');
  3572.               img.classList.add('zoom-3x');
  3573.               img.style.cursor = 'zoom-in';
  3574.               img.style.transform = 'scale(3)'; // Réinitialiser la position
  3575.             } else if (img.classList.contains('zoomed')) {
  3576.               // Passer au zoom 2x
  3577.               img.classList.remove('zoomed');
  3578.               img.classList.add('zoom-2x');
  3579.               img.style.cursor = 'zoom-in';
  3580.               img.style.transform = 'scale(2)'; // Réinitialiser la position
  3581.             } else {
  3582.               // Premier zoom (1.5x)
  3583.               img.classList.add('zoomed');
  3584.               img.style.cursor = 'zoom-in';
  3585.               img.style.transform = 'scale(1.5)'; // Réinitialiser la position
  3586.             }
  3587.           }
  3588.         }
  3589.         // Slider universel pour toutes les images (favoris + galerie normale)
  3590.         class UniversalImageSlider {
  3591.           constructor() {
  3592.             this.currentIndex = 0;
  3593.             this.images = [];
  3594.             this.isOpen = false;
  3595.             this.touchStartX = 0;
  3596.             this.touchEndX = 0;
  3597.           }
  3598.           // Ouvrir avec une image spécifique depuis les favoris
  3599.           openFromFavorites(favoriteId) {
  3600.             console.log('Ouverture du slider depuis favoris:', favoriteId);
  3601.             
  3602.             // Récupérer tous les favoris images
  3603.             this.images = getFavoriteItems().filter(item => item.type === 'image');
  3604.             
  3605.             if (this.images.length === 0) {
  3606.               console.error('Aucun favori image trouvé');
  3607.               return;
  3608.             }
  3609.             // Trouver l'index de l'image courante
  3610.             this.currentIndex = this.images.findIndex(img => img.id === favoriteId);
  3611.             if (this.currentIndex === -1) this.currentIndex = 0;
  3612.             this.openSlider();
  3613.           }
  3614.           // Ouvrir avec une image spécifique depuis la galerie normale
  3615.           openFromGallery(imageId, dayContainer) {
  3616.             console.log('Ouverture du slider depuis galerie:', imageId, dayContainer);
  3617.             
  3618.             // Récupérer toutes les images du jour courant
  3619.             const dayImages = [];
  3620.             
  3621.             // Chercher dans le container du jour (peut être .dynamic-card ou autre)
  3622.             let photoItems;
  3623.             if (dayContainer) {
  3624.               photoItems = dayContainer.querySelectorAll('.photo-item');
  3625.             } else {
  3626.               // Fallback: chercher dans tout le document
  3627.               photoItems = document.querySelectorAll('.photo-item');
  3628.             }
  3629.             
  3630.             console.log('Photo items trouvés:', photoItems.length);
  3631.             
  3632.             photoItems.forEach(item => {
  3633.               const img = item.querySelector('img');
  3634.               const heartIcon = item.querySelector('.heart-icon');
  3635.               
  3636.               console.log('Processing item:', {
  3637.                 img: !!img,
  3638.                 heartIcon: !!heartIcon,
  3639.                 imgSrc: img ? img.src : null,
  3640.                 heartIconId: heartIcon ? heartIcon.getAttribute('data-id') : null
  3641.               });
  3642.               
  3643.               if (img && heartIcon) {
  3644.                 dayImages.push({
  3645.                   id: heartIcon.getAttribute('data-id'),
  3646.                   url: heartIcon.getAttribute('data-path') || img.src,
  3647.                   description: heartIcon.getAttribute('data-description') || img.alt || 'Photo',
  3648.                   date: '', // Pas de date spécifique pour les images de galerie
  3649.                   type: 'image'
  3650.                 });
  3651.               }
  3652.             });
  3653.             console.log('Images trouvées:', dayImages);
  3654.             this.images = dayImages;
  3655.             
  3656.             if (this.images.length === 0) {
  3657.               console.error('Aucune image trouvée dans ce jour');
  3658.               // Essayer de créer au moins l'image courante
  3659.               const currentHeartIcon = document.querySelector(`[data-id="${imageId}"]`);
  3660.               if (currentHeartIcon) {
  3661.                 const currentImg = currentHeartIcon.closest('.photo-item').querySelector('img');
  3662.                 if (currentImg) {
  3663.                   this.images = [{
  3664.                     id: imageId,
  3665.                     url: currentHeartIcon.getAttribute('data-path') || currentImg.src,
  3666.                     description: currentHeartIcon.getAttribute('data-description') || currentImg.alt || 'Photo',
  3667.                     date: '',
  3668.                     type: 'image'
  3669.                   }];
  3670.                   console.log('Image de fallback créée:', this.images);
  3671.                 }
  3672.               }
  3673.               
  3674.               if (this.images.length === 0) {
  3675.                 return;
  3676.               }
  3677.             }
  3678.             // Trouver l'index de l'image courante
  3679.             this.currentIndex = this.images.findIndex(img => img.id === imageId);
  3680.             if (this.currentIndex === -1) this.currentIndex = 0;
  3681.             console.log('Index trouvé:', this.currentIndex);
  3682.             this.openSlider();
  3683.           }
  3684.           openSlider() {
  3685.             console.log('=== openSlider appelée ===');
  3686.             console.log('Index courant:', this.currentIndex, 'Total:', this.images.length);
  3687.             console.log('Images à afficher:', this.images);
  3688.             // Supprimer tout slider existant
  3689.             const existingSlider = document.querySelector('.universal-slider');
  3690.             if (existingSlider) {
  3691.               console.log('Suppression du slider existant');
  3692.               existingSlider.remove();
  3693.             }
  3694.             this.createSlider();
  3695.             this.showSlide(this.currentIndex);
  3696.             this.attachEvents();
  3697.             this.isOpen = true;
  3698.               // Animation d'ouverture immédiate et garantie avec diagnostic
  3699.               setTimeout(() => {
  3700.                 if (window.diagnoseSliderDisplay) {
  3701.                   window.diagnoseSliderDisplay('#universalSlider');
  3702.                 }
  3703.               }, 50);
  3704.           }
  3705.           createSlider() {
  3706.             // ⚡ OPTIMISATION: Créer seulement si nécessaire
  3707.             if (document.querySelector('#universalSlider')) {
  3708.               return; // Déjà créé
  3709.             }
  3710.             
  3711.             console.log('Création slider pour', this.images.length, 'images');
  3712.             
  3713.             // HTML optimisé et minimal
  3714.             const sliderHTML = `
  3715.               <div class="universal-slider" id="universalSlider" style="
  3716.                 position: fixed !important; 
  3717.                 top: 0 !important; 
  3718.                 left: 0 !important; 
  3719.                 width: 100% !important; 
  3720.                 height: 100% !important; 
  3721.                 background: rgba(0,0,0,0.95) !important; 
  3722.                 z-index: 9999 !important; 
  3723.                 display: flex !important; 
  3724.                 align-items: center !important; 
  3725.                 justify-content: center !important; 
  3726.                 opacity: 1 !important; 
  3727.                 visibility: visible !important;
  3728.               ">
  3729.                 <div class="slider-overlay" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: pointer;"></div>
  3730.                 
  3731.                 <!-- Header avec contrôles -->
  3732.                 <div class="slider-header" style="
  3733.                   position: absolute; 
  3734.                   top: 20px; 
  3735.                   right: 20px; 
  3736.                   display: flex; 
  3737.                   align-items: center; 
  3738.                   gap: 15px; 
  3739.                   z-index: 10001;
  3740.                 ">
  3741.                   <button class="slider-btn close-btn" title="Fermer" style="
  3742.                     background: rgba(255,255,255,0.2); 
  3743.                     border: none; 
  3744.                     color: white; 
  3745.                     width: 40px; 
  3746.                     height: 40px; 
  3747.                     border-radius: 50%; 
  3748.                     cursor: pointer; 
  3749.                     display: flex; 
  3750.                     align-items: center; 
  3751.                     justify-content: center;
  3752.                     transition: background 0.2s ease;
  3753.                   ">
  3754.                     <i class="bi bi-x-lg"></i>
  3755.                   </button>
  3756.                 </div>
  3757.                 <!-- Navigation -->
  3758.                 <button class="slider-nav prev-btn" title="Précédent" style="
  3759.                   position: absolute; 
  3760.                   left: 20px; 
  3761.                   top: 50%; 
  3762.                   transform: translateY(-50%); 
  3763.                   background: rgba(255,255,255,0.2); 
  3764.                   border: none; 
  3765.                   color: white; 
  3766.                   width: 50px; 
  3767.                   height: 50px; 
  3768.                   border-radius: 50%; 
  3769.                   cursor: pointer; 
  3770.                   display: ${this.images.length > 1 ? 'flex' : 'none'}; 
  3771.                   align-items: center; 
  3772.                   justify-content: center; 
  3773.                   z-index: 10001;
  3774.                   transition: background 0.2s ease;
  3775.                 ">
  3776.                   <i class="bi bi-arrow-left-circle-fill" style="font-size: 20px;"></i>
  3777.                 </button>
  3778.                 <button class="slider-nav next-btn" title="Suivant" style="
  3779.                   position: absolute; 
  3780.                   right: 20px; 
  3781.                   top: 50%; 
  3782.                   transform: translateY(-50%); 
  3783.                   background: rgba(255,255,255,0.2); 
  3784.                   border: none; 
  3785.                   color: white; 
  3786.                   width: 50px; 
  3787.                   height: 50px; 
  3788.                   border-radius: 50%; 
  3789.                   cursor: pointer; 
  3790.                   display: ${this.images.length > 1 ? 'flex' : 'none'}; 
  3791.                   align-items: center; 
  3792.                   justify-content: center; 
  3793.                   z-index: 10001;
  3794.                   transition: background 0.2s ease;
  3795.                 ">
  3796.                   <i class="bi bi-arrow-right-circle-fill" style="font-size: 20px;"></i>
  3797.                 </button>
  3798.                 <!-- Container des slides -->
  3799.                 <div class="slider-container" style="
  3800.                   position: relative; 
  3801.                   width: 90%; 
  3802.                   height: 90%; 
  3803.                   display: flex; 
  3804.                   align-items: center; 
  3805.                   justify-content: center; 
  3806.                   z-index: 10000;
  3807.                 ">
  3808.                   <div class="slider-track" id="universalSliderTrack" style="
  3809.                     position: relative; 
  3810.                     width: 100%; 
  3811.                     height: 100%; 
  3812.                     display: flex; 
  3813.                     align-items: center; 
  3814.                     justify-content: center;
  3815.                     overflow: hidden;
  3816.                   ">
  3817.                     ${this.images.map((img, index) => `
  3818.                       <div class="slide" data-index="${index}" style="
  3819.                         position: absolute; 
  3820.                         width: 100%; 
  3821.                         height: 100%; 
  3822.                         display: ${index === 0 ? 'flex' : 'none'}; 
  3823.                         align-items: center; 
  3824.                         justify-content: center; 
  3825.                         flex-direction: column;
  3826.                       ">
  3827.                         <div class="slide-content" style="
  3828.                           position: relative; 
  3829.                           max-width: 100%; 
  3830.                           max-height: 90%; 
  3831.                           display: flex; 
  3832.                           align-items: center; 
  3833.                           justify-content: center;
  3834.                         ">
  3835.                           <img src="${img.url}" alt="${img.description || 'Photo'}" 
  3836.                                loading="${index <= 2 ? 'eager' : 'lazy'}"
  3837.                                draggable="false"
  3838.                                style="
  3839.                                  max-width: 100%; 
  3840.                                  max-height: 100%; 
  3841.                                  object-fit: contain; 
  3842.                                  cursor: zoom-in;
  3843.                                  transition: transform 0.3s ease;
  3844.                                  user-select: none;
  3845.                                ">
  3846.                         </div>
  3847.                         ${img.description ? `
  3848.                         <div class="slide-info" style="
  3849.                           position: absolute; 
  3850.                           bottom: 60px; 
  3851.                           left: 50%; 
  3852.                           transform: translateX(-50%); 
  3853.                           text-align: center; 
  3854.                           color: white; 
  3855.                           background: rgba(0,0,0,0.6); 
  3856.                           padding: 8px 16px; 
  3857.                           border-radius: 20px; 
  3858.                           max-width: 80%;
  3859.                           font-size: 14px;
  3860.                         ">
  3861.                           ${img.description}
  3862.                         </div>
  3863.                         ` : ''}
  3864.                       </div>
  3865.                     `).join('')}
  3866.                   </div>
  3867.                 </div>
  3868.                 <!-- Galerie de thumbnails révolutionnaire -->
  3869.                 <div class="cinema-gallery" style="
  3870.                   position: absolute; 
  3871.                   bottom: 0; 
  3872.                   left: 0; 
  3873.                   right: 0;
  3874.                   height: 180px;
  3875.                   background: linear-gradient(0deg, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.7) 50%, transparent 100%);
  3876.                   z-index: 10001;
  3877.                   display: flex;
  3878.                   flex-direction: column;
  3879.                   justify-content: flex-end;
  3880.                   padding: 0 20px 20px;
  3881.                 ">
  3882.                   
  3883.                   <!-- Info header avec compteur élégant -->
  3884.                   <div class="gallery-header" style="
  3885.                     display: flex;
  3886.                     justify-content: space-between;
  3887.                     align-items: center;
  3888.                     margin-bottom: 15px;
  3889.                   ">
  3890.                     <div class="photo-counter" style="
  3891.                       color: white;
  3892.                       font-size: 16px;
  3893.                       font-weight: 600;
  3894.                       display: flex;
  3895.                       align-items: center;
  3896.                       gap: 8px;
  3897.                     ">
  3898.                       <div style="
  3899.                         width: 8px;
  3900.                         height: 8px;
  3901.                         background: #10b981;
  3902.                         border-radius: 50%;
  3903.                         animation: pulse 2s infinite;
  3904.                       "></div>
  3905.                       <span>${(this.currentIndex || 0) + 1}</span>
  3906.                       <span style="opacity: 0.6;">sur</span>
  3907.                       <span>${this.images.length}</span>
  3908.                     </div>
  3909.                     <div class="gallery-controls" style="
  3910.                       display: flex;
  3911.                       gap: 12px;
  3912.                       align-items: center;
  3913.                     ">
  3914.                       <div style="color: rgba(255,255,255,0.7); font-size: 12px;">
  3915.                         Clic = Zoom 5x • Drag = Déplacer
  3916.                       </div>
  3917.                     </div>
  3918.                   </div>
  3919.                   
  3920.                   <!-- Carrousel de thumbnails cinématographique -->
  3921.                   <div class="cinema-carousel" style="
  3922.                     position: relative;
  3923.                     height: 80px;
  3924.                     overflow: hidden;
  3925.                   ">
  3926.                     <!-- Container scrollable -->
  3927.                     <div class="carousel-track" id="carouselTrack" style="
  3928.                       display: flex;
  3929.                       gap: 12px;
  3930.                       height: 100%;
  3931.                       overflow-x: auto;
  3932.                       overflow-y: hidden;
  3933.                       scroll-behavior: smooth;
  3934.                       scrollbar-width: none;
  3935.                       -ms-overflow-style: none;
  3936.                       padding: 0 50px;
  3937.                     ">
  3938.                       ${this.images.map((img, index) => `
  3939.                         <div class="cinema-thumb ${index === 0 ? 'active' : ''}" 
  3940.                              data-index="${index}" 
  3941.                              style="
  3942.                                flex: 0 0 auto;
  3943.                                width: ${index === 0 ? '100px' : '70px'};
  3944.                                height: 70px;
  3945.                                border-radius: 8px;
  3946.                                overflow: hidden;
  3947.                                cursor: pointer;
  3948.                                position: relative;
  3949.                                transition: transform 0.2s ease, opacity 0.2s ease;
  3950.                                transform: ${index === 0 ? 'translateY(-4px)' : 'translateY(0)'};
  3951.                                opacity: ${index === 0 ? '1' : '0.7'};
  3952.                                border: ${index === 0 ? '2px solid #10b981' : '1px solid rgba(255,255,255,0.3)'};
  3953.                              "
  3954.                              onmouseenter="
  3955.                                if (${index} !== (window.universalSlider?.currentIndex || 0)) {
  3956.                                  this.style.opacity = '0.9';
  3957.                                }
  3958.                              "
  3959.                              onmouseleave="
  3960.                                if (${index} !== (window.universalSlider?.currentIndex || 0)) {
  3961.                                  this.style.opacity = '0.7';
  3962.                                }
  3963.                              ">
  3964.                           
  3965.                           <!-- Image principale -->
  3966.                           <img src="${img.url}" 
  3967.                                alt="${img.description || 'Photo'}" 
  3968.                                loading="lazy" 
  3969.                                decoding="async"
  3970.                                style="
  3971.                                  width: 100%; 
  3972.                                  height: 100%; 
  3973.                                  object-fit: cover;
  3974.                                ">
  3975.                           
  3976.                           <!-- Numéro simplifié -->
  3977.                           <div class="thumb-number" style="
  3978.                             position: absolute;
  3979.                             top: 4px;
  3980.                             right: 4px;
  3981.                             background: ${index === 0 ? '#10b981' : 'rgba(0,0,0,0.8)'};
  3982.                             color: white;
  3983.                             font-size: 10px;
  3984.                             font-weight: 500;
  3985.                             padding: 2px 5px;
  3986.                             border-radius: 4px;
  3987.                             min-width: 16px;
  3988.                             text-align: center;
  3989.                           ">
  3990.                             ${index + 1}
  3991.                           </div>
  3992.                           
  3993.                           <!-- Indicateur actif simplifié -->
  3994.                           ${index === 0 ? `
  3995.                           <div class="active-indicator" style="
  3996.                             position: absolute;
  3997.                             bottom: -1px;
  3998.                             left: 50%;
  3999.                             transform: translateX(-50%);
  4000.                             width: 16px;
  4001.                             height: 2px;
  4002.                             background: #10b981;
  4003.                             border-radius: 1px;
  4004.                           "></div>
  4005.                           ` : ''}
  4006.                           
  4007.                           <!-- Preview au hover pour les non-actifs -->
  4008.                           ${index !== 0 ? `
  4009.                           <div class="hover-preview" style="
  4010.                             position: absolute;
  4011.                             bottom: -40px;
  4012.                             left: 50%;
  4013.                             transform: translateX(-50%);
  4014.                             background: rgba(0,0,0,0.9);
  4015.                             color: white;
  4016.                             font-size: 11px;
  4017.                             padding: 4px 8px;
  4018.                             border-radius: 6px;
  4019.                             opacity: 0;
  4020.                             pointer-events: none;
  4021.                             transition: all 0.3s ease;
  4022.                             white-space: nowrap;
  4023.                             z-index: 1000;
  4024.                           ">
  4025.                             Photo ${index + 1}
  4026.                           </div>
  4027.                           ` : ''}
  4028.                         </div>
  4029.                       `).join('')}
  4030.                     </div>
  4031.                     
  4032.                     <!-- Gradients de fade sur les côtés -->
  4033.                     <div style="
  4034.                       position: absolute;
  4035.                       left: 0;
  4036.                       top: 0;
  4037.                       bottom: 0;
  4038.                       width: 50px;
  4039.                       background: linear-gradient(90deg, rgba(0,0,0,0.8), transparent);
  4040.                       pointer-events: none;
  4041.                       z-index: 1;
  4042.                     "></div>
  4043.                     <div style="
  4044.                       position: absolute;
  4045.                       right: 0;
  4046.                       top: 0;
  4047.                       bottom: 0;
  4048.                       width: 50px;
  4049.                       background: linear-gradient(-90deg, rgba(0,0,0,0.8), transparent);
  4050.                       pointer-events: none;
  4051.                       z-index: 1;
  4052.                     "></div>
  4053.                   </div>
  4054.                 </div>
  4055.                 <!-- Animations CSS Optimisées -->
  4056.                 <style>
  4057.                   /* Optimisation: animations réduites et ciblées */
  4058.                   @keyframes pulse {
  4059.                     0%, 100% { opacity: 1; }
  4060.                     50% { opacity: 0.7; }
  4061.                   }
  4062.                   
  4063.                   /* Suppression du glow coûteux - remplacé par border simple */
  4064.                   .cinema-thumb.active .active-indicator {
  4065.                     background: #10b981;
  4066.                     animation: none; /* Suppression de l'animation glow */
  4067.                   }
  4068.                   
  4069.                   /* Hover optimisé - moins d'effets */
  4070.                   .cinema-thumb:hover .hover-preview {
  4071.                     opacity: 1 !important;
  4072.                     bottom: -35px !important;
  4073.                   }
  4074.                   
  4075.                   /* Masquer scrollbar */
  4076.                   .carousel-track::-webkit-scrollbar {
  4077.                     display: none;
  4078.                   }
  4079.                   
  4080.                   /* Optimisation: will-change pour les éléments animés */
  4081.                   .cinema-thumb {
  4082.                     will-change: transform, filter;
  4083.                   }
  4084.                   
  4085.                   .progress-bar {
  4086.                     will-change: width;
  4087.                   }
  4088.                 </style>
  4089.               </div>
  4090.             `;
  4091.             // Injecter dans le DOM
  4092.             console.log('Ajout du slider au DOM');
  4093.             document.body.insertAdjacentHTML('beforeend', sliderHTML);
  4094.             
  4095.             // Vérifier que le slider a été ajouté
  4096.             const addedSlider = document.querySelector('#universalSlider');
  4097.             console.log('Slider ajouté:', !!addedSlider);
  4098.             if (addedSlider) {
  4099.               console.log('Slider trouvé dans le DOM');
  4100.               console.log('Styles du slider:', {
  4101.                 display: addedSlider.style.display,
  4102.                 opacity: addedSlider.style.opacity,
  4103.                 visibility: addedSlider.style.visibility,
  4104.                 zIndex: addedSlider.style.zIndex,
  4105.                 position: addedSlider.style.position
  4106.               });
  4107.               console.log('Slider dans viewport:', addedSlider.getBoundingClientRect());
  4108.             } else {
  4109.               console.error('Erreur: slider non trouvé après ajout');
  4110.             }
  4111.           }
  4112.           showSlide(index) {
  4113.             console.log('=== showSlide appelée ===', 'index:', index, 'total:', this.images.length);
  4114.             if (index < 0 || index >= this.images.length) {
  4115.               console.log('Index invalide, arrêt');
  4116.               return;
  4117.             }
  4118.             this.currentIndex = index;
  4119.             // Masquer toutes les slides et afficher seulement la courante
  4120.             const slides = document.querySelectorAll('#universalSlider .slide');
  4121.             console.log('Slides trouvées:', slides.length);
  4122.             slides.forEach((slide, i) => {
  4123.               slide.style.display = i === index ? 'flex' : 'none';
  4124.               console.log(`Slide ${i}:`, i === index ? 'visible' : 'cachée');
  4125.             });
  4126.             // Mettre à jour le compteur
  4127.             const currentSlideSpan = document.querySelector('#universalSlider .current-slide');
  4128.             if (currentSlideSpan) {
  4129.               currentSlideSpan.textContent = index + 1;
  4130.               console.log('Compteur mis à jour:', index + 1);
  4131.             }
  4132.             // Mettre à jour les boutons de navigation
  4133.             const prevBtn = document.querySelector('#universalSlider .prev-btn');
  4134.             const nextBtn = document.querySelector('#universalSlider .next-btn');
  4135.             
  4136.             if (prevBtn) {
  4137.               prevBtn.style.opacity = index > 0 ? '1' : '0.5';
  4138.             }
  4139.             if (nextBtn) {
  4140.               nextBtn.style.opacity = index < this.images.length - 1 ? '1' : '0.5';
  4141.             }
  4142.             // Mettre à jour la galerie cinématographique
  4143.             this.updateCinemaGallery(index);
  4144.             // Mettre à jour le bouton favori
  4145.             this.updateFavoriteButton();
  4146.           }
  4147.           updateCinemaGallery(activeIndex) {
  4148.             // Mettre à jour la progress bar (optimisé)
  4149.             const progressBar = document.querySelector('.progress-bar');
  4150.             if (progressBar) {
  4151.               progressBar.style.width = `${(activeIndex + 1) / this.images.length * 100}%`;
  4152.             }
  4153.             // Mettre à jour le compteur (optimisé)
  4154.             const counter = document.querySelector('.photo-counter span');
  4155.             if (counter) {
  4156.               counter.textContent = activeIndex + 1;
  4157.             }
  4158.             // Mettre à jour les thumbnails - optimisé avec moins de calculs
  4159.             const cinemaThumbs = document.querySelectorAll('.cinema-thumb');
  4160.             cinemaThumbs.forEach((thumb, i) => {
  4161.               const isActive = i === activeIndex;
  4162.               
  4163.               // Optimisation: changements minimaux
  4164.               if (isActive) {
  4165.                 thumb.style.width = '100px';
  4166.                 thumb.style.transform = 'translateY(-4px)';
  4167.                 thumb.style.opacity = '1';
  4168.                 thumb.style.borderColor = '#10b981';
  4169.                 thumb.style.borderWidth = '2px';
  4170.                 
  4171.                 // Numéro actif - simplifié
  4172.                 const number = thumb.querySelector('.thumb-number');
  4173.                 if (number) {
  4174.                   number.style.background = '#10b981';
  4175.                 }
  4176.               } else {
  4177.                 thumb.style.width = '70px';
  4178.                 thumb.style.transform = 'translateY(0)';
  4179.                 thumb.style.opacity = '0.7';
  4180.                 thumb.style.borderColor = 'rgba(255,255,255,0.3)';
  4181.                 thumb.style.borderWidth = '1px';
  4182.                 
  4183.                 // Numéro inactif - simplifié
  4184.                 const number = thumb.querySelector('.thumb-number');
  4185.                 if (number) {
  4186.                   number.style.background = 'rgba(0,0,0,0.8)';
  4187.                 }
  4188.               }
  4189.             });
  4190.             // Auto-scroll optimisé avec throttling
  4191.             if (!this.scrollTimeout) {
  4192.               this.scrollTimeout = setTimeout(() => {
  4193.                 const track = document.getElementById('carouselTrack');
  4194.                 const activeThumb = document.querySelector(`.cinema-thumb[data-index="${activeIndex}"]`);
  4195.                 if (track && activeThumb) {
  4196.                   const scrollLeft = activeThumb.offsetLeft - (track.clientWidth / 2) + (activeThumb.clientWidth / 2);
  4197.                   track.scrollTo({
  4198.                     left: scrollLeft,
  4199.                     behavior: 'smooth'
  4200.                   });
  4201.                 }
  4202.                 this.scrollTimeout = null;
  4203.               }, 50); // Throttle à 50ms
  4204.             }
  4205.           }
  4206.           updateFavoriteButton() {
  4207.             const currentImage = this.images[this.currentIndex];
  4208.             const favoriteBtn = document.querySelector('.slider-controls .favorite-btn');
  4209.             
  4210.             if (favoriteBtn && currentImage) {
  4211.               favoriteBtn.setAttribute('data-id', currentImage.id);
  4212.               
  4213.               // Vérifier si l'image est déjà en favoris
  4214.               const heartIcon = document.querySelector(`[data-id="${currentImage.id}"]`);
  4215.               const isFavorite = heartIcon && heartIcon.querySelector('i.bi-heart-fill');
  4216.               
  4217.               const icon = favoriteBtn.querySelector('i');
  4218.               if (isFavorite) {
  4219.                 icon.className = 'bi bi-heart-fill';
  4220.                 favoriteBtn.style.color = '#e91e63';
  4221.               } else {
  4222.                 icon.className = 'bi bi-heart';
  4223.                 favoriteBtn.style.color = 'white';
  4224.               }
  4225.             }
  4226.           }
  4227.           nextSlide() {
  4228.             const nextIndex = (this.currentIndex + 1) % this.images.length;
  4229.             this.showSlide(nextIndex);
  4230.           }
  4231.           prevSlide() {
  4232.             const prevIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
  4233.             this.showSlide(prevIndex);
  4234.           }
  4235.           close() {
  4236.             if (!this.isOpen) return;
  4237.             const slider = document.querySelector('.universal-slider');
  4238.             if (slider) {
  4239.               slider.classList.remove('active');
  4240.               
  4241.               setTimeout(() => {
  4242.                 slider.remove();
  4243.                 this.isOpen = false;
  4244.               }, 300);
  4245.             }
  4246.           }
  4247.           attachEvents() {
  4248.             console.log('=== attachEvents appelée ===');
  4249.             const slider = document.getElementById('universalSlider');
  4250.             if (!slider) {
  4251.               console.error('Slider non trouvé pour attachEvents');
  4252.               return;
  4253.             }
  4254.             console.log('Slider trouvé pour attachEvents');
  4255.             
  4256.             // Bouton fermer
  4257.             const closeBtn = slider.querySelector('.close-btn');
  4258.             if (closeBtn) {
  4259.               console.log('Bouton fermer trouvé');
  4260.               closeBtn.addEventListener('click', () => {
  4261.                 console.log('Clic fermer');
  4262.                 this.close();
  4263.               });
  4264.             }
  4265.             
  4266.             // Navigation
  4267.             const prevBtn = slider.querySelector('.prev-btn');
  4268.             const nextBtn = slider.querySelector('.next-btn');
  4269.             if (prevBtn) {
  4270.               console.log('Bouton précédent trouvé');
  4271.               prevBtn.addEventListener('click', () => {
  4272.                 console.log('Clic précédent');
  4273.                 this.prevSlide();
  4274.               });
  4275.             }
  4276.             if (nextBtn) {
  4277.               console.log('Bouton suivant trouvé');
  4278.               nextBtn.addEventListener('click', () => {
  4279.                 console.log('Clic suivant');
  4280.                 this.nextSlide();
  4281.               });
  4282.             }
  4283.             // Navigation cinématographique des thumbnails
  4284.             const cinemaThumbs = slider.querySelectorAll('.cinema-thumb');
  4285.             cinemaThumbs.forEach((thumb, index) => {
  4286.               thumb.addEventListener('click', () => {
  4287.                 console.log('Clic cinema thumbnail:', index);
  4288.                 this.showSlide(index);
  4289.                 this.updateCinemaGallery(index);
  4290.               });
  4291.             });
  4292.             console.log('Cinema thumbnails cliquables:', cinemaThumbs.length);
  4293.             // Clic sur overlay pour fermer
  4294.             const overlay = slider.querySelector('.slider-overlay');
  4295.             if (overlay) {
  4296.               overlay.addEventListener('click', () => {
  4297.                 console.log('Clic overlay');
  4298.                 this.close();
  4299.               });
  4300.             }
  4301.             // Clavier
  4302.             this.keyboardHandler = this.handleKeyboard.bind(this);
  4303.             document.addEventListener('keydown', this.keyboardHandler);
  4304.             console.log('Événements clavier attachés');
  4305.             // Zoom sur les images
  4306.             this.attachZoomEvents(slider);
  4307.             console.log('Événements zoom attachés');
  4308.             console.log('Événements attachés avec succès');
  4309.           }
  4310.           close() {
  4311.             console.log('=== close appelée ===');
  4312.             const slider = document.querySelector('.universal-slider');
  4313.             if (slider) {
  4314.               console.log('Fermeture du slider');
  4315.               // Animation de fermeture
  4316.               slider.style.opacity = '0';
  4317.               
  4318.               setTimeout(() => {
  4319.                 slider.remove();
  4320.                 console.log('Slider supprimé du DOM');
  4321.                 
  4322.                 // Nettoyer les événements
  4323.                 if (this.keyboardHandler) {
  4324.                   document.removeEventListener('keydown', this.keyboardHandler);
  4325.                   this.keyboardHandler = null;
  4326.                 }
  4327.                 
  4328.                 this.isOpen = false;
  4329.               }, 300);
  4330.             }
  4331.           }
  4332.           nextSlide() {
  4333.             if (this.currentIndex < this.images.length - 1) {
  4334.               this.showSlide(this.currentIndex + 1);
  4335.             }
  4336.           }
  4337.           prevSlide() {
  4338.             if (this.currentIndex > 0) {
  4339.               this.showSlide(this.currentIndex - 1);
  4340.             }
  4341.           }
  4342.           handleKeyboard(e) {
  4343.             if (!this.isOpen) return;
  4344.             
  4345.             switch (e.key) {
  4346.               case 'Escape':
  4347.                 this.close();
  4348.                 break;
  4349.               case 'ArrowLeft':
  4350.                 this.prevSlide();
  4351.                 break;
  4352.               case 'ArrowRight':
  4353.                 this.nextSlide();
  4354.                 break;
  4355.             }
  4356.           }
  4357.           attachZoomEvents(slider) {
  4358.             // Fonctionnalité de zoom à 5 niveaux pour les images
  4359.             const images = slider.querySelectorAll('.slide img');
  4360.             images.forEach(img => {
  4361.               let zoomLevel = 1; // 1x, 1.5x, 2x, 3x, 4x, 5x
  4362.               let isDragging = false;
  4363.               let startX = 0;
  4364.               let startY = 0;
  4365.               let translateX = 0;
  4366.               let translateY = 0;
  4367.               // Clic simple pour zoom progressif (5 niveaux)
  4368.               img.addEventListener('click', (e) => {
  4369.                 e.preventDefault();
  4370.                 e.stopPropagation();
  4371.                 
  4372.                 // Cycle à travers 5 niveaux de zoom
  4373.                 switch(zoomLevel) {
  4374.                   case 1: zoomLevel = 1.5; break;  // 1x → 1.5x
  4375.                   case 1.5: zoomLevel = 2; break;  // 1.5x → 2x
  4376.                   case 2: zoomLevel = 3; break;    // 2x → 3x
  4377.                   case 3: zoomLevel = 4; break;    // 3x → 4x
  4378.                   case 4: zoomLevel = 5; break;    // 4x → 5x
  4379.                   case 5: 
  4380.                     zoomLevel = 1;                 // 5x → 1x (reset)
  4381.                     translateX = 0;
  4382.                     translateY = 0;
  4383.                     break;
  4384.                 }
  4385.                 console.log('Zoom niveau:', zoomLevel);
  4386.                 img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  4387.                 
  4388.                 // Curseur selon le niveau
  4389.                 if (zoomLevel === 1) {
  4390.                   img.style.cursor = 'zoom-in';
  4391.                   img.removeAttribute('data-zoomed');
  4392.                 } else if (zoomLevel === 5) {
  4393.                   img.style.cursor = 'zoom-out';
  4394.                   img.setAttribute('data-zoomed', zoomLevel);
  4395.                 } else {
  4396.                   img.style.cursor = 'zoom-in';
  4397.                   img.setAttribute('data-zoomed', zoomLevel);
  4398.                 }
  4399.               });
  4400.               // Drag pour déplacer l'image zoomée
  4401.               img.addEventListener('mousedown', (e) => {
  4402.                 if (zoomLevel > 1) {
  4403.                   isDragging = true;
  4404.                   startX = e.clientX - translateX;
  4405.                   startY = e.clientY - translateY;
  4406.                   img.style.cursor = 'grabbing';
  4407.                   e.preventDefault();
  4408.                   e.stopPropagation();
  4409.                 }
  4410.               });
  4411.               // Mousemove global pour le drag
  4412.               const handleMouseMove = (e) => {
  4413.                 if (isDragging && zoomLevel > 1) {
  4414.                   translateX = e.clientX - startX;
  4415.                   translateY = e.clientY - startY;
  4416.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  4417.                 }
  4418.               };
  4419.               // Mouseup global pour arrêter le drag
  4420.               const handleMouseUp = () => {
  4421.                 if (isDragging) {
  4422.                   isDragging = false;
  4423.                   if (zoomLevel === 1) {
  4424.                     img.style.cursor = 'zoom-in';
  4425.                   } else if (zoomLevel === 5) {
  4426.                     img.style.cursor = 'zoom-out';
  4427.                   } else {
  4428.                     img.style.cursor = 'zoom-in';
  4429.                   }
  4430.                 }
  4431.               };
  4432.               document.addEventListener('mousemove', handleMouseMove);
  4433.               document.addEventListener('mouseup', handleMouseUp);
  4434.               // Touch support pour mobile
  4435.               img.addEventListener('touchstart', (e) => {
  4436.                 if (zoomLevel > 1 && e.touches.length === 1) {
  4437.                   isDragging = true;
  4438.                   const touch = e.touches[0];
  4439.                   startX = touch.clientX - translateX;
  4440.                   startY = touch.clientY - translateY;
  4441.                   e.preventDefault();
  4442.                 }
  4443.               });
  4444.               img.addEventListener('touchmove', (e) => {
  4445.                 if (isDragging && zoomLevel > 1 && e.touches.length === 1) {
  4446.                   const touch = e.touches[0];
  4447.                   translateX = touch.clientX - startX;
  4448.                   translateY = touch.clientY - startY;
  4449.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  4450.                   e.preventDefault();
  4451.                 }
  4452.               });
  4453.               img.addEventListener('touchend', () => {
  4454.                 isDragging = false;
  4455.               });
  4456.               // Initialiser le curseur
  4457.               img.style.cursor = 'zoom-in';
  4458.             });
  4459.           }
  4460.         }
  4461.         // ⚡ OPTIMISATION MAJEURE: Sliders lazy (créés seulement quand utilisés)
  4462.         let favoritesSlider = null;
  4463.         let universalSlider = null;
  4464.         
  4465.         // Fonctions pour créer les sliders à la demande
  4466.         function getFavoritesSlider() {
  4467.           if (!favoritesSlider) {
  4468.             favoritesSlider = new FavoritesSlider();
  4469.           }
  4470.           return favoritesSlider;
  4471.         }
  4472.         
  4473.         function getUniversalSlider() {
  4474.           if (!universalSlider) {
  4475.             universalSlider = new UniversalImageSlider();
  4476.           }
  4477.           return universalSlider;
  4478.         }
  4479.         // Fonction pour voir un favori avec le slider plein écran
  4480.         function viewFavorite(id) {
  4481.           console.log('Ouverture du slider pour favori:', id);
  4482.           // ⚡ OPTIMISÉ: Utiliser le slider lazy
  4483.           const slider = getUniversalSlider();
  4484.           if (slider && slider.openFromFavorites) {
  4485.             slider.openFromFavorites(id);
  4486.           } else {
  4487.             console.error('Erreur slider favoris');
  4488.           }
  4489.         }
  4490.         // Fonction pour voir une image depuis la galerie normale
  4491.         function viewImage(imageId, clickedElement) {
  4492.           console.log('=== viewImage appelée ===');
  4493.           console.log('imageId:', imageId);
  4494.           console.log('clickedElement:', clickedElement);
  4495.           
  4496.           // Trouver le container du jour - peut être plusieurs niveaux au-dessus
  4497.           let dayContainer = clickedElement;
  4498.           
  4499.           // Remonter jusqu'à trouver un container approprié
  4500.           while (dayContainer && dayContainer !== document.body) {
  4501.             console.log('Checking element:', dayContainer.tagName, dayContainer.className, dayContainer.id);
  4502.             if (dayContainer && dayContainer.classList && (
  4503.                 dayContainer.classList.contains('dynamic-card') || 
  4504.                 dayContainer.classList.contains('collapse') ||
  4505.                 dayContainer.id && dayContainer.id.startsWith('demP')
  4506.             )) {
  4507.               break;
  4508.             }
  4509.             dayContainer = dayContainer.parentElement;
  4510.           }
  4511.           
  4512.           // ⚡ OPTIMISÉ: Logs réduits pour performance
  4513.           
  4514.           // ⚡ OPTIMISÉ: Utiliser le slider lazy
  4515.           const slider = getUniversalSlider();
  4516.           if (slider && slider.openFromGallery) {
  4517.             slider.openFromGallery(imageId, dayContainer);
  4518.           } else {
  4519.             console.error('Erreur slider galerie');
  4520.           }
  4521.         }
  4522.         // ⚡ OPTIMISÉ: Fonctions globales avec sliders lazy
  4523.         window.viewImage = viewImage;
  4524.         window.viewFavorite = viewFavorite;
  4525.         window.getUniversalSlider = getUniversalSlider; // Fonction lazy au lieu de l'instance
  4526.         // Variables globales pour éviter les ReferenceError
  4527.         if (typeof favoriteCount === 'undefined') {
  4528.           window.favoriteCount = 0;
  4529.         }
  4530.         if (typeof favButton === 'undefined') {
  4531.           window.favButton = null;
  4532.         }
  4533.         if (typeof favoriteButton === 'undefined') {
  4534.           window.favoriteButton = null;
  4535.         }
  4536.         if (typeof purchaseAlertTimeout === 'undefined') {
  4537.           window.purchaseAlertTimeout = null;
  4538.         }
  4539.         
  4540.         // Définir favoriteCount globalement pour éviter les ReferenceError
  4541.         let favoriteCount = window.favoriteCount || 0;
  4542.         // Fonction de sécurité pour vérifier les éléments DOM
  4543.         function safeAddEventListener(selector, event, callback) {
  4544.           const element = document.querySelector(selector);
  4545.           if (element) {
  4546.             element.addEventListener(event, callback);
  4547.           } else {
  4548.             console.warn(`Élément non trouvé: ${selector}`);
  4549.           }
  4550.         }
  4551.         // Fonction utilitaire pour manipuler classList de manière sécurisée
  4552.         function safeClassList(element, action, ...classes) {
  4553.           if (!element || !element.classList) {
  4554.             console.warn('Élément ou classList invalide:', element);
  4555.             return false;
  4556.           }
  4557.           
  4558.           try {
  4559.             switch(action) {
  4560.               case 'add':
  4561.                 element.classList.add(...classes);
  4562.                 break;
  4563.               case 'remove':
  4564.                 element.classList.remove(...classes);
  4565.                 break;
  4566.               case 'toggle':
  4567.                 return element.classList.toggle(classes[0], classes[1]);
  4568.               case 'contains':
  4569.                 return element.classList.contains(classes[0]);
  4570.               default:
  4571.                 console.warn('Action classList inconnue:', action);
  4572.                 return false;
  4573.             }
  4574.             return true;
  4575.           } catch (error) {
  4576.             console.warn('Erreur lors de la manipulation de classList:', error);
  4577.             return false;
  4578.           }
  4579.         }
  4580.         // Rendre la fonction utilitaire globale
  4581.         window.safeClassList = safeClassList;
  4582.         // Fonction pour sécuriser automatiquement tous les accès à classList
  4583.         function secureClassListAccess() {
  4584.           // Intercepter les erreurs classList globalement
  4585.           window.addEventListener('error', function(e) {
  4586.             if (e.message && (e.message.includes('classList') || e.message.includes('Cannot read properties of null'))) {
  4587.               console.warn('Erreur DOM interceptée et corrigée:', e.message, 'Ligne:', e.lineno, 'Fichier:', e.filename);
  4588.               e.preventDefault(); // Empêcher l'erreur de se propager
  4589.               return true;
  4590.             }
  4591.           });
  4592.           
  4593.           // Intercepter les erreurs non catchées
  4594.           window.addEventListener('unhandledrejection', function(e) {
  4595.             if (e.reason && e.reason.message && e.reason.message.includes('classList')) {
  4596.               console.warn('Promise rejection classList interceptée:', e.reason.message);
  4597.               e.preventDefault();
  4598.               return true;
  4599.             }
  4600.           });
  4601.           
  4602.           console.log('Système de sécurisation DOM/classList activé');
  4603.         }
  4604.         // Activer la sécurisation
  4605.         secureClassListAccess();
  4606.         // Fonction de diagnostic pour vérifier l'affichage du slider
  4607.         function diagnoseSliderDisplay(sliderId) {
  4608.           const slider = document.querySelector(sliderId);
  4609.           if (!slider) {
  4610.             console.error('Diagnostic: Slider non trouvé -', sliderId);
  4611.             return false;
  4612.           }
  4613.           
  4614.           const styles = window.getComputedStyle(slider);
  4615.           const rect = slider.getBoundingClientRect();
  4616.           
  4617.           console.log('=== DIAGNOSTIC SLIDER ===', sliderId);
  4618.           console.log('Élément présent:', !!slider);
  4619.           console.log('Styles calculés:', {
  4620.             display: styles.display,
  4621.             opacity: styles.opacity,
  4622.             visibility: styles.visibility,
  4623.             zIndex: styles.zIndex,
  4624.             position: styles.position
  4625.           });
  4626.           console.log('Position/Taille:', rect);
  4627.           console.log('Dans le viewport:', rect.width > 0 && rect.height > 0);
  4628.           console.log('Parent:', slider.parentElement?.tagName);
  4629.           
  4630.           // Vérifier les règles CSS qui pourraient masquer le slider
  4631.           const hiddenReasons = [];
  4632.           if (styles.display === 'none') hiddenReasons.push('display: none');
  4633.           if (styles.opacity === '0') hiddenReasons.push('opacity: 0');
  4634.           if (styles.visibility === 'hidden') hiddenReasons.push('visibility: hidden');
  4635.           if (parseInt(styles.zIndex) < 0) hiddenReasons.push('z-index négatif');
  4636.           
  4637.           if (hiddenReasons.length > 0) {
  4638.             console.warn('Raisons du masquage:', hiddenReasons);
  4639.             
  4640.             // Forcer l'affichage
  4641.             console.log('Forçage de l\'affichage...');
  4642.             slider.style.display = 'flex';
  4643.             slider.style.opacity = '1';
  4644.             slider.style.visibility = 'visible';
  4645.             slider.style.zIndex = '9999';
  4646.             slider.style.position = 'fixed';
  4647.             slider.style.top = '0';
  4648.             slider.style.left = '0';
  4649.             slider.style.width = '100%';
  4650.             slider.style.height = '100%';
  4651.             
  4652.             console.log('Slider forcé visible');
  4653.           } else {
  4654.             console.log('✅ Slider devrait être visible');
  4655.           }
  4656.           
  4657.           return true;
  4658.         }
  4659.         // Rendre la fonction globale
  4660.         window.diagnoseSliderDisplay = diagnoseSliderDisplay;
  4661.         // Fonction de sécurité pour checkFavorites
  4662.         function checkFavorites() {
  4663.           try {
  4664.             let count = 0;
  4665.             if (typeof getFavoriteItems === 'function') {
  4666.               count = getFavoriteItems().length;
  4667.             } else {
  4668.               // Fallback : compter depuis le DOM
  4669.               const giftCount = document.getElementById('giftCount');
  4670.               if (giftCount && giftCount.textContent) {
  4671.                 count = parseInt(giftCount.textContent.trim()) || 0;
  4672.               }
  4673.             }
  4674.             
  4675.             // Mettre à jour les variables globales
  4676.             window.favoriteCount = count;
  4677.             favoriteCount = count;
  4678.             
  4679.             console.log('Favoris vérifiés:', count);
  4680.             return count;
  4681.           } catch (error) {
  4682.             console.warn('Erreur lors de la vérification des favoris:', error);
  4683.             return 0;
  4684.           }
  4685.         }
  4686.         // Rendre checkFavorites globale si elle n'existe pas
  4687.         if (typeof window.checkFavorites === 'undefined') {
  4688.           window.checkFavorites = checkFavorites;
  4689.         }
  4690.         // Initialisation sécurisée après chargement du DOM
  4691.         document.addEventListener('DOMContentLoaded', function() {
  4692.           console.log('=== DOM chargé, initialisation des sliders ===');
  4693.           console.log('viewImage disponible:', typeof window.viewImage);
  4694.           console.log('viewFavorite disponible:', typeof window.viewFavorite);
  4695.           console.log('universalSlider disponible:', typeof window.universalSlider);
  4696.           
  4697.           // Vérifier que les éléments critiques existent
  4698.           const criticalElements = [
  4699.             'filtre_photos_voir',
  4700.             // Ajoutez d'autres IDs critiques ici si nécessaire
  4701.           ];
  4702.           
  4703.           criticalElements.forEach(id => {
  4704.             const element = document.getElementById(id);
  4705.             if (element) {
  4706.               console.log(`✓ Élément trouvé: ${id}`);
  4707.             } else {
  4708.               console.warn(`⚠ Élément manquant: ${id}`);
  4709.             }
  4710.           });
  4711.           
  4712.           // Initialiser checkFavorites si la fonction existe
  4713.           if (typeof window.checkFavorites === 'function') {
  4714.             window.checkFavorites();
  4715.           }
  4716.         });
  4717.       
  4718.         // Fonction pour obtenir l'image d'un produit selon son type
  4719.         function getProductImage(productId) {
  4720.           const productImages = {
  4721.             'album': '/images/produit/Album5sur5-3.jpg',
  4722.             'digital': '/images/produit/photoNumerique.jpg', 
  4723.             'prints': '/images/produit/PochettePhoto5sur5-2.jpg',
  4724.             'calendrier': '/images/produit/Calendrier5sur5-1.jpg',
  4725.             'livre': '/images/produit/LivreSouvenir5sur5-1.jpg',
  4726.             'coffret': '/images/produit/CoffretCadeau5sur5-2.jpg',
  4727.             'retro': '/images/produit/PochetteRetro5sur5-1.jpg'
  4728.           };
  4729.           
  4730.           return productImages[productId] || '/images/produit/albumm.PNG';
  4731.         }
  4732.         
  4733.         // Fonction pour générer les suggestions de produits (synchronisées avec ecommerce-sidebar)
  4734.         // 🗑️ SUPPRIMÉ : generateProductSuggestions() - remplacé par le sidebar e-commerce
  4735.         // Le sidebar e-commerce (sidebar-ecommerce-pro.js) gère maintenant tous les produits
  4736.         // de manière centralisée et dynamique
  4737.         // Fonction pour obtenir le nombre actuel de favoris
  4738.         function getCurrentFavoriteCount() {
  4739.           return getFavoriteItems().length;
  4740.         }
  4741.         // 🎯 SUPPRIMÉ: Fonction dupliquée - la vraie est dans parent-toasts.js
  4742.         // Cette fonction était en conflit avec updateProductSuggestions() de parent-toasts.js
  4743.         // qui gère déjà les mises à jour dynamiques avec dates et contexte
  4744.         
  4745.         // Fonction legacy pour compatibilité (redirige vers parent-toasts.js)
  4746.         window.updateProductSuggestionsLive = function(favoriteCount) {
  4747.           console.log('[Legacy] updateProductSuggestionsLive appelé, redirection vers parent-toasts.js');
  4748.           // La vraie logique est dans parent-toasts.js > updateProductSuggestions()
  4749.           // qui est appelée automatiquement par le MutationObserver
  4750.         }
  4751.         // Gestion du localStorage pour l'indicateur "nouveau contenu"
  4752.         const lastVisitKey = 'lastVisitISO';
  4753.         const currentVisit = new Date().toISOString();
  4754.         
  4755.         // Sauvegarder la visite actuelle
  4756.         localStorage.setItem(lastVisitKey, currentVisit);
  4757.         
  4758.         // Vérifier et marquer les jours avec nouveau contenu
  4759.         const dateCards = document.querySelectorAll('.date-card.modern-card');
  4760.         dateCards.forEach(card => {
  4761.           // Ici vous devriez comparer avec updatedAt du backend
  4762.           // Pour l'instant, on simule avec un attribut data-updated
  4763.           const updatedAt = card.getAttribute('data-updated');
  4764.           if (updatedAt) {
  4765.             const lastVisit = localStorage.getItem(lastVisitKey);
  4766.             if (new Date(updatedAt) > new Date(lastVisit)) {
  4767.               if (card && card.classList) {
  4768.                 card.classList.add('has-new-content');
  4769.               }
  4770.               // Ajouter le badge "nouveau" si pas déjà présent
  4771.               if (!card.querySelector('.badge-new')) {
  4772.                 const badge = document.createElement('span');
  4773.                 badge.className = 'badge-new';
  4774.                 badge.setAttribute('aria-label', 'Nouveau contenu');
  4775.                 card.querySelector('.title-line').appendChild(badge);
  4776.               }
  4777.             }
  4778.           }
  4779.         });
  4780.         // Gestion des aria-label dynamiques pour les cartes de jours
  4781.         dateCards.forEach(card => {
  4782.           const dayText = card.querySelector('.day').textContent.trim();
  4783.           const dateText = card.querySelector('.full-date').textContent.trim();
  4784.           const photos = card.querySelector('.media-list-horizontal li:nth-child(1)')?.textContent.trim() || '0';
  4785.           const audios = card.querySelector('.media-list-horizontal li:nth-child(2)')?.textContent.trim() || '0';
  4786.           const videos = card.querySelector('.media-list-horizontal li:nth-child(3)')?.textContent.trim() || '0';
  4787.           
  4788.           const ariaLabel = `Contenu du ${dateText} : ${photos} photos, ${audios} audios, ${videos} vidéos`;
  4789.           card.setAttribute('aria-label', ariaLabel);
  4790.         });
  4791.       });
  4792.     </script>
  4793.     
  4794.     <!-- Make sure we're showing the right content by default if no valid content is marked 'show' -->
  4795.     {% if lastValidIndex > 0 %}
  4796.     <script>
  4797.       document.addEventListener('DOMContentLoaded', function() {
  4798.         // Check if no content is showing
  4799.         if (document.querySelectorAll('.container--gallery .collapse.show').length === 0) {
  4800.           // Show the content for the last valid date
  4801.           var lastValidContent = document.getElementById('demP{{ lastValidIndex }}');
  4802.           if (lastValidContent) {
  4803.             if (lastValidContent.classList) {
  4804.               lastValidContent.classList.add('show');
  4805.             }
  4806.             
  4807.             // Also mark the corresponding date card as active
  4808.             var dateCards = document.querySelectorAll('.date-card');
  4809.             if (dateCards.length >= {{ lastValidIndex }}) {
  4810.               dateCards.forEach(card => {
  4811.                 if (card && card.classList) {
  4812.                   card.classList.remove('active');
  4813.                 }
  4814.               });
  4815.               const targetCard = dateCards[{{ lastValidIndex - 1 }}];
  4816.               if (targetCard && targetCard.classList) {
  4817.                 targetCard.classList.add('active');
  4818.               }
  4819.             }
  4820.           }
  4821.         }
  4822.       });
  4823.     </script>
  4824.     {% endif %}
  4825.   </div>
  4826. </div>
  4827.   {% endblock %} {% block Javascript %}
  4828.   {{ parent() }}
  4829.   <script>// Gestion de la sidebar des favoris
  4830. document.addEventListener('DOMContentLoaded', function() {
  4831.     const sidebar = document.getElementById('favorites-sidebar');
  4832.     const openBtn = document.getElementById('openFavoritesSidebar');
  4833.     const closeBtn = document.querySelector('.favorites-close');
  4834.     const giftButton = document.querySelector('.gift-button');
  4835.     function openSidebar() {
  4836.         if (sidebar && sidebar.classList) {
  4837.           sidebar.classList.add('active');
  4838.           updateFavoritesSidebar();
  4839.         }
  4840.     }
  4841.     function closeSidebar() {
  4842.         if (sidebar && sidebar.classList) {
  4843.           sidebar.classList.remove('active');
  4844.         }
  4845.     }
  4846.     function updateFavoritesSidebar() {
  4847.         const grid = document.getElementById('favorites-grid');
  4848.         const counter = document.getElementById('favorites-counter');
  4849.         const emptyState = document.getElementById('favorites-empty-state');
  4850.         const progress = document.getElementById('favorites-progress');
  4851.         
  4852.         const mesFavCount = document.getElementById('mesFavCount');
  4853.         const count = mesFavCount ? getFavorisCount() : 0;
  4854.         counter.textContent = count;
  4855.         
  4856.         if (count === 0) {
  4857.             emptyState.style.display = 'flex';
  4858.             grid.style.display = 'none';
  4859.         } else {
  4860.             emptyState.style.display = 'none';
  4861.             grid.style.display = 'grid';
  4862.             const percentage = (count / 20) * 100;
  4863.             progress.style.width = `${percentage}%`;
  4864.         }
  4865.     }
  4866.     if (openBtn) {
  4867.         openBtn.addEventListener('click', openSidebar);
  4868.     }
  4869.     if (favButton) {
  4870.         favButton.addEventListener('click', openSidebar);
  4871.     }
  4872.     if (closeBtn) {
  4873.         closeBtn.addEventListener('click', closeSidebar);
  4874.     }
  4875.     document.addEventListener('click', (e) => {
  4876.         if (sidebar && sidebar.classList && sidebar.classList.contains('active') && 
  4877.             !sidebar.contains(e.target) && 
  4878.             (!favButton || !favButton.contains(e.target)) && 
  4879.             (!openBtn || !openBtn.contains(e.target))) {
  4880.             closeSidebar();
  4881.         }
  4882.     });
  4883. });
  4884. // Modification des fonctions existantes
  4885. const originalAddFavoris = window.AddFavoris;
  4886. window.AddFavoris = function($id, $idSejour, $urlimg, $description) {
  4887.     if (originalAddFavoris) {
  4888.         originalAddFavoris($id, $idSejour, $urlimg, $description);
  4889.     }
  4890.     
  4891.   
  4892.     
  4893.     // Vérifier si la sidebar est ouverte - noter que nous vérifions la valeur CSS right: 0
  4894.     if ($("#favorites-sidebar").css("right") === "0px") {
  4895.         // Recharger les favoris
  4896.         loadFavorites();
  4897.     }
  4898. };
  4899. const originalSupprimerFavoris = window.supprimerFavoris;
  4900. window.supprimerFavoris = function($id, $idSejour) {
  4901.     if (originalSupprimerFavoris) {
  4902.         originalSupprimerFavoris($id, $idSejour);
  4903.     }
  4904.     
  4905.     // Mettre à jour tous les compteurs de favoris
  4906.     updateAllFavoriteCounters();
  4907.     
  4908.     // Vérifier si la sidebar est ouverte
  4909.     if ($("#favorites-sidebar").css("right") === "0px") {
  4910.         // Recharger les favoris
  4911.         loadFavorites();
  4912.     }
  4913. };
  4914. // Variables globales
  4915. let selectedFavorites = [];
  4916. let allFavorites = [];
  4917. // Fonction pour mettre à jour la sidebar
  4918. function loadFavorites() {
  4919.     $("#favorites-grid").html("<div style='text-align:center;padding:20px;'>Chargement...</div>");
  4920.     
  4921.     $.ajax({
  4922.         url: "/Parent/mes-favoris",
  4923.         type: "GET",
  4924.         dataType: "json",
  4925.         success: function(data) {
  4926.             $("#favorites-grid").empty();
  4927.             allFavorites = data.data || [];
  4928.             
  4929.             if (allFavorites.length > 0) {
  4930.                 $("#favorites-empty-state").hide();
  4931.                 
  4932.                 $.each(allFavorites, function(i, fav) {
  4933.                     var isSelected = selectedFavorites.includes(fav.id);
  4934.                     
  4935.                     var item = $("<div class='favorite-item' style='position:relative;border-radius:8px;overflow:hidden;aspect-ratio:1;cursor:pointer;'></div>");
  4936.                     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");
  4937.                     
  4938.                     // Overlay de sélection
  4939.                     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>");
  4940.                     
  4941.                     // Icône de sélection
  4942.                     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>");
  4943.                     if (isSelected) {
  4944.                         checkIcon.append("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  4945.                     }
  4946.                     
  4947.                     selectionOverlay.append(checkIcon);
  4948.                     
  4949.                     // Overlay d'action (bouton supprimer)
  4950.                     var actionOverlay = $("<div class='action-overlay' style='position:absolute;top:5px;right:5px;opacity:0;transition:opacity 0.2s;'></div>");
  4951.                   
  4952.                     actionOverlay.append(deleteBtn);
  4953.                     
  4954.                     // Ajouter les événements
  4955.                     item.hover(
  4956.                         function() { $(this).find(".action-overlay").css("opacity", "1"); },
  4957.                         function() { $(this).find(".action-overlay").css("opacity", "0"); }
  4958.                     );
  4959.                     
  4960.                     item.click(function() {
  4961.                         toggleSelection(fav.id, $(this).find(".selection-overlay > div"));
  4962.                     });
  4963.                     
  4964.                     item.append(img).append(selectionOverlay).append(actionOverlay);
  4965.                     $("#favorites-grid").append(item);
  4966.                 });
  4967.                 
  4968.                 $("#favorites-counter").text(allFavorites.length);
  4969.                 
  4970.                 // Mettre à jour le compteur sur le bouton également
  4971.                 $("#openFavoritesSidebar span").text(allFavorites.length);
  4972.                 
  4973.                 // Mettre à jour l'interface produits
  4974.                 updateProductsView();
  4975.                 
  4976.             } else {
  4977.                 $("#favorites-empty-state").show();
  4978.                 $("#favorites-counter").text("0");
  4979.                 $("#openFavoritesSidebar span").text("0");
  4980.                 $("#selection-count").text("0");
  4981.                 updateProductsView();
  4982.             }
  4983.         },
  4984.         error: function() {
  4985.             $("#favorites-grid").html("<div style='color:red;text-align:center;padding:20px;'>Erreur de chargement</div>");
  4986.         }
  4987.     });
  4988. }
  4989. // Fonction pour supprimer un favori
  4990. function removeFavorite(id) {
  4991.     $.ajax({
  4992.         url: "/Parent/remove-favorite/" + id,
  4993.         type: "POST",
  4994.         success: function() {
  4995.             // Retirer de la sélection si présent
  4996.             selectedFavorites = selectedFavorites.filter(favId => favId != id);
  4997.             
  4998.             // Mettre à jour le compteur de sélection
  4999.             $("#selection-count").text(selectedFavorites.length);
  5000.             
  5001.             // Recharger les favoris
  5002.             loadFavorites();
  5003.             
  5004.             // Mettre à jour tous les compteurs de favoris
  5005.             updateAllFavoriteCounters();
  5006.             
  5007.             // Mettre à jour l'interface produits
  5008.             updateProductsView();
  5009.         },
  5010.         error: function() {
  5011.             alert("Erreur lors de la suppression du favori");
  5012.         }
  5013.     });
  5014. }
  5015. // Fonction pour basculer la sélection d'une photo
  5016. function toggleSelection(id, checkElement) {
  5017.     const index = selectedFavorites.indexOf(id);
  5018.     
  5019.     if (index === -1) {
  5020.         // Ajouter à la sélection
  5021.         selectedFavorites.push(id);
  5022.         checkElement.css("background", "#F56040");
  5023.         checkElement.html("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  5024.     } else {
  5025.         // Retirer de la sélection
  5026.         selectedFavorites.splice(index, 1);
  5027.         checkElement.css("background", "transparent");
  5028.         checkElement.html("");
  5029.     }
  5030.     
  5031.     // Mettre à jour le compteur
  5032.     $("#selection-count").text(selectedFavorites.length);
  5033.     
  5034.     // Mettre à jour l'interface produits
  5035.     updateProductsView();
  5036. }
  5037. // Fonction pour mettre à jour la vue des produits
  5038. function updateProductsView() {
  5039.     const current = selectedFavorites.length;
  5040.     $("#product-photo-count").text(current);
  5041.     
  5042.     let remainingForAlbum = Math.max(0, 20 - current);
  5043.     let remainingForPochette = Math.max(0, 12 - current);
  5044.     let remainingForPack = Math.max(0, 12 - current);
  5045.     const progressBar = (count, total, color) => `
  5046.         <div style="margin: 5px 0;">
  5047.             <div style="background-color: #e9ecef; border-radius: 5px; overflow: hidden; height: 8px;">
  5048.                 <div style="width: ${(count / total) * 100}%; background-color: ${color}; height: 100%;"></div>
  5049.             </div>
  5050.             <small style="font-size: 12px;">${count}/${total} photos</small>
  5051.         </div>
  5052.     `;
  5053.     // Liste des produits
  5054.     const products = [
  5055.         {
  5056.             name: "Pack numérique (20 photos)",
  5057.             required: 20,
  5058.             remaining: Math.max(0, 20 - current),
  5059.             image: "/images/produit/photoNumerique.jpg",
  5060.             color: "#4caf50",
  5061.             link: "{{ path('PackPhotosNumerique_Favoris', {'nbr':20}) }}",
  5062.         },
  5063.        +
  5064.         {
  5065.             name: "Pochette photo (12 photos)",
  5066.             required: 12,
  5067.             remaining: Math.max(0, 12 - current),
  5068.             image: "/images/produit/PochettePhoto5sur5-2.jpg",
  5069.             color: "#2196f3",
  5070.             link: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}",
  5071.         },
  5072.     ].sort((a, b) => a.remaining - b.remaining);
  5073.     const productList = products
  5074.         .map((product) => {
  5075.             const count = current;
  5076.             const total = product.required;
  5077.             const remaining = product.remaining;
  5078.             return `
  5079.                 <li style="margin-bottom: 20px;">
  5080.                     <div style="display: flex; align-items: center; gap: 10px;">
  5081.                         <img src="${product.image}" alt="${product.name}" style="width: 70px; height: 70px; border-radius: 5px; object-fit: cover;" />
  5082.                         <div style="flex: 1;">
  5083.                             <strong style="font-size: 14px;">${product.name}</strong>
  5084.                             ${progressBar(count, total, product.color)}
  5085.                             ${
  5086.                                 `<small style="color: ${product.color}; font-size: 12px;">
  5087.                                     Encore ${remaining} photos pour compléter ${product.name.toLowerCase()}
  5088.                                 </small>
  5089.                                 <button
  5090.                                     style="
  5091.                                         margin-top: 5px;
  5092.                                         padding: 6px 12px;
  5093.                                         background-color: ${product.color};
  5094.                                         color: white;
  5095.                                         border: none;
  5096.                                         border-radius: 5px;
  5097.                                         font-size: 13px;
  5098.                                         cursor: pointer;
  5099.                                     "
  5100.                                     onclick="window.location.href='${product.link}'"
  5101.                                 >
  5102.                                     Commander
  5103.                                 </button>`
  5104.                             }
  5105.                         </div>
  5106.                     </div>
  5107.                 </li>
  5108.             `;
  5109.         })
  5110.         .join("");
  5111.     const boutiqueButton = `
  5112.         <li style="margin-top: 25px; text-align: center;">
  5113.             <button
  5114.                 style="
  5115.                     padding: 8px 15px;
  5116.                     background-color: #F56040;
  5117.                     color: white;
  5118.                     border: none;
  5119.                     border-radius: 5px;
  5120.                     font-size: 14px;
  5121.                     width: 170px;
  5122.                     height: 40px;
  5123.                     cursor: pointer;
  5124.                 "
  5125.                 onclick="window.location.href='{{ path('boutique5sur5') }}'"
  5126.             >
  5127.                 Voir toute la boutique
  5128.             </button>
  5129.         </li>
  5130.     `;
  5131.     $("#product-list").html(productList + boutiqueButton);
  5132. }
  5133. // Modifications au script existant
  5134. $(document).ready(function() {
  5135.     // Fonctions pour ouvrir/fermer la sidebar
  5136.     $("#openFavoritesSidebar").click(function() {
  5137.         $("#favorites-sidebar").css("right", "0");
  5138.         loadFavorites();
  5139.     });
  5140.     
  5141.     $("#close-favorites-btn").click(function() {
  5142.         closeFavoritesSidebar();
  5143.     });
  5144.     
  5145.     // Fonction pour fermer la sidebar
  5146.     window.closeFavoritesSidebar = function() {
  5147.         $("#favorites-sidebar").css("right", "-500px");
  5148.     };
  5149.     
  5150.     // Tout sélectionner / Tout désélectionner
  5151.     $("#select-all-btn").click(function() {
  5152.         if (selectedFavorites.length === allFavorites.length) {
  5153.             // Tout désélectionner
  5154.             selectedFavorites = [];
  5155.             $(".selection-overlay > div").css("background", "transparent").html("");
  5156.             $(this).text("Tout sélectionner");
  5157.         } else {
  5158.             // Tout sélectionner
  5159.             selectedFavorites = allFavorites.map(fav => fav.id);
  5160.             $(".selection-overlay > div").css("background", "#F56040").html("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  5161.             $(this).text("Tout désélectionner");
  5162.         }
  5163.         
  5164.         // Mettre à jour le compteur
  5165.         $("#selection-count").text(selectedFavorites.length);
  5166.         
  5167.         // Mettre à jour l'interface produits
  5168.         updateProductsView();
  5169.     });
  5170.     
  5171.     // Gestion des onglets
  5172.     $(".sidebar-tab").click(function() {
  5173.         $(".sidebar-tab").removeClass("active").css({
  5174.             "color": "#666",
  5175.             "border-bottom": "none"
  5176.         });
  5177.         $(this).addClass("active").css({
  5178.             "color": "#F56040",
  5179.             "border-bottom": "2px solid #F56040"
  5180.         });
  5181.         
  5182.         const tabId = $(this).attr("id");
  5183.         $(".tab-content").hide();
  5184.         
  5185.         if (tabId === "tab-photos") {
  5186.             $("#photos-content").show();
  5187.         } else if (tabId === "tab-products") {
  5188.             $("#products-content").show();
  5189.         }
  5190.     });
  5191.     
  5192.     // Modifier les fonctions existantes pour intercepter l'ajout/suppression de favoris
  5193.     const originalAddFavoris = window.AddFavoris;
  5194.     window.AddFavoris = function($id, $idSejour, $urlimg, $description) {
  5195.         if (originalAddFavoris) {
  5196.             originalAddFavoris($id, $idSejour, $urlimg, $description);
  5197.         }
  5198.         
  5199.         // Mettre à jour tous les compteurs de favoris
  5200.         updateAllFavoriteCounters();
  5201.         
  5202.         // Recharger les favoris si la sidebar est ouverte
  5203.         if ($("#favorites-sidebar").css("right") === "0px") {
  5204.             loadFavorites();
  5205.         }
  5206.     };
  5207.     const originalSupprimerFavoris = window.supprimerFavoris;
  5208.     window.supprimerFavoris = function($id, $idSejour) {
  5209.         if (originalSupprimerFavoris) {
  5210.             originalSupprimerFavoris($id, $idSejour);
  5211.         }
  5212.         
  5213.         // Mettre à jour tous les compteurs de favoris
  5214.         updateAllFavoriteCounters();
  5215.         
  5216.         // Recharger les favoris si la sidebar est ouverte
  5217.         if ($("#favorites-sidebar").css("right") === "0px") {
  5218.             // Retirer de la sélection si présent
  5219.             selectedFavorites = selectedFavorites.filter(favId => favId != $id);
  5220.             loadFavorites();
  5221.         }
  5222.     };
  5223.     
  5224.     // Fermer en cliquant en dehors
  5225.     $(document).click(function(event) {
  5226.         if (!$(event.target).closest("#favorites-sidebar").length && 
  5227.             !$(event.target).closest("#openFavoritesSidebar").length && 
  5228.             $("#favorites-sidebar").css("right") === "0px") {
  5229.             closeFavoritesSidebar();
  5230.         }
  5231.     });
  5232. });
  5233. // Initialisation de la sidebar
  5234. $(document).ready(function() {
  5235.     // Fonctions pour ouvrir/fermer la sidebar
  5236.     $("#openFavoritesSidebar").click(function() {
  5237.         $("#favorites-sidebar").css("right", "0");
  5238.         loadFavorites();
  5239.     });
  5240.     
  5241.     $("#close-favorites-btn").click(function() {
  5242.         $("#favorites-sidebar").css("right", "-500px");
  5243.     });
  5244.     
  5245.     // Fermer en cliquant en dehors
  5246.     $(document).click(function(event) {
  5247.         if (!$(event.target).closest("#favorites-sidebar").length && 
  5248.             !$(event.target).closest("#openFavoritesSidebar").length && 
  5249.             $("#favorites-sidebar").css("right") === "0px") {
  5250.             $("#favorites-sidebar").css("right", "-500px");
  5251.         }
  5252.     });
  5253. });
  5254.   </script>
  5255.   <script src="{{ asset('js/splide.min.js') }}" defer></script>
  5256.    
  5257. <script>
  5258.   document.addEventListener('DOMContentLoaded', function () {
  5259.     const openBtn = document.getElementById('openFavoritesSidebar');
  5260.     const sidebar = document.getElementById('favorites-sidebar');
  5261.     const closeBtn = document.getElementById('favorites-close');
  5262.     if (openBtn && sidebar) {
  5263.       openBtn.addEventListener('click', () => {
  5264.         if (sidebar && sidebar.classList) {
  5265.           sidebar.classList.add('active');
  5266.         }
  5267.       });
  5268.     }
  5269.     if (closeBtn && sidebar) {
  5270.       closeBtn.addEventListener('click', () => {
  5271.         if (sidebar && sidebar.classList) {
  5272.           sidebar.classList.remove('active');
  5273.         }
  5274.       });
  5275.     }
  5276.   });
  5277. </script>
  5278.  
  5279. <script>
  5280. document.addEventListener('DOMContentLoaded', function() {
  5281.   const filterBadges = document.querySelectorAll('.filter-badge');
  5282.   filterBadges.forEach(badge => {
  5283.     badge.addEventListener('click', function() {
  5284.       // Désactiver tous les badges
  5285.       filterBadges.forEach(b => {
  5286.         if (b && b.classList) {
  5287.           b.classList.remove('active');
  5288.         }
  5289.       });
  5290.       // Activer le badge cliqué
  5291.       if (this && this.classList) {
  5292.         this.classList.add('active');
  5293.       }
  5294.     });
  5295.   });
  5296. });
  5297. </script>
  5298.   <script
  5299.     type="text/javascript"
  5300.     src="{{ asset('Accueil/js/jquery.magnific-popup.min.js') }}"
  5301.   ></script>
  5302.   <script>
  5303. document.addEventListener('DOMContentLoaded', function () {
  5304.   const prevButtons = document.querySelectorAll('.btn-prev-day');
  5305.   const nextButtons = document.querySelectorAll('.btn-next-day');
  5306.   const dateCards = document.querySelectorAll('.date-card.modern-card');
  5307.   const collapseSections = document.querySelectorAll('.collapse');
  5308.   // 🎯 MEDIA TOOLBAR CAPSULE - État global et logique (Senior UX)
  5309.   (() => {
  5310.     // État global de filtre
  5311.     const MediaFilter = { current: 'all' };
  5312.     // Compteurs dynamiques
  5313.     const counters = { photo: 0, audio: 0, video: 0, fav: 0 };
  5314.     
  5315.     const updateCounts = () => {
  5316.       // Compter les médias dans le DOM
  5317.       counters.photo = document.querySelectorAll('.photo-item, [data-media-type="photo"]').length;
  5318.       counters.video = document.querySelectorAll('[data-media-type="video"]').length;
  5319.       counters.audio = document.querySelectorAll('.audio-message-item[data-type="audio"]').length;
  5320.       // NE PAS recalculer seulement les favoris - ils utilisent {{ nblikes }}
  5321.       
  5322.       // Mettre à jour l'affichage (sauf favoris)
  5323.       Object.entries(counters).forEach(([k, v]) => {
  5324.         if (k !== 'fav') { // Garder seulement le compteur favoris intact
  5325.           document.querySelectorAll(`.mtb-count[data-bind="${k}"]`).forEach(el => el.textContent = v);
  5326.         }
  5327.       });
  5328.     };
  5329.     // Simple fonction pour le filtre actuel
  5330.     function setActive(filter) {
  5331.       MediaFilter.current = filter;
  5332.       
  5333.       // Exception : ne pas ajouter de classe active au bouton photos
  5334.       if (filter === 'photos') {
  5335.         const photosBtn = document.querySelector('.mtb-btn[data-filter="photos"]');
  5336.         if (photosBtn) {
  5337.           photosBtn.classList.remove('active', 'is-active');
  5338.         }
  5339.       }
  5340.     }
  5341.     // Fonction simplifiée qui mappe et appelle filterContent
  5342.     function applyFilter(filter) {
  5343.       // Mapper les filtres de la capsule vers les filtres existants si nécessaire
  5344.       const filterMap = {
  5345.         'all': 'toutVoir',
  5346.         'photos': 'photos', 
  5347.         'audio': 'audio',
  5348.         'videos': 'videos',
  5349.         'favoris': 'favoris'
  5350.       };
  5351.       
  5352.       const mappedFilter = filterMap[filter] || filter;
  5353.       
  5354.       // Pour le filtre "all", s'assurer que la vue des jours est restaurée
  5355.       if (filter === 'all') {
  5356.         const containerGallery = document.querySelector('.container--gallery');
  5357.         if (containerGallery) {
  5358.           containerGallery.style.display = 'block';
  5359.         }
  5360.       }
  5361.       
  5362.       if (typeof window.filterContent === 'function') {
  5363.         window.filterContent(mappedFilter);
  5364.       } else {
  5365.         console.error('window.filterContent n\'est pas disponible');
  5366.       }
  5367.     }
  5368.     // Click handlers - exactement la même logique que stat-pill
  5369.     document.addEventListener('click', (e) => {
  5370.       const btn = e.target.closest('.mtb-btn');
  5371.       if (!btn) return;
  5372.       // Bouton fermer
  5373.       if (btn.classList.contains('mtb-close')) {
  5374.         document.querySelector('.media-toolbar')?.remove();
  5375.         return;
  5376.       }
  5377.       const filter = btn.dataset.filter;
  5378.       if (!filter) return;
  5379.       // 🎯 CAS SPÉCIAL : Bouton favoris (filtre + ouvre sidebar, JAMAIS de modification du compteur)
  5380.       if (filter === 'favoris') {
  5381.         console.log('💗 BOUTON FAVORIS CLIQUÉ - Vue favoris + ouverture sidebar');
  5382.         
  5383.         // 🛡️ Protection ABSOLUE : empêcher TOUTE propagation vers d'autres handlers
  5384.         e.stopPropagation();
  5385.         e.stopImmediatePropagation();
  5386.         e.preventDefault();
  5387.         
  5388.         // 1️⃣ Activer visuellement le bouton favoris
  5389.         const mtbBtns = document.querySelectorAll('.mtb-btn[data-filter]');
  5390.         mtbBtns.forEach(b => {
  5391.           if (b === btn) {
  5392.             b.classList.add('active');
  5393.           } else {
  5394.             b.classList.remove('active');
  5395.           }
  5396.         });
  5397.         
  5398.         // 2️⃣ Filtrer la galerie pour afficher uniquement les favoris
  5399.         if (typeof window.filterContent === 'function') {
  5400.           window.filterContent('favoris');
  5401.         } else {
  5402.           console.error('❌ window.filterContent non disponible');
  5403.         }
  5404.         
  5405.         // 3️⃣ Ouvrir le sidebar e-commerce via le bouton cadeau
  5406.         const giftButton = document.getElementById('gift-button-trigger');
  5407.         if (giftButton) {
  5408.           console.log('🎁 Ouverture du sidebar via bouton cadeau');
  5409.           setTimeout(() => giftButton.click(), 100); // Petit délai pour éviter les conflits
  5410.         } else {
  5411.           console.warn('⚠️ Bouton cadeau non trouvé');
  5412.         }
  5413.         
  5414.         return; // ⛔ SORTIE IMMÉDIATE - pas de traitement supplémentaire
  5415.       }
  5416.       // 🎯 CAS SPÉCIAL : Bouton audio avec 0 message → afficher popover
  5417.       if (filter === 'audio') {
  5418.         const audioCount = btn.querySelector('.mtb-count[data-bind="audio"]');
  5419.         const count = audioCount ? parseInt(audioCount.textContent.trim()) : 0;
  5420.         
  5421.         if (count === 0) {
  5422.           console.log('🎤 Bouton audio cliqué avec 0 message - affichage popover');
  5423.           
  5424.           e.stopPropagation();
  5425.           e.preventDefault();
  5426.           
  5427.           // Afficher le popover premium
  5428.           showAudioEmptyPopover(btn);
  5429.           return; // ⛔ Ne pas filtrer si 0 message
  5430.         }
  5431.       }
  5432.       // Exception pour le bouton photos : ne pas modifier les états actifs
  5433.       if (btn.dataset.filter === 'photos') {
  5434.         // Pour le bouton photos, ne pas toucher aux autres boutons
  5435.         // Juste exécuter l'action sans changer les états
  5436.       } else {
  5437.         // Pour les autres boutons : gérer les états actifs normalement
  5438.         const mtbBtns = document.querySelectorAll('.mtb-btn[data-filter]');
  5439.         for (let i = 0; i < mtbBtns.length; i++) {
  5440.           const b = mtbBtns[i];
  5441.           if (b && b.classList) {
  5442.             if (b === btn) {
  5443.               b.classList.add('active');
  5444.             } else {
  5445.               b.classList.remove('active');
  5446.             }
  5447.           }
  5448.         }
  5449.       }
  5450.       
  5451.       // Déclencher le filtrage du contenu - même logique que stat-pill
  5452.       console.log('mtb-btn click:', filter, 'filterContent type:', typeof window.filterContent);
  5453.       if (typeof window.filterContent === 'function') {
  5454.         window.filterContent(filter);
  5455.       } else {
  5456.         console.error('window.filterContent n\'est pas une fonction!');
  5457.       }
  5458.     });
  5459.     // Expose un setter global
  5460.     window.setMediaFilter = (filter) => {
  5461.       // Pour le bouton photos : ne pas modifier les états
  5462.       if (filter !== 'photos') {
  5463.         setActive(filter);
  5464.       }
  5465.       applyFilter(filter);
  5466.     };
  5467.     // 🛡️ PROTECTION ABSOLUE : Empêcher TOUTE interaction directe avec le compteur favoris
  5468.     document.addEventListener('DOMContentLoaded', () => {
  5469.       const mesFavCount = document.getElementById('mesFavCount');
  5470.       if (mesFavCount) {
  5471.         // Bloquer tous les clics directs sur le compteur
  5472.         mesFavCount.addEventListener('click', (e) => {
  5473.           console.log('⚠️ Clic sur mesFavCount bloqué');
  5474.           e.stopPropagation();
  5475.           e.stopImmediatePropagation();
  5476.           e.preventDefault();
  5477.           return false;
  5478.         }, true); // Capture phase pour intercepter avant tout autre listener
  5479.         
  5480.         // Empêcher le double-clic
  5481.         mesFavCount.addEventListener('dblclick', (e) => {
  5482.           console.log('⚠️ Double-clic sur mesFavCount bloqué');
  5483.           e.stopPropagation();
  5484.           e.stopImmediatePropagation();
  5485.           e.preventDefault();
  5486.           return false;
  5487.         }, true);
  5488.         
  5489.         // Ajouter un style pour indiquer visuellement que c'est non-cliquable
  5490.         mesFavCount.style.pointerEvents = 'none';
  5491.         mesFavCount.style.userSelect = 'none';
  5492.         
  5493.         console.log('🛡️ Protection mesFavCount activée - compteur non-cliquable');
  5494.       }
  5495.       
  5496.       // 🎤 Initialiser l'état du bouton audio (griser si 0 message)
  5497.       initAudioButtonState();
  5498.     });
  5499.     // 🎤 Fonction pour afficher le popover "Pas de message audio"
  5500.     function showAudioEmptyPopover(btn) {
  5501.       // Supprimer les popovers existants
  5502.       const existingPopover = document.querySelector('.audio-empty-popover');
  5503.       if (existingPopover) {
  5504.         existingPopover.remove();
  5505.       }
  5506.       
  5507.       // Créer le popover
  5508.       const popover = document.createElement('div');
  5509.       popover.className = 'audio-empty-popover';
  5510.       popover.innerHTML = `
  5511.         <div class="audio-popover-content">
  5512.           <div class="audio-popover-icon">
  5513.             <i class="bi bi-mic-mute-fill"></i>
  5514.           </div>
  5515.           <div class="audio-popover-text">
  5516.             <strong>Aucun message audio</strong>
  5517.             <p>Pas de message audio soumis pour l'instant</p>
  5518.           </div>
  5519.         </div>
  5520.       `;
  5521.       
  5522.       // Positionner le popover
  5523.       document.body.appendChild(popover);
  5524.       
  5525.       const btnRect = btn.getBoundingClientRect();
  5526.       popover.style.position = 'fixed';
  5527.       popover.style.top = `${btnRect.bottom + 10}px`;
  5528.       popover.style.left = `${btnRect.left + (btnRect.width / 2)}px`;
  5529.       popover.style.transform = 'translateX(-50%)';
  5530.       
  5531.       // Animation d'entrée
  5532.       setTimeout(() => popover.classList.add('show'), 10);
  5533.       
  5534.       // Auto-fermeture après 3 secondes
  5535.       setTimeout(() => {
  5536.         popover.classList.remove('show');
  5537.         setTimeout(() => popover.remove(), 300);
  5538.       }, 3000);
  5539.       
  5540.       // Fermer au clic ailleurs
  5541.       const closeOnClick = (e) => {
  5542.         if (!popover.contains(e.target) && !btn.contains(e.target)) {
  5543.           popover.classList.remove('show');
  5544.           setTimeout(() => popover.remove(), 300);
  5545.           document.removeEventListener('click', closeOnClick);
  5546.         }
  5547.       };
  5548.       setTimeout(() => document.addEventListener('click', closeOnClick), 100);
  5549.     }
  5550.     // 🎤 Fonction pour initialiser l'état du bouton audio
  5551.     function initAudioButtonState() {
  5552.       const audioBtn = document.querySelector('.mtb-btn[data-filter="audio"]');
  5553.       if (!audioBtn) return;
  5554.       
  5555.       const audioCount = audioBtn.querySelector('.mtb-count[data-bind="audio"]');
  5556.       if (!audioCount) return;
  5557.       
  5558.       const count = parseInt(audioCount.textContent.trim()) || 0;
  5559.       
  5560.       if (count === 0) {
  5561.         // Masquer le compteur "0"
  5562.         audioCount.style.display = 'none';
  5563.         
  5564.         // Griser l'icône
  5565.         audioBtn.classList.add('disabled-audio');
  5566.         audioBtn.style.opacity = '0.4';
  5567.         audioBtn.style.cursor = 'not-allowed';
  5568.         
  5569.         const icons = audioBtn.querySelectorAll('i');
  5570.         icons.forEach(icon => {
  5571.           icon.style.color = '#999 !important';
  5572.           icon.style.filter = 'grayscale(100%)';
  5573.         });
  5574.         
  5575.         console.log('🎤 Bouton audio désactivé (0 message)');
  5576.       } else {
  5577.         // Afficher le compteur
  5578.         audioCount.style.display = 'inline';
  5579.         
  5580.         // Activer l'icône
  5581.         audioBtn.classList.remove('disabled-audio');
  5582.         audioBtn.style.opacity = '1';
  5583.         audioBtn.style.cursor = 'pointer';
  5584.         
  5585.         console.log(`🎤 Bouton audio activé (${count} message${count > 1 ? 's' : ''})`);
  5586.       }
  5587.     }
  5588.     // 🎤 Observer les changements du compteur audio pour mettre à jour l'état
  5589.     const audioCountObserver = new MutationObserver(() => {
  5590.       initAudioButtonState();
  5591.     });
  5592.     
  5593.     // Démarrer l'observation quand le DOM est prêt
  5594.     if (document.readyState === 'loading') {
  5595.       document.addEventListener('DOMContentLoaded', () => {
  5596.         const audioCount = document.querySelector('.mtb-count[data-bind="audio"]');
  5597.         if (audioCount) {
  5598.           audioCountObserver.observe(audioCount, { 
  5599.             childList: true, 
  5600.             characterData: true, 
  5601.             subtree: true 
  5602.           });
  5603.         }
  5604.       });
  5605.     } else {
  5606.       const audioCount = document.querySelector('.mtb-count[data-bind="audio"]');
  5607.       if (audioCount) {
  5608.         audioCountObserver.observe(audioCount, { 
  5609.           childList: true, 
  5610.           characterData: true, 
  5611.           subtree: true 
  5612.         });
  5613.       }
  5614.     }
  5615.     
  5616.     // Mise à jour des compteurs quand les favoris changent
  5617.     window.addEventListener('favorites:updated', updateCounts);
  5618.     
  5619.     // Initialisation
  5620.     setTimeout(() => {
  5621.       updateCounts();
  5622.       setActive('all');
  5623.     }, 100);
  5624.     // Mise à jour périodique des compteurs (sauf favoris qui reste fixe)
  5625.     setInterval(updateCounts, 2000);
  5626.   })();
  5627.   function navigateToDay(index) {
  5628.      if (index >= 0 && index < collapseSections.length) {
  5629.       // Fermer tous les jours
  5630.       collapseSections.forEach(s => {
  5631.         if (s && s.classList) {
  5632.           s.classList.remove('show');
  5633.         }
  5634.       });
  5635.       dateCards.forEach(c => {
  5636.         if (c && c.classList) {
  5637.           c.classList.remove('active');
  5638.         }
  5639.       });
  5640.       // Ouvrir le bon jour
  5641.       const targetCollapse = collapseSections[index];
  5642.       const targetCard = dateCards[index];
  5643.       if (targetCollapse && targetCard) {
  5644.         if (targetCollapse.classList) {
  5645.           targetCollapse.classList.add('show');
  5646.         }
  5647.         if (targetCard.classList) {
  5648.           targetCard.classList.add('active');
  5649.         }
  5650.       }
  5651.     }
  5652.   }
  5653.   prevButtons.forEach(button => {
  5654.     button.addEventListener('click', function () {
  5655.       const targetIndex = parseInt(this.dataset.target, 10);
  5656.       navigateToDay(targetIndex);
  5657.     });
  5658.   });
  5659.   nextButtons.forEach(button => {
  5660.     button.addEventListener('click', function () {
  5661.       const targetIndex = parseInt(this.dataset.target, 10);
  5662.       navigateToDay(targetIndex);
  5663.     });
  5664.   });
  5665. });
  5666. </script>
  5667.   <script>
  5668.     document.addEventListener('DOMContentLoaded', function() {
  5669.   const style = document.createElement('style');
  5670.   style.textContent = `
  5671.     .hidden {
  5672.       display: none !important;
  5673.     }
  5674.   `;
  5675.   document.head.appendChild(style);
  5676. });
  5677.     document.addEventListener('DOMContentLoaded', function() {
  5678.     // Get the gift button
  5679.     const giftButton = document.querySelector('.gift-button');
  5680.     if (giftButton) {
  5681.       // Keep it clickable for the gift functionality
  5682.       giftButton.style.pointerEvents = 'auto';
  5683.       
  5684.       // Add click handler for gift action
  5685.       giftButton.onclick = function(e) {
  5686.         e.preventDefault();
  5687.         e.stopPropagation();
  5688.         return false;
  5689.       };
  5690.       
  5691.       // Make sure hover still works
  5692.       favoriteButton.addEventListener('mouseover', function() {
  5693.         document.getElementById('purchase-alert').style.display = 'block';
  5694.       });
  5695.       
  5696.       // Keep any existing hover functionality
  5697.       if (typeof showSelection === 'function') {
  5698.         favoriteButton.onmouseover = showSelection;
  5699.       }
  5700.     }
  5701.     
  5702.     // Make sure the purchase alert remains interactive
  5703.     const purchaseAlert = document.getElementById('purchase-alert');
  5704.     if (purchaseAlert) {
  5705.       purchaseAlert.style.pointerEvents = 'auto';
  5706.     }
  5707.   });
  5708.     // Sélection des éléments
  5709.     const purchaseAlert = document.getElementById("purchase-alert");
  5710.     const alertContent = document.getElementById("purchase-alert-content");
  5711.     const likeCountLabel = document.getElementById("likeCount");
  5712.     
  5713.     // Fonction pour obtenir le nombre actuel de favoris
  5714.     function getCurrentFavoriteCount() {
  5715.         const likeCountLabel = document.getElementById("likeCount");
  5716.         return parseInt(likeCountLabel?.textContent.trim(), 10) || 0;
  5717.     }
  5718.     // Fonction pour mettre à jour le contenu de l'alerte
  5719.     function updatePurchaseAlert(current) {
  5720.   let remainingForAlbum = Math.max(0, 20 - current);
  5721.   let remainingForPochette = Math.max(0, 12 - current);
  5722.   let remainingForPack = Math.max(0, 12 - current);
  5723.   const progressBar = (count, total, color) => `
  5724.   <div style="margin: 5px 0;">
  5725.     <div style="background-color: #e9ecef; border-radius: 5px; overflow: hidden; height: 8px;">
  5726.       <div style="width: ${
  5727.         (count / total) * 100
  5728.       }%; background-color: ${color}; height: 100%;"></div>
  5729.     </div>
  5730.     <small style="font-size: 12px;">${count}/${total} photos</small>
  5731.   </div>
  5732. `;
  5733.   // Use Twig paths here:
  5734.   const products = [
  5735.       {
  5736.       name: "Pochette photo (12 photos)",
  5737.       required: 12,
  5738.       remaining: remainingForPochette,
  5739.       image: "/images/produit/PochettePhoto5sur5-2.jpg",
  5740.       color: "#2196f3",
  5741.       link: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}",
  5742.     },
  5743.        {
  5744.       name: "Pack numérique (20 photos)",
  5745.       required: 20,
  5746.       remaining: remainingForAlbum,
  5747.       image: "/images/produit/photoNumerique.jpg",
  5748.       color: "#4caf50",
  5749.       link: "{{ path('PackPhotosNumerique_Favoris', {'nbr': 20}) }}",
  5750.     }
  5751.   
  5752.   
  5753.     
  5754.   ].sort((a, b) => a.remaining - b.remaining);
  5755.   const productList = products
  5756.     .map((product) => {
  5757.       const count = current;
  5758.       const total = product.required;
  5759.       const remaining = product.remaining;
  5760.       return `
  5761.     <li style="margin-bottom: 15px;">
  5762.       <div style="display: flex; align-items: center; gap: 10px;">
  5763.         <img
  5764.           src="${product.image}"
  5765.           alt="${product.name}"
  5766.           style="width: 65px; height: 65px; border-radius: 5px; margin-top:-19px"
  5767.         />
  5768.         <div style="flex: 1;">
  5769.           <strong style="font-size: 14px;">${product.name}</strong>
  5770.           ${progressBar(count, total, product.color)}
  5771.           ${
  5772.             remaining > 0
  5773.               ? `<small style="color: ${product.color}; font-size: 12px;">
  5774.                    Encore ${remaining} photos ❤️ pour compléter ${product.name.toLowerCase()}
  5775.                  </small>`
  5776.               : `<button
  5777.                   style="
  5778.                     margin-top: 5px;
  5779.                     padding: 5px 8px;
  5780.                     background-color: ${product.color};
  5781.                     color: white;
  5782.                     border: none;
  5783.                     border-radius: 5px;
  5784.                     font-size: 12px;
  5785.                     cursor: pointer;
  5786.                   "
  5787.                   onclick="window.location.href='${product.link}'"
  5788.                 >
  5789.                   Commander
  5790.                 </button>`
  5791.           }
  5792.         </div>
  5793.       </div>
  5794.     </li>
  5795.   `;
  5796.     })
  5797.     .join("");
  5798.   const plusButton = `
  5799.     <li style="margin-bottom: 15px; text-align: center;">
  5800.       <button
  5801.         style="
  5802.           padding: 5px 8px;
  5803.           background-color: #F56040;
  5804.           color: white;
  5805.           border: none;
  5806.           border-radius: 5px;
  5807.           font-size: 14px;
  5808.           line-height: 1;
  5809.           width: 150px;
  5810.           height: 40px;
  5811.           cursor: pointer;
  5812.         "
  5813.         onclick="window.location.href='{{ path('boutique5sur5') }}'"
  5814.       >
  5815.         Aller à la boutique
  5816.       </button>
  5817.     </li>
  5818.   `;
  5819.   if (current === 0) {
  5820.     alertContent.innerHTML = `
  5821.     <p style="font-size: 16px; font-weight: bold; color: #333;">
  5822.       Vous n'avez pas encore de photos favorites !
  5823.     </p>
  5824.     <p style="margin-bottom: 20px; color: #555;">
  5825.       Commencez à ajouter vos moments préférés pour profiter de nos offres.
  5826.     </p>
  5827.     <ul style="list-style-type: none; padding: 0;">${productList}${plusButton}</ul>
  5828.   `;
  5829.   } else {
  5830.     alertContent.innerHTML = `
  5831.     <p style="font-size: 16px; font-weight: bold; color: #333;">
  5832.       Vous avez atteint ${current} photos favorites !
  5833.     </p>
  5834.     <p style="margin-bottom: 20px; color: #555;">
  5835.       Profitez de nos offres spéciales :
  5836.     </p>
  5837.     <ul style="list-style-type: none; padding: 0;">${productList}${plusButton}</ul>
  5838.   `;
  5839.   }
  5840.   if (purchaseAlert && purchaseAlert.classList) {
  5841.     purchaseAlert.classList.remove("hidden");
  5842.   }
  5843.   clearTimeout(window.purchaseAlertTimeout);
  5844.   window.purchaseAlertTimeout = setTimeout(() => {
  5845.     if (!purchaseAlert.matches(":hover")) {
  5846.       closePurchaseAlert();
  5847.     }
  5848.   }, 5000);
  5849. }
  5850.     // Fonction pour fermer l'alerte
  5851.     function closePurchaseAlert() {
  5852.       if (purchaseAlert && purchaseAlert.classList) {
  5853.         purchaseAlert.classList.add("hidden");
  5854.       }
  5855.     }
  5856.     // Événement pour mettre à jour le contenu et afficher la popover dynamiquement au hover
  5857.     document.querySelector(".gift-button").addEventListener("mouseover", () => {
  5858.       const currentCount = getCurrentFavoriteCount();
  5859.       updatePurchaseAlert(currentCount);
  5860.       if (purchaseAlert && purchaseAlert.classList) {
  5861.         purchaseAlert.classList.remove("cachee"); // Réaffiche la popover
  5862.       }
  5863.     });
  5864.     // Nouvelles fonctions pour le bouton cadeau
  5865.     function showGiftMessage() {
  5866.       const tooltip = document.getElementById("giftTooltip");
  5867.       if (tooltip) {
  5868.         if (tooltip && tooltip.classList) {
  5869.           tooltip.classList.add("show");
  5870.         }
  5871.       }
  5872.     }
  5873.     function hideGiftMessage() {
  5874.       const tooltip = document.getElementById("giftTooltip");
  5875.       if (tooltip) {
  5876.         if (tooltip && tooltip.classList) {
  5877.           tooltip.classList.remove("show");
  5878.         }
  5879.       }
  5880.     }
  5881.     function showSelection() {
  5882.       document.getElementById("purchase-alert").style.display = "block";
  5883.     }
  5884.     function hideSelection() {
  5885.       document.getElementById("selectionPopover").style.display = "none";
  5886.     }
  5887.     document.addEventListener("DOMContentLoaded", function () {
  5888.       const container = document.querySelector(".date-container");
  5889.       // Vérifie si le conteneur existe pour éviter les erreurs
  5890.       if (container) {
  5891.         container.scrollTo({
  5892.           left: container.scrollWidth, // Scroll directement à la position maximale
  5893.           behavior: "smooth", // Défilement fluide
  5894.         });
  5895.       }
  5896.     });
  5897.     document.addEventListener("DOMContentLoaded", function () {
  5898.       const container = document.querySelector(".date-container");
  5899.       const leftArrow = document.querySelector(".scroll-btn.left");
  5900.       const rightArrow = document.querySelector(".scroll-btn.right");
  5901.       // Fonction pour vérifier le débordement et activer/désactiver les flèches
  5902.       function updateArrowsVisibility() {
  5903.         const isOverflowing = container.scrollWidth > container.clientWidth; // Vérifie si débordement
  5904.         leftArrow.style.display = isOverflowing ? "flex" : "none";
  5905.         rightArrow.style.display = isOverflowing ? "flex" : "none";
  5906.       }
  5907.       // Fonction pour défiler
  5908.       function scrollContainer(direction) {
  5909.         container.scrollBy({
  5910.           left: direction === "left" ? -200 : 200, // Défiler à gauche ou à droite
  5911.           behavior: "smooth",
  5912.         });
  5913.       }
  5914.       // Ajout des événements de clic pour les flèches
  5915.       leftArrow.addEventListener("click", () => scrollContainer("left"));
  5916.       rightArrow.addEventListener("click", () => scrollContainer("right"));
  5917.       // ⚡ OPTIMISÉ: Debounce resize pour éviter surcharge
  5918.       let resizeTimeout;
  5919.       const debouncedResize = () => {
  5920.         clearTimeout(resizeTimeout);
  5921.         resizeTimeout = setTimeout(updateArrowsVisibility, 150);
  5922.       };
  5923.       
  5924.       updateArrowsVisibility();
  5925.       window.addEventListener("resize", debouncedResize, { passive: true });
  5926.     });
  5927.     document.addEventListener("DOMContentLoaded", function () {
  5928.       const container = document.querySelector(".date-container");
  5929.       const leftBtn = document.querySelector(".scroll-btn.left");
  5930.       const rightBtn = document.querySelector(".scroll-btn.right");
  5931.       leftBtn.addEventListener("click", () => {
  5932.         container.scrollBy({
  5933.           left: -200, // Défile vers la gauche
  5934.           behavior: "smooth",
  5935.         });
  5936.       });
  5937.       rightBtn.addEventListener("click", () => {
  5938.         container.scrollBy({
  5939.           left: 200, // Défile vers la droite
  5940.           behavior: "smooth",
  5941.         });
  5942.       });
  5943.     });
  5944.  document.addEventListener("DOMContentLoaded", function () {
  5945.     // Sélectionnez tous les badges de filtre
  5946.     const filterBadges = document.querySelectorAll(".filter-badge");
  5947.     // Sélectionnez tous les éléments de la galerie
  5948.     const galleryItems = document.querySelectorAll(".column");
  5949.     // Sélectionnez tous les jours
  5950.     const days = document.querySelectorAll(".collapse");
  5951.     // Fonction pour réinitialiser les filtres
  5952.     function resetFilters() {
  5953.         // Réinitialisez tous les éléments de la galerie
  5954.         galleryItems.forEach((item) => {
  5955.             item.style.display = "block";
  5956.         });
  5957.         // Réinitialisez les états des badges
  5958.         filterBadges.forEach((badge) => {
  5959.           if (badge && badge.classList) {
  5960.             badge.classList.remove("active");
  5961.           }
  5962.         });
  5963.     }
  5964.     // Ajoutez un gestionnaire d'événements pour chaque badge
  5965.     filterBadges.forEach((badge) => {
  5966.         badge.addEventListener("click", function () {
  5967.             const filter = this.getAttribute("data-filter");
  5968.             // Réinitialisez l'état actif pour tous les badges
  5969.             filterBadges.forEach((btn) => {
  5970.               if (btn && btn.classList) {
  5971.                 btn.classList.remove("active");
  5972.               }
  5973.             });
  5974.             // Ajoutez l'état actif au badge cliqué
  5975.             if (this && this.classList) {
  5976.               this.classList.add("active");
  5977.             }
  5978.             // Affichez ou masquez les éléments de la galerie
  5979.             galleryItems.forEach((item) => {
  5980.                 if (filter === "all") {
  5981.                     item.style.display = "block";
  5982.                 } else if (filter === "photo" && item.querySelector("img")) {
  5983.                     item.style.display = "block";
  5984.                 } else if (filter === "video" && item.querySelector("video")) {
  5985.                     item.style.display = "block";
  5986.                 } else if (filter === "audio" && item.classList && item.classList.contains("audio-message-item")) {
  5987.                     item.style.display = "block";
  5988.                 } else {
  5989.                     item.style.display = "none";
  5990.                 }
  5991.             });
  5992.         });
  5993.     });
  5994.     // Réinitialiser les filtres lors du changement de jour
  5995.     days.forEach((day) => {
  5996.         day.addEventListener("show.bs.collapse", function () {
  5997.             resetFilters();
  5998.         });
  5999.     });
  6000. });
  6001.     $(document).ready(function () {
  6002.       let zoomCounter = 0; // Initialize zoom counter
  6003.       let currentImageSrc = ""; // Track current image source
  6004.       let lastClickPosition = { x: 50, y: 50 }; // Default to center of image
  6005.       $(".container--gallery").magnificPopup({
  6006.         delegate: "a",
  6007.         type: "image",
  6008.         mainClass: "mfp-with-zoom mfp-img-mobile",
  6009.         image: {
  6010.           verticalFit: true,
  6011.         },
  6012.         gallery: {
  6013.           enabled: true,
  6014.           tPrev: "Previous (Left arrow key)", // Alt text on left arrow
  6015.           tNext: "Next (Right arrow key)", // Alt text on right arrow
  6016.           tCounter: "%curr% of %total%", // Markup for "1 of 7" counter
  6017.         },
  6018.         zoom: {
  6019.           enabled: true,
  6020.           duration: 300,
  6021.           opener: function (element) {
  6022.             return element.find("img");
  6023.           },
  6024.         },
  6025.         callbacks: {
  6026.           open: function () {
  6027.             // Get current image data from the link that was clicked
  6028.             const currentLink = this.currItem.el;
  6029.             const imageId =
  6030.               currentLink
  6031.                 .closest(".photo-zoom")
  6032.                 .find(".heart-icon")
  6033.                 .data("id") || "";
  6034.             const sejourId =
  6035.               currentLink
  6036.                 .closest(".photo-zoom")
  6037.                 .find(".heart-icon")
  6038.                 .data("sejour-id") || "";
  6039.             const imagePath =
  6040.               currentLink
  6041.                 .closest(".photo-zoom")
  6042.                 .find(".heart-icon")
  6043.                 .data("path") || "";
  6044.             const imageDesc =
  6045.               currentLink
  6046.                 .closest(".photo-zoom")
  6047.                 .find(".heart-icon")
  6048.                 .data("description") || "";
  6049.             const isFavorite = currentLink
  6050.               .closest(".photo-zoom")
  6051.               .find(".heart-icon i")
  6052.               .hasClass("bi-heart-fill");
  6053.             const favoriteIconClass = isFavorite ? "bi-heart-fill" : "bi-heart";
  6054.             const favoriteIconColor = isFavorite ? "#f56040" : "white";
  6055.             const favoriteTooltip = isFavorite
  6056.               ? "Retirer des favoris"
  6057.               : "Ajouter aux favoris";
  6058.             const zoomControls = `
  6059.           <div class="mfp-zoom-controls">
  6060.             <button class="zoom-btn zoom-out" title="Zoom Out"><i class="fa fa-search-minus"></i></button>
  6061.             <button class="zoom-btn zoom-in" title="Zoom In"><i class="fa fa-search-plus"></i></button>
  6062.           </div>
  6063.           <div class="mfp-favorite">
  6064.             <button class="favorite-btn" 
  6065.                     data-id="${imageId}" 
  6066.                     data-sejour-id="${sejourId}" 
  6067.                     data-path="${imagePath}" 
  6068.                     data-description="${imageDesc}"
  6069.                     title="${favoriteTooltip}">
  6070.               <i class="bi ${favoriteIconClass}" style="color: ${favoriteIconColor}; text-shadow: 0px 0px 3px rgba(0,0,0,0.5);"></i>
  6071.             </button>
  6072.           </div>
  6073.           <div class="mfp-counter"></div>
  6074.         `;
  6075.             $(".mfp-content").append(zoomControls);
  6076.             initializeZoomControls();
  6077.             initializeFavoriteButton();
  6078.             const intervalId = setInterval(() => {
  6079.               const newImageSrc = $(".mfp-img").attr("src");
  6080.               if (newImageSrc !== currentImageSrc) {
  6081.                 currentImageSrc = newImageSrc;
  6082.                 zoomCounter = 0;
  6083.                 lastClickPosition = { x: 50, y: 50 }; // Reset to center
  6084.                 attachZoomHandler(); // Reattach zoom handler to new image
  6085.                 $(".mfp-img").css({
  6086.                   "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  6087.                   transform: `scale(1)`,
  6088.                 });
  6089.                 // Update favorite button for the new image
  6090.                 updateFavoriteButton();
  6091.                 initializeZoomControls();
  6092.                 updateCounter();
  6093.               }
  6094.             }, 100);
  6095.             this.content.on("mfpClose", function () {
  6096.               clearInterval(intervalId);
  6097.             });
  6098.             attachZoomHandler();
  6099.           },
  6100.           close: function () {
  6101.             $(".mfp-zoom-controls").remove();
  6102.             $(".mfp-favorite").remove();
  6103.             $(".mfp-counter").remove();
  6104.             zoomCounter = 0;
  6105.           },
  6106.         },
  6107.       });
  6108.       function attachZoomHandler() {
  6109.         $(".mfp-img")
  6110.           .off("click")
  6111.           .on("click", function (event) {
  6112.             event.stopPropagation(); // Prevent default navigation behavior
  6113.             // Calculate click coordinates relative to the image
  6114.             const imgOffset = $(this).offset();
  6115.             const clickX = event.pageX - imgOffset.left;
  6116.             const clickY = event.pageY - imgOffset.top;
  6117.             const imgWidth = $(this).width();
  6118.             const imgHeight = $(this).height();
  6119.             // Calculate transform-origin based on click position
  6120.             lastClickPosition = {
  6121.               x: (clickX / imgWidth) * 100,
  6122.               y: (clickY / imgHeight) * 100,
  6123.             };
  6124.             // Cycle through zoom levels: 1x, 1.5x, 2x
  6125.             zoomCounter = (zoomCounter + 1) % 3;
  6126.             const zoomLevels = [1, 1.5, 2];
  6127.             const zoomLevel = zoomLevels[zoomCounter];
  6128.             $(this).css({
  6129.               "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  6130.               transform: `scale(${zoomLevel})`,
  6131.             });
  6132.             updateZoomButtonState();
  6133.           });
  6134.       }
  6135.       function initializeZoomControls() {
  6136.         $(".mfp-zoom-controls .zoom-in")
  6137.           .off("click")
  6138.           .on("click", function (event) {
  6139.             event.stopPropagation();
  6140.             zoomCounter = (zoomCounter + 1) % 3;
  6141.             const zoomLevels = [1, 1.5, 2];
  6142.             const zoomLevel = zoomLevels[zoomCounter];
  6143.             $(".mfp-img").css({
  6144.               "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  6145.               transform: `scale(${zoomLevel})`,
  6146.             });
  6147.             updateZoomButtonState();
  6148.           });
  6149.         $(".mfp-zoom-controls .zoom-out")
  6150.           .off("click")
  6151.           .on("click", function (event) {
  6152.             event.stopPropagation();
  6153.             if (zoomCounter > 0) {
  6154.               zoomCounter -= 1;
  6155.               const zoomLevels = [1, 1.5, 2];
  6156.               const zoomLevel = zoomLevels[zoomCounter];
  6157.               $(".mfp-img").css({
  6158.                 "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  6159.                 transform: `scale(${zoomLevel})`,
  6160.               });
  6161.               updateZoomButtonState();
  6162.             } else {
  6163.               $.magnificPopup.close();
  6164.             }
  6165.           });
  6166.       }
  6167.       function initializeFavoriteButton() {
  6168.         $(".mfp-favorite .favorite-btn")
  6169.           .off("click")
  6170.           .on("click", function (event) {
  6171.             event.stopPropagation();
  6172.             const $this = $(this);
  6173.             const imageId = $this.data("id");
  6174.             const sejourId = $this.data("sejour-id");
  6175.             // Toggle favorite status
  6176.             const isFavorite = $this.find("i").hasClass("bi-heart-fill");
  6177.             // Update the button appearance
  6178.             if (isFavorite) {
  6179.               $this
  6180.                 .find("i")
  6181.                 .removeClass("bi-heart-fill")
  6182.                 .addClass("bi-heart")
  6183.                 .css("color", "white");
  6184.               $this.attr("title", "Ajouter aux favoris");
  6185.             } else {
  6186.               $this
  6187.                 .find("i")
  6188.                 .removeClass("bi-heart")
  6189.                 .addClass("bi-heart-fill")
  6190.                 .css("color", "#f56040");
  6191.               $this.attr("title", "Retirer des favoris");
  6192.             }
  6193.             // Update the original heart icon in the gallery
  6194.             const originalHeartIcon = $(
  6195.               `.heart-icon[data-id="${imageId}"]`
  6196.             ).find("i");
  6197.             if (isFavorite) {
  6198.               originalHeartIcon
  6199.                 .removeClass("bi-heart-fill")
  6200.                 .addClass("bi-heart")
  6201.                 .css("color", "");
  6202.             } else {
  6203.               originalHeartIcon
  6204.                 .removeClass("bi-heart")
  6205.                 .addClass("bi-heart-fill")
  6206.                 .css("color", "#f56040");
  6207.             }
  6208.             // Make AJAX call to update favorite status in the backend using Parent routes
  6209.             $.ajax({
  6210.               url: isFavorite ? "/Parent/aSupprimerFav" : "/Parent/ajouterFav",
  6211.               type: "POST",
  6212.               data: {
  6213.                 id: imageId,
  6214.                 idSejour: sejourId,
  6215.               },
  6216.               success: function (response) {
  6217.                 // Optional: Show a success message or handle response
  6218.                 console.log("Favorite status updated", response);
  6219.               },
  6220.               error: function (error) {
  6221.                 console.error("Error updating favorite status", error);
  6222.                 // Revert the icon change on error
  6223.                 if (isFavorite) {
  6224.                   $this
  6225.                     .find("i")
  6226.                     .removeClass("bi-heart")
  6227.                     .addClass("bi-heart-fill")
  6228.                     .css("color", "#f56040");
  6229.                   originalHeartIcon
  6230.                     .removeClass("bi-heart")
  6231.                     .addClass("bi-heart-fill")
  6232.                     .css("color", "#f56040");
  6233.                 } else {
  6234.                   $this
  6235.                     .find("i")
  6236.                     .removeClass("bi-heart-fill")
  6237.                     .addClass("bi-heart")
  6238.                     .css("color", "white");
  6239.                   originalHeartIcon
  6240.                     .removeClass("bi-heart-fill")
  6241.                     .addClass("bi-heart")
  6242.                     .css("color", "");
  6243.                 }
  6244.               },
  6245.             });
  6246.           });
  6247.       }
  6248.       function updateFavoriteButton() {
  6249.         // Get current image data from the current slide
  6250.         const currentSlide = $.magnificPopup.instance.currItem.el;
  6251.         const photoZoom = currentSlide.closest(".photo-zoom");
  6252.         if (photoZoom.length) {
  6253.           const heartIcon = photoZoom.find(".heart-icon");
  6254.           const imageId = heartIcon.data("id") || "";
  6255.           const sejourId = heartIcon.data("sejour-id") || "";
  6256.           const imagePath = heartIcon.data("path") || "";
  6257.           const imageDesc = heartIcon.data("description") || "";
  6258.           const isFavorite = heartIcon.find("i").hasClass("bi-heart-fill");
  6259.           const favoriteIconClass = isFavorite ? "bi-heart-fill" : "bi-heart";
  6260.           const favoriteIconColor = isFavorite ? "#f56040" : "white";
  6261.           const favoriteTooltip = isFavorite
  6262.             ? "Retirer des favoris"
  6263.             : "Ajouter aux favoris";
  6264.           // Update the favorite button
  6265.           const $favoriteBtn = $(".mfp-favorite .favorite-btn");
  6266.           $favoriteBtn.data("id", imageId);
  6267.           $favoriteBtn.data("sejour-id", sejourId);
  6268.           $favoriteBtn.data("path", imagePath);
  6269.           $favoriteBtn.data("description", imageDesc);
  6270.           $favoriteBtn.attr("title", favoriteTooltip);
  6271.           $favoriteBtn
  6272.             .find("i")
  6273.             .removeClass("bi-heart bi-heart-fill")
  6274.             .addClass(favoriteIconClass)
  6275.             .css("color", favoriteIconColor);
  6276.         }
  6277.       }
  6278.       function updateZoomButtonState() {
  6279.         const zoomLevels = [1, 1.5, 2];
  6280.         const currentZoom = zoomLevels[zoomCounter];
  6281.         $(".zoom-in").prop("disabled", currentZoom === 2);
  6282.         $(".zoom-out").prop("disabled", currentZoom === 1);
  6283.       }
  6284.       function updateCounter() {
  6285.         const counterText = $(".mfp-counter")
  6286.           .closest(".mfp-content")
  6287.           .find(".mfp-counter")
  6288.           .text();
  6289.         const matches = counterText.match(/(\d+) of (\d+)/);
  6290.         if (matches) {
  6291.           const currentIndex = matches[1];
  6292.           const totalImages = matches[2];
  6293.           $(".mfp-counter").text(`${currentIndex} of ${totalImages}`);
  6294.         }
  6295.       }
  6296.       // Add CSS for the favorite button and rounded image corners
  6297.       $("<style>")
  6298.         .prop("type", "text/css")
  6299.         .html(
  6300.           `
  6301.       .mfp-favorite {
  6302.         position: absolute;
  6303.         top: 15px;
  6304.         left: 15px;
  6305.         z-index: 1046;
  6306.       }
  6307.       .favorite-btn {
  6308.         background: transparent;
  6309.         border: none;
  6310.         font-size: 24px;
  6311.         padding: 5px;
  6312.         cursor: pointer;
  6313.         outline: none;
  6314.       }
  6315.       .favorite-btn i {
  6316.         transition: all 0.3s ease;
  6317.       }
  6318.       .favorite-btn:hover i {
  6319.         transform: scale(1.2);
  6320.       }
  6321.       /* Rounded corners for zoomed images */
  6322.       .mfp-img {
  6323.         border-radius: 8px;
  6324.       }
  6325.       /* Make sure the container doesn't clip the rounded corners */
  6326.       .mfp-figure:after {
  6327.         border-radius: 8px;
  6328.       }
  6329.     `
  6330.         )
  6331.         .appendTo("head");
  6332.     });
  6333.   </script>
  6334. <script>
  6335. document.addEventListener('DOMContentLoaded', function () {
  6336.   const openBtn = document.getElementById('openFavoritesBtn');
  6337.   const closeBtn = document.getElementById('closeSidebarBtn');
  6338.   const sidebar = document.getElementById('favoritesSidebar');
  6339.   const tbody = document.querySelector('#favoritesTable tbody');
  6340.   openBtn.addEventListener('click', async () => {
  6341.     try {
  6342.       const response = await fetch('/Parent/mes-favoris', {
  6343.         headers: {
  6344.           'Accept': 'application/json'
  6345.         }
  6346.       });
  6347.       const result = await response.json();
  6348.       if (!result.success || !Array.isArray(result.data)) {
  6349.         alert('Erreur lors du chargement des favoris.');
  6350.         return;
  6351.       }
  6352.       tbody.innerHTML = '';
  6353.       result.data.forEach((fav, index) => {
  6354.         const row = document.createElement('tr');
  6355.         row.innerHTML = `
  6356.           <td>${index + 1}</td>
  6357.           <td><img src="${fav.path}" alt="favori"></td>
  6358.           <td>${fav.descreption || '—'}</td>
  6359.           <td>${fav.created_at}</td>
  6360.         `;
  6361.         tbody.appendChild(row);
  6362.       });
  6363.       sidebar.classList.add('active');
  6364.     } catch (e) {
  6365.       console.error('Erreur réseau:', e);
  6366.       alert('Impossible de charger les favoris.');
  6367.     }
  6368.   });
  6369.   closeBtn.addEventListener('click', () => {
  6370.     sidebar.classList.remove('active');
  6371.   });
  6372. });
  6373. </script>
  6374.   <script>
  6375.         // Fonction pour vérifier et afficher l'alerte
  6376.         function checkFavoritesAlert() {
  6377.             const currentCount = window.favoriteCount || 0;
  6378.             if (currentCount >= 10) {
  6379.                 const purchaseAlert = document.getElementById('purchase-alert');
  6380.                 if (purchaseAlert) {
  6381.                     purchaseAlert.style.display = 'block'; // Affiche l'alerte
  6382.                 }
  6383.             } else {
  6384.                 purchaseAlert.style.display = 'none'; // Cache l'alerte si le nombre est réduit
  6385.             }
  6386.         }
  6387.     
  6388.         
  6389.         document.addEventListener('DOMContentLoaded', () => {
  6390.     const favoriteCount = {{ nblikes }};
  6391.     updateCardContent(favoriteCount);
  6392. });
  6393. function updateCardContent(favoriteCount) {
  6394.     const card = document.getElementById('dynamic-card');
  6395.     const cardContent = document.getElementById('dynamic-card-content');
  6396.     let produits = [];
  6397.     if (favoriteCount >= 20) {
  6398.         produits.push({
  6399.             titre: "Album débloqué !",
  6400.             bouton: "Commander",
  6401.             image: "/images/produit/Album5sur5-3.jpg",
  6402.             lien: "{{ path('EditionAlbum') }}"
  6403.         });
  6404.     }
  6405.     if (favoriteCount >= 12) {
  6406.         produits.push({
  6407.             titre: "Pochette débloquée !",
  6408.             bouton: "Commander",
  6409.             image: "/images/produit/PochettePhoto5sur5-2.jpg",
  6410.             lien: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}"
  6411.         });
  6412.     }
  6413.     if (favoriteCount >= 5) {
  6414.         produits.push({
  6415.             titre: "Pack numérique débloqué !",
  6416.             bouton: "Commander",
  6417.             image: "/images/produit/photoNumerique.jpg",
  6418.             lien: "{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}"
  6419.         });
  6420.     }
  6421. if (produits.length === 0) {
  6422.   cardContent.innerHTML = `
  6423.     <div style="position: relative; width: 100%; height: 140px; border-radius: 15px; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,0.1);">
  6424.       <div style="background-image: url('/images/produit/CoffretCadeau5sur5-2.jpg'); background-size: cover; background-position: center; width: 100%; height: 100%;">
  6425.         <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;">
  6426.           <div style="
  6427.     margin-top: 35px;
  6428.     color: white;
  6429.     font-size: 15px;
  6430.     font-weight: bold;
  6431.     text-align: center;
  6432.     line-height: 1;
  6433.     text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);">
  6434.             Ajoutez des favoris ❤️<br><span style="font-size: 16px; font-weight: normal;">pour débloquer un souvenir à offrir 🎁</span>
  6435.           </div>
  6436.         </div>
  6437.       </div>
  6438.     </div>
  6439.   `;
  6440.         return;
  6441.     }
  6442.     cardContent.innerHTML = `
  6443.     <div class="splide" id="dynamicSplide">
  6444.       <div class="splide__track">
  6445.         <ul class="splide__list">
  6446.           ${produits.map(produit => `
  6447.             <li class="splide__slide" style="position: relative;">
  6448.               <img src="${produit.image}" alt="${produit.titre}" style="width: 100%; height: 150px; object-fit: cover; border-radius: 8px;">
  6449.               <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;">
  6450.                 <div style="font-weight: bold; font-size: 14px;">${produit.titre}</div>
  6451.                 <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}'">
  6452.                   ${produit.bouton}
  6453.                 </button>
  6454.               </div>
  6455.             </li>
  6456.           `).join('')}
  6457.         </ul>
  6458.       </div>
  6459.     </div>
  6460.     `;
  6461.     // Monte le carrousel
  6462.     new Splide('#dynamicSplide', {
  6463.         type: 'loop',
  6464.         arrows: true,
  6465.         pagination: false,
  6466.         autoplay: true,
  6467.         interval: 4000,
  6468.         speed: 800,
  6469.     }).mount();
  6470. }
  6471.         // Fonction helper pour lire le compteur de favoris (gère les badges premium)
  6472.         function getFavorisCount() {
  6473.             const mesFavCount = document.getElementById('mesFavCount');
  6474.             if (!mesFavCount) return 0;
  6475.             
  6476.             const badge = mesFavCount.querySelector('.premium-5sur5-badge');
  6477.             if (badge) {
  6478.                 return parseInt(badge.textContent.trim() || '0');
  6479.             }
  6480.             
  6481.             const countText = mesFavCount.textContent.trim();
  6482.             if (countText === 'Pas de favoris' || countText.includes('Pas de favoris')) {
  6483.                 return 0;
  6484.             }
  6485.             
  6486.             return parseInt(countText || '0');
  6487.         }
  6488.         // Fonction helper pour mettre à jour l'affichage du compteur de favoris
  6489.         function updateFavorisDisplay(count) {
  6490.             const mesFavCount = document.getElementById('mesFavCount');
  6491.             const favorisBtn = document.querySelector('.mtb-btn[data-filter="favoris"]');
  6492.             const favorisIcon = favorisBtn?.querySelector('i.bi-heart-fill');
  6493.             
  6494.             if (!mesFavCount || !favorisBtn) return;
  6495.             
  6496.             if (count === 0) {
  6497.                 // Afficher "Pas de favoris" au lieu de "0" avec style premium
  6498.                 mesFavCount.innerHTML = '<span class="favoris-empty-text" style="color: #94a3b8; font-style: italic; font-size: 11px;">Pas de favoris</span>';
  6499.                 favorisBtn.classList.add('favoris-empty');
  6500.                 favorisBtn.disabled = true;
  6501.                 favorisBtn.title = "Pas de favoris pour l'instant";
  6502.                 if (favorisIcon) {
  6503.                     favorisIcon.style.color = '#cbd5e1';
  6504.                 }
  6505.             } else {
  6506.                 // Afficher le badge premium avec le nombre
  6507.                 mesFavCount.innerHTML = `<span class="premium-5sur5-badge" style="background: rgba(245, 96, 64, 0.1); color: #F56040; font-size: 10px; padding: 2px 6px;">${count}</span>`;
  6508.                 favorisBtn.classList.remove('favoris-empty');
  6509.                 favorisBtn.disabled = false;
  6510.                 favorisBtn.title = "Mes favoris";
  6511.                 if (favorisIcon) {
  6512.                     favorisIcon.style.color = '#f56040';
  6513.                 }
  6514.             }
  6515.         }
  6516.         function supprimerFavoris($id, $idSejour) {
  6517.             // 🚨 LOG CRITIQUE : Tracer TOUS les appels à supprimerFavoris
  6518.             console.error('🚨🚨🚨 supprimerFavoris() APPELÉ ! 🚨🚨🚨', {
  6519.                 id: $id,
  6520.                 idSejour: $idSejour,
  6521.                 stack: new Error().stack,
  6522.                 mesFavCountAvant: document.getElementById('mesFavCount')?.textContent
  6523.             });
  6524.             
  6525.             // Vider l'élément coeur pour ce favori
  6526.             const coeurElement = $('#coeur' + $id);
  6527.             coeurElement.empty();
  6528.             // Ajout d'une animation sur le bouton cadeau
  6529.             const giftButton = document.querySelector('.gift-button');
  6530.             if (giftButton) {
  6531.                 giftButton.classList.add('active');
  6532.                 // Retirer l'animation après qu'elle soit jouée
  6533.                 setTimeout(() => {
  6534.                     giftButton.classList.remove('active');
  6535.                 }, 600); // La durée doit correspondre à celle de l'animation
  6536.             }
  6537.             // Mise à jour de l'icône coeur
  6538.             const clas = $('.IconImag6').hasClass('active') ? "IconDelete IconDeletesix" : "IconDelete";
  6539.             coeurElement.html(
  6540.                 `<i class="bi bi-heart ${clas}" ></i>`
  6541.             );
  6542.             // METTRE À JOUR UNIQUEMENT #mesFavCount (source unique de vérité)
  6543.             // L'observer se chargera de synchroniser giftCount automatiquement
  6544.             const mesFavCount = document.getElementById('mesFavCount');
  6545.             if (mesFavCount) {
  6546.                 let currentCount = getFavorisCount();
  6547.                 currentCount = Math.max(0, currentCount - 1); // Empêche d'aller en dessous de 0
  6548.                 updateFavorisDisplay(currentCount);
  6549.                 console.log('[supprimerFavoris] ✅ mesFavCount décrémenté:', currentCount);
  6550.                 
  6551.                 // L'observer MutationObserver va automatiquement :
  6552.                 // - Synchroniser giftCount
  6553.                 // - Mettre à jour le sidebar
  6554.                 // - Mettre à jour product-suggestions
  6555.             }
  6556.             // Préparation des données pour l'Ajax
  6557.             const $_data = { 'id': $id, 'idSejour': $idSejour };
  6558.             // Appel Ajax pour supprimer le favori
  6559.             $.ajax({
  6560.                 type: "POST",
  6561.                 url: "{{ path('Supprimer_fav') }}",
  6562.                 data: $_data,
  6563.                 success: function () {
  6564.                     // Réactiver les icônes après succès
  6565.                     $('.IconDelete').each(function () {
  6566.                         $(this).css('pointer-events', '');
  6567.                     });
  6568.                     
  6569.                     // Mettre à jour l'alerte avec le nouveau nombre
  6570.                     const finalCount = getCurrentFavoriteCount();
  6571.                     if (typeof updatePurchaseAlert === 'function') {
  6572.                         updatePurchaseAlert(finalCount);
  6573.                     }
  6574.                 },
  6575.                 error: function (xhr, status, error) {
  6576.                     console.error('Erreur lors de la suppression du favori :', error);
  6577.                 }
  6578.             });
  6579.         }
  6580.         function AddFavoris($id, $idSejour, $urlimg, $description) {
  6581.             // 🟢 LOG : Tracer les ajouts de favoris
  6582.             console.log('🟢 AddFavoris() APPELÉ', {
  6583.                 id: $id,
  6584.                 idSejour: $idSejour,
  6585.                 mesFavCountAvant: document.getElementById('mesFavCount')?.textContent
  6586.             });
  6587.             
  6588.             // Update heart icon
  6589.             $('#coeur' + $id).empty();
  6590.             var clas = $('.IconImag6').hasClass('active') ? "IconDelete IconDeletesix" : "IconDelete";
  6591.             $('#coeur' + $id).html("<i class=\"bi bi-heart-fill favSelect " + clas + "\" )\"></i>");
  6592.             
  6593.             // METTRE À JOUR UNIQUEMENT #mesFavCount (source unique de vérité)
  6594.             // L'observer se chargera de synchroniser giftCount automatiquement
  6595.             const mesFavCount = document.getElementById('mesFavCount');
  6596.             if (mesFavCount) {
  6597.                 let currentCount = getFavorisCount();
  6598.                 currentCount++;
  6599.                 updateFavorisDisplay(currentCount);
  6600.                 console.log('[AddFavoris] ✅ mesFavCount incrémenté:', currentCount);
  6601.                 
  6602.                 // L'observer MutationObserver va automatiquement :
  6603.                 // - Synchroniser giftCount
  6604.                 // - Mettre à jour le sidebar
  6605.                 // - Mettre à jour product-suggestions
  6606.             }
  6607.             
  6608.             // Update other counters
  6609.             var $total = parseInt($("#totalLike").html()) + 1;
  6610.             $("#totalLike").html($total);
  6611.             $("#totalLikeTitle").html($total);
  6612.             $("#totalLikeMobile").html($total);
  6613.             
  6614.             // Add gift button animation
  6615.             const giftButton = document.querySelector('.gift-button');
  6616.             if (giftButton) {
  6617.                 giftButton.classList.add('active');
  6618.                 setTimeout(() => {
  6619.                     giftButton.classList.remove('active');
  6620.                 }, 600);
  6621.             }
  6622.             var $data = { 'id': $id, 'idSejour': $idSejour };
  6623.             $.ajax({
  6624.                 type: "POST",
  6625.                 url: "{{ path('Ajouter_fav') }}",
  6626.                 data: $data,
  6627.                 success: function () {
  6628.                     $('.IconDelete').each(function () {
  6629.                         $(this).css('pointer-events', '');
  6630.                     });
  6631.                     if ($description === undefined) {
  6632.                         $description = ''; // Set it to an empty string
  6633.                     }
  6634.                     $('.rowMaselection').append(
  6635.                         '<div class="column" id="column-' + $id + '">'+
  6636.                         '<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>'+
  6637.                         '<a class="photo-zoom">'+
  6638.                         '<img data-idAtach="'+$id+'" id="'+$idSejour+'" src="'+$urlimg+'"></a>'+
  6639.                         ($description ? '<h4 id="commint" class="titleHeadPhoto">'+$description+'</h4>' : '')+ // Only add the <h4> if $description is not empty
  6640.                         '</div>'
  6641.                     );
  6642.                     // Directly update nbLikes count in the header
  6643.                     var currentNbLikes = parseInt($('#favoris-link-Accueilpayment .nbrpanier').text());
  6644.                     var newNbLikes = currentNbLikes + 1;
  6645.                     $('#favoris-link-Accueilpayment .nbrpanier').text(newNbLikes);
  6646.                     
  6647.                     // Mettre à jour l'alerte avec le nouveau nombre
  6648.                     const finalCount = getCurrentFavoriteCount();
  6649.                     if (typeof updatePurchaseAlert === 'function') {
  6650.                         updatePurchaseAlert(finalCount);
  6651.                     }
  6652.                 },
  6653.                 error: function (xhr, status, error) {
  6654.                     console.error('Error:', error);
  6655.                 }
  6656.             });
  6657.         }
  6658.         $(document).on('click', '.bi-heart, .bi-heart-fill', function (e) {
  6659.         const heartIcon       = $(this);
  6660.         
  6661.         // 🛡️ PROTECTION : Ignorer les clics sur le bouton favoris de la toolbar
  6662.         // Ce bouton sert uniquement à FILTRER, pas à ajouter/supprimer des favoris
  6663.         if (heartIcon.closest('.mtb-btn').length > 0) {
  6664.             console.log('💗 Clic sur bouton toolbar favoris ignoré (gestion séparée)');
  6665.             return; // Ne rien faire, c'est géré par le listener de la toolbar
  6666.         }
  6667.         
  6668.         const heartContainer  = heartIcon.closest('.heart-icon');
  6669.         // Extract data attributes
  6670.         const attachmentId    = heartContainer.data('id');
  6671.         const sejourId        = heartContainer.data('sejour-id');
  6672.         const path            = heartContainer.data('path');
  6673.         const description     = heartContainer.data('description');
  6674.         const isFavorite      = heartIcon.hasClass('bi-heart-fill');
  6675.         if (isFavorite) {
  6676.             // Remove from favorites
  6677.             supprimerFavoris(attachmentId, sejourId);
  6678.         } else {
  6679.             // Add to favorites
  6680.             AddFavoris(attachmentId, sejourId, path, description);
  6681.         }
  6682.         // Update UI components after the action (sans double comptage)
  6683.         setTimeout(function() {
  6684.             const likeCountLabel = document.getElementById('likeCount');
  6685.             
  6686.             if (likeCountLabel) {
  6687.                 const currentCount = parseInt(likeCountLabel.textContent, 10) || 0;
  6688.                 
  6689.                 // Update UI components seulement
  6690.                 updateCardContent(currentCount);
  6691.                 updateFavoritesSidebar();
  6692.                 $("#close-favorites-btn").click();
  6693.                 
  6694.                 // 🎯 Product suggestions sont mis à jour automatiquement par le MutationObserver
  6695.                 // dans parent-toasts.js qui observe #mesFavCount
  6696.                 // updateProductSuggestionsLive(currentCount); // ← SUPPRIMÉ (doublon)
  6697.             }
  6698.         }, 50);
  6699.     });
  6700.         // Ajoutez les événements sur les icônes de cœur
  6701.         document.querySelectorAll('.IconDelete').forEach((icon) => {
  6702.             icon.addEventListener('click', (event) => {
  6703.                 const isFavorite = icon && icon.classList && icon.classList.contains('bi-heart-fill');
  6704.                 if (isFavorite) {
  6705.                     removeFavorite();
  6706.                     if (icon.classList) {
  6707.                       icon.classList.remove('bi-heart-fill');
  6708.                       icon.classList.add('bi-heart');
  6709.                     }
  6710.                 } else {
  6711.                     addFavorite();
  6712.                     if (icon.classList) {
  6713.                       icon.classList.remove('bi-heart');
  6714.                       icon.classList.add('bi-heart-fill');
  6715.                     }
  6716.                 }
  6717.             });
  6718.         });
  6719.         // Vérifie l'état initial
  6720.         checkFavoritesAlert();
  6721.         
  6722.         
  6723.         // ⚡ OPTIMISÉ: Réduction du délai d'initialisation
  6724.       
  6725.   </script>
  6726.   <!-- Initialisation -->
  6727.   <script>
  6728.     // ⚡ OPTIMISATION: Différer l'initialisation d'AOS pour ne pas bloquer le chargement
  6729.     setTimeout(function() {
  6730.       AOS.init({
  6731.         duration: 800,
  6732.         easing: "ease-in-out"
  6733.       });
  6734.     }, 100);
  6735.     // 🎯 DAY FILTER DROPDOWN LOGIC (Senior UX)
  6736.     document.addEventListener('DOMContentLoaded', function() {
  6737.       initializeDayFilters();
  6738.       
  6739.       // Initialiser le compteur audio avec la valeur correcte
  6740.       setTimeout(function() {
  6741.         updateAudioButtonState('days');
  6742.       }, 500);
  6743.     });
  6744.     function initializeDayFilters() {
  6745.       const dropdowns = document.querySelectorAll('.day-filter-dropdown');
  6746.       
  6747.       dropdowns.forEach(dropdown => {
  6748.         const toggle = dropdown.querySelector('.filter-toggle');
  6749.         const menu = dropdown.querySelector('.filter-dropdown-menu');
  6750.         const options = dropdown.querySelectorAll('.filter-option');
  6751.         const dayIndex = dropdown.dataset.dayIndex;
  6752.         const dayContainer = document.getElementById(`demP${dayIndex}`);
  6753.         
  6754.         if (!toggle || !menu || !dayContainer) return;
  6755.         
  6756.         // Calculer et mettre à jour les compteurs initiaux
  6757.         updateDayFilterCounts(dropdown, dayContainer);
  6758.         
  6759.         // Toggle dropdown
  6760.         toggle.addEventListener('click', (e) => {
  6761.           e.stopPropagation();
  6762.           const isOpen = dropdown.classList.contains('open');
  6763.           
  6764.           // Fermer tous les autres dropdowns
  6765.           document.querySelectorAll('.day-filter-dropdown.open').forEach(d => {
  6766.             if (d !== dropdown) d.classList.remove('open');
  6767.           });
  6768.           
  6769.           // Toggle le dropdown actuel
  6770.           dropdown.classList.toggle('open', !isOpen);
  6771.         });
  6772.         
  6773.         // Gérer les clics sur les options
  6774.         options.forEach(option => {
  6775.           option.addEventListener('click', (e) => {
  6776.             e.stopPropagation();
  6777.             const filter = option.dataset.filter;
  6778.             
  6779.             // Mettre à jour l'état actif
  6780.             options.forEach(opt => opt.classList.remove('active'));
  6781.             option.classList.add('active');
  6782.             
  6783.             // Appliquer le filtre
  6784.             applyDayFilter(dayContainer, filter);
  6785.             
  6786.             // Fermer le dropdown
  6787.             dropdown.classList.remove('open');
  6788.           });
  6789.         });
  6790.       });
  6791.       
  6792.       // Fermer les dropdowns en cliquant ailleurs
  6793.       document.addEventListener('click', () => {
  6794.         document.querySelectorAll('.day-filter-dropdown.open').forEach(dropdown => {
  6795.           dropdown.classList.remove('open');
  6796.         });
  6797.       });
  6798.     }
  6799.     function updateDayFilterCounts(dropdown, dayContainer) {
  6800.       const photoItems = dayContainer.querySelectorAll('[data-type="photo"]');
  6801.       const videoItems = dayContainer.querySelectorAll('[data-type="video"]');
  6802.       // Compter les messages audio individuels plutôt que les conteneurs
  6803.       const audioMessageItems = dayContainer.querySelectorAll('.audio-message-item[data-type="audio"]');
  6804.       const audioContainers = dayContainer.querySelectorAll('.audio-messages-container[data-type="audio"]');
  6805.       const audioRestricted = dayContainer.querySelectorAll('.audio-messages-restricted[data-type="audio"]');
  6806.       
  6807.       const allItems = dayContainer.querySelectorAll('[data-type]');
  6808.       
  6809.       const countAll = dropdown.querySelector('[data-count-all]');
  6810.       const countPhoto = dropdown.querySelector('[data-count-photo]');
  6811.       const countVideo = dropdown.querySelector('[data-count-video]');
  6812.       const countAudio = dropdown.querySelector('[data-count-audio]');
  6813.       
  6814.       // Compter les messages audio individuels + conteneurs + sections restreintes
  6815.       const totalAudio = audioMessageItems.length + audioContainers.length + audioRestricted.length;
  6816.       
  6817.       if (countAll) countAll.textContent = allItems.length;
  6818.       if (countPhoto) countPhoto.textContent = photoItems.length;
  6819.       if (countVideo) countVideo.textContent = videoItems.length;
  6820.       if (countAudio) countAudio.textContent = totalAudio;
  6821.       
  6822.       // Masquer les options sans contenu
  6823.       const photoOption = dropdown.querySelector('[data-filter="photo"]');
  6824.       const videoOption = dropdown.querySelector('[data-filter="video"]');
  6825.       const audioOption = dropdown.querySelector('[data-filter="audio"]');
  6826.       
  6827.       if (photoOption) photoOption.style.display = photoItems.length > 0 ? 'flex' : 'none';
  6828.       if (videoOption) videoOption.style.display = videoItems.length > 0 ? 'flex' : 'none';
  6829.       if (audioOption) audioOption.style.display = totalAudio > 0 ? 'flex' : 'none';
  6830.     }
  6831.     function applyDayFilter(dayContainer, filter) {
  6832.       const items = dayContainer.querySelectorAll('[data-type]');
  6833.       
  6834.       items.forEach(item => {
  6835.         if (filter === 'all' || item.dataset.type === filter) {
  6836.           item.style.display = '';
  6837.           item.classList.remove('filtered-out');
  6838.         } else {
  6839.           item.style.display = 'none';
  6840.           item.classList.add('filtered-out');
  6841.         }
  6842.       });
  6843.       
  6844.       // Animation fluide pour les éléments visibles
  6845.       requestAnimationFrame(() => {
  6846.         const visibleItems = dayContainer.querySelectorAll('[data-type]:not(.filtered-out)');
  6847.         visibleItems.forEach((item, index) => {
  6848.           item.style.animation = `fadeInUp 0.3s ease forwards ${index * 0.05}s`;
  6849.         });
  6850.       });
  6851.     }
  6852.     // Animation CSS pour fadeInUp
  6853.     if (!document.querySelector('#dayFilterAnimations')) {
  6854.       const style = document.createElement('style');
  6855.       style.id = 'dayFilterAnimations';
  6856.       style.textContent = `
  6857.         @keyframes fadeInUp {
  6858.           from {
  6859.             opacity: 0;
  6860.             transform: translateY(20px);
  6861.           }
  6862.           to {
  6863.             opacity: 1;
  6864.             transform: translateY(0);
  6865.           }
  6866.         }
  6867.       `;
  6868.       document.head.appendChild(style);
  6869.     }
  6870.     document.addEventListener("DOMContentLoaded", function () {
  6871.       const dateCards = document.querySelectorAll(".date-card");
  6872.       const sections = document.querySelectorAll(".collapse");
  6873.       dateCards.forEach((card) => {
  6874.         card.addEventListener("click", function () {
  6875.           // Supprimer les classes actives des autres cartes et sections
  6876.           dateCards.forEach((c) => c.classList.remove("active"));
  6877.           sections.forEach((s) => s.classList.remove("show"));
  6878.           // Ajouter la classe active à la carte cliquée
  6879.           this.classList.add("active");
  6880.           // Récupérer la cible et afficher la bonne section
  6881.           const targetId = this.getAttribute("data-bs-target");
  6882.           const targetSection = document.querySelector(targetId);
  6883.           if (targetSection) {
  6884.             targetSection.classList.add("show");
  6885.           }
  6886.         });
  6887.       });
  6888.     });
  6889.     document.addEventListener("DOMContentLoaded", function () {
  6890.       // Initialisation du carrousel Splide
  6891.       var splide = new Splide("#imageSlider", {
  6892.         type: "loop",
  6893.         perPage: 1,
  6894.         autoplay: true,
  6895.         interval: 6000,
  6896.         pauseOnHover: false,
  6897.         pauseOnFocus: false,
  6898.         pagination: false,
  6899.         arrows: false,
  6900.       });
  6901.       splide.mount();
  6902.       // ⚠️ Scroll automatique désactivé pour meilleure UX
  6903.       // Les utilisateurs peuvent défiler manuellement quand ils le souhaitent
  6904.     });
  6905.   </script>
  6906.   <script>
  6907.    
  6908.     const giftButton = document.querySelector('.gift-button');
  6909.     if (giftButton) {
  6910.         giftButton.addEventListener('click', () => {
  6911.         // Ajouter la classe 'active' pour déclencher l'éclat
  6912.             giftButton.classList.add('active');
  6913.         // Retirer l'animation après qu'elle soit jouée
  6914.         setTimeout(() => {
  6915.                 giftButton.classList.remove('active');
  6916.         }, 600); // La durée doit correspondre à celle de l'animation
  6917.             
  6918.             // Optionnel : rediriger vers la page de commande
  6919.             // window.location.href = '/commande';
  6920.     });
  6921.     }
  6922.     
  6923.     //const HeartAddButton = document.querySelector('.IconDelete');
  6924.     $(".IconDelete").on('click', () => {
  6925.         // Ajouter la classe 'active' pour déclencher l'éclat
  6926.         favoriteButton.classList.add('active');
  6927.         // Retirer l'animation après qu'elle soit jouée
  6928.         setTimeout(() => {
  6929.             favoriteButton.classList.remove('active');
  6930.         }, 600); // La durée doit correspondre à celle de l'animation
  6931.     });
  6932.     $(document).ready(function() {
  6933.         // Attach click event to collapse triggers
  6934.         const lastCard = $('.date-card.modern-card.active');
  6935.         const lastTargetId = lastCard.attr('data-bs-target');
  6936.         if (lastTargetId) {
  6937.             $(lastTargetId).collapse('show'); // Expand the last collapse section
  6938.             LoadImagesCloud($(lastTargetId)); // Load images for the last day
  6939.         }
  6940.         $('[data-bs-toggle="collapse"]').on('click', function() {
  6941.             var targetId = $(this).attr('data-bs-target'); // Get the target ID
  6942.             $('.date-card.modern-card').removeClass('active'); // Remove 'active' class from all cards
  6943.             $(this).addClass('active'); // Add 'active' class to the clicked card
  6944.             LoadImagesCloud($(targetId)); // Ensure this function works as expected
  6945.                // Hide all other collapses except the one clicked
  6946.                $('[data-bs-target]').each(function() {
  6947.                 var currentTargetId = $(this).attr('data-bs-target');
  6948.                 // If the current collapse is not the one clicked, hide it
  6949.                 if (currentTargetId !== targetId) {
  6950.                     $(currentTargetId).collapse('hide');
  6951.                     //$('[data-bs-toggle="collapse"]').removeClass('active'); // Remove active class from all cards
  6952.                     //Modifier leurs style en non active aussi
  6953.                 }
  6954.             });
  6955.         });
  6956.     });
  6957.   
  6958.             $(document).ready(function () {
  6959.               
  6960.                 {% if app.session.get("paymentmoniteco") %}
  6961.                 {% if app.session.get("paymentmoniteco") == "succses" %}
  6962.                 Swal.fire({
  6963.                     icon: 'success',
  6964.                     title: ' succès ',
  6965.                     text: 'votre commande est validée'
  6966.                 });
  6967.                 {% endif %}
  6968.                 {% endif %}
  6969.                 if ($total1 > 0) {
  6970.                     $('.iconeFleche').first().click();
  6971.                     //  $([document.documentElement, document.body]).animate({
  6972.                     //  scrollTop: $('.iconeFleche').last().offset().top
  6973.                     //  }, );
  6974.                 }
  6975.                 else {
  6976.                     $(window).scrollTop(0);
  6977.                 }
  6978.                 var slider = $('.responsive').slick({
  6979.                     infinite: true,
  6980.                     slidesToShow: 1,
  6981.                     slidesToScroll: 1,
  6982.                     autoplay: true,
  6983.                     autoplaySpeed: 4000,
  6984.                     pauseOnFocus: false,
  6985.                     pauseOnHover: false,
  6986.                     draggable: false,
  6987.                     fade: true
  6988.                 });
  6989.                 $('.responsive').css('display', 'block');
  6990.                 $('.namePRD').css('display', 'block');
  6991.                 var currSlide = 0;
  6992.                 var nextSlide = 0;
  6993.                 slider.on('afterChange', function (event, slick, currentSlide) {
  6994.                     console.log(typeof ($('.slick-active .slick-current').find('.imgproduit2')) != "undefined");
  6995.                     if (typeof ($('.slick-active .slick-current').find('.imgproduit2')) != "undefined") {
  6996.                         setTimeout(function () {
  6997.                             $('.slick-active .imgproduit1').removeClass('animated fadeIn');
  6998.                             $('.slick-active .imgproduit1').addClass('animated fadeOut');
  6999.                             $('.slick-active .imgproduit1').css('display', 'none');
  7000.                             $('.slick-active .imgproduit2').css('display', 'block');
  7001.                             $('.slick-active .imgproduit2').removeClass('animated fadeOut');
  7002.                             $('.slick-active .imgproduit2').addClass('animated fadeIn');
  7003.                         }, 2000);
  7004.                     }
  7005.                 });
  7006.                 slider.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
  7007.                     currSlide = currentSlide;
  7008.                     $('.imgproduit2').each(function () {
  7009.                         $(this).removeClass('animated fadeIn');
  7010.                         $(this).addClass('animated fadeOut');
  7011.                         $(this).css('display', 'none');
  7012.                     });
  7013.                     $('.imgproduit1').each(function () {
  7014.                         $(this).css('display', 'block');
  7015.                         $(this).removeClass('animated fadeOut');
  7016.                         $(this).addClass('animated fadeIn');
  7017.                     });
  7018.                 });
  7019.                 $('.columnPub').each(function () {
  7020.                     $(this).slick({
  7021.                         infinite: true,
  7022.                         speed: 50,
  7023.                         fade: true,
  7024.                         slidesToShow: 1,
  7025.                         slidesToScroll: 1,
  7026.                         autoplay: true,
  7027.                         pauseOnFocus: false,
  7028.                         pauseOnHover: false,
  7029.                         draggable: false
  7030.                     });
  7031.                     $(this).css('display', 'block');
  7032.                 });
  7033.                 $("#offrePack").click();
  7034.                 {%if app.user.showpubprod != 'false' %}
  7035.                 $('#btnPubProd').click();
  7036.                 $('.modal-backdrop').css('background-color', 'rgba(0, 0, 0, 0.2)');
  7037.                 {% endif %}
  7038.             });
  7039.             $("#closeImage").click(function () {
  7040.                 $('#myModalImage').css('display', "none");
  7041.             });
  7042.             $.ajax({
  7043.                 type: "POST",
  7044.                 url: "{{ path("delateSession_parent") }}",
  7045.                 success: function () { }
  7046.             });
  7047.             function afficheDiv(elem) {
  7048.                 $('.nav-link').each(function () {
  7049.                     $(this).removeClass('active');
  7050.                 });
  7051.                 elem.addClass('active');
  7052.                 if (elem.attr('id') === "esphoto" || elem.attr('id') === "esphotoMobile") {
  7053.                     $("#espacphoto").show();
  7054.                     $("#espacemessage").hide();
  7055.                     $("#espaceMa_selection").hide();
  7056.                     pageMenu = 'MonSejour'
  7057.                     $(this).addClass('active');
  7058.                    $('#imageActifphoto').css('display', 'block');
  7059.                      $('#imagenoActifphoto').css('display', 'none');
  7060.                    $('#VocalActivee').css('display', 'none');
  7061.                      $('#noActifVocal').css('display', 'block');
  7062.                 }
  7063.                 if (elem.attr('id') === "esmessage" || elem.attr('id') === "esmessageMobile") {
  7064.                     $("#espacphoto").hide();
  7065.                     $("#espaceMa_selection").hide();
  7066.                     $("#espacemessage").show();
  7067.                     pageMenu = 'BoiteVocale'
  7068.                     $("#espaceMa_selection").hide();
  7069.                     $(this).addClass('active');
  7070.                   $('#imageActifphoto').css('display', 'none');
  7071.                      $('#imagenoActifphoto').css('display', 'block');
  7072.                    $('#VocalActivee').css('display', 'block');
  7073.                      $('#noActifVocal').css('display', 'none');
  7074.                 }
  7075.                 if (elem.attr('id') === "esselection" || elem.attr('id') === "esselectionMobile") {
  7076.                     $("#espacphoto").hide();
  7077.                     $("#espacemessage").hide();
  7078.                     $("#espaceMa_selection").show();
  7079.                     $(homeNavmob).removeClass('bi bi-house-door-fill');
  7080.                     $(homeNavmob).addClass('bi bi-house-door');
  7081.                     $(micromob).removeClass('bi bi-mic-fill');
  7082.                     $(micromob).addClass('bi bi-mic');
  7083.                     $(selecNavmob).removeClass('bi bi-heart');
  7084.                     $(selecNavmob).addClass('bi bi-heart-fill');
  7085.                 }
  7086.             }
  7087.             function LoadImagesCloud($element) {
  7088.                 $element.find('.photo-zoom img').each(function ($this) {
  7089.                     if ($(this).attr('data-src') != $(this).attr('src')) {
  7090.                         $(this).attr('src', $(this).attr('data-src'));
  7091.                     }
  7092.                 });
  7093.             }
  7094.   </script>
  7095.   <script>
  7096.     // ⚡ OPTIMISATION: Regroupement des initialisations jQuery
  7097.     $(document).ready(function () {
  7098.       // Modal PubProd
  7099.       $("#PubProd").on("hidden.bs.modal", function () {
  7100.         $(document).trigger("modalClosed");
  7101.       });
  7102.       
  7103.       // NoShow checkbox
  7104.       $("#noShow").on("change", function () {
  7105.         if ($(this).is(":checked")) {
  7106.           $.ajax({
  7107.             url: "/Parent/showpub",
  7108.             type: "POST",
  7109.             dataType: "json",
  7110.             success: function (response) {
  7111.               if (response.status === "success") {
  7112.                 console.log("User showpubprod updated successfully.");
  7113.               } else {
  7114.                 console.log("Error:", response.message);
  7115.               }
  7116.             },
  7117.             error: function (xhr, status, error) {
  7118.               console.log("AJAX Error:", error);
  7119.             },
  7120.           });
  7121.         }
  7122.       });
  7123.     });
  7124.   </script>
  7125. </div>
  7126. <!-- Script pour la sidebar des favoris -->
  7127. <script>
  7128.   // ⚡ OPTIMISATION: Utiliser requestIdleCallback pour ne pas bloquer le thread principal
  7129.  
  7130.   
  7131.     
  7132.     // Charger les favoris
  7133.     function loadFavorites() {
  7134.       $.ajax({
  7135.         url: "/Parent/mes-favoris",
  7136.         type: "GET",
  7137.         dataType: "json",
  7138.         beforeSend: function() {
  7139.           $("#favorites-grid").html("<div style='text-align:center'>Chargement...</div>");
  7140.         },
  7141.         success: function(data) {
  7142.           $("#favorites-grid").empty();
  7143.           
  7144.           if (data.data && data.data.length > 0) {
  7145.             $("#favorites-empty-state").hide();
  7146.             
  7147.             $.each(data.data, function(i, fav) {
  7148.               var item = $("<div class='favorite-item'></div>");
  7149.               var img = $("<img>").attr("src", fav.path).attr("alt", fav.descreption || "Photo favorite");
  7150.               var overlay = $("<div class='favorite-overlay'></div>");
  7151.         
  7152.               
  7153.               btn.click(function(e) {
  7154.                 e.preventDefault();
  7155.                 e.stopPropagation();
  7156.                 removeFavorite(fav.id);
  7157.               });
  7158.               
  7159.               overlay.append(btn);
  7160.               item.append(img).append(overlay);
  7161.               $("#favorites-grid").append(item);
  7162.             });
  7163.             
  7164.             $("#favorites-counter").text(data.data.length);
  7165.             var percentage = (data.data.length / 10) * 100;
  7166.             $("#favorites-progress").css("width", percentage + "%");
  7167.             
  7168.           } else {
  7169.             $("#favorites-empty-state").show();
  7170.             $("#favorites-counter").text("0");
  7171.             $("#favorites-progress").css("width", "0%");
  7172.           }
  7173.         },
  7174.         error: function() {
  7175.           $("#favorites-grid").html("<div style='color:red;text-align:center'>Erreur de chargement</div>");
  7176.         }
  7177.       });
  7178.     }
  7179.     
  7180.     // Supprimer un favori
  7181.     function removeFavorite(id) {
  7182.       $.ajax({
  7183.         url: "/Parent/remove-favorite/" + id,
  7184.         type: "POST",
  7185.         success: function() {
  7186.           loadFavorites();
  7187.           
  7188.           // Mettre à jour tous les compteurs de favoris
  7189.           updateAllFavoriteCounters();
  7190.         },
  7191.         error: function() {
  7192.           alert("Erreur lors de la suppression du favori");
  7193.         }
  7194.       });
  7195.     }
  7196.   });
  7197. </script>
  7198. <!-- === E-COMMERCE SIDEBAR JAVASCRIPT === -->
  7199. <script>
  7200. // Configuration UX_VARIANT pour A/B testing
  7201. let UX_VARIANT = 'EMOTION'; // ou 'URGENCY'
  7202. // Variables globales e-commerce
  7203. let sejourEndDate = null; // À définir avec la vraie date de fin du séjour
  7204. // Fonction utilitaire robuste pour gérer les compteurs de favoris
  7205. window.getFavoriteCount = function getFavoriteCount() {
  7206.   const likeCountInput = document.getElementById('likeCount');
  7207.   if (likeCountInput) {
  7208.     // Priorité à la valeur de l'input
  7209.     const value = likeCountInput.value || likeCountInput.textContent || 0;
  7210.     return parseInt(value, 10) || 0;
  7211.   }
  7212.   
  7213.   // Fallback sur giftCount
  7214.   const giftCount = document.getElementById('giftCount');
  7215.   if (giftCount && giftCount.textContent) {
  7216.     return parseInt(giftCount.textContent.trim(), 10) || 0;
  7217.   }
  7218.   
  7219.   return 0;
  7220. };
  7221. window.setFavoriteCount = function setFavoriteCount(count) {
  7222.   const likeCountInput = document.getElementById('likeCount');
  7223.   if (likeCountInput) {
  7224.     // Mettre à jour les deux propriétés pour être sûr
  7225.     likeCountInput.value = count;
  7226.     likeCountInput.textContent = count;
  7227.   }
  7228.   
  7229.   // Mettre à jour les autres compteurs
  7230.   const giftCount = document.getElementById('giftCount');
  7231.   if (giftCount) {
  7232.     giftCount.textContent = count;
  7233.   }
  7234.   
  7235.   const mesFavCount = document.getElementById('mesFavCount');
  7236.   if (mesFavCount) {
  7237.     mesFavCount.textContent = count;
  7238.   }
  7239.   
  7240.   // Mettre à jour l'input hidden
  7241.   const nbFavCurrentInput = document.getElementById('nbFavCurrent');
  7242.   if (nbFavCurrentInput) {
  7243.     nbFavCurrentInput.value = count;
  7244.   }
  7245. };
  7246. // Fonction pour définir la date de fin du séjour
  7247. window.setSejourEndDate = function setSejourEndDate(dateString) {
  7248.   sejourEndDate = dateString;
  7249.   console.log('🎯 Sejour end date set to:', sejourEndDate);
  7250.   
  7251.   // Mettre à jour le countdown si le sidebar est ouvert
  7252.   updateCountdownTimer();
  7253. };
  7254. // Fonctions d'ouverture/fermeture du sidebar
  7255. window.openEcommerceSidebar = function openEcommerceSidebar() {
  7256.   const sidebar = document.getElementById('ecommerce-sidebar');
  7257.   if (!sidebar) {
  7258.     console.error('❌ E-commerce sidebar element not found!');
  7259.     return false;
  7260.   }
  7261.   
  7262.   console.log('🎁 [OPEN SIDEBAR] Ouverture du sidebar e-commerce...');
  7263.   const favCount = window.getFavoriteCount ? window.getFavoriteCount() : 0;
  7264.   console.log('🎁 [OPEN SIDEBAR] Favoris actuels:', favCount);
  7265.   
  7266.   // D'abord ouvrir le sidebar pour que les éléments soient dans le DOM
  7267.   sidebar.classList.add('active');
  7268.   console.log('🎁 [OPEN SIDEBAR] Sidebar classe "active" ajoutée');
  7269.   
  7270.   // Puis mettre à jour le contenu (petit délai pour que le DOM soit prêt)
  7271.   setTimeout(() => {
  7272.     console.log('🎁 [OPEN SIDEBAR] Mise à jour du contenu...');
  7273.     
  7274.     // Appeler la fonction de parent-toasts.js
  7275.     if (typeof window.parentConversion !== 'undefined' && typeof window.parentConversion.updateSidebar === 'function') {
  7276.       console.log('✅ [OPEN SIDEBAR] Appel de parentConversion.updateSidebar()');
  7277.       window.parentConversion.updateSidebar();
  7278.     } else {
  7279.       console.warn('⚠️ [OPEN SIDEBAR] parent-toasts.js non disponible, fallback sur fonction locale');
  7280.       // Fallback sur la fonction locale
  7281.       if (typeof window.updateEcommerceSidebarContent === 'function') {
  7282.         window.updateEcommerceSidebarContent(favCount);
  7283.       }
  7284.     }
  7285.   }, 100); // 100ms de délai pour que le sidebar soit visible
  7286.   
  7287.   return true;
  7288. };
  7289. window.closeEcommerceSidebar = function closeEcommerceSidebar() {
  7290.   const sidebar = document.getElementById('ecommerce-sidebar');
  7291.   if (sidebar) {
  7292.     sidebar.classList.remove('active');
  7293.     console.log('🎯 E-commerce sidebar closed');
  7294.   } else {
  7295.     console.error('❌ E-commerce sidebar element not found!');
  7296.   }
  7297. };
  7298. // 🚫 FONCTION LEGACY SUPPRIMÉE - Utiliser parent-toasts.js à la place
  7299. // Cette fonction était en conflit avec updateEcommerceSidebar() de parent-toasts.js
  7300. // qui gère TOUT : titre, sous-titre, compteurs, visibilité des produits, CTA, etc.
  7301. window.updateEcommerceSidebarContent = function updateEcommerceSidebarContent(favoriteCount) {
  7302.   console.warn('[LEGACY] updateEcommerceSidebarContent appelé - Redirection vers parent-toasts.js');
  7303.   
  7304.   // Rediriger vers la vraie fonction
  7305.   if (typeof window.parentConversion !== 'undefined' && typeof window.parentConversion.updateSidebar === 'function') {
  7306.     window.parentConversion.updateSidebar();
  7307.   } else {
  7308.     console.error('[LEGACY] parent-toasts.js non disponible !');
  7309.   }
  7310. }
  7311. // 🚫 FONCTION SUPPRIMÉE - Géré par parent-toasts.js
  7312. // Tout est maintenant géré dans updateEcommerceSidebar() de parent-toasts.js
  7313. // Fonction pour gérer le countdown
  7314. window.updateCountdownTimer = function updateCountdownTimer() {
  7315.   const countdownTimer = document.getElementById('countdown-timer');
  7316.   const daysLeftSpan = document.getElementById('days-left');
  7317.   
  7318.   if (!countdownTimer || !daysLeftSpan) return;
  7319.   
  7320.   // Si pas de date définie, utiliser une date par défaut (6 semaines à partir d'aujourd'hui)
  7321.   let expirationDate;
  7322.   if (sejourEndDate) {
  7323.     const endDate = new Date(sejourEndDate);
  7324.     expirationDate = new Date(endDate.getTime() + (6 * 7 * 24 * 60 * 60 * 1000)); // +6 semaines
  7325.   } else {
  7326.     // Date par défaut : 6 semaines à partir d'aujourd'hui
  7327.     expirationDate = new Date();
  7328.     expirationDate.setDate(expirationDate.getDate() + (6 * 7));
  7329.   }
  7330.   
  7331.   const now = new Date();
  7332.   const daysLeft = Math.ceil((expirationDate - now) / (24 * 60 * 60 * 1000));
  7333.   
  7334.   if (daysLeft <= 10 && daysLeft > 0) {
  7335.     countdownTimer.style.display = 'block';
  7336.     daysLeftSpan.textContent = daysLeft;
  7337.     
  7338.     // Mettre à jour les titres avec le countdown si variante URGENCY
  7339.     if (UX_VARIANT === 'URGENCY') {
  7340.       const title = document.getElementById('ecommerce-title');
  7341.       if (title) {
  7342.         const favoriteCount = parseInt(document.getElementById('giftCount')?.textContent || '0');
  7343.         if (favoriteCount > 0) {
  7344.           if (favoriteCount < 5) {
  7345.             title.innerHTML = `⏳ ${favoriteCount} souvenirs - Plus que ${daysLeft} jours !`;
  7346.           } else if (favoriteCount < 12) {
  7347.             title.innerHTML = `⏳ ${favoriteCount} magnifiques souvenirs - Plus que ${daysLeft} jours !`;
  7348.           } else {
  7349.             title.innerHTML = `⏳ Superbe collection de ${favoriteCount} photos - Plus que ${daysLeft} jours !`;
  7350.           }
  7351.         }
  7352.       }
  7353.     }
  7354.   } else {
  7355.     countdownTimer.style.display = 'none';
  7356.   }
  7357. }
  7358. // Fonction pour commander un produit
  7359. window.orderProduct = function orderProduct(productType) {
  7360.   let favoriteCount = 0;
  7361.   
  7362.   // Méthode 1: Fonction getCurrentFavoriteCount
  7363.   try {
  7364.     if (typeof getCurrentFavoriteCount === 'function') {
  7365.       favoriteCount = getCurrentFavoriteCount();
  7366.     }
  7367.   } catch (e) {
  7368.     console.log('⚠️ getCurrentFavoriteCount non disponible:', e);
  7369.   }
  7370.   
  7371.   // Méthode 2: Element giftCount
  7372.   if (favoriteCount === 0) {
  7373.     const giftCount = document.getElementById('giftCount');
  7374.     if (giftCount && giftCount.textContent) {
  7375.       favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  7376.     }
  7377.   }
  7378.   
  7379.   // Méthode 3: Récupérer depuis les éléments qui affichent le nombre de favoris
  7380.   if (favoriteCount === 0) {
  7381.     const albumFavCount = document.getElementById('album-fav-count');
  7382.     const digitalFavCount = document.getElementById('digital-fav-count');
  7383.     
  7384.     if (albumFavCount && albumFavCount.textContent) {
  7385.       favoriteCount = parseInt(albumFavCount.textContent.trim()) || 0;
  7386.     } else if (digitalFavCount && digitalFavCount.textContent) {
  7387.       favoriteCount = parseInt(digitalFavCount.textContent.trim()) || 0;
  7388.     }
  7389.   }
  7390.   
  7391.   // Méthode 4: Variable globale likes
  7392.   if (favoriteCount === 0 && typeof likes !== 'undefined' && likes) {
  7393.     favoriteCount = likes.length || 0;
  7394.   }
  7395.   
  7396.   // Méthode 5: Compter les éléments liked dans le DOM
  7397.   if (favoriteCount === 0) {
  7398.     const likedElements = document.querySelectorAll('.liked, .photo.liked, [data-liked="true"]');
  7399.     favoriteCount = likedElements.length;
  7400.   }
  7401.   
  7402.   // Debug pour voir la valeur récupérée
  7403.   console.log('🔍 Nombre de favoris détecté:', favoriteCount);
  7404.   
  7405.   if (favoriteCount === 0) {
  7406.     // Afficher une notification plus élégante
  7407.     const notification = document.createElement('div');
  7408.     notification.className = 'favorite-notification';
  7409.     notification.innerHTML = `
  7410.       <div class="notification-content">
  7411.         <i class="bi bi-heart" style="color: #e91e63; font-size: 1.5rem; margin-right: 10px;"></i>
  7412.         <span>Veuillez d'abord sélectionner des photos favorites !</span>
  7413.         <button onclick="this.parentElement.parentElement.remove()" style="background: none; border: none; color: #666; font-size: 1.2rem; margin-left: 10px;">&times;</button>
  7414.       </div>
  7415.     `;
  7416.     
  7417.     // Styles pour la notification
  7418.     notification.style.cssText = `
  7419.       position: fixed;
  7420.       top: 20px;
  7421.       right: 20px;
  7422.       background: #fff;
  7423.       border: 2px solid #e91e63;
  7424.       border-radius: 8px;
  7425.       padding: 15px;
  7426.       box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  7427.       z-index: 10000;
  7428.       animation: slideInRight 0.3s ease;
  7429.     `;
  7430.     
  7431.     // Ajouter l'animation CSS si elle n'existe pas
  7432.     if (!document.getElementById('notification-styles')) {
  7433.       const style = document.createElement('style');
  7434.       style.id = 'notification-styles';
  7435.       style.textContent = `
  7436.         @keyframes slideInRight {
  7437.           from { transform: translateX(100%); opacity: 0; }
  7438.           to { transform: translateX(0); opacity: 1; }
  7439.         }
  7440.         .notification-content {
  7441.           display: flex;
  7442.           align-items: center;
  7443.         }
  7444.       `;
  7445.       document.head.appendChild(style);
  7446.     }
  7447.     
  7448.     document.body.appendChild(notification);
  7449.     
  7450.     // Supprimer automatiquement après 5 secondes
  7451.     setTimeout(() => {
  7452.       if (notification.parentElement) {
  7453.         notification.remove();
  7454.       }
  7455.     }, 5000);
  7456.     
  7457.     return;
  7458.   }
  7459.   
  7460.   // Construire l'URL avec les favoris pré-sélectionnés
  7461.   let orderUrl = '';
  7462.   
  7463.   switch (productType) {
  7464.     case 'album':
  7465.       orderUrl = `{{ path('EditionAlbum') }}?favorites=${favoriteCount}`;
  7466.       break;
  7467.     case 'digital':
  7468.       orderUrl = `{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}?favorites=${favoriteCount}`;
  7469.       break;
  7470.     case 'prints':
  7471.       orderUrl = `{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}?favorites=${favoriteCount}`;
  7472.       break;
  7473.     default:
  7474.       console.error('Type de produit non reconnu:', productType);
  7475.       alert('Erreur: Type de produit non reconnu');
  7476.       return;
  7477.   }
  7478.   
  7479.   // Analytics/tracking
  7480.   console.log('Product ordered', { productType, favoriteCount, url: orderUrl });
  7481.   
  7482.   // Redirection vers la commande
  7483.   if (orderUrl) {
  7484.     // Vérifier que l'URL est valide
  7485.     try {
  7486.       new URL(orderUrl, window.location.origin);
  7487.     window.location.href = orderUrl;
  7488.     } catch (error) {
  7489.       console.error('URL invalide générée:', orderUrl, error);
  7490.       alert('Erreur: Impossible de générer le lien de commande');
  7491.     }
  7492.   } else {
  7493.     console.error('Aucune URL générée pour le produit:', productType);
  7494.     alert('Erreur: Impossible de générer le lien de commande');
  7495.   }
  7496. }
  7497. // Fonction pour tester tous les liens de produits
  7498. window.testProductLinks = function() {
  7499.   const products = ['album', 'digital', 'prints'];
  7500.   const favoriteCount = window.getFavoriteCount() || 0;
  7501.   
  7502.   console.log('🧪 Test des liens de produits avec', favoriteCount, 'favoris');
  7503.   
  7504.   products.forEach(productType => {
  7505.     try {
  7506.       let testUrl = '';
  7507.       switch (productType) {
  7508.         case 'album':
  7509.           testUrl = `{{ path('EditionAlbum') }}?favorites=${favoriteCount}`;
  7510.           break;
  7511.         case 'digital':
  7512.           testUrl = `{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}?favorites=${favoriteCount}`;
  7513.           break;
  7514.         case 'prints':
  7515.           testUrl = `{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}?favorites=${favoriteCount}`;
  7516.           break;
  7517.       }
  7518.       
  7519.       // Vérifier que l'URL est valide
  7520.       const url = new URL(testUrl, window.location.origin);
  7521.       console.log(`✅ ${productType}: ${url.href}`);
  7522.     } catch (error) {
  7523.       console.error(`❌ ${productType}: Erreur URL`, error);
  7524.     }
  7525.   });
  7526. };
  7527. // Fonction pour valider les liens au chargement de la page
  7528. window.validateProductLinks = function() {
  7529.   // Vérifier que tous les boutons de produits existent
  7530.   const albumBtn = document.querySelector('button[onclick*="orderProduct(\'album\')"]');
  7531.   const digitalBtn = document.querySelector('button[onclick*="orderProduct(\'digital\')"]');
  7532.   const printsBtn = document.querySelector('button[onclick*="orderProduct(\'prints\')"]');
  7533.   
  7534.   if (!albumBtn) console.warn('⚠️ Bouton album non trouvé');
  7535.   if (!digitalBtn) console.warn('⚠️ Bouton digital non trouvé');
  7536.   if (!printsBtn) console.warn('⚠️ Bouton prints non trouvé');
  7537.   
  7538.   // Tester les liens
  7539.   window.testProductLinks();
  7540. };
  7541. // Fonction centrale de mise à jour de TOUS les compteurs de favoris
  7542. window.updateAllFavoriteCounters = function() {
  7543.   // Récupérer le compte depuis #mesFavCount (source unique de vérité)
  7544.   const mesFavCount = document.getElementById('mesFavCount');
  7545.   let favoriteCount = 0;
  7546.   
  7547.   if (mesFavCount) {
  7548.     favoriteCount = parseInt(mesFavCount.textContent || '0');
  7549.   }
  7550.   
  7551.   console.log('[updateAllFavoriteCounters] 💗 Synchronisation:', favoriteCount, 'favoris');
  7552.   
  7553.   // Synchroniser giftCount (bouton cadeau) avec animation
  7554.   const giftCount = document.getElementById('giftCount');
  7555.   if (giftCount) {
  7556.     giftCount.textContent = favoriteCount;
  7557.     giftCount.classList.remove('gift-count-bounce');
  7558.     setTimeout(() => giftCount.classList.add('gift-count-bounce'), 10);
  7559.   }
  7560.   
  7561.   // Mettre à jour les compteurs dans le sidebar même s'il n'est pas ouvert
  7562.   const albumCount = document.getElementById('album-count');
  7563.   const digitalCount = document.getElementById('digital-count');
  7564.   const printsCount = document.getElementById('prints-count');
  7565.   
  7566.   if (albumCount) albumCount.textContent = favoriteCount;
  7567.   if (digitalCount) digitalCount.textContent = favoriteCount;
  7568.   if (printsCount) printsCount.textContent = Math.min(favoriteCount, 12);
  7569.   
  7570.   // Mettre à jour les icônes de favoris dans les produits
  7571.   const albumFavCount = document.getElementById('album-fav-count');
  7572.   const digitalFavCount = document.getElementById('digital-fav-count');
  7573.   const printsFavCount = document.getElementById('prints-fav-count');
  7574.   
  7575.   if (albumFavCount) albumFavCount.textContent = favoriteCount;
  7576.   if (digitalFavCount) digitalFavCount.textContent = favoriteCount;
  7577.   if (printsFavCount) printsFavCount.textContent = Math.min(favoriteCount, 12);
  7578.   
  7579.   // Mettre à jour le sidebar si ouvert
  7580.   const sidebar = document.getElementById('ecommerce-sidebar');
  7581.   if (sidebar && sidebar.classList.contains('active')) {
  7582.     console.log('🔄 Sidebar is open, updating content with favoriteCount:', favoriteCount);
  7583.     window.updateEcommerceSidebarContent(favoriteCount);
  7584.   } else {
  7585.     console.log('ℹ️ Sidebar is closed, but updating content anyway for consistency');
  7586.     // Mettre à jour le contenu même si le sidebar n'est pas ouvert pour la cohérence
  7587.     window.updateEcommerceSidebarContent(favoriteCount);
  7588.   }
  7589. };
  7590. // Fonction pour retirer un favori de la grille et exécuter supprimerFavoris
  7591. window.removeFavorite = function removeFavorite(itemId) {
  7592.   // Animation simple de disparition puis suppression
  7593.   const favoriteItem = document.querySelector(`.favorite-item[data-id="${itemId}"]`);
  7594.  
  7595.   if (favoriteItem) {
  7596.     favoriteItem.classList.add('fade-out');
  7597.     setTimeout(() => {
  7598.       favoriteItem.remove();
  7599.       // Appeler la fonction métier si présente
  7600.       if (typeof window.supprimerFavoris === 'function') {
  7601.         const heartIcon = document.querySelector(`#coeur${itemId}`);
  7602.         const sejourId = heartIcon && heartIcon.dataset.sejourId ? heartIcon.dataset.sejourId : '';
  7603.         window.supprimerFavoris(itemId, sejourId);
  7604.       }
  7605.       // Mettre à jour les compteurs
  7606.       updateAllFavoriteCounters();
  7607.     }, 200);
  7608.   }
  7609. };
  7610. // Vérifier que la fonction est bien définie
  7611. console.log('🔍 removeFavorite function defined:', typeof window.removeFavorite);
  7612. // (Nettoyé) Pas de délégation globale ici; les boutons utilisent des onclick simples
  7613. // Fonction pour voir un favori en grand
  7614. window.viewFavorite = function viewFavorite(itemId) {
  7615.   console.log('👁️ Viewing favorite:', itemId);
  7616.   
  7617.   // Essayer d'utiliser la fonction viewImage existante
  7618.   if (typeof window.viewImage === 'function') {
  7619.     window.viewImage(itemId, null);
  7620.   } else {
  7621.     console.log('ℹ️ viewImage function not available, opening in new tab');
  7622.     // Fallback: ouvrir dans un nouvel onglet
  7623.     const favoriteItem = document.querySelector(`.favorite-item[data-id="${itemId}"] img`);
  7624.     if (favoriteItem && favoriteItem.src) {
  7625.       window.open(favoriteItem.src, '_blank');
  7626.     }
  7627.   }
  7628. };
  7629. // Fonction de debug pour tester les compteurs
  7630. window.debugFavoriteCounters = function() {
  7631.   console.log('🔍 Debug Favorite Counters:');
  7632.   console.log('- getFavoriteCount():', window.getFavoriteCount());
  7633.   console.log('- likeCount input value:', document.getElementById('likeCount')?.value);
  7634.   console.log('- likeCount textContent:', document.getElementById('likeCount')?.textContent);
  7635.   console.log('- giftCount textContent:', document.getElementById('giftCount')?.textContent);
  7636.   console.log('- album-count textContent:', document.getElementById('album-count')?.textContent);
  7637.   console.log('- digital-count textContent:', document.getElementById('digital-count')?.textContent);
  7638.   console.log('- prints-count textContent:', document.getElementById('prints-count')?.textContent);
  7639.   console.log('- Sidebar active:', document.getElementById('ecommerce-sidebar')?.classList.contains('active'));
  7640. };
  7641. // Fonction de test pour les boutons de favoris
  7642. window.testFavoriteButtons = function() {
  7643.   console.log('🧪 Testing favorite buttons:');
  7644.   const removeButtons = document.querySelectorAll('.btn-remove-favorite');
  7645.   const viewButtons = document.querySelectorAll('.btn-view-favorite');
  7646.   
  7647.   console.log('- Remove buttons found:', removeButtons.length);
  7648.   console.log('- View buttons found:', viewButtons.length);
  7649.   
  7650.   removeButtons.forEach((btn, index) => {
  7651.     console.log(`- Remove button ${index}:`, {
  7652.       itemId: btn.dataset.itemId,
  7653.       hasClickListener: btn.onclick !== null
  7654.     });
  7655.   });
  7656.   
  7657.   viewButtons.forEach((btn, index) => {
  7658.     console.log(`- View button ${index}:`, {
  7659.       itemId: btn.dataset.itemId,
  7660.       hasClickListener: btn.onclick !== null
  7661.     });
  7662.   });
  7663. };
  7664. // Fermer le sidebar en cliquant en dehors
  7665. document.addEventListener('click', function(e) {
  7666.   const sidebar = document.getElementById('ecommerce-sidebar');
  7667.   const giftButton = document.querySelector('.gift-button');
  7668.   
  7669.   if (sidebar && sidebar.classList.contains('active') && 
  7670.       !sidebar.contains(e.target) && 
  7671.       !giftButton.contains(e.target)) {
  7672.     window.closeEcommerceSidebar();
  7673.   }
  7674. });
  7675. // Fermer avec Escape
  7676. document.addEventListener('keydown', function(e) {
  7677.   if (e.key === 'Escape') {
  7678.     window.closeEcommerceSidebar();
  7679.   }
  7680. });
  7681. // Initialisation au chargement
  7682. document.addEventListener('DOMContentLoaded', function() {
  7683.   // Vérifier que les éléments existent
  7684.   const sidebar = document.getElementById('ecommerce-sidebar');
  7685.   const giftButton = document.getElementById('gift-button-trigger');
  7686.   
  7687.   // Event listener propre pour le bouton cadeau
  7688.   if (giftButton) {
  7689.     giftButton.addEventListener('click', function(e) {
  7690.       e.preventDefault();
  7691.       e.stopPropagation();
  7692.       window.openEcommerceSidebar();
  7693.     });
  7694.   }
  7695.   
  7696.   // Définir la date de fin du séjour si disponible
  7697.   {% if sejour and sejour.dateFinSejour is defined %}
  7698.     window.setSejourEndDate('{{ sejour.dateFinSejour|date("Y-m-d") }}');
  7699.   {% endif %}
  7700.   
  7701.   // Mise à jour initiale du contenu
  7702.   let favoriteCount = 0;
  7703.   try {
  7704.     if (typeof getCurrentFavoriteCount === 'function') {
  7705.       favoriteCount = getCurrentFavoriteCount();
  7706.     } else {
  7707.       // Essayer d'abord l'input likeCount
  7708.       const likeCountInput = document.getElementById('likeCount');
  7709.       if (likeCountInput && likeCountInput.value) {
  7710.         favoriteCount = parseInt(likeCountInput.value, 10) || 0;
  7711.       } else {
  7712.         // Fallback sur giftCount
  7713.         const giftCount = document.getElementById('giftCount');
  7714.         if (giftCount && giftCount.textContent) {
  7715.           favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  7716.         }
  7717.       }
  7718.     }
  7719.   } catch (e) {
  7720.     console.log('⚠️ Fallback pour favoriteCount initial:', e);
  7721.     // Fallback sur likeCount input
  7722.     const likeCountInput = document.getElementById('likeCount');
  7723.     if (likeCountInput && likeCountInput.value) {
  7724.       favoriteCount = parseInt(likeCountInput.value, 10) || 0;
  7725.     } else {
  7726.       const giftCount = document.getElementById('giftCount');
  7727.       if (giftCount && giftCount.textContent) {
  7728.         favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  7729.       }
  7730.     }
  7731.   }
  7732.   
  7733.   window.updateEcommerceSidebarContent(favoriteCount);
  7734.   
  7735. });
  7736. </script>
  7737. <!-- 🎯 DAY FILTER POPOVER (instancié une seule fois) -->
  7738. <div id="dayFilterPopover" class="day-popover" role="dialog" aria-modal="true" aria-hidden="true">
  7739.   <div class="dp-arrow" aria-hidden="true"></div>
  7740.   <div class="dp-content" role="group" aria-label="Filtres du jour">
  7741.     <button class="dp-btn" data-filter="all" title="Tout" aria-pressed="true">
  7742.       <i class="bi bi-grid-3x3-gap-fill"></i><span class="dp-count dp-label">Tout</span>
  7743.     </button>
  7744.     <button class="dp-btn" data-filter="photos" title="Photos" aria-pressed="false">
  7745.       <i class="bi bi-images"></i><span class="dp-count" data-bind="photo">0</span>
  7746.     </button>
  7747.     <button class="dp-btn" data-filter="audio" title="Audios" aria-pressed="false">
  7748.       <i class="bi bi-mic-fill"></i><span class="dp-count" data-bind="audio">0</span>
  7749.     </button>
  7750.     <button class="dp-btn" data-filter="videos" title="Vidéos" aria-pressed="false">
  7751.       <i class="bi bi-camera-video-fill"></i><span class="dp-count" data-bind="video">0</span>
  7752.     </button>
  7753.     <button class="dp-btn" data-filter="favoris" title="Favoris" aria-pressed="false">
  7754.       <i class="bi bi-heart-fill" style="color:#f56040"></i><span class="dp-count" data-bind="fav">0</span>
  7755.     </button>
  7756.     <button class="dp-btn dp-close" title="Fermer" aria-label="Fermer">
  7757.       <i class="bi bi-x-lg"></i>
  7758.     </button>
  7759.   </div>
  7760. </div>
  7761. {# ==================== Zones B2C Suggestions ==================== #}
  7762. {# Sidebar suggestions (desktop) #}
  7763. <aside class="col-lg-3 d-none d-lg-block" style="position:sticky;top:100px;">
  7764.   <div id="parentSuggestions" class="pcb-sticky">
  7765.     {# Rempli dynamiquement par parent-b2c-nuke.js #}
  7766.   </div>
  7767. </aside>
  7768. {# Bloc suggestions mobile #}
  7769. <div class="col-12 d-lg-none mt-3">
  7770.   <div id="parentSuggestionsMobile">
  7771.     {# Rempli dynamiquement par parent-b2c-nuke.js #}
  7772.   </div>
  7773. </div>
  7774. {# ==================== SIDEBAR E-COMMERCE ==================== #}
  7775. <script src="{{ asset('js/sidebar-ecommerce-pro.js') }}"></script>
  7776. {% endblock %}