/**
 * Embeddable AI chat widget — a drop-in Tawk.to replacement.
 *
 * Install on any site with one line:
 *   <script src="https://your-server.com/widget.js"></script>
 *
 * Optional overrides via data-* attributes on the script tag:
 *   data-api="https://your-server.com"   API base (defaults to the script's origin)
 *   data-color="#6366f1"                 accent color
 *   data-title="Support"                 header title
 *   data-subtitle="Typically replies instantly"
 */
(function () {
  "use strict";

  var script =
    document.currentScript ||
    (function () {
      var s = document.getElementsByTagName("script");
      return s[s.length - 1];
    })();

  var API = (script.getAttribute("data-api") || new URL(script.src).origin).replace(/\/$/, "");
  var TENANT = script.getAttribute("data-tenant") || "";
  var ACCENT = script.getAttribute("data-color") || "#6366f1";
  var ACCENT2 = script.getAttribute("data-color2") || "#8b5cf6";
  var TITLE = script.getAttribute("data-title") || "Chat with us";
  var SUBTITLE = script.getAttribute("data-subtitle") || "AI assistant · replies instantly";

  // Branding is loaded from the server per tenant (overrides defaults above).
  var cfg = { name: TITLE, greeting: null, accentColor: ACCENT };

  if (window.__aiChatWidgetLoaded) return;
  window.__aiChatWidgetLoaded = true;

  var previousResponseId = null;
  var busy = false;

  // ── Styles ──────────────────────────────────────────────────────────────
  var CSS = [
    "#acw,#acw *{box-sizing:border-box}",
    "#acw{--a:" + ACCENT + ";--a2:" + ACCENT2 + ";--ink:#1f2033;--muted:#8a8aa0;--line:#eceef4;--bg:#f7f8fc;",
    "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}",

    /* launcher */
    "#acw .acw-launch{position:fixed;bottom:22px;right:22px;width:62px;height:62px;border-radius:50%;",
    "border:none;cursor:pointer;z-index:2147483000;background:linear-gradient(135deg,var(--a),var(--a2));",
    "box-shadow:0 10px 26px -6px color-mix(in srgb,var(--a) 60%,transparent),0 4px 10px rgba(0,0,0,.12);",
    "display:flex;align-items:center;justify-content:center;transition:transform .25s cubic-bezier(.34,1.56,.64,1),box-shadow .25s}",
    "#acw .acw-launch:hover{transform:translateY(-2px) scale(1.05)}",
    "#acw .acw-launch:active{transform:scale(.96)}",
    "#acw .acw-launch svg{width:28px;height:28px;transition:transform .3s,opacity .2s;position:absolute}",
    "#acw .acw-launch .acw-ic-close{opacity:0;transform:rotate(-90deg) scale(.5)}",
    "#acw.open .acw-launch .acw-ic-chat{opacity:0;transform:rotate(90deg) scale(.5)}",
    "#acw.open .acw-launch .acw-ic-close{opacity:1;transform:rotate(0) scale(1)}",
    "#acw .acw-pulse{position:absolute;inset:0;border-radius:50%;background:var(--a);opacity:.5;animation:acw-pulse 2.4s ease-out infinite;z-index:-1}",
    "#acw.open .acw-pulse{display:none}",
    "@keyframes acw-pulse{0%{transform:scale(1);opacity:.45}70%,100%{transform:scale(1.9);opacity:0}}",

    /* panel */
    "#acw .acw-panel{position:fixed;bottom:98px;right:22px;width:388px;max-width:calc(100vw - 32px);height:min(624px,calc(100vh - 130px));",
    "background:var(--bg);border-radius:22px;z-index:2147483000;display:flex;flex-direction:column;overflow:hidden;",
    "box-shadow:0 24px 60px -12px rgba(24,24,50,.32),0 8px 24px -8px rgba(24,24,50,.18);",
    "opacity:0;transform:translateY(16px) scale(.96);transform-origin:bottom right;pointer-events:none;",
    "transition:opacity .28s ease,transform .34s cubic-bezier(.34,1.4,.5,1)}",
    "#acw.open .acw-panel{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}",

    /* header */
    "#acw .acw-head{background:linear-gradient(135deg,var(--a),var(--a2));color:#fff;padding:18px 18px 20px;position:relative;flex-shrink:0}",
    "#acw .acw-head-row{display:flex;align-items:center;gap:12px}",
    "#acw .acw-avatar{width:42px;height:42px;border-radius:50%;background:rgba(255,255,255,.2);backdrop-filter:blur(4px);",
    "display:flex;align-items:center;justify-content:center;flex-shrink:0;box-shadow:inset 0 0 0 1px rgba(255,255,255,.25)}",
    "#acw .acw-avatar svg{width:24px;height:24px}",
    "#acw .acw-htxt{flex:1;min-width:0}",
    "#acw .acw-title{font-weight:700;font-size:16px;letter-spacing:-.2px;line-height:1.2}",
    "#acw .acw-sub{font-size:12.5px;opacity:.85;margin-top:2px;display:flex;align-items:center;gap:6px}",
    "#acw .acw-dot{width:7px;height:7px;border-radius:50%;background:#4ade80;box-shadow:0 0 0 0 rgba(74,222,128,.7);animation:acw-live 2s infinite}",
    "@keyframes acw-live{0%{box-shadow:0 0 0 0 rgba(74,222,128,.6)}70%{box-shadow:0 0 0 6px rgba(74,222,128,0)}100%{box-shadow:0 0 0 0 rgba(74,222,128,0)}}",
    "#acw .acw-min{background:rgba(255,255,255,.15);border:none;color:#fff;width:30px;height:30px;border-radius:9px;cursor:pointer;",
    "font-size:18px;line-height:1;display:flex;align-items:center;justify-content:center;transition:background .2s}",
    "#acw .acw-min:hover{background:rgba(255,255,255,.28)}",

    /* body */
    "#acw .acw-body{flex:1;overflow-y:auto;padding:18px 16px 8px;display:flex;flex-direction:column;gap:12px;scroll-behavior:smooth}",
    "#acw .acw-body::-webkit-scrollbar{width:6px}",
    "#acw .acw-body::-webkit-scrollbar-thumb{background:#d7d9e6;border-radius:3px}",
    "#acw .acw-row{display:flex;gap:9px;align-items:flex-end;max-width:100%;animation:acw-in .32s cubic-bezier(.22,1,.36,1)}",
    "@keyframes acw-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}",
    "#acw .acw-row.user{flex-direction:row-reverse}",
    "#acw .acw-mini{width:26px;height:26px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;",
    "background:linear-gradient(135deg,var(--a),var(--a2))}",
    "#acw .acw-mini svg{width:15px;height:15px}",
    "#acw .acw-bubble{padding:11px 14px;border-radius:16px;font-size:14.5px;line-height:1.5;max-width:78%;",
    "word-wrap:break-word;white-space:pre-wrap;box-shadow:0 1px 2px rgba(24,24,50,.05)}",
    "#acw .acw-row.bot .acw-bubble{background:#fff;color:var(--ink);border-bottom-left-radius:5px}",
    "#acw .acw-row.user .acw-bubble{background:linear-gradient(135deg,var(--a),var(--a2));color:#fff;border-bottom-right-radius:5px}",
    "#acw .acw-bubble a{color:var(--a);text-decoration:underline}",
    "#acw .acw-row.user .acw-bubble a{color:#fff}",
    "#acw .acw-bubble strong{font-weight:700}",
    "#acw .acw-bubble em{font-style:italic}",
    "#acw .acw-bubble code{background:rgba(99,102,241,.1);color:#4f46e5;padding:1px 5px;border-radius:5px;font-size:12.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}",
    "#acw .acw-bubble ul,#acw .acw-bubble ol{margin:6px 0 4px;padding-left:20px}",
    "#acw .acw-bubble li{margin:3px 0}",
    "#acw .acw-bubble div + div{margin-top:7px}",
    "#acw .acw-bubble div + ul,#acw .acw-bubble div + ol{margin-top:6px}",

    /* live human agent + system notes */
    "#acw .acw-row.agent{display:flex;gap:9px;align-items:flex-end;animation:acw-in .32s cubic-bezier(.22,1,.36,1)}",
    "#acw .acw-row.agent>div{max-width:78%;min-width:0}",
    "#acw .acw-agent{max-width:100% !important;width:fit-content}",
    "#acw .acw-mini-h{background:linear-gradient(135deg,#10b981,#059669)}",
    "#acw .acw-alabel{font-size:11px;font-weight:600;color:#059669;margin:0 0 3px 4px}",
    "#acw .acw-agent{background:#ecfdf5 !important;border:1px solid #d1fae5 !important;color:#065f46 !important;border-bottom-left-radius:5px}",
    "#acw .acw-sys{align-self:center;text-align:center;font-size:12px;color:var(--muted);background:#eceef6;padding:7px 13px;border-radius:13px;max-width:88%;margin:2px auto;line-height:1.4;animation:acw-in .3s ease}",
    "#acw .acw-human{background:rgba(255,255,255,.15);border:none;color:#fff;width:30px;height:30px;border-radius:9px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .2s;margin-right:6px}",
    "#acw .acw-human:hover{background:rgba(255,255,255,.28)}",
    "#acw .acw-human svg{width:17px;height:17px}",

    /* typing / searching */
    "#acw .acw-typing{display:flex;gap:4px;padding:14px 15px;background:#fff;border-radius:16px;border-bottom-left-radius:5px;width:fit-content}",
    "#acw .acw-typing i{width:7px;height:7px;border-radius:50%;background:#c3c6d6;animation:acw-bob 1.2s infinite ease-in-out}",
    "#acw .acw-typing i:nth-child(2){animation-delay:.18s}#acw .acw-typing i:nth-child(3){animation-delay:.36s}",
    "@keyframes acw-bob{0%,60%,100%{transform:translateY(0);opacity:.5}30%{transform:translateY(-5px);opacity:1}}",
    "#acw .acw-status{font-size:12px;color:var(--muted);padding:2px 4px 4px 40px;display:flex;align-items:center;gap:7px}",
    "#acw .acw-spin{width:12px;height:12px;border:2px solid #d7d9e6;border-top-color:var(--a);border-radius:50%;animation:acw-rot .7s linear infinite}",
    "@keyframes acw-rot{to{transform:rotate(360deg)}}",

    /* chips */
    "#acw .acw-chips{display:flex;flex-wrap:wrap;gap:8px;padding:2px 4px 6px 40px}",
    "#acw .acw-chip{background:#fff;border:1px solid var(--line);color:var(--a);font-size:13px;font-weight:500;",
    "padding:8px 13px;border-radius:20px;cursor:pointer;transition:all .18s;box-shadow:0 1px 2px rgba(24,24,50,.04)}",
    "#acw .acw-chip:hover{border-color:var(--a);background:color-mix(in srgb,var(--a) 7%,#fff);transform:translateY(-1px)}",

    /* footer */
    "#acw .acw-foot{padding:12px;background:transparent;flex-shrink:0}",
    "#acw .acw-inwrap{display:flex;align-items:flex-end;gap:8px;background:#fff;border:1.5px solid var(--line);",
    "border-radius:22px;padding:6px 6px 6px 16px;transition:border-color .2s,box-shadow .2s}",
    "#acw .acw-inwrap.focus{border-color:var(--a);box-shadow:0 0 0 4px color-mix(in srgb,var(--a) 14%,transparent)}",
    "#acw textarea{flex:1;border:none;outline:none;resize:none;font-family:inherit;font-size:14.5px;line-height:1.4;",
    "max-height:96px;padding:6px 0;background:transparent;color:var(--ink)}",
    "#acw .acw-send{width:38px;height:38px;border-radius:50%;border:none;flex-shrink:0;cursor:pointer;",
    "background:linear-gradient(135deg,var(--a),var(--a2));display:flex;align-items:center;justify-content:center;",
    "transition:transform .18s,opacity .2s}",
    "#acw .acw-send:hover{transform:scale(1.08)}#acw .acw-send:active{transform:scale(.92)}",
    "#acw .acw-send:disabled{opacity:.4;cursor:default;transform:none}",
    "#acw .acw-send svg{width:18px;height:18px}",
    "#acw .acw-credit{text-align:center;font-size:10.5px;color:var(--muted);margin-top:8px;letter-spacing:.2px}",

    /* gate (name/email verification) */
    "#acw .acw-gate{flex:1;overflow-y:auto;padding:26px 22px;display:none;flex-direction:column;justify-content:center}",
    "#acw.gate .acw-gate{display:flex}#acw.gate .acw-body,#acw.gate .acw-foot{display:none}",
    "#acw .acw-gstep{display:none;animation:acw-in .3s ease}#acw .acw-gstep.on{display:block}",
    "#acw .acw-gemoji{font-size:30px;text-align:center;margin-bottom:6px}",
    "#acw .acw-gh{font-size:18px;font-weight:700;text-align:center;color:var(--ink);letter-spacing:-.3px}",
    "#acw .acw-gp{font-size:13.5px;color:var(--muted);text-align:center;margin:5px 0 20px;line-height:1.5}",
    "#acw .acw-gp b{color:var(--ink)}",
    "#acw .acw-field{margin-bottom:12px}",
    "#acw .acw-field label{display:block;font-size:12px;font-weight:600;color:var(--muted);margin-bottom:6px}",
    "#acw .acw-gate input{width:100%;font-family:inherit;font-size:14.5px;color:var(--ink);padding:12px 14px;",
    "border:1.5px solid var(--line);border-radius:12px;background:#fff;transition:border-color .18s,box-shadow .18s}",
    "#acw .acw-gate input:focus{outline:none;border-color:var(--a);box-shadow:0 0 0 4px color-mix(in srgb,var(--a) 14%,transparent)}",
    "#acw .acw-code{text-align:center;letter-spacing:10px;font-size:22px !important;font-weight:700;padding:14px !important}",
    "#acw .acw-gbtn{width:100%;margin-top:6px;border:none;border-radius:12px;padding:13px;color:#fff;font-family:inherit;",
    "font-size:14.5px;font-weight:600;cursor:pointer;background:linear-gradient(135deg,var(--a),var(--a2));transition:transform .16s,opacity .2s}",
    "#acw .acw-gbtn:hover{transform:translateY(-1px)}#acw .acw-gbtn:disabled{opacity:.55;cursor:default;transform:none}",
    "#acw .acw-gerr{font-size:12.5px;color:#e0416a;text-align:center;min-height:16px;margin-top:10px}",
    "#acw .acw-glink{display:block;margin:14px auto 0;background:none;border:none;color:var(--a);font-size:13px;",
    "font-weight:500;cursor:pointer;font-family:inherit}",
    "#acw .acw-glink.muted{color:var(--muted)}",
    "#acw .acw-gsec{font-size:11px;color:var(--muted);text-align:center;margin-top:18px;display:flex;align-items:center;justify-content:center;gap:5px}",
    "#acw .acw-devcode{margin-top:12px;font-size:13px;color:#92400e;background:#fffbeb;border:1px solid #fde68a;border-radius:10px;padding:9px 12px;text-align:center}",
    "#acw .acw-devcode b{font-size:16px;letter-spacing:2px}",

    "@media(max-width:480px){#acw .acw-panel{right:10px;left:10px;width:auto;bottom:88px}#acw .acw-launch{right:16px;bottom:16px}}",
  ].join("");

  var style = document.createElement("style");
  style.textContent = CSS;
  document.head.appendChild(style);

  var BOT_SVG =
    '<svg viewBox="0 0 24 24" fill="none"><rect x="4" y="6" width="16" height="12" rx="4" fill="#fff"/><circle cx="9.2" cy="12" r="1.5" fill="' +
    ACCENT + '"/><circle cx="14.8" cy="12" r="1.5" fill="' + ACCENT +
    '"/><path d="M12 3v3M8 18l-1.5 2.5M16 18l1.5 2.5" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>';
  var PERSON_SVG =
    '<svg viewBox="0 0 24 24" fill="#fff"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4.4 3.6-7 8-7s8 2.6 8 7z"/></svg>';

  // ── DOM ─────────────────────────────────────────────────────────────────
  var root = document.createElement("div");
  root.id = "acw";
  root.innerHTML =
    '<div class="acw-panel" role="dialog" aria-label="Chat">' +
      '<div class="acw-head"><div class="acw-head-row">' +
        '<div class="acw-avatar">' + BOT_SVG + "</div>" +
        '<div class="acw-htxt"><div class="acw-title"></div>' +
          '<div class="acw-sub"><span class="acw-dot"></span><span class="acw-subtxt"></span></div></div>' +
        '<button class="acw-human" title="Talk to a person" aria-label="Talk to a person">' + PERSON_SVG + "</button>" +
        '<button class="acw-min" aria-label="Minimize">&#8211;</button>' +
      "</div></div>" +
      // ── pre-chat gate ──
      '<div class="acw-gate">' +
        '<div class="acw-gstep on" data-step="form">' +
          '<div class="acw-gemoji">👋</div>' +
          '<div class="acw-gh">Before we start</div>' +
          '<div class="acw-gp">Tell us who you are so we can help you better.</div>' +
          '<div class="acw-field"><label>Your name</label><input class="acw-name" type="text" placeholder="Jane Doe" autocomplete="name"/></div>' +
          '<div class="acw-field"><label>Email address</label><input class="acw-email" type="email" placeholder="jane@company.com" autocomplete="email"/></div>' +
          '<button class="acw-gbtn acw-start">Continue</button>' +
          '<div class="acw-gerr acw-err1"></div>' +
          '<div class="acw-gsec">🔒 We\'ll only use this to assist you.</div>' +
        "</div>" +
        '<div class="acw-gstep" data-step="code">' +
          '<div class="acw-gemoji">✉️</div>' +
          '<div class="acw-gh">Verify your email</div>' +
          '<div class="acw-gp">Enter the 6-digit code we sent to <b class="acw-echo"></b>.</div>' +
          '<input class="acw-code" type="text" inputmode="numeric" maxlength="6" placeholder="000000"/>' +
          '<div class="acw-devcode" style="display:none"></div>' +
          '<button class="acw-gbtn acw-verify" style="margin-top:12px">Verify &amp; start chat</button>' +
          '<div class="acw-gerr acw-err2"></div>' +
          '<button class="acw-glink acw-resend">Resend code</button>' +
          '<button class="acw-glink muted acw-back">Change details</button>' +
        "</div>" +
      "</div>" +
      '<div class="acw-body"></div>' +
      '<div class="acw-foot"><div class="acw-inwrap">' +
        '<textarea rows="1" placeholder="Type your message…"></textarea>' +
        '<button class="acw-send" aria-label="Send" disabled>' +
          '<svg viewBox="0 0 24 24" fill="none"><path d="M4 12l15-7-6 15-2.5-6L4 12z" fill="#fff"/></svg></button>' +
      "</div><div class=\"acw-credit\">Powered by AI</div></div>" +
    "</div>" +
    '<button class="acw-launch" aria-label="Open chat"><span class="acw-pulse"></span>' +
      '<svg class="acw-ic-chat" viewBox="0 0 24 24" fill="#fff"><path d="M12 3C6.5 3 2 6.9 2 11.7c0 2.2.96 4.2 2.5 5.7-.14 1.2-.6 2.4-1.3 3.4 1.5-.2 2.9-.7 4.1-1.5 1.4.6 3 .9 4.7.9 5.5 0 10-3.9 10-8.7S17.5 3 12 3z"/></svg>' +
      '<svg class="acw-ic-close" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.4" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>' +
    "</button>";
  document.body.appendChild(root);

  var body = root.querySelector(".acw-body");
  var ta = root.querySelector("textarea");
  var sendBtn = root.querySelector(".acw-send");
  var inwrap = root.querySelector(".acw-inwrap");
  var launch = root.querySelector(".acw-launch");
  root.querySelector(".acw-title").textContent = TITLE;
  root.querySelector(".acw-subtxt").textContent = SUBTITLE;

  var greeted = false;

  // ── verification state (persisted so verified visitors skip the gate) ──────
  var chatToken = null, userName = "", userEmail = "", otpSession = null, convId = null;
  try {
    chatToken = localStorage.getItem("acw_token");
    userName = localStorage.getItem("acw_name") || "";
    userEmail = localStorage.getItem("acw_email") || "";
    convId = localStorage.getItem("acw_conv");
  } catch (e) {}

  // conversation runtime state
  var mode = "ai";                 // "ai" | "human"
  var lastSeq = 0;                 // highest message seq we've seen
  var renderedIds = {};            // message ids already on screen (dedupe)
  var pollTimer = null;

  function saveIdentity(token, name, email, conversationId) {
    chatToken = token; userName = name; userEmail = email; convId = conversationId;
    try {
      localStorage.setItem("acw_token", token);
      localStorage.setItem("acw_name", name);
      localStorage.setItem("acw_email", email);
      localStorage.setItem("acw_conv", conversationId);
    } catch (e) {}
  }
  function clearIdentity() {
    chatToken = null; convId = null;
    try {
      localStorage.removeItem("acw_token");
      localStorage.removeItem("acw_conv");
    } catch (e) {}
  }

  // gate refs
  var gEmoji = null; // (steps toggled by data-step)
  function gStep(name) {
    root.querySelectorAll(".acw-gstep").forEach(function (s) {
      s.classList.toggle("on", s.getAttribute("data-step") === name);
    });
  }
  var nameInput = root.querySelector(".acw-name");
  var emailInput = root.querySelector(".acw-email");
  var codeInput = root.querySelector(".acw-code");
  var devCode = root.querySelector(".acw-devcode");
  var err1 = root.querySelector(".acw-err1");
  var err2 = root.querySelector(".acw-err2");
  var startBtn = root.querySelector(".acw-start");
  var verifyBtn = root.querySelector(".acw-verify");

  function scrollDown() { body.scrollTop = body.scrollHeight; }

  // ── tiny, safe markdown renderer (bold, italic, code, links, lists) ────────
  function escHtml(s) {
    return s.replace(/[&<>"']/g, function (c) {
      return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c];
    });
  }
  function mdInline(s) {
    s = escHtml(s);
    s = s.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+|mailto:[^\s)]+)\)/g,
      '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
    s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
    s = s.replace(/__([^_]+)__/g, "<strong>$1</strong>");
    s = s.replace(/`([^`]+)`/g, "<code>$1</code>");
    s = s.replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, "$1<em>$2</em>");
    return s;
  }
  function renderMarkdown(src) {
    var lines = src.split("\n");
    var html = "", list = null;
    function closeList() { if (list) { html += "</" + list + ">"; list = null; } }
    for (var i = 0; i < lines.length; i++) {
      var line = lines[i].replace(/\s+$/, "");
      var ul = /^\s*[-*]\s+(.*)$/.exec(line);
      var ol = /^\s*(\d+)[.)]\s+(.*)$/.exec(line);
      if (ul) {
        if (list !== "ul") { closeList(); html += "<ul>"; list = "ul"; }
        html += "<li>" + mdInline(ul[1]) + "</li>";
      } else if (ol) {
        if (list !== "ol") { closeList(); html += "<ol>"; list = "ol"; }
        // Honor the source number so numbering survives any list splitting
        // (blank lines or interleaved bullet sub-items).
        html += '<li value="' + ol[1] + '">' + mdInline(ol[2]) + "</li>";
      } else if (line.trim() === "") {
        // Keep an open list open across blank lines — models often separate
        // list items with a blank line, which would otherwise restart <ol>.
        continue;
      } else {
        closeList();
        html += "<div>" + mdInline(line) + "</div>";
      }
    }
    closeList();
    return html;
  }

  function addRow(who) {
    var row = document.createElement("div");
    row.className = "acw-row " + who;
    var bubble = document.createElement("div");
    bubble.className = "acw-bubble";
    if (who === "bot") {
      var mini = document.createElement("div");
      mini.className = "acw-mini";
      mini.innerHTML = BOT_SVG;
      row.appendChild(mini);
    }
    row.appendChild(bubble);
    body.appendChild(row);
    scrollDown();
    return bubble;
  }

  function addMessage(text, who) {
    var b = addRow(who);
    b.textContent = text;
    return b;
  }

  function addChips(items) {
    var wrap = document.createElement("div");
    wrap.className = "acw-chips";
    items.forEach(function (t) {
      var c = document.createElement("button");
      c.className = "acw-chip";
      c.textContent = t;
      c.addEventListener("click", function () {
        wrap.remove();
        if (/talk to a human/i.test(t)) { talkToHuman(); return; }
        ta.value = t;
        send();
      });
      wrap.appendChild(c);
    });
    body.appendChild(wrap);
    scrollDown();
  }

  function open(isOpen) {
    root.classList.toggle("open", isOpen);
    launch.setAttribute("aria-label", isOpen ? "Close chat" : "Open chat");
    if (!isOpen) return;
    if (chatToken && convId) enterChat();
    else showGate();
  }

  function showGate() {
    root.classList.add("gate");
    gStep("form");
    setTimeout(function () { nameInput.focus(); }, 250);
  }

  function enterChat() {
    root.classList.remove("gate");
    setTimeout(function () { ta.focus(); }, 250);
    // Load history FIRST, then start polling — otherwise the immediate poll can
    // render the same messages before history registers their ids (duplicates).
    if (greeted) { startPolling(); return; }
    greeted = true;
    loadHistory(startPolling);
  }

  function greetFirstName() { return (userName || "").trim().split(/\s+/)[0]; }

  function loadHistory(done) {
    fetch(API + "/api/chat/" + convId + "/poll?all=1&after=0&token=" + encodeURIComponent(chatToken))
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (d) {
        if (d && d.mode) { mode = d.mode; updateModeUI(); }
        if (d && d.messages && d.messages.length) {
          d.messages.forEach(function (m) {
            renderedIds[m.id] = 1;
            if (m.seq > lastSeq) lastSeq = m.seq;
            renderIncoming(m);
          });
        } else {
          greet();
        }
        if (done) done();
      })
      .catch(function () { greet(); if (done) done(); });
  }

  function greet() {
    var first = greetFirstName();
    var g = first ? "Hi " + first + "! 👋 How can I help you today?" : (cfg.greeting || "Hi there! 👋 How can I help?");
    addMessage(g, "bot");
    addChips(["I have a question", "🙋 Talk to a human"]);
  }

  // ── incoming message rendering (agent / system / assistant / user) ─────────
  function renderIncoming(m) {
    if (m.role === "assistant") {
      var b = addRow("bot"); b.innerHTML = renderMarkdown(m.text);
    } else if (m.role === "agent") {
      addAgentRow(m.text);
    } else if (m.role === "system") {
      addSystem(m.text);
    } else if (m.role === "user") {
      var u = addRow("user"); u.textContent = m.text;
    }
    scrollDown();
  }

  function addAgentRow(text) {
    var row = document.createElement("div");
    row.className = "acw-row agent";
    row.innerHTML =
      '<div class="acw-mini acw-mini-h">' + PERSON_SVG + "</div>" +
      '<div><div class="acw-alabel">Support</div><div class="acw-bubble acw-agent"></div></div>';
    row.querySelector(".acw-agent").textContent = text;
    body.appendChild(row);
    scrollDown();
  }

  function addSystem(text) {
    var s = document.createElement("div");
    s.className = "acw-sys";
    s.textContent = text;
    body.appendChild(s);
    scrollDown();
  }

  function updateModeUI() {
    var human = mode === "human";
    root.querySelector(".acw-subtxt").textContent = human ? "Connected to a team member" : SUBTITLE;
    ta.placeholder = human ? "Message the team…" : "Type your message…";
    if (humanBtn) humanBtn.style.display = human ? "none" : "";
  }

  function createTyping() {
    var r = document.createElement("div");
    r.className = "acw-row bot";
    r.innerHTML = '<div class="acw-mini">' + BOT_SVG + '</div><div class="acw-typing"><i></i><i></i><i></i></div>';
    return r;
  }

  // ── polling for agent / system messages ────────────────────────────────────
  function startPolling() { if (!pollTimer) { poll(); pollTimer = setInterval(poll, 3000); } }
  function stopPolling() { clearInterval(pollTimer); pollTimer = null; }

  function poll() {
    if (!convId || !chatToken || !root.classList.contains("open")) return;
    fetch(API + "/api/chat/" + convId + "/poll?after=" + lastSeq + "&token=" + encodeURIComponent(chatToken))
      .then(function (r) { if (r.status === 401) { handleVerifyLost(); return null; } return r.ok ? r.json() : null; })
      .then(function (d) {
        if (!d) return;
        if (d.mode && d.mode !== mode) { mode = d.mode; updateModeUI(); }
        (d.messages || []).forEach(function (m) {
          if (m.seq > lastSeq) lastSeq = m.seq;
          if (renderedIds[m.id]) return;
          renderedIds[m.id] = 1;
          renderIncoming(m);
        });
      })
      .catch(function () {});
  }

  function talkToHuman() {
    if (mode === "human" || !convId) return;
    fetch(API + "/api/chat/" + convId + "/request-human", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ token: chatToken }),
    }).then(function () { poll(); }).catch(function () {});
  }

  function handleVerifyLost() {
    stopPolling();
    clearIdentity();
    greeted = false;
    showGate();
    err1.textContent = "Please verify your email to continue.";
  }

  var humanBtn = root.querySelector(".acw-human");

  // Load per-tenant branding (name + accent) and apply it.
  function loadConfig() {
    if (!TENANT) return;
    fetch(API + "/api/widget-config?t=" + encodeURIComponent(TENANT))
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (c) {
        if (!c) return;
        cfg = c;
        if (c.name) root.querySelector(".acw-title").textContent = c.name;
        if (c.accentColor) root.style.setProperty("--a", c.accentColor);
      })
      .catch(function () {});
  }
  loadConfig();

  launch.addEventListener("click", function () { open(!root.classList.contains("open")); });
  root.querySelector(".acw-min").addEventListener("click", function () { open(false); });
  humanBtn.addEventListener("click", talkToHuman);

  // ── gate flow ──────────────────────────────────────────────────────────────
  function isEmail(v) { return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v.trim()); }

  function startVerify() {
    var name = nameInput.value.trim();
    var email = emailInput.value.trim();
    err1.textContent = "";
    if (name.length < 1) { err1.textContent = "Please enter your name."; nameInput.focus(); return; }
    if (!isEmail(email)) { err1.textContent = "Please enter a valid email."; emailInput.focus(); return; }
    startBtn.disabled = true; startBtn.textContent = "Sending code…";
    fetch(API + "/api/verify/start", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ tenantKey: TENANT, name: name, email: email }),
    })
      .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })
      .then(function (res) {
        startBtn.disabled = false; startBtn.textContent = "Continue";
        if (!res.ok) { err1.textContent = res.d.error || "Something went wrong."; return; }
        userName = name; userEmail = email; otpSession = res.d.sessionId;
        root.querySelector(".acw-echo").textContent = email;
        err2.textContent = "";
        codeInput.value = "";
        // Testing fallback: server returns the code if the email failed to send.
        if (res.d.devCode) {
          devCode.style.display = "block";
          devCode.innerHTML = "🧪 Testing mode — your code is <b>" + res.d.devCode + "</b>";
        } else {
          devCode.style.display = "none";
          devCode.textContent = "";
        }
        gStep("code");
        setTimeout(function () { codeInput.focus(); }, 200);
      })
      .catch(function () { startBtn.disabled = false; startBtn.textContent = "Continue"; err1.textContent = "Network error. Please try again."; });
  }

  function confirmVerify() {
    var code = codeInput.value.trim();
    err2.textContent = "";
    if (!/^\d{6}$/.test(code)) { err2.textContent = "Enter the 6-digit code."; return; }
    verifyBtn.disabled = true; verifyBtn.textContent = "Verifying…";
    fetch(API + "/api/verify/confirm", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ sessionId: otpSession, code: code }),
    })
      .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })
      .then(function (res) {
        verifyBtn.disabled = false; verifyBtn.textContent = "Verify & start chat";
        if (!res.ok) { err2.textContent = res.d.error || "Verification failed."; return; }
        saveIdentity(res.d.token, res.d.name || userName, userEmail, res.d.conversationId);
        enterChat();
      })
      .catch(function () { verifyBtn.disabled = false; verifyBtn.textContent = "Verify & start chat"; err2.textContent = "Network error. Please try again."; });
  }

  startBtn.addEventListener("click", startVerify);
  verifyBtn.addEventListener("click", confirmVerify);
  emailInput.addEventListener("keydown", function (e) { if (e.key === "Enter") startVerify(); });
  codeInput.addEventListener("keydown", function (e) { if (e.key === "Enter") confirmVerify(); });
  codeInput.addEventListener("input", function () { codeInput.value = codeInput.value.replace(/\D/g, "").slice(0, 6); });
  root.querySelector(".acw-resend").addEventListener("click", startVerify);
  root.querySelector(".acw-back").addEventListener("click", function () { err1.textContent = ""; gStep("form"); nameInput.focus(); });

  // input UX
  ta.addEventListener("input", function () {
    ta.style.height = "auto";
    ta.style.height = Math.min(ta.scrollHeight, 96) + "px";
    sendBtn.disabled = !ta.value.trim() || busy;
  });
  ta.addEventListener("focus", function () { inwrap.classList.add("focus"); });
  ta.addEventListener("blur", function () { inwrap.classList.remove("focus"); });
  ta.addEventListener("keydown", function (e) {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); }
  });
  sendBtn.addEventListener("click", send);

  // ── Send ────────────────────────────────────────────────────────────────
  function send() {
    var text = ta.value.trim();
    if (!text || busy) return;
    busy = true;
    ta.value = "";
    ta.style.height = "auto";
    sendBtn.disabled = true;
    addMessage(text, "user");

    var human = mode === "human";
    var typingRow = null;
    if (!human) { typingRow = createTyping(); body.appendChild(typingRow); scrollDown(); }

    var bubble = null, acc = "", doneInfo = null;
    function ensureBubble() { if (!bubble) { if (typingRow) typingRow.remove(); bubble = addRow("bot"); } }
    function finish() {
      busy = false;
      if (typingRow) typingRow.remove();
      if (bubble && !acc) bubble.textContent = "Sorry, I couldn't generate a reply.";
      if (doneInfo && doneInfo.messageId) {
        renderedIds[doneInfo.messageId] = 1;
        if (doneInfo.seq > lastSeq) lastSeq = doneInfo.seq;
      }
      sendBtn.disabled = !ta.value.trim();
      ta.focus();
    }

    sendMessage(text, {
      onUser: function (u) { if (u && u.id) { renderedIds[u.id] = 1; if (u.seq > lastSeq) lastSeq = u.seq; } },
      onQueued: function () { busy = false; if (typingRow) typingRow.remove(); sendBtn.disabled = !ta.value.trim(); ta.focus(); },
      onDelta: function (d) { ensureBubble(); acc += d; bubble.innerHTML = renderMarkdown(acc); scrollDown(); },
      onError: function (msg) { ensureBubble(); acc = msg; bubble.textContent = msg; finish(); },
      onVerifyNeeded: function () { busy = false; if (typingRow) typingRow.remove(); handleVerifyLost(); },
      onDone: function (info) { doneInfo = info; finish(); },
    });
  }

  function sendMessage(text, cb) {
    fetch(API + "/api/chat/" + convId + "/message", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: text, token: chatToken }),
    })
      .then(function (res) {
        if (res.status === 401) { cb.onVerifyNeeded(); return; }
        var ct = res.headers.get("content-type") || "";
        if (ct.indexOf("text/event-stream") !== -1) return readSSE(res, cb);
        return res.json().then(function (d) { if (d && d.user) cb.onUser(d.user); cb.onQueued(); });
      })
      .catch(function () { cb.onError("Network error. Please try again."); });
  }

  function readSSE(res, cb) {
    if (!res.body) { cb.onError("Sorry, something went wrong."); return; }
    var reader = res.body.getReader(), decoder = new TextDecoder(), buf = "", done = null;
    function pump() {
      return reader.read().then(function (r) {
        if (r.done) { cb.onDone(done); return; }
        buf += decoder.decode(r.value, { stream: true });
        var parts = buf.split("\n\n");
        buf = parts.pop();
        parts.forEach(function (chunk) {
          var line = chunk.replace(/^data:\s?/, "").trim();
          if (!line) return;
          var evt; try { evt = JSON.parse(line); } catch (e) { return; }
          if (evt.user) cb.onUser(evt.user);
          else if (evt.delta) cb.onDelta(evt.delta);
          else if (evt.error) cb.onError(evt.error);
          else if (evt.done) done = { messageId: evt.messageId, seq: evt.seq };
        });
        return pump();
      });
    }
    return pump();
  }
})();
