/* SeaVin — Dashboard (user tracking + document vault + surveyor profile) */
const API = (window.SEAVIN_API != null) ? window.SEAVIN_API : "http://localhost:3001";

const APPS_KEY_DASH = "seavin.surveyor_applications";
const APPROVED_KEY_DASH = "seavin.surveyors.approved";

function loadAppsDash() { try { return JSON.parse(localStorage.getItem(APPS_KEY_DASH) || "[]"); } catch(e) { return []; } }
function saveAppsDash(list) { try { localStorage.setItem(APPS_KEY_DASH, JSON.stringify(list)); } catch(e) {} }
function loadApprovedDash() { try { return JSON.parse(localStorage.getItem(APPROVED_KEY_DASH) || "[]"); } catch(e) { return []; } }
function saveApprovedDash(list) { try { localStorage.setItem(APPROVED_KEY_DASH, JSON.stringify(list)); } catch(e) {} }

const SPEC_LABELS_DASH = {
  scafo: "Scafo", vetroresina: "Vetroresina", legno: "Legno",
  acciaio: "Acciaio / Alluminio", motori_eb: "Motori entrobordo",
  motori_fb: "Motori fuoribordo", trasmissioni: "Trasmissioni",
  vele_armo: "Vele e armo", elettronica: "Elettronica",
  impianti: "Impianti elettrici", gps_ais: "GPS · AIS · Radar",
  restauro: "Restauro classiche", osmosi: "Osmosi · Vetroresina",
  yacht_lusso: "Yacht oltre 24 m",
};
const INSTR_LABELS_DASH = {
  igrometro: "Igrometro a contatto", endoscopio: "Endoscopio digitale",
  termocamera: "Termocamera IR", fonometro: "Fonometro",
  multimetro: "Multimetro / Megger", compressiometro: "Compressiometro",
  fessurimetro: "Fessurimetro digitale", sonar: "Sonar portatile",
  battery_test: "Tester batterie", co_meter: "Misuratore CO",
  drone: "Drone con camera 4K", rov: "ROV subacqueo",
};

