templates/home/index.html.twig line 1

Open in your IDE?
  1. {% extends 'base.html.twig' %}
  2. {% block title %}{{ title ?? 'Accueil' }}{% endblock %}
  3. {% block meta_description %}ACOA — Conseil & Formation.{% endblock %}
  4. {% set canEdit = is_granted('ROLE_MANAGER') %}
  5. {% block stylesheets %}
  6. {{ parent() }}
  7. <link rel="stylesheet" href="{{ asset('assets/home.css') }}">
  8. {% endblock %}
  9. {% block javascripts %}
  10. {{ parent() }}
  11. <script defer src="{{ asset('assets/home.js') }}"></script>
  12. {% endblock %}
  13. {% block content %}
  14. {% if not canEdit %}
  15. <style>
  16.   #cms-devbar,
  17.   .cms-edit-fab,
  18.   .cms-edit-btn,
  19.   .cms-actu-edit,
  20.   .cms-kpis-edit,
  21.   .catalogue-edit-btn,
  22.   #catalogue-edit-modal,
  23.   #actu-editor,
  24.   #kpis-editor {
  25.     display: none !important;
  26.   }
  27. </style>
  28. {% endif %}
  29. <style>
  30.    .catalogue-grid{
  31.    display:grid;
  32.    grid-template-columns:1fr 420px;  
  33.    align-items:center;
  34.    gap:56px;
  35.    }
  36.    .catalogue-art{
  37.    position:relative;
  38.    max-width:420px;
  39.    margin-left:auto;         
  40.    overflow:visible;         
  41.    }
  42.   
  43.   .catalogue-art::before{
  44.    content:"";
  45.    position:absolute;
  46.    top:-22px;
  47.    right:-12px;
  48.    width:calc(100% + 12px);
  49.    height:calc(100% + 36px);
  50.    background:#0A2A45;
  51.    border-radius:14px;
  52.    z-index:0;
  53.    box-shadow:0 12px 26px rgba(7,51,73,.22);
  54.    }
  55.    .cat-card{
  56.    position:relative;
  57.    z-index:1;                
  58.    left:-80px;                
  59.    top:50px;                  
  60.    width:calc(100% + 28px); 
  61.    border-radius:14px;
  62.    overflow:hidden;         
  63.    }
  64.    .cat-card img{
  65.    display:block;
  66.    width:100%;
  67.    height:auto;
  68.    border-radius:14px;
  69.    }
  70.    .cat-veil{
  71.    position:absolute;
  72.    inset:0;
  73.    background:rgba(255,255,255,.42);
  74.    backdrop-filter:blur(1px);
  75.    -webkit-backdrop-filter:blur(1px);
  76.    border-radius:14px;
  77.    z-index:2;
  78.    pointer-events:none;
  79.    }
  80.    .cat-overlay{
  81.    position:absolute;
  82.    inset:0;
  83.    border-radius:14px;
  84.    z-index:3;
  85.    pointer-events:none;
  86.    }
  87.    .cat-vrail{ position:absolute; top:16px; left:18px; display:flex; gap:12px; }
  88.    .cat-vtitle{
  89.    writing-mode:vertical-rl; transform:rotate(180deg);
  90.    font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  91.    font-weight:800; letter-spacing:.02em;
  92.    font-size:clamp(24px,2.6vw,44px);
  93.    color:#143F63;
  94.    }
  95.    .cat-vsubtitle{
  96.    writing-mode:vertical-rl; transform:rotate(180deg);
  97.    font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  98.    font-weight:700; letter-spacing:.02em;
  99.    font-size:clamp(12px,1.4vw,22px);
  100.    color:#F14816;
  101.    }
  102.    .cat-year{
  103.    position:absolute;
  104.    left:22px; bottom:78px;
  105.    font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  106.    font-weight:800;
  107.    font-size:clamp(22px,2.4vw,38px);
  108.    color:#143F63;
  109.    }
  110.    .cat-badge{
  111.    position:absolute;
  112.    left:50%; bottom:14px; transform:translateX(-50%);
  113.    width:84%;
  114.    padding:12px 18px;
  115.    border-radius:9999px;
  116.    background:#F14816;
  117.    color:#fff;
  118.    font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  119.    font-weight:800;
  120.    text-transform:uppercase;
  121.    text-align:center;
  122.    line-height:1.25;
  123.    font-size:clamp(10px,1.1vw,13px);
  124.    box-shadow:0 6px 16px rgba(241,72,22,.30);
  125.    }
  126.    @media (max-width:768px){
  127.    .catalogue-grid{
  128.    grid-template-columns:1fr;
  129.    gap:28px;
  130.    text-align:center;
  131.    }
  132.    .catalogue-art{ margin:0 auto; }
  133.    .cat-card{ left:-22px; top:6px; width:calc(100% + 22px); }
  134.    }
  135.    .cat-veil{
  136.    inset: 0 4px 0 0; 
  137.    }
  138.  .catalogue-grid{
  139.    grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  140.    column-gap: clamp(28px, 5vw, 120px);
  141.    }
  142. @media (max-width: 1100px){
  143.    .catalogue-grid{
  144.       grid-template-columns: 1fr;
  145.       gap: 28px;
  146.    }
  147. }
  148.    section.catalogue .catalogue-copy{
  149.    max-width: 640px;
  150.    }
  151.    section.catalogue .catalogue-copy .sec-kicker{
  152.    font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
  153.    color: #F14816;
  154.    font-weight: 800;
  155.    text-transform: uppercase;
  156.    letter-spacing: .08em;
  157.    font-size: clamp(12px, 1.1vw, 16px);
  158.    margin: 0 0 12px 0;
  159.    }
  160.    section.catalogue .catalogue-copy .sec-title{
  161.    font-weight:800;
  162.    line-height:1.25;
  163.    letter-spacing:.02em;
  164.    text-transform:uppercase;
  165.    font-size:clamp(18px,1.05vw,22px);
  166.    max-width:520px;
  167.    }
  168.    section.catalogue .catalogue-copy .sec-text{
  169.    color: #536273;                 
  170.    font-size: clamp(14px, 1.15vw, 18px);
  171.    line-height: 1.65;
  172.    margin: 0 0 20px 0;
  173.    max-width: 620px;
  174.    }
  175.    section.catalogue .catalogue-copy .btn-row{
  176.    display: flex;
  177.    gap: 16px;
  178.    align-items: center;
  179.    }
  180.    section.catalogue .catalogue-copy .btn.btn-primary{
  181.    background: #F14816;
  182.    color: #fff;
  183.    border: 0;
  184.    padding: 12px 22px;
  185.    border-radius: 9999px;
  186.    font-weight: 700;
  187.    font-size: clamp(14px, 1.05vw, 16px);
  188.    box-shadow: 0 6px 16px rgba(241,72,22,.25);
  189.    }
  190.    section.catalogue .catalogue-copy .btn.btn-primary:hover{
  191.    filter: brightness(1.05);
  192.    }
  193.    section.catalogue .catalogue-copy .btn.btn-outline{
  194.    background: #fff;
  195.    color: #F14816;
  196.    border: 1px solid rgba(20,63,99,.10);
  197.    padding: 12px 22px;
  198.    border-radius: 9999px;
  199.    font-weight: 700;
  200.    font-size: clamp(14px, 1.05vw, 16px);
  201.    box-shadow: 0 6px 18px rgba(17,24,39,.08);
  202.    }
  203.    section.catalogue .catalogue-copy .btn.btn-outline:hover{
  204.    background: #fff7f3;
  205.    border-color: rgba(241,72,22,.35);
  206.    }
  207.    @media (max-width: 768px){
  208.    section.catalogue .catalogue-copy{ max-width: 100%; }
  209.    section.catalogue .catalogue-copy .btn-row{ flex-wrap: wrap; }
  210.    }
  211.    section.catalogue .catalogue-copy .sec-title{
  212.    color:#F14816;
  213.    }
  214.    .container{max-width:1180px;margin:0 auto;padding-left:12px;padding-right:12px}
  215.    .hero-title{
  216.    font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  217.    font-weight:700; line-height:1.05; letter-spacing:.002em;
  218.    font-size:clamp(28px,3.6vw,46px);
  219.    color:#fff; margin:10px 0 18px 0;
  220.    }
  221.    .hero-kicker{
  222.    font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  223.    text-transform:uppercase; font-weight:800; letter-spacing:.08em;
  224.    font-size:clamp(11px,1.1vw,13px); color:#fff;
  225.    opacity:.95;
  226.    }
  227.    .hero-ctas .btn{display:inline-flex;align-items:center;justify-content:center;
  228.    padding:12px 22px;border-radius:9999px;font-weight:700}
  229.    .hero-ctas .btn-primary{background:#F14816;color:#fff;box-shadow:0 6px 16px rgba(241,72,22,.25)}
  230.    .hero-ctas .btn-primary:hover{filter:brightness(1.05)}
  231.    .hero-dots{position:absolute;left:50%;bottom:24px;transform:translateX(-50%);z-index:4;display:flex;gap:8px}
  232.    .hero-dots button{width:8px;height:8px;border-radius:9999px;background:rgba(255,255,255,.6)}
  233.    .hero-dots button[aria-selected="true"]{background:#fff}
  234.    .panel{padding:34px 26px}
  235.    /* Supprimer le radius uniquement du background principal */
  236.    .hero,
  237.    .hero-track,
  238.    .hero-slide {
  239.    border-radius: 0 !important;
  240.    }
  241.    .panel--blue{
  242.    background:#0A2A45;
  243.    color:#fff;
  244.    background-image:linear-gradient(180deg,rgba(255,255,255,.02),transparent);
  245.    }
  246.    .sec-title{font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  247.    font-weight:800;letter-spacing:.01em;margin:0 0 22px 0;color:#143F63;font-size:clamp(24px,2.8vw,34px)}
  248.    .panel--blue .sec-title{color:#fff}
  249.    .sec-kicker{font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  250.    color:#F14816;text-transform:uppercase;font-weight:800;letter-spacing:.08em;font-size:12px;margin:0 0 10px 0}
  251.    .center{text-align:center}
  252.    .competences .competences-grid{
  253.    margin-top:18px; display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:18px;
  254.    }
  255.    .comp-card{
  256.    display:flex;flex-direction:column;align-items:center;gap:10px;
  257.    padding:14px;border-radius:12px;background:rgba(255,255,255,.06);min-height:88px;text-align:center
  258.    }
  259.    .comp-card .comp-icon{width:28px;height:28px}
  260.    .comp-card p{font-size:13px;line-height:1.35}
  261.    .prestations .prestations-grid{
  262.    margin-top:10px; display:grid; grid-template-columns:repeat(3,1fr); gap:18px;
  263.    }
  264.    .presta-card{
  265.    position:relative;border-radius:14px;overflow:hidden;min-height:330px;
  266.    background: #0a2a45;
  267.    }
  268.    .presta-card::before{
  269.    content:"";position:absolute;inset:0;background:var(--img) center/cover no-repeat;z-index:0
  270.    }
  271.    .presta-card::after{
  272.    content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(10,42,69,.0) 20%,rgba(10,42,69,.75) 75%);z-index:1
  273.    }
  274.    .presta-inner{position:relative;z-index:2;color:#fff;display:flex;flex-direction:column;gap:10px;
  275.    padding:20px 18px;height:100%;justify-content:flex-end}
  276.    .presta-inner h3{font-weight:800;font-size:18px;margin:0}
  277.    .presta-inner p{font-size:14px;opacity:.95}
  278.    .presta-cta{align-self:flex-start}
  279.    .actu .actu-wrap{display:grid;grid-template-columns:1.1fr .9fr;align-items:center;gap:20px}
  280.    .actu-side{position:relative;height:360px}
  281.    .side-card{position:absolute;right:0;width:68%;height:92%;border-radius:14px;background:#6C9FB4}
  282.    .side-card.layer-1{transform:translate(0,0)}
  283.    .side-card.layer-2{right:18px;top:18px;background:#2C72A0}
  284.    .side-card.layer-3{right:36px;top:36px;background:#0A2A45}
  285.    .side-label{position:absolute;right:12px;bottom:18px;transform:rotate(-90deg);transform-origin:bottom right;
  286.    color:#fff;font-weight:800;letter-spacing:.08em}
  287.    .logos-row{display:flex;gap:36px;align-items:center;justify-content:center;flex-wrap:wrap}
  288.    .logo{height:38px;width:auto;opacity:.9}
  289.    .kpi-row{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:10px}
  290.    .kpi{background:rgba(255,255,255,.06);border-radius:14px;padding:18px;color:#fff;text-align:center}
  291.    .kpi-value{font-weight:800;font-size:38px;line-height:1}
  292.    .kpi-label{opacity:.95;margin-top:6px}
  293.    @media (max-width: 992px){
  294.    .competences .competences-grid{grid-template-columns:repeat(3,1fr)}
  295.    .prestations .prestations-grid{grid-template-columns:1fr}
  296.    .actu .actu-wrap{grid-template-columns:1fr}
  297.    }
  298.    .competences .panel--blue{
  299.    background-color:#041F60;
  300.    background-image:
  301.    linear-gradient(180deg, rgba(255,255,255,.02), transparent),
  302.    url('{{ asset('assets/img/img-conseiller.png') }}');
  303.    background-size: cover;
  304.    background-position: center;
  305.    background-repeat: no-repeat;
  306.    }
  307.    .competences .panel--blue {
  308.    position: relative;
  309.    background: url('{{ asset('assets/img/img-conseiller.png') }}') center/cover no-repeat;
  310.    border-radius: 18px;
  311.    overflow: hidden; 
  312.    }
  313.    .competences .panel--blue::before {
  314.    content: "";
  315.    position: absolute;
  316.    inset: 0;
  317.    background: rgba(4, 31, 96, 0.75); 
  318.    z-index: 0;
  319.    }
  320.    .competences .panel--blue > * {
  321.    position: relative;
  322.    z-index: 1;
  323.    }
  324.    .competences .sec-title{
  325.    margin: 0 0 18px 0;
  326.    text-align: left !important;
  327.    color: #fff !important;
  328.    text-transform: uppercase;
  329.    font-weight: 800;
  330.    letter-spacing: .02em;
  331.    font-size: clamp(18px, 1.7vw, 22px);
  332.    }
  333.    .competences .competences-grid{
  334.    margin-top: 8px;
  335.    display: grid;
  336.    grid-template-columns: repeat(5, minmax(0,1fr));
  337.    gap: 22px 26px;
  338.    }
  339.    .comp-card{
  340.    display: inline-flex;
  341.    align-items: center;
  342.    gap: 12px;
  343.    padding: 14px 18px;
  344.    border-radius: 16px;
  345.    background: transparent;           
  346.    color: #fff;                      
  347.    box-shadow: none;
  348.    transition: background .18s ease, color .18s ease, box-shadow .18s ease, transform .16s ease;
  349.    }
  350.    .comp-icon{
  351.    width: 28px; height: 28px; object-fit: contain;
  352.    filter: brightness(0) invert(1);   
  353.    transition: filter .18s ease;
  354.    }
  355.    .comp-card p{
  356.    margin: 0;
  357.    font-weight: 700;
  358.    font-size: clamp(13px, 1.05vw, 16px);
  359.    line-height: 1.25;
  360.    text-align: left;
  361.    }
  362.    .panel .btn.btn-secondary{
  363.    background: transparent !important;
  364.    color: #F14816 !important;
  365.    border: 2px solid #F14816 !important;
  366.    border-radius: 9999px;
  367.    padding: 12px 28px;                
  368.    font-weight: 800;
  369.    box-shadow: none !important;
  370.    transition: background .18s ease, color .18s ease, transform .16s ease;
  371.    }
  372.    .panel .btn.btn-secondary:hover{
  373.    background: #F14816 !important;
  374.    color: #fff !important;
  375.    transform: translateY(-1px);
  376.    }
  377.    @media (max-width: 1100px){
  378.    .competences .competences-grid{ grid-template-columns: repeat(3,1fr); }
  379.    }
  380.    @media (max-width: 640px){
  381.    .competences .competences-grid{ grid-template-columns: repeat(2,1fr); }
  382.    }
  383.    .competences .sec-title{
  384.    margin: 0 0 18px 0;
  385.    text-align: left !important;
  386.    color: #fff !important;
  387.    text-transform: uppercase;
  388.    font-weight: 800;
  389.    letter-spacing: .02em;
  390.    font-size: clamp(18px,1.7vw,22px);
  391.    }
  392.    .competences .competences-grid{
  393.    margin-top: 10px;
  394.    display: grid;
  395.    grid-template-columns: repeat(5, minmax(0,1fr));
  396.    gap: 22px 28px;
  397.    align-items: start;
  398.    }
  399.    .competences .comp-card{
  400.    display: inline-flex !important;
  401.    flex-direction: row !important;
  402.    align-items: center !important;
  403.    gap: 12px !important;
  404.    padding: 14px 18px !important;
  405.    border-radius: 16px !important;
  406.    background: transparent !important;      
  407.    box-shadow: none !important;
  408.    color: #fff !important;                  
  409.    transition: background .18s ease, color .18s ease,
  410.    box-shadow .18s ease, transform .16s ease;
  411.    }
  412.    .competences .comp-card .comp-icon{
  413.    width: 28px; height: 28px; object-fit: contain;
  414.    margin: 0; display: inline-block;
  415.    filter: brightness(0) invert(1);        
  416.    transition: filter .18s ease, opacity .18s ease;
  417.    }
  418.    .competences .comp-card p{
  419.    margin: 0;
  420.    font-weight: 800;
  421.    font-size: clamp(14px,1.05vw,16px);
  422.    line-height: 1.25;
  423.    text-align: left;
  424.    }
  425.    .competences .comp-card:hover,
  426.    .competences .comp-card.is-active{         
  427.    background: #fff !important;
  428.    color: #143F63 !important;              
  429.    box-shadow: 0 10px 22px rgba(10,42,69,.18);
  430.    transform: translateY(-1px);
  431.    }
  432.    .competences .comp-card:hover .comp-icon,
  433.    .competences .comp-card.is-active .comp-icon{
  434.    filter: invert(15%) sepia(18%) saturate(1431%)
  435.    hue-rotate(173deg);
  436.    }
  437.    .panel .btn.btn-secondary{
  438.    background: transparent !important;
  439.    color: #F14816 !important;
  440.    border: 2px solid #F14816 !important;
  441.    border-radius: 9999px !important;
  442.    padding: 12px 32px !important;          
  443.    font-weight: 800 !important;
  444.    box-shadow: none !important;
  445.    transition: background .18s ease, color .18s ease, transform .16s ease;
  446.    }
  447.    .panel .btn.btn-secondary:hover{
  448.    background: #F14816 !important;
  449.    color: #fff !important;
  450.    transform: translateY(-1px);
  451.    }
  452.    @media (max-width: 1100px){
  453.    .competences .competences-grid{ grid-template-columns: repeat(3,1fr); }
  454.    }
  455.    @media (max-width: 640px){
  456.    .competences .competences-grid{ grid-template-columns: repeat(2,1fr); }
  457.    }
  458.    .prestations .prestations-grid{
  459.    display:grid;
  460.    grid-template-columns:repeat(3,1fr);
  461.    gap:24px;
  462.    }
  463.    .prestations .presta-card{
  464.    position:relative;
  465.    border-radius:28px;
  466.    overflow:hidden;
  467.    min-height:360px;
  468.    }
  469.    .prestations .presta-card::before{
  470.    content:"";
  471.    position:absolute; inset:0;
  472.    background:var(--img) center/cover no-repeat;
  473.    z-index:0;
  474.    }
  475.    .prestations .presta-card::after{
  476.    content:"";
  477.    position:absolute; inset:0; z-index:1;
  478.    background:
  479.    linear-gradient(-45deg, var(--corner, transparent) 0 50%, transparent 50%) top right / 150px 150px no-repeat,
  480.    linear-gradient(0deg, var(--tint, rgba(10,42,69,.50))) center/cover no-repeat,
  481.    linear-gradient(180deg, rgba(10,42,69,0) 0%, rgba(10,42,69,.65) 100%);
  482.    box-shadow: inset 0 0 0 9999px rgba(0,0,0,.0);
  483.    }
  484.    .prestations .tone-c::after{
  485.    background:
  486.    linear-gradient(0deg, var(--line, transparent), var(--line, transparent)) center 46% / 100% 2px no-repeat,
  487.    linear-gradient(0deg, var(--tint, rgba(10,42,69,.50))) center/cover no-repeat,
  488.    linear-gradient(180deg, rgba(10,42,69,0) 0%, rgba(10,42,69,.65) 100%);
  489.    }
  490.    .prestations .tone-a{ --tint: rgba(34,142,160,.50); }      
  491.    .prestations .tone-b{ --tint: rgba(88,110,176,.52); --corner: rgba(255,255,255,.12); } 
  492.    .prestations .tone-c{ --tint: rgba(38,74,66,.52); --line: rgba(255,255,255,.35); }  
  493.    .prestations .presta-inner{
  494.    position:relative; z-index:2;
  495.    display:flex; flex-direction:column; height:100%;
  496.    padding:22px 22px 20px 22px;
  497.    color:#fff;
  498.    }
  499.    .prestations .presta-inner h3{
  500.    margin:0 0 10px 0;
  501.    font-weight:800; font-size:22px; line-height:1.15;
  502.    }
  503.    .prestations .presta-inner p{
  504.    margin:0 0 14px 0;
  505.    font-size:14px; line-height:1.65; opacity:.95;
  506.    max-width:90%;
  507.    }
  508.    .prestations .presta-cta{
  509.    margin-top:auto;                              
  510.    align-self:center;
  511.    background:#F14816; color:#fff;
  512.    border-radius:9999px;
  513.    padding:10px 24px;
  514.    font-weight:800;
  515.    box-shadow:0 10px 22px rgba(241,72,22,.25);
  516.    transition:transform .15s ease, filter .2s ease;
  517.    }
  518.    .prestations .presta-cta:hover{
  519.    filter:brightness(1.06);
  520.    transform:translateY(-1px);
  521.    }
  522.    @media (max-width: 980px){
  523.    .prestations .prestations-grid{ grid-template-columns:1fr; }
  524.    }
  525.    .actu .actu-wrap{
  526.    display:grid;
  527.    grid-template-columns: minmax(520px,1fr) 520px; 
  528.    align-items:center;
  529.    gap: clamp(16px,3vw,32px);
  530.    position:relative;
  531.    border-radius:28px;
  532.    overflow:hidden;
  533.    background:
  534.    radial-gradient(700px 280px at 160px calc(100% - 70px), rgba(0,0,0,.30), transparent 65%),
  535.    linear-gradient(180deg, #153E55 0%, #0A2A45 100%);
  536.    color:#fff;
  537.    padding: clamp(22px,4.2vw,56px);
  538.    }
  539.    .actu .actu-copy .sec-kicker{
  540.    color:#F14816; text-transform:uppercase; font-weight:800;
  541.    letter-spacing:.08em; margin:0 0 12px 0;
  542.    font-size: clamp(12px,1.05vw,15px);
  543.    }
  544.    .actu .actu-copy .sec-title{
  545.    margin:0 0 16px 0; color:#fff; font-weight:800; line-height:1.04;
  546.    letter-spacing:.002em; font-size: clamp(34px,5vw,72px);
  547.    }
  548.    .actu .actu-copy p{
  549.    margin:0 0 22px 0; max-width:58ch; line-height:1.65;
  550.    font-size: clamp(14px,1.15vw,18px); color:rgba(255,255,255,.94);
  551.    }
  552.    .actu .actu-copy .btn.btn-primary{
  553.    background:#F14816; color:#fff; border:0;
  554.    border-radius:9999px; padding:16px 28px; font-weight:800;
  555.    box-shadow:0 20px 38px rgba(241,72,22,.22);
  556.    transition:filter .2s ease, transform .15s ease;
  557.    }
  558.    .actu .actu-copy .btn.btn-primary:hover{ filter:brightness(1.07); transform:translateY(-1px); }
  559.    .actu .actu-side{
  560.    position:relative;
  561.    width:100%; height: clamp(280px, 40vw, 520px);
  562.    }
  563.    .actu .side-card{
  564.    position:absolute; border-radius:26px;
  565.    box-shadow: 0 14px 28px rgba(0,0,0,.18);
  566.    }
  567.    .actu .side-card.layer-1{
  568.    top: 18px; right: 0; left: 24%;
  569.    height: 64%;
  570.    border-radius: 24px;
  571.    background: #8FB8C7;
  572.    }
  573.    .actu .side-card.layer-2{
  574.    top: 62px; right: 28px; left: 20%;
  575.    height: 72%;
  576.    border-radius: 22px;
  577.    background: #2D7199;
  578.    }
  579.    .actu .side-card.layer-3{
  580.    top: 110px; right: 56px; left: 16%; bottom: 14px;
  581.    border-radius: 28px;
  582.    background: #08283B;
  583.    box-shadow:
  584.    inset 0 1px 0 rgba(255,255,255,.05),
  585.    0 18px 36px rgba(0,0,0,.22);
  586.    }
  587.    .actu .side-label{
  588.    position:absolute; right: -10px; bottom: -6px;
  589.    writing-mode: vertical-rl; transform: rotate(180deg);
  590.    color:#fff; font-weight:800; letter-spacing:.14em;
  591.    font-size: clamp(10px,1vw,13px); opacity:.9;
  592.    }
  593.    @media (max-width: 980px){
  594.    .actu .actu-wrap{ grid-template-columns: 1fr; }
  595.    .actu .actu-side{ order: 2; height: 320px; margin-top: 8px; }
  596.    }
  597.    .actu .actu-hero{
  598.    --brand:#F14816;
  599.    max-width: 1280px;
  600.    margin: 28px auto 56px;
  601.    min-height: 560px;
  602.    border-radius: 44px;
  603.    overflow: hidden;
  604.    position: relative;
  605.    display: grid;
  606.    grid-template-columns: 1fr 480px;
  607.    align-items: stretch;
  608.    background: var(--bg, none) 55% center/cover no-repeat;
  609.    box-shadow: 0 22px 40px rgba(0,0,0,.14), 0 2px 0 rgba(0,0,0,.05) inset;
  610.    color:#fff;
  611.    }
  612.    .actu .actu-hero::after{
  613.    content:""; position:absolute; left:40px; right:40px; bottom:-16px;
  614.    height:24px; border-radius:24px; filter: blur(16px); background: rgba(0,0,0,.2);
  615.    }
  616.    .actu .actu-shade{
  617.    position:absolute; inset:0;
  618.    background: linear-gradient(90deg, rgba(0,0,0,.70) 0%, rgba(0,0,0,.68) 60%, rgba(0,0,0,.38) 70%, rgba(0,0,0,.16) 75%, rgba(0,0,0,0) 78%);
  619.    pointer-events:none;
  620.    }
  621.    .actu .actu__content{
  622.    position: relative; z-index:2;
  623.    padding: 60px 56px 64px 56px;
  624.    display:flex; flex-direction:column; justify-content:center;
  625.    }
  626.    .actu .actu__content .sec-kicker{
  627.    color:#ffffff; text-transform:uppercase; font-weight:900; letter-spacing:.08em;
  628.    font-size: 28px; margin: 0 0 6px; text-shadow: 0 1px 0 rgba(0,0,0,.2);
  629.    margin-bottom: 40px;
  630.    }
  631.    .actu .actu__content .sec-title{
  632.    color:#fff; font-weight:800; font-size: 24px; margin: 0 0 28px;
  633.    text-shadow: 0 1px 0 rgba(0,0,0,.2);
  634.    }
  635.    .actu .actu__content p{
  636.    font-size: 16px; line-height:1.6; max-width:78ch; margin-bottom: 20px;
  637.    color:rgba(255,255,255,.95); text-shadow: 0 1px 0 rgba(0,0,0,.18);
  638.    }
  639.    .actu .actu__content .btn.btn-primary{
  640.    background: #FF5A1C; color: #FFFFFF; border:0; border-radius: 20px; width: 167px; height: 32px; font-weight:900; font-size:14px;
  641.    }
  642.    .actu .actu__pills{ position:relative; z-index:2; }
  643.    .actu .pill{
  644.    position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  645.    color:#fff; font-weight:900; letter-spacing:.18em; text-transform:uppercase; font-size:18px;
  646.    writing-mode: vertical-rl; transform: rotate(180deg);
  647.    text-shadow: 0 1px 0 rgba(0,0,0,.24);
  648.    }
  649.    .actu .pill--2{
  650.    right: 24px; z-index:2;
  651.    background-image: linear-gradient(90deg, rgba(43,134,255,.58), rgba(43,134,255,.58)), var(--pill-2-img);
  652.    width: 300px;
  653.    }
  654.    .actu .pill--3{
  655.    right: 0; z-index:1;
  656.    background-image: linear-gradient(90deg, rgba(122,208,255,.46), rgba(122,208,255,.46)), var(--pill-3-img);
  657.    width: 300px;
  658.    }
  659.    .actu .pill__label{
  660.    position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  661.    color:#fff; font-weight:900; letter-spacing:.18em; text-transform:uppercase; font-size:18px;
  662.    writing-mode: vertical-rl; transform: rotate(180deg);
  663.    text-shadow: 0 1px 0 rgba(0,0,0,.24);
  664.    }
  665.    @media (max-width: 900px){
  666.    .actu .actu-hero{ grid-template-columns:1fr; min-height:720px; }
  667.    .actu .actu__pills{ height: auto; min-height: 260px; }
  668.    .actu .pill{  width:52%; right:6%; }
  669.    .actu .pill--2{ right:-2%; }
  670.    .actu .pill--3{ display:none; }
  671.    .actu .actu-shade{
  672.    background: linear-gradient(0deg, rgba(0,0,0,.72), rgba(0,0,0,.45));
  673.    }
  674.    }
  675.    .actu .actu__pills{
  676.    --wide: 140px;
  677.    --narrow: 190px;
  678.    --gap: 18px;
  679.    --radius: 60px;
  680.    --shadow: 0 18px 36px rgba(0,0,0,.22);
  681.    position: relative;
  682.    width: calc(var(--wide) + var(--gap) + var(--narrow) + 24px);
  683.    height: 100%;
  684.    margin-left: auto;
  685.    overflow: visible;
  686.    }
  687.    .actu .pill{ all: unset; }
  688.    .actu .pill{
  689.    position: absolute; top: 0; bottom: 0;
  690.    border-radius: var(--radius);
  691.    overflow: hidden;
  692.    background-position: center;
  693.    background-size: cover;
  694.    box-shadow: var(--shadow);
  695.    }
  696.    .actu .pill--3{
  697.    right: 0;
  698.    width: var(--wide);
  699.    z-index: 1;
  700.    background-image:
  701.    linear-gradient(0deg, rgba(8,40,59,.72), rgba(8,40,59,.72)),
  702.    var(--pill-3-img);
  703.    }
  704.    .actu .pill--2{
  705.    width: var(--wide);
  706.    z-index: 2;
  707.    background-image:
  708.    linear-gradient(0deg, rgba(31,110,194,.62), rgba(31,110,194,.62)),
  709.    var(--pill-2-img);
  710.    }
  711.    .actu .pill--1{
  712.    right: calc(var(--wide) + var(--gap) + 100px);
  713.    width: var(--narrow);
  714.    z-index: 3;
  715.    background-image:
  716.    linear-gradient(0deg, rgba(54,154,255,.58), rgba(54,154,255,.58)),
  717.    var(--pill-1-img);
  718.    }
  719.    .actu .pill__label{
  720.    position: absolute; inset: 0; padding-top: 50px;
  721.    display: flex; align-items: center; justify-content: flex-start;
  722.    color: #fff; font-weight: 900; letter-spacing: .18em;
  723.    text-transform: uppercase; font-size: 20px;
  724.    writing-mode: vertical-rl; transform: rotate(180deg);
  725.    text-shadow: 0 1px 0 rgba(0,0,0,.25);
  726.    }
  727.    .actu .actu__pills,
  728.    .actu .actu-side{ overflow: visible !important; }
  729.    @media (max-width: 980px){
  730.    .actu .actu__pills{
  731.    --wide: 180px;
  732.    --narrow: 96px;
  733.    --gap: 14px;
  734.    height: 100%;
  735.    }
  736.    }
  737.    .actu .pill--1, .actu .pill--2, .actu .pill--3 {
  738.    width: var(--narrow);
  739.    z-index: 3;
  740.    background-image: linear-gradient(0deg, rgba(54, 154, 255, .58), rgba(54, 154, 255, .58)), var(--pill-1-img);
  741.    }
  742.    .actu .actu__pills{
  743.    --pillW: 130px;
  744.    --gap:   18px;
  745.    --radius: 44px;
  746.    }
  747.    .actu .pill{
  748.    top: 0; bottom: 0;
  749.    width: var(--pillW) !important;
  750.    border-radius: var(--radius);
  751.    }
  752.    .actu .pill--3{
  753.    right: 0;
  754.    z-index: 1;
  755.    background-image:
  756.    linear-gradient(0deg, rgba(8,40,59,.72), rgba(8,40,59,.72)),
  757.    var(--pill-3-img) !important;
  758.    }
  759.    .actu .pill--2{
  760.    z-index: 2;
  761.    background-image: linear-gradient(0deg, rgba(31, 110, 194, .62), rgba(31, 110, 194, .62)), var(--pill-2-img) !important;
  762.    margin-left: 162px;
  763.    }
  764.    .actu .pill--1{
  765.    z-index: 3;
  766.    background-image:
  767.    linear-gradient(0deg, rgba(54,154,255,.58), rgba(54,154,255,.58)),
  768.    var(--pill-1-img) !important;
  769.    }
  770.    @media (max-width: 991.98px){
  771.    .actu .pill--1{
  772.    position: relative;
  773.    right: calc(var(--pillW) * var(--gap));
  774.    }
  775.    }
  776.    @media (min-width: 992px){
  777.    .actu .pill--1{
  778.    }
  779.    }
  780.    .actu .actu__pills, .actu .actu-side{ overflow: visible !important; }
  781.    .actu .pill--2 {
  782.    margin-right: 45px;
  783.    }
  784.    .actu .pill--1 {
  785.    margin-right: -100px;
  786.    }
  787.    :root{
  788.    --chiffres-bg: #0e2b5a;
  789.    --card-radius: 18px;
  790.    --box-radius: 26px;
  791.    }
  792.    .chiffres{
  793.    background: var(--chiffres-bg);
  794.    border-radius: var(--box-radius);
  795.    padding: 22px 28px 28px;
  796.    color: #fff;
  797.    }
  798.    .chiffres__title{
  799.    font-size: 16px;
  800.    font-weight: 700;
  801.    margin: 0 0 14px;
  802.    opacity: .95;
  803.    }
  804.    .chiffres__grid{
  805.    display: grid;
  806.    grid-template-columns: repeat(3, 1fr);
  807.    gap: 22px;
  808.    margin: 0;
  809.    padding: 0;
  810.    list-style: none;
  811.    }
  812.    .chiffres__cell{ display: contents; }
  813.    .kpi-card{
  814.    position: relative;
  815.    height: 210px;
  816.    border-radius: var(--card-radius);
  817.    overflow: hidden;
  818.    margin: 0;
  819.    box-shadow: 0 6px 18px rgba(0,0,0,.25);
  820.    }
  821.    .kpi-card__img{
  822.    width: 100%;
  823.    height: 100%;
  824.    object-fit: cover;
  825.    display: block;
  826.    transform: scale(1.02);
  827.    }
  828.    .kpi-card::after{
  829.    content: "";
  830.    position: absolute; inset: 0;
  831.    background:
  832.    linear-gradient(180deg, rgba(0,0,0,0) 38%, rgba(0,0,0,.55) 72%, rgba(0,0,0,.65) 100%);
  833.    }
  834.    .kpi-card__caption{
  835.    position: absolute;
  836.    left: 14px; right: 14px; bottom: 12px;
  837.    display: grid;
  838.    row-gap: 6px;
  839.    z-index: 1;
  840.    color: #fff;
  841.    text-shadow: 0 1px 2px rgba(0,0,0,.5);
  842.    }
  843.    .kpi-card__value{
  844.    font-weight: 800;
  845.    font-size: 28px;
  846.    line-height: 1;
  847.    }
  848.    .kpi-card__label{
  849.    font-size: 13px;
  850.    opacity: .95;
  851.    }
  852.    .kpi-card:hover .kpi-card__img{ transform: scale(1.06); }
  853.    .kpi-card:hover{ box-shadow: 0 10px 24px rgba(0,0,0,.3); }
  854.    @media (max-width: 980px){
  855.    .chiffres__grid{ grid-template-columns: 1fr; }
  856.    .kpi-card{ height: 200px; }
  857.    }
  858.    .kpi-card{
  859.    aspect-ratio: 16 / 11;
  860.    min-height: 230px;
  861.    max-height: 300px;
  862.    }
  863.    .kpi-card__img{
  864.    width: 100%;
  865.    height: 100%;
  866.    object-fit: cover;
  867.    object-position: top center;
  868.    display: block;
  869.    }
  870.    .chiffres__grid{
  871.    display: grid;
  872.    grid-template-columns: repeat(3, 1fr);
  873.    gap: 24px;
  874.    justify-content: center;
  875.    max-width: 1100px;
  876.    margin: 0 auto;
  877.    padding: 0;
  878.    list-style: none;
  879.    }
  880.    :root{
  881.    --chiffres-bg:#0e2b5a;
  882.    --plate-bg:rgba(255,255,255,.18);
  883.    --box-radius:26px;
  884.    --plate-radius:18px;
  885.    }
  886.    .chiffres{ padding: 0; }
  887.    .chiffres__panel{
  888.    background:var(--chiffres-bg);
  889.    border-radius:var(--box-radius);
  890.    padding:18px 22px;
  891.    color:#fff;
  892.    }
  893.    .chiffres__title{
  894.    font-size:16px; font-weight:700;
  895.    margin:6px 6px 12px; opacity:.95;
  896.    }
  897.    .chiffres__grid{
  898.    display:grid;
  899.    grid-template-columns:repeat(3,1fr);
  900.    gap:24px;
  901.    margin:0;
  902.    padding:6px;
  903.    list-style:none;
  904.    }
  905.    .chiffre{
  906.    border-radius:var(--plate-radius);
  907.    padding:10px;
  908.    margin-left:-30px;
  909.    }
  910.    .chiffre__media{
  911.    position:relative;
  912.    border-radius:14px;
  913.    overflow:hidden;
  914.    min-height:300px;
  915.    height:clamp(300px, 28vw, 380px);
  916.    }
  917.    .chiffre__media>img{
  918.    position:absolute; inset:0; width:100%; height:100%;
  919.    object-fit:cover; object-position:center 20%;
  920.    transform:scale(1.02);
  921.    filter:saturate(80%) contrast(85%) brightness(78%);
  922.    transition: filter 0.3s ease;
  923.    }
  924.    .chiffre:hover .chiffre__media>img{
  925.    filter: none;
  926.    }
  927.    .chiffre__media::after{
  928.    content:""; position:absolute; inset:0;
  929.    background:linear-gradient(180deg, rgba(0,0,0,.08), rgba(0,0,0,.45));
  930.    }
  931.    .chiffre__content{
  932.    position:absolute; inset:0; display:flex; flex-direction:column;
  933.    justify-content:flex-end; gap:10px; padding:18px 18px 20px; color:#fff;
  934.    text-shadow:0 1px 2px rgba(0,0,0,.45);
  935.    }
  936.    .chiffre__value{font-weight:900; font-size:clamp(2.9rem,4.6vw,5.1rem); line-height:.95; margin-bottom:0}
  937.    .chiffre__label{
  938.    font-size: clamp(1.15rem,1.9vw,2.35rem);
  939.    line-height: 1.12;
  940.    font-weight:700;
  941.    opacity:.96;
  942.    overflow-wrap:anywhere;
  943.    text-wrap:balance;
  944.    max-width:12ch;
  945.    }
  946.    @media (max-width:980px){
  947.    .chiffres__grid{ grid-template-columns:1fr; }
  948.    .chiffre__media{ min-height:260px; height:260px; }
  949.    .chiffre__label{ max-width:none; }
  950.    }
  951.    .chiffres .narrow{
  952.    width: min(1100px, 100%);
  953.    margin: 0 auto;
  954.    }
  955.    .chiffres{
  956.    background: transparent !important;
  957.    padding: 0 !important;
  958.    }
  959.    .chiffres > .container{
  960.    max-width: 1180px;
  961.    margin: 0 auto;
  962.    padding-left: 12px;
  963.    padding-right: 12px;
  964.    }
  965.    .chiffres__panel{
  966.    background: #0e2b5a;
  967.    border-radius: 26px;
  968.    padding: 18px 22px;
  969.    color: #fff;
  970.    }
  971.    .trust{ margin: clamp(18px,5vw,44px) 0 0; }
  972.    .trust .container{ padding-left: 75px; }
  973.    .trust__kicker{
  974.    font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  975.    color:#F14816;
  976.    font-weight: bold;
  977.    text-transform:uppercase;
  978.    letter-spacing:.08em;
  979.    font-size:clamp(12px,1.2vw,16px);
  980.    margin:0 0 4px 0;
  981.    }
  982.    .trust__group + .trust__group{
  983.    margin-top:40px;
  984.    }
  985.    .trust__label{
  986.    font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  987.    color:#111827;
  988.    font-weight:800;
  989.    text-align:center;
  990.    font-size:clamp(22px,2.4vw,34px);
  991.    margin:0 0 22px 0;
  992.    }
  993.   .trust__logos{
  994.    display:flex;
  995.    align-items:center;
  996.    justify-content:center;
  997.    flex-wrap:wrap;
  998.    gap:clamp(18px,3vw,48px);
  999.    }
  1000.   .trust__group:last-child .trust__logos{
  1001.    padding-bottom:56px;
  1002.    }
  1003. .trust .logo{
  1004.    height:84px;
  1005.    width:auto;
  1006.    opacity:.95;
  1007.    filter:none;
  1008.    transform:none;
  1009.    }
  1010.    .prefooter-gap{
  1011.    height: 56px;
  1012.    background: #fff;
  1013.    }
  1014.    .panel .btn.btn-secondary{
  1015.    background:#F14816 !important;
  1016.    color:#fff !important;
  1017.    border:2px solid #F14816 !important;
  1018.    border-radius:9999px !important;
  1019.    padding:12px 32px !important;
  1020.    font-weight:800 !important;
  1021.    box-shadow:0 10px 22px rgba(241,72,22,.25) !important;
  1022.    transition:filter .18s ease, transform .16s ease;
  1023.    }
  1024.    .panel .btn.btn-secondary:hover{
  1025.    filter:brightness(1.06);
  1026.    transform:translateY(-1px);
  1027.    }
  1028.    .catalogue-text-wrap{ position:relative; }
  1029.    .catalogue-edit-btn{
  1030.    position:absolute; top:-8px; right:-8px;
  1031.    background:#fff; border:1px solid rgba(20,63,99,.12);
  1032.    border-radius:9999px; padding:6px 10px; line-height:1;
  1033.    box-shadow:0 6px 16px rgba(17,24,39,.08); cursor:pointer;
  1034.    }
  1035.    .catalogue-edit-btn:hover{ background:#fff7f3; border-color:rgba(241,72,22,.35); }
  1036.    .catalogue-modal{
  1037.    position:fixed; inset:0; display:grid; place-items:center;
  1038.    background:rgba(0,0,0,.45); z-index:9999;
  1039.    }
  1040.    .catalogue-modal[hidden]{ display:none; }
  1041.    .catalogue-modal__dialog{
  1042.    width:min(720px,92vw); background:#fff; border-radius:16px;
  1043.    padding:18px; box-shadow:0 18px 38px rgba(0,0,0,.28);
  1044.    }
  1045.    #catalogue-edit-textarea{
  1046.    width:100%; border:1px solid #e5e7eb; border-radius:10px; padding:12px; margin:12px 0 16px;
  1047.    font: inherit; line-height:1.5;
  1048.    }
  1049.    .catalogue-modal__actions{ display:flex; gap:12px; justify-content:flex-end; }
  1050.    #hero1,  #hero2{
  1051.    font-size: 40px;
  1052.    }
  1053.    #hero1_h1,  #hero2_h2{
  1054.    font-size: 30px;
  1055.    }
  1056.    /*******************************************************/
  1057.    button.is-loading {
  1058.    position: relative;
  1059.    pointer-events: none;
  1060.    opacity: .9;
  1061.    }
  1062.    button.is-loading::before {
  1063.    content: "";
  1064.    display: inline-block;
  1065.    width: 1em; height: 1em;
  1066.    border: 2px solid currentColor;
  1067.    border-right-color: transparent;
  1068.    border-radius: 50%;
  1069.    vertical-align: -2px;
  1070.    margin-right: .5em;
  1071.    animation: spin .8s linear infinite;
  1072.    }
  1073.    @keyframes spin { to { transform: rotate(360deg) } }
  1074.    .hero-ribbon-shell{
  1075.    position:absolute;
  1076.    top: calc(var(--topbar-h) + var(--nav-offset) + var(--pill-h) + 8px);
  1077.    left:0;
  1078.    right:0;
  1079.    z-index:4;
  1080.    display:flex;
  1081.    justify-content:center;
  1082.    }
  1083.    .hero-ribbon-shell .home-event-ribbon{
  1084.    margin:0;
  1085.    max-width:80%;
  1086.    }
  1087.    .hero {
  1088.    position: relative;
  1089.    overflow: hidden;
  1090.    }
  1091.    .hero-track {
  1092.    display: flex;
  1093.    width: 100%;
  1094.    height: 100%;
  1095.    transition: transform 0.6s ease;
  1096.    will-change: transform;
  1097.    }
  1098.    .hero-track > article.hero-slide,
  1099.    .hero-track > article.hero-slide-config {
  1100.    flex: 0 0 100%;
  1101.    }
  1102.    .hero-track > article {
  1103.    display: none;
  1104.    }
  1105.    .hero-track > article.is-active {
  1106.    display: block;
  1107.    }
  1108.    .swiper-slide {
  1109.    position: relative;          
  1110.    min-height: calc(100vh - 100px); 
  1111.    }
  1112.    .hero-bandeau {
  1113.    position: absolute;
  1114.    top: 110px;              
  1115.    left: 50%;
  1116.    transform: translateX(-50%);
  1117.    z-index: 5;                 
  1118.    }
  1119.    .hero-ribbon-shell {
  1120.    display: flex;
  1121.    justify-content: center;
  1122.    padding: 0 1rem;
  1123.    }
  1124.    .hero-ribbon-shell .home-event-ribbon {
  1125.    display: inline-flex;
  1126.    align-items: center;
  1127.    background: #BA501D;
  1128.    border-radius: 9999px;
  1129.    padding: 6px 20px;
  1130.    width: auto !important;      
  1131.    max-width: 100%;            
  1132.    text-align: center;
  1133.    font-weight: 700;
  1134.    color: #fff;
  1135.    font-size: 0.875rem;
  1136.    }
  1137.    .hero-ribbon-shell .home-event-text {
  1138.    white-space: normal;
  1139.    }
  1140.    .hero-kicker{
  1141.    font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  1142.    text-transform:uppercase;
  1143.    font-weight:800;
  1144.    letter-spacing:.08em;
  1145.    font-size:clamp(13px,1.2vw,18px); 
  1146.    color:#fff;
  1147.    opacity:.95;
  1148.    }
  1149.    .hero-title{
  1150.    font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  1151.    font-weight:800;
  1152.    line-height:1.05;
  1153.    letter-spacing:.01em;
  1154.    font-size:clamp(34px,3.9vw,54px); 
  1155.    color:#fff;
  1156.    margin:10px 0 18px 0;
  1157.    }
  1158.    .chiffres__grid{
  1159.    display: grid;
  1160.    grid-template-columns: repeat(3, 1fr);
  1161.    gap: 22px;
  1162.    margin: 0;
  1163.    padding: 0;
  1164.    list-style: none;
  1165.    justify-content: center;
  1166.    }
  1167.    .chiffres__grid:has(> li:nth-child(4):last-child){
  1168.    grid-template-columns:repeat(2, minmax(0,1fr));
  1169.    }
  1170.    .chiffres__grid:has(> li:nth-child(5):last-child) > li:nth-child(4){
  1171.    grid-column:1;
  1172.    }
  1173.    .chiffres__grid:has(> li:nth-child(5):last-child) > li:nth-child(5){
  1174.    grid-column:3;
  1175.    }
  1176.    @media (max-width:980px){
  1177.    .chiffres__grid{
  1178.    grid-template-columns:1fr;
  1179.    }
  1180.    }
  1181.    .chiffres__grid{
  1182.    display: grid;
  1183.    grid-template-columns: repeat(3, 1fr);
  1184.    gap: 22px;
  1185.    margin: 0;
  1186.    padding: 0;
  1187.    list-style: none;
  1188.    justify-content: center;
  1189.    }
  1190.    .chiffres__grid:has(> li:nth-child(4):last-child){
  1191.    grid-template-columns: repeat(2, 1fr);
  1192.    }
  1193.    .chiffres__grid:has(> li:nth-child(5):last-child) > li:nth-child(4){
  1194.    grid-column: 1;
  1195.    }
  1196.    .chiffres__grid:has(> li:nth-child(5):last-child) > li:nth-child(5){
  1197.    grid-column: 3;
  1198.    }
  1199.    @media (max-width:980px){
  1200.    .chiffres__grid{ grid-template-columns:1fr; }
  1201.    .chiffre__media{ height:240px; }
  1202.    }
  1203.    /*******************************************************/
  1204.    @media (max-width: 767.98px){
  1205.    body.is-home .hero-ribbon-shell{ display:none !important; }
  1206.    body.is-home .hero-nav{ display:none !important; }
  1207.    body.is-home .hero-track > article.is-active{
  1208.    display:flex !important;
  1209.    flex-direction:column;
  1210.    min-height: calc(100vh - 78px);
  1211.    min-height: calc(100svh - 78px);
  1212.    }
  1213.    body.is-home .hero .container.hero-content{
  1214.    flex: 1;
  1215.    max-width: 100% !important;
  1216.    margin: 0 !important;
  1217.    padding: 0 18px 72px !important; 
  1218.    align-items: center !important;
  1219.    justify-content: center !important;
  1220.    text-align: center !important;
  1221.    }
  1222.    body.is-home .hero .hero-copy{
  1223.    width: 100% !important;
  1224.    align-items: center !important;
  1225.    text-align: center !important;
  1226.    }
  1227.    body.is-home .hero-slide .hero-kicker,
  1228.    body.is-home .hero-slide .hero-title{
  1229.    text-align: center !important;
  1230.    }
  1231.    body.is-home .hero-sub{
  1232.    margin-left:auto;
  1233.    margin-right:auto;
  1234.    max-width: 340px;
  1235.    }
  1236.    body.is-home .hero-slide .hero-ctas{
  1237.    width: 100% !important;
  1238.    justify-content: center !important;
  1239.    text-align: center !important;
  1240.    }
  1241.    body.is-home .hero-ctas .btn,
  1242.    body.is-home .hero-ctas .btn-primary{
  1243.    border-radius: 9999px !important;
  1244.    padding: 12px 26px !important;
  1245.    }
  1246.    }
  1247.    .trust .container{ padding-left: 12px; padding-right: 12px; }
  1248. @media (min-width: 992px){
  1249.   .trust .container{ padding-left: 75px; padding-right: 12px; }
  1250. }
  1251. @media (max-width: 991.98px){
  1252.   .trust__kicker,
  1253.   .trust__label{ text-align:center; }
  1254. }
  1255. .only-mobile .chiffres__viewport{
  1256.   width: 100%;
  1257.   max-width: 620px;
  1258.   margin-inline:auto;
  1259.   overflow:hidden;
  1260.   position:relative;
  1261. }
  1262. .chiffres__grid > li{ margin:0; }
  1263. .chiffres__grid .chiffre{ margin:0; }
  1264. .chiffres__grid{ padding:0; }
  1265. .chiffres__grid:has(> li:nth-child(5):last-child){
  1266.   grid-template-columns:repeat(6, minmax(0,1fr));
  1267. }
  1268. .chiffres__grid:has(> li:nth-child(5):last-child) > li{
  1269.   grid-column:span 2;
  1270. }
  1271. .chiffres__grid:has(> li:nth-child(5):last-child) > li:nth-child(4){
  1272.   grid-column:2 / span 2;
  1273. }
  1274. .chiffres__grid:has(> li:nth-child(5):last-child) > li:nth-child(5){
  1275.   grid-column:4 / span 2;
  1276. }
  1277. .trust,
  1278. .chiffres{
  1279.   overflow-x:hidden;
  1280. }
  1281. .trust .container{
  1282.   padding-left:18px;
  1283.   padding-right:18px;
  1284. }
  1285. .chiffre{
  1286.   margin-left:0 !important;
  1287. }
  1288. .only-mobile .chiffres__viewport{
  1289.   width:100% !important;
  1290.   max-width:620px !important;
  1291. }
  1292. @media (max-width: 767.98px){
  1293.   body.is-home .hero-dots{ display:none !important; }
  1294. }
  1295. </style>
  1296. {# --------------------------- HERO / CAROUSEL  --------------------------- #}
  1297. <section class="hero">
  1298.    <div class="hero-track" aria-live="polite">
  1299.       {% set slide1Bg = hero.slide1.bg|default('default') %}
  1300.       {% set slide1IsDefault = (slide1Bg == 'default') %}
  1301.       {% set slide1Ribbon = hero.slide1.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1302.       <article
  1303.       data-scope="hero-slide1"
  1304.       data-ribbon-text="{{ slide1Ribbon|e('html_attr') }}"
  1305.       class="{{ slide1IsDefault ? 'hero-slide-config' : 'hero-slide' }}"
  1306.       {% if not slide1IsDefault %}
  1307.       style="--img:url('{{ asset(slide1Bg) }}'); --bg:url('{{ asset(slide1Bg) }}')"
  1308.       {% endif %}
  1309.       >
  1310.       <button
  1311.          style="opacity:0"
  1312.          class="cms-edit-fab"
  1313.          type="button"
  1314.          aria-label="Éditer le slide 1"
  1315.          data-edit-scope="hero-slide1"
  1316.          data-kicker-key="hero_slide1_kicker"
  1317.          data-title-key="hero_slide1_title"
  1318.          data-cta-key="hero_slide1_cta"
  1319.          data-img-key="hero_slide1_bg"
  1320.          data-ribbon-key="hero_slide1_ribbon"
  1321.          data-link-key="hero_slide1_link" 
  1322.          >✏️</button>
  1323.       {% if not slide1IsDefault %}
  1324.       <div class="hero-overlay"></div>
  1325.       <div class="hero-ribbon-shell">
  1326.          <div class="home-event-ribbon">
  1327.             <span class="home-event-text">{{ slide1Ribbon }}</span>
  1328.          </div>
  1329.       </div>
  1330.       <div class="container hero-content">
  1331.          <div class="hero-copy">
  1332.             <h1 class="hero-title">{{ hero.slide1.title|raw }}</h1>
  1333.             {% if hero.slide1.kicker %}
  1334.             <div class="hero-sub">{{ hero.slide1.kicker|raw }}</div>
  1335.             {% endif %}
  1336.             <div class="hero-ctas">
  1337.                <a href="{{ hero.slide1.link|default('#') }}" class="btn btn-primary">{{ hero.slide1.cta }}</a>
  1338.             </div>
  1339.          </div>
  1340.       </div>
  1341.       {% endif %}
  1342.       </article>
  1343.       {# --- SLIDE 2 --- #}
  1344.       {% set slide2Bg = hero.slide2.bg|default('default') %}
  1345.       {% set slide2IsDefault = (slide2Bg == 'default') %}
  1346.       {% set slide2Ribbon = hero.slide2.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1347.       <article
  1348.       data-scope="hero-slide2"
  1349.       data-ribbon-text="{{ slide2Ribbon|e('html_attr') }}"
  1350.       class="hero-slide {{ slide2IsDefault ? 'hero-slide-config' : '' }}"
  1351.       {% if not slide2IsDefault %}
  1352.       style="--img:url('{{ asset(slide2Bg) }}'); --bg:url('{{ asset(slide2Bg) }}')"
  1353.       {% endif %}
  1354.       >
  1355.       <button
  1356.          style="opacity:0"
  1357.          class="cms-edit-fab"
  1358.          type="button"
  1359.          aria-label="Éditer le slide 2"
  1360.          data-edit-scope="hero-slide2"
  1361.          data-kicker-key="hero_slide2_kicker"
  1362.          data-title-key="hero_slide2_title"
  1363.          data-cta-key="hero_slide2_cta"
  1364.          data-img-key="hero_slide2_bg"
  1365.          data-ribbon-key="hero_slide2_ribbon"
  1366.          data-link-key="hero_slide2_link"
  1367.          >✏️</button>
  1368.       {% if not slide2IsDefault %}
  1369.       <div class="hero-overlay"></div>
  1370.       <div class="hero-ribbon-shell">
  1371.          <div class="home-event-ribbon">
  1372.             <span class="home-event-text">{{ slide2Ribbon }}</span>
  1373.          </div>
  1374.       </div>
  1375.       <div class="container hero-content">
  1376.          <div class="hero-copy">
  1377.             <h1 class="hero-title" id="hero2_h1">{{ hero.slide2.title|raw }}</h1>
  1378.             {% if hero.slide2.kicker %}
  1379.             <div class="hero-sub" id="hero2_kicker">{{ hero.slide2.kicker|raw }}</div>
  1380.             {% endif %}
  1381.             <div class="hero-ctas">
  1382.                <a href="{{ hero.slide2.link|default('#') }}" class="btn btn-primary">{{ hero.slide2.cta }}</a>
  1383.             </div>
  1384.          </div>
  1385.       </div>
  1386.       {% endif %}
  1387.       </article>
  1388.       {# --- SLIDE 3 --- #}
  1389.       {% set slide3Bg = hero.slide3.bg|default('default') %}
  1390.       {% set slide3IsDefault = (slide3Bg == 'default') %}
  1391.       {% set slide3Ribbon = hero.slide3.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1392.       <article
  1393.       data-scope="hero-slide3"
  1394.       data-ribbon-text="{{ slide3Ribbon|e('html_attr') }}"
  1395.       class="{{ slide3IsDefault ? 'hero-slide-config' : 'hero-slide' }}"
  1396.       {% if not slide3IsDefault %}
  1397.       style="--img:url('{{ asset(slide3Bg) }}'); --bg:url('{{ asset(slide3Bg) }}')"
  1398.       {% endif %}
  1399.       >
  1400.       <button
  1401.          style="opacity:0"
  1402.          class="cms-edit-fab"
  1403.          type="button"
  1404.          aria-label="Éditer le slide 3"
  1405.          data-edit-scope="hero-slide3"
  1406.          data-kicker-key="hero_slide3_kicker"
  1407.          data-title-key="hero_slide3_title"
  1408.          data-cta-key="hero_slide3_cta"
  1409.          data-img-key="hero_slide3_bg"
  1410.          data-ribbon-key="hero_slide3_ribbon"
  1411.          data-link-key="hero_slide3_link"
  1412.          >✏️</button>
  1413.       {% if not slide3IsDefault %}
  1414.       <div class="hero-overlay"></div>
  1415.       <div class="hero-ribbon-shell">
  1416.          <div class="home-event-ribbon">
  1417.             <span class="home-event-text">{{ slide3Ribbon }}</span>
  1418.          </div>
  1419.       </div>
  1420.       <div class="container hero-content">
  1421.          <div class="hero-copy">
  1422.             <h1 class="hero-title" id="hero3_h1">{{ hero.slide3.title|raw }}</h1>
  1423.             {% if hero.slide3.kicker %}
  1424.             <div class="hero-sub" id="hero3_kicker">{{ hero.slide3.kicker|raw }}</div>
  1425.             {% endif %}
  1426.             <div class="hero-ctas">
  1427.                <a href="{{ hero.slide3.link|default('#') }}" class="btn btn-primary">{{ hero.slide3.cta }}</a>
  1428.             </div>
  1429.          </div>
  1430.       </div>
  1431.       {% endif %}
  1432.       </article>
  1433.       {# --- SLIDE 4 --- #}
  1434.       {% set slide4Bg = hero.slide4.bg|default('default') %}
  1435.       {% set slide4IsDefault = (slide4Bg == 'default') %}
  1436.       {% set slide4Ribbon = hero.slide4.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1437.       <article
  1438.       data-scope="hero-slide4"
  1439.       data-ribbon-text="{{ slide4Ribbon|e('html_attr') }}"
  1440.       class="hero-slide {{ slide4IsDefault ? 'hero-slide-config' : '' }}"
  1441.       {% if not slide4IsDefault %}
  1442.       style="--img:url('{{ asset(slide4Bg) }}'); --bg:url('{{ asset(slide4Bg) }}')"
  1443.       {% endif %}
  1444.       >
  1445.       <button
  1446.          style="opacity:0"
  1447.          class="cms-edit-fab"
  1448.          type="button"
  1449.          aria-label="Éditer le slide 4"
  1450.          data-edit-scope="hero-slide4"
  1451.          data-kicker-key="hero_slide4_kicker"
  1452.          data-title-key="hero_slide4_title"
  1453.          data-cta-key="hero_slide4_cta"
  1454.          data-img-key="hero_slide4_bg"
  1455.          data-ribbon-key="hero_slide4_ribbon"
  1456.          data-link-key="hero_slide4_link"
  1457.          >✏️</button>
  1458.       {% if not slide4IsDefault %}
  1459.       <div class="hero-overlay"></div>
  1460.       <div class="hero-ribbon-shell">
  1461.          <div class="home-event-ribbon">
  1462.             <span class="home-event-text">{{ slide4Ribbon }}</span>
  1463.          </div>
  1464.       </div>
  1465.       <div class="container hero-content">
  1466.          <div class="hero-copy">
  1467.             <h1 class="hero-title" id="hero4_h1">{{ hero.slide4.title|raw }}</h1>
  1468.             {% if hero.slide4.kicker %}
  1469.             <div class="hero-sub" id="hero4_kicker">{{ hero.slide4.kicker|raw }}</div>
  1470.             {% endif %}
  1471.             <div class="hero-ctas">
  1472.                <a href="{{ hero.slide4.link|default('#') }}" class="btn btn-primary">{{ hero.slide4.cta }}</a>
  1473.             </div>
  1474.          </div>
  1475.       </div>
  1476.       {% endif %}
  1477.       </article>
  1478.       {# --- SLIDE 5 --- #}
  1479.       {% set slide5Bg = hero.slide5.bg|default('default') %}
  1480.       {% set slide5IsDefault = (slide5Bg == 'default') %}
  1481.       {% set slide5Ribbon = hero.slide5.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1482.       <article
  1483.       data-scope="hero-slide5"
  1484.       data-ribbon-text="{{ slide5Ribbon|e('html_attr') }}"
  1485.       class="hero-slide {{ slide5IsDefault ? 'hero-slide-config' : '' }}"
  1486.       {% if not slide5IsDefault %}
  1487.       style="--img:url('{{ asset(slide5Bg) }}'); --bg:url('{{ asset(slide5Bg) }}')"
  1488.       {% endif %}
  1489.       >
  1490.       <button
  1491.          style="opacity:0"
  1492.          class="cms-edit-fab"
  1493.          type="button"
  1494.          aria-label="Éditer le slide 5"
  1495.          data-edit-scope="hero-slide5"
  1496.          data-kicker-key="hero_slide5_kicker"
  1497.          data-title-key="hero_slide5_title"
  1498.          data-cta-key="hero_slide5_cta"
  1499.          data-img-key="hero_slide5_bg"
  1500.          data-ribbon-key="hero_slide5_ribbon"
  1501.          data-link-key="hero_slide5_link"
  1502.          >✏️</button>
  1503.       {% if not slide5IsDefault %}
  1504.       <div class="hero-overlay"></div>
  1505.       <div class="hero-ribbon-shell">
  1506.          <div class="home-event-ribbon">
  1507.             <span class="home-event-text">{{ slide5Ribbon }}</span>
  1508.          </div>
  1509.       </div>
  1510.       <div class="container hero-content">
  1511.          <div class="hero-copy">
  1512.             <h1 class="hero-title" id="hero5_h1">{{ hero.slide5.title|raw }}</h1>
  1513.             {% if hero.slide5.kicker %}
  1514.             <div class="hero-sub" id="hero5_kicker">{{ hero.slide5.kicker|raw }}</div>
  1515.             {% endif %}
  1516.             <div class="hero-ctas">
  1517.                <a href="{{ hero.slide5.link|default('#') }}" class="btn btn-primary">{{ hero.slide5.cta }}</a>
  1518.             </div>
  1519.          </div>
  1520.       </div>
  1521.       {% endif %}
  1522.       </article>
  1523.       {# --- SLIDE 6 --- #}
  1524.       {% set slide6Bg = hero.slide6.bg|default('default') %}
  1525.       {% set slide6IsDefault = (slide6Bg == 'default') %}
  1526.       {% set slide6Ribbon = hero.slide6.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1527.       <article
  1528.       data-scope="hero-slide6"
  1529.       data-ribbon-text="{{ slide6Ribbon|e('html_attr') }}"
  1530.       class="hero-slide {{ slide6IsDefault ? 'hero-slide-config' : '' }}"
  1531.       {% if not slide6IsDefault %}
  1532.       style="--img:url('{{ asset(slide6Bg) }}'); --bg:url('{{ asset(slide6Bg) }}')"
  1533.       {% endif %}
  1534.       >
  1535.       <button
  1536.          style="opacity:0"
  1537.          class="cms-edit-fab"
  1538.          type="button"
  1539.          aria-label="Éditer le slide 6"
  1540.          data-edit-scope="hero-slide6"
  1541.          data-kicker-key="hero_slide6_kicker"
  1542.          data-title-key="hero_slide6_title"
  1543.          data-cta-key="hero_slide6_cta"
  1544.          data-img-key="hero_slide6_bg"
  1545.          data-ribbon-key="hero_slide6_ribbon"
  1546.          data-link-key="hero_slide6_link"
  1547.          >✏️</button>
  1548.       {% if not slide6IsDefault %}
  1549.       <div class="hero-overlay"></div>
  1550.       <div class="hero-ribbon-shell">
  1551.          <div class="home-event-ribbon">
  1552.             <span class="home-event-text">{{ slide6Ribbon }}</span>
  1553.          </div>
  1554.       </div>
  1555.       <div class="container hero-content">
  1556.          <div class="hero-copy">
  1557.             <h1 class="hero-title" id="hero6_h1">{{ hero.slide6.title|raw }}</h1>
  1558.             {% if hero.slide6.kicker %}
  1559.             <div class="hero-sub" id="hero6_kicker">{{ hero.slide6.kicker|raw }}</div>
  1560.             {% endif %}
  1561.             <div class="hero-ctas">
  1562.                <a href="{{ hero.slide6.link|default('#') }}" class="btn btn-primary">{{ hero.slide6.cta }}</a>
  1563.             </div>
  1564.          </div>
  1565.       </div>
  1566.       {% endif %}
  1567.       </article>
  1568.       {# --- SLIDE 7 --- #}
  1569.       {% set slide7Bg = hero.slide7.bg|default('default') %}
  1570.       {% set slide7IsDefault = (slide7Bg == 'default') %}
  1571.       {% set slide7Ribbon = hero.slide7.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1572.       <article
  1573.       data-scope="hero-slide7"
  1574.       data-ribbon-text="{{ slide7Ribbon|e('html_attr') }}"
  1575.       class="hero-slide {{ slide7IsDefault ? 'hero-slide-config' : '' }}"
  1576.       {% if not slide7IsDefault %}
  1577.       style="--img:url('{{ asset(slide7Bg) }}'); --bg:url('{{ asset(slide7Bg) }}')"
  1578.       {% endif %}
  1579.       >
  1580.       <button
  1581.          style="opacity:0"
  1582.          class="cms-edit-fab"
  1583.          type="button"
  1584.          aria-label="Éditer le slide 7"
  1585.          data-edit-scope="hero-slide7"
  1586.          data-kicker-key="hero_slide7_kicker"
  1587.          data-title-key="hero_slide7_title"
  1588.          data-cta-key="hero_slide7_cta"
  1589.          data-img-key="hero_slide7_bg"
  1590.          data-ribbon-key="hero_slide7_ribbon"
  1591.          data-link-key="hero_slide7_link"
  1592.          >✏️</button>
  1593.       {% if not slide7IsDefault %}
  1594.       <div class="hero-overlay"></div>
  1595.       <div class="hero-ribbon-shell">
  1596.          <div class="home-event-ribbon">
  1597.             <span class="home-event-text">{{ slide7Ribbon }}</span>
  1598.          </div>
  1599.       </div>
  1600.       <div class="container hero-content">
  1601.          <div class="hero-copy">
  1602.             <h1 class="hero-title" id="hero7_h1">{{ hero.slide7.title|raw }}</h1>
  1603.             {% if hero.slide7.kicker %}
  1604.             <div class="hero-sub" id="hero7_kicker">{{ hero.slide7.kicker|raw }}</div>
  1605.             {% endif %}
  1606.             <div class="hero-ctas">
  1607.                <a href="{{ hero.slide7.link|default('#') }}" class="btn btn-primary">{{ hero.slide7.cta }}</a>
  1608.             </div>
  1609.          </div>
  1610.       </div>
  1611.       {% endif %}
  1612.       </article>
  1613.       {# --- SLIDE 8 --- #}
  1614.       {% set slide8Bg = hero.slide8.bg|default('default') %}
  1615.       {% set slide8IsDefault = (slide8Bg == 'default') %}
  1616.       {% set slide8Ribbon = hero.slide8.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1617.       <article
  1618.       data-scope="hero-slide8"
  1619.       data-ribbon-text="{{ slide8Ribbon|e('html_attr') }}"
  1620.       class="hero-slide {{ slide8IsDefault ? 'hero-slide-config' : '' }}"
  1621.       {% if not slide8IsDefault %}
  1622.       style="--img:url('{{ asset(slide8Bg) }}'); --bg:url('{{ asset(slide8Bg) }}')"
  1623.       {% endif %}
  1624.       >
  1625.       <button
  1626.          style="opacity:0"
  1627.          class="cms-edit-fab"
  1628.          type="button"
  1629.          aria-label="Éditer le slide 8"
  1630.          data-edit-scope="hero-slide8"
  1631.          data-kicker-key="hero_slide8_kicker"
  1632.          data-title-key="hero_slide8_title"
  1633.          data-cta-key="hero_slide8_cta"
  1634.          data-img-key="hero_slide8_bg"
  1635.          data-ribbon-key="hero_slide8_ribbon"
  1636.          data-link-key="hero_slide8_link"
  1637.          >✏️</button>
  1638.       {% if not slide8IsDefault %}
  1639.       <div class="hero-overlay"></div>
  1640.       <div class="hero-ribbon-shell">
  1641.          <div class="home-event-ribbon">
  1642.             <span class="home-event-text">{{ slide8Ribbon }}</span>
  1643.          </div>
  1644.       </div>
  1645.       <div class="container hero-content">
  1646.          <div class="hero-copy">
  1647.             <h1 class="hero-title" id="hero8_h1">{{ hero.slide8.title|raw }}</h1>
  1648.             {% if hero.slide8.kicker %}
  1649.             <div class="hero-sub" id="hero8_kicker">{{ hero.slide8.kicker|raw }}</div>
  1650.             {% endif %}
  1651.             <div class="hero-ctas">
  1652.                <a href="{{ hero.slide8.link|default('#') }}" class="btn btn-primary">{{ hero.slide8.cta }}</a>
  1653.             </div>
  1654.          </div>
  1655.       </div>
  1656.       {% endif %}
  1657.       </article>
  1658.       {# --- SLIDE 9 --- #}
  1659.       {% set slide9Bg = hero.slide9.bg|default('default') %}
  1660.       {% set slide9IsDefault = (slide9Bg == 'default') %}
  1661.       {% set slide9Ribbon = hero.slide9.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1662.       <article
  1663.       data-scope="hero-slide9"
  1664.       data-ribbon-text="{{ slide9Ribbon|e('html_attr') }}"
  1665.       class="hero-slide {{ slide9IsDefault ? 'hero-slide-config' : '' }}"
  1666.       {% if not slide9IsDefault %}
  1667.       style="--img:url('{{ asset(slide9Bg) }}'); --bg:url('{{ asset(slide9Bg) }}')"
  1668.       {% endif %}
  1669.       >
  1670.       <button
  1671.          style="opacity:0"
  1672.          class="cms-edit-fab"
  1673.          type="button"
  1674.          aria-label="Éditer le slide 9"
  1675.          data-edit-scope="hero-slide9"
  1676.          data-kicker-key="hero_slide9_kicker"
  1677.          data-title-key="hero_slide9_title"
  1678.          data-cta-key="hero_slide9_cta"
  1679.          data-img-key="hero_slide9_bg"
  1680.          data-ribbon-key="hero_slide9_ribbon"
  1681.          data-link-key="hero_slide9_link"
  1682.          >✏️</button>
  1683.       {% if not slide9IsDefault %}
  1684.       <div class="hero-overlay"></div>
  1685.       <div class="hero-ribbon-shell">
  1686.          <div class="home-event-ribbon">
  1687.             <span class="home-event-text">{{ slide9Ribbon }}</span>
  1688.          </div>
  1689.       </div>
  1690.       <div class="container hero-content">
  1691.          <div class="hero-copy">
  1692.             <h1 class="hero-title" id="hero9_h1">{{ hero.slide9.title|raw }}</h1>
  1693.             {% if hero.slide9.kicker %}
  1694.             <div class="hero-sub" id="hero9_kicker">{{ hero.slide9.kicker|raw }}</div>
  1695.             {% endif %}
  1696.             <div class="hero-ctas">
  1697.                <a href="{{ hero.slide9.link|default('#') }}" class="btn btn-primary">{{ hero.slide9.cta }}</a>
  1698.             </div>
  1699.          </div>
  1700.       </div>
  1701.       {% endif %}
  1702.       </article>
  1703.       {# --- SLIDE 10 --- #}
  1704.       {% set slide10Bg = hero.slide10.bg|default('default') %}
  1705.       {% set slide10IsDefault = (slide10Bg == 'default') %}
  1706.       {% set slide10Ribbon = hero.slide10.ribbon|default('JEUDI 5 ET VENDREDI 6 DÉCEMBRE 2024 · HÔTEL ARAWAK · GOSIER, GUADELOUPE') %}
  1707.       <article
  1708.       data-scope="hero-slide10"
  1709.       data-ribbon-text="{{ slide10Ribbon|e('html_attr') }}"
  1710.       class="hero-slide {{ slide10IsDefault ? 'hero-slide-config' : '' }}"
  1711.       {% if not slide10IsDefault %}
  1712.       style="--img:url('{{ asset(slide10Bg) }}'); --bg:url('{{ asset(slide10Bg) }}')"
  1713.       {% endif %}
  1714.       >
  1715.       <button
  1716.          style="opacity:0"
  1717.          class="cms-edit-fab"
  1718.          type="button"
  1719.          aria-label="Éditer le slide 10"
  1720.          data-edit-scope="hero-slide10"
  1721.          data-kicker-key="hero_slide10_kicker"
  1722.          data-title-key="hero_slide10_title"
  1723.          data-cta-key="hero_slide10_cta"
  1724.          data-img-key="hero_slide10_bg"
  1725.          data-ribbon-key="hero_slide10_ribbon"
  1726.          data-link-key="hero_slide10_link"
  1727.          >✏️</button>
  1728.       {% if not slide10IsDefault %}
  1729.       <div class="hero-overlay"></div>
  1730.       <div class="hero-ribbon-shell">
  1731.          <div class="home-event-ribbon">
  1732.             <span class="home-event-text">{{ slide10Ribbon }}</span>
  1733.          </div>
  1734.       </div>
  1735.       <div class="container hero-content">
  1736.          <div class="hero-copy">
  1737.             <h1 class="hero-title" id="hero10_h1">{{ hero.slide10.title|raw }}</h1>
  1738.             {% if hero.slide10.kicker %}
  1739.             <div class="hero-sub" id="hero10_kicker">{{ hero.slide10.kicker|raw }}</div>
  1740.             {% endif %}
  1741.             <div class="hero-ctas">
  1742.                <a href="{{ hero.slide10.link|default('#') }}" class="btn btn-primary">{{ hero.slide10.cta }}</a>
  1743.             </div>
  1744.          </div>
  1745.       </div>
  1746.       {% endif %}
  1747.       </article>
  1748.    </div>
  1749.    <button class="hero-nav prev" aria-label="Slide précédente">‹</button>
  1750.    <button class="hero-nav next" aria-label="Slide suivante">›</button>
  1751.    <div class="hero-dots" role="tablist" aria-label="Sélecteur de diapos"></div>
  1752. </section>
  1753. <div id="cms-devbar">
  1754.    <button id="cms-devbar-toggle" type="button"
  1755.       aria-label="Ouvrir le menu d’édition"
  1756.       aria-expanded="false">
  1757.    ☰
  1758.    </button>
  1759.    <div id="cms-devbar-menu" hidden>
  1760.       <button type="button" data-hero="1">Éditer bandeau slide #1</button>
  1761.       <button type="button" data-hero="2">Éditer bandeau slide #2</button>
  1762.       <button type="button" data-hero="3">Éditer bandeau slide #3</button>
  1763.       <button type="button" data-hero="4">Éditer bandeau slide #4</button>
  1764.       <button type="button" data-hero="5">Éditer bandeau slide #5</button>
  1765.       <button type="button" data-hero="6">Éditer bandeau slide #6</button>
  1766.       <button type="button" data-hero="7">Éditer bandeau slide #7</button>
  1767.       <button type="button" data-hero="8">Éditer bandeau slide #8</button>
  1768.       <button type="button" data-hero="9">Éditer bandeau slide #9</button>
  1769.       <button type="button" data-hero="10">Éditer bandeau slide #10</button>
  1770.    </div>
  1771. </div>
  1772. </div>
  1773. <style>
  1774.    #cms-devbar{
  1775.    position:fixed;
  1776.    top:12px;
  1777.    right:12px;
  1778.    z-index:2147483647;
  1779.    font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
  1780.    }
  1781.    #cms-devbar-toggle{
  1782.    width:40px;
  1783.    height:40px;
  1784.    border-radius:9999px;
  1785.    border:0;
  1786.    background:#0A2A45;
  1787.    color:#fff;
  1788.    font-size:20px;
  1789.    display:flex;
  1790.    align-items:center;
  1791.    justify-content:center;
  1792.    box-shadow:0 8px 20px rgba(0,0,0,.25);
  1793.    cursor:pointer;
  1794.    }
  1795.    /* 👇 menu is HIDDEN by default */
  1796.    #cms-devbar-menu{
  1797.    position:absolute;
  1798.    top:48px;
  1799.    right:0;
  1800.    background:#0A2A45;
  1801.    color:#fff;
  1802.    border-radius:10px;
  1803.    padding:8px;
  1804.    box-shadow:0 8px 20px rgba(0,0,0,.25);
  1805.    min-width:190px;
  1806.    display:none;                 /* <= here */
  1807.    flex-direction:column;
  1808.    gap:4px;
  1809.    }
  1810.    #cms-devbar.is-open #cms-devbar-menu{
  1811.    display:flex;
  1812.    }
  1813.    #cms-devbar-menu button{
  1814.    background:transparent;
  1815.    border:0;
  1816.    color:#fff;
  1817.    text-align:left;
  1818.    padding:6px 10px;
  1819.    border-radius:6px;
  1820.    font-size:13px;
  1821.    cursor:pointer;
  1822.    white-space:nowrap;
  1823.    }
  1824.    #cms-devbar-menu button:hover{
  1825.    background:rgba(255,255,255,.12);
  1826.    }
  1827.    #cms-devbar.is-open #cms-devbar-toggle{
  1828.    background:#F14816;
  1829.    }
  1830. </style>
  1831. <script>
  1832.    (function(){
  1833.      const bar = document.getElementById('cms-devbar');
  1834.      if (!bar) return;
  1835.      const toggle = document.getElementById('cms-devbar-toggle');
  1836.      const menu = document.getElementById('cms-devbar-menu');
  1837.    
  1838.      function setOpen(open){
  1839.        bar.classList.toggle('is-open', open);
  1840.        if (open) {
  1841.          menu.hidden = false;
  1842.          toggle.setAttribute('aria-expanded','true');
  1843.        } else {
  1844.          menu.hidden = true;
  1845.          toggle.setAttribute('aria-expanded','false');
  1846.        }
  1847.      }
  1848.    
  1849.      toggle.addEventListener('click', function(){
  1850.        const isOpen = bar.classList.contains('is-open');
  1851.        setOpen(!isOpen);
  1852.      });
  1853.    
  1854.      bar.addEventListener('click', function(e){
  1855.        const btn = e.target.closest('button[data-hero]');
  1856.        if (!btn) return;
  1857.        const idx = btn.dataset.hero;
  1858.        const fab = document.querySelector('[data-scope=hero-slide' + idx + '] .cms-edit-fab');
  1859.        if (fab) fab.click();
  1860.        setOpen(false);
  1861.      });
  1862.    
  1863.      document.addEventListener('click', function(e){
  1864.        if (!bar.contains(e.target) && bar.classList.contains('is-open')) {
  1865.          setOpen(false);
  1866.        }
  1867.      });
  1868.    
  1869.      setOpen(false);
  1870.    })();
  1871. </script>
  1872. {# {% endif %} #}
  1873. <style>
  1874.    .hero-slide{ position:relative; background:var(--img) center/cover no-repeat; height: min(72vh, 860px); width: 100%; }
  1875.    /* Force hero content alignment */
  1876.    .hero-slide .container.hero-content{ 
  1877.    position: relative !important; 
  1878.    z-index: 1 !important; 
  1879.    height: 100% !important;
  1880.    display: flex !important; 
  1881.    align-items: center !important; 
  1882.    justify-content: flex-start !important;
  1883.    max-width: 1180px !important;
  1884.    margin: 0 auto !important;
  1885.    padding: 0 12px !important;
  1886.    }
  1887.    .hero-slide .hero-text-content {
  1888.    width: 100% !important;
  1889.    text-align: left !important;
  1890.    display: flex !important;
  1891.    flex-direction: column !important;
  1892.    align-items: flex-start !important;
  1893.    }
  1894.    .hero-slide .hero-kicker {
  1895.    text-align: left !important;
  1896.    margin-bottom: 6px !important;
  1897.    width: 100% !important;
  1898.    }
  1899.    .hero-slide .hero-title {
  1900.    text-align: left !important;
  1901.    margin: 10px 0 18px 0 !important;
  1902.    width: 100% !important;
  1903.    }
  1904.    .hero-slide .hero-ctas {
  1905.    text-align: left !important;
  1906.    margin-top: 18px !important;
  1907.    justify-content: flex-start !important;
  1908.    width: 100% !important;
  1909.    }
  1910.    /* Override any existing styles */
  1911.    .hero .hero-content {
  1912.    display: flex !important;
  1913.    align-items: center !important;
  1914.    justify-content: flex-start !important;
  1915.    }
  1916.    .cms-edit-fab{
  1917.    position:absolute; top:12px; right:12px; z-index:9;
  1918.    width:36px; height:36px; border:0; border-radius:50%;
  1919.    background:rgba(255,255,255,.95); box-shadow:0 2px 8px rgba(0,0,0,.2);
  1920.    cursor:pointer; font-size:16px; line-height:36px; text-align:center;
  1921.    }
  1922.    .cms-modal[hidden]{ display:none !important; }
  1923.    .cms-modal{ position:fixed; inset:0; display:flex; align-items:center; justify-content:center; background:rgba(0,0,0,.45); z-index:9999; }
  1924.    .cms-modal__dialog{ background:#fff; width:min(720px,92vw); max-height:90vh; overflow:auto; border-radius:12px; box-shadow:0 10px 40px rgba(0,0,0,.25); }
  1925.    .cms-modal__header,.cms-modal__footer{ padding:12px 16px; background:#f7f7f8; display:flex; align-items:center; justify-content:space-between; }
  1926.    .cms-modal__body{ padding:16px; display:grid; gap:12px; }
  1927.    .cms-field span{ display:block; font-size:.9rem; color:#555; margin-bottom:4px; }
  1928.    .cms-field input[type="text"], .cms-field textarea{ width:100%; padding:8px 10px; border:1px solid #ddd; border-radius:6px; }
  1929. </style>
  1930. <script>
  1931.    (function () {
  1932.      // --- helper loader bouton ---
  1933.      function setLoading(btn, isLoading, text = 'Enregistrement…') {
  1934.        if (!btn) return;
  1935.        if (isLoading) {
  1936.          if (!btn.dataset._label) btn.dataset._label = btn.textContent;
  1937.          btn.disabled = true;
  1938.          btn.setAttribute('aria-busy', 'true');
  1939.          btn.innerHTML =
  1940.            '<svg width="16" height="16" viewBox="0 0 50 50" aria-hidden="true" focusable="false" style="vertical-align:-2px;margin-right:8px;">' +
  1941.              '<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="6" stroke-opacity="0.25"></circle>' +
  1942.              '<path d="M25 5 a20 20 0 0 1 0 40" stroke="currentColor" stroke-width="6" fill="none">' +
  1943.                '<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.8s" repeatCount="indefinite"></animateTransform>' +
  1944.              '</path>' +
  1945.            '</svg>' + text;
  1946.        } else {
  1947.          btn.disabled = false;
  1948.          btn.removeAttribute('aria-busy');
  1949.          btn.textContent = btn.dataset._label || 'Enregistrer';
  1950.        }
  1951.      }
  1952.    
  1953.      const modal = document.createElement('div');
  1954.      modal.id = 'cms-hero-modal';
  1955.      modal.className = 'cms-modal';
  1956.      modal.hidden = true;
  1957.      modal.innerHTML = `
  1958.        <div class="cms-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="cms-hero-modal-title">
  1959.          <div class="cms-modal__header">
  1960.            <strong id="cms-hero-modal-title">Éditer le slide</strong>
  1961.            <button type="button" class="cms-modal__close" aria-label="Fermer">×</button>
  1962.          </div>
  1963.          <div class="cms-modal__body">
  1964.            <label class="cms-field"><span>Kicker</span><input type="text" id="cms-hero-kicker"></label>
  1965.            <label class="cms-field"><span>Titre (HTML autorisé)</span><textarea id="cms-hero-title" rows="4"></textarea></label>
  1966.    
  1967.    
  1968.    
  1969.            <label class="cms-field">
  1970.              <span>Texte du bouton</span>
  1971.              <input type="text" id="cms-hero-cta">
  1972.            </label>
  1973.    
  1974.            <label class="cms-field">
  1975.              <span>Lien du bouton</span>
  1976.              <input type="text" id="cms-hero-link" placeholder="https://exemple.com ou /route-interne">
  1977.            </label>
  1978.    
  1979.            <label class="cms-field">
  1980.              <span>Texte du bandeau orange</span>
  1981.              <input type="text" id="cms-hero-ribbon">
  1982.            </label>
  1983.    
  1984.    
  1985.    
  1986.    
  1987.            <label class="cms-field"><span>Image de fond (png/jpg/webp)</span><input type="file" id="cms-hero-file" accept="image/png,image/jpeg,image/webp"></label>
  1988.            <p class="cms-hint">Astuce : privilégiez un visuel ≥ 1600px de large.</p>
  1989.          </div>
  1990.          <div class="cms-modal__footer">
  1991.            <button type="button" class="btn btn-secondary" id="cms-hero-cancel">Annuler</button>
  1992.            <button type="button" class="btn btn-primary" id="cms-hero-save">Enregistrer</button>
  1993.          </div>
  1994.        </div>`;
  1995.      document.body.appendChild(modal);
  1996.    
  1997.      const $ = (s, r=document)=>r.querySelector(s);
  1998.      const closeBtn = $('.cms-modal__close', modal);
  1999.      const cancelBtn= $('#cms-hero-cancel', modal);
  2000.      const saveBtn  = $('#cms-hero-save', modal);
  2001.      const inK      = $('#cms-hero-kicker', modal);
  2002.      const inT      = $('#cms-hero-title',  modal);
  2003.      const inC      = $('#cms-hero-cta',    modal);
  2004.      const inF      = $('#cms-hero-file',   modal);
  2005.      const inR      = $('#cms-hero-ribbon', modal);
  2006.      const inL = $('#cms-hero-link',   modal);
  2007.    
  2008.      let ctx = null;
  2009.    
  2010.      function openModal(context){
  2011.        ctx = context;
  2012.        const s = ctx.slideEl;
  2013.    
  2014.       inK.value = (s.querySelector('.hero-sub')?.textContent || '').trim();
  2015.       inT.value = (s.querySelector('.hero-title')?.innerHTML || '').trim();
  2016.        inC.value = (s.querySelector('.btn')?.textContent    || '').trim();
  2017.    
  2018.        const btn = s.querySelector('.btn');
  2019.        inL.value = (btn?.getAttribute('href') || '').trim();
  2020.    
  2021.        const ribbonText =
  2022.          s.querySelector('.home-event-text')?.textContent ||
  2023.          s.dataset.ribbonText ||
  2024.          '';
  2025.        inR.value = ribbonText.trim();
  2026.    
  2027.        inF.value = '';
  2028.        modal.hidden = false;
  2029.      }
  2030.    
  2031.      function closeModal(){
  2032.        modal.hidden = true;
  2033.        ctx = null;
  2034.      }
  2035.    
  2036.      closeBtn.addEventListener('click', closeModal);
  2037.      cancelBtn.addEventListener('click', closeModal);
  2038.    
  2039.      document.addEventListener('click', (e)=>{
  2040.        const fab = e.target.closest('.cms-edit-fab');
  2041.        if (!fab) return;
  2042.        const slide = fab.closest('[data-scope]');
  2043.        openModal({
  2044.          slideEl: slide,
  2045.          kickerKey: fab.dataset.kickerKey,
  2046.          titleKey:  fab.dataset.titleKey,
  2047.          ctaKey:    fab.dataset.ctaKey,
  2048.          imgKey:    fab.dataset.imgKey,
  2049.          ribbonKey: fab.dataset.ribbonKey,
  2050.          linkKey:   fab.dataset.linkKey 
  2051.        });
  2052.      });
  2053.    
  2054.      async function postText(key, text){
  2055.        const r = await fetch('{{ path("cms_text_update") }}', {
  2056.          method:'POST', headers:{'Content-Type':'application/json'},
  2057.          body: JSON.stringify({ key, text })
  2058.        });
  2059.        const d = await r.json();
  2060.        if (!d.ok) throw new Error(d.error || 'Erreur texte');
  2061.        return d;
  2062.      }
  2063.    
  2064.      function desiredHeroName(file, key) {
  2065.        const prefix = key === 'hero_slide1_bg' ? 'hero1_' :
  2066.                       key === 'hero_slide4_bg' ? 'hero4_' :
  2067.                       key === 'hero_slide5_bg' ? 'hero5_' :
  2068.                       key === 'hero_slide6_bg' ? 'hero6_' :
  2069.                       key === 'hero_slide7_bg' ? 'hero7_' :
  2070.                       key === 'hero_slide8_bg' ? 'hero8_' :
  2071.                       key === 'hero_slide9_bg' ? 'hero9_' :
  2072.                       key === 'hero_slide10_bg' ? 'hero10_' :
  2073.                       key === 'hero_slide3_bg' ? 'hero3_' :
  2074.                       key === 'hero_slide2_bg' ? 'hero2_' : '';
  2075.        const ext  = (file.name.split('.').pop() || '').toLowerCase();
  2076.        const base = file.name.replace(/\.[^.]+$/, '')
  2077.                              .toLowerCase()
  2078.                              .replace(/[^a-z0-9]+/g, '-')
  2079.                              .replace(/^-+|-+$/g, '') || 'image';
  2080.        const stamp = new Date().toISOString().replace(/[-:TZ.]/g,'').slice(0,14);
  2081.        return `${prefix}${base}-${stamp}.${ext}`;
  2082.      }
  2083.    
  2084.     async function uploadImage(key, file){
  2085.        const fd = new FormData();
  2086.        fd.append('key', key);
  2087.        fd.append('file', file, desiredHeroName(file, key));
  2088.        const r = await fetch('{{ path("cms_page_media_upload") }}', { method:'POST', body: fd });
  2089.        const d = await r.json();
  2090.        if (!d.ok) throw new Error(d.error || 'Erreur upload');
  2091.        return d.path;
  2092.      }
  2093.    
  2094.      saveBtn.addEventListener('click', async ()=>{
  2095.        if (!ctx) return;
  2096.        setLoading(saveBtn, true);
  2097.        try{
  2098.          const tasks = [];
  2099.          if (ctx.kickerKey) tasks.push(postText(ctx.kickerKey, inK.value.trim()));
  2100.          if (ctx.titleKey)  tasks.push(postText(ctx.titleKey,  inT.value.trim()));
  2101.          if (ctx.ctaKey)    tasks.push(postText(ctx.ctaKey,    inC.value.trim()));
  2102.          if (ctx.ribbonKey) tasks.push(postText(ctx.ribbonKey, inR.value.trim()));
  2103.          if (ctx.linkKey)   tasks.push(postText(ctx.linkKey,   inL.value.trim()));
  2104.          if (tasks.length) await Promise.all(tasks);
  2105.    
  2106.          let newPath = null;
  2107.          if (inF.files && inF.files[0]) {
  2108.            newPath = await uploadImage(ctx.imgKey, inF.files[0]);
  2109.          }
  2110.    
  2111.          const s = ctx.slideEl;
  2112.         if (ctx.kickerKey) {
  2113.           const kickerHtml = inK.value.trim();
  2114.           let sub = s.querySelector('.hero-sub');
  2115.           if (kickerHtml) {
  2116.             if (!sub) {
  2117.               sub = document.createElement('div');
  2118.               sub.className = 'hero-sub';
  2119.               const titleEl = s.querySelector('.hero-title');
  2120.               if (titleEl) titleEl.insertAdjacentElement('afterend', sub);
  2121.               else s.querySelector('.hero-copy')?.appendChild(sub);
  2122.             }
  2123.             sub.innerHTML = kickerHtml;
  2124.           } else if (sub) {
  2125.             sub.remove();
  2126.           }
  2127.         }
  2128.         if (ctx.titleKey)  { const t = s.querySelector('.hero-title'); if (t) t.innerHTML = inT.value.trim(); }
  2129.          if (ctx.ctaKey)    { const c = s.querySelector('.btn');   if (c) c.textContent = inC.value.trim(); }
  2130.    
  2131.           if (ctx.linkKey) {
  2132.          const cta = s.querySelector('.btn');
  2133.          if (cta) cta.setAttribute('href', inL.value.trim() || '#'); 
  2134.        }
  2135.          if (ctx.ribbonKey) {
  2136.            const raw = inR.value.trim();
  2137.            const txt = (raw && raw !== '-') ? raw : '';
  2138.            const shell = s.querySelector('.hero-ribbon-shell');
  2139.            const rEl = s.querySelector('.home-event-text');
  2140.            if (rEl) rEl.textContent = txt;
  2141.            if (shell) shell.style.display = txt ? '' : 'none';
  2142.            s.dataset.ribbonText = txt;
  2143.          }
  2144.    
  2145.          if (newPath) {
  2146.            const BASE = "{{ asset('/') }}";
  2147.            s.style.setProperty('--img', `url('${BASE}${newPath}')`);
  2148.            s.style.setProperty('--bg',  `url('${BASE}${newPath}')`);
  2149.          }
  2150.    
  2151.          closeModal();
  2152.        } catch(err){
  2153.          alert(err.message || 'Erreur inconnue');
  2154.        } finally{
  2155.          setLoading(saveBtn, false);
  2156.        }
  2157.      });
  2158.    
  2159.    })();
  2160. </script>
  2161. {# --------------------------- CATALOGUE CTA --------------------------- #}
  2162. <section class="catalogue">
  2163.    <div class="container">
  2164.       <div class="catalogue-grid">
  2165.          <div class="catalogue-copy">
  2166.             <div class="catalogue-text-wrap">
  2167.                <h2 class="sec-title">DÉCOUVREZ NOTRE NOUVEAU CATALOGUE DE CONSEIL ET DE FORMATION 2026</h2>
  2168.                <p id="catalogue-text-left" class="sec-text">
  2169.                   {{ catalogue_text_left }}
  2170.                </p>
  2171.                <button
  2172.                   type="button"
  2173.                   class="catalogue-edit-btn"
  2174.                   aria-label="Modifier le texte du catalogue"
  2175.                   data-url="{{ path('catalogue_text_update') }}"
  2176.                   data-csrf="{{ csrf_token('catalogue_text_update') }}"
  2177.                   data-current-text="{{ catalogue_text_left|e('html_attr') }}"
  2178.                   >
  2179.                ✏️
  2180.                </button>
  2181.                <div class="btn-row">
  2182.                   <a href="https://catalogue.acoa.fr/" class="btn btn-outline">Voir plus</a>
  2183.                   <a style ="display:none;" href="{{ asset('catalogue-2026.pdf') }}" class="btn btn-primary" download="catalogue-2026.pdf">Telecharger</a>
  2184.                </div>
  2185.             </div>
  2186.          </div>
  2187.          {# Modal (render once anywhere inside this template, e.g. after the section) #}
  2188.          <div id="catalogue-edit-modal" class="catalogue-modal" hidden>
  2189.             <div class="catalogue-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="catalogue-edit-title">
  2190.                <h3 id="catalogue-edit-title">Modifier le texte du catalogue</h3>
  2191.                <textarea id="catalogue-edit-textarea" rows="8"></textarea>
  2192.                <div class="catalogue-modal__actions">
  2193.                   <button type="button" class="btn btn-outline" id="catalogue-cancel">Annuler</button>
  2194.                   <button type="button" class="btn btn-primary" id="catalogue-validate">Valider</button>
  2195.                </div>
  2196.             </div>
  2197.          </div>
  2198.          <div class="catalogue-art">
  2199.             <div class="cat-card">
  2200.                <img src="{{ asset('assets/img/pexels-thirdman-5684445.png') }}" alt="Catalogue 2026"/>
  2201.                <div class="cat-veil"></div>
  2202.             </div>
  2203.          </div>
  2204.       </div>
  2205.    </div>
  2206. </section>
  2207. {# --------------------------- DOMAINES DE COMPÉTENCES --------------------------- #}
  2208. <section class="competences">
  2209.    <div class="container">
  2210.       <div class="panel panel--blue"
  2211.          style="background-image:url('{{ asset('assets/img/img-conseiller.png') }}');
  2212.          background-size:cover;
  2213.          background-position:center;
  2214.          background-repeat:no-repeat;">
  2215.          <h2 class="sec-title">Nos domaines de compétences</h2>
  2216.          <div class="competences-grid">
  2217.             {% set items = [
  2218.             ["Organisation du travail par l'approche Qualité", 'stonks.png', '/approches-de-la-relation'],
  2219.             ['Élaboration et conduite de projet', 'eos-icons_content-lifecycle-management.png', '/elaboration-et-conduite-de-projet'],
  2220.             ['Équipe de cadres et managers', 'ri_team-fill.png', '/equipe-cadres-managers'],
  2221.             ['Santé au travail', 'mage_heart-health-fill.png', '/sante-au-travail'],
  2222.             ['Gestion globale des risques, des crises & développement durable', 'graph.png', '/gestion-des-risques'],
  2223.             ['Management des ressources humaines', 'carbon_id-management.png', '/management'],
  2224.             ["Mobilisation & cohésion d'équipes", 'hugeicons_agreement-01.png', '/mobilisation-et-cohesion-d-equipes'],
  2225.             ['Organisation sociale & médico-sociale', 'ion_share-social-sharp.png', '/organisation-sociale-medico-social'],
  2226.             ['Évolution et transition professionnelle', 'ph_plant-bold.png', '/evolution-transition-pro'],
  2227.             ] %}
  2228.             {% for i in items %}
  2229.             <a class="comp-card" href="{{ i[2] }}">
  2230.                <img src="{{ asset('assets/img/icons/' ~ i[1]) }}" alt="" class="comp-icon">
  2231.                <p>{{ i[0] }}</p>
  2232.             </a>
  2233.             {% endfor %}
  2234.          </div>
  2235.          <div class="center mt-24">
  2236.             <a href="https://catalogue.acoa.fr/" class="btn btn-secondary">Explorer</a>
  2237.          </div>
  2238.       </div>
  2239.    </div>
  2240. </section>
  2241. <style>
  2242.    .competences .competences-grid{
  2243.    margin-top: 10px;
  2244.    display: grid;
  2245.    grid-template-columns: repeat(3, minmax(0,1fr));
  2246.    gap: 22px 28px;
  2247.    align-items: start;
  2248.    }
  2249.    @media (max-width: 1100px){
  2250.    .competences .competences-grid{
  2251.    grid-template-columns: repeat(2, minmax(0,1fr));
  2252.    }
  2253.    }
  2254.    @media (max-width: 640px){
  2255.    .competences .competences-grid{
  2256.    grid-template-columns: 1fr;
  2257.    }
  2258.    }
  2259.    .competences .comp-card p{
  2260.    margin: 0;
  2261.    font-weight: 800;
  2262.    font-size: clamp(13px,0.95vw,16px);
  2263.    line-height: 1.3;
  2264.    text-align: left;
  2265.    overflow-wrap: anywhere;
  2266.    text-wrap: balance;
  2267.    }
  2268.    .competences .comp-card{
  2269.    text-decoration: none;
  2270.    color: inherit;
  2271.    cursor: pointer;
  2272.    }
  2273. </style>
  2274. {# --------------------------- PRESTATIONS (3 cards over images) --------------------------- #}
  2275. <section class="prestations">
  2276.    <style>
  2277.       @media (max-width: 768px){
  2278.         .prestations .presta-carousel{
  2279.           position: relative;
  2280.         }
  2281.         #presta-track{
  2282.           display: flex !important;
  2283.           overflow-x: auto;
  2284.           gap: 10px;
  2285.           padding: 0 4px;
  2286.           scroll-snap-type: x mandatory;
  2287.           -webkit-overflow-scrolling: touch;
  2288.           scrollbar-width: none;
  2289.         }
  2290.         #presta-track::-webkit-scrollbar{
  2291.           display:none;
  2292.         }
  2293.         #presta-track .presta-card{
  2294.           flex: 0 0 calc(100% - 8px);
  2295.           min-width: calc(100% - 8px);
  2296.           scroll-snap-align: center;
  2297.           border-radius: 0;
  2298.           min-height: clamp(520px, 78vh, 680px);
  2299.         }
  2300.         .prestations .prestations--overimg .presta-card{
  2301.           padding: 0 !important;
  2302.           background-position: center !important;
  2303.           background-size: cover !important;
  2304.         }
  2305.         .prestations .prestations--overimg .presta-card::after{
  2306.           background: linear-gradient(180deg, rgba(10,43,88,.18) 0%, rgba(10,43,88,.62) 70%, rgba(10,43,88,.82) 100%) !important;
  2307.         }
  2308.         /* Keep text and CTA visible on mobile cards */
  2309.         .prestations .presta-inner{
  2310.           display: flex !important;
  2311.           flex-direction: column;
  2312.           justify-content: flex-end;
  2313.           height: 100%;
  2314.           padding: 18px 16px;
  2315.         }
  2316.         .prestations .presta-inner h3{
  2317.           font-size: clamp(22px, 6vw, 34px);
  2318.           line-height: 1.1;
  2319.           margin: 0 0 10px 0;
  2320.         }
  2321.         .prestations .presta-inner p{
  2322.           font-size: clamp(13px, 3.8vw, 18px);
  2323.           line-height: 1.5;
  2324.           margin: 0 0 12px 0;
  2325.           max-width: 92%;
  2326.         }
  2327.         .prestations .presta-cta{
  2328.           margin-top: auto;
  2329.           align-self: center;
  2330.         }
  2331.         .prestations .presta-nav{
  2332.           position: absolute;
  2333.           top: 50%;
  2334.           transform: translateY(-50%);
  2335.           width: 42px;
  2336.           height: 42px;
  2337.           border: 0;
  2338.           border-radius: 999px;
  2339.           background: rgba(41, 52, 58, .45);
  2340.           color: #fff;
  2341.           box-shadow: 0 8px 18px rgba(0,0,0,.22);
  2342.           display: grid;
  2343.           place-items: center;
  2344.           z-index: 5;
  2345.         }
  2346.         .prestations .presta-nav.prev{ left: 10px; }
  2347.         .prestations .presta-nav.next{ right: 10px; }
  2348.         .prestations .presta-dots{
  2349.           display: flex;
  2350.           justify-content: center;
  2351.           gap: 7px;
  2352.           margin-top: 10px;
  2353.         }
  2354.         .prestations .presta-dots button{
  2355.           width: 7px;
  2356.           height: 7px;
  2357.           border-radius: 999px;
  2358.           border: 0;
  2359.           background: rgba(255,255,255,.55);
  2360.         }
  2361.         .prestations .presta-dots button[aria-selected="true"]{
  2362.           background: #fff;
  2363.           transform: scale(1.1);
  2364.         }
  2365.       }
  2366.       @media (min-width: 769px){
  2367.         .prestations .presta-nav,
  2368.         .prestations .presta-dots{
  2369.           display: none;
  2370.         }
  2371.       }
  2372.    </style>
  2373.    <div class="container">
  2374.       <h2 class="sec-title sec-title--orange"> NOS PRESTATIONS {# (Optional) make the section title editable too — uncomment to use: <button class="cms-edit-btn" data-url="{{ path('cms_text_update') }}" data-key="prestations_title" data-target="#prestations-title" data-mode="text" aria-label="Modifier le titre de la section">✏️</button> #} </h2>
  2375.       <div class="presta-carousel">
  2376.          <div class="prestations-grid prestations--overimg" id="presta-track">
  2377.             <article class="presta-card tone-a"
  2378.                style="--img:url('{{ asset(prestations[0].img) }}')"
  2379.                data-title-key="presta_card1_title"
  2380.                data-text-key="text_action_formation_card1"
  2381.                data-cta-key="presta_card1_cta"
  2382.                data-img-key="presta_card1_img">
  2383.                <div class="presta-inner">
  2384.                   <h3 id="presta1-title">
  2385.                      {{ (prestations is defined ? prestations[0].title : 'Action de formation inter') }}
  2386.                      <button
  2387.                         class="cms-edit-btn"
  2388.                         data-url="{{ path('cms_text_update') }}"
  2389.                         data-key="presta_card1_title"
  2390.                         data-target="#presta1-title"
  2391.                         data-mode="text"
  2392.                         aria-label="Modifier le titre">✏️</button>
  2393.                   </h3>
  2394.                   <p id="presta1-text">
  2395.                      {{ (prestations is defined ? prestations[0].text : text_action_formation_card1)|raw }}
  2396.                      <button
  2397.                         class="cms-edit-btn"
  2398.                         data-url="{{ path('cms_text_update') }}"
  2399.                         data-key="text_action_formation_card1"
  2400.                         data-target="#presta1-text"
  2401.                         data-mode="html"
  2402.                         aria-label="Modifier le texte">✏️</button>
  2403.                   </p>
  2404.                   <a class="btn btn-primary presta-cta" id="presta1-cta" href="https://acoa.fr/intra">
  2405.                   {{ (prestations is defined ? prestations[0].cta : 'Voir plus') }}
  2406.                   </a>
  2407.                </div>
  2408.             </article>
  2409.             <article class="presta-card tone-b"
  2410.                style="--img:url('{{ asset(prestations[1].img) }}')"
  2411.                data-title-key="presta_card2_title"
  2412.                data-text-key="text_action_formation_card2"
  2413.                data-cta-key="presta_card2_cta"
  2414.                data-img-key="presta_card2_img">
  2415.                <div class="presta-inner">
  2416.                   <h3 id="presta2-title">
  2417.                      {{ (prestations is defined ? prestations[1].title : 'Bilan de compétence') }}
  2418.                      <button
  2419.                         class="cms-edit-btn"
  2420.                         data-url="{{ path('cms_text_update') }}"
  2421.                         data-key="presta_card2_title"
  2422.                         data-target="#presta2-title"
  2423.                         data-mode="text"
  2424.                         aria-label="Modifier le titre">✏️</button>
  2425.                   </h3>
  2426.                   <p id="presta2-text">
  2427.                      {{ (prestations is defined ? prestations[1].text : text_action_formation_card2)|raw }}
  2428.                      <button
  2429.                         class="cms-edit-btn"
  2430.                         data-url="{{ path('cms_text_update') }}"
  2431.                         data-key="text_action_formation_card2"
  2432.                         data-target="#presta2-text"
  2433.                         data-mode="html"
  2434.                         aria-label="Modifier le texte">✏️</button>
  2435.                   </p>
  2436.                   <a class="btn btn-primary presta-cta" id="presta2-cta" href="https://acoa.fr/competency-domains">
  2437.                   {{ (prestations is defined ? prestations[1].cta : 'Voir plus') }}
  2438.                   </a>
  2439.                </div>
  2440.             </article>
  2441.             <article class="presta-card tone-c"
  2442.                style="--img:url('{{ asset(prestations[2].img) }}')"
  2443.                data-title-key="presta_card3_title"
  2444.                data-text-key="text_action_formation_card3"
  2445.                data-cta-key="presta_card3_cta"
  2446.                data-img-key="presta_card3_img">
  2447.                <div class="presta-inner">
  2448.                   <h3 id="presta3-title">
  2449.                      {{ (prestations is defined ? prestations[2].title : 'Prestation conseils') }}
  2450.                      <button
  2451.                         class="cms-edit-btn"
  2452.                         data-url="{{ path('cms_text_update') }}"
  2453.                         data-key="presta_card3_title"
  2454.                         data-target="#presta3-title"
  2455.                         data-mode="text"
  2456.                         aria-label="Modifier le titre">✏️</button>
  2457.                   </h3>
  2458.                   <p id="presta3-text">
  2459.                      {{ (prestations is defined ? prestations[2].text : text_action_formation_card3)|raw }}
  2460.                      <button
  2461.                         class="cms-edit-btn"
  2462.                         data-url="{{ path('cms_text_update') }}"
  2463.                         data-key="text_action_formation_card3"
  2464.                         data-target="#presta3-text"
  2465.                         data-mode="html"
  2466.                         aria-label="Modifier le texte">✏️</button>
  2467.                   </p>
  2468.                </div>
  2469.             </article>
  2470.          </div>
  2471.          <button class="presta-nav prev" aria-label="Slide précédente">‹</button>
  2472.          <button class="presta-nav next" aria-label="Slide suivante">›</button>
  2473.          <div class="presta-dots" role="tablist" aria-label="Sélecteur de prestations"></div>
  2474.       </div>
  2475.    </div>
  2476.   <script>
  2477. (() => {
  2478.   const car = document.querySelector('.prestations .presta-carousel');
  2479.   if (!car) return;
  2480.   const track = car.querySelector('#presta-track');
  2481.   const slides = [...track.querySelectorAll('.presta-card')];
  2482.   const prev = car.querySelector('.presta-nav.prev');
  2483.   const next = car.querySelector('.presta-nav.next');
  2484.   const dots = car.querySelector('.presta-dots');
  2485.   if (!track || !slides.length || !prev || !next || !dots) return;
  2486.   dots.innerHTML = '';
  2487.   slides.forEach((_, i) => {
  2488.     const b = document.createElement('button');
  2489.     b.type = 'button';
  2490.     b.setAttribute('role', 'tab');
  2491.     b.addEventListener('click', () => goTo(i, 'smooth'));
  2492.     dots.appendChild(b);
  2493.   });
  2494.   let index = 0;
  2495.   let raf = 0;
  2496.   function clamp(i) {
  2497.     return Math.max(0, Math.min(slides.length - 1, i));
  2498.   }
  2499.   function setActive(i) {
  2500.     index = clamp(i);
  2501.     [...dots.children].forEach((b, idx) => {
  2502.       b.setAttribute('aria-selected', idx === index ? 'true' : 'false');
  2503.     });
  2504.     car.dataset.index = String(index);
  2505.   }
  2506.   function goTo(i, behavior = 'smooth') {
  2507.     i = clamp(i);
  2508.     const tr = track.getBoundingClientRect();
  2509.     const sr = slides[i].getBoundingClientRect();
  2510.     const trCenter = tr.left + tr.width / 2;
  2511.     const srCenter = sr.left + sr.width / 2;
  2512.     let target = track.scrollLeft + (srCenter - trCenter);
  2513.     const max = track.scrollWidth - track.clientWidth;
  2514.     if (target < 0) target = 0;
  2515.     if (target > max) target = max;
  2516.     track.scrollTo({ left: target, behavior });
  2517.     setActive(i);
  2518.   }
  2519.   function computeActiveFromScroll() {
  2520.     const tr = track.getBoundingClientRect();
  2521.     const center = tr.left + tr.width / 2;
  2522.     let best = 0;
  2523.     let bestDist = Infinity;
  2524.     for (let i = 0; i < slides.length; i++) {
  2525.       const r = slides[i].getBoundingClientRect();
  2526.       const c = r.left + r.width / 2;
  2527.       const d = Math.abs(c - center);
  2528.       if (d < bestDist) {
  2529.         bestDist = d;
  2530.         best = i;
  2531.       }
  2532.     }
  2533.     setActive(best);
  2534.   }
  2535.   function onScroll() {
  2536.     if (raf) return;
  2537.     raf = requestAnimationFrame(() => {
  2538.       raf = 0;
  2539.       computeActiveFromScroll();
  2540.     });
  2541.   }
  2542.   prev.addEventListener('click', () => goTo((+car.dataset.index || 0) - 1, 'smooth'));
  2543.   next.addEventListener('click', () => goTo((+car.dataset.index || 0) + 1, 'smooth'));
  2544.   track.addEventListener('scroll', onScroll, { passive: true });
  2545.   window.addEventListener('resize', () => {
  2546.     computeActiveFromScroll();
  2547.   });
  2548.   setActive(0);
  2549.   goTo(0, 'auto');
  2550. })();
  2551. </script>
  2552.  
  2553.    <div id="cms-modal" class="cms-modal" hidden>
  2554.       <div class="cms-modal__dialog">
  2555.          <div class="cms-modal__header"> <strong id="cms-modal-title">Éditer le contenu</strong> <button type="button" class="cms-modal__close" aria-label="Fermer">×</button> </div>
  2556.          <div class="cms-modal__body">
  2557.             <textarea id="cms-editor" rows="8"></textarea>
  2558.             <p class="cms-hint">Astuce : pour les paragraphes, vous pouvez utiliser des sauts de ligne ou du HTML simple (strong, br, a, etc.).</p>
  2559.          </div>
  2560.          <div class="cms-modal__footer"> <button type="button" class="btn btn-secondary" id="cms-cancel">Annuler</button> <button type="button" class="btn btn-primary" id="cms-save">Enregistrer</button> </div>
  2561.       </div>
  2562.    </div>
  2563. </section>
  2564. <script>
  2565.    (() => {
  2566.      // --- Loader helper for the "Enregistrer" button ---
  2567.      function setLoading(btn, isLoading, text = 'Enregistrement…') {
  2568.        if (!btn) return;
  2569.        if (isLoading) {
  2570.          if (!btn.dataset._label) btn.dataset._label = btn.textContent;
  2571.          btn.disabled = true;
  2572.          btn.setAttribute('aria-busy', 'true');
  2573.          btn.innerHTML =
  2574.            '<svg width="16" height="16" viewBox="0 0 50 50" aria-hidden="true" focusable="false" style="vertical-align:-2px;margin-right:8px;">' +
  2575.              '<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="6" stroke-opacity="0.25"></circle>' +
  2576.              '<path d="M25 5 a20 20 0 0 1 0 40" stroke="currentColor" stroke-width="6" fill="none">' +
  2577.                '<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.8s" repeatCount="indefinite"></animateTransform>' +
  2578.              '</path>' +
  2579.            '</svg>' + text;
  2580.        } else {
  2581.          btn.disabled = false;
  2582.          btn.removeAttribute('aria-busy');
  2583.          btn.textContent = btn.dataset._label || 'Enregistrer';
  2584.        }
  2585.      }
  2586.    
  2587.      const modal = document.getElementById('cms-modal');
  2588.      const editor = document.getElementById('cms-editor');
  2589.      const btnSave = document.getElementById('cms-save');
  2590.      const btnCancel = document.getElementById('cms-cancel');
  2591.      const btnClose = modal.querySelector('.cms-modal__close');
  2592.      const titleEl = document.getElementById('cms-modal-title');
  2593.    
  2594.      let current = { url: '', key: '', targetSel: '', mode: 'text' };
  2595.    
  2596.      function openModal({ url, key, targetSel, mode }) {
  2597.        current = { url, key, targetSel, mode };
  2598.        const target = document.querySelector(targetSel);
  2599.        if (!target) return;
  2600.    
  2601.        const value = mode === 'html' ? target.innerHTML.trim() : target.textContent.trim();
  2602.        editor.value = value;
  2603.        titleEl.textContent = `Éditer: ${key}`;
  2604.        modal.hidden = false;
  2605.        editor.focus();
  2606.      }
  2607.    
  2608.      function closeModal() {
  2609.        modal.hidden = true;
  2610.        editor.value = '';
  2611.        current = { url: '', key: '', targetSel: '', mode: 'text' };
  2612.      }
  2613.    
  2614.      document.addEventListener('click', (ev) => {
  2615.        const btn = ev.target.closest('.cms-edit-btn');
  2616.        if (btn) {
  2617.          ev.preventDefault();
  2618.          openModal({
  2619.            url: btn.dataset.url,
  2620.            key: btn.dataset.key,
  2621.            targetSel: btn.dataset.target,
  2622.            mode: btn.dataset.mode || 'text'
  2623.          });
  2624.        }
  2625.      });
  2626.    
  2627.      btnSave.addEventListener('click', async () => {
  2628.        const { url, key, targetSel, mode } = current;
  2629.        if (!url || !key || !targetSel) return;
  2630.    
  2631.        const text = editor.value.trim();
  2632.    
  2633.        setLoading(btnSave, true);
  2634.        try {
  2635.          const res = await fetch(url, {
  2636.            method: 'POST',
  2637.            headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
  2638.            body: JSON.stringify({ key, text })
  2639.          });
  2640.          const data = await res.json();
  2641.    
  2642.          if (!data.ok) throw new Error(data.error || 'Erreur inconnue');
  2643.    
  2644.          const target = document.querySelector(targetSel);
  2645.          if (target) {
  2646.            if (mode === 'html') target.innerHTML = text;
  2647.            else target.textContent = text;
  2648.          }
  2649.    
  2650.          closeModal();
  2651.        } catch (e) {
  2652.          alert('Impossible de sauvegarder: ' + e.message);
  2653.        } finally {
  2654.          setLoading(btnSave, false);
  2655.        }
  2656.      });
  2657.    
  2658.      [btnCancel, btnClose, modal].forEach(el => {
  2659.        el.addEventListener('click', (e) => {
  2660.          if (e.target === modal || e.target === btnCancel || e.target === btnClose) closeModal();
  2661.        });
  2662.      });
  2663.    
  2664.      document.addEventListener('keydown', (e) => {
  2665.        if (!modal.hidden && e.key === 'Escape') closeModal();
  2666.      });
  2667.    })();
  2668.    
  2669.    
  2670. </script>
  2671. <script>
  2672.    (function () {
  2673.      // --- Loader helper for the "Enregistrer" button ---
  2674.      function setLoading(btn, isLoading, text = 'Enregistrement…') {
  2675.        if (!btn) return;
  2676.        if (isLoading) {
  2677.          if (!btn.dataset._label) btn.dataset._label = btn.textContent;
  2678.          btn.disabled = true;
  2679.          btn.setAttribute('aria-busy', 'true');
  2680.          btn.innerHTML =
  2681.            '<svg width="16" height="16" viewBox="0 0 50 50" aria-hidden="true" focusable="false" style="vertical-align:-2px;margin-right:8px;">' +
  2682.              '<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="6" stroke-opacity="0.25"></circle>' +
  2683.              '<path d="M25 5 a20 20 0 0 1 0 40" stroke="currentColor" stroke-width="6" fill="none">' +
  2684.                '<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.8s" repeatCount="indefinite"></animateTransform>' +
  2685.              '</path>' +
  2686.            '</svg>' + text;
  2687.        } else {
  2688.          btn.disabled = false;
  2689.          btn.removeAttribute('aria-busy');
  2690.          btn.textContent = btn.dataset._label || 'Enregistrer';
  2691.        }
  2692.      }
  2693.    
  2694.      const UPDATE_URL = "{{ path('cms_text_update') }}";
  2695.      const $ = (sel, root=document) => root.querySelector(sel);
  2696.    
  2697.      let modal, closeEl, btnSave, btnCancel;
  2698.      let inKicker, inTitle, inP1, inP2, inCta, inPill1;
  2699.    
  2700.      let ctx = null;
  2701.    
  2702.      function openActuEditor(root) {
  2703.        if (!root) return;
  2704.        const els = {
  2705.          kicker: $('#actu-kicker', root),
  2706.          title:  $('#actu-title',  root),
  2707.          p1:     $('#actu-p1',     root),
  2708.          p2:     $('#actu-p2',     root),
  2709.          cta:    $('#actu-cta',    root),
  2710.          pill1:  $('#actu-pill1-label', root)
  2711.        };
  2712.        const keys = {
  2713.          kicker: root.dataset.kickerKey || '',
  2714.          title:  root.dataset.titleKey  || '',
  2715.          p1:     root.dataset.p1Key     || '',
  2716.          p2:     root.dataset.p2Key     || '',
  2717.          cta:    root.dataset.ctaKey    || '',
  2718.          pill1:  root.dataset.pill1Key  || ''
  2719.        };
  2720.    
  2721.        ctx = { root, els, keys };
  2722.    
  2723.        inKicker.value = (els.kicker?.textContent || 'Actualités du moment').trim();
  2724.        inTitle.value  = (els.title?.textContent  || 'Titre de l’actualité').trim();
  2725.        inP1.value     = (els.p1?.innerHTML       || '').trim();
  2726.        inP2.value     = (els.p2?.innerHTML       || '').trim();
  2727.        inCta.value    = (els.cta?.textContent    || 'Découvrir').trim();
  2728.        inPill1.value  = (els.pill1?.textContent  || 'Mieux Gérer').trim();
  2729.    
  2730.        modal.hidden = false;
  2731.        inTitle.focus();
  2732.      }
  2733.    
  2734.      function closeModal() {
  2735.        if (!modal) return;
  2736.        modal.hidden = true;
  2737.        ctx = null;
  2738.      }
  2739.    
  2740.      async function saveAll() {
  2741.        if (!ctx) return;
  2742.    
  2743.        const payloads = [];
  2744.        if (ctx.keys.kicker) payloads.push({ key: ctx.keys.kicker, text: inKicker.value.trim() });
  2745.        if (ctx.keys.title)  payloads.push({ key: ctx.keys.title,  text: inTitle.value.trim()  });
  2746.        if (ctx.keys.p1)     payloads.push({ key: ctx.keys.p1,     text: inP1.value.trim()     });
  2747.        if (ctx.keys.p2)     payloads.push({ key: ctx.keys.p2,     text: inP2.value.trim()     });
  2748.        if (ctx.keys.cta)    payloads.push({ key: ctx.keys.cta,    text: inCta.value.trim()    });
  2749.        if (ctx.keys.pill1)  payloads.push({ key: ctx.keys.pill1,  text: inPill1.value.trim()  });
  2750.    
  2751.        setLoading(btnSave, true);
  2752.        try {
  2753.          const results = await Promise.all(payloads.map(pl =>
  2754.            fetch(UPDATE_URL, {
  2755.              method: 'POST',
  2756.              headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
  2757.              body: JSON.stringify(pl)
  2758.            }).then(r => r.json())
  2759.          ));
  2760.    
  2761.          const bad = results.find(r => !r || r.ok === false);
  2762.          if (bad) throw new Error(bad.error || 'Erreur inconnue');
  2763.    
  2764.          if (ctx.els.kicker) ctx.els.kicker.textContent = inKicker.value.trim();
  2765.          if (ctx.els.title)  ctx.els.title.textContent  = inTitle.value.trim();
  2766.          if (ctx.els.p1)     ctx.els.p1.innerHTML       = inP1.value.trim();
  2767.          if (ctx.els.p2)     ctx.els.p2.innerHTML       = inP2.value.trim();
  2768.          if (ctx.els.cta)    ctx.els.cta.textContent    = inCta.value.trim();
  2769.          if (ctx.els.pill1)  ctx.els.pill1.textContent  = inPill1.value.trim();
  2770.    
  2771.          closeModal();
  2772.        } catch (err) {
  2773.          location.reload();
  2774.          //alert('Modification effectuée!');
  2775.        } finally {
  2776.          setLoading(btnSave, false);
  2777.        }
  2778.      }
  2779.    
  2780.      document.addEventListener('DOMContentLoaded', () => {
  2781.        modal     = $('#actu-editor');
  2782.        if (!modal) { console.warn('[Actu Editor] Modal #actu-editor not found.'); return; }
  2783.        closeEl   = $('.cms-modal__close', modal);
  2784.        btnSave   = $('#ae-save', modal);
  2785.        btnCancel = $('#ae-cancel', modal);
  2786.    
  2787.        inKicker  = $('#ae-kicker', modal);
  2788.        inTitle   = $('#ae-title',  modal);
  2789.        inP1      = $('#ae-p1',     modal);
  2790.        inP2      = $('#ae-p2',     modal);
  2791.        inCta     = $('#ae-cta',    modal);
  2792.        inPill1   = $('#ae-pill1',  modal);
  2793.    
  2794.        document.addEventListener('click', (e) => {
  2795.          const btn = e.target.closest('.cms-actu-edit');
  2796.          if (!btn) return;
  2797.          const root = btn.closest('.actu-hero');
  2798.          if (root) openActuEditor(root);
  2799.        });
  2800.    
  2801.        btnSave?.addEventListener('click', saveAll);
  2802.        btnCancel?.addEventListener('click', closeModal);
  2803.        closeEl?.addEventListener('click', closeModal);
  2804.    
  2805.        modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); });
  2806.    
  2807.        document.addEventListener('keydown', (e) => { if (!modal.hidden && e.key === 'Escape') closeModal(); });
  2808.      });
  2809.    })();
  2810.    
  2811.    
  2812. </script>
  2813. <style>
  2814.    .cms-modal{ 
  2815.    position: fixed; inset: 0; 
  2816.    background: rgba(0,0,0,.45);
  2817.    display: grid; place-items: center;
  2818.    z-index: 9999;
  2819.    color: #111;
  2820.    }
  2821.    .cms-modal__dialog{
  2822.    background: #fff;
  2823.    width: min(740px, 96vw);
  2824.    max-height: 90vh;
  2825.    border-radius: .75rem;
  2826.    box-shadow: 0 10px 30px rgba(0,0,0,.25);
  2827.    overflow: hidden;
  2828.    display: flex;
  2829.    flex-direction: column;
  2830.    }
  2831.    .cms-modal__header,
  2832.    .cms-modal__footer{
  2833.    flex: 0 0 auto;
  2834.    padding: 12px 16px;
  2835.    background: #f7f7f8;
  2836.    display: flex; align-items: center; justify-content: space-between;
  2837.    }
  2838.    .cms-modal__body{
  2839.    flex: 1 1 auto;
  2840.    padding: 12px 16px;
  2841.    overflow-y: auto;
  2842.    -webkit-overflow-scrolling: touch;
  2843.    }
  2844.    .cms-label{ display: grid; gap: 6px; font-weight: 600; color: #111; }
  2845.    .cms-label input, .cms-label textarea{
  2846.    background: #fff; color: #111;
  2847.    font: inherit; padding: 10px;
  2848.    border: 1px solid #e5e7eb; border-radius: .5rem;
  2849.    }
  2850.    .cms-label input::placeholder, .cms-label textarea::placeholder{ color:#6b7280; }
  2851.    .cms-modal__dialog, .cms-modal__dialog *{ color: #111; }
  2852.    .cms-modal__close{ background:none; border:none; font-size:1.25rem; cursor:pointer }
  2853.    .cms-modal[hidden]{ display:none !important }
  2854.    html.modal-open, body.modal-open{ overflow: hidden; }
  2855.    .actu-hero{ position: relative; }
  2856.    .cms-actu-edit{
  2857.    position: absolute; top: 12px; right: 12px;
  2858.    z-index: 9; 
  2859.    background: #fff; border: 1px solid #e5e7eb; border-radius: .5rem;
  2860.    font-size: .9rem; padding: .15rem .4rem; cursor: pointer;
  2861.    pointer-events: auto;
  2862.    }
  2863.    .actu-shade{ pointer-events: none; }
  2864. </style>
  2865. <script>
  2866.    (function () {
  2867.      // --- Loader helper for the "Enregistrer" button ---
  2868.      function setLoading(btn, isLoading, text = 'Enregistrement…') {
  2869.        if (!btn) return;
  2870.        if (isLoading) {
  2871.          if (!btn.dataset._label) btn.dataset._label = btn.textContent;
  2872.          btn.disabled = true;
  2873.          btn.setAttribute('aria-busy', 'true');
  2874.          btn.innerHTML =
  2875.            '<svg width="16" height="16" viewBox="0 0 50 50" aria-hidden="true" focusable="false" style="vertical-align:-2px;margin-right:8px;">' +
  2876.              '<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="6" stroke-opacity="0.25"></circle>' +
  2877.              '<path d="M25 5 a20 20 0 0 1 0 40" stroke="currentColor" stroke-width="6" fill="none">' +
  2878.                '<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.8s" repeatCount="indefinite"></animateTransform>' +
  2879.              '</path>' +
  2880.            '</svg>' + text;
  2881.        } else {
  2882.          btn.disabled = false;
  2883.          btn.removeAttribute('aria-busy');
  2884.          btn.textContent = btn.dataset._label || 'Enregistrer';
  2885.        }
  2886.      }
  2887.    
  2888.      const UPDATE_URL = "{{ path('cms_text_update') }}";
  2889.      const $ = (sel, root=document) => root.querySelector(sel);
  2890.    
  2891.      let modal, closeEl, btnSave, btnCancel;
  2892.      let inTitle, inV1, inL1, inV2, inL2, inV3, inL3, inV4, inL4, inV5, inL5, inImg1, inImg2, inImg3, inImg4, inImg5;
  2893.      let ctx = null;
  2894.    
  2895.      function openEditor(root) {
  2896.        const els = {
  2897.          title: $('#chiffres-title', root),
  2898.          v1:    $('#chiffre1-value', root),  l1: $('#chiffre1-label', root),
  2899.          v2:    $('#chiffre2-value', root),  l2: $('#chiffre2-label', root),
  2900.          v3:    $('#chiffre3-value', root),  l3: $('#chiffre3-label', root),
  2901.          v4:    $('#chiffre4-value', root),  l4: $('#chiffre4-label', root),
  2902.          v5:    $('#chiffre5-value', root),  l5: $('#chiffre5-label', root),
  2903.          img1:  $('li:nth-child(1) img', root),
  2904.          img2:  $('li:nth-child(2) img', root),
  2905.          img3:  $('li:nth-child(3) img', root),
  2906.          img4:  $('li:nth-child(4) img', root),
  2907.          img5:  $('li:nth-child(5) img', root),
  2908.        };
  2909.        const keys = {
  2910.          title: root.dataset.titleKey || '',
  2911.          v1:    root.dataset.v1Key    || '', l1: root.dataset.l1Key || '',
  2912.          v2:    root.dataset.v2Key    || '', l2: root.dataset.l2Key || '',
  2913.          v3:    root.dataset.v3Key    || '', l3: root.dataset.l3Key || '',
  2914.          v4:    root.dataset.v4Key    || '', l4: root.dataset.l4Key || '',
  2915.          v5:    root.dataset.v5Key    || '', l5: root.dataset.l5Key || '',
  2916.          img1:  root.dataset.img1Key  || '',
  2917.          img2:  root.dataset.img2Key  || '',
  2918.          img3:  root.dataset.img3Key  || '',
  2919.          img4:  root.dataset.img4Key  || '',
  2920.          img5:  root.dataset.img5Key  || '',
  2921.        };
  2922.    
  2923.        ctx = { root, els, keys };
  2924.    
  2925.        inTitle.value = (els.title?.textContent || '').trim();
  2926.        inV1.value    = (els.v1?.textContent    || '').trim();
  2927.        inL1.value    = (els.l1?.textContent    || '').trim();
  2928.        inV2.value    = (els.v2?.textContent    || '').trim();
  2929.        inL2.value    = (els.l2?.textContent    || '').trim();
  2930.        inV3.value    = (els.v3?.textContent    || '').trim();
  2931.        inL3.value    = (els.l3?.textContent    || '').trim();
  2932.        inV4.value    = (els.v4?.textContent    || '').trim();
  2933.        inL4.value    = (els.l4?.textContent    || '').trim();
  2934.        inV5.value    = (els.v5?.textContent    || '').trim();
  2935.        inL5.value    = (els.l5?.textContent    || '').trim();
  2936.        inImg1.value  = els.img1?.getAttribute('src') || '';
  2937.        inImg2.value  = els.img2?.getAttribute('src') || '';
  2938.        inImg3.value  = els.img3?.getAttribute('src') || '';
  2939.        inImg4.value  = els.img4?.getAttribute('src') || '';
  2940.        inImg5.value  = els.img5?.getAttribute('src') || '';
  2941.    
  2942.        modal.hidden = false;
  2943.        document.documentElement.classList.add('modal-open');
  2944.        document.body.classList.add('modal-open');
  2945.        inTitle.focus();
  2946.      }
  2947.    
  2948.      function closeModal() {
  2949.        modal.hidden = true;
  2950.        document.documentElement.classList.remove('modal-open');
  2951.        document.body.classList.remove('modal-open');
  2952.        ctx = null;
  2953.      }
  2954.    
  2955.      async function saveAll() {
  2956.        if (!ctx) return;
  2957.    
  2958.        const payloads = [];
  2959.        if (ctx.keys.title) payloads.push({ key: ctx.keys.title, text: inTitle.value.trim() });
  2960.        if (ctx.keys.v1) payloads.push({ key: ctx.keys.v1, text: inV1.value.trim() });
  2961.        if (ctx.keys.l1) payloads.push({ key: ctx.keys.l1, text: inL1.value.trim() });
  2962.        if (ctx.keys.v2) payloads.push({ key: ctx.keys.v2, text: inV2.value.trim() });
  2963.        if (ctx.keys.l2) payloads.push({ key: ctx.keys.l2, text: inL2.value.trim() });
  2964.        if (ctx.keys.v3) payloads.push({ key: ctx.keys.v3, text: inV3.value.trim() });
  2965.        if (ctx.keys.l3) payloads.push({ key: ctx.keys.l3, text: inL3.value.trim() });
  2966.        if (ctx.keys.v4) payloads.push({ key: ctx.keys.v4, text: inV4.value.trim() });
  2967.        if (ctx.keys.l4) payloads.push({ key: ctx.keys.l4, text: inL4.value.trim() });
  2968.        if (ctx.keys.v5) payloads.push({ key: ctx.keys.v5, text: inV5.value.trim() });
  2969.        if (ctx.keys.l5) payloads.push({ key: ctx.keys.l5, text: inL5.value.trim() });
  2970.        if (ctx.keys.img1) payloads.push({ key: ctx.keys.img1, text: inImg1.value.trim() });
  2971.        if (ctx.keys.img2) payloads.push({ key: ctx.keys.img2, text: inImg2.value.trim() });
  2972.        if (ctx.keys.img3) payloads.push({ key: ctx.keys.img3, text: inImg3.value.trim() });
  2973.        if (ctx.keys.img4) payloads.push({ key: ctx.keys.img4, text: inImg4.value.trim() });
  2974.        if (ctx.keys.img5) payloads.push({ key: ctx.keys.img5, text: inImg5.value.trim() });
  2975.    
  2976.        setLoading(btnSave, true);
  2977.        try {
  2978.          const results = await Promise.all(payloads.map(pl =>
  2979.            fetch(UPDATE_URL, {
  2980.              method:'POST', headers:{'Content-Type':'application/json','Accept':'application/json'},
  2981.              body: JSON.stringify(pl)
  2982.            }).then(r=>r.json())
  2983.          ));
  2984.          const bad = results.find(r => !r || r.ok === false);
  2985.          if (bad) throw new Error(bad.error || 'Erreur inconnue');
  2986.    
  2987.          if (ctx.els.title) ctx.els.title.textContent = inTitle.value.trim();
  2988.          if (ctx.els.v1) ctx.els.v1.textContent = inV1.value.trim();
  2989.          if (ctx.els.l1) ctx.els.l1.textContent = inL1.value.trim();
  2990.          if (ctx.els.v2) ctx.els.v2.textContent = inV2.value.trim();
  2991.          if (ctx.els.l2) ctx.els.l2.textContent = inL2.value.trim();
  2992.          if (ctx.els.v3) ctx.els.v3.textContent = inV3.value.trim();
  2993.          if (ctx.els.l3) ctx.els.l3.textContent = inL3.value.trim();
  2994.          if (ctx.els.v4) ctx.els.v4.textContent = inV4.value.trim();
  2995.          if (ctx.els.l4) ctx.els.l4.textContent = inL4.value.trim();
  2996.          if (ctx.els.v5) ctx.els.v5.textContent = inV5.value.trim();
  2997.          if (ctx.els.l5) ctx.els.l5.textContent = inL5.value.trim();
  2998.          if (ctx.els.img1 && inImg1.value.trim()) ctx.els.img1.setAttribute('src', inImg1.value.trim());
  2999.          if (ctx.els.img2 && inImg2.value.trim()) ctx.els.img2.setAttribute('src', inImg2.value.trim());
  3000.          if (ctx.els.img3 && inImg3.value.trim()) ctx.els.img3.setAttribute('src', inImg3.value.trim());
  3001.          if (ctx.els.img4 && inImg4.value.trim()) ctx.els.img4.setAttribute('src', inImg4.value.trim());
  3002.          if (ctx.els.img5 && inImg5.value.trim()) ctx.els.img5.setAttribute('src', inImg5.value.trim());
  3003.    
  3004.          closeModal();
  3005.        } catch (err) {
  3006.          location.reload();
  3007.          //alert('Modification effectuée!');
  3008.        } finally {
  3009.          setLoading(btnSave, false);
  3010.        }
  3011.      }
  3012.    
  3013.      document.addEventListener('DOMContentLoaded', () => {
  3014.        modal = document.querySelector('#kpis-editor');
  3015.        if (!modal) return;
  3016.    
  3017.        closeEl   = modal.querySelector('.cms-modal__close');
  3018.        btnSave   = modal.querySelector('#kpe-save');
  3019.        btnCancel = modal.querySelector('#kpe-cancel');
  3020.    
  3021.        inTitle = modal.querySelector('#kpe-title');
  3022.        inV1 = modal.querySelector('#kpe-v1'); inL1 = modal.querySelector('#kpe-l1');
  3023.        inV2 = modal.querySelector('#kpe-v2'); inL2 = modal.querySelector('#kpe-l2');
  3024.        inV3 = modal.querySelector('#kpe-v3'); inL3 = modal.querySelector('#kpe-l3');
  3025.        inV4 = modal.querySelector('#kpe-v4'); inL4 = modal.querySelector('#kpe-l4');
  3026.         inV5 = modal.querySelector('#kpe-v5'); inL5 = modal.querySelector('#kpe-l5');
  3027.        inImg1 = modal.querySelector('#kpe-img1');
  3028.        inImg2 = modal.querySelector('#kpe-img2');
  3029.        inImg3 = modal.querySelector('#kpe-img3');
  3030.        inImg4 = modal.querySelector('#kpe-img4');
  3031.        inImg5 = modal.querySelector('#kpe-img5');
  3032.    
  3033.        document.addEventListener('click', (e) => {
  3034.          const btn = e.target.closest('.cms-kpis-edit');
  3035.          if (!btn) return;
  3036.          const root = btn.closest('.chiffres__panel');
  3037.          if (root) openEditor(root);
  3038.        });
  3039.    
  3040.        btnSave?.addEventListener('click', saveAll);
  3041.        btnCancel?.addEventListener('click', closeModal);
  3042.        closeEl?.addEventListener('click', closeModal);
  3043.        modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); });
  3044.        document.addEventListener('keydown', (e) => { if (!modal.hidden && e.key === 'Escape') closeModal(); });
  3045.      });
  3046.    })();
  3047.    
  3048.    
  3049. </script>
  3050. {# --------------------------- TRUST (logos + headings) --------------------------- #}
  3051. <section class="trust">
  3052.    <div class="container">
  3053.       <p class="trust__kicker" style="font-size:20px;">ILS NOUS FONT CONFIANCE&nbsp;!</p>
  3054.       {% set trustPartners = [
  3055.       { src: 'assets/img/mimethys.webp', alt: 'Mimethys' },
  3056.       { src: 'assets/img/pegase.webp', alt: 'Pegase Processus' },
  3057.       { src: 'assets/img/gepi.webp', alt: 'GEPI Conseil' },
  3058.       { src: 'assets/img/poste.png', alt: 'La Poste' }
  3059.       ] %}
  3060.       {% set trustApprovals = [
  3061.       { src: 'assets/img/iprp.jpg', alt: 'IPRP' },
  3062.       { src: 'assets/img/agefiph.png', alt: 'Agefiph' }
  3063.       ] %}
  3064.       <div class="trust__group">
  3065.          <h3 class="trust__label" style="font-size:18px;" >Nos partenaires</h3>
  3066.          <div class="trust__logos">
  3067.             {% for logo in trustPartners %}
  3068.             <img src="{{ asset(logo.src) }}" alt="{{ logo.alt }}" class="logo">
  3069.             {% endfor %}
  3070.          </div>
  3071.       </div>
  3072.       <div class="trust__group">
  3073.          <h3 class="trust__label" style="font-size:18px;">Nos agréments et habilitations</h3>
  3074.          <div class="trust__logos">
  3075.             {% for logo in trustApprovals %}
  3076.             <img src="{{ asset(logo.src) }}" alt="{{ logo.alt }}" class="logo">
  3077.             {% endfor %}
  3078.          </div>
  3079.       </div>
  3080.    </div>
  3081. </section>
  3082. {# ==== CHIFFRES CLÉS ==== #}
  3083. <section class="chiffres" aria-labelledby="chiffres-title">
  3084.    <div class="container">
  3085.       <div class="narrow">
  3086.          <div class="chiffres__panel"
  3087.             data-title-key="kpis_title"
  3088.             data-v1-key="chiffre_cles_card1_value" data-l1-key="chiffre_cles_card1_label" data-img1-key="kpis_img1"
  3089.             data-v2-key="chiffre_cles_card2_value" data-l2-key="chiffre_cles_card2_label" data-img2-key="kpis_img2"
  3090.             data-v3-key="chiffre_cles_card3_value" data-l3-key="chiffre_cles_card3_label" data-img3-key="kpis_img3"
  3091.             data-v4-key="chiffre_cles_card4_value" data-l4-key="chiffre_cles_card4_label" data-img4-key="kpis_img4"
  3092.             data-v5-key="chiffre_cles_card5_value" data-l5-key="chiffre_cles_card5_label" data-img5-key="kpis_img5">
  3093.             <button class="cms-kpis-edit" type="button" aria-label="Éditer les chiffres">✏️</button>
  3094.             <h2 id="chiffres-title" class="chiffres__title">
  3095.                {{ (kpis is defined and kpis.title is defined) ? kpis.title : ('Nos chiffres clés !'|trans) }}
  3096.             </h2>
  3097.             {% set items = [
  3098.             {
  3099.             img: 'pexels-fauxels-3182831.png',
  3100.             value: (kpis is defined and kpis.items is defined and kpis.items[0] is defined) ? kpis.items[0].value : chiffre_cles_card1_value,
  3101.             label: (kpis is defined and kpis.items is defined and kpis.items[0] is defined) ? kpis.items[0].label : chiffre_cles_card1_label
  3102.             },
  3103.             {
  3104.             img: 'pexels-growthgal-3719037.png',
  3105.             value: (kpis is defined and kpis.items is defined and kpis.items[1] is defined) ? kpis.items[1].value : chiffre_cles_card2_value,
  3106.             label: (kpis is defined and kpis.items is defined and kpis.items[1] is defined) ? kpis.items[1].label : chiffre_cles_card2_label
  3107.             },
  3108.             {
  3109.             img: 'pexels-shkrabaanthony-5292192.png',
  3110.             value: (kpis is defined and kpis.items is defined and kpis.items[2] is defined) ? kpis.items[2].value : chiffre_cles_card3_value,
  3111.             label: (kpis is defined and kpis.items is defined and kpis.items[2] is defined) ? kpis.items[2].label : chiffre_cles_card3_label
  3112.             },
  3113.             {
  3114.             img: 'pexels-4.png',
  3115.             value: (kpis is defined and kpis.items is defined and kpis.items[3] is defined) ? kpis.items[3].value : chiffre_cles_card4_value,
  3116.             label: (kpis is defined and kpis.items is defined and kpis.items[3] is defined) ? kpis.items[3].label : chiffre_cles_card4_label
  3117.             },
  3118.             {
  3119.             img: 'pexels-5.png',
  3120.             value: (kpis is defined and kpis.items is defined and kpis.items[4] is defined) ? kpis.items[4].value : chiffre_cles_card5_value,
  3121.             label: (kpis is defined and kpis.items is defined and kpis.items[4] is defined) ? kpis.items[4].label : chiffre_cles_card5_label
  3122.             }
  3123.             ] %}
  3124.             <ul class="chiffres__grid" role="list">
  3125.                <li>
  3126.                   <figure class="chiffre">
  3127.                      <div class="chiffre__media">
  3128.                         <img src="{{ asset(kpis.items[0].img) }}" alt="">
  3129.                         <figcaption class="chiffre__content">
  3130.                            <span class="chiffre__value" id="chiffre1-value">{{ items[0].value }}</span>
  3131.                            <span class="chiffre__label" id="chiffre1-label">{{ items[0].label }}</span>
  3132.                         </figcaption>
  3133.                      </div>
  3134.                   </figure>
  3135.                </li>
  3136.                <li>
  3137.                   <figure class="chiffre">
  3138.                      <div class="chiffre__media">
  3139.                         <img src="{{ asset(kpis.items[1].img) }}" alt="">
  3140.                         <figcaption class="chiffre__content">
  3141.                            <span class="chiffre__value" id="chiffre2-value">{{ items[1].value }}</span>
  3142.                            <span class="chiffre__label" id="chiffre2-label">{{ items[1].label }}</span>
  3143.                         </figcaption>
  3144.                      </div>
  3145.                   </figure>
  3146.                </li>
  3147.                <li>
  3148.                   <figure class="chiffre">
  3149.                      <div class="chiffre__media">
  3150.                         <img src="{{ asset(kpis.items[2].img) }}" alt="">
  3151.                         <figcaption class="chiffre__content">
  3152.                            <span class="chiffre__value" id="chiffre3-value">{{ items[2].value }}</span>
  3153.                            <span class="chiffre__label" id="chiffre3-label">{{ items[2].label }}</span>
  3154.                         </figcaption>
  3155.                      </div>
  3156.                   </figure>
  3157.                </li>
  3158.                <li>
  3159.                   <figure class="chiffre">
  3160.                      <div class="chiffre__media">
  3161.                         <img src="{{ asset(kpis.items[3].img) }}" alt="">
  3162.                         <figcaption class="chiffre__content">
  3163.                            <span class="chiffre__value" id="chiffre4-value">{{ items[3].value }}</span>
  3164.                            <span class="chiffre__label" id="chiffre4-label">{{ items[3].label }}</span>
  3165.                         </figcaption>
  3166.                      </div>
  3167.                   </figure>
  3168.                </li>
  3169.                <li>
  3170.                   <figure class="chiffre">
  3171.                      <div class="chiffre__media">
  3172.                         <img src="{{ asset(kpis.items[4].img) }}" alt="">
  3173.                         <figcaption class="chiffre__content">
  3174.                            <span class="chiffre__value" id="chiffre5-value">{{ items[4].value }}</span>
  3175.                            <span class="chiffre__label" id="chiffre5-label">{{ items[4].label }}</span>
  3176.                         </figcaption>
  3177.                      </div>
  3178.                   </figure>
  3179.                </li>
  3180.             </ul>
  3181.             <div class="only-mobile">
  3182.                <div class="chiffres__carousel" aria-roledescription="carousel">
  3183.                   <div class="chiffres__viewport">
  3184.                      <ul class="chiffres__track" id="kpis-track" role="listbox" aria-label="Chiffres clés">
  3185.                         <li class="chiffres__slide" role="option" aria-selected="true">
  3186.                            <figure class="chiffre">
  3187.                               <div class="chiffre__media">
  3188.                                  <img src="{{ asset(kpis.items[0].img) }}" alt="">
  3189.                                  <figcaption class="chiffre__content">
  3190.                                     <span class="chiffre__value">{{ items[0].value }}</span>
  3191.                                     <span class="chiffre__label">{{ items[0].label }}</span>
  3192.                                  </figcaption>
  3193.                               </div>
  3194.                            </figure>
  3195.                         </li>
  3196.                         <li class="chiffres__slide" role="option" aria-selected="false">
  3197.                            <figure class="chiffre">
  3198.                               <div class="chiffre__media">
  3199.                                  <img src="{{ asset(kpis.items[1].img) }}" alt="">
  3200.                                  <figcaption class="chiffre__content">
  3201.                                     <span class="chiffre__value">{{ items[1].value }}</span>
  3202.                                     <span class="chiffre__label">{{ items[1].label }}</span>
  3203.                                  </figcaption>
  3204.                               </div>
  3205.                            </figure>
  3206.                         </li>
  3207.                         <li class="chiffres__slide" role="option" aria-selected="false">
  3208.                            <figure class="chiffre">
  3209.                               <div class="chiffre__media">
  3210.                                  <img src="{{ asset(kpis.items[2].img) }}" alt="">
  3211.                                  <figcaption class="chiffre__content">
  3212.                                     <span class="chiffre__value">{{ items[2].value }}</span>
  3213.                                     <span class="chiffre__label">{{ items[2].label }}</span>
  3214.                                  </figcaption>
  3215.                               </div>
  3216.                            </figure>
  3217.                         </li>
  3218.                         <li class="chiffres__slide" role="option" aria-selected="false">
  3219.                            <figure class="chiffre">
  3220.                               <div class="chiffre__media">
  3221.                                  <img src="{{ asset(kpis.items[3].img) }}" alt="">
  3222.                                  <figcaption class="chiffre__content">
  3223.                                     <span class="chiffre__value">{{ items[3].value }}</span>
  3224.                                     <span class="chiffre__label">{{ items[3].label }}</span>
  3225.                                  </figcaption>
  3226.                               </div>
  3227.                            </figure>
  3228.                         </li>
  3229.                         <li class="chiffres__slide" role="option" aria-selected="false">
  3230.                            <figure class="chiffre">
  3231.                               <div class="chiffre__media">
  3232.                                  <img src="{{ asset(kpis.items[4].img) }}" alt="">
  3233.                                  <figcaption class="chiffre__content">
  3234.                                     <span class="chiffre__value">{{ items[4].value }}</span>
  3235.                                     <span class="chiffre__label">{{ items[4].label }}</span>
  3236.                                  </figcaption>
  3237.                               </div>
  3238.                            </figure>
  3239.                         </li>
  3240.                      </ul>
  3241.                      <button class="chiffres__arrow chiffres__arrow--prev" type="button" aria-label="Diapo précédente">‹</button>
  3242.                      <button class="chiffres__arrow chiffres__arrow--next" type="button" aria-label="Diapo suivante">›</button>
  3243.                   </div>
  3244.                   <div class="chiffres__dots" aria-label="Pagination"></div>
  3245.                  
  3246.                </div>
  3247.             </div>
  3248.          </div>
  3249.          <div id="kpis-editor" class="cms-modal" hidden>
  3250.             <div class="cms-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="kpe-title-label">
  3251.                <div class="cms-modal__header">
  3252.                   <strong id="kpe-title-label">Éditer les chiffres</strong>
  3253.                   <button type="button" class="cms-modal__close" aria-label="Fermer">×</button>
  3254.                </div>
  3255.                <div class="cms-modal__body">
  3256.                   <label class="cms-label">Titre
  3257.                   <input id="kpe-title" type="text" autocomplete="off">
  3258.                   </label>
  3259.                   <fieldset class="cms-fieldset">
  3260.                      <legend>Carte 1</legend>
  3261.                      <label class="cms-label">Valeur <input id="kpe-v1" type="text" autocomplete="off"></label>
  3262.                      <label class="cms-label">Label  <input id="kpe-l1" type="text" autocomplete="off"></label>
  3263.                   </fieldset>
  3264.                   <fieldset class="cms-fieldset">
  3265.                      <legend>Carte 2</legend>
  3266.                      <label class="cms-label">Valeur <input id="kpe-v2" type="text" autocomplete="off"></label>
  3267.                      <label class="cms-label">Label  <input id="kpe-l2" type="text" autocomplete="off"></label>
  3268.                   </fieldset>
  3269.                   <fieldset class="cms-fieldset">
  3270.                      <legend>Carte 3</legend>
  3271.                      <label class="cms-label">Valeur <input id="kpe-v3" type="text" autocomplete="off"></label>
  3272.                      <label class="cms-label">Label  <input id="kpe-l3" type="text" autocomplete="off"></label>
  3273.                   </fieldset>
  3274.                   <fieldset class="cms-fieldset">
  3275.                      <legend>Carte 4</legend>
  3276.                      <label class="cms-label">Valeur <input id="kpe-v4" type="text" autocomplete="off"></label>
  3277.                      <label class="cms-label">Label  <input id="kpe-l4" type="text" autocomplete="off"></label>
  3278.                   </fieldset>
  3279.                   <fieldset class="cms-fieldset">
  3280.                      <legend>Carte 5</legend>
  3281.                      <label class="cms-label">Valeur <input id="kpe-v5" type="text" autocomplete="off"></label>
  3282.                      <label class="cms-label">Label  <input id="kpe-l5" type="text" autocomplete="off"></label>
  3283.                   </fieldset>
  3284.                   <fieldset class="cms-fieldset">
  3285.                      <legend>Images</legend>
  3286.                      <label class="cms-label">
  3287.                         Carte 1 — Image
  3288.                         <div class="cms-img-row">
  3289.                            <input id="kpe-img1" type="text" placeholder="ex: assets/img/visuel1.png">
  3290.                            <input id="kpe-file1" type="file" accept="image/png,image/jpeg,image/webp">
  3291.                         </div>
  3292.                      </label>
  3293.                      <label class="cms-label">
  3294.                         Carte 2 — Image
  3295.                         <div class="cms-img-row">
  3296.                            <input id="kpe-img2" type="text" placeholder="ex: assets/img/visuel2.png">
  3297.                            <input id="kpe-file2" type="file" accept="image/png,image/jpeg,image/webp">
  3298.                         </div>
  3299.                      </label>
  3300.                      <label class="cms-label">
  3301.                         Carte 3 — Image
  3302.                         <div class="cms-img-row">
  3303.                            <input id="kpe-img3" type="text" placeholder="ex: assets/img/visuel3.png">
  3304.                            <input id="kpe-file3" type="file" accept="image/png,image/jpeg,image/webp">
  3305.                         </div>
  3306.                      </label>
  3307.                      <label class="cms-label">
  3308.                         Carte 4 — Image
  3309.                         <div class="cms-img-row">
  3310.                            <input id="kpe-img4" type="text" placeholder="ex: assets/img/visuel4.png">
  3311.                            <input id="kpe-file4" type="file" accept="image/png,image/jpeg,image/webp">
  3312.                         </div>
  3313.                      </label>
  3314.                      <label class="cms-label">
  3315.                         Carte 5 — Image
  3316.                         <div class="cms-img-row">
  3317.                            <input id="kpe-img5" type="text" placeholder="ex: assets/img/visuel5.png">
  3318.                            <input id="kpe-file5" type="file" accept="image/png,image/jpeg,image/webp">
  3319.                         </div>
  3320.                      </label>
  3321.                   </fieldset>
  3322.                </div>
  3323.                <div class="cms-modal__footer">
  3324.                   <button type="button" class="btn btn-secondary" id="kpe-cancel">Annuler</button>
  3325.                   <button type="button" class="btn btn-primary" id="kpe-save">Enregistrer</button>
  3326.                </div>
  3327.             </div>
  3328.          </div>
  3329.       </div>
  3330.    </div>
  3331. </section>
  3332. <style>
  3333.    .only-desktop { display: none; }
  3334.    .only-mobile  { display: block; }
  3335.    @media (min-width: 992px){
  3336.    .only-desktop { display: block !important; }
  3337.    .only-mobile  { display: none  !important; }
  3338.    }
  3339.    .only-mobile .chiffres__viewport{
  3340.    width: min(620px, 96vw);
  3341.    margin-inline:auto;
  3342.    overflow:hidden;
  3343.    position:relative;
  3344.    }
  3345.    .only-mobile .chiffres__track{
  3346.    display:flex; gap:0; transition:.45s ease; will-change:transform;
  3347.    }
  3348.    .only-mobile .chiffres__slide{
  3349.    flex:0 0 100%; min-width:100%;
  3350.    display:flex; justify-content:center;
  3351.    }
  3352.    .only-mobile .chiffre__media{
  3353.    width:92%; max-width:560px; aspect-ratio:4/5; border-radius:28px;
  3354.    overflow:hidden; box-shadow:0 10px 30px rgba(0,0,0,.35);
  3355.    }
  3356.    .only-mobile .chiffre__media img{ width:100%; height:100%; object-fit:cover; }
  3357.    .only-mobile .chiffre__media::after{
  3358.    content:""; position:absolute; inset:0;
  3359.    background:linear-gradient(180deg, rgba(0,0,0,.25) 20%, rgba(0,0,0,.55) 80%);
  3360.    }
  3361.    .only-mobile .chiffre__content{
  3362.    position:absolute; left:18px; right:18px; bottom:18px; z-index:1; color:#fff;
  3363.    text-shadow:0 2px 6px rgba(0,0,0,.45);
  3364.    }
  3365.    .only-mobile .chiffre__value{ display:block; font-weight:800; font-size:clamp(26px, 7vw, 34px); line-height:1.05; }
  3366.    .only-mobile .chiffre__label{ display:block; font-size:13px; }
  3367.    .only-mobile .chiffres__arrow{
  3368.    position:absolute; width:44px; height:44px; border-radius:50%; border:0;
  3369.    background:#fff; color:#1c2d4a; font-size:28px; display:grid; place-items:center;
  3370.    box-shadow:0 8px 18px rgba(0,0,0,.25);
  3371.    transform:translate(-50%, -50%);
  3372.    z-index:3;
  3373.    }
  3374.    .only-mobile .chiffres__dots{ display:flex; justify-content:center; gap:8px; margin-top:10px; }
  3375.    .only-mobile .chiffres__dots button{ width:8px; height:8px; border:0; border-radius:50%; background:#d9dde7; opacity:.7; }
  3376.    .only-mobile .chiffres__dots button[aria-current="true"]{ opacity:1; background:#fff; transform:scale(1.15); }
  3377.    .only-mobile .chiffres__cta{ display:flex; justify-content:center; margin-top:8px; }
  3378.    .only-mobile .btn.btn-light{ background:#fff; color:#0a2a54; border:0; padding:8px 16px; border-radius:8px; font-weight:700; }
  3379.    .only-desktop { display: none !important; }  
  3380.    .only-mobile  { display: block !important; }
  3381.    @media (min-width: 992px){
  3382.    .only-desktop { display: grid !important; }
  3383.    .only-mobile  { display: none !important; } 
  3384.    }
  3385.    @media (max-width: 991.98px){
  3386.    .chiffres__panel > .chiffres__grid { display: none !important; } 
  3387.    }
  3388.    @media (min-width: 992px){
  3389.    .chiffres__panel .only-mobile { display: none !important; }   
  3390.    }
  3391.    @media (max-width: 991.98px){
  3392.    .chiffres .chiffres__title{
  3393.    text-align: center !important;
  3394.    margin: 0 auto 18px !important;
  3395.    display: block;         
  3396.    width: 100%;
  3397.    }
  3398.    }
  3399.    @media (max-width: 991.98px){
  3400.    .catalogue .catalogue-grid{
  3401.    display: flex;
  3402.    flex-direction: column;
  3403.    align-items: center;    
  3404.    }
  3405.    .catalogue .catalogue-art{ order: -1;  display:flex; justify-content:center; }
  3406.    .catalogue .cat-card{
  3407.    width: min(82vw, 320px);         
  3408.    aspect-ratio: 3 / 4;
  3409.    border-radius: 16px;
  3410.    overflow: hidden;
  3411.    box-shadow: 0 12px 30px rgba(0,0,0,.2);
  3412.    position: relative;
  3413.    }
  3414.    .catalogue .cat-card {
  3415.    aspect-ratio: 3 / 4;
  3416.    }
  3417.    .catalogue .catalogue-copy{
  3418.    order: 0;                        
  3419.    width: 100%;
  3420.    }
  3421.    .catalogue .catalogue-copy .catalogue-text-wrap{
  3422.    max-width: 560px;
  3423.    margin: 0 auto;
  3424.    padding: 0 16px;                   
  3425.    text-align: left;                  
  3426.    }
  3427.    .catalogue .catalogue-copy .sec-title{
  3428.    color: #ff6a3d;
  3429.    font-weight: 800;
  3430.    line-height: 1.3;
  3431.    font-size: clamp(18px, 4.8vw, 22px);
  3432.    margin: 12px 0 10px;
  3433.    text-transform: uppercase;
  3434.    }
  3435.    .catalogue .catalogue-copy .sec-text{
  3436.    font-size: 14px;
  3437.    line-height: 1.6;
  3438.    margin: 0 0 10px;
  3439.    }
  3440.    .catalogue .catalogue-copy .btn-row{
  3441.    display: flex;
  3442.    justify-content: center;
  3443.    align-items: center;
  3444.    gap: 12px;
  3445.    flex-wrap: wrap;
  3446.    margin-top: 8px;
  3447.    }
  3448.    .catalogue .catalogue-grid > *{ margin: 0; }
  3449.    }
  3450.    @media (max-width: 991.98px){
  3451.    .trust{
  3452.    background:#fff;           
  3453.    padding: 18px 0 28px;
  3454.    }
  3455.    .trust .trust__kicker{
  3456.    text-align:center;
  3457.    margin: 0 0 20px;
  3458.    font-weight:800;
  3459.    font-size:14px;
  3460.    color:#ff6a3d;            
  3461.    text-transform:uppercase;
  3462.    letter-spacing:.2px;
  3463.    }
  3464.    .trust .trust__group + .trust__group{ margin-top:28px; }
  3465.    .trust .trust__label{
  3466.    margin:0 0 16px;
  3467.    font-size:18px;
  3468.    }
  3469.    .trust .trust__logos{
  3470.    display:grid;
  3471.    grid-template-columns: repeat(2, minmax(0, 1fr));
  3472.    justify-items:center;
  3473.    gap: 22px 36px;            
  3474.    max-width: 320px;          
  3475.    margin: 0 auto;           
  3476.    }
  3477.    .trust .trust__logos .logo{
  3478.    width: 72px;               
  3479.    height: auto;
  3480.    object-fit: contain;
  3481.    display:block;
  3482.    }
  3483.    }
  3484.    @media (max-width: 991.98px){
  3485.    .competences .panel--blue{
  3486.    position: relative;
  3487.    border-radius: 12px;
  3488.    overflow: hidden;
  3489.    padding: 24px 16px 18px;
  3490.    }
  3491.    .competences .panel--blue::after{
  3492.    content:"";
  3493.    position:absolute; inset:0;
  3494.    background: rgba(13, 44, 88, .55);
  3495.    pointer-events:none;
  3496.    }
  3497.    .competences .sec-title{
  3498.    position: relative;         
  3499.    z-index: 1;
  3500.    color:#fff;
  3501.    text-align:center;
  3502.    text-transform: uppercase;
  3503.    letter-spacing:.02em;
  3504.    font-weight:800;
  3505.    font-size: 14px;
  3506.    margin: 0 0 14px;
  3507.    }
  3508.    .competences .competences-grid{
  3509.    position: relative; z-index: 1;
  3510.    display: grid !important;
  3511.    grid-template-columns: 1fr !important;  
  3512.    gap: 12px;
  3513.    max-width: 340px;
  3514.    margin: 0 auto;
  3515.    }
  3516.    .competences .comp-card{
  3517.    display: grid;
  3518.    grid-template-columns: 34px 1fr;
  3519.    align-items: center;
  3520.    column-gap: 12px;
  3521.    padding: 10px 12px;
  3522.    border-radius: 12px;
  3523.    background: rgba(255,255,255,.08);      
  3524.    backdrop-filter: blur(1px);
  3525.    }
  3526.    .competences .comp-icon{
  3527.    width: 28px; height: 28px; object-fit: contain;
  3528.    filter: brightness(1.05);
  3529.    }
  3530.    .competences .comp-card p{
  3531.    margin: 0;
  3532.    color:#fff;
  3533.    font-weight: 700;
  3534.    font-size: 13px;
  3535.    line-height: 1.45;
  3536.    }
  3537.    .competences .center{
  3538.    position: relative; z-index: 1;
  3539.    margin-top: 16px !important;
  3540.    display: flex; justify-content: center;
  3541.    }
  3542.    .competences .btn.btn-secondary{
  3543.    background:#fff; color:#0A2A45; border:0;
  3544.    border-radius: 8px; padding: 8px 16px; font-weight: 700;
  3545.    box-shadow: 0 6px 14px rgba(0,0,0,.18);
  3546.    }
  3547.    }
  3548.    .prestations--overimg .presta-card{
  3549.    position: relative;
  3550.    isolation: isolate;                        
  3551.    border-radius: 24px;
  3552.    overflow: hidden;
  3553.    padding: 24px;
  3554.    background: var(--img) center/cover no-repeat;
  3555.    background-clip: border-box;
  3556.    }
  3557.    .prestations--overimg .presta-card::after{
  3558.    content:"";
  3559.    position: absolute;
  3560.    inset: 0;
  3561.    z-index: 0;                                
  3562.    pointer-events: none;
  3563.    background:
  3564.    linear-gradient(180deg,
  3565.    rgba(10,43,88,0.15) 0%,
  3566.    rgba(10,43,88,0.45) 40%,
  3567.    rgba(10,43,88,0.78) 100%);
  3568.    }
  3569.    .prestations--overimg .presta-inner{
  3570.    position: relative;
  3571.    z-index: 1;                                 
  3572.    display:flex;
  3573.    flex-direction:column;
  3574.    justify-content:flex-start;
  3575.    height:100%;
  3576.    color: #fff;
  3577.    text-shadow: 0 2px 6px rgba(0,0,0,.45);
  3578.    }
  3579.    .prestations--overimg .presta-inner h3,
  3580.    .prestations--overimg .presta-inner p{ color:#fff; }
  3581.    @media (min-width: 992px){
  3582.    .chiffres__grid{
  3583.    display: flex;
  3584.    flex-wrap: wrap;
  3585.    justify-content: center;
  3586.    gap: 24px;
  3587.    margin: 0;
  3588.    padding: 6px;
  3589.    list-style: none;
  3590.    }
  3591.    .chiffres__grid > li{
  3592.    flex: 0 1 calc(33.333% - 16px); 
  3593.    }
  3594.    }
  3595.    @media (max-width: 991.98px){
  3596.    .chiffres__grid{
  3597.    display: flex;
  3598.    flex-wrap: wrap;
  3599.    justify-content: center;
  3600.    gap: 24px;
  3601.    margin: 0;
  3602.    padding: 6px;
  3603.    list-style: none;
  3604.    }
  3605.    .chiffres__grid > li{
  3606.    flex: 0 0 100%;
  3607.    }
  3608.    }
  3609. </style>
  3610. <script>
  3611.    (() => {
  3612.      const panel = document.querySelector('.chiffres__panel');
  3613.      if (!panel) return;
  3614.    
  3615.      const mql = window.matchMedia('(max-width: 991.98px)');
  3616.    
  3617.      let inited = false;
  3618.      let viewport, track, slides, prevBtn, nextBtn, dotsWrap;
  3619.     let index = 0;
  3620.    
  3621.      function cacheEls(){
  3622.        const mobileRoot = panel.querySelector('.only-mobile');
  3623.        if (!mobileRoot) return false;
  3624.        viewport = mobileRoot.querySelector('.chiffres__viewport');
  3625.        track    = mobileRoot.querySelector('.chiffres__track');
  3626.        slides   = [...mobileRoot.querySelectorAll('.chiffres__slide')];
  3627.        prevBtn  = mobileRoot.querySelector('.chiffres__arrow--prev');
  3628.        nextBtn  = mobileRoot.querySelector('.chiffres__arrow--next');
  3629.        dotsWrap = mobileRoot.querySelector('.chiffres__dots');
  3630.        return !!(viewport && track && slides.length && prevBtn && nextBtn && dotsWrap);
  3631.      }
  3632.    
  3633.      function buildDots(){
  3634.        dotsWrap.innerHTML = '';
  3635.        slides.forEach((_, i) => {
  3636.          const b = document.createElement('button');
  3637.          b.type = 'button';
  3638.          b.setAttribute('aria-label', `Aller à la diapo ${i+1}`);
  3639.          if (i === 0) b.setAttribute('aria-current', 'true');
  3640.          b.addEventListener('click', () => goTo(i));
  3641.          dotsWrap.appendChild(b);
  3642.        });
  3643.      }
  3644.    
  3645.      function positionArrows(){
  3646.        if (!viewport || !slides[index]) return;
  3647.        const card = slides[index].querySelector('.chiffre__media');
  3648.        if (!card) return;
  3649.    
  3650.        const vp = viewport.getBoundingClientRect();
  3651.        const cr = card.getBoundingClientRect();
  3652.    
  3653.        const prevR = (prevBtn.getBoundingClientRect().width || 44) / 2;
  3654.        const nextR = (nextBtn.getBoundingClientRect().width || 44) / 2;
  3655.        const SPACING = 12; // gap from card edge to arrow OUTER edge
  3656.    
  3657.        const desiredPrev = (cr.left  - vp.left) - (prevR + SPACING);
  3658.        const desiredNext = (cr.right - vp.left) + (nextR + SPACING);
  3659.    
  3660.        const minPrev = prevR + 6, maxPrev = vp.width - prevR - 6;
  3661.        const minNext = nextR + 6, maxNext = vp.width - nextR - 6;
  3662.    
  3663.        const clampedPrev = Math.max(minPrev, Math.min(desiredPrev, maxPrev));
  3664.        const clampedNext = Math.max(minNext, Math.min(desiredNext, maxNext));
  3665.    
  3666.        const shiftRight = clampedPrev - desiredPrev;
  3667.        const shiftLeft  = desiredNext - clampedNext;
  3668.        const symShift   = Math.max(shiftRight, shiftLeft);
  3669.    
  3670.        const prevCenter = desiredPrev + symShift;
  3671.        const nextCenter = desiredNext - symShift;
  3672.        const centerY    = cr.top - vp.top + cr.height / 2;
  3673.    
  3674.        prevBtn.style.left = prevCenter + 'px';
  3675.        nextBtn.style.left = nextCenter + 'px';
  3676.        prevBtn.style.top  = centerY + 'px';
  3677.        nextBtn.style.top  = centerY + 'px';
  3678.        prevBtn.style.visibility = nextBtn.style.visibility = 'visible';
  3679.      }
  3680.    
  3681.      function update(){
  3682.        track.style.transform = `translateX(${-index * 100}%)`;
  3683.        slides.forEach((li, i) => li.setAttribute('aria-selected', i === index ? 'true' : 'false'));
  3684.        [...dotsWrap.children].forEach((d, i) => i === index ? d.setAttribute('aria-current','true') : d.removeAttribute('aria-current'));
  3685.        prevBtn.disabled = (index === 0);
  3686.        nextBtn.disabled = (index === slides.length - 1);
  3687.        requestAnimationFrame(positionArrows);
  3688.      }
  3689.    
  3690.      function goTo(i){
  3691.        index = Math.max(0, Math.min(slides.length - 1, i));
  3692.        update();
  3693.      }
  3694.    
  3695.      function init(){
  3696.        if (inited || !cacheEls()) return;
  3697.    
  3698.        viewport.style.overflow = 'hidden';
  3699.        viewport.style.position = 'relative';
  3700.        track.style.display = 'flex';
  3701.        track.style.gap = '0';      // <-- guard you asked for
  3702.        track.style.transform = 'translateX(0)';
  3703.        track.style.transition = track.style.transition || '.45s ease';
  3704.        slides.forEach(s => {
  3705.          s.style.minWidth = '100%';
  3706.          s.style.flex = '0 0 100%';
  3707.          s.style.display = 'flex';
  3708.          s.style.justifyContent = 'center';
  3709.        });
  3710.    
  3711.        buildDots();
  3712.        prevBtn.addEventListener('click', onPrev);
  3713.        nextBtn.addEventListener('click', onNext);
  3714.        track.addEventListener('transitionend', positionArrows);
  3715.    
  3716.        viewport.tabIndex = 0;
  3717.        viewport.addEventListener('keydown', onKey);
  3718.    
  3719.        track.addEventListener('pointerdown', onPointerDown);
  3720.        track.addEventListener('pointerup', onPointerEnd);
  3721.        track.addEventListener('pointercancel', onPointerEnd);
  3722.        track.addEventListener('pointerleave', onPointerEnd);
  3723.    
  3724.        window.addEventListener('resize', positionArrows);
  3725.        slides.forEach(sl => {
  3726.          const img = sl.querySelector('img');
  3727.          if (img && !img.complete) img.addEventListener('load', positionArrows, { once:true });
  3728.        });
  3729.    
  3730.        index = 0;
  3731.        update();
  3732.        inited = true;
  3733.      }
  3734.    
  3735.      function destroy(){
  3736.        if (!inited) return;
  3737.        prevBtn.removeEventListener('click', onPrev);
  3738.        nextBtn.removeEventListener('click', onNext);
  3739.        track.removeEventListener('transitionend', positionArrows);
  3740.        viewport.removeEventListener('keydown', onKey);
  3741.        track.removeEventListener('pointerdown', onPointerDown);
  3742.        track.removeEventListener('pointerup', onPointerEnd);
  3743.        track.removeEventListener('pointercancel', onPointerEnd);
  3744.        track.removeEventListener('pointerleave', onPointerEnd);
  3745.        window.removeEventListener('resize', positionArrows);
  3746.        inited = false;
  3747.      }
  3748.    
  3749.      function onPrev(){ goTo(index - 1); }
  3750.      function onNext(){ goTo(index + 1); }
  3751.      function onKey(e){ if (e.key === 'ArrowRight') goTo(index + 1); if (e.key === 'ArrowLeft') goTo(index - 1); }
  3752.    
  3753.      let startX = null, pointerId = null;
  3754.      function onPointerDown(e){ startX = e.clientX; pointerId = e.pointerId; track.setPointerCapture(pointerId); }
  3755.      function onPointerEnd(e){
  3756.        if (startX === null) return;
  3757.        const dx = e.clientX - startX;
  3758.        const THRESH = 40;
  3759.        if (dx >  THRESH) goTo(index - 1);
  3760.        if (dx < -THRESH) goTo(index + 1);
  3761.        startX = null;
  3762.        if (pointerId != null) { try { track.releasePointerCapture(pointerId); } catch {} pointerId = null; }
  3763.      }
  3764.    
  3765.      function handleMQ(e){ e.matches ? init() : destroy(); }
  3766.      mql.addEventListener ? mql.addEventListener('change', handleMQ) : mql.addListener(handleMQ);
  3767.      handleMQ(mql);
  3768.    })();
  3769. </script>
  3770. <script>
  3771.    (function () {
  3772.      // --- Loader helper for the "Enregistrer" button ---
  3773.      function setLoading(btn, isLoading, text = 'Enregistrement…') {
  3774.        if (!btn) return;
  3775.        if (isLoading) {
  3776.          if (!btn.dataset._label) btn.dataset._label = btn.textContent;
  3777.          btn.disabled = true;
  3778.          btn.setAttribute('aria-busy', 'true');
  3779.          btn.innerHTML =
  3780.            '<svg width="16" height="16" viewBox="0 0 50 50" aria-hidden="true" focusable="false" style="vertical-align:-2px;margin-right:8px;">' +
  3781.              '<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="6" stroke-opacity="0.25"></circle>' +
  3782.              '<path d="M25 5 a20 20 0 0 1 0 40" stroke="currentColor" stroke-width="6" fill="none">' +
  3783.                '<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.8s" repeatCount="indefinite"></animateTransform>' +
  3784.              '</path>' +
  3785.            '</svg>' + text;
  3786.        } else {
  3787.          btn.disabled = false;
  3788.          btn.removeAttribute('aria-busy');
  3789.          btn.textContent = btn.dataset._label || 'Enregistrer';
  3790.        }
  3791.      }
  3792.    
  3793.      const UPDATE_URL = "{{ path('cms_text_update') }}";
  3794.      const UPLOAD_URL = "{{ path('cms_page_media_upload') }}";
  3795.      const ASSET_BASE = "{{ asset('/') }}";
  3796.      const $ = (sel, root=document) => root.querySelector(sel);
  3797.    
  3798.      let modal, closeEl, btnSave, btnCancel;
  3799.      let inTitle, inV1, inL1, inV2, inL2, inV3, inL3, inV4, inL4, inV5, inL5;
  3800.      let inImg1, inImg2, inImg3, inImg4, inImg5, file1, file2, file3, file4, file5;
  3801.    
  3802.      let ctx = null;
  3803.    
  3804.      function openEditor(root) {
  3805.        const els = {
  3806.          title: $('#chiffres-title', root),
  3807.          v1:    $('#chiffre1-value', root),  l1: $('#chiffre1-label', root),
  3808.          v2:    $('#chiffre2-value', root),  l2: $('#chiffre2-label', root),
  3809.          v3:    $('#chiffre3-value', root),  l3: $('#chiffre3-label', root),
  3810.          v4:    $('#chiffre4-value', root),  l4: $('#chiffre4-label', root),
  3811.          v5:    $('#chiffre5-value', root),  l5: $('#chiffre5-label', root),
  3812.          img1:  $('li:nth-child(1) img', root),
  3813.          img2:  $('li:nth-child(2) img', root),
  3814.          img3:  $('li:nth-child(3) img', root),
  3815.          img4:  $('li:nth-child(4) img', root),
  3816.          img5:  $('li:nth-child(5) img', root),
  3817.        };
  3818.        const keys = {
  3819.          title: root.dataset.titleKey || '',
  3820.          v1:    root.dataset.v1Key    || '', l1: root.dataset.l1Key || '',
  3821.          v2:    root.dataset.v2Key    || '', l2: root.dataset.l2Key || '',
  3822.          v3:    root.dataset.v3Key    || '', l3: root.dataset.l3Key || '',
  3823.          v4:    root.dataset.v4Key    || '', l4: root.dataset.l4Key || '',
  3824.          v5:    root.dataset.v5Key    || '', l5: root.dataset.l5Key || '',
  3825.          img1:  root.dataset.img1Key  || '',
  3826.          img2:  root.dataset.img2Key  || '',
  3827.          img3:  root.dataset.img3Key  || '',
  3828.          img4:  root.dataset.img4Key  || '',
  3829.          img5:  root.dataset.img5Key  || '',
  3830.        };
  3831.    
  3832.        ctx = { root, els, keys };
  3833.    
  3834.        inTitle.value = (els.title?.textContent || '').trim();
  3835.        inV1.value    = (els.v1?.textContent    || '').trim();
  3836.        inL1.value    = (els.l1?.textContent    || '').trim();
  3837.        inV2.value    = (els.v2?.textContent    || '').trim();
  3838.        inL2.value    = (els.l2?.textContent    || '').trim();
  3839.        inV3.value    = (els.v3?.textContent    || '').trim();
  3840.        inL3.value    = (els.l3?.textContent    || '').trim();
  3841.        inV4.value    = (els.v4?.textContent    || '').trim();
  3842.        inL4.value    = (els.l4?.textContent    || '').trim();
  3843.        inV5.value    = (els.v5?.textContent    || '').trim();
  3844.        inL5.value    = (els.l5?.textContent    || '').trim();
  3845.        inImg1.value  = els.img1?.getAttribute('src')?.replace(ASSET_BASE, '') || '';
  3846.        inImg2.value  = els.img2?.getAttribute('src')?.replace(ASSET_BASE, '') || '';
  3847.        inImg3.value  = els.img3?.getAttribute('src')?.replace(ASSET_BASE, '') || '';
  3848.        inImg4.value  = els.img4?.getAttribute('src')?.replace(ASSET_BASE, '') || '';
  3849.        inImg5.value  = els.img5?.getAttribute('src')?.replace(ASSET_BASE, '') || '';
  3850.    
  3851.        modal.hidden = false;
  3852.        inTitle.focus();
  3853.      }
  3854.    
  3855.      function closeModal() {
  3856.        modal.hidden = true;
  3857.        ctx = null;
  3858.        [file1, file2, file3, file4, file5].forEach(f => { if (f) f.value = ''; });
  3859.      }
  3860.    
  3861.      async function postText(key, text){
  3862.        const r = await fetch(UPDATE_URL, {
  3863.          method:'POST',
  3864.          headers:{'Content-Type':'application/json','Accept':'application/json'},
  3865.          body: JSON.stringify({ key, text })
  3866.        });
  3867.        const d = await r.json();
  3868.        if (!d.ok) throw new Error(d.error || 'Erreur texte');
  3869.        return d;
  3870.      }
  3871.    
  3872.     function buildPrefixedName(file, key) {
  3873.      const map = {
  3874.        'hero_slide1_bg': 'hero1-',
  3875.        'hero_slide2_bg': 'hero2-',
  3876.        'kpis_img1': 'c1-',
  3877.        'kpis_img2': 'c2-',
  3878.        'kpis_img3': 'c3-',
  3879.        'kpis_img4': 'c4-',
  3880.        'kpis_img5': 'c5-'
  3881.      };
  3882.      const prefix = map[key] || '';
  3883.      const ext = (file.name.split('.').pop() || '').toLowerCase();
  3884.      const base = file.name.replace(/\.[^.]+$/, '').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'') || 'image';
  3885.      const stamp = new Date().toISOString().replace(/[-:TZ.]/g,'').slice(0,14);
  3886.      const rand = Math.random().toString(36).slice(2,8);
  3887.      return `${prefix}${base}-${stamp}-${rand}.${ext}`;
  3888.    }
  3889.    
  3890.    async function uploadImage(key, file){
  3891.      const fd = new FormData();
  3892.      fd.append('key', key);
  3893.      fd.append('file', file, buildPrefixedName(file, key));
  3894.      const r = await fetch('{{ path("cms_page_media_upload") }}', { method:'POST', body: fd });
  3895.      const d = await r.json();
  3896.      if (!d.ok) throw new Error(d.error || 'Erreur upload');
  3897.      return d.path;
  3898.    }
  3899.    
  3900.    
  3901.      async function saveAll() {
  3902.        if (!ctx) return;
  3903.    
  3904.        const payloads = [];
  3905.        if (ctx.keys.title) payloads.push(postText(ctx.keys.title, inTitle.value.trim()));
  3906.        if (ctx.keys.v1)    payloads.push(postText(ctx.keys.v1,    inV1.value.trim()));
  3907.        if (ctx.keys.l1)    payloads.push(postText(ctx.keys.l1,    inL1.value.trim()));
  3908.        if (ctx.keys.v2)    payloads.push(postText(ctx.keys.v2,    inV2.value.trim()));
  3909.        if (ctx.keys.l2)    payloads.push(postText(ctx.keys.l2,    inL2.value.trim()));
  3910.        if (ctx.keys.v3)    payloads.push(postText(ctx.keys.v3,    inV3.value.trim()));
  3911.        if (ctx.keys.l3)    payloads.push(postText(ctx.keys.l3,    inL3.value.trim()));
  3912.        if (ctx.keys.v4)    payloads.push(postText(ctx.keys.v4,    inV4.value.trim()));
  3913.        if (ctx.keys.l4)    payloads.push(postText(ctx.keys.l4,    inL4.value.trim()));
  3914.        if (ctx.keys.v5)    payloads.push(postText(ctx.keys.v5,    inV5.value.trim()));
  3915.        if (ctx.keys.l5)    payloads.push(postText(ctx.keys.l5,    inL5.value.trim()));
  3916.        const imgUploads = {};
  3917.        if (file1?.files?.[0] && ctx.keys.img1) imgUploads.img1 = uploadImage(ctx.keys.img1, file1.files[0]);
  3918.        if (file2?.files?.[0] && ctx.keys.img2) imgUploads.img2 = uploadImage(ctx.keys.img2, file2.files[0]);
  3919.        if (file3?.files?.[0] && ctx.keys.img3) imgUploads.img3 = uploadImage(ctx.keys.img3, file3.files[0]);
  3920.        if (file4?.files?.[0] && ctx.keys.img4) imgUploads.img4 = uploadImage(ctx.keys.img4, file4.files[0]);
  3921.        if (file5?.files?.[0] && ctx.keys.img5) imgUploads.img5 = uploadImage(ctx.keys.img5, file5.files[0]);
  3922.    
  3923.        setLoading(btnSave, true);
  3924.        try {
  3925.          // Wait for texts and uploads (if any)
  3926.          await Promise.all([
  3927.            ...payloads,
  3928.            ...Object.values(imgUploads)
  3929.          ]);
  3930.    
  3931.          // Update DOM texts
  3932.          if (ctx.els.title) ctx.els.title.textContent = inTitle.value.trim();
  3933.          if (ctx.els.v1)    ctx.els.v1.textContent    = inV1.value.trim();
  3934.          if (ctx.els.l1)    ctx.els.l1.textContent    = inL1.value.trim();
  3935.          if (ctx.els.v2)    ctx.els.v2.textContent    = inV2.value.trim();
  3936.          if (ctx.els.l2)    ctx.els.l2.textContent    = inL2.value.trim();
  3937.          if (ctx.els.v3)    ctx.els.v3.textContent    = inV3.value.trim();
  3938.          if (ctx.els.l3)    ctx.els.l3.textContent    = inL3.value.trim();
  3939.          if (ctx.els.v4)    ctx.els.v4.textContent    = inV4.value.trim();
  3940.          if (ctx.els.l4)    ctx.els.l4.textContent    = inL4.value.trim();
  3941.          if (ctx.els.v5)    ctx.els.v5.textContent    = inV5.value.trim();
  3942.          if (ctx.els.l5)    ctx.els.l5.textContent    = inL5.value.trim();
  3943.    
  3944.          // Get upload results (promises already settled from the Promise.all above)
  3945.          const settled = await Promise.allSettled(Object.values(imgUploads));
  3946.          const keys = Object.keys(imgUploads);
  3947.          settled.forEach((s, i) => {
  3948.            if (s.status === 'fulfilled') {
  3949.              const rel = s.value;
  3950.              const abs = ASSET_BASE + rel;
  3951.              if (keys[i] === 'img1') { inImg1.value = rel; ctx.els.img1?.setAttribute('src', abs); }
  3952.              if (keys[i] === 'img2') { inImg2.value = rel; ctx.els.img2?.setAttribute('src', abs); }
  3953.              if (keys[i] === 'img3') { inImg3.value = rel; ctx.els.img3?.setAttribute('src', abs); }
  3954.              if (keys[i] === 'img4') { inImg4.value = rel; ctx.els.img4?.setAttribute('src', abs); }
  3955.              if (keys[i] === 'img5') { inImg5.value = rel; ctx.els.img5?.setAttribute('src', abs); }
  3956.            }
  3957.          });
  3958.    
  3959.          closeModal();
  3960.        } catch (err) {
  3961.          location.reload();
  3962.          //alert('Modification effectuée');
  3963.        } finally {
  3964.          setLoading(btnSave, false);
  3965.        }
  3966.      }
  3967.    
  3968.      document.addEventListener('DOMContentLoaded', () => {
  3969.        modal     = $('#kpis-editor');
  3970.        if (!modal) return;
  3971.    
  3972.        closeEl   = $('.cms-modal__close', modal);
  3973.        btnSave   = $('#kpe-save', modal);
  3974.        btnCancel = $('#kpe-cancel', modal);
  3975.    
  3976.        inTitle = $('#kpe-title', modal);
  3977.        inV1 = $('#kpe-v1', modal); inL1 = $('#kpe-l1', modal);
  3978.        inV2 = $('#kpe-v2', modal); inL2 = $('#kpe-l2', modal);
  3979.        inV3 = $('#kpe-v3', modal); inL3 = $('#kpe-l3', modal);
  3980.        inV4 = $('#kpe-v4', modal); inL4 = $('#kpe-l4', modal);
  3981.        inV5 = $('#kpe-v5', modal); inL5 = $('#kpe-l5', modal);
  3982.        inImg1 = $('#kpe-img1', modal); inImg2 = $('#kpe-img2', modal); inImg3 = $('#kpe-img3', modal); inImg4 = $('#kpe-img4', modal); inImg5 = $('#kpe-img5', modal);
  3983.        file1  = $('#kpe-file1', modal); file2  = $('#kpe-file2', modal); file3  = $('#kpe-file3', modal); file4  = $('#kpe-file4', modal); file5  = $('#kpe-file5', modal);
  3984.    
  3985.        document.addEventListener('click', (e) => {
  3986.          const btn = e.target.closest('.cms-kpis-edit');
  3987.          if (!btn) return;
  3988.          const root = btn.closest('.chiffres__panel');
  3989.          if (root) openEditor(root);
  3990.        });
  3991.    
  3992.        btnSave?.addEventListener('click', saveAll);
  3993.        btnCancel?.addEventListener('click', closeModal);
  3994.        closeEl?.addEventListener('click', closeModal);
  3995.    
  3996.        modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); });
  3997.        document.addEventListener('keydown', (e) => { if (!modal.hidden && e.key === 'Escape') closeModal(); });
  3998.      });
  3999.    })();
  4000.    
  4001.    
  4002. </script>
  4003. <div class="prefooter-gap" aria-hidden="true"></div>
  4004. <script>
  4005.    (function whenReady(fn){
  4006.      if (document.readyState === 'loading') {
  4007.        document.addEventListener('DOMContentLoaded', fn, { once: true });
  4008.      } else { fn(); }
  4009.    })(() => {
  4010.      const $ = s => document.querySelector(s);
  4011.    
  4012.      // --- Loader helper for the “Valider” button ---
  4013.      function setLoading(btn, isLoading, text = 'Validation…') {
  4014.        if (!btn) return;
  4015.        if (isLoading) {
  4016.          if (!btn.dataset._label) btn.dataset._label = btn.textContent;
  4017.          btn.disabled = true;
  4018.          btn.setAttribute('aria-busy', 'true');
  4019.          btn.innerHTML =
  4020.            '<svg width="16" height="16" viewBox="0 0 50 50" aria-hidden="true" focusable="false" style="vertical-align:-2px;margin-right:8px;">' +
  4021.              '<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="6" stroke-opacity="0.25"></circle>' +
  4022.              '<path d="M25 5 a20 20 0 0 1 0 40" stroke="currentColor" stroke-width="6" fill="none">' +
  4023.                '<animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.8s" repeatCount="indefinite"></animateTransform>' +
  4024.              '</path>' +
  4025.            '</svg>' + text;
  4026.        } else {
  4027.          btn.disabled = false;
  4028.          btn.removeAttribute('aria-busy');
  4029.          btn.textContent = btn.dataset._label || 'Valider';
  4030.        }
  4031.      }
  4032.    
  4033.      const editBtn     = $('.catalogue-edit-btn');
  4034.      const modal       = $('#catalogue-edit-modal');
  4035.      const textarea    = $('#catalogue-edit-textarea');
  4036.      const cancelBtn   = $('#catalogue-cancel');
  4037.      const validateBtn = $('#catalogue-validate');
  4038.      const textEl      = $('#catalogue-text-left');
  4039.    
  4040.      if (!editBtn || !modal || !textarea || !cancelBtn || !validateBtn || !textEl) {
  4041.        console.warn('[catalogue] missing elements'); return;
  4042.      }
  4043.    
  4044.      const open  = () => { textarea.value = editBtn.dataset.currentText || textEl.textContent.trim(); modal.hidden = false; textarea.focus(); };
  4045.      const close = () => { modal.hidden = true; };
  4046.    
  4047.      editBtn.addEventListener('click', open);
  4048.      cancelBtn.addEventListener('click', close);
  4049.      modal.addEventListener('click', e => { if (e.target === modal) close(); });
  4050.    
  4051.      validateBtn.addEventListener('click', async () => {
  4052.        const url = editBtn.dataset.url;
  4053.        const newText = textarea.value.trim();
  4054.        if (!newText) { alert('Le texte ne peut pas être vide.'); return; }
  4055.    
  4056.        setLoading(validateBtn, true, 'Validation…');
  4057.    
  4058.        try {
  4059.          const res = await fetch(url, {
  4060.            method: 'POST',
  4061.            headers: {
  4062.              'Content-Type': 'application/json',
  4063.              'Accept': 'application/json'
  4064.            },
  4065.            credentials: 'same-origin',
  4066.            body: JSON.stringify({ text: newText })
  4067.          });
  4068.    
  4069.          const ct  = res.headers.get('content-type') || '';
  4070.          const raw = await res.clone().text();
  4071.          console.debug('[catalogue_text_update]', res.status, ct, raw);
  4072.    
  4073.          if (!res.ok) {
  4074.            alert('Échec (HTTP ' + res.status + '). Voir la console pour les détails.');
  4075.            return;
  4076.          }
  4077.          if (!/application\/json/i.test(ct)) {
  4078.            console.error('Non-JSON response:', raw);
  4079.            alert('Réponse inattendue du serveur (non-JSON).');
  4080.            return;
  4081.          }
  4082.    
  4083.          const data = JSON.parse(raw);
  4084.          if (data && data.ok) {
  4085.            textEl.textContent = data.text;
  4086.            editBtn.dataset.currentText = data.text;
  4087.            close();
  4088.          } else {
  4089.            alert(data?.error || 'Échec de la mise à jour.');
  4090.          }
  4091.        } catch (err) {
  4092.          console.error(err);
  4093.          alert('La mise à jour a échoué. Merci de réessayer.');
  4094.        } finally {
  4095.          setLoading(validateBtn, false);
  4096.        }
  4097.      });
  4098.    });
  4099.    
  4100. </script>
  4101. <style>
  4102.    .hero-slide .container.hero-content{ 
  4103.    position: relative !important; 
  4104.    z-index: 1 !important; 
  4105.    height: 100% !important;
  4106.    display: flex !important; 
  4107.    align-items: center !important; 
  4108.    justify-content: flex-start !important;
  4109.    max-width: 1180px !important;
  4110.    margin: 0 auto !important;
  4111.    padding: 0 12px !important;
  4112.    }
  4113.    .hero-slide .hero-text-content {
  4114.    width: 100% !important;
  4115.    text-align: left !important;
  4116.    display: flex !important;
  4117.    flex-direction: column !important;
  4118.    align-items: flex-start !important;
  4119.    }
  4120.    .hero-slide .hero-kicker {
  4121.    text-align: left !important;
  4122.    margin-bottom: 6px !important;
  4123.    width: 100% !important;
  4124.    }
  4125.    .hero-slide .hero-title {
  4126.    text-align: left !important;
  4127.    margin: 10px 0 18px 0 !important;
  4128.    width: 100% !important;
  4129.    }
  4130.    .hero-slide .hero-ctas {
  4131.    text-align: left !important;
  4132.    margin-top: 18px !important;
  4133.    justify-content: flex-start !important;
  4134.    width: 100% !important;
  4135.    }
  4136.    /* Style spécifique pour le titre "Nos prestations" */
  4137.    .sec-title--orange {
  4138.    color: #F14816 !important;
  4139.    }
  4140.    .hero-overlay{background:rgba(7,51,73,.55)}
  4141.    .home-event-ribbon{box-shadow:0 3px 8px rgba(0,0,0,.15)}
  4142.    .hero-ribbon-shell{position:absolute;top:calc(var(--topbar-h, 0px) + var(--nav-offset, 0px) + var(--pill-h, 0px) + 12px);left:0;right:0;z-index:4;display:flex;justify-content:center;padding:0 12px}
  4143.    .hero-ribbon-shell .home-event-ribbon{margin:0;width:min(1180px,calc(100% - 24px));max-width:none}
  4144.    .hero-content{position:relative;z-index:2;display:flex;align-items:center;justify-content:flex-start;height:100%;padding-top:max(calc(var(--topbar-h, 0px) + var(--nav-offset, 0px) + var(--pill-h, 0px) + var(--ribbon-h, 44px) + var(--gap, 40px)), 150px) !important}
  4145.    .hero-copy{display:flex;flex-direction:column;align-items:flex-start;text-align:left;padding-top:clamp(12px,2vh,24px)}
  4146.    .hero-title{font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;font-weight:800;line-height:1.05;letter-spacing:.002em;font-size:clamp(28px,3.6vw,46px);color:#F14816;margin:10px 0 12px 0;max-width:26ch;white-space:normal;text-wrap:balance}
  4147.    .hero-sub{max-width:700px;font-size:clamp(13px,1.2vw,16px);line-height:1.65;color:rgba(255,255,255,.92)}
  4148.    .hero-ctas{margin-top:14px}
  4149.    .hero-ctas .btn-primary{background:#F14816;color:#fff;box-shadow:0 6px 16px rgba(241,72,22,.25)}
  4150.    .hero-dots{position:absolute;left:50%;bottom:24px;transform:translateX(-50%);z-index:4;display:flex;gap:8px}
  4151.    .hero-dots button{width:7px;height:7px;border-radius:9999px;border:0;background:#fff;opacity:.55}
  4152.    .hero-dots button[aria-selected="true"]{background:#F14816;opacity:1;width:9px;height:9px}
  4153.    .hero-nav{background:rgba(141,156,165,.85);border:1px solid rgba(217,217,217,.30);box-shadow:0 2px 8px rgba(0,0,0,.20)}
  4154.    :root{--hero-left:76px;}
  4155.    .hero .container.hero-content{
  4156.    max-width:100% !important;
  4157.    margin:0 !important;
  4158.    padding-left:var(--hero-left) !important;
  4159.    padding-right:0 !important;
  4160.    justify-content:flex-start !important;
  4161.    }
  4162.    .hero .hero-copy{
  4163.    width:100% !important;
  4164.    text-align:left !important;
  4165.    align-items:flex-start !important;
  4166.    }
  4167. </style>
  4168. <script>
  4169.    (function () {
  4170.      const track = document.querySelector('.hero-track');
  4171.      if (!track) return;
  4172.    
  4173.      const allSlides = Array.from(track.querySelectorAll('article[data-scope]'));
  4174.    
  4175.      let slides = allSlides.filter(el =>
  4176.        el.classList.contains('hero-slide') &&
  4177.        !el.classList.contains('hero-slide-config')
  4178.      );
  4179.      if (!slides.length) {
  4180.        slides = allSlides.filter(el => el.classList.contains('hero-slide'));
  4181.      }
  4182.      if (!slides.length) return;
  4183.    
  4184.      const btnPrev  = document.querySelector('.hero-nav.prev');
  4185.      const btnNext  = document.querySelector('.hero-nav.next');
  4186.      const dotsWrap = document.querySelector('.hero-dots');
  4187.    
  4188.      let dots = [];
  4189.      if (dotsWrap) {
  4190.        dotsWrap.innerHTML = '';
  4191.        slides.forEach((_, i) => {
  4192.          const dot = document.createElement('button');
  4193.          dot.type = 'button';
  4194.          dot.setAttribute('role', 'tab');
  4195.          dot.setAttribute('aria-label', 'Aller à la diapo ' + (i + 1));
  4196.          dot.addEventListener('click', () => goTo(i, true));
  4197.          dotsWrap.appendChild(dot);
  4198.          dots.push(dot);
  4199.        });
  4200.      }
  4201.    
  4202.      let index = 0;
  4203.      let timer = null;
  4204.      const AUTOPLAY_DELAY = 6000;
  4205.      function applyRibbonVisibility(slide) {
  4206.        if (!slide) return;
  4207.        const shell = slide.querySelector('.hero-ribbon-shell');
  4208.        const rEl = slide.querySelector('.home-event-text');
  4209.        if (!shell || !rEl) return;
  4210.        const txt = (rEl.textContent || '').trim();
  4211.        shell.style.display = (txt && txt !== '-') ? '' : 'none';
  4212.      }
  4213.      function update() {
  4214.       slides.forEach((slide, i) => {
  4215.         const active = i === index;
  4216.         slide.classList.toggle('is-active', active);
  4217.         slide.setAttribute('aria-hidden', active ? 'false' : 'true');
  4218.         slide.style.display = active ? 'block' : 'none';
  4219.         applyRibbonVisibility(slide);
  4220.       });
  4221.    
  4222.        if (dots.length) {
  4223.          dots.forEach((dot, i) => {
  4224.            dot.setAttribute('aria-selected', i === index ? 'true' : 'false');
  4225.          });
  4226.        }
  4227.      }
  4228.    
  4229.      function goTo(newIndex, fromUser) {
  4230.        const max = slides.length;
  4231.        index = ((newIndex % max) + max) % max;
  4232.        update();
  4233.        if (fromUser) restartAutoplay();
  4234.      }
  4235.    
  4236.      function next(fromUser) {
  4237.        goTo(index + 1, fromUser);
  4238.      }
  4239.    
  4240.      function prev(fromUser) {
  4241.        goTo(index - 1, fromUser);
  4242.      }
  4243.    
  4244.      function startAutoplay() {
  4245.        if (timer || slides.length < 2) return;
  4246.        timer = setInterval(() => next(false), AUTOPLAY_DELAY);
  4247.      }
  4248.    
  4249.      function stopAutoplay() {
  4250.        if (!timer) return;
  4251.        clearInterval(timer);
  4252.        timer = null;
  4253.      }
  4254.    
  4255.      function restartAutoplay() {
  4256.        stopAutoplay();
  4257.        startAutoplay();
  4258.      }
  4259.    
  4260.      if (btnPrev) btnPrev.addEventListener('click', () => prev(true));
  4261.      if (btnNext) btnNext.addEventListener('click', () => next(true));
  4262.    
  4263.      const hero = document.querySelector('.hero');
  4264.      if (hero) {
  4265.        hero.addEventListener('mouseenter', stopAutoplay);
  4266.        hero.addEventListener('mouseleave', startAutoplay);
  4267.        hero.addEventListener('focusin', stopAutoplay);
  4268.        hero.addEventListener('focusout', startAutoplay);
  4269.      }
  4270.      
  4271.    /******************************************************************/
  4272.  (function bindSwipe() {
  4273.     const mql = window.matchMedia('(max-width: 767.98px)');
  4274.     let startX = 0, startY = 0, startT = 0;
  4275.     let pointerId = null;
  4276.     let tracking = false;
  4277.     const THRESH_X = 45;    
  4278.     const THRESH_Y = 80;    
  4279.     const MAX_TIME = 600;    
  4280.     let blockClickUntil = 0;
  4281.     hero.addEventListener('click', (e) => {
  4282.       if (Date.now() < blockClickUntil) {
  4283.         e.preventDefault();
  4284.         e.stopPropagation();
  4285.       }
  4286.     }, true);
  4287.     function shouldIgnoreTarget(e) {
  4288.       return !!e.target.closest('a,button,input,textarea,select,label');
  4289.     }
  4290.     function onDown(e) {
  4291.       if (!mql.matches) return;
  4292.       if (e.pointerType === 'mouse') return; 
  4293.       if (shouldIgnoreTarget(e)) return;
  4294.       tracking = true;
  4295.       pointerId = e.pointerId;
  4296.       startX = e.clientX;
  4297.       startY = e.clientY;
  4298.       startT = performance.now();
  4299.       stopAutoplay();
  4300.       try { hero.setPointerCapture(pointerId); } catch {}
  4301.     }
  4302.     function onUp(e) {
  4303.       if (!tracking) return;
  4304.       tracking = false;
  4305.       const dx = e.clientX - startX;
  4306.       const dy = e.clientY - startY;
  4307.       const dt = performance.now() - startT;
  4308.       try { hero.releasePointerCapture(pointerId); } catch {}
  4309.       pointerId = null;
  4310.       if (Math.abs(dx) >= THRESH_X && Math.abs(dy) <= THRESH_Y && dt <= MAX_TIME) {
  4311.         if (dx < 0) next(true);  
  4312.         else prev(true);        
  4313.         blockClickUntil = Date.now() + 400;
  4314.       }
  4315.       startAutoplay();
  4316.     }
  4317.     hero.addEventListener('pointerdown', onDown, { passive: true });
  4318.     hero.addEventListener('pointerup', onUp, { passive: true });
  4319.     hero.addEventListener('pointercancel', onUp, { passive: true });
  4320.   })();
  4321.    /*******************************************************************/
  4322.      update();
  4323.      startAutoplay();
  4324.    })();
  4325. </script>
  4326. <style>
  4327. .hero,
  4328. .hero-track{
  4329.   touch-action: pan-y;         
  4330.   -webkit-user-select: none;
  4331.   user-select: none;
  4332. }
  4333. </style>
  4334. {% endblock %}