// wizard.jsx — 3-krokowy konfigurator instalacji PV + magazyn energii
const { useState: wUseState, useEffect: wUseEffect, useRef: wUseRef, useMemo: wUseMemo } = React;

/* ═══════════════════════════════════════════════════════════════════
   CENNIK I PRODUKTY — wartości netto w PLN, zgodne z arkuszem klienta.
   net = panele + konstrukcja + falownik(+dodatki) + robocizna + magazyn.
   VAT i ewentualny rabat (kod) na końcu.
   ═══════════════════════════════════════════════════════════════════ */
const PRICING = {
  vat: { individual: 0.08, business: 0.23 },

  // Dane (moc, rama, rodzaj, wymiary) z arkusza „Panele Dla Maćka.xlsx".
  // pricePerKwp = cena netto modułu (kol. H „netto szt." z arkusza) przeliczona na kWp: cena_szt × 1000 / moc_W.
  panels: [
    {
      id: "aiko-a450", name: "AIKO A450-MAH54MW", tier: "Standard", watt: 450,
      img: "assets/panels/aiko-a450.png",
      pricePerKwp: 589, // 265 zł/szt ÷ 0,450 kWp
      params: { Producent: "AIKO Solar", Moc: "450 W", Rama: "Czarna", Rodzaj: "Monoglass", Wymiary: "1757 × 1134 × 30 mm" },
    },
    {
      id: "aiko-a470", name: "AIKO A470-MAH54MB", tier: "Premium", watt: 470,
      img: "assets/panels/aiko-a470.png",
      pricePerKwp: 638, // 300 zł/szt ÷ 0,470 kWp
      params: { Producent: "AIKO Solar", Moc: "470 W", Rama: "Full Black", Rodzaj: "Monoglass", Wymiary: "1757 × 1134 × 30 mm" },
    },
    {
      id: "aiko-a500", name: "AIKO A500-MAH60MW", tier: "Standard Plus", watt: 500,
      img: "assets/panels/aiko-a500.png",
      pricePerKwp: 650, // 325 zł/szt ÷ 0,500 kWp
      params: { Producent: "AIKO Solar", Moc: "500 W", Rama: "Czarna", Rodzaj: "Monoglass", Wymiary: "1954 × 1134 × 30 mm" },
    },
    {
      id: "aiko-a515", name: "AIKO A515-MAH60MW", tier: "Standard Plus", watt: 515,
      img: "assets/panels/aiko-a515.png",
      pricePerKwp: 631, // 325 zł/szt ÷ 0,515 kWp
      params: { Producent: "AIKO Solar", Moc: "515 W", Rama: "Czarna", Rodzaj: "Monoglass", Wymiary: "1954 × 1134 × 30 mm" },
    },
    {
      id: "aiko-a540", name: "AIKO A540-MCE60DB", tier: "Premium", watt: 540,
      img: "assets/panels/aiko-a540.png",
      pricePerKwp: 704, // 380 zł/szt ÷ 0,540 kWp
      params: { Producent: "AIKO Solar", Moc: "540 W", Rama: "Full Black", Rodzaj: "Dual Glass", Wymiary: "1954 × 1134 × 30 mm" },
    },
    {
      id: "suntech-stp510", name: "Suntech STP510S-I60-Nsfb+", tier: "Premium", watt: 650,
      img: "assets/panels/suntech-stp510.png",
      pricePerKwp: 477, // 310 zł/szt ÷ 0,650 kWp
      params: { Producent: "Suntech", Moc: "650 W", Rama: "Full Black", Rodzaj: "Bifacial", Wymiary: "1952 × 1134 × 30 mm" },
    },
  ],

  // Magazyny energii z arkusza „Kalkulator odblokowany" (ceny netto: kol. P).
  // kind "units" = sztuki × unitPrice; unitKwh = MOC jednej sztuki → w UI „Moc" = sztuki × unitKwh.
  // "sigen-modular" = przeskoki Sigen; suwak = POJEMNOŚĆ (kWh), a moc = połowa pojemności.
  // invBrand: dozwolona/auto-dobierana linia falownika.
  batteries: [
    {
      id: "deye-pro-b", name: "Deye Pro-B 5.12kWh", tier: "Standard Plus", kind: "units", invBrand: "deye",
      unitPrice: 2900, unitKwh: 2.56, unitCapKwh: 5.12, maxUnits: 12, img: "assets/batteries/deye-pro-b.png",
      params: { Producent: "Deye", Model: "SE-G5.1 Pro-B", Ogniwa: "LiFePO4", Napięcie: "Niskonapięciowy (LV)" },
    },
    {
      id: "felicity-16", name: "Felicity 16", tier: "Standard", kind: "units", invBrand: "deye",
      unitPrice: 5950, unitKwh: 8.2, unitCapKwh: 16, maxUnits: 6, img: "assets/batteries/felicity-16.png",
      params: { Producent: "Felicity", Model: "FLA48314-EU", Ogniwa: "LiFePO4", Napięcie: "Niskonapięciowy (LV)" },
    },
    {
      id: "sluxer-16", name: "Sluxer 16", tier: "Standard", kind: "units", invBrand: "deye",
      unitPrice: 5950, unitKwh: 7.7, unitCapKwh: 16, maxUnits: 6, img: "assets/batteries/sluxer-16.png",
      params: { Producent: "Sluxer", Model: "Sluxer 16", Ogniwa: "LiFePO4", Napięcie: "Niskonapięciowy (LV)" },
    },
    {
      id: "sluxer-30", name: "Sluxer 30", tier: "Standard Plus", kind: "units", invBrand: "deye",
      unitPrice: 11800, unitKwh: 9.72, unitCapKwh: 30, maxUnits: 3, img: "assets/batteries/sluxer-30.png",
      params: { Producent: "Sluxer", Model: "Sluxer Home 30", Ogniwa: "LiFePO4", Napięcie: "Niskonapięciowy (LV)" },
    },
    {
      id: "felicity-512-hv", name: "Felicity 5,12 HV", tier: "Standard Plus", kind: "units", invBrand: "deye",
      unitPrice: 2700, unitKwh: 2.56, unitCapKwh: 5.12, maxUnits: 5, hvSurcharge: 1500, img: "assets/batteries/felicity-512-hv.png",
      params: { Producent: "Felicity", Model: "FLH48100UG1", Ogniwa: "LiFePO4", Napięcie: "Wysokonapięciowy (HV)" },
    },
    {
      id: "sigenstor-6", name: "SigenStor BAT", tier: "Premium", kind: "sigen-modular", invBrand: "sigen",
      img: "assets/batteries/sigenstor-6.png",
      params: { Producent: "Sigenergy", Model: "SigenStor BAT", Rozbudowa: "modułowo (6–36 kWh)", Napięcie: "Wysokonapięciowy (HV)" },
    },
  ],

  // Kody rabatowe → kwota (zł) odejmowana od ceny końcowej brutto
  discountCodes: { "SUPER1000": 1000 },

  // Koszt konstrukcji za moduł — wg typu montażu, z arkusza „dane wejściowe" (A10:B16).
  // Ceramika ma jednorazową dopłatę +600 (arkusz: IF F25=Ceramika, 600).
  konstrukcja: {
    grunt: 170,            // Grunt
    plaski: 210,           // Trójkąty na dach (dach płaski)
    ceramikaSurcharge: 600,
    covers: {
      "blachodachówka": 90,        // Blachodachówka
      "blacha trapezowa": 70,      // Trapez
      "dachówka ceramiczna": 190,  // Ceramika (+600 jednorazowo)
      "gont bitumiczny": 90,       // ⚠️ brak w arkuszu klienta — przyjęto jak blachodachówka
    },
  },

  // Stałe dodatki do falowników (ze specyfikacji biznesowej).
  inverterAddons: {
    sigenDodatek: 2200, // doliczane przy Sigen
    sigenWysylka: 0,
    deyeWysylka: 500,
    deyeEms: 945,       // EMS — Deye nie ma wbudowanego; opcjonalne dodatkowe urządzenie (doliczane gdy zaznaczone). Sigen ma EMS wbudowany za darmo.
  },

  // Robocizna netto wg mocy instalacji [kW] — kolumna „robocizna netto" (T) z arkusza (R5:T100, krok 0,5 kW).
  // Kwoty ZAWIERAJĄ ukrytą marżę (6–7 tys.). Dobór = największa stawka o kW ≤ moc instalacji (jak VLOOKUP przybliżony).
  robociznaNetto: [
    [2.5, 13975], [3, 14300], [3.5, 14625], [4, 14950], [4.5, 15275], [5, 15600], [5.5, 15925],
    [6, 16250], [6.5, 16575], [7, 17300], [7.5, 17625], [8, 17550], [8.5, 17850], [9, 18150],
    [9.5, 18450], [10, 18750], [10.5, 19050], [11, 20350], [11.5, 20650], [12, 20950], [12.5, 21250],
    [13, 21550], [13.5, 21850], [14, 22150], [14.5, 22450], [15, 22750], [15.5, 21500], [16, 21750],
    [16.5, 22000], [17, 22250], [17.5, 22500], [18, 22750], [18.5, 23000], [19, 23250], [19.5, 23500],
    [20, 23750], [20.5, 25000], [21, 25250], [21.5, 25500], [22, 25750], [22.5, 26000], [23, 26250],
    [23.5, 26500], [24, 26750], [24.5, 27000], [25, 27250], [25.5, 27500], [26, 27750], [26.5, 28000],
    [27, 28250], [27.5, 28500], [28, 28750], [28.5, 29000], [29, 29250], [29.5, 29500], [30, 29750],
    [30.5, 32000], [31, 32250], [31.5, 32500], [32, 32750], [32.5, 33000], [33, 33250], [33.5, 33500],
    [34, 33750], [34.5, 34000], [35, 34250], [35.5, 34500], [36, 34750], [36.5, 35000], [37, 35250],
    [37.5, 35500], [38, 35750], [38.5, 36000], [39, 36250], [39.5, 36500], [40, 36750], [40.5, 37000],
    [41, 37250], [41.5, 37500], [42, 37750], [42.5, 38000], [43, 38250], [43.5, 38500], [44, 38750],
    [44.5, 39000], [45, 39250], [45.5, 39500], [46, 39750], [46.5, 40000], [47, 40250], [47.5, 40500],
    [48, 40750], [48.5, 41000], [49, 41250], [49.5, 41500], [50, 41750],
  ],

  // Magazyn Sigen — cena bazowa za 6 kWh; skoki cyklicznie +2000/+2000/+4000 co 3 kWh, max 36 kWh.
  sigenBattBase6: 6000,

  /**
   * Linie falowników — 2 producentów. Klient wybiera linię; konkretny model (moc)
   * dobierany automatycznie do mocy instalacji, a jego cena netto pochodzi z arkusza
   * (zakładka „dane wejściowe", kolumny K/L). „hv" = model wysokonapięciowy.
   * @typedef {"sigen"|"deye"} InverterBrand
   */
  inverters: [
    {
      id: "deye-sun", name: "Deye SUN", brand: "deye", tier: "Standard",
      img: "assets/inverters/deye-sun.png",
      params: { Producent: "Deye", Seria: "SUN", Faza: "3-fazowy", Hybrydowy: "tak" },
      models: [
        { kw: 3.6, price: 2900 }, { kw: 5, price: 5650 }, { kw: 6, price: 5400 },
        { kw: 8, price: 5500 }, { kw: 10, price: 5600 }, { kw: 12, price: 6250 },
        { kw: 15, price: 6600 }, { kw: 20, price: 8800 }, { kw: 25, price: 7300, hv: true },
        { kw: 30, price: 12500, hv: true }, { kw: 40, price: 14400, hv: true }, { kw: 50, price: 15800, hv: true },
      ],
    },
    {
      id: "sigen-hybrid", name: "Sigen Hybrid", brand: "sigen", tier: "Premium",
      img: "assets/inverters/sigen-hybrid.png",
      params: { Producent: "Sigen", Seria: "Hybrid", Faza: "1-fazowy / 3-fazowy", Hybrydowy: "tak" },
      models: [
        { kw: 3.6, price: 700 }, { kw: 4, price: 1500 }, { kw: 5, price: 1600 },
        { kw: 6, price: 2000 }, { kw: 8, price: 2200 }, { kw: 10, price: 2500 }, { kw: 12, price: 2600 },
      ],
    },
    {
      id: "sigen-store", name: "Sigen Store DC", brand: "sigen", tier: "Premium",
      img: "assets/inverters/sigen-store.png",
      params: { Producent: "Sigen", Seria: "Store DC", Faza: "3-fazowy", Hybrydowy: "tak" },
      models: [
        { kw: 3.6, price: 3900 }, { kw: 5, price: 6000 }, { kw: 6, price: 6500 },
        { kw: 8, price: 6700 }, { kw: 10, price: 7000 }, { kw: 12, price: 8200 },
        { kw: 15, price: 9300 }, { kw: 17, price: 9700 }, { kw: 20, price: 10300 },
        { kw: 25, price: 12000 }, { kw: 30, price: 13000 },
      ],
    },
  ],
};