function Dashboard() {
  const [cfg, setCfg] = useConfig({palette:"cream",theme:"light",typeset:"editorial",heroLayout:"split",tone:"reassuring",lang:"it"});
  const t = window.SEAVIN_I18N[cfg.lang];
  const it = cfg.lang === "it";

  /* ---- Auth gate ----
     Bootstrap sincrono dalla cache, poi conferma con la sessione reale (Supabase
     è asincrono): si reindirizza al login SOLO dopo che l'auth è pronta, così una
     sessione valida non viene scambiata per "non loggato". */
  const [user, setUser] = useState(() => window.SeaVAuth?.currentUser() || null);

  /* ── Modifica profilo (header): nome/cognome/telefono nei metadata Supabase,
     email col flusso di conferma di Supabase (updateUser → doppio link) ── */
  const [profOpen, setProfOpen] = useState(false);
  const [uscendo, setUscendo] = useState(false); // niente "Bentornato, Giovanni" fantasma durante il logout
  const [prof, setProf] = useState({ nome: "", cognome: "", telefono: "", email: "" });
  const [profMsg, setProfMsg] = useState(null);
  const [profBusy, setProfBusy] = useState(false);

  const apriProfilo = async () => {
    setProfMsg(null);
    let telefono = "";
    try {
      const { data } = await window.SeaVAuth.client.auth.getUser();
      telefono = (data && data.user && data.user.user_metadata && data.user.user_metadata.phone) || "";
    } catch (e) {}
    setProf({
      nome: user?.firstName || "",
      cognome: user?.lastName || "",
      telefono,
      email: user?.email || "",
    });
    setProfOpen(true);
  };

  const salvaProfilo = async () => {
    setProfBusy(true); setProfMsg(null);
    try {
      const agg = await window.SeaVAuth.updateProfile({
        firstName: prof.nome.trim(),
        lastName: prof.cognome.trim(),
        phone: prof.telefono.trim(),
      });
      let testo = it ? "Profilo aggiornato." : "Profile updated.";
      const nuovaEmail = prof.email.trim();
      if (nuovaEmail && user?.email && nuovaEmail.toLowerCase() !== user.email.toLowerCase()) {
        const { error } = await window.SeaVAuth.client.auth.updateUser({ email: nuovaEmail });
        if (error) throw new Error(error.message);
        testo = it
          ? "Profilo aggiornato. Per l'email controlla la posta: il cambio vale dopo il clic sui link di conferma."
          : "Profile updated. Check your inbox to confirm the new email.";
      }
      if (agg) setUser(u => ({ ...u, firstName: agg.firstName || prof.nome.trim(), lastName: agg.lastName || prof.cognome.trim() }));
      // a salvataggio riuscito la card si chiude da sola; resta solo la conferma, che poi svanisce
      setProfOpen(false);
      setProfMsg({ ok: true, testo });
      setTimeout(() => setProfMsg(m => (m && m.ok ? null : m)), 8000);
    } catch (e) {
      setProfMsg({ ok: false, testo: e.message || String(e) });
    }
    setProfBusy(false);
  };
  useEffect(() => {
    let cancelled = false;
    const decide = (u) => {
      if (cancelled) return;
      setUser(u || null);
      if (!u) location.replace("login.html?next=" + encodeURIComponent("dashboard.html"));
    };
    const ready = window.SeaVAuth?.ready;
    if (ready?.then) ready.then(() => decide(window.SeaVAuth.currentUser()));
    else decide(window.SeaVAuth?.currentUser() || null);
    const off = window.SeaVAuth?.onChange ? window.SeaVAuth.onChange((u) => !cancelled && setUser(u)) : null;
    return () => { cancelled = true; if (off) off(); };
  }, []);

  /* ---- Surveyor application bound to this user ---- */
  const [myApp, setMyApp] = useState(() => {
    const u = window.SeaVAuth?.currentUser();
    if (!u) return null;
    return loadAppsDash().find(a => a.userId === u.id) || null;
  });

  /* Refresh on focus to pick up admin's approve/reject decisions */
  useEffect(() => {
    const refresh = () => {
      const u = window.SeaVAuth?.currentUser();
      if (!u) return;
      setMyApp(loadAppsDash().find(a => a.userId === u.id) || null);
    };
    window.addEventListener("focus", refresh);
    document.addEventListener("visibilitychange", refresh);
    return () => {
      window.removeEventListener("focus", refresh);
      document.removeEventListener("visibilitychange", refresh);
    };
  }, []);

  const isSurveyor = user?.role === "surveyor" || !!myApp;

  /* ---- Tab state with hash sync (so dashboard.html#perito jumps right in) ---- */
  const initialTab = (() => {
    const h = (location.hash || "").replace("#", "");
    if (["requests", "vault", "boats", "perito"].includes(h)) return h;
    if (isSurveyor) return "perito";
    return "requests";
  })();
  const [tab, setTab] = useState(initialTab);
  useEffect(() => { history.replaceState(null, "", "#" + tab); }, [tab]);

  /* Report reali dal backend (sezione "Richieste report"). null = in caricamento.
     Filtrati per l'email dell'utente loggato: il backend senza ?email risponde vuoto. */
  const [requests, setRequests] = useState(null);
  useEffect(() => {
    if (!user?.email) { setRequests([]); return; }
    let cancelled = false;
    (async () => {
      // Aspetta che l'auth sia pronta, così il token c'è già alla prima chiamata
      // (evita un 401 "a freddo" mentre la sessione si sta ancora ripristinando).
      try { const rdy = window.SeaVAuth?.ready; if (rdy && rdy.then) await rdy; } catch (e) {}
      // Token per l'identità verificata lato server (modalità Supabase).
      const token = window.SeaVAuth?.getAccessToken ? await window.SeaVAuth.getAccessToken() : null;
      const headers = token ? { Authorization: `Bearer ${token}` } : {};
      try {
        const r = await fetch(`${API}/api/reports?email=${encodeURIComponent(user.email)}`, { headers });
        // 401 qui NON deve mai rimandare al login: il redirect d'accesso spetta SOLO
        // al gate (basato sulla sessione reale). Altrimenti un 401 sulla lista report
        // creerebbe un loop dashboard↔login. Se non si carica, mostra vuoto.
        if (r.status === 401) { if (!cancelled) setRequests([]); return; }
        const d = await r.json();
        if (cancelled) return;
        setRequests((d.reports || []).map(r => ({
        id:      r.id,
        boat:    r.title || [r.builder, r.model].filter(Boolean).join(" ") || "Imbarcazione",
        builder: r.builder || "", model: r.model || "", year: r.year || null,
        hin:     r.hin || r.nome || r.id,
        voto:    r.voto || "",
        verdetto: r.verdetto || "",
        status:  r.status || "delivered",
        date:    r.completedAt
          ? new Date(r.completedAt).toLocaleDateString("it-IT", { day: "2-digit", month: "2-digit", year: "2-digit" }).replace(/\//g, "·")
          : "",
        reportUrl: r.reportUrl || `${API}/report/${r.id}`,
        })));
      } catch (e) { if (!cancelled) setRequests([]); }
    })();
    return () => { cancelled = true; };
  }, [user?.email]); // dipende dall'email (primitiva): niente re-fetch a ogni refresh del token

  return (
    <Page active="" cfg={cfg} setCfg={setCfg} t={t}>
      <section className="dash cloud" style={{ paddingTop: "var(--s-7)", paddingBottom: "var(--s-9)" }}>
        <div className="shell">
          {/* Header */}
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 22, gap: 16, flexWrap: "wrap" }}>
            <div>
              <h1 style={{ fontSize: "var(--step-4)", letterSpacing: "-0.03em", lineHeight: 1 }}>
                {uscendo
                  ? <>{it ? "A presto" : "See you"}<em style={{ fontStyle: "italic", color: "var(--accent-deep)" }}>.</em></>
                  : <>{it ? "Bentornato," : "Welcome back,"} <em style={{ fontStyle: "italic", color: "var(--accent-deep)" }}>{user?.firstName || ""}</em></>}
              </h1>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 12, flexWrap: "wrap" }}>
                <span className="cloud-badge cloud-badge--neutra">
                  {isSurveyor ? (it ? "area perito" : "surveyor area") : (it ? "area personale" : "personal area")}
                </span>
                {user && <span style={{ fontSize: 13, color: "var(--ink-mute)" }}>{user.email}</span>}
              </div>
            </div>
            <div style={{ display: "flex", gap: 10, flexWrap: "wrap", alignItems: "center" }}>
              {!isSurveyor && <a href="checkout.html" className="cloud-btn" style={{ textDecoration: "none" }}>+ {it ? "Nuovo report" : "New report"}</a>}
              <button type="button" className="cloud-ghost" onClick={() => (profOpen ? setProfOpen(false) : apriProfilo())}>
                <CloudIcona nome="matita" size={13}/>{it ? "Modifica profilo" : "Edit profile"}
              </button>
              <button type="button" className="cloud-ghost" onClick={async () => { setUscendo(true); try { await window.SeaVAuth?.logout(); } catch (e) {} location.replace("index.html"); }}>{it ? "Esci" : "Sign out"}</button>
            </div>
          </div>

          {/* modifica profilo: scrive DAVVERO su Supabase (metadata; l'email passa dal doppio link di conferma) */}
          {profOpen && (
            <div className="cloud-card cloud-in" style={{ padding: "16px 20px", marginBottom: 24 }}>
              <div style={{ fontSize: 13, color: "var(--ink-mute)", marginBottom: 10 }}>{it ? "Modifica profilo" : "Edit profile"}</div>
              <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(170px, 1fr))", gap: 10, marginBottom: 10 }}>
                <input value={prof.nome} placeholder={it ? "Nome *" : "First name *"} className="cloud-input" onChange={e => setProf({ ...prof, nome: e.target.value })}/>
                <input value={prof.cognome} placeholder={it ? "Cognome" : "Last name"} className="cloud-input" onChange={e => setProf({ ...prof, cognome: e.target.value })}/>
                <input value={prof.telefono} placeholder={it ? "Telefono" : "Phone"} inputMode="tel" className="cloud-input" onChange={e => setProf({ ...prof, telefono: e.target.value })}/>
                <input value={prof.email} placeholder="Email *" type="email" className="cloud-input" onChange={e => setProf({ ...prof, email: e.target.value })}/>
                <button type="button" className="cloud-btn" onClick={salvaProfilo}
                  disabled={profBusy || !prof.nome.trim() || !/.+@.+\..+/.test(prof.email.trim())}>
                  {profBusy ? (it ? "Salvo…" : "Saving…") : (it ? "Salva" : "Save")}
                </button>
              </div>
              <div style={{ fontSize: 11.5, color: "var(--ink-faint)" }}>
                {it ? "Se cambi l'email, Supabase manda un link di conferma sia al vecchio sia al nuovo indirizzo: il cambio vale dopo il clic." : "Changing the email sends confirmation links to both addresses."}
              </div>
              {profMsg && !profMsg.ok && (
                <div style={{ marginTop: 8, fontSize: 12.5, color: "#9e2a1e" }}>{profMsg.testo}</div>
              )}
            </div>
          )}
          {/* conferma dopo la chiusura della card (svanisce da sola) */}
          {!profOpen && profMsg && profMsg.ok && (
            <div className="cloud-in" style={{ display: "flex", alignItems: "center", gap: 7, marginTop: -10, marginBottom: 18, fontSize: 12.5, color: "#1b7a3e" }}>
              <CloudIcona nome="spunta" size={14}/>{profMsg.testo}
            </div>
          )}

          {/* KPI */}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 12, marginBottom: 26 }}>
            {[
              { n: requests ? String(requests.length) : "—", l: it ? "Report totali" : "Total reports", ico: "clipboard" },
              { n: requests ? String(requests.filter(r => r.status && r.status !== "delivered").length) : "—", l: it ? "In elaborazione" : "In process", ico: "scintilla" },
            ].map((k, i) => (
              <div key={i} className="cloud-card" style={{ padding: "16px 20px", display: "flex", alignItems: "center", gap: 14 }}>
                <span className="cloud-ico"><CloudIcona nome={k.ico} size={19}/></span>
                <div>
                  <div style={{ fontFamily: "var(--font-display)", fontSize: 28, letterSpacing: "-0.02em", lineHeight: 1 }}>{k.n}</div>
                  <div style={{ fontSize: 12.5, color: "var(--ink-mute)", marginTop: 4 }}>{k.l}</div>
                </div>
              </div>
            ))}
          </div>

          {/* Tabs */}
          <div className="cloud-seg" style={{ marginBottom: 26 }}>
            {[
              ...(isSurveyor ? [{ k: "perito", l: it ? "Profilo perito" : "Surveyor profile", ico: "tessera" }] : []),
              { k: "requests", l: it ? "I miei report" : "My reports", ico: "clipboard" },
              { k: "vault", l: it ? "I miei documenti" : "My documents", ico: "documento" },
              { k: "boats", l: it ? "Le mie barche" : "My boats", ico: "nave" },
            ].map(o => (
              <button key={o.k} type="button" className={tab === o.k ? "attivo" : ""} onClick={() => setTab(o.k)}>
                <CloudIcona nome={o.ico} size={14}/>{o.l}
              </button>
            ))}
          </div>

          {tab === "requests" && (
            <div className="cloud-in">
              {requests === null && (
                <div style={{ padding: "30px 8px", color: "var(--ink-mute)", fontSize: 13 }}>
                  {it ? "Caricamento report…" : "Loading reports…"}
                </div>
              )}
              {requests && requests.length === 0 && (
                <div className="cloud-card" style={{ padding: "38px 26px", textAlign: "center" }}>
                  <span className="cloud-ico" style={{ margin: "0 auto 12px" }}><CloudIcona nome="clipboard" size={20}/></span>
                  <div style={{ fontSize: 15, fontWeight: 600 }}>{it ? "Nessun report ancora" : "No reports yet"}</div>
                  <div style={{ fontSize: 13.5, color: "var(--ink-mute)", marginTop: 6, marginBottom: 18 }}>
                    {it ? "I report che richiedi appariranno qui, con voto e verdetto." : "Reports you request will appear here."}
                  </div>
                  <a href="checkout.html" className="cloud-btn" style={{ textDecoration: "none" }}>+ {it ? "Nuovo report" : "New report"}</a>
                </div>
              )}
              {requests && requests.length > 0 && (
                <div className="cloud-card" style={{ padding: "6px 8px" }}>
                  {requests.map((r, i) => (
                    <div key={r.id || i} className="cloud-row">
                      <span className="cloud-ico cloud-ico--sm"><CloudIcona nome="clipboard" size={17}/></span>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 14.5, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{r.boat}</div>
                        <div style={{ fontSize: 12, color: "var(--ink-mute)", marginTop: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                          {r.date ? `${r.date} · ` : ""}{r.hin}{r.verdetto ? ` · ${r.verdetto}` : ""}
                        </div>
                      </div>
                      {r.voto && (
                        <span title={it ? "Voto del report" : "Report grade"} style={{ fontFamily: "var(--font-display)", fontSize: 22, letterSpacing: "-0.02em", color: "var(--accent-deep)", whiteSpace: "nowrap" }}>{r.voto}</span>
                      )}
                      <span className="cloud-badge cloud-badge--verde">{it ? "consegnato" : "delivered"}</span>
                      <a href={r.reportUrl} target="_blank" rel="noopener" className="cloud-ghost" style={{ textDecoration: "none", border: "1px solid color-mix(in srgb, var(--rule) 90%, transparent)" }}>
                        {it ? "Apri" : "Open"} <CloudIcona nome="apri" size={13}/>
                      </a>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}

          {tab === "vault" && <CloudDocs it={it}/>}

          {tab === "boats" && <BarcheView it={it} reports={requests}/>}

          {tab === "perito" && (
            <SurveyorPanel app={myApp} setApp={setMyApp} it={it}/>
          )}
        </div>
      </section>
    </Page>
  );
}

/* ═══════════════════════════════════════════════════════════════════
   SeaVin Cloud — archivio documenti di bordo (Fase 5).
   Tutto passa DIRETTAMENTE da Supabase col client dell'utente loggato:
   la Row Level Security garantisce che ognuno veda solo i suoi file
   (tabella public.documenti + bucket privato "documenti", cartella per
   utente). I promemoria delle scadenze partono dal backend 1×/giorno.
   Anteprima gratuita: il canone (€4,99/mese) arriverà con Stripe.
   ═══════════════════════════════════════════════════════════════════ */
const CLOUD_CATEGORIE = [
  { k: "proprieta",     ico: "ancora",     it: "Proprietà e registrazione",  en: "Ownership & registry",   hint_it: "atto di vendita, registrazione, licenza di navigazione, leasing" },
  { k: "tecnici",       ico: "ingranaggio",it: "Documenti tecnici",          en: "Technical documents",    hint_it: "certificato e dichiarazione CE, manuali, schede, certificati motori" },
  { k: "sicurezza",     ico: "salvagente", it: "Sicurezza e certificazioni", en: "Safety & certifications",hint_it: "certificato di sicurezza, zattera, estintori, EPIRB/AIS/VHF" },
  { k: "assicurazione", ico: "ombrello",   it: "Assicurazione",              en: "Insurance",              hint_it: "polizza RC, kasko, certificati, sinistri" },
  { k: "manutenzione",  ico: "chiave",     it: "Manutenzione",               en: "Maintenance",            hint_it: "registro, fatture, alaggi e vari, riparazioni" },
  { k: "fiscali",       ico: "scontrino",  it: "Fiscali e amministrativi",   en: "Tax & admin",            hint_it: "IVA, dogana, ormeggio, rimessaggio, charter, ricevute portuali" },
  { k: "perizie",       ico: "clipboard",  it: "Perizie e valutazioni",      en: "Surveys & valuations",   hint_it: "perizie pre-acquisto e assicurative, survey, valutazioni" },
  { k: "personali",     ico: "tessera",    it: "Patenti e licenze personali",en: "Personal licenses",      hint_it: "patente nautica, licenza RTF (VHF), MMSI, visita medica" },
  { k: "equipaggio",    ico: "persone",    it: "Equipaggio e gestione",      en: "Crew & management",      hint_it: "contratti, certificati professionali, buste paga, ore lavoro" },
  { k: "inventario",    ico: "fotocamera", it: "Foto e inventario di bordo", en: "Photos & inventory",     hint_it: "foto di scafo e dotazioni: prova per assicurazione e furti" },
  { k: "garanzie",      ico: "etichetta",  it: "Garanzie e accessori",       en: "Warranties & gear",      hint_it: "scontrini e garanzie di elettronica e accessori (scadenza = fine garanzia)" },
  { k: "grandi",        ico: "nave",       it: "Grandi yacht",               en: "Large yachts",           hint_it: "SMS, registri di bordo, classe, ISM/ISPS, MARPOL" },
];

/* Categorie che tipicamente contengono dati relativi alla salute (visita medica
   della patente, certificati dell'equipaggio): per conservarli e farli leggere
   all'AI serve il consenso ESPLICITO dell'utente (art. 9.2.a GDPR + art. 2-septies
   Codice Privacy) — senza consenso niente upload e niente chiamata all'AI. */
const CAT_SENSIBILI = ["personali", "equipaggio"];

/* ⚠ ANTEPRIMA: durante i test TUTTI gli account hanno il Cloud (Rivaluta e
   barche manuali sbloccati). Prima del go-live rimettere a false — e c'è il
   gemello CLOUD_ANTEPRIMA in server/server.js, più la funzione SQL
   puo_aggiungere_barca_manuale da ripristinare (versione commentata nello schema). */
const CLOUD_ANTEPRIMA = true;

/* ── Icone SVG dell'archivio: un solo set, stroke coerente 1.7 ── */
const CLOUD_ICONE = {
  cerca:      <g><circle cx="11" cy="11" r="7"/><path d="M16.5 16.5L21 21"/></g>,
  carica:     <g><path d="M12 16V4M6 10l6-6 6 6"/><path d="M4 20h16"/></g>,
  campana:    <g><path d="M6 9a6 6 0 0 1 12 0c0 6.5 2 7.5 2 7.5H4S6 15.5 6 9z"/><path d="M10 20a2 2 0 0 0 4 0"/></g>,
  matita:     <path d="M4 20l.9-3.8L16.7 4.4a2 2 0 0 1 2.9 2.9L7.8 19.1 4 20z"/>,
  cestino:    <g><path d="M4 7h16"/><path d="M9 7V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"/><path d="M6.5 7l1 13h9l1-13"/></g>,
  apri:       <g><path d="M7 17L17 7"/><path d="M9 7h8v8"/></g>,
  indietro:   <g><path d="M19 12H5"/><path d="M11 18l-6-6 6-6"/></g>,
  spunta:     <path d="M4 12.5l5 5L20 7"/>,
  scintilla:  <path d="M12 3l1.9 5.6L19.5 10.5l-5.6 1.9L12 18l-1.9-5.6L4.5 10.5l5.6-1.9L12 3z"/>,
  chiudi:     <path d="M6 6l12 12M18 6L6 18"/>,
  documento:  <g><path d="M7 3h7l4 4v14H7z"/><path d="M14 3v4h4"/></g>,
  allerta:    <g><circle cx="12" cy="12" r="9"/><path d="M12 8v5"/><path d="M12 16.2v.01"/></g>,
  ancora:     <g><circle cx="12" cy="5.5" r="2"/><path d="M12 7.5V21"/><path d="M4.5 13a7.5 7.5 0 0 0 15 0"/><path d="M8.5 10h7"/></g>,
  ingranaggio:<g><circle cx="12" cy="12" r="3.2"/><path d="M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3M5.3 5.3l2.1 2.1M16.6 16.6l2.1 2.1M18.7 5.3l-2.1 2.1M7.4 16.6l-2.1 2.1"/></g>,
  salvagente: <g><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="4"/><path d="M5.7 5.7l3.2 3.2M15.1 15.1l3.2 3.2M18.3 5.7l-3.2 3.2M8.9 15.1l-3.2 3.2"/></g>,
  ombrello:   <g><path d="M3 13a9 9 0 0 1 18 0z"/><path d="M12 13v5.5a2 2 0 0 0 4 0"/><path d="M12 4v-1"/></g>,
  chiave:     <path d="M20.5 7.2a5 5 0 0 1-6.6 4.9L7.5 18.5a2.1 2.1 0 0 1-3-3l6.4-6.4a5 5 0 0 1 4.9-6.6l-2.4 2.4 2.7 2.7 2.4-2.4z"/>,
  scontrino:  <g><path d="M6 3h12v18l-2-1.5-2 1.5-2-1.5L10 21l-2-1.5L6 21z"/><path d="M9.5 8.5h5M9.5 12.5h5"/></g>,
  clipboard:  <g><path d="M9 4h6v3H9z"/><path d="M15 5h2a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h2"/><path d="M9 13.5l2 2 4-4.5"/></g>,
  tessera:    <g><rect x="3" y="5" width="18" height="14" rx="2.5"/><circle cx="8.4" cy="11" r="1.8"/><path d="M5.8 15.6c.5-1.5 4.7-1.5 5.2 0"/><path d="M14 10h5M14 14h3.5"/></g>,
  persone:    <g><circle cx="9" cy="8" r="3.2"/><path d="M3.8 19.5c0-3 2.3-4.8 5.2-4.8s5.2 1.8 5.2 4.8"/><path d="M15.8 5.4a3.2 3.2 0 0 1 0 5.2"/><path d="M17.2 14.9c1.9.7 3 2.3 3 4.6"/></g>,
  fotocamera: <g><rect x="3" y="7" width="18" height="13" rx="2.5"/><path d="M8.5 7l1.4-2.5h4.2L15.5 7"/><circle cx="12" cy="13.2" r="3.4"/></g>,
  etichetta:  <g><path d="M3 11.5V4h7.5l10 10-7.5 7.5-10-10z"/><circle cx="7.3" cy="8.3" r="1.3"/></g>,
  nave:       <g><path d="M3 15.5h18l-2.6 4.5H5.6z"/><path d="M6.5 15.5V9.5h11v6"/><path d="M10 9.5V6.5h4v3"/></g>,
  grafico:    <g><path d="M5 20v-6M11 20V6M17 20v-9"/><path d="M3 20h18"/></g>,
  lucchetto:  <g><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7.5a4 4 0 0 1 8 0V11"/></g>,
};
function CloudIcona({ nome, size = 18, spess = 1.7, style }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor"
      strokeWidth={spess} strokeLinecap="round" strokeLinejoin="round" style={style} aria-hidden="true">
      {CLOUD_ICONE[nome] || CLOUD_ICONE.documento}
    </svg>
  );
}

function CloudDocs({ it }) {
  const client = window.SeaVAuth && window.SeaVAuth.client;
  const supa = window.SeaVAuth && window.SeaVAuth.mode === "supabase" && client;
  const [docs, setDocs] = useState(null);          // null = caricamento
  const [errore, setErrore] = useState(null);
  const [coda, setCoda] = useState([]);            // file in lavorazione: {id, file, stato, ai, categoria, titolo, scadenza, consenso, err}
  const [vista, setVista] = useState(null);        // null = griglia cartelle | chiave categoria aperta
  const [sezione, setSezione] = useState("documenti"); // documenti | spese
  const [speseVer, setSpeseVer] = useState(0);     // incrementa quando una fattura genera una spesa: la vista Spese si ricarica
  const [qa, setQa] = useState(null);              // "Chiedi ai tuoi documenti": null | "loading" | {risposta, fonti} | {fallita: msg}
  const qaReq = useRef(0);                         // scarta risposte di domande ormai superate
  const [cerca, setCerca] = useState("");
  const [drag, setDrag] = useState(false);
  const [scadEdit, setScadEdit] = useState(null);  // id del documento con l'editor scadenza aperto
  const dragCount = useRef(0);                     // dragenter/dragleave si annidano: contatore anti-sfarfallio
  const fileRef = useRef(null);
  const manuali = useRef(new Set());               // item passati a scelta manuale mentre l'AI stava ancora leggendo

  /* Colonne esplicite: la trascrizione integrale (testo_estratto) resta sul
     server — qui non serve, e non deve finire nello snapshot offline. */
  const COLONNE = "id,categoria,titolo,file_path,file_nome,file_size,mime,data_scadenza,promemoria,note,consenso_salute_il,creato_il";
  const SNAPSHOT = "seavin.cloud.docs";
  const carica = async () => {
    if (!supa) { setDocs([]); return; }
    try {
      const { data, error } = await client.from("documenti").select(COLONNE)
        .order("data_scadenza", { ascending: true, nullsFirst: false });
      if (error) throw error;
      setErrore(null); setDocs(data || []);
      try { localStorage.setItem(SNAPSHOT, JSON.stringify(data || [])); } catch (e) {}
    } catch (e) {
      // offline (o Supabase irraggiungibile): si riparte dall'ultima sincronizzazione
      let snap = null;
      try { snap = JSON.parse(localStorage.getItem(SNAPSHOT) || "null"); } catch (e2) {}
      if (snap) { setErrore(null); setDocs(snap); }
      else { setErrore(e.message || String(e)); setDocs([]); }
    }
  };
  useEffect(() => { carica(); /* eslint-disable-next-line */ }, []);

  const [online, setOnline] = useState(typeof navigator === "undefined" ? true : navigator.onLine);
  useEffect(() => {
    const su = () => { setOnline(true); carica(); };
    const giu = () => setOnline(false);
    window.addEventListener("online", su); window.addEventListener("offline", giu);
    return () => { window.removeEventListener("online", su); window.removeEventListener("offline", giu); };
    // eslint-disable-next-line
  }, []);

  const giorni = (d) => Math.round((new Date(d + "T00:00:00") - new Date().setHours(0, 0, 0, 0)) / 86400000);

  /* Categorie personalizzate (card "+"): si sommano alle 12 standard.
     Le chiavi hanno prefisso "c_" per non collidere mai con quelle di serie. */
  const [catCustom, setCatCustom] = useState([]);
  const caricaCatCustom = async () => {
    try {
      const { data, error } = await client.from("categorie_utente").select("*").order("creato_il");
      if (!error && Array.isArray(data)) setCatCustom(data);
    } catch (e) { /* tabella non ancora creata: solo categorie standard */ }
  };
  useEffect(() => { caricaCatCustom(); /* eslint-disable-next-line */ }, []);

  const TUTTE_CAT = [
    ...CLOUD_CATEGORIE,
    ...catCustom.map(c => ({ k: c.chiave, ico: "etichetta", it: c.nome, en: c.nome, hint_it: "categoria personale", custom: true, idCustom: c.id })),
  ];
  const catDi = (k) => TUTTE_CAT.find(x => x.k === k);
  const catLabel = (k) => { const c = catDi(k); return c ? (it ? c.it : c.en) : k; };

  const nuovaCategoria = async () => {
    const nome = (window.prompt(it ? "Nome della nuova categoria (es. Charter, Regate…)" : "New category name") || "").trim().slice(0, 40);
    if (!nome) return;
    const chiave = "c_" + nome.toLowerCase().replace(/[^a-z0-9àèéìòù]+/g, "-").replace(/^-+|-+$/g, "");
    if (chiave === "c_" || catDi(chiave)) { setErrore(it ? "Esiste già una categoria con questo nome." : "A category with this name already exists."); return; }
    const { data: u } = await client.auth.getUser();
    const uid = u && u.user && u.user.id;
    if (!uid) return;
    const esito = await client.from("categorie_utente").insert({ user_id: uid, chiave, nome });
    if (esito.error) {
      setErrore(/categorie|relation|schema/i.test(esito.error.message || "")
        ? (it ? "Le categorie personalizzate si attivano rilanciando db/cloud_schema.sql su Supabase." : "Custom categories need the updated schema.")
        : esito.error.message);
      return;
    }
    await caricaCatCustom();
  };

  const eliminaCategoria = async (c) => {
    if (!window.confirm(it ? `Eliminare la categoria "${it ? c.it : c.en}"?` : "Delete this category?")) return;
    await client.from("categorie_utente").delete().eq("id", c.idCustom);
    setVista(null);
    await caricaCatCustom();
  };

  const badge = (d) => {
    if (!d.data_scadenza) return null;
    const g = giorni(d.data_scadenza);
    const cls = g < 0 ? "cloud-badge cloud-badge--rossa" : g <= 30 ? "cloud-badge cloud-badge--ambra" : "cloud-badge cloud-badge--verde";
    const txt = g < 0 ? (it ? `scaduto da ${-g} g` : `expired ${-g}d ago`)
      : g <= 30 ? (it ? `scade tra ${g} g` : `expires in ${g}d`)
      : new Date(d.data_scadenza).toLocaleDateString(it ? "it-IT" : "en-GB");
    return <span className={cls}>{txt}</span>;
  };

  /* ── Coda di archiviazione: trascini (o scegli) i file, l'AI li legge e li
     archivia DA SOLA nella categoria giusta. Passano dalla conferma manuale
     solo i casi che lo richiedono: categoria con dati sanitari (serve il
     consenso esplicito), AI incerta o non disponibile, file non leggibile. ── */
  const AI_MIME = ["application/pdf", "image/jpeg", "image/png", "image/webp"];
  const aggiorna = (id, patch) => setCoda(q => q.map(x => x.id === id ? { ...x, ...patch } : x));
  const rimuovi = (id) => setCoda(q => q.filter(x => x.id !== id));
  const nuovoId = () => Math.random().toString(36).slice(2) + Date.now().toString(36);
  const aBase64 = (f) => new Promise((ok, ko) => {
    const r = new FileReader();
    r.onload = () => ok(String(r.result).split(",")[1] || "");
    r.onerror = () => ko(new Error("lettura file"));
    r.readAsDataURL(f);
  });

  /* pdf-lib (split dei PDF nel browser) si carica solo quando serve davvero */
  const pdfLibPromise = useRef(null);
  const caricaPdfLib = () => {
    if (window.PDFLib) return Promise.resolve(window.PDFLib);
    if (!pdfLibPromise.current) pdfLibPromise.current = new Promise((ok, ko) => {
      const s = document.createElement("script");
      s.src = "https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js";
      s.onload = () => ok(window.PDFLib);
      s.onerror = () => { pdfLibPromise.current = null; ko(new Error("pdf-lib non caricato")); };
      document.head.appendChild(s);
    });
    return pdfLibPromise.current;
  };

  /* Scatolone: il PDF è la scansione di una cartellina con più documenti in
     sequenza. L'AI restituisce i confini, pdf-lib li separa nel browser e ogni
     segmento rientra nella coda normale (lettura completa, consensi, spese).
     Ritorna false se il file va trattato come documento unico. */
  const scatolone = async (item, f, pdf, PDFLib) => {
    aggiorna(item.id, { stato: "indice", pagine: pdf.getPageCount() });
    try {
      const token = window.SeaVAuth?.getAccessToken ? await window.SeaVAuth.getAccessToken() : null;
      const base64 = await aBase64(f);
      const res = await fetch(`${API}/api/doc-analyze`, {
        method: "POST",
        headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
        body: JSON.stringify({ base64, mime: f.type, nome: f.name, modo: "indice" }),
      });
      if (!res.ok) throw new Error("HTTP " + res.status);
      const d = await res.json();
      const seg = (d.documenti || []).slice(0, 20);
      if (seg.length <= 1) return false;
      const figli = [];
      for (const s of seg) {
        const tot = pdf.getPageCount();
        const da = Math.max(1, Math.min(s.da_pagina, tot));
        const a = Math.max(da, Math.min(s.a_pagina, tot));
        const nuovo = await PDFLib.PDFDocument.create();
        const pagine = await nuovo.copyPages(pdf, Array.from({ length: a - da + 1 }, (_, i) => da - 1 + i));
        pagine.forEach(p => nuovo.addPage(p));
        const bytes = await nuovo.save();
        const nomeFile = (s.titolo || `documento ${da}-${a}`).replace(/[^A-Za-z0-9àèéìòù _-]/g, "").trim().slice(0, 60) || `documento ${da}-${a}`;
        figli.push({
          id: nuovoId(), file: new File([bytes], `${nomeFile}.pdf`, { type: "application/pdf" }),
          stato: "coda", ai: null, categoria: "", titolo: "", scadenza: "", consenso: false, err: null,
          noIndice: true, // un segmento non si ri-suddivide: il flusso converge sempre
        });
      }
      aggiorna(item.id, { stato: "fatto-indice", trovati: figli.length });
      setTimeout(() => rimuovi(item.id), 8000);
      setCoda(q => [...q, ...figli]);
      for (const x of figli) await leggi(x);
      return true;
    } catch (e) { return false; }
  };

  const salva = async (item) => {
    if (CAT_SENSIBILI.includes(item.categoria) && !item.consenso) { aggiorna(item.id, { stato: "conferma" }); return; }
    aggiorna(item.id, { stato: "salvataggio", err: null });
    try {
      const { data: userData } = await client.auth.getUser();
      const uid = userData && userData.user && userData.user.id;
      if (!uid) throw new Error(it ? "Sessione scaduta: rientra." : "Session expired.");
      const f = item.file;
      const safe = f.name.replace(/[^A-Za-z0-9àèéìòù._ -]/g, "_").slice(0, 120);
      const path = `${uid}/${Date.now()}_${safe}`;
      const up = await client.storage.from("documenti").upload(path, f);
      if (up.error) throw up.error;
      const notaAI = item.ai ? [item.ai.numero ? `N. ${item.ai.numero}` : null, item.ai.emittente || null].filter(Boolean).join(" · ") : "";
      const riga = {
        user_id: uid,
        user_email: (userData.user.email || "").toLowerCase(),
        categoria: item.categoria,
        titolo: (item.titolo || f.name).trim().slice(0, 160),
        file_path: path, file_nome: f.name, file_size: f.size, mime: f.type || null,
        data_scadenza: item.scadenza || null,
        note: notaAI || null,
        consenso_salute_il: CAT_SENSIBILI.includes(item.categoria) ? new Date().toISOString() : null,
        testo_estratto: (item.ai && item.ai.testo) || null,
      };
      let ins = await client.from("documenti").insert(riga).select("id").single();
      if (ins.error && /testo_estratto/i.test(ins.error.message || "")) {
        // schema non ancora aggiornato: si salva senza trascrizione, il resto funziona
        delete riga.testo_estratto;
        ins = await client.from("documenti").insert(riga).select("id").single();
      }
      if (ins.error) { await client.storage.from("documenti").remove([path]); throw ins.error; }
      // fattura/ricevuta: la spesa estratta dall'AI entra da sola nel registro
      let spesaReg = null, spesaErr = false;
      const sp = item.ai && item.ai.spesa;
      if (sp && sp.importo && ins.data && ins.data.id) {
        const esito = await client.from("spese").insert({
          user_id: uid, documento_id: ins.data.id,
          data: sp.data, fornitore: sp.fornitore, descrizione: sp.descrizione,
          categoria: sp.categoria, importo: sp.importo,
          da_confermare: (typeof sp.confidenza === "number" ? sp.confidenza : 0.5) < 0.6,
        });
        if (!esito.error) { spesaReg = sp.importo; setSpeseVer(v => v + 1); }
        else { spesaErr = true; console.warn("[cloud] spesa non registrata:", esito.error.message); }
      }
      aggiorna(item.id, { stato: "fatto", categoria: item.categoria, spesaReg, spesaErr });
      setTimeout(() => rimuovi(item.id), 6000);
      await carica();
    } catch (e) {
      aggiorna(item.id, { stato: "conferma", err: e.message || String(e) });
    }
  };

  const leggi = async (item) => {
    const f = item.file;
    if (f.size > 25 * 1024 * 1024) {
      aggiorna(item.id, { stato: "errore", err: it ? "Oltre 25 MB: comprimi il file e riprova." : "Over 25 MB: compress the file and retry." });
      return;
    }
    if (!AI_MIME.includes(f.type) || f.size > 15 * 1024 * 1024) {
      aggiorna(item.id, {
        stato: "conferma", titolo: f.name,
        info: it ? "Questo formato l'AI non lo legge: compila i campi e registra l'eventuale spesa a mano in Spese." : "The AI can't read this format: fill in the fields and log any expense manually.",
      });
      return;
    }
    // PDF corposo = possibile "scatolone" di documenti: prima l'indice dei confini
    if (f.type === "application/pdf" && !item.noIndice) {
      try {
        const PDFLib = await caricaPdfLib();
        const pdf = await PDFLib.PDFDocument.load(await f.arrayBuffer(), { ignoreEncryption: true });
        if (pdf.getPageCount() >= 6 && await scatolone(item, f, pdf, PDFLib)) return;
      } catch (e) { /* pdf-lib assente o PDF anomalo: prosegue come documento unico */ }
    }
    aggiorna(item.id, { stato: "lettura" });
    try {
      const token = window.SeaVAuth?.getAccessToken ? await window.SeaVAuth.getAccessToken() : null;
      const base64 = await aBase64(f);
      const corpo = JSON.stringify({ base64, mime: f.type, nome: f.name });
      const opzioni = {
        method: "POST",
        headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
        body: corpo,
      };
      let res = await fetch(`${API}/api/doc-analyze`, opzioni);
      if (res.status === 422 || res.status === 429) {
        // rate limit (tanti file insieme) o AI momentaneamente ko: un secondo tentativo con calma
        await new Promise(r => setTimeout(r, 15000));
        res = await fetch(`${API}/api/doc-analyze`, opzioni);
      }
      if (!res.ok) throw new Error("HTTP " + res.status);
      const d = await res.json();
      const categoria = d.categoria || "";
      const titolo = (d.titolo || f.name).slice(0, 160);
      const scadenza = d.data_scadenza || "";
      if (manuali.current.has(item.id)) {
        // l'utente ha preso il controllo mentre l'AI leggeva: suggerisci senza sovrascrivere ciò che ha già scritto
        setCoda(q => q.map(x => x.id === item.id
          ? { ...x, ai: d, categoria: x.categoria || categoria, titolo: x.titolo || titolo, scadenza: x.scadenza || scadenza }
          : x));
        return;
      }
      if (!categoria) { aggiorna(item.id, { stato: "conferma", ai: d, titolo, scadenza }); return; }
      if (CAT_SENSIBILI.includes(categoria)) { aggiorna(item.id, { stato: "conferma", ai: d, categoria, titolo, scadenza }); return; }
      // i dati AI vanno PRIMA nello stato: se il salvataggio fallisce, la scheda
      // di conferma riparte con tutto compilato (e la spesa non si perde)
      aggiorna(item.id, { ai: d, categoria, titolo, scadenza });
      await salva({ ...item, ai: d, categoria, titolo, scadenza });
    } catch (e) {
      aggiorna(item.id, { stato: "conferma", titolo: f.name, err: it ? "Lettura AI non riuscita: scegli tu la categoria." : "AI reading failed: pick the category yourself." });
    }
  };

  const onFiles = (lista) => {
    if (!supa) return;
    const arr = Array.from(lista || []).slice(0, 10); // max 10 per volta
    if (!arr.length) return;
    const items = arr.map(f => ({
      id: Math.random().toString(36).slice(2) + Date.now().toString(36),
      file: f, stato: "coda", ai: null, categoria: "", titolo: "", scadenza: "", consenso: false, err: null,
    }));
    setCoda(q => [...q, ...items]);
    (async () => { for (const x of items) await leggi(x); })(); // in serie: rispetta il rate limit dell'AI
    if (fileRef.current) fileRef.current.value = "";
  };

  const apriFile = async (file_path) => {
    const { data, error } = await client.storage.from("documenti").createSignedUrl(file_path, 300);
    if (error) { setErrore(error.message); return; }
    window.open(data.signedUrl, "_blank", "noopener");
  };
  const apri = (d) => apriFile(d.file_path);

  const elimina = async (d) => {
    if (!window.confirm(it ? `Eliminare "${d.titolo}"? L'operazione è definitiva.` : `Delete "${d.titolo}"? This cannot be undone.`)) return;
    await client.storage.from("documenti").remove([d.file_path]);
    await client.from("documenti").delete().eq("id", d.id);
    await carica();
  };

  const togglePromemoria = async (d) => {
    await client.from("documenti").update({ promemoria: !d.promemoria }).eq("id", d.id);
    await carica();
  };

  const sposta = async (d, k) => {
    if (!k || k === d.categoria) return;
    const patch = { categoria: k };
    if (CAT_SENSIBILI.includes(k) && !d.consenso_salute_il) {
      const ok = window.confirm(it
        ? "Questa categoria è per documenti con dati relativi alla salute (patente, visita medica, equipaggio). Confermando acconsenti espressamente al loro trattamento (art. 9, par. 2, lett. a GDPR); puoi revocare il consenso eliminando il documento. Spostare?"
        : "This category holds documents with health data. By confirming you explicitly consent to their processing (art. 9(2)(a) GDPR); you can withdraw consent by deleting the document. Move?");
      if (!ok) return;
      patch.consenso_salute_il = new Date().toISOString();
    }
    await client.from("documenti").update(patch).eq("id", d.id);
    await carica();
  };

  /* "Chiedi ai tuoi documenti": la stessa barra di ricerca, ma con una domanda.
     Il backend risponde usando solo l'archivio dell'utente e cita le fonti. */
  const chiedi = async () => {
    const domanda = cerca.trim();
    if (domanda.length < 4) return;
    const mia = ++qaReq.current;
    setQa("loading");
    try {
      const token = window.SeaVAuth?.getAccessToken ? await window.SeaVAuth.getAccessToken() : null;
      const res = await fetch(`${API}/api/doc-ask`, {
        method: "POST",
        headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
        body: JSON.stringify({ domanda }),
      });
      const d = await res.json().catch(() => ({}));
      if (qaReq.current !== mia) return;
      if (!res.ok) { setQa({ fallita: d.error || (it ? "Risposta non disponibile, riprova." : "No answer available, retry.") }); return; }
      setQa(d);
    } catch (e) { if (qaReq.current === mia) setQa({ fallita: it ? "Risposta non disponibile, riprova." : "No answer available, retry." }); }
  };

  const salvaScadenza = async (d, v) => {
    setScadEdit(null);
    if ((v || "") === (d.data_scadenza || "")) return;
    // data nuova = promemoria da rifare: azzera i flag degli avvisi già mandati
    await client.from("documenti").update({
      data_scadenza: v || null,
      avvisato_90: false, avvisato_30: false, avvisato_7: false, avvisato_scaduto: false,
    }).eq("id", d.id);
    await carica();
  };

  if (!supa) {
    return <div className="cloud"><div className="cloud-card" style={{ padding: 32, maxWidth: 640 }}>
      <p style={{ fontSize: 14, color: "var(--ink-soft)" }}>{it ? "Accedi con un account per usare l'archivio documenti." : "Sign in to use the document vault."}</p>
    </div></div>;
  }

  const tabellaAssente = errore && /documenti|schema|relation|permission/i.test(errore) && (docs || []).length === 0;
  const inScadenza = (docs || []).filter(d => d.data_scadenza && giorni(d.data_scadenza) <= 90);
  const ricerca = cerca.trim().toLowerCase();
  const trovati = ricerca ? (docs || []).filter(d => `${d.titolo} ${d.file_nome || ""}`.toLowerCase().includes(ricerca)) : null;

  /* riga documento (usata in cartella, ricerca e scadenze) */
  const riga = (d, mostraCat) => (
    <div key={d.id} className="cloud-row">
      <span className="cloud-ico cloud-ico--sm"><CloudIcona nome={(catDi(d.categoria) || {}).ico || "documento"} size={17}/></span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14.5, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.titolo}</div>
        <div style={{ fontSize: 12, color: "var(--ink-mute)", marginTop: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
          {mostraCat ? `${catLabel(d.categoria)} · ` : ""}{d.file_nome}{d.file_size ? ` · ${Math.max(1, Math.round(d.file_size / 1024))} KB` : ""}{d.note ? ` · ${d.note}` : ""}
        </div>
      </div>
      {scadEdit === d.id ? (
        <input type="date" className="cloud-input" defaultValue={d.data_scadenza || ""} autoFocus
          onBlur={e => salvaScadenza(d, e.target.value)}
          onKeyDown={e => { if (e.key === "Enter") e.target.blur(); if (e.key === "Escape") setScadEdit(null); }}
          style={{ width: 150, padding: "7px 10px", fontSize: 13 }}/>
      ) : d.data_scadenza ? (
        <span style={{ display: "flex", alignItems: "center", gap: 2 }}>
          {badge(d)}
          <button type="button" className="cloud-iconbtn" onClick={() => setScadEdit(d.id)} title={it ? "Modifica scadenza" : "Edit expiry"} aria-label={it ? "Modifica scadenza" : "Edit expiry"}>
            <CloudIcona nome="matita" size={15}/>
          </button>
        </span>
      ) : (
        <button type="button" className="cloud-ghost" onClick={() => setScadEdit(d.id)}>
          {it ? "+ scadenza" : "+ expiry"}
        </button>
      )}
      {d.data_scadenza && (
        <button type="button" className="cloud-iconbtn" onClick={() => togglePromemoria(d)} style={{ opacity: d.promemoria ? 1 : 0.35 }}
          title={it ? (d.promemoria ? "Promemoria attivi — clicca per spegnere" : "Promemoria spenti — clicca per attivare") : "Toggle reminders"}
          aria-label={it ? "Promemoria" : "Reminders"}>
          <CloudIcona nome="campana" size={16}/>
        </button>
      )}
      <select value={d.categoria} className="cloud-select" onChange={e => sposta(d, e.target.value)} title={it ? "Sposta in un'altra categoria" : "Move to another category"}>
        {TUTTE_CAT.map(c => <option key={c.k} value={c.k}>{it ? c.it : c.en}</option>)}
      </select>
      <button type="button" className="cloud-iconbtn" onClick={() => apri(d)} title={it ? "Apri" : "Open"} aria-label={it ? "Apri" : "Open"}>
        <CloudIcona nome="apri" size={16}/>
      </button>
      <button type="button" className="cloud-iconbtn cloud-iconbtn--rosso" onClick={() => elimina(d)} title={it ? "Elimina" : "Delete"} aria-label={it ? "Elimina" : "Delete"}>
        <CloudIcona nome="cestino" size={16}/>
      </button>
    </div>
  );

  /* scheda della coda di caricamento */
  const schedaCoda = (x) => {
    const sensibile = CAT_SENSIBILI.includes(x.categoria);
    return (
      <div key={x.id} className="cloud-card cloud-in" style={{ padding: "14px 18px", marginBottom: 10 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
          <span className="cloud-ico cloud-ico--sm"><CloudIcona nome="documento" size={17}/></span>
          <span style={{ fontSize: 13, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: 250 }}>{x.file.name}</span>
          {x.stato === "coda" && <span style={{ fontSize: 12.5, color: "var(--ink-mute)" }}>{it ? "in attesa…" : "queued…"}</span>}
          {x.stato === "lettura" && (
            <React.Fragment>
              <span className="cloud-pulse" style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "var(--accent-deep)" }}>
                <CloudIcona nome="scintilla" size={14}/>{it ? "l'AI sta leggendo…" : "AI is reading…"}
              </span>
              <button type="button" className="cloud-ghost" onClick={() => { manuali.current.add(x.id); aggiorna(x.id, { stato: "conferma" }); }}>
                {it ? "scegli tu la categoria" : "pick the category yourself"}
              </button>
            </React.Fragment>
          )}
          {x.stato === "indice" && (
            <span className="cloud-pulse" style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "var(--accent-deep)" }}>
              <CloudIcona nome="scintilla" size={14}/>{it ? `l'AI sta sfogliando le ${x.pagine || ""} pagine…` : `AI is leafing through ${x.pagine || ""} pages…`}
            </span>
          )}
          {x.stato === "fatto-indice" && (
            <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, fontWeight: 500, color: "#1b7a3e" }}>
              <CloudIcona nome="spunta" size={14}/>{it ? `trovati ${x.trovati} documenti — li leggo uno a uno` : `found ${x.trovati} documents — reading them one by one`}
            </span>
          )}
          {x.stato === "salvataggio" && <span style={{ fontSize: 12.5, color: "var(--ink-mute)" }}>{it ? "archiviazione…" : "filing…"}</span>}
          {x.stato === "fatto" && (
            <React.Fragment>
              <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, fontWeight: 500, color: "#1b7a3e" }}>
                <CloudIcona nome="spunta" size={14}/>{it ? "archiviato in" : "filed under"} {catLabel(x.categoria)}
                {x.spesaReg ? ` · € ${Number(x.spesaReg).toLocaleString("it-IT")} → ${it ? "Spese" : "Expenses"}` : ""}
              </span>
              {x.spesaErr && (
                <span style={{ fontSize: 11.5, color: "#a6501f" }}>
                  {it ? "spesa non registrata: aggiungila a mano in Spese" : "expense not logged: add it manually"}
                </span>
              )}
              <button type="button" className="cloud-ghost" onClick={() => { setCerca(""); setVista(x.categoria); rimuovi(x.id); }}>{it ? "vedi" : "view"}</button>
            </React.Fragment>
          )}
          {x.stato === "errore" && (
            <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "#9e2a1e" }}>
              <CloudIcona nome="allerta" size={14}/>{x.err}
            </span>
          )}
          {(x.stato === "errore" || x.stato === "conferma") && (
            <button type="button" className="cloud-iconbtn" onClick={() => rimuovi(x.id)} title={it ? "Annulla" : "Cancel"} aria-label={it ? "Annulla" : "Cancel"} style={{ marginLeft: "auto" }}>
              <CloudIcona nome="chiudi" size={15}/>
            </button>
          )}
        </div>
        {x.stato === "conferma" && (
          <div style={{ marginTop: 14 }}>
            {x.err && (
              <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "#9e2a1e", marginBottom: 10 }}>
                <CloudIcona nome="allerta" size={14}/>{x.err}
              </div>
            )}
            {x.info && !x.err && (
              <div style={{ fontSize: 12.5, color: "var(--ink-mute)", marginBottom: 10 }}>{x.info}</div>
            )}
            {x.ai && x.categoria && (
              <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "var(--accent-deep)", marginBottom: 10 }}>
                <CloudIcona nome="scintilla" size={14}/>{it ? "proposta dell'AI — controlla e conferma" : "AI proposal — check and confirm"}
              </div>
            )}
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 10, marginBottom: 12 }}>
              <select value={x.categoria} className="cloud-select cloud-select--pieno" onChange={e => aggiorna(x.id, { categoria: e.target.value })}>
                <option value="">{it ? "Scegli la categoria…" : "Pick a category…"}</option>
                {TUTTE_CAT.map(c => <option key={c.k} value={c.k}>{it ? c.it : c.en}</option>)}
              </select>
              <input value={x.titolo} className="cloud-input" onChange={e => aggiorna(x.id, { titolo: e.target.value })} placeholder={it ? "Titolo" : "Title"}/>
              <input type="date" value={x.scadenza} className="cloud-input" onChange={e => aggiorna(x.id, { scadenza: e.target.value })} title={it ? "Scadenza (opzionale)" : "Expiry (optional)"}/>
            </div>
            {sensibile && (
              <div className="cloud-consenso">
                <div style={{ color: "var(--ink-soft)", marginBottom: 8 }}>
                  {it
                    ? "Patente nautica, visita medica e certificati dell'equipaggio contengono dati relativi alla salute: per conservarli serve il tuo consenso esplicito (art. 9, par. 2, lett. a GDPR)."
                    : "Licenses, medical certificates and crew papers contain health data: storing them requires your explicit consent (art. 9(2)(a) GDPR)."}
                </div>
                <label style={{ display: "flex", gap: 9, alignItems: "flex-start", cursor: "pointer" }}>
                  <input type="checkbox" checked={x.consenso} onChange={e => aggiorna(x.id, { consenso: e.target.checked })} style={{ marginTop: 2 }}/>
                  <span>
                    {it
                      ? "Acconsento espressamente al trattamento dei dati relativi alla salute contenuti nei documenti che carico. Posso revocare il consenso eliminando il documento."
                      : "I explicitly consent to the processing of health data in the documents I upload. I can withdraw consent by deleting the document."}
                  </span>
                </label>
              </div>
            )}
            <button type="button" className="cloud-btn" onClick={() => salva(x)} disabled={!x.categoria || (sensibile && !x.consenso)}>
              {!x.categoria ? (it ? "Scegli la categoria ↑" : "Pick a category ↑")
                : (sensibile && !x.consenso) ? (it ? "Serve il consenso ↑" : "Consent required ↑")
                : (it ? "Archivia" : "File it")}
            </button>
          </div>
        )}
      </div>
    );
  };

  return (
    <div className="cloud" style={{ maxWidth: 980, position: "relative" }}
      onDragEnter={e => { e.preventDefault(); dragCount.current++; setDrag(true); }}
      onDragOver={e => e.preventDefault()}
      onDragLeave={e => { e.preventDefault(); dragCount.current = Math.max(0, dragCount.current - 1); if (!dragCount.current) setDrag(false); }}
      onDrop={e => { e.preventDefault(); dragCount.current = 0; setDrag(false); onFiles(e.dataTransfer.files); }}>

      {drag && (
        <div className="cloud-drop">
          <div style={{ textAlign: "center" }}>
            <span className="cloud-ico" style={{ margin: "0 auto 12px" }}><CloudIcona nome="carica" size={22}/></span>
            <div style={{ fontFamily: "var(--font-display)", fontSize: 24, letterSpacing: "-0.02em" }}>{it ? "Rilascia qui" : "Drop here"}</div>
            <div style={{ fontSize: 13, color: "var(--ink-mute)", marginTop: 6 }}>
              {it ? "L'AI legge il documento e lo archivia da sola" : "The AI reads and files it automatically"}
            </div>
          </div>
        </div>
      )}

      {/* intestazione: titolo, ricerca, carica */}
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 14, marginBottom: 8 }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 12 }}>
          <span style={{ fontFamily: "var(--font-display)", fontSize: 27, letterSpacing: "-0.02em" }}>SeaVin Cloud</span>
          <span className="cloud-badge cloud-badge--neutra">{it ? "anteprima gratuita" : "free preview"}</span>
          {!online && (
            <span className="cloud-badge cloud-badge--ambra" title={it ? "Sei senza connessione: vedi l'ultima sincronizzazione" : "You are offline: showing last sync"}>
              {it ? "offline — ultima sincronizzazione" : "offline — last sync"}
            </span>
          )}
        </div>
        <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
          <label className="cloud-search">
            <CloudIcona nome="cerca" size={15} style={{ color: "var(--ink-mute)", flexShrink: 0 }}/>
            <input value={cerca}
              onChange={e => { setSezione("documenti"); setQa(null); ++qaReq.current; setCerca(e.target.value); }}
              onKeyDown={e => { if (e.key === "Enter") chiedi(); }}
              placeholder={it ? "Cerca, o fai una domanda" : "Search, or ask a question"}/>
            {cerca.trim().length >= 4 && (
              <button type="button" onClick={chiedi} disabled={qa === "loading"}
                style={{ display: "inline-flex", alignItems: "center", gap: 5, border: "none", cursor: "pointer", fontSize: 11.5, fontWeight: 600, color: "var(--accent-deep)", background: "color-mix(in srgb, var(--accent-deep) 10%, transparent)", padding: "5px 12px", borderRadius: 999, whiteSpace: "nowrap", opacity: qa === "loading" ? 0.6 : 1 }}>
                <CloudIcona nome="scintilla" size={12}/>{it ? "Chiedi all'AI" : "Ask the AI"}
              </button>
            )}
          </label>
          <button type="button" className="cloud-btn" onClick={() => fileRef.current && fileRef.current.click()}>
            <CloudIcona nome="carica" size={15}/>{it ? "Carica" : "Upload"}
          </button>
        </div>
      </div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 12, marginBottom: 20 }}>
        <div className="cloud-seg">
          <button type="button" className={sezione === "documenti" ? "attivo" : ""} onClick={() => setSezione("documenti")}>
            <CloudIcona nome="documento" size={14}/>{it ? "Documenti" : "Documents"}
          </button>
          <button type="button" className={sezione === "spese" ? "attivo" : ""} onClick={() => { setCerca(""); setSezione("spese"); }}>
            <CloudIcona nome="grafico" size={14}/>{it ? "Spese" : "Expenses"}
          </button>
        </div>
      </div>
      <input type="file" multiple ref={fileRef} onChange={e => onFiles(e.target.files)}
        accept=".pdf,.jpg,.jpeg,.png,.webp,.heic,.doc,.docx,.xls,.xlsx" style={{ display: "none" }}/>

      {qa === "loading" && (
        <div className="cloud-card cloud-in" style={{ padding: "14px 18px", marginBottom: 16 }}>
          <span className="cloud-pulse" style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "var(--accent-deep)" }}>
            <CloudIcona nome="scintilla" size={14}/>{it ? "Sto cercando la risposta nei tuoi documenti…" : "Looking for the answer in your documents…"}
          </span>
        </div>
      )}
      {qa && typeof qa === "object" && qa.risposta && (
        <div className="cloud-card cloud-in" style={{ padding: "16px 20px", marginBottom: 16, borderColor: "color-mix(in srgb, var(--accent-deep) 35%, transparent)" }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, marginBottom: 8 }}>
            <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12, fontWeight: 600, color: "var(--accent-deep)" }}>
              <CloudIcona nome="scintilla" size={13}/>{it ? "Risposta dall'archivio" : "Answer from your vault"}
            </span>
            <button type="button" className="cloud-iconbtn" onClick={() => setQa(null)} title={it ? "Chiudi" : "Close"} aria-label={it ? "Chiudi" : "Close"}>
              <CloudIcona nome="chiudi" size={14}/>
            </button>
          </div>
          <div style={{ fontSize: 14, lineHeight: 1.6, marginBottom: 10 }}>{qa.risposta}</div>
          <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
            {(qa.fonti || []).map(f0 => {
              const doc = (docs || []).find(d => d.id === f0.id);
              return (
                <button key={f0.id} type="button" className="cloud-ghost" disabled={!doc}
                  onClick={() => doc && apri(doc)}
                  style={{ border: "1px solid color-mix(in srgb, var(--rule) 90%, transparent)", fontSize: 11.5, padding: "4px 12px" }}>
                  <CloudIcona nome="documento" size={12}/>{f0.titolo}{doc ? " · apri" : ""}
                </button>
              );
            })}
            <span style={{ fontSize: 11.5, color: "var(--ink-faint)" }}>{it ? "verifica sempre sul documento" : "always check the document"}</span>
          </div>
        </div>
      )}
      {qa && typeof qa === "object" && qa.fallita && (
        <div className="cloud-in" style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 14, fontSize: 12.5, color: "var(--ink-mute)" }}>
          <CloudIcona nome="allerta" size={14}/>{qa.fallita}
        </div>
      )}

      {tabellaAssente && (
        <div className="cloud-card" style={{ padding: 24, marginBottom: 20, fontSize: 13.5, color: "var(--ink-soft)", lineHeight: 1.6 }}>
          {it ? "Il cloud è in attivazione su questo ambiente. Riprova tra poco." : "The cloud is being activated on this environment. Try again shortly."}
        </div>
      )}
      {errore && !tabellaAssente && (
        <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 14, fontSize: 12.5, color: "#9e2a1e" }}>
          <CloudIcona nome="allerta" size={14}/>{errore}
        </div>
      )}

      {/* coda di archiviazione */}
      {coda.length > 0 && <div style={{ marginBottom: 22 }}>{coda.map(schedaCoda)}</div>}

      {/* dropzone esplicita: il trascinamento deve VEDERSI, non solo esserci */}
      {sezione === "documenti" && !ricerca && (
        <div className="cloud-dropzone" role="button" tabIndex={0}
          onClick={() => fileRef.current && fileRef.current.click()}
          onKeyDown={e => { if (e.key === "Enter") fileRef.current && fileRef.current.click(); }}>
          <span className="cloud-ico cloud-ico--sm"><CloudIcona nome="carica" size={16}/></span>
          <span style={{ fontSize: 13.5, lineHeight: 1.5 }}>
            <strong>{it ? "Trascina qui i tuoi documenti" : "Drop your documents here"}</strong>
            {it ? " o clicca per sceglierli — l'AI li legge, trova le scadenze e li archivia da sola" : " or click to choose — the AI reads and files them automatically"}
          </span>
        </div>
      )}

      {/* prossime scadenze */}
      {sezione === "documenti" && !ricerca && vista === null && inScadenza.length > 0 && (
        <div className="cloud-card cloud-in" style={{ padding: "16px 20px", marginBottom: 22 }}>
          <div style={{ fontSize: 13.5, fontWeight: 600, marginBottom: 8 }}>{it ? "Prossime scadenze" : "Upcoming expiries"}</div>
          {inScadenza.slice(0, 4).map(d => (
            <div key={d.id} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10, padding: "7px 0", fontSize: 13.5 }}>
              <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.titolo}</span>
              {badge(d)}
            </div>
          ))}
        </div>
      )}

      {sezione === "documenti" && docs === null && <div style={{ fontSize: 13, color: "var(--ink-mute)" }}>{it ? "Caricamento…" : "Loading…"}</div>}

      {/* risultati di ricerca (piatti, con categoria) — nascosti mentre c'è una risposta AI:
          per una domanda ("quanto ho speso...") lo "0 risultati" confonde e basta */}
      {ricerca && docs !== null && !qa && (
        <div>
          <div style={{ fontSize: 13, color: "var(--ink-mute)", marginBottom: 10 }}>
            {(trovati || []).length} {(trovati || []).length === 1 ? (it ? "risultato per" : "result for") : (it ? "risultati per" : "results for")} “{cerca.trim()}”
          </div>
          <div className="cloud-card" style={{ padding: "6px 8px" }}>
            {(trovati || []).map(d => riga(d, true))}
            {(trovati || []).length === 0 && (
              <div style={{ padding: 22, fontSize: 13.5, color: "var(--ink-soft)" }}>{it ? "Nessun documento trovato." : "No documents found."}</div>
            )}
          </div>
        </div>
      )}

      {/* griglia cartelle */}
      {sezione === "documenti" && !ricerca && vista === null && docs !== null && (
        <div>
          {docs.length === 0 && !tabellaAssente && (
            <div className="cloud-card" style={{ padding: "34px 26px", textAlign: "center", marginBottom: 22 }}>
              <span className="cloud-ico" style={{ margin: "0 auto 12px" }}><CloudIcona nome="carica" size={20}/></span>
              <div style={{ fontSize: 15, fontWeight: 600 }}>{it ? "Inizia dal primo documento" : "Start with your first document"}</div>
              <div style={{ fontSize: 13.5, color: "var(--ink-mute)", marginTop: 6, maxWidth: "46ch", marginLeft: "auto", marginRight: "auto" }}>
                {it ? "Trascina qui la polizza o la licenza di navigazione: al resto pensa l'AI." : "Drop your insurance or navigation license here: the AI does the rest."}
              </div>
            </div>
          )}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(215px, 1fr))", gap: 12 }}>
            {TUTTE_CAT.map((c, i) => {
              const inCat = (docs || []).filter(d => d.categoria === c.k);
              const urgenti = inCat.filter(d => d.data_scadenza && giorni(d.data_scadenza) <= 30).length;
              return (
                <button key={c.k} type="button" className="cloud-folder cloud-in" onClick={() => setVista(c.k)} title={c.hint_it}
                  style={{ animationDelay: `${Math.min(i * 35, 350)}ms`, opacity: undefined }}>
                  <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 8 }}>
                    <span className="cloud-ico" style={{ opacity: inCat.length ? 1 : 0.55 }}><CloudIcona nome={c.ico} size={19}/></span>
                    {urgenti > 0 && <span className="cloud-badge cloud-badge--ambra">{urgenti} {it ? "in scadenza" : "expiring"}</span>}
                  </div>
                  <div style={{ fontSize: 14, fontWeight: 600, marginTop: 12, letterSpacing: "-0.01em", opacity: inCat.length ? 1 : 0.6 }}>
                    {it ? c.it : c.en}
                  </div>
                  <div style={{ fontSize: 12, color: "var(--ink-mute)", marginTop: 3 }}>
                    {inCat.length === 0 ? (it ? "vuota" : "empty") : inCat.length === 1 ? (it ? "1 documento" : "1 document") : `${inCat.length} ${it ? "documenti" : "documents"}`}
                  </div>
                </button>
              );
            })}
            <button type="button" className="cloud-folder cloud-folder--nuova cloud-in" onClick={nuovaCategoria}
              title={it ? "Crea una categoria tutta tua" : "Create your own category"}>
              <span style={{ fontSize: 26, lineHeight: 1 }}>+</span>
              <span style={{ fontSize: 13, fontWeight: 500 }}>{it ? "Nuova categoria" : "New category"}</span>
            </button>
          </div>
        </div>
      )}

      {/* cartella aperta */}
      {sezione === "spese" && <SpeseView client={client} it={it} versione={speseVer} apriFile={apriFile}/>}

      {sezione === "documenti" && !ricerca && vista !== null && docs !== null && (
        <div className="cloud-in">
          <button type="button" className="cloud-ghost" onClick={() => setVista(null)} style={{ marginBottom: 14, display: "inline-flex", alignItems: "center", gap: 6 }}>
            <CloudIcona nome="indietro" size={14}/>{it ? "Tutte le categorie" : "All categories"}
          </button>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
            <span className="cloud-ico"><CloudIcona nome={(catDi(vista) || {}).ico || "documento"} size={19}/></span>
            <div>
              <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: "-0.01em" }}>{catLabel(vista)}</div>
              <div style={{ fontSize: 12, color: "var(--ink-mute)" }}>
                {(docs || []).filter(d => d.categoria === vista).length === 1
                  ? (it ? "1 documento" : "1 document")
                  : `${(docs || []).filter(d => d.categoria === vista).length} ${it ? "documenti" : "documents"}`}
              </div>
            </div>
            {(catDi(vista) || {}).custom && (docs || []).filter(d => d.categoria === vista).length === 0 && (
              <button type="button" className="cloud-ghost" onClick={() => eliminaCategoria(catDi(vista))}
                style={{ marginLeft: "auto", border: "1px solid color-mix(in srgb, var(--rule) 90%, transparent)" }}>
                {it ? "Elimina categoria" : "Delete category"}
              </button>
            )}
          </div>
          <div className="cloud-card" style={{ padding: "6px 8px" }}>
            {(docs || []).filter(d => d.categoria === vista).map(d => riga(d, false))}
            {(docs || []).filter(d => d.categoria === vista).length === 0 && (
              <div style={{ padding: 22, fontSize: 13.5, color: "var(--ink-soft)" }}>
                {it ? "Vuota. Trascina qui un documento: se appartiene a un'altra categoria, l'AI lo sistemerà comunque al posto giusto." : "Empty. Drop a document here: the AI will file it where it belongs."}
              </div>
            )}
          </div>
        </div>
      )}

      {/* disclaimer legale: copie di cortesia, mai sostitutive degli originali a bordo */}
      <div style={{ fontSize: 12, color: "var(--ink-faint)", lineHeight: 1.7, marginTop: 30, paddingTop: 16, borderTop: "1px solid var(--rule)", maxWidth: "78ch" }}>
        {it
          ? "Le copie conservate su SeaVin Cloud sono copie di cortesia a fini organizzativi: non sostituiscono gli originali né le copie autentiche che la legge richiede di tenere a bordo (art. 23 cod. nautica da diporto). Le scadenze lette dall'AI sono un aiuto alla compilazione e vanno sempre verificate sul documento."
          : "Copies stored on SeaVin Cloud are courtesy copies for organisational purposes: they do not replace the originals or certified copies the law requires to be kept on board. AI-read expiry dates are an aid and must always be checked against the document."}
      </div>
    </div>
  );
}

