/* ============================================================
   VKTR TCO — Shared UI components
   ============================================================ */
const { useState, useRef, useEffect } = React;

// ---- Field wrapper: English label (bold) + Indonesian (muted) ----
function Field({ en, id, req, opt, help, helpEn, children, full }) {
  const { lang } = useLang();
  const primary = lang === "en" ? en : (id || en);
  const secondary = lang === "en" ? id : (id ? en : null);
  const helpText = lang === "en" ? (helpEn || help) : help;
  return (
    <div className={"field" + (full ? " row-full" : "")}>
      <label>
        <div className="lab-en">{primary}{req && <span className="req">*</span>}{opt && <span className="opt-tag">{lang === "en" ? "optional" : "opsional"}</span>}</div>
        {secondary && <div className="lab-id">{secondary}</div>}
      </label>
      {children}
      {helpText && <div className="help">{helpText}</div>}
    </div>
  );
}

function TextInput({ value, onChange, placeholder, type = "text" }) {
  return (
    <input className="input" type={type} value={value}
      placeholder={placeholder}
      onChange={(e) => onChange(e.target.value)} />
  );
}

function TextArea({ value, onChange, placeholder, rows = 3 }) {
  return (
    <textarea className="textarea" rows={rows} value={value}
      placeholder={placeholder} onChange={(e) => onChange(e.target.value)} />
  );
}

function Select({ value, onChange, options, placeholder, disabled }) {
  return (
    <select className="select" value={value} onChange={(e) => onChange(e.target.value)} disabled={disabled}>
      {placeholder && <option value="">{placeholder}</option>}
      {options.map(o =>
        typeof o === "string"
          ? <option key={o} value={o}>{o}</option>
          : <option key={o.value} value={o.value}>{o.label}</option>
      )}
    </select>
  );
}

// ---- Affixed numeric input ----
function AffixInput({ value, onChange, prefix, suffix, readonly, align, placeholder }) {
  return (
    <div className={"affix" + (readonly ? " readonly" : "")}>
      {prefix && <span className="fix pre">{prefix}</span>}
      <input value={value} placeholder={placeholder}
        readOnly={readonly}
        style={align ? { textAlign: align } : null}
        onChange={(e) => onChange && onChange(e.target.value)} />
      {suffix && <span className="fix suf">{suffix}</span>}
    </div>
  );
}

// ---- Slider with synced number input + live pill ----
function SliderField({ en, id, min, max, value, onChange, unit }) {
  const v = Number(value);
  return (
    <div className="field slider-field row-full">
      <div className="slider-top">
        <label>
          <div className="lab-en">{en}</div>
          {id && <div className="lab-id">{id}</div>}
        </label>
        <div className="live">{fmt.num(v)} {unit}</div>
      </div>
      <div className="slider-wrap">
        <input type="range" className="range" min={min} max={max} value={v}
          style={{ background: `linear-gradient(to right, var(--c-accent) 0%, var(--c-accent) ${((v - min) / (max - min)) * 100}%, var(--border) ${((v - min) / (max - min)) * 100}%, var(--border) 100%)` }}
          onChange={(e) => onChange(Number(e.target.value))} />
        <input className="input num-sync" type="number" min={min} max={max} value={v}
          onChange={(e) => {
            let n = Number(e.target.value);
            if (isNaN(n)) n = min;
            onChange(Math.max(min, Math.min(max, n)));
          }} />
      </div>
    </div>
  );
}

// ---- Pill toggle (two options) ----
function PillToggle({ value, onChange, left, right }) {
  return (
    <div className="pill-toggle">
      <button className={value === left.value ? "on" : ""} onClick={() => onChange(left.value)}>
        {left.icon}{left.label}
      </button>
      <button className={value === right.value ? "on" : ""} onClick={() => onChange(right.value)}>
        {right.icon}{right.label}
      </button>
    </div>
  );
}

function Badge({ kind, children }) {
  return <span className={"badge " + kind}>{children}</span>;
}

function Alert({ kind = "info", icon, children }) {
  const dft = { info: "ℹ️", warn: "⚠️", error: "✕" };
  return (
    <div className={"alert " + kind}>
      <span className="ic">{icon || dft[kind]}</span>
      <div>{children}</div>
    </div>
  );
}