const ROOF_TYPES = ["dach skośny", "dach płaski"];
const ROOF_COVERS = ["blachodachówka", "blacha trapezowa", "dachówka ceramiczna", "gont bitumiczny"];
const ROOF_SPLITS = ["1 strona świata", "2 strony świata", "3 strony świata"];

const ROOF_GALLERY = [
  { id: "roof-blachodachowka", label: "blachodachówka", img: "blachodachowka" },
  { id: "roof-trapezowa", label: "blacha trapezowa", img: "trapezowa" },
  { id: "roof-ceramiczna", label: "dachówka ceramiczna", img: "ceramiczna" },
  { id: "roof-gont", label: "gont bitumiczny", img: "gont" },
];

// Przykładowe realizacje na gruncie — pokazywane w kroku 1 po wyborze „Na gruncie"
const GROUND_GALLERY = [
  { id: "ground-pruszcz", label: "Pruszcz Gdański", img: "pruszcz-1" },
  { id: "ground-dlugi-kat", label: "Długi Kąt — 9 kWp + magazyn", img: "dlugi-kat-1" },
  { id: "ground-wziachowo", label: "Wziąchowo Wielkie", img: "wziachowo-3" },
  { id: "ground-uherce", label: "Uherce Mineralne — 10,35 kWp + magazyn 10 kWh", img: "uherce-1" },
];

/* ── Obliczenia ─────────────────────────────────────────── */

/** Wymagana moc falownika (kW). Dopuszczalne przewymiarowanie paneli ~12% (DC/AC ≈ 1,12),
 *  więc 8–8,9 kWp dobiera falownik 8 kW, a od 9 kWp już 10 kW (smallest model ≥ tej wartości). */
function neededInverterKw(kwp) {
  return kwp / 1.12;
}

/** Koszt konstrukcji — stawka za moduł wg typu montażu × liczba paneli (+ dopłata ceramika). */
function konstrukcjaCost(s) {
  const K = PRICING.konstrukcja;
  let perModule;
  if (s.location === "grunt") perModule = K.grunt;
  else if (s.roofType === "dach płaski") perModule = K.plaski;
  else perModule = K.covers[s.roofCover] ?? K.covers["blachodachówka"];
  let cost = (s.panelCount || 0) * perModule;
  if (s.location === "dach" && s.roofType !== "dach płaski" && s.roofCover === "dachówka ceramiczna") {
    cost += K.ceramikaSurcharge;
  }
  return cost;
}

/** Robocizna netto wg mocy instalacji (kolumna T z arkusza; zawiera ukrytą marżę).
 *  Dobór jak VLOOKUP przybliżony: największa stawka o progu kW ≤ moc instalacji. */
function robociznaForKwp(kwp) {
  const tab = PRICING.robociznaNetto;
  let price = tab[0][1];
  for (const [kw, val] of tab) {
    if (kwp >= kw) price = val;
    else break;
  }
  return price;
}

/** Magazyn Sigen — modułowy 6..36 kWh co 3 kWh; skoki cyklicznie +2000/+2000/+4000 od bazy 6 kWh. */
function sigenBatteryPrice(kwh, base6 = PRICING.sigenBattBase6) {
  const clamped = Math.max(6, Math.min(36, Math.round(kwh / 3) * 3));
  const steps = (clamped - 6) / 3;
  let price = base6;
  // Cykl skoków co +3 kWh: +2 tys, +2 tys, +4 tys (co trzeci skok +4000, reszta +2000).
  for (let i = 1; i <= steps; i++) price += i % 3 === 0 ? 4000 : 2000;
  return price;
}

/** Wybrany magazyn energii. */
function selectedBattery(s) {
  return PRICING.batteries.find((b) => b.id === s.battId) || PRICING.batteries[0];
}

/** Pojemność magazynu Sigen wybrana suwakiem (6..36 kWh, co 3). */
function sigenKwh(s) {
  return Math.max(6, Math.min(36, Math.round((s.sigenKwh || 6) / 3) * 3));
}

/** Liczba sztuk magazynu (1..maxUnits) — dla magazynów na sztuki. */
function magazynUnits(s) {
  const b = selectedBattery(s);
  return Math.max(1, Math.min(b.maxUnits || 1, Math.round(s.battUnits || 1)));
}

/** Cena netto magazynu (z arkusza, kol. P: sztuki×unitPrice / przeskoki Sigen). */
function magazynPrice(s) {
  const b = selectedBattery(s);
  if (b.kind === "sigen-modular") return sigenBatteryPrice(sigenKwh(s));
  // Dopłata HV (np. Felicity 5,12 HV +1500) jest JEDNORAZOWA — niezależna od liczby sztuk.
  return magazynUnits(s) * (b.unitPrice || 0) + (b.hvSurcharge || 0);
}

/** Pojemność magazynu (kWh). */
function magazynKwh(s) {
  const b = selectedBattery(s);
  if (b.kind === "sigen-modular") return sigenKwh(s);
  return magazynUnits(s) * (b.unitKwh || 0);
}

/** Moc magazynu (kW) — połowa pojemności. */
function magazynKw(s) {
  return magazynKwh(s) / 2;
}

/** Pojemność magazynu (kWh) — z nazwy: sztuki × unitCapKwh; dla Sigen = wartość suwaka. */
function magazynCapKwh(s) {
  const b = selectedBattery(s);
  if (b.kind === "sigen-modular") return sigenKwh(s);
  return magazynUnits(s) * (b.unitCapKwh || 0);
}

/** Format kWh/kW z dokładnością do 2 miejsc, bez zer końcowych (2,56 → „2,56"; 6 → „6"). */
const fmtKwh = (n) => new Intl.NumberFormat("pl-PL", { maximumFractionDigits: 2 }).format(n);