/* ── Vista Spese: registro auto-compilato dalle fatture + grafico annuale ──
   Le righe nascono da sole quando l'AI riconosce una fattura all'upload
   (salva() in CloudDocs) o a mano dal modulo in fondo. Le voci su cui l'AI
   era incerta arrivano con da_confermare=true e si confermano qui. */
const SPESE_CATS = [
  ["ormeggio", "Ormeggio"], ["manutenzione", "Manutenzione"], ["assicurazione", "Assicurazione"],
  ["carburante", "Carburante"], ["cantiere", "Cantiere"], ["tasse", "Tasse"],
  ["accessori", "Accessori"], ["altro", "Altro"],
];
const MESI_IT = ["gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic"];

function SpeseView({ client, it, versione, apriFile }) {
  const [righe, setRighe] = useState(null);   // null = caricamento
  const [errore, setErrore] = useState(null);
  const [anno, setAnno] = useState(new Date().getFullYear());
  const [nuova, setNuova] = useState({ data: "", fornitore: "", importo: "", categoria: "altro" });
  const [busy, setBusy] = useState(false);
  const annoToccato = useRef(false); // se l'utente sceglie un anno, non glielo cambiamo più noi

  const carica = async () => {
    try {
      const { data, error } = await client.from("spese")
        .select("*, documenti(titolo, file_path)")
        .order("data", { ascending: false });
      if (error) throw error;
      setErrore(null); setRighe(data || []);
      // una fattura vecchia (es. 2025) finirebbe sotto un anno che non stai guardando:
      // se l'anno corrente è vuoto ma ci sono spese altrove, la vista salta lì da sola
      if (!annoToccato.current && data && data.length) {
        const anniConDati = data.map(r => Number((r.data || "").slice(0, 4))).filter(Boolean);
        if (anniConDati.length && !anniConDati.includes(anno)) setAnno(Math.max(...anniConDati));
      }
      try { localStorage.setItem("seavin.cloud.spese", JSON.stringify(data || [])); } catch (e) {}
    } catch (e) {
      let snap = null;
      try { snap = JSON.parse(localStorage.getItem("seavin.cloud.spese") || "null"); } catch (e2) {}
      if (snap && !navigator.onLine) { setErrore(null); setRighe(snap); }
      else { setErrore(e.message || String(e)); setRighe([]); }
    }
  };
  useEffect(() => { carica(); /* eslint-disable-next-line */ }, [versione]);

  const eur = (n) => "€ " + Number(n || 0).toLocaleString("it-IT", { minimumFractionDigits: Number(n) % 1 ? 2 : 0, maximumFractionDigits: 2 });
  const catSpesaLabel = (k) => (SPESE_CATS.find(c => c[0] === k) || ["", k])[1];

  const schemaAssente = errore && /spese|relation|schema|permission/i.test(errore) && (righe || []).length === 0;
  const oggi = new Date();
  const anni = Array.from(new Set([oggi.getFullYear(), ...(righe || []).map(r => Number((r.data || "").slice(0, 4))).filter(Boolean)])).sort((a, b) => b - a);
  const delAnno = (righe || []).filter(r => (r.data || "").slice(0, 4) === String(anno));
  const confermate = delAnno.filter(r => !r.da_confermare);
  const sospese = delAnno.filter(r => r.da_confermare);
  const totale = confermate.reduce((s, r) => s + Number(r.importo || 0), 0);
  const mesiTrascorsi = anno === oggi.getFullYear() ? oggi.getMonth() + 1 : 12;
  const perMese = MESI_IT.map((_, i) => confermate
    .filter(r => Number((r.data || "").slice(5, 7)) === i + 1)
    .reduce((s, r) => s + Number(r.importo || 0), 0));
  const maxMese = Math.max(1, ...perMese);
  const perCat = SPESE_CATS
    .map(([k, label]) => [label, confermate.filter(r => r.categoria === k).reduce((s, r) => s + Number(r.importo || 0), 0)])
    .filter(([, v]) => v > 0)
    .sort((a, b) => b[1] - a[1]);

  const conferma = async (r, valore) => {
    const imp = Number(String(valore).replace(",", "."));
    if (!imp || imp <= 0) return;
    await client.from("spese").update({ importo: imp, da_confermare: false }).eq("id", r.id);
    await carica();
  };
  const eliminaSpesa = async (r) => {
    if (!window.confirm(it ? "Eliminare questa voce di spesa?" : "Delete this expense?")) return;
    await client.from("spese").delete().eq("id", r.id);
    await carica();
  };
  const aggiungi = async () => {
    const imp = Number(String(nuova.importo).replace(",", "."));
    if (!imp || imp <= 0) return;
    setBusy(true);
    try {
      const { data: userData } = await client.auth.getUser();
      const uid = userData && userData.user && userData.user.id;
      if (!uid) throw new Error(it ? "Sessione scaduta: rientra." : "Session expired.");
      const esito = await client.from("spese").insert({
        user_id: uid, data: nuova.data || new Date().toISOString().slice(0, 10),
        fornitore: nuova.fornitore.trim().slice(0, 120) || null,
        categoria: nuova.categoria, importo: Math.round(imp * 100) / 100,
      });
      if (esito.error) throw esito.error;
      setNuova({ data: "", fornitore: "", importo: "", categoria: "altro" });
      await carica();
    } catch (e) { setErrore(e.message || String(e)); }
    setBusy(false);
  };

  const rigaSpesa = (r) => (
    <div key={r.id} className="cloud-row" style={r.da_confermare ? { background: "color-mix(in srgb, #a6501f 6%, transparent)" } : undefined}>
      <span style={{ fontSize: 12, color: "var(--ink-mute)", width: 44, flexShrink: 0 }}>
        {r.data ? `${r.data.slice(8, 10)}/${r.data.slice(5, 7)}` : "—"}
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14, fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
          {r.fornitore || (it ? "Spesa" : "Expense")}{r.descrizione ? ` — ${r.descrizione}` : ""}
        </div>
        <div style={{ fontSize: 12, color: "var(--ink-mute)", marginTop: 1 }}>
          {catSpesaLabel(r.categoria)}
          {r.da_confermare ? ` · ${it ? "importo da confermare" : "amount to confirm"}` : ""}
        </div>
      </div>
      {r.da_confermare ? (
        <input type="text" inputMode="decimal" defaultValue={r.importo} className="cloud-input"
          onBlur={e => conferma(r, e.target.value)}
          onKeyDown={e => { if (e.key === "Enter") e.target.blur(); }}
          title={it ? "Conferma l'importo (Invio)" : "Confirm the amount (Enter)"}
          style={{ width: 96, padding: "6px 10px", fontSize: 13, textAlign: "right", borderColor: "#a6501f" }}/>
      ) : (
        <span style={{ fontSize: 14, fontWeight: 600, whiteSpace: "nowrap" }}>{eur(r.importo)}</span>
      )}
      {r.documenti && r.documenti.file_path && (
        <button type="button" className="cloud-iconbtn" onClick={() => apriFile(r.documenti.file_path)}
          title={r.documenti.titolo || (it ? "Apri la fattura" : "Open the invoice")} aria-label={it ? "Apri la fattura" : "Open invoice"}>
          <CloudIcona nome="apri" size={15}/>
        </button>
      )}
      <button type="button" className="cloud-iconbtn cloud-iconbtn--rosso" onClick={() => eliminaSpesa(r)}
        title={it ? "Elimina" : "Delete"} aria-label={it ? "Elimina" : "Delete"}>
        <CloudIcona nome="cestino" size={15}/>
      </button>
    </div>
  );

  if (righe === null) return <div style={{ fontSize: 13, color: "var(--ink-mute)" }}>{it ? "Caricamento…" : "Loading…"}</div>;

  if (schemaAssente) {
    return (
      <div className="cloud-card" style={{ padding: 24, fontSize: 13.5, color: "var(--ink-soft)", lineHeight: 1.6 }}>
        {it ? "La vista Spese si attiva con l'aggiornamento dello schema: esegui di nuovo db/cloud_schema.sql nel SQL Editor di Supabase (è rilanciabile senza danni)." : "The Expenses view needs the updated schema: re-run db/cloud_schema.sql in the Supabase SQL Editor."}
      </div>
    );
  }

  return (
    <div className="cloud-in">
      <div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 12 }}>
        <select value={anno} className="cloud-select" style={{ width: "auto", padding: "7px 10px", fontSize: 13 }}
          onChange={e => { annoToccato.current = true; setAnno(Number(e.target.value)); }}>
          {anni.map(a => <option key={a} value={a}>{a}</option>)}
        </select>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 12, marginBottom: 14 }}>
        <div className="cloud-card" style={{ padding: "14px 18px" }}>
          <div style={{ fontSize: 12.5, color: "var(--ink-mute)" }}>{anno === oggi.getFullYear() ? (it ? "Quest'anno" : "This year") : anno}</div>
          <div style={{ fontFamily: "var(--font-display)", fontSize: 26, letterSpacing: "-0.02em", marginTop: 2 }}>{eur(totale)}</div>
        </div>
        <div className="cloud-card" style={{ padding: "14px 18px" }}>
          <div style={{ fontSize: 12.5, color: "var(--ink-mute)" }}>{it ? "Media al mese" : "Monthly average"}</div>
          <div style={{ fontFamily: "var(--font-display)", fontSize: 26, letterSpacing: "-0.02em", marginTop: 2 }}>{eur(totale / mesiTrascorsi)}</div>
        </div>
        <div className="cloud-card" style={{ padding: "14px 18px" }}>
          <div style={{ fontSize: 12.5, color: "var(--ink-mute)" }}>{it ? "Voci registrate" : "Entries"}</div>
          <div style={{ fontFamily: "var(--font-display)", fontSize: 26, letterSpacing: "-0.02em", marginTop: 2 }}>
            {delAnno.length}{sospese.length > 0 && <span style={{ fontSize: 13, color: "#a6501f" }}> · {sospese.length} {it ? "da confermare" : "pending"}</span>}
          </div>
        </div>
      </div>

      <div className="cloud-card" style={{ padding: "16px 20px", marginBottom: 14 }}>
        <div style={{ fontSize: 13, color: "var(--ink-mute)", marginBottom: 10 }}>{it ? "Spesa per mese" : "Spend by month"}</div>
        <div style={{ display: "flex", alignItems: "flex-end", gap: 6, height: 110 }}>
          {perMese.map((v, i) => {
            const corrente = anno === oggi.getFullYear() && i === oggi.getMonth();
            return (
              <div key={i} className="cloud-mese" style={{ flex: 1, display: "flex", flexDirection: "column", justifyContent: "flex-end", alignItems: "center", gap: 4, minWidth: 0, position: "relative" }}>
                <span className="cloud-tip">{MESI_IT[i]} · {eur(v)}</span>
                <div className="cloud-mese__barra" style={{ width: "100%", height: Math.max(2, Math.round(v / maxMese * 88)), borderRadius: "3px 3px 0 0", background: v === 0 ? "var(--rule)" : corrente ? "var(--accent-deep)" : "color-mix(in srgb, var(--ink) 22%, transparent)", transition: "height .3s ease" }}></div>
                <span style={{ fontSize: 10.5, color: corrente ? "var(--accent-deep)" : "var(--ink-faint)", fontWeight: corrente ? 600 : 400 }}>{MESI_IT[i]}</span>
              </div>
            );
          })}
        </div>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: 12, marginBottom: 14 }}>
        <div className="cloud-card" style={{ padding: "16px 20px" }}>
          <div style={{ fontSize: 13, color: "var(--ink-mute)", marginBottom: 10 }}>{it ? "Per categoria" : "By category"}</div>
          {perCat.length === 0 && <div style={{ fontSize: 13, color: "var(--ink-soft)" }}>{it ? "Ancora nessuna spesa quest'anno." : "No expenses yet this year."}</div>}
          <div style={{ display: "flex", flexDirection: "column", gap: 9, fontSize: 13 }}>
            {perCat.map(([label, v]) => (
              <div key={label}>
                <div style={{ display: "flex", justifyContent: "space-between" }}><span>{label}</span><span style={{ fontWeight: 600 }}>{eur(v)}</span></div>
                <div style={{ height: 6, background: "color-mix(in srgb, var(--ink) 6%, transparent)", borderRadius: 3, marginTop: 3 }}>
                  <div style={{ width: `${Math.max(2, Math.round(v / totale * 100))}%`, height: 6, background: "var(--accent-deep)", borderRadius: 3 }}></div>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="cloud-card" style={{ padding: "10px 12px" }}>
          <div style={{ fontSize: 13, color: "var(--ink-mute)", padding: "6px 8px" }}>{it ? "Ultime spese" : "Latest expenses"}</div>
          {delAnno.length === 0 && (
            <div style={{ padding: "14px 8px", fontSize: 13, color: "var(--ink-soft)", lineHeight: 1.6 }}>
              {it ? "Trascina una fattura nell'archivio: importo e categoria si registrano da soli." : "Drop an invoice in the vault: amount and category are logged automatically."}
            </div>
          )}
          {[...sospese, ...confermate].slice(0, 8).map(rigaSpesa)}
        </div>
      </div>

      <div className="cloud-card" style={{ padding: "14px 18px" }}>
        <div style={{ fontSize: 13, color: "var(--ink-mute)", marginBottom: 10 }}>{it ? "Aggiungi a mano" : "Add manually"}</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 10, alignItems: "center" }}>
          <input type="date" value={nuova.data} className="cloud-input" onChange={e => setNuova({ ...nuova, data: e.target.value })}/>
          <input value={nuova.fornitore} placeholder={it ? "Fornitore" : "Supplier"} className="cloud-input" onChange={e => setNuova({ ...nuova, fornitore: e.target.value })}/>
          <input value={nuova.importo} placeholder="€" inputMode="decimal" className="cloud-input" style={{ textAlign: "right" }} onChange={e => setNuova({ ...nuova, importo: e.target.value })}/>
          <select value={nuova.categoria} className="cloud-select" onChange={e => setNuova({ ...nuova, categoria: e.target.value })}>
            {SPESE_CATS.map(([k, label]) => <option key={k} value={k}>{label}</option>)}
          </select>
          <button type="button" className="cloud-btn" onClick={aggiungi} disabled={busy || !Number(String(nuova.importo).replace(",", "."))}>
            {it ? "Registra" : "Add"}
          </button>
        </div>
      </div>
      {errore && !schemaAssente && (
        <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 12, fontSize: 12.5, color: "#9e2a1e" }}>
          <CloudIcona nome="allerta" size={14}/>{errore}
        </div>
      )}
    </div>
  );
}

