/* =========================================================
   ANTARES — Schermate 9-13
   Preventivi & Consuntivi · Statistiche · Anagrafica
   Calendario attrezzatura · Carico collaboratori
   ========================================================= */

const { useState: useState3, useMemo: useMemo3 } = React;
const {
  fmt: fmt3, fmtNum: fmtNum3, fmtPct: fmtPct3, MESI: MESI3, MESI_FULL: MESI_FULL3,
  PROGETTI: PROG3, FATTURE: FATT3, COLLABORATORI: COLLAB3, CESPITI: CESP3,
  PREVENTIVI, STATISTICHE, CONTROPARTI, IMPEGNI_CESPITI, IMPEGNI_COLLAB
} = window.AntaresData;
const Icon3 = window.AntaresScreens1.Icon;

/* helpers */
const totRiga = (r) => {
  const qta = Number(r.qta) || 1;
  const giorni = Number(r.giorni) || 0;
  const prezzo = Number(r.prezzo) || 0;
  return giorni > 0 ? qta * giorni * prezzo : qta * prezzo;
};
const sommaCategoria = (righe) => righe.reduce((s, r) => s + totRiga(r), 0);

/* ---- Listino CCNL: riferimento minimi + controllo congruità ---- */
const ccnlList = () => window.AntaresData.CCNL_LISTINO || [];
const ccnlById = (id) => ccnlList().find(c => c.id === id) || null;
const baseLabel = (b) => b === "giornaliero" ? "/gg" : b === "settimanale" ? "/sett" : "/mese";
// Confronta il prezzo della riga col minimo CCNL collegato. Ritorna null se non collegata.
function ccnlCheck(row) {
  const e = row && row.ccnl_id ? ccnlById(row.ccnl_id) : null;
  if (!e || !(e.minimo > 0)) return null;
  const prezzo = Number(row.prezzo) || 0;
  let min = e.minimo, base = e.base, approx = false;
  if (Number(row.giorni) > 0) {
    // riga a giornata → confronto su base giornaliera
    base = "giornaliero";
    if (e.base === "mensile") { min = e.minimo / 26; approx = true; }
    else if (e.base === "settimanale") { min = e.minimo / 6; approx = true; }
  }
  return { ok: prezzo >= min, min, base, prezzo, approx, entry: e };
}
function CongruitaBadge({ row }) {
  const c = ccnlCheck(row);
  if (!c) return null;
  const title = `Minimo CCNL · ${c.entry.ruolo}: ${fmt3(c.min)} ${baseLabel(c.base)}`
    + (c.approx ? ` (≈ ${fmt3(c.entry.minimo)} /mese ÷ 26 gg)` : "")
    + ` · tuo: ${fmt3(c.prezzo)}`;
  return (
    <span className={"pill " + (c.ok ? "teal" : "coral")} title={title} style={{ marginLeft: 8 }}>
      {c.ok ? "🟢 congruo" : "🔴 sotto contratto"}
    </span>
  );
}

/* ---- Costo del personale assunto: oneri datore + costo busta ---- */
function oneriParams() {
  const s = window.AntaresData.APP_SETTINGS || {};
  return {
    inps:  Number(s.oneri_inps_datore) || 0,
    irap:  Number(s.oneri_irap) || 0,
    inail: Number(s.oneri_inail) || 0,
    tfr:   Number(s.oneri_tfr) || 0,
    busta: Number(s.costo_busta) || 0,
    cu:    Number(s.costo_cu) || 0,
  };
}
const isDipendente = (r) => r && (r.regime === "dipendente_spett" || r.regime === "dipendente");
function riepilogoPersonale(costi, p) {
  const dip = (costi || []).filter(isDipendente);
  const lordo = dip.reduce((s, r) => s + totRiga(r), 0);
  const nteste = dip.reduce((s, r) => s + (Number(r.qta) || 1), 0);
  const inps = lordo * p.inps / 100, irap = lordo * p.irap / 100;
  const inail = lordo * p.inail / 100, tfr = lordo * p.tfr / 100;
  const busta = dip.reduce((s, r) => s + p.busta * (Number(r.qta) || 1), 0);
  const cu = (p.cu || 0) * nteste;
  const oneri = inps + irap + inail + tfr + busta + cu;
  return { n: dip.length, lordo, inps, irap, inail, tfr, busta, cu, oneri, costoAzienda: lordo + oneri };
}

// Elenco standard di esclusioni ("Non incluso nel preventivo") — modificabile in app.
const ESCLUSIONI_PRESET = [
  "Alimentazione elettrica / fornitura di corrente (gruppo elettrogeno)",
  "Lavoro straordinario / ore eccedenti il pacchetto concordato",
  "Vitto e alloggio della troupe",
  "Trasporti e trasferte fuori sede",
  "Diritti SIAE / diritti musicali",
  "Permessi, occupazione suolo pubblico, ZTL",
  "Assicurazioni aggiuntive / RC specifica",
  "Materiali di consumo (gaffer, gelatine, nastri)",
  "Ponteggi / piattaforme aeree / facchinaggio",
  "IVA di legge",
].join("\n");

// Catalogo voci standard per il preventivo (righe di costo precompilate, prezzo 0).
const VOCI_CATALOGO = [
  { gruppo: "Tecnici / Troupe", voci: [
    { voce: "Regista", categoria: "Collaboratori" },
    { voce: "Operatore", categoria: "Collaboratori" },
    { voce: "Direttore della fotografia", categoria: "Collaboratori" },
    { voce: "Assistente operatore", categoria: "Collaboratori" },
    { voce: "Fonico", categoria: "Collaboratori" },
    { voce: "Microfonista", categoria: "Collaboratori" },
    { voce: "Datore luci / gaffer", categoria: "Collaboratori" },
    { voce: "Elettricista", categoria: "Collaboratori" },
    { voce: "Macchinista", categoria: "Collaboratori" },
    { voce: "Tecnico video / mixer", categoria: "Collaboratori" },
    { voce: "Runner", categoria: "Collaboratori" },
  ]},
  { gruppo: "Artisti", voci: [
    { voce: "Conduttore / Presentatore", categoria: "Collaboratori" },
    { voce: "Attore", categoria: "Collaboratori" },
    { voce: "Comparsa", categoria: "Collaboratori" },
    { voce: "Speaker / voce narrante", categoria: "Collaboratori" },
  ]},
  { gruppo: "Attrezzature (noleggio)", voci: [
    { voce: "Telecamera", categoria: "Noleggio" },
    { voce: "Ottiche", categoria: "Noleggio" },
    { voce: "Kit luci", categoria: "Noleggio" },
    { voce: "Kit audio", categoria: "Noleggio" },
    { voce: "Monitor / regia video", categoria: "Noleggio" },
    { voce: "Jimmy Jib / gru", categoria: "Noleggio" },
    { voce: "Slider / dolly", categoria: "Noleggio" },
    { voce: "Gruppo elettrogeno", categoria: "Noleggio" },
  ]},
  { gruppo: "Logistica / spese", voci: [
    { voce: "Trasporti", categoria: "Trasporti" },
    { voce: "Vitto e alloggio", categoria: "Trasferte" },
    { voce: "Trasferte / diarie", categoria: "Trasferte" },
    { voce: "Materiali di consumo", categoria: "Materiali" },
  ]},
];

// Raggruppa le righe di costo per categoria: usa la categoria formale (categoria_id)
// se assegnata, altrimenti ricade sulla categoria testuale già presente sulla riga.
function raggruppaPerCategoria(costi, categorie) {
  const catById = {};
  (categorie || []).forEach(c => { catById[c.id] = c; });
  const groups = new Map();
  (costi || []).forEach(r => {
    let key, nome, codice = "", fringe_pct = 0;
    if (r.categoria_id && catById[r.categoria_id]) {
      const c = catById[r.categoria_id];
      key = "cat:" + c.id; nome = c.nome; codice = c.codice || ""; fringe_pct = Number(c.fringe_pct) || 0;
    } else {
      const t = r.categoria && r.categoria !== "—" ? r.categoria : "Senza categoria";
      key = "txt:" + t; nome = t;
    }
    if (!groups.has(key)) groups.set(key, { key, nome, codice, fringe_pct, righe: [] });
    groups.get(key).righe.push(r);
  });
  return [...groups.values()].map(g => {
    const subtot = sommaCategoria(g.righe);
    const oneri = subtot * (g.fringe_pct || 0) / 100;
    return { ...g, subtot, oneri, totale: subtot + oneri };
  });
}

// Topsheet linea Produzione: riepilogo categorie con subtotali, oneri e imprevisti.
function TopsheetProduzione({ costi, categorie, contingencyPct, oneriPersonale = 0, versioneId, onManage, onLoadModel }) {
  const [expanded, setExpanded] = useState3({});
  const [cont, setCont] = useState3(String(contingencyPct || 0));
  React.useEffect(() => { setCont(String(contingencyPct || 0)); }, [contingencyPct, versioneId]);

  const rows = raggruppaPerCategoria(costi, categorie);
  const totSub = rows.reduce((s, r) => s + r.totale, 0);
  const oneri = Number(oneriPersonale) || 0;
  const imprevisti = totSub * (Number(contingencyPct) || 0) / 100;
  const totGen = totSub + oneri + imprevisti;

  const toggle = (k) => setExpanded(e => ({ ...e, [k]: !e[k] }));
  const saveCont = async () => {
    const v = parseFloat(String(cont).replace(",", ".")) || 0;
    if (v === (Number(contingencyPct) || 0)) return;
    const { error } = await window.AntaresStore.setContingency(versioneId, v);
    if (error) { alert(error.message); return; }
    await window.AntaresStore.refresh("preventivi");
  };

  return (
    <div className="card">
      <div className="card-bar yellow" />
      <div className="card-head">
        <h3 className="h3">Topsheet · riepilogo categorie</h3>
        <div className="row" style={{ gap: 8, alignItems: "center" }}>
          <span className="pill teal">linea Produzione</span>
          {categorie.length === 0 && onLoadModel && (
            <button className="btn" onClick={onLoadModel}>Carica modello completo</button>
          )}
          <button className="btn ghost" onClick={onManage}>Gestisci categorie</button>
        </div>
      </div>
      <div className="table-wrap">
        <table className="table">
          <thead>
            <tr>
              <th>Categoria</th>
              <th className="num">Subtotale</th>
              <th className="num">Oneri</th>
              <th className="num">Totale</th>
            </tr>
          </thead>
          <tbody>
            {rows.length === 0 && (
              <tr><td colSpan={4} style={{ padding: 20, textAlign: "center", color: "var(--muted)" }}>
                Nessun costo inserito. Aggiungi voci di costo qui sotto: si raggruppano qui per categoria.
              </td></tr>
            )}
            {rows.map(r => (
              <React.Fragment key={r.key}>
                <tr onClick={() => toggle(r.key)} style={{ cursor: "pointer" }}>
                  <td>
                    <strong>{expanded[r.key] ? "▾ " : "▸ "}{r.codice ? r.codice + " · " : ""}{r.nome}</strong>
                    <div className="muted" style={{ fontSize: 11 }}>
                      {r.righe.length} voci{r.fringe_pct ? ` · oneri ${fmtPct3(r.fringe_pct)}` : ""}
                    </div>
                  </td>
                  <td className="num">{fmt3(r.subtot)}</td>
                  <td className="num">{r.oneri ? fmt3(r.oneri) : "—"}</td>
                  <td className="num"><strong>{fmt3(r.totale)}</strong></td>
                </tr>
                {expanded[r.key] && r.righe.map(rr => (
                  <tr key={rr.id} style={{ background: "var(--surface-2)" }}>
                    <td style={{ paddingLeft: 28, fontSize: 13 }} className="muted">{rr.voce}</td>
                    <td className="num muted" style={{ fontSize: 13 }}>{fmt3(totRiga(rr))}</td>
                    <td></td><td></td>
                  </tr>
                ))}
              </React.Fragment>
            ))}
          </tbody>
          <tfoot>
            <tr className="row-total">
              <td>Totale categorie</td><td></td><td></td>
              <td className="num"><strong>{fmt3(totSub)}</strong></td>
            </tr>
            {oneri > 0 && (
              <tr>
                <td className="muted">Oneri personale (assunti)</td><td></td><td></td>
                <td className="num">{fmt3(oneri)}</td>
              </tr>
            )}
            <tr>
              <td>
                <span className="muted">Imprevisti</span>{" "}
                <input
                  className="input num"
                  style={{ width: 62, display: "inline-block", padding: "2px 6px", height: "auto" }}
                  value={cont}
                  onClick={e => e.stopPropagation()}
                  onChange={e => setCont(e.target.value.replace(",", "."))}
                  onBlur={saveCont}
                  onKeyDown={e => { if (e.key === "Enter") e.target.blur(); }}
                />{" "}
                <span className="muted">%</span>
              </td>
              <td></td><td></td>
              <td className="num">{fmt3(imprevisti)}</td>
            </tr>
            <tr className="row-total">
              <td><strong>TOTALE GENERALE</strong></td><td></td><td></td>
              <td className="num"><strong style={{ fontSize: 16 }}>{fmt3(totGen)}</strong></td>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
  );
}