// ---- Per-screen step-by-step guide (collapsible sidebar) ----
function GuideSidebar({ step, guides, open, onClose }) {
  const { lang } = useLang();
  const guide = guides[step];
  if (!guide) return null;
  const title = lang === "en" ? guide.titleEn : guide.titleId;
  return (
    <>
      {open && <div className="guide-overlay" onClick={onClose} />}
      <div className={"guide-sidebar" + (open ? " open" : "")}>
        <div className="guide-sidebar-head">
          <h3>📖 {title}</h3>
          <button onClick={onClose} aria-label="Close">✕</button>
        </div>
        <div className="guide-sidebar-body">
          {guide.steps.map((st, i) => (
            <div className="guide-step" key={i}>
              <div className="gs-num">{i + 1}</div>
              <div className="gs-text">
                {lang === "en" ? st.en : st.id}
                <span className="gs-id">{lang === "en" ? st.id : st.en}</span>
              </div>
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

function GuideToggle({ open, onToggle }) {
  const { lang } = useLang();
  return (
    <button className={"guide-toggle" + (open ? " open" : "")} onClick={onToggle}
      title={tr(lang, "Step-by-step guide", "Panduan langkah demi langkah")}
      data-tooltip={tr(lang, "Help / Guide", "Bantuan / Panduan")}>
      {open ? "✕" : "❓"}
    </button>
  );
}

// ---- Searchable dropdown ----
// items: [{ id, name, sub, render?(item) }]
function SearchableDropdown({ value, onChange, items, placeholder, renderItem, disabled }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState("");
  const [hl, setHl] = useState(0);
  const ref = useRef(null);
  const selected = items.find(i => i.id === value);

  useEffect(() => {
    function onDoc(e) { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);

  const filtered = q.trim()
    ? items.filter(i => !i.isHeader && (i.name + " " + (i.sub || "") + " " + (i.brand || "")).toLowerCase().includes(q.toLowerCase()))
    : items;

  const display = open ? q : (selected ? selected.name : "");

  function pick(item) { if (item.isHeader) return; onChange(item.id); setOpen(false); setQ(""); }

  // Move hl by `dir` (+1/-1), skipping over non-selectable header rows.
  function stepHl(h, dir) {
    let n = h + dir;
    while (n >= 0 && n < filtered.length && filtered[n].isHeader) n += dir;
    return Math.max(0, Math.min(n, filtered.length - 1));
  }

  return (
    <div className={"sdrop" + (disabled ? " disabled" : "")} ref={ref}>
      <div className="sdrop-input">
        <span className="si">
          <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
            <circle cx="7" cy="7" r="5" stroke="currentColor" strokeWidth="1.6" />
            <path d="M11 11l3.5 3.5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
          </svg>
        </span>
        <input className="input" value={display} placeholder={placeholder} disabled={disabled}
          onFocus={() => { if (disabled) return; setOpen(true); setQ(""); }}
          onChange={(e) => { setQ(e.target.value); setOpen(true); setHl(0); }}
          onKeyDown={(e) => {
            if (e.key === "ArrowDown") { e.preventDefault(); setHl(h => stepHl(h, 1)); }
            else if (e.key === "ArrowUp") { e.preventDefault(); setHl(h => stepHl(h, -1)); }
            else if (e.key === "Enter" && filtered[hl] && !filtered[hl].isHeader) { e.preventDefault(); pick(filtered[hl]); }
            else if (e.key === "Escape") setOpen(false);
          }} />
        {selected && value && !disabled && (
          <button className="clr" onClick={() => { onChange(items[0] ? "" : ""); onChange(""); setQ(""); }}>✕</button>
        )}
      </div>
      {open && !disabled && (
        <div className="sdrop-list">
          {filtered.length === 0 && <div className="sdrop-item none"><span className="di-name">Tidak ditemukan</span></div>}
          {filtered.map((item, i) => (
            item.isHeader ? (
              <div key={item.id} className="sdrop-group-header">{item.name}</div>
            ) : (
              <div key={item.id} className={"sdrop-item" + (item.id === "none" || item.id === "" ? " none" : "") + (i === hl ? " hl" : "")}
                onMouseEnter={() => setHl(i)} onMouseDown={(e) => e.preventDefault()} onClick={() => pick(item)}>
                {renderItem ? renderItem(item) : (
                  <>
                    <div className="di-name">{item.name}</div>
                    {item.sub && <div className="di-sub">{item.sub}</div>}
                  </>
                )}
              </div>
            )
          ))}
        </div>
      )}
    </div>
  );
}

// ---- Card ----
function Card({ title, idSub, head, children, className, style }) {
  const { lang } = useLang();
  const primary = title ? (lang === "en" ? title : (idSub || title)) : null;
  const secondary = title ? (lang === "en" ? idSub : (idSub ? title : null)) : null;
  return (
    <div className={"card " + (className || "")} style={style}>
      {(title || head) && (
        <div className="card-head">
          <div>
            {primary && <div className="card-title">{primary}{secondary && <div className="id-sub">{secondary}</div>}</div>}
          </div>
          {head}
        </div>
      )}
      {children}
    </div>
  );
}

/* ============================================================
   v1.3 — Infrastructure redesign components
   ============================================================ */

// ---- Expert Mode toggle (per Screen 4 tab / Screen 5) ----
function ExpertToggle({ isOn, onToggle, labelEn, labelId, icon }) {
  const { lang } = useLang();
  return (
    <div className={"expert-toggle" + (isOn ? " active" : "")} onClick={() => onToggle(!isOn)}>
      <span className="et-icon">{icon || "🔬"}</span>
      <span className="et-label">{tr(lang, labelEn || "Expert Mode", labelId || "Mode Ahli")}</span>
      <span className={"et-switch" + (isOn ? " on" : "")}><span className="et-knob" /></span>
    </div>
  );
}

// ---- Preset card (top of Screen 4) ----
function PresetCard({ preset, onChangePreset }) {
  const { lang } = useLang();
  if (!preset) return null;
  return (
    <div className="preset-card">
      <div className="pc-current">
        <span className="pc-icon">{preset.icon}</span>
        <div>
          <div className="pc-label">{preset.label}</div>
          {preset.description && <div className="pc-desc">{preset.description}</div>}
        </div>
      </div>
      <button className="btn btn-ghost" onClick={onChangePreset}>
        {tr(lang, "Change Preset", "Ganti Preset")}
      </button>
    </div>
  );
}

// ---- Preset selection modal (5x4 grid, filterable by ecosystem) ----
function PresetModal({ currentPresetId, onSelect, onClose }) {
  const { lang } = useLang();
  const [ecoFilter, setEcoFilter] = useState("all");
  const [pending, setPending] = useState(null);
  const PRESETS = window.INFRA_PROFILES.PRESETS;
  const ECOSYSTEM_OPTIONS = window.ECOSYSTEM_OPTIONS;

  const filtered = ecoFilter === "all" ? PRESETS : PRESETS.filter(p => p.ecosystemId === ecoFilter);
  const pendingPreset = pending ? PRESETS.find(p => p.id === pending) : null;

  return (
    <div className="preset-modal-overlay" onClick={onClose}>
      <div className="preset-modal" onClick={e => e.stopPropagation()}>
        <div className="preset-modal-head">
          <h3>{tr(lang, "Choose a Preset", "Pilih Preset")}</h3>
          <button className="btn btn-ghost" onClick={onClose}>✕</button>
        </div>

        <div className="preset-filter-tabs">
          <button className={ecoFilter === "all" ? "on" : ""} onClick={() => setEcoFilter("all")}>
            {tr(lang, "All", "Semua")}
          </button>
          {ECOSYSTEM_OPTIONS.map(eco => (
            <button key={eco.id} className={ecoFilter === eco.id ? "on" : ""} onClick={() => setEcoFilter(eco.id)}>
              {eco.icon} {tr(lang, eco.label, eco.labelId)}
            </button>
          ))}
        </div>

        <div className="preset-grid">
          {filtered.map(p => (
            <div key={p.id}
              className={"preset-item" + (p.id === currentPresetId ? " active" : "")}
              onClick={() => setPending(p.id)}>
              <div className="pi-icon">{p.icon}</div>
              <div className="pi-label">{p.label}</div>
              <div className="pi-desc">{p.description}</div>
            </div>
          ))}
        </div>

        {pendingPreset && (
          <div className="preset-confirm-overlay" onClick={() => setPending(null)}>
            <div className="preset-confirm" onClick={e => e.stopPropagation()}>
              <p>
                {tr(lang,
                  `Reset all infrastructure values to "${pendingPreset.label}" defaults?`,
                  `Atur ulang semua nilai infrastruktur ke default "${pendingPreset.label}"?`)}
              </p>
              <div className="preset-confirm-actions">
                <button className="btn btn-ghost" onClick={() => setPending(null)}>
                  {tr(lang, "No", "Tidak")}
                </button>
                <button className="btn btn-accent" onClick={() => { onSelect(pendingPreset.id); setPending(null); }}>
                  {tr(lang, "Yes", "Ya")}
                </button>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// ---- Value-change flag: amber dot shown when currentValue !== defaultValue ----
function ValueFlag({ defaultValue, currentValue, onReset, label }) {
  const { lang } = useLang();
  const [hover, setHover] = useState(false);
  if (defaultValue == null || currentValue == null) return null;
  if (currentValue === defaultValue) return null;
  return (
    <span className="value-flag" onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
      <span className="value-flag-dot" onClick={() => onReset && onReset()}>●</span>
      {hover && (
        <span className="value-flag-tooltip">
          {tr(lang, "Default (assumption)", "Default (asumsi)")}: <b>{String(defaultValue)}</b>
          <br />
          <em>{tr(lang, "Click to reset", "Klik untuk reset")}{label ? ` — ${label}` : ""}</em>
        </span>
      )}
    </span>
  );
}

// ---- Collapsible section ----
function CollapsibleSection({ title, defaultOpen = false, children }) {
  const [open, setOpen] = useState(defaultOpen);
  return (
    <div className="collapsible-section">
      <div className={"collapsible-header" + (open ? " open" : "")} onClick={() => setOpen(o => !o)}>
        <span className="chevron">
          <svg width="11" height="11" viewBox="0 0 12 12" fill="none">
            <path d="M4 2l4 4-4 4" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </span>
        <span>{title}</span>
      </div>
      <div className="collapsible-content" style={{ maxHeight: open ? 2000 : 0 }}>
        <div className="collapsible-inner">{children}</div>
      </div>
    </div>
  );
}

// ---- Budget alert banner ----
function BudgetAlert({ capexTotal, budgetCap, recommendations }) {
  const { lang } = useLang();
  if (budgetCap == null) return null;
  const over = capexTotal > budgetCap;
  if (!over) return null;
  const delta = capexTotal - budgetCap;
  const top = (recommendations && recommendations[0]) || null;
  return (
    <div className={"budget-alert" + (top && top.id === "infeasible" ? " infeasible" : "")}>
      <div className="ba-head">
        ⚠ {tr(lang, "CAPEX exceeds budget by", "CAPEX melebihi anggaran sebesar")} {fmt.rpShort(delta)}
      </div>
      {top && (
        <div className="ba-reco">
          <b>{tr(lang, "Top recommendation", "Rekomendasi utama")}:</b> {top.label}
          {top.detail && <div className="ba-reco-detail">{top.detail}</div>}
        </div>
      )}
      <div className="ba-link">{tr(lang, "See full optimization → Screen 5", "Lihat optimasi lengkap → Layar 5")}</div>
    </div>
  );
}

// ---- Side-by-side tooltip: shows a param's value across all 4 non-Others ecosystems ----
function SideBySideTooltip({ param }) {
  const { lang } = useLang();
  const ECO = window.INFRA_PROFILES.ECOSYSTEM_PROFILES;
  const ECOSYSTEM_OPTIONS = window.ECOSYSTEM_OPTIONS.filter(e => e.id !== "others");
  return (
    <table className="sstt-table">
      <tbody>
        {ECOSYSTEM_OPTIONS.map(eco => {
          const entry = ECO[eco.id]?.[param];
          return (
            <tr key={eco.id}>
              <td>{tr(lang, eco.label, eco.labelId)}</td>
              <td>{entry ? String(entry.value) : "—"}</td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

// ---- Infrastructure reusability score ----
function ReusabilityScore({ ccs2, ocpp, customerOwned }) {
  const { lang } = useLang();
  const score = (ccs2 ? 0.30 : 0) + (ocpp ? 0.20 : 0) + (customerOwned ? 0.50 : 0);
  const pct = Math.round(score * 100);
  const level = pct >= 80 ? "high" : pct >= 50 ? "medium" : "low";
  const labelMap = {
    high:   { en: "High Reusability",   id: "Kelayakan Tinggi" },
    medium: { en: "Medium Reusability", id: "Kelayakan Sedang" },
    low:    { en: "Low Reusability",    id: "Kelayakan Rendah" },
  };
  return (
    <div className="reusability-score">
      <div className="reusability-top">
        <span className={"reusability-badge " + level}>{pct}% — {tr(lang, labelMap[level].en, labelMap[level].id)}</span>
      </div>
      <div className="reusability-bar">
        <div className={"reusability-bar-fill " + level} style={{ width: pct + "%" }} />
      </div>
    </div>
  );
}

// ---- Infrastructure readiness indicator ----
function InfraReadinessIndicator({ status, message }) {
  const icon = { green: "🟢", yellow: "🟡", red: "🔴" }[status] || "⚪";
  return (
    <div className={"readiness-" + status + " readiness-indicator"}>
      <span className="ri-icon">{icon}</span>
      <span className="ri-msg">{message}</span>
    </div>
  );
}

// ---- CAPEX category row (Tab 5) ----
// ---- Per-screen "Reset to Default" button (task #33) ----
function ResetScreenButton({ s, set, screenKey }) {
  const { lang } = useLang();
  const onClick = () => {
    const ok = window.confirm(tr(lang,
      "Reset all fields on this screen to their default values? This cannot be undone.",
      "Reset semua kolom di layar ini ke nilai default? Tindakan ini tidak dapat dibatalkan."));
    if (ok) window.resetScreenState(s, set, screenKey);
  };
  return (
    <button type="button" className="btn btn-ghost btn-sm reset-screen-btn" onClick={onClick}>
      ↺ {tr(lang, "Reset to Default", "Reset ke Default")}
    </button>
  );
}

// ---- Consolidated change-log overlay (task #33) ----
function ChangeLogOverlay({ s, set, open, onClose }) {
  const { lang } = useLang();
  if (!open) return null;
  const entries = window.computeChangeLog(s);
  return (
    <div className="changelog-backdrop" onClick={onClose}>
      <div className="changelog-modal" onClick={e => e.stopPropagation()}>
        <div className="changelog-head">
          <h3>{tr(lang, "Change Log", "Log Perubahan")}</h3>
          <button type="button" className="btn-icon" onClick={onClose}>✕</button>
        </div>
        <div className="changelog-sub">
          {tr(lang,
            `${entries.length} value(s) currently differ from the platform default across all screens.`,
            `${entries.length} nilai saat ini berbeda dari default platform di semua layar.`)}
        </div>
        {entries.length === 0 ? (
          <div className="changelog-empty">{tr(lang, "No overrides yet — everything is at its default value.", "Belum ada override — semua dalam nilai default.")}</div>
        ) : (
          <div className="changelog-list">
            {entries.map((e, i) => (
              <div className="changelog-row" key={i}>
                <div className="changelog-row-main">
                  <span className="changelog-screen-tag">{tr(lang, e.screenLabel[0], e.screenLabel[1])}</span>
                  <span className="changelog-field">{tr(lang, e.fieldLabel[0], e.fieldLabel[1])}</span>
                </div>
                <div className="changelog-row-vals">
                  {!e.isYearly && (
                    <span className="changelog-default">{tr(lang, "Default", "Default")}: <b>{String(e.defaultValue)}</b></span>
                  )}
                  <span className="changelog-current">{tr(lang, "Current", "Saat ini")}: <b>{String(e.currentValue)}</b></span>
                  <button type="button" className="btn btn-ghost btn-sm" onClick={() => window.revertChangeLogEntry(s, set, e)}>
                    {tr(lang, "Revert", "Kembalikan")}
                  </button>
                </div>
              </div>
            ))}
          </div>
        )}
        {entries.length > 0 && (
          <div className="changelog-foot">
            <button type="button" className="btn btn-danger btn-sm" onClick={() => {
              const ok = window.confirm(tr(lang,
                "Reset the ENTIRE profile to defaults? This affects every screen and cannot be undone.",
                "Reset SELURUH profil ke default? Ini memengaruhi semua layar dan tidak dapat dibatalkan."));
              if (ok) { window.resetAllState(set); onClose(); }
            }}>
              ↺ {tr(lang, "Reset Entire Profile", "Reset Seluruh Profil")}
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

// ---- Yearly expense grid (Operation/Maintenance/Infrastructure/Financial) ----
// Lets the user override any single year's cost for a given category &
// vehicle, while still showing the computed default underneath via ValueFlag.
// `defaultsA`/`defaultsB` must be the *unoverridden* per-year arrays (caller
// computes these via computeTCO on a clone of state with yearlyOverrides
// stripped) so the reset affordance always points at the true DB default.
function YearlyExpenseEditor({ s, set, lang, title, helpEn, helpId, category, defaultsA, defaultsB, labelA, labelB }) {
  const horizon = s.horizon || 5;
  const ov = s.yearlyOverrides || {};
  const setCell = (vehKey, year, val) => {
    const key = `${category}.${vehKey}.${year}`;
    const next = { ...ov };
    if (val == null) delete next[key]; else next[key] = val;
    set("yearlyOverrides", next);
  };
  const rowFor = (vehKey, defaults, label) => (
    <tr key={vehKey}>
      <td className="lbl">{label}</td>
      {Array.from({ length: horizon }, (_, i) => {
        const y = i + 1;
        const def = defaults && defaults[i] != null ? defaults[i] : 0;
        const key = `${category}.${vehKey}.${y}`;
        const cur = ov[key];
        return (
          <td key={y}>
            <input className="yearly-cell-input"
              value={fmt.num(Math.round(cur != null ? cur : def))}
              onChange={e => { const n = Number(e.target.value.replace(/\D/g, "")); setCell(vehKey, y, isNaN(n) ? null : n); }} />
            <ValueFlag defaultValue={Math.round(def)} currentValue={cur != null ? Math.round(cur) : null}
              onReset={() => setCell(vehKey, y, null)} />
          </td>
        );
      })}
    </tr>
  );
  return (
    <div style={{ marginTop: 14 }}>
      <div style={{ fontWeight: 700, fontSize: 13, marginBottom: 4 }}>{title}</div>
      {helpEn && <div style={{ fontSize: 11.5, color: "var(--text-muted)", marginBottom: 8 }}>{tr(lang, helpEn, helpId)}</div>}
      <div className="yearly-table-scroll">
        <table className="sbs-table yearly-table">
          <thead>
            <tr>
              <th></th>
              {Array.from({ length: horizon }, (_, i) => <th key={i}>{tr(lang, "Yr", "Thn")} {i + 1}</th>)}
            </tr>
          </thead>
          <tbody>
            {rowFor("A", defaultsA, labelA)}
            {rowFor("B", defaultsB, labelB)}
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ---- Source citation tooltip (hover-only) — surfaces the R()/A() note
// field from infra_profiles.js so a buyer can see exactly where a number
// came from without leaving the page. ----
function SourceTag({ source, note }) {
  const { lang } = useLang();
  const [hover, setHover] = useState(false);
  if (!note) return null;
  const icon = source === "research" ? "🔬" : source === "assumption" ? "📝" : source === "computed" ? "🧮" : "✎";
  const kindLabel = source === "research" ? tr(lang, "Researched", "Hasil Riset")
    : source === "assumption" ? tr(lang, "Assumption", "Asumsi")
    : source === "computed" ? tr(lang, "Calculated", "Dihitung")
    : tr(lang, "User-entered", "Input Pengguna");
  return (
    <span className="source-tag" onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
      <span className={"source-tag-icon " + source}>{icon}</span>
      {hover && (
        <span className="source-tag-tooltip">
          <b>{kindLabel}</b><br />{note}
        </span>
      )}
    </span>
  );
}

// Generic hover-help icon -- reuses SourceTag's tooltip CSS but with a
// neutral icon, for explaining a toggle/control without a permanent
// paragraph taking up space whether or not anyone needs the explanation.
function InfoHint({ note }) {
  const [hover, setHover] = useState(false);
  if (!note) return null;
  return (
    <span className="source-tag" onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} style={{ marginLeft: 6 }}>
      <span className="source-tag-icon">ℹ️</span>
      {hover && <span className="source-tag-tooltip">{note}</span>}
    </span>
  );
}

// v1.7.7 Wave 4 — replaces every persistent yellow <Alert kind="warn"> box
// platform-wide: a short, always-visible amber label (so the warning is
// still discoverable without hunting for an icon) plus the full explanation
// on hover, same InfoHint mechanism, instead of a paragraph-sized box eating
// permanent space whether or not it's relevant right now.
function WarnHint({ label, note, style }) {
  return (
    <span style={{ display: "inline-flex", alignItems: "center", color: "#B45309", fontSize: 12, fontWeight: 600, ...style }}>
      ⚠ {label}
      <InfoHint note={note} />
    </span>
  );
}

function CapexCategoryRow({ category, label, items, total, applicable, reason, s, set }) {
  const { lang } = useLang();
  const editable = !!(s && set);
  return (
    <div className={"capex-row" + (!applicable ? " not-applicable" : "")}>
      <div className="capex-row-head">
        <span className="capex-cat-label">{category}. {label}</span>
        <span className="capex-cat-total">{applicable ? fmt.rp(total) : "Rp 0"}</span>
        {!applicable && <span className="badge-na">{tr(lang, "NOT APPLICABLE", "TIDAK BERLAKU")}</span>}
      </div>
      {!applicable && reason && <div className="capex-row-reason">{reason}</div>}
      {applicable && items && items.length > 0 && (
        <div className="capex-row-items">
          {items.map((it, i) => (
            <div className="capex-item" key={i}>
              <span>{it.label}<SourceTag source={it.source} note={it.note} /></span>
              {editable && it.key ? (
                <span style={{ textAlign: "right" }}>
                  <span className="inline-price-edit">
                    <span className="fix pre">Rp</span>
                    <input className="inline-price-input" value={fmt.num(it.rawValue)}
                      onChange={e => { const n = Number(e.target.value.replace(/\D/g, "")); setOv(set, s, "tab5." + it.key, isNaN(n) ? null : n); }} />
                    <span style={{ fontWeight: 400, color: "var(--text-muted)", fontSize: 11 }}>{tr(lang, "/unit", "/unit")}</span>
                    <ValueFlag defaultValue={it.rawDefault} currentValue={(s.screen4_valueOverrides || {})["tab5." + it.key] ?? null}
                      onReset={() => setOv(set, s, "tab5." + it.key, null)}
                      label={tr(lang, "per unit — line total updates automatically", "per unit — total baris diperbarui otomatis")} />
                  </span>
                  <div style={{ fontSize: 11, color: "var(--text-muted)", marginTop: 2 }}>= {fmt.rp(it.value)}</div>
                </span>
              ) : (
                <span>{fmt.rp(it.value)}</span>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Object.assign(window, {
  Field, TextInput, TextArea, Select, AffixInput, SliderField,
  PillToggle, Badge, Alert, SearchableDropdown, Card,
  ExpertToggle, PresetCard, PresetModal, ValueFlag, CollapsibleSection,
  BudgetAlert, SideBySideTooltip, ReusabilityScore, InfraReadinessIndicator,
  CapexCategoryRow, SourceTag, InfoHint, WarnHint, YearlyExpenseEditor,
  ResetScreenButton, ChangeLogOverlay,
});
