(function () {
    "use strict";

    const RecorsaCore = window.RecorsaCore || {};

    RecorsaCore.utils = RecorsaCore.utils || {};
    RecorsaCore.dom = RecorsaCore.dom || {};
    RecorsaCore.http = RecorsaCore.http || {};

    RecorsaCore.utils.deepMerge = function deepMerge(base, extra) {
        const out = Array.isArray(base) ? base.slice() : { ...(base || {}) };

        if (!extra || typeof extra !== "object") {
            return out;
        }

        for (const [key, value] of Object.entries(extra)) {
            if (value && typeof value === "object" && !Array.isArray(value)) {
                out[key] = deepMerge(out[key] || {}, value);
            } else {
                out[key] = value;
            }
        }

        return out;
    };

    RecorsaCore.utils.buildOverridesFromDataAttributes = function(scriptEl) {
        const overrides = {};
        console.log("buildOverridesFromDataAttributes");
        for (const attr of scriptEl.attributes) {
            if (!attr.name.startsWith("data-")) continue;

            const rawKey = attr.name.slice(5); // entfernt "data-"
            const path = rawKey.split("-");    // z.B. search-input-selector

            console.log("---rawKey", rawKey, " : ", attr.value);

            // optional: letzter Teil zu camelCase zusammensetzen
            // search-input-selector => search.inputSelector
            const normalizedPath = [];

            for (let i = 0; i < path.length; i++) {
                if (i === 0) {
                    normalizedPath.push(path[i]);
                } else {
                    // hier NICHT sofort zusammenziehen, weil wir nesting wollen
                    normalizedPath.push(path[i]);
                }
            }

            // Regel:
            // data-input-selector => inputSelector
            // data-search-input-selector => search.inputSelector
            if (normalizedPath.length === 1) {
                RecorsaCore.utils.setDeep(overrides, [normalizedPath[0]], attr.value);
            } else {
                const first = normalizedPath[0];
                const rest = normalizedPath.slice(1);

                const camelLast = rest.reduce((acc, part, index) => {
                    if (index === 0) return part;
                    return acc + part.charAt(0).toUpperCase() + part.slice(1);
                }, "");

                RecorsaCore.utils.setDeep(overrides, [first, camelLast], attr.value);
            }
        }

        return overrides;
    }

    RecorsaCore.utils.parseValue = function(value) {
        if (value === "true") return true;
        if (value === "false") return false;
        if (value === "null") return null;
        if (value === "undefined") return undefined;
        if (value !== "" && !isNaN(value)) return Number(value);

        return value;
    }

    RecorsaCore.utils.trans = function (cfg, key, fallback = "") {
        if (!cfg || !key) return fallback;

        const value = key.split(".").reduce((acc, key) => {
            return acc && typeof acc === "object" ? acc[key] : undefined;
        }, cfg);

        return value ?? fallback;
    }

    RecorsaCore.utils.setDeep = function(obj, path, value) {
        let current = obj;

        for (let i = 0; i < path.length - 1; i++) {
            const key = path[i];

            if (
                typeof current[key] !== "object" ||
                current[key] === null ||
                Array.isArray(current[key])
            ) {
                current[key] = {};
            }

            current = current[key];
        }

        current[path[path.length - 1]] = RecorsaCore.utils.parseValue(value);
    }

    RecorsaCore.utils.escapeHtml = function (value) {
        return String(value ?? "")
            .replaceAll("&", "&amp;")
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;")
            .replaceAll('"', "&quot;")
            .replaceAll("'", "&#039;");
    };

    RecorsaCore.utils.escapeAttr = function (value) {
        return RecorsaCore.utils.escapeHtml(value);
    };

    RecorsaCore.utils.clampInt = function (value, fallback) {
        const num = Number(value);
        return Number.isFinite(num) ? Math.round(num) : fallback;
    };

    RecorsaCore.utils.uuidv4 = function () {
        if (window.crypto?.randomUUID) {
            return window.crypto.randomUUID();
        }

        return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
            const r = crypto.getRandomValues(new Uint8Array(1))[0] & 15;
            const v = c === "x" ? r : (r & 3) | 8;
            return v.toString(16);
        });
    };

    RecorsaCore.utils.safeJoinUrl = function (base, path) {
        const b = String(base || "").replace(/\/+$/, "");
        const p = String(path || "").replace(/^\/+/, "");

        if (!b && !p) return "#";
        if (!b) return "/" + p;
        if (!p) return b;

        return b + "/" + p;
    };

    RecorsaCore.utils.cleanCategoryPath = function (category) {
        if (!category) return "";

        const parts = String(category)
            .split(">")
            .map((x) => String(x || "").trim())
            .filter(Boolean);

        if (parts.length >= 2 && parts[0].toLowerCase() === "shop") {
            parts.shift();
        }

        return parts.join(" > ");
    };


    RecorsaCore.utils.pickFirstValueByContains = function (obj, needle) {
        if (!obj || typeof obj !== "object") return undefined;
        const k = Object.keys(obj).find((key) => key.includes(needle));
        return k ? obj[k] : undefined;
    }

    RecorsaCore.utils.ICON_PAGE = `<svg class="recorsa-icon d-inline" viewBox="0 0 640 640"><path d="M325.5 96C334 96 342.1 99.4 348.1 105.4L470.6 227.9C476.6 233.9 480 242 480 250.5L480 512C480 529.7 465.7 544 448 544L192 544C174.3 544 160 529.7 160 512L160 128C160 110.3 174.3 96 192 96L325.5 96zM192 64C156.7 64 128 92.7 128 128L128 512C128 547.3 156.7 576 192 576L448 576C483.3 576 512 547.3 512 512L512 250.5C512 233.5 505.3 217.2 493.3 205.2L370.7 82.7C358.7 70.7 342.5 64 325.5 64L192 64z"/></svg>`;
    RecorsaCore.utils.ICON_PDF_LIGHT = `<svg class="recorsa-icon d-inline" viewBox="0 0 640 640"><path d="M128 80L261.5 80C265.1 80 268.6 80.4 272 81.2L272 184C272 214.9 297.1 240 328 240L430.8 240C431.6 243.4 432 246.9 432 250.5L432 400L448 400L448 250.5C448 233.5 441.3 217.2 429.3 205.2L306.7 82.7C294.7 70.7 278.5 64 261.5 64L128 64C92.7 64 64 92.7 64 128L64 512C64 547.3 92.7 576 128 576L208 576L208 560L128 560C101.5 560 80 538.5 80 512L80 128C80 101.5 101.5 80 128 80zM424 224L328 224C305.9 224 288 206.1 288 184L288 88C290.7 89.8 293.1 91.8 295.4 94.1L417.9 216.6C420.2 218.9 422.2 221.4 424 224zM272 464C267.6 464 264 467.6 264 472L264 600C264 604.4 267.6 608 272 608C276.4 608 280 604.4 280 600L280 560L304 560C330.5 560 352 538.5 352 512C352 485.5 330.5 464 304 464L272 464zM304 544L280 544L280 480L304 480C321.7 480 336 494.3 336 512C336 529.7 321.7 544 304 544zM400 464C395.6 464 392 467.6 392 472L392 600C392 604.4 395.6 608 400 608L432 608C454.1 608 472 590.1 472 568L472 504C472 481.9 454.1 464 432 464L400 464zM408 592L408 480L432 480C445.3 480 456 490.7 456 504L456 568C456 581.3 445.3 592 432 592L408 592zM520 472L520 600C520 604.4 523.6 608 528 608C532.4 608 536 604.4 536 600L536 544L576 544C580.4 544 584 540.4 584 536C584 531.6 580.4 528 576 528L536 528L536 480L576 480C580.4 480 584 476.4 584 472C584 467.6 580.4 464 576 464L528 464C523.6 464 520 467.6 520 472z"/></svg>`;
    RecorsaCore.utils.ICON_PDF = `<svg class="recorsa-icon d-inline" viewBox="0 0 640 640"><path d="M128 64C92.7 64 64 92.7 64 128L64 512C64 547.3 92.7 576 128 576L208 576L208 464C208 428.7 236.7 400 272 400L448 400L448 234.5C448 217.5 441.3 201.2 429.3 189.2L322.7 82.7C310.7 70.7 294.5 64 277.5 64L128 64zM389.5 240L296 240C282.7 240 272 229.3 272 216L272 122.5L389.5 240zM272 444C261 444 252 453 252 464L252 592C252 603 261 612 272 612C283 612 292 603 292 592L292 564L304 564C337.1 564 364 537.1 364 504C364 470.9 337.1 444 304 444L272 444zM304 524L292 524L292 484L304 484C315 484 324 493 324 504C324 515 315 524 304 524zM400 444C389 444 380 453 380 464L380 592C380 603 389 612 400 612L432 612C460.7 612 484 588.7 484 560L484 496C484 467.3 460.7 444 432 444L400 444zM420 572L420 484L432 484C438.6 484 444 489.4 444 496L444 560C444 566.6 438.6 572 432 572L420 572zM508 464L508 592C508 603 517 612 528 612C539 612 548 603 548 592L548 548L576 548C587 548 596 539 596 528C596 517 587 508 576 508L548 508L548 484L576 484C587 484 596 475 596 464C596 453 587 444 576 444L528 444C517 444 508 453 508 464z"/></svg>`;
    RecorsaCore.utils.ICON_COMMENT = `<svg class="recorsa-icon" viewBox="0 0 640 640"><path d="M115.9 448.9C83.3 408.6 64 358.4 64 304C64 171.5 178.6 64 320 64C461.4 64 576 171.5 576 304C576 436.5 461.4 544 320 544C283.5 544 248.8 536.8 217.4 524L101 573.9C97.3 575.5 93.5 576 89.5 576C75.4 576 64 564.6 64 550.5C64 546.2 65.1 542 67.1 538.3L115.9 448.9zM153.2 418.7C165.4 433.8 167.3 454.8 158 471.9L140 505L198.5 479.9C210.3 474.8 223.7 474.7 235.6 479.6C261.3 490.1 289.8 496 319.9 496C437.7 496 527.9 407.2 527.9 304C527.9 200.8 437.8 112 320 112C202.2 112 112 200.8 112 304C112 346.8 127.1 386.4 153.2 418.7z"/></svg>`;
    RecorsaCore.utils.ICON_CHEVRON_RIGHT = `<svg class="recorsa-icon-chevron-right" viewBox="0 0 320 512"><path fill="currentColor" d="M305 239c9.4 9.4 9.4 24.6 0 33.9L113 465c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l175-175L79 81c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0L305 239z"/></svg>`;
    RecorsaCore.utils.ICON_TURN_DOWN_RIGHT = `<svg class="recorsa-icon" viewBox="0 0 640 640"><path d="M96 144C96 135.2 88.8 128 80 128C71.2 128 64 135.2 64 144L64 240C64 293 107 336 160 336L521.4 336L404.7 452.7C398.5 458.9 398.5 469.1 404.7 475.3C410.9 481.5 421.1 481.5 427.3 475.3L571.3 331.3C574.3 328.3 576 324.2 576 320C576 315.8 574.3 311.7 571.3 308.7L427.3 164.7C421.1 158.5 410.9 158.5 404.7 164.7C398.5 170.9 398.5 181.1 404.7 187.3L521.4 304L160 304C124.7 304 96 275.3 96 240L96 144z"/></svg>`;
    RecorsaCore.utils.ICON_IMAGE_PLACEHOLDER = `<svg class="recorsa-icon d-inline" viewBox="0 0 640 640" aria-hidden="true"><path d="M480 112C506.5 112 528 133.5 528 160L528 480C528 506.5 506.5 528 480 528L160 528C133.5 528 112 506.5 112 480L112 160C112 133.5 133.5 112 160 112L480 112zM160 96C124.7 96 96 124.7 96 160L96 480C96 515.3 124.7 544 160 544L480 544C515.3 544 544 515.3 544 480L544 160C544 124.7 515.3 96 480 96L160 96zM224 192C241.7 192 256 206.3 256 224C256 241.7 241.7 256 224 256C206.3 256 192 241.7 192 224C192 206.3 206.3 192 224 192zM224 272C250.5 272 272 250.5 272 224C272 197.5 250.5 176 224 176C197.5 176 176 197.5 176 224C176 250.5 197.5 272 224 272zM354 296.2C356.8 291.1 362.2 288 368 288C373.8 288 379.2 291.2 382 296.2L462 440.2C464.8 445.2 464.7 451.2 461.8 456.1C458.9 461 453.7 464 448 464L192 464C186.1 464 180.7 460.8 177.9 455.5C175.1 450.2 175.4 444 178.7 439.1L242.7 343.1C245.3 339.2 249.5 336.6 254.1 336.1C258.7 335.6 263.4 337.1 266.8 340.2L300.8 371.6C302.6 373.3 305.1 374 307.5 373.6C309.9 373.2 312 371.7 313.2 369.6L354 296.2zM368 272C356.4 272 345.7 278.3 340 288.5L304.2 353L277.7 328.5C270.8 322.1 261.5 319.1 252.2 320.2C242.9 321.3 234.6 326.4 229.4 334.2L165.4 430.2C158.9 440 158.2 452.6 163.8 463.1C169.4 473.6 180.2 480 192 480L448 480C459.3 480 469.8 474 475.6 464.2C481.4 454.4 481.5 442.3 476 432.4L396 288.4C390.4 278.2 379.6 271.9 368 271.9z"/></svg>`;

    RecorsaCore.utils.buildUrl = function (baseUrl, path) {
        const b = String(baseUrl ?? "").replace(/\/+$/, "");
        const p = String(path ?? "").replace(/^\/+/, "");
        if (!b && !p) return "#";
        if (!b) return "/" + p;
        if (!p) return b;
        return `${b}/${p}`;
    }

    RecorsaCore.utils.formatEuro = function (value, locale) {
        const finalLocale = locale || "de-DE";

        if (value === undefined || value === null || value === "") {
            return "";
        }

        const num = typeof value === "number"
            ? value
            : Number(String(value).replace(",", "."));

        if (!Number.isFinite(num)) {
            return "";
        }

        return new Intl.NumberFormat(finalLocale, {
            minimumFractionDigits: 2,
            maximumFractionDigits: 2
        }).format(num) + " €";
    };

    RecorsaCore.utils.ensureElement = function (selectorOrEl) {
        if (!selectorOrEl) return null;
        if (typeof selectorOrEl === "string") return document.querySelector(selectorOrEl);
        return selectorOrEl;
    };

    RecorsaCore.utils.buildConversationStorageKey = function (key) {
        return `recorsa_conversation_${key}`;
    };

    RecorsaCore.http.buildHeaders = function (cfg) {
        const headers = {
            "Content-Type": "application/json",
            "X-Recorsa-Widget": "1"
        };

        if (cfg?.csrf) {
            headers["X-CSRF-TOKEN"] = cfg.csrf;
        }

        if (cfg?.token) {
            headers["Authorization"] = `Bearer ${cfg.token}`;
        }

        return headers;
    };

    RecorsaCore.dom.injectCssIntoShadow = function (shadowRoot, cfg) {
        const href = String(cfg?.cssUrl || "").trim();
        if (!href) return;

        if (shadowRoot.querySelector(`link[data-recorsa-css="1"][href="${href}"]`)) {
            return;
        }

        const link = document.createElement("link");
        link.rel = "stylesheet";
        link.href = href;
        link.setAttribute("data-recorsa-css", "1");
        shadowRoot.appendChild(link);
    };

    RecorsaCore.dom.ensureShadowRoot = function (hostEl, cfg, rootClassName) {
        let rootEl = hostEl;
        let shadowRoot = null;

        if (cfg?.useShadowDom && hostEl && typeof hostEl.attachShadow === "function") {
            try {
                shadowRoot = hostEl.shadowRoot || hostEl.attachShadow({ mode: "open" });

                while (shadowRoot.firstChild) {
                    shadowRoot.removeChild(shadowRoot.firstChild);
                }

                RecorsaCore.dom.injectCssIntoShadow(shadowRoot, cfg);

                rootEl = document.createElement("div");
                rootEl.className = rootClassName || "";
                shadowRoot.appendChild(rootEl);

                return {
                    rootEl,
                    shadowRoot,
                    usedShadowDom: true
                };
            } catch (_) {
                // fallback to light DOM
            }
        }

        while (hostEl.firstChild) {
            hostEl.removeChild(hostEl.firstChild);
        }

        rootEl = hostEl;

        if (rootClassName) {
            rootEl.classList.add(rootClassName);
        }

        return {
            rootEl,
            shadowRoot: null,
            usedShadowDom: false
        };
    };

    window.RecorsaCore = RecorsaCore;
})();

;
(function () {
    "use strict";

    if (!window.RecorsaCore) {
        throw new Error("RecorsaCore must be loaded before RecorsaPluginRegistry");
    }

    const registryState = window.__RecorsaPluginRegistryState || {
        plugins: []
    };

    const RecorsaPluginRegistry = window.RecorsaPluginRegistry || {};

    function assertUniqueMapEntries(target, source, label) {
        for (const key of Object.keys(source || {})) {
            if (target[key]) {
                throw new Error(`${label} '${key}' already registered`);
            }
        }
    }

    RecorsaPluginRegistry.register = function (plugin) {
        if (!plugin || typeof plugin !== "object") {
            throw new Error("Plugin must be an object");
        }

        if (!plugin.id) {
            throw new Error("Plugin requires an id");
        }

        if (registryState.plugins.some((x) => x.id === plugin.id)) {
            throw new Error(`Plugin '${plugin.id}' already registered`);
        }

        registryState.plugins.push(plugin);
    };

    RecorsaPluginRegistry.getAll = function () {
        return registryState.plugins.slice();
    };

    RecorsaPluginRegistry.collectSearch = function () {
        const sections = [];
        const renderers = {};
        const entityCallbacks = {};

        for (const plugin of registryState.plugins) {
            const part = plugin.search;
            if (!part) continue;

            if (Array.isArray(part.sections)) {
                sections.push(...part.sections);
            }

            if (part.renderers) {
                assertUniqueMapEntries(renderers, part.renderers, "Search renderer");
                Object.assign(renderers, part.renderers);
            }
        }

        return { sections, renderers, entityCallbacks };
    };

    RecorsaPluginRegistry.collectChat = function () {
        const sections = [];
        const renderers = {};
        const entityCallbacks = {};

        for (const plugin of registryState.plugins) {
            const part = plugin.chat;
            if (!part) continue;

            if (Array.isArray(part.sections)) {
                sections.push(...part.sections);
            }

            if (part.renderers) {
                assertUniqueMapEntries(renderers, part.renderers, "Search renderer");
                Object.assign(renderers, part.renderers);
            }
        }

        return { sections, renderers, entityCallbacks };
    };

    window.__RecorsaPluginRegistryState = registryState;
    window.RecorsaPluginRegistry = RecorsaPluginRegistry;
})();

