/* ============================================================
   VKTR TCO Platform — App shell, stepper, nav  v1.2
   Screen order (v1.2):
     1. Customer Profile
     2. Vehicle Selection   (moved from Screen 4)
     3. Operation           (moved from Screen 2)
     4. Infrastructure
     5. Financials
     6. Results / TCO Report
   ============================================================ */

const STEPS = [
  { lbl: "Customer Profile",  sub: "Profil Pelanggan"      },
  { lbl: "Vehicle Selection", sub: "Pemilihan Kendaraan"   },
  { lbl: "Operation",         sub: "Skenario Operasional"  },
  { lbl: "Infrastructure",    sub: "Infrastruktur"         },
  { lbl: "Financials",        sub: "Asumsi Keuangan"       },
  { lbl: "Results",           sub: "Hasil Analisis"        },
];

// ---------- Per-screen step-by-step guides (collapsible sidebar) ----------
const SCREEN_GUIDES = [
  { // Screen 1 — Customer Profile
    titleEn: "Customer Profile — how to use this screen",
    titleId: "Profil Pelanggan — cara menggunakan layar ini",
    steps: [
      { en: "Fill in the company name, contact person, and operating city.", id: "Isi nama perusahaan, kontak person, dan kota operasional." },
      { en: "Choose the Ecosystem (e.g. Logistics, Mining, Public Transport) and Project Type (Greenfield, Expansion, etc.) — these set sensible defaults for the infrastructure sizing on Screen 4.", id: "Pilih Ekosistem (mis. Logistik, Pertambangan, Transportasi Umum) dan Tipe Proyek (Greenfield, Ekspansi, dll.) — ini menentukan nilai default yang masuk akal untuk sizing infrastruktur di Layar 4." },
      { en: "\"Use flat baseline cost model\": turn this on to ignore the selected industry's cost multipliers entirely and price everything off the neutral, fully user-editable \"Others\" baseline — useful for an industry-agnostic estimate.", id: "\"Gunakan model biaya dasar rata\": aktifkan untuk mengabaikan pengganda biaya industri yang dipilih dan menghitung semuanya dari baseline \"Lainnya\" yang netral dan dapat diedit penuh — berguna untuk estimasi yang tidak terikat industri." },
      { en: "Optionally enter approximate fleet size and project start date — used for reference only at this stage.", id: "Opsional: isi perkiraan ukuran armada dan tanggal mulai proyek — hanya sebagai referensi pada tahap ini." },
      { en: "Made a wrong turn? Every screen has a \"Reset to Default\" button in its header, and the 🕘 Change Log button (top right, every screen) lists every overridden value platform-wide with one-click revert.", id: "Salah input? Setiap layar punya tombol \"Reset ke Default\" di headernya, dan tombol 🕘 Log Perubahan (kanan atas, semua layar) menampilkan semua nilai yang diubah di seluruh platform dengan kembalikan satu klik." },
    ],
  },
  { // Screen 2 — Vehicle Selection
    titleEn: "Vehicle Selection — how to use this screen",
    titleId: "Pemilihan Kendaraan — cara menggunakan layar ini",
    steps: [
      { en: "Use the search boxes to pick Vehicle A and Vehicle B — the two vehicles being compared throughout the report.", id: "Gunakan kotak pencarian untuk memilih Kendaraan A dan Kendaraan B — dua kendaraan yang dibandingkan di seluruh laporan." },
      { en: "Adjust the unit price if your actual quote differs from the catalog default.", id: "Sesuaikan harga unit jika penawaran aktual berbeda dari default katalog." },
      { en: "Open the \"Maintenance Breakdown\" tab to review parts grouped into 6 categories (tyre, brake, battery/fuel, cooling, powertrain, other) with per-group subtotals, and optionally enable it to replace the standard PM schedule in the TCO calculation.", id: "Buka tab \"Rincian Perawatan\" untuk melihat komponen yang dikelompokkan dalam 6 kategori (ban, rem, baterai/BBM, pendingin, penggerak, lainnya) dengan subtotal per kelompok, dan opsional aktifkan untuk menggantikan jadwal PM standar dalam perhitungan TCO." },
      { en: "Tyre cost: use the Cheap/Premium chip to switch tyre price tier per vehicle, and edit the Rp value directly if your supplier quote differs.", id: "Biaya ban: gunakan chip Hemat/Premium untuk mengganti tier harga ban per kendaraan, dan edit nilai Rp langsung jika penawaran pemasok Anda berbeda." },
      { en: "Need to compare the other way around? Click \"Flip A ↔ B\" to swap every field (vehicle, price, maintenance overrides) between the two sides.", id: "Ingin membandingkan dengan urutan terbalik? Klik \"Tukar A ↔ B\" untuk menukar semua kolom (kendaraan, harga, override perawatan) antara kedua sisi." },
      { en: "Optionally set a manual maintenance cost override (per year, per month, or per km) — this takes precedence over both the PM schedule and the parts-based estimate.", id: "Opsional: atur override biaya perawatan manual (per tahun, per bulan, atau per km) — ini didahulukan dari jadwal PM maupun estimasi berbasis komponen." },
    ],
  },
  { // Screen 3 — Operation
    titleEn: "Operation — how to use this screen",
    titleId: "Skenario Operasional — cara menggunakan layar ini",
    steps: [
      { en: "Set the Ritase Distance (one depot→site→depot round trip), fleet size, and analysis horizon (years) — Annual Mileage becomes computed automatically once an EV vehicle is selected (Screen 2), from ritase distance × daily ritase count × operating days/year.", id: "Atur Jarak Ritase (satu perjalanan pulang-pergi depot→lokasi→depot), ukuran armada, dan horizon analisis (tahun) — Jarak Tempuh Tahunan otomatis terhitung setelah kendaraan EV dipilih (Layar 2), dari jarak ritase × jumlah ritase harian × hari operasional/tahun." },
      { en: "Set the terrain manually (Flat / Rolling / Hilly / Mixed), or check \"Use a computed track profile\" below to auto-detect it (and Ritase Distance) from a KML/KMZ route or the map search.", id: "Atur medan secara manual (Flat / Rolling / Hilly / Mixed), atau centang \"Gunakan profil rute terhitung\" di bawah untuk mendeteksi otomatis (beserta Jarak Ritase) dari rute KML/KMZ atau pencarian peta." },
      { en: "Terrain affects energy consumption and maintenance cost via a multiplier; for the EV side it also models a regen-braking credit on choppy/hilly routes.", id: "Medan memengaruhi konsumsi energi dan biaya perawatan melalui faktor pengali; untuk sisi EV, model ini juga menghitung kredit pengereman regeneratif pada rute berbukit/tidak rata." },
      { en: "Use the \"Audit\" tab to see exactly which terrain/route multiplier and mileage scaling were applied to each vehicle's numbers.", id: "Gunakan tab \"Audit\" untuk melihat persis pengganda medan/rute dan skala jarak yang diterapkan pada angka tiap kendaraan." },
    ],
  },
  { // Screen 4 — Infrastructure
    titleEn: "Infrastructure — how to use this screen",
    titleId: "Infrastruktur — cara menggunakan layar ini",
    steps: [
      { en: "This screen's sizing is driven by the ecosystem chosen in Screen 1/2 — parameters are pre-filled automatically for that ecosystem, and every tab has its own Expert Mode toggle when you need to override a value manually.", id: "Ukuran pada layar ini ditentukan oleh ekosistem yang dipilih di Layar 1/2 — parameter terisi otomatis sesuai ekosistem tersebut, dan setiap tab punya sakelar Mode Ahli sendiri saat Anda perlu meng-override nilai secara manual." },
      { en: "Tab \"Fleet & Charging\": propose a charging cycle count/day and a scheduled group count/cycle (Charging Strategy) — the platform validates both against ritase physics and depot capacity, auto-adjusting and flagging in Results if a proposal doesn't fit. Also set the scheduled charging shift (SS) duration and payload utilization.", id: "Tab \"Armada & Pengisian\": usulkan jumlah siklus pengisian/hari dan jumlah grup terjadwal/siklus (Strategi Pengisian) — platform memvalidasi keduanya terhadap fisika ritase dan kapasitas depot, menyesuaikan otomatis dan menandai di Hasil jika usulan tidak sesuai. Atur juga durasi shift pengisian terjadwal (SS) dan utilisasi muatan." },
      { en: "Tab \"Charging Strategy\": charger type, power rating, nozzles/vehicle, and voltage level are all auto-computed from the Charging Session Duration (SS) you set on the Fleet & Charging tab and the selected EV's battery spec. Turn on Expert Mode to override any of them manually.", id: "Tab \"Charging Strategy\": tipe charger, rating daya, nosel/kendaraan, dan tingkat tegangan seluruhnya dihitung otomatis dari Durasi Sesi Pengisian (SS) yang diatur di tab Armada & Pengisian dan spesifikasi baterai EV terpilih. Aktifkan Mode Ahli untuk meng-override salah satunya secara manual." },
      { en: "Tab \"Growth & Assets\": if this is an expansion, enter your existing EVs, chargers, and transformer capacity so sizing accounts for what you already have.", id: "Tab \"Growth & Assets\": jika ini proyek ekspansi, isi jumlah EV, charger, dan kapasitas trafo yang sudah ada agar sizing memperhitungkan aset eksisting." },
      { en: "Tab \"Sizing Engine\": review the computed charger count and transformer size. Turn on Expert Mode to override any of these values manually.", id: "Tab \"Sizing Engine\": tinjau jumlah charger dan ukuran trafo hasil perhitungan. Aktifkan Mode Ahli untuk meng-override nilai-nilai ini secara manual." },
      { en: "Tab \"CAPEX Breakdown\": review the itemized EVCS investment (categories A–E: chargers, electrical, civil works, utility upgrade, software). Hover the 🔬/📝 icon next to any line item to see exactly where that number is sourced from. With \"Use computed EVCS CAPEX in TCO\" enabled, this total feeds directly into the Results screen.", id: "Tab \"CAPEX Breakdown\": tinjau rincian investasi EVCS (kategori A–E: charger, kelistrikan, sipil, upgrade utilitas, software). Arahkan kursor ke ikon 🔬/📝 di samping item mana pun untuk melihat sumber angka tersebut. Dengan \"Gunakan CAPEX EVCS terhitung di TCO\" aktif, total ini langsung masuk ke Layar Hasil." },
    ],
  },
  { // Screen 5 — Financials
    titleEn: "Financials — how to use this screen",
    titleId: "Asumsi Keuangan — cara menggunakan layar ini",
    steps: [
      { en: "Choose Cash or Loan as the payment method. For Loan, set the flat interest rate, down payment %, and loan tenor (years) — these determine the financing cost shown in the Results table.", id: "Pilih metode pembayaran Tunai atau Kredit. Untuk Kredit, atur suku bunga flat, uang muka (%), dan tenor pinjaman (tahun) — nilai ini menentukan biaya pembiayaan pada tabel Hasil." },
      { en: "Set diesel, electricity, and AdBlue prices, plus the annual inflation rate used to escalate OPEX year over year.", id: "Atur harga solar, listrik, dan AdBlue, serta tingkat inflasi tahunan yang digunakan untuk eskalasi OPEX setiap tahun." },
      { en: "Set the WACC (discount rate) — this is used to compute NPV and IRR on the Results screen.", id: "Atur WACC (discount rate) — digunakan untuk menghitung NPV dan IRR di Layar Hasil." },
      { en: "Optionally set an infrastructure or total project budget cap — if the computed CAPEX exceeds it, Screen 4 will surface cost-reduction recommendations.", id: "Opsional: atur batas anggaran infrastruktur atau total proyek — jika CAPEX terhitung melebihi batas, Layar 4 akan menampilkan rekomendasi penghematan biaya." },
    ],
  },
  { // Screen 6 — Results
    titleEn: "Results — how to use this screen",
    titleId: "Hasil Analisis — cara menggunakan layar ini",
    steps: [
      { en: "Review the KPI cards at the top: lifetime savings, payback period, NPV, IRR, and CO2 difference between Vehicle A and B.", id: "Tinjau kartu KPI di bagian atas: penghematan seumur pakai, periode payback, NPV, IRR, dan selisih CO2 antara Kendaraan A dan B." },
      { en: "Scroll through the TCO breakdown table — it lists all 8 cost components (purchase price, financing, energy, AdBlue, maintenance, infra CAPEX/OPEX, residual value) side by side for A and B.", id: "Gulir tabel rincian TCO — berisi 8 komponen biaya (harga beli, pembiayaan, energi, AdBlue, perawatan, CAPEX/OPEX infra, nilai residu) berdampingan untuk A dan B." },
      { en: "Open the \"Details\" tab for the Maintenance and Financial breakdown cards, plus the \"Yearly Expense Editor\" — override any single year's Energy/AdBlue, Maintenance, Infrastructure OPEX, or Financing cost while every untouched year keeps using the computed default.", id: "Buka tab \"Detail\" untuk kartu rincian Perawatan dan Keuangan, serta \"Editor Biaya Tahunan\" — timpa biaya Energi/AdBlue, Perawatan, OPEX Infrastruktur, atau Pembiayaan untuk tahun tertentu, sementara tahun yang tidak diubah tetap memakai nilai default terhitung." },
      { en: "Check the Annual OPEX chart and the Cumulative Total Project Cost chart to see year-by-year costs and the break-even point between the two vehicles.", id: "Periksa grafik OPEX Tahunan dan grafik Total Biaya Proyek Kumulatif untuk melihat biaya per tahun dan titik impas (BEP) antara kedua kendaraan." },
      { en: "Go back to any earlier screen at any time using the stepper at the top — your inputs are saved automatically.", id: "Kembali ke layar sebelumnya kapan saja melalui stepper di bagian atas — input Anda disimpan otomatis." },
    ],
  },
];

