// ─────────────────────────────────────────────────────────────────────────────
//  Optimizador de Despiece · INside Elementos Arquitectónicos
// ─────────────────────────────────────────────────────────────────────────────

const SHEET_FORMATS = {
  A: { key: "A", w: 1200, h: 2800, label: "A – 1200 × 2800 mm  (12 mm)",  thick: "12mm" },
  B: { key: "B", w: 1800, h: 2400, label: "B – 1800 × 2400 mm  (24 mm)",  thick: "24mm" },
  C: { key: "C", w: 1220, h: 2440, label: "C – 1220 × 2440 mm  (8/9 mm)", thick: "8/9mm" },
  D: { key: "D", w: 1225, h: 2800, label: "D – 1225 × 2800 mm  (12 mm)",  thick: "12mm" },
};

const PIECE_PALETTE = [
  "#5B9CF6","#F97316","#22C55E","#A78BFA","#EC4899",
  "#14B8A6","#F59E0B","#6366F1","#EF4444","#84CC16",
  "#06B6D4","#D946EF","#F472B6","#34D399","#FBBF24",
  "#60A5FA","#FB923C","#4ADE80","#C084FC","#FB7185",
];

// ── Shape definitions ─────────────────────────────────────────────────────────

const SHAPES = {
  rectangulo: { label: "Rectángulo",  icon: "▬", dims: ["ancho", "alto"] },
  circulo:    { label: "Círculo",     icon: "●", dims: ["diametro"] },
  elipse:     { label: "Elipse",      icon: "⬯", dims: ["ancho", "alto"] },
  hexagono:   { label: "Hexágono",    icon: "⬡", dims: ["lado"] },
  triangulo:  { label: "Triángulo",   icon: "▲", dims: ["ancho", "alto"] },
  libre:      { label: "Libre / Curva", icon: "⌾", dims: ["ancho", "alto", "area_real"] },
};

const DIM_LABELS = {
  ancho:     "Ancho (mm)",
  alto:      "Alto (mm)",
  diametro:  "Diámetro (mm)",
  lado:      "Lado (mm)",
  area_real: "Área real (mm²)",
};

// Returns the bounding box {w, h} used for placement
function getBoundingBox(forma, dims) {
  switch (forma) {
    case "circulo":   return { w: dims.diametro, h: dims.diametro };
    case "hexagono":  return { w: dims.lado * 2, h: Math.ceil(dims.lado * Math.sqrt(3)) };
    default:          return { w: dims.ancho, h: dims.alto };
  }
}

// Returns the actual cut area in mm² (used for % aprovechamiento)
function getActualArea(forma, dims) {
  switch (forma) {
    case "circulo":   return Math.PI * (dims.diametro / 2) ** 2;
    case "elipse":    return Math.PI * (dims.ancho / 2) * (dims.alto / 2);
    case "hexagono":  return (3 * Math.sqrt(3) / 2) * dims.lado ** 2;
    case "triangulo": return 0.5 * dims.ancho * dims.alto;
    case "libre":     return dims.area_real > 0 ? dims.area_real : dims.ancho * dims.alto;
    default:          return dims.ancho * dims.alto;
  }
}

// Returns a hint line describing bounding box for non-rectangular shapes
function getBBHint(forma, dims) {
  const bb = getBoundingBox(forma, dims);
  if (forma === "rectangulo") return null;
  return `Caja envolvente usada para corte: ${bb.w} × ${bb.h} mm`;
}

// ── SVG shape renderer (draws actual shape within its bounding box) ───────────

function ShapeSVGPath({ forma, px, py, pw, ph, fill, stroke }) {
  const cx = px + pw / 2, cy = py + ph / 2;
  const props = { fill, stroke, strokeWidth: 1.2 };

  switch (forma) {
    case "circulo":
    case "elipse":
      return <ellipse cx={cx} cy={cy} rx={pw / 2} ry={ph / 2} {...props} />;
    case "hexagono": {
      // flat-top hex (vértices a izquierda/derecha): ángulos 0°,60°,120°,180°,240°,300°
      // El bounding box es w=2L, h=L√3 → rx=L (radio), ry=L√3/2=rx*√3/2
      // Usando r=rx para ambos ejes, los vértices tocan exactamente los 4 lados de la caja.
      const r = pw / 2;
      const pts = Array.from({ length: 6 }, (_, i) => {
        const a = (Math.PI / 180) * (60 * i);
        return `${cx + r * Math.cos(a)},${cy + r * Math.sin(a)}`;
      }).join(" ");
      return <polygon points={pts} {...props} />;
    }
    case "triangulo": {
      const pts = `${cx},${py} ${px + pw},${py + ph} ${px},${py + ph}`;
      return <polygon points={pts} {...props} />;
    }
    case "libre":
      return <rect x={px} y={py} width={pw} height={ph} rx={3} fill={props.fill} stroke={props.stroke} strokeWidth={props.strokeWidth} strokeDasharray="4 2" />;
    default:
      return <rect x={px} y={py} width={pw} height={ph} rx={1} {...props} />;
  }
}

// ── Core optimizer ────────────────────────────────────────────────────────────

function expandPieces(piezas) {
  let id = 0;
  const out = [];
  for (const p of piezas) {
    const bb = getBoundingBox(p.forma, p.dims);
    for (let i = 0; i < p.cantidad; i++) {
      out.push({ id: id++, nombre: p.nombre, forma: p.forma, w: bb.w, h: bb.h });
    }
  }
  return out;
}