// Modale gestione categorie del topsheet (crea/rinomina/ordina/oneri/elimina)
function CategorieManagerModal({ versioneId, categorie, onClose, onSaved }) {
  const [rows, setRows] = useState3(
    (categorie || []).slice().sort((a, b) => a.ordine - b.ordine).map(c => ({ ...c }))
  );
  const [busy, setBusy] = useState3(false);
  const [err, setErr]   = useState3("");

  const addRow = () => setRows(rs => [...rs, { id: null, codice: "", nome: "", fringe_pct: 0, ordine: rs.length }]);
  const upd = (i, k, v) => setRows(rs => rs.map((r, idx) => idx === i ? { ...r, [k]: v } : r));
  const move = (i, dir) => setRows(rs => {
    const j = i + dir; if (j < 0 || j >= rs.length) return rs;
    const copy = rs.slice(); [copy[i], copy[j]] = [copy[j], copy[i]]; return copy;
  });
  const removeRow = async (i) => {
    const r = rows[i];
    if (r.id) {
      if (!confirm(`Eliminare la categoria "${r.nome}"? Le righe assegnate torneranno "senza categoria".`)) return;
      const { error } = await window.AntaresStore.deleteCategoria(r.id);
      if (error) { setErr(error.message); return; }
      await onSaved();
    }
    setRows(rs => rs.filter((_, idx) => idx !== i));
  };
  const loadPreset = async () => {
    setBusy(true); setErr("");
    const { error } = await window.AntaresStore.seedCategoriePreset(versioneId);
    setBusy(false);
    if (error) { setErr(error.message); return; }
    await onSaved(); onClose();
  };
  const saveAll = async () => {
    setBusy(true); setErr("");
    for (let i = 0; i < rows.length; i++) {
      const r = rows[i];
      if (!r.nome || !r.nome.trim()) continue;
      const { error } = await window.AntaresStore.saveCategoria(versioneId, {
        id: r.id || undefined, codice: r.codice, nome: r.nome.trim(), ordine: i, fringe_pct: r.fringe_pct,
      });
      if (error) { setErr(error.message); setBusy(false); return; }
    }
    setBusy(false);
    await onSaved(); onClose();
  };

  const btnSmall = { background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "4px 8px", cursor: "pointer", fontSize: 12 };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 640, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 6, fontFamily: "Playfair Display, serif", fontSize: 22 }}>Gestisci categorie</h3>
        <p style={{ margin: 0, marginBottom: 16, fontSize: 13, color: "var(--muted)" }}>
          Categorie del topsheet Produzione. La % oneri si applica automaticamente al subtotale della categoria.
        </p>
        {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}

        {rows.length === 0 && (
          <div style={{ textAlign: "center", padding: "16px 0 8px" }}>
            <p className="muted" style={{ fontSize: 13 }}>Nessuna categoria. Parti dai preset standard audiovisivo o aggiungi a mano.</p>
            <button className="btn primary" onClick={loadPreset} disabled={busy} style={{ marginBottom: 8 }}>
              {busy ? "Carico…" : "Carica preset standard"}
            </button>
          </div>
        )}

        {rows.length > 0 && (
          <div className="stack" style={{ gap: 6, maxHeight: "52vh", overflow: "auto", marginBottom: 12 }}>
            <div className="row" style={{ fontSize: 11, color: "var(--muted)", padding: "0 2px", gap: 6 }}>
              <span style={{ width: 48 }}>Cod.</span>
              <span style={{ flex: 1 }}>Nome categoria</span>
              <span style={{ width: 66 }}>Oneri %</span>
              <span style={{ width: 96 }}>Ordine / elimina</span>
            </div>
            {rows.map((r, i) => (
              <div key={i} className="row" style={{ gap: 6, alignItems: "center" }}>
                <input className="input" style={{ width: 48, padding: "6px 6px" }} value={r.codice || ""} onChange={e => upd(i, "codice", e.target.value)} />
                <input className="input" style={{ flex: 1 }} placeholder="Nome categoria" value={r.nome || ""} onChange={e => upd(i, "nome", e.target.value)} />
                <input className="input num" style={{ width: 66, padding: "6px 6px" }} value={r.fringe_pct ?? 0} onChange={e => upd(i, "fringe_pct", e.target.value.replace(",", "."))} />
                <button type="button" style={btnSmall} onClick={() => move(i, -1)} title="Su">▲</button>
                <button type="button" style={btnSmall} onClick={() => move(i, 1)} title="Giù">▼</button>
                <button type="button" style={{ ...btnSmall, color: "var(--danger, #c0392b)" }} onClick={() => removeRow(i)} title="Elimina">×</button>
              </div>
            ))}
            <button type="button" className="btn ghost" onClick={addRow} style={{ alignSelf: "flex-start", marginTop: 4 }}>
              <Icon3 name="plus" size={12} /> Aggiungi categoria
            </button>
          </div>
        )}

        <div className="row" style={{ justifyContent: "flex-end", gap: 8, marginTop: 8 }}>
          <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Chiudi</button>
          {rows.length > 0 && (
            <button type="button" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} onClick={saveAll} disabled={busy}>
              {busy ? "Salvo…" : "Salva categorie"}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

// Sezione "Non incluso nel preventivo": testo multilinea (una riga = una voce).
function EsclusioniSection({ versioneId, esclusioni }) {
  const [val, setVal] = useState3(esclusioni || "");
  React.useEffect(() => { setVal(esclusioni || ""); }, [versioneId, esclusioni]);

  const persist = async (newVal) => {
    setVal(newVal);
    if ((newVal || "") === (esclusioni || "")) return;
    const { error } = await window.AntaresDB.from("preventivo_versioni")
      .update({ esclusioni: newVal }).eq("id", versioneId);
    if (error) { alert(error.message); return; }
    await window.AntaresStore.refresh("preventivi");
  };

  const caricaStandard = () => {
    const have = new Set((val || "").split("\n").map(s => s.trim()).filter(Boolean));
    const nuovi = ESCLUSIONI_PRESET.split("\n").filter(l => !have.has(l.trim()));
    const base = (val || "").trim();
    persist(base ? base + "\n" + nuovi.join("\n") : ESCLUSIONI_PRESET);
  };

  return (
    <div className="card">
      <div className="card-bar" />
      <div className="card-head">
        <h3 className="h3">Non incluso nel preventivo</h3>
        <button className="btn ghost" onClick={caricaStandard}>Carica elenco standard</button>
      </div>
      <div className="card-body">
        <p className="muted" style={{ fontSize: 12, marginTop: 0, marginBottom: 8 }}>
          Una riga per voce. Compare in fondo al PDF/Excel del cliente sotto "Non incluso nel preventivo".
        </p>
        <textarea
          className="input"
          style={{ width: "100%", minHeight: 120, resize: "vertical", fontFamily: "inherit", lineHeight: 1.5 }}
          value={val}
          onChange={e => setVal(e.target.value)}
          onBlur={() => persist(val)}
          placeholder={"es. Alimentazione elettrica\nLavoro straordinario\nVitto e alloggio"}
        />
      </div>
    </div>
  );
}

// Riquadro "Costo del personale assunto": lordo, oneri datore, costo busta, costo azienda.
function CostoPersonaleSection({ costi, versioneId, includiOneri }) {
  const p = oneriParams();
  const r = riepilogoPersonale(costi, p);
  const [par, setPar] = useState3({ inps: p.inps, irap: p.irap, inail: p.inail, tfr: p.tfr, busta: p.busta, cu: p.cu });
  React.useEffect(() => { setPar({ inps: p.inps, irap: p.irap, inail: p.inail, tfr: p.tfr, busta: p.busta, cu: p.cu }); }, [p.inps, p.irap, p.inail, p.tfr, p.busta, p.cu]);

  const saveParam = async (key, val) => {
    const v = parseFloat(String(val).replace(",", ".")) || 0;
    const { error } = await window.AntaresDB.from("app_settings").upsert({ key, value: v }, { onConflict: "key" });
    if (error) { alert(error.message); return; }
    await window.AntaresStore.refresh("app_settings");
  };
  const toggleOneri = async () => {
    const { error } = await window.AntaresDB.from("preventivo_versioni").update({ includi_oneri: !includiOneri }).eq("id", versioneId);
    if (error) { alert(error.message); return; }
    await window.AntaresStore.refresh("preventivi");
  };

  const Riga = ({ label, val, strong }) => (
    <div className="row" style={{ justifyContent: "space-between", padding: "3px 0" }}>
      <span className={strong ? "" : "muted"} style={{ fontSize: strong ? 14 : 13 }}>{label}</span>
      <span className={"mono" + (strong ? "" : " muted")} style={{ fontSize: strong ? 15 : 13 }}>{fmt3(val)}</span>
    </div>
  );
  const parInput = (key, label) => (
    <label style={{ fontSize: 11, color: "var(--muted)", display: "flex", flexDirection: "column", gap: 2 }}>
      {label}
      <input className="input num" style={{ width: 72, padding: "4px 6px" }}
        value={par[key]} onChange={e => setPar(s => ({ ...s, [key]: e.target.value.replace(",", ".") }))}
        onBlur={() => saveParam({ inps: "oneri_inps_datore", irap: "oneri_irap", inail: "oneri_inail", tfr: "oneri_tfr", busta: "costo_busta", cu: "costo_cu" }[key], par[key])} />
    </label>
  );

  return (
    <div className="card">
      <div className="card-bar coral" />
      <div className="card-head">
        <h3 className="h3">Costo del personale assunto</h3>
        <span className="muted" style={{ fontSize: 12 }}>{r.n} {r.n === 1 ? "figura" : "figure"} dipendente</span>
      </div>
      <div className="card-body">
        {r.n === 0 ? (
          <div className="muted" style={{ fontSize: 13 }}>
            Nessuna riga marcata come <strong>Dipendente (assunto)</strong>. Apri una voce di costo →
            "Tipo rapporto" → <strong>Dipendente</strong> per calcolare oneri e costo azienda.
          </div>
        ) : (
          <>
            <Riga label="Lordo (al lavoratore)" val={r.lordo} strong />
            <div className="divider" style={{ margin: "6px 0" }} />
            <Riga label={`Contributi datore (${fmtNum3(p.inps,2)}%)`} val={r.inps} />
            <Riga label={`IRAP (${fmtNum3(p.irap,2)}%)`} val={r.irap} />
            <Riga label={`INAIL (${fmtNum3(p.inail,2)}%)`} val={r.inail} />
            <Riga label={`TFR (${fmtNum3(p.tfr,2)}%)`} val={r.tfr} />
            <Riga label={`Costo busta (${fmt3(p.busta)} × cedolino)`} val={r.busta} />
            <Riga label={`Certificazione Unica (${fmt3(p.cu)} × dipendente)`} val={r.cu} />
            <div className="divider" style={{ margin: "6px 0" }} />
            <Riga label="Totale oneri azienda" val={r.oneri} />
            <Riga label="COSTO AZIENDA" val={r.costoAzienda} strong />
            <div className="pos" style={{ marginTop: 12, fontSize: 13, fontWeight: 600 }}>
              ✓ Questi oneri ({fmt3(r.oneri)}) sono già inclusi nel totale del preventivo (costo azienda).
            </div>
          </>
        )}
        <div style={{ marginTop: 14, paddingTop: 10, borderTop: "1px solid var(--border)" }}>
          <div className="muted" style={{ fontSize: 11, marginBottom: 6 }}>Parametri oneri (su lordo) · <span style={{ color: "var(--coral, #c0392b)" }}>da verificare col consulente</span></div>
          <div className="row" style={{ gap: 10, flexWrap: "wrap" }}>
            {parInput("inps", "Contributi %")}
            {parInput("irap", "IRAP %")}
            {parInput("inail", "INAIL %")}
            {parInput("tfr", "TFR %")}
            {parInput("busta", "Busta €")}
            {parInput("cu", "CU €")}
          </div>
        </div>
      </div>
    </div>
  );
}

// Modale per aggiungere voci standard al preventivo (caselle raggruppate).
function VociStandardModal({ versioneId, esistenti, onClose, onSaved }) {
  const [sel, setSel] = useState3({});
  const [busy, setBusy] = useState3(false);
  const [err, setErr]   = useState3("");

  const have = new Set((esistenti || []).map(s => String(s).toLowerCase().trim()));
  const allKeys = [];
  VOCI_CATALOGO.forEach((g, gi) => g.voci.forEach((v, vi) => allKeys.push(gi + "-" + vi)));
  const selCount = Object.values(sel).filter(Boolean).length;

  const toggle = (k) => setSel(s => ({ ...s, [k]: !s[k] }));
  const selectAll = () => { const o = {}; allKeys.forEach(k => { o[k] = true; }); setSel(o); };
  const clearAll = () => setSel({});

  const aggiungi = async () => {
    setBusy(true); setErr("");
    const rows = [];
    VOCI_CATALOGO.forEach((g, gi) => g.voci.forEach((v, vi) => {
      if (sel[gi + "-" + vi] && !have.has(v.voce.toLowerCase())) {
        rows.push({
          versione_id: versioneId, tipo: "costo", voce: v.voce, categoria: v.categoria,
          categoria_id: null, qta: 1, giorni: 0, prezzo: 0, markup: 0, ordine: 0,
          visibile_cliente: true, tipo_extra: null,
        });
      }
    }));
    if (rows.length === 0) { setBusy(false); onClose(); return; }
    const { error } = await window.AntaresDB.from("preventivo_righe").insert(rows);
    setBusy(false);
    if (error) { setErr(error.message); return; }
    await onSaved(); onClose();
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 560, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 6, fontFamily: "Playfair Display, serif", fontSize: 22 }}>Aggiungi voci standard</h3>
        <p style={{ margin: 0, marginBottom: 12, fontSize: 13, color: "var(--muted)" }}>
          Le voci selezionate vengono aggiunte come righe di costo (prezzo 0, da compilare). Quelle già presenti vengono saltate.
        </p>
        {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}

        <div className="row" style={{ gap: 8, marginBottom: 10 }}>
          <button type="button" className="btn ghost" onClick={selectAll}>Seleziona tutto</button>
          <button type="button" className="btn ghost" onClick={clearAll}>Deseleziona</button>
        </div>

        <div style={{ maxHeight: "50vh", overflow: "auto", marginBottom: 14 }}>
          {VOCI_CATALOGO.map((g, gi) => (
            <div key={gi} style={{ marginBottom: 12 }}>
              <div style={{ fontWeight: 700, fontSize: 13, marginBottom: 6 }}>{g.gruppo}</div>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "4px 14px" }}>
                {g.voci.map((v, vi) => {
                  const k = gi + "-" + vi;
                  const gia = have.has(v.voce.toLowerCase());
                  return (
                    <label key={vi} style={{ display: "flex", alignItems: "center", gap: 8, cursor: gia ? "default" : "pointer", fontSize: 13, opacity: gia ? 0.45 : 1 }}>
                      <input type="checkbox" checked={!!sel[k]} disabled={gia} onChange={() => toggle(k)} />
                      <span>{v.voce}{gia ? " (già presente)" : ""}</span>
                    </label>
                  );
                })}
              </div>
            </div>
          ))}
        </div>

        <div className="row" style={{ justifyContent: "flex-end", gap: 8 }}>
          <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
          <button type="button" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} onClick={aggiungi} disabled={busy || selCount === 0}>
            {busy ? "Aggiungo…" : `Aggiungi ${selCount > 0 ? "(" + selCount + ")" : "voci"}`}
          </button>
        </div>
      </div>
    </div>
  );
}

// Modale: duplica l'intero preventivo (dalla versione corrente) su un altro progetto.
function DuplicaPreventivoModal({ data, versionInfo, onClose, onDuplicated }) {
  const progettiAll = window.AntaresData.PROGETTI || [];
  const PREV = window.AntaresData.PREVENTIVI || {};
  const disponibili = progettiAll.filter(p => !PREV[p.id]); // progetti senza preventivo
  const [targetId, setTargetId] = useState3(disponibili[0]?.id || "");
  const [cliente, setCliente]   = useState3("");
  const [busy, setBusy]         = useState3(false);
  const [err, setErr]           = useState3("");

  React.useEffect(() => {
    const t = progettiAll.find(p => p.id === targetId);
    setCliente(t?.cliente || "");
  }, [targetId]);

  const dup = async () => {
    if (!targetId) { setErr("Seleziona un progetto di destinazione"); return; }
    setBusy(true); setErr("");
    const res = await window.AntaresStore.duplicaPreventivo(
      versionInfo.versioneId, targetId, cliente, data.tipoPreventivo || "service"
    );
    setBusy(false);
    if (res.error) { setErr(res.error.message); return; }
    await window.AntaresStore.refresh("preventivi");
    if (onDuplicated) onDuplicated(targetId);
    onClose();
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 460, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 6, fontFamily: "Playfair Display, serif", fontSize: 22 }}>Duplica preventivo</h3>
        <p style={{ margin: 0, marginBottom: 14, fontSize: 13, color: "var(--muted)" }}>
          Copia <strong>{data.progetto}</strong> · versione <strong>{versionInfo.etichetta}</strong> (righe,
          categorie, pagamenti, esclusioni, imprevisti) su un nuovo progetto.
        </p>
        {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}

        {disponibili.length === 0 ? (
          <div className="muted" style={{ fontSize: 13, padding: "8px 0 16px" }}>
            Tutti i progetti hanno già un preventivo. Crea prima un nuovo progetto
            (menu utente → <strong>Gestisci progetti</strong>), poi torna qui a duplicare.
          </div>
        ) : (
          <>
            <div className="field" style={{ marginBottom: 12 }}>
              <label className="label">Progetto di destinazione</label>
              <select className="select" value={targetId} onChange={e => setTargetId(e.target.value)}>
                {disponibili.map(p => (
                  <option key={p.id} value={p.id}>{p.nome}{p.cliente ? " · " + p.cliente : ""}</option>
                ))}
              </select>
            </div>
            <div className="field" style={{ marginBottom: 16 }}>
              <label className="label">Cliente (opzionale)</label>
              <input className="input" value={cliente} onChange={e => setCliente(e.target.value)} placeholder="Cliente del nuovo preventivo" />
            </div>
          </>
        )}

        <div className="row" style={{ justifyContent: "flex-end", gap: 8 }}>
          <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
          {disponibili.length > 0 && (
            <button type="button" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} onClick={dup} disabled={busy}>
              {busy ? "Duplico…" : "Duplica"}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

// Modale gestione Listino CCNL (minimi di riferimento per figura).
function ListinoCcnlModal({ onClose, onSaved }) {
  const [rows, setRows] = useState3((window.AntaresData.CCNL_LISTINO || []).map(c => ({ ...c })));
  const [busy, setBusy] = useState3(false);
  const [err, setErr]   = useState3("");

  const upd = (i, k, v) => setRows(rs => rs.map((r, idx) => idx === i ? { ...r, [k]: v } : r));
  const addRow = () => setRows(rs => [...rs, { id: null, ruolo: "", ccnl: "", livello: "", base: "mensile", minimo: 0, note: "", ordine: rs.length }]);
  const removeRow = async (i) => {
    const r = rows[i];
    if (r.id) {
      if (!confirm(`Eliminare "${r.ruolo}" dal listino?`)) return;
      const { error } = await window.AntaresStore.deleteCcnl(r.id);
      if (error) { setErr(error.message); return; }
      await onSaved();
    }
    setRows(rs => rs.filter((_, idx) => idx !== i));
  };
  const saveAll = async () => {
    setBusy(true); setErr("");
    for (let i = 0; i < rows.length; i++) {
      const r = rows[i];
      if (!r.ruolo || !r.ruolo.trim()) continue;
      const { error } = await window.AntaresStore.saveCcnl({
        id: r.id || undefined, ruolo: r.ruolo.trim(), ccnl: r.ccnl, livello: r.livello,
        base: r.base, minimo: r.minimo, note: r.note, ordine: i,
      });
      if (error) { setErr(error.message); setBusy(false); return; }
    }
    setBusy(false);
    await onSaved(); onClose();
  };
  const btnSmall = { background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "4px 8px", cursor: "pointer", fontSize: 12 };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 860, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 4, fontFamily: "Playfair Display, serif", fontSize: 22 }}>Listino CCNL · minimi di riferimento</h3>
        <p style={{ margin: 0, marginBottom: 12, fontSize: 12, color: "var(--coral, #c0392b)" }}>
          ⚠️ Valori precompilati da fonti pubbliche e <strong>stime di livello</strong>: da verificare col consulente del lavoro. Tutto modificabile.
        </p>
        {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}

        <div className="stack" style={{ gap: 5, maxHeight: "56vh", overflow: "auto", marginBottom: 12 }}>
          <div className="row" style={{ fontSize: 11, color: "var(--muted)", gap: 6, padding: "0 2px" }}>
            <span style={{ flex: 2 }}>Ruolo</span>
            <span style={{ flex: 1 }}>CCNL</span>
            <span style={{ width: 44 }}>Liv.</span>
            <span style={{ width: 104 }}>Base</span>
            <span style={{ width: 84 }}>Minimo €</span>
            <span style={{ width: 28 }}></span>
          </div>
          {rows.map((r, i) => (
            <div key={i} className="row" style={{ gap: 6, alignItems: "center" }}>
              <input className="input" style={{ flex: 2 }} value={r.ruolo || ""} placeholder="Ruolo" onChange={e => upd(i, "ruolo", e.target.value)} />
              <input className="input" style={{ flex: 1 }} value={r.ccnl || ""} placeholder="CCNL" onChange={e => upd(i, "ccnl", e.target.value)} />
              <input className="input" style={{ width: 44, padding: "6px 4px" }} value={r.livello || ""} onChange={e => upd(i, "livello", e.target.value)} />
              <select className="select" style={{ width: 104 }} value={r.base || "mensile"} onChange={e => upd(i, "base", e.target.value)}>
                <option value="mensile">mensile</option>
                <option value="giornaliero">giornaliero</option>
                <option value="settimanale">settimanale</option>
              </select>
              <input className="input num" style={{ width: 84, padding: "6px 6px" }} value={r.minimo ?? 0} onChange={e => upd(i, "minimo", e.target.value.replace(",", "."))} />
              <button type="button" style={{ ...btnSmall, color: "var(--danger, #c0392b)" }} onClick={() => removeRow(i)} title="Elimina">×</button>
            </div>
          ))}
          <button type="button" className="btn ghost" onClick={addRow} style={{ alignSelf: "flex-start", marginTop: 4 }}>
            <Icon3 name="plus" size={12} /> Aggiungi figura
          </button>
        </div>

        <div className="row" style={{ justifyContent: "flex-end", gap: 8 }}>
          <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Chiudi</button>
          <button type="button" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} onClick={saveAll} disabled={busy}>
            {busy ? "Salvo…" : "Salva listino"}
          </button>
        </div>
      </div>
    </div>
  );
}