// Gate tweaks panel behind ?tweaks=1 — remove for production
const SHOW_TWEAKS = window.location.search.includes("tweaks=1");

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "palette": ["#0F1E27", "#1C3B49", "#00A651"],
  "accent": "#00A651",
  "headerStyle": "solid",
  "cornerRadius": 10
}/*EDITMODE-END*/;

function Stepper({ step, goTo }) {
  const { lang } = useLang();
  return (
    <div className="stepper">
      <div className="stepper-inner">
        {STEPS.map((st, i) => {
          const state    = i < step ? "done" : i === step ? "active" : "future";
          const primary   = lang === "en" ? st.lbl : st.sub;
          const secondary = lang === "en" ? st.sub : st.lbl;
          return (
            <div key={i}
              className={"step " + state + " clickable"}
              onClick={() => goTo(i)}>
              <div className="connector" />
              <div className="bubble">{i < step ? "✓" : i + 1}</div>
              <div className="lbl">{primary}</div>
              <div className="sub">{secondary}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function App() {
  const [t, setTweak]    = useTweaks(TWEAK_DEFAULTS);
  const [lang, setLangState] = React.useState(() => VKTRStore.loadLang());
  const [step, setStep]  = React.useState(() => VKTRStore.loadStep());
  const [s, setState]    = React.useState(() => window.sanitizeLoadedState(VKTRStore.loadState(DEFAULT_STATE)));
  const [guideOpen, setGuideOpen] = React.useState(false);
  const [changeLogOpen, setChangeLogOpen] = React.useState(false);
  const [needsIntakeOpen, setNeedsIntakeOpen] = React.useState(false);
  const [adminPanelOpen, setAdminPanelOpen] = React.useState(false);
  const [libraryOpen, setLibraryOpen] = React.useState(false);
  // v1.7.5: opt-in, off by default -- exported files stay plain catalog-id
  // references unless the user deliberately asks to bundle their custom
  // vehicle data along with them (see exportProfile/importProfile below).
  const [includeCustomVehicles, setIncludeCustomVehicles] = React.useState(false);
  const { customVehicles, importCustomVehicle } = useCustomVehicles();
  const fileInputRef = React.useRef(null);

  const setLang = (l) => { setLangState(l); VKTRStore.saveLang(l); };
  const set = (key, val) => setState(prev => {
    const next = { ...prev, [key]: val };
    VKTRStore.saveState(next);
    return next;
  });
  const goTo = (n) => {
    const c = Math.max(0, Math.min(5, n));
    setStep(c);
    VKTRStore.saveStep(c);
    window.scrollTo(0, 0);
  };

  // ---------- Export / Import profile (cross-device exchange) ----------
  // v1.7.5: opt-in custom-vehicle bundling -- vehA/vehB are plain catalog-id
  // strings in `s`; if either points at a per-account custom vehicle (an
  // edited catalog vehicle or a wholly new one, see vehicle_library.jsx), a
  // plain export silently loses that vehicle's real spec on another
  // account/device (findVeh() falls back to nothing or the unedited catalog
  // default). Off by default -- bundling means embedding another account's
  // personalized vehicle data in a file that might get shared further,
  // which isn't something to do silently. (v1.7.7: existingVehicleId
  // dropped from this list along with the rest of the project-type concept.)
  const referencedCustomVehicles = () => {
    const ids = [s.vehA, s.vehB].filter(Boolean);
    const bundle = {};
    ids.forEach(id => { if (customVehicles[id]) bundle[id] = customVehicles[id]; });
    return bundle;
  };

  const exportProfile = () => {
    const customVehiclesBundle = includeCustomVehicles ? referencedCustomVehicles() : {};
    const payload = {
      app: "VKTR TCO Competitive Analysis Platform",
      version: "V1.7.8",
      schemaVersion: window.SCHEMA_VERSION,
      exportedAt: new Date().toISOString(),
      lang,
      step,
      state: s,
      // v1.7.5: integrity check, not a security/tamper-proofing measure --
      // catches accidental corruption or a stray hand-edit typo, checked
      // (and only warned on, never blocked) on import below.
      _checksum: window.simpleChecksum(JSON.stringify(s)),
      ...(Object.keys(customVehiclesBundle).length ? { _customVehiclesBundle: customVehiclesBundle } : {}),
    };
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
    const url  = URL.createObjectURL(blob);
    const slug = (s.company || "vktr-tco").trim().toLowerCase()
      .replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "vktr-tco";
    const stamp = new Date().toISOString().slice(0, 10);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${slug}_tco-profile_${stamp}.json`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const importProfile = (file) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      let data;
      try {
        data = JSON.parse(e.target.result);
      } catch {
        alert(tr(lang, "Could not read this file — it is not a valid VKTR profile (.json).", "File tidak dapat dibaca — bukan profil VKTR (.json) yang valid."));
        return;
      }
      const importedState = (data && typeof data.state === "object" && data.state) ? data.state : data;
      if (!importedState || typeof importedState !== "object") {
        alert(tr(lang, "Could not read this file — it is not a valid VKTR profile (.json).", "File tidak dapat dibaca — bukan profil VKTR (.json) yang valid."));
        return;
      }
      if (data.schemaVersion && data.schemaVersion !== window.SCHEMA_VERSION) {
        const proceed = window.confirm(tr(lang,
          `This profile was exported from a different app version (schema ${data.schemaVersion}, current ${window.SCHEMA_VERSION}). Some fields may reset to defaults. Continue?`,
          `Profil ini diekspor dari versi aplikasi berbeda (skema ${data.schemaVersion}, saat ini ${window.SCHEMA_VERSION}). Beberapa kolom mungkin kembali ke default. Lanjutkan?`));
        if (!proceed) return;
      }
      // v1.7.5: checksum is a corruption/stray-typo check, not a security
      // boundary -- warn and let the user decide, never hard-block, since a
      // deliberate hand-edit by a power user is a legitimate use case.
      if (data._checksum && window.simpleChecksum(JSON.stringify(importedState)) !== data._checksum) {
        const proceed = window.confirm(tr(lang,
          "This file's contents don't match its saved checksum — it may have been edited or partially corrupted since it was exported. Import anyway?",
          "Isi file ini tidak cocok dengan checksum tersimpannya — mungkin telah diedit atau sebagian rusak sejak diekspor. Tetap impor?"));
        if (!proceed) return;
      }
      const merged = window.sanitizeLoadedState({ ...DEFAULT_STATE, ...window.migratePaymentField(importedState) });
      setState(merged);
      VKTRStore.saveState(merged);
      if (data.lang === "en" || data.lang === "id") {
        setLangState(data.lang);
        VKTRStore.saveLang(data.lang);
      }
      goTo(typeof data.step === "number" ? data.step : 0);
      // v1.7.5: opt-in custom-vehicle bundle, written by exportProfile above
      // -- ask before writing, since this creates/overwrites vehicle data on
      // whichever account is currently signed in (or this session, if guest).
      const bundle = data._customVehiclesBundle;
      if (bundle && typeof bundle === "object" && Object.keys(bundle).length) {
        const n = Object.keys(bundle).length;
        const proceed = window.confirm(tr(lang,
          `This profile includes ${n} custom vehicle definition(s) used by Vehicle A/B or the existing fleet vehicle. Import them into your account too, so they resolve correctly here? (Guests: kept for this session only.)`,
          `Profil ini menyertakan ${n} definisi kendaraan kustom yang digunakan oleh Kendaraan A/B atau kendaraan armada eksisting. Impor juga ke akun Anda agar terbaca dengan benar di sini? (Tamu: hanya disimpan untuk sesi ini.)`));
        if (proceed) {
          Object.entries(bundle).forEach(([docId, vehData]) => importCustomVehicle(docId, vehData));
        }
      }
      alert(tr(lang, "Profile imported successfully.", "Profil berhasil diimpor."));
    };
    reader.readAsText(file);
  };

  // Load an Excel-derived reference preset (presets.js) — same merge path
  // as a normal profile import, just sourced from a built-in array instead
  // of a user-uploaded file.
  const loadPreset = (presetId) => {
    const preset = (window.TCO_REFERENCE_PRESETS || []).find(p => p.id === presetId);
    if (!preset) return;
    const merged = window.sanitizeLoadedState({ ...DEFAULT_STATE, ...window.migratePaymentField(preset.state) });
    setState(merged);
    VKTRStore.saveState(merged);
    goTo(0);
  };

  // Same merge/save/navigate as loadPreset, but for a raw state object read
  // directly from disk (the File System Access preset Explorer) rather than
  // an id looked up against the static, build_presets.py-generated array.
  const loadPresetData = (rawState) => {
    if (!rawState) return;
    const merged = window.sanitizeLoadedState({ ...DEFAULT_STATE, ...window.migratePaymentField(rawState) });
    setState(merged);
    VKTRStore.saveState(merged);
    goTo(0);
  };

  // Customer Needs Intake Wizard (v1.7) — same merge path as a preset load
  // (starts a fresh profile from the wizard's answers), but lands on Screen
  // 2 (Vehicle Selection, index 1) instead of Screen 1: the wizard covers
  // Customer Profile + Operation + Payment fields, but deliberately does
  // NOT guess which catalog vehicles are being compared.
  const applyNeedsIntake = (partial) => {
    const merged = window.sanitizeLoadedState({ ...DEFAULT_STATE, ...partial });
    setState(merged);
    VKTRStore.saveState(merged);
    setNeedsIntakeOpen(false);
    goTo(1);
  };

  const handleImportFile = (e) => {
    const file = e.target.files && e.target.files[0];
    if (file) importProfile(file);
    e.target.value = "";
  };

  // Apply palette tweaks to CSS variables
  React.useEffect(() => {
    const pal  = t.palette || TWEAK_DEFAULTS.palette;
    const root = document.documentElement.style;
    root.setProperty("--c-primary",       pal[0]);
    root.setProperty("--c-primary-light", pal[1]);
    root.setProperty("--c-accent",        t.accent || pal[2]);
    root.setProperty("--radius",          t.cornerRadius + "px");
    root.setProperty("--radius-sm",       Math.max(4, t.cornerRadius - 3) + "px");
  }, [t.palette, t.accent, t.cornerRadius]);

  const screens = [Screen1, Screen2, Screen3, Screen4, Screen5, Screen6];
  const Current = screens[step];
  const isLast  = step === 5;

  const stepPrimary   = lang === "en" ? STEPS[step].lbl : STEPS[step].sub;
  const stepSecondary = lang === "en" ? STEPS[step].sub : STEPS[step].lbl;

  return (
   <LangContext.Provider value={{ lang, setLang }}>
    <div className="app-shell">

      {/* Header */}
      <header className="app-header">
        <div className="brand">
          <img src="assets/vktr-white-horizontal.png" alt="VKTR" />
          <div className="divider" />
          <div className="product">
            TCO Competitive Analysis Platform
            {/* Platform version + build (cache-bust) number -- the tracking
                signature going forward (2026-07-13). BUILD must be bumped by
                hand alongside every index.html cache-bust change (CLAUDE.md's
                "?v=N" rule) -- there's no build step to inject it automatically. */}
            <small>V1.7.8 by Rija Akhal Hilmi<span className="build-tag">build v159</span></small>
          </div>
        </div>
        <div className="header-actions">
          <div className="profile-io" role="group" aria-label="Profile data">
            <label className="io-toggle" data-tooltip={tr(lang,
              "Include your custom vehicle definitions (added/edited vehicles used by Vehicle A/B) in the exported file, so it still resolves correctly if opened on another account. Off by default.",
              "Sertakan definisi kendaraan kustom Anda (kendaraan yang ditambah/diedit dan dipakai Kendaraan A/B) dalam file yang diekspor, agar tetap terbaca jika dibuka di akun lain. Nonaktif secara default.")}>
              <input type="checkbox" checked={includeCustomVehicles} onChange={(e) => setIncludeCustomVehicles(e.target.checked)} />
              <span aria-hidden="true">🚗+</span>
            </label>
            <button type="button" className="io-btn" onClick={exportProfile}
              title={tr(lang, "Export profile to a file you can share or move to another device", "Ekspor profil ke file yang bisa dibagikan atau dipindah ke perangkat lain")}>
              <span className="io-icon" aria-hidden="true">⬇</span>
              <span className="io-label"><Tr en="Export" id="Ekspor" /></span>
            </button>
            <button type="button" className="io-btn" onClick={() => fileInputRef.current && fileInputRef.current.click()}
              title={tr(lang, "Import a previously exported profile file", "Impor file profil yang sebelumnya diekspor")}>
              <span className="io-icon" aria-hidden="true">⬆</span>
              <span className="io-label"><Tr en="Import" id="Impor" /></span>
            </button>
            <input ref={fileInputRef} type="file" accept="application/json,.json" style={{ display: "none" }} onChange={handleImportFile} />
          </div>
          <button type="button" className="io-btn" onClick={() => setChangeLogOpen(true)}
            title={tr(lang, "View and revert overridden values across all screens", "Lihat dan kembalikan nilai yang diubah di semua layar")}>
            <span className="io-icon" aria-hidden="true">🕘</span>
            <span className="io-label"><Tr en="Change Log" id="Log Perubahan" /></span>
          </button>
          <button type="button" className="io-btn" onClick={() => setLibraryOpen(true)}
            title={tr(lang, "Manage your saved profiles, customized vehicles, and routes", "Kelola profil, kendaraan yang disesuaikan, dan rute tersimpan Anda")}>
            <span className="io-icon" aria-hidden="true">📚</span>
            <span className="io-label"><Tr en="My Library" id="Perpustakaan Saya" /></span>
          </button>
          <div className="lang-badge" role="group" aria-label="Language">
            <span className={lang === "id" ? "on" : ""} onClick={() => setLang("id")}>ID</span>
            <span className={lang === "en" ? "on" : ""} onClick={() => setLang("en")}>EN</span>
          </div>
          <AuthHeaderWidget onOpenAdmin={() => setAdminPanelOpen(true)} />
        </div>
      </header>

      {adminPanelOpen && <AdminPanel s={s} onClose={() => setAdminPanelOpen(false)} />}
      {libraryOpen && <MyLibraryPanel s={s} onLoadProfile={loadPresetData} onClose={() => setLibraryOpen(false)} />}

      {/* Function 2 (v1.7): Live Assistant, Screens 2-6 only (step >= 1) --
          mounted app-wide (not per-screen) so its position/conversation
          state survives navigating between screens within a session. */}
      <LiveAssistantWidget s={s} set={set} screenId={`screen${step + 1}`} visible={step >= 1} />

      <Stepper step={step} goTo={goTo} />

      {/* Content */}
      <main className="content">
        <div className="content-inner">
          <div className="screen-head">
            <h1>{stepPrimary}</h1>
            <div className="id-sub">{stepSecondary}</div>
          </div>
          <Current s={s} set={set} goTo={goTo} loadPreset={loadPreset} loadPresetData={loadPresetData}
            onOpenNeedsIntake={() => setNeedsIntakeOpen(true)} />
        </div>
      </main>

      {/* Background Depot Design auto-sync — mounted app-wide (not gated by
          Screen 4's "Depot Design" tab) so depotBom/depotMetrics populate as
          soon as an EV is selected, and the Results screen's depot layout
          card shows by default without requiring a manual visit to that tab.
          Visually hidden, fully functional (the iframe still boots, syncs,
          and auto-generates Plan 2 exactly as the visible instance does). */}
      {window.getEvVehicle(s) && (
        <div aria-hidden="true" style={{ position: "fixed", top: -9999, left: -9999, width: 1, height: 1, overflow: "hidden", opacity: 0, pointerEvents: "none" }}>
          <Tab6DepotDesign s={s} set={set} lang={lang} />
        </div>
      )}

      {/* Bottom nav */}
      <nav className="bottom-nav">
        <button className="btn btn-ghost" disabled={step === 0} onClick={() => goTo(step - 1)}>
          ← <Tr en="Back" id="Kembali" />
        </button>
        <div className="counter">
          <Tr en="Step" id="Langkah" /> <b>{step + 1}</b> <Tr en="of" id="dari" /> 6 · <b>{stepPrimary}</b>
        </div>
        {isLast
          ? <button className="btn btn-accent" onClick={() => alert(tr(lang, "Analysis complete. Report is ready to export.", "Analisis selesai. Laporan siap diekspor."))}>
              ✓ <Tr en="Done" id="Selesai" />
            </button>
          : <button className="btn btn-primary" onClick={() => goTo(step + 1)}>
              <Tr en="Next" id="Lanjut" /> →
            </button>
        }
      </nav>

      {/* Copyright footer — every page + PDF */}
      <footer className="app-copyright">
        <span className="cr-platform">TCO Competitive Analysis Platform · Version V1.5 / PT VKTR Teknologi Mobilitas Tbk</span>
        <span className="cr-rights">PT VKTR Teknologi Mobilitas Tbk. <Tr en="All rights reserved" id="Hak cipta dilindungi" /></span>
      </footer>

      {/* Per-screen step-by-step guide */}
      <GuideToggle open={guideOpen} onToggle={() => setGuideOpen(o => !o)} />
      <GuideSidebar step={step} guides={SCREEN_GUIDES} open={guideOpen} onClose={() => setGuideOpen(false)} />

      {/* Consolidated change log — view/revert overrides across every screen */}
      <ChangeLogOverlay s={s} set={set} open={changeLogOpen} onClose={() => setChangeLogOpen(false)} />

      {/* Customer Needs Intake Wizard — plain-language onboarding path */}
      {needsIntakeOpen && (
        <NeedsIntakeWizard onComplete={applyNeedsIntake} onClose={() => setNeedsIntakeOpen(false)} />
      )}

      {/* Tweaks — gated behind ?tweaks=1 */}
      {SHOW_TWEAKS && (
        <TweaksPanel>
          <TweakSection label="Brand palette" />
          <TweakColor label="Theme" value={t.palette}
            options={[
              ["#0F1E27", "#1C3B49", "#00A651"],
              ["#003366", "#004D99", "#00A651"],
              ["#10221C", "#1C3D32", "#16B364"],
              ["#1A1A1E", "#33333B", "#00A651"],
            ]}
            onChange={(v) => setTweak({ palette: v, accent: v[2] })} />
          <TweakColor label="Accent" value={t.accent}
            options={["#00A651", "#16B364", "#0EA5E9", "#F59E0B"]}
            onChange={(v) => setTweak("accent", v)} />
          <TweakSection label="Shape" />
          <TweakSlider label="Corner radius" value={t.cornerRadius} min={2} max={18} step={1} unit="px"
            onChange={(v) => setTweak("cornerRadius", v)} />
          <TweakButton label="Reset to sample data" onClick={() => { VKTRStore.clearState(); setState({ ...DEFAULT_STATE }); }} />
        </TweaksPanel>
      )}
    </div>
   </LangContext.Provider>
  );
}

// ---------- Boot ----------
// Awaits all Excel-reference loaders (vehicles.jsx / infra_profiles.js /
// lead_time.jsx -- each fetches its own data/*.xlsx and overwrites the
// corresponding hardcoded JS defaults in place, mirroring
// depot_floorplan/js/main.js's own boot sequencing) before the first React
// render, so the UI never flashes the hardcoded defaults and then jumps to
// the spreadsheet values a moment later. All three loaders fall back
// silently to their hardcoded defaults on fetch failure, so this resolves
// quickly either way.
(async function boot() {
  await Promise.all([window.loadVehiclesReference(), window.loadInfraReference(), window.loadLeadTimeReference()]);
  ReactDOM.createRoot(document.getElementById("root")).render(
    <AuthProvider><AccessGate><CustomVehiclesProvider><App /></CustomVehiclesProvider></AccessGate></AuthProvider>
  );
})();