function packOneSheet(items, SW, SH, kerf, allowRotation) {
  const placedIds = new Set();
  const placed    = [];

  // Sort tallest first so shelves are efficient
  const sorted = [...items].sort((a, b) => {
    const ah = allowRotation ? Math.max(a.w, a.h) : a.h;
    const bh = allowRotation ? Math.max(b.w, b.h) : b.h;
    return bh - ah || b.w - a.w;
  });

  // Shelf packing: each shelf has a fixed height (tallest piece in it).
  // Pieces of different widths can share a shelf → far more efficient than strip packing.
  const shelves = []; // { y0, h, nextX }

  for (const item of sorted) {
    if (placedIds.has(item.id)) continue;

    // Build orientations to try (normal, then rotated if allowed)
    const orients = [{ w: item.w, h: item.h }];
    if (allowRotation && item.w !== item.h) orients.push({ w: item.h, h: item.w });

    let did = false;
    for (const { w: pw, h: ph } of orients) {
      if (did) break;
      // Try fitting in an existing shelf
      for (const shelf of shelves) {
        if (shelf.nextX + pw <= SW && ph <= shelf.h) {
          placed.push({ ...item, x: shelf.nextX, y: shelf.y0, w: pw, h: ph });
          placedIds.add(item.id);
          shelf.nextX += pw + kerf;
          did = true; break;
        }
      }
      if (did) break;
      // Open a new shelf
      const prev = shelves[shelves.length - 1];
      const y0   = prev ? prev.y0 + prev.h + kerf : 0;
      if (y0 + ph <= SH && pw <= SW) {
        shelves.push({ y0, h: ph, nextX: pw + kerf });
        placed.push({ ...item, x: 0, y: y0, w: pw, h: ph });
        placedIds.add(item.id);
        did = true;
      }
    }
  }

  return { placed, leftover: items.filter(p => !placedIds.has(p.id)) };
}

function optimizeFormat(piezas, fmtKey, kerf, allowRotation) {
  const fmt = SHEET_FORMATS[fmtKey];
  const { w: SW, h: SH } = fmt;

  const bad = [];
  for (const p of piezas) {
    const bb = getBoundingBox(p.forma, p.dims);
    const normalFits = bb.w <= SW && bb.h <= SH;
    const rotFits    = allowRotation && bb.h <= SW && bb.w <= SH;
    if (!normalFits && !rotFits) bad.push(`"${p.nombre}" (${bb.w}×${bb.h}mm)`);
  }
  if (bad.length) return { error: `No caben en formato ${fmtKey}: ${[...new Set(bad)].join(", ")}`, laminas: null, aprovechamiento: null, sheets: [] };

  let items = expandPieces(piezas);
  const sheets = [];
  let guard = items.length + 20;
  while (items.length > 0 && guard-- > 0) {
    const { placed, leftover } = packOneSheet(items, SW, SH, kerf, allowRotation);
    if (placed.length === 0) break;
    sheets.push(placed);
    items = leftover;
  }

  const totalSheetArea = sheets.length * SW * SH;
  const usedArea       = piezas.reduce((s, p) => s + getActualArea(p.forma, p.dims) * p.cantidad, 0);
  const aprovechamiento = totalSheetArea > 0 ? Math.round((usedArea / totalSheetArea) * 100) : 0;

  return { laminas: sheets.length, aprovechamiento, sheets, error: null };
}

function buildValidation(piezas, sheets) {
  const pedidas = {};
  for (const p of piezas) pedidas[p.nombre] = (pedidas[p.nombre] || 0) + p.cantidad;
  const colocadas = {};
  for (const sheet of sheets) for (const p of sheet) colocadas[p.nombre] = (colocadas[p.nombre] || 0) + 1;
  const coincide = Object.entries(pedidas).every(([k, v]) => colocadas[k] === v)
    && Object.values(pedidas).reduce((a, b) => a + b, 0) === Object.values(colocadas).reduce((a, b) => a + b, 0);
  return { piezas_pedidas: pedidas, piezas_colocadas: colocadas, coincide };
}

// ── Sheet SVG ─────────────────────────────────────────────────────────────────

function SheetSVG({ sheet, fmtKey, colorMap, displayWidth }) {
  const fmt  = SHEET_FORMATS[fmtKey];
  const scale = displayWidth / fmt.w;
  const svgH  = fmt.h * scale;

  return (
    <svg width={displayWidth} height={svgH}
      style={{ display: "block", border: "1px solid var(--border)", borderRadius: 6, background: "var(--surface)" }}>
      <rect width={displayWidth} height={svgH} fill="var(--surface)" />
      {/* grid every 100mm */}
      {Array.from({ length: Math.floor(fmt.w / 100) - 1 }, (_, i) => (
        <line key={"vg" + i} x1={(i+1)*100*scale} y1={0} x2={(i+1)*100*scale} y2={svgH} stroke="var(--border)" strokeWidth={0.4} />
      ))}
      {Array.from({ length: Math.floor(fmt.h / 100) - 1 }, (_, i) => (
        <line key={"hg" + i} x1={0} y1={(i+1)*100*scale} x2={displayWidth} y2={(i+1)*100*scale} stroke="var(--border)" strokeWidth={0.4} />
      ))}
      {sheet.map((p, i) => {
        const px = p.x * scale, py = p.y * scale;
        const pw = p.w * scale, ph = p.h * scale;
        const color  = colorMap[p.nombre] || "#888";
        const forma  = p.forma || "rectangulo";
        const showLabel = pw > 26 && ph > 14;
        const fontSize  = Math.max(6, Math.min(10, pw / 6, ph / 2.5));
        return (
          <g key={i}>
            <ShapeSVGPath forma={forma} px={px} py={py} pw={pw} ph={ph}
              fill={color + "40"} stroke={color} />
            {showLabel && (
              <text x={px + pw/2} y={py + ph/2} textAnchor="middle" dominantBaseline="middle"
                fontSize={fontSize} fill={color}
                style={{ fontFamily:"var(--body)", fontWeight:600, userSelect:"none", pointerEvents:"none" }}>
                {p.nombre.length > 13 ? p.nombre.slice(0,11)+"…" : p.nombre}
              </text>
            )}
            {showLabel && ph > fontSize * 3.5 && (
              <text x={px + pw/2} y={py + ph/2 + fontSize + 2} textAnchor="middle" dominantBaseline="middle"
                fontSize={Math.max(5, fontSize - 1)} fill={color + "aa"}
                style={{ fontFamily:"var(--mono)", userSelect:"none", pointerEvents:"none" }}>
                {p.w}×{p.h}
              </text>
            )}
          </g>
        );
      })}
      <text x={4} y={svgH - 4} fontSize={8} fill="var(--faint)" style={{ fontFamily:"var(--mono)" }}>
        {fmt.w} × {fmt.h} mm
      </text>
    </svg>
  );
}

