function Vins({ lang }) {
  const { useState, useEffect, useRef } = React;
  const T = window.T;
  const t = T[lang].vins;

  const [sections, setSections]       = useState(null);
  const [cellState, setCellState]     = useState({});
  const [saved, setSaved]             = useState(false);
  const [confirmTarget, setConfirmTarget] = useState(null);
  const [deleteCheck, setDeleteCheck]     = useState(false);

  // Edit modal state: null or { item, isNew }
  const [editModal, setEditModal] = useState(null);
  const [form, setForm]           = useState({});

  // Section modal: null or { mode: 'rename'|'create', sec?, name_ca, name_es }
  const [secModal, setSecModal] = useState(null);

  function tr(ca, es) { return lang === "es" ? es : ca; }

  const clientIdRef          = useRef(null);
  const sectionsRef          = useRef([]);
  const sectionEls           = useRef({});
  const sortablesRef         = useRef({});
  const sectionsContainerRef = useRef(null);
  const secSortableRef       = useRef(null);

  // ── Load ─────────────────────────────────────────────────────────────────
  useEffect(function () { loadItems(); }, []);

  async function loadItems() {
    var client = await window.SVX.getCurrentClient();
    if (!client) return;
    clientIdRef.current = client.id;

    var res = await window.SB
      .from("wines")
      .select("*")
      .eq("client_id", client.id)
      .order("section_sort", { ascending: true })
      .order("sort_order",   { ascending: true });

    if (res.error) {
      console.error("[Vins] load:", res.error);
      window.SVX.toast(tr("Error carregant els vins", "Error cargando los vinos"), "error");
      setSections([]);
      return;
    }
    var grouped = groupBySections(res.data || []);
    setSections(grouped);
    sectionsRef.current = grouped;
  }

  function groupBySections(items) {
    var map = new Map();
    for (var i = 0; i < items.length; i++) {
      var item = items[i];
      if (!map.has(item.section_tab)) {
        map.set(item.section_tab, {
          section_tab:     item.section_tab,
          section_name_ca: item.section_name_ca,
          section_name_es: item.section_name_es,
          section_sort:    item.section_sort,
          items: [],
          _empty: false,
        });
      }
      map.get(item.section_tab).items.push(item);
    }
    return Array.from(map.values()).sort(function (a, b) { return a.section_sort - b.section_sort; });
  }

  // ── SortableJS for wines (within + across sections) ─────────────────────
  useEffect(function () {
    if (!sections || typeof Sortable === "undefined") return;

    Object.values(sortablesRef.current).forEach(function (s) { try { s.destroy(); } catch (_) {} });
    sortablesRef.current = {};

    sections.forEach(function (sec) {
      var el = sectionEls.current[sec.section_tab];
      if (!el) return;
      sortablesRef.current[sec.section_tab] = Sortable.create(el, {
        group:     "wines",
        handle:    ".col-grip",
        animation: 150,
        ghostClass: "dish-ghost",
        dragClass:  "dish-dragging",
        onEnd: function (evt) {
          var fromTab = evt.from.getAttribute("data-section-tab");
          var toTab   = evt.to.getAttribute("data-section-tab");
          var itemId  = evt.item.getAttribute("data-id");
          if (fromTab === toTab && evt.oldIndex === evt.newIndex) return;
          handleWineDragEnd(itemId, fromTab, toTab, evt.to);
        },
      });
    });

    return function () {
      Object.values(sortablesRef.current).forEach(function (s) { try { s.destroy(); } catch (_) {} });
      sortablesRef.current = {};
    };
  }, [sections]);

  // ── SortableJS for section ordering ────────────────────────────────────
  useEffect(function () {
    if (!sections || !sectionsContainerRef.current || typeof Sortable === "undefined") return;
    if (secSortableRef.current) { try { secSortableRef.current.destroy(); } catch (_) {} }

    secSortableRef.current = Sortable.create(sectionsContainerRef.current, {
      handle: ".section-drag-handle",
      animation: 200,
      ghostClass: "section-ghost",
      onEnd: function (evt) {
        if (evt.oldIndex === evt.newIndex) return;
        var reordered = arrayMove(sectionsRef.current, evt.oldIndex, evt.newIndex);
        reordered = reordered.map(function (s, i) {
          return Object.assign({}, s, {
            section_sort: i,
            items: s.items.map(function (it) { return Object.assign({}, it, { section_sort: i }); }),
          });
        });
        setSections(reordered);
        sectionsRef.current = reordered;
        persistSectionOrder(reordered);
      },
    });

    return function () {
      if (secSortableRef.current) { try { secSortableRef.current.destroy(); } catch (_) {} secSortableRef.current = null; }
    };
  }, [sections ? sections.length : 0]);

  async function persistSectionOrder(reordered) {
    var updates = [];
    for (var i = 0; i < reordered.length; i++) {
      var sec = reordered[i];
      if (sec.items.length > 0) {
        updates.push(
          window.SB.from("wines")
            .update({ section_sort: i })
            .eq("client_id", clientIdRef.current)
            .eq("section_tab", sec.section_tab)
        );
      }
    }
    if (updates.length > 0) {
      var results = await Promise.all(updates);
      if (results.some(function (r) { return r.error; })) { loadItems(); }
      else { globalSaved(); }
    }
  }

  function arrayMove(arr, from, to) {
    var copy = arr.slice();
    var item = copy.splice(from, 1)[0];
    copy.splice(to, 0, item);
    return copy;
  }

  // ── Wine drag end: reorder within section or move across sections ──────
  async function handleWineDragEnd(itemId, fromTab, toTab, toContainer) {
    var newIds = Array.from(toContainer.querySelectorAll("[data-id]")).map(function (n) { return n.dataset.id; });

    var allItems = {};
    sectionsRef.current.forEach(function (s) {
      s.items.forEach(function (it) { allItems[it.id] = it; });
    });

    var movedItem = allItems[itemId];
    if (!movedItem) { loadItems(); return; }

    var crossSection = fromTab !== toTab;
    var toSec = sectionsRef.current.find(function (s) { return s.section_tab === toTab; });

    // Optimistic update
    setSections(function (prev) {
      var next = prev.map(function (s) {
        if (crossSection && s.section_tab === fromTab) {
          var remaining = s.items.filter(function (i) { return i.id !== itemId; });
          remaining = remaining.map(function (i, idx) { return Object.assign({}, i, { sort_order: idx }); });
          return Object.assign({}, s, { items: remaining, _empty: s._empty || remaining.length === 0 });
        }
        if (s.section_tab === toTab) {
          var destItems = newIds.map(function (id, idx) {
            var it = id === itemId && crossSection
              ? Object.assign({}, movedItem, {
                  section_tab:     toSec.section_tab,
                  section_name_ca: toSec.section_name_ca,
                  section_name_es: toSec.section_name_es,
                  section_sort:    toSec.section_sort,
                })
              : allItems[id];
            return it ? Object.assign({}, it, { sort_order: idx }) : null;
          }).filter(Boolean);
          return Object.assign({}, s, { items: destItems, _empty: false });
        }
        return s;
      });
      sectionsRef.current = next;
      return next;
    });

    // Persist
    var updates = [];
    if (crossSection && toSec) {
      updates.push(
        window.SB.from("wines").update({
          section_tab:     toSec.section_tab,
          section_name_ca: toSec.section_name_ca,
          section_name_es: toSec.section_name_es,
          section_sort:    toSec.section_sort,
        }).eq("id", itemId)
      );
    }
    newIds.forEach(function (id, idx) {
      updates.push(window.SB.from("wines").update({ sort_order: idx }).eq("id", id));
    });
    if (crossSection) {
      var fromEl = sectionEls.current[fromTab];
      if (fromEl) {
        var fromIds = Array.from(fromEl.querySelectorAll("[data-id]")).map(function (n) { return n.dataset.id; });
        fromIds.forEach(function (id, idx) {
          updates.push(window.SB.from("wines").update({ sort_order: idx }).eq("id", id));
        });
      }
    }

    var results = await Promise.all(updates);
    if (results.some(function (r) { return r.error; })) {
      window.SVX.toast(tr("Error desant l'ordre", "Error guardando el orden"), "error");
      loadItems();
    } else {
      globalSaved();
    }
  }

  // ── State helpers ───────────────────────────────────────────────────────
  function patchState(itemId, patch) {
    setSections(function (prev) {
      var next = prev.map(function (sec) {
        return Object.assign({}, sec, {
          items: sec.items.map(function (it) { return it.id === itemId ? Object.assign({}, it, patch) : it; }),
        });
      });
      sectionsRef.current = next;
      return next;
    });
  }

  function flashCell(itemId, status) {
    setCellState(function (prev) { return Object.assign({}, prev, { [itemId]: status }); });
    if (status !== "saving") {
      var delay = status === "saved" ? 700 : 1500;
      setTimeout(function () {
        setCellState(function (prev) {
          var next = Object.assign({}, prev);
          if (next[itemId] === status) delete next[itemId];
          return next;
        });
      }, delay);
    }
  }

  function globalSaved() {
    setSaved(true);
    setTimeout(function () { setSaved(false); }, 1800);
  }

  // ── Toggle active ───────────────────────────────────────────────────────
  async function doToggle(item) {
    var newActive = !item.active;
    patchState(item.id, { active: newActive });
    var res = await window.SB.from("wines").update({ active: newActive }).eq("id", item.id);
    if (res.error) {
      patchState(item.id, { active: item.active });
      window.SVX.toast(tr("Error desant el canvi", "Error al guardar"), "error");
    } else { globalSaved(); }
  }

  // ── Delete wine ─────────────────────────────────────────────────────────
  function handleDelete(item) {
    setConfirmTarget({ type: "delete", item: item });
    setDeleteCheck(false);
  }

  async function doDelete(item) {
    setSections(function (prev) {
      var next = prev.map(function (sec) {
        var filtered = sec.items.filter(function (i) { return i.id !== item.id; });
        return Object.assign({}, sec, { items: filtered, _empty: sec._empty || filtered.length === 0 });
      });
      sectionsRef.current = next;
      return next;
    });
    var res = await window.SB.from("wines").delete().eq("id", item.id);
    if (res.error) { window.SVX.toast(tr("Error eliminant el vi", "Error eliminando el vino"), "error"); loadItems(); }
    else { globalSaved(); }
  }

  // ── Delete section ──────────────────────────────────────────────────────
  async function doDeleteSection(sec) {
    setSections(function (prev) {
      var next = prev.filter(function (s) { return s.section_tab !== sec.section_tab; });
      sectionsRef.current = next;
      return next;
    });
    if (sec.items.length > 0) {
      var res = await window.SB.from("wines").delete().eq("client_id", clientIdRef.current).eq("section_tab", sec.section_tab);
      if (res.error) { window.SVX.toast(tr("Error eliminant la secció", "Error eliminando la sección"), "error"); loadItems(); }
      else { globalSaved(); }
    }
  }

  function handleDeleteSection(sec) {
    setConfirmTarget({ type: "delete_section", sec: sec });
    setDeleteCheck(false);
  }

  // ── Confirm action ──────────────────────────────────────────────────────
  function executeConfirm() {
    if (!confirmTarget) return;
    if (confirmTarget.type === "delete") doDelete(confirmTarget.item);
    if (confirmTarget.type === "delete_section") doDeleteSection(confirmTarget.sec);
    setConfirmTarget(null);
    setDeleteCheck(false);
  }
  function dismissConfirm() { setConfirmTarget(null); setDeleteCheck(false); }

  // ── Section management ──────────────────────────────────────────────────
  function slugify(str) {
    return str.toLowerCase()
      .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
      .replace(/[·.]/g, "").replace(/[^a-z0-9]+/g, "-")
      .replace(/^-|-$/g, "") || "seccio";
  }

  function uniqueTab(base) {
    var existing = (sectionsRef.current || []).map(function (s) { return s.section_tab; });
    if (!existing.includes(base)) return base;
    for (var n = 2; n < 100; n++) { if (!existing.includes(base + "-" + n)) return base + "-" + n; }
    return base + "-" + Date.now();
  }

  function openSecRename(sec) {
    setSecModal({ mode: "rename", sec: sec, name_ca: sec.section_name_ca, name_es: sec.section_name_es });
  }

  function openSecCreate() {
    setSecModal({ mode: "create", sec: null, name_ca: "", name_es: "" });
  }

  function updateSecModal(key, val) {
    setSecModal(function (prev) { return Object.assign({}, prev, { [key]: val }); });
  }

  async function saveSecModal(e) {
    e.preventDefault();
    if (!secModal) return;
    var nameCa = (secModal.name_ca || "").trim();
    var nameEs = (secModal.name_es || "").trim();
    if (!nameCa && !nameEs) return;

    if (secModal.mode === "rename") {
      var sec = secModal.sec;
      setSections(function (prev) {
        var next = prev.map(function (s) {
          if (s.section_tab !== sec.section_tab) return s;
          return Object.assign({}, s, {
            section_name_ca: nameCa || s.section_name_ca,
            section_name_es: nameEs || s.section_name_es,
            items: s.items.map(function (it) {
              return Object.assign({}, it, {
                section_name_ca: nameCa || it.section_name_ca,
                section_name_es: nameEs || it.section_name_es,
              });
            }),
          });
        });
        sectionsRef.current = next;
        return next;
      });
      setSecModal(null);
      if (sec.items.length > 0) {
        var res = await window.SB.from("wines")
          .update({ section_name_ca: nameCa || sec.section_name_ca, section_name_es: nameEs || sec.section_name_es })
          .eq("client_id", clientIdRef.current)
          .eq("section_tab", sec.section_tab);
        if (res.error) { window.SVX.toast(tr("Error desant", "Error al guardar"), "error"); loadItems(); }
        else { globalSaved(); }
      }
    } else {
      var tab = uniqueTab(slugify(nameCa || nameEs));
      var maxSort = (sectionsRef.current || []).reduce(function (m, s) { return Math.max(m, s.section_sort); }, -1);
      var newSec = {
        section_tab: tab,
        section_name_ca: nameCa || nameEs,
        section_name_es: nameEs || nameCa,
        section_sort: maxSort + 1,
        items: [],
        _empty: true,
      };
      setSections(function (prev) {
        var next = prev.concat(newSec);
        sectionsRef.current = next;
        return next;
      });
      setSecModal(null);
    }
  }

  function cancelSecModal() { setSecModal(null); }

  // ── Edit modal ──────────────────────────────────────────────────────────
  function openEditModal(item, isNew) {
    setForm({
      name:           item.name || "",
      variety:        item.variety || "",
      description_ca: item.description_ca || "",
      description_es: item.description_es || "",
      aging:          item.aging || "",
      price_glass:    item.price_glass_cents != null ? (item.price_glass_cents / 100).toFixed(2).replace(".", ",") : "",
      price_bottle:   item.price_bottle_cents != null ? (item.price_bottle_cents / 100).toFixed(2).replace(".", ",") : "",
      section_tab:    item.section_tab,
    });
    setEditModal({ item: item, isNew: !!isNew });
  }

  function updateForm(key, val) {
    setForm(function (prev) { return Object.assign({}, prev, { [key]: val }); });
  }

  function parsePriceCents(str) {
    if (!str) return null;
    var raw = str.replace(",", ".").trim();
    if (raw === "" || isNaN(parseFloat(raw))) return null;
    return Math.round(parseFloat(raw) * 100);
  }

  async function saveModal(e) {
    e.preventDefault();
    if (!editModal) return;
    var item = editModal.item;

    var targetSec = (sectionsRef.current || []).find(function (s) { return s.section_tab === form.section_tab; });
    var secPatch = targetSec ? {
      section_tab:     targetSec.section_tab,
      section_name_ca: targetSec.section_name_ca,
      section_name_es: targetSec.section_name_es,
      section_sort:    targetSec.section_sort,
    } : {};

    var patch = Object.assign({
      name:              form.name,
      variety:           form.variety,
      description_ca:    form.description_ca,
      description_es:    form.description_es,
      aging:             form.aging,
      price_glass_cents: parsePriceCents(form.price_glass),
      price_bottle_cents:parsePriceCents(form.price_bottle),
    }, secPatch);

    var oldTab = item.section_tab;
    var newTab = form.section_tab;

    if (oldTab !== newTab) {
      setSections(function (prev) {
        var updatedItem = Object.assign({}, item, patch);
        var next = prev.map(function (s) {
          if (s.section_tab === oldTab) {
            var remaining = s.items.filter(function (i) { return i.id !== item.id; });
            return Object.assign({}, s, { items: remaining, _empty: s._empty || remaining.length === 0 });
          }
          if (s.section_tab === newTab) return Object.assign({}, s, { items: s.items.concat(updatedItem), _empty: false });
          return s;
        });
        sectionsRef.current = next;
        return next;
      });
    } else {
      patchState(item.id, patch);
    }

    setEditModal(null);
    flashCell(item.id, "saving");

    var res = await window.SB.from("wines").update(patch).eq("id", item.id);
    if (res.error) {
      flashCell(item.id, "error");
      window.SVX.toast(tr("Error desant el vi", "Error guardando el vino"), "error");
      loadItems();
    } else {
      flashCell(item.id, "saved");
      globalSaved();
    }
  }

  async function cancelModal() {
    if (!editModal) return;
    if (editModal.isNew) {
      var itemId = editModal.item.id;
      setEditModal(null);
      setSections(function (prev) {
        var next = prev.map(function (sec) {
          var filtered = sec.items.filter(function (i) { return i.id !== itemId; });
          return Object.assign({}, sec, { items: filtered, _empty: sec._empty || filtered.length === 0 });
        });
        sectionsRef.current = next;
        return next;
      });
      await window.SB.from("wines").delete().eq("id", itemId);
    } else {
      setEditModal(null);
    }
  }

  // ── Add wine ────────────────────────────────────────────────────────────
  async function addWine(sec) {
    if (!clientIdRef.current) return;
    var maxSort = sec.items.reduce(function (m, i) { return Math.max(m, i.sort_order); }, -1);
    var newItem = {
      client_id:        clientIdRef.current,
      section_tab:      sec.section_tab,
      section_name_ca:  sec.section_name_ca,
      section_name_es:  sec.section_name_es,
      section_sort:     sec.section_sort,
      name:             "",
      variety:          "",
      description_ca:   "",
      description_es:   "",
      aging:            "",
      price_glass_cents:  null,
      price_bottle_cents: null,
      sort_order:       maxSort + 1,
      active:           true,
    };

    var res = await window.SB.from("wines").insert(newItem).select().single();
    if (res.error) {
      window.SVX.toast(tr("Error afegint el vi", "Error al añadir el vino"), "error");
      return;
    }

    setSections(function (prev) {
      var next = prev.map(function (s) {
        return s.section_tab === sec.section_tab ? Object.assign({}, s, { items: s.items.concat(res.data), _empty: false }) : s;
      });
      sectionsRef.current = next;
      return next;
    });
    openEditModal(res.data, true);
  }

  // ── Helpers ─────────────────────────────────────────────────────────────
  function fmtPrice(cents) {
    if (cents == null) return "—";
    return (cents / 100).toFixed(2).replace(".", ",");
  }

  // ── Render ──────────────────────────────────────────────────────────────
  if (sections === null) return (
    <div style={{padding: 32, color: "var(--ink-3)", fontSize: 14}}>—</div>
  );

  var inputStyle = {
    display: "block", width: "100%", padding: "8px 10px", border: "1px solid var(--line)",
    borderRadius: 6, fontSize: 14, fontFamily: "inherit", marginTop: 4, boxSizing: "border-box",
  };
  var labelStyle = { display: "block", fontSize: 13, color: "var(--ink-2)", marginBottom: 12 };

  return (
    <div>
      <header className="srv-page-head">
        <div>
          <h1 className="t-h1">{t.title}</h1>
          <p style={{margin: "8px 0 0", color: "var(--ink-2)", fontSize: 15}}>{t.sub}</p>
        </div>
        {saved && (
          <div style={{display: "inline-flex", alignItems: "center", gap: 8, color: "var(--oliva-700)", background: "var(--oliva-50)", padding: "8px 14px", borderRadius: 999, fontSize: 13, fontWeight: 500}}>
            <i className="ph ph-check-circle"></i>{t.saved}
          </div>
        )}
      </header>

      {sections.length === 0 && (
        <p style={{color: "var(--ink-3)", fontSize: 15, marginTop: 24}}>
          {tr("La carta de vins és buida. Crea la primera secció.", "La carta de vinos está vacía. Crea la primera sección.")}
        </p>
      )}

      <div ref={sectionsContainerRef}>
      {sections.map(function (sec) {
        var secName = lang === "es" ? sec.section_name_es : sec.section_name_ca;
        return (
          <div key={sec.section_tab} className="section-block" data-section-tab={sec.section_tab} style={{marginBottom: 28}}>
            <div className="section-head">
              <span className="section-drag-handle"><i className="ph ph-dots-six-vertical"></i></span>
              <h2 className="t-h2" style={{margin: 0}}>{secName}</h2>
              <div className="section-head-actions">
                <button className="section-action-btn" onClick={function () { openSecRename(sec); }} title={tr("Reanomenar", "Renombrar")}>
                  <i className="ph ph-pencil-simple"></i>
                </button>
                <button className="section-action-btn" onClick={function () { handleDeleteSection(sec); }} title={tr("Eliminar secció", "Eliminar sección")} style={{color: "var(--danger)"}}>
                  <i className="ph ph-trash"></i>
                </button>
              </div>
            </div>

            {sec.items.length === 0 ? (
              <p style={{color: "var(--ink-3)", fontSize: 14, fontStyle: "italic", padding: "12px 0"}}>
                {tr("Cap vi en aquesta secció.", "Ningún vino en esta sección.")}
              </p>
            ) : (
              <div className="srv-table srv-table-vins">
                <div className="srv-table-head">
                  <span></span>
                  <span>{t.nom}</span>
                  <span>{t.varietat}</span>
                  <span style={{textAlign: "right"}}>{t.preuCopa}</span>
                  <span style={{textAlign: "right"}}>{t.preuAmpolla}</span>
                  <span style={{textAlign: "right"}}>{t.actiu}</span>
                </div>
                <div ref={function (el) { if (el) sectionEls.current[sec.section_tab] = el; }} data-section-tab={sec.section_tab}>
                  {sec.items.map(function (item) {
                    var cs = cellState[item.id];
                    var rowCls = ["srv-table-row", item.active ? "" : "off",
                      cs === "saving" ? "cell-saving" : cs === "saved" ? "cell-saved" : cs === "error" ? "cell-error" : "",
                    ].filter(Boolean).join(" ");

                    return (
                      <div key={item.id} data-id={item.id} className={rowCls}>
                        <span className="col-grip"><i className="ph ph-dots-six-vertical"></i></span>
                        <div className="col-name" style={{cursor: "pointer"}} onClick={function () { openEditModal(item, false); }}>
                          <div className="dish-name">
                            {item.name || <span style={{color: "var(--ink-4)", fontStyle: "italic", fontSize: 13}}>+ {tr("nom", "nombre")}</span>}
                          </div>
                          <div className="dish-sub">
                            {item.aging || <span style={{color: "var(--ink-4)", fontStyle: "italic", fontSize: 12}}>{t.envelliment}</span>}
                          </div>
                        </div>
                        <div className="col-variety">
                          {item.variety || "—"}
                        </div>
                        <div className="col-price" style={{cursor: "pointer"}} onClick={function () { openEditModal(item, false); }}>
                          {item.price_glass_cents != null
                            ? <>{fmtPrice(item.price_glass_cents)} <span className="price-suffix">€</span></>
                            : <span style={{color: "var(--ink-4)"}}>—</span>}
                        </div>
                        <div className="col-price" style={{cursor: "pointer"}} onClick={function () { openEditModal(item, false); }}>
                          {item.price_bottle_cents != null
                            ? <>{fmtPrice(item.price_bottle_cents)} <span className="price-suffix">€</span></>
                            : <span style={{color: "var(--ink-4)"}}>—</span>}
                        </div>
                        <div className="col-on" style={{display: "flex", alignItems: "center", gap: 6, justifyContent: "flex-end"}}>
                          <button className={"toggle " + (item.active ? "on" : "")} onClick={function () { doToggle(item); }} aria-label="toggle"><span className="thumb"></span></button>
                          <button style={{background: "none", border: "none", cursor: "pointer", padding: 2, color: "var(--ink-4)", fontSize: 14}} onClick={function () { handleDelete(item); }} title={tr("Eliminar", "Eliminar")}><i className="ph ph-trash"></i></button>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
            <button className="btn ghost" style={{marginTop: 8, fontSize: 13}} onClick={function () { addWine(sec); }}>
              <i className="ph ph-plus"></i>{t.add}
            </button>
          </div>
        );
      })}
      </div>

      {/* ── + Nova secció ──────────────────────────────────────────── */}
      <button className="new-section-btn" onClick={openSecCreate}>
        <i className="ph ph-plus"></i> {t.novaSeccio}
      </button>

      {/* ── Section rename / create modal ──────────────────────────── */}
      {secModal && (
        <div
          style={{position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 200}}
          onClick={function (e) { if (e.target === e.currentTarget) cancelSecModal(); }}
        >
          <div style={{background: "var(--paper)", borderRadius: 12, padding: 24, width: "90%", maxWidth: 400, boxShadow: "0 8px 30px rgba(0,0,0,0.12)"}}>
            <h2 className="t-h2" style={{marginBottom: 16}}>
              {secModal.mode === "rename" ? tr("Reanomenar secció", "Renombrar sección") : tr("Nova secció", "Nueva sección")}
            </h2>
            <form onSubmit={saveSecModal}>
              <label style={labelStyle}>
                {tr("Nom (CA)", "Nombre (CA)")}
                <input autoFocus value={secModal.name_ca} onChange={function (e) { updateSecModal("name_ca", e.target.value); }} style={inputStyle} />
              </label>
              <label style={labelStyle}>
                {tr("Nom (ES)", "Nombre (ES)")}
                <input value={secModal.name_es} onChange={function (e) { updateSecModal("name_es", e.target.value); }} style={inputStyle} />
              </label>
              <div style={{display: "flex", gap: 8, marginTop: 8}}>
                <button type="submit" className="btn primary">
                  {secModal.mode === "rename" ? tr("Desar", "Guardar") : tr("Crear", "Crear")}
                </button>
                <button type="button" className="btn ghost" onClick={cancelSecModal}>{tr("Cancel·lar", "Cancelar")}</button>
              </div>
            </form>
          </div>
        </div>
      )}

      {/* ── Edit wine modal ────────────────────────────────────────── */}
      {editModal && (
        <div
          style={{position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 200}}
          onClick={function (e) { if (e.target === e.currentTarget) cancelModal(); }}
        >
          <div className="dish-edit-modal" onClick={function (e) { e.stopPropagation(); }}>
            <h2 className="t-h2" style={{marginBottom: 4}}>
              {editModal.isNew ? tr("Nou vi", "Nuevo vino") : tr("Editar vi", "Editar vino")}
            </h2>
            <form onSubmit={saveModal}>
              <label style={labelStyle}>
                {tr("Secció", "Sección")}
                <select value={form.section_tab} onChange={function (e) { updateForm("section_tab", e.target.value); }} style={Object.assign({}, inputStyle, {padding: "8px 6px"})}>
                  {(sectionsRef.current || []).map(function (s) {
                    return <option key={s.section_tab} value={s.section_tab}>{lang === "es" ? s.section_name_es : s.section_name_ca}</option>;
                  })}
                </select>
              </label>
              <label style={labelStyle}>
                {t.nom}
                <input autoFocus value={form.name} onChange={function (e) { updateForm("name", e.target.value); }} style={inputStyle} placeholder={tr("Celler X · Xarel·lo 2022", "Bodega X · Xarel·lo 2022")} />
              </label>
              <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12}}>
                <label style={labelStyle}>
                  {t.varietat}
                  <input value={form.variety} onChange={function (e) { updateForm("variety", e.target.value); }} style={inputStyle} placeholder="Xarel·lo" />
                </label>
                <label style={labelStyle}>
                  {t.envelliment}
                  <input value={form.aging} onChange={function (e) { updateForm("aging", e.target.value); }} style={inputStyle} placeholder={tr("8 mesos en bóta", "8 meses en barrica")} />
                </label>
              </div>
              <label style={labelStyle}>
                {tr("Descripció (CA)", "Descripción (CA)")}
                <textarea rows="2" value={form.description_ca} onChange={function (e) { updateForm("description_ca", e.target.value); }} style={Object.assign({}, inputStyle, {resize: "vertical"})} maxLength={300} />
              </label>
              <label style={labelStyle}>
                {tr("Descripció (ES)", "Descripción (ES)")}
                <textarea rows="2" value={form.description_es} onChange={function (e) { updateForm("description_es", e.target.value); }} style={Object.assign({}, inputStyle, {resize: "vertical"})} maxLength={300} />
              </label>
              <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12}}>
                <label style={labelStyle}>
                  {t.preuCopa + " (€)"}
                  <input value={form.price_glass} onChange={function (e) { updateForm("price_glass", e.target.value); }} onFocus={function (e) { e.target.select(); }} placeholder="4,50" style={inputStyle} />
                </label>
                <label style={labelStyle}>
                  {t.preuAmpolla + " (€)"}
                  <input value={form.price_bottle} onChange={function (e) { updateForm("price_bottle", e.target.value); }} onFocus={function (e) { e.target.select(); }} placeholder="22,00" style={inputStyle} />
                </label>
              </div>
              <div style={{display: "flex", gap: 8}}>
                <button type="submit" className="btn primary">{tr("Desar", "Guardar")}</button>
                <button type="button" className="btn ghost" onClick={cancelModal}>{tr("Cancel·lar", "Cancelar")}</button>
              </div>
            </form>
          </div>
        </div>
      )}

      {/* ── Confirm modal ──────────────────────────────────────────── */}
      {confirmTarget && (function () {
        var isDelete    = confirmTarget.type === "delete";
        var isDeleteSec = confirmTarget.type === "delete_section";

        var title, body, btnLabel;
        if (isDeleteSec) {
          var sec = confirmTarget.sec;
          var sName = lang === "es" ? sec.section_name_es : sec.section_name_ca;
          var count = sec.items.length;
          title = tr("Eliminar secció?", "¿Eliminar sección?");
          body = count > 0
            ? tr('Això eliminarà la secció «' + sName + '» i els seus ' + count + ' vins permanentment.', 'Esto eliminará la sección «' + sName + '» y sus ' + count + ' vinos permanentemente.')
            : tr('Això eliminarà la secció «' + sName + '».', 'Esto eliminará la sección «' + sName + '».');
          btnLabel = tr("Eliminar secció", "Eliminar sección");
        } else {
          var iName = item_name(confirmTarget.item);
          title = tr("Eliminar vi?", "¿Eliminar vino?");
          body = tr("Això eliminarà «" + iName + "» permanentment.", "Esto eliminará «" + iName + "» permanentemente.");
          btnLabel = tr("Eliminar", "Eliminar");
        }

        var needsCheck = isDelete || (isDeleteSec && confirmTarget.sec.items.length > 0);

        return (
          <div
            style={{position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 200}}
            onClick={function (e) { if (e.target === e.currentTarget) dismissConfirm(); }}
          >
            <div style={{background: "var(--paper)", borderRadius: 12, padding: 24, width: "90%", maxWidth: 400, boxShadow: "0 8px 30px rgba(0,0,0,0.12)"}}>
              <h2 className="t-h2" style={{marginBottom: 8}}>{title}</h2>
              <p style={{margin: "0 0 16px", fontSize: 14, color: "var(--ink-2)"}}>{body}</p>
              {needsCheck && (
                <label style={{display: "flex", alignItems: "center", gap: 8, fontSize: 13, color: "var(--ink-2)", marginBottom: 16, cursor: "pointer"}}>
                  <input type="checkbox" checked={deleteCheck} onChange={function (e) { setDeleteCheck(e.target.checked); }} />
                  {tr("Entenc que això no es pot desfer", "Entiendo que esto no se puede deshacer")}
                </label>
              )}
              <div style={{display: "flex", gap: 8}}>
                <button
                  className="btn primary"
                  style={{background: "var(--danger)"}}
                  disabled={needsCheck && !deleteCheck}
                  onClick={executeConfirm}
                >{btnLabel}</button>
                <button className="btn ghost" onClick={dismissConfirm}>{tr("Cancel·lar", "Cancelar")}</button>
              </div>
            </div>
          </div>
        );
      })()}
    </div>
  );

  function item_name(item) { return item.name || "—"; }
}

window.Vins = Vins;
