/**
 * locations.js — v3.1.0 — Réécriture complète
 * CRUD + vue zones + table triable + import CSV + popup étiquettes dédiée
 *   v2.6.5 (2026-07-08)    — Harmonisation Lot 5 : data-label mobile, actions tactiles et cohérence visuelle admin.
 */
function _esc(s){ return String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c])); }

const LOC_TYPES = {
  rack:      { icon:'🗄️', label:'Rack/Étagère', color:'var(--blue)' },
  zone:      { icon:'📍', label:'Zone', color:'var(--green)' },
  sol:       { icon:'⬛', label:'Au sol', color:'var(--orange)' },
  armoire:   { icon:'🚪', label:'Armoire', color:'#8b5cf6' },
  exterieur: { icon:'🌤️', label:'Extérieur', color:'#06b6d4' },
  tampon:    { icon:'⏳', label:'Zone tampon', color:'#f43f5e' },
  autre:     { icon:'📦', label:'Autre', color:'var(--text-muted)' },
};

const LocationsPage = {
  rows: [], warehouses: [], stock: [],
  filter: { warehouseId:null, q:'' },
  currentView: 'zones', sortCol:'code', sortAsc:true,

  async init(){
    if(!document.getElementById('pgLocations')) return;
    this.el = {
      wh: document.getElementById('locWarehouse'),
      search: document.getElementById('locSearch'),
      tbl: document.getElementById('tblLocations')?.querySelector('tbody'),
      tblWrap: document.getElementById('locTableWrap'),
      zones: document.getElementById('locZonesView'),
      btnAdd: document.getElementById('btnLocAdd'),
      btnImport: document.getElementById('btnLocImport'),
      btnLabels: document.getElementById('btnLocLabels'),
    };
    this._bind();
    await this._loadWarehouses();
    await this.load();
  },

  _bind(){
    this.el.btnAdd?.addEventListener('click', ()=>this.openModal(null));
    this.el.btnImport?.addEventListener('click', ()=>this.openImportModal());
    this.el.btnLabels?.addEventListener('click', ()=>this.openLabelsWindow());
    this.el.search?.addEventListener('input', ()=>{ this.filter.q=(this.el.search.value||'').trim().toLowerCase(); this.render(); });
    this.el.wh?.addEventListener('change', async()=>{ this.filter.warehouseId=this.el.wh.value||null; await this.load(); });
    document.querySelectorAll('.loc-vbtn').forEach(btn=>{
      btn.addEventListener('click', ()=>{
        document.querySelectorAll('.loc-vbtn').forEach(b=>b.classList.remove('active'));
        btn.classList.add('active');
        this.currentView = btn.dataset.view;
        this.render();
      });
    });
    document.querySelectorAll('#tblLocations th.sortable').forEach(th=>{
      th.addEventListener('click', ()=>{
        if(this.sortCol===th.dataset.col) this.sortAsc=!this.sortAsc;
        else { this.sortCol=th.dataset.col; this.sortAsc=true; }
        this.render();
      });
    });
  },

  async _loadWarehouses(){
    this.warehouses = await Api.warehouses();
    if(this.el.wh) this.el.wh.innerHTML = '<option value="">Tous les magasins</option>' +
      this.warehouses.map(w=>`<option value="${w.id}">${_esc(w.name)}</option>`).join('');
  },

  async load(){
    [this.rows, this.stock] = await Promise.all([Api.locations(this.filter.warehouseId), Api.stock()]);
    this.render();
  },

  // ═══════════════════════════════════════════════════════════════
  // RENDER
  // ═══════════════════════════════════════════════════════════════
  _getFiltered(){
    const q = this.filter.q;
    let rows = this.rows.slice();
    if(q) rows = rows.filter(r=>`${r.code||''} ${r.label||''} ${r.zone||''} ${r.type||''}`.toLowerCase().includes(q));
    return rows;
  },
  _sorted(rows){
    const c=this.sortCol, asc=this.sortAsc;
    return rows.slice().sort((a,b)=>{
      let va='',vb='';
      if(c==='warehouse'){ va=(this.warehouses.find(w=>String(w.id)===String(a.warehouseId))||{}).name||''; vb=(this.warehouses.find(w=>String(w.id)===String(b.warehouseId))||{}).name||''; }
      else { va=String(a[c]||''); vb=String(b[c]||''); }
      return asc?va.localeCompare(vb,'fr'):vb.localeCompare(va,'fr');
    });
  },
  render(){
    const f=this._getFiltered();
    if(this.el.zones) this.el.zones.style.display = this.currentView==='zones'?'block':'none';
    if(this.el.tblWrap) this.el.tblWrap.style.display = this.currentView==='table'?'block':'none';
    if(this.currentView==='zones') this._renderZones(f); else this._renderTable(f);
  },

  _renderZones(filtered){
    if(!this.el.zones) return;
    if(!filtered.length){ this.el.zones.innerHTML='<div class="loc-zones-empty">Aucun emplacement. Cliquez sur "+ Ajouter".</div>'; return; }
    const groups={};
    filtered.forEach(r=>{ const z=(r.zone||'').trim()||'Sans zone'; (groups[z]=groups[z]||[]).push(r); });
    const zn=Object.keys(groups).sort((a,b)=>a==='Sans zone'?1:b==='Sans zone'?-1:a.localeCompare(b,'fr'));
    this.el.zones.innerHTML = zn.map(z=>{
      const items=groups[z].sort((a,b)=>(a.code||'').localeCompare(b.code||''));
      return `<div class="loc-zone-group"><div class="loc-zone-hdr"><span class="loc-zone-name">${_esc(z)}</span><span class="loc-zone-count">${items.length}</span></div><div class="loc-zone-grid">${items.map(r=>this._card(r)).join('')}</div></div>`;
    }).join('');
    this.el.zones.querySelectorAll('[data-act]').forEach(btn=>{
      btn.onclick=()=>{
        const row=this.rows.find(x=>String(x.id)===String(btn.dataset.id));
        if(btn.dataset.act==='edit') this.openModal(row);
        if(btn.dataset.act==='del') this.deleteRow(row);
      };
    });
  },

  _card(r){
    const t=LOC_TYPES[r.type||'autre']||LOC_TYPES.autre;
    const wh=this.warehouses.find(w=>String(w.id)===String(r.warehouseId));
    const inactive=r.active===false?' loc-card-inactive':'';
    return `<div class="loc-emplacement${inactive}">
      <div class="loc-emp-top">
        <span class="loc-emp-icon" style="background:${t.color}">${t.icon}</span>
        <div class="loc-emp-info">
          <div class="loc-emp-code">${_esc(r.code)}</div>
          <div class="loc-emp-label">${_esc(r.label||'—')}</div>
        </div>
        ${r.active===false?'<span class="badge badge-muted" style="font-size:10px">Inactif</span>':''}
      </div>
      <div class="loc-emp-meta">
        <span class="loc-emp-type">${t.icon} ${_esc(t.label)}</span>
        ${wh?`<span class="loc-emp-wh">${_esc(wh.name)}</span>`:''}
      </div>
      <div class="loc-emp-actions">
        <button class="btn btn-ghost btn-sm" data-act="edit" data-id="${r.id}">✏️ Éditer</button>
        <button class="btn btn-ghost btn-sm" data-act="del" data-id="${r.id}" style="color:var(--red)">🗑️</button>
      </div>
    </div>`;
  },

  _renderTable(filtered){
    if(!this.el.tbl) return;
    const rows=this._sorted(filtered);
    document.querySelectorAll('#tblLocations th.sortable').forEach(th=>{
      th.classList.toggle('sorted',th.dataset.col===this.sortCol);
      th.setAttribute('data-dir',this.sortAsc?'asc':'desc');
    });
    if(!rows.length){ this.el.tbl.innerHTML='<tr><td colspan="7" style="text-align:center;color:var(--text-muted);padding:32px">Aucun emplacement</td></tr>'; return; }
    this.el.tbl.innerHTML = rows.map(r=>{
      const wh=this.warehouses.find(w=>String(w.id)===String(r.warehouseId));
      const t=LOC_TYPES[r.type||'autre']||LOC_TYPES.autre;
      return `<tr>
        <td data-label="Code"><strong>${_esc(r.code)}</strong></td>
        <td data-label="Libellé">${_esc(r.label||'')||'<span style="color:var(--text-muted)">—</span>'}</td>
        <td data-label="Type"><span class="loc-type-badge" style="--tc:${t.color}">${t.icon} ${_esc(t.label)}</span></td>
        <td data-label="Zone">${_esc(r.zone||'')||'<span style="color:var(--text-muted)">—</span>'}</td>
        <td data-label="Magasin">${wh?_esc(wh.name):'—'}</td>
        <td data-label="Actif" style="text-align:center">${r.active===false?'<span class="badge badge-muted">Non</span>':'<span class="badge badge-green">Oui</span>'}</td>
        <td data-label="Actions" class="cell-actions" style="text-align:right;white-space:nowrap">
          <button class="btn btn-ghost btn-sm" data-act="edit" data-id="${r.id}">Éditer</button>
          <button class="btn btn-danger btn-sm" data-act="del" data-id="${r.id}">Suppr.</button>
        </td></tr>`;
    }).join('');
    this.el.tbl.querySelectorAll('[data-act]').forEach(btn=>{
      btn.onclick=()=>{
        const row=this.rows.find(x=>String(x.id)===String(btn.dataset.id));
        if(btn.dataset.act==='edit') this.openModal(row);
        if(btn.dataset.act==='del') this.deleteRow(row);
      };
    });
  },

  // ═══════════════════════════════════════════════════════════════
  // CRUD MODAL
  // ═══════════════════════════════════════════════════════════════
  openModal(row){
    const isNew=!row;
    const whOpts=this.warehouses.map(w=>`<option value="${w.id}">${_esc(w.name)}</option>`).join('');
    const typeOpts=Object.entries(LOC_TYPES).map(([k,v])=>`<option value="${k}" ${(row?.type||'autre')===k?'selected':''}>${v.icon} ${_esc(v.label)}</option>`).join('');
    const html=`<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px">
      <div><label class="lbl">Magasin *</label><select id="locMWarehouse" class="sel">${whOpts}</select></div>
      <div><label class="lbl">Code *</label><input id="locMCode" class="inp" placeholder="A1, R2-E3…" value="${_esc(row?.code||'')}"></div>
      <div><label class="lbl">Type</label><select id="locMType" class="sel">${typeOpts}</select></div>
      <div><label class="lbl">Zone</label><input id="locMZone" class="inp" placeholder="Réserve, Quarantaine…" value="${_esc(row?.zone||'')}"></div>
      <div style="grid-column:1/-1"><label class="lbl">Libellé</label><input id="locMLabel" class="inp" placeholder="Rack 2 – Étage 1…" value="${_esc(row?.label||'')}"></div>
      <div><label class="lbl">Actif</label><select id="locMActive" class="sel">
        <option value="1" ${row?.active!==false?'selected':''}>Oui</option>
        <option value="0" ${row?.active===false?'selected':''}>Non</option>
      </select></div>
    </div><input type="hidden" id="locMId" value="${_esc(String(row?.id||''))}">`;
    Modal.show(isNew?'Ajouter un emplacement':'Modifier l\'emplacement', html, [
      {label:'Annuler',cls:'btn-secondary',onclick:()=>Modal.close()},
      {label:isNew?'Enregistrer':'Mettre à jour',cls:'btn-primary',onclick:()=>this.saveModal()},
    ], isNew?'success':'info');
    const sel=document.getElementById('locMWarehouse');
    if(sel) sel.value=String(row?.warehouseId||this.filter.warehouseId||this.warehouses[0]?.id||'');
  },

  async saveModal(){
    const id=(document.getElementById('locMId')?.value||'').trim();
    const p={id:id||undefined, warehouseId:parseInt(document.getElementById('locMWarehouse')?.value||'0',10),
      code:(document.getElementById('locMCode')?.value||'').trim(), label:(document.getElementById('locMLabel')?.value||'').trim(),
      zone:(document.getElementById('locMZone')?.value||'').trim(), type:document.getElementById('locMType')?.value||'autre',
      active:document.getElementById('locMActive')?.value==='1'};
    if(!p.warehouseId||!p.code){ toast('Magasin et code obligatoires.','warning'); return; }
    try{ if(id) await Api.locationUpdate(p); else await Api.locationSave(p);
      toast(id?'Mis à jour.':'Ajouté.','success'); Modal.close(); await this.load();
    }catch(e){ toast(e?.message||'Erreur','error'); }
  },

  async deleteRow(row){
    if(!row||!confirm(`Supprimer "${row.code}" ?`)) return;
    try{ await Api.locationDelete(row.id); toast('Supprimé.','success'); await this.load(); }
    catch(e){ toast(e?.message||'Erreur','error'); }
  },

  // ═══════════════════════════════════════════════════════════════
  // IMPORT CSV
  // ═══════════════════════════════════════════════════════════════
  openImportModal(){
    const wid=this.filter.warehouseId||(this.warehouses[0]?.id||'');
    if(!wid){ toast('Sélectionnez un magasin.','warning'); return; }
    const tpl=document.getElementById('tplLocImport');
    Modal.show('Import en masse',tpl?tpl.innerHTML:'<p>Erreur</p>',[
      {label:'Annuler',cls:'btn-secondary',onclick:()=>Modal.close()},
      {label:'Importer',cls:'btn-primary',onclick:()=>this._doImport(wid)},
    ],'info');
  },
  async _doImport(wid){
    const raw=(document.getElementById('locImportText')?.value||'').trim();
    if(!raw){ toast('Collez des données.','warning'); return; }
    let ok=0,err=0;
    for(const line of raw.split(/\r?\n/).filter(l=>l.trim())){
      const sep=line.includes(';')?';':',', p=line.split(sep).map(s=>s.trim());
      if(!p[0]){err++;continue;}
      try{ await Api.locationSave({warehouseId:parseInt(wid,10),code:p[0],zone:p[1]||'',label:p[2]||'',type:p[3]||'autre',active:true}); ok++; }
      catch(e){err++;}
    }
    const r=document.getElementById('locImportResult');
    if(r) r.innerHTML=`<div style="padding:8px;border-radius:8px;background:var(--green-pale);color:var(--green);font-weight:700">${ok} importé(s), ${err} erreur(s)</div>`;
    if(ok>0) await this.load();
  },

  // ═══════════════════════════════════════════════════════════════
  // ÉTIQUETTES — POPUP DÉDIÉE (fenêtre séparée, CSS intégré)
  // ═══════════════════════════════════════════════════════════════
  openLabelsWindow(){
    const rows = this.rows.filter(r=>r.active!==false).sort((a,b)=>(a.code||'').localeCompare(b.code||''));
    if(!rows.length){ toast('Aucun emplacement actif.','warning'); return; }

    // Enrich with warehouse name
    const enriched = rows.map(r=>{
      const wh = this.warehouses.find(w=>String(w.id)===String(r.warehouseId));
      return {...r, whName: wh?.name||''};
    });

    const win = window.open('','_blank','width=1100,height=800');
    if(!win){ toast('Popup bloquée.','error'); return; }

    const baseHref = (() => {
      try{ const u=new URL(window.location.href); const parts=u.pathname.split('/').filter(Boolean); parts.pop(); return `${u.origin}/${parts.join('/')}/`; }
      catch(e){ return '/'; }
    })();

    const dataJSON = JSON.stringify(enriched.map(r=>({id:String(r.id),code:r.code||'',zone:r.zone||'',label:r.label||'',whName:r.whName||''})));

    win.document.write(this._labelWindowHTML(baseHref, dataJSON));
    win.document.close();
  },

  _labelWindowHTML(baseHref, dataJSON){
    return `<!doctype html><html lang="fr"><head><meta charset="utf-8"><base href="${baseHref}">
<title>Étiquettes emplacements — MageStock</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{font-family:Arial,Helvetica,sans-serif;background:#e8eaed;color:#1e293b}

/* ── HEADER ── */
.hdr{background:#fff;border-bottom:2px solid #e2e8f0;padding:14px 24px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;position:sticky;top:0;z-index:10}
.hdr h1{font-size:16px;font-weight:800}
.hdr-right{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.hdr label{font-size:13px;font-weight:700;display:flex;align-items:center;gap:6px}
.hdr select{padding:6px 10px;border:1.5px solid #cbd5e1;border-radius:8px;font-size:13px;font-weight:600;background:#fff}
.hdr .info{font-size:12px;color:#64748b}
.btn-pr{padding:10px 24px;background:#2563eb;color:#fff;border:none;border-radius:10px;font-weight:700;font-size:14px;cursor:pointer}
.btn-pr:hover{background:#1d4ed8}

/* ── SÉLECTION ── */
.sel-bar{background:#fff;padding:12px 24px;border-bottom:1px solid #e2e8f0;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.sel-bar label{font-size:13px;cursor:pointer;display:flex;align-items:center;gap:6px}
.sel-bar .cnt{font-size:12px;color:#64748b;font-weight:600;margin-left:auto}
.chk-list{display:flex;flex-wrap:wrap;gap:6px;max-width:100%}
.chk-item{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border:1.5px solid #e2e8f0;border-radius:20px;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;background:#fff}
.chk-item:hover{border-color:#3b82f6}
.chk-item.on{border-color:#3b82f6;background:#eff6ff;color:#2563eb}

/* ── PLANCHE A4 ── */
.sheet{
  width:297mm;height:210mm;
  box-sizing:border-box;padding:8mm;
  display:grid;gap:3mm;
  justify-content:center;align-content:center;
  background:#fff;margin:20px auto;
  box-shadow:0 2px 16px rgba(0,0,0,.1);border-radius:4px;
  overflow:hidden;
}

/* ── ÉTIQUETTE ── */
.lbl{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;
  border:1.5px solid #e2e8f0;border-radius:4mm;padding:3mm;overflow:hidden;gap:2mm}
.lbl-logo{object-fit:contain}
.lbl-code{font-weight:900;color:#111;line-height:1.15;letter-spacing:.5px}
.lbl-sub{color:#64748b;line-height:1.15}
.lbl-empty{border-color:transparent}

/* ── DENSITÉ — tailles ── */
.sheet[data-d="1"]{grid-template-columns:1fr;grid-template-rows:1fr}
.sheet[data-d="1"] .lbl-logo{width:70mm;height:70mm}
.sheet[data-d="1"] .lbl-code{font-size:42pt}
.sheet[data-d="1"] .lbl-sub{font-size:18pt}

.sheet[data-d="2"]{grid-template-columns:1fr;grid-template-rows:1fr 1fr}
.sheet[data-d="2"] .lbl-logo{width:45mm;height:45mm}
.sheet[data-d="2"] .lbl-code{font-size:28pt}
.sheet[data-d="2"] .lbl-sub{font-size:14pt}

.sheet[data-d="4"]{grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr}
.sheet[data-d="4"] .lbl-logo{width:32mm;height:32mm}
.sheet[data-d="4"] .lbl-code{font-size:20pt}
.sheet[data-d="4"] .lbl-sub{font-size:10pt}

.sheet[data-d="8"]{grid-template-columns:1fr 1fr;grid-template-rows:repeat(4,1fr)}
.sheet[data-d="8"] .lbl-logo{width:18mm;height:18mm}
.sheet[data-d="8"] .lbl-code{font-size:14pt}
.sheet[data-d="8"] .lbl-sub{font-size:8pt}

.sheet[data-d="12"]{grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(4,1fr)}
.sheet[data-d="12"] .lbl-logo{width:14mm;height:14mm}
.sheet[data-d="12"] .lbl-code{font-size:11pt}
.sheet[data-d="12"] .lbl-sub{font-size:7pt}

.sheet[data-d="18"]{grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(6,1fr)}
.sheet[data-d="18"] .lbl-logo{width:10mm;height:10mm}
.sheet[data-d="18"] .lbl-code{font-size:9pt}
.sheet[data-d="18"] .lbl-sub{font-size:6pt}

/* ── IMPRESSION ── */
@media print{
  @page{size:A4 landscape;margin:0}
  html,body{background:#fff!important;margin:0!important;padding:0!important}
  .hdr,.sel-bar{display:none!important}
  .sheet{
    width:297mm!important;height:210mm!important;
    margin:0!important;padding:8mm!important;
    box-shadow:none!important;border-radius:0!important;
    page-break-after:always!important;
    transform:scale(0.95)!important;transform-origin:center center!important;
  }
  .sheet:last-child{page-break-after:auto!important}
  .lbl{border:1px solid #cbd5e1!important;-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
  .lbl-empty{border-color:transparent!important}
}
</style></head><body>

<div class="hdr">
  <div><h1>🏷️ Étiquettes emplacements — MageStock</h1><span class="info" id="pgInfo"></span></div>
  <div class="hdr-right">
    <label>Densité : <select id="selDensity">
      <option value="1">1 / feuille</option>
      <option value="2">2 / feuille</option>
      <option value="4" selected>4 / feuille</option>
      <option value="8">8 / feuille</option>
      <option value="12">12 / feuille</option>
      <option value="18">18 / feuille</option>
    </select></label>
    <button class="btn-pr" onclick="window.print()">🖨️ Imprimer</button>
  </div>
</div>

<div class="sel-bar">
  <label><input type="checkbox" id="chkAll"> <strong>Tout sélectionner</strong></label>
  <div class="chk-list" id="chkList"></div>
  <span class="cnt" id="selCnt">0 sélection(s)</span>
</div>

<div id="sheets"></div>

<script>
const ALL = ${dataJSON};
const selected = new Set();
const density = ()=> parseInt(document.getElementById('selDensity').value||'4',10);

function renderCheckboxes(){
  const list = document.getElementById('chkList');
  list.innerHTML = ALL.map(r=>{
    const on = selected.has(r.id)?'on':'';
    return '<span class="chk-item '+on+'" data-id="'+r.id+'" onclick="toggle(this)"><input type="checkbox" '+(on?'checked':'')+' style="pointer-events:none"> '+esc(r.code)+'</span>';
  }).join('');
  document.getElementById('selCnt').textContent = selected.size + ' sélection(s)';
  const chkAll = document.getElementById('chkAll');
  chkAll.checked = selected.size === ALL.length;
  chkAll.indeterminate = selected.size > 0 && selected.size < ALL.length;
}

function toggle(el){
  const id = el.dataset.id;
  if(selected.has(id)) selected.delete(id); else selected.add(id);
  renderCheckboxes();
  renderSheets();
}

document.getElementById('chkAll').addEventListener('change', function(){
  if(this.checked) ALL.forEach(r=>selected.add(r.id));
  else selected.clear();
  renderCheckboxes();
  renderSheets();
});

document.getElementById('selDensity').addEventListener('change', renderSheets);

function esc(s){ return String(s||'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c])); }

function renderSheets(){
  const d = density();
  const items = ALL.filter(r=>selected.has(r.id));
  const container = document.getElementById('sheets');
  if(!items.length){ container.innerHTML='<div style="text-align:center;padding:60px;color:#64748b;font-size:15px;font-weight:600">Sélectionnez des emplacements ci-dessus</div>'; updateInfo(0); return; }

  const pages = [];
  for(let i=0;i<items.length;i+=d){ pages.push(items.slice(i,i+d)); }

  container.innerHTML = pages.map(pg=>{
    while(pg.length<d) pg.push(null);
    return '<div class="sheet" data-d="'+d+'">' + pg.map(r=>{
      if(!r) return '<div class="lbl lbl-empty"></div>';
      const sub = [r.zone,r.whName].filter(Boolean).join(' · ');
      return '<div class="lbl"><img class="lbl-logo" src="assets/img/sm_logo.png" alt="Logo"><div class="lbl-code">'+esc(r.code)+'</div>'+(sub?'<div class="lbl-sub">'+esc(sub)+'</div>':'')+'</div>';
    }).join('') + '</div>';
  }).join('');

  updateInfo(pages.length);
}

function updateInfo(n){ document.getElementById('pgInfo').textContent = n>0 ? n+' page(s) · A4 paysage · '+density()+'/feuille' : ''; }

// Init: select all by default
ALL.forEach(r=>selected.add(r.id));
renderCheckboxes();
renderSheets();
<\/script></body></html>`;
  },
};

document.addEventListener('DOMContentLoaded', ()=>{
  if(window.SM_PAGE==='locations') LocationsPage.init();
});