function SheetGrid({ sheets, fmtKey, colorMap }) {
  const svgW = 340;
  return (
    <div style={{ display:"grid", gridTemplateColumns:"repeat(auto-fill, minmax("+svgW+"px, 1fr))", gap:16 }}>
      {sheets.map((sheet, idx) => (
        <div key={idx} style={{ background:"var(--panel)", borderRadius:10, border:"1px solid var(--border)", overflow:"hidden" }}>
          <div style={{ padding:"8px 12px", background:"var(--bg2)", borderBottom:"1px solid var(--border)",
            display:"flex", justifyContent:"space-between", alignItems:"center", fontSize:11 }}>
            <span style={{ fontWeight:700 }}>Lámina {idx+1}</span>
            <span style={{ color:"var(--muted)", fontFamily:"var(--mono)" }}>{sheet.length} pieza{sheet.length!==1?"s":""}</span>
          </div>
          <div style={{ padding:10 }}>
            <SheetSVG sheet={sheet} fmtKey={fmtKey} colorMap={colorMap} displayWidth={svgW-20} />
          </div>
        </div>
      ))}
    </div>
  );
}

// ── Input helpers ──────────────────────────────────────────────────────────────

function DField({ label, value, onChange, placeholder, hint }) {
  return (
    <div style={{ display:"flex", flexDirection:"column", gap:4 }}>
      <label style={{ fontSize:11, fontWeight:600, color:"var(--muted)", textTransform:"uppercase", letterSpacing:".04em" }}>{label}</label>
      <input type="number" value={value} onChange={e => onChange(e.target.value)} placeholder={placeholder || "0"}
        style={{ padding:"7px 10px", borderRadius:7, border:"1px solid var(--border)", background:"var(--surface)", color:"var(--text)", fontSize:13, width:"100%" }} />
      {hint && <div style={{ fontSize:10, color:"var(--faint)" }}>{hint}</div>}
    </div>
  );
}

// ── Shape preview mini SVG (in the form) ─────────────────────────────────────

function ShapePreview({ forma, dims }) {
  const W = 56, H = 56;
  const bb   = getBoundingBox(forma, dims);
  const valid = bb.w > 0 && bb.h > 0;
  if (!valid) return <div style={{ width:W, height:H, border:"1px dashed var(--border)", borderRadius:6, display:"flex", alignItems:"center", justifyContent:"center", fontSize:20, color:"var(--faint)" }}>{SHAPES[forma]?.icon || "?"}</div>;

  const scale  = Math.min((W - 8) / bb.w, (H - 8) / bb.h);
  const pw     = bb.w * scale, ph = bb.h * scale;
  const ox     = (W - pw) / 2, oy = (H - ph) / 2;
  return (
    <svg width={W} height={H} style={{ border:"1px solid var(--border)", borderRadius:6, background:"var(--surface)", display:"block" }}>
      <ShapeSVGPath forma={forma} px={ox} py={oy} pw={pw} ph={ph} fill="var(--gold)44" stroke="var(--gold)" />
    </svg>
  );
}

// ── Main view ──────────────────────────────────────────────────────────────────

const despUid = () => Math.random().toString(36).slice(2, 9);
const DCOL = () => window.DB.collection(window.QG_COLS.despieces);
const DTS  = () => firebase.firestore.FieldValue.serverTimestamp();

const BLANK_DIMS = { ancho: "", alto: "", diametro: "", lado: "", area_real: "" };