/* ── Le mie barche: profilo + "oggi vale…" (stima AI di mercato) ── */
function BarcheView({ it, reports }) {
  const client = window.SeaVAuth && window.SeaVAuth.client;
  const [barche, setBarche] = useState(null);
  const [errore, setErrore] = useState(null);
  const [nuova, setNuova] = useState({ nome: "", cantiere: "", modello: "", anno: "", lunghezza: "" });
  const [busy, setBusy] = useState(false);
  const [valutando, setValutando] = useState(null); // id della barca in valutazione
  const [abbonato, setAbbonato] = useState(CLOUD_ANTEPRIMA); // la rivalutazione periodica è dell'abbonamento Cloud
  useEffect(() => {
    if (CLOUD_ANTEPRIMA) return; // test: tutti abbonati (⚠ go-live: togliere il flag)
    (async () => {
      try {
        const { data } = await client.from("abbonamenti").select("attivo,scade_il");
        const ab = data && data[0];
        setAbbonato(!!(ab && ab.attivo && (!ab.scade_il || new Date(ab.scade_il) > new Date())));
      } catch (e) { setAbbonato(false); }
    })();
    // eslint-disable-next-line
  }, []);

  const carica = async () => {
    try {
      const { data, error } = await client.from("barche").select("*").order("creato_il", { ascending: false });
      if (error) throw error;
      setErrore(null); setBarche(data || []);
    } catch (e) { setErrore(e.message || String(e)); setBarche([]); }
  };
  useEffect(() => { carica(); /* eslint-disable-next-line */ }, []);

  const eur = (n) => "€ " + Math.round(Number(n)).toLocaleString("it-IT");
  const schemaAssente = errore && /barche|relation|schema|permission/i.test(errore) && (barche || []).length === 0;
  const giorniDa = (ts) => ts ? Math.floor((Date.now() - new Date(ts).getTime()) / 86400000) : null;

  /* Le barche dei report richiesti entrano da sole (una volta): dedup per
     report_id e per cantiere+modello; se eliminata, non torna (localStorage). */
  useEffect(() => {
    if (!reports || !reports.length || barche === null || schemaAssente) return;
    (async () => {
      let fatte = [];
      try { fatte = JSON.parse(localStorage.getItem("seavin.barche.importate") || "[]"); } catch (e) {}
      const candidati = reports.filter(r => r.builder && r.model && !fatte.includes(r.id)
        && !barche.some(x => x.report_id === r.id
          || `${x.cantiere} ${x.modello}`.toLowerCase() === `${r.builder} ${r.model}`.toLowerCase()));
      if (!candidati.length) return;
      const { data: u } = await client.auth.getUser();
      const uid = u && u.user && u.user.id;
      if (!uid) return;
      let aggiunta = null;
      for (const r of candidati.slice(0, 3)) {
        const ins = await client.from("barche").insert({
          user_id: uid, cantiere: r.builder, modello: r.model,
          anno: Number(r.year) >= 1900 ? Number(r.year) : null, report_id: r.id,
        }).select("*").single();
        if (!ins.error) { fatte.push(r.id); if (!aggiunta) aggiunta = ins.data; }
        else if (/report_id|barche|relation/i.test(ins.error.message || "")) return; // schema vecchio: niente import
      }
      try { localStorage.setItem("seavin.barche.importate", JSON.stringify(fatte)); } catch (e) {}
      await carica();
      if (aggiunta) valuta(aggiunta); // la prima stima è inclusa
    })();
    // eslint-disable-next-line
  }, [reports, barche === null]);

  const valuta = async (b, tentativo = 0) => {
    if (valutando && tentativo === 0) return; // una stima alla volta: le altre si mettono in fila da sole
    setValutando(b.id); setErrore(null);
    try {
      const token = window.SeaVAuth?.getAccessToken ? await window.SeaVAuth.getAccessToken() : null;
      const res = await fetch(`${API}/api/boat-value`, {
        method: "POST",
        headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
        body: JSON.stringify({ barca_id: b.id, cantiere: b.cantiere, modello: b.modello, anno: b.anno, lunghezza: b.lunghezza_m }),
      });
      const d = await res.json().catch(() => ({}));
      // paracadute anti-costi del server (2 stime/min): non è un errore da mostrare,
      // si aspetta un minuto e si riprova da soli — la card resta "in lettura"
      if (res.status === 429 && String(d.error || "").includes("di fila") && tentativo < 2) {
        setTimeout(() => valuta(b, tentativo + 1), 65000);
        return;
      }
      if (!res.ok) throw new Error(d.error || (it ? "Stima non riuscita, riprova." : "Valuation failed."));
      const agg = await client.from("barche").update({
        valore_min: d.valore_min, valore_max: d.valore_max,
        valore_nota: d.nota || null, valore_valutato_il: d.valutato_il,
      }).eq("id", b.id);
      if (agg.error) throw agg.error;
      await carica();
    } catch (e) { setErrore(e.message || String(e)); }
    setValutando(null);
  };

  const aggiungi = async () => {
    if (!nuova.cantiere.trim() || !nuova.modello.trim()) return;
    setBusy(true); setErrore(null);
    try {
      const { data: userData } = await client.auth.getUser();
      const uid = userData && userData.user && userData.user.id;
      if (!uid) throw new Error(it ? "Sessione scaduta: rientra." : "Session expired.");
      const ins = await client.from("barche").insert({
        user_id: uid,
        nome: nuova.nome.trim().slice(0, 80) || null,
        cantiere: nuova.cantiere.trim().slice(0, 80),
        modello: nuova.modello.trim().slice(0, 80),
        anno: Number(nuova.anno) >= 1900 ? Number(nuova.anno) : null,
        lunghezza_m: Number(String(nuova.lunghezza).replace(",", ".")) > 0 ? Number(String(nuova.lunghezza).replace(",", ".")) : null,
      }).select("*").single();
      if (ins.error) {
        // la regola vera vive nel database: la policy rifiuta senza abbonamento o oltre le 3 manuali
        if (/row-level security|violates/i.test(ins.error.message || "")) {
          throw new Error(it ? "L'aggiunta manuale richiede l'abbonamento Cloud attivo (massimo 3 barche)." : "Manual boats require an active Cloud plan (max 3).");
        }
        throw ins.error;
      }
      setNuova({ nome: "", cantiere: "", modello: "", anno: "", lunghezza: "" });
      await carica();
      if (ins.data) valuta(ins.data); // appena aggiunta, la stima parte da sola
    } catch (e) { setErrore(e.message || String(e)); }
    setBusy(false);
  };

  const eliminaBarca = async (b) => {
    if (!window.confirm(it ? `Eliminare "${b.nome || `${b.cantiere} ${b.modello}`}"?` : "Delete this boat?")) return;
    await client.from("barche").delete().eq("id", b.id);
    await carica();
  };

  if (barche === null) return <div style={{ fontSize: 13, color: "var(--ink-mute)" }}>{it ? "Caricamento…" : "Loading…"}</div>;

  if (schemaAssente) {
    return (
      <div className="cloud-card" style={{ padding: 24, fontSize: 13.5, color: "var(--ink-soft)", lineHeight: 1.6, maxWidth: 640 }}>
        {it ? "Le mie barche si attiva con l'aggiornamento dello schema: esegui di nuovo db/cloud_schema.sql nel SQL Editor di Supabase (rilanciabile senza danni)." : "My boats needs the updated schema: re-run db/cloud_schema.sql in the Supabase SQL Editor."}
      </div>
    );
  }

  return (
    <div className="cloud-in" style={{ maxWidth: 980 }}>
      {barche.length === 0 && (
        <div className="cloud-card" style={{ padding: "38px 26px", textAlign: "center", marginBottom: 14 }}>
          <span className="cloud-ico" style={{ margin: "0 auto 12px" }}><CloudIcona nome="nave" size={20}/></span>
          <div style={{ fontSize: 15, fontWeight: 600 }}>{it ? "Le tue barche compariranno qui" : "Your boats will appear here"}</div>
          <div style={{ fontSize: 13.5, color: "var(--ink-mute)", marginTop: 6, maxWidth: "48ch", marginLeft: "auto", marginRight: "auto" }}>
            {it ? "Le barche dei report che richiedi entrano da sole, con la stima di quanto valgono oggi. Con l'abbonamento Cloud puoi aggiungerne a mano fino a 3." : "Boats from your reports appear automatically with today's market value. With the Cloud plan you can add up to 3 manually."}
          </div>
        </div>
      )}

      {barche.map(b => {
        const gg = giorniDa(b.valore_valutato_il);
        const rivalutabile = gg === null || gg >= 7;
        return (
          <div key={b.id} className="cloud-card cloud-in" style={{ padding: "20px 24px", marginBottom: 14 }}>
            <div style={{ display: "flex", alignItems: "flex-start", gap: 14, flexWrap: "wrap" }}>
              <span className="cloud-ico"><CloudIcona nome="nave" size={19}/></span>
              <div style={{ flex: 1, minWidth: 220 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                  <span style={{ fontSize: 16, fontWeight: 600, letterSpacing: "-0.01em" }}>{b.nome || `${b.cantiere} ${b.modello}`}</span>
                  {b.report_id && <span className="cloud-badge cloud-badge--neutra">{it ? "dal report" : "from report"}</span>}
                </div>
                <div style={{ fontSize: 12.5, color: "var(--ink-mute)", marginTop: 2 }}>
                  {b.cantiere} {b.modello}{b.anno ? ` · ${b.anno}` : ""}{b.lunghezza_m ? ` · ${b.lunghezza_m} m` : ""}{b.hin ? ` · ${b.hin}` : ""}
                </div>
              </div>
              <button type="button" className="cloud-iconbtn cloud-iconbtn--rosso" onClick={() => eliminaBarca(b)}
                title={it ? "Elimina" : "Delete"} aria-label={it ? "Elimina" : "Delete"}>
                <CloudIcona nome="cestino" size={15}/>
              </button>
            </div>

            <div style={{ marginTop: 16, paddingTop: 16, borderTop: "1px solid color-mix(in srgb, var(--rule) 55%, transparent)" }}>
              {valutando === b.id ? (
                <span className="cloud-pulse" style={{ display: "inline-flex", alignItems: "center", gap: 7, fontSize: 13, color: "var(--accent-deep)" }}>
                  <CloudIcona nome="scintilla" size={14}/>{it ? "Sto guardando gli annunci comparabili…" : "Checking comparable listings…"}
                </span>
              ) : b.valore_min ? (
                <div style={{ display: "flex", alignItems: "baseline", gap: 14, flexWrap: "wrap" }}>
                  <div>
                    <div style={{ fontSize: 12, color: "var(--ink-mute)", display: "flex", alignItems: "center", gap: 5 }}>
                      <CloudIcona nome="scintilla" size={12}/>{it ? "Oggi vale circa" : "Worth today"}
                    </div>
                    <div style={{ fontFamily: "var(--font-display)", fontSize: 30, letterSpacing: "-0.02em", color: "var(--accent-deep)", marginTop: 2 }}>
                      {eur(b.valore_min)} – {eur(b.valore_max)}
                    </div>
                    {b.valore_nota && <div style={{ fontSize: 12.5, color: "var(--ink-mute)", marginTop: 4, maxWidth: "58ch", lineHeight: 1.5 }}>{b.valore_nota}</div>}
                    <div style={{ fontSize: 11.5, color: "var(--ink-faint)", marginTop: 4 }}>
                      {it ? `stima AI dagli annunci correnti · aggiornata ${gg === 0 ? "oggi" : gg === 1 ? "ieri" : `${gg} giorni fa`}` : `AI market estimate · updated ${gg}d ago`}
                    </div>
                  </div>
                  {abbonato ? (
                    <span className="cloud-tipwrap">
                      {!rivalutabile && (() => {
                        // countdown preciso: il title nativo sui bottoni disabilitati non appare
                        const ms = new Date(b.valore_valutato_il).getTime() + 7 * 86400000 - Date.now();
                        const g = Math.floor(ms / 86400000);
                        const h = Math.floor(ms / 3600000) % 24;
                        const testo = g >= 1
                          ? (it ? `Rivalutabile tra ${g} ${g === 1 ? "giorno" : "giorni"}${h ? ` e ${h} ore` : ""}` : `Available in ${g}d ${h}h`)
                          : (it ? `Rivalutabile tra ${Math.max(1, Math.floor(ms / 3600000))} ore` : `Available in ${Math.max(1, Math.floor(ms / 3600000))}h`);
                        return <span className="cloud-tip">{testo}</span>;
                      })()}
                      <button type="button" className="cloud-ghost" disabled={!rivalutabile}
                        onClick={() => valuta(b)}
                        style={{ border: "1px solid color-mix(in srgb, var(--rule) 90%, transparent)", opacity: rivalutabile ? 1 : 0.45 }}>
                        {it ? "Rivaluta" : "Revalue"}
                      </button>
                    </span>
                  ) : (
                    <a href="pricing.html" className="cloud-ghost"
                      title={it ? "La rivalutazione periodica fa parte dell'abbonamento Cloud (4,99 €/mese)" : "Periodic revaluation is part of the Cloud plan"}
                      style={{ border: "1px solid color-mix(in srgb, var(--rule) 90%, transparent)", textDecoration: "none" }}>
                      <CloudIcona nome="lucchetto" size={13}/>{it ? "Rivaluta · con Cloud" : "Revalue · with Cloud"}
                    </a>
                  )}
                </div>
              ) : (
                <button type="button" className="cloud-btn" onClick={() => valuta(b)}>
                  <CloudIcona nome="scintilla" size={14}/>{it ? "Quanto vale oggi?" : "What's it worth?"}
                </button>
              )}
            </div>
          </div>
        );
      })}

      {(() => {
        const manuali = (barche || []).filter(b => !b.report_id).length;
        if (!abbonato) return (
          <div className="cloud-card" style={{ padding: "16px 20px", display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
            <span className="cloud-ico cloud-ico--sm"><CloudIcona nome="lucchetto" size={16}/></span>
            <span style={{ flex: 1, minWidth: 240, fontSize: 13.5, color: "var(--ink-soft)", lineHeight: 1.55 }}>
              {it ? "Le barche dei report che richiedi compaiono qui da sole. Con l'abbonamento Cloud puoi aggiungerne a mano fino a 3." : "Boats from your reports appear here automatically. With the Cloud plan you can add up to 3 manually."}
            </span>
            <a href="pricing.html" className="cloud-ghost" style={{ textDecoration: "none", border: "1px solid color-mix(in srgb, var(--rule) 90%, transparent)" }}>
              {it ? "Scopri Cloud" : "See plans"}
            </a>
          </div>
        );
        return (
          <div className="cloud-card" style={{ padding: "16px 20px" }}>
            <div style={{ display: "flex", justifyContent: "space-between", gap: 10, marginBottom: 10 }}>
              <span style={{ fontSize: 13, color: "var(--ink-mute)" }}>{it ? "Aggiungi una barca" : "Add a boat"}</span>
              <span style={{ fontSize: 12, color: manuali >= 3 ? "#a6501f" : "var(--ink-faint)" }}>{manuali}/3 {it ? "manuali" : "manual"}</span>
            </div>
            {manuali >= 3 ? (
              <div style={{ fontSize: 13, color: "var(--ink-soft)" }}>
                {it ? "Hai raggiunto le 3 barche aggiunte a mano: eliminane una per inserirne un'altra." : "You've reached the 3 manual boats: delete one to add another."}
              </div>
            ) : (
              <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 10 }}>
                <input value={nuova.cantiere} placeholder={it ? "Cantiere *" : "Builder *"} className="cloud-input" onChange={e => setNuova({ ...nuova, cantiere: e.target.value })}/>
                <input value={nuova.modello} placeholder={it ? "Modello *" : "Model *"} className="cloud-input" onChange={e => setNuova({ ...nuova, modello: e.target.value })}/>
                <input value={nuova.anno} placeholder={it ? "Anno" : "Year"} inputMode="numeric" className="cloud-input" onChange={e => setNuova({ ...nuova, anno: e.target.value })}/>
                <input value={nuova.lunghezza} placeholder={it ? "Lunghezza (m)" : "Length (m)"} inputMode="decimal" className="cloud-input" onChange={e => setNuova({ ...nuova, lunghezza: e.target.value })}/>
                <input value={nuova.nome} placeholder={it ? "Nome (opz.)" : "Name (opt.)"} className="cloud-input" onChange={e => setNuova({ ...nuova, nome: e.target.value })}/>
                <button type="button" className="cloud-btn" onClick={aggiungi} disabled={busy || !nuova.cantiere.trim() || !nuova.modello.trim()}>
                  {busy ? (it ? "Aggiungo…" : "Adding…") : (it ? "Aggiungi" : "Add")}
                </button>
              </div>
            )}
          </div>
        );
      })()}

      {errore && !schemaAssente && (
        <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 12, fontSize: 12.5, color: "#9e2a1e" }}>
          <CloudIcona nome="allerta" size={14}/>{errore}
        </div>
      )}

      <div style={{ fontSize: 12, color: "var(--ink-faint)", lineHeight: 1.7, marginTop: 22, paddingTop: 14, borderTop: "1px solid var(--rule)", maxWidth: "76ch" }}>
        {it
          ? "Il valore è una stima indicativa generata dall'AI sui prezzi richiesti negli annunci correnti di esemplari comparabili: non è una perizia, non considera le condizioni reali della tua barca e non costituisce un'offerta. Per un valore certificato serve una perizia."
          : "The value is an indicative AI estimate based on asking prices of comparable current listings: it is not a survey, does not reflect your boat's actual condition and is not an offer."}
      </div>
    </div>
  );
}