/**
 * @typedef {Object} Quote
 * @property {number} kwp
 * @property {number} konstrukcja
 * @property {number} inverterNet      cena netto modelu falownika (wg mocy)
 * @property {number} inverterKw       dobrana moc falownika
 * @property {number} addons           dodatki: wysyłka + sigen/EMS
 * @property {number} robocizna        zawiera ukrytą marżę
 * @property {number} net
 * @property {number} grossBeforeDiscount
 * @property {number} discount
 * @property {number} gross
 * @property {number} vatRate
 */

/** @returns {Quote} */
function computeQuote(s) {
  const panel = PRICING.panels.find((p) => p.id === s.panelId) || PRICING.panels[1];
  const line = selectedInverter(s);
  const model = selectedInverterModel(s);
  const kwp = (s.panelCount * panel.watt) / 1000;

  // Konstrukcja: stawka za moduł wg typu montażu × liczba paneli (+ dopłata ceramika)
  const konstrukcja = konstrukcjaCost(s);

  // Falownik + dodatki producenta
  const A = PRICING.inverterAddons;
  const inverterNet = model.price;
  const addons = line.brand === "sigen"
    ? A.sigenDodatek + A.sigenWysylka  // Sigen: EMS wbudowany, bez dopłat
    : A.deyeWysylka + (s.emsAddon ? A.deyeEms : 0);  // Deye: EMS opcjonalny (dodatkowe urządzenie)

  // Robocizna (ukryta marża w środku)
  const robocizna = robociznaForKwp(kwp);

  // Magazyn energii — wg rodzaju (arkusz: kol. P / przeskoki Sigen).
  const batt = selectedBattery(s);
  const magazyn = magazynPrice(s);

  // Panele — wartość zastępcza (ceny paneli podmienimy później).
  const panele = kwp * panel.pricePerKwp;

  // Grunt nie ma osobnej dopłaty — koszt gruntu to konstrukcja 170 zł/moduł (jak w arkuszu klienta).
  let net = panele + konstrukcja + inverterNet + addons + robocizna + magazyn;

  const vatRate = PRICING.vat[s.vat] ?? PRICING.vat.individual;
  const grossBeforeDiscount = net * (1 + vatRate);
  const code = String(s.kod || "").trim().toUpperCase();
  const discount = (PRICING.discountCodes[code] || 0);
  const gross = Math.max(0, grossBeforeDiscount - discount);

  return {
    panel, line, model, batt, kwp, konstrukcja, inverterNet, inverterKw: model.kw, addons,
    robocizna, magazyn, magazynKwh: magazynKwh(s), magazynKw: magazynKw(s), magazynCap: magazynCapKwh(s), magazynUnits: magazynUnits(s),
    net, vatRate, grossBeforeDiscount, discount, code: discount ? code : "", gross,
  };
}

/** Linia falownika dobierana automatycznie po wybranym magazynie (Sigen→Sigen, reszta→Deye). */
function autoInverterId(s) {
  const b = selectedBattery(s);
  return b && b.invBrand === "sigen" ? "sigen-hybrid" : "deye-sun";
}
/** Wybrana linia falownika (lub auto). */
function selectedInverter(s) {
  const id = s.invId && s.invId !== "auto" ? s.invId : autoInverterId(s);
  return PRICING.inverters.find((i) => i.id === id) || PRICING.inverters[0];
}
/** Konkretny model (moc) w wybranej linii — ręcznie (s.invKw) lub dobierany do mocy instalacji. */
function selectedInverterModel(s) {
  const line = selectedInverter(s);
  if (s.invKw != null) {
    const manual = line.models.find((m) => m.kw === s.invKw);
    if (manual) return manual;
  }
  const panel = PRICING.panels.find((p) => p.id === s.panelId) || PRICING.panels[1];
  const kwp = (s.panelCount * panel.watt) / 1000;
  const need = neededInverterKw(kwp);
  return line.models.find((m) => m.kw >= need) || line.models[line.models.length - 1];
}

/* Render konfiguracji — pokazuje na żywo wybrany panel, magazyn i falownik (zdjęcia). */
function ConfigRender({ q, sticky }) {
  const items = [
    { label: "Panel", name: q.panel.name, sub: q.panel.params.Moc, img: q.panel.img },
    { label: "Magazyn", name: q.batt.name, sub: "Pojemność " + fmtKwh(q.magazynCap) + " kWh", img: q.batt.img },
    { label: "Falownik", name: q.line.name + " " + fmtNum(q.model.kw) + " kW" + (q.model.hv ? " HV" : ""), sub: q.line.params.Producent, img: q.line.img },
  ];
  return (
    <div className={`config-render ${sticky ? "config-render-sticky" : ""}`}>
      <div className="config-render-title">Twoja konfiguracja</div>
      {items.map((it) => (
        <div key={it.label} className="config-render-item">
          <div className="config-render-thumb">
            {it.img ? <img src={it.img} alt={it.name} loading="lazy" /> : <span className="config-render-ph"><Ic.Bolt size={22} /></span>}
          </div>
          <div className="config-render-meta">
            <span className="crl">{it.label}</span>
            <span className="crn">{it.name}</span>
            {it.sub ? <span className="crs">{it.sub}</span> : null}
          </div>
        </div>
      ))}
    </div>
  );
}

/* ── Stepper ────────────────────────────────────────────── */
const STEPS = ["Umiejscowienie", "Parametry", "Cena"];
function Stepper({ step, goStep }) {
  return (
    <div className="stepper" role="navigation" aria-label="Postęp konfiguracji">
      {STEPS.map((label, i) => {
        const n = i + 1;
        const state = n < step ? "done" : n === step ? "active" : "";
        return (
          <React.Fragment key={n}>
            {i > 0 ? <div className={`step-line ${n <= step ? "done" : ""}`}></div> : null}
            <div className={`step-node ${state}`}>
              <button onClick={() => n < step && goStep(n)} disabled={n >= step}
                      aria-label={`Krok ${n}: ${label}${n < step ? " (ukończony — kliknij aby edytować)" : ""}`}
                      aria-current={n === step ? "step" : undefined}
                      style={{ cursor: n < step ? "pointer" : "default" }}>
                <span className="step-circle">{n < step ? <Ic.Check size={18} /> : n}</span>
                <span className="step-label">{label}</span>
              </button>
            </div>
          </React.Fragment>
        );
      })}
    </div>
  );
}

/* ── Suwak z wypełnieniem ───────────────────────────────── */
function OzeSlider({ value, min, max, step = 1, onChange, label, ariaLabel }) {
  const fill = ((value - min) / (max - min)) * 100;
  return (
    <input type="range" className="oze-slider" min={min} max={max} step={step} value={value}
           aria-label={ariaLabel || label}
           style={{ "--fill": `${fill}%` }}
           onChange={(e) => onChange(Number(e.target.value))} />
  );
}

/* Ręczne wpisanie wartości — alternatywa dla suwaka.
   Pozwala swobodnie wpisać dowolną liczbę (też tymczasowo poniżej min, np. „3" w drodze do „32");
   klampowanie do przedziału [min,max] i kroku następuje dopiero przy opuszczeniu pola lub Enter. */
function ManualNumber({ value, min, max, step = 1, onChange, label, unit }) {
  const [draft, setDraft] = wUseState(String(value));
  const focusedRef = wUseRef(false);
  // Synchronizuj z zewnątrz tylko gdy pole NIE jest aktywnie edytowane —
  // inaczej przeliczenie kalkulacji nadpisałoby cyfrę w trakcie wpisywania.
  wUseEffect(() => { if (!focusedRef.current) setDraft(String(value)); }, [value]);
  const clamp = (n) => Math.max(min, Math.min(max, Math.round(n / step) * step));
  const handleChange = (e) => {
    const raw = e.target.value;
    setDraft(raw);
    if (raw === "" || Number.isNaN(Number(raw))) return;
    onChange(clamp(Number(raw))); // natychmiastowa aktualizacja wyceny w trakcie pisania
  };
  const commit = () => {
    focusedRef.current = false;
    if (draft === "" || Number.isNaN(Number(draft))) { setDraft(String(value)); return; }
    const v = clamp(Number(draft));
    setDraft(String(v)); // po opuszczeniu pola „dociągnij" do prawidłowej wartości (zakres/krok)
    onChange(v);
  };
  return (
    <label className="manual-input">
      <span>{label || "lub wpisz ręcznie"}</span>
      <span className="manual-input-box">
        <input type="number" inputMode="numeric" min={min} max={max} step={step} value={draft}
               onFocus={() => { focusedRef.current = true; }}
               onChange={handleChange}
               onBlur={commit}
               onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); e.currentTarget.blur(); } }} />
        {unit ? <em>{unit}</em> : null}
      </span>
    </label>
  );
}

/* ── Karta produktu ─────────────────────────────────────── */
function ProductCard({ item, selected, onSelect, slotPrefix, slotH = 120, badge = null, disabled = false }) {
  return (
    <button className={`product-card ${selected ? "selected" : ""} ${disabled ? "is-disabled" : ""}`}
            onClick={disabled ? undefined : onSelect} aria-pressed={selected} aria-disabled={disabled} disabled={disabled}>
      {badge ? <span className="product-auto-badge">{badge}</span> : null}
      <span className="tile-check"><Ic.Check /></span>
      <image-slot id={`${slotPrefix}-${item.id}`} style={{ width: "100%", height: slotH + "px", display: "block" }}
                  radius="8" fit="contain" src={item.img || undefined} placeholder={`Zdjęcie: ${item.name}`}></image-slot>
      <span className="product-name">{item.name}</span>
      <span className={`tier-badge ${item.tier === "Premium" ? "premium" : ""}`}>
        {item.tier === "Premium" ? <Ic.Star /> : null}{item.tier}
      </span>
    </button>
  );
}