;
(function () {
    "use strict";

    if (!window.RecorsaCore) {
        throw new Error("RecorsaCore must be loaded before brochure plugin");
    }

    if (!window.RecorsaPluginRegistry) {
        throw new Error("RecorsaPluginRegistry must be loaded before brochure plugin");
    }

    const {
        escapeHtml,
        escapeAttr,
        safeJoinUrl,
        cleanCategoryPath,
        trans
    } = window.RecorsaCore.utils;

    const ICON_YARN = '<svg class="recorsa-icon d-inline" viewBox="0 0 640 640"><path d="M96.6 336L543.5 336C541.9 358.4 537 379.9 529.4 400L110.7 400C103 379.9 98.1 358.4 96.6 336zM225.8 523.3L250.3 432L333.5 432L303.9 543.4C276.2 541.4 249.9 534.4 225.9 523.3zM197 507.2C167.9 488 143.5 462.2 126 432L217.2 432L197 507.2zM514 432C478 494.3 412.7 537.7 337 543.4L366.6 432L514 432zM444 544C522.7 500.3 576 416.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576L560 576C568.8 576 576 568.8 576 560C576 551.2 568.8 544 560 544L444 544zM543.4 304L96.6 304C98.2 281.6 103.1 260.1 110.7 240L529.3 240C537 260.1 541.9 281.6 543.4 304zM307.4 96.3L277.2 208L126 208C162.7 144.4 229.8 100.6 307.4 96.3zM340.4 96.9C367.8 99.4 393.8 106.8 417.5 118.3L393.5 208L310.4 208L340.5 96.9zM514 208L426.6 208L446.2 134.9C473.9 153.8 497.2 178.8 514 208z"/></svg>';

    function renderSearchSuedwolleLibraryBrochureSection(items, cfg, section) {
        const limit = Number.isInteger(section.limit) ? section.limit : items.length;
        const itemsHtml = items.slice(0, limit) .map((item) => renderSearchSuedwolleLibraryBrochureItem(item, cfg)).join("");

        const title = trans(cfg.texts, `sections.${section.key}.title`) || "";

        return `
        <div class="recorsa-suggest-products">
            <h6 class="recorsa-suggest-section-header">${escapeHtml(title)}</h6>
            <div class="recorsa-suggest-section-content">
                ${itemsHtml}
            </div>
        </div>
    `;
    }

    function renderSearchSuedwolleLibraryBrochureItem(item, cfg) {

        const img = item.list_image || "";
        const url = item?.url ?? "#";
        const category = cleanCategoryPath(item?.category ?? '');

        return `
            <div class="recorsa-suggest-item recorsa-suggest-brochure"
                 data-entity-type="suedwolle_library_brochure"
                 data-entity-json='${escapeAttr(JSON.stringify(item))}'
                 role="button">

                <div class="recorsa-suggest-item-row">
                    <div class="recorsa-suggest-thumb-wrap">
                        ${img ? `<img class="recorsa-suggest-thumb" src="${escapeAttr(img)}" alt="">` : ""}
                    </div>
                    <div class="recorsa-suggest-grow">
                        <div class="recorsa-suggest-item-title">${escapeHtml(item?.title ?? "")}</div>
                        <div class="recorsa-suggest-item-category">${escapeHtml(category)}</div>
                    </div>
                </div>

            </div>
            `;
    }

    function renderSearchSuedwolleLibraryCategorySection(items, cfg, section) {
        const blockTitle = trans(cfg.texts, `sections.${section.key}.title`);
        const limit = Number.isInteger(section.limit) ? section.limit : items.length;
        const itemsHtml = items.slice(0, limit).map((item, cfg) => {
            var html = "";

            const a = document.createElement("a");
            a.className = "recorsa-suggest-linkitem";
            a.target = "_blank";
            a.rel = "noopener noreferrer";
            a.href = RecorsaCore.utils.buildUrl(cfg.shopBaseUrl, item?.url ?? "#");

            const title = document.createElement("div");
            title.className = "recorsa-suggest-linkitem-title";
            title.innerHTML = `${RecorsaCore.utils.ICON_PAGE}`;

            let path = Array.isArray(item?.category_path) ? [...item.category_path] : [];
            if (path.length >= 2 && String(path[0]).trim().toLowerCase() === "shop") {
                path.shift();
            }

            const text = document.createElement("span");
            text.textContent = String(item.title ?? "");
            title.appendChild(text);

            a.appendChild(title);
            html += a.outerHTML;
            return html;
        }).join("");

        return `
        <div class="recorsa-suggest-categories">
            <h6 class="recorsa-suggest-section-header">${escapeHtml(blockTitle)}</h6>
            <div class="recorsa-suggest-section-content">
                <div class="recorsa-suggest-linklist">
                    ${itemsHtml}
                </div>
            </div>
        </div>
    `;
    }

    function renderSearchSuedwolleLibraryYarnSection(items, cfg, section) {
        console.log('test');
        const limit = Number.isInteger(section.limit) ? section.limit : items.length;
        const itemsHtml = items.slice(0, limit) .map((item) => renderSearchSuedwolleLibraryYarnItem(item, cfg)).join("");

        const title = trans(cfg.texts, `sections.${section.key}.title`) || "";

        return `
        <div class="recorsa-suggest-products">
            <h6 class="recorsa-suggest-section-header">${escapeHtml(title)}</h6>
            <div class="recorsa-suggest-section-content">
                ${itemsHtml}
            </div>
        </div>
    `;
    }

    function renderSearchSuedwolleLibraryYarnItem(item, cfg) {
        console.log('test', item);

        const img = undefined;
        const url = item?.url ?? "#";

        return `
            <div class="recorsa-suggest-item recorsa-suggest-brochure"
                 data-entity-type="suedwolle_library_yarn"
                 data-entity-json='${escapeAttr(JSON.stringify(item))}'
                 role="button">

                <div class="recorsa-suggest-item-row">
                    <div class="recorsa-suggest-thumb-wrap">
                        ${img ? `<img class="recorsa-suggest-thumb" src="${escapeAttr(img)}" alt="">` : `${ICON_YARN}`}
                    </div>
                    <div class="recorsa-suggest-grow">
                        <div class="recorsa-suggest-item-title">${escapeHtml(item?.title ?? "")}</div>
                    </div>
                </div>

            </div>
            `;
    }

    function renderChatSuedwolleLibraryBrochureSection(items, cfg, section) {
        const limit = Number.isInteger(section.limit) ? section.limit : items.length;
        var itemsHtml = items.slice(0, limit) .map((item) => {
            const id = item.id || "";
            const url = item.url ? safeJoinUrl(cfg.shopBaseUrl, String(item.url)) : "#";
            const title = item.title || "";
            const img = item.list_image || "";
            const badge = "";



            const askText = trans(cfg.texts, "actions.ask_question");

            return `
                      <div class="product-suggestion-item recorsa-suggest-brochure"
                         data-entity-type="suedwolle_library_brochure"
                         data-entity-json='${escapeAttr(JSON.stringify(item))}'
                         style="height:fit-content!important;     width: 200px;">
                          <div class="product-card brochure-card border rounded-0 h-100 d-flex flex-column bg-white"
                            style="
                                background-image: url('${escapeAttr(img)}') !important;
                                background-size: cover !important;
                                background-repeat: no-repeat !important;
                                background-position: center !important;
                            "
                          >
                            <div class="product-info p-2" style="background:transparent!important; min-height:90px;">
                              <span class="product-title fs-11 mb-0"
                                style="
                                background: #fff;
                                display: inline;
                                padding: 2px 4px;
                                border-radius: 4px;
                                text-transform: uppercase;
                                font-size: 10px;
                              ">${escapeHtml(title)}</span>
                            </div>
                            <button
                                type="button"
                                class="entity-selection"
                                data-recorsa-action="select-entity"
                                data-entity-type="suedwolle_library_brochure"
                                data-entity-id="${escapeHtml(id)}"
                                data-entity-title="${escapeHtml(title)}"
                                data-entity-url="${escapeHtml(url)}"
                                data-entity-image="${escapeHtml(img || "")}"
                                style="align-self: end;
                                    margin: 2px;
                                    width: calc(100% - 4px);
                                    border-radius: 8px;
                                    "
                            >
                                <div class="d-flex justify-content-between align-items-center px-2">${RecorsaCore.utils.ICON_COMMENT}<span>${escapeHtml(askText)}</span></div>
                            </button>
                          </div>
                      </div>
                    `;
        }).join("");
        return '<div class="product-suggestions-container">' + itemsHtml + '</div>';
    }

    function renderChatSuedwolleLibraryCategorySection(items, cfg, section) {
        const limit = Number.isInteger(section.limit) ? section.limit : items.length;
        var itemsHtml = items.slice(0, limit) .map((item, cfg) => {
            const url = item.url ? RecorsaCore.utils.safeJoinUrl(cfg.shopBaseUrl, String(item.url)) : "#";
            return `<li><a href="${escapeHtml(url)}" class="recorsa-chip page-chip" target="_blank" rel="noopener">${RecorsaCore.utils.ICON_PAGE} ${escapeHtml(
                item.title || ""
            )} ${RecorsaCore.utils.ICON_CHEVRON_RIGHT}</a></li>`;
        })
            .join("");

        return `
            <div class="recorsa-linklist">
              <ul class="recorsa-linklist-items">${itemsHtml}</ul>
            </div>
          `;
    }

    function renderChatSuedwolleLibraryYarnSection(items, cfg, section) {
        const limit = Number.isInteger(section.limit) ? section.limit : items.length;
        var itemsHtml = items.slice(0, limit) .map((item) => {
            const id = item.id || "";
            const url = item.url ? safeJoinUrl(cfg.shopBaseUrl, String(item.url)) : "#";
            const title = item.title || "";
            const img = item.list_image || "";
            const badge = "";

            const askText = trans(cfg.texts, "actions.ask_question");

            return `
                      <div class="product-suggestion-item recorsa-suggest-brochure"
                         data-entity-type="suedwolle_library_yarn"
                         data-entity-json='${escapeAttr(JSON.stringify(item))}'
                         style="height:fit-content!important">
                          <div class="product-card brochure-card border rounded-0 h-100 d-flex flex-column bg-white">
                            <div class="product-image-wrapper">
                              ${img ? `<img class="product-image object-fit-contain rounded-3" style="width:80px;" src="${escapeHtml(img)}" alt="">`
                : `${ICON_YARN}`}
                              ${badge ? `<span class="badge rounded-pill bg-success position-absolute mt-2 me-2 z-2 top-0 end-0">${escapeHtml(badge)}</span>` : ``}
                            </div>
                            <div class="product-info p-2">
                              <span class="product-title fs-11 mb-0">${escapeHtml(title)}</span>
                            </div>
                            <button
                                type="button"
                                class="entity-selection"
                                data-recorsa-action="select-entity"
                                data-entity-type="suedwolle_library_yarn"
                                data-entity-id="${escapeHtml(id)}"
                                data-entity-title="${escapeHtml(title)}"
                                data-entity-url="${escapeHtml(url)}"
                                data-entity-image="${escapeHtml(img || "")}"
                            >
                                <div class="d-flex justify-content-between align-items-center px-2">${RecorsaCore.utils.ICON_COMMENT}<span>${escapeHtml(askText)}</span></div>
                            </button>
                          </div>
                      </div>
                    `;
        }).join("");
        return '<div class="product-suggestions-container">' + itemsHtml + '</div>';
    }

    function renderEntityFollowups(block, ctx) {
        const entity = block.entity || {};
        const items = Array.isArray(block.items) ? block.items : [];

        if (!items.length) return;

        const html = `
            <div class="recorsa-chipblock">
                <div class="recorsa-chiprow">
                    ${items.map((text) => `
                        <button
                            class="recorsa-chip entity-followup-chip"
                            type="button"
                            data-recorsa-action="entity-followup"
                            data-question="${escapeAttr(text)}"
                            data-entity-type="${escapeAttr(entity.entity_type || "")}"
                            data-entity-id="${escapeAttr(entity.entity_id || "")}"
                            data-entity-title="${escapeAttr(entity.entity_title || "")}"
                            data-entity-url="${escapeAttr(entity.entity_url || "")}"
                            data-entity-image="${escapeAttr(entity.entity_image || "")}"
                        >
                            ${escapeHtml(text)}
                        </button>
                    `).join("")}
                </div>
            </div>
        `;

        ctx.api.appendBotHtml(html, {
            showAvatar: false,
            showPersonLine: false,
            bubble: false
        });
    }


    window.RecorsaPluginRegistry.register({
        id: "wordpress-plugin-suedwolle-library-brochure",

        search: {
            sections: [
                {
                    key: "suedwolle_library_category",
                    renderSection: "suedwolle_library_category.search.section",
                },
                {
                    key: "suedwolle_library_brochure",
                    renderSection: "suedwolle_library_brochure.search.section",
                    callback: function(e, el, query, item, cfg) {
                        if (typeof suedwolle_library_brochure_open_brochure === 'function') {
                            suedwolle_library_brochure_open_brochure(query, item);
                        }
                    }
                },
                {
                    key: "suedwolle_library_yarn",
                    renderSection: "suedwolle_library_yarn.search.section",
                    callback: function(e, el, query, item, cfg) {
                        if (typeof suedwolle_library_brochure_open_yarn === 'function') {
                            suedwolle_library_brochure_open_yarn(query, item);
                        }
                    }
                }
            ],
            renderers: {
                "suedwolle_library_category.search.section": renderSearchSuedwolleLibraryCategorySection,
                "suedwolle_library_brochure.search.section": renderSearchSuedwolleLibraryBrochureSection,
                "suedwolle_library_yarn.search.section": renderSearchSuedwolleLibraryYarnSection
            }
        },

        chat: {
            sections: [
                {
                    key: "suedwolle_library_category",
                    renderSection: "suedwolle_library_category.chat.section",
                },
                {
                    key: 'suedwolle_library_brochure',
                    renderSection: 'suedwolle_library_brochure.chat.section',
                    callback: function(e, el, query, item, cfg) {
                        if (typeof suedwolle_library_brochure_open_brochure === 'function') {
                            suedwolle_library_brochure_open_brochure(item, cfg);
                        }
                    }
                },
                {
                    key: 'suedwolle_library_yarn',
                    renderSection: 'suedwolle_library_yarn.chat.section',
                    callback: function(e, el, query, item, cfg) {
                        if (typeof suedwolle_library_brochure_open_yarn === 'function') {
                            suedwolle_library_brochure_open_yarn(item, cfg);
                        }
                    }
                }
            ],
            renderers: {
                'suedwolle_library_category.chat.section': renderChatSuedwolleLibraryCategorySection,
                'suedwolle_library_brochure.chat.section': renderChatSuedwolleLibraryBrochureSection,
                'suedwolle_library_yarn.chat.section': renderChatSuedwolleLibraryYarnSection
            }
        }
    });
})();

