templates/base_home.html.twig line 1

Open in your IDE?
  1. <!DOCTYPE html>
  2. <html lang="fr" class="no-js" data-theme="light" data-bs-theme="light">
  3. <head>
  4.     <!-- Mobile Specific Meta -->
  5.     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
  6.     <meta name="mobile-web-app-capable" content="yes">
  7.     <meta name="apple-mobile-web-app-capable" content="yes">
  8.     <meta name="apple-mobile-web-app-status-bar-style" content="default">
  9.     <meta name="apple-mobile-web-app-title" content="MaketOu">
  10.     <meta name="theme-color" content="#ffa200">
  11.     <meta name="msapplication-TileColor" content="#ffa200">
  12.     <meta name="msapplication-config" content="{{ asset('favicon/browserconfig.xml') }}">
  13.     
  14.     <!-- Favicon-->
  15.     <link rel="shortcut icon" href="{{ asset('favicon/favicon-16x16.png') }}">
  16.     <link rel="apple-touch-icon" sizes="180x180" href="{{ asset('favicon/apple-touch-icon.png') }}">
  17.     <link rel="icon" type="image/png" sizes="32x32" href="{{ asset('favicon/favicon-32x32.png') }}">
  18.     <link rel="icon" type="image/png" sizes="16x16" href="{{ asset('favicon/favicon-16x16.png') }}">
  19.     
  20.     <!-- PWA Manifest -->
  21.     <link rel="manifest" href="{{ asset('manifest.json') }}">
  22.     <!-- Author Meta -->
  23.     <meta name="author" content="Foulgor Tech">
  24.     <!-- Meta Description -->
  25.     <meta name="description" content="">
  26.     <!-- Meta Keyword -->
  27.     <meta name="keywords" content="">
  28.     <!-- meta character set -->
  29.     <meta charset="UTF-8">
  30.     <!-- Site Title -->
  31.     <title>{% block title %}MaketOu{% endblock %}</title>
  32.     <script>
  33.         (function () {
  34.             try {
  35.                 var storageKey = 'maketou-theme';
  36.                 var savedTheme = window.localStorage.getItem(storageKey);
  37.                 var theme = savedTheme === 'dark' || savedTheme === 'light'
  38.                     ? savedTheme
  39.                     : (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
  40.                 document.documentElement.setAttribute('data-theme', theme);
  41.                 document.documentElement.setAttribute('data-bs-theme', theme);
  42.                 document.documentElement.classList.toggle('dark-mode', theme === 'dark');
  43.             } catch (error) {
  44.                 document.documentElement.setAttribute('data-theme', 'light');
  45.                 document.documentElement.setAttribute('data-bs-theme', 'light');
  46.             }
  47.         })();
  48.     </script>
  49.     <!--
  50.         CSS
  51.         ============================================= -->
  52.     <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" />
  53.     <link rel="stylesheet" href="{{ asset('ui/css/linearicons.css') }}">
  54.     <link rel="stylesheet" href="{{ asset('ui/css/font-awesome.min.css') }}">
  55.     <link rel="stylesheet" href="{{ asset('ui/css/themify-icons.css') }}">
  56.     <link rel="stylesheet" href="{{ asset('ui/css/bootstrap.css') }}">
  57.     <link rel="stylesheet" href="{{ asset('ui/css/owl.carousel.css') }}">
  58.     <link rel="stylesheet" href="{{ asset('ui/css/nice-select.css') }}">
  59.     <link rel="stylesheet" href="{{ asset('ui/css/nouislider.min.css') }}">
  60.     <link rel="stylesheet" href="{{ asset('ui/css/ion.rangeSlider.css') }}"/>
  61.     <link rel="stylesheet" href="{{ asset('ui/css/ion.rangeSlider.skinFlat.css') }}"/>
  62.     <link rel="stylesheet" href="{{ asset('ui/css/magnific-popup.css') }}">
  63.     <link rel="stylesheet" href="{{ asset('ui/css/main.css') }}">
  64.     <link rel="stylesheet" href="{{ asset('ui/css/buttons-unified.css') }}">
  65.     <link rel="stylesheet" href="{{ asset('ui/css/theme-public.css') }}">
  66.     <meta charset="UTF-8">
  67.     {# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
  68.     <style>
  69.         /* Styles pour l'icône de notification */
  70.         .notification-icon {
  71.             position: relative;
  72.             display: inline-block;
  73.             color: #333;
  74.             text-decoration: none;
  75.             transition: color 0.3s ease;
  76.         }
  77.         
  78.         .notification-icon:hover {
  79.             color: #007bff;
  80.         }
  81.         
  82.         .notification-badge {
  83.             position: absolute;
  84.             top: -4px;
  85.             right: -2px;
  86.             background: #dc3545 !important;
  87.             color: white !important;
  88.             border-radius: 50%;
  89.             width: 18px;
  90.             height: 18px;
  91.             font-size: 8px !important;
  92.             display: flex !important;
  93.             align-items: center !important;
  94.             justify-content: center !important;
  95.             font-weight: bold !important;
  96.             animation: pulse 2s infinite;
  97.             z-index: 10;
  98.             line-height: 1 !important;
  99.         }
  100.         
  101.         /* Forcer le texte en blanc pour tous les éléments du badge - avec spécificité maximale */
  102.         .notification-badge,
  103.         .notification-badge.text-white,
  104.         .notification-badge *,
  105.         #notificationBadge,
  106.         #notificationBadge.text-white,
  107.         #notificationBadge *,
  108.         span.notification-badge,
  109.         span.notification-badge.text-white,
  110.         span#notificationBadge,
  111.         span#notificationBadge.text-white {
  112.             color: white !important;
  113.             text-color: white !important;
  114.             -webkit-text-fill-color: white !important;
  115.         }
  116.         
  117.         /* S'assurer que le contenu texte est bien en blanc */
  118.         .notification-badge::before,
  119.         .notification-badge::after {
  120.             color: white !important;
  121.         }
  122.         
  123.         /* Forcer la couleur pour tous les sélecteurs possibles avec spécificité maximale */
  124.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon .notification-badge,
  125.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon .notification-badge.text-white,
  126.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon span.notification-badge,
  127.         .notification-icon .notification-badge,
  128.         .notification-icon .notification-badge.text-white,
  129.         .notification-icon span.notification-badge,
  130.         a.notification-icon .notification-badge,
  131.         a.notification-icon span.notification-badge {
  132.             color: white !important;
  133.             text-color: white !important;
  134.             -webkit-text-fill-color: white !important;
  135.         }
  136.         
  137.         /* Forcer la couleur pour tous les enfants directs */
  138.         .notification-icon > .notification-badge,
  139.         .notification-icon > span.notification-badge {
  140.             color: white !important;
  141.         }
  142.         
  143.         @keyframes pulse {
  144.             0% { transform: scale(1); }
  145.             50% { transform: scale(1.1); }
  146.             100% { transform: scale(1); }
  147.         }
  148.         
  149.         .notification-icon:hover .notification-badge {
  150.             animation: none;
  151.         }
  152.         
  153.         /* Container pour les icônes - toujours alignées horizontalement */
  154.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper {
  155.             display: flex !important;
  156.             flex-direction: row !important;
  157.             align-items: center !important;
  158.             justify-content: center !important;
  159.             gap: 15px !important;
  160.             list-style: none !important;
  161.             margin: 0 !important;
  162.             padding: 0 !important;
  163.             width: auto !important;
  164.         }
  165.         
  166.         /* Surcharger les styles existants pour les li contenant le wrapper */
  167.         .header_area .navbar .nav.navbar-nav.navbar-right li:has(.navbar-icons-wrapper) {
  168.             margin-left: 0 !important;
  169.             margin-right: 0 !important;
  170.             display: flex !important;
  171.             align-items: center !important;
  172.             justify-content: center !important;
  173.         }
  174.         
  175.         /* Styles pour les icônes */
  176.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart,
  177.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon,
  178.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search {
  179.             display: flex !important;
  180.             align-items: center !important;
  181.             justify-content: center !important;
  182.             width: 40px !important;
  183.             height: 40px !important;
  184.             min-width: 40px !important;
  185.             min-height: 40px !important;
  186.             color: #333 !important;
  187.             text-decoration: none !important;
  188.             transition: all 0.3s ease !important;
  189.             border-radius: 50% !important;
  190.             position: relative !important;
  191.             margin: 0 !important;
  192.             padding: 0 !important;
  193.         }
  194.         
  195.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart:hover,
  196.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon:hover,
  197.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search:hover {
  198.             background-color: #f0f0f0 !important;
  199.             color: #ffa200 !important;
  200.             transform: scale(1.1) !important;
  201.         }
  202.         
  203.         /* Surcharger le line-height de main.css */
  204.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart span,
  205.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon span,
  206.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search span {
  207.             font-size: 18px !important;
  208.             line-height: 1 !important;
  209.             display: flex !important;
  210.             align-items: center !important;
  211.             justify-content: center !important;
  212.             color: inherit !important;
  213.             font-weight: normal !important;
  214.         }
  215.         
  216.         .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search {
  217.             background: transparent !important;
  218.             border: 0 !important;
  219.             cursor: pointer !important;
  220.             padding: 0 !important;
  221.         }
  222.         
  223.         /* Alignement des icônes navbar sur mobile */
  224.         @media (max-width: 991.98px) {
  225.             /* Container navbar-right en colonne sur mobile */
  226.             .header_area .navbar .nav.navbar-nav.navbar-right {
  227.                 display: flex !important;
  228.                 flex-direction: column !important;
  229.                 align-items: center !important;
  230.                 justify-content: center !important;
  231.                 width: 100% !important;
  232.                 gap: 0 !important;
  233.                 margin: 0 !important;
  234.                 padding: 10px 0 !important;
  235.             }
  236.             
  237.             /* Le container d'icônes reste horizontal */
  238.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper {
  239.                 width: 100% !important;
  240.                 justify-content: center !important;
  241.                 display: flex !important;
  242.                 flex-direction: row !important;
  243.             }
  244.             
  245.             /* Le bouton compte en bas - pleine largeur */
  246.             .header_area .navbar .nav.navbar-nav.navbar-right li:last-child {
  247.                 width: 100% !important;
  248.                 display: flex !important;
  249.                 justify-content: center !important;
  250.                 margin-top: 12px !important;
  251.                 padding-top: 12px !important;
  252.                 border-top: 1px solid #e0e0e0 !important;
  253.             }
  254.             
  255.             /* Bouton compte responsive */
  256.             .header_area .navbar .nav.navbar-nav.navbar-right .btn-account {
  257.                 width: 100%;
  258.                 max-width: 200px;
  259.                 padding: 10px 20px;
  260.                 font-size: 14px;
  261.             }
  262.         }
  263.         
  264.         /* Styles pour les messages flash - Design moderne */
  265.         .flash-message {
  266.             animation: slideInDownFlash 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  267.             position: relative;
  268.             overflow: visible !important;
  269.             border: none !important;
  270.             padding: 16px 20px !important;
  271.             border-radius: 12px !important;
  272.             box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
  273.             display: flex !important;
  274.             align-items: center;
  275.             gap: 12px;
  276.             margin-bottom: 15px;
  277.             backdrop-filter: blur(10px);
  278.             transition: all 0.3s ease;
  279.         }
  280.         
  281.         /* S'assurer que les icônes Font Awesome sont visibles */
  282.         .flash-message i.fa {
  283.             display: inline-block !important;
  284.             visibility: visible !important;
  285.             opacity: 1 !important;
  286.             width: auto !important;
  287.             height: auto !important;
  288.             font-family: "FontAwesome" !important;
  289.             font-style: normal !important;
  290.             font-weight: normal !important;
  291.             text-rendering: auto !important;
  292.             -webkit-font-smoothing: antialiased !important;
  293.             -moz-osx-font-smoothing: grayscale !important;
  294.             line-height: 1 !important;
  295.             flex-shrink: 0 !important;
  296.         }
  297.         
  298.         /* Forcer l'affichage des pseudo-éléments Font Awesome */
  299.         .flash-message i.fa::before {
  300.             display: inline-block !important;
  301.             visibility: visible !important;
  302.             opacity: 1 !important;
  303.             content: attr(data-icon) !important;
  304.         }
  305.         
  306.         /* Styles spécifiques pour chaque icône */
  307.         .flash-message i.fa-check-circle::before {
  308.             content: "\f058" !important;
  309.         }
  310.         
  311.         .flash-message i.fa-exclamation-circle::before {
  312.             content: "\f06a" !important;
  313.         }
  314.         
  315.         .flash-message i.fa-exclamation-triangle::before {
  316.             content: "\f071" !important;
  317.         }
  318.         
  319.         .flash-message i.fa-info-circle::before {
  320.             content: "\f05a" !important;
  321.         }
  322.         
  323.         .flash-message:hover {
  324.             box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2) !important;
  325.             transform: translateY(-2px);
  326.         }
  327.         
  328.         .flash-message::before {
  329.             content: '';
  330.             position: absolute;
  331.             left: 0;
  332.             top: 0;
  333.             bottom: 0;
  334.             width: 5px;
  335.             background: linear-gradient(180deg, currentColor 0%, rgba(255,255,255,0.3) 100%);
  336.             border-radius: 12px 0 0 12px;
  337.         }
  338.         
  339.         .flash-message.alert-success {
  340.             background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%) !important;
  341.             color: #155724 !important;
  342.             border-left: 5px solid #28a745 !important;
  343.         }
  344.         
  345.         .flash-message.alert-success i.fa {
  346.             color: #28a745 !important;
  347.             font-size: 1.3rem !important;
  348.             display: inline-block !important;
  349.             margin-right: 8px;
  350.             vertical-align: middle;
  351.         }
  352.         
  353.         .flash-message.alert-danger {
  354.             background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%) !important;
  355.             color: #721c24 !important;
  356.             border-left: 5px solid #dc3545 !important;
  357.         }
  358.         
  359.         .flash-message.alert-danger i.fa {
  360.             color: #dc3545 !important;
  361.             font-size: 1.3rem !important;
  362.             display: inline-block !important;
  363.             margin-right: 8px;
  364.             vertical-align: middle;
  365.         }
  366.         
  367.         .flash-message.alert-warning {
  368.             background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%) !important;
  369.             color: #856404 !important;
  370.             border-left: 5px solid #ffc107 !important;
  371.         }
  372.         
  373.         .flash-message.alert-warning i.fa {
  374.             color: #ffa200 !important;
  375.             font-size: 1.3rem !important;
  376.             display: inline-block !important;
  377.             margin-right: 8px;
  378.             vertical-align: middle;
  379.         }
  380.         
  381.         .flash-message.alert-info {
  382.             background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%) !important;
  383.             color: #0c5460 !important;
  384.             border-left: 5px solid #17a2b8 !important;
  385.         }
  386.         
  387.         .flash-message.alert-info i.fa {
  388.             color: #17a2b8 !important;
  389.             font-size: 1.3rem !important;
  390.             display: inline-block !important;
  391.             margin-right: 8px;
  392.             vertical-align: middle;
  393.         }
  394.         
  395.         .flash-message strong {
  396.             flex: 1;
  397.             font-weight: 600;
  398.             font-size: 0.95rem;
  399.             line-height: 1.5;
  400.         }
  401.         
  402.         .flash-message .btn-close {
  403.             opacity: 0.7;
  404.             padding: 8px !important;
  405.             margin: -8px -8px -8px auto !important;
  406.             transition: all 0.2s ease;
  407.             border-radius: 50% !important;
  408.             background: rgba(0, 0, 0, 0.05) !important;
  409.             width: 32px !important;
  410.             height: 32px !important;
  411.             display: flex !important;
  412.             align-items: center !important;
  413.             justify-content: center !important;
  414.             border: none !important;
  415.             cursor: pointer !important;
  416.             position: relative !important;
  417.         }
  418.         
  419.         /* Ajouter une icône X visible dans le bouton */
  420.         .flash-message .btn-close::before {
  421.             content: '×' !important;
  422.             font-size: 24px !important;
  423.             line-height: 1 !important;
  424.             color: currentColor !important;
  425.             font-weight: bold !important;
  426.             display: block !important;
  427.             position: absolute !important;
  428.             top: 50% !important;
  429.             left: 50% !important;
  430.             transform: translate(-50%, -50%) !important;
  431.         }
  432.         
  433.         .flash-message .btn-close:hover {
  434.             opacity: 1 !important;
  435.             background: rgba(0, 0, 0, 0.15) !important;
  436.             transform: rotate(90deg) !important;
  437.         }
  438.         
  439.         .flash-message .btn-close:hover::before {
  440.             transform: translate(-50%, -50%) rotate(-90deg) !important;
  441.         }
  442.         
  443.         @keyframes slideInDownFlash {
  444.             from {
  445.                 opacity: 0;
  446.                 transform: translateY(-30px) scale(0.95);
  447.             }
  448.             to {
  449.                 opacity: 1;
  450.                 transform: translateY(0) scale(1);
  451.             }
  452.         }
  453.         
  454.         #flash-messages-container {
  455.             margin-top: 20px;
  456.             margin-bottom: 0;
  457.         }
  458.         
  459.         @media (max-width: 768px) {
  460.             .flash-message {
  461.                 padding: 14px 16px !important;
  462.                 font-size: 0.9rem;
  463.             }
  464.             
  465.             .flash-message i {
  466.                 font-size: 1.2rem !important;
  467.             }
  468.         }
  469.         
  470.         /* Styles pour l'icône de comparaison */
  471.         .comparison-icon {
  472.             position: relative;
  473.             display: inline-block;
  474.             color: #333;
  475.             text-decoration: none;
  476.             transition: color 0.3s ease;
  477.         }
  478.         
  479.         .comparison-icon:hover {
  480.             color: #ffa200;
  481.         }
  482.         
  483.         .comparison-badge {
  484.             position: absolute;
  485.             top: 17px;
  486.             left: 12px;
  487.             background: #ffa200;
  488.             color: white;
  489.             border-radius: 50%;
  490.             width: 18px;
  491.             height: 18px;
  492.             font-size: 10px;
  493.             display: flex;
  494.             align-items: center;
  495.             justify-content: center;
  496.             font-weight: bold;
  497.             animation: pulse 2s infinite;
  498.         }
  499.         
  500.         .comparison-icon:hover .comparison-badge {
  501.             animation: none;
  502.         }
  503.         
  504.         /* Styles pour les mega-menus - style comme la barre de recherche */
  505.         .mega-menu {
  506.             width: 100%;
  507.             max-width: 1200px;
  508.             left: 50%;
  509.             transform: translateX(-50%);
  510.             padding: 25px;
  511.             border-radius: 12px;
  512.             box-shadow: 0 8px 30px rgba(0,0,0,0.12);
  513.             border: none;
  514.             background: white;
  515.             display: none;
  516.             position: absolute;
  517.             top: 100%;
  518.             z-index: 9999 !important;
  519.             margin-top: 10px;
  520.             opacity: 0;
  521.             visibility: hidden;
  522.             transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;
  523.         }
  524.         
  525.         .mega-menu.active {
  526.             display: block;
  527.             opacity: 1;
  528.             visibility: visible;
  529.             transform: translateX(-50%) translateY(0);
  530.         }
  531.         
  532.         .nav-item.dropdown {
  533.             position: relative !important;
  534.         }
  535.         
  536.         @media (min-width: 768px) {
  537.             .nav-item.dropdown {
  538.                 position: relative !important;
  539.             }
  540.             
  541.             .mega-menu {
  542.                 position: absolute !important;
  543.                 left: 50% !important;
  544.                 transform: translateX(-50%) !important;
  545.                 width: auto !important;
  546.                 min-width: 600px !important;
  547.             }
  548.         }
  549.         
  550.         /* Correction supplémentaire pour l'affichage sur la page d'accueil */
  551.         body.home .nav-item.dropdown,
  552.         .nav-item.dropdown {
  553.             position: relative !important;
  554.         }
  555.         
  556.         body.home .mega-menu,
  557.         .mega-menu {
  558.             position: absolute !important;
  559.             left: 50% !important;
  560.             transform: translateX(-50%) translateY(0) !important;
  561.             width: auto !important;
  562.             min-width: 600px !important;
  563.             max-width: 90% !important;
  564.             top: 100% !important;
  565.             margin-top: 10px !important;
  566.         }
  567.         
  568.         /* Assurer que les mega-menus sont toujours visibles au survol */
  569.         .nav-item.dropdown:hover .mega-menu {
  570.             opacity: 1 !important;
  571.             visibility: visible !important;
  572.             display: block !important;
  573.             transform: translateX(-50%) translateY(0) !important;
  574.         }
  575.         
  576.         .mega-menu .dropdown-item {
  577.             padding: 0;
  578.             border: none;
  579.         }
  580.         
  581.         .mega-menu .dropdown-header {
  582.             font-weight: 700;
  583.             color: #2c3e50;
  584.             margin-bottom: 18px;
  585.             padding-bottom: 10px;
  586.             border-bottom: 3px solid #3498db;
  587.             font-size: 16px;
  588.             text-transform: uppercase;
  589.             letter-spacing: 0.5px;
  590.         }
  591.         
  592.         .mega-menu .dropdown-item a {
  593.             display: block;
  594.             padding: 10px 15px;
  595.             color: #555;
  596.             text-decoration: none;
  597.             border-radius: 6px;
  598.             transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  599.             position: relative;
  600.             overflow: hidden;
  601.         }
  602.         
  603.         .mega-menu .dropdown-item a::before {
  604.             content: '';
  605.             position: absolute;
  606.             top: 0;
  607.             left: -100%;
  608.             width: 100%;
  609.             height: 100%;
  610.             background: linear-gradient(90deg, transparent, rgba(52, 152, 219, 0.1), transparent);
  611.             transition: left 0.5s;
  612.         }
  613.         
  614.         .mega-menu .dropdown-item a:hover::before {
  615.             left: 100%;
  616.         }
  617.         
  618.         .mega-menu .dropdown-item a:hover {
  619.             background: linear-gradient(135deg, #f8f9fa, #e3f2fd);
  620.             color: #2980b9;
  621.             transform: translateX(8px);
  622.             box-shadow: 0 2px 8px rgba(52, 152, 219, 0.2);
  623.         }
  624.         
  625.         .mega-menu .dropdown-item a i {
  626.             width: 22px;
  627.             text-align: center;
  628.             margin-right: 8px;
  629.             color: #3498db;
  630.             transition: color 0.3s ease;
  631.         }
  632.         
  633.         .mega-menu .dropdown-item a:hover i {
  634.             color: #2980b9;
  635.         }
  636.         
  637.         /* Animation d'apparition améliorée */
  638.         @keyframes slideInDown {
  639.             from {
  640.                 opacity: 0;
  641.                 transform: translateY(-20px);
  642.             }
  643.             to {
  644.                 opacity: 1;
  645.                 transform: translateY(0);
  646.             }
  647.         }
  648.         
  649.         .mega-menu {
  650.             animation: slideInDown 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  651.         }
  652.         
  653.         /* Effet de survol sur le lien principal */
  654.         .nav-item.dropdown .nav-link {
  655.             position: relative;
  656.             transition: all 0.3s ease;
  657.         }
  658.         
  659.         .nav-item.dropdown .nav-link::after {
  660.             content: '';
  661.             position: absolute;
  662.             bottom: -2px;
  663.             left: 50%;
  664.             width: 0;
  665.             height: 2px;
  666.             background: linear-gradient(90deg, #3498db, #2980b9);
  667.             transition: all 0.3s ease;
  668.             transform: translateX(-50%);
  669.         }
  670.         
  671.         .nav-item.dropdown:hover .nav-link::after {
  672.             width: 100%;
  673.         }
  674.         
  675.         .nav-item.dropdown:hover .nav-link {
  676.             color: #2980b9;
  677.         }
  678.         
  679.         @keyframes fadeInDown {
  680.             from {
  681.                 opacity: 0;
  682.                 transform: translateY(-10px);
  683.             }
  684.             to {
  685.                 opacity: 1;
  686.                 transform: translateY(0);
  687.             }
  688.         }
  689.         
  690.         /* Responsive */
  691.         @media (max-width: 768px) {
  692.             .mega-menu {
  693.                 width: 100%;
  694.                 left: 0;
  695.                 padding: 15px;
  696.             }
  697.             
  698.             .mega-menu .row {
  699.                 margin: 0;
  700.             }
  701.             
  702.             .mega-menu .col-md-3 {
  703.                 margin-bottom: 20px;
  704.             }
  705.         }
  706.     </style>
  707.     
  708.     
  709.     {% block stylesheets %}
  710.         {{ encore_entry_link_tags('app') }}
  711.     {% endblock %}
  712.     
  713.     <!-- Script pour forcer l'application des styles CSS après chargement -->
  714.     <script>
  715.         document.addEventListener('DOMContentLoaded', function() {
  716.             // Forcer le masquage du toggle sur grands écrans
  717.             if (window.innerWidth >= 992) {
  718.                 const togglers = document.querySelectorAll('.navbar-toggler, .header_area .navbar .navbar-toggler');
  719.                 togglers.forEach(function(toggler) {
  720.                     if (toggler) {
  721.                         toggler.style.display = 'none';
  722.                         toggler.style.visibility = 'hidden';
  723.                         toggler.style.opacity = '0';
  724.                         toggler.style.width = '0';
  725.                         toggler.style.height = '0';
  726.                         toggler.style.padding = '0';
  727.                         toggler.style.margin = '0';
  728.                         toggler.style.border = 'none';
  729.                         toggler.style.position = 'absolute';
  730.                         toggler.style.left = '-9999px';
  731.                     }
  732.                 });
  733.                 
  734.                 const iconBars = document.querySelectorAll('.navbar-toggler .icon-bar, .header_area .navbar .navbar-toggler .icon-bar, .navbar-toggler-icon');
  735.                 iconBars.forEach(function(bar) {
  736.                     if (bar) {
  737.                         bar.style.display = 'none';
  738.                         bar.style.visibility = 'hidden';
  739.                     }
  740.                 });
  741.             }
  742.             
  743.             // Alignement des icônes navbar sur mobile
  744.             const navbarRight = document.querySelectorAll('.navbar-right, .header_area .navbar .nav.navbar-nav.navbar-right');
  745.             navbarRight.forEach(function(nav) {
  746.                 if (nav && window.innerWidth <= 991.98) {
  747.                     // Mobile: colonne avec icônes alignées horizontalement
  748.                     nav.style.display = 'flex';
  749.                     nav.style.flexDirection = 'column';
  750.                     nav.style.alignItems = 'center';
  751.                     nav.style.textAlign = 'center';
  752.                     
  753.                     // Le container d'icônes reste horizontal
  754.                     const iconsContainer = nav.querySelector('.navbar-icons-wrapper');
  755.                     if (iconsContainer) {
  756.                         iconsContainer.style.display = 'flex';
  757.                         iconsContainer.style.flexDirection = 'row';
  758.                         iconsContainer.style.justifyContent = 'center';
  759.                     }
  760.                     
  761.                     // Le bouton compte en bas
  762.                     const accountBtn = nav.querySelector('li:last-child');
  763.                     if (accountBtn) {
  764.                         accountBtn.style.width = '100%';
  765.                         accountBtn.style.display = 'flex';
  766.                         accountBtn.style.justifyContent = 'center';
  767.                         accountBtn.style.marginTop = '12px';
  768.                         accountBtn.style.paddingTop = '12px';
  769.                         accountBtn.style.borderTop = '1px solid #e0e0e0';
  770.                     }
  771.                 } else if (nav && window.innerWidth > 991.98) {
  772.                     // Desktop: alignement horizontal normal
  773.                     nav.style.display = 'flex';
  774.                     nav.style.flexDirection = 'row';
  775.                     nav.style.alignItems = 'center';
  776.                     nav.style.justifyContent = 'flex-end';
  777.                 }
  778.             });
  779.         });
  780.         
  781.         // Réappliquer lors du redimensionnement
  782.         window.addEventListener('resize', function() {
  783.             if (window.innerWidth >= 992) {
  784.                 const togglers = document.querySelectorAll('.navbar-toggler, .header_area .navbar .navbar-toggler');
  785.                 togglers.forEach(function(toggler) {
  786.                     if (toggler) {
  787.                         toggler.style.display = 'none';
  788.                         toggler.style.visibility = 'hidden';
  789.                     }
  790.                 });
  791.             }
  792.             
  793.             // Réappliquer l'alignement des icônes navbar
  794.             const navbarRight = document.querySelectorAll('.navbar-right, .header_area .navbar .nav.navbar-nav.navbar-right');
  795.             navbarRight.forEach(function(nav) {
  796.                 if (nav && window.innerWidth <= 991.98) {
  797.                     // Mobile: colonne avec icônes alignées horizontalement
  798.                     nav.style.display = 'flex';
  799.                     nav.style.flexDirection = 'column';
  800.                     nav.style.alignItems = 'center';
  801.                     nav.style.textAlign = 'center';
  802.                     
  803.                     // Le container d'icônes reste horizontal
  804.                     const iconsContainer = nav.querySelector('.navbar-icons-wrapper');
  805.                     if (iconsContainer) {
  806.                         iconsContainer.style.display = 'flex';
  807.                         iconsContainer.style.flexDirection = 'row';
  808.                         iconsContainer.style.justifyContent = 'center';
  809.                     }
  810.                     
  811.                     const accountBtn = nav.querySelector('li:last-child');
  812.                     if (accountBtn) {
  813.                         accountBtn.style.width = '100%';
  814.                         accountBtn.style.display = 'flex';
  815.                         accountBtn.style.justifyContent = 'center';
  816.                         accountBtn.style.marginTop = '12px';
  817.                         accountBtn.style.paddingTop = '12px';
  818.                         accountBtn.style.borderTop = '1px solid #e0e0e0';
  819.                     }
  820.                 } else if (nav && window.innerWidth > 991.98) {
  821.                     // Desktop: alignement horizontal normal
  822.                     nav.style.display = 'flex';
  823.                     nav.style.flexDirection = 'row';
  824.                     nav.style.alignItems = 'center';
  825.                     nav.style.justifyContent = 'flex-end';
  826.                 }
  827.             });
  828.         });
  829.         
  830.     </script>
  831.     {% set current_route = app.request.attributes.get('_route') %}
  832.     {% set is_account_route = current_route starts with 'ui_account_' or current_route == 'notifications_list' %}
  833.     <style>
  834.         /* Shared dark-mode fixes for the public account area. */
  835.         @media (prefers-color-scheme: dark) {
  836.             body.account-theme-scope {
  837.                 background-color: #111827;
  838.                 color: #e5e7eb;
  839.             }
  840.             body.account-theme-scope .card,
  841.             body.account-theme-scope .notification-item,
  842.             body.account-theme-scope .list-group-item,
  843.             body.account-theme-scope .modal-content,
  844.             body.account-theme-scope .dropdown-menu,
  845.             body.account-theme-scope .form-control,
  846.             body.account-theme-scope .form-select,
  847.             body.account-theme-scope .input-group-text,
  848.             body.account-theme-scope .accordion-item,
  849.             body.account-theme-scope .table,
  850.             body.account-theme-scope .table td,
  851.             body.account-theme-scope .table th {
  852.                 background-color: #1f2937 !important;
  853.                 border-color: #374151 !important;
  854.                 color: #e5e7eb !important;
  855.             }
  856.             body.account-theme-scope .card-header,
  857.             body.account-theme-scope .card-header.bg-white,
  858.             body.account-theme-scope .bg-white,
  859.             body.account-theme-scope .account-menu-list .menu-main {
  860.                 background-color: #182131 !important;
  861.                 border-color: #374151 !important;
  862.             }
  863.             body.account-theme-scope .text-muted,
  864.             body.account-theme-scope .form-text,
  865.             body.account-theme-scope .form-label,
  866.             body.account-theme-scope small,
  867.             body.account-theme-scope .small,
  868.             body.account-theme-scope .btn-outline-secondary,
  869.             body.account-theme-scope .btn-outline-secondary i {
  870.                 color: #aeb8c7 !important;
  871.             }
  872.             body.account-theme-scope h1,
  873.             body.account-theme-scope h2,
  874.             body.account-theme-scope h3,
  875.             body.account-theme-scope h4,
  876.             body.account-theme-scope h5,
  877.             body.account-theme-scope h6,
  878.             body.account-theme-scope .list-group-item,
  879.             body.account-theme-scope .card-title,
  880.             body.account-theme-scope .card-text,
  881.             body.account-theme-scope .notification-title,
  882.             body.account-theme-scope .notification-message,
  883.             body.account-theme-scope .notification-meta,
  884.             body.account-theme-scope .table,
  885.             body.account-theme-scope .table td,
  886.             body.account-theme-scope .table th,
  887.             body.account-theme-scope .table strong,
  888.             body.account-theme-scope .table a:not(.btn),
  889.             body.account-theme-scope .breadcrumb-banner,
  890.             body.account-theme-scope .breadcrumb-banner a,
  891.             body.account-theme-scope .breadcrumb-banner h1,
  892.             body.account-theme-scope .empty-state-title,
  893.             body.account-theme-scope .empty-state-text {
  894.                 color: #e5e7eb !important;
  895.             }
  896.             body.account-theme-scope .account-menu-list .list-group-item:hover:not(.menu-action-active):not(.menu-main) {
  897.                 background-color: #243041 !important;
  898.             }
  899.             body.account-theme-scope .notification-item.unread {
  900.                 background-color: #172554 !important;
  901.                 border-left-color: #60a5fa !important;
  902.             }
  903.             body.account-theme-scope .notification-item.archived {
  904.                 opacity: 0.85;
  905.             }
  906.             body.account-theme-scope .btn-outline-primary,
  907.             body.account-theme-scope .btn-outline-secondary,
  908.             body.account-theme-scope .btn-outline-warning,
  909.             body.account-theme-scope .btn-outline-danger {
  910.                 background-color: transparent !important;
  911.             }
  912.             body.account-theme-scope .empty-state-icon {
  913.                 color: #6b7280 !important;
  914.             }
  915.             body.account-theme-scope .profile-picture-container .profile-picture,
  916.             body.account-theme-scope .profile-picture-mobile,
  917.             body.account-theme-scope .profile-picture-edit-icon,
  918.             body.account-theme-scope .profile-picture-edit-icon-mobile {
  919.                 border-color: #111827 !important;
  920.             }
  921.         }
  922.         html.dark-mode body.account-theme-scope,
  923.         body.dark-mode.account-theme-scope,
  924.         html[data-theme="dark"] body.account-theme-scope,
  925.         body[data-theme="dark"].account-theme-scope,
  926.         html[data-bs-theme="dark"] body.account-theme-scope,
  927.         body[data-bs-theme="dark"].account-theme-scope {
  928.             background-color: #111827;
  929.             color: #e5e7eb;
  930.         }
  931.         html.dark-mode body.account-theme-scope .card,
  932.         html.dark-mode body.account-theme-scope .notification-item,
  933.         html.dark-mode body.account-theme-scope .list-group-item,
  934.         html.dark-mode body.account-theme-scope .modal-content,
  935.         html.dark-mode body.account-theme-scope .dropdown-menu,
  936.         html.dark-mode body.account-theme-scope .form-control,
  937.         html.dark-mode body.account-theme-scope .form-select,
  938.         html.dark-mode body.account-theme-scope .input-group-text,
  939.         html.dark-mode body.account-theme-scope .accordion-item,
  940.         html.dark-mode body.account-theme-scope .table,
  941.         html.dark-mode body.account-theme-scope .table td,
  942.         html.dark-mode body.account-theme-scope .table th,
  943.         body.dark-mode.account-theme-scope .card,
  944.         body.dark-mode.account-theme-scope .notification-item,
  945.         body.dark-mode.account-theme-scope .list-group-item,
  946.         body.dark-mode.account-theme-scope .modal-content,
  947.         body.dark-mode.account-theme-scope .dropdown-menu,
  948.         body.dark-mode.account-theme-scope .form-control,
  949.         body.dark-mode.account-theme-scope .form-select,
  950.         body.dark-mode.account-theme-scope .input-group-text,
  951.         body.dark-mode.account-theme-scope .accordion-item,
  952.         body.dark-mode.account-theme-scope .table,
  953.         body.dark-mode.account-theme-scope .table td,
  954.         body.dark-mode.account-theme-scope .table th,
  955.         html[data-theme="dark"] body.account-theme-scope .card,
  956.         html[data-theme="dark"] body.account-theme-scope .notification-item,
  957.         html[data-theme="dark"] body.account-theme-scope .list-group-item,
  958.         html[data-theme="dark"] body.account-theme-scope .modal-content,
  959.         html[data-theme="dark"] body.account-theme-scope .dropdown-menu,
  960.         html[data-theme="dark"] body.account-theme-scope .form-control,
  961.         html[data-theme="dark"] body.account-theme-scope .form-select,
  962.         html[data-theme="dark"] body.account-theme-scope .input-group-text,
  963.         html[data-theme="dark"] body.account-theme-scope .accordion-item,
  964.         html[data-theme="dark"] body.account-theme-scope .table,
  965.         html[data-theme="dark"] body.account-theme-scope .table td,
  966.         html[data-theme="dark"] body.account-theme-scope .table th,
  967.         body[data-theme="dark"].account-theme-scope .card,
  968.         body[data-theme="dark"].account-theme-scope .notification-item,
  969.         body[data-theme="dark"].account-theme-scope .list-group-item,
  970.         body[data-theme="dark"].account-theme-scope .modal-content,
  971.         body[data-theme="dark"].account-theme-scope .dropdown-menu,
  972.         body[data-theme="dark"].account-theme-scope .form-control,
  973.         body[data-theme="dark"].account-theme-scope .form-select,
  974.         body[data-theme="dark"].account-theme-scope .input-group-text,
  975.         body[data-theme="dark"].account-theme-scope .accordion-item,
  976.         body[data-theme="dark"].account-theme-scope .table,
  977.         body[data-theme="dark"].account-theme-scope .table td,
  978.         body[data-theme="dark"].account-theme-scope .table th,
  979.         html[data-bs-theme="dark"] body.account-theme-scope .card,
  980.         html[data-bs-theme="dark"] body.account-theme-scope .notification-item,
  981.         html[data-bs-theme="dark"] body.account-theme-scope .list-group-item,
  982.         html[data-bs-theme="dark"] body.account-theme-scope .modal-content,
  983.         html[data-bs-theme="dark"] body.account-theme-scope .dropdown-menu,
  984.         html[data-bs-theme="dark"] body.account-theme-scope .form-control,
  985.         html[data-bs-theme="dark"] body.account-theme-scope .form-select,
  986.         html[data-bs-theme="dark"] body.account-theme-scope .input-group-text,
  987.         html[data-bs-theme="dark"] body.account-theme-scope .accordion-item,
  988.         html[data-bs-theme="dark"] body.account-theme-scope .table,
  989.         html[data-bs-theme="dark"] body.account-theme-scope .table td,
  990.         html[data-bs-theme="dark"] body.account-theme-scope .table th,
  991.         body[data-bs-theme="dark"].account-theme-scope .card,
  992.         body[data-bs-theme="dark"].account-theme-scope .notification-item,
  993.         body[data-bs-theme="dark"].account-theme-scope .list-group-item,
  994.         body[data-bs-theme="dark"].account-theme-scope .modal-content,
  995.         body[data-bs-theme="dark"].account-theme-scope .dropdown-menu,
  996.         body[data-bs-theme="dark"].account-theme-scope .form-control,
  997.         body[data-bs-theme="dark"].account-theme-scope .form-select,
  998.         body[data-bs-theme="dark"].account-theme-scope .input-group-text,
  999.         body[data-bs-theme="dark"].account-theme-scope .accordion-item,
  1000.         body[data-bs-theme="dark"].account-theme-scope .table,
  1001.         body[data-bs-theme="dark"].account-theme-scope .table td,
  1002.         body[data-bs-theme="dark"].account-theme-scope .table th {
  1003.             background-color: #1f2937 !important;
  1004.             border-color: #374151 !important;
  1005.             color: #e5e7eb !important;
  1006.         }
  1007.         html.dark-mode body.account-theme-scope .card-header,
  1008.         html.dark-mode body.account-theme-scope .card-header.bg-white,
  1009.         html.dark-mode body.account-theme-scope .bg-white,
  1010.         html.dark-mode body.account-theme-scope .account-menu-list .menu-main,
  1011.         body.dark-mode.account-theme-scope .card-header,
  1012.         body.dark-mode.account-theme-scope .card-header.bg-white,
  1013.         body.dark-mode.account-theme-scope .bg-white,
  1014.         body.dark-mode.account-theme-scope .account-menu-list .menu-main,
  1015.         html[data-theme="dark"] body.account-theme-scope .card-header,
  1016.         html[data-theme="dark"] body.account-theme-scope .card-header.bg-white,
  1017.         html[data-theme="dark"] body.account-theme-scope .bg-white,
  1018.         html[data-theme="dark"] body.account-theme-scope .account-menu-list .menu-main,
  1019.         body[data-theme="dark"].account-theme-scope .card-header,
  1020.         body[data-theme="dark"].account-theme-scope .card-header.bg-white,
  1021.         body[data-theme="dark"].account-theme-scope .bg-white,
  1022.         body[data-theme="dark"].account-theme-scope .account-menu-list .menu-main,
  1023.         html[data-bs-theme="dark"] body.account-theme-scope .card-header,
  1024.         html[data-bs-theme="dark"] body.account-theme-scope .card-header.bg-white,
  1025.         html[data-bs-theme="dark"] body.account-theme-scope .bg-white,
  1026.         html[data-bs-theme="dark"] body.account-theme-scope .account-menu-list .menu-main,
  1027.         body[data-bs-theme="dark"].account-theme-scope .card-header,
  1028.         body[data-bs-theme="dark"].account-theme-scope .card-header.bg-white,
  1029.         body[data-bs-theme="dark"].account-theme-scope .bg-white,
  1030.         body[data-bs-theme="dark"].account-theme-scope .account-menu-list .menu-main {
  1031.             background-color: #182131 !important;
  1032.             border-color: #374151 !important;
  1033.         }
  1034.         html.dark-mode body.account-theme-scope .text-muted,
  1035.         html.dark-mode body.account-theme-scope .form-text,
  1036.         html.dark-mode body.account-theme-scope .form-label,
  1037.         html.dark-mode body.account-theme-scope small,
  1038.         html.dark-mode body.account-theme-scope .small,
  1039.         html.dark-mode body.account-theme-scope .btn-outline-secondary,
  1040.         html.dark-mode body.account-theme-scope .btn-outline-secondary i,
  1041.         body.dark-mode.account-theme-scope .text-muted,
  1042.         body.dark-mode.account-theme-scope .form-text,
  1043.         body.dark-mode.account-theme-scope .form-label,
  1044.         body.dark-mode.account-theme-scope small,
  1045.         body.dark-mode.account-theme-scope .small,
  1046.         body.dark-mode.account-theme-scope .btn-outline-secondary,
  1047.         body.dark-mode.account-theme-scope .btn-outline-secondary i,
  1048.         html[data-theme="dark"] body.account-theme-scope .text-muted,
  1049.         html[data-theme="dark"] body.account-theme-scope .form-text,
  1050.         html[data-theme="dark"] body.account-theme-scope .form-label,
  1051.         html[data-theme="dark"] body.account-theme-scope small,
  1052.         html[data-theme="dark"] body.account-theme-scope .small,
  1053.         html[data-theme="dark"] body.account-theme-scope .btn-outline-secondary,
  1054.         html[data-theme="dark"] body.account-theme-scope .btn-outline-secondary i,
  1055.         body[data-theme="dark"].account-theme-scope .text-muted,
  1056.         body[data-theme="dark"].account-theme-scope .form-text,
  1057.         body[data-theme="dark"].account-theme-scope .form-label,
  1058.         body[data-theme="dark"].account-theme-scope small,
  1059.         body[data-theme="dark"].account-theme-scope .small,
  1060.         body[data-theme="dark"].account-theme-scope .btn-outline-secondary,
  1061.         body[data-theme="dark"].account-theme-scope .btn-outline-secondary i,
  1062.         html[data-bs-theme="dark"] body.account-theme-scope .text-muted,
  1063.         html[data-bs-theme="dark"] body.account-theme-scope .form-text,
  1064.         html[data-bs-theme="dark"] body.account-theme-scope .form-label,
  1065.         html[data-bs-theme="dark"] body.account-theme-scope small,
  1066.         html[data-bs-theme="dark"] body.account-theme-scope .small,
  1067.         html[data-bs-theme="dark"] body.account-theme-scope .btn-outline-secondary,
  1068.         html[data-bs-theme="dark"] body.account-theme-scope .btn-outline-secondary i,
  1069.         body[data-bs-theme="dark"].account-theme-scope .text-muted,
  1070.         body[data-bs-theme="dark"].account-theme-scope .form-text,
  1071.         body[data-bs-theme="dark"].account-theme-scope .form-label,
  1072.         body[data-bs-theme="dark"].account-theme-scope small,
  1073.         body[data-bs-theme="dark"].account-theme-scope .small,
  1074.         body[data-bs-theme="dark"].account-theme-scope .btn-outline-secondary,
  1075.         body[data-bs-theme="dark"].account-theme-scope .btn-outline-secondary i {
  1076.             color: #aeb8c7 !important;
  1077.         }
  1078.         html.dark-mode body.account-theme-scope .empty-state-icon,
  1079.         body.dark-mode.account-theme-scope .empty-state-icon,
  1080.         html[data-theme="dark"] body.account-theme-scope .empty-state-icon,
  1081.         body[data-theme="dark"].account-theme-scope .empty-state-icon,
  1082.         html[data-bs-theme="dark"] body.account-theme-scope .empty-state-icon,
  1083.         body[data-bs-theme="dark"].account-theme-scope .empty-state-icon {
  1084.             color: #6b7280 !important;
  1085.         }
  1086.     </style>
  1087. </head>
  1088. <body {% if current_menu is defined and current_menu == "listing" %}id="category"{% endif %} class="{% if is_account_route %}account-theme-scope{% endif %}">
  1089. <!-- Start Header Area -->
  1090. <header class="header_area sticky-header">
  1091.     <div class="main_menu">
  1092.         <nav class="navbar navbar-expand-lg navbar-light main_box">
  1093.             <div class="container">
  1094.                 <!-- Brand and toggle get grouped for better mobile display -->
  1095.                 <a class="navbar-brand logo_h" href="{{ path('ui_home') }}"><img style="width: auto;height: 60px" src="{{ asset('logo.png') }}" alt=""></a>
  1096.                 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
  1097.                         aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
  1098.                     <span class="icon-bar"></span>
  1099.                     <span class="icon-bar"></span>
  1100.                     <span class="icon-bar"></span>
  1101.                 </button>
  1102.                 <!-- Collect the nav links, forms, and other content for toggling -->
  1103.                 <div class="collapse navbar-collapse offset" id="navbarSupportedContent">
  1104.                     <ul class="nav navbar-nav menu_nav ml-auto">
  1105.                         <li class="nav-item {% if current_menu is defined and current_menu == "home" %}active{% endif %}">
  1106.                             <a class="nav-link" href="{{ path('ui_home') }}">Accueil</a>
  1107.                         </li>
  1108.                         <li class="nav-item {% if current_menu is defined and current_menu == "shops" %}active{% endif %}">
  1109.                             <a class="nav-link" href="{{ path('ui_shops_list') }}">Boutiques</a>
  1110.                         </li>
  1111.                         <li class="nav-item {% if current_menu is defined and current_menu == "listing" %}active{% endif %}">
  1112.                             <a class="nav-link" href="{{ path('ui_listing') }}">Produits</a>
  1113.                         </li>
  1114.                         <li class="nav-item {% if current_menu is defined and current_menu == "blog" %}active{% endif %}">
  1115.                             <a class="nav-link" href="{{ path('ui_blog') }}">Blog</a>
  1116.                         </li>
  1117.                         <li class="nav-item {% if current_menu is defined and current_menu == "contact" %}active{% endif %}">
  1118.                             <a class="nav-link" href="{{ path('ui_contact') }}">Contact</a>
  1119.                         </li>
  1120.                     </ul>
  1121.                     <ul class="nav navbar-nav navbar-right">
  1122.                         <li class="nav-item header-icons-item">
  1123.                             <div class="navbar-icons-wrapper">
  1124.                                 <a href="{{ path('ui_cart') }}" class="cart {% if current_menu is defined and current_menu == "cart" %}active{% endif %}" aria-label="Panier">
  1125.                                     <span class="ti-bag"></span>
  1126.                                 </a>
  1127.                                 <a href="{{ path('ui_product_comparison') }}" class="comparison-icon {% if current_menu is defined and current_menu == "comparison" %}active{% endif %}" title="Comparaison premium" aria-label="Comparaison premium">
  1128.                                     <span class="lnr lnr-sync"></span>
  1129.                                     <span class="comparison-badge" id="comparisonBadge" data-comparison-badge style="display: none;">0</span>
  1130.                                 </a>
  1131.                                 {% if app.user %}
  1132.                                 <a href="{{ path('notifications_list') }}" class="notification-icon" title="Notifications" aria-label="Notifications">
  1133.                                     <span class="ti-bell"></span>
  1134.                                     <span class="notification-badge text-white" id="notificationBadge" style="display: none;color: white;">0</span>
  1135.                                 </a>
  1136.                                 {% endif %}
  1137.                                 <button class="theme-toggle-btn" type="button" data-theme-toggle aria-label="Passer au mode sombre" title="Passer au mode sombre">
  1138.                                     <span class="ti-moon" data-theme-icon></span>
  1139.                                 </button>
  1140.                                 <button class="search" type="button" aria-label="Recherche">
  1141.                                     <span class="lnr lnr-magnifier" id="search"></span>
  1142.                                 </button>
  1143.                             </div>
  1144.                         </li>
  1145.                         <li class="nav-item header-account-item">
  1146.                             {% if app.user %}
  1147.                                 <a class="btn btn-primary btn-account header-account-link text-white" href="{{ path('ui_account_index') }}">
  1148.                                     <i class="ti-user"></i> Mon compte
  1149.                                 </a>
  1150.                             {% else %}
  1151.                                 <a class="btn btn-primary btn-account header-account-link text-white" href="{{ path('ui_app_login') }}">
  1152.                                     <i class="ti-lock"></i>Se connecter
  1153.                                 </a>
  1154.                             {% endif %}
  1155.                         </li>
  1156.                     </ul>
  1157.                 </div>
  1158.             </div>
  1159.         </nav>
  1160.     </div>
  1161.     <div class="search_input" id="search_input_box">
  1162.         <div class="container">
  1163.             <form class="d-flex justify-content-between">
  1164.                 <input type="text" class="form-control" id="search_input" placeholder="Search Here">
  1165.                 <button type="submit" class="btn"></button>
  1166.                 <span class="lnr lnr-cross" id="close_search" title="Close Search"></span>
  1167.             </form>
  1168.         </div>
  1169.     </div>
  1170. </header>
  1171. <!-- End Header Area -->
  1172. <!-- Flash Messages -->
  1173. <div class="container mt-3" id="flash-messages-container" style="position: relative; z-index: 1050;">
  1174.     <div class="row">
  1175.         <div class="col-12">
  1176.             {% for message in app.flashes('success') %}
  1177.                 <div class="alert alert-success alert-dismissible fade show flash-message" role="alert">
  1178.                     <i class="fa fa-check-circle"></i>
  1179.                     <strong>{{ message }}</strong>
  1180.                     <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1181.                 </div>
  1182.             {% endfor %}
  1183.             
  1184.             {% for message in app.flashes('error') %}
  1185.                 <div class="alert alert-danger alert-dismissible fade show flash-message" role="alert">
  1186.                     <i class="fa fa-exclamation-circle"></i>
  1187.                     <strong>{{ message }}</strong>
  1188.                     <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1189.                 </div>
  1190.             {% endfor %}
  1191.             
  1192.             {% for message in app.flashes('warning') %}
  1193.                 <div class="alert alert-warning alert-dismissible fade show flash-message" role="alert">
  1194.                     <i class="fa fa-exclamation-triangle"></i>
  1195.                     <strong>{{ message }}</strong>
  1196.                     <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1197.                 </div>
  1198.             {% endfor %}
  1199.             
  1200.             {% for message in app.flashes('info') %}
  1201.                 <div class="alert alert-info alert-dismissible fade show flash-message" role="alert">
  1202.                     <i class="fa fa-info-circle"></i>
  1203.                     <strong>{{ message }}</strong>
  1204.                     <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1205.                 </div>
  1206.             {% endfor %}
  1207.         </div>
  1208.     </div>
  1209. </div>
  1210. <div id="comparisonTray" class="comparison-tray" aria-live="polite">
  1211.     <div class="comparison-tray__header">
  1212.         <div>
  1213.             <p class="comparison-tray__title">Comparaison premium</p>
  1214.             <p class="comparison-tray__meta" id="comparisonTrayMeta">Aucun produit en comparaison</p>
  1215.         </div>
  1216.         <a href="{{ path('ui_product_comparison') }}" class="comparison-tray__go">Ouvrir</a>
  1217.     </div>
  1218.     <div class="comparison-tray__items" id="comparisonTrayItems"></div>
  1219.     <p class="comparison-tray__empty" id="comparisonTrayEmpty">Ajoute des produits d'une meme categorie pour obtenir une comparaison ultra pertinente.</p>
  1220.     <div class="comparison-tray__actions">
  1221.         <a href="{{ path('ui_product_comparison') }}" class="comparison-tray__go">Comparer maintenant</a>
  1222.         <button type="button" class="comparison-tray__clear" id="comparisonTrayClear">Vider</button>
  1223.     </div>
  1224. </div>
  1225. {% block body %}{% endblock %}
  1226. <!-- start footer Area -->
  1227. <footer class="footer-area section_gap">
  1228.     <div class="footer-pattern"></div>
  1229.     <div class="container">
  1230.         <div class="row">
  1231.             <div class="col-lg-3 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1232.                 <div class="single-footer-widget">
  1233.                     <div class="footer-logo mb-3">
  1234.                         <h4 class="footer-brand">MaketOu</h4>
  1235.                     </div>
  1236.                     <p class="footer-description">
  1237.                         MaketOu est la première marketplace haïtienne qui connecte les acheteurs et les vendeurs. 
  1238.                         Nous offrons une plateforme sécurisée et moderne pour le commerce en ligne en Haïti.
  1239.                     </p>
  1240.                     <div class="footer-social mt-3">
  1241.                         <h6 class="mb-2">Suivez-nous</h6>
  1242.                         <div class="footer-social-icons">
  1243.                             <a href="#" class="social-icon" aria-label="Facebook" title="Facebook">
  1244.                                 <i class="fa fa-facebook"></i>
  1245.                             </a>
  1246.                             <a href="#" class="social-icon" aria-label="Twitter" title="Twitter">
  1247.                                 <i class="fa fa-twitter"></i>
  1248.                             </a>
  1249.                             <a href="#" class="social-icon" aria-label="Instagram" title="Instagram">
  1250.                                 <i class="fa fa-instagram"></i>
  1251.                             </a>
  1252.                             <a href="#" class="social-icon" aria-label="LinkedIn" title="LinkedIn">
  1253.                                 <i class="fa fa-linkedin"></i>
  1254.                             </a>
  1255.                         </div>
  1256.                     </div>
  1257.                 </div>
  1258.             </div>
  1259.             <div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1260.                 <div class="single-footer-widget">
  1261.                     <h6 class="footer-widget-title">Liens rapides</h6>
  1262.                     <ul class="footer-list">
  1263.                         <li><a href="{{ path('ui_home') }}"><i class="fa fa-angle-right me-2"></i>Accueil</a></li>
  1264.                         <li><a href="{{ path('ui_listing') }}"><i class="fa fa-angle-right me-2"></i>Produits</a></li>
  1265.                         <li><a href="{{ path('ui_shops_list') }}"><i class="fa fa-angle-right me-2"></i>Boutiques</a></li>
  1266.                         {% if app.user %}
  1267.                             <li><a href="{{ path('ui_account_index') }}"><i class="fa fa-angle-right me-2"></i>Mon compte</a></li>
  1268.                         {% else %}
  1269.                             <li><a href="{{ path('ui_app_login') }}"><i class="fa fa-angle-right me-2"></i>Se connecter</a></li>
  1270.                             <li><a href="{{ path('ui_app_register') }}"><i class="fa fa-angle-right me-2"></i>S'inscrire</a></li>
  1271.                         {% endif %}
  1272.                     </ul>
  1273.                 </div>
  1274.             </div>
  1275.             <div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1276.                 <div class="single-footer-widget">
  1277.                     <h6 class="footer-widget-title">Pour les vendeurs</h6>
  1278.                     <ul class="footer-list">
  1279.                         <li><a href="{{ path('seller_index') }}"><i class="fa fa-angle-right me-2"></i>Ouvrir une boutique</a></li>
  1280.                         <li><a href="{{ path('seller_help_how_to_sell') }}"><i class="fa fa-angle-right me-2"></i>Comment vendre</a></li>
  1281.                         <li><a href="{{ path('seller_help_pricing') }}"><i class="fa fa-angle-right me-2"></i>Tarifs</a></li>
  1282.                         <li><a href="{{ path('seller_help_support') }}"><i class="fa fa-angle-right me-2"></i>Support vendeur</a></li>
  1283.                     </ul>
  1284.                 </div>
  1285.             </div>
  1286.             <div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1287.                 <div class="single-footer-widget">
  1288.                     <h6 class="footer-widget-title">Support</h6>
  1289.                     <ul class="footer-list">
  1290.                         <li><a href="{{ path('ui_help') }}"><i class="fa fa-angle-right me-2"></i> Centre d'Aide</a></li>
  1291.                         <li><a href="{{ path('ui_contact') }}"><i class="fa fa-angle-right me-2"></i> Contactez-nous</a></li>
  1292.                         <li><a href="{{ path('ui_faq') }}"><i class="fa fa-angle-right me-2"></i> FAQ</a></li>
  1293.                         <li><a href="{{ path('ui_privacy') }}"><i class="fa fa-angle-right me-2"></i> Politique de Confidentialité</a></li>
  1294.                         <li><a href="{{ path('ui_terms') }}"><i class="fa fa-angle-right me-2"></i> Conditions d'Utilisation</a></li>
  1295.                     </ul>
  1296.                 </div>
  1297.             </div>
  1298.             <div class="col-lg-3 col-md-6 col-sm-6">
  1299.                 <div class="single-footer-widget">
  1300.                     <h6 class="footer-widget-title">Newsletter</h6>
  1301.                     <p class="footer-newsletter-text">Restez informé des dernières nouveautés</p>
  1302.                     <div class="footer-newsletter-wrapper" id="mc_embed_signup">
  1303.                         <form action="{{ path('ui_newsletter_subscribe') }}" method="post" class="footer-newsletter-form">
  1304.                             <div class="newsletter-input-group">
  1305.                                 <i class="fa fa-envelope newsletter-icon"></i>
  1306.                                 <input class="form-control newsletter-input" name="email" placeholder="Votre email" required type="email">
  1307.                                 <button type="submit" class="newsletter-btn" aria-label="S'abonner">
  1308.                                     <span class="newsletter-btn-icon">
  1309.                                         <i class="fa fa-paper-plane" aria-hidden="true"></i>
  1310.                                     </span>
  1311.                                     <span class="newsletter-btn-loader" style="display: none;">
  1312.                                         <i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
  1313.                                     </span>
  1314.                                 </button>
  1315.                             </div>
  1316.                         </form>
  1317.                     </div>
  1318.                     <div class="footer-trust-badges mt-3">
  1319.                         <div class="trust-item">
  1320.                             <i class="fa fa-shield"></i>
  1321.                             <span>Paiement sécurisé</span>
  1322.                         </div>
  1323.                         <div class="trust-item">
  1324.                             <i class="fa fa-truck"></i>
  1325.                             <span>Livraison rapide</span>
  1326.                         </div>
  1327.                     </div>
  1328.                 </div>
  1329.             </div>
  1330.         </div>
  1331.         <div class="footer-divider"></div>
  1332.         <div class="footer-bottom d-flex justify-content-center align-items-center flex-wrap">
  1333.             <p class="footer-text m-0">
  1334.                 Copyright &copy;<script>document.write(new Date().getFullYear());</script>
  1335.                 Tous droits réservés | Ce site a été fait avec <i class="fa fa-heart-o" aria-hidden="true" style="color: #dc3545;"></i> par <a
  1336.                         href="https://tech.foulgor.com" target="_blank" style="color: #ffa200;">Foulgor Tech</a>
  1337.             </p>
  1338.         </div>
  1339.     </div>
  1340. </footer>
  1341. <style>
  1342. /* ============================================
  1343.    FOOTER MODERNE - Design amélioré
  1344.    ============================================ */
  1345. .footer-area {
  1346.     background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
  1347.     position: relative;
  1348.     overflow: hidden;
  1349.     padding: 50px 0 20px;
  1350. }
  1351. .footer-pattern {
  1352.     position: absolute;
  1353.     top: 0;
  1354.     left: 0;
  1355.     right: 0;
  1356.     bottom: 0;
  1357.     background-image: 
  1358.         radial-gradient(circle at 20% 50%, rgba(255, 162, 0, 0.1) 0%, transparent 50%),
  1359.         radial-gradient(circle at 80% 80%, rgba(255, 162, 0, 0.08) 0%, transparent 50%);
  1360.     pointer-events: none;
  1361. }
  1362. .footer-area .container {
  1363.     position: relative;
  1364.     z-index: 1;
  1365. }
  1366. /* Widgets */
  1367. .single-footer-widget {
  1368.     color: rgba(255, 255, 255, 0.9);
  1369. }
  1370. .footer-brand {
  1371.     font-size: 1.6rem;
  1372.     font-weight: 700;
  1373.     background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  1374.     -webkit-background-clip: text;
  1375.     -webkit-text-fill-color: transparent;
  1376.     background-clip: text;
  1377.     margin-bottom: 12px;
  1378.     display: inline-block;
  1379. }
  1380. .footer-widget-title {
  1381.     color: #fff;
  1382.     font-size: 1rem;
  1383.     font-weight: 600;
  1384.     margin-bottom: 18px;
  1385.     position: relative;
  1386.     padding-bottom: 8px;
  1387. }
  1388. .footer-widget-title::after {
  1389.     content: '';
  1390.     position: absolute;
  1391.     bottom: 0;
  1392.     left: 0;
  1393.     width: 40px;
  1394.     height: 3px;
  1395.     background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  1396.     border-radius: 2px;
  1397. }
  1398. .footer-description {
  1399.     color: rgba(255, 255, 255, 0.7);
  1400.     line-height: 1.6;
  1401.     font-size: 0.9rem;
  1402.     margin-bottom: 0;
  1403. }
  1404. /* Liste de liens */
  1405. .footer-list {
  1406.     list-style: none;
  1407.     padding: 0;
  1408.     margin: 0;
  1409. }
  1410. .footer-list li {
  1411.     margin-bottom: 10px;
  1412. }
  1413. .footer-list li a {
  1414.     color: rgba(255, 255, 255, 0.7);
  1415.     text-decoration: none;
  1416.     transition: all 0.3s ease;
  1417.     font-size: 0.9rem;
  1418.     display: inline-flex;
  1419.     align-items: center;
  1420. }
  1421. .footer-list li a i {
  1422.     font-size: 0.8rem;
  1423.     opacity: 0.6;
  1424.     transition: all 0.3s ease;
  1425. }
  1426. .footer-list li a:hover {
  1427.     color: #ffa200;
  1428.     transform: translateX(5px);
  1429. }
  1430. .footer-list li a:hover i {
  1431.     opacity: 1;
  1432.     color: #ffa200;
  1433. }
  1434. /* Newsletter */
  1435. .footer-newsletter-text {
  1436.     color: rgba(255, 255, 255, 0.7);
  1437.     font-size: 0.85rem;
  1438.     margin-bottom: 15px;
  1439.     line-height: 1.5;
  1440. }
  1441. .footer-newsletter-wrapper {
  1442.     margin-bottom: 15px;
  1443. }
  1444. .newsletter-input-group {
  1445.     display: flex;
  1446.     align-items: center;
  1447.     background: rgba(255, 255, 255, 0.08);
  1448.     border-radius: 30px;
  1449.     padding: 4px 4px 4px 15px;
  1450.     backdrop-filter: blur(10px);
  1451.     border: 1px solid rgba(255, 255, 255, 0.15);
  1452.     transition: all 0.3s ease;
  1453.     gap: 8px;
  1454. }
  1455. .newsletter-input-group:focus-within {
  1456.     background: rgba(255, 255, 255, 0.12);
  1457.     border-color: rgba(255, 162, 0, 0.6);
  1458.     box-shadow: 0 0 0 2px rgba(255, 162, 0, 0.15);
  1459. }
  1460. .newsletter-icon {
  1461.     color: rgba(255, 255, 255, 0.5);
  1462.     font-size: 0.9rem;
  1463.     flex-shrink: 0;
  1464. }
  1465. .newsletter-input-group:focus-within .newsletter-icon {
  1466.     color: #ffa200;
  1467. }
  1468. .newsletter-input {
  1469.     flex: 1;
  1470.     background: transparent;
  1471.     border: none;
  1472.     color: #fff;
  1473.     padding: 10px 8px;
  1474.     font-size: 0.9rem;
  1475.     min-width: 0;
  1476. }
  1477. .newsletter-input::placeholder {
  1478.     color: rgba(255, 255, 255, 0.4);
  1479.     font-size: 0.85rem;
  1480. }
  1481. .newsletter-input:focus {
  1482.     outline: none;
  1483.     box-shadow: none;
  1484. }
  1485. .newsletter-btn {
  1486.     background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  1487.     border: none;
  1488.     color: white;
  1489.     width: 42px;
  1490.     height: 42px;
  1491.     min-width: 42px;
  1492.     min-height: 42px;
  1493.     border-radius: 50%;
  1494.     display: flex;
  1495.     align-items: center;
  1496.     justify-content: center;
  1497.     cursor: pointer;
  1498.     transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  1499.     flex-shrink: 0;
  1500.     box-shadow: 0 4px 15px rgba(255, 162, 0, 0.4), 
  1501.                 0 0 0 0 rgba(255, 162, 0, 0.4);
  1502.     font-size: 0.9rem;
  1503.     position: relative;
  1504.     overflow: hidden;
  1505. }
  1506. .newsletter-btn::before {
  1507.     content: '';
  1508.     position: absolute;
  1509.     top: 50%;
  1510.     left: 50%;
  1511.     width: 0;
  1512.     height: 0;
  1513.     border-radius: 50%;
  1514.     background: rgba(255, 255, 255, 0.3);
  1515.     transform: translate(-50%, -50%);
  1516.     transition: width 0.6s ease, height 0.6s ease;
  1517. }
  1518. .newsletter-btn:hover::before {
  1519.     width: 100%;
  1520.     height: 100%;
  1521. }
  1522. .newsletter-btn:hover {
  1523.     transform: translateX(3px) scale(1.05);
  1524.     box-shadow: 0 6px 20px rgba(255, 162, 0, 0.5),
  1525.                 0 0 0 8px rgba(255, 162, 0, 0.1);
  1526.     background: linear-gradient(135deg, #e8910a 0%, #ffa200 100%);
  1527. }
  1528. .newsletter-btn:active {
  1529.     transform: translateX(3px) scale(0.98);
  1530.     box-shadow: 0 3px 10px rgba(255, 162, 0, 0.4);
  1531. }
  1532. .newsletter-btn:focus {
  1533.     outline: none;
  1534.     box-shadow: 0 4px 15px rgba(255, 162, 0, 0.4),
  1535.                 0 0 0 3px rgba(255, 162, 0, 0.3);
  1536. }
  1537. .newsletter-btn-icon,
  1538. .newsletter-btn-loader {
  1539.     position: relative;
  1540.     z-index: 2;
  1541.     display: flex;
  1542.     align-items: center;
  1543.     justify-content: center;
  1544.     transition: all 0.3s ease;
  1545. }
  1546. .newsletter-btn:hover .newsletter-btn-icon {
  1547.     transform: translateX(2px) rotate(-10deg);
  1548. }
  1549. .newsletter-btn.loading .newsletter-btn-icon {
  1550.     opacity: 0;
  1551.     transform: scale(0);
  1552. }
  1553. .newsletter-btn.loading .newsletter-btn-loader {
  1554.     display: flex !important;
  1555. }
  1556. .newsletter-btn.success {
  1557.     background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
  1558.     box-shadow: 0 4px 15px rgba(40, 167, 69, 0.4);
  1559. }
  1560. .newsletter-btn.success .newsletter-btn-icon i::before {
  1561.     content: "\f00c";
  1562. }
  1563. /* Trust badges */
  1564. .footer-trust-badges {
  1565.     display: flex;
  1566.     flex-direction: row;
  1567.     gap: 15px;
  1568.     flex-wrap: wrap;
  1569. }
  1570. .trust-item {
  1571.     display: flex;
  1572.     align-items: center;
  1573.     gap: 6px;
  1574.     color: rgba(255, 255, 255, 0.6);
  1575.     font-size: 0.8rem;
  1576. }
  1577. .trust-item i {
  1578.     color: #ffa200;
  1579.     font-size: 0.9rem;
  1580. }
  1581. /* Social icons */
  1582. .footer-social {
  1583.     margin-top: 0 !important;
  1584.     position: relative !important;
  1585.     width: 100% !important;
  1586.     clear: both !important;
  1587.     float: none !important;
  1588. }
  1589. .footer-social h6 {
  1590.     color: #fff;
  1591.     font-size: 0.95rem;
  1592.     font-weight: 600;
  1593.     margin-bottom: 12px !important;
  1594.     display: block !important;
  1595.     width: 100% !important;
  1596.     position: relative !important;
  1597.     clear: both !important;
  1598.     float: none !important;
  1599. }
  1600. .footer-social-icons {
  1601.     display: flex !important;
  1602.     gap: 12px !important;
  1603.     flex-wrap: wrap !important;
  1604.     align-items: center !important;
  1605.     justify-content: flex-start !important;
  1606.     width: 100% !important;
  1607.     position: relative !important;
  1608.     clear: both !important;
  1609.     float: none !important;
  1610.     margin: 0 !important;
  1611.     padding: 0 !important;
  1612. }
  1613. .social-icon {
  1614.     width: 38px !important;
  1615.     height: 38px !important;
  1616.     min-width: 38px !important;
  1617.     min-height: 38px !important;
  1618.     display: inline-flex !important;
  1619.     align-items: center !important;
  1620.     justify-content: center !important;
  1621.     background: rgba(255, 255, 255, 0.1) !important;
  1622.     border-radius: 50% !important;
  1623.     color: rgba(255, 255, 255, 0.8) !important;
  1624.     text-decoration: none !important;
  1625.     transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  1626.     font-size: 16px !important;
  1627.     line-height: 1 !important;
  1628.     backdrop-filter: blur(10px) !important;
  1629.     border: 1px solid rgba(255, 255, 255, 0.1) !important;
  1630.     flex-shrink: 0 !important;
  1631.     position: relative !important;
  1632.     float: none !important;
  1633.     margin: 0 !important;
  1634.     padding: 0 !important;
  1635.     vertical-align: middle !important;
  1636. }
  1637. .social-icon i {
  1638.     font-size: 16px !important;
  1639.     line-height: 1 !important;
  1640.     display: inline-block !important;
  1641.     transition: transform 0.3s ease !important;
  1642.     position: relative !important;
  1643.     z-index: 1 !important;
  1644.     margin: 0 !important;
  1645.     padding: 0 !important;
  1646. }
  1647. .social-icon:hover {
  1648.     background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%) !important;
  1649.     transform: translateY(-5px) scale(1.1) !important;
  1650.     color: white !important;
  1651.     border-color: transparent !important;
  1652.     box-shadow: 0 8px 20px rgba(255, 162, 0, 0.4) !important;
  1653. }
  1654. .social-icon:hover i {
  1655.     transform: scale(1.1) !important;
  1656. }
  1657. /* Footer divider */
  1658. .footer-divider {
  1659.     height: 1px;
  1660.     background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
  1661.     margin: 30px 0 20px;
  1662. }
  1663. /* Footer bottom */
  1664. .footer-bottom {
  1665.     padding-top: 15px;
  1666. }
  1667. .footer-text {
  1668.     color: rgba(255, 255, 255, 0.6);
  1669.     font-size: 0.9rem;
  1670.     margin: 0;
  1671.     text-align: center;
  1672. }
  1673. /* Responsive */
  1674. @media(max-width: 991.98px) {
  1675.     .footer-area {
  1676.         padding: 40px 0 20px;
  1677.     }
  1678.     
  1679.     .single-footer-widget {
  1680.         margin-bottom: 30px;
  1681.     }
  1682.     
  1683.     .footer-social {
  1684.         margin-top: 15px;
  1685.     }
  1686. }
  1687. @media(max-width: 767.98px) {
  1688.     .footer-area {
  1689.         padding: 35px 0 18px;
  1690.     }
  1691.     
  1692.     .footer-brand {
  1693.         font-size: 1.4rem;
  1694.     }
  1695.     
  1696.     .footer-widget-title {
  1697.         font-size: 0.95rem;
  1698.     }
  1699.     
  1700.     
  1701.     .newsletter-input-group {
  1702.         padding: 3px 3px 3px 12px;
  1703.     }
  1704.     
  1705.     .newsletter-input {
  1706.         padding: 8px 6px;
  1707.         font-size: 0.85rem;
  1708.     }
  1709.     
  1710.     .newsletter-btn {
  1711.         width: 38px;
  1712.         height: 38px;
  1713.         min-width: 38px;
  1714.         min-height: 38px;
  1715.         font-size: 0.85rem;
  1716.     }
  1717.     
  1718.     .footer-trust-badges {
  1719.         flex-direction: row;
  1720.         flex-wrap: wrap;
  1721.         justify-content: flex-start;
  1722.         gap: 12px;
  1723.     }
  1724. }
  1725. @media(max-width: 575.98px) {
  1726.     .footer-area {
  1727.         padding: 40px 0 20px;
  1728.     }
  1729.     
  1730.     .footer-brand {
  1731.         font-size: 1.3rem;
  1732.     }
  1733.     
  1734.     .social-icon {
  1735.         width: 38px;
  1736.         height: 38px;
  1737.         font-size: 16px;
  1738.     }
  1739.     
  1740.     .social-icon i {
  1741.         font-size: 16px;
  1742.     }
  1743. }
  1744. </style>
  1745. <script>
  1746. // Debounced autocomplete for header search
  1747. (function(){
  1748.   const input = document.getElementById('search_input');
  1749.   const box = document.getElementById('search_suggestions');
  1750.   if (!input || !box) return;
  1751.   let t = null;
  1752.   input.addEventListener('input', function(){
  1753.     const q = input.value.trim();
  1754.     clearTimeout(t);
  1755.     if (q.length === 0) { box.style.display = 'none'; box.innerHTML=''; return; }
  1756.     t = setTimeout(() => fetchSuggestions(q), 200);
  1757.   });
  1758.   function fetchSuggestions(q){
  1759.     const url = '{{ path('ui_api_search_suggest') }}' + '?q=' + encodeURIComponent(q);
  1760.     fetch(url).then(r => r.json()).then(data => {
  1761.       if (!data.success) { box.style.display='none'; return; }
  1762.       renderSuggestions(data.results || []);
  1763.     }).catch(() => { box.style.display='none'; });
  1764.   }
  1765.   function renderSuggestions(items){
  1766.     if (!items.length) { box.style.display='none'; box.innerHTML=''; return; }
  1767.     box.innerHTML = items.map(it => suggestionItem(it)).join('');
  1768.     box.style.display = 'block';
  1769.   }
  1770.   function suggestionItem(it){
  1771.     const icon = it.type === 'product' ? 'ti-package' : (it.type === 'shop' ? 'ti-briefcase' : (it.type === 'category' ? 'ti-layers' : 'ti-tag'));
  1772.     return '<a href="' + it.url + '" class="list-group-item list-group-item-action d-flex align-items-center">'
  1773.          + '<i class="' + icon + ' me-4"></i>'
  1774.          + '<span class="ml-2">' + escapeHtml(it.label) + '</span>'
  1775.          + '<span class="badge bg-light text-dark ms-auto">' + it.type + '</span>'
  1776.          + '</a>';
  1777.   }
  1778.   function escapeHtml(s){
  1779.     return s.replace(/[&<>"]+/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
  1780.   }
  1781.   document.addEventListener('click', function(e){
  1782.     if (!box.contains(e.target) && e.target !== input) {
  1783.       box.style.display='none';
  1784.     }
  1785.   });
  1786.   window.handleSearchSubmit = function(e){
  1787.     if (e) e.preventDefault();
  1788.     const first = box.querySelector('a');
  1789.     if (first) { window.location.href = first.getAttribute('href'); return false; }
  1790.     // fallback to listing page search by name
  1791.     const q = input.value.trim();
  1792.     if (q.length) {
  1793.       window.location.href = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
  1794.     }
  1795.     return false;
  1796.   }
  1797. })();
  1798. // Gestion des mega-menus avec clic (comme la barre de recherche)
  1799. document.addEventListener('DOMContentLoaded', function() {
  1800.     const dropdownItems = document.querySelectorAll('.nav-item.dropdown');
  1801.     
  1802.     dropdownItems.forEach(function(item) {
  1803.         const dropdownToggle = item.querySelector('.dropdown-toggle');
  1804.         const dropdownMenu = item.querySelector('.mega-menu');
  1805.         
  1806.         if (!dropdownToggle || !dropdownMenu) return;
  1807.         
  1808.         // Fermer les autres menus quand on ouvre un nouveau
  1809.         function closeAllMenus() {
  1810.             dropdownItems.forEach(function(otherItem) {
  1811.                 const otherMenu = otherItem.querySelector('.mega-menu');
  1812.                 if (otherMenu && otherMenu !== dropdownMenu) {
  1813.                     $(otherMenu).slideUp(300);
  1814.                     otherMenu.classList.remove('active');
  1815.                 }
  1816.             });
  1817.         }
  1818.         
  1819.         // Toggle au clic (comme la barre de recherche)
  1820.         dropdownToggle.addEventListener('click', function(e) {
  1821.             e.preventDefault();
  1822.             e.stopPropagation();
  1823.             
  1824.             closeAllMenus();
  1825.             
  1826.             if (dropdownMenu.classList.contains('active')) {
  1827.                 // Fermer le menu
  1828.                 dropdownMenu.style.opacity = '0';
  1829.                 dropdownMenu.style.visibility = 'hidden';
  1830.                 dropdownMenu.style.transform = 'translateX(-50%) translateY(-10px)';
  1831.                 setTimeout(() => {
  1832.                     dropdownMenu.classList.remove('active');
  1833.                     dropdownMenu.style.display = 'none';
  1834.                 }, 300);
  1835.             } else {
  1836.                 // Ouvrir le menu
  1837.                 dropdownMenu.style.display = 'block';
  1838.                 setTimeout(() => {
  1839.                     dropdownMenu.classList.add('active');
  1840.             dropdownMenu.style.opacity = '1';
  1841.             dropdownMenu.style.visibility = 'visible';
  1842.                     dropdownMenu.style.transform = 'translateX(-50%) translateY(0)';
  1843.                 }, 10);
  1844.             }
  1845.         });
  1846.         
  1847.         // Fermer au clic en dehors
  1848.         document.addEventListener('click', function(e) {
  1849.             if (!item.contains(e.target) && dropdownMenu.classList.contains('active')) {
  1850.                 dropdownMenu.style.opacity = '0';
  1851.                 dropdownMenu.style.visibility = 'hidden';
  1852.                 dropdownMenu.style.transform = 'translateX(-50%) translateY(-10px)';
  1853.                 setTimeout(() => {
  1854.                     dropdownMenu.classList.remove('active');
  1855.                     dropdownMenu.style.display = 'none';
  1856.                 }, 300);
  1857.             }
  1858.         });
  1859.         
  1860.         // Animation des éléments du menu
  1861.         const menuItems = dropdownMenu.querySelectorAll('.dropdown-item a');
  1862.         menuItems.forEach(function(menuItem, index) {
  1863.             menuItem.style.animationDelay = (index * 0.05) + 's';
  1864.         });
  1865.     });
  1866.     
  1867.     // Gestion des clics sur mobile
  1868.     if (window.innerWidth <= 768) {
  1869.         dropdownItems.forEach(function(item) {
  1870.             const dropdownToggle = item.querySelector('.dropdown-toggle');
  1871.             const dropdownMenu = item.querySelector('.mega-menu');
  1872.             
  1873.             dropdownToggle.addEventListener('click', function(e) {
  1874.                 e.preventDefault();
  1875.                 
  1876.                 // Fermer tous les autres menus
  1877.                 dropdownItems.forEach(function(otherItem) {
  1878.                     if (otherItem !== item) {
  1879.                         const otherMenu = otherItem.querySelector('.mega-menu');
  1880.                         otherMenu.style.opacity = '0';
  1881.                         otherMenu.style.visibility = 'hidden';
  1882.                         otherMenu.style.transform = 'translateY(-10px)';
  1883.                     }
  1884.                 });
  1885.                 
  1886.                 // Toggle le menu actuel
  1887.                 if (dropdownMenu.style.opacity === '1') {
  1888.                     dropdownMenu.style.opacity = '0';
  1889.                     dropdownMenu.style.visibility = 'hidden';
  1890.                     dropdownMenu.style.transform = 'translateY(-10px)';
  1891.                 } else {
  1892.                     dropdownMenu.style.opacity = '1';
  1893.                     dropdownMenu.style.visibility = 'visible';
  1894.                     dropdownMenu.style.transform = 'translateY(0)';
  1895.                 }
  1896.             });
  1897.         });
  1898.     }
  1899.     
  1900.     // Fermer les menus quand on clique ailleurs
  1901.     document.addEventListener('click', function(e) {
  1902.         if (!e.target.closest('.nav-item.dropdown')) {
  1903.             dropdownItems.forEach(function(item) {
  1904.                 const dropdownMenu = item.querySelector('.mega-menu');
  1905.                 dropdownMenu.style.opacity = '0';
  1906.                 dropdownMenu.style.visibility = 'hidden';
  1907.                 dropdownMenu.style.transform = 'translateY(-10px)';
  1908.             });
  1909.         }
  1910.     });
  1911. });
  1912. </script>
  1913. <!-- End footer Area -->
  1914. <script src="{{ asset('ui/js/vendor/jquery-2.2.4.min.js') }}"></script>
  1915. <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
  1916. <script>
  1917.     // Initialiser le collapse Bootstrap 5 pour le menu hamburger après chargement de Bootstrap
  1918.     document.addEventListener('DOMContentLoaded', function() {
  1919.         // Attendre que Bootstrap soit complètement chargé
  1920.         if (typeof bootstrap !== 'undefined') {
  1921.             const navbarToggler = document.querySelector('.navbar-toggler');
  1922.             const navbarCollapse = document.getElementById('navbarSupportedContent');
  1923.             
  1924.             if (navbarToggler && navbarCollapse) {
  1925.                 // Bootstrap 5 devrait gérer automatiquement avec data-bs-toggle
  1926.                 // Mais on s'assure que ça fonctionne en écoutant les événements
  1927.                 navbarCollapse.addEventListener('show.bs.collapse', function() {
  1928.                     navbarToggler.setAttribute('aria-expanded', 'true');
  1929.                     navbarToggler.classList.add('active');
  1930.                 });
  1931.                 
  1932.                 navbarCollapse.addEventListener('hide.bs.collapse', function() {
  1933.                     navbarToggler.setAttribute('aria-expanded', 'false');
  1934.                     navbarToggler.classList.remove('active');
  1935.                 });
  1936.             }
  1937.         }
  1938.     });
  1939. </script>
  1940. <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
  1941. <script src="{{ asset('ui/js/jquery.ajaxchimp.min.js') }}"></script>
  1942. <script src="{{ asset('ui/js/jquery.nice-select.min.js') }}"></script>
  1943. <script src="{{ asset('ui/js/jquery.sticky.js') }}"></script>
  1944. <script src="{{ asset('ui/js/nouislider.min.js') }}"></script>
  1945. <script src="{{ asset('ui/js/countdown.js') }}"></script>
  1946. <script src="{{ asset('ui/js/jquery.magnific-popup.min.js') }}"></script>
  1947. <script src="{{ asset('ui/js/owl.carousel.min.js') }}"></script>
  1948. <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCjCGmQ0Uq4exrzdcL6rvxywDDOvfAu6eE"></script>
  1949. <script src="{{ asset('ui/js/gmaps.min.js') }}"></script>
  1950. <script src="{{ asset('ui/js/main.js') }}"></script>
  1951. <script>
  1952.     window.maketouConfig = {
  1953.         themeStorageKey: 'maketou-theme',
  1954.         comparison: {
  1955.             assetBase: '{{ asset('')|e('js') }}',
  1956.             addUrlTemplate: '{{ path('ui_api_comparison_add', {'id': '__ID__'})|e('js') }}',
  1957.             removeUrlTemplate: '{{ path('ui_api_comparison_remove', {'id': '__ID__'})|e('js') }}',
  1958.             clearUrl: '{{ path('ui_api_comparison_clear')|e('js') }}',
  1959.             countUrl: '{{ path('ui_api_comparison_count')|e('js') }}',
  1960.             dataUrl: '{{ path('ui_api_comparison_data')|e('js') }}',
  1961.             pageUrl: '{{ path('ui_product_comparison')|e('js') }}'
  1962.         }
  1963.     };
  1964. </script>
  1965. <script src="{{ asset('ui/js/theme-public.js') }}"></script>
  1966. <script src="{{ asset('ui/js/comparison-v2.js') }}"></script>
  1967.     {{ encore_entry_script_tags('app') }}
  1968.     
  1969.     <!-- PWA Service Worker Registration -->
  1970.     <script>
  1971.         // Enregistrement du Service Worker
  1972.         if ('serviceWorker' in navigator) {
  1973.             window.addEventListener('load', () => {
  1974.                 navigator.serviceWorker.register('{{ asset('sw.js') }}')
  1975.                     .then((registration) => {
  1976.                         console.log('Service Worker enregistré avec succès:', registration.scope);
  1977.                         
  1978.                         // Vérifie les mises à jour du service worker
  1979.                         registration.addEventListener('updatefound', () => {
  1980.                             const newWorker = registration.installing;
  1981.                             newWorker.addEventListener('statechange', () => {
  1982.                                 if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
  1983.                                     // Nouveau service worker disponible
  1984.                                     if (confirm('Une nouvelle version de MaketOu est disponible. Voulez-vous l\'installer maintenant ?')) {
  1985.                                         newWorker.postMessage({ type: 'SKIP_WAITING' });
  1986.                                         window.location.reload();
  1987.                                     }
  1988.                                 }
  1989.                             });
  1990.                         });
  1991.                     })
  1992.                     .catch((error) => {
  1993.                         console.log('Échec de l\'enregistrement du Service Worker:', error);
  1994.                     });
  1995.                 
  1996.                 // Écoute les messages du service worker
  1997.                 navigator.serviceWorker.addEventListener('message', (event) => {
  1998.                     console.log('Message du Service Worker:', event.data);
  1999.                 });
  2000.             });
  2001.         }
  2002.     </script>
  2003.     
  2004.     <!-- PWA Install Script -->
  2005.     <script src="{{ asset('js/pwa-install.js') }}"></script>
  2006.     
  2007.     <script>
  2008.     // Charger les compteurs au chargement de la page
  2009.     document.addEventListener('DOMContentLoaded', function() {
  2010.         {% if app.user %}
  2011.             loadNotificationCount();
  2012.             // Délai pour s'assurer que la session est bien établie après connexion
  2013.             setTimeout(function() {
  2014.                 loadComparisonCount();
  2015.             }, 500);
  2016.         {% endif %}
  2017.         
  2018.         // Gérer le bouton de recherche mobile
  2019.         const searchMobileBtn = document.getElementById('searchMobileBtn');
  2020.         if (searchMobileBtn) {
  2021.             searchMobileBtn.addEventListener('click', function(e) {
  2022.                 e.preventDefault();
  2023.                 // Utiliser le même comportement que le bouton desktop
  2024.                 const searchBox = document.getElementById('search_input_box');
  2025.                 if (searchBox) {
  2026.                     searchBox.style.display = 'block';
  2027.                     const searchInput = document.getElementById('search_input');
  2028.                     if (searchInput) {
  2029.                         setTimeout(() => searchInput.focus(), 100);
  2030.                     }
  2031.                 }
  2032.             });
  2033.         }
  2034.     });
  2035.     
  2036.     function loadNotificationCount() {
  2037.         {% if app.user %}
  2038.         fetch('{{ path("api_notifications_count") }}')
  2039.             .then(response => {
  2040.                 if (!response.ok) {
  2041.                     throw new Error('Erreur HTTP: ' + response.status);
  2042.                 }
  2043.                 return response.json();
  2044.             })
  2045.             .then(data => {
  2046.                 const count = data.count || 0;
  2047.                 
  2048.                 // Mettre à jour tous les badges de notification
  2049.                 const badges = [
  2050.                     document.getElementById('notificationBadge'),
  2051.                     document.getElementById('notificationBadgeMobile'),
  2052.                     document.querySelector('.notification-badge')
  2053.                 ].filter(badge => badge !== null);
  2054.                 
  2055.                 badges.forEach(badge => {
  2056.                     if (count > 0) {
  2057.                         badge.textContent = count;
  2058.                         badge.style.display = 'flex';
  2059.                         badge.style.color = 'white';
  2060.                         badge.style.backgroundColor = '#dc3545';
  2061.                     } else {
  2062.                         badge.style.display = 'none';
  2063.                     }
  2064.                 });
  2065.                 
  2066.                 // Si on est sur la page des notifications, mettre à jour aussi le contenu de la page
  2067.                 if (typeof updateNotificationPageContent === 'function') {
  2068.                     updateNotificationPageContent(count);
  2069.                 }
  2070.             })
  2071.             .catch(error => {
  2072.                 console.error('Erreur lors du chargement du compteur de notifications:', error);
  2073.             });
  2074.         {% endif %}
  2075.     }
  2076.     
  2077.     // Exposer la fonction globalement pour qu'elle puisse être appelée depuis d'autres pages
  2078.     window.loadNotificationCount = loadNotificationCount;
  2079.     
  2080.     // Recharger le compteur toutes les 30 secondes pour garder les données à jour
  2081.     {% if app.user %}
  2082.     setInterval(function() {
  2083.         loadNotificationCount();
  2084.     }, 30000);
  2085.     {% endif %}
  2086.     
  2087.     function loadComparisonCount() {
  2088.         // Ne pas appeler l'API si on est sur une page d'erreur ou de redirection
  2089.         if (window.location.pathname.includes('/api/') || window.location.pathname.includes('/login')) {
  2090.             return;
  2091.         }
  2092.         
  2093.         // Utiliser un try-catch pour éviter toute redirection
  2094.         try {
  2095.             fetch('{{ path("ui_api_comparison_count") }}', {
  2096.                 method: 'GET',
  2097.                 headers: {
  2098.                     'X-Requested-With': 'XMLHttpRequest',
  2099.                     'Accept': 'application/json'
  2100.                 },
  2101.                 credentials: 'same-origin',
  2102.                 redirect: 'error' // Empêcher les redirections automatiques
  2103.             })
  2104.             .then(response => {
  2105.                 // Vérifier si la réponse est OK
  2106.                 if (!response.ok) {
  2107.                     // Si c'est une redirection (301, 302, etc.) ou une erreur d'authentification, ignorer silencieusement
  2108.                     if (response.status >= 300 && response.status < 400) {
  2109.                         console.warn('Redirection détectée, arrêt de la requête');
  2110.                         return null;
  2111.                     }
  2112.                     if (response.status === 401 || response.status === 403) {
  2113.                         console.warn('Utilisateur non authentifié pour la comparaison');
  2114.                         return null;
  2115.                     }
  2116.                     // Pour les autres erreurs, throw pour être capturé par catch
  2117.                     throw new Error('Erreur HTTP: ' + response.status);
  2118.                 }
  2119.                 
  2120.                 // Vérifier que c'est bien du JSON
  2121.                 const contentType = response.headers.get('content-type');
  2122.                 if (!contentType || !contentType.includes('application/json')) {
  2123.                     // Si ce n'est pas du JSON, c'est probablement une redirection HTML
  2124.                     console.warn('La réponse n\'est pas du JSON, probable redirection');
  2125.                     return null;
  2126.                 }
  2127.                 
  2128.                 return response.json();
  2129.             })
  2130.             .then(data => {
  2131.                 // Si data est null (redirection ou erreur), ne rien faire
  2132.                 if (!data || typeof data.count === 'undefined') {
  2133.                     return;
  2134.                 }
  2135.                 
  2136.                 // Mettre à jour les badges desktop et mobile
  2137.                 const badges = [
  2138.                     document.getElementById('comparisonBadge'),
  2139.                     document.getElementById('comparisonBadgeMobile')
  2140.                 ];
  2141.                 badges.forEach(badge => {
  2142.                     if (badge) {
  2143.                         if (data.count > 0) {
  2144.                             badge.textContent = data.count;
  2145.                             badge.style.display = 'flex';
  2146.                         } else {
  2147.                             badge.style.display = 'none';
  2148.                         }
  2149.                     }
  2150.                 });
  2151.             })
  2152.             .catch(error => {
  2153.                 // Ne pas afficher d'erreur si c'est juste une absence d'authentification ou une redirection
  2154.                 if (error.message && !error.message.includes('401') && !error.message.includes('403') && !error.message.includes('redirect')) {
  2155.                     console.error('Erreur lors du chargement du compteur de comparaison:', error);
  2156.                 }
  2157.                 // Empêcher toute redirection vers l'URL de l'API
  2158.                 return false;
  2159.             });
  2160.         } catch (e) {
  2161.             // Capturer toute erreur qui pourrait causer une redirection
  2162.             console.warn('Erreur lors de l\'appel à l\'API de comparaison:', e);
  2163.         }
  2164.     }
  2165.     
  2166.     // Fonction globale pour gérer la comparaison
  2167.     function toggleComparison(productId, element) {
  2168.         if (!element) {
  2169.             element = document.querySelector(`.comparison-btn[data-product-id="${productId}"]`);
  2170.         }
  2171.         
  2172.         if (!element) {
  2173.             console.error('Bouton de comparaison non trouvé pour le produit', productId);
  2174.             return;
  2175.         }
  2176.         
  2177.         const originalHtml = element.innerHTML;
  2178.         // Désactiver le bouton/lien
  2179.         if (element.tagName === 'BUTTON') {
  2180.             element.disabled = true;
  2181.         } else {
  2182.             element.style.pointerEvents = 'none';
  2183.             element.style.opacity = '0.6';
  2184.         }
  2185.         element.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
  2186.         
  2187.         // Vérifier d'abord si le produit est déjà en comparaison
  2188.         let isInComparison = false;
  2189.         fetch(`/api/comparison/status/${productId}`)
  2190.             .then(response => {
  2191.                 const contentType = response.headers.get('content-type');
  2192.                 if (contentType && contentType.includes('text/html')) {
  2193.                     window.location.href = '{{ path("ui_app_login") }}';
  2194.                     throw new Error('Not authenticated');
  2195.                 }
  2196.                 return response.json();
  2197.             })
  2198.             .then(data => {
  2199.                 isInComparison = data.inComparison;
  2200.                 const url = isInComparison ? `/api/comparison/remove/${productId}` : `/api/comparison/add/${productId}`;
  2201.                 
  2202.                 return fetch(url, {
  2203.                     method: 'POST',
  2204.                     headers: {
  2205.                         'Content-Type': 'application/json',
  2206.                         'X-Requested-With': 'XMLHttpRequest'
  2207.                     }
  2208.                 });
  2209.             })
  2210.             .then(response => {
  2211.                 const contentType = response.headers.get('content-type');
  2212.                 if (contentType && contentType.includes('text/html')) {
  2213.                     window.location.href = '{{ path("ui_app_login") }}';
  2214.                     throw new Error('Not authenticated');
  2215.                 }
  2216.                 return response.json();
  2217.             })
  2218.             .then(data => {
  2219.                 if (data.success) {
  2220.                     // Mettre à jour le compteur
  2221.                     loadComparisonCount();
  2222.                     
  2223.                     // Mettre à jour l'apparence du bouton
  2224.                     if (element) {
  2225.                         // Inverser l'état (car on vient de le changer)
  2226.                         const newState = !isInComparison;
  2227.                         
  2228.                         // Restaurer le HTML original puis mettre à jour
  2229.                         element.innerHTML = originalHtml;
  2230.                         
  2231.                         if (newState) {
  2232.                             element.classList.add('active');
  2233.                             // Mettre à jour le texte pour les deux types de boutons
  2234.                             const textEl = element.querySelector('.comparison-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
  2235.                             if (textEl) textEl.textContent = 'En comparaison';
  2236.                             // Mettre à jour l'icône si nécessaire (chercher dans span ou i)
  2237.                             const iconEl = element.querySelector('.lnr-sync, span.lnr-sync, [class*="sync"], i[class*="sync"]');
  2238.                             if (iconEl) {
  2239.                                 iconEl.style.color = '#28a745';
  2240.                                 iconEl.classList.add('active');
  2241.                             }
  2242.                         } else {
  2243.                             element.classList.remove('active');
  2244.                             const textEl = element.querySelector('.comparison-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
  2245.                             if (textEl) textEl.textContent = 'Comparer';
  2246.                             // Restaurer la couleur de l'icône
  2247.                             const iconEl = element.querySelector('.lnr-sync, span.lnr-sync, [class*="sync"], i[class*="sync"]');
  2248.                             if (iconEl) {
  2249.                                 iconEl.style.color = '';
  2250.                                 iconEl.classList.remove('active');
  2251.                             }
  2252.                         }
  2253.                     }
  2254.                     
  2255.                     // Afficher une notification
  2256.                     showNotification(data.message, 'success');
  2257.                 } else {
  2258.                     showNotification(data.message, 'error');
  2259.                     if (element) {
  2260.                         element.innerHTML = originalHtml;
  2261.                     }
  2262.                 }
  2263.             })
  2264.             .catch(error => {
  2265.                 console.error('Erreur:', error);
  2266.                 showNotification('Une erreur est survenue', 'error');
  2267.                 if (element) {
  2268.                     element.innerHTML = originalHtml;
  2269.                 }
  2270.             })
  2271.             .finally(() => {
  2272.                 if (element) {
  2273.                     if (element.tagName === 'BUTTON') {
  2274.                         element.disabled = false;
  2275.                     } else {
  2276.                         element.style.pointerEvents = '';
  2277.                         element.style.opacity = '';
  2278.                     }
  2279.                 }
  2280.             });
  2281.     }
  2282.     
  2283.     // Fonction globale pour gérer les favoris
  2284.     function toggleWishlist(productId, element) {
  2285.         if (!element) {
  2286.             element = document.querySelector(`.wishlist-btn[data-product-id="${productId}"]`);
  2287.         }
  2288.         
  2289.         if (!element) {
  2290.             console.error('Bouton de wishlist non trouvé pour le produit', productId);
  2291.             return;
  2292.         }
  2293.         
  2294.         const originalHtml = element.innerHTML;
  2295.         // Désactiver le bouton/lien
  2296.         if (element.tagName === 'BUTTON') {
  2297.             element.disabled = true;
  2298.         } else {
  2299.             element.style.pointerEvents = 'none';
  2300.             element.style.opacity = '0.6';
  2301.         }
  2302.         element.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
  2303.         
  2304.         // Vérifier d'abord si le produit est déjà en favoris
  2305.         let isInWishlist = false;
  2306.         fetch(`/api/wishlist/status/${productId}`)
  2307.             .then(response => {
  2308.                 const contentType = response.headers.get('content-type');
  2309.                 if (contentType && contentType.includes('text/html')) {
  2310.                     window.location.href = '{{ path("ui_app_login") }}';
  2311.                     throw new Error('Not authenticated');
  2312.                 }
  2313.                 return response.json();
  2314.             })
  2315.             .then(data => {
  2316.                 isInWishlist = data.inWishlist;
  2317.                 const url = isInWishlist ? `/api/wishlist/remove/${productId}` : `/api/wishlist/add/${productId}`;
  2318.                 
  2319.                 return fetch(url, {
  2320.                     method: 'POST',
  2321.                     headers: {
  2322.                         'Content-Type': 'application/json',
  2323.                         'X-Requested-With': 'XMLHttpRequest'
  2324.                     }
  2325.                 });
  2326.             })
  2327.             .then(response => {
  2328.                 const contentType = response.headers.get('content-type');
  2329.                 if (contentType && contentType.includes('text/html')) {
  2330.                     window.location.href = '{{ path("ui_app_login") }}';
  2331.                     throw new Error('Not authenticated');
  2332.                 }
  2333.                 return response.json();
  2334.             })
  2335.             .then(data => {
  2336.                 if (data.success) {
  2337.                     // Mettre à jour l'apparence du bouton
  2338.                     if (element) {
  2339.                         // Inverser l'état (car on vient de le changer)
  2340.                         const newState = !isInWishlist;
  2341.                         
  2342.                         // Restaurer le HTML original puis mettre à jour
  2343.                         element.innerHTML = originalHtml;
  2344.                         
  2345.                         // Chercher l'icône dans span ou i
  2346.                         const icon = element.querySelector('span.lnr-heart, i.lnr-heart, [class*="lnr-heart"]');
  2347.                         const text = element.querySelector('.wishlist-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
  2348.                         
  2349.                         if (newState) {
  2350.                             if (icon) {
  2351.                                 // Pour les icônes linr, on change la classe
  2352.                                 if (icon.classList.contains('lnr-heart')) {
  2353.                                     icon.classList.remove('lnr-heart');
  2354.                                 }
  2355.                                 if (!icon.classList.contains('lnr-heart-filled')) {
  2356.                                     icon.classList.add('lnr-heart-filled');
  2357.                                 }
  2358.                                 icon.style.color = '#dc3545';
  2359.                             }
  2360.                             element.classList.add('active');
  2361.                             if (text) text.textContent = 'Dans favoris';
  2362.                         } else {
  2363.                             if (icon) {
  2364.                                 // Restaurer l'icône originale
  2365.                                 if (icon.classList.contains('lnr-heart-filled')) {
  2366.                                     icon.classList.remove('lnr-heart-filled');
  2367.                                 }
  2368.                                 if (!icon.classList.contains('lnr-heart')) {
  2369.                                     icon.classList.add('lnr-heart');
  2370.                                 }
  2371.                                 icon.style.color = '';
  2372.                             }
  2373.                             element.classList.remove('active');
  2374.                             if (text) text.textContent = 'Favoris';
  2375.                         }
  2376.                     }
  2377.                     
  2378.                     // Afficher une notification
  2379.                     showNotification(data.message, 'success');
  2380.                     
  2381.                     // Si on est sur la page wishlist, recharger la page
  2382.                     if (window.location.pathname.includes('/account/wishlist')) {
  2383.                         setTimeout(() => location.reload(), 1000);
  2384.                     }
  2385.                 } else {
  2386.                     showNotification(data.message, 'error');
  2387.                     if (element) {
  2388.                         element.innerHTML = originalHtml;
  2389.                     }
  2390.                 }
  2391.             })
  2392.             .catch(error => {
  2393.                 console.error('Erreur:', error);
  2394.                 showNotification('Une erreur est survenue', 'error');
  2395.                 if (element) {
  2396.                     element.innerHTML = originalHtml;
  2397.                 }
  2398.             })
  2399.             .finally(() => {
  2400.                 if (element) {
  2401.                     if (element.tagName === 'BUTTON') {
  2402.                         element.disabled = false;
  2403.                     } else {
  2404.                         element.style.pointerEvents = '';
  2405.                         element.style.opacity = '';
  2406.                     }
  2407.                 }
  2408.             });
  2409.     }
  2410.     
  2411.     // Fonction pour afficher des notifications
  2412.     function showNotification(message, type = 'success') {
  2413.         // Créer l'élément de notification
  2414.         const notification = document.createElement('div');
  2415.         notification.className = `alert alert-${type === 'success' ? 'success' : 'danger'} alert-dismissible fade show position-fixed`;
  2416.         notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);';
  2417.         notification.innerHTML = `
  2418.             ${message}
  2419.             <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
  2420.         `;
  2421.         
  2422.         document.body.appendChild(notification);
  2423.         
  2424.         // Supprimer automatiquement après 3 secondes
  2425.         setTimeout(() => {
  2426.             notification.remove();
  2427.         }, 3000);
  2428.     }
  2429.     
  2430.     // Initialiser les alertes Bootstrap 5 pour la fermeture
  2431.     document.addEventListener('DOMContentLoaded', function() {
  2432.         // S'assurer que les alertes peuvent être fermées
  2433.         const alertCloseButtons = document.querySelectorAll('.flash-message .btn-close');
  2434.         alertCloseButtons.forEach(function(button) {
  2435.             button.addEventListener('click', function() {
  2436.                 const alert = this.closest('.alert');
  2437.                 if (alert) {
  2438.                     alert.classList.remove('show');
  2439.                     setTimeout(function() {
  2440.                         alert.remove();
  2441.                     }, 150);
  2442.                 }
  2443.             });
  2444.         });
  2445.         
  2446.         // Charger les statuts initiaux des produits
  2447.         {% if app.user %}
  2448.         // Charger les statuts de comparaison pour tous les produits visibles
  2449.         document.querySelectorAll('.comparison-btn[data-product-id]').forEach(btn => {
  2450.             const productId = btn.dataset.productId;
  2451.             fetch(`/api/comparison/status/${productId}`)
  2452.                 .then(response => response.json())
  2453.                 .then(data => {
  2454.                     if (data.inComparison) {
  2455.                         btn.classList.add('active');
  2456.                         // Mettre à jour le texte pour les deux types de boutons
  2457.                         const textEl = btn.querySelector('.comparison-text') || btn.querySelector('.hover-text') || btn.querySelector('p.hover-text');
  2458.                         if (textEl) textEl.textContent = 'En comparaison';
  2459.                         // Mettre à jour l'icône si nécessaire
  2460.                         const iconEl = btn.querySelector('.lnr-sync, [class*="sync"]');
  2461.                         if (iconEl) iconEl.style.color = '#28a745';
  2462.                     }
  2463.                 })
  2464.                 .catch(() => {});
  2465.         });
  2466.         
  2467.         // Charger les statuts de wishlist pour tous les produits visibles
  2468.         document.querySelectorAll('.wishlist-btn[data-product-id]').forEach(btn => {
  2469.             const productId = btn.dataset.productId;
  2470.             fetch(`/api/wishlist/status/${productId}`)
  2471.                 .then(response => response.json())
  2472.                 .then(data => {
  2473.                     if (data.inWishlist) {
  2474.                         btn.classList.add('active');
  2475.                         // Chercher l'icône dans span ou i
  2476.                         const icon = btn.querySelector('span.lnr-heart, i.lnr-heart, [class*="lnr-heart"]');
  2477.                         if (icon) {
  2478.                             icon.classList.remove('lnr-heart');
  2479.                             icon.classList.add('lnr-heart-filled');
  2480.                             icon.style.color = '#dc3545';
  2481.                         }
  2482.                         // Mettre à jour le texte pour les deux types de boutons
  2483.                         const textEl = btn.querySelector('.wishlist-text') || btn.querySelector('.hover-text') || btn.querySelector('p.hover-text');
  2484.                         if (textEl) textEl.textContent = 'Dans favoris';
  2485.                     }
  2486.                 })
  2487.                 .catch(() => {});
  2488.         });
  2489.         
  2490.         // Charger le compteur de comparaison
  2491.         loadComparisonCount();
  2492.         {% endif %}
  2493.     });
  2494.     </script>
  2495. <script>
  2496. // Loader global pour tous les boutons de formulaire
  2497. document.addEventListener('DOMContentLoaded', function() {
  2498.     // Intercepter tous les formulaires
  2499.     const forms = document.querySelectorAll('form');
  2500.     
  2501.     forms.forEach(function(form) {
  2502.         form.addEventListener('submit', function(e) {
  2503.             const submitButtons = form.querySelectorAll('button[type="submit"], input[type="submit"], button:not([type])');
  2504.             
  2505.             submitButtons.forEach(function(button) {
  2506.                 // Vérifier si le bouton n'est pas déjà désactivé
  2507.                 if (!button.disabled && !button.classList.contains('loading')) {
  2508.                     button.classList.add('loading');
  2509.                     button.disabled = true;
  2510.                     
  2511.                     // Sauvegarder le texte original
  2512.                     const originalText = button.innerHTML;
  2513.                     button.setAttribute('data-original-text', originalText);
  2514.                     
  2515.                     // Ajouter le loader
  2516.                     button.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Envoi...';
  2517.                     
  2518.                     // Si le formulaire n'est pas valide, réactiver le bouton après 1 seconde
  2519.                     setTimeout(function() {
  2520.                         if (!form.checkValidity()) {
  2521.                             button.classList.remove('loading');
  2522.                             button.disabled = false;
  2523.                             button.innerHTML = originalText;
  2524.                         }
  2525.                     }, 1000);
  2526.                 }
  2527.             });
  2528.         });
  2529.     });
  2530.     
  2531.     // Pour les boutons qui déclenchent des actions AJAX
  2532.     const ajaxButtons = document.querySelectorAll('[data-ajax], .ajax-submit, button[data-submit]');
  2533.     
  2534.     ajaxButtons.forEach(function(button) {
  2535.         button.addEventListener('click', function(e) {
  2536.             if (this.disabled || this.classList.contains('loading')) {
  2537.                 e.preventDefault();
  2538.                 return false;
  2539.             }
  2540.             
  2541.             this.classList.add('loading');
  2542.             this.disabled = true;
  2543.             
  2544.             const originalText = this.innerHTML;
  2545.             this.setAttribute('data-original-text', originalText);
  2546.             
  2547.             this.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Chargement...';
  2548.         });
  2549.     });
  2550.     
  2551.     // Gestionnaire spécifique pour le formulaire de newsletter
  2552.     const newsletterForm = document.querySelector('.footer-newsletter-form');
  2553.     if (newsletterForm) {
  2554.         newsletterForm.addEventListener('submit', function(e) {
  2555.             const submitBtn = this.querySelector('.newsletter-btn');
  2556.             if (submitBtn && !submitBtn.disabled) {
  2557.                 submitBtn.classList.add('loading');
  2558.                 submitBtn.disabled = true;
  2559.                 
  2560.                 // Afficher le loader et masquer l'icône
  2561.                 const icon = submitBtn.querySelector('.newsletter-btn-icon');
  2562.                 const loader = submitBtn.querySelector('.newsletter-btn-loader');
  2563.                 if (icon) icon.style.display = 'none';
  2564.                 if (loader) loader.style.display = 'flex';
  2565.                 
  2566.                 // Réactiver le bouton après 5 secondes au cas où la requête échoue silencieusement
  2567.                 setTimeout(function() {
  2568.                     if (submitBtn.classList.contains('loading')) {
  2569.                         submitBtn.classList.remove('loading');
  2570.                         submitBtn.disabled = false;
  2571.                         if (icon) icon.style.display = 'flex';
  2572.                         if (loader) loader.style.display = 'none';
  2573.                     }
  2574.                 }, 5000);
  2575.             }
  2576.         });
  2577.         
  2578.         // Gérer la réponse du formulaire (si AJAX)
  2579.         newsletterForm.addEventListener('submit', function(e) {
  2580.             // Si le formulaire est soumis normalement (non-AJAX), le navigateur gère la réponse
  2581.             // Sinon, on peut intercepter pour gérer la réponse AJAX ici
  2582.         });
  2583.     }
  2584. });
  2585. // CSS pour le spinner
  2586. const style = document.createElement('style');
  2587. style.textContent = `
  2588.     .spinner-border-sm {
  2589.         width: 1rem;
  2590.         height: 1rem;
  2591.         border-width: 0.15em;
  2592.     }
  2593.     .spinner-border {
  2594.         display: inline-block;
  2595.         width: 1rem;
  2596.         height: 1rem;
  2597.         vertical-align: text-bottom;
  2598.         border: 0.15em solid currentColor;
  2599.         border-right-color: transparent;
  2600.         border-radius: 50%;
  2601.         animation: spinner-border 0.75s linear infinite;
  2602.     }
  2603.     @keyframes spinner-border {
  2604.         to { transform: rotate(360deg); }
  2605.     }
  2606.     button.loading {
  2607.         opacity: 0.7;
  2608.         cursor: not-allowed !important;
  2609.     }
  2610. `;
  2611. document.head.appendChild(style);
  2612. </script>
  2613.     <!-- Modal Global Custom Alert -->
  2614.     <div class="modal fade custom-alert-modal" id="customAlertModal" tabindex="-1" aria-labelledby="customAlertModalLabel" aria-hidden="true">
  2615.         <div class="modal-dialog modal-dialog-centered">
  2616.             <div class="modal-content">
  2617.                 <div class="modal-header">
  2618.                     <h5 class="modal-title text-white" id="customAlertModalLabel">Notification</h5>
  2619.                     <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
  2620.                 </div>
  2621.                 <div class="modal-body">
  2622.                     <div class="alert-icon" id="alertIcon">
  2623.                         <i class="lnr lnr-info-circle"></i>
  2624.                     </div>
  2625.                     <p class="alert-message" id="alertMessage"></p>
  2626.                 </div>
  2627.                 <div class="modal-footer">
  2628.                     <button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button>
  2629.                 </div>
  2630.             </div>
  2631.         </div>
  2632.     </div>
  2633.     <script>
  2634.         // Fonction globale pour remplacer alert() ou afficher des messages
  2635.         function showCustomAlert(message, type = 'info') {
  2636.             const modalEl = document.getElementById('customAlertModal');
  2637.             if (!modalEl) {
  2638.                 // Fallback sur showNotification
  2639.                 let notifyType = type === 'error' || type === 'danger' ? 'error' : (type === 'success' ? 'success' : 'warning');
  2640.                 if (typeof showNotification === 'function') {
  2641.                     showNotification(message, notifyType);
  2642.                 } else {
  2643.                     alert(message);
  2644.                 }
  2645.                 return;
  2646.             }
  2647.             
  2648.             const modal = new bootstrap.Modal(modalEl);
  2649.             const alertMessage = document.getElementById('alertMessage');
  2650.             const alertIcon = document.getElementById('alertIcon');
  2651.             const modalTitle = document.getElementById('customAlertModalLabel');
  2652.             alertMessage.textContent = message;
  2653.             // Définir l'icône et le titre selon le type
  2654.             let iconClass = 'lnr lnr-info-circle';
  2655.             let title = 'Information';
  2656.             switch (type) {
  2657.                 case 'success': 
  2658.                     iconClass = 'lnr lnr-checkmark-circle';
  2659.                     title = 'Succès';
  2660.                     alertIcon.className = 'alert-icon success';
  2661.                     break;
  2662.                 case 'error':
  2663.                 case 'danger': 
  2664.                     iconClass = 'lnr lnr-warning';
  2665.                     title = 'Erreur';
  2666.                     alertIcon.className = 'alert-icon error';
  2667.                     break;
  2668.                 case 'warning': 
  2669.                     iconClass = 'lnr lnr-warning';
  2670.                     title = 'Attention';
  2671.                     alertIcon.className = 'alert-icon warning';
  2672.                     break;
  2673.                 default: 
  2674.                     iconClass = 'lnr lnr-info-circle';
  2675.                     title = 'Information';
  2676.                     alertIcon.className = 'alert-icon info';
  2677.             }
  2678.             alertIcon.innerHTML = `<i class="${iconClass}"></i>`;
  2679.             modalTitle.textContent = title;
  2680.             modal.show();
  2681.         };
  2682.     </script>
  2683. {% block javascripts %}
  2684. {% endblock %}
  2685. </body>
  2686. </html>