/* ============================================================
   base.css — общие стили: шапка с плитками разделов, подвал,
   модалка, лайтбокс, подбор, кнопки, плавающие кнопки, тост
   ============================================================ */
:root{
  --accent:#00b1b0; --text:#1c1c1c; --muted:#8a8a8a;
  --line:#e7e7e7; --bg-soft:#f5f5f5; --radius:12px;
}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:"Segoe UI",Arial,sans-serif;color:var(--text);background:#fff}
button{font:inherit}
a{color:inherit}
.no-scroll{overflow:hidden}
.hidden{display:none!important}

/* ---------- страница ---------- */
.page{max-width:1280px;margin:0 auto;padding:0 40px}

/* ---------- шапка: 5 колонок (4 плитки + контакты) ---------- */
.header{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;
  padding:16px 0;border-bottom:1px solid var(--line);align-items:stretch}
.brandnav{display:contents}
.logo{color:var(--text);display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;
  padding:12px 16px;border-radius:var(--radius);border:1px solid var(--line)}
.logo b{font-size:18px;letter-spacing:.5px;line-height:1.2}
.logo span{font-size:9px;letter-spacing:.4px;margin-top:4px;line-height:1.3;color:var(--muted)}

/* ---------- плитки разделов: у каждой свой цвет,
   неактивная = бледный оттенок, активная = насыщенный ---------- */
