templates/index.html.twig line 1

  1. {% extends 'base.html.twig' %}
  2. {% block title %}DAIP - Direction de l'Apprentissage et de l'Insertion Professionnelle{% endblock %}
  3. {% block meta %}
  4. {{ parent() }}
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
  6. <meta name="keywords" content="daip, recrutement, 4000 jeunes, formation professionnelle, apprentissage, cqp">
  7. <meta name="description" content="Recrutement de 4000 jeunes pour la formation au Certificat de Qualification Professionnelle (CQP). Inscriptions du 16 au 28 février 2026.">
  8. {% endblock %}
  9. {% block body %}
  10. <!-- ==================== HERO SECTION ==================== -->
  11. <section class="hero relative min-h-[600px] flex items-center justify-center bg-gradient-to-br from-indigo-700 via-purple-700 to-blue-700">
  12.     
  13.     <!-- Éléments décoratifs - FIXED: contain overflow -->
  14.     <div class="absolute inset-0 overflow-hidden pointer-events-none">
  15.         <div class="absolute top-0 left-0 w-64 h-64 bg-indigo-50 rounded-full filter blur-3xl opacity-5 animate-pulse-glow"></div>
  16.         <div class="absolute bottom-0 right-0 w-64 h-64 bg-purple-50 rounded-full filter blur-3xl opacity-5 animate-pulse-glow animation-delay-2000"></div>
  17.         
  18.         <!-- Animations de fond - FIXED: contained within inset-0 -->
  19.         <div class="absolute -top-40 -right-40 w-80 h-80 bg-purple-400 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob"></div>
  20.         <div class="absolute -bottom-40 -left-40 w-80 h-80 bg-indigo-400 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-2000"></div>
  21.         <div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-80 h-80 bg-pink-400 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-4000"></div>
  22.     </div>
  23.     
  24.     <div class="container mx-auto px-4 relative z-10 w-full max-w-full">
  25.         <div class="max-w-4xl mx-auto text-center">
  26.             <!-- Badge -->
  27.             <div class="inline-flex items-center rounded-full border px-4 py-1.5 text-sm font-semibold bg-white text-purple-400 border-white mb-6 animate-fade-up animation-delay-200">
  28.                 <span class="flex h-2 w-2 rounded-full bg-green-400 mr-2 animate-pulse"></span>
  29.                 RECRUTEMENT
  30.             </div>
  31.             
  32.             <!-- Titre - FIXED: text size for mobile -->
  33.             <h1 class="text-3xl sm:text-4xl md:text-6xl lg:text-7xl font-bold text-white mb-6 leading-tight animate-fade-up animation-delay-400 break-words">
  34.                 recrutement 4000 
  35.                 <span class="bg-gradient-to-r from-indigo-200 via-purple-200 to-pink-200 bg-clip-text text-transparent">
  36.                     jeunes pour formation au CQP
  37.                 </span>
  38.             </h1>
  39.             
  40.             <!-- Description -->
  41.             <p class="text-base sm:text-lg md:text-xl text-white/80 mb-8 max-w-2xl mx-auto px-2 animate-fade-up animation-delay-600">
  42.                 Rejoignez les 4000 jeunes qui seront formés aux métiers d'avenir dans 10 secteurs d'activité
  43.             </p>
  44.             
  45.             <!-- Stats rapides - FIXED: better mobile grid -->
  46.             <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-2 sm:gap-4 max-w-4xl mx-auto mb-10">
  47.                 <div class="bg-white/5 backdrop-blur-sm rounded-xl p-2 sm:p-4 border border-white/10 animate-fade-up animation-delay-800">
  48.                     <div class="text-lg sm:text-2xl font-bold text-white">{{ stats.metiers }}</div>
  49.                     <div class="text-[10px] sm:text-xs text-white/60">Métiers</div>
  50.                 </div>
  51.                 <div class="bg-white/5 backdrop-blur-sm rounded-xl p-2 sm:p-4 border border-white/10 animate-fade-up animation-delay-1000">
  52.                     <div class="text-lg sm:text-2xl font-bold text-white">{{ stats.secteurs }}</div>
  53.                     <div class="text-[10px] sm:text-xs text-white/60">Secteurs</div>
  54.                 </div>
  55.                 <div class="bg-white/5 backdrop-blur-sm rounded-xl p-2 sm:p-4 border border-white/10 animate-fade-up animation-delay-1200">
  56.                     <div class="text-lg sm:text-2xl font-bold text-white">{{ stats.etablissements }}</div>
  57.                     <div class="text-[10px] sm:text-xs text-white/60">etablissements</div>
  58.                 </div>
  59.                 <div class="bg-white/5 backdrop-blur-sm rounded-xl p-2 sm:p-4 border border-white/10 animate-fade-up animation-delay-1400">
  60.                     <div class="text-lg sm:text-2xl font-bold text-white">{{ stats.places }}</div>
  61.                     <div class="text-[10px] sm:text-xs text-white/60">Places</div>
  62.                 </div>
  63.                 <div class="bg-white/5 backdrop-blur-sm rounded-xl p-2 sm:p-4 border border-white/10 animate-fade-up animation-delay-1600 col-span-2 sm:col-span-1">
  64.                     <div class="text-lg sm:text-2xl font-bold text-white">{{ stats.candidatures }}</div>
  65.                     <div class="text-[10px] sm:text-xs text-white/60">Candidatures</div>
  66.                 </div>
  67.             </div>
  68.         </div>
  69.     </div>
  70.     <!-- Scroll indicator -->
  71.     <a href="#presentation" class="absolute bottom-8 left-1/2 transform -translate-x-1/2 animate-bounce">
  72.         <div class="w-6 h-10 rounded-full border-2 border-white/30 flex justify-center">
  73.             <div class="w-1 h-3 bg-white/60 rounded-full mt-2 animate-pulse"></div>
  74.         </div>
  75.     </a>
  76. </section>
  77. <!-- ==================== PRÉSENTATION SECTION ==================== -->
  78. <section id="presentation" class="relative py-12 sm:py-20 bg-white w-full overflow-hidden">
  79.     <!-- Conteneur de graffitis animés -->
  80.     <div id="graffiti-container" class="absolute inset-0 overflow-hidden pointer-events-none"></div>
  81.     
  82.     <div class="container mx-auto px-4 relative z-10 w-full max-w-full">
  83.         <div class="grid lg:grid-cols-5 gap-8 lg:gap-12 items-start">
  84.             <!-- Colonne gauche : Texte (3/5) -->
  85.             <div class="lg:col-span-3 space-y-4 text-slate-700 leading-relaxed">
  86.                 <p class="text-base sm:text-lg md:text-xl text-slate-800 font-light leading-relaxed">
  87.                     Il est ouvert, au titre de l'année 2026, un recrutement de 
  88.                     <span class="relative inline-block px-2">
  89.                         <span class="absolute inset-0 bg-indigo-100 rounded-lg -rotate-1"></span>
  90.                         <strong class="relative text-indigo-700 font-bold">quatre mille (4 000) jeunes</strong>
  91.                     </span>
  92.                     pour la formation au Certificat de Qualification Professionnelle (CQP) dans divers métiers. 
  93.                     Les candidatures sont reçues en ligne sur le site 
  94.                     <a href="https://1jeune1metier.daip.ci/" class="text-indigo-600 font-medium hover:text-indigo-700 underline underline-offset-2 transition break-all" target="_blank">1jeune1metier.daip.ci</a> 
  95.                     ou de façon physique dans les différentes structures de formation.
  96.                 </p>
  97.                 
  98.                 <p class="text-base sm:text-lg md:text-xl text-slate-800 font-light">
  99.                     Pour faire acte de candidature vous devez remplir les conditions suivantes :
  100.                 </p>
  101.                 <div class="space-y-3">
  102.                     <!-- Puce 1 : Niveau scolaire -->
  103.                     <div class="flex items-start gap-3 sm:gap-4 group">
  104.                         <div class="relative flex-shrink-0">
  105.                             <div class="w-8 h-8 sm:w-10 sm:h-10 bg-indigo-100 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform shadow-md group-hover:shadow-indigo-200">
  106.                                 <i class="fas fa-graduation-cap text-indigo-600 text-sm sm:text-lg"></i>
  107.                             </div>
  108.                         </div>
  109.                         <div class="flex-1 pt-1">
  110.                             <p class="text-sm sm:text-base text-slate-700">
  111.                                 <span class="font-semibold text-indigo-700">Niveau scolaire requis :</span>
  112.                                 <span class="block text-slate-600 mt-1">Avoir au moins le niveau de la classe de 5ème ou 3ème (selon le secteur d'activité)</span>
  113.                             </p>
  114.                         </div>
  115.                     </div>
  116.                     
  117.                     <!-- Puce 2 : Âge -->
  118.                     <div class="flex items-start gap-3 sm:gap-4 group">
  119.                         <div class="relative flex-shrink-0">
  120.                             <div class="w-8 h-8 sm:w-10 sm:h-10 bg-purple-100 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform shadow-md group-hover:shadow-purple-200">
  121.                                 <i class="fas fa-birthday-cake text-purple-600 text-sm sm:text-lg"></i>
  122.                             </div>
  123.                         </div>
  124.                         <div class="flex-1 pt-1">
  125.                             <p class="text-sm sm:text-base text-slate-700">
  126.                                 <span class="font-semibold text-purple-700">Tranche d'âge :</span>
  127.                                 <span class="block text-slate-600 mt-1">Être âgé de 16 à 40 ans au 31 décembre 2025</span>
  128.                             </p>
  129.                         </div>
  130.                     </div>
  131.                     
  132.                     <!-- Puce 3 : Nationalité -->
  133.                     <div class="flex items-start gap-3 sm:gap-4 group">
  134.                         <div class="relative flex-shrink-0">
  135.                             <div class="w-8 h-8 sm:w-10 sm:h-10 bg-pink-100 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform shadow-md group-hover:shadow-pink-200">
  136.                                 <i class="fas fa-flag text-pink-600 text-sm sm:text-lg"></i>
  137.                             </div>
  138.                         </div>
  139.                         <div class="flex-1 pt-1">
  140.                             <p class="text-sm sm:text-base text-slate-700">
  141.                                 <span class="font-semibold text-pink-700">Nationalité :</span>
  142.                                 <span class="block text-slate-600 mt-1">Être de nationalité ivoirienne</span>
  143.                             </p>
  144.                         </div>
  145.                     </div>
  146.                 </div>
  147.             </div>
  148.             
  149.             <!-- Colonne droite : Illustration (2/5) - FIXED: hide on very small screens if needed -->
  150.             <div class="lg:col-span-2 relative hidden sm:block">
  151.                 <!-- Illustration principale -->
  152.                 <div class="relative rounded-2xl overflow-hidden shadow-2xl">
  153.                     <img src="{{ asset('svg/candidate.svg') }}" alt="Illustration formation professionnelle" class="w-full h-auto object-cover" onerror="this.onerror=null; this.src='{{ asset('/svg/candidate.svg') }}'">
  154.                     
  155.                     <!-- Overlay léger -->
  156.                     <div class="absolute inset-0 bg-gradient-to-tr from-indigo-600/10 to-purple-600/10 mix-blend-overlay"></div>
  157.                 </div>
  158.                 
  159.                 <!-- Éléments décoratifs flottants -->
  160.                 <div class="absolute -top-6 -right-6 w-20 h-20 bg-indigo-200 rounded-full opacity-20 animate-pulse-glow"></div>
  161.                 <div class="absolute -bottom-6 -left-6 w-28 h-28 bg-purple-200 rounded-full opacity-20 animate-pulse-glow animation-delay-2000"></div>
  162.                 
  163.                 <!-- Badge flottant avec statistique -->
  164.                 <div class="absolute -bottom-4 -right-4 bg-white rounded-xl shadow-lg p-3 animate-float">
  165.                     <div class="flex items-center gap-2">
  166.                         <div class="w-10 h-10 bg-indigo-100 rounded-full flex items-center justify-center">
  167.                             <i class="fas fa-graduation-cap text-indigo-600 text-lg"></i>
  168.                         </div>
  169.                         <div>
  170.                             <p class="text-xl font-bold text-indigo-600">4000+</p>
  171.                             <p class="text-xs text-slate-500">jeunes formés</p>
  172.                         </div>
  173.                     </div>
  174.                 </div>
  175.             </div>
  176.         </div>
  177.     </div>
  178. </section>
  179. <!-- ==================== PROCESSUS SECTION ==================== -->
  180. <section class="py-12 sm:py-20 bg-gradient-to-br from-indigo-50 to-purple-50 border-y border-indigo-200">
  181.     <div class="container mx-auto px-4 w-full max-w-full">
  182.         <div class="text-center mb-8 sm:mb-12">
  183.             <div class="inline-flex items-center rounded-full border border-indigo-200 bg-white px-3 sm:px-4 py-1.5 text-xs sm:text-sm font-semibold text-indigo-600 mb-4 shadow-sm">
  184.                 <i class="fas fa-clock mr-2"></i>
  185.                 PROCESSUS DE SÉLECTION
  186.             </div>
  187.             <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-slate-900 mb-4 px-2">
  188.                 3 étapes pour réussir
  189.             </h2>
  190.             <p class="text-sm sm:text-base md:text-lg text-slate-600 max-w-2xl mx-auto px-4">
  191.                 Un processus simple et transparent en trois étapes
  192.             </p>
  193.         </div>
  194.         <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 sm:gap-6 md:gap-8 mx-auto">
  195.             <!-- Étape 1 -->
  196.             <div class="relative bg-white rounded-2xl p-6 sm:p-8 shadow-lg border border-indigo-200 hover:shadow-xl transition-all hover:-translate-y-2 group">
  197.                 <div class="w-12 h-12 sm:w-16 sm:h-16 bg-indigo-100 rounded-xl flex items-center justify-center mb-4 sm:mb-6 group-hover:scale-110 transition">
  198.                     <i class="fas fa-file-alt text-xl sm:text-2xl text-indigo-600"></i>
  199.                 </div>
  200.                 <h3 class="text-lg sm:text-xl font-bold mb-2 sm:mb-3 text-slate-900">Analyse des dossiers</h3>
  201.                 <p class="text-sm sm:text-base text-slate-600">Vérification de la conformité des pièces fournies et des critères d'éligibilité</p>
  202.             </div>
  203.             
  204.             <!-- Étape 2 -->
  205.             <div class="relative bg-white rounded-2xl p-6 sm:p-8 shadow-lg border border-purple-200 hover:shadow-xl transition-all hover:-translate-y-2 group">
  206.                 <div class="w-12 h-12 sm:w-16 sm:h-16 bg-purple-100 rounded-xl flex items-center justify-center mb-4 sm:mb-6 group-hover:scale-110 transition">
  207.                     <i class="fas fa-users text-xl sm:text-2xl text-purple-600"></i>
  208.                 </div>
  209.                 <h3 class="text-lg sm:text-xl font-bold mb-2 sm:mb-3 text-slate-900">Entretien de motivation</h3>
  210.                 <p class="text-sm sm:text-base text-slate-600">Épreuves écrites et orales pour évaluer la motivation et les aptitudes</p>
  211.             </div>
  212.             
  213.             <!-- Étape 3 -->
  214.             <div class="relative bg-white rounded-2xl p-6 sm:p-8 shadow-lg border border-pink-200 hover:shadow-xl transition-all hover:-translate-y-2 group sm:col-span-2 md:col-span-1">
  215.                 <div class="w-12 h-12 sm:w-16 sm:h-16 bg-pink-100 rounded-xl flex items-center justify-center mb-4 sm:mb-6 group-hover:scale-110 transition">
  216.                     <i class="fas fa-heartbeat text-xl sm:text-2xl text-pink-600"></i>
  217.                 </div>
  218.                 <h3 class="text-lg sm:text-xl font-bold mb-2 sm:mb-3 text-slate-900">Visite médicale</h3>
  219.                 <p class="text-sm sm:text-base text-slate-600">Examen d'aptitude physique obligatoire</p>
  220.             </div>
  221.         </div>
  222.     </div>
  223. </section>
  224. <!-- ==================== SECTION ÉTABLISSEMENTS ==================== -->
  225. <section id="etablissements" class="py-12 sm:py-20 bg-gradient-to-br from-indigo-700 via-purple-700 to-blue-700">
  226.     <div class="container mx-auto px-4 w-full max-w-full">
  227.         <div class="text-center mb-8 sm:mb-12">
  228.             <div class="inline-flex items-center rounded-full border border-white bg-white px-3 sm:px-4 py-1.5 text-xs sm:text-sm font-semibold text-purple-500 mb-4 shadow-lg">
  229.                 <i class="fas fa-briefcase mr-2"></i>
  230.                 ÉTABLISSEMENTS DE FORMATION
  231.             </div>
  232.             <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-4 px-2">
  233.                 {{ stats.etablissements }} établissements à découvrir
  234.             </h2>
  235.             <p class="text-sm sm:text-base md:text-lg text-white/80 max-w-2xl mx-auto px-4">
  236.                 Trouvez votre établissement par direction régionale
  237.             </p>
  238.         </div>
  239.         <!-- Filtres - FIXED: mobile layout -->
  240.         <div class="max-w-4xl mx-auto mb-8 sm:mb-10 px-2">
  241.             <div class="flex flex-col sm:flex-row gap-3 sm:gap-4">
  242.                 <div class="flex-1">
  243.                     <div class="relative">
  244.                         <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400"></i>
  245.                         <input type="text" 
  246.                                id="etablissement-search" 
  247.                                placeholder="Rechercher un établissement..." 
  248.                                class="w-full pl-10 pr-4 py-2 sm:py-3 rounded-lg border border-white/30 bg-white/10 backdrop-blur-sm text-white placeholder-white/70 text-sm sm:text-base focus:outline-none focus:ring-2 focus:ring-white focus:border-transparent"
  249.                                value="{{ rechercheEtablissement|default('') }}">
  250.                     </div>
  251.                 </div>
  252.                 <div class="sm:w-64">
  253.                     <select id="dr-filter" class="w-full px-4 py-2 sm:py-3 rounded-lg border border-white/30 bg-white/10 backdrop-blur-sm text-white text-sm sm:text-base focus:outline-none focus:ring-2 focus:ring-white focus:border-transparent">
  254.                         <option value="">Toutes les directions</option>
  255.                         {% for direction in directions|default([]) %}
  256.                             <option value="{{ direction.id }}" {{ directionSelectionnee|default('') == direction.id ? 'selected' : '' }}>
  257.                                 {{ direction.nom }}
  258.                             </option>
  259.                         {% endfor %}
  260.                     </select>
  261.                 </div>
  262.             </div>
  263.         </div>
  264.         <!-- Grille des établissements - FIXED: responsive grid -->
  265.         <div id="etablissements-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
  266.             {% include 'partials/_etablissements_cards.html.twig' with {
  267.                 'etablissements': etablissements
  268.             } %}
  269.         </div>
  270.         <!-- Loading indicator -->
  271.         <div id="etablissements-loading" class="hidden text-center py-8">
  272.             <div class="inline-block w-8 h-8 border-4 border-white/30 border-t-white rounded-full animate-spin"></div>
  273.             <p class="text-white/70 mt-2">Chargement en cours...</p>
  274.         </div>
  275.         
  276.         <!-- Pagination -->
  277.         <div id="etablissements-pagination" class="flex justify-center mt-8 sm:mt-10"></div>
  278.         
  279.         <div class="text-center mt-6">
  280.             <a href="{{ path('app_etablissements_list') }}"
  281.             class="inline-flex items-center gap-2 px-6 py-3 bg-white text-indigo-600 rounded-xl font-semibold hover:bg-indigo-50 transition border border-white/30">
  282.                 <i class="fas fa-building"></i>
  283.                 Voir tous les établissements
  284.                 <i class="fas fa-arrow-right text-sm"></i>
  285.             </a>
  286.         </div>
  287.     </div>
  288. </section>
  289. <!-- ==================== SECTION MÉTIERS ==================== -->
  290. <section id="metiers" class="py-12 sm:py-20 bg-gradient-to-br from-indigo-50 to-purple-50 border-y border-indigo-200">
  291.     <div class="container mx-auto px-4 w-full max-w-full">
  292.         <div class="text-center mb-8 sm:mb-12">
  293.             <div class="inline-flex items-center rounded-full border border-indigo-200 bg-white px-3 sm:px-4 py-1.5 text-xs sm:text-sm font-semibold text-indigo-600 mb-4 shadow-sm">
  294.                 <i class="fas fa-school mr-2"></i>
  295.                 NOS METIERS
  296.             </div>
  297.             <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-slate-900 mb-4 px-2">
  298.                 {{ stats.metiers }} métiers à découvrir
  299.             </h2>
  300.             <p class="text-sm sm:text-base md:text-lg text-slate-600 max-w-2xl mx-auto px-4">
  301.                 Filtrez par secteur ou recherchez un métier
  302.             </p>
  303.         </div>
  304.         <!-- Filtres - FIXED: mobile layout -->
  305.         <div class="max-w-3xl mx-auto mb-8 sm:mb-10 px-2">
  306.             <div class="flex flex-col sm:flex-row gap-3 sm:gap-4">
  307.                 <div class="flex-1">
  308.                     <div class="relative">
  309.                         <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400"></i>
  310.                         <input type="text" 
  311.                                id="search-input" 
  312.                                placeholder="Rechercher un métier..." 
  313.                                class="w-full pl-10 pr-4 py-2 sm:py-3 rounded-lg border border-indigo-200 bg-white text-slate-700 text-sm sm:text-base focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
  314.                                value="{{ rechercheMetier|default('') }}">
  315.                     </div>
  316.                 </div>
  317.                 <div class="flex-1">
  318.                     <select id="secteur-filter" class="w-full px-4 py-2 sm:py-3 rounded-lg border border-indigo-200 bg-white text-slate-700 text-sm sm:text-base focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent">
  319.                         <option value="">Tous les secteurs</option>
  320.                         {% for secteur in secteurs|default([]) %}
  321.                             <option value="{{ secteur.id }}" {{ secteurSelectionne|default('') == secteur.id ? 'selected' : '' }}>
  322.                                 {{ secteur.nom }}
  323.                             </option>
  324.                         {% endfor %}
  325.                     </select>
  326.                 </div>
  327.             </div>
  328.         </div>
  329.         <!-- Grille des métiers - FIXED: responsive grid -->
  330.         <div id="metiers-grid" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 sm:gap-6">
  331.             {% include 'partials/_metiers_cards.html.twig' with {'metiers': metiers} %}
  332.         </div>
  333.         <!-- Loading indicator -->
  334.         <div id="loading" class="hidden text-center py-8">
  335.             <div class="inline-block w-8 h-8 border-4 border-indigo-200 border-t-indigo-600 rounded-full animate-spin"></div>
  336.         </div>
  337.         <!-- Pagination -->
  338.         <div id="pagination" class="flex justify-center mt-8 sm:mt-10"></div>
  339.         
  340.         <div class="text-center mt-6">
  341.             <a href="{{ path('app_metiers_list') }}" 
  342.             class="inline-flex items-center gap-2 px-6 py-3 bg-indigo-600 text-white rounded-xl font-semibold hover:bg-indigo-700 transition">
  343.                 <i class="fas fa-briefcase"></i>
  344.                 Voir tous les métiers
  345.                 <i class="fas fa-arrow-right text-sm"></i>
  346.             </a>
  347.         </div>
  348.     </div>
  349. </section>
  350. <!-- ==================== DOSSIER DE CANDIDATURE SECTION ==================== -->
  351. <section class="py-12 sm:py-20 bg-white">
  352.     <div class="container mx-auto px-4 w-full max-w-full">
  353.         <div class="text-center mb-8 sm:mb-12">
  354.             <div class="inline-flex items-center rounded-full border border-indigo-200 bg-white px-3 sm:px-4 py-1.5 text-xs sm:text-sm font-semibold text-indigo-600 mb-4 shadow-sm">
  355.                 <i class="fas fa-file-alt mr-2"></i>
  356.                 DOSSIER DE CANDIDATURE
  357.             </div>
  358.             <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-slate-900 mb-4 px-2">
  359.                 Pièces à fournir
  360.             </h2>
  361.             <p class="text-sm sm:text-base md:text-lg text-slate-600 max-w-2xl mx-auto px-4">
  362.                 Préparez les documents nécessaires pour votre inscription
  363.             </p>
  364.         </div>
  365.         <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4 max-w-4xl mx-auto px-2">
  366.             <!-- Pièce 1 -->
  367.             <div class="bg-slate-50 rounded-xl p-4 sm:p-5 border border-slate-200 hover:shadow-lg transition-all hover:-translate-y-1 group">
  368.                 <div class="flex items-start gap-3">
  369.                     <div class="w-8 h-8 sm:w-10 sm:h-10 bg-indigo-100 rounded-lg flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-indigo-200">
  370.                         <i class="fas fa-baby text-indigo-600 text-sm sm:text-base"></i>
  371.                     </div>
  372.                     <div>
  373.                         <h4 class="font-semibold text-sm sm:text-base text-slate-900">Extrait de naissance</h4>
  374.                         <p class="text-xs text-indigo-600 font-medium mt-1">Pièce n°1</p>
  375.                         <p class="text-xs text-slate-500 mt-2">Original ou photocopie de l'acte de naissance ou jugement supplétif</p>
  376.                     </div>
  377.                 </div>
  378.             </div>
  379.             <!-- Pièce 2 -->
  380.             <div class="bg-slate-50 rounded-xl p-4 sm:p-5 border border-slate-200 hover:shadow-lg transition-all hover:-translate-y-1 group">
  381.                 <div class="flex items-start gap-3">
  382.                     <div class="w-8 h-8 sm:w-10 sm:h-10 bg-purple-100 rounded-lg flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-purple-200">
  383.                         <i class="fas fa-id-card text-purple-600 text-sm sm:text-base"></i>
  384.                     </div>
  385.                     <div>
  386.                         <h4 class="font-semibold text-sm sm:text-base text-slate-900">CNI ou attestation</h4>
  387.                         <p class="text-xs text-purple-600 font-medium mt-1">Pièce n°2</p>
  388.                         <p class="text-xs text-slate-500 mt-2">Carte Nationale d'Identité ou attestation d'identité en cours de validité</p>
  389.                     </div>
  390.                 </div>
  391.             </div>
  392.             <!-- Pièce 3 -->
  393.             <div class="bg-slate-50 rounded-xl p-4 sm:p-5 border border-slate-200 hover:shadow-lg transition-all hover:-translate-y-1 group">
  394.                 <div class="flex items-start gap-3">
  395.                     <div class="w-8 h-8 sm:w-10 sm:h-10 bg-pink-100 rounded-lg flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-pink-200">
  396.                         <i class="fas fa-graduation-cap text-pink-600 text-sm sm:text-base"></i>
  397.                     </div>
  398.                     <div>
  399.                         <h4 class="font-semibold text-sm sm:text-base text-slate-900">Niveau scolaire</h4>
  400.                         <p class="text-xs text-pink-600 font-medium mt-1">Pièce n°3</p>
  401.                         <p class="text-xs text-slate-500 mt-2">Document attestant du niveau scolaire requis par secteur</p>
  402.                     </div>
  403.                 </div>
  404.             </div>
  405.             <!-- Pièce 4 -->
  406.             <div class="bg-slate-50 rounded-xl p-4 sm:p-5 border border-slate-200 hover:shadow-lg transition-all hover:-translate-y-1 group">
  407.                 <div class="flex items-start gap-3">
  408.                     <div class="w-8 h-8 sm:w-10 sm:h-10 bg-green-100 rounded-lg flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-green-200">
  409.                         <i class="fas fa-heartbeat text-green-600 text-sm sm:text-base"></i>
  410.                     </div>
  411.                     <div>
  412.                         <h4 class="font-semibold text-sm sm:text-base text-slate-900">Carte CMU</h4>
  413.                         <p class="text-xs text-green-600 font-medium mt-1">Pièce n°4</p>
  414.                         <p class="text-xs text-slate-500 mt-2">Couverture Maladie Universelle (si disponible)</p>
  415.                     </div>
  416.                 </div>
  417.             </div>
  418.         </div>
  419.     </div>
  420. </section>
  421. <!-- ==================== CHRONOGRAMME SECTION ==================== -->
  422. <section class="py-12 sm:py-20 bg-gradient-to-br from-green-50 to-emerald-50 border-y border-green-200">
  423.     <div class="container mx-auto px-4 w-full max-w-full">
  424.         <div class="text-center mb-8 sm:mb-12">
  425.             <div class="inline-flex items-center rounded-full border border-green-200 bg-white px-3 sm:px-4 py-1.5 text-xs sm:text-sm font-semibold text-green-700 mb-4 shadow-sm">
  426.                 <i class="fas fa-calendar-alt mr-2"></i>
  427.                 CHRONOGRAMME
  428.             </div>
  429.             <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-slate-900 mb-4 px-2">
  430.                 Calendrier des opérations
  431.             </h2>
  432.             <p class="text-sm sm:text-base md:text-lg text-slate-600 max-w-2xl mx-auto px-4">
  433.                 Les dates clés à retenir pour votre candidature
  434.             </p>
  435.         </div>
  436.         <div class="grid lg:grid-cols-5 gap-6 lg:gap-8 items-start">
  437.             <!-- Colonne gauche : Illustration SVG - FIXED: hide on mobile -->
  438.             <div class="relative col-span-2 hidden lg:block">
  439.                 <div class="relative rounded-2xl overflow-hidden shadow-xl bg-gradient-to-br from-white to-slate-100 p-8">
  440.                     <img src="{{ asset('svg/work-in-progress.svg') }}" alt="Calendrier des inscriptions" class="w-full h-auto object-contain" onerror="this.onerror=null; this.src='{{ asset('/svg/done-checking.svg') }}'">
  441.                 </div>
  442.                 
  443.                 <!-- Badge flottant -->
  444.                 <div class="absolute -bottom-4 -right-4 bg-white rounded-xl shadow-lg p-3 animate-float hidden md:block">
  445.                     <div class="flex items-center gap-2">
  446.                         <div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
  447.                             <i class="fas fa-clock text-green-600"></i>
  448.                         </div>
  449.                         <div>
  450.                             <p class="text-sm font-bold text-green-600">Ne tardez pas !</p>
  451.                             <p class="text-xs text-slate-500">Inscriptions limitées</p>
  452.                         </div>
  453.                     </div>
  454.                 </div>
  455.             </div>
  456.             <!-- Colonne droite : Timeline -->
  457.             <div class="space-y-4 sm:space-y-6 col-span-3 px-2">
  458.                 <!-- Étape 1 -->
  459.                 <div class="relative flex items-start gap-3 sm:gap-4 group">
  460.                     <div class="w-8 h-8 sm:w-12 sm:h-12 bg-indigo-100 rounded-full flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-indigo-200">
  461.                         <span class="text-sm sm:text-lg font-bold text-indigo-600">1</span>
  462.                     </div>
  463.                     <div class="flex-1 bg-white rounded-xl p-4 sm:p-5 shadow-md border border-indigo-100">
  464.                         <h4 class="text-base sm:text-lg font-bold text-slate-900">Dépôt des candidatures</h4>
  465.                         <p class="text-sm sm:text-base text-indigo-600 font-semibold mt-1">16 - 28 février 2026</p>
  466.                         <p class="text-xs sm:text-sm text-slate-600 mt-2">Dépôt en ligne ou dans les structures de formation</p>
  467.                     </div>
  468.                 </div>
  469.                 <!-- Étape 2 -->
  470.                 <div class="relative flex items-start gap-3 sm:gap-4 group">
  471.                     <div class="w-8 h-8 sm:w-12 sm:h-12 bg-purple-100 rounded-full flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-purple-200">
  472.                         <span class="text-sm sm:text-lg font-bold text-purple-600">2</span>
  473.                     </div>
  474.                     <div class="flex-1 bg-white rounded-xl p-4 sm:p-5 shadow-md border border-purple-100">
  475.                         <h4 class="text-base sm:text-lg font-bold text-slate-900">Étude des dossiers</h4>
  476.                         <p class="text-sm sm:text-base text-purple-600 font-semibold mt-1">1er - 15 mars 2026</p>
  477.                         <p class="text-xs sm:text-sm text-slate-600 mt-2">Analyse de la conformité des pièces fournies</p>
  478.                     </div>
  479.                 </div>
  480.                 <!-- Étape 3 -->
  481.                 <div class="relative flex items-start gap-3 sm:gap-4 group">
  482.                     <div class="w-8 h-8 sm:w-12 sm:h-12 bg-pink-100 rounded-full flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-pink-200">
  483.                         <span class="text-sm sm:text-lg font-bold text-pink-600">3</span>
  484.                     </div>
  485.                     <div class="flex-1 bg-white rounded-xl p-4 sm:p-5 shadow-md border border-pink-100">
  486.                         <h4 class="text-base sm:text-lg font-bold text-slate-900">Entretiens de sélection</h4>
  487.                         <p class="text-sm sm:text-base text-pink-600 font-semibold mt-1">16 mars - 15 avril 2026</p>
  488.                         <p class="text-xs sm:text-sm text-slate-600 mt-2">Évaluation des candidats par les jurys</p>
  489.                     </div>
  490.                 </div>
  491.                 <!-- Étape 4 -->
  492.                 <div class="relative flex items-start gap-3 sm:gap-4 group">
  493.                     <div class="w-8 h-8 sm:w-12 sm:h-12 bg-green-100 rounded-full flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition group-hover:bg-green-200">
  494.                         <span class="text-sm sm:text-lg font-bold text-green-600">4</span>
  495.                     </div>
  496.                     <div class="flex-1 bg-white rounded-xl p-4 sm:p-5 shadow-md border border-green-100">
  497.                         <h4 class="text-base sm:text-lg font-bold text-slate-900">Début des formations</h4>
  498.                         <p class="text-sm sm:text-base text-green-600 font-semibold mt-1">Mai 2026</p>
  499.                         <p class="text-xs sm:text-sm text-slate-600 mt-2">Rentrée des promos dans les établissements de formation</p>
  500.                     </div>
  501.                 </div>
  502.             </div>
  503.         </div>
  504.     </div>
  505. </section>
  506. <!-- ==================== SECTION PARTENAIRES ==================== -->
  507. <section class="py-12 sm:py-16 bg-gradient-to-br from-slate-50 to-white border-t border-slate-200">
  508.     <div class="container mx-auto px-4 w-full max-w-full">
  509.         <div class="text-center mb-8 sm:mb-10">
  510.             <div class="inline-flex items-center rounded-full border border-indigo-200 bg-indigo-50 px-3 sm:px-4 py-1.5 text-xs sm:text-sm font-semibold text-indigo-600 mb-4 shadow-sm">
  511.                 <i class="fas fa-handshake mr-2"></i>
  512.                 ILS NOUS FONT CONFIANCE
  513.             </div>
  514.             <h2 class="text-xl sm:text-2xl md:text-3xl font-bold text-slate-800 mb-2 px-2">
  515.                 Nos partenaires institutionnels et techniques
  516.             </h2>
  517.             <p class="text-sm sm:text-base text-slate-600 max-w-2xl mx-auto px-4">
  518.                 Ils accompagnent la DAIP dans sa mission de formation et d'insertion professionnelle
  519.             </p>
  520.         </div>
  521.         <!-- CARROUSEL AVEC FLÈCHES SUR TOUS LES ÉCRANS -->
  522.         <div class="relative w-full flex justify-center"
  523.              x-data="partenairesCarousel"
  524.              @mouseenter="stopAutoplay"
  525.              @mouseleave="startAutoplay">
  526.             
  527.             <!-- Conteneur principal -->
  528.             <div class="w-full max-w-6xl px-4 md:px-8 relative">
  529.                 
  530.                 <!-- Bouton précédent - VISIBLE SUR MOBILE ET DESKTOP -->
  531.                 <button @click="prevSlide; $el.blur()" 
  532.                         class="absolute -left-2 sm:-left-3 md:-left-4 top-1/2 transform -translate-y-1/2 w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12 rounded-full bg-white shadow-lg hover:bg-indigo-600 hover:text-white transition-all duration-300 flex items-center justify-center border border-indigo-200 z-20 opacity-80 hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-indigo-400"
  533.                         title="Précédent"
  534.                         aria-label="Voir le partenaire précédent">
  535.                     <i class="fas fa-chevron-left text-xs sm:text-sm md:text-base"></i>
  536.                 </button>
  537.                 
  538.                 <!-- Conteneur du carrousel -->
  539.                 <div class="overflow-hidden">
  540.                     <div class="flex transition-transform duration-500 ease-out" 
  541.                          :style="'transform: translateX(-' + (currentSlide * 100 / slidesPerView) + '%)'">
  542.                         
  543.                         <template x-for="partenaire in partenaires" :key="partenaire.nom">
  544.                             <!-- Slide avec largeur dynamique -->
  545.                             <div class="flex-shrink-0 px-2 sm:px-3" :style="'width: ' + (100 / slidesPerView) + '%'">
  546.                                 <!-- Centrage de la carte -->
  547.                                 <div class="flex justify-center w-full">
  548.                                     <!-- Carte -->
  549.                                     <div class="w-full max-w-[260px] xs:max-w-[280px] sm:max-w-[300px] md:max-w-[320px]">
  550.                                         <a :href="partenaire.lien" 
  551.                                            target="_blank"
  552.                                            class="block group relative overflow-hidden rounded-xl shadow-md hover:shadow-xl transition-all duration-300 hover:-translate-y-1">
  553.                                             
  554.                                             <!-- Image avec ratio 3:2 -->
  555.                                             <div class="relative w-full pt-[66.67%] bg-gradient-to-br from-indigo-100 to-purple-100 rounded-xl overflow-hidden">
  556.                                                 
  557.                                                 <!-- Image de fond -->
  558.                                                 <div class="absolute inset-0 bg-cover bg-center transition-transform duration-500 group-hover:scale-105"
  559.                                                      :style="'background-image: url(' + partenaire.image + ')'"
  560.                                                      x-on:error="$el.style.backgroundImage = 'url(images/placeholder.jpg)'">
  561.                                                     
  562.                                                     <!-- Overlay -->
  563.                                                     <div class="absolute inset-0 bg-gradient-to-t from-black/80 via-black/30 to-transparent"></div>
  564.                                                     
  565.                                                     <!-- Nom du partenaire -->
  566.                                                     <div class="absolute bottom-0 left-0 right-0 p-3 sm:p-4 text-center">
  567.                                                         <h3 class="text-white font-bold text-xs sm:text-sm md:text-base drop-shadow-lg line-clamp-2 px-1" 
  568.                                                             x-text="partenaire.nom"></h3>
  569.                                                     </div>
  570.                                                 </div>
  571.                                             </div>
  572.                                         </a>
  573.                                     </div>
  574.                                 </div>
  575.                             </div>
  576.                         </template>
  577.                     </div>
  578.                 </div>
  579.                 <!-- Bouton suivant - VISIBLE SUR MOBILE ET DESKTOP -->
  580.                 <button @click="nextSlide; $el.blur()" 
  581.                         class="absolute -right-2 sm:-right-3 md:-right-4 top-1/2 transform -translate-y-1/2 w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12 rounded-full bg-white shadow-lg hover:bg-indigo-600 hover:text-white transition-all duration-300 flex items-center justify-center border border-indigo-200 z-20 opacity-80 hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-indigo-400"
  582.                         title="Suivant"
  583.                         aria-label="Voir le partenaire suivant">
  584.                     <i class="fas fa-chevron-right text-xs sm:text-sm md:text-base"></i>
  585.                 </button>
  586.             </div>
  587.         </div>
  588.         <!-- Indicateurs de slide -->
  589.         <div class="flex justify-center mt-6 sm:mt-8 space-x-2">
  590.             <template x-for="index in Math.ceil(partenaires.length / slidesPerView)" :key="index">
  591.                 <button @click="goToSlide(index - 1)" 
  592.                         class="h-2 sm:h-2.5 rounded-full transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-indigo-400"
  593.                         :class="currentSlide === index - 1 ? 'w-6 sm:w-8 bg-indigo-600' : 'w-2 sm:w-2.5 bg-slate-300 hover:bg-indigo-400'"
  594.                         :aria-label="'Aller à la slide ' + index">
  595.                 </button>
  596.             </template>
  597.         </div>
  598.     </div>
  599. </section>
  600. <!-- ==================== INFOLINE SECTION ==================== -->
  601. <section class="py-12 sm:py-16 bg-gradient-to-r from-indigo-600 to-purple-600 text-white">
  602.     <div class="container mx-auto px-4 w-full max-w-full text-center">
  603.         <i class="fas fa-phone-alt text-3xl sm:text-4xl md:text-5xl mb-4 animate-pulse"></i>
  604.         <h3 class="text-xl sm:text-2xl md:text-3xl font-bold mb-2 px-2">Une question ? Besoin d'aide ?</h3>
  605.         <p class="text-sm sm:text-base text-white/80 mb-4 sm:mb-6">Notre équipe est à votre écoute du lundi au vendredi</p>
  606.         
  607.         <div class="flex flex-col sm:flex-row flex-wrap justify-center gap-3 sm:gap-6 px-2">
  608.             <a href="tel:+2252722229600" class="inline-flex items-center justify-center bg-white/10 backdrop-blur-sm rounded-lg px-4 sm:px-6 py-2 sm:py-3 border border-white/20 hover:bg-white/20 transition transform hover:scale-105 text-sm sm:text-base">
  609.                 <i class="fas fa-phone-alt mr-2"></i>
  610.                 27 22 22 96 00
  611.             </a>
  612.             <a href="tel:+2250565390459" class="inline-flex items-center justify-center bg-white/10 backdrop-blur-sm rounded-lg px-4 sm:px-6 py-2 sm:py-3 border border-white/20 hover:bg-white/20 transition transform hover:scale-105 text-sm sm:text-base">
  613.                 <i class="fas fa-phone-alt mr-2"></i>
  614.                 05 65 39 04 59
  615.             </a>
  616.             <a href="mailto:contact@daip.ci" class="inline-flex items-center justify-center bg-white/10 backdrop-blur-sm rounded-lg px-4 sm:px-6 py-2 sm:py-3 border border-white/20 hover:bg-white/20 transition transform hover:scale-105 text-sm sm:text-base break-all">
  617.                 <i class="fas fa-envelope mr-2"></i>
  618.                 contact@daip.ci
  619.             </a>
  620.         </div>
  621.     </div>
  622. </section>
  623. <!-- ==================== MODALE DES ÉTABLISSEMENTS ==================== -->
  624. <div id="etablissement-modal" class="fixed inset-0 z-50 hidden overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
  625.     <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
  626.         <!-- Background overlay -->
  627.         <div class="fixed inset-0 bg-slate-900 bg-opacity-75 transition-opacity modal-backdrop"></div>
  628.         <!-- Modal panel - FIXED: better mobile sizing -->
  629.         <div class="inline-block align-bottom bg-white rounded-2xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full w-full max-w-full mx-4">
  630.             <!-- En-tête -->
  631.             <div class="bg-gradient-to-r from-indigo-600 to-purple-600 px-4 sm:px-6 py-3 sm:py-4">
  632.                 <div class="flex items-center justify-between">
  633.                     <h3 class="text-base sm:text-lg font-semibold text-white" id="modal-title">
  634.                         Établissements de formation
  635.                     </h3>
  636.                     <button type="button" class="modal-close text-white/80 hover:text-white transition-colors">
  637.                         <i class="fas fa-times text-lg sm:text-xl"></i>
  638.                     </button>
  639.                 </div>
  640.                 <p class="text-xs sm:text-sm text-white/70 mt-1" id="modal-metier-name"></p>
  641.             </div>
  642.             
  643.             <!-- Corps de la modale -->
  644.             <div class="px-4 sm:px-6 py-3 sm:py-4 max-h-80 sm:max-h-96 overflow-y-auto">
  645.                 <div id="modal-etablissements-list" class="space-y-2">
  646.                 </div>
  647.             </div>
  648.         </div>
  649.     </div>
  650. </div>
  651. {% endblock %}
  652. {% block javascripts %}
  653. <!-- jQuery d'abord (si nécessaire) -->
  654. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  655. <script>
  656. // ==================== DONNÉES PARTENAIRES ====================
  657. let partenairesData = {{ partenaires|default([])|json_encode|raw }};
  658. console.log('✅ Données partenaires du contrôleur:', partenairesData);
  659. // Corriger les chemins d'images (la racine web Symfony est deja /public)
  660. if (partenairesData && partenairesData.length > 0) {
  661.     partenairesData = partenairesData.map(partenaire => {
  662.         if (partenaire.image && partenaire.image.startsWith('/public/')) {
  663.             partenaire.image = partenaire.image.replace('/public', '');
  664.         } else if (partenaire.image && partenaire.image.startsWith('public/')) {
  665.             partenaire.image = '/' + partenaire.image.replace('public/', '');
  666.         } else if (partenaire.image && partenaire.image.startsWith('image/')) {
  667.             partenaire.image = '/' + partenaire.image;
  668.         }
  669.         return partenaire;
  670.     });
  671.     console.log('✅ Chemins d\'images corrigés:', partenairesData);
  672. }
  673. // CRÉER UNE SEULE INSTANCE GLOBALE
  674. let carouselInstance = null;
  675. window.partenairesCarousel = function() {
  676.     // Si l'instance existe déjà, la retourner
  677.     if (carouselInstance) {
  678.         console.log('⚠️ Utilisation de l\'instance existante');
  679.         return carouselInstance;
  680.     }
  681.     
  682.     // Créer la nouvelle instance
  683.     carouselInstance = {
  684.         partenaires: partenairesData || [],
  685.         currentSlide: 0,
  686.         slidesPerView: 1, // CHANGÉ : 1 au lieu de 4
  687.         autoplayInterval: null,
  688.         _eventListenersAdded: false,
  689.         init() {
  690.             console.log('🎠 Carousel initialisé');
  691.             
  692.             if (!this.partenaires || this.partenaires.length === 0) return;
  693.             
  694.             // Ajouter les event listeners UNE SEULE FOIS
  695.             if (!this._eventListenersAdded) {
  696.                 this._eventListenersAdded = true;
  697.                 this.updateSlidesPerView();
  698.                 window.addEventListener('resize', () => this.updateSlidesPerView());
  699.                 this.startAutoplay();
  700.             }
  701.         },
  702.         updateSlidesPerView() {
  703.             // CORRECTION : Ne pas dépasser le nombre de partenaires
  704.             const width = window.innerWidth;
  705.             let newValue;
  706.             
  707.             if (width < 640) {
  708.                 newValue = 1; // Mobile
  709.             } else if (width < 768) {
  710.                 newValue = 2; // Petite tablette
  711.             } else if (width < 1024) {
  712.                 newValue = 3; // Tablette/Desktop
  713.             } else {
  714.                 newValue = 4; // Grand écran
  715.             }
  716.             
  717.             // TRÈS IMPORTANT : Ne jamais afficher plus de cartes que de partenaires
  718.             this.slidesPerView = Math.min(newValue, this.partenaires.length);
  719.             
  720.             // Ajuster la slide courante si nécessaire
  721.             const totalSlides = Math.ceil(this.partenaires.length / this.slidesPerView);
  722.             if (this.currentSlide >= totalSlides) {
  723.                 this.currentSlide = Math.max(0, totalSlides - 1);
  724.             }
  725.         },
  726.         startAutoplay() {
  727.             this.stopAutoplay();
  728.             if (this.partenaires.length > this.slidesPerView) {
  729.                 this.autoplayInterval = setInterval(() => this.nextSlide(), 5000);
  730.             }
  731.         },
  732.         stopAutoplay() {
  733.             if (this.autoplayInterval) {
  734.                 clearInterval(this.autoplayInterval);
  735.                 this.autoplayInterval = null;
  736.             }
  737.         },
  738.         nextSlide() {
  739.             if (!this.partenaires?.length) return;
  740.             const totalSlides = Math.ceil(this.partenaires.length / this.slidesPerView);
  741.             if (totalSlides <= 1) return;
  742.             this.currentSlide = (this.currentSlide + 1) % totalSlides;
  743.         },
  744.         prevSlide() {
  745.             if (!this.partenaires?.length) return;
  746.             const totalSlides = Math.ceil(this.partenaires.length / this.slidesPerView);
  747.             if (totalSlides <= 1) return;
  748.             this.currentSlide = (this.currentSlide - 1 + totalSlides) % totalSlides;
  749.         },
  750.         goToSlide(index) {
  751.             const totalSlides = Math.ceil(this.partenaires.length / this.slidesPerView);
  752.             if (index >= 0 && index < totalSlides) this.currentSlide = index;
  753.         }
  754.     };
  755.     
  756.     return carouselInstance;
  757. };
  758. </script>
  759. <!-- Alpine.js ENSUITE -->
  760. <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.10/dist/cdn.min.js" ></script>
  761. {{ parent() }}
  762. <script src="{{ asset('public/js/animation.js') }}"></script>
  763. <script src="{{ asset('public/js/common.js') }}"></script>
  764. <script>
  765. // ==================== GESTION DE LA MODALE ====================
  766. document.addEventListener('DOMContentLoaded', function() {
  767.     // Vérifier que Alpine est chargé
  768.     console.log('DOM chargé, Alpine.js disponible:', typeof Alpine !== 'undefined');
  769.     
  770.     // Initialisation des établissements et métiers
  771.     initEtablissements();
  772.     initMetiers();
  773.     
  774.     // Gestion de la modale
  775.     initModal();
  776. });
  777. // Fonction pour initialiser la modale
  778. function initModal() {
  779.     const modal = document.getElementById('etablissement-modal');
  780.     if (!modal) return;
  781.     
  782.     const modalMetierName = document.getElementById('modal-metier-name');
  783.     const modalList = document.getElementById('modal-etablissements-list');
  784.     const closeButtons = document.querySelectorAll('.modal-close');
  785.     const backdrop = document.querySelector('.modal-backdrop');
  786.     
  787.     window.openEtablissementsModal = function(metierId, metierNom, etablissementsData) {
  788.         try {
  789.             const etablissements = JSON.parse(etablissementsData);
  790.             
  791.             modalMetierName.textContent = `Métier: ${metierNom}`;
  792.             
  793.             let html = '';
  794.             etablissements.forEach(em => {
  795.                 html += `
  796.                     <a href="/candidature/new?etablissement=${em.id}&metier=${metierId}" 
  797.                        class="block p-2 sm:p-3 bg-slate-50 hover:bg-indigo-50 rounded-lg transition-colors group">
  798.                         <div class="flex items-center justify-between">
  799.                             <span class="text-sm sm:text-base font-medium text-slate-900 group-hover:text-indigo-700">${em.nom}</span>
  800.                             <i class="fas fa-chevron-right text-slate-400 group-hover:text-indigo-600 text-xs sm:text-sm"></i>
  801.                         </div>
  802.                         ${em.places ? `<p class="text-xs text-slate-500 mt-1">${em.places} place(s) disponible(s)</p>` : ''}
  803.                     </a>
  804.                 `;
  805.             });
  806.             modalList.innerHTML = html;
  807.             
  808.             modal.classList.remove('hidden');
  809.             document.body.style.overflow = 'hidden';
  810.             
  811.         } catch (e) {
  812.             console.error('Erreur:', e);
  813.         }
  814.     };
  815.     
  816.     function closeModal() {
  817.         modal.classList.add('hidden');
  818.         document.body.style.overflow = '';
  819.     }
  820.     
  821.     closeButtons.forEach(btn => {
  822.         btn.addEventListener('click', closeModal);
  823.     });
  824.     
  825.     if (backdrop) {
  826.         backdrop.addEventListener('click', closeModal);
  827.     }
  828.     
  829.     document.addEventListener('keydown', function(e) {
  830.         if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
  831.             closeModal();
  832.         }
  833.     });
  834.     
  835.     document.addEventListener('click', function(e) {
  836.         const btn = e.target.closest('.etablissement-modal-btn');
  837.         if (btn) {
  838.             e.preventDefault();
  839.             const metierId = btn.dataset.metierId;
  840.             const metierNom = btn.dataset.metierNom;
  841.             const etablissements = btn.dataset.etablissements;
  842.             openEtablissementsModal(metierId, metierNom, etablissements);
  843.         }
  844.     });
  845. }
  846. // ==================== GESTION DES ÉTABLISSEMENTS ====================
  847. function initEtablissements() {
  848.     const elements = {
  849.         champRecherche: document.getElementById('etablissement-search'),
  850.         listeDirection: document.getElementById('dr-filter'),
  851.         grille: document.getElementById('etablissements-grid'),
  852.         indicateurChargement: document.getElementById('etablissements-loading'),
  853.         zonePagination: document.getElementById('etablissements-pagination')
  854.     };
  855.     
  856.     if (!elements.grille) return;
  857.     
  858.     let etatEtablissements = {
  859.         pageCourante: {{ pageEtablissementActuelle|default(1) }},
  860.         nombreTotalPages: Math.ceil({{ totalEtablissements|default(0) }} / 9) || 1,
  861.         enCoursDeChargement: false,
  862.         temporisateur: null
  863.     };
  864.     
  865.     async function chargerEtablissements(numeroPage = 1) {
  866.         if (etatEtablissements.enCoursDeChargement) return;
  867.         
  868.         const direction = elements.listeDirection?.value || '';
  869.         const recherche = elements.champRecherche?.value.trim() || '';
  870.         
  871.         etatEtablissements.enCoursDeChargement = true;
  872.         if (elements.indicateurChargement) elements.indicateurChargement.classList.remove('hidden');
  873.         
  874.         try {
  875.             const url = new URL('/load-etablissements', window.location.origin);
  876.             url.searchParams.append('page', numeroPage);
  877.             url.searchParams.append('limit', 9);
  878.             if (direction) url.searchParams.append('direction', direction);
  879.             if (recherche) url.searchParams.append('recherche', recherche);
  880.             
  881.             const reponse = await fetch(url);
  882.             const donnees = await reponse.json();
  883.             
  884.             if (elements.grille) {
  885.                 elements.grille.innerHTML = donnees.html;
  886.             }
  887.             
  888.             etatEtablissements.pageCourante = donnees.page;
  889.             etatEtablissements.nombreTotalPages = Math.ceil(donnees.total / 9);
  890.             
  891.             mettreAJourPaginationEtablissements();
  892.             
  893.         } catch (erreur) {
  894.             console.error('Erreur chargement établissements:', erreur);
  895.         } finally {
  896.             etatEtablissements.enCoursDeChargement = false;
  897.             if (elements.indicateurChargement) {
  898.                 elements.indicateurChargement.classList.add('hidden');
  899.             }
  900.         }
  901.     }
  902.     
  903.     function mettreAJourPaginationEtablissements() {
  904.         if (!elements.zonePagination) return;
  905.         
  906.         if (etatEtablissements.nombreTotalPages <= 1) {
  907.             elements.zonePagination.innerHTML = '';
  908.             return;
  909.         }
  910.         
  911.         let html = '<nav class="flex items-center gap-1 sm:gap-2 flex-wrap justify-center">';
  912.         
  913.         html += `<button class="etablissements-prev px-2 sm:px-3 py-1 sm:py-2 rounded-lg border border-white/30 bg-white/10 backdrop-blur-sm text-white hover:bg-white/20 transition-all text-sm sm:text-base ${etatEtablissements.pageCourante === 1 ? 'opacity-50 cursor-not-allowed' : ''}" ${etatEtablissements.pageCourante === 1 ? 'disabled' : ''}>
  914.                     <i class="fas fa-chevron-left text-xs sm:text-sm"></i>
  915.                 </button>`;
  916.         
  917.         let pageDepart = Math.max(1, etatEtablissements.pageCourante - 2);
  918.         let pageFin = Math.min(etatEtablissements.nombreTotalPages, pageDepart + 4);
  919.         
  920.         if (pageDepart > 1) {
  921.             html += `<button class="etablissements-page px-2 sm:px-4 py-1 sm:py-2 rounded-lg border border-white/30 bg-white/10 backdrop-blur-sm text-white hover:bg-white/20 text-sm sm:text-base" data-page="1">1</button>`;
  922.             if (pageDepart > 2) {
  923.                 html += `<span class="px-1 sm:px-2 text-white/50 text-sm sm:text-base">...</span>`;
  924.             }
  925.         }
  926.         
  927.         for (let i = pageDepart; i <= pageFin; i++) {
  928.             html += `<button class="etablissements-page px-2 sm:px-4 py-1 sm:py-2 rounded-lg border transition-all text-sm sm:text-base 
  929.                 ${etatEtablissements.pageCourante === i 
  930.                     ? 'bg-white text-indigo-700 border-white font-bold' 
  931.                     : 'border-white/30 bg-white/10 backdrop-blur-sm text-white hover:bg-white/20'}" 
  932.                 data-page="${i}">${i}</button>`;
  933.         }
  934.         
  935.         if (pageFin < etatEtablissements.nombreTotalPages) {
  936.             if (pageFin < etatEtablissements.nombreTotalPages - 1) {
  937.                 html += `<span class="px-1 sm:px-2 text-white/50 text-sm sm:text-base">...</span>`;
  938.             }
  939.             html += `<button class="etablissements-page px-2 sm:px-4 py-1 sm:py-2 rounded-lg border border-white/30 bg-white/10 backdrop-blur-sm text-white hover:bg-white/20 text-sm sm:text-base" data-page="${etatEtablissements.nombreTotalPages}">${etatEtablissements.nombreTotalPages}</button>`;
  940.         }
  941.         
  942.         html += `<button class="etablissements-next px-2 sm:px-3 py-1 sm:py-2 rounded-lg border border-white/30 bg-white/10 backdrop-blur-sm text-white hover:bg-white/20 transition-all text-sm sm:text-base ${etatEtablissements.pageCourante === etatEtablissements.nombreTotalPages ? 'opacity-50 cursor-not-allowed' : ''}" ${etatEtablissements.pageCourante === etatEtablissements.nombreTotalPages ? 'disabled' : ''}>
  943.                     <i class="fas fa-chevron-right text-xs sm:text-sm"></i>
  944.                 </button>`;
  945.         
  946.         html += '</nav>';
  947.         elements.zonePagination.innerHTML = html;
  948.         
  949.         elements.zonePagination.querySelectorAll('.etablissements-page').forEach(btn => {
  950.             btn.addEventListener('click', (e) => {
  951.                 e.preventDefault();
  952.                 const page = parseInt(e.currentTarget.dataset.page);
  953.                 if (page !== etatEtablissements.pageCourante) {
  954.                     chargerEtablissements(page);
  955.                 }
  956.             });
  957.         });
  958.         
  959.         elements.zonePagination.querySelector('.etablissements-prev')?.addEventListener('click', (e) => {
  960.             e.preventDefault();
  961.             if (etatEtablissements.pageCourante > 1 && !e.currentTarget.disabled) {
  962.                 chargerEtablissements(etatEtablissements.pageCourante - 1);
  963.             }
  964.         });
  965.         
  966.         elements.zonePagination.querySelector('.etablissements-next')?.addEventListener('click', (e) => {
  967.             e.preventDefault();
  968.             if (etatEtablissements.pageCourante < etatEtablissements.nombreTotalPages && !e.currentTarget.disabled) {
  969.                 chargerEtablissements(etatEtablissements.pageCourante + 1);
  970.             }
  971.         });
  972.     }
  973.     
  974.     if (elements.listeDirection) {
  975.         elements.listeDirection.addEventListener('change', () => chargerEtablissements(1));
  976.     }
  977.     
  978.     if (elements.champRecherche) {
  979.         elements.champRecherche.addEventListener('input', () => {
  980.             clearTimeout(etatEtablissements.temporisateur);
  981.             etatEtablissements.temporisateur = setTimeout(() => chargerEtablissements(1), 500);
  982.         });
  983.     }
  984.     
  985.     mettreAJourPaginationEtablissements();
  986. }
  987. // ==================== GESTION DES MÉTIERS ====================
  988. function initMetiers() {
  989.     const elements = {
  990.         champRecherche: document.getElementById('search-input'),
  991.         listeSecteur: document.getElementById('secteur-filter'),
  992.         grille: document.getElementById('metiers-grid'),
  993.         indicateurChargement: document.getElementById('loading'),
  994.         zonePagination: document.getElementById('pagination')
  995.     };
  996.     
  997.     if (!elements.grille) return;
  998.     
  999.     let etatMetiers = {
  1000.         pageCourante: {{ pageMetierActuelle|default(1) }},
  1001.         nombreTotalPages: Math.ceil({{ totalMetiers|default(0) }} / 9) || 1,
  1002.         enCoursDeChargement: false,
  1003.         temporisateur: null
  1004.     };
  1005.     
  1006.     async function chargerMetiers(numeroPage = 1) {
  1007.         if (etatMetiers.enCoursDeChargement) return;
  1008.         
  1009.         const secteur = elements.listeSecteur?.value || '';
  1010.         const recherche = elements.champRecherche?.value.trim() || '';
  1011.         
  1012.         etatMetiers.enCoursDeChargement = true;
  1013.         if (elements.indicateurChargement) elements.indicateurChargement.classList.remove('hidden');
  1014.         
  1015.         try {
  1016.             const url = new URL('/load-metiers', window.location.origin);
  1017.             url.searchParams.append('page', numeroPage);
  1018.             url.searchParams.append('limit', 9);
  1019.             if (secteur) url.searchParams.append('secteur', secteur);
  1020.             if (recherche) url.searchParams.append('recherche', recherche);
  1021.             
  1022.             const reponse = await fetch(url);
  1023.             const donnees = await reponse.json();
  1024.             
  1025.             if (elements.grille) {
  1026.                 elements.grille.innerHTML = donnees.html;
  1027.             }
  1028.             
  1029.             etatMetiers.pageCourante = donnees.page;
  1030.             etatMetiers.nombreTotalPages = Math.ceil(donnees.total / 9);
  1031.             
  1032.             mettreAJourPaginationMetiers();
  1033.             
  1034.         } catch (erreur) {
  1035.             console.error('Erreur chargement métiers:', erreur);
  1036.         } finally {
  1037.             etatMetiers.enCoursDeChargement = false;
  1038.             if (elements.indicateurChargement) {
  1039.                 elements.indicateurChargement.classList.add('hidden');
  1040.             }
  1041.         }
  1042.     }
  1043.     
  1044.     function mettreAJourPaginationMetiers() {
  1045.         if (!elements.zonePagination) return;
  1046.         
  1047.         if (etatMetiers.nombreTotalPages <= 1) {
  1048.             elements.zonePagination.innerHTML = '';
  1049.             return;
  1050.         }
  1051.         
  1052.         let html = '<nav class="flex items-center gap-1 sm:gap-2 flex-wrap justify-center">';
  1053.         
  1054.         html += `<button class="metiers-prev px-2 sm:px-3 py-1 sm:py-2 rounded-lg border border-indigo-200 bg-white text-indigo-600 hover:bg-indigo-50 transition-all text-sm sm:text-base ${etatMetiers.pageCourante === 1 ? 'opacity-50 cursor-not-allowed' : ''}" ${etatMetiers.pageCourante === 1 ? 'disabled' : ''}>
  1055.                     <i class="fas fa-chevron-left text-xs sm:text-sm"></i>
  1056.                 </button>`;
  1057.         
  1058.         let pageDepart = Math.max(1, etatMetiers.pageCourante - 2);
  1059.         let pageFin = Math.min(etatMetiers.nombreTotalPages, pageDepart + 4);
  1060.         
  1061.         if (pageDepart > 1) {
  1062.             html += `<button class="metiers-page px-2 sm:px-4 py-1 sm:py-2 rounded-lg border border-indigo-200 bg-white text-indigo-600 hover:bg-indigo-50 text-sm sm:text-base" data-page="1">1</button>`;
  1063.             if (pageDepart > 2) {
  1064.                 html += `<span class="px-1 sm:px-2 text-indigo-400 text-sm sm:text-base">...</span>`;
  1065.             }
  1066.         }
  1067.         
  1068.         for (let i = pageDepart; i <= pageFin; i++) {
  1069.             html += `<button class="metiers-page px-2 sm:px-4 py-1 sm:py-2 rounded-lg border transition-all text-sm sm:text-base 
  1070.                 ${etatMetiers.pageCourante === i 
  1071.                     ? 'bg-indigo-600 text-white border-indigo-600 font-bold' 
  1072.                     : 'border-indigo-200 bg-white text-indigo-600 hover:bg-indigo-50'}" 
  1073.                 data-page="${i}">${i}</button>`;
  1074.         }
  1075.         
  1076.         if (pageFin < etatMetiers.nombreTotalPages) {
  1077.             if (pageFin < etatMetiers.nombreTotalPages - 1) {
  1078.                 html += `<span class="px-1 sm:px-2 text-indigo-400 text-sm sm:text-base">...</span>`;
  1079.             }
  1080.             html += `<button class="metiers-page px-2 sm:px-4 py-1 sm:py-2 rounded-lg border border-indigo-200 bg-white text-indigo-600 hover:bg-indigo-50 text-sm sm:text-base" data-page="${etatMetiers.nombreTotalPages}">${etatMetiers.nombreTotalPages}</button>`;
  1081.         }
  1082.         
  1083.         html += `<button class="metiers-next px-2 sm:px-3 py-1 sm:py-2 rounded-lg border border-indigo-200 bg-white text-indigo-600 hover:bg-indigo-50 transition-all text-sm sm:text-base ${etatMetiers.pageCourante === etatMetiers.nombreTotalPages ? 'opacity-50 cursor-not-allowed' : ''}" ${etatMetiers.pageCourante === etatMetiers.nombreTotalPages ? 'disabled' : ''}>
  1084.                     <i class="fas fa-chevron-right text-xs sm:text-sm"></i>
  1085.                 </button>`;
  1086.         
  1087.         html += '</nav>';
  1088.         elements.zonePagination.innerHTML = html;
  1089.         
  1090.         elements.zonePagination.querySelectorAll('.metiers-page').forEach(btn => {
  1091.             btn.addEventListener('click', (e) => {
  1092.                 e.preventDefault();
  1093.                 const page = parseInt(e.currentTarget.dataset.page);
  1094.                 if (page !== etatMetiers.pageCourante) {
  1095.                     chargerMetiers(page);
  1096.                 }
  1097.             });
  1098.         });
  1099.         
  1100.         elements.zonePagination.querySelector('.metiers-prev')?.addEventListener('click', (e) => {
  1101.             e.preventDefault();
  1102.             if (etatMetiers.pageCourante > 1 && !e.currentTarget.disabled) {
  1103.                 chargerMetiers(etatMetiers.pageCourante - 1);
  1104.             }
  1105.         });
  1106.         
  1107.         elements.zonePagination.querySelector('.metiers-next')?.addEventListener('click', (e) => {
  1108.             e.preventDefault();
  1109.             if (etatMetiers.pageCourante < etatMetiers.nombreTotalPages && !e.currentTarget.disabled) {
  1110.                 chargerMetiers(etatMetiers.pageCourante + 1);
  1111.             }
  1112.         });
  1113.     }
  1114.     
  1115.     if (elements.listeSecteur) {
  1116.         elements.listeSecteur.addEventListener('change', () => chargerMetiers(1));
  1117.     }
  1118.     
  1119.     if (elements.champRecherche) {
  1120.         elements.champRecherche.addEventListener('input', () => {
  1121.             clearTimeout(etatMetiers.temporisateur);
  1122.             etatMetiers.temporisateur = setTimeout(() => chargerMetiers(1), 500);
  1123.         });
  1124.     }
  1125.     
  1126.     mettreAJourPaginationMetiers();
  1127. }
  1128. </script>
  1129. {% endblock %}