// Griglia editabile in-cella (tipo Excel) per righe ricavo/costo del preventivo.
// Modello: modifichi tutto in locale, poi "Salva" persiste in blocco (insert/update/delete).
function EditableRigheGrid({ tipo, righe, versioneId, categorie = [], tipoPreventivo = "service", onDone, onCancel }) {
  const isCosto = tipo === "costo";
  const usaFormali = isCosto && tipoPreventivo === "produzione" && categorie.length > 0;
  let _kc = 0;
  const mk = (r) => ({
    _k: r.id || ("new-" + (_kc++) + "-" + (Math.round((r.prezzo || 0) * 100))),
    id: r.id || null, voce: r.voce || "",
    categoria: r.categoria && r.categoria !== "—" ? r.categoria : "",
    categoria_id: r.categoria_id || "",
    qta: r.qta != null ? r.qta : 1, giorni: r.giorni != null ? r.giorni : 0,
    prezzo: r.prezzo != null ? r.prezzo : 0, markup: r.markup != null ? r.markup : 0,
  });
  const [rows, setRows] = useState3((righe || []).map(mk));
  const [busy, setBusy] = useState3(false);
  const [err, setErr]   = useState3("");
  const [pasteOpen, setPasteOpen] = useState3(false);
  const [pasteTxt, setPasteTxt]   = useState3("");
  const origIds = (righe || []).filter(r => r.id).map(r => r.id);

  const num = (v) => { const n = parseFloat(String(v).replace(",", ".")); return isNaN(n) ? 0 : n; };
  const setCell = (k, f, v) => setRows(rs => rs.map(r => r._k === k ? { ...r, [f]: v } : r));
  const addRow = () => setRows(rs => [...rs, mk({})]);
  const delRow = (k) => setRows(rs => rs.filter(r => r._k !== k));
  const totR = (r) => { const q = num(r.qta) || 1, g = num(r.giorni) || 0, p = num(r.prezzo) || 0; return g > 0 ? q * g * p : q * p; };
  const grand = rows.reduce((s, r) => s + totR(r), 0);

  // Incolla da Excel: TSV (celle separate da TAB). Ordine: Voce, Q.tà, Giorni, Prezzo[, Markup]
  // Scorciatoia: 2 sole colonne = Voce, Prezzo.
  const importaIncolla = () => {
    const numC = (x) => { const v = parseFloat(String(x || "").replace(/[^0-9,.\-]/g, "").replace(",", ".")); return isNaN(v) ? 0 : v; };
    const isHeader = (cells) => /voce|descr|prezzo|q\.?t|giorni|importo|imponibile|markup/i.test(cells.join(" ")) && numC(cells[cells.length - 1]) === 0;
    const lines = String(pasteTxt || "").split(/\r?\n/).map(l => l.replace(/\s+$/, "")).filter(l => l.trim());
    const nuove = [];
    lines.forEach((line, i) => {
      let cells = line.includes("\t") ? line.split("\t") : line.split(/ {2,}|;|\|/);
      cells = cells.map(c => c.trim());
      if (i === 0 && isHeader(cells)) return;
      const voce = cells[0] || "";
      if (!voce) return;
      let qta = 1, giorni = 0, prezzo = 0, markup = 0;
      if (cells.length === 2) { prezzo = numC(cells[1]); }
      else { qta = numC(cells[1]) || 1; giorni = numC(cells[2]) || 0; prezzo = numC(cells[3]) || 0; markup = numC(cells[4]) || 0; }
      nuove.push(mk({ voce, qta, giorni, prezzo, markup: isCosto ? markup : 0 }));
    });
    if (nuove.length === 0) { setErr("Niente da incollare (controlla il formato)."); return; }
    setRows(rs => [...rs, ...nuove]);
    setPasteTxt(""); setPasteOpen(false); setErr("");
  };

  // Carica file Excel/CSV: riconosce le colonne dall'INTESTAZIONE (qualsiasi ordine).
  const toNum = (v) => {
    if (typeof v === "number") return v;
    let s = String(v || "").replace(/[€\s]/g, "");
    if (!s) return 0;
    if (s.includes(".") && s.includes(",")) s = (s.lastIndexOf(",") > s.lastIndexOf(".")) ? s.replace(/\./g, "").replace(",", ".") : s.replace(/,/g, "");
    else if (s.includes(",")) s = s.replace(",", ".");
    const n = parseFloat(s); return isNaN(n) ? 0 : n;
  };
  const caricaFile = (file) => {
    setErr("");
    if (!file) return;
    if (!window.XLSX) { setErr("Libreria Excel non caricata. Ricarica la pagina."); return; }
    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const wb = window.XLSX.read(new Uint8Array(e.target.result), { type: "array" });
        const ws = wb.Sheets[wb.SheetNames[0]];
        const grid = window.XLSX.utils.sheet_to_json(ws, { header: 1, defval: "", raw: true });
        const norm = (c) => String(c).toLowerCase().trim();
        const hi = grid.findIndex(r => r.some(c => /voce|descr/.test(norm(c))) && r.some(c => /prezzo|importo|costo/.test(norm(c))));
        if (hi < 0) { setErr("Non trovo le intestazioni: il file deve avere una riga con almeno “Voce/Descrizione” e “Prezzo”."); return; }
        const H = grid[hi].map(norm);
        const find = (re) => H.findIndex(h => re.test(h));
        const ci = {
          voce: find(/voce|descr/),
          qta: find(/q\.?t|quant/),
          giorni: find(/giorn|gg|giornate/),
          prezzo: find(/prezzo/) >= 0 ? find(/prezzo/) : find(/importo|costo/),
          markup: find(/markup|ricaric/),
          cat: find(/categoria/),
        };
        const nuove = [];
        for (let i = hi + 1; i < grid.length; i++) {
          const r = grid[i];
          const voce = String(r[ci.voce] || "").trim();
          if (!voce) continue;
          nuove.push(mk({
            voce,
            qta: ci.qta >= 0 ? (toNum(r[ci.qta]) || 1) : 1,
            giorni: ci.giorni >= 0 ? toNum(r[ci.giorni]) : 0,
            prezzo: ci.prezzo >= 0 ? toNum(r[ci.prezzo]) : 0,
            markup: isCosto && ci.markup >= 0 ? toNum(r[ci.markup]) : 0,
            categoria: isCosto && ci.cat >= 0 ? String(r[ci.cat] || "").trim() : "",
          }));
        }
        if (nuove.length === 0) { setErr("Nessuna riga valida trovata sotto l’intestazione."); return; }
        setRows(rs => [...rs, ...nuove]);
      } catch (ex) {
        console.error("[carica excel grid]", ex);
        setErr("Errore lettura file: " + (ex.message || ex));
      }
    };
    reader.readAsArrayBuffer(file);
  };

  // timeout di sicurezza: una richiesta che non risponde fallisce (niente "Salvo…" all'infinito)
  const withTimeout = (p, ms = 20000) => Promise.race([
    Promise.resolve(p),
    new Promise((_, rej) => setTimeout(() => rej(new Error("Timeout: il server non ha risposto. Riprova fra qualche secondo.")), ms)),
  ]);

  const save = async () => {
    setBusy(true); setErr("");
    try {
      const db = window.AntaresDB;
      const localIds = rows.filter(r => r.id).map(r => r.id);
      const deleted = origIds.filter(id => !localIds.includes(id));
      // costruzione payload (riga -> DB), ordine = posizione nella griglia
      const toRow = (r, idx) => {
        const base = {
          versione_id: versioneId, tipo, voce: r.voce.trim(),
          qta: num(r.qta) || 1, giorni: num(r.giorni) || 0, prezzo: num(r.prezzo) || 0, ordine: idx,
        };
        if (isCosto) {
          base.markup = num(r.markup) || 0;
          if (usaFormali) base.categoria_id = r.categoria_id || null;
          else base.categoria = r.categoria || null;
        }
        return base;
      };
      const updates = [], inserts = [];
      rows.forEach((r, idx) => {
        if (!r.voce.trim()) return;
        if (r.id) updates.push({ id: r.id, ...toRow(r, idx) });
        else inserts.push(toRow(r, idx));
      });
      // tutto in BLOCCO: max 3 chiamate invece di una per riga
      if (deleted.length) {
        const { error } = await withTimeout(db.from("preventivo_righe").delete().in("id", deleted));
        if (error) throw new Error(error.message);
      }
      if (updates.length) {
        const { error } = await withTimeout(db.from("preventivo_righe").upsert(updates, { onConflict: "id" }));
        if (error) throw new Error(error.message);
      }
      if (inserts.length) {
        const { error } = await withTimeout(db.from("preventivo_righe").insert(inserts));
        if (error) throw new Error(error.message);
      }
      await withTimeout(onDone());
    } catch (e) { setErr(e.message || String(e)); setBusy(false); }
  };

  const inp = { width: "100%", padding: "4px 6px", border: "1px solid var(--border)", borderRadius: 6, font: "inherit", background: "var(--surface, #fff)" };
  const colsCount = isCosto ? 7 : 5;

  return (
    <div className="card-body">
      {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
      <div className="table-wrap">
        <table className="table">
          <thead>
            <tr>
              <th style={{ minWidth: 200 }}>Voce</th>
              {isCosto && <th style={{ width: 150 }}>Categoria</th>}
              <th className="num" style={{ width: 70 }}>Q.tà</th>
              <th className="num" style={{ width: 70 }}>Giorni</th>
              <th className="num" style={{ width: 100 }}>Prezzo</th>
              {isCosto && <th className="num" style={{ width: 80 }}>Markup%</th>}
              <th className="num" style={{ width: 100 }}>Totale</th>
              <th style={{ width: 32 }}></th>
            </tr>
          </thead>
          <tbody>
            {rows.length === 0 && (
              <tr><td colSpan={colsCount + 1} style={{ padding: 14, textAlign: "center", color: "var(--muted)", fontSize: 13 }}>Nessuna riga. Clicca “+ Riga”.</td></tr>
            )}
            {rows.map(r => (
              <tr key={r._k}>
                <td><input style={inp} value={r.voce} placeholder="Descrizione" onChange={e => setCell(r._k, "voce", e.target.value)} /></td>
                {isCosto && (
                  <td>
                    {usaFormali ? (
                      <select style={inp} value={r.categoria_id} onChange={e => setCell(r._k, "categoria_id", e.target.value)}>
                        <option value="">—</option>
                        {categorie.slice().sort((a, b) => a.ordine - b.ordine).map(c => (
                          <option key={c.id} value={c.id}>{c.codice ? c.codice + " · " : ""}{c.nome}</option>
                        ))}
                      </select>
                    ) : (
                      <select style={inp} value={r.categoria} onChange={e => setCell(r._k, "categoria", e.target.value)}>
                        {["Collaboratori", "Noleggio", "Trasferte", "Trasporti", "Catering", "Servizi", "Materiali", "Altro"].map(c => <option key={c} value={c}>{c}</option>)}
                      </select>
                    )}
                  </td>
                )}
                <td><input className="num" style={{ ...inp, textAlign: "right" }} value={r.qta} onChange={e => setCell(r._k, "qta", e.target.value)} /></td>
                <td><input className="num" style={{ ...inp, textAlign: "right" }} value={r.giorni} placeholder="0" onChange={e => setCell(r._k, "giorni", e.target.value)} /></td>
                <td><input className="num" style={{ ...inp, textAlign: "right" }} value={r.prezzo} onChange={e => setCell(r._k, "prezzo", e.target.value)} /></td>
                {isCosto && <td><input className="num" style={{ ...inp, textAlign: "right" }} value={r.markup} onChange={e => setCell(r._k, "markup", e.target.value)} /></td>}
                <td className="num mono"><strong>{fmt3(totR(r))}</strong></td>
                <td><button type="button" onClick={() => delRow(r._k)} title="Elimina riga" style={{ background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "2px 7px", cursor: "pointer", color: "var(--danger, #c0392b)" }}>×</button></td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr className="row-total">
              <td>Totale</td>
              {isCosto && <td></td>}
              <td></td><td></td><td></td>
              {isCosto && <td></td>}
              <td className="num"><strong>{fmt3(grand)}</strong></td>
              <td></td>
            </tr>
          </tfoot>
        </table>
      </div>
      <div className="row" style={{ justifyContent: "space-between", marginTop: 10, flexWrap: "wrap", gap: 8 }}>
        <div className="row" style={{ gap: 8, flexWrap: "wrap" }}>
          <button type="button" className="btn ghost" onClick={addRow}><Icon3 name="plus" size={12} /> Riga</button>
          <label className="btn ghost" style={{ cursor: "pointer" }} title="Carica un file Excel: riconosce le colonne dall'intestazione (qualsiasi ordine)">
            <Icon3 name="download" size={12} /> Carica Excel
            <input type="file" accept=".xls,.xlsx,.csv" style={{ display: "none" }} onChange={e => { const f = e.target.files[0]; if (f) caricaFile(f); e.target.value = ""; }} />
          </label>
          <button type="button" className="btn ghost" onClick={() => setPasteOpen(v => !v)}><Icon3 name="copy" size={12} /> Incolla da Excel</button>
        </div>
        <div className="row" style={{ gap: 8 }}>
          <button type="button" onClick={onCancel} style={{ padding: "8px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
          <button type="button" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "8px 16px" }} onClick={save} disabled={busy}>{busy ? "Salvo…" : "Salva modifiche"}</button>
        </div>
      </div>

      {pasteOpen && (
        <div style={{ marginTop: 10, padding: 12, background: "var(--surface-2)", borderRadius: 8 }}>
          <div className="muted" style={{ fontSize: 12, marginBottom: 6 }}>
            Copia le celle da Excel e incolla qui. Colonne (separate da TAB), in ordine:
            <strong> Voce · Q.tà · Giorni · Prezzo{isCosto ? " · Markup" : ""}</strong>. Scorciatoia: 2 sole colonne = <strong>Voce · Prezzo</strong>. Un'eventuale riga di intestazione viene saltata.
          </div>
          <textarea
            value={pasteTxt}
            onChange={e => setPasteTxt(e.target.value)}
            placeholder={"Operatore\t1\t3\t300\nFonico\t1\t3\t250"}
            style={{ width: "100%", minHeight: 90, resize: "vertical", fontFamily: "monospace", fontSize: 12, padding: 8, border: "1px solid var(--border)", borderRadius: 6 }}
          />
          <div className="row" style={{ justifyContent: "flex-end", gap: 8, marginTop: 8 }}>
            <button type="button" onClick={() => { setPasteOpen(false); setPasteTxt(""); }} style={{ padding: "6px 12px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Chiudi</button>
            <button type="button" className="btn" onClick={importaIncolla}>Aggiungi righe</button>
          </div>
        </div>
      )}
      <div className="help" style={{ marginTop: 6 }}>Modifica le celle liberamente (Tab per spostarti), aggiungi/elimina/incolla righe, poi “Salva modifiche”.</div>
    </div>
  );
}

// Importa un budget Excel "a sezioni" (formato Monica) come preventivo Produzione.
function ImportBudgetModal({ progettoIdDefault, onClose, onDone }) {
  const progettiAll = window.AntaresData.PROGETTI || [];
  const [targetId, setTargetId] = useState3(progettoIdDefault || progettiAll[0]?.id || "");
  const [sezioni, setSezioni]   = useState3(null);
  const [fileName, setFileName] = useState3("");
  const [feeInfo, setFeeInfo]   = useState3("");
  const [busy, setBusy] = useState3(false);
  const [err, setErr]   = useState3("");
  const [done, setDone] = useState3(null);

  const toNum = (v) => {
    if (typeof v === "number") return v;
    let s = String(v || "").replace(/[€%\s]/g, "");
    if (!s) return 0;
    if (s.includes(".") && s.includes(",")) s = (s.lastIndexOf(",") > s.lastIndexOf(".")) ? s.replace(/\./g, "").replace(",", ".") : s.replace(/,/g, "");
    else if (s.includes(",")) s = s.replace(",", ".");
    const n = parseFloat(s); return isNaN(n) ? 0 : n;
  };
  const totRigaB = (r) => { const q = r.qta || 1, g = r.giorni || 0, p = r.prezzo || 0; const base = g > 0 ? q * g * p : q * p; return base * (1 + (r.markup || 0) / 100); };

  const parse = (grid) => {
    const norm = (c) => String(c).toLowerCase().trim();
    const hi = grid.findIndex(r => r.some(c => norm(c) === "item") && r.some(c => /unitary cost|costo unit|prezzo/.test(norm(c))));
    if (hi < 0) return { error: "Formato non riconosciuto: manca la riga con 'Item' e 'Unitary cost'." };
    const H = grid[hi].map(norm);
    const ci = {
      item: H.indexOf("item") >= 0 ? H.indexOf("item") : 0,
      name: H.indexOf("name") >= 0 ? H.indexOf("name") : 1,
      cost: H.findIndex(h => /unitary cost|costo unit|prezzo/.test(h)),
      oneri: H.findIndex(h => /vat|tax|tips|oneri|markup/.test(h)),
      days: H.findIndex(h => /week|day|giorni|giornate/.test(h)),
    };
    const sezioni = []; let cur = null; let fee = "";
    for (let i = hi + 1; i < grid.length; i++) {
      const r = grid[i];
      const item = String(r[ci.item] || "").trim();
      const name = String(r[ci.name] || "").trim();
      const cost = ci.cost >= 0 ? toNum(r[ci.cost]) : 0;
      // blocco totali finali → stop (ma cattura info fee/prezzo)
      const joined = (item + " " + name + " " + (r[3] || "") + " " + (r[4] || "")).toLowerCase();
      if (/final price|grand total|mark ?up @|administrative fee|totale parziale/.test(joined)) {
        if (/final price|grand total/.test(joined)) fee = "Prezzo finale nel file: vedi riga totali (fee 5%+2% + IVA).";
        // non interrompo del tutto: continuo per eventuali note, ma le righe senza voce vengono saltate
      }
      if (item && !name && !(cost > 0)) {
        if (/total|mark ?up|administr|vat|grand|final|iva/i.test(item)) continue;
        cur = { nome: item, righe: [] }; sezioni.push(cur); continue;
      }
      const voce = name ? (item ? item + " — " + name : name) : item;
      if (!voce || !(cost > 0)) continue;
      if (!cur) { cur = { nome: "Generale", righe: [] }; sezioni.push(cur); }
      const giorni = ci.days >= 0 ? toNum(r[ci.days]) : 0;
      const oneri = ci.oneri >= 0 ? toNum(r[ci.oneri]) : 0;
      cur.righe.push({ voce, prezzo: cost, giorni, qta: 1, markup: oneri });
    }
    const pulite = sezioni.filter(s => s.righe.length > 0);
    return { sezioni: pulite, fee };
  };

  const handleFile = (file) => {
    setErr(""); setSezioni(null); setDone(null);
    if (!file) return;
    if (!window.XLSX) { setErr("Libreria Excel non caricata. Ricarica la pagina."); return; }
    setFileName(file.name);
    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const wb = window.XLSX.read(new Uint8Array(e.target.result), { type: "array" });
        const ws = wb.Sheets[wb.SheetNames[0]];
        const grid = window.XLSX.utils.sheet_to_json(ws, { header: 1, defval: "", raw: false });
        const res = parse(grid);
        if (res.error) { setErr(res.error); return; }
        if (!res.sezioni.length) { setErr("Nessuna voce trovata."); return; }
        setSezioni(res.sezioni); setFeeInfo(res.fee || "");
      } catch (ex) { console.error("[import budget]", ex); setErr("Errore lettura file: " + (ex.message || ex)); }
    };
    reader.readAsArrayBuffer(file);
  };

  const totVoci = sezioni ? sezioni.reduce((s, x) => s + x.righe.length, 0) : 0;
  const totImporto = sezioni ? sezioni.reduce((s, x) => s + x.righe.reduce((a, r) => a + totRigaB(r), 0), 0) : 0;

  const doImport = async () => {
    if (!targetId) { setErr("Seleziona un progetto di destinazione."); return; }
    setBusy(true); setErr("");
    const res = await window.AntaresStore.importaBudgetSezioni(targetId, sezioni);
    setBusy(false);
    if (res.error) { setErr(res.error.message || String(res.error)); return; }
    await window.AntaresStore.refresh("preventivi");
    setDone({ voci: totVoci, et: res.etichetta });
    if (onDone) onDone(targetId);
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 640, maxHeight: "90vh", overflow: "auto", boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 6, fontFamily: "Playfair Display, serif", fontSize: 22 }}>Importa budget da Excel (formato a sezioni)</h3>
        <p style={{ margin: 0, marginBottom: 12, fontSize: 13, color: "var(--muted)" }}>
          Crea un preventivo <strong>Produzione</strong>: le sezioni diventano categorie, le righe diventano voci di costo
          (prezzo, giorni, oneri→markup). Le colonne consuntivo del file vengono ignorate.
        </p>
        {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}

        {done ? (
          <div style={{ padding: "8px 0" }}>
            <div className="pill teal" style={{ padding: "8px 14px" }}>✓ Importate {done.voci} voci — versione “{done.et}”</div>
            <p className="muted" style={{ fontSize: 13, marginTop: 10 }}>Apri il preventivo del progetto per vedere il topsheet. La fee/IVA del file le imposti tu (imprevisti o voce ricavo).</p>
          </div>
        ) : (
          <>
            <div className="field" style={{ marginBottom: 12 }}>
              <label className="label">Progetto di destinazione</label>
              <select className="select" value={targetId} onChange={e => setTargetId(e.target.value)}>
                {progettiAll.slice().sort((a, b) => (b.anno || 0) - (a.anno || 0) || String(a.nome).localeCompare(String(b.nome))).map(p => (
                  <option key={p.id} value={p.id}>{p.nome}{p.cliente ? " · " + p.cliente : ""}</option>
                ))}
              </select>
            </div>
            <input type="file" accept=".xls,.xlsx,.csv" onChange={e => handleFile(e.target.files[0])} style={{ marginBottom: 10 }} />
            {fileName && <div className="muted" style={{ fontSize: 12, marginBottom: 8 }}>File: {fileName}</div>}

            {sezioni && (
              <>
                <div className="row" style={{ gap: 10, marginBottom: 8, flexWrap: "wrap" }}>
                  <span className="pill teal">{sezioni.length} categorie · {totVoci} voci</span>
                  <span className="muted" style={{ fontSize: 13 }}>Totale (con markup): <strong className="mono">{fmt3(totImporto)}</strong></span>
                </div>
                <div style={{ maxHeight: "40vh", overflow: "auto", marginBottom: 12, border: "1px solid var(--border)", borderRadius: 8, padding: 10 }}>
                  {sezioni.map((s, i) => (
                    <div key={i} style={{ marginBottom: 8 }}>
                      <div style={{ fontWeight: 700, fontSize: 13 }}>{s.nome} <span className="muted" style={{ fontWeight: 400 }}>({s.righe.length})</span></div>
                      {s.righe.slice(0, 6).map((r, j) => (
                        <div key={j} className="muted" style={{ fontSize: 12, paddingLeft: 10 }}>
                          {r.voce} — {fmt3(r.prezzo)}{r.giorni ? " × " + r.giorni + "gg" : ""}{r.markup ? " +" + r.markup + "%" : ""}
                        </div>
                      ))}
                      {s.righe.length > 6 && <div className="muted" style={{ fontSize: 11, paddingLeft: 10 }}>… +{s.righe.length - 6}</div>}
                    </div>
                  ))}
                </div>
              </>
            )}
            <div className="row" style={{ justifyContent: "flex-end", gap: 8 }}>
              <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
              <button type="button" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} onClick={doImport} disabled={busy || !sezioni}>
                {busy ? "Importo…" : "Importa budget"}
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

/* ============================================================
   9. PREVENTIVI & CONSUNTIVI
   ============================================================ */
function Preventivi() {
  const progettiAll = window.AntaresData.PROGETTI || [];
  const progettiConPreventivo = Object.keys(PREVENTIVI);
  const [progettoId, setProgettoId] = useState3(progettiConPreventivo[0] || progettiAll[0]?.id || "");
  const [view, setView] = useState3("preventivo");
  const [versione, setVersione] = useState3(null);
  const [showRigaModal, setShowRigaModal] = useState3(null); // { tipo, editing? }
  const [showVerModal, setShowVerModal]   = useState3(false);
  const [showCatModal, setShowCatModal]   = useState3(false);
  const [showVociModal, setShowVociModal] = useState3(false);
  const [showDupModal, setShowDupModal]   = useState3(false);
  const [showCcnlModal, setShowCcnlModal] = useState3(false);
  const [editRicavi, setEditRicavi] = useState3(false);
  const [editCosti, setEditCosti]   = useState3(false);
  const [showImpBudget, setShowImpBudget] = useState3(false);

  const data = PREVENTIVI[progettoId];

  // Empty state: progetto senza preventivo, oppure nessun progetto
  if (!data) {
    return (
      <div className="stack">
        <div className="page-head">
          <div className="page-title-block">
            <span className="eyebrow">Pianificazione commessa</span>
            <h1 className="h1">Preventivi & Consuntivi</h1>
          </div>
        </div>
        <div className="card">
          <div className="card-body" style={{ padding: 30 }}>
            {progettiAll.length === 0 ? (
              <div style={{ textAlign: "center", color: "var(--muted)" }}>
                Nessun progetto ancora creato. Vai sul menu utente → <strong>Gestisci progetti</strong> per aggiungerne uno.
              </div>
            ) : (
              <>
                <div className="field" style={{ marginBottom: 16 }}>
                  <label className="label">Crea preventivo per un progetto</label>
                  <select className="select" value={progettoId} onChange={e => setProgettoId(e.target.value)}>
                    {progettiAll.map(p => (
                      <option key={p.id} value={p.id}>{p.nome}{p.cliente ? " · " + p.cliente : ""}</option>
                    ))}
                  </select>
                </div>
                {(() => {
                  const creaPreventivo = async (conModello) => {
                    // Re-sync progetti prima di creare (evita FK violation se stato locale stale)
                    await window.AntaresStore.refresh("progetti");
                    const fresh = (window.AntaresData.PROGETTI || []).find(p => p.id === progettoId);
                    if (!fresh) {
                      alert("Il progetto '" + progettoId + "' non esiste più nel database.\nApri 'Gestisci progetti' dal menu utente e verifica/ricrealo.");
                      return;
                    }
                    const res = await window.AntaresStore.createPreventivo(progettoId, fresh.cliente || "");
                    if (res.error) {
                      if (res.error.code === "23503" || /foreign key/i.test(res.error.message)) {
                        alert("Errore: il progetto selezionato non è più nel database.\nApri 'Gestisci progetti' e verifica.");
                      } else {
                        alert(res.error.message);
                      }
                      return;
                    }
                    if (conModello && res.versione) {
                      // Service precompilato: tutte le voci standard come righe di costo (prezzo 0)
                      const rows = [];
                      VOCI_CATALOGO.forEach(g => g.voci.forEach((v, vi) => rows.push({
                        versione_id: res.versione.id, tipo: "costo", voce: v.voce, categoria: v.categoria,
                        categoria_id: null, qta: 1, giorni: 0, prezzo: 0, markup: 0, ordine: vi,
                        visibile_cliente: true, tipo_extra: null,
                      })));
                      const { error } = await window.AntaresDB.from("preventivo_righe").insert(rows);
                      if (error) { alert("Preventivo creato, ma errore caricando le voci: " + error.message); }
                    }
                    await window.AntaresStore.refresh("preventivi");
                  };
                  return (
                    <div className="row" style={{ gap: 8, flexWrap: "wrap" }}>
                      <button className="btn primary" onClick={() => creaPreventivo(false)}>
                        <Icon3 name="plus" /> Crea preventivo vuoto (v1)
                      </button>
                      <button className="btn" onClick={() => creaPreventivo(true)} title="Crea un preventivo Service già pieno delle voci standard (a prezzo 0), pronto da compilare e snellire">
                        <Icon3 name="plus" /> Crea preventivo precompilato (Service)
                      </button>
                      <button className="btn ghost" onClick={() => setShowImpBudget(true)} title="Importa un budget Excel a sezioni (es. formato Monica) come preventivo Produzione">
                        <Icon3 name="download" /> Importa budget Excel
                      </button>
                    </div>
                  );
                })()}
              </>
            )}
          </div>
        </div>
        {showImpBudget && (
          <ImportBudgetModal
            progettoIdDefault={progettoId}
            onClose={() => setShowImpBudget(false)}
            onDone={() => window.AntaresStore.refresh("preventivi")}
          />
        )}
      </div>
    );
  }

  const verAttiva = versione || data.versioneAttiva;
  const versionInfo = (data.versioni || []).find(v => v.id === verAttiva) || (data.versioni || [])[((data.versioni || []).length - 1)] || null;
  const versioneIdCorrente = versionInfo?.versioneId;

  // Guard: preventivo senza versioni (situazione anomala)
  if (!versionInfo) {
    return (
      <div className="stack">
        <div className="page-head">
          <div className="page-title-block">
            <span className="eyebrow">Pianificazione commessa</span>
            <h1 className="h1">Preventivi & Consuntivi</h1>
          </div>
        </div>
        <div className="card">
          <div className="card-body" style={{ padding: 30, textAlign: "center" }}>
            <p style={{ marginBottom: 16 }}>Preventivo trovato ma senza versioni. Creane una.</p>
            <button className="btn primary" onClick={async () => {
              const res = await window.AntaresStore.createVersione(data.preventivoId, "v1", "Prima versione", null);
              if (res.error) { alert(res.error.message); return; }
              await window.AntaresStore.setVersioneAttiva(data.preventivoId, res.versione.id);
              await window.AntaresStore.refresh("preventivi");
            }}>+ Crea versione v1</button>
          </div>
        </div>
      </div>
    );
  }

  const prev = data.preventivo;
  const cons = data.consuntivo;

  const _riepPers = riepilogoPersonale(prev.costi, oneriParams());
  // oneri dipendenti SEMPRE inclusi nel totale: è il costo azienda reale (niente preventivi sottostimati)
  const oneriInBudget = _riepPers.oneri;
  const totRicaviPrev = sommaCategoria(prev.ricavi);
  const totCostiPrev = sommaCategoria(prev.costi) + oneriInBudget;
  const marginePrev = totRicaviPrev - totCostiPrev;
  const pctPrev = totRicaviPrev > 0 ? (marginePrev / totRicaviPrev) * 100 : 0;

  const totRicaviCons = sommaCategoria(cons.ricavi);
  const totCostiCons = sommaCategoria(cons.costi);
  const margineCons = totRicaviCons - totCostiCons;
  const pctCons = totRicaviCons > 0 ? (margineCons / totRicaviCons) * 100 : 0;
  const safePct = (num, den) => den > 0 ? (num / den) * 100 : 0;

  const tipoPreventivo = data.tipoPreventivo || "service";
  const setTipoPreventivo = async (tipo) => {
    if (tipo === tipoPreventivo) return;
    const { error } = await window.AntaresDB.from("preventivi")
      .update({ tipo_preventivo: tipo }).eq("id", data.preventivoId);
    if (error) { alert(error.message); return; }
    await window.AntaresStore.refresh("preventivi");
  };

  /* RIGHE TABELLA */
  const RigheRicavi = ({ righe, mode, onEdit, onDelete }) => (
    <table className="table preventivo-table">
      <thead>
        <tr>
          <th>Voce di ricavo</th>
          <th className="num">Q.tà</th>
          <th className="num">Giorni</th>
          <th className="num">Prezzo</th>
          <th className="num">Totale</th>
          {mode === "consuntivo" && <th>Fonte</th>}
          {mode === "preventivo" && <th></th>}
        </tr>
      </thead>
      <tbody>
        {righe.length === 0 && (
          <tr><td colSpan={mode === "consuntivo" ? 5 : 5} style={{ padding: 16, textAlign: "center", color: "var(--muted)", fontSize: 13 }}>
            {mode === "preventivo" ? "Nessuna voce. Clicca 'Aggiungi voce'." : "Nessuna fattura attiva collegata a questo progetto."}
          </td></tr>
        )}
        {righe.map(r => {
          const nascosta = mode === "preventivo" && r.visibile_cliente === false;
          const extra = r.tipo_extra || r.extra;
          return (
            <tr key={r.id} className={extra ? "row-warn" : ""} style={nascosta ? { opacity: 0.55 } : {}}>
              <td>
                {nascosta && <span title="Non visibile al cliente" style={{ marginRight: 6 }}>🚫</span>}
                {r.voce}
                {r.tipo_extra === "straordinario" && <span className="pill yellow" style={{ marginLeft: 8 }}>straordinario</span>}
                {r.tipo_extra === "fuori_preventivo" && <span className="pill coral" style={{ marginLeft: 8 }}>fuori preventivo</span>}
                {r.extra && !r.tipo_extra && <span className="pill yellow" style={{ marginLeft: 8 }}>extra</span>}
              </td>
              <td className="num mono">{r.qta}</td>
              <td className="num mono muted">{r.giorni > 0 ? r.giorni : "—"}</td>
              <td className="num">{fmt3(r.prezzo)}</td>
              <td className="num"><strong>{fmt3(totRiga(r))}</strong></td>
              {mode === "consuntivo" && <td><span className="pill teal">auto · fatture</span></td>}
              {mode === "preventivo" && (
                <td style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                  <button onClick={() => onEdit?.(r)} style={{ marginRight: 4, background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "3px 8px", cursor: "pointer", fontSize: 12 }}>Modifica</button>
                  <button onClick={() => onDelete?.(r)} style={{ background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "3px 8px", cursor: "pointer", fontSize: 12, color: "var(--danger, #c0392b)" }}>×</button>
                </td>
              )}
            </tr>
          );
        })}
        <tr className="row-total">
          <td>Totale ricavi</td>
          <td></td><td></td><td></td>
          <td className="num"><strong className="pos">{fmt3(sommaCategoria(righe))}</strong></td>
          {mode === "consuntivo" && <td></td>}
          {mode === "preventivo" && <td></td>}
        </tr>
      </tbody>
    </table>
  );

  const RigheCosti = ({ righe, mode, onEdit, onDelete }) => (
    <table className="table preventivo-table">
      <thead>
        <tr>
          <th>Voce di costo</th>
          <th>Categoria</th>
          <th className="num">Q.tà</th>
          <th className="num">Giorni</th>
          <th className="num">Prezzo</th>
          {mode === "preventivo" && <th className="num">Markup</th>}
          <th className="num">Totale</th>
          {mode === "consuntivo" && <th>Fonte</th>}
          {mode === "preventivo" && <th></th>}
        </tr>
      </thead>
      <tbody>
        {righe.length === 0 && (
          <tr><td colSpan={mode === "consuntivo" ? 6 : 7} style={{ padding: 16, textAlign: "center", color: "var(--muted)", fontSize: 13 }}>
            {mode === "preventivo" ? "Nessun costo previsto. Aggiungi il primo." : "Nessun costo collegato a questo progetto (fatture passive o collaboratori)."}
          </td></tr>
        )}
        {righe.map(r => {
          const nascosta = mode === "preventivo" && r.visibile_cliente === false;
          return (
          <tr key={r.id} className={r.extra || r.tipo_extra ? "row-warn" : ""} style={nascosta ? { opacity: 0.55 } : {}}>
            <td>
              {nascosta && <span title="Non visibile al cliente" style={{ marginRight: 6 }}>🚫</span>}
              {r.voce}
              {r.tipo_extra === "straordinario" && <span className="pill yellow" style={{ marginLeft: 8 }}>straordinario</span>}
              {r.tipo_extra === "fuori_preventivo" && <span className="pill coral" style={{ marginLeft: 8 }}>fuori preventivo</span>}
              {r.extra && !r.tipo_extra && <span className="pill yellow" style={{ marginLeft: 8 }}>extra</span>}
              {r.parziale && <span className="pill blue" style={{ marginLeft: 8 }}>in corso</span>}
              {mode === "preventivo" && <CongruitaBadge row={r} />}
            </td>
            <td><span className="pill">{r.categoria}</span></td>
            <td className="num mono">{r.qta}</td>
            <td className="num mono muted">{r.giorni > 0 ? r.giorni : "—"}</td>
            <td className="num">{fmt3(r.prezzo)}</td>
            {mode === "preventivo" && (
              <td className="num muted">{r.markup ? "+" + r.markup + "%" : "—"}</td>
            )}
            <td className="num"><strong>{fmt3(totRiga(r))}</strong></td>
            {mode === "consuntivo" && (
              <td>
                <span className={"pill " + (r.fonte === "FATTURE" ? "coral" : "blue")}>
                  {r.fonte || "—"}
                </span>
              </td>
            )}
            {mode === "preventivo" && (
              <td style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                <button onClick={() => onEdit?.(r)} style={{ marginRight: 4, background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "3px 8px", cursor: "pointer", fontSize: 12 }}>Modifica</button>
                <button onClick={() => onDelete?.(r)} style={{ background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "3px 8px", cursor: "pointer", fontSize: 12, color: "var(--danger, #c0392b)" }}>×</button>
              </td>
            )}
          </tr>
          );
        })}
        <tr className="row-total">
          <td>Totale costi</td>
          <td></td><td></td><td></td><td></td>
          {mode === "preventivo" && <td></td>}
          <td className="num"><strong className="neg">{fmt3(sommaCategoria(righe))}</strong></td>
          {mode === "consuntivo" && <td></td>}
          {mode === "preventivo" && <td></td>}
        </tr>
      </tbody>
    </table>
  );

  const handleDeleteRiga = async (r) => {
    if (!confirm("Eliminare la voce '" + r.voce + "'?")) return;
    const res = await window.AntaresStore.deleteRiga(r.id);
    if (res.error) { alert(res.error.message); return; }
    await window.AntaresStore.refresh("preventivi");
  };

  /* SCOSTAMENTO — non uso useMemo per evitare ordine hooks instabile fra render */
  const allVoci = (() => {
    const map = new Map();
    [...prev.costi.map(r => ({ ...r, _src: "prev" })), ...cons.costi.map(r => ({ ...r, _src: "cons" }))].forEach(r => {
      const key = r.id;
      if (!map.has(key)) map.set(key, { id: key, voce: r.voce, categoria: r.categoria });
      const entry = map.get(key);
      entry[r._src === "prev" ? "prev" : "cons"] = totRiga(r);
    });
    return Array.from(map.values());
  })();

  return (
    <div className="stack">
      <div className="page-head">
        <div className="page-title-block">
          <span className="eyebrow">Pianificazione commessa</span>
          <h1 className="h1">Preventivi & Consuntivi</h1>
        </div>
        <div className="page-actions">
          <button className="btn" onClick={() => setShowVerModal(true)}>
            <Icon3 name="plus" size={12} /> Nuova versione
          </button>
          <button className="btn ghost" onClick={() => setShowDupModal(true)} title="Duplica l'intero preventivo su un altro progetto">
            <Icon3 name="copy" size={12} /> Duplica
          </button>
          <button className="btn ghost" onClick={() => setShowCcnlModal(true)} title="Gestisci il listino CCNL (minimi di riferimento per figura)">
            <Icon3 name="users" size={12} /> Listino CCNL
          </button>
          <button className="btn ghost" onClick={() => setShowImpBudget(true)} title="Importa un budget Excel a sezioni (es. formato Monica) come preventivo Produzione">
            <Icon3 name="download" size={12} /> Importa budget Excel
          </button>
          <button
            className="btn ghost"
            onClick={() => tipoPreventivo === "produzione"
              ? window.AntaresPDF.exportTopsheetPDF(data, versionInfo)
              : window.AntaresPDF.exportPreventivoPDF(data, versionInfo)}
            title={tipoPreventivo === "produzione"
              ? "Esporta il budget di produzione (topsheet + dettaglio) in PDF"
              : "Esporta un PDF pulito per il cliente (senza markup e costi interni)"}
          >
            <Icon3 name="pdf" /> {tipoPreventivo === "produzione" ? "PDF budget" : "PDF"}
          </button>
          <button
            className="btn ghost"
            onClick={() => tipoPreventivo === "produzione"
              ? window.AntaresXLSX.exportTopsheetXLSX(data, versionInfo)
              : window.AntaresXLSX.exportPreventivoXLSX(data, versionInfo)}
            title={tipoPreventivo === "produzione"
              ? "Esporta il budget di produzione (Topsheet + Dettaglio) in Excel"
              : "Esporta un foglio Excel editabile per il cliente"}
          >
            <Icon3 name="download" /> {tipoPreventivo === "produzione" ? "Excel budget" : "Excel"}
          </button>
          <button
            className="btn ghost"
            onClick={() => window.AntaresPDF.exportRiepilogoFamigliePDF(data, versionInfo)}
            title="Esporta un riepilogo PDF con i costi accorpati per macro area (Collaboratori, Noleggi, Trasporti…)"
          >
            <Icon3 name="chart" /> Riepilogo per macro aree
          </button>
        </div>
      </div>

      {/* Selettori */}
      <div className="card">
        <div className="card-bar" />
        <div className="card-body">
          <div className="form-grid" style={{ gridTemplateColumns: "2fr 1fr 1fr" }}>
            <div className="field">
              <label className="label">Progetto</label>
              <select className="select" value={progettoId} onChange={e => { setProgettoId(e.target.value); setVersione(null); }}>
                {(() => {
                  // progetti con preventivo, raggruppati per anno
                  const conPrev = progettiConPreventivo
                    .map(id => progettiAll.find(p => p.id === id) || { id, nome: PREVENTIVI[id].progetto, cliente: PREVENTIVI[id].cliente, anno: null })
                    .filter(Boolean);
                  const byYear = new Map();
                  conPrev.forEach(p => { const y = p.anno || "—"; if (!byYear.has(y)) byYear.set(y, []); byYear.get(y).push(p); });
                  const years = [...byYear.keys()].sort((a, b) => (a === "—" ? 1 : b === "—" ? -1 : b - a));
                  return years.map(y => (
                    <optgroup key={y} label={y === "—" ? "Senza anno" : String(y)}>
                      {byYear.get(y).sort((a, b) => String(a.nome).localeCompare(String(b.nome))).map(p => (
                        <option key={p.id} value={p.id}>{p.nome}{p.cliente ? " · " + p.cliente : ""}</option>
                      ))}
                    </optgroup>
                  ));
                })()}
                {progettiAll.filter(p => !PREVENTIVI[p.id]).length > 0 && (
                  <optgroup label="— progetti senza preventivo —">
                    {progettiAll.filter(p => !PREVENTIVI[p.id]).sort((a, b) => (b.anno || 0) - (a.anno || 0) || String(a.nome).localeCompare(String(b.nome))).map(p => (
                      <option key={p.id} value={p.id}>{p.nome}{p.cliente ? " · " + p.cliente : ""} (crea preventivo)</option>
                    ))}
                  </optgroup>
                )}
              </select>
            </div>
            <div className="field">
              <label className="label">Versione preventivo</label>
              <select className="select" value={verAttiva} onChange={e => setVersione(e.target.value)}>
                {data.versioni.map(v => (
                  <option key={v.id} value={v.id}>
                    {v.id} · {v.data}
                    {v.stato === "approvato" ? " · in lavorazione"
                      : v.stato === "firmato" ? " · firmato"
                      : v.stato === "inviato"  ? " · inviato"
                      : v.stato === "archiviato" ? " · archiviato"
                      : ""}
                  </option>
                ))}
              </select>
            </div>
            <div className="field">
              <label className="label">Stato versione</label>
              <select
                className="select"
                value={versionInfo.stato || (versionInfo.firmata ? "firmato" : "draft")}
                onChange={async (e) => {
                  const nuovoStato = e.target.value;
                  const firmata = nuovoStato === "firmato";
                  const { error } = await window.AntaresDB
                    .from("preventivo_versioni")
                    .update({ stato: nuovoStato, firmata })
                    .eq("id", versionInfo.versioneId);
                  if (error) { alert(error.message); return; }
                  await window.AntaresStore.refresh("preventivi");
                }}
              >
                <option value="draft">In trattativa</option>
                <option value="inviato">Inviato al cliente</option>
                <option value="approvato">Approvato · in lavorazione</option>
                <option value="firmato">Firmato dal cliente</option>
                <option value="archiviato">Archiviato</option>
              </select>
              {versionInfo.nota && (
                <div className="muted" style={{ fontSize: 12, marginTop: 6 }}>{versionInfo.nota}</div>
              )}
            </div>
          </div>
        </div>
      </div>

      {/* KPI riepilogo */}
      <div className="kpi-grid">
        <div className="kpi">
          <div className="kpi-label">Ricavo previsto</div>
          <div className="kpi-value">{fmt3(totRicaviPrev)}</div>
          <div className="kpi-sub">Consuntivato {fmt3(totRicaviCons)} · {fmtPct3(safePct(totRicaviCons - totRicaviPrev, totRicaviPrev), 1)}</div>
        </div>
        <div className="kpi coral">
          <div className="kpi-label">Costo previsto</div>
          <div className="kpi-value">{fmt3(totCostiPrev)}</div>
          <div className="kpi-sub" style={{ color: totCostiCons > totCostiPrev ? "var(--coral)" : "var(--teal)" }}>
            Consuntivato {fmt3(totCostiCons)} · {fmtPct3(safePct(totCostiCons - totCostiPrev, totCostiPrev), 1)}
          </div>
        </div>
        <div className="kpi blue">
          <div className="kpi-label">Margine previsto</div>
          <div className="kpi-value">{fmt3(marginePrev)}</div>
          <div className="kpi-sub">{fmtPct3(pctPrev)} sul ricavo</div>
        </div>
        <div className={"kpi " + (margineCons < marginePrev ? "yellow" : "")}>
          <div className="kpi-label">Margine reale</div>
          <div className="kpi-value" style={{ color: margineCons < 0 ? "var(--coral)" : (margineCons < marginePrev ? "var(--yellow)" : "var(--teal)") }}>
            {fmt3(margineCons)}
          </div>
          <div className="kpi-sub">{fmtPct3(pctCons)} sul ricavo · Δ {fmt3(margineCons - marginePrev, { sign: true })}</div>
        </div>
      </div>

      {/* Tabs viste */}
      <div className="row" style={{ justifyContent: "space-between", flexWrap: "wrap", gap: 8 }}>
        <div className="segmented teal">
          <button className={view === "preventivo" ? "active" : ""} onClick={() => setView("preventivo")}>Preventivo</button>
          <button className={view === "consuntivo" ? "active" : ""} onClick={() => setView("consuntivo")}>Consuntivo</button>
          <button className={view === "scostamento" ? "active" : ""} onClick={() => setView("scostamento")}>Scostamento</button>
        </div>
        <div className="row" style={{ gap: 10, alignItems: "center" }}>
          {view === "consuntivo" && (
            <span className="muted" style={{ fontSize: 12 }}>
              <Icon3 name="check" size={12} /> Auto-popolato da fatture e collaboratori del progetto
            </span>
          )}
          <div className="row" style={{ gap: 6, alignItems: "center" }}>
            <span className="muted" style={{ fontSize: 12 }}>Tipo:</span>
            <div className="segmented compact">
              <button className={tipoPreventivo === "service" ? "active" : ""} onClick={() => setTipoPreventivo("service")}>Service</button>
              <button className={tipoPreventivo === "produzione" ? "active" : ""} onClick={() => setTipoPreventivo("produzione")}>Produzione</button>
            </div>
          </div>
        </div>
      </div>

      {view === "preventivo" && (
        <>
          {tipoPreventivo === "produzione" && (
            <TopsheetProduzione
              costi={prev.costi}
              categorie={data.categorie || []}
              contingencyPct={data.contingencyPct || 0}
              oneriPersonale={oneriInBudget}
              versioneId={versioneIdCorrente}
              onManage={() => setShowCatModal(true)}
              onLoadModel={async () => {
                if ((prev.costi || []).length > 0 &&
                    !confirm("Ci sono già righe di costo. Caricare comunque il modello completo (categorie + voci a 0)?")) return;
                const m = await window.AntaresStore.caricaModelloProduzione(data.preventivoId, versioneIdCorrente);
                if (m && m.error) { alert(m.error.message); return; }
                await window.AntaresStore.refresh("preventivi");
              }}
            />
          )}
          <div className="card">
            <div className="card-bar" />
            <div className="card-head">
              <h3 className="h3">Ricavi previsti</h3>
              <div className="row" style={{ gap: 6 }}>
                <button className={"btn ghost" + (editRicavi ? " active" : "")} onClick={() => setEditRicavi(v => !v)}><Icon3 name="edit" size={12} /> Modifica rapida</button>
                {!editRicavi && <button className="btn ghost" onClick={() => setShowRigaModal({ tipo: "ricavo" })}><Icon3 name="plus" size={12} /> Aggiungi voce</button>}
              </div>
            </div>
            {editRicavi
              ? <EditableRigheGrid tipo="ricavo" righe={prev.ricavi} versioneId={versioneIdCorrente} onCancel={() => setEditRicavi(false)} onDone={async () => { await window.AntaresStore.refresh("preventivi"); setEditRicavi(false); }} />
              : <div className="table-wrap"><RigheRicavi righe={prev.ricavi} mode="preventivo" onEdit={(r) => setShowRigaModal({ tipo: "ricavo", editing: r })} onDelete={(r) => handleDeleteRiga(r)} /></div>}
          </div>
          <div className="card">
            <div className="card-bar coral" />
            <div className="card-head">
              <h3 className="h3">Costi previsti</h3>
              <div className="row" style={{ gap: 6 }}>
                <button className={"btn ghost" + (editCosti ? " active" : "")} onClick={() => setEditCosti(v => !v)}><Icon3 name="edit" size={12} /> Modifica rapida</button>
                {!editCosti && <button className="btn ghost" onClick={() => setShowVociModal(true)}><Icon3 name="plus" size={12} /> Voci standard</button>}
                {!editCosti && <button className="btn ghost" onClick={() => setShowRigaModal({ tipo: "costo" })}><Icon3 name="plus" size={12} /> Aggiungi voce</button>}
              </div>
            </div>
            {editCosti
              ? <EditableRigheGrid tipo="costo" righe={prev.costi} versioneId={versioneIdCorrente} categorie={data.categorie || []} tipoPreventivo={tipoPreventivo} onCancel={() => setEditCosti(false)} onDone={async () => { await window.AntaresStore.refresh("preventivi"); setEditCosti(false); }} />
              : <div className="table-wrap"><RigheCosti righe={prev.costi} mode="preventivo" onEdit={(r) => setShowRigaModal({ tipo: "costo", editing: r })} onDelete={(r) => handleDeleteRiga(r)} /></div>}
            {!editCosti && oneriInBudget > 0 && (
              <div style={{ padding: "8px 14px", borderTop: "1px solid var(--border)" }}>
                <div className="row" style={{ justifyContent: "space-between" }}>
                  <span className="muted">+ Oneri datore dipendenti (contributi, IRAP, INAIL, TFR, busta, CU)</span>
                  <span className="mono">{fmt3(oneriInBudget)}</span>
                </div>
                <div className="row" style={{ justifyContent: "space-between", marginTop: 4 }}>
                  <strong>Totale costo azienda</strong>
                  <strong className="mono neg">{fmt3(totCostiPrev)}</strong>
                </div>
              </div>
            )}
          </div>

          <CostoPersonaleSection
            costi={prev.costi}
            versioneId={versioneIdCorrente}
            includiOneri={data.includiOneri}
          />

          <PagamentiSection
            pagamenti={data.pagamenti || []}
            versioneId={versioneIdCorrente}
            totaleRicavi={totRicaviPrev}
          />

          <EsclusioniSection
            versioneId={versioneIdCorrente}
            esclusioni={data.esclusioni || ""}
          />
        </>
      )}

      {view === "consuntivo" && (
        <>
          <div className="card">
            <div className="card-bar" />
            <div className="card-head">
              <h3 className="h3">Ricavi consuntivati</h3>
              <span className="pill teal"><span className="dot" /> auto · da fatture</span>
            </div>
            <div className="table-wrap"><RigheRicavi righe={cons.ricavi} mode="consuntivo" /></div>
          </div>
          <div className="card">
            <div className="card-bar coral" />
            <div className="card-head">
              <h3 className="h3">Costi consuntivati</h3>
              <span className="pill teal"><span className="dot" /> auto · da fatture e collaboratori</span>
            </div>
            <div className="table-wrap"><RigheCosti righe={cons.costi} mode="consuntivo" /></div>
          </div>
        </>
      )}

      {showRigaModal && (
        <RigaModal
          tipo={showRigaModal.tipo}
          editing={showRigaModal.editing}
          versioneId={versioneIdCorrente}
          categorie={data.categorie || []}
          tipoPreventivo={tipoPreventivo}
          onClose={() => setShowRigaModal(null)}
          onSaved={() => window.AntaresStore.refresh("preventivi")}
        />
      )}
      {showCatModal && (
        <CategorieManagerModal
          versioneId={versioneIdCorrente}
          categorie={data.categorie || []}
          onClose={() => setShowCatModal(false)}
          onSaved={() => window.AntaresStore.refresh("preventivi")}
        />
      )}
      {showVociModal && (
        <VociStandardModal
          versioneId={versioneIdCorrente}
          esistenti={(prev.costi || []).map(c => c.voce)}
          onClose={() => setShowVociModal(false)}
          onSaved={() => window.AntaresStore.refresh("preventivi")}
        />
      )}
      {showDupModal && (
        <DuplicaPreventivoModal
          data={data}
          versionInfo={versionInfo}
          onClose={() => setShowDupModal(false)}
          onDuplicated={(newProgId) => { setProgettoId(newProgId); setVersione(null); }}
        />
      )}
      {showCcnlModal && (
        <ListinoCcnlModal
          onClose={() => setShowCcnlModal(false)}
          onSaved={() => window.AntaresStore.refresh("ccnl")}
        />
      )}
      {showImpBudget && (
        <ImportBudgetModal
          progettoIdDefault={progettoId}
          onClose={() => setShowImpBudget(false)}
          onDone={(pid) => { setProgettoId(pid); setVersione(null); }}
        />
      )}
      {showVerModal && (
        <VersioneModal
          preventivoId={data.preventivoId}
          versioniEsistenti={data.versioni}
          versioneAttualeId={versioneIdCorrente}
          onClose={() => setShowVerModal(false)}
          onSaved={() => window.AntaresStore.refresh("preventivi")}
        />
      )}

      {view === "scostamento" && (
        <div className="card">
          <div className="card-bar yellow" />
          <div className="card-head">
            <h3 className="h3">Scostamento voce per voce</h3>
            <span className="muted" style={{ fontSize: 12 }}>Verde = sotto preventivo · Rosso = oltre preventivo</span>
          </div>
          <div className="table-wrap">
            <table className="table">
              <thead>
                <tr>
                  <th>Voce</th>
                  <th>Categoria</th>
                  <th className="num">Preventivo</th>
                  <th className="num">Reale</th>
                  <th className="num">Δ €</th>
                  <th className="num">Δ %</th>
                </tr>
              </thead>
              <tbody>
                {allVoci.map(v => {
                  const p = v.prev || 0;
                  const c = v.cons || 0;
                  const delta = c - p;
                  const pct = p ? (delta / p) * 100 : null;
                  // for costs: under (c < p) is good (green), over is bad (red)
                  const cls = delta === 0 ? "" : (delta > 0 ? "neg" : "pos");
                  return (
                    <tr key={v.id}>
                      <td>
                        {v.voce}
                        {!v.prev && <span className="pill yellow" style={{ marginLeft: 8 }}>nuovo a consuntivo</span>}
                      </td>
                      <td><span className="pill">{v.categoria}</span></td>
                      <td className="num">{p ? fmt3(p) : "—"}</td>
                      <td className="num">{c ? fmt3(c) : "—"}</td>
                      <td className="num"><strong className={cls}>{delta === 0 ? "—" : fmt3(delta, { sign: true })}</strong></td>
                      <td className="num"><strong className={cls}>{pct == null ? "—" : fmtPct3(pct)}</strong></td>
                    </tr>
                  );
                })}
                <tr className="row-total">
                  <td>Totale costi</td><td></td>
                  <td className="num">{fmt3(totCostiPrev)}</td>
                  <td className="num">{fmt3(totCostiCons)}</td>
                  <td className="num">
                    <strong className={totCostiCons > totCostiPrev ? "neg" : "pos"}>
                      {fmt3(totCostiCons - totCostiPrev, { sign: true })}
                    </strong>
                  </td>
                  <td className="num">
                    <strong className={totCostiCons > totCostiPrev ? "neg" : "pos"}>
                      {fmtPct3(safePct(totCostiCons - totCostiPrev, totCostiPrev))}
                    </strong>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
          {/* verdetto margine */}
          <div className="card-body" style={{ borderTop: "1px solid var(--border)" }}>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 18 }}>
              <div>
                <div className="kpi-label">Margine previsto</div>
                <div style={{ fontFamily: "var(--serif)", fontSize: 26, fontWeight: 600 }}>{fmt3(marginePrev)}</div>
                <div className="muted" style={{ fontSize: 12 }}>{fmtPct3(pctPrev)}</div>
              </div>
              <div>
                <div className="kpi-label">Margine reale</div>
                <div style={{ fontFamily: "var(--serif)", fontSize: 26, fontWeight: 600, color: margineCons < marginePrev ? "var(--yellow)" : "var(--teal)" }}>
                  {fmt3(margineCons)}
                </div>
                <div className="muted" style={{ fontSize: 12 }}>{fmtPct3(pctCons)}</div>
              </div>
              <div>
                <div className="kpi-label">Verdetto</div>
                <div style={{ fontFamily: "var(--serif)", fontSize: 26, fontWeight: 600 }} className={margineCons >= marginePrev ? "pos" : "neg"}>
                  {fmt3(margineCons - marginePrev, { sign: true })}
                </div>
                <div className="muted" style={{ fontSize: 12 }}>
                  {margineCons >= marginePrev ? "in linea o meglio del preventivo" : "sotto preventivo"}
                </div>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ============================================================
   10. STATISTICHE
   ============================================================ */
/* ---------- Sezione Pagamenti del preventivo ---------- */
function PagamentiSection({ pagamenti, versioneId, totaleRicavi }) {
  const [editing, setEditing] = useState3(null);
  const [showModal, setShowModal] = useState3(false);

  const totImporto = pagamenti.reduce((s, p) => {
    if (p.importo > 0) return s + Number(p.importo);
    if (p.percentuale > 0) return s + (Number(p.percentuale) / 100) * totaleRicavi;
    return s;
  }, 0);

  const deletePag = async (id) => {
    if (!confirm("Eliminare questa modalità di pagamento?")) return;
    const res = await window.AntaresStore.deletePagamento(id);
    if (res.error) { alert(res.error.message); return; }
    await window.AntaresStore.refresh("preventivi");
  };

  return (
    <div className="card">
      <div className="card-bar blue" />
      <div className="card-head">
        <h3 className="h3">Modalità di pagamento</h3>
        <button className="btn ghost" onClick={() => { setEditing(null); setShowModal(true); }}>
          <Icon3 name="plus" size={12} /> Aggiungi
        </button>
      </div>
      <div className="table-wrap">
        <table className="table preventivo-table">
          <thead>
            <tr>
              <th>Descrizione</th>
              <th className="num">Percentuale</th>
              <th className="num">Importo</th>
              <th>Scadenza</th>
              <th>Note</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            {pagamenti.length === 0 && (
              <tr><td colSpan="6" style={{ padding: 16, textAlign: "center", color: "var(--muted)", fontSize: 13 }}>
                Nessun pagamento. Tipico: 30% acconto firma, 40% inizio lavori, 30% saldo consegna.
              </td></tr>
            )}
            {pagamenti.map(p => {
              const importoCalcolato = p.importo > 0 ? p.importo : (p.percentuale > 0 ? totaleRicavi * p.percentuale / 100 : 0);
              return (
                <tr key={p.id}>
                  <td>{p.descrizione}</td>
                  <td className="num">{p.percentuale > 0 ? p.percentuale + "%" : "—"}</td>
                  <td className="num"><strong>{fmt3(importoCalcolato)}</strong></td>
                  <td className="mono" style={{ fontSize: 12 }}>{p.scadenza ? new Date(p.scadenza).toLocaleDateString("it-IT") : "—"}</td>
                  <td className="muted" style={{ fontSize: 12 }}>{p.note || ""}</td>
                  <td style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                    <button onClick={() => { setEditing(p); setShowModal(true); }} style={{ marginRight: 4, background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "3px 8px", cursor: "pointer", fontSize: 12 }}>Modifica</button>
                    <button onClick={() => deletePag(p.id)} style={{ background: "none", border: "1px solid var(--border)", borderRadius: 6, padding: "3px 8px", cursor: "pointer", fontSize: 12, color: "var(--danger, #c0392b)" }}>×</button>
                  </td>
                </tr>
              );
            })}
            {pagamenti.length > 0 && (
              <tr className="row-total">
                <td>Totale</td>
                <td></td>
                <td className="num"><strong>{fmt3(totImporto)}</strong></td>
                <td colSpan="3" className="muted" style={{ fontSize: 12 }}>
                  {Math.abs(totImporto - totaleRicavi) < 1
                    ? <span className="pos">✓ copre i ricavi previsti</span>
                    : <span className="warn">attenzione: differenza {fmt3(totImporto - totaleRicavi, { sign: true })}</span>}
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </div>

      {showModal && (
        <PagamentoModal
          editing={editing}
          versioneId={versioneId}
          totaleRicavi={totaleRicavi}
          onClose={() => setShowModal(false)}
          onSaved={() => window.AntaresStore.refresh("preventivi")}
        />
      )}
    </div>
  );
}

function PagamentoModal({ editing, versioneId, totaleRicavi, onClose, onSaved }) {
  const [descrizione, setDescrizione] = useState3(editing?.descrizione || "");
  const [modo, setModo] = useState3(editing?.percentuale > 0 ? "percentuale" : "importo");
  const [importo, setImporto] = useState3(String(editing?.importo || ""));
  const [percentuale, setPercentuale] = useState3(String(editing?.percentuale || ""));
  const [scadenza, setScadenza] = useState3(editing?.scadenza || "");
  const [note, setNote] = useState3(editing?.note || "");
  const [busy, setBusy] = useState3(false);
  const [err, setErr] = useState3("");

  const submit = async (e) => {
    e.preventDefault();
    if (!descrizione.trim()) { setErr("Descrizione obbligatoria"); return; }
    setBusy(true);
    const payload = {
      id: editing?.id, descrizione: descrizione.trim(),
      importo: modo === "importo" ? (parseFloat(String(importo).replace(",", ".")) || null) : null,
      percentuale: modo === "percentuale" ? (parseFloat(String(percentuale).replace(",", ".")) || null) : null,
      scadenza: scadenza || null,
      note: note.trim() || null,
    };
    const res = await window.AntaresStore.savePagamento(versioneId, payload);
    setBusy(false);
    if (res.error) { setErr(res.error.message); return; }
    await onSaved();
    onClose();
  };

  const presets = [
    { d: "Acconto alla firma", p: 30 },
    { d: "Inizio lavori", p: 40 },
    { d: "Saldo a consegna", p: 30 },
    { d: "Acconto 50%", p: 50 },
    { d: "Saldo a 30gg fattura", p: 100 },
  ];

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 500, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 14, fontFamily: "Playfair Display, serif", fontSize: 22 }}>
          {editing ? "Modifica pagamento" : "Nuovo pagamento"}
        </h3>
        <form onSubmit={submit}>
          {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}

          {!editing && (
            <div style={{ marginBottom: 12 }}>
              <div className="help" style={{ marginBottom: 6 }}>Preset rapidi:</div>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                {presets.map(p => (
                  <button key={p.d} type="button" onClick={() => { setDescrizione(p.d); setModo("percentuale"); setPercentuale(String(p.p)); }}
                    style={{ background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 6, padding: "4px 10px", cursor: "pointer", fontSize: 12 }}>
                    {p.d} {p.p}%
                  </button>
                ))}
              </div>
            </div>
          )}

          <div className="field" style={{ marginBottom: 12 }}>
            <label className="label">Descrizione</label>
            <input className="input" placeholder="es. Acconto 30% alla firma del contratto" value={descrizione} onChange={e => setDescrizione(e.target.value)} autoFocus />
          </div>

          <div className="field" style={{ marginBottom: 12 }}>
            <label className="label">Modalità importo</label>
            <div className="segmented" style={{ display: "flex" }}>
              <button type="button" className={modo === "percentuale" ? "active" : ""} onClick={() => setModo("percentuale")} style={{ flex: 1 }}>Percentuale sul totale</button>
              <button type="button" className={modo === "importo" ? "active" : ""} onClick={() => setModo("importo")} style={{ flex: 1 }}>Importo fisso</button>
            </div>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}>
            {modo === "percentuale" ? (
              <div className="field">
                <label className="label">Percentuale %</label>
                <input className="input num" type="text" value={percentuale} onChange={e => setPercentuale(e.target.value.replace(",", "."))} placeholder="30" />
              </div>
            ) : (
              <div className="field">
                <label className="label">Importo €</label>
                <input className="input num" type="text" value={importo} onChange={e => setImporto(e.target.value.replace(",", "."))} placeholder="0,00" />
              </div>
            )}
            <div className="field">
              <label className="label">Scadenza</label>
              <input className="input" type="date" value={scadenza} onChange={e => setScadenza(e.target.value)} />
            </div>
          </div>

          <div className="field" style={{ marginBottom: 16 }}>
            <label className="label">Note</label>
            <input className="input" placeholder="opzionale (es. bonifico 30gg fattura)" value={note} onChange={e => setNote(e.target.value)} />
          </div>

          {modo === "percentuale" && percentuale && totaleRicavi > 0 && (
            <div className="help" style={{ marginBottom: 12 }}>
              Corrisponde a: <strong>{fmt3(totaleRicavi * (parseFloat(percentuale) || 0) / 100)}</strong>
            </div>
          )}

          <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
            <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
            <button type="submit" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} disabled={busy}>
              {busy ? "Salvo…" : (editing ? "Aggiorna" : "Salva")}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ---------- Modal: riga preventivo ---------- */
function RigaModal({ tipo, editing, versioneId, categorie = [], tipoPreventivo = "service", onClose, onSaved }) {
  const [voce, setVoce]           = useState3(editing?.voce || "");
  const [categoria, setCategoria] = useState3(editing?.categoria || "Collaboratori");
  const [categoriaId, setCategoriaId] = useState3(editing?.categoria_id || "");
  const [ccnlId, setCcnlId]       = useState3(editing?.ccnl_id || "");
  const [regime, setRegime]       = useState3(editing?.regime || "");
  const usaCategorieFormali = tipo === "costo" && tipoPreventivo === "produzione" && categorie.length > 0;
  const listinoCcnl = ccnlList();
  const [qta, setQta]             = useState3(String(editing?.qta || 1));
  const [giorni, setGiorni]       = useState3(String(editing?.giorni || ""));
  const [prezzo, setPrezzo]       = useState3(String(editing?.prezzo || ""));
  const [markup, setMarkup]       = useState3(String(editing?.markup || 0));
  const [visibileCliente, setVisibileCliente] = useState3(editing?.visibile_cliente !== false);
  const [tipoExtra, setTipoExtra] = useState3(editing?.tipo_extra || "");
  const [busy, setBusy]           = useState3(false);
  const [err, setErr]             = useState3("");

  // Anteprima totale live
  const totale = (() => {
    const q = parseFloat(String(qta).replace(",", ".")) || 1;
    const g = parseFloat(String(giorni).replace(",", ".")) || 0;
    const p = parseFloat(String(prezzo).replace(",", ".")) || 0;
    return g > 0 ? q * g * p : q * p;
  })();

  const submit = async (e) => {
    e.preventDefault();
    if (!voce.trim() || !prezzo) { setErr("Voce e prezzo obbligatori"); return; }
    setBusy(true);
    const payload = {
      tipo, voce: voce.trim(),
      categoria: tipo === "costo" ? categoria : null,
      categoria_id: tipo === "costo" ? (categoriaId || null) : null,
      ccnl_id: tipo === "costo" ? (ccnlId || null) : null,
      regime: tipo === "costo" ? (regime || null) : null,
      qta: parseFloat(String(qta).replace(",", ".")) || 1,
      giorni: parseFloat(String(giorni).replace(",", ".")) || 0,
      prezzo: parseFloat(String(prezzo).replace(",", ".")) || 0,
      markup: tipo === "costo" ? (parseFloat(String(markup).replace(",", ".")) || 0) : 0,
      ordine: 0,
      visibile_cliente: visibileCliente,
      tipo_extra: tipoExtra || null,
    };
    if (editing?.id) payload.id = editing.id;
    const res = await window.AntaresStore.saveRiga(versioneId, payload);
    setBusy(false);
    if (res.error) { setErr(res.error.message); return; }
    await onSaved();
    onClose();
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 520, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 14, fontFamily: "Playfair Display, serif", fontSize: 22 }}>
          {editing ? "Modifica voce" : (tipo === "ricavo" ? "Nuova voce di ricavo" : "Nuova voce di costo")}
        </h3>
        <form onSubmit={submit}>
          {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
          <div className="field" style={{ marginBottom: 12 }}>
            <label className="label">Descrizione</label>
            <input className="input" placeholder={tipo === "ricavo" ? "es. Produzione - giorni riprese" : "es. Noleggio attrezzature"} value={voce} onChange={e => setVoce(e.target.value)} autoFocus />
          </div>
          {tipo === "costo" && usaCategorieFormali && (
            <div className="field" style={{ marginBottom: 12 }}>
              <label className="label">Categoria (topsheet Produzione)</label>
              <select className="select" value={categoriaId} onChange={e => setCategoriaId(e.target.value)}>
                <option value="">— nessuna —</option>
                {categorie.slice().sort((a, b) => a.ordine - b.ordine).map(c => (
                  <option key={c.id} value={c.id}>{c.codice ? c.codice + " · " : ""}{c.nome}</option>
                ))}
              </select>
            </div>
          )}
          {tipo === "costo" && !usaCategorieFormali && (
            <div className="field" style={{ marginBottom: 12 }}>
              <label className="label">Categoria</label>
              <select className="select" value={categoria} onChange={e => setCategoria(e.target.value)}>
                <option>Collaboratori</option>
                <option>Noleggio</option>
                <option>Trasferte</option>
                <option>Trasporti</option>
                <option>Catering</option>
                <option>Servizi</option>
                <option>Materiali</option>
                <option>Altro</option>
              </select>
            </div>
          )}
          {tipo === "costo" && (
            <div className="field" style={{ marginBottom: 12 }}>
              <label className="label">Tipo rapporto (per oneri / costo azienda)</label>
              <select className="select" value={regime} onChange={e => setRegime(e.target.value)}>
                <option value="">— non specificato —</option>
                <option value="dipendente_spett">Dipendente (assunto)</option>
                <option value="piva">P.IVA</option>
                <option value="ritenuta">Ritenuta d'acconto</option>
                <option value="esente">Esente / altro</option>
              </select>
              {regime === "dipendente_spett" && (
                <div className="help" style={{ marginTop: 4 }}>Su questa riga si calcolano oneri datore + costo busta (riquadro "Costo del personale assunto").</div>
              )}
            </div>
          )}
          {tipo === "costo" && listinoCcnl.length > 0 && (
            <div className="field" style={{ marginBottom: 12 }}>
              <label className="label">Figura CCNL · riferimento congruità (opzionale)</label>
              <select className="select" value={ccnlId} onChange={e => setCcnlId(e.target.value)}>
                <option value="">— nessuna —</option>
                {listinoCcnl.map(c => (
                  <option key={c.id} value={c.id}>{c.ruolo}{c.livello ? ` · liv. ${c.livello}` : ""} — {fmt3(c.minimo)} {baseLabel(c.base)}</option>
                ))}
              </select>
              {ccnlId && (() => {
                const chk = ccnlCheck({ ccnl_id: ccnlId, prezzo: parseFloat(String(prezzo).replace(",", ".")) || 0, giorni: parseFloat(String(giorni).replace(",", ".")) || 0 });
                if (!chk) return null;
                return (
                  <div className="help" style={{ marginTop: 6 }}>
                    Minimo CCNL: <strong>{fmt3(chk.min)} {baseLabel(chk.base)}</strong>{chk.approx ? " (≈ /mese ÷ 26 gg)" : ""} ·{" "}
                    <span className={chk.ok ? "pos" : "neg"} style={{ fontWeight: 600 }}>{chk.ok ? "🟢 congruo" : "🔴 sotto contratto"}</span>
                  </div>
                );
              })()}
            </div>
          )}
          <div style={{ display: "grid", gridTemplateColumns: tipo === "costo" ? "1fr 1fr 1fr 1fr" : "1fr 1fr 1fr", gap: 12, marginBottom: 8 }}>
            <div className="field">
              <label className="label">Q.tà</label>
              <input className="input num" type="text" value={qta} onChange={e => setQta(e.target.value.replace(",", "."))} />
            </div>
            <div className="field">
              <label className="label">Giorni</label>
              <input className="input num" type="text" placeholder="opz." value={giorni} onChange={e => setGiorni(e.target.value.replace(",", "."))} />
            </div>
            <div className="field">
              <label className="label">{giorni && parseFloat(giorni) > 0 ? "Prezzo / giorno" : "Prezzo unitario"}</label>
              <input className="input num" type="text" placeholder="0,00 €" value={prezzo} onChange={e => setPrezzo(e.target.value.replace(",", "."))} />
            </div>
            {tipo === "costo" && (
              <div className="field">
                <label className="label">Markup %</label>
                <input className="input num" type="text" value={markup} onChange={e => setMarkup(e.target.value.replace(",", "."))} />
              </div>
            )}
          </div>
          <div className="help" style={{ marginBottom: 16, textAlign: "right" }}>
            Totale riga: <strong className="mono">{fmt3(totale)}</strong>
            {giorni && parseFloat(giorni) > 0 && (
              <span className="muted"> ({qta} × {giorni} giorni × {fmt3(parseFloat(String(prezzo).replace(",","."))||0)})</span>
            )}
          </div>

          {/* Visibilità e tipo riga */}
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16, padding: 12, background: "var(--surface-2)", borderRadius: 8 }}>
            <div>
              <label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13 }}>
                <input type="checkbox" checked={visibileCliente} onChange={e => setVisibileCliente(e.target.checked)} />
                <strong>👁 Visibile al cliente</strong>
              </label>
              <div className="help" style={{ marginTop: 4 }}>Se spunta tolta, la riga NON appare nel PDF del cliente</div>
            </div>
            <div>
              <label className="label" style={{ marginBottom: 6 }}>Tipo riga</label>
              <select className="select" value={tipoExtra} onChange={e => setTipoExtra(e.target.value)}>
                <option value="">Normale (parte del preventivo)</option>
                <option value="straordinario">Straordinario (aggiunto in corso)</option>
                <option value="fuori_preventivo">Fuori preventivo (extra non concordato)</option>
              </select>
            </div>
          </div>

          <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
            <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
            <button type="submit" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} disabled={busy}>
              {busy ? "Salvo…" : (editing ? "Aggiorna" : "Aggiungi")}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ---------- Modal: nuova versione preventivo ---------- */
function VersioneModal({ preventivoId, versioniEsistenti, versioneAttualeId, onClose, onSaved }) {
  const next = "v" + (versioniEsistenti.length + 1);
  const [etichetta, setEtichetta] = useState3(next);
  const [nota, setNota]           = useState3("");
  const [copia, setCopia]         = useState3(true);
  const [setAttiva, setSetAttiva] = useState3(true);
  const [busy, setBusy]           = useState3(false);
  const [err, setErr]             = useState3("");

  const submit = async (e) => {
    e.preventDefault();
    if (!etichetta.trim()) { setErr("Etichetta obbligatoria"); return; }
    setBusy(true);
    const res = await window.AntaresStore.createVersione(preventivoId, etichetta.trim(), nota.trim() || null, copia ? versioneAttualeId : null);
    if (res.error) { setBusy(false); setErr(res.error.message); return; }
    if (setAttiva) {
      await window.AntaresStore.setVersioneAttiva(preventivoId, res.versione.id);
    }
    setBusy(false);
    await onSaved();
    onClose();
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 480, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 14, fontFamily: "Playfair Display, serif", fontSize: 22 }}>Nuova versione preventivo</h3>
        <form onSubmit={submit}>
          {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
          <div style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gap: 12, marginBottom: 12 }}>
            <div className="field">
              <label className="label">Etichetta</label>
              <input className="input" placeholder="v2" value={etichetta} onChange={e => setEtichetta(e.target.value)} />
            </div>
            <div className="field">
              <label className="label">Nota</label>
              <input className="input" placeholder="es. dopo call cliente — ridotto 1 giorno" value={nota} onChange={e => setNota(e.target.value)} />
            </div>
          </div>
          <div style={{ display: "flex", flexDirection: "column", gap: 8, marginBottom: 16, fontSize: 13 }}>
            <label style={{ display: "flex", gap: 8, alignItems: "center", cursor: "pointer" }}>
              <input type="checkbox" checked={copia} onChange={e => setCopia(e.target.checked)} />
              Copia tutte le voci dalla versione corrente
            </label>
            <label style={{ display: "flex", gap: 8, alignItems: "center", cursor: "pointer" }}>
              <input type="checkbox" checked={setAttiva} onChange={e => setSetAttiva(e.target.checked)} />
              Imposta come versione attiva
            </label>
          </div>
          <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
            <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
            <button type="submit" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} disabled={busy}>
              {busy ? "Salvo…" : "Crea versione"}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