/* ── KROK 1 — Umiejscowienie ────────────────────────────── */
function Step1({ s, set, onNext }) {
  const [lightbox, setLightbox] = wUseState(null);
  const dach = s.location === "dach";
  const skosny = s.roofType === "dach skośny";
  // Dach płaski → od razu dalej. Dach skośny → wymagane pokrycie + podział instalacji.
  const canNext = s.location === "grunt" ||
    (dach && s.roofType && (!skosny || (s.roofCover && s.roofSplit)));
  return (
    <div className="step-panel">
      <div className="wizard-grid">
        <div style={{ display: "flex", flexDirection: "column", gap: 20 }}>
          <div className="card card-pad" style={{ display: "flex", flexDirection: "column", gap: 20 }}>
            <div className="tile-row" role="radiogroup" aria-label="Umiejscowienie instalacji">
              <button className={`choice-tile ${dach ? "selected" : ""}`} role="radio" aria-checked={dach}
                      onClick={() => set({ location: "dach" })}>
                <span className="tile-check"><Ic.Check /></span>
                <Ic.Roof /> <span style={{ whiteSpace: "nowrap" }}>Na dachu</span>
              </button>
              <button className={`choice-tile ${s.location === "grunt" ? "selected" : ""}`} role="radio" aria-checked={s.location === "grunt"}
                      onClick={() => set({ location: "grunt" })}>
                <span className="tile-check"><Ic.Check /></span>
                <Ic.Ground /> <span style={{ whiteSpace: "nowrap" }}>Na gruncie</span>
              </button>
            </div>

            {dach ? (
              <div className="step-panel" style={{ display: "grid", gap: 4 }}>
                <Field label="Rodzaj dachu" name="roofType" options={ROOF_TYPES} value={s.roofType} onChange={(v) => set({ roofType: v })} />
                {skosny ? (
                  <React.Fragment>
                    <Field label="Pokrycie dachu" name="roofCover" options={ROOF_COVERS} value={s.roofCover} onChange={(v) => set({ roofCover: v })} />
                    <Field label="Podział instalacji" name="roofSplit" options={ROOF_SPLITS} value={s.roofSplit} onChange={(v) => set({ roofSplit: v })} />
                  </React.Fragment>
                ) : (
                  <p style={{ margin: "6px 2px 0", fontSize: "0.92rem", color: "var(--muted)" }}>
                    Na dachu płaskim dobierzemy montaż automatycznie — przejdź dalej, aby dobrać parametry.
                  </p>
                )}
              </div>
            ) : null}
          </div>

          {dach ? (
            <div className="hint-strip">
              <strong style={{ fontFamily: "var(--font-head)" }}>Podpowiedź</strong>
              <p style={{ margin: "4px 0 0", fontSize: "0.92rem", color: "var(--muted)" }}>
                Przejdź dalej, aby dobrać parametry instalacji. Nie jesteś pewny, jakie masz pokrycie dachowe? Sprawdź! Kliknij zdjęcie, aby powiększyć.
              </p>
              <div className="roof-gallery">
                {ROOF_GALLERY.map((r) => (
                  <button key={r.id} className="roof-tile"
                          onClick={() => setLightbox({ src: `assets/roof/${r.img}-lg.jpg`, srcWebp: `assets/roof/${r.img}-lg.webp`, caption: r.label })}
                          aria-label={`Powiększ zdjęcie: ${r.label}`}>
                    <figure>
                      <image-slot id={r.id} src={`assets/roof/${r.img}.jpg`}
                                  style={{ width: "100%", height: "92px", display: "block" }} radius="10" placeholder={r.label}></image-slot>
                    </figure>
                    <figcaption>{r.label}</figcaption>
                  </button>
                ))}
              </div>
            </div>
          ) : (
            <div className="hint-strip">
              <strong style={{ fontFamily: "var(--font-head)" }}>4 przykładowe realizacje na gruncie</strong>
              <p style={{ margin: "4px 0 0", fontSize: "0.92rem", color: "var(--muted)" }}>
                Zobacz nasze instalacje gruntowe. Kliknij zdjęcie, aby powiększyć.
              </p>
              <div className="roof-gallery">
                {GROUND_GALLERY.map((g) => (
                  <button key={g.id} className="roof-tile"
                          onClick={() => setLightbox({ src: `assets/real/${g.img}-lg.jpg`, srcWebp: `assets/real/${g.img}-lg.webp`, caption: g.label })}
                          aria-label={`Powiększ zdjęcie: ${g.label}`}>
                    <figure>
                      <picture>
                        <source srcSet={`assets/real/${g.img}.webp`} type="image/webp" />
                        <img src={`assets/real/${g.img}.jpg`} alt={`Realizacja na gruncie — ${g.label}`}
                             loading="lazy" width="760" height="500"
                             style={{ width: "100%", height: "92px", objectFit: "cover", display: "block", borderRadius: "10px" }} />
                      </picture>
                    </figure>
                    <figcaption>{g.label}</figcaption>
                  </button>
                ))}
              </div>
            </div>
          )}

          <div style={{ display: "flex", justifyContent: "flex-end" }}>
            <button className="btn btn-primary btn-lg" disabled={!canNext} onClick={onNext}>
              Dalej <Ic.ArrowR />
            </button>
          </div>
        </div>

        <aside className="wizard-aside">
          <picture>
            <source srcSet={s.location === "grunt" ? "assets/calc-ground.webp" : "assets/calc-house.webp"} type="image/webp" />
            <img src={s.location === "grunt" ? "assets/calc-ground.jpg" : "assets/calc-house.jpg"}
                 alt={s.location === "grunt" ? "Instalacja fotowoltaiczna na gruncie" : "Dom z panelami fotowoltaicznymi na dachu"}
                 style={{ width: "100%", aspectRatio: "16 / 9", objectFit: "cover", display: "block", borderRadius: "20px" }} />
          </picture>
        </aside>
      </div>
      {lightbox ? <Lightbox src={lightbox.src} srcWebp={lightbox.srcWebp} caption={lightbox.caption} onClose={() => setLightbox(null)} /> : null}
    </div>
  );
}