/* ========== Surveyor profile panel ========== */
function SurveyorPanel({ app, setApp, it }) {
  const [editing, setEditing] = useState(false);
  if (!app) {
    return (
      <div className="dash-perito-empty">
        <span className="eyebrow">{it ? "Nessuna candidatura" : "No application"}</span>
        <h2 style={{ fontSize: "var(--step-3)", letterSpacing: "-0.02em", lineHeight: 1, marginTop: 14 }}>
          {it ? "Diventa parte della" : "Join the"} <em style={{ fontStyle: "italic", color: "var(--accent-deep)" }}>{it ? "rete." : "network."}</em>
        </h2>
        <p style={{ color: "var(--ink-soft)", marginTop: 14, lineHeight: 1.55, maxWidth: "52ch" }}>
          {it
            ? "Compila la candidatura: foto, città base, iscrizione CCIAA, specializzazioni, strumentazione e tariffa. Ti rispondiamo in 3 giorni."
            : "Fill in the application: photo, home port, CCIAA, specialties, instruments and fee. We reply within 3 days."}
        </p>
        <a href="surveyor-signup.html" className="btn btn--accent" style={{ marginTop: 20 }}>{it ? "Compila candidatura" : "Open application"} →</a>
      </div>
    );
  }

  const statusInfo = {
    pending:  { l: it ? "In verifica"  : "Under review", tone: "accent",  desc: it ? "Il team SeaVin sta verificando CCIAA, polizza RC e referenze. In media 3 giorni lavorativi." : "The SeaVin team is verifying CCIAA, insurance and references. ~3 business days." },
    approved: { l: it ? "Approvata"    : "Approved",     tone: "ok",      desc: it ? "Il tuo profilo è pubblico nella rete SeaVin. Modificalo qui sotto quando vuoi." : "Your profile is live in the SeaVin network. Edit it below anytime." },
    rejected: { l: it ? "Non approvata": "Rejected",     tone: "warn",    desc: it ? "Purtroppo non abbiamo potuto approvare la candidatura. Riapri o scrivi a hello@seavin.it." : "We couldn't approve this application. Reopen or contact hello@seavin.it." },
  }[app.status];

  return (
    <div className="dash-perito">

      {/* Status hero */}
      <div className={`dash-perito__status dash-perito__status--${statusInfo.tone}`}>
        <div className="dash-perito__status-photo">
          {app.photoDataUrl
            ? <img src={app.photoDataUrl} alt={`${app.firstName} ${app.lastName}`}/>
            : <div className="adm__avatar adm__avatar--lg"><span className="mono">{(app.firstName[0] || "?") + (app.lastName[0] || "")}</span></div>}
        </div>
        <div className="dash-perito__status-main">
          <div className="mono" style={{ fontSize: 11, letterSpacing: "0.18em", color: "var(--ink-mute)" }}>§ ID · {app.id}</div>
          <h2 style={{ fontSize: "var(--step-3)", letterSpacing: "-0.02em", lineHeight: 1, margin: "10px 0 0" }}>
            {app.firstName} <em style={{ fontStyle: "italic", color: "var(--accent-deep)" }}>{app.lastName}</em>
          </h2>
          <div className="mono" style={{ fontSize: 12, color: "var(--ink-mute)", letterSpacing: "0.1em", marginTop: 8, textTransform: "uppercase" }}>
            {app.city} · {it ? "raggio" : "radius"} {app.radiusKm} km · {app.yearsExperience} {it ? "anni" : "years"}
          </div>
          <div className={`adm__pill adm__pill--${app.status}`} style={{ marginTop: 16 }}>
            ● {statusInfo.l}
          </div>
          <p style={{ marginTop: 14, color: "var(--ink-soft)", lineHeight: 1.55, maxWidth: "60ch" }}>{statusInfo.desc}</p>
        </div>
      </div>

      {/* Edit toggle / read-only / editor */}
      {app.status !== "approved" && <SurveyorReadonly app={app} it={it}/>}
      {app.status === "approved" && !editing && (
        <SurveyorReadonly app={app} it={it} onEdit={() => setEditing(true)}/>
      )}
      {app.status === "approved" && editing && (
        <SurveyorEditor
          app={app}
          it={it}
          onCancel={() => setEditing(false)}
          onSave={(updated) => {
            const list = loadAppsDash().map(a => a.id === updated.id ? updated : a);
            saveAppsDash(list);
            const approved = loadApprovedDash();
            const without = approved.filter(x => x.id !== updated.id);
            saveApprovedDash([{
              id: updated.id,
              name: `${updated.firstName} ${updated.lastName}`,
              city: updated.city,
              radiusKm: updated.radiusKm,
              specialties: updated.specialties,
              instrumentation: updated.instrumentation,
              yearsExperience: updated.yearsExperience,
              baseRate: updated.baseRate,
              photoDataUrl: updated.photoDataUrl,
              bio: updated.bio,
              cciaaNumber: updated.cciaaNumber,
              approvedAt: updated.reviewedAt,
            }, ...without]);
            setApp(updated);
            setEditing(false);
          }}
        />
      )}
    </div>
  );
}