function DespieceView() {
  const [formato,      setFormato]      = React.useState("auto");
  const [kerf,         setKerf]         = React.useState("3");
  const [allowRot,     setAllowRot]     = React.useState(false);
  const [piezas,       setPiezas]       = React.useState([]);
  const [prices,       setPrices]       = React.useState({ A:"", B:"", C:"", D:"" });
  const [results,      setResults]      = React.useState(null);
  const [calcError,    setCalcError]    = React.useState(null);
  const [activeResult, setActiveResult] = React.useState(null);

  const [forma,   setForma]   = React.useState("rectangulo");
  const [nombre,  setNombre]  = React.useState("");
  const [dims,    setDims]    = React.useState({ ...BLANK_DIMS });
  const [cant,    setCant]    = React.useState("1");
  const [editId,  setEditId]  = React.useState(null);

  // ── Proyectos guardados ──────────────────────────────────────────────────────
  const [savedProjects,  setSavedProjects]  = React.useState([]);
  const [saveModalOpen,  setSaveModalOpen]  = React.useState(false);
  const [saveNombre,     setSaveNombre]     = React.useState("");
  const [activeProjId,   setActiveProjId]   = React.useState(null);
  const [saving,         setSaving]         = React.useState(false);

  React.useEffect(() => {
    const unsub = DCOL().orderBy("_ts", "desc").onSnapshot(
      snap => setSavedProjects(snap.docs.map(d => ({ id: d.id, ...d.data() }))),
      () => {}
    );
    return unsub;
  }, []);

  const loadProject = (proj) => {
    setFormato(proj.formato || "auto");
    setKerf(String(proj.kerf ?? "3"));
    setAllowRot(proj.allowRot ?? false);
    setPrices(proj.prices || { A:"", B:"", C:"", D:"" });
    setPiezas((proj.piezas || []).map(p => ({ ...p, id: despUid() })));
    setResults(null); setCalcError(null); setActiveProjId(proj.id);
    setEditId(null); setNombre(""); setDims({ ...BLANK_DIMS }); setCant("1"); setForma("rectangulo");
  };

  const saveProject = async () => {
    const n = saveNombre.trim();
    if (!n || piezas.length === 0) return;
    setSaving(true);
    try {
      const data = { nombre: n, piezas, formato, kerf, allowRot, prices, _ts: DTS() };
      if (activeProjId) {
        await DCOL().doc(activeProjId).set(data);
      } else {
        const ref = await DCOL().add(data);
        setActiveProjId(ref.id);
      }
      setSaveModalOpen(false); setSaveNombre("");
    } catch(e) { alert("Error al guardar: " + e.message); }
    setSaving(false);
  };

  const deleteProject = async (id, e) => {
    e.stopPropagation();
    if (!confirm("¿Eliminar este proyecto?")) return;
    await DCOL().doc(id).delete();
    if (activeProjId === id) setActiveProjId(null);
  };

  const openSaveModal = () => {
    const active = savedProjects.find(p => p.id === activeProjId);
    setSaveNombre(active ? active.nombre : "");
    setSaveModalOpen(true);
  };

  const setD = (k, v) => setDims(d => ({ ...d, [k]: v }));

  // Hint for current shape's bounding box
  const dimsParsed = React.useMemo(() => {
    const n = (k) => parseFloat(dims[k]) || 0;
    return { ancho: n("ancho"), alto: n("alto"), diametro: n("diametro"), lado: n("lado"), area_real: n("area_real") };
  }, [dims]);
  const bbHint = getBBHint(forma, dimsParsed);

  const commitPiece = () => {
    const n = nombre.trim();
    const bb = getBoundingBox(forma, dimsParsed);
    if (!n || bb.w <= 0 || bb.h <= 0) return;
    const piece = { id: editId || despUid(), nombre: n, forma, dims: { ...dimsParsed }, cantidad: Math.max(1, parseInt(cant) || 1) };
    if (editId) { setPiezas(ps => ps.map(p => p.id === editId ? piece : p)); setEditId(null); }
    else setPiezas(ps => [...ps, piece]);
    setNombre(""); setDims({ ...BLANK_DIMS }); setCant("1"); if (!editId) setForma("rectangulo");
  };

  const startEdit = (p) => {
    setEditId(p.id); setForma(p.forma); setNombre(p.nombre);
    setDims({ ...BLANK_DIMS, ...Object.fromEntries(Object.entries(p.dims).map(([k,v]) => [k, String(v)])) });
    setCant(String(p.cantidad));
  };

  const cancelEdit = () => { setEditId(null); setNombre(""); setDims({ ...BLANK_DIMS }); setCant("1"); };

  const removePieza = (id) => { setPiezas(ps => ps.filter(p => p.id !== id)); if (editId === id) cancelEdit(); };

  // Color + shape map by piece name
  const colorMap = React.useMemo(() => {
    const map = {}, seen = [];
    for (const p of piezas) {
      if (map[p.nombre] === undefined) {
        if (!seen.includes(p.nombre)) seen.push(p.nombre);
        map[p.nombre] = PIECE_PALETTE[seen.indexOf(p.nombre) % PIECE_PALETTE.length];
      }
    }
    return map;
  }, [piezas]);

  const shapeMap = React.useMemo(() => {
    const map = {};
    for (const p of piezas) map[p.nombre] = p.forma;
    return map;
  }, [piezas]);

  const calculate = () => {
    if (piezas.length === 0) { setCalcError("Agrega al menos una pieza."); return; }
    setCalcError(null); setResults(null);
    const kerfN = Math.max(0, parseFloat(kerf) || 3);
    const fmts  = formato === "auto" ? ["A","B","C","D"] : [formato];
    const byFmt = {};
    for (const f of fmts) byFmt[f] = optimizeFormat(piezas, f, kerfN, allowRot);
    const valid = fmts.filter(f => !byFmt[f].error);
    if (valid.length === 0) { setCalcError(fmts.map(f => byFmt[f].error).filter(Boolean).join(" | ")); return; }

    let recommended;
    if (formato !== "auto") {
      recommended = formato;
    } else {
      const hasPrices = valid.some(f => parseFloat(prices[f]) > 0);
      if (hasPrices) {
        recommended = valid.slice().sort((a, b) => {
          const ca = (byFmt[a].laminas||999) * (parseFloat(prices[a])||0);
          const cb = (byFmt[b].laminas||999) * (parseFloat(prices[b])||0);
          if (ca===0&&cb===0) return (byFmt[a].laminas||999)-(byFmt[b].laminas||999);
          return ca===0?1:cb===0?-1:ca-cb;
        })[0];
      } else {
        recommended = valid.slice().sort((a, b) => (byFmt[a].laminas||999)-(byFmt[b].laminas||999))[0];
      }
    }
    const mainResult = byFmt[recommended];
    const validation = mainResult && !mainResult.error ? buildValidation(piezas, mainResult.sheets) : null;
    setResults({ byFmt, recommended, fmts, validation, kerf: kerfN, piezas:[...piezas] });
    setActiveResult(recommended);
  };

  const totalPiezas = piezas.reduce((s, p) => s + p.cantidad, 0);
  const shapeDims   = SHAPES[forma]?.dims || ["ancho","alto"];

  return (
    <div style={{ padding:"28px 30px", maxWidth:1400, margin:"0 auto" }}>
      <div style={{ marginBottom:24, display:"flex", alignItems:"flex-start", justifyContent:"space-between", gap:16 }}>
        <div>
          <div style={{ display:"flex", alignItems:"center", gap:10, flexWrap:"wrap" }}>
            <h2 style={{ margin:0, fontSize:22, fontWeight:700 }}>Optimizador de Despiece</h2>
            {activeProjId && savedProjects.find(p=>p.id===activeProjId) && (
              <span style={{ fontSize:12, fontWeight:700, padding:"3px 10px", borderRadius:20,
                background:"color-mix(in oklch,var(--gold) 15%,transparent)", color:"var(--gold)",
                border:"1px solid color-mix(in oklch,var(--gold) 30%,transparent)" }}>
                {savedProjects.find(p=>p.id===activeProjId).nombre}
              </span>
            )}
          </div>
          <p style={{ margin:"6px 0 0", color:"var(--muted)", fontSize:13 }}>
            Calcula cuántas láminas PET necesitas y genera el plano de corte.
          </p>
        </div>
        {(piezas.length > 0 || activeProjId) && (
          <button onClick={() => { setPiezas([]); setResults(null); setCalcError(null); setActiveProjId(null); setEditId(null); setNombre(""); setDims({...BLANK_DIMS}); setCant("1"); setForma("rectangulo"); }}
            style={{ padding:"7px 14px", borderRadius:8, border:"1px solid var(--border)", background:"var(--panel)",
              color:"var(--muted)", fontSize:12, fontWeight:600, cursor:"pointer", flexShrink:0, marginTop:4 }}>
            + Nuevo
          </button>
        )}
      </div>

      <div style={{ display:"grid", gridTemplateColumns:"minmax(320px,420px) 1fr", gap:24, alignItems:"start" }}>

        {/* ── LEFT: inputs ─────────────────────────────────────────────── */}
        <div style={{ display:"flex", flexDirection:"column", gap:14 }}>

          {/* ── Proyectos guardados ── */}
          {savedProjects.length > 0 && (
            <div style={{ background:"var(--surface)", border:"1px solid var(--border)", borderRadius:10, overflow:"hidden" }}>
              <div style={{ padding:"10px 14px", borderBottom:"1px solid var(--border)", display:"flex", justifyContent:"space-between", alignItems:"center" }}>
                <span style={{ fontWeight:700, fontSize:13 }}>Proyectos guardados</span>
                <span style={{ fontSize:11, color:"var(--muted)" }}>{savedProjects.length}</span>
              </div>
              <div style={{ maxHeight:180, overflowY:"auto" }}>
                {savedProjects.map(proj => (
                  <div key={proj.id} onClick={() => loadProject(proj)}
                    style={{ display:"flex", alignItems:"center", gap:10, padding:"9px 14px",
                      borderBottom:"1px solid var(--border)", cursor:"pointer",
                      background: activeProjId===proj.id ? "color-mix(in oklch,var(--gold) 10%,transparent)" : "transparent",
                      transition:"background .1s" }}>
                    <div style={{ flex:1, minWidth:0 }}>
                      <div style={{ fontWeight:600, fontSize:13, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap",
                        color: activeProjId===proj.id ? "var(--gold)" : "var(--text)" }}>
                        {proj.nombre}
                      </div>
                      <div style={{ fontSize:10, color:"var(--muted)", marginTop:1 }}>
                        {(proj.piezas||[]).reduce((s,p)=>s+p.cantidad,0)} piezas · fmt {proj.formato==="auto"?"auto":proj.formato}
                      </div>
                    </div>
                    {activeProjId===proj.id && <span style={{ fontSize:10, fontWeight:700, color:"var(--gold)" }}>activo</span>}
                    <button onClick={e => deleteProject(proj.id, e)}
                      style={{ background:"none", border:"none", color:"var(--muted)", cursor:"pointer", fontSize:13, padding:"2px 4px", lineHeight:1, flexShrink:0 }}
                      title="Eliminar">✕</button>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* Formato + opciones */}
          <div style={{ background:"var(--surface)", border:"1px solid var(--border)", borderRadius:10, padding:18 }}>
            <div style={{ fontWeight:700, fontSize:13, marginBottom:14 }}>Configuración</div>
            <div style={{ marginBottom:12 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"var(--muted)", textTransform:"uppercase", letterSpacing:".04em", marginBottom:6 }}>Formato de lámina</div>
              <div style={{ display:"flex", flexWrap:"wrap", gap:6 }}>
                {["auto","A","B","C","D"].map(f => (
                  <button key={f} onClick={() => setFormato(f)}
                    style={{ padding:"5px 12px", borderRadius:7, fontSize:12, fontWeight:600, cursor:"pointer",
                      border:"1px solid "+(formato===f?"var(--gold)":"var(--border)"),
                      background:formato===f?"var(--gold)":"var(--panel)",
                      color:formato===f?"var(--accent-ink)":"var(--text)" }}>
                    {f==="auto"?"Auto":f}
                  </button>
                ))}
              </div>
              {formato!=="auto" && <div style={{ fontSize:11, color:"var(--muted)", marginTop:5 }}>{SHEET_FORMATS[formato].label}</div>}
              {formato==="auto" && <div style={{ fontSize:11, color:"var(--muted)", marginTop:5 }}>Evalúa A, B, C y D — recomienda el mejor.</div>}
            </div>
            <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:12 }}>
              <DField label="Kerf (mm)" value={kerf} onChange={setKerf} placeholder="3" />
            </div>
            <label style={{ display:"flex", alignItems:"center", gap:8, cursor:"pointer", fontSize:13 }}>
              <input type="checkbox" checked={allowRot} onChange={e => setAllowRot(e.target.checked)}
                style={{ width:15, height:15, accentColor:"var(--gold)", cursor:"pointer" }} />
              <span>Permitir rotación 90°</span>
            </label>
            <div style={{ fontSize:11, color:"var(--muted)", marginTop:3, paddingLeft:23 }}>Desactivado por defecto (PET con veta/relieve).</div>

            {formato==="auto" && (
              <div style={{ marginTop:14, paddingTop:14, borderTop:"1px solid var(--border)" }}>
                <div style={{ fontSize:11, fontWeight:600, color:"var(--muted)", textTransform:"uppercase", letterSpacing:".04em", marginBottom:8 }}>Precio por lámina (opcional)</div>
                <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:10 }}>
                  {["A","B","C","D"].map(f => (
                    <DField key={f} label={"Formato "+f} value={prices[f]} placeholder="0.00"
                      onChange={v => setPrices(pr => ({ ...pr, [f]:v }))} />
                  ))}
                </div>
              </div>
            )}
          </div>

          {/* Piece form */}
          <div style={{ background:"var(--surface)", border:"1px solid var(--border)", borderRadius:10, padding:18 }}>
            <div style={{ fontWeight:700, fontSize:13, marginBottom:14 }}>{editId ? "Editar pieza" : "Agregar pieza"}</div>

            {/* Shape selector */}
            <div style={{ marginBottom:12 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"var(--muted)", textTransform:"uppercase", letterSpacing:".04em", marginBottom:6 }}>Forma</div>
              <div style={{ display:"flex", flexWrap:"wrap", gap:5 }}>
                {Object.entries(SHAPES).map(([k, s]) => (
                  <button key={k} onClick={() => { setForma(k); setDims({ ...BLANK_DIMS }); }}
                    title={s.label}
                    style={{ padding:"5px 10px", borderRadius:7, fontSize:12, fontWeight:600, cursor:"pointer",
                      display:"flex", alignItems:"center", gap:5,
                      border:"1px solid "+(forma===k?"var(--gold)":"var(--border)"),
                      background:forma===k?"var(--gold)":"var(--panel)",
                      color:forma===k?"var(--accent-ink)":"var(--text)" }}>
                    <span style={{ fontSize:14 }}>{s.icon}</span>
                    <span style={{ fontSize:11 }}>{s.label}</span>
                  </button>
                ))}
              </div>
            </div>

            {/* Nombre + preview */}
            <div style={{ display:"flex", gap:10, alignItems:"flex-end", marginBottom:10 }}>
              <div style={{ flex:1 }}>
                <div style={{ fontSize:11, fontWeight:600, color:"var(--muted)", textTransform:"uppercase", letterSpacing:".04em", marginBottom:4 }}>Nombre / referencia</div>
                <input value={nombre} onChange={e => setNombre(e.target.value)} placeholder="Panel lateral"
                  style={{ padding:"7px 10px", borderRadius:7, border:"1px solid var(--border)", background:"var(--surface)", color:"var(--text)", fontSize:13, width:"100%" }} />
              </div>
              <ShapePreview forma={forma} dims={dimsParsed} />
            </div>

            {/* Dimension inputs */}
            <div style={{ display:"grid", gridTemplateColumns:"repeat(auto-fill,minmax(110px,1fr))", gap:10, marginBottom:10 }}>
              {shapeDims.map(dk => (
                <DField key={dk} label={DIM_LABELS[dk] || dk} value={dims[dk] || ""} onChange={v => setD(dk, v)}
                  hint={dk==="area_real" ? "Opcional si dejas 0 se usa la caja" : null} />
              ))}
              <DField label="Cantidad" value={cant} onChange={setCant} placeholder="1" />
            </div>

            {/* Bounding box hint */}
            {bbHint && dimsParsed[SHAPES[forma]?.dims[0]] > 0 && (
              <div style={{ fontSize:11, color:"var(--muted)", background:"var(--bg2)", padding:"6px 10px", borderRadius:6, marginBottom:10 }}>
                ℹ️ {bbHint}
              </div>
            )}

            <div style={{ display:"flex", gap:8 }}>
              <button onClick={commitPiece}
                style={{ flex:1, padding:"8px 0", borderRadius:8, border:"none", background:"var(--gold)", color:"var(--accent-ink)", fontWeight:700, fontSize:13, cursor:"pointer" }}>
                {editId ? "Guardar cambios" : "+ Agregar"}
              </button>
              {editId && (
                <button onClick={cancelEdit}
                  style={{ padding:"8px 16px", borderRadius:8, border:"1px solid var(--border)", background:"var(--panel)", color:"var(--muted)", fontWeight:600, fontSize:13, cursor:"pointer" }}>
                  Cancelar
                </button>
              )}
            </div>
          </div>

          {/* Piece list */}
          {piezas.length > 0 && (
            <div style={{ background:"var(--surface)", border:"1px solid var(--border)", borderRadius:10, overflow:"hidden" }}>
              <div style={{ padding:"10px 14px", borderBottom:"1px solid var(--border)", display:"flex", justifyContent:"space-between", alignItems:"center" }}>
                <span style={{ fontWeight:700, fontSize:13 }}>Lista de piezas</span>
                <span style={{ fontSize:11, color:"var(--muted)" }}>{totalPiezas} unidades</span>
              </div>
              <div style={{ maxHeight:260, overflowY:"auto" }}>
                <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                  <thead>
                    <tr style={{ background:"var(--bg2)" }}>
                      {["","Nombre","Forma","Caja (mm)","Cant.",""].map((h,i) => (
                        <th key={i} style={{ padding:"6px 8px", textAlign:i<=1?"left":"center", fontWeight:600, color:"var(--muted)", fontSize:10 }}>{h}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {piezas.map(p => {
                      const bb = getBoundingBox(p.forma, p.dims);
                      return (
                        <tr key={p.id} style={{ borderBottom:"1px solid var(--border)" }}>
                          <td style={{ padding:"6px 8px", width:12 }}>
                            <div style={{ width:10, height:10, borderRadius:3, background:colorMap[p.nombre]||"#888" }} />
                          </td>
                          <td style={{ padding:"6px 8px", fontWeight:500 }}>{p.nombre}</td>
                          <td style={{ padding:"6px 8px", textAlign:"center" }}>
                            <span title={SHAPES[p.forma]?.label}>{SHAPES[p.forma]?.icon}</span>
                          </td>
                          <td style={{ padding:"6px 8px", textAlign:"center", fontFamily:"var(--mono)", fontSize:11 }}>{bb.w}×{bb.h}</td>
                          <td style={{ padding:"6px 8px", textAlign:"center", fontFamily:"var(--mono)" }}>{p.cantidad}</td>
                          <td style={{ padding:"6px 6px", whiteSpace:"nowrap" }}>
                            <button onClick={() => startEdit(p)} style={{ background:"none", border:"none", color:"var(--muted)", cursor:"pointer", fontSize:12, padding:"0 3px" }}>✏️</button>
                            <button onClick={() => removePieza(p.id)} style={{ background:"none", border:"none", color:"var(--red)", cursor:"pointer", fontSize:12, padding:"0 3px" }}>✕</button>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            </div>
          )}

          <div style={{ display:"flex", gap:8 }}>
            <button onClick={calculate} disabled={piezas.length===0}
              style={{ flex:1, padding:"13px 0", borderRadius:10, border:"none",
                background:piezas.length===0?"var(--border)":"var(--gold)",
                color:piezas.length===0?"var(--muted)":"var(--accent-ink)",
                fontWeight:700, fontSize:14, cursor:piezas.length===0?"not-allowed":"pointer" }}>
              Calcular despiece
            </button>
            <button onClick={openSaveModal} disabled={piezas.length===0}
              title="Guardar proyecto"
              style={{ padding:"13px 16px", borderRadius:10, border:"1px solid var(--border)",
                background:"var(--panel)", color:piezas.length===0?"var(--faint)":"var(--text)",
                fontWeight:600, fontSize:13, cursor:piezas.length===0?"not-allowed":"pointer",
                display:"flex", alignItems:"center", gap:6, flexShrink:0 }}>
              💾 Guardar
            </button>
          </div>

          {calcError && (
            <div style={{ padding:"12px 14px", borderRadius:8, background:"rgba(194,73,47,.1)", border:"1px solid var(--red)", color:"var(--red)", fontSize:12 }}>
              {calcError}
            </div>
          )}

          {/* ── Save modal ── */}
          {saveModalOpen && (
            <div style={{ position:"fixed", inset:0, background:"rgba(0,0,0,.45)", zIndex:200,
              display:"flex", alignItems:"center", justifyContent:"center" }}
              onClick={() => setSaveModalOpen(false)}>
              <div onClick={e => e.stopPropagation()}
                style={{ background:"var(--panel)", border:"1px solid var(--border)", borderRadius:14,
                  padding:28, width:340, boxShadow:"0 20px 60px rgba(0,0,0,.25)" }}>
                <div style={{ fontWeight:700, fontSize:16, marginBottom:16 }}>
                  {activeProjId ? "Actualizar proyecto" : "Guardar proyecto"}
                </div>
                <div style={{ fontSize:11, fontWeight:600, color:"var(--muted)", textTransform:"uppercase", letterSpacing:".04em", marginBottom:6 }}>
                  Nombre del proyecto
                </div>
                <input
                  autoFocus
                  value={saveNombre}
                  onChange={e => setSaveNombre(e.target.value)}
                  onKeyDown={e => e.key==="Enter" && saveProject()}
                  placeholder="Ej: Cocina apartamento 4B"
                  style={{ width:"100%", padding:"9px 12px", borderRadius:8, border:"1px solid var(--border)",
                    background:"var(--surface)", color:"var(--text)", fontSize:14, boxSizing:"border-box" }} />
                <div style={{ fontSize:11, color:"var(--muted)", marginTop:6 }}>
                  {piezas.length} tipo{piezas.length!==1?"s":""} · {piezas.reduce((s,p)=>s+p.cantidad,0)} piezas · formato {formato}
                </div>
                <div style={{ display:"flex", gap:8, marginTop:18 }}>
                  <button onClick={saveProject} disabled={saving || !saveNombre.trim()}
                    style={{ flex:1, padding:"10px 0", borderRadius:8, border:"none",
                      background:!saveNombre.trim()?"var(--border)":"var(--gold)",
                      color:!saveNombre.trim()?"var(--muted)":"var(--accent-ink)",
                      fontWeight:700, fontSize:13, cursor:!saveNombre.trim()?"not-allowed":"pointer" }}>
                    {saving ? "Guardando…" : (activeProjId ? "Actualizar" : "Guardar")}
                  </button>
                  <button onClick={() => setSaveModalOpen(false)}
                    style={{ padding:"10px 16px", borderRadius:8, border:"1px solid var(--border)",
                      background:"var(--panel)", color:"var(--muted)", fontWeight:600, fontSize:13, cursor:"pointer" }}>
                    Cancelar
                  </button>
                </div>
              </div>
            </div>
          )}
        </div>

        {/* ── RIGHT: results ────────────────────────────────────────────── */}
        <div>
          {!results && (
            <div style={{ display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center",
              minHeight:320, border:"1.5px dashed var(--border)", borderRadius:12, padding:40, textAlign:"center" }}>
              <div style={{ fontSize:40 }}>✂️</div>
              <div style={{ fontSize:14, color:"var(--muted)", marginTop:12 }}>
                Agrega tus piezas y pulsa <strong>Calcular despiece</strong>.
              </div>
              <div style={{ fontSize:12, color:"var(--faint)", marginTop:6 }}>
                Soporta rectángulos, círculos, hexágonos, triángulos y formas libres.
              </div>
            </div>
          )}

          {results && (
            <div style={{ display:"flex", flexDirection:"column", gap:16 }}>

              {/* ── Resultado destacado ── */}
              {(() => {
                const rec = results.byFmt[results.recommended];
                return (
                  <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:12 }}>
                    <div style={{ background:"var(--gold)", borderRadius:12, padding:"16px 20px" }}>
                      <div style={{ fontSize:10, fontWeight:700, textTransform:"uppercase", letterSpacing:".07em", color:"rgba(0,0,0,.5)", marginBottom:4 }}>Láminas necesarias</div>
                      <div style={{ fontSize:36, fontWeight:800, color:"var(--accent-ink)", lineHeight:1 }}>{rec.laminas}</div>
                      <div style={{ fontSize:11, color:"rgba(0,0,0,.55)", marginTop:4 }}>Formato {results.recommended} · {SHEET_FORMATS[results.recommended].thick}</div>
                    </div>
                    <div style={{ background:"var(--surface)", border:"1px solid var(--border)", borderRadius:12, padding:"16px 20px" }}>
                      <div style={{ fontSize:10, fontWeight:700, textTransform:"uppercase", letterSpacing:".07em", color:"var(--muted)", marginBottom:4 }}>Aprovechamiento</div>
                      <div style={{ fontSize:36, fontWeight:800, lineHeight:1,
                        color: rec.aprovechamiento >= 70 ? "var(--green)" : rec.aprovechamiento >= 50 ? "var(--amber)" : "var(--red)" }}>
                        {rec.aprovechamiento}%
                      </div>
                      <div style={{ fontSize:11, color:"var(--muted)", marginTop:4 }}>Área real / área láminas</div>
                    </div>
                    <div style={{ background:"var(--surface)", border:"1px solid var(--border)", borderRadius:12, padding:"16px 20px" }}>
                      <div style={{ fontSize:10, fontWeight:700, textTransform:"uppercase", letterSpacing:".07em", color:"var(--muted)", marginBottom:4 }}>Total piezas</div>
                      <div style={{ fontSize:36, fontWeight:800, lineHeight:1, color:"var(--text)" }}>
                        {results.piezas.reduce((s,p)=>s+p.cantidad,0)}
                      </div>
                      <div style={{ fontSize:11, color:"var(--muted)", marginTop:4 }}>{results.piezas.length} tipo{results.piezas.length!==1?"s":""} distintos</div>
                    </div>
                  </div>
                );
              })()}

              {/* Summary table */}
              <div style={{ background:"var(--surface)", border:"1px solid var(--border)", borderRadius:10, overflow:"hidden" }}>
                <div style={{ padding:"12px 16px", borderBottom:"1px solid var(--border)", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                  <span style={{ fontWeight:700, fontSize:14 }}>Comparativa de formatos</span>
                  {results.validation && (
                    <span style={{ fontSize:11, fontWeight:700, padding:"3px 10px", borderRadius:20,
                      background:results.validation.coincide?"rgba(34,197,94,.12)":"rgba(194,73,47,.12)",
                      color:results.validation.coincide?"var(--green)":"var(--red)",
                      border:"1px solid "+(results.validation.coincide?"var(--green)":"var(--red)")+"44" }}>
                      {results.validation.coincide ? "✓ Validación correcta" : "⚠ Error de validación"}
                    </span>
                  )}
                </div>
                <table style={{ width:"100%", borderCollapse:"collapse", fontSize:13 }}>
                  <thead>
                    <tr style={{ background:"var(--bg2)" }}>
                      {["FORMATO","LÁMINAS","APROVECHAMIENTO","COSTO TOTAL"].map((h,i) => (
                        <th key={i} style={{ padding:"7px 12px", textAlign:i===0?"left":i===3?"right":"center", fontWeight:600, color:"var(--muted)", fontSize:10 }}>{h}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {results.fmts.map(f => {
                      const r    = results.byFmt[f];
                      const isRec = f===results.recommended;
                      const cost = r.laminas!=null && parseFloat(prices[f])>0
                        ? (r.laminas*parseFloat(prices[f])).toLocaleString("es-PA",{minimumFractionDigits:2,maximumFractionDigits:2})
                        : null;
                      return (
                        <tr key={f} onClick={() => !r.error && setActiveResult(f)}
                          style={{ borderBottom:"1px solid var(--border)", cursor:r.error?"default":"pointer",
                            background:activeResult===f?"color-mix(in oklch,var(--gold) 8%,transparent)":"transparent" }}>
                          <td style={{ padding:"9px 12px" }}>
                            <div style={{ display:"flex", alignItems:"center", gap:7 }}>
                              {isRec && <span style={{ fontSize:10, fontWeight:700, padding:"2px 6px", borderRadius:5, background:"var(--gold)", color:"var(--accent-ink)" }}>★ REC.</span>}
                              <span style={{ fontWeight:isRec?700:500 }}>{SHEET_FORMATS[f].label}</span>
                            </div>
                          </td>
                          <td style={{ padding:"9px 12px", textAlign:"center", fontFamily:"var(--mono)", fontWeight:isRec?700:400 }}>
                            {r.error ? <span style={{ color:"var(--red)", fontSize:11 }}>No cabe</span> : r.laminas}
                          </td>
                          <td style={{ padding:"9px 12px", textAlign:"center" }}>
                            {r.error ? "—" : (
                              <div style={{ display:"flex", alignItems:"center", justifyContent:"center", gap:7 }}>
                                <div style={{ width:56, height:6, borderRadius:3, background:"var(--border)" }}>
                                  <div style={{ width:r.aprovechamiento+"%", height:"100%", borderRadius:3,
                                    background:r.aprovechamiento>=70?"var(--green)":r.aprovechamiento>=50?"var(--amber)":"var(--red)" }} />
                                </div>
                                <span style={{ fontFamily:"var(--mono)", fontSize:12 }}>{r.aprovechamiento}%</span>
                              </div>
                            )}
                          </td>
                          <td style={{ padding:"9px 12px", textAlign:"right", fontFamily:"var(--mono)", fontSize:12 }}>
                            {cost ? "$"+cost : "—"}
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
                <div style={{ padding:"8px 14px", fontSize:11, color:"var(--faint)", borderTop:"1px solid var(--border)" }}>
                  El % de aprovechamiento usa el área real de cada forma (no la caja envolvente). Las piezas se empacan por su caja envolvente.
                </div>
              </div>

              {/* Legend */}
              <div style={{ display:"flex", flexWrap:"wrap", gap:7 }}>
                {results.piezas.map(p => {
                  const bb = getBoundingBox(p.forma, p.dims);
                  return (
                    <div key={p.id} style={{ display:"flex", alignItems:"center", gap:6, padding:"4px 10px", borderRadius:20,
                      border:"1px solid var(--border)", background:"var(--surface)", fontSize:11 }}>
                      <div style={{ width:10, height:10, borderRadius:2, background:colorMap[p.nombre]||"#888", flexShrink:0 }} />
                      <span style={{ fontSize:14 }}>{SHAPES[p.forma]?.icon}</span>
                      <span style={{ fontWeight:600 }}>{p.nombre}</span>
                      <span style={{ color:"var(--muted)" }}>×{p.cantidad}</span>
                      <span style={{ color:"var(--faint)", fontFamily:"var(--mono)", fontSize:10 }}>{bb.w}×{bb.h}</span>
                    </div>
                  );
                })}
              </div>

              {/* Sheet plans */}
              {activeResult && results.byFmt[activeResult] && !results.byFmt[activeResult].error && (
                <div>
                  <div style={{ fontWeight:700, fontSize:12, marginBottom:12, color:"var(--muted)", textTransform:"uppercase", letterSpacing:".05em" }}>
                    Plano de corte · Formato {activeResult} · {results.byFmt[activeResult].laminas} lámina{results.byFmt[activeResult].laminas!==1?"s":""}
                  </div>
                  <SheetGrid sheets={results.byFmt[activeResult].sheets} fmtKey={activeResult}
                    colorMap={colorMap} />
                </div>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

window.DespieceView = DespieceView;