function Statistiche() {
  const serie = STATISTICHE.serieAnnuale || [];
  const maxRic = serie.length > 0 ? Math.max(...serie.map(s => s.ricavi)) || 1 : 1;
  const annoCorrente = serie[serie.length - 1] || { anno: new Date().getFullYear(), ricavi: 0, costi: 0, margine: 0, ebitda: 0 };

  const topProgetti = [...(window.AntaresData.MARGINI || [])].sort((a, b) => b.pct - a.pct).slice(0, 3);
  const bottomProgetti = [...(window.AntaresData.MARGINI || [])].sort((a, b) => a.pct - b.pct).slice(0, 3);

  const lineaService = STATISTICHE.margineLinea?.["Service"] || { ricavi: 0, costi: 0, margine: 0, pct: 0 };
  const lineaFormat  = STATISTICHE.margineLinea?.["Produzioni"] || { ricavi: 0, costi: 0, margine: 0, pct: 0 };

  return (
    <div className="stack">
      <div className="page-head">
        <div className="page-title-block">
          <span className="eyebrow">Performance · vista aggregata</span>
          <h1 className="h1">Statistiche</h1>
        </div>
      </div>

      <div className="kpi-grid">
        <div className="kpi">
          <div className="kpi-label">EBITDA 2026 (YTD)</div>
          <div className="kpi-value">{fmt3(annoCorrente.ebitda)}</div>
          <div className="kpi-sub pos">{(() => {
            const prev = serie[serie.length - 2];
            if (!prev || !prev.ebitda) return "—";
            return "▲ " + fmtPct3(((annoCorrente.ebitda - prev.ebitda)/prev.ebitda)*100) + " vs anno scorso";
          })()}</div>
        </div>
        <div className="kpi blue">
          <div className="kpi-label">Margine medio Service</div>
          <div className="kpi-value">{fmtPct3(lineaService.pct)}</div>
          <div className="kpi-sub">{fmt3(lineaService.margine)} contributo</div>
        </div>
        <div className="kpi yellow">
          <div className="kpi-label">Margine medio Produzioni</div>
          <div className="kpi-value">{fmtPct3(lineaFormat.pct)}</div>
          <div className="kpi-sub">{fmt3(lineaFormat.margine)} contributo</div>
        </div>
        <div className="kpi coral">
          <div className="kpi-label">CAGR ricavi 2022→2026</div>
          <div className="kpi-value">{(() => {
            const first = serie[0];
            if (!first || !first.ricavi || serie.length < 2) return "—";
            const n = serie.length - 1;
            return fmtPct3((Math.pow(annoCorrente.ricavi/first.ricavi, 1/n) - 1) * 100);
          })()}</div>
          <div className="kpi-sub">crescita media annua</div>
        </div>
      </div>

      <div className="card">
        <div className="card-bar" />
        <div className="card-head">
          <h3 className="h3">Andamento ricavi e margine · 5 anni</h3>
          <span className="muted" style={{ fontSize: 12 }}>Bar = ricavi · linea = margine %</span>
        </div>
        <div className="card-body">
          <div className="chart-bars">
            {serie.map(s => {
              const h = (s.ricavi / maxRic) * 100;
              const mPct = (s.margine / s.ricavi) * 100;
              return (
                <div className="chart-col" key={s.anno}>
                  <div className="chart-numbers">
                    <div className="chart-num">{fmt3(s.ricavi)}</div>
                    <div className="chart-num muted">{fmtPct3(mPct)}</div>
                  </div>
                  <div className="chart-bar-wrap">
                    <div className="chart-bar" style={{ height: h + "%" }}>
                      <div className="chart-bar-margin" style={{ height: (mPct / 30 * 100) + "%" }} />
                    </div>
                  </div>
                  <div className="chart-label">
                    <strong>{s.anno}</strong>
                    {s.ytd && <span className="pill yellow" style={{ marginLeft: 4 }}>YTD</span>}
                  </div>
                </div>
              );
            })}
          </div>
          <div className="legend" style={{ marginTop: 14, paddingTop: 14, borderTop: "1px solid var(--border)" }}>
            <span className="legend-item">
              <span className="legend-swatch" style={{ background: "var(--teal-dim)", borderColor: "var(--teal)" }} /> Ricavi
            </span>
            <span className="legend-item">
              <span className="legend-swatch" style={{ background: "var(--teal)" }} /> Margine assoluto
            </span>
          </div>
        </div>
      </div>

      <div className="two-col">
        <div className="card">
          <div className="card-bar" />
          <div className="card-head">
            <h3 className="h3">Top 3 progetti per redditività</h3>
            <span className="pill teal">YTD 2026</span>
          </div>
          <div>
            {topProgetti.map((p, i) => (
              <div key={p.id} className="rank-row">
                <div className="rank-num">{i + 1}</div>
                <div>
                  <div className="rank-title">{p.progetto}</div>
                  <div className="rank-sub">{p.linea} · {fmt3(p.ricavi)}</div>
                </div>
                <div className="rank-value pos">{fmtPct3(p.pct)}</div>
              </div>
            ))}
          </div>
        </div>
        <div className="card">
          <div className="card-bar coral" />
          <div className="card-head">
            <h3 className="h3">Bottom 3 · da attenzionare</h3>
            <span className="pill coral">attenzione</span>
          </div>
          <div>
            {bottomProgetti.map((p, i) => (
              <div key={p.id} className="rank-row">
                <div className="rank-num bad">{i + 1}</div>
                <div>
                  <div className="rank-title">{p.progetto}</div>
                  <div className="rank-sub">{p.linea} · {fmt3(p.ricavi)}</div>
                </div>
                <div className={"rank-value " + (p.pct < 0 ? "neg" : "warn")}>{fmtPct3(p.pct)}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   11. ANAGRAFICA
   ============================================================ */
function Anagrafica() {
  const [filter, setFilter] = useState3("tutti");
  const [selectedId, setSelectedId] = useState3(CONTROPARTI[0]?.id || null);
  const [search, setSearch] = useState3("");

  const lista = CONTROPARTI.filter(c => {
    if (filter !== "tutti" && c.tipo !== filter) return false;
    if (search && !c.nome.toLowerCase().includes(search.toLowerCase())) return false;
    return true;
  });

  // se la selezione corrente non esiste più, prendi la prima della lista
  const effectiveId = selectedId && CONTROPARTI.find(c => c.id === selectedId) ? selectedId : (lista[0]?.id || null);
  const sel = effectiveId ? CONTROPARTI.find(c => c.id === effectiveId) : null;
  const fattureCollegate = sel ? FATT3.filter(f => f.controparte === sel.nome) : [];

  if (!sel) {
    return (
      <div className="stack">
        <div className="page-head">
          <div className="page-title-block">
            <span className="eyebrow">Controparti · dati aggregati dalle fatture</span>
            <h1 className="h1">Anagrafica</h1>
          </div>
        </div>
        <div className="card"><div className="card-body" style={{ padding: 40, textAlign: "center", color: "var(--muted)" }}>
          Nessuna controparte. L'anagrafica si popola automaticamente quando registri le fatture.
        </div></div>
      </div>
    );
  }

  return (
    <div className="stack">
      <div className="page-head">
        <div className="page-title-block">
          <span className="eyebrow">Controparti · dati aggregati dalle fatture</span>
          <h1 className="h1">Anagrafica</h1>
        </div>
        <div className="page-actions">
          <button className="btn"><Icon3 name="plus" /> Nuova controparte</button>
        </div>
      </div>

      <div className="anagrafica-layout">
        {/* Lista */}
        <div className="card">
          <div className="card-bar" />
          <div className="card-head" style={{ flexDirection: "column", alignItems: "stretch", gap: 10 }}>
            <div className="row" style={{ justifyContent: "space-between", width: "100%" }}>
              <h3 className="h3">Elenco</h3>
              <span className="pill">{lista.length}</span>
            </div>
            <div className="segmented compact">
              <button className={filter === "tutti" ? "active" : ""} onClick={() => setFilter("tutti")}>Tutti</button>
              <button className={filter === "cliente" ? "active" : ""} onClick={() => setFilter("cliente")}>Clienti</button>
              <button className={filter === "fornitore" ? "active" : ""} onClick={() => setFilter("fornitore")}>Fornitori</button>
            </div>
            <div style={{ position: "relative" }}>
              <input className="input" style={{ paddingLeft: 34 }} placeholder="Cerca..." value={search} onChange={e => setSearch(e.target.value)} />
              <div style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", color: "var(--muted)" }}>
                <Icon3 name="search" size={14} />
              </div>
            </div>
          </div>
          <div className="contact-list">
            {lista.map(c => (
              <button
                key={c.id}
                className={"contact-item" + (c.id === selectedId ? " active" : "")}
                onClick={() => setSelectedId(c.id)}
              >
                <div className={"contact-avatar " + (c.tipo === "cliente" ? "teal" : "")}>
                  {c.nome.split(" ").slice(0, 2).map(w => w[0]).join("")}
                </div>
                <div className="contact-info">
                  <div className="contact-name">{c.nome}</div>
                  <div className="contact-meta">
                    {c.tipo === "cliente"
                      ? <span className="pill teal" style={{ padding: "1px 6px", fontSize: 10 }}>cliente</span>
                      : <span className="pill" style={{ padding: "1px 6px", fontSize: 10 }}>fornitore</span>}
                    {" "}<span style={{ fontSize: 11 }} className="muted">{c.fatture} fatture</span>
                  </div>
                </div>
                <div className="contact-amount">{fmt3(c.fatturatoAnno)}</div>
              </button>
            ))}
          </div>
        </div>

        {/* Dettaglio */}
        <div className="stack">
          <div className="card">
            <div className={"card-bar " + (sel.tipo === "cliente" ? "" : "muted")} />
            <div className="card-body">
              <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-start" }}>
                <div>
                  <span className="eyebrow">{sel.tipo === "cliente" ? "Cliente" : "Fornitore"}</span>
                  <h2 className="h1" style={{ fontSize: 28, marginTop: 4 }}>{sel.nome}</h2>
                  <div className="muted mono" style={{ fontSize: 13, marginTop: 4 }}>P.IVA {sel.piva}</div>
                </div>
                <div className="row">
                  <button className="btn ghost"><Icon3 name="edit" size={14} /> Modifica</button>
                </div>
              </div>
              {sel.nota && (
                <div className="alert" style={{ marginTop: 14 }}>
                  <div className="alert-icon">i</div>
                  <div>{sel.nota}</div>
                </div>
              )}
            </div>
          </div>

          <div className="kpi-grid">
            <div className="kpi">
              <div className="kpi-label">Fatturato 2026</div>
              <div className="kpi-value">{fmt3(sel.fatturatoAnno)}</div>
              <div className="kpi-sub muted">{sel.fatture} fatture · YTD</div>
            </div>
            <div className="kpi blue">
              <div className="kpi-label">Storico totale</div>
              <div className="kpi-value">{fmt3(sel.fatturatoStorico)}</div>
              <div className="kpi-sub muted">somma anni precedenti</div>
            </div>
            <div className={"kpi " + (sel.giorniMediPagamento > 60 ? "yellow" : "")}>
              <div className="kpi-label">Giorni medi pagamento</div>
              <div className="kpi-value" style={{ color: sel.giorniMediPagamento > 60 ? "var(--yellow)" : "var(--text)" }}>
                {sel.giorniMediPagamento} gg
              </div>
              <div className="kpi-sub muted">
                {sel.giorniMediPagamento > 60 ? "sopra la media di settore" : "in linea"}
              </div>
            </div>
            {sel.margine !== null && sel.margine !== undefined && (
              <div className={"kpi " + (sel.margine < 10 ? "coral" : "")}>
                <div className="kpi-label">Margine medio commesse</div>
                <div className="kpi-value" style={{ color: sel.margine < 0 ? "var(--coral)" : (sel.margine < 10 ? "var(--yellow)" : "var(--teal)") }}>
                  {fmtPct3(sel.margine)}
                </div>
                <div className="kpi-sub muted">sui progetti del cliente</div>
              </div>
            )}
          </div>

          <div className="card">
            <div className="card-bar muted" />
            <div className="card-head">
              <h3 className="h3">Fatture collegate</h3>
              <span className="pill">aggregato</span>
            </div>
            {fattureCollegate.length === 0 ? (
              <div className="card-body muted" style={{ fontStyle: "italic" }}>
                Nessuna fattura registrata nell'esercizio corrente.
              </div>
            ) : (
              <div className="table-wrap">
                <table className="table">
                  <thead>
                    <tr>
                      <th>N°</th>
                      <th>Data</th>
                      <th>Progetto / categoria</th>
                      <th className="num">Imponibile</th>
                      <th>Stato</th>
                    </tr>
                  </thead>
                  <tbody>
                    {fattureCollegate.map(f => (
                      <tr key={f.id} className={f.tipo === "attiva" ? "row-active" : "row-passive"}>
                        <td className="mono">{f.id}</td>
                        <td className="mono">{f.data}</td>
                        <td className="muted">{f.progetto !== "—" ? f.progetto : (f.categoria || "—")}</td>
                        <td className="num">{fmt3(f.imponibile)}</td>
                        <td>
                          {f.stato === "incassata" && <span className="pill teal">incassata</span>}
                          {f.stato === "emessa" && <span className="pill blue">emessa</span>}
                          {f.stato === "pagata" && <span className="pill">pagata</span>}
                          {f.stato === "da pagare" && <span className="pill coral">da pagare</span>}
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   Helper: track-scheduling per timeline (overlap detection)
   ============================================================ */
function assignTracksAndOverlaps(bookings) {
  const sorted = [...bookings].sort((a, b) => a.dal - b.dal);
  const tracksEnd = []; // per ogni track, ultimo "al" assegnato
  const assigned = sorted.map(b => {
    let track = tracksEnd.findIndex(end => end < b.dal);
    if (track === -1) { tracksEnd.push(b.al); track = tracksEnd.length - 1; }
    else tracksEnd[track] = b.al;
    return { ...b, track };
  });
  // overlap: any booking that shares months with another on same item
  return assigned.map(b => {
    const overlap = assigned.some(o =>
      o.id !== b.id && o.dal <= b.al && b.dal <= o.al
    );
    return { ...b, overlap };
  });
}

/* ============================================================
   12. CALENDARIO ATTREZZATURA
   ============================================================ */
function CalendarioAttrezzatura() {
  const [showModal, setShowModal] = useState3(null); // null | { editing? }
  const rows = CESP3.map(c => {
    const bookings = IMPEGNI_CESPITI.filter(b => b.cespiteId === c.id);
    const tagged = assignTracksAndOverlaps(bookings);
    const maxTrack = tagged.reduce((m, b) => Math.max(m, b.track), 0);
    const giornateAnno = tagged.reduce((s, b) => s + b.giornate, 0);
    const overlaps = tagged.filter(b => b.overlap).length;
    return { cespite: c, bookings: tagged, maxTrack, giornateAnno, overlaps };
  });

  const totOverlaps = rows.reduce((s, r) => s + (r.overlaps > 0 ? 1 : 0), 0);

  const handleDelete = async (id) => {
    if (!confirm("Eliminare questo impegno?")) return;
    const res = await window.AntaresStore.deleteImpegnoCespite(id);
    if (res.error) { alert(res.error.message); return; }
    await window.AntaresStore.refresh("impegni_cespiti");
  };

  return (
    <div className="stack">
      <div className="page-head">
        <div className="page-title-block">
          <span className="eyebrow">Pianificazione · 2026</span>
          <h1 className="h1">Calendario attrezzatura</h1>
        </div>
        <div className="page-actions">
          <button className="btn" onClick={() => setShowModal({})}><Icon3 name="plus" /> Nuovo impegno</button>
        </div>
      </div>

      {totOverlaps > 0 && (
        <div className="alert coral">
          <div className="alert-icon">!</div>
          <div>
            <strong>{totOverlaps} cespit{totOverlaps === 1 ? "e" : "i"} in sovrapposizione</strong>
            <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>
              Doppio booking su pezzi presenti in più progetti contemporaneamente — verificare disponibilità.
            </div>
          </div>
        </div>
      )}

      <div className="card">
        <div className="card-bar" />
        <div className="card-head">
          <h3 className="h3">Timeline impegni · 12 mesi</h3>
          <div className="legend">
            <span className="legend-item"><span className="legend-swatch" style={{ background: "var(--teal-dim)", borderColor: "var(--teal)" }} /> impegno</span>
            <span className="legend-item"><span className="legend-swatch" style={{ background: "var(--coral-dim)", borderColor: "var(--coral)" }} /> sovrapposizione</span>
          </div>
        </div>
        <div className="timeline-wrap">
          <div className="timeline">
            <div className="timeline-row timeline-head">
              <div className="timeline-label">Bene</div>
              <div className="timeline-lane months">
                {MESI3.map(m => <div key={m} className="timeline-month-label">{m}</div>)}
              </div>
              <div className="timeline-side">Gg/anno</div>
            </div>
            {rows.map(r => (
              <div className="timeline-row" key={r.cespite.id}>
                <div className="timeline-label">
                  <div style={{ fontWeight: 600, fontSize: 13 }}>{r.cespite.bene}</div>
                  <div className="muted" style={{ fontSize: 11 }}>{r.bookings.length} impegni</div>
                </div>
                <div className="timeline-lane" style={{ minHeight: 28 + (r.maxTrack) * 30 }}>
                  {r.bookings.map(b => {
                    const left = (b.dal / 12) * 100;
                    const width = ((b.al - b.dal + 1) / 12) * 100;
                    return (
                      <div
                        key={b.id}
                        className={"timeline-bar" + (b.overlap ? " overlap" : "")}
                        style={{
                          left: left + "%",
                          width: `calc(${width}% - 4px)`,
                          top: 4 + b.track * 30,
                          cursor: "pointer",
                        }}
                        title={`${b.progetto} · ${MESI3[b.dal]}–${MESI3[b.al]} · ${b.giornate} gg — click per modificare`}
                        onClick={() => setShowModal({ editing: b })}
                      >
                        <span className="timeline-bar-label">{b.progetto}</span>
                        <span className="timeline-bar-meta">{b.giornate}gg</span>
                      </div>
                    );
                  })}
                </div>
                <div className="timeline-side">
                  <strong>{r.giornateAnno}</strong>
                  {r.overlaps > 0 && <div className="neg" style={{ fontSize: 11 }}>{r.overlaps} overlap</div>}
                </div>
              </div>
            ))}
            {rows.every(r => r.bookings.length === 0) && (
              <div style={{ padding: 30, textAlign: "center", color: "var(--muted)" }}>
                Nessun impegno. Clicca <strong>+ Nuovo impegno</strong> in alto per pianificare l'utilizzo di un cespite.
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Riepilogo utilizzo */}
      <div className="card">
        <div className="card-bar blue" />
        <div className="card-head">
          <h3 className="h3">Utilizzo per pezzo · anno</h3>
          <span className="muted" style={{ fontSize: 12 }}>Giornate impegnate / saturazione</span>
        </div>
        <div className="table-wrap">
          <table className="table">
            <thead>
              <tr>
                <th>Bene</th>
                <th className="num">Impegni</th>
                <th className="num">Giornate</th>
                <th>Saturazione (su 220gg)</th>
                <th>Sovrapposizioni</th>
              </tr>
            </thead>
            <tbody>
              {rows.map(r => {
                const sat = (r.giornateAnno / 220) * 100;
                return (
                  <tr key={r.cespite.id}>
                    <td><strong>{r.cespite.bene}</strong></td>
                    <td className="num">{r.bookings.length}</td>
                    <td className="num"><strong>{r.giornateAnno}</strong></td>
                    <td style={{ width: 260 }}>
                      <div className="row" style={{ gap: 10 }}>
                        <div className="minibar" style={{ flex: 1 }}>
                          <div
                            className={"minibar-fill " + (sat > 90 ? "coral" : (sat > 60 ? "" : "blue"))}
                            style={{ width: Math.min(100, sat) + "%" }}
                          />
                        </div>
                        <span className="mono" style={{ fontSize: 12, minWidth: 36 }}>{fmtPct3(sat, 0)}</span>
                      </div>
                    </td>
                    <td>
                      {r.overlaps > 0
                        ? <span className="pill coral">{r.overlaps} doppio booking</span>
                        : <span className="pill teal">ok</span>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {showModal && (
        <ImpegnoCespiteModal
          editing={showModal.editing}
          onClose={() => setShowModal(null)}
          onDelete={handleDelete}
          onSaved={() => window.AntaresStore.refresh("impegni_cespiti")}
        />
      )}
    </div>
  );
}

function ImpegnoCespiteModal({ editing, onClose, onDelete, onSaved }) {
  const today = new Date().toISOString().slice(0,10);
  const cespiti = window.AntaresData.CESPITI || [];
  const progetti = window.AntaresData.PROGETTI || [];
  const [cespiteId, setCespiteId]   = useState3(editing?._raw?.cespite_id || cespiti[0]?.id || "");
  const [progettoId, setProgettoId] = useState3(editing?._raw?.progetto_id || "");
  const [dataInizio, setDataInizio] = useState3(editing?._raw?.data_inizio || today);
  const [dataFine, setDataFine]     = useState3(editing?._raw?.data_fine || today);
  const [giornate, setGiornate]     = useState3(String(editing?._raw?.giornate || ""));
  const [ricorrente, setRicorrente] = useState3(!!editing?._raw?.ricorrente);
  const [note, setNote]             = useState3(editing?._raw?.note || "");
  const [busy, setBusy]             = useState3(false);
  const [err, setErr]               = useState3("");

  const submit = async (e) => {
    e.preventDefault();
    setErr("");
    if (!cespiteId) { setErr("Cespite obbligatorio"); return; }
    if (!dataInizio || !dataFine) { setErr("Date obbligatorie"); return; }
    if (dataFine < dataInizio) { setErr("Data fine prima della data inizio"); return; }
    setBusy(true);
    const res = await window.AntaresStore.saveImpegnoCespite({
      id: editing?.id, cespite_id: cespiteId, progetto_id: progettoId || null,
      data_inizio: dataInizio, data_fine: dataFine,
      giornate: parseInt(giornate, 10) || 0,
      ricorrente, note,
    });
    setBusy(false);
    if (res.error) { setErr(res.error.message); return; }
    await onSaved(); onClose();
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 520, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 14, fontFamily: "Playfair Display, serif", fontSize: 22 }}>
          {editing ? "Modifica impegno cespite" : "Nuovo impegno cespite"}
        </h3>
        <form onSubmit={submit}>
          {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
          <div className="field" style={{ marginBottom: 12 }}>
            <label className="label">Cespite</label>
            <select className="select" value={cespiteId} onChange={e => setCespiteId(e.target.value)}>
              {cespiti.length === 0 && <option value="">— nessun cespite in libro —</option>}
              {cespiti.map(c => <option key={c.id} value={c.id}>{c.bene}</option>)}
            </select>
          </div>
          <div className="field" style={{ marginBottom: 12 }}>
            <label className="label">Progetto</label>
            <select className="select" value={progettoId} onChange={e => setProgettoId(e.target.value)}>
              <option value="">— nessuno —</option>
              {progetti.map(p => <option key={p.id} value={p.id}>{p.nome}</option>)}
            </select>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 12 }}>
            <div className="field">
              <label className="label">Dal</label>
              <input className="input" type="date" value={dataInizio} onChange={e => setDataInizio(e.target.value)} />
            </div>
            <div className="field">
              <label className="label">Al</label>
              <input className="input" type="date" value={dataFine} onChange={e => setDataFine(e.target.value)} />
            </div>
            <div className="field">
              <label className="label">Giornate</label>
              <input className="input num" type="number" min="0" value={giornate} onChange={e => setGiornate(e.target.value)} />
            </div>
          </div>
          <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, marginBottom: 12, cursor: "pointer" }}>
            <input type="checkbox" checked={ricorrente} onChange={e => setRicorrente(e.target.checked)} />
            Impegno ricorrente (es. studio Rai tutto l'anno)
          </label>
          <div className="field" style={{ marginBottom: 16 }}>
            <label className="label">Note</label>
            <input className="input" placeholder="opzionale" value={note} onChange={e => setNote(e.target.value)} />
          </div>
          <div style={{ display: "flex", gap: 8, justifyContent: "space-between" }}>
            <div>
              {editing && (
                <button type="button" onClick={() => { onDelete(editing.id); onClose(); }}
                  style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer", color: "var(--danger, #c0392b)" }}>
                  Elimina
                </button>
              )}
            </div>
            <div style={{ display: "flex", gap: 8 }}>
              <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
              <button type="submit" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} disabled={busy}>
                {busy ? "Salvo…" : (editing ? "Aggiorna" : "Salva impegno")}
              </button>
            </div>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ============================================================
   13. CARICO COLLABORATORI
   ============================================================ */
function CaricoCollaboratori() {
  const [showModal, setShowModal] = useState3(null);
  const persone = COLLAB3.filter(c => IMPEGNI_COLLAB.some(i => i.collaboratoreId === c.id));
  const rows = persone.map(p => {
    const bookings = IMPEGNI_COLLAB.filter(b => b.collaboratoreId === p.id);
    const tagged = assignTracksAndOverlaps(bookings);
    const maxTrack = tagged.reduce((m, b) => Math.max(m, b.track), 0);
    const fteGiornate = tagged.reduce((s, b) => s + b.fteGiornate, 0);
    const overlaps = tagged.filter(b => b.overlap).length;
    return { persona: p, bookings: tagged, maxTrack, fteGiornate, overlaps };
  });

  const totOverlaps = rows.reduce((s, r) => s + (r.overlaps > 0 ? 1 : 0), 0);

  const handleDelete = async (id) => {
    if (!confirm("Eliminare questo impegno?")) return;
    const res = await window.AntaresStore.deleteImpegnoCollab(id);
    if (res.error) { alert(res.error.message); return; }
    await window.AntaresStore.refresh("impegni_collab");
  };

  return (
    <div className="stack">
      <div className="page-head">
        <div className="page-title-block">
          <span className="eyebrow">Risorse umane · 2026</span>
          <h1 className="h1">Carico collaboratori</h1>
        </div>
        <div className="page-actions">
          <button className="btn" onClick={() => setShowModal({})}><Icon3 name="plus" /> Nuovo impegno</button>
        </div>
      </div>

      {totOverlaps > 0 && (
        <div className="alert coral">
          <div className="alert-icon">!</div>
          <div>
            <strong>{totOverlaps} collaborator{totOverlaps === 1 ? "e" : "i"} in sovrapposizione</strong>
            <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>
              Stessa persona allocata a più progetti nello stesso mese — verificare disponibilità o ridistribuire.
            </div>
          </div>
        </div>
      )}

      <div className="card">
        <div className="card-bar" />
        <div className="card-head">
          <h3 className="h3">Timeline impegni · 12 mesi</h3>
          <div className="legend">
            <span className="legend-item"><span className="legend-swatch" style={{ background: "var(--teal-dim)", borderColor: "var(--teal)" }} /> assegnato</span>
            <span className="legend-item"><span className="legend-swatch" style={{ background: "var(--coral-dim)", borderColor: "var(--coral)" }} /> sovrapposizione</span>
          </div>
        </div>
        <div className="timeline-wrap">
          <div className="timeline">
            <div className="timeline-row timeline-head">
              <div className="timeline-label">Persona</div>
              <div className="timeline-lane months">
                {MESI3.map(m => <div key={m} className="timeline-month-label">{m}</div>)}
              </div>
              <div className="timeline-side">Gg/anno</div>
            </div>
            {rows.map(r => (
              <div className="timeline-row" key={r.persona.id}>
                <div className="timeline-label">
                  <div style={{ fontWeight: 600, fontSize: 13 }}>{r.persona.nome}</div>
                  <div className="muted" style={{ fontSize: 11 }}>{r.persona.ruolo}</div>
                </div>
                <div className="timeline-lane" style={{ minHeight: 28 + (r.maxTrack) * 30 }}>
                  {r.bookings.map(b => {
                    const left = (b.dal / 12) * 100;
                    const width = ((b.al - b.dal + 1) / 12) * 100;
                    return (
                      <div
                        key={b.id}
                        className={"timeline-bar" + (b.overlap ? " overlap" : "") + (b.ricorrente ? " ricorrente" : "")}
                        style={{
                          left: left + "%",
                          width: `calc(${width}% - 4px)`,
                          top: 4 + b.track * 30,
                          cursor: "pointer",
                        }}
                        title={`${b.progetto} · ${MESI3[b.dal]}–${MESI3[b.al]} · ${b.fteGiornate} gg — click per modificare`}
                        onClick={() => setShowModal({ editing: b })}
                      >
                        <span className="timeline-bar-label">{b.progetto}</span>
                        <span className="timeline-bar-meta">{b.fteGiornate}gg</span>
                      </div>
                    );
                  })}
                </div>
                <div className="timeline-side">
                  <strong>{r.fteGiornate}</strong>
                  {r.overlaps > 0 && <div className="neg" style={{ fontSize: 11 }}>{r.overlaps} overlap</div>}
                </div>
              </div>
            ))}
            {rows.length === 0 && (
              <div style={{ padding: 30, textAlign: "center", color: "var(--muted)" }}>
                Nessun impegno collaboratore. Clicca <strong>+ Nuovo impegno</strong> per assegnare una persona a un progetto.
              </div>
            )}
          </div>
        </div>
      </div>

      {showModal && (
        <ImpegnoCollabModal
          editing={showModal.editing}
          onClose={() => setShowModal(null)}
          onDelete={handleDelete}
          onSaved={() => window.AntaresStore.refresh("impegni_collab")}
        />
      )}
    </div>
  );
}

function ImpegnoCollabModal({ editing, onClose, onDelete, onSaved }) {
  const today = new Date().toISOString().slice(0,10);
  const collaboratori = window.AntaresData.COLLABORATORI || [];
  const progetti = window.AntaresData.PROGETTI || [];
  const [collabId, setCollabId]     = useState3(editing?._raw?.collaboratore_id || collaboratori[0]?.id || "");
  const [progettoId, setProgettoId] = useState3(editing?._raw?.progetto_id || "");
  const [dataInizio, setDataInizio] = useState3(editing?._raw?.data_inizio || today);
  const [dataFine, setDataFine]     = useState3(editing?._raw?.data_fine || today);
  const [giornate, setGiornate]     = useState3(String(editing?._raw?.fte_giornate || ""));
  const [ricorrente, setRicorrente] = useState3(!!editing?._raw?.ricorrente);
  const [note, setNote]             = useState3(editing?._raw?.note || "");
  const [busy, setBusy]             = useState3(false);
  const [err, setErr]               = useState3("");

  const submit = async (e) => {
    e.preventDefault();
    setErr("");
    if (!collabId) { setErr("Collaboratore obbligatorio"); return; }
    if (!dataInizio || !dataFine) { setErr("Date obbligatorie"); return; }
    if (dataFine < dataInizio) { setErr("Data fine prima della data inizio"); return; }
    setBusy(true);
    const res = await window.AntaresStore.saveImpegnoCollab({
      id: editing?.id, collaboratore_id: collabId, progetto_id: progettoId || null,
      data_inizio: dataInizio, data_fine: dataFine,
      giornate: parseInt(giornate, 10) || 0,
      ricorrente, note,
    });
    setBusy(false);
    if (res.error) { setErr(res.error.message); return; }
    await onSaved(); onClose();
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.4)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: "var(--surface, #fff)", borderRadius: 14, padding: 24, width: "100%", maxWidth: 520, boxShadow: "0 20px 60px rgba(0,0,0,.25)" }}>
        <h3 style={{ margin: 0, marginBottom: 14, fontFamily: "Playfair Display, serif", fontSize: 22 }}>
          {editing ? "Modifica impegno collaboratore" : "Nuovo impegno collaboratore"}
        </h3>
        <form onSubmit={submit}>
          {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
          <div className="field" style={{ marginBottom: 12 }}>
            <label className="label">Collaboratore</label>
            <select className="select" value={collabId} onChange={e => setCollabId(e.target.value)}>
              {collaboratori.length === 0 && <option value="">— nessun collaboratore —</option>}
              {collaboratori.map(c => <option key={c.id} value={c.id}>{c.nome} {c.ruolo ? "— " + c.ruolo : ""}</option>)}
            </select>
          </div>
          <div className="field" style={{ marginBottom: 12 }}>
            <label className="label">Progetto</label>
            <select className="select" value={progettoId} onChange={e => setProgettoId(e.target.value)}>
              <option value="">— nessuno —</option>
              {progetti.map(p => <option key={p.id} value={p.id}>{p.nome}</option>)}
            </select>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 12 }}>
            <div className="field">
              <label className="label">Dal</label>
              <input className="input" type="date" value={dataInizio} onChange={e => setDataInizio(e.target.value)} />
            </div>
            <div className="field">
              <label className="label">Al</label>
              <input className="input" type="date" value={dataFine} onChange={e => setDataFine(e.target.value)} />
            </div>
            <div className="field">
              <label className="label">Giornate FTE</label>
              <input className="input num" type="number" min="0" value={giornate} onChange={e => setGiornate(e.target.value)} />
            </div>
          </div>
          <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, marginBottom: 12, cursor: "pointer" }}>
            <input type="checkbox" checked={ricorrente} onChange={e => setRicorrente(e.target.checked)} />
            Impegno ricorrente (es. PM/amministrazione full-year)
          </label>
          <div className="field" style={{ marginBottom: 16 }}>
            <label className="label">Note</label>
            <input className="input" placeholder="opzionale" value={note} onChange={e => setNote(e.target.value)} />
          </div>
          <div style={{ display: "flex", gap: 8, justifyContent: "space-between" }}>
            <div>
              {editing && (
                <button type="button" onClick={() => { onDelete(editing.id); onClose(); }}
                  style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer", color: "var(--danger, #c0392b)" }}>
                  Elimina
                </button>
              )}
            </div>
            <div style={{ display: "flex", gap: 8 }}>
              <button type="button" onClick={onClose} style={{ padding: "9px 14px", borderRadius: 8, border: "1px solid var(--border)", background: "none", cursor: "pointer" }}>Annulla</button>
              <button type="submit" className="login-btn" style={{ flex: "0 0 auto", width: "auto", padding: "9px 16px" }} disabled={busy}>
                {busy ? "Salvo…" : (editing ? "Aggiorna" : "Salva impegno")}
              </button>
            </div>
          </div>
        </form>
      </div>
    </div>
  );
}

window.AntaresScreens3 = {
  Preventivi, Statistiche, Anagrafica, CalendarioAttrezzatura, CaricoCollaboratori
};