function SurveyorReadonly({ app, it, onEdit }) {
  const editable = !!onEdit;
  return (
    <div className="dash-perito__sec">
      <div className="dash-perito__sec-head">
        <div className="mono dash-perito__h" style={{ margin: 0 }}>§ {it ? "I TUOI DATI" : "YOUR DATA"}</div>
        {editable && (
          <button type="button" className="btn btn--ghost btn--sm" onClick={onEdit}>
            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ marginRight: 6, verticalAlign: -2 }} aria-hidden="true">
              <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
              <path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5Z"/>
            </svg>
            {it ? "Modifica inserzione" : "Edit listing"}
          </button>
        )}
      </div>
      <div className="dash-perito__grid">
        <Spec k="Email" v={app.email}/>
        {app.phone && <Spec k={it ? "Telefono" : "Phone"} v={app.phone}/>}
        <Spec k="CCIAA" v={app.cciaaNumber} mono/>
        <Spec k={it ? "Tariffa base" : "Base fee"} v={`€ ${app.baseRate}`}/>
      </div>
      <div className="mono dash-perito__h">{it ? "SPECIALIZZAZIONI" : "SPECIALTIES"}</div>
      <div className="adm-detail__chips">
        {app.specialties.map(s => <span key={s} className="adm-detail__chip">{SPEC_LABELS_DASH[s] || s}</span>)}
      </div>
      <div className="mono dash-perito__h">{it ? "STRUMENTAZIONE" : "INSTRUMENTATION"}</div>
      <div className="adm-detail__chips">
        {app.instrumentation.map(s => <span key={s} className="adm-detail__chip">{INSTR_LABELS_DASH[s] || s}</span>)}
      </div>
      <div className="mono dash-perito__h">{it ? "BIOGRAFIA" : "BIO"}</div>
      <p style={{ fontSize: 14, color: "var(--ink-soft)", lineHeight: 1.6, whiteSpace: "pre-wrap", margin: 0 }}>{app.bio}</p>
      {!editable && (
        <p className="srv-form__legal" style={{ marginTop: 24 }}>
          {it
            ? "Potrai modificare il profilo (foto, tariffa, specializzazioni, strumentazione, bio) appena la candidatura sarà approvata."
            : "You'll be able to edit the profile (photo, fee, specialties, instruments, bio) as soon as the application is approved."}
        </p>
      )}
    </div>
  );
}

