<?php
// ============================================================
// zones.php — zones d'image éditables en façade (admin connecté).
// ============================================================
require_once __DIR__ . '/config.php';

// Détecte si l'admin est connecté (lit la même session que le back-office)
function admin_connecte(): bool {
    // Ne démarre une session que si un cookie admin existe : les visiteurs
    // anonymes n'ouvrent pas de session (HTML cacheable, pas de cookie inutile).
    if (session_status() !== PHP_SESSION_ACTIVE) {
        if (empty($_COOKIE['tuina_admin'])) return false;
        require_once __DIR__ . '/auth.php';
        if (function_exists('auth_start')) {
            auth_start();
        } elseif (!headers_sent()) {
            session_name('tuina_admin');
            @session_start();
        }
    }
    return !empty($_SESSION['uid']);
}

// Récupère l'URL d'image d'une zone (ou null)
function zone_media(string $cle): ?array {
    static $cache = [];
    if (array_key_exists($cle, $cache)) return $cache[$cle];
    $st = db()->prepare("SELECT z.media_id, z.alt, z.taille, z.marge_haut, z.marge_bas, z.align, z.pos_x, z.pos_y, z.forme, z.bordure, z.ajustement, z.zoom, m.url
        FROM zones_images z LEFT JOIN medias m ON m.id=z.media_id
        WHERE z.cle = ?");
    $st->execute([$cle]);
    $r = $st->fetch();
    // renvoie les réglages même sans image (pour appliquer marges/décalage sur zone vide)
    return $cache[$cle] = $r ?: null;
}

// Affiche une zone image éditable.
//  $cle      : identifiant unique de la zone
//  $ratio    : ex "4/5" (CSS aspect-ratio) pour le cadre
//  $defaut   : HTML affiché s'il n'y a pas encore d'image (placeholder)
//  $classe   : classes CSS supplémentaires
function zone_image(string $cle, string $ratio = '4/5', string $defaut = '', string $classe = ''): void {
    $m = zone_media($cle);
    $edit = admin_connecte();
    // Hors mode admin : une zone sans image n'affiche aucun conteneur (pas de rond/placeholder vide).
    if (!$edit && (!$m || empty($m['url']))) return;
    $mh = (int)($m['marge_haut'] ?? 0);
    $mb = (int)($m['marge_bas'] ?? 0);
    $align = $m['align'] ?? 'center';
    $px = isset($m['pos_x']) ? (int)$m['pos_x'] : 50;
    $py = isset($m['pos_y']) ? (int)$m['pos_y'] : 50;
    $styles = [];
    if ($ratio) $styles[] = 'aspect-ratio:'.$ratio;
    if ($m && !empty($m['taille']) && (int)$m['taille'] > 0) $styles[] = 'width:'.(int)$m['taille'].'px';
    if ($mh !== 0) $styles[] = 'transform:translateY('.$mh.'px)';
    if ($align === 'left')   { $styles[] = 'margin-left:0'; $styles[] = 'margin-right:auto'; }
    elseif ($align === 'right'){ $styles[] = 'margin-left:auto'; $styles[] = 'margin-right:0'; }
    else                     { $styles[] = 'margin-left:auto'; $styles[] = 'margin-right:auto'; }
    $style = $styles ? ' style="'.implode(';',$styles).'"' : '';
    // Ajustement (cover = remplit/rogne · contain = image entière) + zoom interne
    $ajust = $m['ajustement'] ?? 'cover';
    if (!in_array($ajust, ['cover','contain'], true)) $ajust = 'cover';
    $zoom = isset($m['zoom']) ? max(50, min(300, (int)$m['zoom'])) : 100;
    $imgStyleParts = [];
    if ($px !== 50 || $py !== 50) $imgStyleParts[] = 'object-position:'.$px.'% '.$py.'%';
    $imgStyleParts[] = 'object-fit:'.$ajust;
    if ($zoom !== 100) $imgStyleParts[] = 'transform:scale('.($zoom/100).')';
    $imgStyle = $imgStyleParts ? ' style="'.implode(';',$imgStyleParts).'"' : '';
    $forme = $m['forme'] ?? 'rond';
    $bordure = isset($m['bordure']) ? (int)$m['bordure'] : 0;
    $classe = trim(preg_replace('/\bronde\b/', '', (string)$classe));
    $cls = ['zone-img'];
    if ($classe !== '') $cls[] = e($classe);
    $cls[] = ($forme === 'rond') ? 'ronde' : 'normale';
    if ($bordure === 1) $cls[] = 'avec-bordure';
    if ($edit) $cls[] = 'is-editable';
    echo '<div class="'.implode(' ',$cls).'" data-zone="'.e($cle).'" data-taille="'.($m['taille']??0).'" data-mh="'.$mh.'" data-mb="'.$mb.'" data-align="'.e($align).'" data-px="'.$px.'" data-py="'.$py.'" data-forme="'.e($forme).'" data-bordure="'.$bordure.'" data-ajust="'.e($ajust).'" data-zoom="'.$zoom.'"'.$style.'>';
    if ($m && !empty($m['url'])) {
        echo '<img src="/'.e($m['url']).'" alt="'.e($m['alt']).'" loading="lazy"'.$imgStyle.'>';
    } else {
        echo '<div class="zone-vide">'.($defaut ?: 'Image').'</div>';
    }
    if ($edit) {
        echo '<button type="button" class="zone-btn" data-zone="'.e($cle).'">'
           . (($m && !empty($m['url'])) ? 'Changer l\'image' : 'Ajouter une image') . '</button>';
    }
    echo '</div>';
}

// --- Zones de TEXTE éditables ---
function zone_texte_contenu(string $cle): ?string {
    static $cache = [];
    if (array_key_exists($cle, $cache)) return $cache[$cle];
    $st = db()->prepare("SELECT contenu FROM zones_textes WHERE cle=?");
    $st->execute([$cle]);
    $r = $st->fetch();
    return $cache[$cle] = ($r && $r['contenu'] !== '') ? $r['contenu'] : null;
}

// Affiche un bloc de texte éditable en mode admin.
//   zone_texte('accueil.intro.titre', '<p>Texte par défaut</p>', 'classe-css')
// Le défaut peut contenir du HTML simple (p, h2, strong, em, ul…).
function zone_texte(string $cle, string $defaut = '', string $classe = ''): void {
    $contenu = zone_texte_contenu($cle);
    $html = $contenu !== null ? $contenu : $defaut;
    $edit = admin_connecte();
    $cls = 'zone-txt' . ($classe ? ' '.e($classe) : '') . ($edit ? ' is-txt-editable' : '');
    echo '<div class="'.$cls.'" data-ztxt="'.e($cle).'"'.($edit ? ' tabindex="0"' : '').'>'.$html.'</div>';
}

// --- Réglages d'espacement de section ---
function section_reglage(string $cle): array {
    static $cache = [];
    if (array_key_exists($cle, $cache)) return $cache[$cle];
    $st = db()->prepare("SELECT pad_haut, pad_bas FROM sections_reglages WHERE cle=?");
    $st->execute([$cle]);
    $r = $st->fetch();
    return $cache[$cle] = $r ?: ['pad_haut'=>-1,'pad_bas'=>-1];
}

// Ouvre une <section> avec espacement réglable. À utiliser à la place de <section ...>
//   section_ouvrir('accueil.piliers', 'piliers-section')
function section_ouvrir(string $cle, string $classe = ''): void {
    $r = section_reglage($cle);
    $styles = [];
    if ((int)$r['pad_haut'] >= 0) $styles[] = 'padding-top:'.(int)$r['pad_haut'].'px';
    if ((int)$r['pad_bas']  >= 0) $styles[] = 'padding-bottom:'.(int)$r['pad_bas'].'px';
    $style = $styles ? ' style="'.implode(';',$styles).'"' : '';
    $edit = admin_connecte();
    echo '<section class="'.e($classe).($edit?' sec-editable':'').'" data-section="'.e($cle).'"'
        .' data-ph="'.(int)$r['pad_haut'].'" data-pb="'.(int)$r['pad_bas'].'"'.$style.'>';
    if ($edit) {
        echo '<button type="button" class="sec-btn" data-section="'.e($cle).'">⇕ espacement</button>';
    }
}
function section_fermer(): void { echo '</section>'; }

// Script UNIVERSEL (tous visiteurs) : marque les blocs de texte du contenu avec une clé
// stable (auto.<page>.<hash du texte d'origine>) et réinjecte les versions enregistrées.
function textes_auto_runtime(): void {
    ?>
<script>
window.__tuinaAutoText = (function(){
  function pageKey(){
    var p = location.pathname.replace(/\/+$/,'') || '/accueil';
    return p.replace(/[^a-z0-9]+/gi,'_').replace(/^_+|_+$/g,'').toLowerCase() || 'accueil';
  }
  // hash court & stable d'une chaîne (djb2)
  function hash(s){ var h=5381; for(var i=0;i<s.length;i++){ h=((h<<5)+h+s.charCodeAt(i))>>>0; } return h.toString(36); }
  function marque(){
    var pk = pageKey();
    var blocs = [];
    // zones à NE PAS rendre éditables (structure, navigation, titres de hero, boutons…)
    var EXCLUS = 'header, footer, .topbar, .nav, .mobile-menu, .breadcrumb, .kick, h1, .lead, .btn, .cta-line, .filtres, .tem-search, .piliers-nav, .piliers-row, .pagination, .adminbar, .txtbar, #txtbar, script, style, .sig, .logo-square, .r-cap, .rythme, .pn-pastille, .pilier, .article-meta, .lie-card, .tc, .lien-card';
    // tous les blocs de texte du contenu
    document.querySelectorAll('p, h2, h3, h4, li, blockquote').forEach(function(el){
      if (el.closest('.zone-txt')) return;        // déjà géré par zone_texte()
      if (el.hasAttribute('data-ztxt')) return;
      if (el.closest(EXCLUS)) return;             // dans une zone exclue
      if (el.closest('a')) return;                // pas de texte dans un lien
      // ignorer les blocs qui contiennent eux-mêmes d'autres blocs de texte (conteneurs)
      if (el.querySelector('p, h2, h3, h4, li, blockquote')) return;
      // ignorer le vide
      if (el.textContent.trim() === '') return;
      var base = (el.getAttribute('data-orig') || el.innerHTML).trim();
      var cle = 'auto.' + pk + '.' + hash(base);
      el.setAttribute('data-ztxt', cle);
      el.setAttribute('data-orig', base);
      blocs.push(el);
    });
    return blocs;
  }
  // réinjecter les contenus enregistrés
  function applique(blocs){
    fetch('/admin/texte_get.php?prefixe=auto.' + pageKey() + '.', {credentials:'same-origin'})
      .then(function(r){ return r.json(); })
      .then(function(map){
        blocs.forEach(function(el){
          var cle = el.getAttribute('data-ztxt');
          if (map && Object.prototype.hasOwnProperty.call(map, cle)) {
            el.innerHTML = map[cle];
          }
        });
        document.documentElement.classList.add('txt-ready');
      })
      .catch(function(){ document.documentElement.classList.add('txt-ready'); });
  }
  var blocs = marque();
  applique(blocs);
  return { marque: marque, blocs: blocs };
})();
</script>
    <?php
}

// Barre d'admin + sélecteur, injectés en bas de page si connecté
function admin_bar(): void {
    if (!admin_connecte()) return;
    ?>
<link rel="stylesheet" href="/assets/sections-admin.css"><script defer src="/assets/sections-admin.js"></script>
<script>/*gal-btn-init*/document.addEventListener("click",function(e){var b=e.target.closest&&e.target.closest(".gal-btn");if(!b)return;e.preventDefault();var z=b.closest("[data-galerie]");var slug=z?z.getAttribute("data-galerie"):"";var slider=slug.indexOf("slider-")===0;window.open("/admin/"+(slider?"slides.php":"galeries.php"),"_blank");});</script>
<div class="adminbar">
  <span class="ab-dot"></span> Mode édition · connecté
  <a href="/admin/dashboard.php">Tableau de bord</a>
  <a href="/admin/medias.php">Médiathèque</a>
  <span class="ab-hint">Survolez une image et cliquez « Changer l'image »</span>
</div>

<!-- Sélecteur d'image (modale) -->
<div class="zpick" id="zpick" aria-hidden="true">
  <div class="zpick-box">
    <div class="zpick-head">
      <b>Choisir une image</b>
      <button type="button" class="zpick-close" id="zpickClose">&times;</button>
    </div>
    <div class="zpick-drop" id="zpickDrop">
      Glisser-déposer, <kbd>Ctrl</kbd>+<kbd>V</kbd> pour coller, ou
      <button type="button" class="btn-mini" id="zpickPick">choisir un fichier</button>
      <input type="file" id="zpickFile" accept="image/*" hidden>
    </div>
    <div class="zpick-status" id="zpickStatus"></div>
    <div class="zpick-actions" id="zpickActions" style="display:none">
      <div class="zpick-size">
        <label>Taille de l'image : <b id="zpickSizeVal">auto</b>
          <button type="button" class="btn-auto" id="zpickAuto">↺ auto</button>
        </label>
        <input type="range" id="zpickSize" min="0" max="500" step="10" value="0">
      </div>
      <div class="zpick-size">
        <label>Décalage vertical : <b id="zpickMhVal">0 px</b>
          <span class="zpick-size-hint">(négatif = monter · ne crée pas d'espace)</span></label>
        <input type="range" id="zpickMh" min="-150" max="150" step="5" value="0">
      </div>
      <div class="zpick-size">
        <label>Alignement dans la colonne</label>
        <div class="zpick-align">
          <button type="button" data-align="left" id="zalLeft">⬅ Gauche</button>
          <button type="button" data-align="center" id="zalCenter">⬌ Centre</button>
          <button type="button" data-align="right" id="zalRight">Droite ➡</button>
        </div>
      </div>
      <div class="zpick-size">
        <label>Forme de l\'image</label>
        <div class="zpick-align">
          <button type="button" data-forme="rond" id="zfRond">● Rond</button>
          <button type="button" data-forme="normal" id="zfNormal">▭ Normal</button>
        </div>
      </div>
      <div class="zpick-size">
        <label>Bordure <span class="zpick-size-hint">(contour blanc + ombre · à désactiver pour un PNG transparent)</span></label>
        <div class="zpick-align">
          <button type="button" data-bordure="1" id="zbOui">Avec bordure</button>
          <button type="button" data-bordure="0" id="zbNon">Sans bordure</button>
        </div>
      </div>
      <div class="zpick-size">
        <label>Ajustement dans le cadre <span class="zpick-size-hint">(« Entière » = jamais coupée, idéal calligraphie/PNG)</span></label>
        <div class="zpick-align">
          <button type="button" data-ajust="cover" id="zajCover">Remplir</button>
          <button type="button" data-ajust="contain" id="zajContain">Image entière</button>
        </div>
      </div>
      <div class="zpick-size">
        <label>Zoom de l\'image : <b id="zpickZoomVal">100 %</b></label>
        <input type="range" id="zpickZoom" min="50" max="300" step="5" value="100">
      </div>
      <div class="zpick-size">
        <label>Position de l'image dans le cadre <span class="zpick-size-hint">(pour recadrer)</span></label>
        <div style="display:flex;gap:.8rem;align-items:center">
          <span style="font-size:.8rem;color:var(--ink2)">↔</span>
          <input type="range" id="zpickPx" min="0" max="100" step="5" value="50" style="flex:1">
        </div>
        <div style="display:flex;gap:.8rem;align-items:center;margin-top:.4rem">
          <span style="font-size:.8rem;color:var(--ink2)">↕</span>
          <input type="range" id="zpickPy" min="0" max="100" step="5" value="50" style="flex:1">
        </div>
      </div>
      <button type="button" class="btn-clear" id="zpickClear">Retirer l'image de cette zone</button>
    </div>
    <div class="zpick-grid" id="zpickGrid"></div>
  </div>
</div>

<!-- Réglage espacement de section -->
<div class="zpick" id="secpick" aria-hidden="true">
  <div class="zpick-box" style="max-width:460px">
    <div class="zpick-head">
      <b>Espacement de la section</b>
      <button type="button" class="zpick-close" id="secClose">&times;</button>
    </div>
    <div style="padding:1.4rem">
      <div class="zpick-size">
        <label>Espace en haut : <b id="secPhVal">défaut</b>
          <button type="button" class="btn-auto" id="secPhAuto">↺ défaut</button></label>
        <input type="range" id="secPh" min="0" max="200" step="5" value="0">
      </div>
      <div class="zpick-size">
        <label>Espace en bas : <b id="secPbVal">défaut</b>
          <button type="button" class="btn-auto" id="secPbAuto">↺ défaut</button></label>
        <input type="range" id="secPb" min="0" max="200" step="5" value="0">
      </div>
    </div>
  </div>
</div>

<script>
(function(){
  // ---- réglage espacement des sections ----
  var secModal=document.getElementById('secpick');
  var curSec=null;
  var secPh=document.getElementById('secPh'), secPb=document.getElementById('secPb');
  var secPhVal=document.getElementById('secPhVal'), secPbVal=document.getElementById('secPbVal');
  function secOpen(key){
    curSec=key; secModal.classList.add('on');
    var s=document.querySelector('.sec-editable[data-section="'+key+'"]');
    var ph=s?parseInt(s.getAttribute('data-ph'),10):-1;
    var pb=s?parseInt(s.getAttribute('data-pb'),10):-1;
    secPh.value=ph>=0?ph:0; secPhVal.textContent=ph>=0?ph+' px':'défaut';
    secPb.value=pb>=0?pb:0; secPbVal.textContent=pb>=0?pb+' px':'défaut';
  }
  function secClose(){ secModal.classList.remove('on'); curSec=null; }
  document.getElementById('secClose').addEventListener('click',secClose);
  secModal.addEventListener('click',function(e){ if(e.target===secModal) secClose(); });
  document.querySelectorAll('.sec-btn').forEach(function(b){
    b.addEventListener('click',function(e){ e.preventDefault(); e.stopPropagation(); secOpen(b.getAttribute('data-section')); });
  });
  var secTimer=null;
  function secSave(ph,pb){
    clearTimeout(secTimer);
    secTimer=setTimeout(function(){
      var fd=new FormData(); fd.append('section',curSec); fd.append('pad_haut',ph); fd.append('pad_bas',pb);
      fetch('/admin/section_action.php',{method:'POST',body:fd,credentials:'same-origin'});
    },400);
  }
  secPh.addEventListener('input',function(){
    var v=parseInt(secPh.value,10); secPhVal.textContent=v+' px';
    var s=document.querySelector('.sec-editable[data-section="'+curSec+'"]');
    if(s){ s.style.paddingTop=v+'px'; s.setAttribute('data-ph',v); }
    secSave(v,parseInt(secPb.value,10));
  });
  secPb.addEventListener('input',function(){
    var v=parseInt(secPb.value,10); secPbVal.textContent=v+' px';
    var s=document.querySelector('.sec-editable[data-section="'+curSec+'"]');
    if(s){ s.style.paddingBottom=v+'px'; s.setAttribute('data-pb',v); }
    secSave(parseInt(secPh.value,10),v);
  });
  document.getElementById('secPhAuto').addEventListener('click',function(){
    secPhVal.textContent='défaut'; secPh.value=0;
    var s=document.querySelector('.sec-editable[data-section="'+curSec+'"]');
    if(s){ s.style.paddingTop=''; s.setAttribute('data-ph',-1); }
    secSave(-1,parseInt(secPb.value,10));
  });
  document.getElementById('secPbAuto').addEventListener('click',function(){
    secPbVal.textContent='défaut'; secPb.value=0;
    var s=document.querySelector('.sec-editable[data-section="'+curSec+'"]');
    if(s){ s.style.paddingBottom=''; s.setAttribute('data-pb',-1); }
    secSave(parseInt(secPh.value,10),-1);
  });
})();
</script>

<!-- Barre de mise en forme du texte (mode admin) -->
<div id="txtbar" class="txtbar" aria-hidden="true">
  <button type="button" data-cmd="bold" title="Gras"><b>G</b></button>
  <button type="button" data-cmd="italic" title="Italique"><i>I</i></button>
  <span class="txtbar-sep"></span>
  <button type="button" data-cmd="formatBlock" data-val="h2" title="Titre">T</button>
  <button type="button" data-cmd="formatBlock" data-val="h3" title="Sous-titre">t</button>
  <button type="button" data-cmd="formatBlock" data-val="p" title="Paragraphe">¶</button>
  <span class="txtbar-sep"></span>
  <button type="button" data-cmd="fontbig" title="Agrandir">A+</button>
  <button type="button" data-cmd="fontsmall" title="Réduire">A−</button>
  <span class="txtbar-sep"></span>
  <button type="button" data-cmd="insertUnorderedList" title="Puces">•</button>
  <button type="button" data-cmd="justifyLeft" title="Gauche">⬅</button>
  <button type="button" data-cmd="justifyCenter" title="Centrer">⬌</button>
  <button type="button" data-cmd="justifyRight" title="Droite">➡</button>
  <span class="txtbar-sep"></span>
  <button type="button" data-cmd="removeFormat" title="Effacer la mise en forme">⌫</button>
  <span class="txtbar-sep"></span>
  <span class="txtbar-colors" title="Couleur du texte">
    <button type="button" class="txtcolor" data-color="#20262b" style="background:#20262b" title="Encre"></button>
    <button type="button" class="txtcolor" data-color="#1f4e79" style="background:#1f4e79" title="Bleu"></button>
    <button type="button" class="txtcolor" data-color="#4f7d6e" style="background:#4f7d6e" title="Jade"></button>
    <button type="button" class="txtcolor" data-color="#b08d57" style="background:#b08d57" title="Or"></button>
    <label class="txtcolor-pick" title="Autre couleur"><input type="color" id="txtColorInput" value="#1f4e79"><span>🎨</span></label>
  </span>
  <span class="txtbar-sep"></span>
  <button type="button" id="txtSave" class="txtbar-save">Enregistrer</button>
</div>

<script>
(function(){
  var bar=document.getElementById('txtbar');
  var current=null;       // zone en cours d'édition
  var dirty=false;

  function showBar(z){
    current=z;
    var r=z.getBoundingClientRect();
    bar.style.display='flex';
    // placer la barre au-dessus de la zone
    var top=window.scrollY + r.top - bar.offsetHeight - 8;
    if(top < window.scrollY+8) top = window.scrollY + r.bottom + 8; // sinon en dessous
    bar.style.top=top+'px';
    bar.style.left=(window.scrollX + r.left)+'px';
  }
  function hideBar(){ bar.style.display='none'; }

  // ===== AUTO (admin) : rendre éditables les blocs marqués par textes_auto_runtime() =====
  (function(){
    var blocs = (window.__tuinaAutoText && window.__tuinaAutoText.blocs) ? window.__tuinaAutoText.blocs : [];
    blocs.forEach(function(el){
      if (!el.classList.contains('is-txt-editable')) {
        el.classList.add('is-txt-editable');
        el.setAttribute('tabindex','0');
      }
    });
  })();
  // activer l'édition au clic sur une zone de texte (zone_texte() + blocs auto)
  document.querySelectorAll('.is-txt-editable').forEach(function(z){
    z.addEventListener('focus',function(){
      z.setAttribute('contenteditable','true');
      showBar(z);
    });
    z.addEventListener('click',function(){
      if(z.getAttribute('contenteditable')!=='true'){ z.setAttribute('contenteditable','true'); z.focus(); }
      showBar(z);
    });
    z.addEventListener('input',function(){ dirty=true; });
    z.addEventListener('keydown',function(e){
      // Ctrl+S = enregistrer
      if((e.ctrlKey||e.metaKey) && e.key==='s'){ e.preventDefault(); save(); }
    });
  });

  // commandes de mise en forme
  bar.querySelectorAll('button[data-cmd]').forEach(function(b){
    b.addEventListener('mousedown',function(e){
      e.preventDefault(); // ne pas perdre le focus de la zone
      if(!current) return;
      var cmd=b.getAttribute('data-cmd'), val=b.getAttribute('data-val')||null;
      if(cmd==='fontbig'){ document.execCommand('fontSize',false,'5'); }
      else if(cmd==='fontsmall'){ document.execCommand('fontSize',false,'2'); }
      else if(cmd==='formatBlock'){ document.execCommand('formatBlock',false,val); }
      else { document.execCommand(cmd,false,val); }
      dirty=true;
      current.focus();
    });
  });
  // pastilles de couleur du thème
  bar.querySelectorAll('.txtcolor').forEach(function(b){
    b.addEventListener('mousedown',function(e){
      e.preventDefault();
      if(!current) return;
      document.execCommand('foreColor',false,b.getAttribute('data-color'));
      dirty=true; current.focus();
    });
  });
  // sélecteur de couleur libre
  (function(){
    var ci=document.getElementById('txtColorInput');
    if(!ci) return;
    var applyColor=function(){
      if(!current) return;
      document.execCommand('foreColor',false,ci.value);
      dirty=true; current.focus();
    };
    ci.addEventListener('input',applyColor);
    ci.addEventListener('change',applyColor);
    // empêcher la perte de focus à l'ouverture du picker
    ci.closest('.txtcolor-pick').addEventListener('mousedown',function(e){ e.stopPropagation(); });
  })();

  function save(){
    if(!current) return;
    var cle=current.getAttribute('data-ztxt');
    var html=current.innerHTML;
    var btn=document.getElementById('txtSave');
    btn.textContent='…';
    var fd=new FormData(); fd.append('zone',cle); fd.append('contenu',html);
    fetch('/admin/texte_action.php',{method:'POST',body:fd,credentials:'same-origin'})
      .then(r=>r.json()).then(function(d){
        btn.textContent = d.ok ? '✓ enregistré' : '✗ erreur';
        dirty=false;
        setTimeout(function(){ btn.textContent='Enregistrer'; },1500);
      }).catch(function(){ btn.textContent='✗'; });
  }
  document.getElementById('txtSave').addEventListener('mousedown',function(e){ e.preventDefault(); save(); });

  // cacher la barre si on clique ailleurs (et proposer d'enregistrer si modifié)
  document.addEventListener('click',function(e){
    if(current && !current.contains(e.target) && !bar.contains(e.target)){
      if(dirty){ save(); }
      current.removeAttribute('contenteditable');
      current=null; hideBar();
    }
  });
  window.addEventListener('scroll',function(){ if(current) showBar(current); });
})();
</script>

<script>
(function(){
  var modal=document.getElementById('zpick');
  var grid=document.getElementById('zpickGrid');
  var statusEl=document.getElementById('zpickStatus');
  var drop=document.getElementById('zpickDrop');
  var fileInput=document.getElementById('zpickFile');
  var currentZone=null;

  var actions=document.getElementById('zpickActions');
  var sizeInput=document.getElementById('zpickSize');
  var sizeVal=document.getElementById('zpickSizeVal');
  var mhInput=document.getElementById('zpickMh');
  var mhVal=document.getElementById('zpickMhVal');
  var pxInput=document.getElementById('zpickPx');
  var pyInput=document.getElementById('zpickPy');
  var alBtns=[document.getElementById('zalLeft'),document.getElementById('zalCenter'),document.getElementById('zalRight')];
  var zfRond=document.getElementById('zfRond');
  var zfNormal=document.getElementById('zfNormal');
  var zbOui=document.getElementById('zbOui');
  var zbNon=document.getElementById('zbNon');
  var zajCover=document.getElementById('zajCover');
  var zajContain=document.getElementById('zajContain');
  var zoomInput=document.getElementById('zpickZoom');
  var zoomVal=document.getElementById('zpickZoomVal');

  function setAlignActive(al){
    alBtns.forEach(function(b){ b.classList.toggle('on', b.getAttribute('data-align')===al); });
  }

  function open(zone){
    currentZone=zone; modal.classList.add('on'); load();
    var z=document.querySelector('.zone-img[data-zone="'+zone+'"]');
    actions.style.display = 'block';
    var t = z ? parseInt(z.getAttribute('data-taille')||'0',10) : 0;
    var mh = z ? parseInt(z.getAttribute('data-mh')||'0',10) : 0;
    var al = z ? (z.getAttribute('data-align')||'center') : 'center';
    var px = z ? parseInt(z.getAttribute('data-px')||'50',10) : 50;
    var py = z ? parseInt(z.getAttribute('data-py')||'50',10) : 50;
    sizeInput.value=t; sizeVal.textContent = t>0 ? t+' px' : 'auto';
    mhInput.value=mh; mhVal.textContent = mh+' px';
    pxInput.value=px; pyInput.value=py;
    setAlignActive(al);
    var fo = z ? (z.getAttribute('data-forme')||'rond') : 'rond';
    var bo = z ? parseInt(z.getAttribute('data-bordure')||'0',10) : 0;
    setFormeActive(fo);
    setBordureActive(bo);
    var aj = z ? (z.getAttribute('data-ajust')||'cover') : 'cover';
    var zm = z ? parseInt(z.getAttribute('data-zoom')||'100',10) : 100;
    setAjustActive(aj);
    if(zoomInput){ zoomInput.value=zm; zoomVal.textContent=zm+' %'; }
  }
  function setAjustActive(aj){
    if(zajCover) zajCover.classList.toggle('on', aj==='cover');
    if(zajContain) zajContain.classList.toggle('on', aj==='contain');
  }
  function setFormeActive(fo){
    if(zfRond) zfRond.classList.toggle('on', fo==='rond');
    if(zfNormal) zfNormal.classList.toggle('on', fo==='normal');
  }
  function setBordureActive(bo){
    if(zbOui) zbOui.classList.toggle('on', bo===1);
    if(zbNon) zbNon.classList.toggle('on', bo!==1);
  }

  // enregistrement différé générique
  var saveTimer=null;
  function saveZone(extra){
    clearTimeout(saveTimer);
    saveTimer=setTimeout(function(){
      var fd=new FormData(); fd.append('zone',currentZone);
      Object.keys(extra).forEach(function(k){ fd.append(k,extra[k]); });
      fetch('/admin/zone_action.php',{method:'POST',body:fd,credentials:'same-origin'});
    },400);
  }

  // taille
  sizeInput.addEventListener('input',function(){
    var v=parseInt(sizeInput.value,10);
    sizeVal.textContent = v>0 ? v+' px' : 'auto';
    var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
    if(z){ z.style.width = v>0 ? v+'px' : ''; z.setAttribute('data-taille',v); }
    saveZone({op:'size',taille:v});
  });
  // bouton taille auto
  document.getElementById('zpickAuto').addEventListener('click',function(){
    sizeInput.value=0; sizeVal.textContent='auto';
    var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
    if(z){ z.style.width=''; z.setAttribute('data-taille',0); }
    saveZone({op:'size',taille:0});
  });
  // décalage vertical (transform : ne pousse pas le reste, pas d'espace blanc)
  mhInput.addEventListener('input',function(){
    var v=parseInt(mhInput.value,10); mhVal.textContent=v+' px';
    var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
    if(z){ z.style.transform = v!==0 ? 'translateY('+v+'px)' : ''; z.setAttribute('data-mh',v); }
    saveZone({op:'marges',marge_haut:v,marge_bas:0});
  });

  // alignement horizontal dans la colonne
  alBtns.forEach(function(b){
    b.addEventListener('click',function(){
      var al=b.getAttribute('data-align'); setAlignActive(al);
      var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
      if(z){
        if(al==='left'){ z.style.marginLeft='0'; z.style.marginRight='auto'; }
        else if(al==='right'){ z.style.marginLeft='auto'; z.style.marginRight='0'; }
        else { z.style.marginLeft='auto'; z.style.marginRight='auto'; }
        z.setAttribute('data-align',al);
      }
      saveZone({op:'align',align:al});
    });
  });

  // forme : rond / normal
  function applyForme(fo){
    var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
    if(z){
      z.classList.toggle('ronde', fo==='rond');
      z.classList.toggle('normale', fo!=='rond');
      z.setAttribute('data-forme',fo);
    }
    setFormeActive(fo);
    saveZone({op:'forme',forme:fo});
  }
  if(zfRond) zfRond.addEventListener('click',function(){ applyForme('rond'); });
  if(zfNormal) zfNormal.addEventListener('click',function(){ applyForme('normal'); });

  // bordure : oui / non
  function applyBordure(bo){
    var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
    if(z){
      z.classList.toggle('avec-bordure', bo===1);
      z.setAttribute('data-bordure',bo);
    }
    setBordureActive(bo);
    saveZone({op:'bordure',bordure:bo});
  }
  if(zbOui) zbOui.addEventListener('click',function(){ applyBordure(1); });
  if(zbNon) zbNon.addEventListener('click',function(){ applyBordure(0); });

  // ajustement : remplir (cover) / image entière (contain)
  function applyAjust(aj){
    var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
    if(z){
      var img=z.querySelector('img');
      if(img){ img.style.objectFit = aj; }
      z.setAttribute('data-ajust',aj);
    }
    setAjustActive(aj);
    saveZone({op:'ajustement',ajustement:aj});
  }
  if(zajCover) zajCover.addEventListener('click',function(){ applyAjust('cover'); });
  if(zajContain) zajContain.addEventListener('click',function(){ applyAjust('contain'); });

  // zoom de l'image dans le cadre
  if(zoomInput){
    zoomInput.addEventListener('input',function(){
      var v=parseInt(zoomInput.value,10); zoomVal.textContent=v+' %';
      var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
      if(z){
        var img=z.querySelector('img');
        if(img){ img.style.transform = v!==100 ? 'scale('+(v/100)+')' : ''; }
        z.setAttribute('data-zoom',v);
      }
      saveZone({op:'zoom',zoom:v});
    });
  }

  // position de l'image dans le cadre (object-position)
  function applyPos(){
    var px=parseInt(pxInput.value,10), py=parseInt(pyInput.value,10);
    var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
    if(z){ var img=z.querySelector('img'); if(img){ img.style.objectPosition=px+'% '+py+'%'; }
      z.setAttribute('data-px',px); z.setAttribute('data-py',py); }
    saveZone({op:'position',pos_x:px,pos_y:py});
  }
  pxInput.addEventListener('input',applyPos);
  pyInput.addEventListener('input',applyPos);
  function close(){ modal.classList.remove('on'); currentZone=null; }

  // bouton retirer
  document.getElementById('zpickClear').addEventListener('click',function(){
    if(!currentZone) return;
    if(!confirm('Retirer l\'image de cette zone ?')) return;
    setSt('Suppression…');
    var fd=new FormData(); fd.append('zone',currentZone); fd.append('op','clear');
    fetch('/admin/zone_action.php',{method:'POST',body:fd,credentials:'same-origin'})
      .then(r=>r.json()).then(d=>{
        if(d.ok){
          var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
          if(z){ z.querySelector('img')?.remove();
            if(!z.querySelector('.zone-vide')){ var v=document.createElement('div'); v.className='zone-vide'; v.textContent='Image'; z.insertBefore(v,z.firstChild); } }
          close();
        } else setSt('✗ '+(d.error||'erreur'),'err');
      }).catch(e=>setSt('✗ '+e,'err'));
  });
  document.getElementById('zpickClose').addEventListener('click',close);
  modal.addEventListener('click',function(e){ if(e.target===modal) close(); });

  // ouvrir depuis les boutons de zone (empêcher le lien parent de naviguer)
  document.querySelectorAll('.zone-btn').forEach(function(b){
    b.addEventListener('click',function(e){
      e.preventDefault(); e.stopPropagation();
      open(b.getAttribute('data-zone'));
    });
  });
  // si la zone elle-même est dans un lien, neutraliser le clic en mode édition
  document.querySelectorAll('.zone-img.is-editable').forEach(function(z){
    z.addEventListener('click',function(e){
      var a = z.closest('a');
      if(a){ e.preventDefault(); e.stopPropagation(); open(z.getAttribute('data-zone')); }
    });
  });

  function setSt(m,c){ statusEl.textContent=m||''; statusEl.className='zpick-status'+(c?' '+c:''); }

  // upload
  document.getElementById('zpickPick').addEventListener('click',function(){ fileInput.click(); });
  fileInput.addEventListener('change',function(){ for(const f of fileInput.files) up(f); });
  ['dragenter','dragover'].forEach(function(ev){ drop.addEventListener(ev,function(e){e.preventDefault();drop.classList.add('over');}); });
  ['dragleave','drop'].forEach(function(ev){ drop.addEventListener(ev,function(e){e.preventDefault();drop.classList.remove('over');}); });
  drop.addEventListener('drop',function(e){ for(const f of e.dataTransfer.files) if(f.type.indexOf('image/')===0) up(f); });
  window.addEventListener('paste',function(e){ if(!modal.classList.contains('on'))return;
    for(const it of e.clipboardData.items) if(it.type.indexOf('image/')===0){ var f=it.getAsFile(); if(f) up(f); } });

  function up(f){
    setSt('Envoi…');
    var fd=new FormData(); fd.append('action','upload'); fd.append('file',f,f.name||'collee.png');
    fetch('/admin/media_action.php',{method:'POST',body:fd,credentials:'same-origin'})
      .then(r=>r.json()).then(d=>{ if(d.ok){ setSt('✓ ajoutée',''); assign(d.id); } else setSt('✗ '+(d.error||'erreur'),'err'); })
      .catch(e=>setSt('✗ '+e,'err'));
  }

  function load(){
    grid.innerHTML='<p style="color:#888;padding:1rem">Chargement…</p>';
    fetch('/admin/media_action.php?action=list',{credentials:'same-origin'})
      .then(r=>r.json()).then(list=>{
        grid.innerHTML='';
        if(!list.length){ grid.innerHTML='<p style="color:#888;padding:1rem">Médiathèque vide. Envoyez une image ci-dessus.</p>'; return; }
        list.forEach(function(it){
          var d=document.createElement('button'); d.type='button'; d.className='mediatile';
          d.innerHTML='<img src="/'+it.url+'" loading="lazy" alt="">';
          d.addEventListener('click',function(){ assign(it.id); });
          grid.appendChild(d);
        });
      });
  }

  function assign(mediaId){
    if(!currentZone) return;
    setSt('Application…');
    var fd=new FormData(); fd.append('zone',currentZone); fd.append('media_id',mediaId);
    fetch('/admin/zone_action.php',{method:'POST',body:fd,credentials:'same-origin'})
      .then(r=>r.json()).then(d=>{
        if(d.ok){ // remplace l'image dans la page sans recharger
          var z=document.querySelector('.zone-img[data-zone="'+currentZone+'"]');
          if(z){ z.querySelector('img')?.remove(); z.querySelector('.zone-vide')?.remove();
            var img=document.createElement('img'); img.src='/'+d.url+'?t='+Date.now(); z.insertBefore(img,z.firstChild); }
          close();
        } else setSt('✗ '+(d.error||'erreur'),'err');
      }).catch(e=>setSt('✗ '+e,'err'));
  }
})();
</script>
<?php
}
