/* ============================================================
   Подбор техники — render, state, navigation, submit
   ============================================================ */

const Podbor = (function () {
  const STORAGE_KEY = "zov-podbor-v4";
  const STEPS = ["intro", "categories", "detail", "brand", "budget", "strategy", "infra", "summary"];
  const STEP_LABELS = ["Старт", "Категории", "Параметры", "Бренд", "Бюджет", "Стратегия", "Инфра", "Итог"];

  // Внутренний sub-state для шага «detail»: 'menu' | 'cat:<key>'
  let detailView = "menu";

  let state = loadState();
  let root = null;
  let currentStep = "intro";

  function loadState() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (raw) {
        const parsed = JSON.parse(raw);
        // Мерж с дефолтами для совместимости с новыми полями
        return { ...defaultState(), ...parsed };
      }
    } catch (e) {}
    return defaultState();
  }

  function defaultState() {
    return {
      client_name: "",
      client_phone: "",
      address: "",
      categories: [],          // ['fridge','hob',...]
      per_cat: {},             // { fridge: { answers: {install:'built_in',...}, notes: '', _step: 0 } }
      brand_strategy: "",      // 'ai' | 'single' | 'different'
      single_brand: "",        // key из PODBOR_SINGLE_BRAND_OPTIONS, если brand_strategy === 'single'
      brands: {},              // если brand_strategy === 'different' — { fridge: {Bosch:'preferred'|'acceptable'|'avoid'} }
      budget_preset: "",       // 'luxe'|'premium'|'middle'|'budget'|'exact'
      price_ranges: {},        // только если budget_preset === 'exact'
      pick_strategies: [],     // ['reviews','balance','tech',...] — multi
      model_count: "5",        // '3' | '5' | '7' — сколько моделей в каждой категории
      infra: { stove: "", vent: "" },
      notes: "",
    };
  }

  function saveState() {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch (e) {}
  }

  function update(patch) {
    state = { ...state, ...patch };
    saveState();
  }

  /* ===================== Render entry ===================== */

  function mount(container) {
    root = container;
    document.body.classList.remove("has-bottom-nav");
    const oldNav = document.getElementById("bottom-nav");
    if (oldNav) oldNav.remove();
    render();
  }

  function go(step) {
    if (!STEPS.includes(step)) return;
    currentStep = step;
    detailView = "menu"; // на любой переход detail возвращается в меню
    render();
    window.scrollTo({ top: 0, behavior: "smooth" });
    haptic && haptic("impact");
  }

  function render() {
    if (!root) return;
    root.innerHTML = "";
    root.appendChild(renderHeader());
    root.appendChild(renderProgress());
    const strip = renderCategoryStrip();
    if (strip) root.appendChild(strip);
    const screen = el(`<div class="podbor-screen"></div>`);
    root.appendChild(screen);

    switch (currentStep) {
      case "intro":      screen.appendChild(renderIntro()); break;
      case "categories": screen.appendChild(renderCategories()); break;
      case "detail":     screen.appendChild(renderDetail()); break;
      case "brand":      screen.appendChild(renderBrand()); break;
      case "budget":     screen.appendChild(renderBudget()); break;
      case "strategy":   screen.appendChild(renderStrategy()); break;
      case "infra":      screen.appendChild(renderInfra()); break;
      case "summary":    screen.appendChild(renderSummary()); break;
    }
  }

  /* ===================== Header & progress ===================== */

  function renderHeader() {
    const h = el(`
      <header class="podbor-header">
        <button class="podbor-back" aria-label="Назад">${ICONS.arrow_left}</button>
        <div class="podbor-title">Подбор техники</div>
        <div style="width:28px"></div>
      </header>
    `);
    h.querySelector(".podbor-back").addEventListener("click", () => {
      const idx = STEPS.indexOf(currentStep);
      if (idx <= 0) {
        // Выход из подбора в главный экран кабинета
        location.hash = "";
        location.reload();
      } else {
        go(STEPS[idx - 1]);
      }
    });
    return h;
  }

  /* Лента выбранных категорий — видна на шагах после "categories" */
  function renderCategoryStrip() {
    if (!state.categories.length) return null;
    if (currentStep === "intro" || currentStep === "categories") return null;
    // Активная категория — если внутри wizard'а одной из них
    let activeCat = null;
    if (currentStep === "detail" && detailView.startsWith("cat:")) {
      activeCat = detailView.slice(4);
    }
    const chips = state.categories.map(catKey => {
      const cat = PODBOR_CATEGORIES.find(c => c.key === catKey);
      const filled = isCategoryFilled(catKey);
      const isActive = catKey === activeCat;
      return `
        <button class="cat-strip-chip${isActive ? " active" : ""}${filled ? " filled" : ""}" data-cat="${catKey}">
          <span class="cat-strip-icon">${ICONS[cat.icon] || ""}</span>
          <span class="cat-strip-label">${cat.label}</span>
          ${filled ? `<span class="cat-strip-tick">${ICONS.check}</span>` : ""}
        </button>
      `;
    }).join("");
    const node = el(`<div class="cat-strip">${chips}</div>`);
    node.querySelectorAll(".cat-strip-chip").forEach(btn => {
      btn.addEventListener("click", () => {
        const cat = btn.dataset.cat;
        currentStep = "detail";
        detailView = "cat:" + cat;
        render();
        window.scrollTo({ top: 0, behavior: "smooth" });
        haptic && haptic("impact");
      });
    });
    return node;
  }

  function renderProgress() {
    const idx = STEPS.indexOf(currentStep);
    const total = STEPS.length;
    const pct = Math.round(((idx + 1) / total) * 100);
    return el(`
      <div class="podbor-progress">
        <div class="podbor-progress-bar"><div class="bar" style="width:${pct}%"></div></div>
        <div class="podbor-progress-meta">
          <span>${STEP_LABELS[idx] || ""}</span><span class="num">${idx + 1}/${total}</span>
        </div>
      </div>
    `);
  }

  /* ===================== Step: intro ===================== */

  function renderIntro() {
    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Подбор техники<br><span class="accent">для клиента</span></h2>
        <p class="lede">7 коротких шагов. Категории, ценовой коридор, инфраструктура и предпочтения. AI соберёт предложение.</p>

        <div class="form-row">
          <label class="field">
            <span class="field-label">Клиент</span>
            <input type="text" data-bind="client_name" value="${state.client_name || ""}" placeholder="Например: А. Пестова">
            <span class="field-error" id="nameError"></span>
          </label>
        </div>
        <div class="form-row">
          <label class="field">
            <span class="field-label">Телефон</span>
            <input type="tel" data-bind="client_phone" value="${state.client_phone || ""}" placeholder="+7 900 123-45-67">
            <span class="field-hint">Формат: +7, 8, или 10 цифр начиная с 9</span>
            <span class="field-error" id="phoneError"></span>
          </label>
        </div>

        <div class="podbor-cta-row">
          <button class="btn-primary" id="introNext">Начать</button>
        </div>
      </section>
    `);
    bindInputs(node);

    const phoneInput = node.querySelector("input[data-bind='client_phone']");
    const phoneError = node.querySelector("#phoneError");
    const nameInput = node.querySelector("input[data-bind='client_name']");
    const nameError = node.querySelector("#nameError");

    // Валидация на blur — мягкие подсказки
    phoneInput.addEventListener("blur", () => {
      const v = phoneInput.value.trim();
      if (v && !isValidPhone(v)) {
        phoneError.textContent = "Похоже на неполный номер. Нужно 11 цифр (или 10 с цифры 9)";
      } else {
        phoneError.textContent = "";
      }
    });

    node.querySelector("#introNext").addEventListener("click", () => {
      // Имя
      const name = (state.client_name || "").trim();
      if (!name) {
        nameError.textContent = "Укажите имя клиента";
        nameInput.focus();
        haptic && haptic("warning");
        return;
      } else {
        nameError.textContent = "";
      }

      // Телефон
      const phone = (state.client_phone || "").trim();
      if (!phone) {
        phoneError.textContent = "Укажите телефон клиента";
        phoneInput.focus();
        haptic && haptic("warning");
        return;
      }
      if (!isValidPhone(phone)) {
        phoneError.textContent = "Неверный формат. Пример: +7 900 123-45-67 или 89001234567";
        phoneInput.focus();
        haptic && haptic("warning");
        return;
      }
      // Нормализуем перед переходом
      const normalized = normalizePhone(phone);
      if (normalized !== phone) {
        update({ client_phone: normalized });
      }
      go("categories");
    });

    return node;
  }

  /* ===================== Step: categories ===================== */

  function renderCategories() {
    const grid = PODBOR_CATEGORIES.map(c => `
      <button class="cat-card${state.categories.includes(c.key) ? " active" : ""}" data-cat="${c.key}">
        <div class="cat-icon">${ICONS[c.icon] || ""}</div>
        <div class="cat-label">${c.label}</div>
        ${state.categories.includes(c.key) ? `<div class="cat-check">${ICONS.check}</div>` : ""}
      </button>
    `).join("");

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Какую технику<br><span class="accent">подбираем?</span></h2>
        <p class="lede">Выберите все категории, что нужно подобрать клиенту.</p>
        <div class="cat-grid">${grid}</div>
        <div class="podbor-cta-row">
          <button class="btn-secondary" data-go="intro">Назад</button>
          <button class="btn-primary" data-go="detail">Дальше</button>
        </div>
      </section>
    `);
    node.querySelectorAll(".cat-card").forEach(card => {
      card.addEventListener("click", () => {
        const cat = card.dataset.cat;
        const next = state.categories.includes(cat)
          ? state.categories.filter(x => x !== cat)
          : [...state.categories, cat];
        update({ categories: next });
        render();
      });
    });
    bindNav(node);
    return node;
  }

  /* ===================== Step: detail — menu + per-category sub-screen ===================== */

  function isCategoryFilled(catKey) {
    const cs = state.per_cat[catKey];
    if (!cs) return false;
    const config = PODBOR_PARAMS[catKey];
    if (!config) return false;
    // Новая схема: все активные single-шаги должны иметь ответ. Multi (features) — необязательно.
    if (config.steps) {
      const ans = cs.answers || {};
      return config.steps.every(step => {
        if (!isStepActive(step, ans)) return true; // неактивный пропускаем
        if (step.type === "multi") return true;     // multi необязателен
        return !!ans[step.key];
      });
    }
    // Старая схема
    if (!cs.params) return false;
    const params = config.primary || [];
    return params.every(p => cs.params[p.key]);
  }

  function renderDetail() {
    if (!state.categories.length) {
      return el(`
        <section class="podbor-step">
          <div class="empty">Сначала выберите категории.</div>
          <div class="podbor-cta-row">
            <button class="btn-secondary" data-go="categories">Назад</button>
          </div>
        </section>
      `);
    }
    if (detailView !== "menu" && detailView.startsWith("cat:")) {
      const catKey = detailView.slice(4);
      const config = PODBOR_PARAMS[catKey];
      // Новая иерархическая схема → wizard. Старая → legacy-форма.
      if (config?.steps) return renderCategoryWizard(catKey);
      return renderCategoryDetail(catKey);
    }
    return renderDetailMenu();
  }

  function renderDetailMenu() {
    const cards = state.categories.map(catKey => {
      const cat = PODBOR_CATEGORIES.find(x => x.key === catKey);
      const filled = isCategoryFilled(catKey);
      const summary = filled ? buildPerCatSummary(catKey) : "Заполнить параметры";
      return `
        <button class="detail-card${filled ? " done" : ""}" data-cat="${catKey}">
          <div class="detail-icon">${ICONS[cat.icon] || ""}</div>
          <div class="detail-text">
            <div class="detail-name">${cat.label}</div>
            <div class="detail-sum">${summary}</div>
          </div>
          <div class="detail-status">${filled ? ICONS.check : ICONS.chevron}</div>
        </button>
      `;
    }).join("");

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Параметры<br><span class="accent">по категориям</span></h2>
        <p class="lede">Только главное: тип, размер, цвет. Технические фичи — в «Подробнее ↓», по желанию.</p>
        <div class="detail-list">${cards}</div>
        <div class="podbor-cta-row">
          <button class="btn-secondary" data-go="categories">Назад</button>
          <button class="btn-primary" data-go="brand">Дальше</button>
        </div>
      </section>
    `);
    node.querySelectorAll(".detail-card").forEach(c => {
      c.addEventListener("click", () => {
        detailView = "cat:" + c.dataset.cat;
        render();
      });
    });
    bindNav(node);
    return node;
  }

  function buildPerCatSummary(catKey) {
    const cs = state.per_cat[catKey];
    if (!cs) return "—";
    const config = PODBOR_PARAMS[catKey];
    // Новая схема
    if (config?.steps) {
      const ans = cs.answers || {};
      const labels = [];
      for (const step of config.steps) {
        if (step.type === "multi") continue;
        if (!isStepActive(step, ans)) continue;
        const val = ans[step.key];
        if (!val) continue;
        const opts = resolveStepOptions(step, ans);
        const opt = opts.find(o => o.key === val);
        if (opt) labels.push(opt.label);
      }
      return labels.join(" · ") || "—";
    }
    // Старая схема
    if (!cs.params) return "—";
    const params = config?.primary || [];
    const labels = params
      .map(p => {
        const opt = p.options.find(o => o.key === cs.params[p.key]);
        return opt ? opt.label : null;
      })
      .filter(Boolean);
    return labels.join(" · ") || "—";
  }

  /* Возвращает реальный options[] для шага с учётом optionsBy */
  function resolveStepOptions(step, answers) {
    if (step.options) return step.options;
    if (step.optionsBy) {
      const depVal = answers[step.optionsBy.dependsOn];
      return (step.optionsBy.map && step.optionsBy.map[depVal]) || [];
    }
    return [];
  }

  /* Активен ли шаг (выполняется ли condition) */
  function isStepActive(step, answers) {
    if (!step.condition) return true;
    for (const [key, expected] of Object.entries(step.condition)) {
      const actual = answers[key];
      const ok = Array.isArray(expected) ? expected.includes(actual) : actual === expected;
      if (!ok) return false;
    }
    return true;
  }

  /* Найти следующий активный шаг (или steps.length если все после inactive) */
  function findNextActiveIdx(steps, fromIdx, answers) {
    for (let i = fromIdx + 1; i < steps.length; i++) {
      if (isStepActive(steps[i], answers)) return i;
    }
    return steps.length;
  }

  /* Найти предыдущий активный (или -1) */
  function findPrevActiveIdx(steps, fromIdx, answers) {
    for (let i = fromIdx - 1; i >= 0; i--) {
      if (isStepActive(steps[i], answers)) return i;
    }
    return -1;
  }

  /* ===================== Иерархический wizard внутри категории ===================== */

  function getCatState(catKey) {
    const cs = state.per_cat[catKey];
    if (cs && cs.answers) {
      // Чистим ответы для шагов, которых больше нет в текущей схеме
      // (нужно при обновлении логики категории: например убрали класс энерго у ПММ)
      const config = PODBOR_PARAMS[catKey];
      if (config?.steps) {
        const validKeys = new Set(config.steps.map(s => s.key));
        const filtered = {};
        for (const [k, v] of Object.entries(cs.answers)) {
          if (validKeys.has(k)) filtered[k] = v;
        }
        if (Object.keys(filtered).length !== Object.keys(cs.answers).length) {
          cs.answers = filtered;
        }
      }
      return cs;
    }
    // Миграция / инициализация
    return { answers: {}, notes: cs?.notes || "", _step: 0 };
  }

  function setCatState(catKey, patch) {
    const prev = getCatState(catKey);
    const next = { ...prev, ...patch };
    update({ per_cat: { ...state.per_cat, [catKey]: next } });
  }

  function renderCategoryWizard(catKey) {
    const cat = PODBOR_CATEGORIES.find(x => x.key === catKey);
    const config = PODBOR_PARAMS[catKey];
    const cs = getCatState(catKey);
    let stepIdx = Math.max(0, Math.min(cs._step || 0, config.steps.length));

    // Нормализация: пропускаем неактивные шаги вперёд
    while (stepIdx < config.steps.length && !isStepActive(config.steps[stepIdx], cs.answers)) {
      stepIdx++;
    }

    // Финальный экран категории — обзор + заметки + кнопка "Готово"
    if (stepIdx >= config.steps.length) {
      return renderCategoryReview(catKey);
    }

    const step = config.steps[stepIdx];
    const options = resolveStepOptions(step, cs.answers);
    const isMulti = step.type === "multi";
    const currentVal = cs.answers[step.key];
    const currentArr = isMulti ? (Array.isArray(currentVal) ? currentVal : []) : null;

    // Чипы прошлых ответов (single-шаги)
    const prevChips = config.steps.slice(0, stepIdx)
      .filter(s => s.type !== "multi")
      .map(s => {
        const v = cs.answers[s.key];
        if (!v) return "";
        const opts = resolveStepOptions(s, cs.answers);
        const o = opts.find(x => x.key === v);
        return o ? `<span class="wiz-chip" data-jump="${s.key}">${o.label}</span>` : "";
      }).join("");

    // Определяем layout: если ни у одной опции нет pict — компактные пин-кнопки,
    // иначе — крупные карточки с пиктограммами.
    const hasPicts = options.some(o => o.pict && PODBOR_PICTS[o.pict]);
    const gridMode = hasPicts ? "cards" : "pins";

    const cardsHtml = options.map(o => {
      const isOn = isMulti ? currentArr.includes(o.key) : currentVal === o.key;
      const pict = o.pict && PODBOR_PICTS[o.pict];
      const cardCls = "wiz-card" + (hasPicts ? "" : " wiz-card--pin") + (isOn ? " on" : "") + (o.star ? " star" : "");
      if (hasPicts) {
        return `
          <button class="${cardCls}" data-val="${o.key}">
            ${pict ? `<div class="wiz-pict">${pict}</div>` : `<div class="wiz-pict wiz-pict-placeholder"></div>`}
            <div class="wiz-label">${o.label}</div>
            ${o.hint ? `<div class="wiz-hint">${o.hint}</div>` : ""}
            ${isOn ? `<div class="wiz-tick">${ICONS.check}</div>` : ""}
          </button>
        `;
      }
      // Пин-режим: компактная inline-кнопка с label, опц. hint мелкий справа
      return `
        <button class="${cardCls}" data-val="${o.key}">
          <span class="wiz-label">${o.label}</span>
          ${o.hint ? `<span class="wiz-hint">${o.hint}</span>` : ""}
          ${isOn ? `<span class="wiz-tick">${ICONS.check}</span>` : ""}
        </button>
      `;
    }).join("");

    const stepNum = stepIdx + 1;
    const stepTotal = config.steps.length;

    const node = el(`
      <section class="podbor-step podbor-wizard">
        <header class="wiz-header">
          <button class="podbor-back" aria-label="Назад">${ICONS.arrow_left}</button>
          <div class="wiz-header-meta">
            <div class="wiz-cat">${cat.label}</div>
            <div class="wiz-progress">Шаг ${stepNum} из ${stepTotal}</div>
          </div>
          <div class="wiz-cat-icon">${ICONS[cat.icon] || ""}</div>
        </header>

        ${prevChips ? `<div class="wiz-chips">${prevChips}</div>` : ""}

        <h3 class="wiz-title">${step.title}${isMulti ? ' <span class="wiz-multi">· можно несколько</span>' : ""}</h3>

        <div class="wiz-grid wiz-grid--${gridMode}">${cardsHtml}</div>

        <div class="podbor-cta-row">
          ${stepIdx > 0
            ? `<button class="btn-secondary" id="wizPrev">Назад</button>`
            : `<button class="btn-secondary" id="wizMenu">К списку</button>`
          }
          ${isMulti
            ? `<button class="btn-primary" id="wizNext">Дальше</button>`
            : (currentVal ? `<button class="btn-primary" id="wizNext">Дальше</button>` : "")
          }
        </div>
      </section>
    `);

    // Клик по карточке
    node.querySelectorAll(".wiz-card").forEach(card => {
      card.addEventListener("click", () => {
        const val = card.dataset.val;
        const cs2 = getCatState(catKey);
        const newAns = { ...cs2.answers };
        if (isMulti) {
          const arr = Array.isArray(newAns[step.key]) ? newAns[step.key] : [];
          newAns[step.key] = arr.includes(val) ? arr.filter(x => x !== val) : [...arr, val];
        } else {
          newAns[step.key] = val;
          // Если меняем answer для шага, от которого зависят следующие — сбросим их answers
          for (let i = stepIdx + 1; i < config.steps.length; i++) {
            const s = config.steps[i];
            if (s.optionsBy && s.optionsBy.dependsOn === step.key) {
              delete newAns[s.key];
            }
            if (s.condition && Object.prototype.hasOwnProperty.call(s.condition, step.key)) {
              delete newAns[s.key];
            }
          }
        }
        setCatState(catKey, { answers: newAns });
        // Single-select: автопереход на следующий АКТИВНЫЙ шаг
        if (!isMulti) {
          const nextIdx = findNextActiveIdx(config.steps, stepIdx, newAns);
          setCatState(catKey, { _step: nextIdx });
          haptic && haptic("impact");
        }
        render();
      });
    });

    // Чипы — клик возвращает к шагу
    node.querySelectorAll(".wiz-chip[data-jump]").forEach(chip => {
      chip.addEventListener("click", () => {
        const targetKey = chip.dataset.jump;
        const targetIdx = config.steps.findIndex(s => s.key === targetKey);
        if (targetIdx >= 0) {
          setCatState(catKey, { _step: targetIdx });
          render();
        }
      });
    });

    // Кнопки — через активные шаги
    const wizPrev = node.querySelector("#wizPrev");
    if (wizPrev) wizPrev.addEventListener("click", () => {
      const prevIdx = findPrevActiveIdx(config.steps, stepIdx, cs.answers);
      setCatState(catKey, { _step: Math.max(0, prevIdx) });
      render();
    });
    const wizMenu = node.querySelector("#wizMenu");
    if (wizMenu) wizMenu.addEventListener("click", () => { detailView = "menu"; render(); });
    const wizNext = node.querySelector("#wizNext");
    if (wizNext) wizNext.addEventListener("click", () => {
      const nextIdx = findNextActiveIdx(config.steps, stepIdx, cs.answers);
      setCatState(catKey, { _step: nextIdx });
      haptic && haptic("impact");
      render();
    });
    // Header back — на предыдущий активный или к меню
    node.querySelector(".podbor-back").addEventListener("click", () => {
      const prevIdx = findPrevActiveIdx(config.steps, stepIdx, cs.answers);
      if (prevIdx >= 0) {
        setCatState(catKey, { _step: prevIdx });
        render();
      } else {
        detailView = "menu";
        render();
      }
    });

    return node;
  }

  function renderCategoryReview(catKey) {
    const cat = PODBOR_CATEGORIES.find(x => x.key === catKey);
    const config = PODBOR_PARAMS[catKey];
    const cs = getCatState(catKey);

    const rows = config.steps.filter(step => isStepActive(step, cs.answers)).map(step => {
      const v = cs.answers[step.key];
      const opts = resolveStepOptions(step, cs.answers);
      if (step.type === "multi") {
        const arr = Array.isArray(v) ? v : [];
        const labels = arr.map(k => opts.find(o => o.key === k)?.label).filter(Boolean);
        return `
          <div class="rev-row">
            <div class="rev-label">${step.title}</div>
            <div class="rev-val">${labels.length ? labels.join(" · ") : '<span class="muted">не выбрано</span>'}</div>
          </div>
        `;
      }
      const opt = opts.find(o => o.key === v);
      return `
        <div class="rev-row">
          <div class="rev-label">${step.title}</div>
          <div class="rev-val">${opt ? opt.label : '<span class="muted">—</span>'}</div>
        </div>
      `;
    }).join("");

    const node = el(`
      <section class="podbor-step podbor-wizard">
        <header class="wiz-header">
          <button class="podbor-back" aria-label="Назад">${ICONS.arrow_left}</button>
          <div class="wiz-header-meta">
            <div class="wiz-cat">${cat.label}</div>
            <div class="wiz-progress">Готово</div>
          </div>
          <div class="wiz-cat-icon">${ICONS[cat.icon] || ""}</div>
        </header>

        <h3 class="wiz-title">Проверьте ответы</h3>

        <div class="rev-list">${rows}</div>

        <label class="field">
          <span class="field-label">Заметки по категории</span>
          <textarea data-bind="cat_notes" rows="2" placeholder="Особые пожелания клиента?">${cs.notes || ""}</textarea>
        </label>

        <div class="podbor-cta-row">
          <button class="btn-secondary" id="wizEdit">Изменить</button>
          <button class="btn-primary" id="wizDone">К списку категорий</button>
        </div>
      </section>
    `);
    node.querySelector("#wizEdit").addEventListener("click", () => {
      setCatState(catKey, { _step: 0 });
      render();
    });
    node.querySelector("#wizDone").addEventListener("click", () => {
      detailView = "menu";
      haptic && haptic("success");
      render();
    });
    node.querySelector(".podbor-back").addEventListener("click", () => {
      setCatState(catKey, { _step: config.steps.length - 1 });
      render();
    });
    const ta = node.querySelector("textarea[data-bind='cat_notes']");
    if (ta) ta.addEventListener("input", e => {
      setCatState(catKey, { notes: e.target.value });
    });
    return node;
  }

  function renderCategoryDetail(catKey) {
    const cat = PODBOR_CATEGORIES.find(x => x.key === catKey);
    const config = PODBOR_PARAMS[catKey];
    if (!config) {
      return el(`<section class="podbor-step"><div class="empty">Параметры для «${cat?.label}» ещё не описаны.</div></section>`);
    }
    const catState = state.per_cat[catKey] || { params: {}, features: [], notes: "" };
    const isExpanded = catState._expanded || false;

    const primaryHtml = config.primary.map(p => {
      const cur = catState.params?.[p.key] || "";
      return `
        <div class="param-group">
          <div class="param-label">${p.label}</div>
          <div class="opt-list">
            ${p.options.map(o => `
              <button class="opt${cur === o.key ? " on" : ""}" data-param="${p.key}" data-val="${o.key}">${o.label}</button>
            `).join("")}
          </div>
        </div>
      `;
    }).join("");

    const featuresHtml = config.features.map(f => {
      const on = (catState.features || []).includes(f.key);
      return `
        <button class="feature${on ? " on" : ""}" data-feat="${f.key}">
          <div class="feature-name">${f.label}</div>
          <div class="feature-hint">${f.hint}</div>
          <div class="feature-tick">${on ? ICONS.check : ""}</div>
        </button>
      `;
    }).join("");

    const node = el(`
      <section class="podbor-step podbor-cat-detail">
        <header class="cat-detail-header">
          <button class="podbor-back" aria-label="К меню">${ICONS.arrow_left}</button>
          <div class="cat-detail-icon">${ICONS[cat.icon] || ""}</div>
          <h2 class="cat-detail-title">${cat.label}</h2>
        </header>

        <div class="block">
          <div class="block-head">Главное</div>
          ${primaryHtml}
        </div>

        <button class="accordion-head" data-toggle="exp">
          <span>Подробнее</span>
          <span class="accordion-chev${isExpanded ? " open" : ""}">${ICONS.chevron}</span>
        </button>
        <div class="accordion-body${isExpanded ? " open" : ""}">
          <div class="hint">Технические фичи — необязательно. Если не отметите, AI выберет сам и пояснит в подборе.</div>
          <div class="feature-list">${featuresHtml}</div>
          <label class="field">
            <span class="field-label">Заметки по этой категории</span>
            <textarea data-bind="cat_notes" rows="2" placeholder="Что-то особенное?">${catState.notes || ""}</textarea>
          </label>
        </div>

        <div class="podbor-cta-row">
          <button class="btn-secondary" id="catBack">К списку</button>
          <button class="btn-primary" id="catSave">Сохранить</button>
        </div>
      </section>
    `);

    // Главное — radio
    node.querySelectorAll("[data-param]").forEach(b => {
      b.addEventListener("click", () => {
        const cs = state.per_cat[catKey] || { params: {}, features: [], notes: "" };
        cs.params = { ...(cs.params || {}), [b.dataset.param]: b.dataset.val };
        update({ per_cat: { ...state.per_cat, [catKey]: cs } });
        render();
      });
    });
    // Features — toggle
    node.querySelectorAll("[data-feat]").forEach(b => {
      b.addEventListener("click", () => {
        const cs = state.per_cat[catKey] || { params: {}, features: [], notes: "" };
        const cur = cs.features || [];
        cs.features = cur.includes(b.dataset.feat) ? cur.filter(x => x !== b.dataset.feat) : [...cur, b.dataset.feat];
        update({ per_cat: { ...state.per_cat, [catKey]: cs } });
        render();
      });
    });
    // Accordion
    node.querySelector("[data-toggle='exp']").addEventListener("click", () => {
      const cs = state.per_cat[catKey] || { params: {}, features: [], notes: "" };
      cs._expanded = !cs._expanded;
      update({ per_cat: { ...state.per_cat, [catKey]: cs } });
      render();
    });
    // Notes
    const ta = node.querySelector("textarea[data-bind='cat_notes']");
    if (ta) ta.addEventListener("input", e => {
      const cs = state.per_cat[catKey] || { params: {}, features: [], notes: "" };
      cs.notes = e.target.value;
      update({ per_cat: { ...state.per_cat, [catKey]: cs } });
    });
    // Back / save → menu
    node.querySelector(".podbor-back").addEventListener("click", () => { detailView = "menu"; render(); });
    node.querySelector("#catBack").addEventListener("click", () => { detailView = "menu"; render(); });
    node.querySelector("#catSave").addEventListener("click", () => { detailView = "menu"; render(); haptic && haptic("success"); });
    return node;
  }

  function formatRub(n) {
    if (!n) return "—";
    return Math.round(n).toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
  }

  /* Универсальный рендер пин-карточек (label + hint, single или multi) */
  function renderPinCards(items, getStatus, onClick, opts = {}) {
    const html = items.map(o => {
      const status = getStatus(o); // 'on' | 'on-star' | ''
      const isOn = status === "on" || status === "on-star";
      const cls = "wiz-card wiz-card--pin" + (isOn ? " on" : "") + (o.recommended ? " star" : "");
      return `
        <button class="${cls}" data-key="${o.key}">
          <span class="wiz-label">${o.label}</span>
          ${o.hint ? `<span class="wiz-hint">${o.hint}</span>` : ""}
          ${isOn ? `<span class="wiz-tick">${ICONS.check}</span>` : ""}
        </button>
      `;
    }).join("");
    const wrap = el(`<div class="wiz-grid wiz-grid--pins">${html}</div>`);
    wrap.querySelectorAll(".wiz-card").forEach(btn => {
      btn.addEventListener("click", () => {
        onClick(btn.dataset.key);
      });
    });
    return wrap;
  }

  /* ===================== Step: brand (бренд-стратегия + выбор) ===================== */

  function renderBrand() {
    const bs = state.brand_strategy || "";
    const strategyGrid = renderPinCards(
      PODBOR_BRAND_STRATEGY,
      o => (bs === o.key ? "on" : ""),
      key => { update({ brand_strategy: key }); render(); }
    );

    // Подблок зависит от выбранной стратегии
    let subBlock = "";
    if (bs === "single") {
      const sb = state.single_brand || "";
      const cardsHtml = PODBOR_SINGLE_BRAND_OPTIONS.map(o => {
        const on = sb === o.key;
        return `
          <button class="wiz-card wiz-card--pin${on ? " on" : ""}${o.recommended ? " star" : ""}" data-sb="${o.key}">
            <span class="wiz-label">${o.label}</span>
            ${o.tier ? `<span class="wiz-hint">${tierLabel(o.tier)}</span>` : ""}
            ${on ? `<span class="wiz-tick">${ICONS.check}</span>` : ""}
          </button>
        `;
      }).join("");
      subBlock = `
        <div class="block">
          <div class="block-head">Какая марка</div>
          <div class="wiz-grid wiz-grid--pins">${cardsHtml}</div>
        </div>
      `;
    } else if (bs === "different") {
      // Чипы по категориям с 4-state статусами (none → preferred → acceptable → avoid → none)
      const blocks = state.categories.map(catKey => {
        const cat = PODBOR_CATEGORIES.find(x => x.key === catKey);
        const brands = PODBOR_BRANDS[catKey] || { premium: [], middle: [], budget: [] };
        const catState = state.brands[catKey] || {};
        const tierGroup = (tier) => `
          <div class="brand-chips brand-tier-${tier}">
            ${(brands[tier] || []).map(b => {
              const status = catState[b] || "none";
              return `<button class="chip tier-${tier} status-${status}" data-cat="${catKey}" data-brand="${b}">${b}</button>`;
            }).join("")}
          </div>
        `;
        return `
          <div class="block">
            <div class="block-head">${cat.label}</div>
            ${tierGroup("premium")}${tierGroup("middle")}${tierGroup("budget")}
          </div>
        `;
      }).join("");
      subBlock = `
        <div class="hint">Тап — ★ хочу · повторно — ✓ согласен · третий — ✗ не хочу · четвёртый — снять</div>
        ${blocks}
      `;
    } else if (bs === "ai") {
      subBlock = `
        <div class="block">
          <div class="hint">AI подберёт оптимальный микс брендов под выбранный бюджет и стратегию. Можно ничего больше не указывать.</div>
        </div>
      `;
    }

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Бренд<br><span class="accent">стратегия</span></h2>
        <p class="lede">Хочет ли клиент всю технику от одной марки, или собираем оптимальный микс?</p>
      </section>
    `);
    node.appendChild(strategyGrid);
    if (subBlock) {
      const sub = el(`<div>${subBlock}</div>`);
      node.appendChild(sub);
      // Single-brand chips
      sub.querySelectorAll("[data-sb]").forEach(b => {
        b.addEventListener("click", () => {
          update({ single_brand: b.dataset.sb });
          render();
        });
      });
      // Different-brand 4-state cycle
      sub.querySelectorAll(".chip[data-brand]").forEach(c => {
        c.addEventListener("click", () => {
          const catKey = c.dataset.cat, brand = c.dataset.brand;
          const cur = (state.brands[catKey] || {})[brand] || "none";
          const nextStatus = cur === "none" ? "preferred"
                          : cur === "preferred" ? "acceptable"
                          : cur === "acceptable" ? "avoid"
                          : "none";
          const catBrands = { ...(state.brands[catKey] || {}) };
          if (nextStatus === "none") delete catBrands[brand];
          else catBrands[brand] = nextStatus;
          update({ brands: { ...state.brands, [catKey]: catBrands } });
          render();
        });
      });
    }
    const cta = el(`
      <div class="podbor-cta-row">
        <button class="btn-secondary" data-go="detail">Назад</button>
        <button class="btn-primary" data-go="budget"${bs ? "" : " disabled"}>Дальше</button>
      </div>
    `);
    node.appendChild(cta);
    bindNav(node);
    return node;
  }

  function tierLabel(tier) {
    return tier === "premium" ? "премиум" : tier === "middle" ? "средний" : tier === "budget" ? "бюджет" : "";
  }

  /* ===================== Step: budget (пресет или точные цифры) ===================== */

  function renderBudget() {
    const bp = state.budget_preset || "";

    // Считаем суммарную долю выбранных категорий от полного комплекта
    const share = (state.categories || []).reduce(
      (s, c) => s + (PODBOR_BUDGET_SHARES[c] || 0), 0
    ) / 100;
    const shareSafe = share > 0 ? share : 1;

    // Подмешиваем hint с реальной вилкой для выбранных категорий
    const presetsWithRange = PODBOR_BUDGET_PRESETS.map(o => {
      if (o.key === "exact") return { ...o, hint: o.desc };
      const r = PODBOR_BUDGET_RANGES[o.key];
      if (!r) return { ...o, hint: o.desc };
      const from = Math.round(r.from * shareSafe);
      const to   = Math.round(r.to   * shareSafe);
      const label = from >= 1000 ? `${(from/1000).toFixed(1)}–${(to/1000).toFixed(1)}М ₽` : `${from}–${to} тыс ₽`;
      return { ...o, hint: `${label} · ${o.desc}` };
    });

    const presetGrid = renderPinCards(
      presetsWithRange,
      o => (bp === o.key ? "on" : ""),
      key => { update({ budget_preset: key }); render(); }
    );

    // Если "exact" — показываем поля от-до по категориям
    let exactBlock = null;
    if (bp === "exact") {
      let totalFrom = 0, totalTo = 0;
      state.categories.forEach(c => {
        const r = state.price_ranges[c] || {};
        if (r.from) totalFrom += parseInt(r.from, 10) || 0;
        if (r.to)   totalTo   += parseInt(r.to,   10) || 0;
      });
      const rows = state.categories.map(c => {
        const cat = PODBOR_CATEGORIES.find(x => x.key === c);
        const r = state.price_ranges[c] || {};
        return `
          <div class="price-row">
            <div class="price-label">${cat.label}</div>
            <div class="price-inputs">
              <input type="number" inputmode="numeric" data-price="${c}.from" value="${r.from || ""}" placeholder="от">
              <span class="dash">—</span>
              <input type="number" inputmode="numeric" data-price="${c}.to"   value="${r.to   || ""}" placeholder="до">
              <span class="rub">₽</span>
            </div>
          </div>
        `;
      }).join("");
      exactBlock = el(`
        <div class="block">
          <div class="block-head">По категориям, ₽</div>
          <div class="price-list">${rows}</div>
          <div class="price-total" id="priceTotalLine">${
            (totalFrom || totalTo)
              ? `Итого: <strong>${formatRub(totalFrom)} — ${formatRub(totalTo)} ₽</strong>`
              : `<span class="muted">Сумма посчитается автоматически</span>`
          }</div>
        </div>
      `);
      // Внимание: НЕ вызываем render() на input — иначе клавиатура слетает
      exactBlock.querySelectorAll("[data-price]").forEach(inp => {
        inp.addEventListener("input", e => {
          const [cat, key] = e.target.dataset.price.split(".");
          const next = { ...state.price_ranges, [cat]: { ...(state.price_ranges[cat] || {}), [key]: e.target.value } };
          update({ price_ranges: next });
          // Локально пересчитываем сумму
          let tf = 0, tt = 0;
          state.categories.forEach(c => {
            const r = state.price_ranges[c] || {};
            if (r.from) tf += parseInt(r.from, 10) || 0;
            if (r.to)   tt += parseInt(r.to, 10) || 0;
          });
          const line = exactBlock.querySelector("#priceTotalLine");
          if (line) {
            line.innerHTML = (tf || tt)
              ? `Итого: <strong>${formatRub(tf)} — ${formatRub(tt)} ₽</strong>`
              : `<span class="muted">Сумма посчитается автоматически</span>`;
          }
        });
      });
    }

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Бюджет<br><span class="accent">на технику</span></h2>
        <p class="lede">Выбери диапазон. AI сам распределит бюджет по категориям (холодильник ~25%, варочная ~15%, духовка ~15% и т.д.).</p>
      </section>
    `);
    node.appendChild(presetGrid);
    if (exactBlock) node.appendChild(exactBlock);
    const cta = el(`
      <div class="podbor-cta-row">
        <button class="btn-secondary" data-go="brand">Назад</button>
        <button class="btn-primary" data-go="strategy"${bp ? "" : " disabled"}>Дальше</button>
      </div>
    `);
    node.appendChild(cta);
    bindNav(node);
    return node;
  }

  /* ===================== Step: strategy (что важно при подборе — multi) ===================== */

  function renderStrategy() {
    const cur = state.pick_strategies || [];
    const grid = renderPinCards(
      PODBOR_PICK_STRATEGIES,
      o => (cur.includes(o.key) ? "on" : ""),
      key => {
        const next = cur.includes(key) ? cur.filter(x => x !== key) : [...cur, key];
        update({ pick_strategies: next });
        render();
      }
    );

    // Количество моделей в категории
    const mc = state.model_count || "5";
    const countGrid = renderPinCards(
      PODBOR_MODEL_COUNTS,
      o => (mc === o.key ? "on" : ""),
      key => { update({ model_count: key }); render(); }
    );

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Стратегия<br><span class="accent">подбора</span></h2>
        <p class="lede">Что для клиента важно при выборе? Можно несколько — AI учтёт всё.</p>
      </section>
    `);
    node.appendChild(grid);

    const countHead = el(`
      <div class="block">
        <div class="block-head">Сколько моделей предложить</div>
      </div>
    `);
    countHead.appendChild(countGrid);
    node.appendChild(countHead);

    const cta = el(`
      <div class="podbor-cta-row">
        <button class="btn-secondary" data-go="budget">Назад</button>
        <button class="btn-primary" data-go="infra">Дальше</button>
      </div>
    `);
    node.appendChild(cta);
    bindNav(node);
    return node;
  }

  /* ===================== Step: infra ===================== */

  function renderInfra() {
    const cats = state.categories || [];
    const askStove = cats.includes("hob");
    // Вытяжка НЕ спрашиваем здесь — у hood-категории уже есть шаг "Подключение"
    // (Отвод в вентшахту / Рециркуляция / Универсальная) — это дубль.

    // Если варочная не выбрана — пропускаем шаг полностью
    if (!askStove) {
      setTimeout(() => { go("summary"); }, 50);
      return el(`
        <section class="podbor-step">
          <p class="lede" style="text-align:center;padding:30px;">
            Инфраструктурные вопросы для выбранных категорий не требуются — переходим к итогу...
          </p>
        </section>
      `);
    }

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Инфраструктура<br><span class="accent">кухни</span></h2>
        <p class="lede">Газ или электрика — определит тип варочной (индукция требует 380В, обычная электро 220В).</p>
        <div class="block">
          <div class="block-head">Подключение варочной</div>
          <div class="opt-list">
            ${PODBOR_INFRA.stove.map(o => `
              <button class="opt${state.infra.stove === o.key ? " on" : ""}" data-infra="stove" data-val="${o.key}">${o.label}</button>
            `).join("")}
          </div>
        </div>
        <div class="podbor-cta-row">
          <button class="btn-secondary" data-go="strategy">Назад</button>
          <button class="btn-primary" data-go="summary">Дальше</button>
        </div>
      </section>
    `);
    node.querySelectorAll("[data-infra]").forEach(b => {
      b.addEventListener("click", () => {
        update({ infra: { ...state.infra, [b.dataset.infra]: b.dataset.val } });
        render();
      });
    });
    bindNav(node);
    return node;
  }

  /* ===================== Step: summary + submit ===================== */

  function renderSummary() {
    // Бренд-стратегия
    const bs = state.brand_strategy;
    const bsLabel = PODBOR_BRAND_STRATEGY.find(s => s.key === bs)?.label || "—";
    let brandDetail = "";
    if (bs === "single") {
      const sb = PODBOR_SINGLE_BRAND_OPTIONS.find(o => o.key === state.single_brand);
      brandDetail = sb ? ` · ${sb.label}` : "";
    } else if (bs === "different") {
      const totalBrands = Object.values(state.brands || {}).reduce((s, c) => s + Object.keys(c || {}).length, 0);
      brandDetail = totalBrands ? ` · ${totalBrands} отметок` : "";
    }

    // Бюджет
    const bp = state.budget_preset;
    const bpDef = PODBOR_BUDGET_PRESETS.find(p => p.key === bp);
    let budgetLabel = bpDef?.label || "—";
    if (bp === "exact") {
      let totalFrom = 0, totalTo = 0;
      state.categories.forEach(c => {
        const r = state.price_ranges[c] || {};
        totalFrom += parseInt(r.from || "0", 10) || 0;
        totalTo   += parseInt(r.to   || "0", 10) || 0;
      });
      if (totalFrom || totalTo) budgetLabel = `${formatRub(totalFrom)} — ${formatRub(totalTo)} ₽`;
    } else if (bpDef?.hint) {
      budgetLabel = `${bpDef.label} · ${bpDef.hint}`;
    }

    // Стратегия подбора
    const strategyLabels = (state.pick_strategies || [])
      .map(k => PODBOR_PICK_STRATEGIES.find(s => s.key === k)?.label)
      .filter(Boolean).join(" · ");

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">Готово<br><span class="accent">к подбору</span></h2>
        <p class="lede">Проверьте и отправьте — AI вернёт предложение в чат с ботом.</p>
        <div class="block summary-block">
          <div class="kv"><span>Клиент</span><strong>${state.client_name || "—"}</strong></div>
          <div class="kv"><span>Категорий</span><strong>${state.categories.length}</strong></div>
          <div class="kv"><span>Бренд</span><strong>${bsLabel}${brandDetail}</strong></div>
          <div class="kv"><span>Бюджет</span><strong>${budgetLabel}</strong></div>
          <div class="kv"><span>Стратегия</span><strong>${strategyLabels || "—"}</strong></div>
          ${state.categories.includes("hob") ? `<div class="kv"><span>Подключение</span><strong>${PODBOR_INFRA.stove.find(f => f.key === state.infra.stove)?.label || "—"}</strong></div>` : ""}
        </div>

        <label class="field">
          <span class="field-label">Дополнительные пожелания</span>
          <textarea data-bind="notes" rows="3" placeholder="Что-то особенное от клиента?">${state.notes || ""}</textarea>
        </label>

        <div class="podbor-cta-row">
          <button class="btn-secondary" id="summaryBack">Назад</button>
          <button class="btn-primary" id="submitBtn">Отправить · AI подберёт</button>
        </div>

        <div id="submitResult" class="submit-result"></div>
      </section>
    `);
    bindInputs(node);
    bindNav(node);
    // Кнопка "Назад" — обходим infra если она авто-пропускается
    node.querySelector("#summaryBack").addEventListener("click", () => {
      const cats = state.categories || [];
      // Infra-шаг показывается только если выбрана варочная (для электро/газ).
      // Вытяжка задаёт свою вентиляцию в hood-step "Подключение".
      const goTo = cats.includes("hob") ? "infra" : "strategy";
      go(goTo);
    });
    node.querySelector("#submitBtn").addEventListener("click", () => onSubmit(node));
    return node;
  }

  /* ===================== Submit ===================== */

  async function onSubmit(node) {
    const btn = node.querySelector("#submitBtn");
    const result = node.querySelector("#submitResult");
    btn.disabled = true;
    btn.innerHTML = '<span class="spinner-inline"></span> AI думает...';
    result.innerHTML = "";

    if (!BACKEND_URL) {
      result.innerHTML = `<div class="error">BACKEND_URL не настроен (dev-режим).</div>`;
      btn.disabled = false; btn.textContent = "Отправить · AI подберёт"; return;
    }

    try {
      // Финальная нормализация телефона перед отправкой
      const normPhone = normalizePhone(state.client_phone || "");
      if (normPhone && normPhone !== state.client_phone) {
        update({ client_phone: normPhone });
      }
      const res = await fetch(`${BACKEND_URL}/api/podbor`, {
        method: "POST",
        body: JSON.stringify({
          initData: tg?.initData || "",
          checklist: state,
          client_name: state.client_name,
        }),
      });
      const data = await res.json();
      if (data.error) {
        result.innerHTML = `<div class="error">Ошибка: ${data.error}</div>`;
      } else {
        // Успех + красивый inline-отчёт под кнопкой
        const headSuccess = `
          <div class="success">
            <div class="success-icon">${ICONS.check}</div>
            <div>
              <div class="success-title">Подбор готов</div>
              <div class="success-sub">Лид #${(data.id || "").slice(0, 6)} · также отправлен в Telegram</div>
            </div>
          </div>
        `;
        result.innerHTML = headSuccess;
        // Рендер отчёта (если AI вернул by_category)
        if (data.ai) {
          const reportNode = renderReport(data.ai, data.id || "");
          result.appendChild(reportNode);
        }
        haptic && haptic("success");
        // Скроллим к отчёту
        setTimeout(() => result.scrollIntoView({ behavior: "smooth", block: "start" }), 100);
      }
    } catch (e) {
      result.innerHTML = `<div class="error">Сеть: ${e.message}</div>`;
    }
    btn.disabled = false;
    btn.textContent = "Отправить ещё раз";
  }

  /* ===================== Отчёт (inline, в шаге summary) ===================== */

  function renderReport(ai, leadId) {
    const summary = ai.summary || "";
    const byCat = ai.by_category || {};
    const total = ai.total_price_estimate_rub || {};
    const budgetStatus = ai.budget_status || "";
    const warnings = ai.warnings || [];

    const wrap = el(`<section class="report"></section>`);

    // Шапка
    wrap.appendChild(el(`
      <div class="report-head">
        <div class="kicker">Отчёт · ${leadId.slice(0, 8)}</div>
        ${summary ? `<p class="report-summary">${_esc(summary)}</p>` : ""}
      </div>
    `));

    // Категории
    for (const [catKey, catData] of Object.entries(byCat)) {
      const catMeta = PODBOR_CATEGORIES.find(c => c.key === catKey);
      const catLabel = catMeta?.label || catKey;
      const catIcon = catMeta?.icon;
      const models = (catData && catData.models) || [];
      const catAnalysis = (catData && catData.analysis) || "";
      if (!models.length) continue;

      const catNode = el(`
        <div class="report-cat">
          <h3 class="report-cat-head">
            <span class="report-cat-icon">${(catIcon && ICONS[catIcon]) || ""}</span>
            ${_esc(catLabel)}
          </h3>
          ${catAnalysis ? `<div class="report-cat-analysis">${_esc(catAnalysis)}</div>` : ""}
        </div>
      `);

      // Сравнение цен — основной блок, всегда вверху
      const matrixNode = _renderPriceMatrix(models);
      if (matrixNode) catNode.appendChild(matrixNode);

      // Детальные карточки с pros/cons
      const modelsWrap = el(`<div class="report-models"></div>`);
      for (const m of models) {
        modelsWrap.appendChild(_renderModelCard(m));
      }
      catNode.appendChild(modelsWrap);

      wrap.appendChild(catNode);
    }

    // Итого
    if (total && (total.min || total.max)) {
      const tmin = total.min, tmax = total.max;
      const range = (tmin && tmax && tmin !== tmax)
        ? `${formatRub(tmin)} — ${formatRub(tmax)} ₽`
        : `${formatRub(tmin || tmax)} ₽`;
      wrap.appendChild(el(`
        <div class="report-total">
          <span class="lbl">ИТОГО</span>
          <strong>${range}</strong>
          ${budgetStatus ? `<span class="status">${_esc(budgetStatus)}</span>` : ""}
        </div>
      `));
    }

    // Предупреждения
    if (warnings.length) {
      const wn = el(`<div class="report-warnings"></div>`);
      warnings.forEach(w => wn.appendChild(el(`<div>⚠️ ${_esc(w)}</div>`)));
      wrap.appendChild(wn);
    }

    // Кнопки экспорта в конце
    const exportNode = el(`
      <div class="report-export">
        <div class="report-export-head">Сохранить отчёт</div>
        <div class="report-export-buttons">
          <button class="btn-export" id="exportHtml">⬇ Скачать HTML</button>
          <button class="btn-export" id="exportPrint">🖨 Печать → PDF</button>
        </div>
        <div class="report-export-hint">HTML удобно отправить клиенту в мессенджер · PDF — для печати или вложения</div>
      </div>
    `);
    exportNode.querySelector("#exportHtml").addEventListener("click", () => _exportReportHtml(ai, leadId));
    exportNode.querySelector("#exportPrint").addEventListener("click", () => _exportReportPrint(wrap, leadId));
    wrap.appendChild(exportNode);

    return wrap;
  }

  /* Экспорт: HTML файл с inline стилями и данными — скачивается */
  function _exportReportHtml(ai, leadId) {
    // Создаём stand-alone HTML вокруг текущего DOM отчёта
    const reportEl = document.querySelector(".report");
    if (!reportEl) return;
    // Копируем все стили со страницы (link + style)
    const stylesheets = [];
    document.querySelectorAll("link[rel='stylesheet'], style").forEach(s => stylesheets.push(s.outerHTML));
    const html = `<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Подбор техники · ${leadId.slice(0,8)}</title>
${stylesheets.join("\n")}
<style>
  body { background: #FBF7F0; padding: 20px; max-width: 900px; margin: 0 auto; }
  .report-export { display: none; }
  .podbor-header, .podbor-progress, .cat-strip, #submitResult > .success { display: none; }
</style>
</head>
<body>
<h1 style="font-family: 'Newsreader', serif; font-style: italic; color: #1F1A14;">Подбор техники · клиент: ${_esc(state.client_name || "—")}</h1>
${reportEl.outerHTML}
<footer style="margin-top: 40px; padding: 20px; border-top: 1px solid #ddd; font-size: 11px; color: #888; font-family: 'JetBrains Mono', monospace;">
  Отчёт сгенерирован ${new Date().toLocaleString("ru-RU")} · ZOV Tech Picker · Lead ID: ${leadId}
</footer>
</body>
</html>`;
    const blob = new Blob([html], { type: "text/html;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `podbor-${leadId.slice(0,8)}-${(state.client_name || "client").replace(/\s+/g, "_")}.html`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    setTimeout(() => URL.revokeObjectURL(url), 1000);
    haptic && haptic("success");
  }

  /* Экспорт: системный print-диалог. На Telegram WebApp popups часто блокируются,
     поэтому печатаем inline — `@media print` в podbor.css скрывает всё лишнее. */
  function _exportReportPrint(reportNode, leadId) {
    const isTelegram = !!(window.Telegram && window.Telegram.WebApp);
    // Сначала пробуем открыть отдельное окно (на десктопе/Safari чище)
    if (!isTelegram) {
      const reportEl = document.querySelector(".report");
      if (reportEl) {
        const stylesheets = [];
        document.querySelectorAll("link[rel='stylesheet'], style").forEach(s => stylesheets.push(s.outerHTML));
        const w = window.open("", "_blank");
        if (w) {
          w.document.write(`<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Подбор техники · ${leadId.slice(0,8)} · Печать</title>
${stylesheets.join("\n")}
<style>
  body { background: #fff; padding: 20px; max-width: 900px; margin: 0 auto; }
  .report-export, .podbor-header, .podbor-progress, .cat-strip { display: none; }
  @media print {
    body { padding: 10px; }
    .report-model { page-break-inside: avoid; }
    .report-cat { page-break-after: auto; }
  }
</style>
</head>
<body>
<h1 style="font-family: 'Newsreader', serif; font-style: italic;">Подбор техники · ${_esc(state.client_name || "—")}</h1>
${reportEl.outerHTML}
<script>setTimeout(() => window.print(), 500);<\/script>
</body>
</html>`);
          w.document.close();
          haptic && haptic("success");
          return;
        }
      }
    }
    // Inline-печать: помечаем body классом, чтобы скрыть ненужное через @media print
    document.body.classList.add("printing-report");
    setTimeout(() => {
      try { window.print(); } catch (e) { console.warn("Print failed", e); }
      document.body.classList.remove("printing-report");
    }, 50);
    haptic && haptic("success");
  }

  function _renderModelCard(m) {
    const enriched = m.enriched || {};
    const pMin = enriched.price_min_rub || m.price_min_rub;
    const pMax = enriched.price_max_rub || m.price_max_rub;
    const img = enriched.image_url;
    const rating = enriched.rating_max;
    const reviews = enriched.reviews_total;
    const stores = enriched.stores_count;

    const priceHtml = (pMin && pMax && pMin !== pMax)
      ? `<strong>${formatRub(pMin)}</strong> — <strong>${formatRub(pMax)}</strong> ₽`
      : pMin ? `<strong>${formatRub(pMin)}</strong> ₽`
      : `<span class="muted">цена уточняется</span>`;

    const metaParts = [];
    if (rating) metaParts.push(`<span class="rating">★ ${Number(rating).toFixed(1)}</span>`);
    if (reviews) metaParts.push(`<span class="reviews">${reviews} отзыв.</span>`);
    if (stores) metaParts.push(`<span class="stores">${stores} магазинов</span>`);

    // Бейджи источников + ссылки
    const sourcesData = [
      { key: "ozon",     label: "OZON",        item: enriched.ozon },
      { key: "citilink", label: "Citilink",    item: enriched.citilink },
      { key: "wb",       label: "Wildberries", item: enriched.wb },
      { key: "yamarket", label: "Я.Маркет",    item: enriched.yamarket },
      { key: "dns",      label: "DNS",         item: enriched.dns },
    ];
    const sourceLinks = sourcesData
      .filter(s => s.item && s.item.url)
      .map(s => `<a href="${_esc(s.item.url)}" target="_blank" rel="noopener noreferrer" class="report-link report-link--${s.key}">${s.label}${s.item.price_min_rub ? ` · ${formatRub(s.item.price_min_rub)} ₽` : ""}</a>`);

    const card = el(`
      <article class="report-model">
        <div class="report-model-img${img ? "" : " placeholder"}">${img ? `<img src="${_esc(img)}" alt="" loading="lazy">` : ""}</div>
        <div class="report-model-body">
          <div class="report-model-brand">${_esc(m.brand || "")}</div>
          <div class="report-model-name">${_esc(m.model || "")}</div>
          ${metaParts.length ? `<div class="report-model-meta">${metaParts.join(" · ")}</div>` : ""}
          <div class="report-model-price">${priceHtml}</div>
          ${(m.highlights || []).length ? `<div class="report-highlights">✓ ${m.highlights.map(_esc).join(" · ")}</div>` : ""}

          ${(m.pros || []).length ? `
            <div class="report-pros-block">
              <div class="pc-head">Плюсы</div>
              <ul class="pc-list">${m.pros.slice(0, 4).map(p => `<li>${_esc(p)}</li>`).join("")}</ul>
            </div>
          ` : ""}

          ${(m.cons || []).length ? `
            <div class="report-cons-block">
              <div class="pc-head">Минусы</div>
              <ul class="pc-list">${m.cons.slice(0, 3).map(c => `<li>${_esc(c)}</li>`).join("")}</ul>
            </div>
          ` : ""}

          ${_renderSpecsBlock(m.specs || {})}

          ${m.reasoning ? `<div class="report-reasoning">💡 ${_esc(m.reasoning)}</div>` : ""}

          ${_renderUtilityLinks(m)}

          ${sourceLinks.length ? `
            <div class="report-links">
              <div class="report-links-head">${sourceLinks.length} ${_pluralStores(sourceLinks.length)} нашли товар:</div>
              ${sourceLinks.join("")}
            </div>
          ` : `<div class="report-links-empty">— не найден в подключённых магазинах</div>`}
        </div>
      </article>
    `);
    return card;
  }

  /* Спеки технические: габариты, объём, шум, класс энергии, цвет.
     Габариты — критично для проектирования кухни. */
  function _renderSpecsBlock(specs) {
    if (!specs || Object.keys(specs).length === 0) return "";
    const items = [];
    if (specs.dimensions_mm) items.push({ label: "Габариты ШхГxВ", value: `${specs.dimensions_mm} мм`, highlight: true });
    if (specs.volume_l)      items.push({ label: "Объём",          value: `${specs.volume_l} л` });
    if (specs.weight_kg)     items.push({ label: "Вес",            value: `${specs.weight_kg} кг` });
    if (specs.noise_db)      items.push({ label: "Шум",            value: `${specs.noise_db} дБ` });
    if (specs.energy_class)  items.push({ label: "Класс энергии",  value: specs.energy_class });
    if (specs.color)         items.push({ label: "Цвет / материал", value: specs.color });

    if (!items.length) return "";

    return `
      <div class="report-specs">
        <div class="report-specs-head">Характеристики</div>
        <div class="report-specs-grid">
          ${items.map(i => `
            <div class="spec-item${i.highlight ? " highlight" : ""}">
              <div class="spec-label">${_esc(i.label)}</div>
              <div class="spec-value">${_esc(i.value)}</div>
            </div>
          `).join("")}
        </div>
      </div>
    `;
  }

  /* Кнопки "Инструкция" (Google поиск) + "Габариты" (если есть в specs). */
  function _renderUtilityLinks(m) {
    const buttons = [];
    const manualQuery = m.manual_search_query
      || `${m.brand || ""} ${m.model || ""} manual инструкция pdf`.trim();
    if (manualQuery && manualQuery.length > 5) {
      const url = `https://www.google.com/search?q=${encodeURIComponent(manualQuery)}`;
      buttons.push(`<a href="${url}" target="_blank" rel="noopener noreferrer" class="util-link util-link--manual">📄 Инструкция</a>`);
    }
    const dims = (m.specs || {}).dimensions_mm;
    if (dims) {
      const url = `https://www.google.com/search?q=${encodeURIComponent(`${m.brand} ${m.model} габариты схема установки`)}`;
      buttons.push(`<a href="${url}" target="_blank" rel="noopener noreferrer" class="util-link util-link--dims">📐 Схема установки</a>`);
    }
    if (!buttons.length) return "";
    return `<div class="report-util-links">${buttons.join("")}</div>`;
  }

  function _pluralStores(n) {
    const last = n % 10, lastTwo = n % 100;
    if (lastTwo >= 11 && lastTwo <= 14) return "магазинов";
    if (last === 1) return "магазин";
    if (last >= 2 && last <= 4) return "магазина";
    return "магазинов";
  }

  /* Матрица цен: rows = models, cols = магазины, cells = цены, лучшая подсвечена */
  function _renderPriceMatrix(models) {
    if (!models || !models.length) return null;

    const STORES = [
      { key: "ozon",     label: "OZON" },
      { key: "citilink", label: "Citilink" },
      { key: "wb",       label: "Wildberries" },
      { key: "yamarket", label: "Я.Маркет" },
      { key: "dns",      label: "DNS" },
    ];

    // Определяем какие магазины имеют хоть какую-то цену — только их колонки
    const activeStores = STORES.filter(s =>
      models.some(m => (m.enriched || {})[s.key] && ((m.enriched || {})[s.key].price_min_rub || (m.enriched || {})[s.key].url))
    );

    // Если ни один store не дал цены — показываем простую таблицу с AI-ценами
    const showStoresCols = activeStores.length > 0;

    const head = `
      <thead>
        <tr>
          <th class="col-model">Модель</th>
          ${showStoresCols
            ? activeStores.map(s => `<th class="col-store">${s.label}</th>`).join("")
            : `<th class="col-store">Цена (AI)</th>`}
          <th class="col-best">Мин</th>
        </tr>
      </thead>
    `;

    const rows = models.map(m => {
      const enriched = m.enriched || {};
      // Собираем цены по каждому активному магазину
      const cellPrices = showStoresCols
        ? activeStores.map(s => {
            const item = enriched[s.key];
            return {
              store: s.key,
              price: item && item.price_min_rub,
              url: item && item.url,
            };
          })
        : [{ store: "ai", price: enriched.price_min_rub || m.price_min_rub, url: enriched.best_url || null }];

      const validPrices = cellPrices.map(c => c.price).filter(p => p && p > 0);
      const bestPrice = validPrices.length ? Math.min(...validPrices) : null;

      const modelCell = `
        <td class="col-model">
          <div class="m-brand">${_esc(m.brand || "")}</div>
          <div class="m-name">${_esc(m.model || "")}</div>
        </td>
      `;
      const storeCells = cellPrices.map(c => {
        if (!c.price) {
          return c.url
            ? `<td class="cell-price"><a href="${_esc(c.url)}" target="_blank" rel="noopener noreferrer" class="cell-noprice-link">смотреть →</a></td>`
            : `<td class="cell-price empty">—</td>`;
        }
        const isBest = bestPrice && c.price === bestPrice;
        const priceHtml = `<strong>${formatRub(c.price)}</strong> ₽`;
        const cellInner = c.url
          ? `<a href="${_esc(c.url)}" target="_blank" rel="noopener noreferrer">${priceHtml}${isBest ? ' <span class="best-mark">✓</span>' : ''}</a>`
          : `${priceHtml}${isBest ? ' <span class="best-mark">✓</span>' : ''}`;
        return `<td class="cell-price${isBest ? ' best' : ''}">${cellInner}</td>`;
      }).join("");
      const bestCell = `<td class="col-best">${bestPrice ? `<strong>${formatRub(bestPrice)}</strong>` : "—"}</td>`;

      return `<tr>${modelCell}${storeCells}${bestCell}</tr>`;
    }).join("");

    return el(`
      <div class="report-matrix-wrap">
        <div class="report-matrix-head">Цены по магазинам — лучшая отмечена ✓</div>
        <div class="report-matrix-scroll">
          <table class="report-matrix">
            ${head}
            <tbody>${rows}</tbody>
          </table>
        </div>
        ${!showStoresCols ? `<div class="report-matrix-hint">Магазины ещё не нашли товар. Цены — оценка AI на основе текущих рыночных данных.</div>` : ""}
      </div>
    `);
  }

  function _renderCompareTable(models) {
    const rows = models.map(m => {
      const e = m.enriched || {};
      const p = m.price_min_rub || e.price_min_rub;
      return `
        <tr>
          <td><strong>${_esc(m.brand || "")}</strong> ${_esc(m.model || "")}</td>
          <td>${p ? formatRub(p) + " ₽" : "—"}</td>
          <td>${e.reviews_total || "—"}</td>
          <td>${e.rating_max ? Number(e.rating_max).toFixed(1) : "—"}</td>
        </tr>
      `;
    }).join("");

    return el(`
      <details class="report-compare">
        <summary>Сравнить модели</summary>
        <table>
          <thead>
            <tr><th>Модель</th><th>Цена от</th><th>Отзывов</th><th>★</th></tr>
          </thead>
          <tbody>${rows}</tbody>
        </table>
      </details>
    `);
  }

  function _esc(s) {
    return String(s == null ? "" : s)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }

  /* ===================== Helpers ===================== */

  function bindInputs(node) {
    node.querySelectorAll("[data-bind]").forEach(inp => {
      inp.addEventListener("input", e => {
        update({ [e.target.dataset.bind]: e.target.value });
      });
      // Нормализация телефона на blur
      if (inp.dataset.bind === "client_phone") {
        inp.addEventListener("blur", e => {
          const normalized = normalizePhone(e.target.value);
          if (normalized && normalized !== e.target.value) {
            e.target.value = normalized;
            update({ client_phone: normalized });
          }
        });
      }
    });
  }

  /* Приводим телефон к единому формату +7 XXX XXX-XX-XX.
     Принимает: 8XXXXXXXXXX, 7XXXXXXXXXX, +7XXXXXXXXXX, 9XXXXXXXXX (без префикса). */
  function normalizePhone(raw) {
    if (!raw) return "";
    const digits = raw.replace(/\D/g, "");
    let d = digits;
    if (d.length === 11 && d.startsWith("8")) d = "7" + d.slice(1);
    if (d.length === 10 && d.startsWith("9")) d = "7" + d; // мобильный без префикса
    if (d.length !== 11 || !d.startsWith("7")) return raw.trim(); // не похоже на РФ-номер — не трогаем
    return `+7 ${d.slice(1, 4)} ${d.slice(4, 7)}-${d.slice(7, 9)}-${d.slice(9, 11)}`;
  }

  /* Проверка: получится ли валидный РФ-номер из введённого. */
  function isValidPhone(raw) {
    if (!raw) return false;
    let d = raw.replace(/\D/g, "");
    if (d.length === 11 && d.startsWith("8")) d = "7" + d.slice(1);
    if (d.length === 10 && d.startsWith("9")) d = "7" + d;
    return d.length === 11 && d.startsWith("7");
  }

  function bindNav(node) {
    node.querySelectorAll("[data-go]").forEach(b => {
      b.addEventListener("click", () => go(b.dataset.go));
    });
  }

  return {
    mount,
    go,
    getState: () => state,
    reset: () => { state = defaultState(); saveState(); render(); },
    // Внешний API для рендеринга сохранённого отчёта (используется в Clients)
    renderSavedReport: (ai, leadId) => renderReport(ai, leadId || ""),
  };
})();