function SurveyorEditor({ app, onSave, onCancel, it }) {
  const [draft, setDraft] = useState({
    city: app.city,
    radiusKm: String(app.radiusKm),
    baseRate: String(app.baseRate),
    yearsExperience: String(app.yearsExperience),
    specialties: app.specialties.slice(),
    instrumentation: app.instrumentation.slice(),
    bio: app.bio,
    photoDataUrl: app.photoDataUrl,
  });
  const [photoSource, setPhotoSource] = useState(null);  // raw URL fed to cropper
  const fileRef = useRef(null);
  const set = (k, v) => setDraft(d => ({ ...d, [k]: v }));
  const toggle = (k, v) => setDraft(d => ({ ...d, [k]: d[k].includes(v) ? d[k].filter(x => x !== v) : [...d[k], v] }));

  const onPhotoFile = (e) => {
    const file = e.target.files?.[0];
    e.target.value = "";
    if (!file) return;
    if (file.size > 8 * 1024 * 1024) return alert(it ? "Foto troppo grande (max 8 MB)" : "Photo too large (max 8 MB)");
    setPhotoSource(URL.createObjectURL(file));
  };

  const submit = (e) => {
    e.preventDefault();
    const updated = {
      ...app,
      city: draft.city.trim(),
      radiusKm: Number(draft.radiusKm) || app.radiusKm,
      baseRate: Number(draft.baseRate) || app.baseRate,
      yearsExperience: Number(draft.yearsExperience) || app.yearsExperience,
      specialties: draft.specialties,
      instrumentation: draft.instrumentation,
      bio: draft.bio.trim(),
      photoDataUrl: draft.photoDataUrl,
      updatedAt: new Date().toISOString(),
    };
    onSave(updated);
  };

  return (
    <form className="dash-perito__sec" onSubmit={submit}>
      <div className="dash-perito__sec-head">
        <div className="mono dash-perito__h" style={{ margin: 0 }}>§ {it ? "MODIFICA INSERZIONE" : "EDIT LISTING"}</div>
        <button type="button" className="btn btn--ghost btn--sm" onClick={onCancel}>
          {it ? "Annulla" : "Cancel"}
        </button>
      </div>

      {/* Photo editor */}
      <div className="dash-perito__photo-edit">
        <div className="dash-perito__photo-thumb">
          {draft.photoDataUrl
            ? <img src={draft.photoDataUrl} alt=""/>
            : <div className="adm__avatar adm__avatar--lg" style={{ width: 120, height: 120, fontSize: 28 }}><span className="mono">{(app.firstName[0] || "?") + (app.lastName[0] || "")}</span></div>}
        </div>
        <div className="dash-perito__photo-actions">
          <span className="srv-field__lbl">{it ? "Foto profilo" : "Profile photo"}</span>
          <div style={{ display: "flex", gap: 8, marginTop: 8, flexWrap: "wrap" }}>
            {draft.photoDataUrl && (
              <button type="button" className="btn btn--ghost btn--sm" onClick={() => setPhotoSource(draft.photoDataUrl)}>
                {it ? "Adatta" : "Adjust"}
              </button>
            )}
            <button type="button" className="btn btn--ghost btn--sm" onClick={() => fileRef.current?.click()}>
              {it ? "Cambia foto" : "Replace photo"}
            </button>
            <input ref={fileRef} type="file" accept="image/*" hidden onChange={onPhotoFile}/>
          </div>
          <p className="srv-form__legal" style={{ marginTop: 10, marginBottom: 0 }}>
            {it ? "Trascina e zooma per inquadrare il viso al centro." : "Drag and zoom to centre the subject."}
          </p>
        </div>
      </div>

      {photoSource && (
        <PhotoCropper
          imageSrc={photoSource}
          lang={it ? "it" : "en"}
          onConfirm={(dataUrl) => {
            set("photoDataUrl", dataUrl);
            URL.revokeObjectURL(photoSource);
            setPhotoSource(null);
          }}
          onCancel={() => {
            URL.revokeObjectURL(photoSource);
            setPhotoSource(null);
          }}
        />
      )}
      <div className="srv-row srv-row--3">
        <FieldDash label={it ? "Città base" : "Home port"} value={draft.city} onChange={v => set("city", v)}/>
        <FieldDash label={it ? "Raggio (km)" : "Radius (km)"} type="number" value={draft.radiusKm} onChange={v => set("radiusKm", v)}/>
        <FieldDash label={it ? "Anni esperienza" : "Years"} type="number" value={draft.yearsExperience} onChange={v => set("yearsExperience", v)}/>
      </div>
      <div className="srv-row srv-row--2" style={{ marginTop: 14 }}>
        <FieldDash label={it ? "Tariffa base (€)" : "Base fee (€)"} type="number" value={draft.baseRate} onChange={v => set("baseRate", v)} prefix="€"/>
        <div/>
      </div>

      <div className="mono dash-perito__h">{it ? "SPECIALIZZAZIONI" : "SPECIALTIES"}</div>
      <div className="srv-chips">
        {Object.entries(SPEC_LABELS_DASH).map(([v, l]) => (
          <button type="button" key={v} onClick={() => toggle("specialties", v)} className={`srv-chip ${draft.specialties.includes(v) ? "is-on" : ""}`}>
            <span className="srv-chip__check">{draft.specialties.includes(v) && (
              <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
            )}</span>
            <span>{l}</span>
          </button>
        ))}
      </div>

      <div className="mono dash-perito__h">{it ? "STRUMENTAZIONE" : "INSTRUMENTATION"}</div>
      <div className="srv-chips">
        {Object.entries(INSTR_LABELS_DASH).map(([v, l]) => (
          <button type="button" key={v} onClick={() => toggle("instrumentation", v)} className={`srv-chip ${draft.instrumentation.includes(v) ? "is-on" : ""}`}>
            <span className="srv-chip__check">{draft.instrumentation.includes(v) && (
              <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
            )}</span>
            <span>{l}</span>
          </button>
        ))}
      </div>

      <div className="srv-field" style={{ marginTop: 18 }}>
        <label className="srv-field__lbl">{it ? "Biografia" : "Bio"}</label>
        <textarea
          className="srv-field__input srv-field__input--ta"
          rows={4}
          value={draft.bio}
          onChange={(e) => set("bio", e.target.value)}
        />
      </div>

      <div className="dash-perito__save">
        <button type="button" className="btn btn--ghost" onClick={onCancel}>{it ? "Annulla" : "Cancel"}</button>
        <button type="submit" className="btn btn--accent">{it ? "Salva modifiche" : "Save changes"}</button>
      </div>
    </form>
  );
}

function Spec({ k, v, mono }) {
  return (
    <div className="dash-perito__spec">
      <span className="mono dash-perito__spec-k">{k}</span>
      <span className={`dash-perito__spec-v ${mono ? "mono" : ""}`}>{v}</span>
    </div>
  );
}

function FieldDash({ label, value, onChange, type = "text", prefix }) {
  return (
    <div className="srv-field">
      <label className="srv-field__lbl">{label}</label>
      <div className="srv-field__wrap">
        {prefix && <span className="srv-field__prefix">{prefix}</span>}
        <input type={type} value={value} onChange={(e) => onChange(e.target.value)} className="srv-field__input"/>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<Dashboard />);