/* ── KROK 2 — Parametry ─────────────────────────────────── */
function Step2({ s, set, onNext, onBack, go }) {
  const q = computeQuote(s);
  const inv = selectedInverter(s);
  const autoMode = !s.invId || s.invId === "auto";
  const autoId = autoInverterId(s);
  const battBrand = q.batt.invBrand; // dozwolona marka falownika dla wybranego magazynu
  const goPremium = () => {
    if (go) go("start");
    setTimeout(() => scrollToId("standard-premium"), 80);
  };
  return (
    <div className="step-panel">
      <div className="wizard-grid">
        <div style={{ display: "flex", flexDirection: "column", gap: 20 }}>

          {/* Baner: różnice standard vs premium */}
          <button type="button" className="premium-banner" onClick={goPremium}>
            <span className="premium-banner-ic"><Ic.Star size={16} /></span>
            <span>Chcesz poznać więcej różnic między instalacją <b>standard</b> i <b>premium</b>? <u>Kliknij tutaj, aby dowiedzieć się więcej.</u></span>
            <Ic.ArrowR size={16} />
          </button>

          {/* SEKCJA A — Panele */}
          <section className="card card-pad" style={{ display: "flex", flexDirection: "column", gap: 18, order: 1 }} aria-labelledby="sec-panels">
            <h3 id="sec-panels" style={{ display: "flex", alignItems: "center", gap: 10 }}><Ic.Panel /> Panele fotowoltaiczne</h3>
            <div className="product-grid-2">
              {PRICING.panels.map((p) => (
                <ProductCard key={p.id} item={p} slotPrefix="panel" selected={s.panelId === p.id}
                             onSelect={() => set({ panelId: p.id })} />
              ))}
            </div>
            <div className="slider-block">
              <div className="slider-label">
                Wybierz moc instalacji
                <InfoTip text="kWp (kilowat peak) to moc szczytowa instalacji. 1 kWp produkuje w Polsce ok. 1000 kWh energii rocznie." />
              </div>
              <OzeSlider value={s.panelCount} min={6} max={60} onChange={(v) => set({ panelCount: v })}
                         ariaLabel="Moc instalacji — liczba paneli" />
              <ManualNumber value={s.panelCount} min={6} max={60} onChange={(v) => set({ panelCount: v })}
                            label="lub wpisz liczbę paneli" unit="szt." />
              <div className="stat-pair">
                <div className="stat-box"><div className="k">Moc instalacji</div><div className="v"><CountUp value={q.kwp} format={(v) => fmtNum(v) + " kWp"} /></div></div>
                <div className="stat-box"><div className="k">Ilość paneli</div><div className="v"><CountUp value={s.panelCount} format={(v) => Math.round(v) + " szt."} /></div></div>
              </div>
            </div>
            <div>
              <div className="slider-label" style={{ marginBottom: 8 }}>
                Parametry modułu
                <InfoTip text="Dane wybranego modułu fotowoltaicznego — przydatne przy ocenie, ile paneli zmieści się na dachu." />
              </div>
              <dl className="params-list">
                {Object.entries(q.panel.params).map(([k, v]) => (
                  <div key={k}><dt>{k}</dt><dd>{v}</dd></div>
                ))}
              </dl>
            </div>
          </section>

          {/* SEKCJA B — Falownik (auto-dobór + ręczny wybór) */}
          <section className="card card-pad" style={{ display: "flex", flexDirection: "column", gap: 18, order: 3 }} aria-labelledby="sec-inv">
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap" }}>
              <h3 id="sec-inv" style={{ display: "flex", alignItems: "center", gap: 10 }}><Ic.Bolt size={22} /> Falownik</h3>
              <span className={`inv-mode ${!autoMode ? "manual" : ""}`}>
                {!autoMode
                  ? <React.Fragment>Wybór ręczny · <button type="button" onClick={() => set({ invId: "auto", invKw: null })}>wróć do automatycznego</button></React.Fragment>
                  : "Linia falownika dobierana automatycznie do wybranego magazynu; moc (model) dopasowujemy do wielkości instalacji. Możesz wybrać ręcznie poniżej."}
              </span>
            </div>
            <div className="product-grid">
              <button type="button" className={`product-card inv-auto-card ${autoMode ? "selected" : ""}`}
                      onClick={() => set({ invId: "auto", invKw: null })} aria-pressed={autoMode}>
                <span className="tile-check"><Ic.Check /></span>
                <span className="inv-auto-ic"><Ic.Bolt size={30} /></span>
                <span className="product-name">Wybierz automatycznie</span>
                <span className="tier-badge">Zalecane</span>
              </button>
              {PRICING.inverters.map((iv) => (
                <ProductCard key={iv.id} item={iv} slotPrefix="inv"
                             selected={!autoMode && inv.id === iv.id}
                             disabled={iv.brand !== battBrand}
                             badge={autoMode && iv.id === autoId ? "Dobrany automatycznie" : null}
                             onSelect={() => set({ invId: iv.id, invKw: null })} />
              ))}
            </div>

            {/* Ręczny wybór mocy (modelu) — z oznaczeniem napięcia LV/HV */}
            {!autoMode && (
              <div>
                <div className="slider-label" style={{ marginBottom: 8 }}>
                  Wybierz moc falownika
                  <InfoTip text="Domyślnie dobieramy moc do wielkości instalacji. Tu możesz ustawić ją ręcznie." />
                </div>
                <div className="moc-chips">
                  {inv.models.map((m) => (
                    <button key={m.kw} type="button"
                            className={`moc-chip ${q.model.kw === m.kw ? "selected" : ""}`}
                            onClick={() => set({ invKw: m.kw })}>
                      <span className="moc-kw">{fmtNum(m.kw)} kW</span>
                      {inv.brand === "deye" ? <span className={`moc-volt ${m.hv ? "hv" : "lv"}`}>{m.hv ? "HV · wysokie" : "LV · niskie"}<br /><small>napięciowy</small></span> : null}
                    </button>
                  ))}
                </div>
              </div>
            )}

            <div className="stat-pair">
              <div className="stat-box"><div className="k">Dobrany model</div><div className="v">{inv.name} {fmtNum(q.model.kw)} kW{q.model.hv ? " HV" : ""}</div></div>
              <div className="stat-box"><div className="k">Wymagana moc falownika</div><div className="v">{fmtNum(neededInverterKw(q.kwp))} kW</div></div>
            </div>
            <div>
              <div className="slider-label" style={{ marginBottom: 8 }}>Parametry falownika</div>
              <dl className="params-list">
                {Object.entries(inv.params).map(([k, v]) => (
                  <div key={k}><dt>{k}</dt><dd>{v}</dd></div>
                ))}
                {inv.brand === "deye" ? (
                  <div><dt>Napięcie</dt><dd>{q.model.hv ? "Wysokonapięciowy (HV)" : "Niskonapięciowy (LV)"}</dd></div>
                ) : null}
                <div><dt>EMS</dt><dd>{inv.brand === "sigen" ? "Tak — wbudowany" : (s.emsAddon ? "Tak (dodatkowe urządzenie +945 zł netto)" : "Opcjonalnie (dodatkowe urządzenie +945 zł netto)")}</dd></div>
              </dl>
            </div>

            {inv.brand === "deye" && (
              <label className="vat-option" style={{ cursor: "pointer", alignItems: "flex-start" }}>
                <input type="checkbox" checked={!!s.emsAddon} onChange={(e) => set({ emsAddon: e.target.checked })}
                       style={{ marginTop: 4 }} />
                <span>Dodaj EMS <strong>(+945 zł netto)</strong>
                  <InfoTip text="Falownik Deye nie ma wbudowanego EMS — można dołożyć dodatkowe urządzenie. EMS (Energy Management System) inteligentnie zarządza energią: steruje przepływem prądu między PV, siecią i odbiornikami, zwiększa autokonsumpcję i umożliwia korzystanie z taryf dynamicznych. Opcja dobrowolna." />
                </span>
              </label>
            )}

            {inv.brand === "sigen" && (
              <div className="hint-strip">
                <p style={{ margin: 0, fontSize: "0.92rem", color: "var(--muted)" }}>
                  <b>EMS:</b> falownik Sigen ma moduł EMS <b>wbudowany na stałe</b> — bez dopłat.
                </p>
              </div>
            )}

            {inv.brand === "deye" && (
              <div className="hint-strip">
                <p style={{ margin: 0, fontSize: "0.92rem", color: "var(--muted)" }}>
                  <b>Napięcie:</b> modele <b>do 20 kW</b> to wersje <b>LV (niskonapięciowe)</b>, a <b>powyżej 20 kW</b> (SUN-25K i większe) — <b>HV (wysokonapięciowe)</b>.
                </p>
              </div>
            )}
          </section>

          {/* SEKCJA C — Magazyn energii */}
          <section className="card card-pad" style={{ display: "flex", flexDirection: "column", gap: 18, order: 2 }} aria-labelledby="sec-batt">
            <h3 id="sec-batt" style={{ display: "flex", alignItems: "center", gap: 10 }}><Ic.Battery /> Magazyn energii</h3>
            <div className="product-grid">
              {PRICING.batteries.map((b) => (
                <ProductCard key={b.id} item={b} slotPrefix="mag" selected={s.battId === b.id}
                             onSelect={() => set({ battId: b.id, invId: "auto", invKw: null })} />
              ))}
            </div>
            {q.batt.kind === "sigen-modular" ? (
              <div className="slider-block">
                <div className="slider-label">Pojemność magazynu SigenStor
                  <InfoTip text="Magazyn modułowy SigenStor — od 6 do 36 kWh, rozbudowa co 3 kWh. Cena rośnie skokowo." />
                </div>
                <OzeSlider value={sigenKwh(s)} min={6} max={36} step={3} onChange={(v) => set({ sigenKwh: v })}
                           ariaLabel="Pojemność magazynu SigenStor (kWh)" />
                <ManualNumber value={sigenKwh(s)} min={6} max={36} step={3} onChange={(v) => set({ sigenKwh: v })}
                              label="lub wpisz pojemność" unit="kWh" />
              </div>
            ) : (
              <div className="slider-block">
                <div className="slider-label">Liczba sztuk magazynu
                  <InfoTip text="Magazyn dobierany na sztuki — moc i cena rosną z każdą sztuką." />
                </div>
                <OzeSlider value={magazynUnits(s)} min={1} max={q.batt.maxUnits || 1} step={1} onChange={(v) => set({ battUnits: v })}
                           ariaLabel="Liczba sztuk magazynu" />
                <ManualNumber value={magazynUnits(s)} min={1} max={q.batt.maxUnits || 1} step={1} onChange={(v) => set({ battUnits: v })}
                              label="lub wpisz liczbę sztuk" unit="szt." />
              </div>
            )}
            <div className="stat-pair">
              {q.batt.kind === "sigen-modular" ? (
                <>
                  <div className="stat-box"><div className="k">Moc</div><div className="v">{fmtKwh(q.magazynKw)} kW</div></div>
                  <div className="stat-box"><div className="k">Pojemność</div><div className="v">{fmtKwh(q.magazynKwh)} kWh</div></div>
                </>
              ) : (
                <>
                  <div className="stat-box"><div className="k">Moc</div><div className="v">{fmtKwh(q.magazynKwh)} kWh</div></div>
                  <div className="stat-box"><div className="k">Pojemność</div><div className="v">{fmtKwh(q.magazynCap)} kWh</div></div>
                </>
              )}
            </div>
            <div>
              <div className="slider-label" style={{ marginBottom: 8 }}>Parametry magazynu</div>
              <dl className="params-list">
                {Object.entries(q.batt.params).map(([k, v]) => (
                  <div key={k}><dt>{k}</dt><dd>{v}</dd></div>
                ))}
              </dl>
            </div>
          </section>

          <div style={{ display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap", order: 4 }}>
            <button className="btn btn-ghost" onClick={onBack}>← Wstecz</button>
            <button className="btn btn-primary btn-lg" onClick={onNext}>Dalej <Ic.ArrowR /></button>
          </div>
        </div>

        <aside className="wizard-aside">
          <ConfigRender q={q} sticky />
        </aside>
      </div>
    </div>
  );
}

/* ── KROK 3 — Cena ──────────────────────────────────────── */
function Step3({ s, set, onBack, go }) {
  const q = computeQuote(s);
  const inv = selectedInverter(s);
  const [gateOpen, setGateOpen] = wUseState(false);

  // Buduje dane oferty PDF z aktualnej konfiguracji (re-używane: pobranie ORAZ przyszła wysyłka mailem).
  const buildOfferData = (name) => ({
    name: name || "",
    opiekun: "Mateusz Zwierzchowski",
    netto: fmtPLN(q.net),
    brutto: fmtPLN(q.gross),
    modul:    { rodzaj: q.panel.name, moc: q.panel.params.Moc, ilosc: s.panelCount + " szt" },
    magazyn:  { rodzaj: q.batt.name, moc: fmtKwh(q.magazynKwh) + " kWh", ilosc: q.magazynUnits + " szt" },
    falownik: { rodzaj: inv.name, moc: fmtNum(q.model.kw) + " kW" + (q.model.hv ? " HV" : ""), ilosc: "1 szt" },
    mont: s.location === "grunt" ? "grunt" : (s.roofType === "dach płaski" ? "dach płaski" : (s.roofCover || "dach")),
    filename: "Oferta-fotowoltaika-" + Math.round(q.kwp * 10) / 10 + "kWp",
  });

  return (
    <div className="step-panel" style={{ display: "flex", flexDirection: "column", gap: 24 }}>
      <div className="card card-pad" style={{ display: "flex", flexDirection: "column", gap: 22 }}>
        <div className="summary-grid">
          <div className="summary-tile"><span className="k">Moc instalacji</span><span className="v"><CountUp value={q.kwp} format={(v) => fmtNum(v) + " kWp"} /></span></div>
          <div className="summary-tile"><span className="k">Ilość paneli</span><span className="v"><CountUp value={s.panelCount} format={(v) => Math.round(v) + " szt."} /></span></div>
          {q.batt.kind === "sigen-modular"
            ? <div className="summary-tile"><span className="k">Moc magazynu</span><span className="v"><CountUp value={q.magazynKw} format={(v) => fmtKwh(v) + " kW"} /></span></div>
            : <div className="summary-tile"><span className="k">Moc magazynu</span><span className="v"><CountUp value={q.magazynKwh} format={(v) => fmtKwh(v) + " kWh"} /></span></div>}
          <div className="summary-tile"><span className="k">Pojemność magazynu</span><span className="v"><CountUp value={q.magazynCap} format={(v) => fmtKwh(v) + " kWh"} /></span></div>
          <div className="summary-tile"><span className="k">Falownik</span><span className="v">{inv.name}</span></div>
          <div className="summary-tile">
            <span className="k" style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>Moc falownika
              <InfoTip text="Falownik dobierany automatycznie do mocy instalacji — dopuszczalne przewymiarowanie paneli ok. 12% (np. 8–8,9 kWp → 8 kW, od 9 kWp → 10 kW). Moc możesz też wybrać ręcznie." />
            </span>
            <span className="v">{fmtNum(q.inverterKw)} kW{q.model.hv ? " HV" : ""}</span>
          </div>
        </div>

        <div className="vat-row" role="radiogroup" aria-label="Typ klienta">
          <button className={`vat-option ${s.vat === "individual" ? "selected" : ""}`} role="radio" aria-checked={s.vat === "individual"}
                  onClick={() => set({ vat: "individual" })}>
            <span className="radio-dot"></span>
            <span>Klient indywidualny<br /><small style={{ color: "var(--muted)" }}>VAT 8%</small></span>
          </button>
          <button className={`vat-option ${s.vat === "business" ? "selected" : ""}`} role="radio" aria-checked={s.vat === "business"}
                  onClick={() => set({ vat: "business" })}>
            <span className="radio-dot"></span>
            <span>Klient biznesowy<br /><small style={{ color: "var(--muted)" }}>VAT 23%</small></span>
          </button>
        </div>

        <div className="price-hero">
          <div>
            <div className="note">Cena brutto (VAT {Math.round(q.vatRate * 100)}%) — wprost od instalatora</div>
            <div className="amount" aria-live="polite"><CountUp value={q.gross} format={fmtPLN} duration={600} /></div>
            {q.discount ? (
              <div className="price-discount">
                <span className="price-was">{fmtPLN(q.grossBeforeDiscount)}</span>
                <span className="price-save"><Ic.Check size={13} /> Rabat {q.code}: −{fmtPLN(q.discount)}</span>
              </div>
            ) : null}
          </div>
          <div className="rata-badge" role="status">
            <span className="rata-shine" aria-hidden="true"></span>
            <span className="rata-k">Rata już od</span>
            <span className="rata-v"><CountUp value={q.gross * 1.55 / 119} format={(v) => fmtPLN(v) + "/mc"} duration={600} /></span>
            <span className="rata-note">Skontaktuj się — przeliczymy dokładnie.</span>
          </div>
          <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
            <a className="btn btn-amber" href="#kontakt-form" onClick={(e) => { e.preventDefault(); scrollToId("kontakt-form"); }}>
              Skontaktuj się z nami
            </a>
            <button className="btn btn-secondary" style={{ background: "rgba(255,255,255,0.94)" }}
                    onClick={() => setGateOpen(true)}>
              Pobierz ofertę PDF <Ic.ArrowR />
            </button>
          </div>
        </div>

        <div className="price-disclaimer">
          <p style={{ margin: 0 }}>
            Cena nie zawiera prac dodatkowych (przekopy, przewierty itp.).
          </p>
          <p style={{ margin: "6px 0 0" }}>
            Niniejszy cennik/dokument ma charakter informacyjny i nie stanowi oferty w rozumieniu art. 66 § 1 Kodeksu
            Cywilnego oraz innych właściwych przepisów prawnych. Podane ceny mają charakter wyłącznie orientacyjny i mogą
            ulec zmianie.
          </p>
        </div>

        <p style={{ margin: 0, color: "var(--muted)", textAlign: "center" }}>
          Jeżeli chcesz zindywidualizować ofertę lub porozmawiać z technikiem, wypełnij poniższy formularz.
        </p>

        <div style={{ display: "flex", justifyContent: "flex-start" }}>
          <button className="btn btn-ghost" onClick={onBack}>← Wstecz</button>
        </div>
      </div>

      {/* Formularz na tle zdjęcia paneli */}
      <div className="form-photo-wrap" id="kontakt-form">
        <image-slot id="form-bg" src="assets/form-bg.jpg" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", display: "block" }}
                    radius="0" placeholder="Zdjęcie paneli (tło formularza)"></image-slot>
        <div className="overlay"></div>
        <div className="form-photo-content">
          <div className="split-2">
            <div style={{ color: "#fff", display: "flex", flexDirection: "column", gap: 12, justifyContent: "center" }}>
              <h2 className="h-lg" style={{ color: "#fff" }}>Skontaktuj się z nami!</h2>
              <p style={{ color: "#D7E4DC", margin: 0 }}>
                Otrzymasz podsumowanie konfiguracji na maila i bezpłatną konsultację z technikiem — bez zobowiązań.
              </p>
              <div style={{ display: "flex", alignItems: "center", gap: 10, fontWeight: 700 }}><Ic.Phone /> 798-040-369</div>
              <div style={{ display: "flex", alignItems: "center", gap: 10, color: "#D7E4DC" }}><Ic.Clock /> pn–pt 10:00–16:00</div>
              <ConfigRender q={q} />
            </div>
            <LeadForm context={{ typ: "kreator", konfiguracja: {
                        kwp: q.kwp, panele: s.panelCount, panelModel: q.panel.name,
                        magazynKwh: q.magazynKwh, magazynCap: q.magazynCap, magazynModel: q.batt.name,
                        falownikKw: q.inverterKw,
                        falownikModel: `${inv.name} ${fmtNum(q.model.kw)} kW${q.model.hv ? " HV" : ""}`,
                        ems: inv.brand === "sigen" ? "tak — wbudowany na stałe" : (s.emsAddon ? "tak (+945 zł netto)" : "nie"),
                        klientTyp: s.vat === "business" ? "biznesowy" : "indywidualny",
                        vatProc: Math.round(q.vatRate * 100), rabatKod: q.code, rabatKwota: q.discount, cenaBrutto: Math.round(q.gross),
                      } }}
                      kod={s.kod} onKodChange={(val) => set({ kod: val })}
                      doubleConsent submitLabel="WYŚLIJ" />
          </div>
        </div>
      </div>

      {gateOpen ? <OfferGate q={q} inv={inv} buildOfferData={buildOfferData} onClose={() => setGateOpen(false)} /> : null}
    </div>
  );
}

/** Wysyła zgłoszenie z dowolnego formularza na backend (Vercel Function → Resend). */
async function sendLead(payload) {
  const r = await fetch("/api/lead", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
  if (!r.ok) throw new Error("Wysyłka nie powiodła się (" + r.status + ")");
  return r.json().catch(() => ({}));
}

/* ── Bramka pobrania oferty — wymaga imienia + e-maila (lead capture) ─────────
 * Imię trafia do PDF („Oferta przygotowana dla:”), e-mail to lead + przyszły adres
 * docelowy Resend. Po podpięciu backendu: zapis leada + wysyłka PDF na podany mail. */
function OfferGate({ q, inv, buildOfferData, onClose }) {
  const [v, setV] = wUseState({ name: "", email: "", phone: "", rodo: false });
  const [touched, setTouched] = wUseState({});
  const [status, setStatus] = wUseState("idle"); // idle | loading | error | success
  const errors = {
    name: validators.name(v.name),
    email: validators.email(v.email),
    phone: validators.phone(v.phone),
    rodo: validators.rodo(v.rodo),
  };
  const hasErrors = Object.values(errors).some(Boolean);
  const setField = (k) => (val) => setV((p) => ({ ...p, [k]: val }));
  const touch = (k) => setTouched((t) => ({ ...t, [k]: true }));

  wUseEffect(() => {
    const onKey = (e) => e.key === "Escape" && onClose();
    window.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { window.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, [onClose]);

  const submit = async (e) => {
    e.preventDefault();
    setTouched({ name: true, email: true, phone: true, rodo: true });
    if (hasErrors) return;
    setStatus("loading");
    try {
      const data = buildOfferData(v.name.trim());
      // Wyślij leada na maila (Resend) — fire-and-forget, żeby błąd wysyłki nie blokował pobrania PDF.
      sendLead({ typ: "oferta", name: v.name.trim(), email: v.email.trim(), phone: v.phone.trim(), konfiguracja: data })
        .catch((err) => console.error("Lead (oferta) nie został wysłany:", err));
      await window.PEO_downloadOffer(data);
      setStatus("success");
    } catch (err) {
      console.error("Generowanie oferty PDF nie powiodło się:", err);
      setStatus("error");
    }
  };

  return (
    <div className="lightbox" role="dialog" aria-modal="true" aria-label="Pobierz ofertę PDF" onClick={onClose}>
      <div className="lightbox-inner" onClick={(e) => e.stopPropagation()} style={{ maxWidth: 460, width: "100%" }}>
        <div className="form-card" style={{ position: "relative" }}>
          <button type="button" onClick={onClose} aria-label="Zamknij"
                  style={{ position: "absolute", top: 10, right: 12, background: "transparent", border: "none",
                           fontSize: "1.3rem", cursor: "pointer", color: "var(--muted)", lineHeight: 1 }}>✕</button>

          {status === "success" ? (
            <div className="success-box">
              <span className="ic"><Ic.Check size={20} /></span>
              <div>
                <strong style={{ fontFamily: "var(--font-head)", fontSize: "1.05rem" }}>Gotowe! Oferta została pobrana.</strong>
                <p style={{ margin: "6px 0 0", color: "var(--muted)", fontSize: "0.92rem" }}>
                  Plik PDF zapisał się na Twoim urządzeniu. W razie pytań zadzwoń: 798-040-369 (pn–pt 10:00–16:00).
                </p>
                <button className="btn btn-secondary" onClick={onClose} style={{ marginTop: 14 }}>Zamknij</button>
              </div>
            </div>
          ) : (
            <form onSubmit={submit} noValidate>
              <h3 style={{ marginBottom: 6 }}>Pobierz ofertę PDF</h3>
              <p style={{ margin: "0 0 14px", color: "var(--muted)", fontSize: "0.94rem" }}>
                Podaj dane — przygotujemy spersonalizowaną ofertę z Twoją konfiguracją.
              </p>

              <div className="cfg-summary" style={{ marginBottom: 14 }}>
                <div className="cfg-summary-head"><Ic.Check size={14} /> Twoja konfiguracja</div>
                <dl className="cfg-summary-list">
                  <div><dt>Moc instalacji</dt><dd>{fmtNum(q.kwp)} kWp</dd></div>
                  <div className="cfg-summary-price"><dt>Cena brutto</dt><dd>{fmtPLN(q.gross)}</dd></div>
                </dl>
              </div>

              <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
                <Field label="Imię i nazwisko" name="g-name" value={v.name} onChange={setField("name")}
                       onBlur={() => touch("name")} error={errors.name} touched={touched.name}
                       autoComplete="name" placeholder="Jan Kowalski" />
                <Field label="Adres e-mail" name="g-email" type="email" value={v.email} onChange={setField("email")}
                       onBlur={() => touch("email")} error={errors.email} touched={touched.email}
                       autoComplete="email" placeholder="jan@przyklad.pl" />
                <Field label="Telefon" name="g-phone" type="tel" value={v.phone} onChange={setField("phone")}
                       onBlur={() => touch("phone")} error={errors.phone} touched={touched.phone}
                       autoComplete="tel" placeholder="600 000 000" />

                <label className="checkbox-row" style={{ marginTop: 4 }}>
                  <input type="checkbox" checked={v.rodo} onChange={(e) => { setField("rodo")(e.target.checked); touch("rodo"); }} />
                  <span>Wyrażam zgodę na przetwarzanie moich danych osobowych w celu przygotowania i przedstawienia oferty,
                    zgodnie z <a href="/polityka-prywatnosci" target="_blank" rel="noopener" style={{ color: "var(--green-700)", fontWeight: 700 }}>Polityką prywatności</a>. <b style={{ color: "var(--danger)" }}>*</b>
                    {touched.rodo && errors.rodo ? <span style={{ color: "var(--danger)", display: "block", fontWeight: 700 }}>{errors.rodo}</span> : null}
                  </span>
                </label>

                {status === "error" ? (
                  <p style={{ margin: "10px 0 0", color: "var(--danger)", fontSize: "0.92rem", fontWeight: 600 }}>
                    Nie udało się wygenerować oferty. Sprawdź połączenie i spróbuj ponownie.
                  </p>
                ) : null}

                <button className="btn btn-primary btn-lg" type="submit" disabled={status === "loading"}
                        style={{ marginTop: 16, width: "100%" }}>
                  {status === "loading"
                    ? <React.Fragment><Ic.Spinner /> Generowanie oferty…</React.Fragment>
                    : <React.Fragment>Pobierz ofertę PDF <Ic.ArrowR /></React.Fragment>}
                </button>
              </div>
            </form>
          )}
        </div>
      </div>
    </div>
  );
}

function scrollToId(id) {
  const el = document.getElementById(id);
  if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 84, behavior: "smooth" });
}

/* ── Formularz kontaktowy / leadowy (reużywalny) ────────── */
function LeadForm({ context, doubleConsent, pvRadio, submitLabel = "WYŚLIJ", title, kod, onKodChange }) {
  const [v, setV] = wUseState({ name: "", email: "", phone: "", woj: "", termin: "", kod: "", pv: "", rodo1: false, rodo2: false });
  const [touched, setTouched] = wUseState({});
  const [status, setStatus] = wUseState("idle"); // idle | loading | success
  const kfg = context && context.konfiguracja;
  const errors = {
    name: validators.name(v.name),
    email: validators.email(v.email),
    phone: validators.phone(v.phone),
    woj: validators.woj(v.woj),
    rodo1: validators.rodo(v.rodo1),
    rodo2: doubleConsent ? validators.rodo(v.rodo2) : "",
    pv: pvRadio && !v.pv ? "Zaznacz jedną z opcji" : "",
  };
  const hasErrors = Object.values(errors).some(Boolean);
  const touch = (k) => setTouched((t) => ({ ...t, [k]: true }));
  const setField = (k) => (val) => setV((s) => ({ ...s, [k]: val }));

  const submit = async (e) => {
    e.preventDefault();
    setTouched({ name: true, email: true, phone: true, woj: true, rodo1: true, rodo2: true, pv: true });
    if (hasErrors) return;
    setStatus("loading");
    try {
      // Payload: dane formularza + kontekst (typ + ewentualna konfiguracja z kalkulatora).
      await sendLead({ ...v, kod: onKodChange ? (kod || "") : v.kod, ...context });
      setStatus("success");
    } catch (err) {
      console.error("Wysyłka zgłoszenia nie powiodła się:", err);
      setStatus("error");
    }
  };

  if (status === "success") {
    return (
      <div className="form-card">
        <div className="success-box">
          <span className="ic"><Ic.Check size={20} /></span>
          <div>
            <strong style={{ fontFamily: "var(--font-head)", fontSize: "1.05rem" }}>Dziękujemy! Wiadomość została wysłana.</strong>
            <p style={{ margin: "6px 0 0", color: "var(--muted)", fontSize: "0.92rem" }}>
              Odezwiemy się w godzinach pracy: pn–pt 10:00–16:00. Kopię zapytania znajdziesz w swojej skrzynce e-mail.
            </p>
          </div>
        </div>
      </div>
    );
  }

  return (
    <form className="form-card" onSubmit={submit} noValidate>
      {title ? <h3 style={{ marginBottom: 16 }}>{title}</h3> : null}
      {kfg ? (
        <div className="cfg-summary">
          <div className="cfg-summary-head"><Ic.Check size={14} /> Twoja konfiguracja z kalkulatora</div>
          <dl className="cfg-summary-list">
            <div><dt>Moc instalacji</dt><dd>{fmtNum(kfg.kwp)} kWp</dd></div>
            <div><dt>Ilość paneli</dt><dd>{kfg.panele} szt.</dd></div>
            {kfg.panelModel ? <div className="cfg-row-wide"><dt>Panel</dt><dd>{kfg.panelModel}</dd></div> : null}
            {kfg.magazynModel ? <div className="cfg-row-wide"><dt>Magazyn</dt><dd>{kfg.magazynModel}{Number.isFinite(kfg.magazynKwh) ? ` · moc ${fmtKwh(kfg.magazynKwh)} kWh` : ""}{Number.isFinite(kfg.magazynCap) ? ` · pojemność ${fmtKwh(kfg.magazynCap)} kWh` : ""}</dd></div> : null}
            {kfg.falownikModel ? <div className="cfg-row-wide"><dt>Falownik</dt><dd>{kfg.falownikModel}</dd></div> : null}
            {kfg.ems ? <div><dt>EMS</dt><dd>{kfg.ems}</dd></div> : null}
            <div><dt>Klient</dt><dd>{kfg.klientTyp} (VAT {kfg.vatProc}%)</dd></div>
            {kfg.rabatKwota ? <div><dt>Rabat ({kfg.rabatKod})</dt><dd style={{ color: "#1F8A4C" }}>−{fmtPLN(kfg.rabatKwota)}</dd></div> : null}
            {kfg.cenaBrutto ? <div className="cfg-summary-price"><dt>{kfg.rabatKwota ? "Cena brutto po rabacie" : "Szacowana cena brutto"}</dt><dd>{fmtPLN(kfg.cenaBrutto)}</dd></div> : null}
          </dl>
          <p className="cfg-summary-note">Te dane dołączymy automatycznie do Twojego zapytania.</p>
        </div>
      ) : null}
      <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
        <Field label="Imię i nazwisko" name={"name" + (pvRadio ? "-c" : "")} value={v.name} onChange={setField("name")}
               onBlur={() => touch("name")} error={errors.name} touched={touched.name} autoComplete="name" placeholder="Jan Kowalski" />
        <div className="form-grid-2">
          <Field label="Adres e-mail" name={"email" + (pvRadio ? "-c" : "")} type="email" value={v.email} onChange={setField("email")}
                 onBlur={() => touch("email")} error={errors.email} touched={touched.email} autoComplete="email" placeholder="jan@przyklad.pl" />
          <Field label="Numer telefonu" name={"phone" + (pvRadio ? "-c" : "")} type="tel" value={v.phone} onChange={setField("phone")}
                 onBlur={() => touch("phone")} error={errors.phone} touched={touched.phone} autoComplete="tel" placeholder="600 000 000" />
        </div>
        <Field label="Województwo" name={"woj" + (pvRadio ? "-c" : "")} options={WOJEWODZTWA} value={v.woj} onChange={setField("woj")}
               onBlur={() => touch("woj")} error={errors.woj} touched={touched.woj} />

        <Field label="Kiedy planujesz wykonać inwestycję?" name={"termin" + (pvRadio ? "-c" : "")}
               options={["W przeciągu miesiąca", "W przeciągu pół roku", "Inne"]} value={v.termin} onChange={setField("termin")} />

        <Field label="Kod rabatowy (opcjonalnie)" name={"kod" + (pvRadio ? "-c" : "")}
               value={onKodChange ? (kod || "") : v.kod}
               onChange={onKodChange ? onKodChange : setField("kod")}
               placeholder="Wpisz, jeśli posiadasz" />

        {pvRadio ? (
          <div className="field" role="radiogroup" aria-label="Czy masz fotowoltaikę?">
            <label>Czy posiadasz już fotowoltaikę?</label>
            <div className="vat-row">
              {["Mam fotowoltaikę", "Nie mam fotowoltaiki"].map((opt) => (
                <button type="button" key={opt} className={`vat-option ${v.pv === opt ? "selected" : ""}`} role="radio" aria-checked={v.pv === opt}
                        onClick={() => { setField("pv")(opt); touch("pv"); }} style={{ padding: "12px 14px", fontSize: "0.9rem" }}>
                  <span className="radio-dot"></span>{opt}
                </button>
              ))}
            </div>
            <span className="field-error" role="alert">{touched.pv ? errors.pv : ""}</span>
          </div>
        ) : null}

        <label className="checkbox-row">
          <input type="checkbox" checked={v.rodo1} onChange={(e) => { setField("rodo1")(e.target.checked); touch("rodo1"); }} />
          <span>Wyrażam zgodę na przetwarzanie moich danych osobowych w celu przygotowania i przedstawienia oferty handlowej. <b style={{ color: "var(--danger)" }}>*</b>
            {touched.rodo1 && errors.rodo1 ? <span style={{ color: "var(--danger)", display: "block", fontWeight: 700 }}>{errors.rodo1}</span> : null}
          </span>
        </label>
        {doubleConsent ? (
          <label className="checkbox-row" style={{ marginTop: 8 }}>
            <input type="checkbox" checked={v.rodo2} onChange={(e) => { setField("rodo2")(e.target.checked); touch("rodo2"); }} />
            <span>Wyrażam zgodę na kontakt telefoniczny i e-mailowy w celu przedstawienia oferty produktów i usług. <b style={{ color: "var(--danger)" }}>*</b>
              {touched.rodo2 && errors.rodo2 ? <span style={{ color: "var(--danger)", display: "block", fontWeight: 700 }}>{errors.rodo2}</span> : null}
            </span>
          </label>
        ) : null}

        {status === "error" ? (
          <p style={{ margin: "12px 0 0", color: "var(--danger)", fontSize: "0.92rem", fontWeight: 600 }}>
            Nie udało się wysłać wiadomości. Spróbuj ponownie lub zadzwoń: 798-040-369.
          </p>
        ) : null}

        <button className="btn btn-primary btn-lg" type="submit" disabled={status === "loading"} style={{ marginTop: 18, width: "100%" }}>
          {status === "loading" ? <React.Fragment><Ic.Spinner /> Wysyłanie…</React.Fragment> : <React.Fragment>{submitLabel} <Ic.ArrowR /></React.Fragment>}
        </button>
      </div>
    </form>
  );
}

/* ── Wizard (kontener) ──────────────────────────────────── */
const WIZARD_DEFAULT = {
  step: 1, location: "dach", roofType: "dach skośny", roofCover: "", roofSplit: "",
  panelId: "aiko-a500", panelCount: 23, battId: "deye-pro-b", battUnits: 1, sigenKwh: 6, invId: "auto", emsAddon: false, vat: "individual", kod: "",
};
const WIZARD_KEY = "tkoze-wizard-v1";

function Wizard({ go }) {
  const [s, setS] = wUseState(() => {
    try { return { ...WIZARD_DEFAULT, ...JSON.parse(localStorage.getItem(WIZARD_KEY) || "{}") }; }
    catch { return WIZARD_DEFAULT; }
  });
  const topRef = wUseRef(null);
  const firstRef = wUseRef(true);
  wUseEffect(() => { localStorage.setItem(WIZARD_KEY, JSON.stringify(s)); }, [s]);
  // Przewiń do góry kalkulatora PO przerenderowaniu nowego kroku. Na telefonie krok 3
  // jest niższy niż krok 2 — przy płynnym przewijaniu strona „kurczy się" w trakcie
  // animacji i scroll lądował przycięty na dole. Dlatego: czekamy aż layout się ustabilizuje
  // (podwójny rAF) i przewijamy natychmiastowo do policzonej pozycji.
  wUseEffect(() => {
    if (firstRef.current) { firstRef.current = false; return; }
    requestAnimationFrame(() => requestAnimationFrame(() => {
      const el = topRef.current;
      if (!el) return;
      const top = el.getBoundingClientRect().top + window.scrollY - 80;
      window.scrollTo({ top: Math.max(0, top), behavior: "instant" });
    }));
  }, [s.step]);
  const set = (patch) => setS((prev) => ({ ...prev, ...patch }));
  const goStep = (n) => { set({ step: n }); };
  const titles = {
    1: ["Umiejscowienie instalacji", "Wybierz, gdzie zamontujemy Twoją instalację fotowoltaiczną."],
    2: ["Parametry instalacji", "Dobierz panele i magazyn energii — pełną cenę zobaczysz w kroku „Cena”."],
    3: ["Cena instalacji", "Twoja wycena wprost od instalatora, bez marży handlowca."],
  };
  return (
    <div ref={topRef} id="kalkulator-start" data-screen-label={`Kalkulator — krok ${s.step}`}>
      <div className="page-hero section-tight">
        <div className="container">
          <div className="eyebrow">Kalkulator OZE</div>
          <h1 className="h-xl">Oblicz swoją własną instalację</h1>
          <p style={{ marginTop: 10 }}>
            Skonfiguruj instalację w 3 krokach i poznaj cenę wprost od instalatora — bez marży handlowca.
          </p>
        </div>
      </div>
      <div className="section-tight container">
        <h2 className="wizard-title" style={{ fontSize: "clamp(1.35rem, 2.6vw, 1.9rem)", margin: "0 0 6px" }}>{titles[s.step][0]}</h2>
        <p className="wizard-sub">{titles[s.step][1]}</p>
        <div style={{ display: "flex", justifyContent: "center", position: "relative", margin: "6px 0 28px" }}>
          <Stepper step={s.step} goStep={goStep} />
        </div>
        {s.step === 1 ? <Step1 s={s} set={set} onNext={() => goStep(2)} /> : null}
        {s.step === 2 ? <Step2 s={s} set={set} onNext={() => goStep(3)} onBack={() => goStep(1)} go={go} /> : null}
        {s.step === 3 ? <Step3 s={s} set={set} onBack={() => goStep(2)} go={go} /> : null}
      </div>
    </div>
  );
}

Object.assign(window, { PRICING, computeQuote, Wizard, LeadForm, scrollToId });