;
(function () {
    "use strict";

    if (!window.RecorsaCore) {
        throw new Error("RecorsaCore must be loaded before RecorsaSearchWidget");
    }

    if (!window.RecorsaPluginRegistry) {
        throw new Error("RecorsaPluginRegistry must be loaded before RecorsaSearchWidget");
    }

    function deepMerge(base, extra) {
        const out = Array.isArray(base) ? base.slice() : { ...base };
        if (!extra || typeof extra !== "object") return out;

        for (const [k, v] of Object.entries(extra)) {
            if (v && typeof v === "object" && !Array.isArray(v)) {
                out[k] = deepMerge(out[k] || {}, v);
            } else {
                out[k] = v;
            }
        }
        return out;
    }

    function escapeHtml(value) {
        return String(value ?? "")
            .replaceAll("&", "&amp;")
            .replaceAll("<", "&lt;")
            .replaceAll(">", "&gt;")
            .replaceAll('"', "&quot;")
            .replaceAll("'", "&#039;");
    }

    function clampInt(n, fallback) {
        const x = Number(n);
        return Number.isFinite(x) ? Math.round(x) : fallback;
    }

    function neutralizeInput(input) {
        if (!input) return;

        input.setAttribute("autocomplete", "off");
        input.setAttribute("autocorrect", "off");
        input.setAttribute("autocapitalize", "off");
        input.setAttribute("spellcheck", "false");

        const form = input.closest("form");
        if (form) {
            form.removeAttribute("data-bs-toggle");
            form.removeAttribute("data-bs-display");
        }
    }

    function injectCssIntoShadow(shadowRoot, finalCfg) {
        const href = String(finalCfg.links.cssEndpoint || "").trim();
        if (!href) return Promise.resolve();

        const existing = shadowRoot.querySelector(`link[data-recorsa-css="1"][href="${href}"]`);
        if (existing) {
            if (existing.dataset.loaded === "1") return Promise.resolve();

            return new Promise((resolve, reject) => {
                existing.addEventListener("load", () => resolve(), { once: true });
                existing.addEventListener("error", reject, { once: true });
            });
        }

        return new Promise((resolve, reject) => {
            const link = document.createElement("link");
            link.rel = "stylesheet";
            link.href = href;
            link.setAttribute("data-recorsa-css", "1");

            link.addEventListener("load", () => {
                link.dataset.loaded = "1";
                resolve();
            }, { once: true });

            link.addEventListener("error", reject, { once: true });

            shadowRoot.appendChild(link);
        });
    }

    function ensureHostWrapper(inputEl) {
        const parent = inputEl.parentElement;
        if (!parent) return null;

        if (parent.classList.contains("recorsa-search-anchor")) {
            return parent;
        }

        const wrapper = document.createElement("div");
        wrapper.className = "recorsa-search-anchor";
        wrapper.style.position = "relative";
        wrapper.style.width = "100%";

        parent.insertBefore(wrapper, inputEl);
        wrapper.appendChild(inputEl);

        return wrapper;
    }

    function positionShadowHost(wrapperEl, inputEl, shadowHostEl, gapPx = 8) {
        if (!wrapperEl || !inputEl || !shadowHostEl) return;

        const left = inputEl.offsetLeft;
        const top = inputEl.offsetTop + inputEl.offsetHeight + gapPx;
        const width = inputEl.offsetWidth;

        shadowHostEl.style.left = `${left}px`;
        shadowHostEl.style.top = `${top}px`;
        shadowHostEl.style.width = `${width}px`;
    }

    function ensureShadowHost(wrapperEl) {
        let hostEl = wrapperEl.querySelector(":scope > .recorsa-search-shadow-host");
        if (hostEl) return hostEl;

        hostEl = document.createElement("div");
        hostEl.className = "recorsa-search-shadow-host";
        hostEl.style.zIndex = "9999"; //FIXME Später über Konfiguration
        hostEl.style.position = "absolute";
        hostEl.style.pointerEvents = "none";
        /*
        hostEl.style.zIndex = "1";
        hostEl.style.pointerEvents = "none";
        hostEl.style.position = "absolute";
        hostEl.style.left = "0";
        hostEl.style.top = "calc(100% + 8px)";
        hostEl.style.width = "100%";
        hostEl.style.zIndex = "1";
        hostEl.style.pointerEvents = "none";
         */

        wrapperEl.appendChild(hostEl);
        return hostEl;
    }

    function buildMarkup(rootEl, cfg) {
        rootEl.innerHTML = `
            <div class="recorsa-search">
                <div class="recorsa-search-dropdown" data-recorsa="dropdown" style="display:none;">
                    <div class="recorsa-search-panel">

                        <div class="recorsa-search-loading d-none" data-recorsa="loading">
                            <div class="recorsa-search-spinner" aria-hidden="true"></div>
                        </div>

                        <div class="recorsa-search-scroll" data-recorsa="scroll" style="max-height:${clampInt(cfg.maxHeight, 650)}px;">
                        </div>
                        <div class="recorsa-search-empty d-none">${escapeHtml((cfg.texts && cfg.texts.empty) || "Keine Ergebnisse gefunden.")}</div>
                    </div>
                </div>
            </div>
        `;
    }

    function applyTheme(hostEl, rootEl, cfg) {
        const t = cfg.theme || {};

        const px = (value, fallback) => {
            const n = Number(value);
            return Number.isFinite(n) ? `${Math.round(n)}px` : fallback;
        };

        hostEl.style.setProperty("--recorsa-search-z", String(clampInt(t.containerZIndex, 9999)));

        hostEl.style.setProperty(
            "--recorsa-search-container-bg",
            String(t.containerBgColor || "#ffffff")
        );

        hostEl.style.setProperty(
            "--recorsa-search-container-border",
            String(t.containerBorderColor || "#d8e2ef")
        );

        hostEl.style.setProperty(
            "--recorsa-search-container-shadow",
            t.containerShadowEnabled === false
                ? "none"
                : String(t.containerShadow || "0 0.5em 1.25em rgba(15, 23, 42, 0.12)")
        );

        hostEl.style.setProperty(
            "--recorsa-search-container-radius",
            px(t.containerBorderRadius, "12px")
        );

        hostEl.style.setProperty(
            "--recorsa-search-container-font-family",
            String(t.containerFontFamily || "").trim() || "inherit"
        );

        hostEl.style.setProperty(
            "--recorsa-search-section-heading",
            String(t.sectionHeadingColor || "#5e6e82")
        );

        hostEl.style.setProperty(
            "--recorsa-search-section-text-transform",
            String(t.sectionTextTransform || "uppercase")
        );

        hostEl.style.setProperty(
            "--recorsa-search-section-letter-spacing",
            px(t.sectionLetterSpacing, "1px")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-bg",
            String(t.productCardBgColor || "rgba(255, 255, 255, 0.7)")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-hover-bg",
            String(t.productCardHoverBgColor || "rgba(255, 255, 255, 1)")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-text",
            String(t.productCardTextColor || "#344050")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-hover-text",
            String(t.productCardHoverTextColor || "#344050")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-heading",
            String(t.productCardHeadingColor || "#344050")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-hover-heading",
            String(t.productCardHoverHeadingColor || "#344050")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-price",
            String(t.productCardPriceColor || "#00a854")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-card-hover-price",
            String(t.productCardHoverPriceColor || "#00a854")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-image-bg",
            String(t.productImageBgColor || "#ffffff")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-image-hover-bg",
            String(t.productImageHoverBgColor || "#ffffff")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-image-border",
            String(t.productImageBorderColor || "#dddddd")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-image-hover-border",
            String(t.productImageHoverBorderColor || "#dddddd")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-image-radius",
            px(t.productImageBorderRadius, "5px")
        );

        hostEl.style.setProperty(
            "--recorsa-search-product-image-padding",
            px(t.productImagePadding, "5px")
        );

        hostEl.style.setProperty(
            "--recorsa-search-badge-bg",
            String(t.badgeBgColor || "#d8f3ff")
        );

        hostEl.style.setProperty(
            "--recorsa-search-badge-hover-bg",
            String(t.badgeHoverBgColor || "#d9e7fa")
        );

        hostEl.style.setProperty(
            "--recorsa-search-badge-text",
            String(t.badgeTextColor || "#197aa4")
        );

        hostEl.style.setProperty(
            "--recorsa-search-badge-hover-text",
            String(t.badgeHoverTextColor || "#1c5bbf")
        );

        hostEl.style.setProperty(
            "--recorsa-search-badge-radius",
            px(t.badgeBorderRadius, "8px")
        );

        hostEl.style.setProperty(
            "--recorsa-search-link-bg",
            String(t.linkBgColor || "transparent")
        );

        hostEl.style.setProperty(
            "--recorsa-search-link-hover-bg",
            String(t.linkHoverBgColor || "#ffffff")
        );

        hostEl.style.setProperty(
            "--recorsa-search-link-text",
            String(t.linkTextColor || "#344050")
        );

        hostEl.style.setProperty(
            "--recorsa-search-link-hover-text",
            String(t.linkHoverTextColor || "#344050")
        );

        hostEl.style.setProperty(
            "--recorsa-search-link-radius",
            px(t.linkBorderRadius, "8px")
        );

        hostEl.style.setProperty(
            "--recorsa-search-muted",
            String(t.muted || "#748194")
        );

        hostEl.style.setProperty(
            "--recorsa-search-pdf",
            String(t.pdf || "#e63757")
        );

        const scrollEl = rootEl.querySelector('[data-recorsa="scroll"]');
        if (scrollEl) {
            scrollEl.style.maxHeight = px(t.maxHeight, "650px");
        }

        const allowedBgVariants = new Set(["none", "minimal", "waves", "bubbles", "dots", "stripes"]);
        const bgVariant = allowedBgVariants.has(String(t.containerBackgroundVariant || "").trim())
            ? String(t.containerBackgroundVariant).trim()
            : "minimal";

        rootEl.classList.remove(
            "recorsa-search-bg-none",
            "recorsa-search-bg-minimal",
            "recorsa-search-bg-waves",
            "recorsa-search-bg-bubbles",
            "recorsa-search-bg-dots",
            "recorsa-search-bg-stripes"
        );
        rootEl.classList.add(`recorsa-search-bg-${bgVariant}`);
    }

    const RecorsaSearchWidget = {
        mount(finalCfg) {
            const inputEl = document.querySelector(finalCfg.inputSelector);

            if (!inputEl) {
                console.error("[RecorsaSearchWidget] input element not found");
                return null;
            }

            if (!finalCfg.links.suggestEndpoint) {
                console.error("[RecorsaSearchWidget] Missing config.suggestEndpoint");
                return null;
            }

            if (!finalCfg.saleschannelId && inputEl.dataset.saleschannelId) {
                finalCfg.saleschannelId = inputEl.dataset.saleschannelId;
            }

            if (inputEl.__recorsaInstance && typeof inputEl.__recorsaInstance.destroy === "function") {
                try {
                    inputEl.__recorsaInstance.destroy();
                } catch (_) {}
            }

            const wrapperEl = ensureHostWrapper(inputEl);
            if (!wrapperEl) {
                console.error("[RecorsaSearchWidget] could not create wrapper");
                return null;
            }

            const shadowHostEl = ensureShadowHost(wrapperEl);
            positionShadowHost(
                wrapperEl,
                inputEl,
                shadowHostEl,
                clampInt(finalCfg.dropdownOffsetY, 8)
            );

            let widgetRootEl = shadowHostEl;
            let shadowRoot = null;

            shadowHostEl.classList.add("recorsa-search-host");

            shadowRoot = shadowHostEl.shadowRoot || shadowHostEl.attachShadow({ mode: "open" });

            while (shadowRoot.firstChild) shadowRoot.removeChild(shadowRoot.firstChild);

            widgetRootEl = document.createElement("div");
            widgetRootEl.className = "recorsa-search-root";
            widgetRootEl.style.display = "none";
            shadowRoot.appendChild(widgetRootEl);

            buildMarkup(widgetRootEl, finalCfg);
            applyTheme(shadowHostEl, widgetRootEl, finalCfg);

            const instance = createInstance(wrapperEl, shadowHostEl, widgetRootEl, inputEl, finalCfg);

            inputEl.__recorsaInstance = instance;
            shadowHostEl.__recorsaInstance = instance;

            injectCssIntoShadow(shadowRoot, finalCfg)
                .catch((err) => {
                    console.error("[RecorsaSearchWidget] CSS failed to load", err);
                })
                .finally(() => {
                    widgetRootEl.style.display = "";

                    if (typeof finalCfg.onReady === "function") {
                        try {
                            finalCfg.onReady(instance);
                        } catch (_) {}
                    }
                });

            return instance;
        },

        unmount(inputSelectorOrEl) {
            const inputEl = typeof inputSelectorOrEl === "string"
                ? document.querySelector(inputSelectorOrEl)
                : inputSelectorOrEl;

            if (!inputEl) return;

            const inst = inputEl.__recorsaInstance;
            if (inst && typeof inst.destroy === "function") {
                inst.destroy();
            }

            inputEl.__recorsaInstance = null;
        }
    };

    function createInstance(wrapperEl, shadowHostEl, rootEl, inputEl, cfg) {
        const state = {
            destroyed: false,
            isOpen: false,
            debounceTimer: null,
            abortController: null,
            lastQuery: "",
            lastData: null
        };

        const $ = (sel) => rootEl.querySelector(sel);

        const dropdownEl = $('[data-recorsa="dropdown"]');
        const scrollEl = $('[data-recorsa="scroll"]');
        const loadingEl = $('[data-recorsa="loading"]');
        const resultContainer = rootEl.querySelector(".recorsa-search-scroll");
        const emptyState = rootEl.querySelector(".recorsa-search-empty");

        const cleanupFns = [];

        function syncPosition() {
            const gapPx = clampInt(cfg.dropdownOffsetY, 8);
            positionShadowHost(wrapperEl, inputEl, shadowHostEl, gapPx);
        }

        function on(target, event, handler, options) {
            if (!target) return;
            target.addEventListener(event, handler, options);
            addCleanup(() => {
                try {
                    target.removeEventListener(event, handler, options);
                } catch (_) {}
            });
        }

        function addCleanup(fn) {
            cleanupFns.push(fn);
        }

        function isEventInsideWidget(e) {
            const path = typeof e.composedPath === "function" ? e.composedPath() : [];

            if (path.includes(inputEl)) return true;
            if (path.includes(wrapperEl)) return true;
            if (path.includes(shadowHostEl)) return true;
            if (path.includes(rootEl)) return true;

            return false;
        }

        function openDropdown() {
            if (state.destroyed) return;
            dropdownEl.style.display = "block";
            shadowHostEl.style.pointerEvents = "auto";
            shadowHostEl.classList.add("recorsa-search-open");
            state.isOpen = true;

            if (typeof cfg.onOpen === "function") {
                try {
                    cfg.onOpen();
                } catch (_) {}
            }
        }

        function closeDropdown() {
            if (state.destroyed) return;
            dropdownEl.style.display = "none";
            shadowHostEl.style.pointerEvents = "none";
            shadowHostEl.classList.remove("recorsa-search-open");
            state.isOpen = false;

            if (typeof cfg.onClose === "function") {
                try {
                    cfg.onClose();
                } catch (_) {}
            }
        }

        function clearResults() {
            resultContainer.innerHTML = "";
            emptyState.classList.add("d-none");
        }

        function showEmptyState() {
            emptyState.classList.remove("d-none");
        }

        function hideEmptyState() {
            emptyState.classList.add("d-none");
        }

        function showLoading() {
            if (!loadingEl) return;

            loadingEl.classList.remove("d-none");
            resultContainer.classList.add("recorsa-is-loading");
            emptyState.classList.add("d-none");

            openDropdown();
        }

        function hideLoading() {
            if (!loadingEl) return;

            loadingEl.classList.add("d-none");
            resultContainer.classList.remove("recorsa-is-loading");
        }

        function hasAnyVisibleResults() {
            return Boolean(
                resultContainer.innerHTML.trim()
            );
        }

        async function fetchResults(query) {
            if (state.abortController) {
                try {
                    state.abortController.abort();
                } catch (_) {}
            }

            state.abortController = new AbortController();
            console.log(cfg);
            const url = new URL(cfg.links.suggestEndpoint, window.location.href);
            url.searchParams.set("q", query);
            url.searchParams.set("l", cfg.locale);
            url.searchParams.set("widgetLocale", cfg.widgetLocale);
            console.log(url.toString());
            const response = await fetch(url.toString(), {
                method: "GET",
                credentials: "same-origin",
                signal: state.abortController.signal
            });

            if (!response.ok) {
                throw new Error("Network error: " + response.status);
            }

            return await response.json();
        }

        function renderResults(result) {
            state.lastData = result;

            let html = "";

            var registrySearchCollection = RecorsaPluginRegistry.collectSearch();
            if (Array.isArray(result.blocks)) {
                for (const block of result.blocks) {
                    if (!block || !block.type) continue;

                    if (block.type === "text") {
                        if (!block.content) continue;
                        html += `<h6 class="recorsa-suggest-section-header">${escapeHtml(block.content)}</h6>`;
                    } else if (block.type === "entity_type") {
                        if (!block.items) continue;
                        for (const entityKey in block.items) {
                            console.log(entityKey);
                            var entityItems = block.items[entityKey];
                            if (!entityItems) continue;

                            const section = registrySearchCollection.sections.find((s) => s.key === entityKey);
                            if (!section) continue;

                            const rendererSection = registrySearchCollection.renderers?.[section.renderSection];
                            if (typeof rendererSection !== "function") continue;

                            const items = Array.isArray(entityItems) ? entityItems : [];
                            if (!items.length) continue;

                            html += rendererSection(items, cfg, section);
                        }
                    }
                }
            }
            /*
            for (const section of registrySearchCollection.sections) {
                const items = Array.isArray(result.data?.[section.key]) ? result.data[section.key] : [];
                if (!items.length) continue;

                const rendererSection = registrySearchCollection.renderers?.[section.renderSection];
                if (typeof rendererSection !== "function") continue;

                html += rendererSection(items, cfg, section);
            }
            */

            resultContainer.innerHTML = html;

            if (hasAnyVisibleResults()) {
                hideEmptyState();
                openDropdown();
            } else {
                showEmptyState();
                openDropdown();
            }

            if (scrollEl) {
                scrollEl.scrollTop = 0;
            }

            if (typeof cfg.onResults === "function") {
                try {
                    cfg.onResults(result);
                } catch (_) {}
            }
        }

        function reopenFromCache() {
            const query = String(inputEl.value || "").trim();

            if (!query || query.length < clampInt(cfg.minChars, 2)) {
                closeDropdown();
                return;
            }

            if (state.lastData) {
                renderResults(state.lastData);
                return;
            }

            if (hasAnyVisibleResults()) {
                openDropdown();
            }
        }

        async function handleQuery(query) {
            const trimmed = String(query ?? "").trim();
            state.lastQuery = trimmed;

            if (!trimmed || trimmed.length < clampInt(cfg.minChars, 2)) {
                hideLoading();
                clearResults();
                closeDropdown();
                return;
            }

            showLoading();

            try {
                const result = await fetchResults(trimmed);
                if (trimmed !== state.lastQuery) return;
                hideLoading();
                renderResults(result);
            } catch (err) {
                if (err?.name === "AbortError") return;

                console.error("[RecorsaSearchWidget] fetch failed:", err);

                hideLoading();
                clearResults();
                showEmptyState();
                openDropdown();

                if (typeof cfg.onError === "function") {
                    try {
                        cfg.onError(err);
                    } catch (_) {}
                }
            }
        }

        function scheduleQuery(query) {
            if (state.debounceTimer) {
                clearTimeout(state.debounceTimer);
            }

            state.debounceTimer = window.setTimeout(() => {
                handleQuery(query);
            }, clampInt(cfg.debounceMs, 400));
        }

        function neutralizeInputBehavior() {
            if (!cfg.disableExistingBehavior) return;
            neutralizeInput(inputEl);
        }

        function shouldSuppressResultsNavigation() {
            return cfg.suppressResultsNavigation === true
                || cfg.suppressResultsNavigation === 1
                || cfg.suppressResultsNavigation === "1"
                || cfg.suppressResultsNavigation === "true";
        }

        function isDedicatedSearchForm(form) {
            if (!form) return false;

            const fields = form.querySelectorAll("input, textarea, select");
            for (const field of fields) {
                if (field === inputEl) continue;

                const type = String(field.getAttribute("type") || "text").toLowerCase();
                if (type === "hidden" || type === "submit" || type === "button" || type === "image" || type === "reset") {
                    continue;
                }

                return false;
            }

            return true;
        }

        function suppressResultsNavigation() {
            if (!shouldSuppressResultsNavigation()) return;

            const blockEnter = (e) => {
                if (e.key !== "Enter" || e.isComposing) return;
                e.preventDefault();
                e.stopPropagation();
            };

            on(inputEl, "keydown", blockEnter, true);

            const form = inputEl.closest("form");
            if (!form) return;

            const dedicatedSearchForm = isDedicatedSearchForm(form);
            on(form, "submit", (e) => {
                if (!dedicatedSearchForm && document.activeElement !== inputEl) return;
                e.preventDefault();
                e.stopPropagation();
            }, true);
        }

        function bindEvents() {
            neutralizeInputBehavior();
            suppressResultsNavigation();

            on(window, "resize", () => {
                syncPosition();
            });

            on(inputEl, "input", (e) => {
                syncPosition();
                const query = e.target.value.trim();
                scheduleQuery(query);
            });

            on(inputEl, "pointerdown", () => {
                syncPosition();
                reopenFromCache();
            });

            on(inputEl, "focus", () => {
                syncPosition();
                reopenFromCache();
            });

            on(inputEl, "keydown", (e) => {
                if (e.key === "Escape") {
                    closeDropdown();
                }
            });

            on(document, "pointerdown", (e) => {
                //if (!cfg.closeOnOutsideClick) return;
                if (!state.isOpen) return;

                if (isEventInsideWidget(e)) return;

                closeDropdown();
            }, true);

            on(dropdownEl, "click", (e) => {
                const link = e.target.closest("a");
                if (!link) return;
            });
            // Callbacks
            on(rootEl, "click", (e) => {

                const el = e.target.closest("[data-entity-type]");
                if (!el) return;

                const type = el.dataset.entityType;
                const raw = el.dataset.entityJson;

                if (!type || !raw) return;

                let item;
                try {
                    item = JSON.parse(raw);
                } catch {
                    return;
                }

                var registrySearchCollection= RecorsaPluginRegistry.collectSearch();
                for (const section of registrySearchCollection.sections) {
                    if (section.key === type) {
                        const cb = section.callback;
                        if (typeof cb === "function") {
                            e.preventDefault();
                            cb(e, el, String(inputEl.value || "").trim(), item, cfg);
                        }
                    }
                }
            });
        }

        const api = {
            open() {
                openDropdown();
            },

            close() {
                closeDropdown();
            },

            search(query) {
                if (typeof query === "string") {
                    inputEl.value = query;
                }
                return handleQuery(inputEl.value);
            },

            clear() {
                inputEl.value = "";
                clearResults();
                closeDropdown();
            },

            refresh() {
                return handleQuery(inputEl.value);
            },

            setTheme(nextTheme = {}) {
                cfg.theme = deepMerge(cfg.theme, nextTheme);

                if (nextTheme.containerDropdownOffsetY !== undefined && nextTheme.containerDropdownOffsetY !== null) {
                    cfg.dropdownOffsetY = nextTheme.containerDropdownOffsetY;
                }

                if (nextTheme.containerZIndex !== undefined && nextTheme.containerZIndex !== null) {
                    cfg.zIndex = nextTheme.containerZIndex;
                }

                applyTheme(shadowHostEl, rootEl, cfg);
                syncPosition();
            },

            destroy() {
                state.destroyed = true;

                if (state.debounceTimer) {
                    clearTimeout(state.debounceTimer);
                    state.debounceTimer = null;
                }

                if (state.abortController) {
                    try {
                        state.abortController.abort();
                    } catch (_) {}
                    state.abortController = null;
                }

                while (cleanupFns.length) {
                    const fn = cleanupFns.pop();
                    try {
                        fn();
                    } catch (_) {}
                }

                if (shadowHostEl && shadowHostEl.parentNode) {
                    shadowHostEl.parentNode.removeChild(shadowHostEl);
                }

                if (inputEl.__recorsaInstance === api) {
                    inputEl.__recorsaInstance = null;
                }
                if (shadowHostEl.__recorsaInstance === api) {
                    shadowHostEl.__recorsaInstance = null;
                }
            }
        };

        bindEvents();
        syncPosition();
        clearResults();
        closeDropdown();

        return api;
    }

    window.RecorsaSearchWidget = RecorsaSearchWidget;
})();

;
/*!
 * Recorsa Chat Widget (vanilla JS)
 * - Mounts itself into a root container
 * - No Laravel/Blade dependencies
 * - Scoped DOM (no global #chat-history collisions)
 * - Supports streaming NDJSON lines: {"message":{"content":"..."}} and {"ui":{...}}
 * - Product slider rendering
 * - NEW: Chat-History laden und exakt so rendern wie live (Bot/User + UI blocks)
 *
 * Usage:
 *   <link rel="stylesheet" href="recorsa-chat-widget.css">
 *   <script src="recorsa-chat-widget.js"></script>
 *   <div id="recorsa-chat" class="recorsa-chat corner-bottom-right collapsed"></div>
 *   <script>
 *     RecorsaChatWidget.mount('#recorsa-chat', {
 *       saleschannelId: '123',
 *       endpoint: 'https://api.example.com/api/widget/chat',
 *       historyEndpoint: 'https://api.example.com/saleschannels/123/chat-widget/history', // optional, falls null -> wird abgeleitet
 *       shopBaseUrl: 'https://shop.example.com',
 *       initialState: 'closed', // or 'open'
 *       token: 'PUBLIC_WIDGET_KEY', // optional (Bearer)
 *       csrf: null, // optional (Laravel internal)
 *       texts: { title, subtitle, placeholder, welcome, personLine },
 *       assets: { botAvatarUrl },
 *       theme: { headerBg, headerTxt, footerBg, footerTxt, bodyBg, botBg, botTxt, userBg, userTxt, zIndex, offsetX, offsetY, width, height }
 *     });
 *   </script>
 */

