/* Cubi — mascota + chatbot de m3-cargo.
   Exporta:
     window.CubiArt   → { cube, box, container, chip }  (SVG strings, parametrizables por expresión/pose)
     window.Cubi      → <Cubi context="public|portal" name? /> widget de chat flotante
   La mascota deriva del isotipo m³ (cubo isométrico: tapa lima, cara blanca, lado cobalto). */

(function (g) {
  const { useState, useRef, useEffect } = React;

  /* ---------------- Paleta ---------------- */
  const C = {
    lime: "#84CC16", lime3: "#BEF264", lime7: "#4D7C0F",
    blue: "#1D4ED8", blue4: "#6090FA", blue5: "#3B6DF5",
    navy: "#0A1A2F", navy8: "#0F2440",
    white: "#FFFFFF", ink: "#0A1A2F", kraft: "#D8A965", kraft2: "#C2904E", kraftHi: "#E7C796",
  };

  /* ---------------- Cara (ojos + boca), eje frontal ---------------- */
  function face(cx, cy, expr, ink) {
    ink = ink || C.navy;
    const dot = (x, y, r) =>
      `<circle cx="${x}" cy="${y}" r="${r}" fill="${ink}"/><circle cx="${x + r * 0.36}" cy="${y - r * 0.36}" r="${r * 0.3}" fill="#fff"/>`;
    const ex = 6.2; // separación ojos
    let eyes, mouth;
    switch (expr) {
      case "wink":
        eyes = dot(cx - ex, cy, 2.7) +
          `<path d="M${cx + ex - 3} ${cy} Q${cx + ex} ${cy - 3.4} ${cx + ex + 3} ${cy}" stroke="${ink}" stroke-width="2.2" fill="none" stroke-linecap="round"/>`;
        mouth = `<path d="M${cx - 4.5} ${cy + 5.5} Q${cx} ${cy + 10} ${cx + 4.5} ${cy + 5.5}" stroke="${ink}" stroke-width="2.4" fill="none" stroke-linecap="round"/>`;
        break;
      case "excited":
        eyes = dot(cx - ex, cy - 0.5, 3.1) + dot(cx + ex, cy - 0.5, 3.1);
        mouth = `<path d="M${cx - 5} ${cy + 4.5} Q${cx} ${cy + 13} ${cx + 5} ${cy + 4.5} Z" fill="${ink}"/>`;
        break;
      case "thinking":
        eyes = dot(cx - ex, cy - 1.2, 2.6) + dot(cx + ex, cy - 1.2, 2.6);
        mouth = `<path d="M${cx - 3.5} ${cy + 6.5} Q${cx} ${cy + 4.5} ${cx + 3.5} ${cy + 6.5}" stroke="${ink}" stroke-width="2.2" fill="none" stroke-linecap="round"/>`;
        break;
      case "surprised":
        eyes = dot(cx - ex, cy, 2.9) + dot(cx + ex, cy, 2.9);
        mouth = `<circle cx="${cx}" cy="${cy + 7}" r="2.7" fill="${ink}"/>`;
        break;
      case "sleeping":
        eyes = `<path d="M${cx - ex - 3} ${cy} Q${cx - ex} ${cy + 3} ${cx - ex + 3} ${cy}" stroke="${ink}" stroke-width="2.2" fill="none" stroke-linecap="round"/>` +
          `<path d="M${cx + ex - 3} ${cy} Q${cx + ex} ${cy + 3} ${cx + ex + 3} ${cy}" stroke="${ink}" stroke-width="2.2" fill="none" stroke-linecap="round"/>`;
        mouth = `<circle cx="${cx}" cy="${cy + 7}" r="1.8" fill="${ink}"/>` +
          `<text x="${cx + 14}" y="${cy - 6}" font-family="Sora,sans-serif" font-size="7" font-weight="800" fill="${ink}">z</text>`;
        break;
      default: // happy
        eyes = dot(cx - ex, cy, 2.9) + dot(cx + ex, cy, 2.9);
        mouth = `<path d="M${cx - 5} ${cy + 5} Q${cx} ${cy + 10.5} ${cx + 5} ${cy + 5}" stroke="${ink}" stroke-width="2.4" fill="none" stroke-linecap="round"/>`;
    }
    return eyes + mouth;
  }

  /* ---------------- Cuerpo A: Cubo isométrico (on-brand, = isotipo) ---------------- */
  function cube(size, expr, opts) {
    expr = expr || "happy"; opts = opts || {};
    const arm = opts.wave
      ? `<g stroke="${C.navy}" stroke-width="4.4" stroke-linecap="round"><path d="M96 58 L110 42"/></g><circle cx="111" cy="40" r="4" fill="${C.navy}"/>`
      : "";
    return `<svg width="${size}" height="${size}" viewBox="0 0 124 132" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Cubi">
      <ellipse cx="62" cy="120" rx="34" ry="5" fill="#0A1A2F" opacity="0.10"/>
      <g stroke="${C.navy}" stroke-width="5" stroke-linecap="round"><path d="M48 100 L48 112"/><path d="M76 100 L76 112"/></g>
      <path d="M62 18 L96 36 L62 54 L28 36 Z" fill="${C.lime}"/>
      <path d="M28 36 L62 54 L62 98 L28 80 Z" fill="${C.blue}"/>
      <path d="M96 36 L62 54 L62 98 L96 80 Z" fill="#fff"/>
      <path d="M62 54 L62 98" stroke="#E5EAF1" stroke-width="1"/>
      ${face(79, 67, expr, C.navy)}
      ${arm}
    </svg>`;
  }

  /* ---------------- Cuerpo B: Caja de cartón (cálido / juguetón) ---------------- */
  function box(size, expr, opts) {
    expr = expr || "happy"; opts = opts || {};
    return `<svg width="${size}" height="${size}" viewBox="0 0 124 132" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Cubi caja">
      <ellipse cx="62" cy="120" rx="33" ry="5" fill="#0A1A2F" opacity="0.10"/>
      <g stroke="${C.navy}" stroke-width="5" stroke-linecap="round"><path d="M50 102 L50 113"/><path d="M74 102 L74 113"/></g>
      <path d="M62 16 L98 34 L62 52 L26 34 Z" fill="${C.kraftHi}"/>
      <path d="M26 34 L62 52 L62 100 L26 82 Z" fill="${C.kraft2}"/>
      <path d="M98 34 L62 52 L62 100 L98 82 Z" fill="${C.kraft}"/>
      <path d="M62 16 L62 52" stroke="${C.kraft2}" stroke-width="1.4" opacity=".6"/>
      <path d="M62 6 L62 52" stroke="${C.lime}" stroke-width="7"/>
      <path d="M44 25 L80 25" stroke="${C.lime}" stroke-width="6" opacity=".9"/>
      ${face(80, 66, expr, C.navy)}
    </svg>`;
  }

  /* ---------------- Cuerpo C: Mini-contenedor (logístico) ---------------- */
  function container(size, expr) {
    expr = expr || "happy";
    let lines = "";
    for (let i = 0; i < 6; i++) lines += `<path d="M${36 + i * 8} 40 L${36 + i * 8} 92" stroke="${C.navy}" stroke-width="1.4" opacity=".18"/>`;
    return `<svg width="${size}" height="${size}" viewBox="0 0 124 132" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Cubi contenedor">
      <ellipse cx="62" cy="118" rx="40" ry="5" fill="#0A1A2F" opacity="0.10"/>
      <rect x="26" y="36" width="72" height="62" rx="7" fill="${C.lime}"/>
      <rect x="26" y="36" width="72" height="13" rx="7" fill="${C.lime7}" opacity=".25"/>
      ${lines}
      <rect x="32" y="100" width="14" height="8" rx="3" fill="${C.navy}"/>
      <rect x="78" y="100" width="14" height="8" rx="3" fill="${C.navy}"/>
      ${face(62, 64, expr, C.navy)}
    </svg>`;
  }

  /* ===================================================================
     CONTENEDOR CUBI — personaje principal (estilo carácter, on-brand)
     Contenedor azul con cara, brazos, piernas y zapatillas.
     =================================================================== */
  const SHADE = (hex, p) => { const n = parseInt(hex.slice(1), 16); let r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255; const f = p < 0 ? 0 : 255, t = Math.abs(p) / 100; r = Math.round((f - r) * t + r); g = Math.round((f - g) * t + g); b = Math.round((f - b) * t + b); return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); };
  let UID = 0;
  const CUBI_BLUE = "#5BA6E6"; // azul claro oficial
  const CC = { bl: CUBI_BLUE, bld: SHADE(CUBI_BLUE, -32), bls: SHADE(CUBI_BLUE, -16), hi: SHADE(CUBI_BLUE, 30), ink: "#0D1B33", shoe: "#F4F7FB", red: "#E53935", tong: "#C62828", lime: "var(--lime-400)" };

  // Normaliza expresiones del widget al set del personaje
  function exprMap(e) {
    return { happy: "feliz", wave: "feliz", excited: "orgulloso", thinking: "enfocado",
      wink: "amigable", surprised: "feliz", sleeping: "dormido", feliz: "feliz",
      enfocado: "enfocado", orgulloso: "orgulloso", amigable: "amigable" }[e] || "feliz";
  }

  // Cara rica (ojos grandes + cejas + boca roja)
  function face2(cx, cy, expr) {
    const ink = CC.ink;
    const eye = (ex) => `<ellipse cx="${ex}" cy="${cy}" rx="10.5" ry="13.5" fill="#fff" stroke="${ink}" stroke-width="1.6"/><circle cx="${ex + 1.5}" cy="${cy + 3}" r="5" fill="${ink}"/><circle cx="${ex + 3.6}" cy="${cy + 0.5}" r="1.8" fill="#fff"/>`;
    const closed = (ex) => `<path d="M${ex - 8} ${cy + 1} Q${ex} ${cy - 7} ${ex + 8} ${cy + 1}" stroke="${ink}" stroke-width="3" fill="none" stroke-linecap="round"/>`;
    const wink = (ex) => `<path d="M${ex - 8} ${cy - 1} Q${ex} ${cy + 5} ${ex + 8} ${cy - 1}" stroke="${ink}" stroke-width="3" fill="none" stroke-linecap="round"/>`;
    const sleep = (ex) => `<path d="M${ex - 8} ${cy} Q${ex} ${cy + 6} ${ex + 8} ${cy}" stroke="${ink}" stroke-width="3" fill="none" stroke-linecap="round"/>`;
    const browL = (a) => `<path d="M${cx - 26} ${cy - 17} Q${cx - 15} ${cy - 20 - a} ${cx - 5} ${cy - 15 + a}" stroke="${ink}" stroke-width="3.4" fill="none" stroke-linecap="round"/>`;
    const browR = (a) => `<path d="M${cx + 5} ${cy - 15 + a} Q${cx + 15} ${cy - 20 - a} ${cx + 26} ${cy - 17}" stroke="${ink}" stroke-width="3.4" fill="none" stroke-linecap="round"/>`;
    const open = `<path d="M${cx - 15} ${cy + 15} Q${cx} ${cy + 19} ${cx + 15} ${cy + 15} Q${cx + 11} ${cy + 33} ${cx} ${cy + 34} Q${cx - 11} ${cy + 33} ${cx - 15} ${cy + 15} Z" fill="${CC.red}" stroke="${ink}" stroke-width="1.4"/><path d="M${cx - 8} ${cy + 27} Q${cx} ${cy + 37} ${cx + 8} ${cy + 27} Z" fill="${CC.tong}"/>`;
    const smirk = `<path d="M${cx - 11} ${cy + 22} Q${cx} ${cy + 18} ${cx + 11} ${cy + 22}" stroke="${ink}" stroke-width="3" fill="none" stroke-linecap="round"/>`;
    switch (expr) {
      case "enfocado": return browL(-7) + browR(-7) + eye(cx - 15) + eye(cx + 15) + smirk;
      case "orgulloso": return closed(cx - 15) + closed(cx + 15) + open;
      case "amigable": return browL(0) + browR(0) + eye(cx - 15) + wink(cx + 15) + open;
      case "dormido": return sleep(cx - 15) + sleep(cx + 15) + `<circle cx="${cx}" cy="${cy + 22}" r="2.4" fill="${ink}"/><text x="${cx + 22}" y="${cy - 8}" font-family="Sora,sans-serif" font-size="9" font-weight="800" fill="${ink}">z</text>`;
      default: return browL(0) + browR(0) + eye(cx - 15) + eye(cx + 15) + open;
    }
  }

  // Accesorios sobre la "cabeza" del contenedor
  function accessory(kind) {
    switch (kind) {
      case "safari": return `<path d="M70 56 L80 92" stroke="#8B5E34" stroke-width="6" stroke-linecap="round" opacity=".9"/><path d="M130 56 L120 92" stroke="#8B5E34" stroke-width="6" stroke-linecap="round" opacity=".9"/>
        <ellipse cx="100" cy="50" rx="46" ry="11" fill="#C8A063" stroke="${CC.ink}" stroke-width="1.6"/><path d="M76 50 Q76 26 100 26 Q124 26 124 50 Z" fill="#D8B47A" stroke="${CC.ink}" stroke-width="1.6"/><path d="M76 46 L124 46" stroke="#A8814A" stroke-width="3.5"/>`;
      case "hardhat": return `<path d="M52 66 L74 66 L68 140 L52 140 Z" fill="#F2792B" stroke="${CC.ink}" stroke-width="1.2" opacity=".95"/><path d="M148 66 L126 66 L132 140 L148 140 Z" fill="#F2792B" stroke="${CC.ink}" stroke-width="1.2" opacity=".95"/><path d="M55 96 L70 96 M130 96 L145 96" stroke="#F4F7FB" stroke-width="3"/>
        <path d="M72 50 Q72 26 100 26 Q128 26 128 50 Z" fill="#F4C20D" stroke="${CC.ink}" stroke-width="1.6"/><ellipse cx="100" cy="50" rx="42" ry="8" fill="#F4C20D" stroke="${CC.ink}" stroke-width="1.6"/><rect x="96" y="28" width="8" height="22" fill="#caa00b"/>`;
      case "captain": return `<path d="M76 46 Q76 28 100 28 Q124 28 124 46 Z" fill="#F4F7FB" stroke="${CC.ink}" stroke-width="1.6"/><rect x="58" y="44" width="84" height="9" rx="4" fill="#16243f"/><ellipse cx="100" cy="44" rx="42" ry="6" fill="#1A2A44"/><path d="M88 36 L112 36" stroke="${CC.ink}" stroke-width="2"/><circle cx="100" cy="40" r="4.5" fill="#E3B341" stroke="${CC.ink}" stroke-width="1"/>`;
      default: return "";
    }
  }

  // Cuerpo + cara (sin extremidades) — reutilizado por personaje y avatar. Acabado: sombra suave.
  function bodyFace(expr, acc) {
    let corr = "";
    for (let x = 56; x <= 144; x += 11) corr += `<rect x="${x}" y="60" width="4" height="76" rx="2" fill="${CC.bld}" opacity=".26"/>`;
    const cast = [[48, 48], [142, 48], [48, 140], [142, 140]].map(([x, y]) => `<rect x="${x}" y="${y}" width="10" height="10" rx="2.5" fill="${CC.bld}"/>`).join("");
    const id = "cbg" + (UID++);
    const defs = `<defs><radialGradient id="${id}" cx="34%" cy="26%" r="82%"><stop offset="0%" stop-color="${CC.hi}" stop-opacity=".8"/><stop offset="55%" stop-color="${CC.bl}" stop-opacity="0"/></radialGradient><linearGradient id="${id}b" x1="0" y1="0" x2="0" y2="1"><stop offset="58%" stop-color="${CC.bld}" stop-opacity="0"/><stop offset="100%" stop-color="${CC.bld}" stop-opacity=".42"/></linearGradient></defs>`;
    const overlay = `<rect x="46" y="46" width="108" height="104" rx="15" fill="url(#${id})"/><rect x="46" y="46" width="108" height="104" rx="15" fill="url(#${id}b)"/>`;
    return `${defs}<rect x="46" y="46" width="108" height="104" rx="15" fill="${CC.bl}"/>
      <rect x="46" y="46" width="108" height="13" rx="6" fill="${CC.bld}" opacity=".5"/>
      <rect x="46" y="137" width="108" height="13" rx="6" fill="${CC.bld}" opacity=".5"/>
      ${corr}${cast}${overlay}
      ${face2(100, 84, expr)}
      <text x="100" y="129" text-anchor="middle" font-family="'Sora',sans-serif" font-weight="800" font-size="13" letter-spacing="-.5"><tspan fill="${CC.lime}">m³</tspan><tspan fill="#fff">cargo</tspan></text>
      ${accessory(acc)}`;
  }

  function hand(x, y) { return `<circle cx="${x}" cy="${y}" r="12" fill="${CC.shoe}" stroke="${CC.ink}" stroke-width="1.6"/>`; }

  function limbs(pose) {
    const leg = (x) => `<rect x="${x - 8}" y="146" width="16" height="44" rx="7" fill="${CC.bls}" stroke="${CC.ink}" stroke-width="1.2"/>`;
    const shoe = (x) => `<path d="M${x - 19} 202 Q${x - 19} 192 ${x - 9} 192 L${x + 5} 192 Q${x + 21} 192 ${x + 21} 205 Q${x + 21} 213 ${x + 11} 213 L${x - 13} 213 Q${x - 19} 213 ${x - 19} 205 Z" fill="${CC.shoe}" stroke="${CC.ink}" stroke-width="1.6"/><path d="M${x - 18} 210 L${x + 20} 210" stroke="${CC.bls}" stroke-width="4" stroke-linecap="round"/>`;
    const legs = leg(84) + leg(116) + shoe(84) + shoe(116);
    let arms;
    if (pose === "hips") arms = `<path d="M54 102 Q40 116 56 130" stroke="${CC.bl}" stroke-width="15" fill="none" stroke-linecap="round"/>${hand(58, 130)}<path d="M146 102 Q160 116 144 130" stroke="${CC.bl}" stroke-width="15" fill="none" stroke-linecap="round"/>${hand(142, 130)}`;
    else if (pose === "proud") arms = `<path d="M54 100 Q38 126 44 150" stroke="${CC.bl}" stroke-width="15" fill="none" stroke-linecap="round"/>${hand(44, 152)}<path d="M146 100 Q162 126 156 150" stroke="${CC.bl}" stroke-width="15" fill="none" stroke-linecap="round"/>${hand(156, 152)}`;
    else arms = `<path d="M56 100 Q42 122 40 142" stroke="${CC.bl}" stroke-width="15" fill="none" stroke-linecap="round"/>${hand(40, 144)}<path d="M144 92 Q170 78 178 54" stroke="${CC.bl}" stroke-width="15" fill="none" stroke-linecap="round"/>${hand(180, 50)}`;
    return { legs, arms };
  }

  function containerChar(size, opts) {
    opts = opts || {};
    const expr = exprMap(opts.expr || "feliz");
    const pose = opts.pose || ({ enfocado: "hips", orgulloso: "proud" }[expr] || "wave");
    const { legs, arms } = limbs(pose);
    return `<svg width="${size}" height="${size * 1.24}" viewBox="0 0 200 248" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Cubi">
      <ellipse cx="100" cy="234" rx="58" ry="8" fill="#0A1A2F" opacity="0.12"/>
      ${legs}${arms}${bodyFace(expr, opts.accessory)}
    </svg>`;
  }

  // Sólo cuerpo+cara (para FAB / avatares de chat)
  function containerHead(size, expr) {
    return `<svg width="${size}" height="${size}" viewBox="38 40 124 120" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Cubi">${bodyFace(exprMap(expr || "feliz"))}</svg>`;
  }

  g.CubiArt = { cube, box, face, face2, containerChar, containerHead, container: containerChar, palette: C };

  /* =====================================================================
     Chat widget
     ===================================================================== */

  // Avatar pequeño (contenedor Cubi) para FAB + burbujas
  function Avatar({ size = 34, expr = "happy" }) {
    return <span style={{ display: "inline-flex", flex: "none" }}
      dangerouslySetInnerHTML={{ __html: containerHead(size, expr) }} />;
  }

  /* --------- Guiones de conversación (sin backend) --------- */
  const KB = {
    public: {
      greeting: { expr: "wave",
        text: "¡Hola! Soy Cubi 📦 Te ayudo a importar desde China sin enredos. ¿Qué necesitas?",
        chips: ["¿Cuánto cuesta?", "¿Cómo funciona?", "¿Cuánto demora?", "Hablar con un ejecutivo"] },
      intents: [
        { k: ["cuanto cuesta", "precio", "cotiz", "valor", "tarifa"], expr: "excited",
          text: "Tu precio total (flete + Ad valorem + IVA) se calcula al instante, sin registrarte. Ingresa volumen y producto en el cotizador y verás el total en segundos.",
          chips: ["Ir al cotizador", "¿Qué es el Ad valorem?", "¿Hay carga mínima?"] },
        { k: ["ir al cotiz", "cotizador"], expr: "happy", action: { href: "#cotizador", label: "Abrir cotizador" },
          text: "Te dejo el cotizador acá abajo 👇 Mueve el volumen y mira el total cambiar en vivo." },
        { k: ["como funciona", "proceso", "pasos", "como es"], expr: "thinking",
          text: "Cuatro pasos, cero llamadas:\n1️⃣ Cotizas online\n2️⃣ Confirmas y te damos etiquetas en chino\n3️⃣ Tu proveedor envía a nuestra bodega en China (te avisamos con foto)\n4️⃣ Consolidamos y te lo entregamos en tu puerta.",
          chips: ["¿Cuánto demora?", "¿Cuánto cuesta?"] },
        { k: ["demora", "tiempo", "cuanto tarda", "dias", "llega"], expr: "happy",
          text: "Un LCL China→Chile demora típicamente 30–40 días puerta a puerta, según puerto de origen y consolidación. Te mostramos cada estado en tiempo real.",
          chips: ["¿Cómo funciona?", "Hablar con un ejecutivo"] },
        { k: ["carga minima", "minimo", "1 m3", "poco"], expr: "thinking",
          text: "La carga mínima es 1 m³. Si tu carga es menor, se cobra como 1 m³ igual — el cotizador lo ajusta solo.",
          chips: ["Ir al cotizador"] },
        { k: ["ad valorem", "arancel", "impuesto", "iva", "tlc"], expr: "thinking",
          text: "Sobre el CIF (FOB ×1,17) se aplica Ad valorem (10%, o 6% si tu producto tiene TLC China–Chile) y luego IVA 19%. Todo queda desglosado en la cotización, sin letra chica.",
          chips: ["¿Qué es el TLC?", "Ir al cotizador"] },
        { k: ["tlc", "tratado"], expr: "happy",
          text: "El TLC Chile–China rebaja el Ad valorem del 10% al 6% para la mayoría de productos. En el cotizador hay un switch para activarlo.",
          chips: ["Ir al cotizador"] },
        { k: ["puedo importar", "que importar", "restriccion", "sag", "isp", "prohibido", "alimentos", "cosmetic"], expr: "thinking",
          text: "Casi todo. Algunas categorías requieren permiso: alimentos (SAG) y cosméticos/medicamentos (ISP). No te bloqueamos: te avisamos qué trámite necesitas.",
          chips: ["¿Cómo funciona?", "Hablar con un ejecutivo"] },
        { k: ["rastrear", "tracking", "donde esta", "seguir", "codigo"], expr: "happy", action: { href: "https://portal.m3cargo.cl", label: "Ingresar al portal" },
          text: "Puedes seguir tu carga en tiempo real desde tu portal de cliente. Si aún no tienes acceso, escríbenos y te ayudamos." },
        { k: ["ejecutivo", "humano", "persona", "whatsapp", "hablar", "contacto"], expr: "happy", action: { href: "https://wa.me/56936716236", label: "Escribir por WhatsApp" },
          text: "¡Claro! Un ejecutivo te atiende por WhatsApp. También puedo seguir respondiendo por acá 😄" },
        { k: ["ingresar", "login", "portal", "cuenta"], expr: "happy", action: { href: "https://portal.m3cargo.cl", label: "Ir al portal" },
          text: "Si ya eres cliente, entra al portal para ver tus envíos, documentos y facturas." },
        { k: ["gracias", "genial", "perfecto", "ok", "buena"], expr: "excited",
          text: "¡Para eso estoy! 📦 Si te animas, cotizar toma 10 segundos.", chips: ["Ir al cotizador"] },
      ],
      fallback: { expr: "thinking",
        text: "Mmm, no estoy seguro de eso, pero puedo ayudarte con precios, tiempos, el proceso o restricciones. ¿Por dónde partimos?",
        chips: ["¿Cuánto cuesta?", "¿Cómo funciona?", "Hablar con un ejecutivo"] },
    },
    portal: {
      greeting: { expr: "wave",
        text: "¡Hola, Carolina! Soy Cubi 📦 Tu asistente en el portal. ¿Qué reviso por ti?",
        chips: ["Estado de mis envíos", "¿Tengo algo por pagar?", "Documentos pendientes", "Agendar despacho"] },
      intents: [
        { k: ["estado", "mis envios", "donde esta", "envio", "carga", "seguir"], expr: "happy",
          text: "Tienes envíos activos en curso. El SHP-2418 está EN ADUANA y podría requerir tu atención por un aforo. Ábrelo en «Mis envíos» para ver la línea de tiempo y la evidencia.",
          chips: ["¿Qué es un aforo?", "Documentos pendientes"] },
        { k: ["pagar", "factura", "por pagar", "deuda", "cobro", "facturacion"], expr: "thinking",
          text: "En Facturación verás lo que está por pagar y los impuestos adelantados. Cada factura tiene su botón [Pagar]. ¿Quieres que te lleve allí?",
          chips: ["Ir a Facturación", "Documentos pendientes"] },
        { k: ["documento", "papeles", "factura comercial", "subir", "pendiente"], expr: "thinking",
          text: "Hay documentos pendientes o rechazados en tu cuenta. En «Documentos» puedes verlos por envío y subir los que falten para no frenar la liberación en aduana.",
          chips: ["Estado de mis envíos", "¿Qué es un aforo?"] },
        { k: ["despacho", "agendar", "entrega", "camion", "horario", "slot"], expr: "excited",
          text: "Para coordinar la entrega, entra a «Despacho» y elige un slot de camión en la grilla de la semana. Te confirmo el resumen al tiro.",
          chips: ["Estado de mis envíos"] },
        { k: ["aforo", "aduana", "revision", "sna"], expr: "thinking",
          text: "Un aforo es una revisión de Aduana (documental o físico). No siempre implica costo extra; te avisamos apenas hay novedad y qué necesitamos de ti. Tu SHP-2418 está en ese estado.",
          chips: ["Documentos pendientes"] },
        { k: ["puntos", "recompensa", "beneficio", "referir", "premio"], expr: "excited",
          text: "Acumulas puntos por cada m³ importado y por referir clientes. Revisa tu nivel y canjes en «Recompensas».",
          chips: ["Estado de mis envíos"] },
        { k: ["ejecutivo", "humano", "persona", "whatsapp", "ayuda", "hablar"], expr: "happy", action: { href: "https://wa.me/56936716236", label: "Escribir por WhatsApp" },
          text: "Te conecto con tu ejecutivo por WhatsApp. Igual sigo aquí para lo que necesites 😄" },
        { k: ["gracias", "genial", "perfecto", "ok", "listo"], expr: "excited",
          text: "¡Cuando quieras, Carolina! 📦 Reviso lo que necesites del portal." },
      ],
      fallback: { expr: "thinking",
        text: "No tengo eso a mano, pero puedo ayudarte con tus envíos, pagos, documentos o despacho. ¿Qué reviso?",
        chips: ["Estado de mis envíos", "¿Tengo algo por pagar?", "Documentos pendientes"] },
    },
  };

  function norm(s) { return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, ""); }
  function matchIntent(ctx, text) {
    const t = norm(text);
    const kb = KB[ctx] || KB.public;
    for (const it of kb.intents) if (it.k.some(k => t.includes(norm(k)))) return it;
    return kb.fallback;
  }

  function Cubi({ context = "public" }) {
    const kb = KB[context] || KB.public;
    const LS = "cubi_chat_" + context;
    const [open, setOpen] = useState(false);
    const [typing, setTyping] = useState(false);
    const [input, setInput] = useState("");
    const [msgs, setMsgs] = useState(() => {
      try { const s = JSON.parse(localStorage.getItem(LS)); if (s && s.length) return s; } catch (e) {}
      return [{ from: "bot", text: kb.greeting.text, expr: kb.greeting.expr, chips: kb.greeting.chips }];
    });
    const [chips, setChips] = useState(() => kb.greeting.chips);
    const scrollRef = useRef(null);

    useEffect(() => { try { localStorage.setItem(LS, JSON.stringify(msgs.slice(-30))); } catch (e) {} }, [msgs]);
    useEffect(() => { if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight; }, [msgs, typing, open]);

    function pushBot(intent) {
      setTyping(true); setChips([]);
      setTimeout(() => {
        setTyping(false);
        setMsgs(m => [...m, { from: "bot", text: intent.text, expr: intent.expr || "happy", action: intent.action }]);
        setChips(intent.chips || []);
      }, 520 + Math.min(intent.text.length * 8, 700));
    }
    function send(raw) {
      const text = (raw == null ? input : raw).trim();
      if (!text) return;
      setMsgs(m => [...m, { from: "me", text }]);
      setInput("");
      pushBot(matchIntent(context, text));
    }

    const headExpr = typing ? "thinking" : "happy";

    return (
      <div className="cubi-root">
        {/* Ventana */}
        {open && (
          <div className="cubi-win" role="dialog" aria-label="Chat con Cubi">
            <div className="cubi-head">
              <div className="cubi-head-ava"><Avatar size={40} expr={headExpr} /></div>
              <div style={{ lineHeight: 1.2 }}>
                <div className="cubi-head-name">Cubi</div>
                <div className="cubi-head-sub"><span className="cubi-online" />En línea · responde al instante</div>
              </div>
              <button className="cubi-x" onClick={() => setOpen(false)} aria-label="Cerrar">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg>
              </button>
            </div>

            <div className="cubi-body" ref={scrollRef}>
              {msgs.map((m, i) => (
                m.from === "bot" ? (
                  <div className="cubi-msg bot" key={i}>
                    <Avatar size={26} expr={m.expr || "happy"} />
                    <div className="cubi-bubble bot">
                      {m.text.split("\n").map((l, j) => <div key={j}>{l}</div>)}
                      {m.action && <a className="cubi-action" href={m.action.href} target={m.action.href.startsWith("http") ? "_blank" : undefined}>
                        {m.action.label}
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
                      </a>}
                    </div>
                  </div>
                ) : (
                  <div className="cubi-msg me" key={i}><div className="cubi-bubble me">{m.text}</div></div>
                )
              ))}
              {typing && (
                <div className="cubi-msg bot">
                  <Avatar size={26} expr="thinking" />
                  <div className="cubi-bubble bot cubi-typing"><span /><span /><span /></div>
                </div>
              )}
            </div>

            {chips.length > 0 && (
              <div className="cubi-chips">
                {chips.map((c, i) => <button key={i} className="cubi-chip" onClick={() => send(c)}>{c}</button>)}
              </div>
            )}

            <form className="cubi-input" onSubmit={e => { e.preventDefault(); send(); }}>
              <input value={input} onChange={e => setInput(e.target.value)} placeholder="Escríbele a Cubi…" aria-label="Mensaje" />
              <button type="submit" className="cubi-send" disabled={!input.trim()} aria-label="Enviar">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M22 2 11 13M22 2 15 22l-4-9-9-4z" /></svg>
              </button>
            </form>
          </div>
        )}

        {/* Botón flotante */}
        <button className={"cubi-fab" + (open ? " open" : "")} onClick={() => setOpen(o => !o)} aria-label={open ? "Cerrar chat" : "Abrir chat con Cubi"}>
          {open
            ? <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.4" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg>
            : <span className="cubi-fab-art" dangerouslySetInnerHTML={{ __html: containerHead(40, "feliz") }} />}
          {!open && <span className="cubi-fab-badge">1</span>}
        </button>
      </div>
    );
  }

  /* Visibilidad del chat: ver shared/preview-gate.js (CUBI_ENABLED, ?cubi=on/off) */
  const cubiOn = !!g.CUBI_ON; // definido en shared/preview-gate.js
  g.Cubi = function CubiGate(props) { return cubiOn ? React.createElement(Cubi, props) : null; };
})(window);