.logo{--c:#9aa0a6;--c-soft:#eceff1}
.logo{background:var(--c-soft)}
.logo.is-active{background:var(--c);border-color:transparent;
  box-shadow:0 4px 14px rgba(0,0,0,.22)}
.logo.is-active b{color:#fff}
.logo.is-active span{color:rgba(255,255,255,.8)}

.logo--inst {--c:#00b1b0;--c-soft:#dff2f1}
.logo--timo {--c:#d95f5f;--c-soft:#fbe4e4}
.logo--acc  {--c:#6cb946;--c-soft:#e9f4e4}
.logo--drain{--c:#4e8ccb;--c-soft:#e2ecf7}

/* ---------- плитки-ссылки: поведение ---------- */
.brandnav .logo{text-decoration:none;transition:.2s}
.brandnav .logo:hover{transform:translateY(-2px);box-shadow:0 8px 20px rgba(0,0,0,.18)}

/* ---------- контакты как последняя плитка ---------- */
.header__contacts{display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;font-size:15px;line-height:1.55;
  background:var(--bg-soft);border:1px solid var(--line);border-radius:var(--radius);
  padding:12px 16px}
.header__contacts a{color:var(--text);text-decoration:none;font-weight:600}
.header__contacts a:hover{text-decoration:underline}

/* ---------- бегущая строка ---------- */
.marquee{overflow:hidden;background:var(--bg-soft);border-bottom:1px solid var(--line)}
.marquee__inner{display:flex;width:max-content;animation:marquee 28s linear infinite}
.marquee__inner span{white-space:nowrap;padding:9px 0;color:var(--muted);
  font-size:13px;letter-spacing:.3px}
@keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ---------- общие атомы ---------- */
.cnt{color:var(--muted);font-size:13px;font-weight:400}
.dot{width:16px;height:16px;border-radius:50%;border:1px solid rgba(0,0,0,.18);flex-shrink:0}

/* ---------- кнопка ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;width:100%;padding:12px 20px;
  border:1px solid var(--text);background:#fff;border-radius:26px;cursor:pointer;transition:.2s}
.btn:hover{background:var(--text);color:#fff}
.btn--solid{background:var(--text);color:#fff}
.btn--solid:hover{opacity:.85}

/* ---------- нативный select ---------- */
.select-native{padding:11px 40px 11px 16px;border:1px solid #ddd;border-radius:26px;background:#fff;
  font:inherit;cursor:pointer;appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path d='M1 1l5 5 5-5' stroke='%23777' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
  background-repeat:no-repeat;background-position:right 16px center}
.select-native:focus{outline:0;border-color:var(--text)}

/* ---------- модальное окно ---------- */
.modal{position:fixed;inset:0;background:rgba(20,20,20,.45);display:none;
  align-items:center;justify-content:center;z-index:100;padding:16px}
.modal.open{display:flex}
.modal__box{background:#fff;border-radius:18px;max-width:920px;width:100%;
  max-height:92vh;overflow:auto;position:relative}
.modal__close{position:absolute;top:12px;right:12px;width:38px;height:38px;border-radius:50%;
  border:1px solid var(--line);background:#fff;cursor:pointer;z-index:2}
.modal__close:hover{background:var(--bg-soft)}
.modal__grid{display:grid;grid-template-columns:1fr 1fr;gap:28px;padding:28px}
.modal__media{display:flex;flex-direction:column}
.modal__img{position:relative;background:#fff;border:1px solid var(--line);border-radius:12px;min-height:320px;
  display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:zoom-in}
.modal__img img{max-width:100%;max-height:380px;object-fit:contain}
.modal__info h2{font-size:24px;margin-bottom:6px}
.modal__spec{color:var(--muted);font-size:14px;margin-top:6px}
.modal__desc{font-size:14px;line-height:1.5;margin-top:10px}
.modal__price{font-size:22px;font-weight:700;margin:14px 0 18px}
.success{background:#eef7ee;border:1px solid #cdeacd;color:#1d6b1d;
  padding:14px 16px;border-radius:12px;margin-top:8px}

/* ---------- галерея в модалке ---------- */
.gal__nav{position:absolute;top:50%;transform:translateY(-50%);width:38px;height:38px;
  border-radius:50%;border:1px solid var(--line);background:rgba(255,255,255,.92);
  cursor:pointer;font-size:20px;line-height:1;color:var(--text);z-index:2}
.gal__nav:hover{background:#fff}
.gal__prev{left:10px}.gal__next{right:10px}
.gal__counter{position:absolute;right:12px;bottom:10px;background:rgba(255,255,255,.92);
  border:1px solid var(--line);border-radius:12px;padding:2px 10px;font-size:12px;color:var(--muted)}
.gal__thumbs{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
.gal__thumb{width:64px;height:64px;border:1px solid var(--line);border-radius:8px;
  background:var(--bg-soft);cursor:pointer;padding:3px;overflow:hidden}
.gal__thumb img{width:100%;height:100%;object-fit:contain}
.gal__thumb.active{border-color:var(--text)}

/* ---------- лайтбокс ---------- */
.lightbox{position:fixed;inset:0;background:rgba(10,10,10,.92);display:none;
  align-items:center;justify-content:center;z-index:200}
.lightbox.open{display:flex}
.lightbox img{max-width:94vw;max-height:94vh;object-fit:contain}
.lightbox__close{position:absolute;top:14px;right:14px;width:42px;height:42px;
  border-radius:50%;border:1px solid rgba(255,255,255,.35);background:rgba(0,0,0,.4);
  color:#fff;font-size:20px;cursor:pointer}
.lightbox__nav{position:absolute;top:50%;transform:translateY(-50%);width:46px;height:46px;
  border-radius:50%;border:1px solid rgba(255,255,255,.35);background:rgba(0,0,0,.4);
  color:#fff;font-size:24px;cursor:pointer}
.lightbox__prev{left:16px}.lightbox__next{right:16px}
.lightbox__counter{position:absolute;bottom:14px;left:50%;transform:translateX(-50%);
  color:#ddd;font-size:13px}

/* ---------- подбор ---------- */
.pick-line{display:flex;align-items:center;gap:14px;padding:14px 0;border-bottom:1px solid var(--line)}
.pick-line__thumb{width:72px;height:72px;flex-shrink:0;overflow:hidden;
  border-radius:8px;background:var(--bg-soft)}
.pick-line__thumb img{width:100%;height:100%;object-fit:contain}
.pick-line__body{flex:1;min-width:0}
.pick-line__name{font-weight:600;font-size:15px}
.pick-line__meta{color:var(--muted);font-size:13px;margin-top:2px}
.pick-line .select-native{padding:6px 32px 6px 12px;font-size:13px;border-radius:18px;margin-top:6px}
.qty{display:flex;align-items:center;gap:8px;flex-shrink:0}
.qty button{width:28px;height:28px;border-radius:50%;border:1px solid #ddd;background:#fff;cursor:pointer}
.qty button:hover{background:var(--bg-soft)}
.pick-line__rm{border:0;background:none;color:#999;cursor:pointer;font-size:15px;padding:4px}
.pick-line__rm:hover{color:var(--accent)}
.pick-total{display:flex;justify-content:space-between;font-weight:700;font-size:17px;margin:18px 0}
.pick-hint{color:var(--muted);font-size:13px;line-height:1.5;margin-bottom:16px}
.pick-number{font-size:30px;font-weight:800;letter-spacing:1px;background:var(--bg-soft);
  border-radius:12px;padding:16px 18px;text-align:center;margin:16px 0 6px}
.copy-btn{border:0;background:none;color:var(--accent);cursor:pointer;text-decoration:underline;
  font-size:13px;margin:0 auto 14px;display:block}
.modal__actions{display:flex;flex-direction:column;gap:10px;margin-top:14px}
.link-line{text-align:center;font-size:15px;margin-top:12px}
.link-line a{color:var(--text);font-weight:700;text-decoration:none}
.link-line a:hover{text-decoration:underline}

/* ---------- плавающие кнопки у внешних краёв окна ---------- */
.fab-rail{position:fixed;left:28px;right:28px;bottom:28px;z-index:60;
  display:flex;justify-content:space-between;align-items:flex-end;pointer-events:none}
.fab-rail>*{pointer-events:auto}
.pick-fab{display:flex;align-items:center;gap:10px;
  padding:14px 24px;border-radius:32px;background:var(--text);color:#fff;
  border:2px solid var(--text);cursor:pointer;
  box-shadow:0 10px 26px rgba(0,0,0,.25);font-size:15px;transition:.25s}
.pick-fab:hover{opacity:.9}
.pick-fab.has{background:#fff;color:var(--text);border-color:var(--accent);
  animation:pickPulse 1.6s ease-in-out infinite}
.pick-fab.has .pick-fab__cnt{background:var(--accent)}
@keyframes pickPulse{
  0%,100%{box-shadow:0 10px 26px rgba(0,0,0,.25)}
  50%{box-shadow:0 10px 30px rgba(0,177,176,.55)}
}
.pick-fab__cnt{background:var(--accent);border-radius:12px;padding:1px 9px;font-size:13px}
.fab-left{display:flex;flex-direction:column;gap:10px}
.fab{width:50px;height:50px;border-radius:50%;border:0;cursor:pointer;
  background:var(--text);color:#fff;font-size:20px;line-height:1;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 6px 18px rgba(0,0,0,.2);transition:.2s}
.fab:hover{transform:translateY(-2px);box-shadow:0 10px 24px rgba(0,0,0,.3)}
.fab--top{opacity:0;pointer-events:none;transition:opacity .25s,transform .2s}
.fab--top.show{opacity:1;pointer-events:auto}

/* ---------- тост ---------- */
.toast{position:fixed;left:50%;bottom:96px;transform:translateX(-50%) translateY(16px);
  background:var(--text);color:#fff;padding:11px 22px;border-radius:24px;font-size:14px;
  opacity:0;pointer-events:none;transition:.25s;z-index:80}
.toast.show{opacity:1;transform:translateX(-50%)}

/* ---------- подвал ---------- */
.footer{border-top:1px solid var(--line);padding:30px 0;display:flex;gap:24px;
  justify-content:space-between;flex-wrap:wrap;color:var(--muted);font-size:14px}

/* ---------- адаптив (общий) ---------- */
@media(max-width:860px){
  .page{padding:0 16px}
  .header{grid-template-columns:repeat(2,minmax(0,1fr))}
  .header__contacts{grid-column:1/-1}
  .brandnav{flex-wrap:wrap;justify-content:center}
  .modal__grid{grid-template-columns:1fr;padding:20px}
  .modal__img{min-height:220px}
  .fab-rail{left:16px;right:16px;bottom:16px;width:auto}
}
/* ================================================================
   ЧАСТЬ 3: новый товар, выгрузка, картинки, запуск
================================================================ */

/* ---------- новый товар (пустой или копией существующего) ---------- */
 $("btnNew").addEventListener("click", ()=>{
  const arr = sectionArr(sec)||[];
  $("newBox").innerHTML = `
  <button class="x" data-close="maskNew">✕</button>
  <h3>Новый товар</h3>
  <label class="field">Скопировать из существующего (необязательно)
    <select id="nwCopy">
      <option value="">— пустой товар —</option>
      ${arr.map(p=>`<option value="${p.id}">${esc(p.name)} (арт. ${esc(p.article||"—")})</option>`).join("")}
    </select></label>
  <div class="note">При копировании переносятся все поля (цвета, цены, фото, описание) —
  дальше поправите в карточке. ID будет новый.</div>
  <div class="grid2" style="margin-top:10px">
    <label class="field">Название *<input id="nwName"></label>
    <label class="field">Артикул *<input id="nwArt"></label>
    <label class="field">Бренд<input id="nwBrand"></label>
    <label class="field">Тип<input id="nwType"></label>
    <label class="field">Серия<input id="nwSeries"></label>
    <label class="field">Цена<input id="nwPrice"></label>
  </div>
  <div style="display:flex;gap:12px;margin-top:14px;justify-content:flex-end">
    <button class="btn" data-close="maskNew">Отмена</button>
    <button class="btn btn--acc" id="nwCreate">Создать и открыть карточку</button>
  </div>`;
  openMask("maskNew");
});

 $("newBox").addEventListener("click", e=>{
  const cls = e.target.closest("[data-close]");
  if (cls){ closeMask(cls.dataset.close); return; }
  if (e.target.closest("#nwCreate")){
    const arr = sectionArr(sec);
    const name = $("nwName").value.trim();
    const art  = $("nwArt").value.trim();
    if (!name || !art){ msg("Название и артикул обязательны.", true); return; }
    const copyId = $("nwCopy").value;
    let p;
    if (copyId){
      const src = arr.find(x=>x.id==+copyId);
      if (src){
        p = JSON.parse(JSON.stringify(src));
        p.name = name; p.article = art;
      }
    }
    if (!p) p = { name, article:art };
    p.id = Math.max(0, ...arr.map(x=>+x.id||0)) + 1;
    const put = (k, v)=>{ const t=v.trim(); if (t) p[k]=t; };
    put("brand",$("nwBrand").value); put("type",$("nwType").value);
    put("collection",$("nwSeries").value);
    const pr = num($("nwPrice").value);
    if (pr!=null) p.price = pr;
    arr.push(p);
    dirty.add(p.id);
    closeMask("maskNew");
    renderAll();
    openEdit(p.id);
    msg(`Товар создан (id ${p.id}). Заполните карточку и выгрузите файл.`);
  }
});

/* ---------- выгрузка data-файла ---------- */
function serializeSection(key, arr){
  const body = arr.map(p=>
    "  " + JSON.stringify(p, null, 1).replace(/\n/g, "\n  ")
  ).join(",\n");
  return `/* ============================================================
   data/${key}.js — сгенерировано tools/admin.html ${today()}
   Правки вручную: только мелкие. Основной путь — админка.
   ============================================================ */
window.CATALOG_SECTIONS = window.CATALOG_SECTIONS || {};
window.CATALOG_SECTIONS[${JSON.stringify(key)}] = [
 ${body}
];
`;
}

function exportSection(){
  const arr = sectionArr(sec);
  if (!arr){ msg("Нет данных раздела для выгрузки.", true); return; }

  /* страховки перед выгрузкой */
  const ids = arr.map(p=>p.id);
  const dup = ids.filter((v,i)=>ids.indexOf(v)!==i);
  const noArt = arr.filter(p=>!p.article);
  if (dup.length && !confirm(`Внимание: дублируются ID товаров: ${[...new Set(dup)].join(", ")}.\nПродолжить выгрузку?`)) return;
  if (noArt.length && !confirm(`Внимание: у ${noArt.length} товаров пустой артикул (id: ${noArt.map(p=>p.id).join(", ")}).\nБез артикула не работают «Подбор» и обновление цен из прайса. Продолжить?`)) return;

  if ($("doBackup").checked && SNAP[sec]){
    const bak = `/* БЭКАП раздела ${sec} на момент открытия админки ${today()} */\n` +
      `window.CATALOG_SECTIONS = window.CATALOG_SECTIONS || {};\n` +
      `window.CATALOG_SECTIONS[${JSON.stringify(sec)}] = ` +
      JSON.stringify(SNAP[sec], null, 1) + `;\n`;
    download(`${sec}-backup-${today()}.js`, bak, "text/javascript");
  }
  download(`${sec}.js`, serializeSection(sec, arr), "text/javascript");
  msg(`Выгружено: ${sec}.js (${arr.length} товаров)${$("doBackup").checked?` + бэкап ${sec}-backup-${today()}.js`:""}.
Положите файл в папку data/ с заменой и обновите сайт (Ctrl+F5).`);
}
 $("btnExport").addEventListener("click", exportSection);

/* ---------- список картинок для скачивания ---------- */
function collectImageURLs(){
  const arr = sectionArr(sec)||[];
  const out = [];
  const isExt = u => /^(https?:)?\/\//i.test(u);
  arr.forEach(p=>{
    const art = String(p.article||("id"+p.id)).replace(/\//g,"-");
    const seen = new Set();
    const add = (u, name)=>{
      if (!u || !isExt(u) || seen.has(u)) return;
      seen.add(u);
      out.push(`${u}\timages/${sec}/${name}`);
    };
    if (p.images) Object.keys(p.images).forEach((c,i)=>{
      add(p.images[c], art + (i===0?"":"_"+(i+1)) + ".jpg");
    });
    (p.gallery||[]).forEach((g,n)=>{ add(g, art+"_"+(n+2)+".jpg"); });
    if (p.fallback) add(p.fallback, "common-"+art+".jpg");
    if (p.manual) add(p.manual, "docs/"+art+".pdf");
  });
  return out;
}

 $("btnImgs").addEventListener("click", ()=>{
  const rows = collectImageURLs();
  if (!rows.length){ msg("Внешних URL картинок в разделе не найдено.", true); return; }
  const txt = "# Список для массового скачивания (URL → локальный путь)\n" +
    "# Скачайте загрузчиком (или PowerShell) и положите файлы по указанным путям в папке сайта.\n" +
    rows.join("\n") + "\n";
  download(`${sec}-images-${today()}.txt`, txt, "text/plain", true);
  msg(`Список сохранён: ${rows.length} файлов. После скачивания замените в карточках URL на локальные пути — массово это сделает импорт (кнопка «Вставить таблицу»).`);
});

/* ---------- защита от случайного закрытия ---------- */
window.addEventListener("beforeunload", e=>{
  if (dirty.size){ e.preventDefault(); e.returnValue = ""; }
});

/* ---------- запуск ---------- */
renderSecs();
(function(){
  const first = SECTIONS_META.find(m=>sectionArr(m.key));
  if (first) chooseSection(first.key);
  else msg("Ни один data-файл не загрузился. Проверьте пути в конце admin.html и откройте страницу через F5.", true);
})();

/* ================================================================
   ЧАСТЬ 4: массовый импорт таблицы (Вставить таблицу)
================================================================ */
const COLOR_NORM = {
  "хром":"хром глянцевый","хром глянец":"хром глянцевый",
  "черный":"черный матовый","чёрный":"черный матовый","черный глянец":"черный глянцевый",
  "белый":"белый матовый","белый глянец":"белый глянцевый",
  "латунь":"латунь металлик","никель":"никель металлик",
  "сатин":"сатинированная сталь","нержавеющая сталь":"сатинированная сталь",
  "нерж. сталь":"сатинированная сталь"
};
function normColor(c){
  const t = String(c||"").trim(); if (!t) return "";
  const k = t.toLowerCase();
  if (COLOR_NORM[k]) return COLOR_NORM[k];
  const hit = Object.keys(COLORS).find(x=>x.toLowerCase()===k);
  return hit || t;
}

function parseTSV(text){
  const rows=[]; let row=[], cell="", q=false;
  for (let i=0;i<text.length;i++){
    const ch=text[i];
    if (q){
      if (ch==='"'){ if (text[i+1]==='"'){ cell+='"'; i++; } else q=false; }
      else cell+=ch;
    } else {
      if (ch==='"') q=true;
      else if (ch==='\t'){ row.push(cell); cell=""; }
      else if (ch==='\n'){ row.push(cell); rows.push(row); row=[]; cell=""; }
      else if (ch!=='\r') cell+=ch;
    }
  }
  if (cell!=="" || row.length){ row.push(cell); rows.push(row); }
  return rows;
}

const IMP_POS = {article:0,name:1,type:2,collection:3,ph1:4,ph2:5,ph3:6,ph4:7,ph5:8,
  drawing:9,contents:10,manual:11,price:12,chars:13,schema:14,mount:15,purpose:16,
  control:17,color:18,material:19,spout:20,warranty:21};

function detectColumns(header){
  const col = {};
  header.forEach((h,i)=>{
    const t = String(h).trim().toLowerCase();
    if (!t) return;
    if (col.article==null && /^артикул/.test(t)) col.article=i;
    else if (col.name==null && /^(название|наименован)/.test(t)) col.name=i;
    else if (col.control==null && /управлен/.test(t)) col.control=i;
    else if (col.type==null && /^тип\b/.test(t)) col.type=i;
    else if (col.collection==null && /^(коллекция|серия)/.test(t)) col.collection=i;
    else if (/^фото\s*1/.test(t)) col.ph1=i;
    else if (/^фото\s*2/.test(t)) col.ph2=i;
    else if (/^фото\s*3/.test(t)) col.ph3=i;
    else if (/^фото\s*4/.test(t)) col.ph4=i;
    else if (/^фото\s*5/.test(t)) col.ph5=i;
    else if (col.drawing==null && /^черт/.test(t)) col.drawing=i;
    else if (col.contents==null && /^компл/.test(t)) col.contents=i;
    else if (col.manual==null && /инструкц/.test(t)) col.manual=i;
    else if (col.price==null && /(м\.?\s*р\.?\s*\.?\s*ц|мрц|цена)/.test(t)) col.price=i;
    else if (col.warranty==null && /гаранти/.test(t)) col.warranty=i;
    else if (col.chars==null && /^характеристик/.test(t)) col.chars=i;
    else if (col.schema==null && /^схема/.test(t)) col.schema=i;
    else if (col.mount==null && /^монтаж/.test(t)) col.mount=i;
    else if (col.purpose==null && /^назначен/.test(t)) col.purpose=i;
    else if (col.color==null && /^цвет/.test(t)) col.color=i;
    else if (col.material==null && /материал/.test(t)) col.material=i;
    else if (col.spout==null && /излив/.test(t)) col.spout=i;
  });
  return col;
}

/* кнопка импорта — вставляется на панель перед «Вставить прайс» */
(function(){
  const b=document.createElement("button");
  b.className="btn"; b.id="btnImport"; b.textContent="⬆ Вставить таблицу (импорт)";
  $("btnPrice").parentNode.insertBefore(b, $("btnPrice"));
  b.addEventListener("click", openImport);
})();

const maskImport=document.createElement("div");
maskImport.className="mask";
maskImport.innerHTML='<div class="box" id="importBox"></div>';
document.body.appendChild(maskImport);
maskImport.addEventListener("click", e=>{ if (e.target===maskImport) maskImport.classList.remove("on"); });

let IMPORT_IMGS = [];   /* строки "URL ⇥ локальный путь" */

function openImport(){
  $("importBox").innerHTML = `
  <button class="x" id="impX">✕</button>
  <h3>Импорт таблицы ассортимента</h3>
  <div class="note">Вставьте таблицу из Excel <b>целиком</b> (первой строкой — заголовки).
  Одна строка = один товар-вариант со своим артикулом. URL картинок автоматически
  переводятся в локальные пути <code>images/${sec}/…</code>; список для скачивания — кнопкой ниже.
  Импорт идёт в текущий раздел: <b>${sec}</b>.</div>
  <div class="grid2">
    <label class="field">Бренд для всех строк<input id="impBrand" value="TiMO"></label>
    <label class="flag" style="align-self:end"><input type="checkbox" id="impOverwrite">
      перезаписывать существующие (совпадение по артикулу)</label>
  </div>
  <textarea id="impPaste" style="width:100%;min-height:150px;margin-top:8px;border:1px solid var(--line);border-radius:8px;padding:8px;font:inherit"
    placeholder="Артикул&#9;Название&#9;Тип&#9;Коллекция&#9;…"></textarea>
  <div id="impPrev" style="max-height:300px;overflow:auto;margin-top:10px"></div>
  <div style="display:flex;gap:12px;margin-top:12px;align-items:center;flex-wrap:wrap">
    <button class="btn btn--acc" id="impApply">Импортировать</button>
    <button class="btn" id="impImgs">⬇ Список картинок (txt)</button>
    <span class="sub" id="impStat"></span>
    <span style="flex:1"></span>
    <button class="btn" id="impClose">Закрыть</button>
  </div>`;
  IMPORT_IMGS = [];
  $("impX").addEventListener("click", ()=>maskImport.classList.remove("on"));
  $("impClose").addEventListener("click", ()=>maskImport.classList.remove("on"));
  $("impPaste").addEventListener("input", impPreview);
  $("impImgs").addEventListener("click", ()=>{
    if (!IMPORT_IMGS.length){ msg("Сначала вставьте таблицу с URL картинок.", true); return; }
    download(`${sec}-import-images-${today()}.txt`,
      "# URL → локальный путь (от корня сайта)\n"+IMPORT_IMGS.join("\n")+"\n",
      "text/plain", true);
  });
  $("impApply").addEventListener("click", impApply);
  impPreview();
}

function impBuild(){
  const rows = parseTSV($("impPaste").value);
  const hasHeader = rows.length && /артикул/i.test(rows[0].join("\t"));
  const col = hasHeader ? detectColumns(rows[0]) : IMP_POS;
  const body = rows.slice(hasHeader ? 1 : 0);
  const brand = $("impBrand").value.trim();
  const items = [], stat = {rows:body.length, noArt:0, noPrice:0, dups:0};
  const seen = new Set();

  body.forEach(r=>{
    const cell = k => col[k]!=null ? String(r[col[k]]||"").trim() : "";
    const art  = cell("article");
    const price= num(cell("price"));
    if (!art){ stat.noArt++; return; }
    if (price==null) stat.noPrice++;
    if (seen.has(art)){ stat.dups++; return; }
    seen.add(art);

    const safe = art.replace(/\//g,"-").replace(/[\\:*?"<>|]/g,"").trim();
    const cRaw = cell("color");
    const c    = normColor(cRaw);
    const it   = { name:"", article:art };
    if (brand) it.brand = brand;

    /* чистое имя: тип + серия; иначе — исходное название без артикула и цвета */
    const type = cell("type"), coll = cell("collection");
    let nm = [type, coll].filter(Boolean).join(" ").trim();
    if (!nm){
      nm = cell("name").replace(new RegExp("\\s*"+art.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"i"),"");
      if (cRaw) nm = nm.replace(new RegExp("[,\\s]*"+cRaw.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"i"),"");
      nm = nm.replace(/\s{2,}/g," ").trim();
    }
    it.name = nm || art;
    if (type) it.type = type;
    if (coll) it.collection = coll;
    if (price!=null) it.price = price;

    /* фото: URL → локальные имена */
    const gal = [];
    const img = (url, local)=>{
      if (!url) return;
      IMPORT_IMGS.push(`${url}\timages/${sec}/${local}`);
      return local;
    };
    const main = img(cell("ph1"), `${safe}.jpg`);
    if (c && main){ it.colors=[c]; it.articles={[c]:art}; it.images={[c]:main}; }
    else if (main){ it.images={ "": main }; it.colors=[]; }
    [cell("ph2"),cell("ph3"),cell("ph4"),cell("ph5")].forEach((u,i)=>{
      const l = img(u, `${safe}_${i+2}.jpg`); if (l) gal.push(l);
    });
    const dr = img(cell("drawing"), `${safe}_shema.jpg`); if (dr) gal.push(dr);
    const sc = img(cell("schema"),  `${safe}_connect.jpg`); if (sc) gal.push(sc);
    if (gal.length) it.gallery = gal;

    const man = cell("manual");
    if (man){ IMPORT_IMGS.push(`${man}\timages/${sec}/docs/${safe}.pdf`);
              it.manual = `../images/${sec}/docs/${safe}.pdf`; }

    const cont = cell("contents");
    if (cont){ const L = cont.split(/\s*\n\s*/).filter(Boolean); if (L.length) it.contents = L; }

    if (cell("mount"))    it.mount    = cell("mount");
    if (cell("purpose"))  it.purpose  = cell("purpose");
    if (cell("control"))  it.control  = cell("control");
    const mat = cell("material").split(/[,;]+/).map(s=>s.trim()).filter(Boolean);
    if (mat.length) it.material = mat.length>1 ? mat : mat[0];
    const sp = cell("spout").replace(/[^\d.]/g,"");
    if (sp) it.spout = sp;

    let w = cell("warranty");
    if (!w && col.chars!=null){
      const m = String(r[col.chars]||"").match(/гаранти[^\n]*/i);
      if (m) w = m[0];
    }
    if (w) it.description = "Гарантия: " + w.replace(/^гарантия\s*/i,"").replace(/\.$/,"") + ".";

    items.push(it);
  });
  return {items, stat};
}

function impPreview(){
  const {items, stat} = impBuild();
  const arr = sectionArr(sec)||[];
  const ex = new Set(arr.map(p=>String(p.article).trim()));
  const dup = items.filter(i=>ex.has(i.article)).length;
  $("impStat").textContent =
    `строк: ${stat.rows} · к добавлению: ${items.length-dup} · обновится: ${dup} · без артикула: ${stat.noArt} · без цены: ${stat.noPrice} · дублей в таблице: ${stat.dups}`;
  $("impPrev").innerHTML = items.length ? `
    <table class="priceRpt"><thead><tr>
      <th>Артикул</th><th>Название</th><th>Цвет</th><th class="num">Цена</th><th>Фото</th><th>Статус</th>
    </tr></thead><tbody>
    ${items.slice(0,80).map(i=>`
      <tr><td>${esc(i.article)}</td><td>${esc(i.name)}</td>
      <td>${esc((i.colors&&i.colors[0])||"—")}</td>
      <td class="num">${i.price!=null?i.price.toLocaleString("ru-RU"):'<span class="miss">—</span>'}</td>
      <td>${(i.images?1:0)+((i.gallery||[]).length)}</td>
      <td class="${ex.has(i.article)?"miss":""}">${ex.has(i.article)?"обновление":"новый"}</td></tr>`).join("")}
    </tbody></table>${items.length>80?`<div class="sub">…первые 80 из ${items.length}</div>`:""}`
    : '<div class="sub">Вставьте таблицу — здесь появится предпросмотр.</div>';
}

function impApply(){
  const {items} = impBuild();
  if (!items.length){ msg("Нет распознанных строк — проверьте вставленную таблицу.", true); return; }
  if (!confirm(`Импортировать ${items.length} позиций в раздел «${sec}»?`)) return;
  const arr = sectionArr(sec) || (window.CATALOG_SECTIONS[sec] = []);
  const byArt = new Map(arr.map(p=>[String(p.article).trim(), p]));
  const over = $("impOverwrite").checked;
  let added=0, updated=0, skipped=0;
  items.forEach(it=>{
    const ex = byArt.get(it.article);
    if (ex){
      if (over){ Object.assign(ex, it, {id:ex.id}); dirty.add(ex.id); updated++; }
      else skipped++;
      return;
    }
    it.id = arr.reduce((m,p)=>Math.max(m, +p.id||0), 3000) + 1;
    arr.push(it); byArt.set(it.article, it); dirty.add(it.id); added++;
  });
  maskImport.classList.remove("on");
  chooseSection(sec);
  msg(`Импорт завершён: добавлено ${added}, обновлено ${updated}${skipped?`, пропущено дублей: ${skipped}`:""}. Проверьте таблицу → «Выгрузить data-файл раздела» → положите файл в data/ → добавьте <script> строку на страницы.`);
}
</script>
</body>
</html>