(function () {
    "use strict";

    const ICON_EXPAND = `<svg class="recorsa-icon" viewBox="0 0 640 640"><path d="M128 96C110.3 96 96 110.3 96 128L96 224C96 241.7 110.3 256 128 256C145.7 256 160 241.7 160 224L160 160L224 160C241.7 160 256 145.7 256 128C256 110.3 241.7 96 224 96L128 96zM160 416C160 398.3 145.7 384 128 384C110.3 384 96 398.3 96 416L96 512C96 529.7 110.3 544 128 544L224 544C241.7 544 256 529.7 256 512C256 494.3 241.7 480 224 480L160 480L160 416zM416 96C398.3 96 384 110.3 384 128C384 145.7 398.3 160 416 160L480 160L480 224C480 241.7 494.3 256 512 256C529.7 256 544 241.7 544 224L544 128C544 110.3 529.7 96 512 96L416 96zM544 416C544 398.3 529.7 384 512 384C494.3 384 480 398.3 480 416L480 480L416 480C398.3 480 384 494.3 384 512C384 529.7 398.3 544 416 544L512 544C529.7 544 544 529.7 544 512L544 416z"/></svg>`;
    const ICON_COMPRESS = `<svg class="recorsa-icon" viewBox="0 0 640 640"><path d="M256 128C256 110.3 241.7 96 224 96C206.3 96 192 110.3 192 128L192 192L128 192C110.3 192 96 206.3 96 224C96 241.7 110.3 256 128 256L224 256C241.7 256 256 241.7 256 224L256 128zM128 384C110.3 384 96 398.3 96 416C96 433.7 110.3 448 128 448L192 448L192 512C192 529.7 206.3 544 224 544C241.7 544 256 529.7 256 512L256 416C256 398.3 241.7 384 224 384L128 384zM448 128C448 110.3 433.7 96 416 96C398.3 96 384 110.3 384 128L384 224C384 241.7 398.3 256 416 256L512 256C529.7 256 544 241.7 544 224C544 206.3 529.7 192 512 192L448 192L448 128zM416 384C398.3 384 384 398.3 384 416L384 512C384 529.7 398.3 544 416 544C433.7 544 448 529.7 448 512L448 448L512 448C529.7 448 544 433.7 544 416C544 398.3 529.7 384 512 384L416 384z"/></svg>`;
    const ICON_SEND = `<svg class="recorsa-icon" viewBox="0 0 512 512"><path d="M498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6L284 427.7l-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1S160 493.2 160 480l0-83.6c0-4 1.5-7.8 4.2-10.8L331.8 202.8c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7L106 360.8 17.7 316.6C7.1 311.3 .3 300.7 0 288.9s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z"/></svg>`;
    const ICON_USER = `<svg viewBox="0 0 24 24" aria-hidden="true" class="recorsa-icon"><path d="M12 12a4 4 0 1 0-4-4 4 4 0 0 0 4 4zm0 2c-4.4 0-8 2.2-8 5v1h16v-1c0-2.8-3.6-5-8-5z"/></svg>`;
    const ICON_CLOSE = `<svg class="recorsa-icon" viewBox="0 0 640 640"><path d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM231 231C240.4 221.6 255.6 221.6 264.9 231L319.9 286L374.9 231C384.3 221.6 399.5 221.6 408.8 231C418.1 240.4 418.2 255.6 408.8 264.9L353.8 319.9L408.8 374.9C418.2 384.3 418.2 399.5 408.8 408.8C399.4 418.1 384.2 418.2 374.9 408.8L319.9 353.8L264.9 408.8C255.5 418.2 240.3 418.2 231 408.8C221.7 399.4 221.6 384.2 231 374.9L286 319.9L231 264.9C221.6 255.5 221.6 240.3 231 231z"/></svg>`;
    const ICON_COMMENTS = `<svg class="recorsa-icon" viewBox="0 0 640 512"><path d="M416 176c0 97.2-93.1 176-208 176c-36.2 0-70.3-7.8-99.9-21.6L0 352l37.6-75.1C13.9 248.3 0 213.5 0 176C0 78.8 93.1 0 208 0S416 78.8 416 176zm32 0c0-5.2-.2-10.4-.6-15.5C555.1 167.1 640 243.2 640 336c0 37.5-13.9 72.3-37.6 100.9L640 512 531.9 490.4C502.3 504.2 468.2 512 432 512c-95.6 0-176.2-54.6-200.5-129C348.9 372.9 448 288.3 448 176z"/></svg>`;
    const ICON_PAGE = `<svg class="recorsa-icon d-inline" viewBox="0 0 640 640"><path d="M325.5 96C334 96 342.1 99.4 348.1 105.4L470.6 227.9C476.6 233.9 480 242 480 250.5L480 512C480 529.7 465.7 544 448 544L192 544C174.3 544 160 529.7 160 512L160 128C160 110.3 174.3 96 192 96L325.5 96zM192 64C156.7 64 128 92.7 128 128L128 512C128 547.3 156.7 576 192 576L448 576C483.3 576 512 547.3 512 512L512 250.5C512 233.5 505.3 217.2 493.3 205.2L370.7 82.7C358.7 70.7 342.5 64 325.5 64L192 64z"/></svg>`;
    const ICON_PDF_LIGHT = `<svg class="recorsa-icon d-inline" viewBox="0 0 640 640"><path d="M128 80L261.5 80C265.1 80 268.6 80.4 272 81.2L272 184C272 214.9 297.1 240 328 240L430.8 240C431.6 243.4 432 246.9 432 250.5L432 400L448 400L448 250.5C448 233.5 441.3 217.2 429.3 205.2L306.7 82.7C294.7 70.7 278.5 64 261.5 64L128 64C92.7 64 64 92.7 64 128L64 512C64 547.3 92.7 576 128 576L208 576L208 560L128 560C101.5 560 80 538.5 80 512L80 128C80 101.5 101.5 80 128 80zM424 224L328 224C305.9 224 288 206.1 288 184L288 88C290.7 89.8 293.1 91.8 295.4 94.1L417.9 216.6C420.2 218.9 422.2 221.4 424 224zM272 464C267.6 464 264 467.6 264 472L264 600C264 604.4 267.6 608 272 608C276.4 608 280 604.4 280 600L280 560L304 560C330.5 560 352 538.5 352 512C352 485.5 330.5 464 304 464L272 464zM304 544L280 544L280 480L304 480C321.7 480 336 494.3 336 512C336 529.7 321.7 544 304 544zM400 464C395.6 464 392 467.6 392 472L392 600C392 604.4 395.6 608 400 608L432 608C454.1 608 472 590.1 472 568L472 504C472 481.9 454.1 464 432 464L400 464zM408 592L408 480L432 480C445.3 480 456 490.7 456 504L456 568C456 581.3 445.3 592 432 592L408 592zM520 472L520 600C520 604.4 523.6 608 528 608C532.4 608 536 604.4 536 600L536 544L576 544C580.4 544 584 540.4 584 536C584 531.6 580.4 528 576 528L536 528L536 480L576 480C580.4 480 584 476.4 584 472C584 467.6 580.4 464 576 464L528 464C523.6 464 520 467.6 520 472z"/></svg>`;
    const ICON_TURN_DOWN_RIGHT = `<svg class="recorsa-icon" viewBox="0 0 640 640"><path d="M96 144C96 135.2 88.8 128 80 128C71.2 128 64 135.2 64 144L64 240C64 293 107 336 160 336L521.4 336L404.7 452.7C398.5 458.9 398.5 469.1 404.7 475.3C410.9 481.5 421.1 481.5 427.3 475.3L571.3 331.3C574.3 328.3 576 324.2 576 320C576 315.8 574.3 311.7 571.3 308.7L427.3 164.7C421.1 158.5 410.9 158.5 404.7 164.7C398.5 170.9 398.5 181.1 404.7 187.3L521.4 304L160 304C124.7 304 96 275.3 96 240L96 144z"/></svg>`;

    const HISTORY_LOADER = `
    <div class="recorsa-history-loader d-flex justify-content-center align-items-center">
      <div class="recorsa-spinner"></div>
    </div>
    `;
    function deepMerge(base, extra) {
        const out = Array.isArray(base) ? base.slice() : { ...base };
        if (!extra || typeof extra !== "object") return out;
        for (const [k, v] of Object.entries(extra)) {
            if (v && typeof v === "object" && !Array.isArray(v)) {
                out[k] = deepMerge(out[k] || {}, v);
            } else {
                out[k] = v;
            }
        }
        return out;
    }

    function getRequestLocale(cfg) {
        return (
            (window.RecorsaRuntimeConfig?.locale && String(window.RecorsaRuntimeConfig.locale).trim()) ||
            (cfg?.locale && String(cfg.locale).trim()) ||
            (document.documentElement?.lang && String(document.documentElement.lang).trim()) ||
            "de-DE"
        );
    }

    function isChatDebugEnabled() {
        try {
            const params = new URLSearchParams(window.location.search);
            if (params.get("recorsa_debug") === "1") {
                return true;
            }
            if (window.localStorage?.getItem("recorsa_debug") === "1") {
                return true;
            }
        } catch (_) {}
        return false;
    }

    function uuidv4() {
        if (window.crypto?.randomUUID) return window.crypto.randomUUID();
        // RFC4122-ish fallback
        return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
            const r = (crypto.getRandomValues(new Uint8Array(1))[0] & 0xf) >> 0;
            const v = c === "x" ? r : (r & 0x3) | 0x8;
            return v.toString(16);
        });
    }

    function escapeHtml(unsafe) {
        return String(unsafe)
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#039;");
    }

    function clampInt(n, fallback) {
        const x = Number(n);
        return Number.isFinite(x) ? Math.round(x) : fallback;
    }

    function resolveIconBackground(theme) {
        const t = theme || {};
        if (t.iconBackground === false || t.iconBackground === 0 || t.iconBackground === "0") {
            return "transparent";
        }
        const custom = String(t.iconBg || "").trim();
        return custom || t.botBg || "#2c7be5";
    }

    function normalizeCorner(corner) {
        const allowed = new Set(["bottom-right", "bottom-left", "top-right", "top-left"]);
        return allowed.has(corner) ? corner : "bottom-right";
    }

    function toCornerClass(corner) {
        return `corner-${normalizeCorner(corner)}`;
    }

    function conversationStorageKey(saleschannelId) {
        return `recorsa_sc_chat_id_${saleschannelId}`;
    }

    function getConversationId(saleschannelId) {
        if (!saleschannelId) return null;
        return localStorage.getItem(conversationStorageKey(saleschannelId));
    }

    function ensureConversationId(saleschannelId) {
        if (!saleschannelId) return uuidv4();
        let id = getConversationId(saleschannelId);
        if (!id) {
            id = uuidv4();
            localStorage.setItem(conversationStorageKey(saleschannelId), id);
        }
        return id;
    }

    function clearConversationId(saleschannelId) {
        if (!saleschannelId) return;
        localStorage.removeItem(conversationStorageKey(saleschannelId));
    }

    function scrollChatToBottom(scrollEl) {
        // Du hattest das aktuell bewusst deaktiviert.
        // Ich lasse das so (return), damit du dein eigenes Scroll-Verhalten beibehältst.
        // Falls du wieder "klassisch" scrollen willst: return entfernen.
        return;
        /*
        if (!scrollEl) return;
        scrollEl.scrollTop = scrollEl.scrollHeight;
        */
    }

    function el(tag, attrs = {}, children = []) {
        const node = document.createElement(tag);
        for (const [k, v] of Object.entries(attrs || {})) {
            if (k === "class") node.className = v;
            else if (k === "style" && v && typeof v === "object") Object.assign(node.style, v);
            else if (k.startsWith("data-")) node.setAttribute(k, String(v));
            else if (k === "html") node.innerHTML = v;
            else if (k === "text") node.textContent = v;
            else node.setAttribute(k, String(v));
        }
        for (const ch of children) node.appendChild(ch);
        return node;
    }

    function buildMarkup(rootEl, cfg) {

        const startingChips = Array.isArray(cfg?.texts?.startingChips)
            ? cfg.texts.startingChips
                .map(chip => String(chip || "").trim())
                .filter(Boolean)
            : [];

        const hasStartingChips = startingChips.length > 0;

        const startingChipsHtml = hasStartingChips
            ? `
            <div class="predefined-chips mb-3 d-flex flex-column gap-1 justify-content-end recorsa-welcome-hidden" data-recorsa="chips">
              ${startingChips.map((chip) => `
                <span class="chat-chip btn btn-outline-primary fs-11 px-2 align-self-end"
                      data-chip-question="${escapeHtml(chip)}">
                  ${escapeHtml(chip)} ${RecorsaCore.utils.ICON_CHEVRON_RIGHT}
                </span>
              `).join("")}
            </div>
        `
            : "";

        rootEl.innerHTML = `
      <div class="card rounded-4 h-100">
        <div class="card-header px-3 pe-2 py-0 position-relative z-3">
          <div class="d-flex justify-content-between align-items-center gap-3">
            <div class="d-flex justify-content-between align-items-center gap-2">
              <div class="recorsa-header-avatar">
                <img class="recorsa-avatar-img" src="${escapeHtml(cfg.assets.botAvatarUrl || "")}" alt="" width="28"/>
              </div>
              <div class="d-flex flex-column py-2">
                <p class="p-0 m-0 lh-1 recorsa-title">${escapeHtml(cfg.texts.title || "")}</p>
                <p class="p-0 m-0 lh-1 recorsa-subtitle">${escapeHtml(cfg.texts.subtitle || "")}</p>
              </div>
            </div>
            <div class="d-flex align-items-center">
              <button class="bg-transparent border-0 outline-none p-0 chatbot-resize me-2 d-none" type="button" aria-label="${escapeHtml(cfg.texts.ui.resize || 'Resize')}">
                ${ICON_EXPAND}
              </button>
              <button class="bg-transparent border-0 outline-none p-0 chatbot-toggle position-relative" type="button" aria-label="Toggle"></button>
            </div>
          </div>
        </div>

        <div class="recorsa-chat-content d-flex flex-column justify-content-between h-100">

          <div class="card-body p-0 position-relative">
            <div class="recorsa-scroll overflow-y-auto p-2 h-100" data-recorsa="scroll">

                <div class="bg-holder bg-card" aria-hidden="true">
                    <svg class="recorsa-bg-art" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
                        <defs>
                          <linearGradient id="recorsa-bg-gradient-1" x1="0%" y1="0%" x2="100%" y2="0%">
                            <stop offset="0%" stop-color="var(--recorsa-bg-shape-1)" stop-opacity="0.03" />
                            <stop offset="100%" stop-color="var(--recorsa-bg-shape-1)" stop-opacity="0.18" />
                          </linearGradient>

                          <linearGradient id="recorsa-bg-gradient-2" x1="0%" y1="0%" x2="100%" y2="0%">
                            <stop offset="0%" stop-color="var(--recorsa-bg-shape-2)" stop-opacity="0.00" />
                            <stop offset="100%" stop-color="var(--recorsa-bg-shape-2)" stop-opacity="0.10" />
                          </linearGradient>

                          <linearGradient id="recorsa-bg-gradient-3" x1="0%" y1="100%" x2="100%" y2="0%">
                            <stop offset="0%" stop-color="var(--recorsa-bg-shape-3)" stop-opacity="0.02" />
                            <stop offset="100%" stop-color="var(--recorsa-bg-shape-3)" stop-opacity="0.09" />
                          </linearGradient>
                        </defs>

                          <path class="bg-shape bg-shape-1" fill="url(#recorsa-bg-gradient-1)" d="M53 0 C53 10,54 18,58 25 C62 33,69 39,76 44 C83 49,87 56,89 65 C91 76,92 88,96 95 C98 98,100 100,100 100 L100 0 Z" />
                          <path class="bg-shape bg-shape-2" fill="url(#recorsa-bg-gradient-2)" d="M100 20 C91 21,83 24,76 31 C71 36,69 43,68 53 C67 64,66 75,61 84 C56 92,50 97,49 100 L100 100 Z" />
                          <path class="bg-shape bg-shape-3" fill="url(#recorsa-bg-gradient-3)" d="M11 100 C13 92,18 86,25 83 C33 79,42 78,52 76 C61 74,69 70,76 64 C83 58,89 53,96 51 C98 50,99 50,100 50 L100 100 Z" />
                    </svg>
                </div>

              <div class="chat-history d-flex flex-column gap-1" data-recorsa="history">
                <div class="chat-bot d-flex gap-2 justify-content-start recorsa-welcome-hidden" data-recorsa="welcome">
                  <div class="chat-icon d-flex align-items-end">
                    <div class="chat-icon-circle">
                      <img class="recorsa-avatar-img" src="${escapeHtml(cfg.assets.botAvatarUrl || "")}" alt="" width="28"/>
                    </div>
                  </div>
                  <div class="chat-content">
                    ${cfg.texts.personLine ? `<div class="chat-person d-flex gap-2 justify-content-start fs-11 ps-1">${escapeHtml(cfg.texts.personLine)}</div>` : ``}
                    <div class="chat-bubble mt-0 mb-2 py-2 px-3 fs-11">${escapeHtml(cfg.texts.welcome || "")}</div>
                  </div>
                </div>

                ${startingChipsHtml}

              </div>
            </div>
          </div>

            <div class="card-footer p-2 position-relative">

              <div class="recorsa-contextbar d-none" data-recorsa="contextbar">
                <div class="recorsa-contextbar-inner">
                  <div class="recorsa-contextbar-main">

                    <div>
                        <div class="d-flex justify-content-center forwarding">${RecorsaCore.utils.ICON_TURN_DOWN_RIGHT}</div>
                        <div class="recorsa-contextbar-icon" data-recorsa="context-icon">
                          ${RecorsaCore.utils.ICON_COMMENT}
                        </div>
                    </div>

                    <div class="recorsa-contextbar-texts">
                      <div class="recorsa-contextbar-label">${escapeHtml(cfg.texts.context.active)}</div>
                      <div class="recorsa-contextbar-title" data-recorsa="context-title"></div>
                      <div class="recorsa-contextbar-subline">
                        ${escapeHtml(cfg.texts.context.next_question_hint)}
                      </div>
                    </div>
                  </div>

                  <button
                    type="button"
                    class="recorsa-contextbar-clear"
                    data-recorsa="context-clear"
                    aria-label="${escapeHtml(cfg.texts.ui.remove_context)}"
                    title="${escapeHtml(cfg.texts.ui.remove_context)}"
                  >
                    ✕
                  </button>
                </div>
              </div>

              <div class="recorsa-inputwrap">
                <input
                  data-recorsa="input"
                  class="border-0 outline-none rounded-4 fs-10 w-100 p-2 ps-3 recorsa-chat-input"
                  type="text"
                  autocomplete="off"
                  autocorrect="off"
                  autocapitalize="off"
                  spellcheck="false"
                  placeholder="${escapeHtml(cfg.texts.placeholder || "")}"
                />
                <i
                  data-recorsa="send"
                  class="sendmessage fa fa-paper-plane d-flex justify-content-center align-items-center position-absolute"
                ></i>
              </div>
            </div>
        </div>
      </div>
    `;
    }

    function applyTheme(hostEl, widgetRootEl, cfg) {
        const t = cfg.theme || {};

        // Wo sollen die CSS vars hin?
        // - ShadowDOM: auf hostEl (damit :host sie sieht)
        // - ohne ShadowDOM: hostEl === widgetRootEl, daher egal
        const varTarget = hostEl;

        varTarget.style.setProperty("--recorsa-header-bg", t.headerBg);
        varTarget.style.setProperty("--recorsa-header-txt", t.headerTxt);
        varTarget.style.setProperty("--recorsa-footer-bg", t.footerBg);
        varTarget.style.setProperty("--recorsa-footer-txt", t.footerTxt);
        varTarget.style.setProperty("--recorsa-body-bg", t.bodyBg);
        varTarget.style.setProperty("--recorsa-body-txt", t.bodyTxt);
        varTarget.style.setProperty("--recorsa-bot-bg", t.botBg);
        varTarget.style.setProperty("--recorsa-bot-txt", t.botTxt);
        varTarget.style.setProperty("--recorsa-user-bg", t.userBg);
        varTarget.style.setProperty("--recorsa-user-txt", t.userTxt);

        varTarget.style.setProperty("--recorsa-icon-bg", resolveIconBackground(t));
        const iconRadius = Math.max(0, Math.min(50, clampInt(t.iconRadius, 50)));
        varTarget.style.setProperty("--recorsa-icon-radius", `${iconRadius}%`);
        const iconScale = Math.max(40, Math.min(160, clampInt(t.iconScale, 100)));
        varTarget.style.setProperty("--recorsa-icon-scale", String(iconScale / 100));

        varTarget.style.setProperty("--recorsa-z-index", String(clampInt(t.zIndex, 999999)));
        varTarget.style.setProperty("--recorsa-offset-x", `${clampInt(t.offsetX, 20)}px`);
        varTarget.style.setProperty("--recorsa-offset-y", `${clampInt(t.offsetY, 20)}px`);
        varTarget.style.setProperty("--recorsa-width", `${clampInt(t.width, 300)}px`);
        varTarget.style.setProperty("--recorsa-height", `${clampInt(t.height, 500)}px`);

        // Font-Style
        varTarget.style.setProperty(
            "--recorsa-chat-font-family",
            String(t.fontFamily || "").trim() || "inherit"
        );

        // Background Style
        varTarget.style.setProperty("--recorsa-bg-shape-1", t.headerBg || "#2c7be5");
        varTarget.style.setProperty("--recorsa-bg-shape-2", t.headerBg || "#2c7be5");
        varTarget.style.setProperty("--recorsa-bg-shape-3", t.headerBg || "#2c7be5");

        // Sicherheitsabfrage zu validen Varianten
        const allowedBgVariants = new Set(["none", "minimal", "waves", "bubbles", "dots", "stripes"]);
        const bgVariant = allowedBgVariants.has(String(t.backgroundVariant || "").trim())
            ? String(t.backgroundVariant).trim()
            : "waves";
        widgetRootEl.classList.remove(
            "recorsa-chat-bg-none",
            "recorsa-chat-bg-minimal",
            "recorsa-chat-bg-waves",
            "recorsa-chat-bg-bubbles",
            "recorsa-chat-bg-dots",
            "recorsa-chat-bg-stripes"
        );
        widgetRootEl.classList.add(`recorsa-chat-bg-${bgVariant}`);


        // 2 Corner
        const corner = normalizeCorner(t.corner);
        const cls = toCornerClass(corner);

        // 2.1) Corner-Klasse am INNEREN Element (Shadow CSS)
        widgetRootEl.classList.remove("corner-bottom-right", "corner-bottom-left", "corner-top-right", "corner-top-left");
        widgetRootEl.classList.add(cls);

        // 2.2) Corner-Klasse AUCH am HOST (für Debug / falls außerhalb CSS was drauf sitzt)
        hostEl.classList.remove("corner-bottom-right", "corner-bottom-left", "corner-top-right", "corner-top-left");
        hostEl.classList.add(cls);
    }

    function buildHeaders(cfg) {
        // NOTE:
        // - Für das Streaming-Chat-Endpoint brauchst du JSON
        // - Für History (GET) brauchst du keinen Body, aber wir können optional trotzdem Token mitsenden.
        const headers = { "Content-Type": "application/json" };
        if (cfg.csrf) headers["X-CSRF-TOKEN"] = cfg.csrf;
        if (cfg.token) headers["Authorization"] = `Bearer ${cfg.token}`;
        // Optional: identify widget
        headers["X-Recorsa-Widget"] = "1";
        // Optional: Accepted Language als möglicher Steuerparameter
        //headers["Accept-Language"] = getRequestLocale(cfg);
        // Anstelle von CSRF
        // headers["X-Recorsa-Widget-Key"] = cfg.token;
        return headers;
    }

    function safeJoinUrl(base, path) {
        const b = String(base || "").replace(/\/+$/, "");
        const p = String(path || "").replace(/^\/+/, "");
        if (!b) return "/" + p;
        if (!p) return b;
        return b + "/" + p;
    }

    function injectCssIntoShadow(shadowRoot, finalCfg) {
        const href = String(finalCfg.links.cssEndpoint || "").trim();
        if (!href) return Promise.resolve();

        const existing = shadowRoot.querySelector(`link[data-recorsa-css="1"][href="${href}"]`);
        if (existing) {
            if (existing.dataset.loaded === "1") return Promise.resolve();

            return new Promise((resolve, reject) => {
                existing.addEventListener("load", () => resolve(), { once: true });
                existing.addEventListener("error", reject, { once: true });
            });
        }

        return new Promise((resolve, reject) => {
            const link = document.createElement("link");
            link.rel = "stylesheet";
            link.href = href;
            link.setAttribute("data-recorsa-css", "1");

            link.addEventListener("load", () => {
                link.dataset.loaded = "1";
                resolve();
            }, { once: true });

            link.addEventListener("error", reject, { once: true });

            shadowRoot.appendChild(link);
        });
    }

    // ---------------------------------------------------------------------------
    // NEW: History-URL bestimmen
    // ---------------------------------------------------------------------------
    /* BRAUCHT MAN NICHT MEHR
    function resolveHistoryEndpoint(finalCfg) {
        // 1) Falls explizit gesetzt: immer nutzen
        if (finalCfg.links && finalCfg.links.historyEndpoint && String(finalCfg.links.historyEndpoint).trim() !== "") {
            return String(finalCfg.links.historyEndpoint).trim();
        }

        throw new Error("History-Endpoint not set");
    }

    function resolveContextEndpoint(finalCfg) {
        if (finalCfg.links.contextEndpoint && String(finalCfg.links.contextEndpoint).trim() !== "") {
            return String(finalCfg.links.contextEndpoint).trim();
        }

        throw new Error("Context-Endpoint not set");
    }
    */
    const RecorsaChatWidget = {
        mount(rootSelectorOrEl, finalCfg) {
            const hostEl = typeof rootSelectorOrEl === "string" ? document.querySelector(rootSelectorOrEl) : rootSelectorOrEl;

            if (!hostEl) {
                console.error("[RecorsaChatWidget] root element not found");
                return null;
            }

            if (!finalCfg.links.chatEndpoint) {
                console.error("[RecorsaChatWidget] Missing config.endpoint");
                return null;
            }

            if (!finalCfg.saleschannelId && hostEl.dataset.saleschannelId) {
                finalCfg.saleschannelId = hostEl.dataset.saleschannelId;
            }

            // ------------------------------------------------------------
            // Shadow DOM optional
            // ------------------------------------------------------------
            let widgetRootEl = hostEl; // das Element, in das buildMarkup() schreibt
            let shadowRoot = null;

            // Host aufräumen / markieren
            hostEl.classList.add("recorsa-chat-host");

            // Falls wir bereits gemounted haben -> sauber unmounten
            if (hostEl.__recorsaInstance && typeof hostEl.__recorsaInstance.destroy === "function") {
                try {
                    hostEl.__recorsaInstance.destroy();
                } catch (_) {}
            }

            // ShadowRoot (einmalig)
            shadowRoot = hostEl.shadowRoot || hostEl.attachShadow({ mode: "open" });

            // Shadow leeren (wichtig bei re-mount)
            while (shadowRoot.firstChild) shadowRoot.removeChild(shadowRoot.firstChild);

            // Inneren Container erzeugen
            widgetRootEl = document.createElement("div");
            widgetRootEl.className = "recorsa-chat";
            widgetRootEl.style.display = "none";

            // Falls Host vor-mount Klassen hatte (corner/collapsed), übernehmen
            // -> damit initialState/corner etc. nicht "weg" sind
            for (const cls of Array.from(hostEl.classList)) {
                if (cls.startsWith("corner-") || cls === "collapsed" || cls === "recorsa-expanded") {
                    widgetRootEl.classList.add(cls);
                }
            }

            shadowRoot.appendChild(widgetRootEl);

            // ------------------------------------------------------------
            // UI bauen + Theme anwenden
            // ------------------------------------------------------------
            buildMarkup(widgetRootEl, finalCfg);
            applyTheme(hostEl, widgetRootEl, finalCfg);

            // Initial state (collapsed/open) sauber setzen
            if (finalCfg.theme.initialState === "open") {
                widgetRootEl.classList.remove("collapsed");
            } else {
                widgetRootEl.classList.add("collapsed");
            }

            // Instance erstellen
            const instance = createInstance(widgetRootEl, finalCfg, hostEl);

            // Instance immer am HOST speichern (wichtig für unmount & externe Kontrolle)
            hostEl.__recorsaInstance = instance;

            injectCssIntoShadow(shadowRoot, finalCfg)
                .catch((err) => {
                    console.error("[RecorsaChatWidget] CSS failed to load", err);
                })
                .finally(() => {
                    widgetRootEl.style.display = "";

                    if (typeof finalCfg.onReady === "function") {
                        try {
                            finalCfg.onReady(instance);
                        } catch (_) {}
                    }
                });

            return instance;
        },

        unmount(rootSelectorOrEl) {
            const rootEl = typeof rootSelectorOrEl === "string" ? document.querySelector(rootSelectorOrEl) : rootSelectorOrEl;
            if (!rootEl) return;

            const inst = rootEl.__recorsaInstance;
            if (inst && typeof inst.destroy === "function") inst.destroy();
            rootEl.__recorsaInstance = null;
            rootEl.innerHTML = "";
        },
    };

    function createInstance(rootEl, cfg, hostEl) {
        const state = {
            destroyed: false,
            streamInProgress: false,
            typeQueues: new Map(), // uuid -> string
            typingState: new Map(), // uuid -> boolean
            lastTimestamp: "",

            // Merker, ob History schon versucht/geladen wurde (damit nicht mehrfach)
            historyLoaded: false,
            historyLoading: false,

            // Kontextbezogener Chat
            activeContext: null,
        };
        rootEl.addEventListener("click", (e) => {
            const btn = e.target.closest('[data-recorsa-action="select-entity"]');
            if (!btn) return;

            e.preventDefault();
            e.stopPropagation();

            const payload = {
                entity_type: btn.dataset.entityType || "",
                entity_id: btn.dataset.entityId || "",
                entity_title: btn.dataset.entityTitle || "",
                entity_url: btn.dataset.entityUrl || "",
                entity_image: btn.dataset.entityImage || ""
            };

            selectEntityContext(payload);
        });
        rootEl.addEventListener("click", (e) => {
            const btn = e.target.closest('[data-recorsa-action="entity-followup"]');
            if (!btn) return;

            e.preventDefault();
            e.stopPropagation();

            const question = btn.dataset.question || btn.textContent.trim();

            const payload = {
                entity_type: btn.dataset.entityType || "",
                entity_id: btn.dataset.entityId || "",
                entity_title: btn.dataset.entityTitle || "",
                entity_url: btn.dataset.entityUrl || "",
                entity_image: btn.dataset.entityImage || ""
            };

            sendEntityFollowup(question, payload);
        });

        const $ = (sel) => rootEl.querySelector(sel);

        const toggleBtn = $(".chatbot-toggle");
        const resizeBtn = $(".chatbot-resize");
        const header = rootEl.querySelector(".card-header");

        const input = rootEl.querySelector('[data-recorsa="input"]');
        const sendBtn = rootEl.querySelector('[data-recorsa="send"]');

        // WICHTIG: chatHistory ist der Container, in den wir Nachrichten append-en
        const chatHistory = rootEl.querySelector('[data-recorsa="history"]');

        // predefined chips Container
        const chipsContainer = rootEl.querySelector('[data-recorsa="chips"]');

        // welcome block (damit wir ihn ggf. entfernen, wenn History da ist)
        const welcomeBlock = rootEl.querySelector('[data-recorsa="welcome"]');

        sendBtn.innerHTML = ICON_SEND;

        const scrollContainer = rootEl.querySelector('[data-recorsa="scroll"]');

        // Kontextbezogener Chat
        const contextBar = rootEl.querySelector('[data-recorsa="contextbar"]');
        const contextTitleEl = rootEl.querySelector('[data-recorsa="context-title"]');
        const contextClearBtn = rootEl.querySelector('[data-recorsa="context-clear"]');
        const contextIconEl = rootEl.querySelector('[data-recorsa="context-icon"]');

        const cleanupFns = [];

        function on(target, event, handler, options) {
            if (!target) return;
            target.addEventListener(event, handler, options);
            addCleanup(() => {
                try {
                    target.removeEventListener(event, handler, options);
                } catch (_) {}
            });
        }

        function addCleanup(fn) {
            cleanupFns.push(fn);
        }

        on(rootEl, "wheel", (e) => {
            const track = e.target.closest(".product-suggestions-container");
            if (!track || !rootEl.contains(track)) return;
            if (track.scrollWidth <= track.clientWidth + 6) return;

            const delta = Math.abs(e.deltaY) >= Math.abs(e.deltaX) ? e.deltaY : e.deltaX;
            if (!delta) return;

            const max = track.scrollWidth - track.clientWidth;
            const next = Math.min(max, Math.max(0, track.scrollLeft + delta));
            if (next === track.scrollLeft) return;

            e.preventDefault();
            track.scrollLeft = next;
        }, { passive: false });

        function forceScrollToBottomSoon(delay = 120) {
            if (!scrollContainer) return;

            setTimeout(() => {
                requestAnimationFrame(() => {
                    requestAnimationFrame(() => {
                        scrollContainer.scrollTop = scrollContainer.scrollHeight;
                    });
                });
            }, delay);
        }

        let historyLoaderEl = null;

        function showWelcomeAndChips() {
            try { welcomeBlock?.classList.remove("recorsa-welcome-hidden"); } catch (_) {}
            try { chipsContainer?.classList.remove("recorsa-welcome-hidden"); } catch (_) {}
        }

        function hideWelcomeAndChips() {
            try { welcomeBlock?.classList.add("recorsa-welcome-hidden"); } catch (_) {}
            try { chipsContainer?.classList.add("recorsa-welcome-hidden"); } catch (_) {}
        }

        function showHistoryLoader() {
            if (historyLoaderEl) return;

            const wrapper = document.createElement("div");
            wrapper.innerHTML = HISTORY_LOADER;

            historyLoaderEl = wrapper.firstElementChild;
            chatHistory.appendChild(historyLoaderEl);
        }


        function hideHistoryLoader() {
            if (!historyLoaderEl) return;
            historyLoaderEl.remove();
            historyLoaderEl = null;
        }

        function getTimestampSuffix() {
            return String(cfg?.texts?.chat?.timestamp_suffix ?? "").trim();
        }

        function formatChatTime(dateLike = new Date()) {
            const date = dateLike instanceof Date ? dateLike : new Date(dateLike);

            if (Number.isNaN(date.getTime())) {
                return null;
            }

            const locale = getRequestLocale(cfg);
            return date.toLocaleTimeString(locale, {
                hour: "2-digit",
                minute: "2-digit",
            });
        }
        function buildTimestampText(dateLike = new Date()) {
            const time = formatChatTime(dateLike);
            if (!time) return null;

            const suffix = getTimestampSuffix();
            return suffix ? `${time} ${suffix}` : time;
        }

        function createTimestampElement(dateLike = new Date()) {
            const text = buildTimestampText(dateLike);
            if (!text) return null;

            const timestampEl = document.createElement("div");
            timestampEl.className = "chat-timestamp p-1 text-center";
            timestampEl.textContent = text;

            return timestampEl;
        }

        function appendTimestampIfNeeded(dateLike = new Date()) {
            const time = formatChatTime(dateLike);
            if (!time) return;

            if (state.lastTimestamp === time) {
                return;
            }

            const timestampEl = createTimestampElement(dateLike);
            if (!timestampEl) return;

            chatHistory.appendChild(timestampEl);
            state.lastTimestamp = time;
        }


        function scrollChatToNewestBotChat() {
            if (!scrollContainer) return;
            const scrollEl = scrollContainer;

            const botMessages = scrollEl.querySelectorAll(".chat-bot-message");
            if (!botMessages.length) return;

            const lastBot = botMessages[botMessages.length - 1];

            // Position von lastBot relativ zum Scroll-Container berechnen
            const cRect = scrollEl.getBoundingClientRect();
            const bRect = lastBot.getBoundingClientRect();

            // bottom innerhalb des Containers (inkl. current scrollTop)
            const bottomInContainer = bRect.bottom - cRect.top + scrollEl.scrollTop;

            const padding = 16; // etwas Luft, optional
            const target = Math.max(0, bottomInContainer - scrollEl.clientHeight + padding);

            scrollEl.scrollTop = target;
        }

        // Stabilize resize button visibility at start
        function updateResizeButtonVisibility() {
            if (!resizeBtn) return;
            if (rootEl.classList.contains("collapsed")) resizeBtn.classList.add("d-none");
            else resizeBtn.classList.remove("d-none");
        }

        function isBotTyping() {
            for (const v of state.typingState.values()) if (v === true) return true;
            return false;
        }

        function updateInputState(forceDisabled = false, autofocus = true) {
            if (!input) return;
            const disabled = forceDisabled || state.streamInProgress || isBotTyping();
            input.disabled = disabled;
            input.classList.toggle("opacity-75", disabled);
            if (!disabled && autofocus) {
                input.focus();
            }
        }

        // ---------------------------------------------------------------------------
        // NEW: kleine Helper, um User/Bot bubbles für History sauber zu rendern
        // ---------------------------------------------------------------------------

        function appendContextHint(text) {

            if (!chatHistory) return;
            /*
            const row = document.createElement("div");
            row.className = "recorsa-context-hint";
            row.innerHTML = `<span>${escapeHtml(text)}</span>`;
            chatHistory.appendChild(row);
             */
            forceScrollToBottomSoon();
        }


        // Timestamp-Bubble (nur wenn sich die Minute ändert)
        function appendTimestamp(tsLike) {
            if (!tsLike) return;

            try {
                const normalized = String(tsLike)
                    .trim()
                    .replace(" ", "T")
                    .replace(/\.(\d{3})\d+Z$/, ".$1Z")
                    .replace(/\.(\d{3})\d+$/, ".$1");

                const date = new Date(normalized);

                if (Number.isNaN(date.getTime())) {
                    console.warn("invalid date:", tsLike, "normalized:", normalized);
                    return;
                }

                appendTimestampIfNeeded(date);
            } catch (e) {
                console.error("appendTimestamp failed:", e, tsLike);
            }
        }

        // User message bubble (History + live)
        function appendUserMessage(text) {
            const userEl = document.createElement("div");
            userEl.className = "chat-user d-flex gap-2 justify-content-end";
            userEl.innerHTML = `
        <div class="chat-content d-flex justify-content-end">
          <div class="chat-bubble py-2 px-3 fs-11">${escapeHtml(text)}</div>
        </div>
        <div class="chat-icon">
          <div class="chat-icon-circle">${ICON_USER}</div>
        </div>
      `;
            chatHistory.appendChild(userEl);
        }

        // Bot message bubble (History: "sofort", live: "typewriter" separat)
        function appendBotBubbleInstant(text) {
            // Wir nutzen deine bestehende appendBotMessageHtml() Funktion,
            // damit Layout/Avatar/PersonLine identisch bleibt.
            appendBotMessageHtml(escapeHtml(text), {
                showAvatar: true,
                showPersonLine: false,
                bubble: true,
                name: cfg.texts.personLine || "",
            });
        }

        function toggleChat() {
            const isCollapsed = rootEl.classList.toggle("collapsed");

            if (!isCollapsed) {
                // open
                if (typeof cfg.onOpen === "function") {
                    try {
                        cfg.onOpen();
                    } catch (_) {}
                }

                setTimeout(() => input?.focus(), 200);

                // History-Laden lazy nur beim Öffnen
                maybeLoadHistory("onOpen");

                // Nach dem Öffnen/Rendern mehrfach nachscrollen,
                // weil Höhe/Transition/History-Render async sein können
                setTimeout(() => forceScrollToBottomSoon(), 50);
                setTimeout(() => forceScrollToBottomSoon(), 150);
                setTimeout(() => forceScrollToBottomSoon(), 300);
            } else {
                // close
                if (typeof cfg.onClose === "function") {
                    try {
                        cfg.onClose();
                    } catch (_) {}
                }
            }

            updateResizeButtonVisibility();
            updateToggleButtonIcon();
        }

        function setupResize() {
            if (!resizeBtn) return;

            // Initial icon setzen (falls du später mal default expanded möchtest)
            resizeBtn.innerHTML = rootEl.classList.contains("recorsa-expanded") ? ICON_COMPRESS : ICON_EXPAND;

            resizeBtn.addEventListener("click", (e) => {
                e.stopPropagation();

                const isExpanded = rootEl.classList.toggle("recorsa-expanded");

                resizeBtn.innerHTML = isExpanded ? ICON_COMPRESS : ICON_EXPAND;

                const expandedPanel = rootEl.querySelector('[data-recorsa="expanded-panel"]');
                if (expandedPanel) {
                    expandedPanel.classList.toggle("d-none", !isExpanded);
                }

                scrollChatToBottom(scrollContainer);
            });
        }

        function updateToggleButtonIcon() {
            if (!toggleBtn) return;

            if (rootEl.classList.contains("collapsed")) {
                toggleBtn.innerHTML = getCollapsedToggleHtml();
                toggleBtn.setAttribute("aria-label", cfg.texts.ui.open_chat);
            } else {
                forceScrollToBottomSoon();
                toggleBtn.innerHTML = ICON_CLOSE;
                toggleBtn.setAttribute("aria-label", cfg.texts.ui.close_chat);
            }
        }

        function getCollapsedToggleHtml() {
            return `
        <span class="recorsa-toggle-wrap">
          ${ICON_COMMENTS}
          <span class="recorsa-dot" aria-hidden="true"></span>
        </span>
      `;
        }


        async function typewriterTick(uuid, options = {}) {
            if (state.typingState.get(uuid)) return;
            state.typingState.set(uuid, true);

            const keepBottomVisible = options.keepBottomVisible === true;

            const el = rootEl.querySelector(`[data-recorsa-msg="${uuid}"]`);
            if (!el) {
                state.typingState.set(uuid, false);
                updateInputState();
                return;
            }

            if (keepBottomVisible) {
                forceScrollToBottomSoon();
            } else {
                scrollChatToNewestBotChat();
            }

            while (true) {
                const q = state.typeQueues.get(uuid) || "";
                if (!q.length) break;

                el.textContent += q[0];
                state.typeQueues.set(uuid, q.slice(1));

                if (keepBottomVisible) {
                    forceScrollToBottomSoon();
                } else {
                    scrollChatToNewestBotChat();
                }

                await new Promise((r) => setTimeout(r, 15));
            }

            state.typingState.set(uuid, false);

            if (keepBottomVisible) {
                forceScrollToBottomSoon();
            }

            updateInputState();
        }

        function appendBotMessageHtml(html, options = {}) {
            const msgId = uuidv4();
            const showAvatar = options.showAvatar ?? true;
            const showPersonLine = options.showPersonLine ?? true;
            const bubble = options.bubble ?? true;
            const name = options.name || "";

            const wrapper = document.createElement("div");
            wrapper.className = "chat-bot d-flex justify-content-start " + (showAvatar ? "gap-2" : "gap-0");

            const avatarHtml = showAvatar
                ? `
          <div class="chat-icon d-flex align-items-end">
            <div class="chat-icon-circle">
              <img class="recorsa-avatar-img" src="${escapeHtml(cfg.assets.botAvatarUrl || "")}" alt="" width="28"/>
            </div>
          </div>`
                : `
          <div class="chat-icon" style="width:0; flex-basis:0; flex-shrink:0;"></div>`;

            const containerClass = bubble ? "chat-bubble mb-2 py-2 px-3 fs-11" : "mb-2 py-0 px-0 fs-11";

            wrapper.innerHTML = `
        ${avatarHtml}
        <div class="chat-content">
          ${
                showPersonLine
                    ? `<div class="chat-person d-flex gap-2 justify-content-start fs-11 ${
                        showAvatar ? "ps-1" : "ps-0"
                    }">${escapeHtml(name)}</div>`
                    : ``
            }
          <div data-recorsa-msg="${msgId}" class="${containerClass}"></div>
        </div>
      `;

            chatHistory.appendChild(wrapper);
            const el = wrapper.querySelector(`[data-recorsa-msg="${msgId}"]`);
            el.innerHTML = html;
            enhanceProductSliders(wrapper);

            setTimeout(() => scrollChatToBottom(scrollContainer), 50);
            return msgId;
        }

        const SLIDER_CHEVRON_LEFT = `<svg class="recorsa-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M15.5 4.5 8 12l7.5 7.5" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
        const SLIDER_CHEVRON_RIGHT = `<svg class="recorsa-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.5 4.5 16 12l-7.5 7.5" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;

        function syncProductSlider(wrap, track, prevBtn, nextBtn) {
            const max = Math.max(0, track.scrollWidth - track.clientWidth);
            const overflow = max > 6;
            const atStart = track.scrollLeft <= 6;
            const atEnd = track.scrollLeft >= max - 6;

            wrap.classList.toggle("has-overflow", overflow);
            wrap.classList.toggle("has-more-left", overflow && !atStart);
            wrap.classList.toggle("has-more-right", overflow && !atEnd);
            prevBtn.disabled = !overflow || atStart;
            nextBtn.disabled = !overflow || atEnd;
        }

        function enhanceProductSliders(scope) {
            if (!scope || !scope.querySelectorAll) return;

            scope.querySelectorAll(".product-suggestions-container").forEach((track) => {
                if (track.closest(".recorsa-product-slider")) {
                    return;
                }

                const wrap = document.createElement("div");
                wrap.className = "recorsa-product-slider";
                track.parentNode.insertBefore(wrap, track);
                wrap.appendChild(track);

                const prevBtn = document.createElement("button");
                prevBtn.type = "button";
                prevBtn.className = "recorsa-product-slider-btn recorsa-product-slider-prev";
                prevBtn.setAttribute("aria-label", "Previous");
                prevBtn.innerHTML = SLIDER_CHEVRON_LEFT;

                const nextBtn = document.createElement("button");
                nextBtn.type = "button";
                nextBtn.className = "recorsa-product-slider-btn recorsa-product-slider-next";
                nextBtn.setAttribute("aria-label", "Next");
                nextBtn.innerHTML = SLIDER_CHEVRON_RIGHT;

                wrap.appendChild(prevBtn);
                wrap.appendChild(nextBtn);

                const step = () => Math.max(track.clientWidth * 0.75, 160);

                const sync = () => syncProductSlider(wrap, track, prevBtn, nextBtn);

                on(track, "scroll", sync, { passive: true });
                on(prevBtn, "click", (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    track.scrollBy({ left: -step(), behavior: "smooth" });
                });
                on(nextBtn, "click", (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    track.scrollBy({ left: step(), behavior: "smooth" });
                });

                requestAnimationFrame(sync);
                setTimeout(sync, 80);
            });
        }

        function renderChatUi(ui) {
            if (!ui) return;

            var registryChatCollection = RecorsaPluginRegistry.collectChat();

            // Alt: ui.data
            /*
            if (ui.data !== undefined) {
                for (const section of registryChatCollection.sections) {
                    const items = Array.isArray(ui.data?.[section.key]) ? ui.data[section.key] : [];
                    if (!items.length) continue;

                    const rendererSection = registryChatCollection.renderers?.[section.renderSection];
                    if (typeof rendererSection !== "function") continue;

                    var html = rendererSection(items, cfg, section);
                    appendBotMessageHtml(`${html}`, {
                        showAvatar: false,
                        showPersonLine: false,
                        bubble: false,
                    });
                }
                return;
            }
            */

            // Neu: ui.blocks
            if (Array.isArray(ui.blocks)) {
                for (const block of ui.blocks) {
                    if (!block || !block.type) continue;

                    if (block.type === "text") {
                        appendBotMessageHtml(
                            `<div class="chat-bubble mb-2 py-2 px-3 fs-11">${escapeHtml(block.content || "")}</div>`,
                            {
                                showAvatar: false,
                                showPersonLine: false,
                                bubble: false,
                            }
                        );
                    } else if (block.type === "entity_type") {
                        if (!block.items) continue;
                        for (const entityKey in block.items) {
                            var entityItems = block.items[entityKey];
                            if (!entityItems) continue;

                            const section = registryChatCollection.sections.find((s) => s.key === entityKey);
                            if (!section) continue;

                            const rendererSection = registryChatCollection.renderers?.[section.renderSection];
                            if (typeof rendererSection !== "function") continue;

                            const items = Array.isArray(entityItems) ? entityItems : [];
                            if (!items.length) continue;

                            const html = rendererSection(items, cfg, section);
                            appendBotMessageHtml(`${html}`, {
                                showAvatar: false,
                                showPersonLine: false,
                                bubble: false,
                            });
                        }
                    }
                }
            }
        }

        function formatMoneyEUR(value, locale = "de-DE") {
            const number = Number(value);
            if (!Number.isFinite(number)) return "";

            return (
                new Intl.NumberFormat(locale, {
                    minimumFractionDigits: 2,
                    maximumFractionDigits: 2,
                }).format(number) + " €"
            );
        }

        function renderEntityChips(items, entityPayload = {}) {
            if (!Array.isArray(items) || !items.length) return;

            const safeEntityType = escapeHtml(entityPayload.entity_type || "");
            const safeEntityId = escapeHtml(entityPayload.entity_id || "");
            const safeEntityTitle = escapeHtml(entityPayload.entity_title || "");
            const safeEntityUrl = escapeHtml(entityPayload.entity_url || "");
            const safeEntityImage = escapeHtml(entityPayload.entity_image || "");

            const html = `
                <div class="recorsa-chipblock">
                    <div class="recorsa-chiprow">
                        ${items.map((text) => `
                            <button
                                class="recorsa-chip entity-followup-chip"
                                type="button"
                                data-recorsa-action="entity-followup"
                                data-question="${escapeHtml(text)}"
                                data-entity-type="${safeEntityType}"
                                data-entity-id="${safeEntityId}"
                                data-entity-title="${safeEntityTitle}"
                                data-entity-url="${safeEntityUrl}"
                                data-entity-image="${safeEntityImage}"
                            >
                                ${escapeHtml(text)}
                            </button>
                        `).join("")}
                    </div>
                </div>
            `;

            appendBotMessageHtml(html, {
                showAvatar: false,
                showPersonLine: false,
                bubble: false,
            });

            forceScrollToBottomSoon();
        }

        function normalizeEntityContext(entity = {}) {
            if (!entity || typeof entity !== "object") {
                return null;
            }

            const entity_type = String(entity.entity_type || "").trim();
            const entity_id = String(entity.entity_id || "").trim();
            const entity_title = String(entity.entity_title || "").trim();
            const entity_url = String(entity.entity_url || "").trim();
            const entity_image = String(entity.entity_image || "").trim();

            if (!entity_type || !entity_id) return null;

            return {
                entity_type,
                entity_id,
                entity_title,
                entity_url,
                entity_image,
            };
        }

        function isSameEntityContext(a, b) {
            if (!a || !b) return false;

            return String(a.entity_type || "") === String(b.entity_type || "")
                && String(a.entity_id || "") === String(b.entity_id || "");
        }

        function setActiveContext(entity) {
            const previous = state.activeContext;
            const normalized = normalizeEntityContext(entity);

            const wasActive = !!previous;
            const isNowActive = !!normalized;
            const contextChanged = !isSameEntityContext(previous, normalized);

            state.activeContext = normalized;

            if (!contextBar || !contextTitleEl || !input) return;

            if (!normalized) {
                contextBar.classList.add("d-none");
                contextTitleEl.textContent = "";

                if (contextIconEl) {
                    contextIconEl.innerHTML = RecorsaCore.utils.ICON_COMMENT;
                }

                input.classList.remove("recorsa-chat-input--context");
                input.setAttribute("placeholder", cfg.texts.placeholder || "");

                if (wasActive) {
                    appendContextHint(cfg.texts.context.leave);
                }

                return;
            }

            contextBar.classList.remove("d-none");
            contextTitleEl.textContent = normalized.entity_title || cfg.texts.context.default_entity_title;

            if (contextIconEl) {
                if (normalized.entity_image) {
                    contextIconEl.innerHTML = `
                <img
                    src="${escapeHtml(normalized.entity_image)}"
                    alt=""
                    class="recorsa-contextbar-thumb"
                >
            `;
                } else {
                    contextIconEl.innerHTML = RecorsaCore.utils.ICON_COMMENT;
                }
            }

            input.classList.add("recorsa-chat-input--context");
            input.setAttribute(
                "placeholder",
                cfg.texts.context.placeholder
                    .replace(":title", normalized.entity_title || cfg.texts.context.default_entity)
            );

            if (contextChanged) {
                appendContextHint(
                    cfg.texts.context.active_with_title
                        .replace(":title", normalized.entity_title)
                );
            }

            forceScrollToBottomSoon();
            updateInputState();
        }

        function clearActiveContext() {
            setActiveContext(null);
        }

        function hasActiveContext() {
            return !!(state.activeContext && state.activeContext.entity_id);
        }

        async function sendEntityFollowup(question, entityPayload) {
            if (state.streamInProgress || isBotTyping()) {
                return;
            }

            if (!question || !String(question).trim()) return;

            setActiveContext(entityPayload);

            const message = String(question).trim();
            updateInputState(true);

            if (typeof cfg.onMessageSent === "function") {
                try {
                    cfg.onMessageSent(message);
                } catch (_) {}
            }

            appendTimestampIfNeeded(new Date());

            appendUserMessage(message);

            try {
                rootEl.querySelectorAll(".entity-followup-chip").forEach((chip) => {
                    const chipBlock = chip.closest(".chat-bot");
                    if (chipBlock) {
                        chipBlock.remove();
                    }
                });
            } catch (_) {}

            const msgId = uuidv4();
            state.typeQueues.set(msgId, "");
            state.typingState.set(msgId, false);

            const botWrap = document.createElement("div");
            botWrap.className = "chat-bot chat-bot-message d-flex gap-2 justify-content-start";
            botWrap.innerHTML = `
        <div class="chat-icon d-flex align-items-end">
          <div class="chat-icon-circle">
            <img class="recorsa-avatar-img" src="${escapeHtml(cfg.assets.botAvatarUrl || "")}" alt="" width="28"/>
          </div>
        </div>
        <div class="chat-content">
          <div data-recorsa-msg="${msgId}" class="placeholder-glow">
            <div class="chat-bubble px-2 fs-8 placeholder">...</div>
          </div>
        </div>
    `;
            chatHistory.appendChild(botWrap);
            forceScrollToBottomSoon();
            scrollChatToNewestBotChat();

            state.streamInProgress = true;

            const chat_id = ensureConversationId(cfg.saleschannelId);
            try {
                const resp = await fetch(cfg.links.chatEndpoint, {
                    method: "POST",
                    headers: buildHeaders(cfg),
                    body: JSON.stringify({
                        message,
                        chat_id,
                        saleschannel_id: cfg.saleschannelId || undefined,
                        locale: cfg.locale,
                        widgetLocale: cfg.widgetLocale,
                        is_entity_followup: true,
                        entity_context: {
                            entity_type: entityPayload.entity_type || "",
                            entity_id: entityPayload.entity_id || "",
                            entity_title: entityPayload.entity_title || "",
                            entity_url: entityPayload.entity_url || "",
                            entity_image: entityPayload.entity_image || ""
                        }
                    }),
                });

                if (!resp.body) {
                    const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                    el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                    el.textContent = cfg.texts.errors.stream_not_supported;
                    return;
                }

                const reader = resp.body.getReader();
                const decoder = new TextDecoder("utf-8");
                let buffer = "";
                let firstChunk = true;

                while (true) {
                    const { done, value } = await reader.read();
                    if (done) break;

                    buffer += decoder.decode(value, { stream: true });
                    const lines = buffer.split("\n");
                    buffer = lines.pop();

                    for (const line of lines) {
                        if (!line.trim()) continue;

                        let json;
                        try {
                            json = JSON.parse(line);
                        } catch (e) {
                            continue;
                        }

                        const chunk = json?.message?.content;
                        if (chunk) {
                            if (firstChunk) {
                                const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                                el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                                el.textContent = "";
                                firstChunk = false;
                            }

                            state.typeQueues.set(msgId, (state.typeQueues.get(msgId) || "") + String(chunk));
                            typewriterTick(msgId);

                            if (typeof cfg.onMessageReceived === "function") {
                                try {
                                    cfg.onMessageReceived(chunk);
                                } catch (_) {}
                            }
                        }
                        if (json.ui) renderChatUi(json.ui);
                    }
                }
            } catch (err) {
                console.error("[RecorsaChatWidget] entity followup failed:", err);
                const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                if (el) {
                    el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                    el.textContent = cfg.texts.errors.generic;
                }
            } finally {
                state.streamInProgress = false;
                updateInputState(false);
            }
        }

        // ---------------------------------------------------------------------------
        // Chat-History laden
        // ---------------------------------------------------------------------------

        async function loadChatHistory({ saleschannelId, chatId, locale }) {
            // Wenn keine chatId vorhanden ist (neuer User), gibt es keine History.
            if (!chatId) return null;

            // Endpoint: /saleschannels/{saleschannel}/chat-widget/history
            const url = String(cfg.links.historyEndpoint || "").trim();
            if (!url) return null;

            // POST (weil Laravel Route::post)
            // NOTE:
            // - Content-Type JSON -> Preflight (OPTIONS) ist normal
            // - Authorization Header -> ebenfalls Preflight
            const resp = await fetch(url, {
                method: "POST",
                headers: buildHeaders(cfg),
                body: JSON.stringify({
                    chat_id: chatId,
                    locale: cfg.locale,
                    widgetLocale: cfg.widgetLocale,
                }),
            });

            if (!resp.ok) return null;
            return await resp.json();
        }

        // ---------------------------------------------------------------------------
        // Entity-Bezug "Fragen stellen"
        // ---------------------------------------------------------------------------
        async function fetchEntityContext(payload, options = {}) {
            const chatId = options.chatId === null
                ? null
                : ensureConversationId(cfg.saleschannelId);

            const resp = await fetch(cfg.links.contextEndpoint, {
                method: "POST",
                headers: buildHeaders(cfg),
                body: JSON.stringify({
                    saleschannel_id: cfg.saleschannelId,
                    chat_id: chatId,
                    locale: cfg.locale,
                    widgetLocale: cfg.widgetLocale,
                    entity: payload
                }),
            });

            if (!resp.ok) {
                throw new Error("Context request failed: " + resp.status);
            }

            return await resp.json();
        }

        async function selectEntityContext(payload) {
            updateInputState(true);
            state.streamInProgress = true;

            const msgId = uuidv4();

            const botWrap = document.createElement("div");
            botWrap.className = "chat-bot chat-bot-message d-flex gap-2 justify-content-start";
            botWrap.innerHTML = `
                <div class="chat-icon d-flex align-items-end">
                  <div class="chat-icon-circle">
                    <img class="recorsa-avatar-img" src="${escapeHtml(cfg.assets.botAvatarUrl || "")}" alt="" width="28"/>
                  </div>
                </div>
                <div class="chat-content">
                  <div data-recorsa-msg="${msgId}" class="placeholder-glow">
                    <div class="chat-bubble px-2 fs-8 placeholder">...</div>
                  </div>
                </div>
              `;

            chatHistory.appendChild(botWrap);
            forceScrollToBottomSoon();
            scrollChatToNewestBotChat();

            try {
                const data = await fetchEntityContext(payload);

                //const resolvedEntity = data?.entity || payload;
                // Mergen falls, data.entity kein entity_image zurückliefert sonst wird ursprünglicher Payload überschrieben
                const resolvedEntity = {
                    ...payload,
                    ...(data?.entity || {}),
                    entity_image: (data?.entity?.entity_image || payload.entity_image || "")
                };

                setActiveContext(resolvedEntity);
                //setActiveContext(resolvedEntity);

                const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                if (!el) return;

                el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                el.textContent = "";

                const introText = String(data?.message || "").trim();

                if (introText !== "") {
                    state.typeQueues.set(msgId, introText);
                    state.typingState.set(msgId, false);
                    await typewriterTick(msgId, { keepBottomVisible: true });
                } else {
                    el.textContent = cfg.texts.context.info_found;
                }

                if (Array.isArray(data?.chips) && data.chips.length) {
                    renderEntityChips(data.chips, payload);
                    forceScrollToBottomSoon();
                }

                forceScrollToBottomSoon();
            } catch (err) {
                console.error("[RecorsaChatWidget] entity context failed:", err);

                const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                if (el) {
                    el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                    el.textContent = cfg.texts.errors.context_load_failed;
                }

                forceScrollToBottomSoon();
            }
            finally {
                state.streamInProgress = false;
                updateInputState(false);
            }
        }

        function clearHistoryAreaButKeepMarkup() {
            // Du hast am Anfang "Welcome + predefined chips".
            // Wenn History existiert, soll sie stattdessen angezeigt werden.
            // Daher entfernen wir (optional) welcomeBlock + chipsContainer, bevor wir History rendern.
            try {
                welcomeBlock?.remove();
            } catch (_) {}
            try {
                chipsContainer?.remove();
            } catch (_) {}

            // Jetzt die restlichen Messages löschen (falls bereits welche drin sind)
            // ACHTUNG: chatHistory enthält ggf. noch welcome/chips, aber die haben wir oben schon entfernt.
            // Damit du nicht doppelte History bekommst, leeren wir jetzt komplett.
            chatHistory.innerHTML = "";
            state.lastTimestamp = "";
        }

        function renderHistoryIntoChat(historyPayload) {
            // Du hattest hier bereits Code – aber es waren Referenzen auf nicht existierende Funktionen/Variablen:
            // - chatHistoryEl (gibt es nicht) -> korrekt: chatHistory
            // - appendMessageBubble/appendUiBlocks (gibt es nicht) -> wir nutzen appendUserMessage/appendBotBubbleInstant + renderChatUi

            if (!historyPayload || !Array.isArray(historyPayload.messages)) return;

            // Wenn es History gibt: Welcome/Chips nicht zusätzlich rendern
            clearHistoryAreaButKeepMarkup();

            // Erwartete Payload:
            // {
            //   chat_id: "...",
            //   messages: [
            //     { actor_type: "customer"|"chatbot", content: "...", meta: {...}, created_at: "..." },
            //     ...
            //   ]
            // }
            // -> Du kannst serverseitig beliebig erweitern, wir lesen nur das, was da ist.

            let lastEntityContext = null;

            for (const m of historyPayload.messages) {
                const actor = m.actor_type; // "customer" | "chatbot"
                const content = (m.content || "").trim();

                // optional timestamp (falls vorhanden)
                appendTimestamp(m.created_at || null);

                if (content) {
                    if (actor === "customer") {
                        appendUserMessage(content);
                    } else {
                        // chatbot
                        appendBotBubbleInstant(content);
                    }
                }

                // UI direkt nach der Bot-Message rendern (wie bei deinem Streaming)
                // NOTE: Du speicherst UI meist bei Bot Messages in meta.ui.
                const ui = m?.meta?.ui;
                if (ui) {
                    renderChatUi(ui);
                }

                const entity = m?.meta?.entity;
                if (entity && entity.entity_id) {
                    lastEntityContext = entity;
                } else {
                    lastEntityContext = undefined;
                }
            }

            if (lastEntityContext) {
                setActiveContext(lastEntityContext);
            }

            forceScrollToBottomSoon();
        }

        async function maybeLoadHistory(reason = "auto") {
            // Wir laden History:
            // - nur einmal pro Instance
            // - nur wenn chat_id existiert (localStorage)
            // - nur wenn endpoint bekannt ist

            // 1) Hard-Guards ganz am Anfang (WICHTIG, bevor wir loader/state setzen)
            if (state.historyLoaded) return;
            if (state.historyLoading) return;

            const chat_id = getConversationId(cfg.saleschannelId);
            if (!chat_id) {
                // Keine bestehende Session -> keine History
                state.historyLoaded = true;
                showWelcomeAndChips();
                return;
            }

            // 2) Ab hier wirklich "laden" -> Loader & State setzen
            state.historyLoading = true;

            // IMMER: zuerst UI "loading mode"
            hideWelcomeAndChips();
            showHistoryLoader();

            try {
                const chat_id = getConversationId(cfg.saleschannelId);

                // 1) keine bestehende Session -> KEINE History -> Welcome anzeigen
                if (!chat_id) {
                    state.historyLoaded = true;
                    showWelcomeAndChips();
                    return;
                }

                const payload = await loadChatHistory({
                    saleschannelId: cfg.saleschannelId,
                    chatId: chat_id,
                    locale: cfg.locale || "de-DE",
                });

                const hasMessages = payload && Array.isArray(payload.messages) && payload.messages.length > 0;

                // 2) History vorhanden -> rendern
                if (hasMessages) {
                    renderHistoryIntoChat(payload);
                } else {
                    // 3) keine History -> Welcome anzeigen
                    showWelcomeAndChips();
                }

                state.historyLoaded = true;
            } catch (err) {
                console.error("[RecorsaChatWidget] history load failed:", err);

                // Fehlerfall: lieber Welcome anzeigen (statt leerer Fläche)
                showWelcomeAndChips();

                state.historyLoaded = true;
            } finally {
                hideHistoryLoader();
                state.historyLoading = false;
                forceScrollToBottomSoon(500);
                forceScrollToBottomSoon(1000);
            }
        }

        async function sendMessage(forcedMessage = null) {
            if (state.streamInProgress || isBotTyping()) {
                return;
            }
            if (!input) return;
            const message = forcedMessage !== null ? forcedMessage : input.value;
            if (!message || !message.trim()) return;

            if (forcedMessage === null) input.value = "";
            updateInputState(true);

            // NEW:
            // Wenn der User sendet und es noch Chips gab, können wir sie entfernen,
            // damit sie nicht “über” der Konversation kleben bleiben.
            // (Du hast das bisher nur beim Chip-Klick gemacht.)
            try {
                chipsContainer?.remove();
            } catch (_) {}

            if (typeof cfg.onMessageSent === "function") {
                try {
                    cfg.onMessageSent(message);
                } catch (_) {}
            }

            // Timestamp (live)
            appendTimestampIfNeeded(new Date());

            // User bubble
            appendUserMessage(message);

            // Bot placeholder (scoped by data-recorsa-msg)
            const msgId = uuidv4();
            state.typeQueues.set(msgId, "");
            state.typingState.set(msgId, false);

            const botWrap = document.createElement("div");
            botWrap.className = "chat-bot chat-bot-message d-flex gap-2 justify-content-start";
            botWrap.innerHTML = `
        <div class="chat-icon d-flex align-items-end">
          <div class="chat-icon-circle">
            <img class="recorsa-avatar-img" src="${escapeHtml(cfg.assets.botAvatarUrl || "")}" alt="" width="28"/>
          </div>
        </div>
        <div class="chat-content">
          <div data-recorsa-msg="${msgId}" class="placeholder-glow">
            <div class="chat-bubble px-2 fs-8 placeholder">...</div>
          </div>
        </div>
      `;
            chatHistory.appendChild(botWrap);
            scrollChatToBottom(scrollContainer);
            scrollChatToNewestBotChat();

            state.streamInProgress = true;

            const chat_id = ensureConversationId(cfg.saleschannelId);

            try {
                const payload = {
                    message,
                    chat_id,
                    saleschannel_id: cfg.saleschannelId || undefined,
                    locale: cfg.locale,
                    widgetLocale: cfg.widgetLocale,
                };

                if (isChatDebugEnabled()) {
                    payload.debug = true;
                }

                if (hasActiveContext()) {
                    payload.is_entity_followup = true;
                    payload.entity_context = {
                        entity_type: state.activeContext.entity_type,
                        entity_id: state.activeContext.entity_id,
                        entity_title: state.activeContext.entity_title,
                        entity_url: state.activeContext.entity_url,
                        entity_image: state.activeContext.entity_image || "",
                    };
                }

                const resp = await fetch(cfg.links.chatEndpoint, {
                    method: "POST",
                    headers: buildHeaders(cfg),
                    body: JSON.stringify(payload),
                });

                if (!resp.body) {
                    const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                    el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                    el.textContent = cfg.texts.errors.stream_not_supported;
                    input.disabled = true;
                    return;
                }

                const reader = resp.body.getReader();
                const decoder = new TextDecoder("utf-8");
                let buffer = "";
                let firstChunk = true;

                while (true) {
                    const { done, value } = await reader.read();
                    if (done) break;

                    buffer += decoder.decode(value, { stream: true });
                    const lines = buffer.split("\n");
                    buffer = lines.pop();

                    for (const line of lines) {
                        if (!line.trim()) continue;

                        let json;
                        try {
                            json = JSON.parse(line);
                        } catch (e) {
                            console.error("[RecorsaChatWidget] JSON parse error", e, line);
                            continue;
                        }

                        if (json.debug) {
                            console.info("[RecorsaChat] pipeline", json.debug);
                        }

                        // Streaming text
                        const chunk = json?.message?.content;
                        if (chunk) {
                            if (firstChunk) {
                                const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                                el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                                el.textContent = "";
                                firstChunk = false;
                            }

                            // Typewriter queue auffüllen
                            state.typeQueues.set(msgId, (state.typeQueues.get(msgId) || "") + String(chunk));
                            typewriterTick(msgId);

                            if (typeof cfg.onMessageReceived === "function") {
                                try {
                                    cfg.onMessageReceived(chunk);
                                } catch (_) {}
                            }
                        }

                        // UI payload
                        if (json.ui) renderChatUi(json.ui);
                    }
                }
            } catch (err) {
                console.error("[RecorsaChatWidget] request failed:", err);
                const el = botWrap.querySelector(`[data-recorsa-msg="${msgId}"]`);
                if (el) {
                    el.className = "chat-bubble mb-2 py-2 px-3 fs-11";
                    el.textContent = cfg.texts.errors.generic;
                }
            } finally {
                state.streamInProgress = false;
                updateInputState(false);
            }
        }

        function bindEvents() {
            toggleBtn?.addEventListener("click", (e) => {
                e.stopPropagation();
                toggleChat();
            });

            header?.addEventListener("click", () => {
                if (rootEl.classList.contains("collapsed")) toggleChat();
            });

            sendBtn?.addEventListener("click", () => sendMessage());

            input?.addEventListener("keydown", (e) => {
                if (e.key === "Enter") {
                    e.preventDefault();
                    e.stopPropagation();
                    sendMessage();
                }
            });

            contextClearBtn?.addEventListener("click", (e) => {
                e.preventDefault();
                e.stopPropagation();
                clearActiveContext();
                input?.focus();
            });


            // Callbacks
            on(rootEl, "click", (e) => {

                const el = e.target.closest("[data-entity-type]");
                if (!el) return;

                const type = el.dataset.entityType;
                const raw = el.dataset.entityJson;

                if (!type || !raw) return;

                let item;
                try {
                    item = JSON.parse(raw);
                } catch {
                    return;
                }

                var registrySearchCollection= RecorsaPluginRegistry.collectSearch();
                for (const section of registrySearchCollection.sections) {
                    if (section.key === type) {
                        const cb = section.callback;
                        if (typeof cb === "function") {
                            e.preventDefault();
                            cb(e, el, '', item, cfg);
                        }
                    }
                }
            });

            // Predefined chips: senden eine “vorgefertigte” Frage und entfernen sich dann
            if (chipsContainer) {
                chipsContainer.querySelectorAll(".chat-chip").forEach((chip) => {
                    chip.addEventListener("click", () => {
                        const q = chip.dataset.chipQuestion || chip.textContent.trim();
                        sendMessage(q);
                        chipsContainer.remove();
                    });
                });
            }

            setupResize();
            updateResizeButtonVisibility();
        }

        // Public API for instance
        const api = {
            open() {
                if (rootEl.classList.contains("collapsed")) toggleChat();
            },
            close() {
                if (!rootEl.classList.contains("collapsed")) toggleChat();
            },
            toggle() {
                toggleChat();
            },

            getChatId() {
                return getConversationId(cfg.saleschannelId);
            },

            ensureChatId() {
                return ensureConversationId(cfg.saleschannelId);
            },

            // NEW: History per API triggern (z.B. manuell)
            loadHistory() {
                return maybeLoadHistory("api");
            },

            resetConversation() {
                clearConversationId(cfg.saleschannelId);

                // NEW:
                // Wenn du reset machst, willst du meistens auch UI “zurück” setzen.
                // Wir lassen Welcome & Chips stehen und leeren nur dynamische Messages.
                // (Optional kannst du hier auch hard-reload machen)
                try {
                    chatHistory.innerHTML = "";
                } catch (_) {}
                state.lastTimestamp = "";
                state.historyLoaded = false;
            },

            setTheme(nextTheme = {}) {
                cfg.theme = deepMerge(cfg.theme, nextTheme);
                applyTheme(hostEl, rootEl, cfg);
            },

            setTexts(nextTexts = {}) {
                cfg.texts = deepMerge(cfg.texts, nextTexts);
                // easiest: rebuild header texts + input placeholder
                const titleEl = rootEl.querySelector(".recorsa-title");
                const subEl = rootEl.querySelector(".recorsa-subtitle");
                if (titleEl) titleEl.textContent = cfg.texts.title || "";
                if (subEl) subEl.textContent = cfg.texts.subtitle || "";
                if (input) input.setAttribute("placeholder", cfg.texts.placeholder || "");
                const personEl = rootEl.querySelector(".chat-person");
                if (personEl) personEl.textContent = cfg.texts.personLine || "";
            },

            setAssets(nextAssets = {}) {
                cfg.assets = deepMerge(cfg.assets || {}, nextAssets);
                const avatarUrl = cfg.assets.botAvatarUrl || "";
                if (!avatarUrl) return;
                rootEl.querySelectorAll(".recorsa-header-avatar img, .chat-icon-circle img").forEach((img) => {
                    img.src = avatarUrl;
                });
            },

            send(message) {
                return sendMessage(String(message ?? ""));
            },

            appendBotHtml(html, options) {
                return appendBotMessageHtml(String(html ?? ""), options || {});
            },

            selectEntityContext(entity) {
                return selectEntityContext(entity);
            },

            fetchEntityContext(entity, options = {}) {
                return fetchEntityContext(entity, options);
            },

            sendEntityFollowup(question, entity) {
                const payload = entity || state.activeContext;
                if (!payload) return Promise.resolve();

                return sendEntityFollowup(String(question || ""), payload);
            },

            askEntity(question, entity) {
                const payload = entity || state.activeContext;
                if (!payload) return Promise.resolve();

                api.open();

                return new Promise((resolve) => {
                    setTimeout(resolve, 120);
                }).then(() => {
                    return sendEntityFollowup(String(question || ""), payload);
                });
            },

            destroy() {
                state.destroyed = true;
                // Nothing to remove explicitly because root is being cleared by unmount (optional)
            },
        };

        // ---------------------------------------------------------------------------
        // Init
        // ---------------------------------------------------------------------------

        bindEvents();
        updateToggleButtonIcon();
        updateResizeButtonVisibility();
        scrollChatToBottom(scrollContainer);
        updateInputState(false, false);

        // Initial icon state (legacy/optional)
        const icon = toggleBtn?.querySelector("i");
        if (icon) {
            if (rootEl.classList.contains("collapsed")) {
                icon.classList.remove("fa-xmark-circle");
                icon.classList.add("fa-comments");
            } else {
                icon.classList.remove("fa-comments");
                icon.classList.add("fa-xmark-circle");
            }
        }

        maybeLoadHistory("init");

        return api;
    }
    // Expose
    window.RecorsaChatWidget = RecorsaChatWidget;
})();


// Assistent für PDP-Seiten
(function () {
    "use strict";

    let entityAssistDataPromise = null;
    let entityAssistDataCache = null;

    function getChatInstance() {
        return (
            window.__recorsaChatWidget ||
            window.RecorsaChatInstance ||
            document.querySelector(".recorsa-chat-host")?.__recorsaInstance ||
            null
        );
    }

    function escapeHtml(value) {
        return String(value ?? "")
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#039;");
    }

    function getMeta(name) {
        return document.querySelector(`meta[name="${name}"]`)?.getAttribute("content")?.trim() || "";
    }

    function readEntityFromPage() {
        const entity = {
            entity_type: getMeta("recorsa:chat-entity-type"),
            entity_id: getMeta("recorsa:chat-entity-id"),
            entity_title: getMeta("recorsa:chat-entity-title"),
            entity_url: getMeta("recorsa:chat-entity-url"),
            entity_image: getMeta("recorsa:chat-entity-image"),
        };

        if (!entity.entity_type || !entity.entity_id) {
            return null;
        }

        return entity;
    }

    function getDefaultChips() {
        return [
        ];
    }

    function renderLoader(target) {
        target.innerHTML = `
            <div class="recorsa-chat-entity-loader">
                <span class="recorsa-chat-entity-spinner"></span>
                <span></span>
            </div>
        `;
    }

    function renderEntityAssist(target, options = {}) {
        const mode = options.mode || "chips";
        const entity = options.entity || {};
        const config = options.config || {};
        const texts = config.texts || {};
        const entityAssistTexts = texts.chat?.entityAssist || {};

        const chips = Array.isArray(options.chips) && options.chips.length
            ? options.chips
            : getDefaultChips();

        const headline = entityAssistTexts.headline || "";

        const placeholder = [
            entityAssistTexts.placeholder || "",
            entity.entity_title || ""
        ].filter(Boolean).join(" ");

        const askSomethingElse = entityAssistTexts.askSomethingElse || "";

        // SUBMIT-BUTTON Example <button type="submit" class="recorsa-chat-entity-question-submit" aria-label="Frage senden"></button>
        // BRANDING SPARK <span class="recorsa-chat-entity-assist-spark"></span>
        const inputHtml = mode === "full"
            ? `
                <div class="recorsa-chat-entity-assist-headline">
                    <strong>${escapeHtml(headline)}</strong>
                </div>

                <form class="recorsa-chat-entity-question-form">
                    <input
                        type="text"
                        class="recorsa-chat-entity-question-input"
                        placeholder="${escapeHtml(placeholder)}"
                        autocomplete="off"
                    >
                </form>
            `
            : `
                <div class="recorsa-chat-entity-assist-headline recorsa-chat-entity-assist-headline-compact">
                    <strong></strong>
                </div>
            `;
            // Option auf Frage/Titel in strong-Tag, wie z.B.: "Frag deine digitale Assistentin"

        target.innerHTML = `
            <div class="recorsa-chat-entity-assist-box recorsa-chat-entity-assist-box-${escapeHtml(mode)}">
                ${inputHtml}

                <div class="recorsa-chat-entity-chip-row">
                    ${chips.map((chip) => `
                        <button
                            type="button"
                            class="recorsa-chat-entity-chip"
                            data-recorsa-chat-entity-question="${escapeHtml(chip)}"
                        >
                            ${escapeHtml(chip)}
                        </button>
                    `).join("")}

                    ${mode === "chips" ? `
                        <button
                            type="button"
                            class="recorsa-chat-entity-chip recorsa-chat-entity-chip-primary"
                            data-recorsa-chat-entity-focus-input="1"
                        >
                            ${escapeHtml(askSomethingElse)}
                        </button>
                    ` : ""}
                </div>
            </div>
        `;

        bindEntityAssistEvents(target, entity);
    }

    function setEntityAssistBusy(isBusy) {
        document.querySelectorAll("[data-recorsa-chat-entity-assist]").forEach((host) => {
            const root = host.shadowRoot?.querySelector('[data-recorsa-chat-entity-root="1"]');
            if (!root) return;

            root.classList.toggle("recorsa-chat-entity-assist-busy", isBusy);

            root.querySelectorAll(
                ".recorsa-chat-entity-chip, .recorsa-chat-entity-question-input"
            ).forEach((el) => {
                el.disabled = isBusy;
            });
        });
    }

    function bindEntityAssistEvents(target, entity) {
        const form = target.querySelector(".recorsa-chat-entity-question-form");
        const input = target.querySelector(".recorsa-chat-entity-question-input");

        function ask(question) {
            const cleanedQuestion = String(question || "").trim();
            if (!cleanedQuestion) return;

            if (target.dataset.recorsaBusy === "1") {
                return;
            }

            target.dataset.recorsaBusy = "1";
            setEntityAssistBusy(true);

            const instance = getChatInstance();

            const done = () => {
                target.dataset.recorsaBusy = "0";
                setEntityAssistBusy(false);
            };

            if (instance && typeof instance.askEntity === "function") {
                Promise.resolve(instance.askEntity(cleanedQuestion, entity))
                    .finally(done);
                return;
            }

            if (instance && typeof instance.open === "function" && typeof instance.send === "function") {
                Promise.resolve()
                    .then(() => {
                        instance.open();
                        return instance.send(cleanedQuestion);
                    })
                    .finally(done);
                return;
            }

            done();
            console.warn("[RecorsaEntityAssist] Chat instance not found");
        }

        form?.addEventListener("submit", function (event) {
            event.preventDefault();

            const question = input?.value || "";
            if (input) input.value = "";

            ask(question);
        });

        target.querySelectorAll("[data-recorsa-chat-entity-question]").forEach((button) => {
            button.addEventListener("click", function () {
                ask(button.getAttribute("data-recorsa-chat-entity-question"));
            });
        });

        target.querySelectorAll("[data-recorsa-chat-entity-focus-input]").forEach((button) => {
            button.addEventListener("click", function () {
                const fullAssist = document.querySelector('[data-recorsa-chat-entity-assist="full"]');
                const fullInput = fullAssist?.querySelector(".recorsa-chat-entity-question-input");

                if (fullInput) {
                    fullInput.focus();
                    fullInput.scrollIntoView({
                        behavior: "smooth",
                        block: "center",
                    });
                    return;
                }

                const instance = getChatInstance();

                if (instance && typeof instance.open === "function") {
                    instance.open();

                    if (typeof instance.selectEntityContext === "function") {
                        instance.selectEntityContext(entity);
                    }
                }
            });
        });
    }

    async function fetchEntityAssistData(config, entity, mode) {
        const instance = getChatInstance();

        if (instance && typeof instance.fetchEntityContext === "function") {
            return await instance.fetchEntityContext(entity, {
                source: "inline_entity_assist",
                display: mode,
                chatId: instance.ensureChatId?.()
            });
        }

        throw new Error("Chat instance with fetchEntityContext not found");
    }

    function ensureEntityShadow(target, config = {}) {
        const shadow = target.shadowRoot || target.attachShadow({ mode: "open" });

        let root = shadow.querySelector('[data-recorsa-chat-entity-root="1"]');

        if (!root) {
            root = document.createElement("div");
            root.className = "recorsa-chat-entity-shadow-root";
            root.setAttribute("data-recorsa-chat-entity-root", "1");
            root.style.opacity = "0";
            root.style.visibility = "hidden";
            root.style.transition = "opacity .16s ease";
            shadow.appendChild(root);
        }

        applyEntityHostTheme(target, config);

        const cssHref =
            config?.links?.entityAssistCssEndpoint ||
            config?.links?.cssEndpoint ||
            config?.links?.chatCssEndpoint ||
            "";

        if (!cssHref) {
            return {
                root,
                cssReady: Promise.resolve(),
            };
        }

        let link = shadow.querySelector(`link[data-recorsa-chat-entity-css="1"]`);

        if (!link) {
            link = document.createElement("link");
            link.rel = "stylesheet";
            link.href = cssHref;
            link.setAttribute("data-recorsa-chat-entity-css", "1");

            shadow.insertBefore(link, root);
        }

        const cssReady = new Promise((resolve) => {
            let done = false;

            const finish = () => {
                if (done) return;
                done = true;
                link.dataset.loaded = "1";
                resolve();
            };

            if (link.dataset.loaded === "1") {
                finish();
                return;
            }

            link.addEventListener("load", finish, { once: true });

            link.addEventListener("error", () => {
                console.warn("[RecorsaChatEntityAssist] CSS failed to load:", cssHref);
                finish();
            }, { once: true });

            // Sicherheitsnetz: blockiere nie dauerhaft den Fetch wegen CSS
            setTimeout(finish, 1200);
        });

        return {
            root,
            cssReady,
        };
    }

    function showEntityShadowRoot(root) {
        if (!root) return;

        requestAnimationFrame(() => {
            root.style.visibility = "visible";
            root.style.opacity = "1";
        });
    }

    function applyEntityHostTheme(host, config = {}) {
        const theme = config.theme || {};

        host.style.setProperty("--recorsa-header-bg", theme.headerBg || "#2c7be5");
        host.style.setProperty("--recorsa-header-txt", theme.headerTxt || "#ffffff");
        host.style.setProperty("--recorsa-footer-bg", theme.footerBg || "#2c7be5");
        host.style.setProperty("--recorsa-footer-txt", theme.footerTxt || "#ffffff");
        host.style.setProperty("--recorsa-body-bg", theme.bodyBg || "#ffffff");
        host.style.setProperty("--recorsa-body-txt", theme.bodyTxt || "#07111f");
        host.style.setProperty("--recorsa-bot-bg", theme.botBg || "#2c7be5");
        host.style.setProperty("--recorsa-bot-txt", theme.botTxt || "#ffffff");
        host.style.setProperty("--recorsa-user-bg", theme.userBg || "#f7f7f7");
        host.style.setProperty("--recorsa-user-txt", theme.userTxt || "#707070");
        host.style.setProperty("--recorsa-icon-bg", resolveIconBackground(theme));
        const iconRadius = Math.max(0, Math.min(50, Number.isFinite(Number(theme.iconRadius)) ? Math.round(Number(theme.iconRadius)) : 50));
        host.style.setProperty("--recorsa-icon-radius", `${iconRadius}%`);
        host.style.setProperty("--recorsa-chat-font-family", String(theme.fontFamily || "").trim() || "inherit");
    }

    function getEntityAssistDataOnce(config, entity) {
        if (entityAssistDataCache) {
            return Promise.resolve(entityAssistDataCache);
        }

        if (entityAssistDataPromise) {
            return entityAssistDataPromise;
        }

        entityAssistDataPromise = fetchEntityAssistData(config, entity, "full")
            .then((data) => {
                entityAssistDataCache = data;
                return data;
            })
            .catch((error) => {
                entityAssistDataPromise = null;
                throw error;
            });

        return entityAssistDataPromise;
    }

    const RecorsaEntityAssist = {
        mountAll(config = {}) {
            config = config || window.RecorsaRuntimeConfig || {};
            console.log("[RecorsaEntityAssist] Mounting", config);
            const entity = readEntityFromPage();

            if (!entity) {
                return;
            }

            const targets = document.querySelectorAll("[data-recorsa-chat-entity-assist]");

            targets.forEach((target) => {
                const mode = target.getAttribute("data-recorsa-chat-entity-assist") || "chips";

                this.mount(target, {
                    ...config,
                    entity,
                    mode,
                });
            });
        },

        async mount(target, config = {}) {
            if (!target) return;

            const entity = config.entity || readEntityFromPage();

            if (!entity) {
                target.innerHTML = "";
                return;
            }

            const mode = config.mode || target.getAttribute("data-recorsa-chat-entity-assist") || "chips";

            const shadowMount = ensureEntityShadow(target, config);
            const shadowRootEl = shadowMount.root;

            renderLoader(shadowRootEl);

            try {
                await shadowMount.cssReady;
                const data = await getEntityAssistDataOnce(config, entity);

                renderEntityAssist(shadowRootEl, {
                    config,
                    mode,
                    entity: {
                        ...entity,
                        ...(data?.entity || {}),
                        entity_image: data?.entity?.entity_image || entity.entity_image || "",
                    },
                    chips: data?.chips || [],
                    headline: data?.headline,
                    placeholder: data?.placeholder,
                });
            } catch (error) {
                console.warn("[RecorsaChatEntityAssist] fallback chips used", error);

                renderEntityAssist(shadowRootEl, {
                    config,
                    mode,
                    entity,
                    chips: getDefaultChips(),
                });
            } finally {
                showEntityShadowRoot(shadowRootEl);
            }
        },
    };

    window.RecorsaChatEntityAssist = RecorsaEntityAssist;
})();

;
(function () {
                        "use strict";

                        if (!window.RecorsaCore) {
                            throw new Error('RecorsaCore must be loaded before bootstrap');
                        }

                        window.RecorsaCore.cfg = {};
                        window.RecorsaCore.cfg.saleschannelId = 'c6c7099b-cdcd-4415-8ccd-f56ba5d68834';window.RecorsaCore.cfg.links = {};window.RecorsaCore.cfg.links.configEndpoint = '/recorsa/widget/config';window.RecorsaCore.cfg.links.cssEndpoint = '/recorsa/widget/widget.css';window.RecorsaCore.cfg.links.chat = {};window.RecorsaCore.cfg.links.chat.chatEndpoint = '/recorsa/widget/chat';window.RecorsaCore.cfg.links.chat.historyEndpoint = '/recorsa/widget/chat/history';window.RecorsaCore.cfg.links.chat.contextEndpoint = '/recorsa/widget/chat/context';window.RecorsaCore.cfg.links.search = {};window.RecorsaCore.cfg.links.search.suggestEndpoint = '/recorsa/widget/suggest';window.RecorsaCore.cfg.links.search.searchEndpoint = '/recorsa/widget/search';})();
;
(function () {
    "use strict";

    if (!window.RecorsaChatWidget || !window.RecorsaSearchWidget) {
        throw new Error("Widgets must be loaded before bootstrap");
    }

    window.Recorsa = window.Recorsa || {};

    function ensureChatRoot(selector) {
        let root = document.querySelector(selector);

        if (!root && String(selector).startsWith("#")) {
            root = document.createElement("div");
            root.id = selector.slice(1);
            document.body.appendChild(root);
        }
        return root;
    }

    function buildConfigUrl(cfg) {
        const url = new URL(
            cfg.links.configEndpoint,
            window.location.origin
        );
        if (cfg.locale) {
            url.searchParams.set('l', cfg.locale);
        }
        return url.toString();
    }

    async function loadConfig(cfg) {

        const r = await fetch(buildConfigUrl(cfg), { credentials: "same-origin" });

        if (!r.ok) {
            throw new Error('Config fetch failed: ' + r.status);
        }

        return await r.json();
    }

    window.Recorsa.mountChat = function (cfg) {
        const mount = cfg.mount || "#recorsa-chat";

        if (!cfg.links) {
            cfg.links = {};
        }

        if (!cfg.links.chatEndpoint) {
            cfg.links.chatEndpoint = window.RecorsaCore.cfg.links.chat.chatEndpoint;
        }

        if (!cfg.links.historyEndpoint) {
            cfg.links.historyEndpoint = window.RecorsaCore.cfg.links.chat.historyEndpoint;
        }

        if (!cfg.links.contextEndpoint) {
            cfg.links.contextEndpoint = window.RecorsaCore.cfg.links.chat.contextEndpoint;
        }

        if (!cfg.links.chatEndpoint) {
            throw new Error("Chat widget requires chatEndpoint");
        }

        if (!cfg.links.historyEndpoint) {
            throw new Error("Chat widget requires historyEndpoint");
        }

        ensureChatRoot(mount);

        const instance = window.RecorsaChatWidget.mount(mount, cfg);
        window.__recorsaChatWidget = instance;
        return instance;
    };

    window.Recorsa.mountSearch = function (cfg) {
        if (!cfg.links) {
            cfg.links = {};
        }

        if (!cfg.links.suggestEndpoint) {
            cfg.links.suggestEndpoint = window.RecorsaCore.cfg.links.search.suggestEndpoint;
        }

        if (!cfg.links.searchEndpoint) {
            cfg.links.searchEndpoint = window.RecorsaCore.cfg.links.search.searchEndpoint;
        }

        if (!cfg.inputSelector) {
            throw new Error("Search widget requires inputSelector");
        }

        if (!cfg.links.suggestEndpoint) {
            throw new Error("Search widget requires suggestEndpoint");
        }

        if (!cfg.links.searchEndpoint) {
            throw new Error("Search widget requires searchEndpoint");
        }

        const instance = window.RecorsaSearchWidget.mount(cfg);
        window.__recorsaSearchWidget = instance;

        if (instance) {
            window.dispatchEvent(new CustomEvent('recorsa:search-widget-ready'));
        }

        return instance;
    };

    function normalizePagePath(pathname) {
        let path = String(pathname || "/").split("?")[0].split("#")[0];
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        path = path.replace(/\/{2,}/g, "/");
        if (path.length > 1 && path.endsWith("/")) {
            path = path.slice(0, -1);
        }
        return path || "/";
    }

    function isChatVisibleOnCurrentPage(chatCfg) {
        if (!chatCfg || typeof chatCfg !== "object") {
            return false;
        }

        const ignore = chatCfg.ignorePageVisibility;
        if (ignore === true || ignore === "true" || ignore === 1 || ignore === "1") {
            return true;
        }

        const visibility = chatCfg.visibility || {};
        const mode = visibility.mode || "all";
        if (mode === "off") {
            return false;
        }
        if (mode !== "include") {
            return true;
        }

        const rules = Array.isArray(visibility.rules) ? visibility.rules : [];
        if (!rules.length) {
            return false;
        }

        const current = normalizePagePath(window.location.pathname);

        return rules.some((rule) => {
            const rulePath = normalizePagePath(rule && rule.path ? rule.path : "");
            if (!rulePath) {
                return false;
            }
            if (current === rulePath) {
                return true;
            }
            const includeChildren = !!(rule && (rule.include_children === true || rule.include_children === 1 || rule.include_children === "1"));
            if (!includeChildren) {
                return false;
            }
            return rulePath === "/" || current.startsWith(rulePath + "/");
        });
    }

    window.Recorsa.mountAll = function (cfg) {
        const out = {
            chat: null,
            search: []
        };

        if (cfg.chat) {
            if (isChatVisibleOnCurrentPage(cfg.chat)) {
                out.chat = window.Recorsa.mountChat(cfg.chat);
            }
        }

        if (cfg.search) {
            const searches = Array.isArray(cfg.search) ? cfg.search : [cfg.search];
            for (const searchCfg of searches) {
                out.search.push(
                    window.Recorsa.mountSearch(searchCfg)
                );
            }
        }

        return out;
    };

    async function initRecorsa() {
        if ('attachShadow' in Element.prototype) {
            const script = document.currentScript;
            const overrides = RecorsaCore.utils.buildOverridesFromDataAttributes(script);

            var baseConfig = RecorsaCore.utils.deepMerge(
                window.RecorsaCore.cfg || {},
                window.RecorsaRuntimeConfig || {}
            );

            baseConfig = RecorsaCore.utils.deepMerge(baseConfig, overrides);

            if (!baseConfig.links) {
                baseConfig.links = {};
            }

            if (!baseConfig.links.configEndpoint) {
                baseConfig.links.configEndpoint = window.RecorsaCore.cfg.links.configEndpoint;
            }

            if (!baseConfig.links.cssEndpoint) {
                baseConfig.links.cssEndpoint = window.RecorsaCore.cfg.links.cssEndpoint;
            }

            var loadedConfig = await loadConfig(baseConfig);

            var finalConfig = RecorsaCore.utils.deepMerge(baseConfig, loadedConfig);
            finalConfig = RecorsaCore.utils.deepMerge(finalConfig, overrides);

            if (finalConfig.chat && typeof finalConfig.chat === 'object') {
                if (!finalConfig.chat.saleschannelId) {
                    finalConfig.chat.saleschannelId = finalConfig.saleschannelId;
                }
                if (!finalConfig.chat.locale) {
                    finalConfig.chat.locale = finalConfig.locale;
                }
                if (!finalConfig.chat.widgetLocale) {
                    finalConfig.chat.widgetLocale = finalConfig.widgetLocale;
                }
                if (!finalConfig.chat.links) {
                    finalConfig.chat.links = {};
                }
                if (!finalConfig.chat.links.cssEndpoint) {
                    finalConfig.chat.links.cssEndpoint = finalConfig.links.cssEndpoint;
                }
            } else {
                finalConfig.chat = undefined;
            }

            if (finalConfig.search && typeof finalConfig.search === 'object') {
                if (!finalConfig.search.saleschannelId) {
                    finalConfig.search.saleschannelId = finalConfig.saleschannelId;
                }
                if (!finalConfig.search.locale) {
                    finalConfig.search.locale = finalConfig.locale;
                }
                if (!finalConfig.search.widgetLocale) {
                    finalConfig.search.widgetLocale = finalConfig.widgetLocale;
                }
                if (!finalConfig.search.links) {
                    finalConfig.search.links = {};
                }
                if (!finalConfig.search.links.cssEndpoint) {
                    finalConfig.search.links.cssEndpoint = finalConfig.links.cssEndpoint;
                }
            } else {
                // FIXME Prüfen, warum hier nur search auf undefined?
                finalConfig.search = undefined;
            }

            window.RecorsaRuntimeConfig = finalConfig;

            if (finalConfig) {
                try {
                    const mounted = window.Recorsa.mountAll(finalConfig);

                    window.dispatchEvent(new CustomEvent("recorsa:ready", {
                        detail: {
                            config: finalConfig,
                            mounted: mounted
                        }
                    }));

                    if (window.RecorsaChatEntityAssist) {
                        window.RecorsaChatEntityAssist.mountAll(finalConfig.chat || finalConfig);
                    }
                } catch (err) {
                    console.error("[Recorsa]", err);
                }
            }
        } else {
            console.error("[Recorsa]", 'Shadow Dom must be available to use Recorsa. Please upgrade your browser to the latest version.');
        }
    }

    initRecorsa().catch(err => {
        console.error("[Recorsa] Init failed", err);
    });
})();
