{"id":1165,"date":"2026-04-14T10:48:53","date_gmt":"2026-04-14T08:48:53","guid":{"rendered":"https:\/\/www.tisoft.it\/?page_id=1165"},"modified":"2026-04-17T10:14:20","modified_gmt":"2026-04-17T08:14:20","slug":"shop-service","status":"publish","type":"page","link":"https:\/\/www.tisoft.it\/en\/shop-service\/","title":{"rendered":"Shop &amp; Service"},"content":{"rendered":"    <script id=\"ti-ss-maintenance-auto-496\">\n    (function(){\n        \"use strict\";\n        var issue = {\"active\":false,\"code\":\"\",\"message\":\"\",\"detail\":\"\",\"db\":\"\"};\n        var isAdmin = false;\n        window.__tiSsMaintenanceIssue496 = issue;\n        window.__tiSsMaintenanceAdmin496 = isAdmin;\n        function esc(s){ return String(s == null ? '' : s).replace(\/[&<>\"']\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c];}); }\n        function copyText573(text, statusEl){\n            function ok(){ if (statusEl) { statusEl.style.color = '#86efac'; statusEl.textContent = 'Testo copiato.'; } }\n            function fail(){\n                try {\n                    var ta = document.createElement('textarea');\n                    ta.value = String(text || ''); ta.setAttribute('readonly','readonly');\n                    ta.style.position = 'fixed'; ta.style.left = '-9999px'; ta.style.top = '0';\n                    document.body.appendChild(ta); ta.focus(); ta.select();\n                    document.execCommand('copy'); document.body.removeChild(ta); ok();\n                } catch(e) { if (statusEl) { statusEl.style.color = '#fca5a5'; statusEl.textContent = 'Copia non riuscita.'; } }\n            }\n            if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(String(text || '')).then(ok).catch(fail);\n            else fail();\n        }\n        function showMaintenance(reason, detail){\n            if (window.__tiSsMaintenanceShown496) return;\n            window.__tiSsMaintenanceShown496 = true;\n            reason = reason || (issue && issue.message) || 'Shop & Service temporaneamente in manutenzione.';\n            detail = detail || (issue && issue.detail) || '';\n            var userMessage = 'Accesso temporaneamente non disponibile con il profilo corrente.';\n            var infoMessage = 'Shop & Service ha rilevato una condizione di manutenzione o blocco tecnico. Puoi riprovare pi\u00f9 tardi oppure accedere con un profilo amministratore abilitato. Per supporto tecnico: SSSupport@targetinformatica.it.';\n            if (isAdmin) {\n                try {\n                    if (window.tiAlert) window.tiAlert('Shop & Service: problema rilevato\\n\\n' + reason + (detail ? '\\n\\nDettaglio: ' + detail : ''));\n                    else alert('Shop & Service: problema rilevato\\n\\n' + reason + (detail ? '\\n\\nDettaglio: ' + detail : ''));\n                } catch(e) {}\n                return;\n            }\n            var div = document.createElement('div');\n            div.id = 'ti-ss-maintenance-runtime-496';\n            div.style.cssText = 'position:fixed;inset:0;background:rgba(2,6,23,.86);z-index:2147483600;display:flex;align-items:center;justify-content:center;padding:18px;font-family:Arial,Helvetica,sans-serif;';\n            div.innerHTML = '<div style=\"width:min(560px,96vw);background:#0f172a;color:#e5e7eb;border:1px solid #38bdf8;border-radius:18px;padding:20px;box-shadow:0 22px 70px rgba(0,0,0,.65);\">'\n                + '<div style=\"display:flex;align-items:center;gap:10px;margin-bottom:10px;\"><h2 style=\"flex:1;margin:0;color:#fff;font-size:21px;\">Shop &amp; Service in manutenzione.<\/h2><button type=\"button\" id=\"ti-maint-runtime-info-672\" style=\"padding:7px 9px;border:1px solid #38bdf8;border-radius:999px;background:#082f49;color:#e0f2fe;font-size:12px;font-weight:800;cursor:pointer;white-space:nowrap;\">? info<\/button><\/div>'\n                + '<div style=\"background:#111827;border:1px solid #334155;border-radius:12px;padding:12px 14px;line-height:1.45;margin-bottom:12px;\">' + esc(userMessage) + '<\/div>'\n                + '<div id=\"ti-maint-runtime-info-box-672\" style=\"display:none;font-size:12px;color:#cbd5e1;white-space:pre-wrap;max-height:220px;overflow:auto;background:#020617;border:1px solid #1f2937;border-radius:8px;padding:10px;margin-bottom:12px;\">' + esc(infoMessage + (reason ? '\\n\\nMessaggio tecnico: ' + reason : '') + (detail ? '\\n\\nDettaglio tecnico:\\n' + detail : '')) + '<\/div>'\n                + '<div style=\"display:flex;gap:8px;align-items:center;margin-top:14px;\"><button type=\"button\" id=\"ti-maint-runtime-copy-573\" style=\"padding:9px 11px;border:0;border-radius:10px;background:#334155;color:#fff;font-weight:800;cursor:pointer;\">Copia testo<\/button><span id=\"ti-maint-runtime-copy-msg-573\" style=\"font-size:12px;color:#93c5fd;\"><\/span><\/div>'\n                + '<button type=\"button\" onclick=\"location.reload()\" style=\"width:100%;margin-top:14px;padding:11px;border:0;border-radius:10px;background:#10b981;color:#fff;font-weight:800;cursor:pointer;\">Riprova \/ accesso amministratore<\/button>'\n                + '<\/div>';\n            document.body.appendChild(div);\n            var infoBtn = div.querySelector('#ti-maint-runtime-info-672');\n            if (infoBtn) infoBtn.addEventListener('click', function(){ var box = div.querySelector('#ti-maint-runtime-info-box-672'); if (box) box.style.display = (box.style.display === 'none' || !box.style.display) ? 'block' : 'none'; });\n            var copyBtn = div.querySelector('#ti-maint-runtime-copy-573');\n            if (copyBtn) copyBtn.addEventListener('click', function(){\n                var txt = ['Shop & Service in manutenzione.', userMessage, 'Info: ' + infoMessage, reason ? ('Messaggio tecnico: ' + String(reason)) : '', detail ? ('Dettaglio tecnico:\\n' + String(detail)) : ''].filter(Boolean).join('\\n\\n');\n                copyText573(txt, div.querySelector('#ti-maint-runtime-copy-msg-573'));\n            });\n        }\n        window.tiSsEnterMaintenance496 = showMaintenance;\n        if (issue && issue.active) setTimeout(function(){ showMaintenance(issue.message, issue.detail); }, 300);\n        window.addEventListener('error', function(ev){\n            var msg = (ev && (ev.message || (ev.error && ev.error.message))) || 'Errore JavaScript imprevisto.';\n            if (\/ResizeObserver loop|Script error\/i.test(String(msg))) return;\n            showMaintenance('Errore tecnico del plugin.', msg + (ev && ev.filename ? ('\\n' + ev.filename + ':' + ev.lineno) : ''));\n        });\n        window.addEventListener('unhandledrejection', function(ev){\n            var r = ev && ev.reason;\n            var msg = (r && (r.message || r.statusText)) || r || 'Errore asincrono imprevisto.';\n            showMaintenance('Errore tecnico del plugin.', String(msg));\n        });\n    })();\n    <\/script>\n    <style>\n        #ti-ai-outer {\n            --ti-font-base: 13px;\n            --ti-font-small: 12px;\n            --ti-font-xs: 11px;\n            --ti-font-large: 14px;\n            --ti-btn-h: 34px;\n            --ti-btn-radius: 8px;\n            --ti-input-h: 34px;\n            --ti-row-h: 34px;\n            --ti-vh: 1vh;\n            width: 100%;\n            max-width: 800px;\n            margin: 0 auto;\n            padding: 12px;\n            font-family: Arial, Helvetica, sans-serif !important;\n            font-size: var(--ti-font-base) !important;\n            line-height: 1.3 !important;\n            color: #e5e7eb;\n            box-sizing: border-box;\n            overflow-x: hidden;\n            overflow-y: visible;\n            touch-action: pan-y;\n            overscroll-behavior-y: auto;\n            -webkit-overflow-scrolling: touch;\n            -webkit-text-size-adjust: 100%;\n            text-size-adjust: 100%;\n        }\n        #ti-ai-outer, #ti-ai-outer * { box-sizing: border-box; }\n        #ti-ai-outer button,\n        #ti-ai-outer input,\n        #ti-ai-outer select,\n        #ti-ai-outer textarea,\n        #ti-ai-outer label,\n        #ti-ai-outer th,\n        #ti-ai-outer td,\n        #ti-ai-outer span,\n        #ti-ai-outer div,\n        #ti-ai-outer a {\n            font-family: Arial, Helvetica, sans-serif !important;\n        }\n        #ti-ai-outer button,\n        #ti-ai-outer input,\n        #ti-ai-outer select,\n        #ti-ai-outer textarea {\n            font-size: var(--ti-font-base) !important;\n            line-height: 1.2 !important;\n        }\n        #ti-ai-outer .ti-btn,\n        #ti-ai-outer .ti-send,\n        #ti-ai-outer .ti-help,\n        #ti-ai-outer #ti-sort-btn,\n        #ti-ai-outer #ti-show-sospese-btn,\n        #ti-ai-outer #ti-login-btn,\n        #ti-ai-outer #ti-logout-btn,\n        #ti-ai-outer .ti-lang-opt {\n            min-height: var(--ti-btn-h) !important;\n            font-size: var(--ti-font-base) !important;\n            line-height: 1.1 !important;\n            border-radius: var(--ti-btn-radius) !important;\n            white-space: nowrap !important;\n        }\n        #ti-ai-outer .ti-btn {\n            padding: 8px 12px !important;\n            font-weight: 600 !important;\n        }\n        #ti-ai-outer .ti-send {\n            height: 38px !important;\n            min-height: 38px !important;\n            font-weight: 900 !important;\n            font-size: var(--ti-font-base) !important;\n        }\n        #ti-ai-outer .ti-help {\n            height: 24px !important;\n            min-height: 24px !important;\n            font-size: var(--ti-font-large) !important;\n            line-height: 1 !important;\n        }\n        #ti-ai-outer #ti-filter-ditte,\n        #ti-ai-outer #ti-ditta,\n        #ti-ai-outer .ti-in,\n        #ti-ai-outer .ti-msg {\n            font-size: var(--ti-font-base) !important;\n        }\n        #ti-ai-outer #ti-filter-ditte,\n        #ti-ai-outer #ti-ditta,\n        #ti-ai-outer .ti-in {\n            min-height: var(--ti-input-h) !important;\n            border-radius: 8px !important;\n        }\n        #ti-ai-outer .ti-user-name,\n        #ti-ai-outer .ti-user-role,\n        #ti-ai-outer #ti-company-sub,\n        #ti-ai-outer #ti-company-name {\n            font-family: Arial, Helvetica, sans-serif !important;\n        }\n        #ti-ai-outer .ti-ditta-row,\n        #ti-ai-outer .ti-ditta-row-main {\n            font-size: var(--ti-font-base) !important;\n            line-height: 1.25 !important;\n        }\n        #ti-ai-outer .ti-ditta-row {\n            min-height: var(--ti-row-h) !important;\n            padding: 8px 10px !important;\n        }\n        #ti-ai-outer .ti-ditta-row-badge {\n            font-size: var(--ti-font-xs) !important;\n            line-height: 1.1 !important;\n        }\n        #ti-ai-outer .ti-prod-table {\n            font-size: var(--ti-font-base) !important;\n        }\n        #ti-ai-outer .ti-prod-table th {\n            font-size: var(--ti-font-xs) !important;\n        }\n        #ti-ai-outer .ti-prod-table td,\n        #ti-ai-outer .ti-filter-input,\n        #ti-ai-outer .ti-order-filterlabel,\n        #ti-ai-outer .ti-order-filtervalue {\n            font-size: var(--ti-font-small) !important;\n        }\n        #ti-ai-outer .ti-modal h3,\n        #ti-ai-outer .ti-modal h4,\n        #ti-ai-outer .ti-modal p,\n        #ti-ai-outer .ti-modal label,\n        #ti-ai-outer .ti-modal .ti-btn,\n        #ti-ai-outer .ti-modal .ti-in,\n        #ti-ai-outer .ti-modal select,\n        #ti-ai-outer .ti-modal textarea {\n            font-family: Arial, Helvetica, sans-serif !important;\n        }\n        #ti-ai-outer .ti-modal h3 { font-size: 18px !important; line-height: 1.2 !important; }\n        #ti-ai-outer .ti-modal h4 { font-size: 16px !important; line-height: 1.2 !important; }\n        #ti-ai-outer .ti-modal p,\n        #ti-ai-outer .ti-modal label,\n        #ti-ai-outer .ti-modal .ti-in,\n        #ti-ai-outer .ti-modal select,\n        #ti-ai-outer .ti-modal textarea,\n        #ti-ai-outer .ti-modal .ti-btn { font-size: var(--ti-font-base) !important; }\n        @media (max-width: 640px) {\n            #ti-ai-outer {\n                --ti-font-base: 13px;\n                --ti-font-small: 12px;\n                --ti-font-xs: 11px;\n                --ti-font-large: 14px;\n                --ti-btn-h: 34px;\n                --ti-input-h: 34px;\n                --ti-row-h: 34px;\n            }\n        }\n        #ti-ai-outer * { box-sizing: border-box; }\n        .ti-header { display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; padding: 15px; min-height: 85px; border-radius: 14px; background: #111827; border: 1px solid rgba(255,255,255,0.08); margin-bottom: 10px; position: relative; gap: 10px; }\n        .ti-header-top { display:flex; align-items:center; justify-content:space-between; gap:12px; width:100%; min-width:0; }\n        .ti-header-main-left { display:flex; align-items:center; gap:12px; min-width:0; flex:1 1 auto; }\n        .ti-hl { display: flex; align-items: center; justify-content: flex-start; gap: 12px; width: 100%; min-width: 0; flex:1 1 auto; }\n        .ti-left-stack { display:flex; flex-direction:row; align-items:center; gap:6px; }\n        .ti-company-sub-row { display:flex; width:100%; justify-content:center; align-items:center; min-height:0; }\n        #ti-company-sub { text-align:center; width:100%; }\n        #ti-company-name { font-weight:900; font-size:2.2em; line-height:1.05; color:#fff; letter-spacing:0.01em; }\n        #ti-company-sub { font-size:14px; line-height:1.2; }\n        .ti-header-bottom { display:flex; align-items:center; justify-content:space-between; gap:10px; width:100%; flex-wrap:wrap; }\n        .ti-header-left-controls { display:flex; align-items:center; gap:8px; flex:1 1 auto; min-width:0; flex-wrap:wrap; }\n        .ti-header-right-controls { display:flex; align-items:center; justify-content:flex-end; gap:8px; margin-left:auto; flex:0 1 auto; flex-wrap:wrap; }\n        .ti-hr-right { display: flex; align-items: center; justify-content: flex-end; gap: 8px; width: auto; flex: 0 0 auto; min-height:26px; }\n        .ti-hr-bottom { display: flex; align-items: center; gap: 8px; }\n        .ti-company-ref-datetime { display:none; align-items:center; justify-content:center; min-height:24px; padding:3px 8px; border-radius:999px; background:#0f172a; border:1px solid #334155; color:#bfdbfe; font-size:12px; font-weight:800; white-space:nowrap; }\n        .ti-header-help-btn { display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; min-width:24px; min-height:24px; padding:0; border-radius:999px; background:#374151; color:#fff; border:1px solid #4b5563; font-size:14px; font-weight:900; line-height:1; cursor:pointer; flex:0 0 24px; }\n        .ti-header-help-btn:hover { background:#4b5563; color:#fff; }\n        @media (max-width:767px) { .ti-header-help-btn { width:22px; height:22px; min-width:22px; min-height:22px; font-size:12px; } }\n        .ti-conf-header-right { display:flex !important; align-items:center !important; justify-content:flex-end !important; gap:8px !important; margin-left:auto !important; padding-right:42px !important; min-width:0 !important; }\n        .ti-conf-ref-datetime { display:none; flex:0 0 auto !important; max-width:260px !important; overflow:hidden !important; text-overflow:ellipsis !important; }\n        @media (max-width: 767px) { .ti-conf-header-right { gap:5px !important; padding-right:38px !important; } .ti-conf-ref-datetime { max-width:150px !important; font-size:10px !important; padding:2px 6px !important; } }\n        .ti-lang-wrap { display:inline-flex !important; gap:10px; font-size:15px; font-weight:800; z-index:10; align-items:center !important; justify-content:flex-end; text-align:right; min-height:24px !important; height:24px !important; line-height:24px !important; vertical-align:middle; }\n        .ti-lang-wrap .ti-lang-opt { display:inline-flex !important; align-items:center !important; justify-content:center !important; min-height:24px !important; height:24px !important; line-height:24px !important; padding:0 1px !important; border-radius:0 !important; }\n        .ti-lang-opt { color: #6b7280; cursor: pointer; }\n        .ti-lang-opt.active { color: #38bdf8; text-decoration: underline; }\n        .ti-logo { width: 44px; height: 44px; object-fit: contain; border-radius: 8px; background: rgba(255,255,255,0.1); padding: 2px; cursor: pointer; transition: transform 0.2s; }\n        .ti-logo:hover { transform: scale(1.06); }\n        .ti-qr { width: 44px; height: 44px; object-fit: contain; border-radius: 8px; background: #fff; padding: 2px; cursor: pointer; transition: transform 0.2s; }\n        .ti-qr:hover { transform: scale(1.1); }\n        .ti-user-area { display: none; align-items: center; gap: 6px; text-align: left; margin: 0; background: rgba(255,255,255,0.05); padding: 0 10px; border-radius: 18px; border: 1px solid rgba(255,255,255,0.12); min-height: 34px; height: 34px; max-width: 240px; width: fit-content; box-sizing: border-box; }\n        .ti-hr-bottom .ti-user-area { flex: 0 1 auto; align-self: center; margin-left: 2px; }\n        .ti-user-avatar { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; cursor: pointer; border: 1px solid #38bdf8; background: #000; flex: 0 0 20px; }\n        .ti-user-info { cursor: pointer; line-height: 1.05; min-width: 0; display:flex; align-items:center; height:100%; }\n        .ti-user-name { display: block; font-weight: 700; color: #fff; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n        .ti-user-role { display: inline; font-size: 10px; color: #cbd5e1; font-weight: 500; }\n        .ti-chat-box { min-height: 250px; max-height: 60vh; overflow-y: auto; padding: 12px; border-radius: 14px; background: #111827; border: 1px solid rgba(255,255,255,0.08); margin-bottom: 10px; -webkit-overflow-scrolling: touch; }\n        .ti-chat-box.ti-chat-after-ai-form-717 { margin-top: 10px; margin-bottom: 10px; }\n        .ti-bubble { max-width: 96%; padding: 10px 12px; border-radius: 14px; margin: 10px 0; line-height: 1.45; white-space: pre-wrap; word-wrap: break-word; font-size: 14px !important; }\n        .ti-bubble p { font-size: 14px !important; margin: 6px 0; }\n        .ti-bubble.user { margin-left: auto; background: rgba(56,189,248,0.2); border: 1px solid rgba(56,189,248,0.3); text-align: right; }\n        .ti-bubble.ai { margin-right: auto; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12); font-size: 15.5px !important; line-height: 1.58 !important; letter-spacing: 0.01em; }\n        .ti-bubble.ai p, .ti-bubble.ai li { font-size: 15.5px !important; line-height: 1.58 !important; margin: 7px 0; }\n        .ti-bubble.ai b, .ti-bubble.ai strong { font-weight: 800; }\n        .ti-bubble.ai table, .ti-bubble.ai th, .ti-bubble.ai td { font-size: 14px !important; line-height: 1.42 !important; }\n        .ti-input-wrap { padding: 10px; border-radius: 14px; background: #111827; border: 1px solid rgba(255,255,255,0.08); }\n        .ti-input-main { display: flex; gap: 10px; }\n        .ti-msg { flex: 1; padding: 12px; border-radius: 12px; background: rgba(255,255,255,0.06); color: #fff; border: 1px solid rgba(255,255,255,0.12); outline: none; min-height: 126px; resize: vertical; font-size: 14px !important; line-height: 1.45; }\n\n        #ti-ai-outer,\n        #ti-ai-outer input,\n        #ti-ai-outer textarea,\n        #ti-ai-outer select,\n        #ti-ai-outer option,\n        #ti-ai-outer .ti-in,\n        #ti-ai-outer .ti-msg,\n        #ti-ai-outer .ti-filter-input,\n        #ti-ai-outer .ti-cfg-filter,\n        #ti-ai-outer .ti-import-map-select,\n        #ti-ai-outer .ti-import-map-input,\n        #ti-ai-outer .ti-p-input,\n        #ti-ai-outer .ti-q,\n        #ti-ai-outer .ti-support-box input,\n        #ti-ai-outer .ti-support-box textarea,\n        #ti-ai-outer #help-bot-in,\n        #ti-ai-outer #ai-gen-prompt,\n        #ti-ai-outer #ti-kbd-prev,\n        #ti-ai-outer [contenteditable=\"true\"] { color:#fff !important; -webkit-text-fill-color:#fff !important; caret-color:#fff !important; }\n        #ti-ai-outer .ti-conf-window input,\n        #ti-ai-outer .ti-conf-window textarea,\n        #ti-ai-outer .ti-conf-window select,\n        #ti-ai-outer .ti-conf-window option,\n        #ti-ai-outer .ti-conf-body input,\n        #ti-ai-outer .ti-conf-body textarea,\n        #ti-ai-outer .ti-conf-body select,\n        #ti-ai-outer .ti-conf-body option,\n        #ti-ai-outer .ti-cfg-table input,\n        #ti-ai-outer .ti-cfg-table textarea,\n        #ti-ai-outer .ti-cfg-table select,\n        #ti-ai-outer .ti-cfg-table option,\n        #ti-ai-outer .ti-import-map-table input,\n        #ti-ai-outer .ti-import-map-table textarea,\n        #ti-ai-outer .ti-import-map-table select,\n        #ti-ai-outer .ti-import-map-table option,\n        #ti-ai-outer .ti-conf-window .ti-in,\n        #ti-ai-outer .ti-conf-window .ti-q,\n        #ti-ai-outer .ti-conf-window .ti-p-input,\n        #ti-ai-outer .ti-conf-window .ti-import-map-input,\n        #ti-ai-outer .ti-conf-window .ti-import-map-select,\n        #ti-ai-outer .ti-conf-window [contenteditable=\"true\"] {\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            caret-color:#fff !important;\n        }\n        #ti-ai-outer .ti-conf-window input::placeholder,\n        #ti-ai-outer .ti-conf-window textarea::placeholder,\n        #ti-ai-outer .ti-conf-window .ti-in::placeholder,\n        #ti-ai-outer .ti-conf-window .ti-q::placeholder,\n        #ti-ai-outer .ti-conf-window .ti-p-input::placeholder,\n        #ti-ai-outer .ti-conf-window .ti-import-map-input::placeholder {\n            color:#cbd5e1 !important;\n            -webkit-text-fill-color:#cbd5e1 !important;\n            opacity:1 !important;\n        }\n        #ti-ai-outer .ti-conf-window input,\n        #ti-ai-outer .ti-conf-window textarea,\n        #ti-ai-outer .ti-conf-window select,\n        #ti-ai-outer .ti-cfg-table input,\n        #ti-ai-outer .ti-cfg-table textarea,\n        #ti-ai-outer .ti-cfg-table select,\n        #ti-ai-outer .ti-import-map-table input,\n        #ti-ai-outer .ti-import-map-table textarea,\n        #ti-ai-outer .ti-import-map-table select {\n            background:#000 !important;\n            border-color:#4b5563 !important;\n        }\n        #ti-ai-outer .ti-conf-window input:focus,\n        #ti-ai-outer .ti-conf-window textarea:focus,\n        #ti-ai-outer .ti-conf-window select:focus,\n        #ti-ai-outer .ti-cfg-table input:focus,\n        #ti-ai-outer .ti-cfg-table textarea:focus,\n        #ti-ai-outer .ti-cfg-table select:focus,\n        #ti-ai-outer .ti-import-map-table input:focus,\n        #ti-ai-outer .ti-import-map-table textarea:focus,\n        #ti-ai-outer .ti-import-map-table select:focus {\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            border-color:#93c5fd !important;\n            box-shadow:0 0 0 1px rgba(147,197,253,.35) !important;\n            outline:none !important;\n        }\n        #ti-ai-outer .ti-conf-window input:active,\n        #ti-ai-outer .ti-conf-window textarea:active,\n        #ti-ai-outer .ti-conf-window select:active,\n        #ti-ai-outer .ti-cfg-table input:active,\n        #ti-ai-outer .ti-cfg-table textarea:active,\n        #ti-ai-outer .ti-cfg-table select:active,\n        #ti-ai-outer .ti-import-map-table input:active,\n        #ti-ai-outer .ti-import-map-table textarea:active,\n        #ti-ai-outer .ti-import-map-table select:active,\n        #ti-ai-outer .ti-conf-window [contenteditable=\"true\"]:focus,\n        #ti-ai-outer .ti-conf-window [contenteditable=\"true\"]:active,\n        #ti-ai-outer .ti-cfg-table [contenteditable=\"true\"]:focus,\n        #ti-ai-outer .ti-cfg-table [contenteditable=\"true\"]:active,\n        #ti-ai-outer .ti-import-map-table [contenteditable=\"true\"]:focus,\n        #ti-ai-outer .ti-import-map-table [contenteditable=\"true\"]:active {\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            caret-color:#fff !important;\n            background:#000 !important;\n            text-shadow:none !important;\n        }\n        #ti-ai-outer .ti-conf-window input:-webkit-autofill,\n        #ti-ai-outer .ti-conf-window textarea:-webkit-autofill,\n        #ti-ai-outer .ti-conf-window select:-webkit-autofill,\n        #ti-ai-outer .ti-cfg-table input:-webkit-autofill,\n        #ti-ai-outer .ti-cfg-table textarea:-webkit-autofill,\n        #ti-ai-outer .ti-cfg-table select:-webkit-autofill,\n        #ti-ai-outer .ti-import-map-table input:-webkit-autofill,\n        #ti-ai-outer .ti-import-map-table textarea:-webkit-autofill,\n        #ti-ai-outer .ti-import-map-table select:-webkit-autofill {\n            -webkit-text-fill-color:#fff !important;\n            caret-color:#fff !important;\n            box-shadow:0 0 0 1000px #000 inset !important;\n            transition:background-color 99999s ease-in-out 0s !important;\n        }\n        #ti-ai-outer .ti-conf-window input,\n        #ti-ai-outer .ti-conf-window textarea,\n        #ti-ai-outer .ti-conf-window select,\n        #ti-ai-outer .ti-conf-window option,\n        #ti-ai-outer .ti-conf-body input,\n        #ti-ai-outer .ti-conf-body textarea,\n        #ti-ai-outer .ti-conf-body select,\n        #ti-ai-outer .ti-conf-body option,\n        #ti-ai-outer .ti-cfg-table input,\n        #ti-ai-outer .ti-cfg-table textarea,\n        #ti-ai-outer .ti-cfg-table select,\n        #ti-ai-outer .ti-cfg-table option,\n        #ti-ai-outer .ti-import-map-table input,\n        #ti-ai-outer .ti-import-map-table textarea,\n        #ti-ai-outer .ti-import-map-table select,\n        #ti-ai-outer .ti-import-map-table option {\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            caret-color:#fff !important;\n            background:#000 !important;\n            background-color:#000 !important;\n            background-image:none !important;\n            text-shadow:none !important;\n            color-scheme:dark !important;\n        }\n        #ti-ai-outer .ti-conf-window input:hover,\n        #ti-ai-outer .ti-conf-window textarea:hover,\n        #ti-ai-outer .ti-conf-window select:hover,\n        #ti-ai-outer .ti-cfg-table input:hover,\n        #ti-ai-outer .ti-cfg-table textarea:hover,\n        #ti-ai-outer .ti-cfg-table select:hover,\n        #ti-ai-outer .ti-import-map-table input:hover,\n        #ti-ai-outer .ti-import-map-table textarea:hover,\n        #ti-ai-outer .ti-import-map-table select:hover,\n        #ti-ai-outer .ti-conf-window input:focus,\n        #ti-ai-outer .ti-conf-window textarea:focus,\n        #ti-ai-outer .ti-conf-window select:focus,\n        #ti-ai-outer .ti-cfg-table input:focus,\n        #ti-ai-outer .ti-cfg-table textarea:focus,\n        #ti-ai-outer .ti-cfg-table select:focus,\n        #ti-ai-outer .ti-import-map-table input:focus,\n        #ti-ai-outer .ti-import-map-table textarea:focus,\n        #ti-ai-outer .ti-import-map-table select:focus,\n        #ti-ai-outer .ti-conf-window input:active,\n        #ti-ai-outer .ti-conf-window textarea:active,\n        #ti-ai-outer .ti-conf-window select:active,\n        #ti-ai-outer .ti-cfg-table input:active,\n        #ti-ai-outer .ti-cfg-table textarea:active,\n        #ti-ai-outer .ti-cfg-table select:active,\n        #ti-ai-outer .ti-import-map-table input:active,\n        #ti-ai-outer .ti-import-map-table textarea:active,\n        #ti-ai-outer .ti-import-map-table select:active {\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            background:#000 !important;\n            background-color:#000 !important;\n            background-image:none !important;\n        }\n        #ti-ai-outer .ti-conf-window option:checked,\n        #ti-ai-outer .ti-conf-window option:hover,\n        #ti-ai-outer .ti-cfg-table option:checked,\n        #ti-ai-outer .ti-cfg-table option:hover,\n        #ti-ai-outer .ti-import-map-table option:checked,\n        #ti-ai-outer .ti-import-map-table option:hover {\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            background:#111827 !important;\n            background-color:#111827 !important;\n        }\n        #ti-ai-outer .ti-conf-window input:-webkit-autofill,\n        #ti-ai-outer .ti-conf-window input:-webkit-autofill:hover,\n        #ti-ai-outer .ti-conf-window input:-webkit-autofill:focus,\n        #ti-ai-outer .ti-conf-window textarea:-webkit-autofill,\n        #ti-ai-outer .ti-conf-window textarea:-webkit-autofill:hover,\n        #ti-ai-outer .ti-conf-window textarea:-webkit-autofill:focus,\n        #ti-ai-outer .ti-cfg-table input:-webkit-autofill,\n        #ti-ai-outer .ti-cfg-table input:-webkit-autofill:hover,\n        #ti-ai-outer .ti-cfg-table input:-webkit-autofill:focus,\n        #ti-ai-outer .ti-cfg-table textarea:-webkit-autofill,\n        #ti-ai-outer .ti-cfg-table textarea:-webkit-autofill:hover,\n        #ti-ai-outer .ti-cfg-table textarea:-webkit-autofill:focus,\n        #ti-ai-outer .ti-import-map-table input:-webkit-autofill,\n        #ti-ai-outer .ti-import-map-table input:-webkit-autofill:hover,\n        #ti-ai-outer .ti-import-map-table input:-webkit-autofill:focus,\n        #ti-ai-outer .ti-import-map-table textarea:-webkit-autofill,\n        #ti-ai-outer .ti-import-map-table textarea:-webkit-autofill:hover,\n        #ti-ai-outer .ti-import-map-table textarea:-webkit-autofill:focus {\n            -webkit-text-fill-color:#fff !important;\n            caret-color:#fff !important;\n            -webkit-box-shadow:0 0 0 1000px #000 inset !important;\n            box-shadow:0 0 0 1000px #000 inset !important;\n            background:#000 !important;\n            background-color:#000 !important;\n            background-image:none !important;\n            transition:background-color 99999s ease-in-out 0s !important;\n        }\n        #ti-ai-outer .ti-conf-window input:-moz-autofill,\n        #ti-ai-outer .ti-conf-window textarea:-moz-autofill,\n        #ti-ai-outer .ti-cfg-table input:-moz-autofill,\n        #ti-ai-outer .ti-cfg-table textarea:-moz-autofill,\n        #ti-ai-outer .ti-import-map-table input:-moz-autofill,\n        #ti-ai-outer .ti-import-map-table textarea:-moz-autofill {\n            color:#fff !important;\n            background:#000 !important;\n            background-color:#000 !important;\n        }\n        #ti-ai-outer .ti-header-right-controls { display:flex; align-items:center; gap:8px; }\n        #ti-ai-outer #ti-user-area { order:1; }\n        #ti-ai-outer #ti-login-btn, #ti-ai-outer #ti-logout-btn { order:2; }\n        .ti-shop-footer { margin-top:12px; padding:12px 10px 4px; text-align:center; color:#cbd5e1; font-size:12px; line-height:1.45; }\n        .ti-shop-footer a { color:#93c5fd; text-decoration:none; }\n        #ti-ai-outer #lbl-conf-save.ti-save-dirty { background:#10b981 !important; color:#fff !important; box-shadow:0 0 0 2px rgba(16,185,129,.35), 0 0 16px rgba(16,185,129,.25) !important; font-weight:900 !important; }\n        #ti-ai-outer #lbl-conf-save.ti-save-clean { background:#374151 !important; color:#e5e7eb !important; box-shadow:none !important; }\n        #ti-ai-outer .ti-kbd-modal { width:96vw !important; max-width:760px !important; }\n        #ti-ai-outer .ti-kbd { display:flex; flex-direction:column; gap:8px; width:100%; }\n        #ti-ai-outer .ti-kbd-row { display:flex; gap:8px; width:100%; }\n        #ti-ai-outer .ti-kbd-row button { flex:1 1 0; min-width:0; min-height:48px; padding:10px 6px; border-radius:12px; border:1px solid #475569; background:#1f2937; color:#fff; font-size:18px; font-weight:900; cursor:pointer; }\n        #ti-ai-outer .ti-kbd-row button:active { transform:scale(.98); background:#334155; }\n        @media (max-width:640px) { #ti-ai-outer .ti-kbd-row { gap:5px; } #ti-ai-outer .ti-kbd-row button { min-height:44px; font-size:16px; padding:8px 4px; } }\n        #ti-ditta option.ti-ditta-attivo { color:#ffffff !important; }\n        #ti-ditta option.ti-ditta-sospesa { color:#ef4444 !important; }\n        #ti-ditta option.ti-ditta-configurazione { color:#facc15 !important; }\n        #ti-ditta option.ti-ditta-demo { color:#38bdf8 !important; font-weight:700 !important; }\n        .ti-order-confirm-host { display:none; margin:10px 0 12px; padding:12px; background:#0b1220; border:1px solid #223047; border-radius:12px; position:relative; z-index:30; box-shadow:0 8px 28px rgba(0,0,0,0.35); }\n        .ti-order-confirm-title { color:#fff; font-weight:800; font-size:15px; margin-bottom:8px; }\n        .ti-order-confirm-note { color:#94a3b8; font-size:12px; margin-bottom:10px; }\n        .ti-order-confirm-box { background:#000; border:1px solid #374151; border-radius:10px; padding:12px; color:#e5e7eb; white-space:pre-wrap; font-family:monospace; max-height:220px; overflow:auto; }\n        .ti-order-confirm-actions { display:flex; gap:8px; margin-top:10px; flex-wrap:wrap; }\n        .ti-order-confirm-actions .ti-btn { flex:1; min-width:160px; }\n        .ti-order-confirm-meta { display:flex; gap:12px; flex-wrap:wrap; margin-top:8px; color:#cbd5e1; font-size:12px; }\n        .ti-ai-response-host { display:none; margin:10px 0 12px; padding:12px; background:#08111f; border:1px solid #1e3a5f; border-radius:12px; position:relative; z-index:29; box-shadow:0 8px 28px rgba(0,0,0,0.30); }\n        .ti-ai-response-head { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:8px; }\n        .ti-ai-response-title { color:#fff; font-weight:900; font-size:15px; line-height:1.25; }\n        .ti-ai-response-body { color:#e5e7eb; background:#0b1220; border:1px solid rgba(148,163,184,.32); border-radius:10px; padding:10px 12px; font-size:15.5px; line-height:1.55; max-height:260px; overflow:auto; }\n        .ti-ai-response-actions { display:flex; justify-content:flex-start; align-items:center; gap:8px; margin-top:10px; }\n        .ti-ai-response-copy { font-size:11px !important; padding-left:8px !important; padding-right:8px !important; min-height:var(--ti-btn-h); border-radius:8px; white-space:nowrap; }\n        .ti-ai-response-copy:hover { background:#2563eb !important; color:#fff !important; }\n        .ti-ai-response-body .ti-ai-order-final-check-637, .ti-ai-response-body .ti-ai-order-final-check-631 { margin-top:10px; color:#bfdbfe; font-weight:900; }\n        .ti-cfg-summary { display:flex; align-items:center; justify-content:space-between; gap:8px; }\n        .ti-cfg-summary-main { flex:1 1 auto; }\n        .ti-cfg-summary-help { flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; width:22px; height:22px; margin-left:12px; border-radius:999px; border:1px solid #475569; background:#1f2937; color:#fff; font-weight:700; cursor:pointer; }\n        .ti-send-wrap { display:flex; flex-direction:column; gap:6px; align-items:stretch; width:96px; flex:0 0 96px; }\n        .ti-send { height: 38px; width: 100%; border-radius: 10px; background: rgba(56,189,248,0.2); color: #fff; font-weight: 900; cursor: pointer; border: 1px solid rgba(56,189,248,0.4); }\n        .ti-help { height: 24px; width: 100%; border-radius: 8px; background: #374151; color: #aaa; font-weight: bold; cursor: pointer; border: 1px solid #4b5563; font-size: 14px; }\n        .ti-send-wrap .ti-btn { width:100%; min-height:30px; padding:6px 8px; font-size:11px; line-height:1.1; }\n        .ti-actions-row { margin-top: 10px; display: flex; gap: 8px; justify-content: flex-start; align-items:center; flex-wrap: nowrap; overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch; padding-bottom:2px; }\n        .ti-admin-actions-row { margin-top: 6px; display:none; gap:8px; justify-content:flex-start; align-items:center; flex-wrap:nowrap; overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch; padding-bottom:2px; border-top:1px solid rgba(148,163,184,0.16); padding-top:6px; }\n        .ti-admin-actions-row .ti-btn { flex:0 0 auto; min-height:var(--ti-btn-h); }\n        .ti-actions-row #ti-help-btn { margin-left:auto !important; flex:0 0 42px !important; width:42px !important; min-width:42px !important; max-width:42px !important; height:var(--ti-btn-h) !important; min-height:var(--ti-btn-h) !important; padding:8px 0 !important; display:flex !important; align-items:center !important; justify-content:center !important; background:#374151 !important; color:#fff !important; border:1px solid #4b5563 !important; border-radius:8px !important; font-size:16px !important; font-weight:900 !important; line-height:1 !important; }\n        .ti-actions-row #ti-help-btn:hover { background:#4b5563 !important; color:#fff !important; }\n        .ti-btn { padding: 8px 12px; border-radius: 8px; background: #222; color: #ccc; border: 1px solid #444; cursor: pointer; font-weight: 600; font-size: var(--ti-font-base) !important; display: flex; align-items: center; justify-content: center; gap: 5px; line-height:1.1 !important; }\n        .ti-btn-ok { background: #065f46; border-color: #059669; color: #fff; }\n        .ti-btn-x { background: #7f1d1d; border-color: #b91c1c; color: #fff; }\n\n        .ti-booking-nav-actions { margin-top:10px; }\n        .ti-booking-nav-actions .ti-btn { display:inline-flex !important; min-height:34px !important; height:34px !important; padding:6px 12px !important; font-size:13px !important; line-height:1 !important; }\n        @media (max-width: 767px) { .ti-booking-nav-actions .ti-btn { flex:1 1 120px !important; min-width:110px !important; font-size:12px !important; } }\n        html.ti-no-scroll, body.ti-no-scroll { overflow: hidden !important; height: 100% !important; touch-action: auto !important; }\n        #ti-ai-outer .ti-touch-scroll, #ti-ai-outer .ti-modal, #ti-ai-outer .ti-conf-body, #ti-ai-outer .ti-chat-box, #ti-ai-outer .ti-cfg-table-container, #ti-ai-outer .ti-table-wrap { touch-action: pan-y !important; -webkit-overflow-scrolling: touch !important; }\n        \n        #ti-ai-outer { position: relative; z-index: 1; isolation: isolate; }\n        .ti-ov { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; background: rgba(0,0,0,0.85) !important; display: none; align-items: center; justify-content: center; z-index: 2147482000 !important; isolation: isolate !important; }\n        #ti-alert-ov { z-index: 2147483647 !important; }\n        #ti-ai-loader-ov { z-index: 2147483646 !important; }\n        .ti-modal { width: min(92vw, 820px); max-width: min(92vw, 820px); background: #1f2937; border-radius: 16px; padding: 25px; border: 1px solid #374151; text-align: center; position: relative; display: flex; flex-direction: column; pointer-events: auto; max-height: 90vh; overflow-y: auto; -webkit-overflow-scrolling: touch; z-index: 2; }\n        .ti-modal-x-red { position:absolute !important; top:10px !important; right:10px !important; width:32px !important; height:32px !important; min-width:32px !important; min-height:32px !important; border-radius:50% !important; border:2px solid #fecaca !important; background:#dc2626 !important; color:#fff !important; display:flex !important; align-items:center !important; justify-content:center !important; font-size:18px !important; line-height:1 !important; font-weight:900 !important; cursor:pointer !important; z-index:2147483647 !important; padding:0 !important; box-shadow:0 4px 14px rgba(0,0,0,.65) !important; }\n        .ti-modal-x-red:hover { background:#b91c1c !important; transform:scale(1.04); }\n        .ti-modal-bottom-close-row { display:flex !important; justify-content:center !important; align-items:center !important; margin-top:16px !important; width:100% !important; }\n        .ti-modal-bottom-close-row .ti-btn { min-width:160px !important; max-width:260px !important; background:#4b5563 !important; color:#fff !important; }\n        .ti-conf-window > .ti-modal-x-red { top:12px !important; right:12px !important; z-index:100001 !important; }\n        #ti-login-ov .ti-modal { width: min(38vw, 320px) !important; max-width: min(38vw, 320px) !important; padding: 22px !important; }\n\n        #ti-login-ov #l-db { width:100% !important; box-sizing:border-box !important; margin-top:8px !important; background:#111827 !important; color:#fff !important; border:1px solid #374151 !important; border-radius:8px !important; min-height:42px !important; }\n        @media (pointer: coarse), (max-width: 900px) {\n            #ti-login-ov { pointer-events:auto !important; touch-action:pan-y !important; }\n            #ti-login-ov .ti-modal { transform:none !important; pointer-events:auto !important; touch-action:pan-y !important; }\n            #ti-login-ov #l-db { display:block; font-size:16px !important; min-height:44px !important; }\n            #ti-login-ov #l-do { pointer-events:auto !important; touch-action:manipulation !important; }\n        }\n        #ti-login-ov #ti-login-form { width: 100%; margin: 0 auto; }\n        #ti-login-ov #ti-login-form .ti-in { margin: 8px 0; }\n        .ti-conf-window { z-index: 2 !important; }\n        #ti-plugin-modal-root { position: fixed !important; inset: 0 !important; z-index: 2147483000 !important; pointer-events: none !important; isolation:isolate !important; }\n        #ti-plugin-modal-root > .ti-ov, #ti-plugin-modal-root > #ti-ai-loader-ov { pointer-events: auto !important; }\n        .ti-plugin-scroll-lock { overscroll-behavior: contain; }\n        #ti-ai-outer, #ti-ai-outer * { box-sizing: border-box; }\n        #ti-ai-outer .ti-scrollable-plugin { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }\n        #ti-profile-form button[type=\"submit\"] { position: relative; z-index: 2; pointer-events: auto; }\n        .ti-profile-actions .ti-btn { flex:1; min-height:42px; height:42px; padding:10px 12px; display:flex; align-items:center; justify-content:center; font-size:15px; font-weight:bold; line-height:1.2; text-transform:none; }\n\n        \n        .ti-conf-window { position: fixed !important; top: 3vh !important; bottom: 3vh !important; left: 2vw !important; right: 2vw !important; width: auto !important; max-width: 800px !important; margin: auto !important; background: #1f2937 !important; border-radius: 12px !important; border: 2px solid #4b5563 !important; overflow: hidden !important; pointer-events: auto !important; box-shadow: 0 10px 40px rgba(0,0,0,0.9) !important; }\n        .ti-conf-header { position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; height: 60px !important; padding: 0 15px !important; background: #111827 !important; border-bottom: 1px solid #444 !important; display: flex !important; justify-content: space-between !important; align-items: center !important; z-index: 10 !important; }\n        .ti-conf-body { position: absolute !important; top: 60px !important; bottom: 70px !important; left: 0 !important; right: 0 !important; overflow-y: auto !important; overflow-x: hidden !important; padding: 15px !important; background: #1f2937 !important; -webkit-overflow-scrolling: touch !important; overscroll-behavior: contain !important; z-index: 1 !important; }\n        .ti-conf-footer { position: absolute !important; bottom: 0 !important; left: 0 !important; right: 0 !important; height: 70px !important; padding: 15px !important; background: #111827 !important; border-top: 1px solid #444 !important; display: flex !important; gap: 10px !important; z-index: 99999 !important; pointer-events: auto !important; }\n        \/* 30.9.664 - Pulsanti compatti nel piede della main form e della configurazione *\/\n        #ti-ai-outer .ti-actions-row,\n        #ti-ai-outer .ti-admin-actions-row { gap:5px !important; padding-bottom:1px !important; }\n        #ti-ai-outer .ti-actions-row .ti-btn,\n        #ti-ai-outer .ti-actions-row .ti-help,\n        #ti-ai-outer .ti-admin-actions-row .ti-btn { min-height:30px !important; height:30px !important; padding:5px 8px !important; font-size:11px !important; line-height:1 !important; border-radius:7px !important; white-space:nowrap !important; flex:0 0 auto !important; }\n        #ti-ai-outer .ti-actions-row #ti-help-btn { flex:0 0 34px !important; width:34px !important; min-width:34px !important; max-width:34px !important; height:30px !important; min-height:30px !important; padding:0 !important; font-size:14px !important; border-radius:7px !important; }\n        #ti-conf-ov .ti-conf-body { bottom:52px !important; }\n        #ti-conf-ov .ti-conf-footer { height:52px !important; padding:8px 10px !important; gap:6px !important; align-items:center !important; }\n        #ti-conf-ov .ti-conf-footer .ti-btn { min-height:34px !important; height:34px !important; padding:6px 10px !important; font-size:12px !important; line-height:1 !important; border-radius:7px !important; }\n        @media (max-width: 767px) {\n            #ti-ai-outer .ti-actions-row .ti-btn,\n            #ti-ai-outer .ti-actions-row .ti-help,\n            #ti-ai-outer .ti-admin-actions-row .ti-btn { min-height:28px !important; height:28px !important; padding:4px 6px !important; font-size:10.5px !important; }\n            #ti-ai-outer .ti-actions-row #ti-help-btn { width:30px !important; min-width:30px !important; max-width:30px !important; height:28px !important; min-height:28px !important; }\n            #ti-ai-outer .ti-conf-window .ti-cfg-sort-ind { width:18px !important; min-width:18px !important; height:18px !important; min-height:18px !important; font-size:10.5px !important; }\n            #ti-conf-ov .ti-conf-footer { height:48px !important; padding:7px 8px !important; }\n            #ti-conf-ov .ti-conf-body { bottom:48px !important; }\n            #ti-conf-ov .ti-conf-footer .ti-btn { min-height:32px !important; height:32px !important; padding:5px 8px !important; font-size:11px !important; }\n        }\n        \n        .ti-in { width: 100%; padding: 10px; margin: 10px 0; background: #111827; border: 1px solid #4b5563; color: #fff; border-radius: 8px; }\n        #ti-ai-outer input[type=\"text\"], #ti-ai-outer input[type=\"search\"], #ti-ai-outer input[type=\"email\"], #ti-ai-outer input[type=\"password\"], #ti-ai-outer input[type=\"number\"], #ti-ai-outer input[type=\"date\"], #ti-ai-outer input[type=\"time\"], #ti-ai-outer input[type=\"url\"], #ti-ai-outer input[type=\"tel\"], #ti-ai-outer textarea, #ti-ai-outer select, #ti-ai-outer option,\n        #ti-ai-outer .ti-in, #ti-ai-outer .ti-msg, #ti-ai-outer .ti-filter-input, #ti-ai-outer .ti-cfg-filter, #ti-ai-outer .ti-import-map-select, #ti-ai-outer .ti-import-map-input, #ti-ai-outer .ti-p-input, #ti-ai-outer .ti-q, #ti-ai-outer [contenteditable=\"true\"] {\n            color:#fff !important; -webkit-text-fill-color:#fff !important; caret-color:#fff !important; text-shadow:none !important;\n        }\n        #ti-ai-outer input:not([type=\"checkbox\"]):not([type=\"radio\"]), #ti-ai-outer textarea, #ti-ai-outer select, #ti-ai-outer .ti-in, #ti-ai-outer .ti-msg, #ti-ai-outer .ti-filter-input, #ti-ai-outer .ti-cfg-filter, #ti-ai-outer .ti-import-map-select, #ti-ai-outer .ti-import-map-input, #ti-ai-outer .ti-p-input, #ti-ai-outer .ti-q {\n            background-color:#111827 !important;\n        }\n        #ti-ai-outer input::placeholder, #ti-ai-outer textarea::placeholder, #ti-ai-outer select::placeholder, #ti-ai-outer .ti-in::placeholder, #ti-ai-outer .ti-msg::placeholder, #ti-ai-outer .ti-filter-input::placeholder, #ti-ai-outer .ti-cfg-filter::placeholder, #ti-ai-outer .ti-import-map-input::placeholder, #ti-ai-outer .ti-p-input::placeholder, #ti-ai-outer .ti-q::placeholder {\n            color:#d1d5db !important; opacity:1 !important; -webkit-text-fill-color:#d1d5db !important;\n        }\n        #ti-ai-outer input:focus, #ti-ai-outer textarea:focus, #ti-ai-outer select:focus, #ti-ai-outer .ti-in:focus, #ti-ai-outer .ti-msg:focus {\n            color:#fff !important; -webkit-text-fill-color:#fff !important;\n        }\n        #ti-filter-ditte { width: 35px; min-width: 240px; padding: 6px; border-radius: 8px; background: #000; border: 1px solid #444; color: #fff; text-align: center; margin-right: 5px; transition: width 0.3s; }\n        #ti-filter-ditte:focus { width: 140px; text-align: left; }\n        #ti-sort-btn { width: 35px; padding: 6px; border-radius: 8px; background: #222; border: 1px solid #444; color: #aaa; cursor: pointer; margin-right: 5px; text-align: center; font-size: 12px; font-weight: bold; }\n        .ti-table-wrap { max-height: 450px; overflow-y: auto; overflow-x: auto; border: 1px solid #444; border-radius: 6px; margin-top: 10px; background: #111; -webkit-overflow-scrolling: touch !important; }\n        .ti-prod-table { min-width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; text-align: left; }\n        .ti-prod-table th { background: #1f2937; padding: 8px 4px; border-bottom: 1px solid #555; font-size: 11px; position: sticky; top: 0; z-index: 20; box-shadow: 0 1px 0 #555; text-transform: uppercase; color: #aaa; cursor: pointer; }\n        .ti-prod-table td { padding: 4px; border-bottom: 1px solid #222; vertical-align: middle; line-height: 1.2; font-size: 12px; background: #000; }\n        .ti-filter-input { width: 100%; box-sizing: border-box; margin-top: 4px; padding: 2px; background: #000; border: 1px solid #444; color: #fff; font-size: 10px; border-radius: 3px; }\n        .ti-order-filterbar { display:flex; gap:8px; flex-wrap:nowrap; align-items:center; margin:8px 0 10px 0; overflow-x:auto; overflow-y:hidden; white-space:nowrap; -webkit-overflow-scrolling:touch; padding-bottom:2px; }\n        .ti-order-filtercell { display:flex; align-items:center; gap:4px; flex:0 0 auto; white-space:nowrap; min-height:32px; }\n        .ti-order-filterlabel { font-size:12px; color:#cbd5e1; min-width:auto; line-height:1; display:inline-flex; align-items:center; white-space:nowrap; margin:0; padding:0; }\n        .ti-order-filterbar .ti-filter-input, .ti-order-filterbar .ti-in { margin:0; font-size:12px; min-height:30px; line-height:1.1; vertical-align:middle; }\n        .ti-order-filterbar .ti-filter-input { width:126px; padding:5px 7px; }\n        .ti-order-filterbar .ti-in { width:150px; padding:5px 7px; }\n        .ti-order-filterbar::-webkit-scrollbar { height:7px; }\n        .ti-cfg-top-scroll, .ti-cfg-bottom-scroll, .ti-order-top-scroll, .ti-order-bottom-scroll { height:12px; overflow-x:auto; overflow-y:hidden; background:#000; border:1px solid #444; -webkit-overflow-scrolling:touch; }\n        .ti-cfg-top-scroll, .ti-order-top-scroll { position:sticky; top:0; z-index:4; border-bottom:none; border-radius:8px 8px 0 0; }\n        .ti-cfg-bottom-scroll, .ti-order-bottom-scroll { position:sticky; bottom:0; z-index:8; border-top:none; border-radius:0 0 8px 8px; margin-top:0; display:block; box-shadow:0 -1px 0 #111827, 0 -4px 10px rgba(0,0,0,0.35); }\n        .ti-order-top-scroll, .ti-order-bottom-scroll { position:relative; }\n        .ti-order-top-scroll { margin-top:10px; }\n        .ti-cfg-top-scroll-inner, .ti-sync-hscroll-inner { height:1px; }\n        .ti-cfg-top-scroll::-webkit-scrollbar, .ti-cfg-bottom-scroll::-webkit-scrollbar, .ti-order-top-scroll::-webkit-scrollbar, .ti-order-bottom-scroll::-webkit-scrollbar { height:8px; }\n        .ti-cfg-top-scroll, .ti-cfg-bottom-scroll, .ti-import-top-scroll, .ti-import-bottom-scroll, .ti-order-top-scroll, .ti-order-bottom-scroll { background:#d1d5db; border:1px solid #9ca3af; scrollbar-color:#9ca3af #e5e7eb; scrollbar-width:auto; }\n        .ti-cfg-top-scroll::-webkit-scrollbar, .ti-cfg-bottom-scroll::-webkit-scrollbar, .ti-import-top-scroll::-webkit-scrollbar, .ti-import-bottom-scroll::-webkit-scrollbar, .ti-order-top-scroll::-webkit-scrollbar, .ti-order-bottom-scroll::-webkit-scrollbar { height:12px; background:#e5e7eb; }\n        .ti-cfg-top-scroll::-webkit-scrollbar-track, .ti-cfg-bottom-scroll::-webkit-scrollbar-track, .ti-import-top-scroll::-webkit-scrollbar-track, .ti-import-bottom-scroll::-webkit-scrollbar-track, .ti-order-top-scroll::-webkit-scrollbar-track, .ti-order-bottom-scroll::-webkit-scrollbar-track { background:#e5e7eb; border-radius:8px; }\n        .ti-cfg-top-scroll::-webkit-scrollbar-thumb, .ti-cfg-bottom-scroll::-webkit-scrollbar-thumb, .ti-import-top-scroll::-webkit-scrollbar-thumb, .ti-import-bottom-scroll::-webkit-scrollbar-thumb, .ti-order-top-scroll::-webkit-scrollbar-thumb, .ti-order-bottom-scroll::-webkit-scrollbar-thumb { background:#9ca3af; border-radius:8px; border:2px solid #e5e7eb; }\n        .ti-cfg-top-scroll::-webkit-scrollbar-thumb:hover, .ti-cfg-bottom-scroll::-webkit-scrollbar-thumb:hover, .ti-import-top-scroll::-webkit-scrollbar-thumb:hover, .ti-import-bottom-scroll::-webkit-scrollbar-thumb:hover, .ti-order-top-scroll::-webkit-scrollbar-thumb:hover, .ti-order-bottom-scroll::-webkit-scrollbar-thumb:hover { background:#6b7280; }\n        .ti-ai-image-generator textarea, .ti-ai-image-generator input[type=\"text\"], .ti-imggen-wrap textarea, .ti-imggen-wrap input[type=\"text\"], #ai-gen-prompt { color:#fff !important; -webkit-text-fill-color:#fff !important; caret-color:#fff !important; background:#111827 !important; }\n        #ti-conf-ai-prompt, #ti-shared-base-path, #ti-shared-base-url, #help-email input, #help-email textarea, .ti-help-input, .ti-help-textarea { color:#fff !important; -webkit-text-fill-color:#fff !important; caret-color:#fff !important; background:#111827 !important; }\n        #ti-conf-ai-prompt::placeholder, #ti-shared-base-path::placeholder, #ti-shared-base-url::placeholder, #help-email input::placeholder, #help-email textarea::placeholder, .ti-help-input::placeholder, .ti-help-textarea::placeholder { color:#d1d5db !important; opacity:1 !important; }\n        .ti-conf-ai-desc-label { font-size:15px !important; line-height:1.45 !important; color:#cbd5e1 !important; font-weight:600 !important; }\n        .ti-shop-service-inline-icon, .ti-shop-service-logo-icon { display:inline-block; width:20px; height:20px; object-fit:cover; border-radius:6px; vertical-align:middle; box-shadow:0 0 0 1px rgba(255,255,255,.08); }\n        .ti-with-shop-service-icon{ display:inline-flex !important; align-items:center; gap:8px; }\n        .ti-ai-image-generator textarea::placeholder, .ti-ai-image-generator input[type=\"text\"]::placeholder, .ti-imggen-wrap textarea::placeholder, .ti-imggen-wrap input[type=\"text\"]::placeholder, #ai-gen-prompt::placeholder { color:#d1d5db !important; opacity:1 !important; }\n        .ti-ai-image-generator textarea:focus, .ti-ai-image-generator input[type=\"text\"]:focus, .ti-imggen-wrap textarea:focus, .ti-imggen-wrap input[type=\"text\"]:focus, #ai-gen-prompt:focus { color:#fff !important; -webkit-text-fill-color:#fff !important; outline:none !important; border-color:#9ca3af !important; box-shadow:0 0 0 1px #9ca3af inset !important; }\n        .ti-col-img { width: 50px; text-align: center; white-space: nowrap; } \n        .ti-col-disp { width: 80px; text-align: right; } \n        .ti-col-eur { width: 80px; white-space: nowrap; text-align: right; } \n        .ti-col-qta { width: 70px; text-align: right; } \n        .ti-col-tot { width: 80px; text-align: right; font-weight: bold; color: #38bdf8; }\n        .ti-row-img { width: 32px; height: 32px; object-fit: cover; border-radius: 4px; cursor: pointer; display: inline-block; vertical-align: middle; }\n        .ti-cfg-head-label { display:flex; align-items:center; justify-content:space-between; gap:6px; font-size:11px; }\n        .ti-cfg-head-actions { display:inline-flex; align-items:center; justify-content:flex-end; gap:4px; flex:0 0 auto; }\n        .ti-cfg-sort-ind { display:inline-flex; align-items:center; justify-content:center; width:18px; min-width:18px; height:18px; min-height:18px; margin-left:4px; border-radius:999px; background:#1e3a8a; color:#dbeafe; font-size:11px; font-weight:800; line-height:1; vertical-align:middle; box-shadow:0 0 0 1px rgba(147,197,253,.45) inset; }\n        .ti-cfg-filter { width:100%; box-sizing:border-box; margin-top:5px; padding:3px 5px; background:#000; border:1px solid #444; color:#fff; font-size:10px; border-radius:4px; }\n        .ti-cfg-filter:focus { outline:1px solid #38bdf8; border-color:#38bdf8; background:#020617; }\n        #ti-ai-outer .ti-conf-window .ti-cfg-filter,\n        #ti-ai-outer .ti-conf-window .ti-cfg-table input:not([data-ti-activation-protected=\"1\"]),\n        #ti-ai-outer .ti-conf-window .ti-cfg-table textarea:not([data-ti-activation-protected=\"1\"]),\n        #ti-ai-outer .ti-conf-window .ti-cfg-table select:not([data-ti-activation-protected=\"1\"]) { pointer-events:auto !important; user-select:text !important; -webkit-user-select:text !important; }\n        #ti-ai-outer .ti-conf-window .ti-cfg-table input:not([data-ti-activation-protected=\"1\"]):not([readonly]):not([disabled]),\n        #ti-ai-outer .ti-conf-window .ti-cfg-table textarea:not([data-ti-activation-protected=\"1\"]):not([readonly]):not([disabled]),\n        #ti-ai-outer .ti-conf-window .ti-cfg-filter:not([readonly]):not([disabled]),\n        #ti-ai-outer .ti-conf-window .ti-config-fast-table-507 input:not([readonly]):not([disabled]),\n        #ti-ai-outer .ti-conf-window .ti-config-fast-table-507 textarea:not([readonly]):not([disabled]),\n        #ti-ai-outer .ti-conf-window .ti-config-fast-table-507 select:not([disabled]) { pointer-events:auto !important; user-select:text !important; -webkit-user-select:text !important; opacity:1 !important; cursor:text !important; }\n        .ti-cfg-help-btn, .ti-cfg-pin-btn { display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:999px; color:#fff; font-size:11px; cursor:pointer; border:none; padding:0; line-height:1; }\n        .ti-cfg-help-btn { background:#1d4ed8; }\n        .ti-cfg-help-btn:hover { background:#2563eb; }\n        .ti-cfg-pin-btn { background:#374151; opacity:.78; }\n        .ti-cfg-pin-btn:hover { opacity:1; background:#4b5563; }\n        .ti-cfg-pin-btn.is-active { background:#f59e0b; color:#111827; opacity:1; box-shadow:0 0 0 1px #fde68a inset; }\n        .ti-import-map-table { width:max-content; min-width:1400px; border-collapse:collapse; font-size:12px; }\n        .ti-import-map-table th, .ti-import-map-table td { border:1px solid #475569; padding:6px; vertical-align:top; }\n        .ti-import-map-table th { background:#0f172a; position:sticky; top:0; z-index:3; white-space:nowrap; }\n        .ti-import-map-select, .ti-import-map-input { width:100%; min-width:160px; box-sizing:border-box; background:#111827; color:#fff; border:1px solid #475569; border-radius:6px; padding:5px 6px; font-size:12px; }\n        .ti-import-table-container { max-width:100%; overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch; }\n        .ti-import-top-scroll, .ti-import-bottom-scroll { margin-top:6px; display:block; }\n        .ti-import-bottom-scroll, .ti-order-bottom-scroll, .ti-cfg-bottom-scroll { background:#000; }\n        .ti-import-top-scroll .ti-cfg-top-scroll-inner, .ti-import-bottom-scroll .ti-cfg-top-scroll-inner { min-width:1400px; }\n        .ti-order-modal-grid { display:grid; grid-template-columns:minmax(180px,1fr) minmax(220px,1fr); gap:14px; align-items:start; text-align:left; }\n        .ti-order-modal-img { width:100%; max-height:340px; object-fit:contain; border-radius:10px; background:#000; border:1px solid #374151; }\n        .ti-order-modal-caption { font-size:14px; color:#d1d5db; line-height:1.45; margin-bottom:10px; }\n        .ti-order-modal-line { margin:8px 0; font-size:14px; color:#fff; }\n        .ti-order-modal-actions { display:flex; gap:10px; margin-top:14px; }\n        .ti-order-modal-files { margin-top:12px; padding-top:10px; border-top:1px solid #334155; }\n        .ti-order-modal-files-title { font-size:12px; color:#93c5fd; font-weight:800; margin-bottom:8px; }\n        .ti-order-modal-files-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(92px, 1fr)); gap:8px; }\n        .ti-order-file-preview { border:1px solid #334155; background:#020617; border-radius:10px; padding:7px; color:#e5e7eb; cursor:pointer; min-height:96px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px; text-align:center; }\n        .ti-order-file-preview:hover { border-color:#38bdf8; background:#0f172a; }\n        .ti-order-file-preview img { width:100%; height:62px; object-fit:cover; border-radius:7px; background:#000; border:1px solid #1f2937; }\n        .ti-order-file-preview iframe { width:100%; height:62px; border:1px solid #1f2937; border-radius:7px; background:#000; pointer-events:none; }\n        .ti-order-file-preview-icon { width:100%; height:62px; display:flex; align-items:center; justify-content:center; border-radius:7px; background:#111827; font-size:26px; border:1px solid #1f2937; }\n        .ti-order-file-preview-name { width:100%; font-size:10px; color:#cbd5e1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n        .ti-order-file-preview-open { font-size:10px; color:#38bdf8; font-weight:700; }\n        @media (max-width: 768px) { \n            #ti-login-ov .ti-modal { width: min(92vw, 340px) !important; max-width: min(92vw, 340px) !important; padding: 20px !important; }.ti-order-modal-grid { grid-template-columns:1fr; } }\n        @media (max-width: 760px) {\n            html:not(.ti-no-scroll), body:not(.ti-no-scroll) { overflow-y:auto !important; height:auto !important; min-height:100% !important; touch-action:pan-y !important; }\n            #ti-ai-outer { width:100% !important; max-width:100% !important; min-height:auto !important; height:auto !important; overflow-y:visible !important; touch-action:pan-y !important; overscroll-behavior-y:auto !important; }\n            #ti-ai-outer .ti-header, #ti-ai-outer .ti-chat-box, #ti-ai-outer .ti-input-wrap, #ti-ai-outer .ti-actions-row, #ti-ai-outer .ti-admin-actions-row { touch-action:pan-y !important; }\n            .ti-ov { align-items:flex-start !important; justify-content:center !important; overflow-y:auto !important; overflow-x:hidden !important; padding: max(10px, env(safe-area-inset-top)) 8px max(10px, env(safe-area-inset-bottom)) 8px !important; touch-action:pan-y !important; -webkit-overflow-scrolling:touch !important; }\n            .ti-modal { max-height: calc((var(--ti-vh, 1vh) * 92)) !important; overflow-y:auto !important; touch-action:pan-y !important; -webkit-overflow-scrolling:touch !important; }\n            #ti-login-ov .ti-modal { width:min(94vw, 380px) !important; max-width:min(94vw, 380px) !important; max-height:calc((var(--ti-vh, 1vh) * 88)) !important; }\n            #ti-login-ov .ti-in, #ti-login-ov .ti-btn, #ti-ai-outer input, #ti-ai-outer textarea, #ti-ai-outer select { font-size:16px !important; }\n            #ti-login-ov .ti-in, #ti-login-ov .ti-btn { min-height:44px !important; }\n            .ti-conf-window { top: max(8px, env(safe-area-inset-top)) !important; bottom: max(8px, env(safe-area-inset-bottom)) !important; left: 1vw !important; right: 1vw !important; max-width:98vw !important; }\n            .ti-conf-body { overflow-y:auto !important; overflow-x:auto !important; touch-action:pan-y !important; -webkit-overflow-scrolling:touch !important; }\n\n            #ti-ai-outer .ti-actions-row {\n                display:grid !important;\n                grid-template-columns:repeat(5, minmax(0, 1fr)) !important;\n                gap:4px !important;\n                overflow:visible !important;\n                padding-bottom:0 !important;\n                align-items:stretch !important;\n            }\n            #ti-ai-outer .ti-actions-row .ti-btn,\n            #ti-ai-outer .ti-actions-row .ti-help {\n                width:100% !important;\n                min-width:0 !important;\n                max-width:none !important;\n                height:30px !important;\n                min-height:30px !important;\n                padding:3px 3px !important;\n                border-radius:7px !important;\n                font-size:10px !important;\n                line-height:1.05 !important;\n                white-space:nowrap !important;\n                overflow:hidden !important;\n                text-overflow:ellipsis !important;\n                display:flex !important;\n                align-items:center !important;\n                justify-content:center !important;\n                gap:2px !important;\n            }\n            #ti-ai-outer .ti-actions-row #ti-help-btn {\n                margin-left:0 !important;\n                flex:1 1 auto !important;\n                width:100% !important;\n                min-width:0 !important;\n                max-width:none !important;\n                font-size:13px !important;\n                padding:3px 0 !important;\n            }\n            #ti-ai-outer .ti-admin-actions-row {\n                display:grid !important;\n                grid-template-columns:repeat(2, minmax(0, 1fr)) !important;\n                gap:5px !important;\n                overflow:visible !important;\n                padding-bottom:0 !important;\n            }\n            #ti-ai-outer .ti-admin-actions-row[style*=\"display: none\"] {\n                display:none !important;\n            }\n            #ti-ai-outer .ti-admin-actions-row .ti-btn {\n                width:100% !important;\n                min-width:0 !important;\n                min-height:31px !important;\n                height:31px !important;\n                padding:4px 5px !important;\n                font-size:10.5px !important;\n                border-radius:7px !important;\n                white-space:nowrap !important;\n                overflow:hidden !important;\n                text-overflow:ellipsis !important;\n            }\n        }\n        .ti-p-input { width: 100%; box-sizing: border-box; padding: 4px; background: #222; border: 1px solid #444; color: #fff; border-radius: 3px; font-size: 12px; font-family: inherit; height: 24px; line-height: 16px; }\n        .ti-p-input:focus { border-color: #38bdf8; outline: none; }\n        .ti-num-step { text-align: right; }\n        .ti-qty-stepper { display:inline-flex; align-items:center; gap:4px; width:100%; }\n        .ti-qty-stepper .ti-q, .ti-qty-stepper .ti-num-step, .ti-qty-stepper #ti-order-item-qty { flex:1 1 auto; min-width:0; margin:0; }\n        .ti-qty-btn { width:26px; min-width:26px; height:26px; border:1px solid #475569; background:#0f172a; color:#fff; border-radius:6px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; font-weight:700; line-height:1; padding:0; }\n        .ti-qty-btn:hover { background:#1e293b; }\n        .ti-order-card-line .ti-qty-stepper { max-width:170px; }\n        .ti-order-modal-line .ti-qty-stepper { max-width:220px; display:inline-flex; vertical-align:middle; margin-left:8px; }\n        .ti-cfg-summary { font-weight: bold; color: #38bdf8; cursor: pointer; padding: 10px; background: #111827; border-radius: 5px; display: block; border: 1px solid #333; }\n        .ti-cfg-table-container { max-height: 60vh; overflow: auto; position: relative; background: #000; border: 1px solid #444; border-top: none; border-radius: 0 0 8px 8px; -webkit-overflow-scrolling: touch; }\n        .ti-cfg-table { width: max-content; min-width: 100%; border-collapse: separate; border-spacing: 0; }\n        .ti-cfg-table th { background: #1f2937; padding: 8px 10px; font-size: 11px; color: #ccc; border-bottom: 1px solid #444; border-right: 1px solid #444; position: sticky; top: 0; z-index: 20; text-transform: uppercase; white-space: nowrap; }\n        .ti-cfg-table th.ti-sticky-col { left: 58px; z-index: 34; background: #2563eb; color: #fff; box-shadow: 2px 0 0 #444; }\n        .ti-cfg-table th.ti-cfg-rownum, .ti-cfg-table td.ti-cfg-rownum { position: sticky; left: 0; width: 58px; min-width: 58px; max-width: 58px; text-align: center; font-weight: 700; box-shadow: 1px 0 0 #444; }\n        .ti-cfg-table th.ti-cfg-rownum { top: 0; z-index: 35; background: #0f172a; color: #bfdbfe; }\n        .ti-cfg-table td.ti-cfg-rownum { z-index: 12; background: #111827; color: #93c5fd; }\n        .ti-cfg-table td.ti-cfg-rownum.ti-instr-expired { color:#fecaca !important; background:#7f1d1d !important; border-color:#ef4444 !important; }\n        .ti-cfg-table td.ti-cfg-rownum.ti-instr-future { color:#fde68a !important; background:#78350f !important; border-color:#f59e0b !important; }\n        .ti-cfg-table td { padding: 5px; border-bottom: 1px solid #333; border-right: 1px solid #333; background: #000; vertical-align: top; }\n        .ti-cfg-table td.ti-sticky-col { position: sticky; left: 58px; z-index: 11; background: #111827; box-shadow: 2px 0 0 #333; }\n        .ti-cfg-table .ti-in { background: transparent; border: none; font-size: 12px; margin: 0; padding: 4px; color: #fff; width: 100%; box-sizing: border-box; }\n        .ti-cfg-table .ti-in:focus { background: rgba(56, 189, 248, 0.1); outline: 1px solid #38bdf8; }\n        .ti-profile-btn-eq { min-height:46px !important; height:46px !important; display:flex !important; align-items:center !important; justify-content:center !important; box-sizing:border-box !important; padding:0 12px !important; line-height:1 !important; font-size:15px !important; font-weight:700 !important; white-space:nowrap !important; border-radius:10px !important; }\n        .ti-profile-actions { display:grid !important; grid-template-columns:1fr 1fr 1fr !important; gap:8px !important; align-items:stretch !important; justify-content:stretch !important; }\n        .ti-profile-actions .ti-btn { min-height:46px !important; height:46px !important; display:flex !important; align-items:center !important; justify-content:center !important; box-sizing:border-box !important; margin:0 !important; }\n        .ti-eye-btn { font-size: 16px; cursor: pointer; margin-left: 5px; filter: grayscale(1); transition: 0.2s; }\n        .ti-eye-btn:hover { filter: grayscale(0); transform: scale(1.2); }\n\n        .ti-report-toolbar { display:flex; align-items:center; gap:8px; flex-wrap:nowrap; overflow-x:auto; padding-bottom:8px; margin-bottom:10px; }\n        .ti-report-toolbar .ti-btn { white-space:nowrap; flex:0 0 auto; }\n        .ti-report-hidden-cols { display:flex; gap:6px; flex-wrap:wrap; margin:0 0 10px 0; }\n        .ti-report-col-chip { display:inline-flex; align-items:center; gap:6px; background:#111827; border:1px solid #374151; color:#e5e7eb; border-radius:999px; padding:4px 9px; font-size:11px; }\n        .ti-report-config-filter { display:inline-flex; align-items:center; gap:6px; background:#0f172a; border:1px solid #334155; color:#e5e7eb; border-radius:999px; padding:5px 10px; font-size:11px; white-space:nowrap; flex:0 0 auto; }\n        .ti-report-config-filter input { margin:0; accent-color:#38bdf8; }\n        .ti-report-table-wrap { max-height:58vh; overflow:auto; border:1px solid #444; border-radius:8px; background:#000; }\n        .ti-report-table { width:max-content; min-width:100%; border-collapse:separate; border-spacing:0; }\n        .ti-report-table th { position:sticky; top:0; background:#111827; color:#fff; z-index:3; min-width:120px; padding:8px 6px; border-bottom:1px solid #444; border-right:1px solid #333; cursor:pointer; font-size:11px; text-transform:uppercase; }\n        .ti-report-table td { padding:6px; border-bottom:1px solid #222; border-right:1px solid #222; background:#000; color:#e5e7eb; font-size:12px; vertical-align:top; }\n        .ti-report-table tr.ti-report-row-excluded td { opacity:0.45; background:#140c0c; text-decoration:line-through; }\n        .ti-report-table tfoot td { background:#0f172a; color:#38bdf8; font-weight:bold; position:sticky; bottom:0; z-index:2; border-top:1px solid #444; }\n        .ti-report-filter { width:100%; margin-top:6px; padding:4px 5px; border-radius:4px; border:1px solid #444; background:#000; color:#fff; font-size:10px; }\n        .ti-report-num { text-align:right; white-space:nowrap; }\n        .ti-report-flag-btn { display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:50%; border:1px solid #7f1d1d; background:#b91c1c; color:#fff; font-size:11px; font-weight:bold; cursor:pointer; margin-right:6px; }\n        .ti-report-flag-btn.is-off { background:#111827; color:#f87171; border-color:#ef4444; }\n        .ti-report-row-flag-cell { position:sticky; left:0; z-index:2; background:#000 !important; min-width:44px; text-align:center; }\n        .ti-report-row-flag-cell.head { z-index:4; background:#1f2937 !important; }\n        .ti-chart-wrap { background:#000; border:1px solid #444; border-radius:8px; padding:12px; overflow:auto; }\n        \n        #ti-ai-loader-ov { position:fixed !important; inset:0 !important; background:rgba(0,0,0,0.7); display:none; align-items:center; justify-content:center; z-index:2147483646 !important; border-radius:14px; isolation:isolate !important; pointer-events:auto !important; }\n        .ti-loader-box { background:#1f2937; border:2px solid #38bdf8; padding:20px 30px; border-radius:12px; text-align:center; box-shadow:0 10px 25px rgba(0,0,0,0.5); }\n        .ti-loader-spinner { font-size:30px; margin-bottom:10px; animation: ti-spin 1.5s linear infinite; display:inline-block; }\n        @keyframes ti-spin { 100% { transform: rotate(360deg); } }\n\n        \/* GRID PROFILO UTENTE E FILE MANAGER *\/\n        .ti-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; text-align: left; }\n        .ti-grid-2 label { font-size:10px; color:#aaa; display:block; margin-bottom:2px;}\n        .ti-grid-2 .ti-in { margin:0; padding:6px; font-size:12px; }\n        .ti-cb-wrap { display: flex; align-items: center; gap: 5px; font-size:11px; color:#ccc;}\n        .ti-file-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 10px; margin-top: 15px; max-height: 300px; overflow-y: auto; }\n        .ti-file-item { position: relative; background: #222; border: 1px solid #444; border-radius: 6px; padding: 5px; text-align: center; }\n        .ti-file-item img { width: 100%; height: 60px; object-fit: cover; border-radius: 4px; }\n        .ti-file-item .file-icon { font-size: 30px; line-height: 60px; color: #888; }\n        .ti-file-name { font-size: 9px; color: #ccc; margin-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n        .ti-file-del { position: absolute; top: -5px; right: -5px; background: #ef4444; color: white; border: none; border-radius: 50%; width: 20px; height: 20px; font-size: 10px; cursor: pointer; }\n        .ti-btn-catalog { background:#1d4ed8; color:#fff; border-color:#1e40af; }\n        .ti-btn-catalog.ti-btn-services { background:#047857; border-color:#065f46; }\n        #ti-ai-outer #ti-activities-btn.ti-btn-activities { background:#7c3aed; border-color:#5b21b6; color:#fff; font-weight:800; }\n        #ti-ditta { height: 34px; min-height: 34px; width: 400px; max-width: 400px; min-width: 400px; box-sizing: border-box; }\n        #ti-ditta option { background:#000 !important; }\n        #ti-ditta option[data-status=\"attivo\"] { color:#ffffff !important; }\n        #ti-ditta option[data-status=\"sospesa\"] { color:#ef4444 !important; font-weight:700 !important; }\n        #ti-ditta option[data-status=\"configurazione\"] { color:#facc15 !important; font-weight:700 !important; }\n        #ti-ditta option[data-status=\"demo\"] { color:#38bdf8 !important; font-weight:700 !important; }\n        #ti-ditta option[data-status=\"nuova\"] { color:#10b981 !important; font-weight:700 !important; }\n        #ti-ditta option[data-status=\"sviluppo\"] { color:#ef4444 !important; font-weight:700 !important; }\n        .ti-ditta-picker { position:relative; flex:1 1 100%; width:100%; min-width:0; max-width:none; }\n        .ti-header-left-controls .ti-ditta-picker { flex:1 1 100%; width:100%; max-width:none; }\n        .ti-ditta-trigger { display:block; width:100%; min-height:36px; padding:6px 12px; border-radius:8px; background:#000; color:#fff; border:1px solid #444; text-align:left; cursor:pointer; font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n        .ti-ditta-popup { position:absolute; top:calc(100% + 4px); left:0; right:0; width:100%; z-index:999999; max-height:320px; overflow:auto; background:#030712; border:1px solid #334155; border-radius:10px; box-shadow:0 12px 32px rgba(0,0,0,.55); padding:6px; }\n        .ti-ditta-row { display:flex; align-items:center; justify-content:space-between; gap:10px; width:100%; padding:8px 10px; border-radius:8px; cursor:pointer; background:#020617; border:1px solid transparent; text-align:left; font-size:13px; line-height:1.3; }\n        .ti-ditta-row:hover, .ti-ditta-row.active { background:#111827; border-color:#334155; }\n        .ti-ditta-row.attivo { border-left:3px solid #ffffff; }\n        .ti-ditta-row.sospesa { border-left:3px solid #ef4444; background:rgba(127,29,29,.18); }\n        .ti-ditta-row.sviluppo { border-left:3px solid #ef4444; background:rgba(127,29,29,.18); }\n        .ti-ditta-row.configurazione { border-left:3px solid #facc15; background:rgba(161,98,7,.16); }\n        .ti-ditta-row.demo { border-left:3px solid #38bdf8; background:rgba(14,116,144,.20); }\n        .ti-ditta-row.nuova { border-left:3px solid #10b981; background:rgba(6,95,70,.16); }\n        .ti-ditta-row.empty, .ti-ditta-row.none { border-left:3px solid #94a3b8; }\n        .ti-ditta-row-main { display:block; flex:1 1 auto; min-width:0; color:#fff !important; font-weight:500 !important; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-size:var(--ti-font-base) !important; line-height:1.25 !important; }\n        .ti-ditta-row-badge { flex:0 0 auto; padding:2px 8px; border-radius:999px; font-size:11px; font-weight:800; letter-spacing:.01em; border:1px solid currentColor; }\n        .ti-ditta-row-badge.sospesa { color:#ef4444 !important; background:rgba(127,29,29,.20); }\n        .ti-ditta-row-badge.sviluppo { color:#ef4444 !important; background:rgba(127,29,29,.20); }\n        .ti-ditta-row-badge.configurazione { color:#facc15 !important; background:rgba(161,98,7,.18); }\n        .ti-ditta-row-badge.demo { color:#38bdf8 !important; background:rgba(14,116,144,.22); }\n        .ti-ditta-row-badge.nuova { color:#10b981 !important; background:rgba(6,95,70,.18); }\n        .ti-ditta-row-badge.attivo { color:#ffffff !important; background:rgba(255,255,255,.06); }\n        #ti-login-btn, #ti-logout-btn { min-height: 34px; height: 34px; padding: 0 12px; line-height: 34px; box-sizing: border-box; display:inline-flex; align-items:center; justify-content:center; }\n        \/* 30.9.664 - Allineamento verticale Accedi pre-login con campo ditta scelta *\/\n        @media (min-width: 769px) {\n            #ti-ai-outer .ti-header-bottom {\n                align-items: flex-end !important;\n            }\n            #ti-ai-outer .ti-header-right-controls {\n                align-self: flex-end !important;\n                display: flex !important;\n                align-items: center !important;\n                justify-content: flex-end !important;\n                min-height: 36px !important;\n                margin-top: auto !important;\n                margin-bottom: 0 !important;\n                padding-bottom: 0 !important;\n            }\n            #ti-ai-outer #ti-login-btn,\n            #ti-ai-outer #ti-logout-btn {\n                height: 36px !important;\n                min-height: 36px !important;\n                line-height: 1 !important;\n                display: inline-flex !important;\n                align-items: center !important;\n                justify-content: center !important;\n                margin-top: 0 !important;\n                margin-bottom: 0 !important;\n                align-self: center !important;\n            }\n            #ti-ai-outer .ti-ditta-trigger {\n                min-height: 36px !important;\n                height: 36px !important;\n                display: flex !important;\n                align-items: center !important;\n            }\n        }\n        #ti-user-area { align-self: center; height:34px; min-height:34px; display:none; align-items:center; }\n\n        .ti-order-wrap { margin-top:10px; }\n        .ti-order-toolbar { display:flex; gap:6px; flex-wrap:wrap; align-items:center; justify-content:space-between; padding:8px; background:#0b1220; border:1px solid #223047; border-bottom:none; border-radius:8px 8px 0 0; }\n        .ti-order-toolbar-left, .ti-order-toolbar-right { display:flex; gap:6px; flex-wrap:wrap; align-items:center; }\n        .ti-order-view-btn { padding:6px 10px; border-radius:8px; background:#1f2937; color:#cbd5e1; border:1px solid #334155; cursor:pointer; font-size:12px; font-weight:700; }\n        .ti-order-view-btn.active { background:#2563eb; color:#fff; border-color:#1d4ed8; }\n        .ti-order-hint { color:#94a3b8; font-size:11px; }\n        .ti-order-table-shell { display:block; }\n        .ti-order-grid-view { display:none; padding:12px; border:1px solid #444; border-top:none; border-radius:0 0 8px 8px; background:#111; }\n        .ti-order-wrap.ti-view-small .ti-order-table-shell,\n        .ti-order-wrap.ti-view-medium .ti-order-table-shell,\n        .ti-order-wrap.ti-view-large .ti-order-table-shell { display:none; }\n        .ti-order-wrap.ti-view-small .ti-order-grid-view,\n        .ti-order-wrap.ti-view-medium .ti-order-grid-view,\n        .ti-order-wrap.ti-view-large .ti-order-grid-view { display:grid; }\n        .ti-order-wrap.ti-view-small .ti-order-grid-view { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }\n        .ti-order-wrap.ti-view-medium .ti-order-grid-view { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }\n        .ti-order-wrap.ti-view-large .ti-order-grid-view { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }\n        .ti-qty-stepper { display:flex; align-items:center; gap:4px; min-width:118px; }\n        .ti-qty-stepper .ti-q, .ti-qty-stepper .ti-num-step { min-width:64px; flex:1 1 70px; display:block; }\n        .ti-col-qta { min-width:132px; }\n        .ti-order-card { background:#06080f; border:1px solid #243042; border-radius:12px; overflow:hidden; display:flex; flex-direction:column; min-height:100%; }\n        .ti-order-card-media { background:#000; display:flex; align-items:center; justify-content:center; overflow:hidden; }\n        .ti-order-card-media img { width:100%; object-fit:cover; display:block; }\n        .ti-order-card-media .ti-order-file-icon { font-size:36px; color:#64748b; padding:30px 0; }\n        .ti-order-wrap.ti-view-small .ti-order-card-media { height:120px; }\n        .ti-order-wrap.ti-view-medium .ti-order-card-media { height:180px; }\n        .ti-order-wrap.ti-view-large .ti-order-card-media { height:260px; }\n        .ti-order-card-body { padding:10px; display:flex; flex-direction:column; gap:8px; }\n        .ti-order-card-title { font-size:13px; font-weight:800; color:#fff; display:flex; align-items:center; gap:6px; }\n        .ti-order-card-title .ti-order-badge { font-size:11px; font-weight:900; padding:2px 6px; border-radius:99px; }\n        .ti-order-card-title .ti-order-badge.product { background:#1d4ed8; color:#fff; }\n        .ti-order-card-title .ti-order-badge.service { background:#047857; color:#fff; }\n        .ti-order-card-desc { font-size:11px; color:#cbd5e1; line-height:1.35; min-height:42px; }\n        .ti-order-card-meta { display:grid; grid-template-columns:1fr 1fr; gap:8px; align-items:end; }\n        .ti-order-card-label { font-size:10px; color:#94a3b8; text-transform:uppercase; letter-spacing:.04em; margin-bottom:3px; }\n        .ti-order-card-total { font-weight:800; color:#38bdf8; font-size:14px; }\n        .ti-order-card .ti-q { width:100%; padding:6px; border-radius:8px; border:1px solid #334155; background:#0f172a; color:#fff; text-align:right; }\n        .ti-order-footer { display:none !important; }\n        .ti-order-footer-grid { display:none !important; }\n        .ti-order-footer-total { display:none !important; }\n        .ti-order-footer-total b { display:none !important; }\n        .ti-order-footer-btn { display:none !important; visibility:hidden !important; width:0 !important; min-width:0 !important; padding:0 !important; border:0 !important; margin:0 !important; overflow:hidden !important; }\n\n        @media (max-width: 768px) { \n            .ti-header { flex-direction: column; align-items: stretch; padding: 12px; } \n            .ti-header-top { flex-wrap: wrap; align-items:flex-start; } \n            .ti-header-main-left { width:100%; } \n            .ti-hr-right { width: 100%; align-items: center; justify-content:flex-end; margin-top: 0; } \n            .ti-header-bottom { flex-direction: column; align-items: stretch; } \n            .ti-header-left-controls { width:100%; } \n            .ti-header-right-controls { width:100%; justify-content:flex-end; } \n            .ti-hr-bottom { flex-wrap: wrap; width: 100%; justify-content: flex-start; } \n            #ti-ditta { max-width: 100%; min-width: 0; width: 100%; flex: 1; } \n            .ti-actions-row button { padding: 8px 10px; font-size: 11px; } \n            .ti-grid-2 { grid-template-columns: 1fr; }\n            .ti-modal { width: min(94vw, 520px); max-width:min(94vw, 520px); max-height: 88vh; overflow-y: auto; }\n\n            .ti-order-toolbar-right { width:100%; }\n            .ti-order-hint { display:block; }\n            #ti-ai-outer .ti-actions-row {\n                display:grid !important;\n                grid-template-columns:repeat(5, minmax(0, 1fr)) !important;\n                gap:4px !important;\n                overflow:visible !important;\n                padding-bottom:0 !important;\n            }\n            #ti-ai-outer .ti-actions-row button,\n            #ti-ai-outer .ti-actions-row .ti-btn,\n            #ti-ai-outer .ti-actions-row .ti-help {\n                padding:3px 3px !important;\n                font-size:10px !important;\n                min-height:30px !important;\n                height:30px !important;\n                min-width:0 !important;\n                width:100% !important;\n                max-width:none !important;\n                border-radius:7px !important;\n                white-space:nowrap !important;\n                overflow:hidden !important;\n                text-overflow:ellipsis !important;\n            }\n            #ti-ai-outer .ti-actions-row #ti-help-btn {\n                margin-left:0 !important;\n                width:100% !important;\n                min-width:0 !important;\n                max-width:none !important;\n                font-size:13px !important;\n            }\n            #ti-ai-outer .ti-admin-actions-row {\n                grid-template-columns:repeat(2, minmax(0, 1fr)) !important;\n                gap:5px !important;\n                overflow:visible !important;\n            }\n            #ti-ai-outer .ti-admin-actions-row .ti-btn {\n                width:100% !important;\n                min-width:0 !important;\n                height:31px !important;\n                min-height:31px !important;\n                padding:4px 5px !important;\n                font-size:10.5px !important;\n                border-radius:7px !important;\n                white-space:nowrap !important;\n                overflow:hidden !important;\n                text-overflow:ellipsis !important;\n            }\n        }\n\n        @media (max-width: 640px) {\n            .ti-header { align-items:flex-start; }\n            .ti-header-top { align-items:flex-start; }\n            .ti-hl { align-items:flex-start; }\n            .ti-left-stack { gap:4px; }\n            .ti-user-area { max-width: 170px; padding: 2px 6px; }\n            .ti-user-name { font-size: 10px; }\n        }\n\n    \n        \/* Mobile hardening 30.9.664: login e scroll verticale a un dito *\/\n        @media (max-width: 760px) {\n            html, body, html.ti-no-scroll, body.ti-no-scroll {\n                overflow-y:auto !important;\n                overflow-x:hidden !important;\n                height:auto !important;\n                min-height:100% !important;\n                position:static !important;\n                touch-action:auto !important;\n                -webkit-overflow-scrolling:touch !important;\n            }\n            #ti-ai-outer,\n            #ti-ai-outer * {\n                -webkit-tap-highlight-color: rgba(59,130,246,.25);\n            }\n            #ti-ai-outer,\n            #ti-ai-outer .ti-chat-box,\n            #ti-ai-outer .ti-input-wrap,\n            #ti-ai-outer .ti-actions-row,\n            #ti-ai-outer .ti-admin-actions-row,\n            #ti-ai-outer .ti-modal,\n            #ti-ai-outer .ti-ov,\n            #ti-ai-outer .ti-conf-body,\n            #ti-ai-outer .ti-cfg-table-container,\n            #ti-ai-outer .ti-table-wrap {\n                touch-action:auto !important;\n                overscroll-behavior-y:auto !important;\n                -webkit-overflow-scrolling:touch !important;\n            }\n            .ti-ov {\n                overflow-y:auto !important;\n                overflow-x:hidden !important;\n                align-items:flex-start !important;\n                pointer-events:auto !important;\n            }\n            .ti-modal {\n                max-height:90dvh !important;\n                overflow-y:auto !important;\n                pointer-events:auto !important;\n            }\n            #ti-login-ov {\n                z-index:2147483600 !important;\n                padding: max(8px, env(safe-area-inset-top)) 6px max(8px, env(safe-area-inset-bottom)) 6px !important;\n            }\n            #ti-login-ov .ti-modal {\n                width:96vw !important;\n                max-width:390px !important;\n                min-width:0 !important;\n                margin:8px auto 20px auto !important;\n                padding:16px !important;\n                box-sizing:border-box !important;\n            }\n            #ti-login-ov input,\n            #ti-login-ov select,\n            #ti-login-ov button {\n                pointer-events:auto !important;\n                font-size:16px !important;\n                min-height:42px !important;\n            }\n            #ti-login-ov #l-do {\n                min-height:44px !important;\n                touch-action:manipulation !important;\n            }\n        }\n\n\n        \/* Mobile\/touch hardening 30.9.664: scroll a un dito e comandi login\/logout *\/\n        @media (hover:none), (pointer:coarse), (max-width:900px) {\n            html, body,\n            html.ti-no-scroll, body.ti-no-scroll {\n                overflow-y:auto !important;\n                overflow-x:hidden !important;\n                height:auto !important;\n                min-height:100% !important;\n                max-height:none !important;\n                position:static !important;\n                touch-action:pan-y !important;\n                overscroll-behavior-y:auto !important;\n                -webkit-overflow-scrolling:touch !important;\n            }\n            #ti-ai-outer,\n            #ti-ai-outer.ti-plugin-scroll-lock,\n            .ti-plugin-scroll-lock {\n                overflow-y:visible !important;\n                overflow-x:hidden !important;\n                height:auto !important;\n                min-height:auto !important;\n                max-height:none !important;\n                position:relative !important;\n                touch-action:pan-y !important;\n                overscroll-behavior-y:auto !important;\n                -webkit-overflow-scrolling:touch !important;\n            }\n            #ti-ai-outer *,\n            #ti-plugin-modal-root,\n            #ti-plugin-modal-root * {\n                touch-action:pan-y !important;\n            }\n            #ti-ai-outer button,\n            #ti-ai-outer input,\n            #ti-ai-outer textarea,\n            #ti-ai-outer select,\n            #ti-plugin-modal-root button,\n            #ti-plugin-modal-root input,\n            #ti-plugin-modal-root textarea,\n            #ti-plugin-modal-root select,\n            #ti-login-btn,\n            #ti-logout-btn,\n            #l-do {\n                touch-action:manipulation !important;\n                -webkit-user-select:auto !important;\n                user-select:auto !important;\n                pointer-events:auto !important;\n            }\n            .ti-ov,\n            #ti-ai-loader-ov {\n                overflow-y:auto !important;\n                overflow-x:hidden !important;\n                align-items:flex-start !important;\n                justify-content:center !important;\n                touch-action:pan-y !important;\n                overscroll-behavior-y:auto !important;\n                -webkit-overflow-scrolling:touch !important;\n            }\n            .ti-modal,\n            .ti-conf-body,\n            .ti-chat-box,\n            .ti-cfg-table-container,\n            .ti-table-wrap,\n            #ti-conf-import-preview-body,\n            #global-action-res,\n            #help-bot-reply,\n            #col-help-content,\n            #ateco-res,\n            #fm-grid {\n                overflow-y:auto !important;\n                touch-action:pan-y !important;\n                overscroll-behavior-y:auto !important;\n                -webkit-overflow-scrolling:touch !important;\n            }\n            #ti-login-ov .ti-modal {\n                width:min(96vw, 390px) !important;\n                max-width:min(96vw, 390px) !important;\n                max-height:calc((var(--ti-vh, 1vh) * 88)) !important;\n                margin:8px auto 20px auto !important;\n            }\n            #ti-login-ov input,\n            #ti-login-ov button {\n                font-size:16px !important;\n                min-height:44px !important;\n            }\n        }\n        \/* 30.9.664 - Login: prima interfaccia PC\/modal, accesso diretto solo come fallback se il primo login non procede *\/\n        #ti-ai-outer #ti-mobile-direct-login {\n            display:none !important;\n            width:min(100%, 720px) !important;\n            margin:10px auto 12px auto !important;\n            padding:12px !important;\n            border-radius:14px !important;\n            background:#111827 !important;\n            border:1px solid #374151 !important;\n            color:#fff !important;\n            touch-action:pan-y !important;\n            box-sizing:border-box !important;\n        }\n        #ti-ai-outer #ti-mobile-direct-login.ti-direct-login-visible {\n            display:block !important;\n        }\n        #ti-ai-outer #ti-mobile-direct-login .ti-mobile-login-title {\n            font-size:14px !important;\n            font-weight:900 !important;\n            margin-bottom:8px !important;\n            color:#bfdbfe !important;\n        }\n        #ti-ai-outer #ti-mobile-direct-login form {\n            display:grid !important;\n            grid-template-columns:1fr 1fr 1.2fr auto !important;\n            gap:8px !important;\n            width:100% !important;\n            align-items:center !important;\n        }\n        #ti-ai-outer #ti-mobile-direct-login input,\n        #ti-ai-outer #ti-mobile-direct-login select,\n        #ti-ai-outer #ti-mobile-direct-login button {\n            width:100% !important;\n            min-width:0 !important;\n            min-height:38px !important;\n            height:38px !important;\n            font-size:16px !important;\n            line-height:18px !important;\n            padding:6px 8px !important;\n            border-radius:9px !important;\n            box-sizing:border-box !important;\n            touch-action:manipulation !important;\n        }\n        #ti-ai-outer #ti-mobile-direct-login input,\n        #ti-ai-outer #ti-mobile-direct-login select {\n            background:#0f172a !important;\n            color:#fff !important;\n            border:1px solid #334155 !important;\n        }\n        #ti-ai-outer #ti-mobile-direct-login .ti-mobile-login-submit {\n            background:#2563eb !important;\n            color:#fff !important;\n            border:1px solid #1d4ed8 !important;\n            font-weight:900 !important;\n            white-space:nowrap !important;\n        }\n        @media (hover:none), (pointer:coarse), (max-width:900px) {\n            #ti-ai-outer #ti-mobile-direct-login {\n                width:100% !important;\n                margin:8px 0 10px 0 !important;\n                padding:10px !important;\n            }\n            #ti-ai-outer #ti-mobile-direct-login form {\n                grid-template-columns:1fr 1fr !important;\n                gap:7px !important;\n            }\n            #ti-ai-outer #ti-mobile-direct-login select,\n            #ti-ai-outer #ti-mobile-direct-login .ti-mobile-login-submit {\n                grid-column:1 \/ -1 !important;\n            }\n        }\n\n        \/* 30.9.664 - Fix persi reintegrati in modo chirurgico *\/\n        #ti-purchase-ov .ti-purchase-table-wrap {\n            width:100% !important;\n            max-width:100% !important;\n            overflow-x:auto !important;\n            overflow-y:visible !important;\n            -webkit-overflow-scrolling:touch !important;\n            touch-action:pan-x pan-y !important;\n        }\n        #ti-purchase-ov #ti-purchase-table {\n            border-collapse:collapse !important;\n            width:100% !important;\n            min-width:1060px !important;\n        }\n        #ti-purchase-ov #ti-purchase-table th,\n        #ti-purchase-ov #ti-purchase-table td {\n            white-space:nowrap !important;\n        }\n        #ti-purchase-ov .ti-purchase-row input,\n        #ti-purchase-ov .ti-purchase-row select,\n        #ti-purchase-ov .ti-purchase-row textarea {\n            background:#0f172a !important;\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            caret-color:#fff !important;\n        }\n        .ti-purchase-row-search-popover {\n            position:fixed !important;\n            z-index:2147483647 !important;\n            background:#020617 !important;\n            border:1px solid #38bdf8 !important;\n            border-radius:10px !important;\n            box-shadow:0 16px 44px rgba(0,0,0,.65) !important;\n            max-height:min(320px, 52vh) !important;\n            overflow:auto !important;\n            padding:6px !important;\n            min-width:220px !important;\n            max-width:min(92vw, 520px) !important;\n            color:#fff !important;\n            pointer-events:auto !important;\n            -webkit-overflow-scrolling:touch !important;\n        }\n        .ti-purchase-row-search-popover .ti-purchase-result-row {\n            display:block !important;\n            width:100% !important;\n            text-align:left !important;\n            margin:0 0 5px 0 !important;\n            padding:7px 8px !important;\n            border-radius:8px !important;\n            border:1px solid #334155 !important;\n            background:#1e293b !important;\n            color:#fff !important;\n            font-size:12px !important;\n            line-height:1.25 !important;\n            cursor:pointer !important;\n        }\n        .ti-purchase-row-search-popover .ti-purchase-result-row small {\n            display:block !important;\n            color:#cbd5e1 !important;\n            font-size:10.5px !important;\n            margin-top:2px !important;\n        }\n        .ti-report-outcome-filter-wrap {\n            display:inline-flex !important;\n            align-items:center !important;\n            gap:6px !important;\n            background:#0f172a !important;\n            border:1px solid #334155 !important;\n            color:#e5e7eb !important;\n            border-radius:999px !important;\n            padding:5px 10px !important;\n            font-size:11px !important;\n            white-space:nowrap !important;\n            flex:0 0 auto !important;\n        }\n        .ti-report-outcome-filter {\n            width:auto !important;\n            min-width:120px !important;\n            padding:4px 7px !important;\n            border-radius:7px !important;\n            border:1px solid #94a3b8 !important;\n            background:#fff !important;\n            color:#111827 !important;\n            -webkit-text-fill-color:#111827 !important;\n            font-size:11px !important;\n            margin:0 !important;\n        }\n        .ti-report-outcome-filter option {\n            background:#fff !important;\n            color:#111827 !important;\n            -webkit-text-fill-color:#111827 !important;\n        }\n        #ti-ai-outer input[style*=\"background:#020617\"],\n        #ti-ai-outer input[style*=\"background: #020617\"],\n        #ti-ai-outer input[style*=\"background:#0f172a\"],\n        #ti-ai-outer input[style*=\"background: #0f172a\"],\n        #ti-ai-outer input[style*=\"background:#111827\"],\n        #ti-ai-outer input[style*=\"background: #111827\"],\n        #ti-ai-outer textarea[style*=\"background:#020617\"],\n        #ti-ai-outer textarea[style*=\"background: #020617\"],\n        #ti-ai-outer textarea[style*=\"background:#0f172a\"],\n        #ti-ai-outer textarea[style*=\"background: #0f172a\"],\n        #ti-ai-outer textarea[style*=\"background:#111827\"],\n        #ti-ai-outer textarea[style*=\"background: #111827\"],\n        #ti-ai-outer select[style*=\"background:#020617\"],\n        #ti-ai-outer select[style*=\"background: #020617\"],\n        #ti-ai-outer select[style*=\"background:#0f172a\"],\n        #ti-ai-outer select[style*=\"background: #0f172a\"],\n        #ti-ai-outer select[style*=\"background:#111827\"],\n        #ti-ai-outer select[style*=\"background: #111827\"],\n        #ti-ai-outer .ti-in,\n        #ti-plugin-modal-root .ti-in {\n            color:#fff !important;\n            -webkit-text-fill-color:#fff !important;\n            caret-color:#fff !important;\n        }\n        @media (max-width:767px) and (orientation:portrait) {\n            #ti-purchase-ov .ti-purchase-modal { width:98vw !important; max-width:98vw !important; padding:10px !important; }\n            #ti-purchase-ov #ti-purchase-table { min-width:980px !important; }\n            #ti-purchase-ov #ti-purchase-table th,\n            #ti-purchase-ov #ti-purchase-table td { font-size:10.5px !important; padding:4px !important; }\n            #ti-purchase-ov #ti-purchase-table input,\n            #ti-purchase-ov #ti-purchase-table select,\n            #ti-purchase-ov #ti-purchase-table textarea,\n            #ti-purchase-ov #ti-purchase-table button { font-size:10.5px !important; padding:4px !important; min-height:27px !important; }\n            #ti-purchase-ov .ti-pur-name { min-width:190px !important; }\n            .ti-purchase-row-search-popover .ti-purchase-result-row { font-size:10.5px !important; padding:6px !important; }\n            .ti-purchase-row-search-popover .ti-purchase-result-row small { font-size:9.5px !important; }\n        }\n        @media (max-width:920px) and (orientation:landscape) {\n            #ti-purchase-ov .ti-purchase-modal { width:98vw !important; max-width:98vw !important; padding:10px !important; }\n            #ti-purchase-ov #ti-purchase-table { min-width:900px !important; }\n            #ti-purchase-ov #ti-purchase-table th,\n            #ti-purchase-ov #ti-purchase-table td { font-size:10px !important; padding:3px !important; }\n            #ti-purchase-ov #ti-purchase-table input,\n            #ti-purchase-ov #ti-purchase-table select,\n            #ti-purchase-ov #ti-purchase-table textarea,\n            #ti-purchase-ov #ti-purchase-table button { font-size:10px !important; padding:3px !important; min-height:25px !important; }\n        }\n\n        \/* 30.9.664 - Acquisizioni smartphone orizzontale: riga ultra compatta *\/\n        @media (max-width: 980px) and (orientation: landscape) {\n            #ti-purchase-ov.ti-purchase-open {\n                align-items:flex-start !important;\n                padding:2px !important;\n            }\n            #ti-purchase-ov .ti-purchase-modal {\n                width:99vw !important;\n                max-width:99vw !important;\n                max-height:96vh !important;\n                margin:2px auto !important;\n                padding:5px !important;\n                border-radius:10px !important;\n                overflow:auto !important;\n            }\n            #ti-purchase-ov .ti-purchase-modal h3,\n            #ti-purchase-ov .ti-purchase-modal h4 {\n                font-size:11px !important;\n                line-height:1.05 !important;\n                margin:0 0 4px 0 !important;\n            }\n            #ti-purchase-ov .ti-purchase-table-wrap {\n                max-height:58vh !important;\n                overflow:auto !important;\n            }\n            #ti-purchase-ov #ti-purchase-table {\n                min-width:790px !important;\n                table-layout:auto !important;\n            }\n            #ti-purchase-ov #ti-purchase-table th,\n            #ti-purchase-ov #ti-purchase-table td {\n                font-size:8.5px !important;\n                line-height:1.05 !important;\n                padding:1px 2px !important;\n                height:22px !important;\n                max-height:22px !important;\n                vertical-align:middle !important;\n            }\n            #ti-purchase-ov #ti-purchase-table input,\n            #ti-purchase-ov #ti-purchase-table select,\n            #ti-purchase-ov #ti-purchase-table textarea,\n            #ti-purchase-ov #ti-purchase-table button {\n                font-size:8.5px !important;\n                line-height:1.05 !important;\n                padding:1px 2px !important;\n                height:20px !important;\n                min-height:20px !important;\n                max-height:20px !important;\n                border-radius:4px !important;\n                box-sizing:border-box !important;\n            }\n            #ti-purchase-ov .ti-pur-name { min-width:145px !important; max-width:165px !important; }\n            #ti-purchase-ov .ti-pur-type { width:42px !important; min-width:42px !important; }\n            #ti-purchase-ov .ti-pur-qty { width:46px !important; min-width:46px !important; }\n            #ti-purchase-ov .ti-pur-cost { width:62px !important; min-width:62px !important; }\n            #ti-purchase-ov .ti-pur-margin { width:56px !important; min-width:56px !important; }\n            #ti-purchase-ov .ti-pur-sale { width:70px !important; min-width:70px !important; }\n            #ti-purchase-ov .ti-pur-iva { width:42px !important; min-width:42px !important; }\n            #ti-purchase-ov .ti-pur-note { min-width:96px !important; max-width:115px !important; }\n            #ti-purchase-ov .ti-pur-row-total-net,\n            #ti-purchase-ov .ti-pur-row-total-gross {\n                display:inline-block !important;\n                min-width:42px !important;\n                font-size:8.5px !important;\n                line-height:1 !important;\n            }\n            #ti-purchase-ov .ti-pur-dup,\n            #ti-purchase-ov .ti-pur-del {\n                width:21px !important;\n                min-width:21px !important;\n                height:20px !important;\n                min-height:20px !important;\n                padding:0 !important;\n                margin:0 1px !important;\n                font-size:9px !important;\n                line-height:1 !important;\n            }\n            #ti-purchase-ov .ti-purchase-summary-grid {\n                grid-template-columns:repeat(4,minmax(70px,1fr)) !important;\n                gap:4px !important;\n                margin-top:4px !important;\n            }\n            #ti-purchase-ov .ti-purchase-summary-card {\n                font-size:8.5px !important;\n                line-height:1.05 !important;\n                padding:4px !important;\n                border-radius:7px !important;\n            }\n            #ti-purchase-ov .ti-purchase-summary-card b {\n                font-size:10px !important;\n                line-height:1.05 !important;\n            }\n            #ti-purchase-ov .ti-purchase-toolbar-fixed {\n                padding-top:4px !important;\n                gap:4px !important;\n            }\n            #ti-purchase-ov .ti-purchase-toolbar-fixed .ti-btn,\n            #ti-purchase-ov .ti-purchase-toolbar-fixed button {\n                font-size:9px !important;\n                line-height:1.05 !important;\n                padding:4px 6px !important;\n                min-height:23px !important;\n                height:23px !important;\n            }\n            .ti-purchase-row-search-popover .ti-purchase-result-row {\n                font-size:9px !important;\n                line-height:1.08 !important;\n                padding:4px 5px !important;\n                margin-bottom:3px !important;\n            }\n            .ti-purchase-row-search-popover .ti-purchase-result-row small {\n                font-size:8px !important;\n                line-height:1.05 !important;\n            }\n        }\n\n\n\n        \/* 30.9.664 - Popup separati in primo piano per comunicazioni\/report dei ruoli privilegiati *\/\n        \/* 30.9.664 - Prenotazione multipla: applica stessa scelta alle voci successive; operatore non definito nascosto all utente comune. *\/\n        \/* 30.9.664 - Fix sintassi JS nota funzioni utente: nota costruita da JSON per evitare apostrofi non protetti; ruoli privilegiati abilitati alle funzionalita ordine complete. *\/\n        #ti-admin-comm-ov { z-index:2147483646 !important; }\n        #ti-admin-comm-ov .ti-admin-comm-modal { width:min(94vw,820px); max-height:88vh; overflow:hidden; text-align:left; padding:18px; box-shadow:0 18px 50px rgba(0,0,0,.82); border:1px solid #475569; }\n        #ti-admin-comm-title { margin:0 0 10px 0; color:#fff; border-bottom:1px solid #334155; padding-bottom:10px; font-size:16px; font-weight:800; }\n        #ti-admin-comm-msg { max-height:62vh; overflow:auto; background:#020617; color:#e5e7eb; border:1px solid #334155; border-radius:10px; padding:12px; font-size:13px; line-height:1.45; white-space:normal; }\n        #ti-admin-comm-actions { display:flex; gap:10px; justify-content:flex-end; flex-wrap:wrap; margin-top:12px; }\n        #ti-admin-comm-copy { font-size:11px !important; padding:7px 10px !important; min-width:92px; }\n        @media (max-width:767px) { #ti-admin-comm-ov .ti-admin-comm-modal { width:96vw; max-height:92vh; padding:12px; } #ti-admin-comm-msg { max-height:66vh; font-size:12px; } }\n\n<\/style>\n\n    <div id=\"ti-ai-outer\" class=\"ti-plugin-scroll-lock\">\n        <div id=\"ti-ai-loader-ov\">\n            <div class=\"ti-loader-box\">\n                <div class=\"ti-loader-spinner\">\u23f3<\/div>\n                <div id=\"ti-loader-title\" style=\"color:#fff; font-weight:bold; font-size:14px;\">Elaborazione AI in corso...<\/div>\n                <div id=\"ti-loader-sub\" style=\"color:#aaa; font-size:11px; margin-top:5px;\">Attendi, l'operazione potrebbe richiedere alcuni secondi.<\/div>\n                <div id=\"ti-loader-current-item\" style=\"display:none;margin-top:8px;padding:7px 9px;border-radius:8px;background:#0f172a;border:1px solid #334155;color:#bfdbfe;font-size:12px;font-weight:700;word-break:break-word;\"><\/div>\n                <div id=\"ti-loader-progress-wrap\" style=\"margin-top:12px; width:100%; display:none;\">\n                    <div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:6px;\">\n                        <span style=\"font-size:11px; color:#cbd5e1;\">Avanzamento stimato<\/span>\n                        <span id=\"ti-loader-percent\" style=\"font-size:12px; color:#fff; font-weight:700;\">0%<\/span>\n                    <\/div>\n                    <div style=\"width:100%; height:10px; background:#111827; border:1px solid #374151; border-radius:999px; overflow:hidden;\">\n                        <div id=\"ti-loader-bar\" style=\"width:0%; height:100%; background:linear-gradient(90deg,#38bdf8,#10b981); transition:width .25s ease;\"><\/div>\n                    <\/div>\n                <\/div>\n                <div id=\"ti-loader-actions\" style=\"margin-top:12px; display:none;\">\n                    <button type=\"button\" id=\"ti-loader-close\" class=\"ti-btn\" style=\"width:100%; background:#b91c1c; color:#fff;\" onclick=\"window.cancelLongAIProcess()\">Esci<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-alert-ov\" class=\"ti-ov\" style=\"z-index: 2147483647 !important;\">\n            <div id=\"ti-alert-modal\" class=\"ti-modal\" style=\"width: 300px; padding: 20px; box-shadow: 0 5px 20px rgba(0,0,0,0.8);\">\n                <h4 id=\"ti-alert-msg\" style=\"color: #fff; margin-bottom: 20px; font-size: 15px; font-weight: normal; line-height: 1.4;\"><\/h4>\n                <div style=\"display:flex; gap:10px; justify-content:center; flex-wrap:wrap;\">\n                    <button type=\"button\" id=\"ti-alert-copy\" class=\"ti-btn\" style=\"background:#2563eb; color:#fff; flex:1; min-width:90px; font-size:11px; padding:7px 8px;\">Copia testo<\/button>\n                    <button type=\"button\" id=\"ti-alert-yes\" class=\"ti-btn\" style=\"background:#10b981; color:#fff; display:none; flex:1; min-width:90px;\">SI<\/button>\n                    <button type=\"button\" id=\"ti-alert-no\" class=\"ti-btn\" style=\"background:#4b5563; color:#fff; flex:1; min-width:90px;\">OK<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-admin-comm-ov\" class=\"ti-ov ti-admin-comm-ov\" style=\"z-index: 2147483646 !important;\">\n            <div class=\"ti-modal ti-admin-comm-modal\">\n                <h3 id=\"ti-admin-comm-title\">Comunicazione<\/h3>\n                <div id=\"ti-admin-comm-msg\"><\/div>\n                <div id=\"ti-admin-comm-actions\">\n                    <button type=\"button\" id=\"ti-admin-comm-copy\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;\">Copia testo<\/button>\n                    <button type=\"button\" id=\"ti-admin-comm-close\" class=\"ti-btn btn-close\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div class=\"ti-header\">\n            <div class=\"ti-header-top\">\n                <div class=\"ti-header-main-left\">\n                    <div class=\"ti-hl\">\n                         <div class=\"ti-left-stack\">\n                              <img id=\"ti-qr-img\" class=\"ti-qr\" style=\"display:none;\" title=\"Apri ditta in nuova scheda\" data-no-lazy=\"1\" \/>\n                         <\/div>\n                         <img id=\"ti-logo-img\" class=\"ti-logo\" style=\"display:none;\" onerror=\"this.style.display='none'\" data-no-lazy=\"1\" \/>\n                         <div style=\"display:flex; flex-direction:column; align-items:flex-start; min-width:0;\">\n                              <div style=\"display:flex; align-items:center; flex-wrap:wrap; gap:8px;\">\n                                   <div id=\"ti-company-name\" style=\"font-weight:900; font-size:1.1em; color:#fff;\">SHOP & SERVICE<\/div>\n                              <\/div>\n                         <\/div>\n                    <\/div>\n                <\/div>\n                <div class=\"ti-hr-right\">\n                     <span id=\"ti-company-ref-datetime\" class=\"ti-company-ref-datetime\" title=\"Data e ora sistema ditta\"><\/span>\n                     <button type=\"button\" id=\"ti-help-top-btn\" class=\"ti-header-help-btn\" title=\"Aiuto\" aria-label=\"Aiuto\" onclick=\"if(window.openHelpModal){window.openHelpModal();} return false;\">?<\/button>\n                     <div class=\"ti-lang-wrap\"><span class=\"ti-lang-opt active\" data-lang=\"it\">IT<\/span><span class=\"ti-lang-opt\" data-lang=\"en\">EN<\/span><\/div>\n                <\/div>\n            <\/div>\n            <div class=\"ti-company-sub-row\"><div id=\"ti-company-sub\" style=\"display:none;\"><\/div><\/div>\n            <div class=\"ti-header-bottom\">\n                 <div class=\"ti-header-left-controls\">\n                     <div id=\"ti-sort-btn\" title=\"Ordina\" style=\"cursor:pointer; background:#2563eb; border-color:#1d4ed8; color:#fff;\">AZ<\/div>\n                     <button type=\"button\" id=\"ti-show-sospese-btn\" class=\"ti-btn\" title=\"Tutte: mostra tutte le ditte, incluse sospese e demo\" style=\"display:flex; align-items:center; justify-content:center; min-width:68px; background:#374151; color:#fff;\">Tutte<\/button>\n                     <input type=\"text\" id=\"ti-filter-ditte\" placeholder=\"\ud83d\udd0d Cerca ditta o attivit\u00e0\" autocomplete=\"off\">\n                     <div class=\"ti-ditta-picker\"><button type=\"button\" id=\"ti-ditta-trigger\" class=\"ti-ditta-trigger\">-- Scegli una ditta --<\/button><div id=\"ti-ditta-popup\" class=\"ti-ditta-popup\" style=\"display:none;\"><\/div><select id=\"ti-ditta\" style=\"display:none !important;\"><\/select><\/div>\n                 <\/div>\n                 <div class=\"ti-header-right-controls\">\n                     <button type=\"button\" id=\"ti-login-btn\" class=\"ti-btn\" style=\"\" onclick=\"if (window.openLoginModalSafe) window.openLoginModalSafe(); else if(window.openModal) window.openModal('ti-login-ov'); return false;\">Accedi<\/button>\n                     <div id=\"ti-user-area\" class=\"ti-user-area\" onclick=\"window.openProfileModal()\">\n                          <img decoding=\"async\" id=\"ti-user-avatar\" class=\"ti-user-avatar\" src=\"\" style=\"display:none;\" onerror=\"this.style.display='none'\">\n                          <div class=\"ti-user-info\">\n                              <span id=\"ti-user-name\" class=\"ti-user-name\">Cliente\/Utente<\/span><span id=\"ti-user-role\" class=\"ti-user-role\"><\/span>\n                          <\/div>\n                      <\/div>\n                     <button type=\"button\" id=\"ti-logout-btn\" class=\"ti-btn ti-logout-btn\" data-ti-logout=\"1\" aria-label=\"Esci\" title=\"Esci\" style=\"display:none;\" onclick=\"if(window.doLogout){return window.doLogout(event);} return false;\">Esci<\/button>\n                 <\/div>\n            <\/div>\n        <\/div>\n                <div id=\"ti-mobile-direct-login\" class=\"\" aria-label=\"Login diretto Shop & Service fallback\">\n            <div class=\"ti-mobile-login-title\">Accesso diretto Shop & Service <span style=\"font-weight:600;color:#93c5fd;\">(fallback)<\/span><\/div>\n            <div style=\"font-size:12px;color:#cbd5e1;margin:-4px 0 8px 0;\">Usa questo accesso solo se il login principale non procede o restituisce errore.<\/div>\n            <div id=\"ti-direct-login-fallback-alert\" style=\"display:none;background:#78350f;color:#fff;border:1px solid #f59e0b;border-radius:8px;padding:7px;margin-bottom:8px;font-size:12px;line-height:1.35;\"><\/div>\n                        <form method=\"post\" action=\"\/en\/wp-json\/wp\/v2\/pages\/1165\" autocomplete=\"on\">\n                <input type=\"hidden\" name=\"ti_action\" value=\"ti_ai_login\">\n                <input type=\"hidden\" name=\"action\" value=\"ti_ai_login\">\n                <input type=\"hidden\" name=\"ti_mobile_login_redirect\" value=\"1\">\n                <input type=\"hidden\" name=\"redirect\" value=\"1\">\n                <input id=\"ti-direct-login-user\" name=\"username\" placeholder=\"Username\" autocomplete=\"username\" data-ti-login-username=\"1\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\" inputmode=\"text\">\n                <input id=\"ti-direct-login-pass\" name=\"password\" type=\"password\" placeholder=\"Password\" autocomplete=\"current-password\" data-ti-login-password=\"1\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\">\n                <select id=\"ti-direct-login-db\" name=\"db_select\" aria-label=\"Ditta\">\n                    <option value=\"\">-- Scegli una ditta --<\/option>\n                                            <option value=\"Dev_hairstyle.json\" >Dev_hairstyle<\/option>\n                                            <option value=\"bmw-terni.json\" >bmw-terni<\/option>\n                                            <option value=\"catering-terni.json\" >catering-terni<\/option>\n                                            <option value=\"farmaciaterni.json\" >farmaciaterni<\/option>\n                                            <option value=\"ferramentaterni.json\" >ferramentaterni<\/option>\n                                            <option value=\"fioraio.json\" >fioraio<\/option>\n                                            <option value=\"hairstyle.json\" >hairstyle<\/option>\n                                            <option value=\"hard&amp;soft.json\" >hard&amp;soft<\/option>\n                                            <option value=\"ristoranteterni.json\" >ristoranteterni<\/option>\n                                            <option value=\"termoidraulica-italiana.json\" >termoidraulica-italiana<\/option>\n                                    <\/select>\n                <button type=\"submit\" class=\"ti-mobile-login-submit\">Entra<\/button>\n                <button type=\"button\" class=\"ti-mobile-login-submit ti-forgot-password-wide\" style=\"grid-column:1 \/ -1;margin-top:8px;background:#4b5563;font-size:12px;min-width:260px;min-height:44px;padding:10px 14px;white-space:normal;line-height:1.25;\" onclick=\"if(window.tiForgotPasswordFromLogin) window.tiForgotPasswordFromLogin('direct'); return false;\">Password dimenticata? Cambia tramite email<\/button>\n            <\/form>\n        <\/div>\n                <div class=\"ti-input-wrap\">\n            <div class=\"ti-input-main\">\n                <textarea id=\"ti-msg\" class=\"ti-msg\" rows=\"5\" placeholder=\"Scrivi qui...\"><\/textarea>\n                <div class=\"ti-send-wrap\">\n                    <button type=\"button\" id=\"ti-send\" class=\"ti-send\">INVIA<\/button>\n                    <button type=\"button\" id=\"ti-ok\" class=\"ti-btn ti-btn-ok\">OK \/ SI<\/button>\n                    <button type=\"button\" id=\"ti-x\" class=\"ti-btn ti-btn-x\">X \/ NO<\/button>\n                <\/div>\n            <\/div>\n            <div class=\"ti-actions-row\">\n                <button type=\"button\" id=\"ti-new\" class=\"ti-btn\">Nuova<\/button>\n                <button type=\"button\" id=\"ti-copy\" class=\"ti-btn\" style=\"font-size:11px; padding-left:8px; padding-right:8px;\">Copia testo<\/button>\n                <button type=\"button\" id=\"ti-file\" class=\"ti-btn\" onclick=\"window.showUploadChoice('chat')\">File<\/button>\n                <button type=\"button\" id=\"ti-products-btn\" class=\"ti-btn ti-btn-catalog\">Prodotti<\/button>\n                <button type=\"button\" id=\"ti-services-btn\" class=\"ti-btn ti-btn-catalog ti-btn-services\">Servizi<\/button>\n                <button type=\"button\" id=\"ti-activities-btn\" class=\"ti-btn ti-btn-catalog ti-btn-activities\" style=\"display:none !important;\">Attivit\u00e0<\/button>\n                <button type=\"button\" id=\"ti-kbd-btn\" class=\"ti-btn\">Tastiera<\/button>\n                <button type=\"button\" id=\"ti-mic\" class=\"ti-btn\">Mic<\/button>\n                <button type=\"button\" id=\"ti-cam\" class=\"ti-btn\">Cam<\/button>\n                <button type=\"button\" id=\"ti-mute-btn\" class=\"ti-btn\">\ud83d\udd0a ON<\/button>\n                <button type=\"button\" id=\"ti-help-btn\" class=\"ti-help\" title=\"Aiuto\" aria-label=\"Aiuto\">?<\/button>\n            <\/div>\n            <div id=\"ti-admin-actions-row\" class=\"ti-admin-actions-row\" aria-label=\"Comandi amministrativi\"><\/div>\n        <\/div>\n        <div id=\"ti-chat-box\" class=\"ti-chat-box ti-scrollable-plugin ti-chat-after-ai-form-717\" data-ti-placement=\"after-ai-form\"><\/div>\n        <div id=\"ti-order-confirm-host\" class=\"ti-order-confirm-host\"><\/div>\n        <div id=\"ti-ai-response-host\" class=\"ti-ai-response-host\"><\/div>\n        <div class=\"ti-shop-footer\">Target Informatica S.r.l. Via Filippo Turati, 16 05100 Terni \u2013 Italy Tel. +39 0744 288409\u201310<br>P.IVA 00664210556 CCIAA Terni 67219 \u2013 Trib.Terni n. 132\/94 \u00a9 Copyright 2019 Target Informatica S.r.l.<br><a href=\"https:\/\/www.targetinformatica.it\/\" target=\"_blank\" rel=\"noopener\">www.targetinformatica.it<\/a><div class=\"ti-shop-footer-contacts\" style=\"font-size:11px; text-align:center; margin-top:5px; line-height:1.55;\">\n            <div>Info: <a href=\"mailto:SSInfo@targetinformatica.it\">SSInfo@targetinformatica.it<\/a><\/div>\n            <div>Commerciale\/amministrazione: <a href=\"mailto:SSComm@targetinformatica.it\">SSComm@targetinformatica.it<\/a><\/div>\n            <div>Supporto tecnico: <a href=\"mailto:SSSupport@targetinformatica.it\">SSSupport@targetinformatica.it<\/a><\/div>\n        <\/div><\/div>\n\n        <div id=\"ti-conf-ov\" class=\"ti-ov ti-closable-ov\" style=\"z-index: 1000 !important;\">\n            <div class=\"ti-conf-window\">\n                <div class=\"ti-conf-header\">\n                    <h3 id=\"ti-conf-title\" style=\"color:#fff; margin:0; font-size:18px; font-weight:bold;\">Impostazioni Ditta<\/h3>\n                    <div class=\"ti-conf-header-right\">\n                        <span id=\"ti-conf-ref-datetime\" class=\"ti-company-ref-datetime ti-conf-ref-datetime\" title=\"Data e ora sistema ditta\"><\/span>\n                        <div id=\"ti-conf-global-btn\"><\/div>\n                    <\/div>\n                <\/div>\n                <div class=\"ti-conf-body\">\n                    <div id=\"ti-conf-cnt\" class=\"ti-scrollable-plugin\"><\/div>\n                <\/div>\n                <div class=\"ti-conf-footer\">\n                    <button type=\"button\" id=\"lbl-conf-save\" class=\"ti-btn\" style=\"flex:1; background:#374151 !important; color:#fff !important; cursor:pointer !important; font-size:12px !important; border:none !important;\" onpointerdown=\"try{window.tiShowConfigSaveDiag688&&window.tiShowConfigSaveDiag688('inline_pointerdown');}catch(e){}\" onmousedown=\"try{window.tiShowConfigSaveDiag688&&window.tiShowConfigSaveDiag688('inline_mousedown');}catch(e){}\" ontouchstart=\"try{window.tiShowConfigSaveDiag688&&window.tiShowConfigSaveDiag688('inline_touchstart');}catch(e){}\" onclick=\"try{window.tiShowConfigSaveDiag688&&window.tiShowConfigSaveDiag688('inline_onclick_before_save');}catch(e){}; window.saveConfig()\">Salva Modifiche<\/button>\n                                        <button type=\"button\" class=\"ti-btn btn-close\" style=\"flex:1; background:#b91c1c !important; color:#fff !important; cursor:pointer !important; font-size:12px !important; border:none !important;\" onclick=\"window.closeConfigModal()\">Chiudi<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-conf-import-preview-ov\" class=\"ti-ov\">\n            <div class=\"ti-modal\" style=\"width:90%;max-width:760px;text-align:left;\">\n                <h3 style=\"color:#fff;margin-top:0;border-bottom:1px solid #444;padding-bottom:10px;\">Anteprima modifiche import AI<\/h3>\n                <div style=\"font-size:12px;color:#9ca3af;margin-bottom:10px;\">Controlla l'anteprima delle modifiche. L'elenco non verr\u00e0 letto vocalmente. Nessun dato viene salvato finche non premi OK Conferma e salva oppure scrivi CONFERMA IMPORT.<\/div>\n                <div id=\"ti-conf-import-preview-last-update\" style=\"display:none;margin-top:10px;padding:8px 10px;border-radius:8px;border:1px solid #22c55e;background:#052e16;color:#bbf7d0;font-size:12px;font-weight:700;\"><\/div>\n                <div id=\"ti-conf-import-preview-body\" style=\"display:block;margin-top:10px;background:#000;padding:12px;border-radius:8px;border:1px solid #374151;color:#e5e7eb;white-space:normal;max-height:55vh;overflow:auto;\"><\/div>\n                <div id=\"ti-conf-import-refine-box\" style=\"margin-top:12px;padding:10px;border-radius:8px;border:1px solid #1d4ed8;background:#0f172a;\">\n                    <label for=\"ti-conf-import-refine-prompt\" style=\"display:block;font-size:12px;color:#bfdbfe;font-weight:700;margin-bottom:6px;\">Raffina import con AI sulla base della prima anteprima<\/label>\n                    <textarea id=\"ti-conf-import-refine-prompt\" class=\"ti-in\" rows=\"3\" style=\"width:100%;min-height:70px;resize:vertical;\" placeholder=\"Es: usa il prezzo medio del range, sposta il principio attivo nella descrizione, elimina righe duplicate, normalizza categorie...\"><\/textarea>\n                    <div style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:8px;align-items:center;\">\n                        <button type=\"button\" id=\"ti-conf-import-refine-btn\" class=\"ti-btn\" style=\"background:#2563eb;\" onclick=\"window.refineConfigImportPreview()\">Raffina anteprima con AI<\/button>\n                        <span id=\"ti-conf-import-refine-status\" style=\"font-size:12px;color:#93c5fd;\"><\/span>\n                    <\/div>\n                <\/div>\n                <div style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;\">\n                    <button type=\"button\" class=\"ti-btn ti-btn-ok\" onclick=\"window.confirmConfigImportPreview()\">OK Conferma e salva import<\/button>\n                    <button type=\"button\" class=\"ti-btn ti-btn-x\" onclick=\"window.cancelConfigImportPreview()\">X Annulla import<\/button>\n                    <button type=\"button\" class=\"ti-btn\" onclick=\"window.closeConfigImportPreview()\">Chiudi<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-file-manager-ov\" class=\"ti-ov ti-closable-ov\" style=\"z-index: 1001 !important;\">\n            <div class=\"ti-modal\" style=\"width:90%; max-width:600px;\">\n                <h3 style=\"color:#fff;margin-top:0;border-bottom:1px solid #444;padding-bottom:10px;\">Gestione File Associati<\/h3>\n                <div id=\"fm-element-description\" style=\"display:none;margin:0 0 10px 0;padding:10px 12px;border-radius:10px;background:#0f172a;border:1px solid #2563eb;color:#dbeafe;font-size:13px;line-height:1.45;text-align:left;\"><\/div>\n                <div id=\"fm-storage-path\" style=\"display:none;margin:0 0 10px 0;padding:8px 10px;border-radius:8px;background:#020617;border:1px solid #334155;color:#bfdbfe;font-size:11px;word-break:break-all;\"><\/div>\n                <div id=\"fm-grid\" class=\"ti-file-grid\"><\/div>\n                <div id=\"ti-fm-native-wrap-617\" class=\"ti-native-upload-617\" style=\"margin-top:20px;\">\n                    <div class=\"ti-native-upload-row-617\">\n                        <input type=\"file\" id=\"ti-fm-native-file-617\" class=\"ti-native-file-617\" aria-label=\"Scegli file\" style=\"position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;opacity:0;\" tabindex=\"-1\" aria-hidden=\"true\" \/>\n                        <button type=\"button\" id=\"ti-fm-native-upload-btn-617\" class=\"ti-btn\" style=\"background:#2563eb;\" onclick=\"window.tiNativeUpload617 && window.tiNativeUpload617('manager'); return false;\">\ud83d\udcc1 Aggiungi file<\/button>\n                        <button type=\"button\" class=\"ti-btn\" style=\"background:#8b5cf6;\" onclick=\"window.chooseAIManager()\">\ud83e\udd16 Genera con AI<\/button>\n                    <\/div>\n                    <div id=\"ti-fm-native-status-617\" class=\"ti-native-status-617\"><\/div>\n                <\/div>\n                <button type=\"button\" onclick=\"window.closeModal('ti-file-manager-ov')\" class=\"ti-btn btn-close\" style=\"width:100%; margin-top:10px; background:#4b5563;\">Chiudi<\/button>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-login-ov\" class=\"ti-ov ti-closable-ov\">\n            <div class=\"ti-modal\">\n                <h3 id=\"lbl-login\" style=\"color:#fff;margin-top:0;\">Accedi<\/h3>\n                                <form id=\"ti-login-form\" autocomplete=\"on\" method=\"post\">\n                    <input type=\"hidden\" name=\"ti_action\" value=\"ti_ai_login\">\n                    <input type=\"hidden\" name=\"action\" value=\"ti_ai_login\">\n                    <input type=\"hidden\" name=\"ti_mobile_login_redirect\" value=\"1\">\n                    <input type=\"hidden\" id=\"l-db-hidden\" name=\"db\" value=\"\">\n                    <input id=\"l-user\" name=\"username\" class=\"ti-in\" placeholder=\"Username\" autocomplete=\"username\" data-ti-login-username=\"1\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\" inputmode=\"text\">\n                    <input id=\"l-pass\" name=\"password\" class=\"ti-in\" type=\"password\" placeholder=\"Password\" autocomplete=\"current-password\" data-ti-login-password=\"1\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\">\n                    <select id=\"l-db\" name=\"db_select\" class=\"ti-in\" aria-label=\"Ditta\" style=\"margin-top:8px; display:block;\">\n                        <option value=\"\">-- Scegli una ditta --<\/option>\n                                                    <option value=\"Dev_hairstyle.json\" >Dev_hairstyle<\/option>\n                                                    <option value=\"bmw-terni.json\" >bmw-terni<\/option>\n                                                    <option value=\"catering-terni.json\" >catering-terni<\/option>\n                                                    <option value=\"farmaciaterni.json\" >farmaciaterni<\/option>\n                                                    <option value=\"ferramentaterni.json\" >ferramentaterni<\/option>\n                                                    <option value=\"fioraio.json\" >fioraio<\/option>\n                                                    <option value=\"hairstyle.json\" >hairstyle<\/option>\n                                                    <option value=\"hard&amp;soft.json\" >hard&amp;soft<\/option>\n                                                    <option value=\"ristoranteterni.json\" >ristoranteterni<\/option>\n                                                    <option value=\"termoidraulica-italiana.json\" >termoidraulica-italiana<\/option>\n                                            <\/select>\n                    <button type=\"submit\" id=\"l-do\" class=\"ti-btn\" style=\"width:100%; background:#3b82f6; margin-top:10px;\">Entra<\/button>\n                <\/form>\n                <button type=\"button\" id=\"ti-forgot-password-btn\" class=\"ti-btn ti-forgot-password-wide\" style=\"width:100%; min-width:260px; min-height:44px; padding:10px 14px; margin-top:8px; background:#4b5563; color:#fff; font-size:12px; line-height:1.25; white-space:normal;\" onclick=\"if(window.tiForgotPasswordFromLogin) window.tiForgotPasswordFromLogin('modal'); return false;\">Password dimenticata? Cambia tramite email<\/button>\n                <button type=\"button\" id=\"ti-show-direct-login-fallback\" class=\"ti-btn\" style=\"width:100%; margin-top:8px; background:#374151; color:#fff; font-size:12px;\" onclick=\"if(window.revealDirectLoginFallback) window.revealDirectLoginFallback('manuale'); return false;\">Se il login non procede, usa Accesso diretto<\/button>\n                <hr style=\"border-top:1px solid #444; margin:15px 0;\">\n                <button type=\"button\" class=\"ti-btn\" style=\"width:100%; background:#10b981;\" onclick=\"window.closeModal('ti-login-ov'); window.openProfileModal(true);\">Non hai un account? Registrati<\/button>\n                <button type=\"button\" onclick=\"window.closeModal('ti-login-ov')\" class=\"ti-btn btn-close\" style=\"width:100%; margin-top:10px;\">Chiudi<\/button>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-new-db-ov\" class=\"ti-ov ti-closable-ov\">\n            <div class=\"ti-modal\" style=\"width:90%;max-width:450px;text-align:left;\">\n                <h3 style=\"color:#fff;margin-top:0;text-align:center;border-bottom:1px solid #444;padding-bottom:10px;\">Crea Nuova Ditta<\/h3>\n                <div class=\"ti-new-db-info-top\" style=\"font-size:12px;color:#d1fae5;text-align:left;line-height:1.45;background:#052e25;border:1px solid #065f46;border-radius:10px;padding:10px;margin:0 0 12px 0;\">\n                    <p style=\"margin:0 0 8px 0;\">Alla fine del processo di creazione della ditta, verr\u00e0 generato l'archivio e farai l'accesso automatico a te riservato come configuratore.<\/p>\n                    <p style=\"margin:0 0 8px 0;\">Il servizio sar\u00e0 <b>GRATUITO di prova per 30 giorni senza impegno<\/b>, poi potrai eventualmente attivare la ditta alle condizioni commerciali previste per il servizio Shop &amp; Service disponibili direttamente durante la configurazione ed il periodo di prova.<\/p>\n                    <p style=\"margin:0;\">Trascorso il periodo di prova di 30 giorni, potrai comunque mantenere i dati in attesa del da farsi per ulteriori 30 giorni o cancellarli definitivamente.<\/p>\n                <\/div>\n                <form onsubmit=\"event.preventDefault(); window.createDbSubmit();\">\n                    <label style=\"font-size:11px;color:#aaa;\">Nome della nuova Ditta*<\/label>\n                    <input id=\"nd-name\" class=\"ti-in\" style=\"margin-top:2px;\" placeholder=\"Es: Pizzeria Roma\" required>\n                    <h4 style=\"color:#38bdf8; margin:15px 0 5px 0; font-size:14px;\">Primo Utente (Configuratore)<\/h4>\n                    <label style=\"font-size:11px;color:#aaa;\">Username*<\/label>\n                    <input id=\"nd-user\" class=\"ti-in\" style=\"margin-top:2px;\" placeholder=\"Es: admin_roma\" required>\n                    <label style=\"font-size:11px;color:#aaa;\">Password*<\/label>\n                    <input id=\"nd-pass\" type=\"password\" class=\"ti-in\" style=\"margin-top:2px;\" placeholder=\"***\" required>\n                    <label style=\"font-size:11px;color:#aaa;\">Email (Opzionale)<\/label>\n                    <input id=\"nd-email\" type=\"email\" class=\"ti-in\" style=\"margin-top:2px;\" placeholder=\"info@pizzeriaroma.it\">\n                    <button type=\"submit\" class=\"ti-btn\" style=\"width:100%; background:#10b981; margin-top:10px; font-weight:bold; font-size:15px; padding:10px;\">Crea e Accedi<\/button>\n                <\/form>\n                <a href=\"https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service\/Shop-Service%20Manual.pdf\" target=\"_blank\" rel=\"noopener\" class=\"ti-btn ti-new-db-manual-bottom\" style=\"width:100%;box-sizing:border-box;background:#059669;color:#fff;text-decoration:none;text-align:center;margin:8px 0 0 0;font-weight:bold;display:block;\">Manuale operativo Shop &amp; Service<\/a>\n                <button type=\"button\" onclick=\"window.closeModal('ti-new-db-ov')\" class=\"ti-btn btn-close\" style=\"width:100%; margin-top:5px;\">Annulla<\/button>\n            <\/div>\n        <\/div>\n        \n        <div id=\"ti-profile-ov\" class=\"ti-ov ti-closable-ov\">\n            <div class=\"ti-modal ti-scrollable-plugin\" style=\"width:90%;max-width:500px;text-align:left;\">\n                <h3 id=\"lbl-profile-title\" style=\"color:#fff;margin-top:0;text-align:center;border-bottom:1px solid #444;padding-bottom:10px;\">Profilo Utente<\/h3>\n                <form id=\"ti-profile-form\" autocomplete=\"off\" onsubmit=\"event.preventDefault(); window.saveProfile();\">\n                    <div style=\"display:flex; gap:15px; align-items:center; margin-bottom:15px;\">\n                        <img decoding=\"async\" id=\"p-foto-preview\" src=\"\" style=\"width:60px;height:60px;border-radius:50%;background:#000;display:none;object-fit:cover;border:2px solid #38bdf8;\">\n                        <input type=\"hidden\" id=\"p-foto\" value=\"\">\n                        <button type=\"button\" class=\"ti-btn\" style=\"flex:1;\" onclick=\"window.showUploadChoice('p-foto')\">\ud83d\udcf7 Scegli Avatar \/ Logo<\/button>\n                    <\/div>\n                    \n                    <div class=\"ti-grid-2\">\n                        <div><label>Utente (Nome Cognome)*<\/label><input id=\"p-utente\" class=\"ti-in\" required autocomplete=\"off\" autocapitalize=\"words\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        <div><label>Username (Accesso)*<\/label><input id=\"p-user\" class=\"ti-in\" required autocomplete=\"off\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        \n                        <div><label id=\"lbl-p-pass\">Password*<\/label><input id=\"p-pass\" type=\"password\" class=\"ti-in\" placeholder=\"***\" autocomplete=\"new-password\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        <div><label>Email<\/label><input id=\"p-email\" type=\"email\" class=\"ti-in\" autocomplete=\"off\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        \n                        <div><label>Ragione Sociale<\/label><input id=\"p-ragione_sociale\" class=\"ti-in\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        <div><label>Indirizzo<\/label><input id=\"p-ind\" class=\"ti-in\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        \n                        <div><label>P.IVA \/ VAT<\/label><input id=\"p-piva_vat\" class=\"ti-in\" autocomplete=\"off\" autocapitalize=\"characters\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        <div><label>Codice Fiscale<\/label><input id=\"p-cod_fisc\" class=\"ti-in\" autocomplete=\"off\" autocapitalize=\"characters\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        \n                        <div><label>Codice ADE \/ SDI<\/label><input id=\"p-ade\" class=\"ti-in\" autocomplete=\"off\" autocapitalize=\"characters\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        <div><label>Telefono<\/label><input id=\"p-tel\" class=\"ti-in\" autocomplete=\"off\" autocapitalize=\"none\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        \n                        <div><label>Sesso<\/label><input id=\"p-sesso\" class=\"ti-in\" placeholder=\"M\/F\" autocomplete=\"off\" autocapitalize=\"characters\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        <div><label>Et\u00e0<\/label><input id=\"p-eta\" type=\"number\" class=\"ti-in\" value=\"0\" min=\"0\" autocomplete=\"off\"><\/div>\n\n                        <div><label>Dato 1<\/label><input id=\"p-dato_1\" class=\"ti-in\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        <div><label>Dato 2<\/label><input id=\"p-dato_2\" class=\"ti-in\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        \n                        <div style=\"grid-column: span 2;\"><label>Dato 3<\/label><input id=\"p-dato_3\" class=\"ti-in\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\"><\/div>\n                        \n                        <div style=\"grid-column: span 2;\">\n                            <label style=\"border-bottom:1px solid #444; padding-bottom:5px; margin-bottom:5px;\">Consensi<\/label>\n                            <div style=\"display:grid; grid-template-columns: 1fr 1fr; gap:5px;\">\n                                <label class=\"ti-cb-wrap\"><input type=\"checkbox\" id=\"p-com_mail\" checked> Com Mail<\/label>\n                                <label class=\"ti-cb-wrap\"><input type=\"checkbox\" id=\"p-com_email\" checked> Com Email<\/label>\n                                <label class=\"ti-cb-wrap\"><input type=\"checkbox\" id=\"p-com_sms\" checked> Com SMS<\/label>\n                                <label class=\"ti-cb-wrap\"><input type=\"checkbox\" id=\"p-com_tel\" checked> Com Tel<\/label>\n                            <\/div>\n                        <\/div>\n                        \n                        <div style=\"grid-column: span 2;\">\n                            <label>Note aggiuntive<\/label>\n                            <textarea id=\"p-nota\" class=\"ti-in\" rows=\"2\" autocomplete=\"off\" autocorrect=\"off\" spellcheck=\"false\"><\/textarea>\n                        <\/div>\n                    <\/div>\n\n                    <div class=\"ti-profile-actions\" style=\"display:grid; grid-template-columns:1fr 1fr 1fr; gap:8px; margin-top:10px; align-items:stretch; justify-content:stretch;\">\n                        <button type=\"submit\" id=\"ti-profile-save-btn\" class=\"ti-btn ti-profile-btn-eq\" style=\"width:100%; background:#10b981 !important; color:#fff !important; font-weight:bold; font-size:15px; padding:0 12px !important; min-height:46px !important; height:46px !important; line-height:1 !important; display:flex !important; align-items:center !important; justify-content:center !important; box-sizing:border-box; white-space:nowrap;\">Salva<\/button>\n                        <button type=\"button\" onclick=\"window.closeModal('ti-profile-ov')\" class=\"ti-btn btn-close ti-profile-btn-eq\" style=\"width:100%; background:#374151 !important; color:#fff !important; font-size:15px; font-weight:bold; padding:0 12px !important; min-height:46px !important; height:46px !important; line-height:1 !important; display:flex !important; align-items:center !important; justify-content:center !important; box-sizing:border-box; white-space:nowrap;\">Chiudi<\/button>\n                        <button type=\"button\" id=\"ti-profile-cancel-btn\" onclick=\"window.cancelProfile()\" class=\"ti-btn ti-profile-btn-eq\" style=\"width:100%; background:#b91c1c !important; color:#fff !important; font-size:15px; font-weight:bold; padding:0 12px !important; min-height:46px !important; height:46px !important; line-height:1 !important; display:flex !important; align-items:center !important; justify-content:center !important; box-sizing:border-box; white-space:nowrap;\">Cancella<\/button>\n                    <\/div>\n                <\/form>\n            <\/div>\n        <\/div>\n\n\n        <div id=\"ti-report-ov\" class=\"ti-ov ti-closable-ov\" style=\"z-index:1002 !important;\">\n            <div class=\"ti-modal\" style=\"width:95%;max-width:1200px;text-align:left;\">\n                <h3 id=\"ti-report-title\" style=\"color:#fff;margin-top:0;border-bottom:1px solid #374151;padding-bottom:10px;\">\ud83d\udcca Report Attivit\u00e0<\/h3>\n                <div id=\"ti-report-cnt\"><\/div>\n                <div style=\"display:flex;gap:8px;margin-top:12px;\">\n                    <button type=\"button\" class=\"ti-btn btn-close\" style=\"flex:1;background:#4b5563;\" onclick=\"window.closeModal('ti-report-ov')\">Chiudi<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-global-action-ov\" class=\"ti-ov ti-closable-ov\">\n            <div class=\"ti-modal\" style=\"width:90%;max-width:500px;\">\n                <h3 style=\"color:#fff;margin-top:0;\">\ud83e\ude84 Modifica Globale Database<\/h3>\n                <p style=\"font-size:12px; color:#aaa; margin-bottom:10px;\">Chiedi all'AI di modificare dati in <b>TUTTE le ditte esistenti<\/b> contemporaneamente.<\/p>\n                <div style=\"display:flex;gap:6px;align-items:stretch;margin-bottom:10px;\">\n                    <textarea id=\"global-action-prompt\" class=\"ti-in\" rows=\"3\" placeholder=\"Es: Elimina la parola Terni dai nomi delle ditte...\" style=\"flex:1;margin:0;min-width:0;\"><\/textarea>\n                    <button type=\"button\" id=\"global-action-template-btn\" class=\"ti-btn\" title=\"Scrivi comando: Adegua struttura database a template shopservicemain.json\" style=\"width:58px;min-width:58px;padding:6px 4px;background:#0f766e;color:#fff;font-size:10px;line-height:1.1;font-weight:800;white-space:normal;\" onclick=\"var el=document.getElementById('global-action-prompt'); if(el){el.value='Adegua struttura database a template shopservicemain.json'; el.focus(); try{el.dispatchEvent(new Event('input',{bubbles:true}));}catch(e){} try{el.dispatchEvent(new Event('change',{bubbles:true}));}catch(e){} } return false;\">Template<\/button>\n                <\/div>\n                <button type=\"button\" id=\"global-action-btn\" class=\"ti-btn\" style=\"width:100%;background:#8b5cf6;padding:10px;font-weight:bold;margin-bottom:10px;\" onclick=\"window.doGlobalAction()\">Esegui Modifica su Tutti i DB<\/button>\n                <div id=\"global-action-progress-wrap\" style=\"display:none; margin-bottom:10px;\">\n                    <div style=\"height:12px; background:#1f2937; border:1px solid #374151; border-radius:999px; overflow:hidden;\">\n                        <div id=\"global-action-progress-bar\" style=\"width:0%; height:100%; background:linear-gradient(90deg,#8b5cf6,#38bdf8);\"><\/div>\n                    <\/div>\n                    <div id=\"global-action-progress-text\" style=\"margin-top:6px; font-size:12px; color:#cbd5e1;\">0% - In attesa di avvio...<\/div>\n                <\/div>\n                <div id=\"global-action-res\" style=\"display:none; text-align:left; background:#222; padding:10px; border-radius:8px; border:1px solid #444; color:#10b981; font-size:12px; margin-bottom:10px; max-height:150px; overflow-y:auto; white-space:pre-wrap;\"><\/div>\n                <button type=\"button\" id=\"ti-clean-orphans-global-btn\" class=\"ti-btn\" style=\"display:none;width:100%;background:#7c2d12;color:#fff;border:1px solid #9a3412;font-size:11px;margin-bottom:10px;padding:10px;font-weight:bold;\" onclick=\"if(window.runGlobalOrphanFileCleanup) window.runGlobalOrphanFileCleanup(); return false;\">\ud83e\uddf9 <b>Pulisci file non associati - Globale<\/b><\/button>\n                <button type=\"button\" class=\"ti-btn btn-close\" style=\"width:100%;background:#4b5563;\" onclick=\"window.closeModal('ti-global-action-ov')\">Chiudi<\/button>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-up-choice-ov\" class=\"ti-ov ti-closable-ov\">\n            <div class=\"ti-modal\" style=\"width:min(46vw,410px);max-width:min(46vw,410px);min-width:280px;\">\n                <h3 style=\"color:#fff;margin-top:0;\">Carica Immagine\/File<\/h3>\n                <div id=\"ti-main-native-wrap-617\" class=\"ti-native-upload-617\" style=\"margin-bottom:10px;\">\n                    <input type=\"file\" id=\"ti-main-native-file-617\" class=\"ti-native-file-617\" aria-label=\"Scegli file dal dispositivo\" style=\"position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;opacity:0;\" tabindex=\"-1\" aria-hidden=\"true\" \/>\n                    <button type=\"button\" id=\"ti-main-native-upload-btn-617\" class=\"ti-btn\" style=\"width:100%;margin-top:8px;background:#2563eb;padding:12px;font-size:14px;\" onclick=\"window.tiNativeUpload617 && window.tiNativeUpload617('main'); return false;\">\ud83d\udcf1 Carica da dispositivo\/supporto esterno<\/button>\n                    <div id=\"ti-main-native-status-617\" class=\"ti-native-status-617\"><\/div>\n                <\/div>\n                <button type=\"button\" class=\"ti-btn\" style=\"width:100%;margin-bottom:10px;background:#059669;padding:12px;font-size:14px;\" onclick=\"window.chooseAI()\">\ud83e\udd16 Cerca \/ Genera con AI<\/button>\n                <button type=\"button\" class=\"ti-btn btn-close\" style=\"width:100%;background:#4b5563;padding:10px;\" onclick=\"window.closeModal('ti-up-choice-ov')\">Annulla<\/button>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-ai-gen-ov\" class=\"ti-ov ti-closable-ov\" style=\"z-index: 1002 !important;\">\n            <div class=\"ti-modal\" style=\"width:90%;max-width:500px;\">\n                <h3 style=\"color:#fff;margin-top:0;\">\ud83e\udd16 AI Image Generator<\/h3>\n                <textarea id=\"ai-gen-prompt\" class=\"ti-in\" rows=\"3\" placeholder=\"Cosa ti serve? (es. Un logo per una pizzeria con forno a legna)...\"><\/textarea>\n                <div style=\"font-size:12px;color:#cbd5e1;margin:8px 0 6px 0;\">Istruzioni di dettaglio per la AI (opzionale). Esempio: <i>stile elegante, sfondo chiaro, inquadratura ravvicinata, foto realistica<\/i>.<\/div>\n                <textarea id=\"ai-gen-detail\" class=\"ti-in\" rows=\"3\" placeholder=\"Indica eventuali istruzioni di dettaglio per la generazione della foto\"><\/textarea>\n                <button type=\"button\" id=\"ai-gen-btn\" class=\"ti-btn\" style=\"width:100%;background:#8b5cf6;padding:10px;font-weight:bold;margin-bottom:10px;\" onclick=\"window.doAIGen()\">Genera Immagine<\/button>\n                <div id=\"ai-gen-res\" style=\"display:none; text-align:center;\">\n                    <img decoding=\"async\" id=\"ai-gen-img\" src=\"\" style=\"max-width:100%;border-radius:8px;margin-bottom:10px;max-height:300px;object-fit:contain;\">\n                    <button type=\"button\" class=\"ti-btn\" style=\"width:100%;background:#10b981;padding:10px;font-weight:bold;margin-bottom:5px;\" onclick=\"window.useAIGen()\">\u2705 Usa questa Immagine<\/button>\n                    <button type=\"button\" class=\"ti-btn\" style=\"width:100%;background:#2563eb;padding:8px;\" onclick=\"window.resetAIGen()\">\ud83d\udd04 Riprova<\/button>\n                <\/div>\n                <button type=\"button\" class=\"ti-btn btn-close\" style=\"width:100%;background:#4b5563;margin-top:10px;\" onclick=\"window.closeModal('ti-ai-gen-ov')\">Chiudi<\/button>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-col-help-ov\" class=\"ti-ov ti-closable-ov\">\n            <div class=\"ti-modal\" style=\"width:90%;max-width:500px;text-align:left;\">\n                <h3 id=\"col-help-title\" style=\"color:#fff;margin-top:0;border-bottom:1px solid #444;padding-bottom:8px;\">Aiuto Colonna<\/h3>\n                <div id=\"col-help-company\" style=\"display:none;text-align:center;color:#e5e7eb;font-size:12px;line-height:1.35;margin:-4px 0 10px 0;padding-bottom:10px;border-bottom:1px solid #374151;\"><\/div>\n                <div id=\"col-help-content\" style=\"background:#222; padding:15px; border-radius:8px; border:1px solid #444; color:#ccc; font-size:13px; min-height:80px; max-height:300px; overflow-y:auto; white-space:pre-wrap;\">\n                    \u23f3 Cerco nel manuale...\n                <\/div>\n                <div style=\"display:flex;gap:8px;margin-top:15px;flex-wrap:wrap;\">\n                    <button type=\"button\" id=\"ti-col-help-copy\" onclick=\"window.copyColumnHelpText357 && window.copyColumnHelpText357()\" class=\"ti-btn\" style=\"flex:1;min-width:120px;background:#2563eb;color:#fff;font-size:11px;padding:8px 10px;\">Copia testo<\/button>\n                    <button type=\"button\" onclick=\"window.closeModal('ti-col-help-ov')\" class=\"ti-btn btn-close\" style=\"flex:1;min-width:120px;background:#4b5563;\">Chiudi<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-cam-ov\" class=\"ti-ov ti-closable-ov\"><div class=\"ti-modal\" style=\"width:90%;max-width:500px\">\n            <h3 id=\"lbl-cam-title\" style=\"color:#fff\">Fotocamera<\/h3>\n            <video id=\"ti-video-feed\" style=\"width:100%;border-radius:8px;background:#000\" autoplay playsinline><\/video>\n            <canvas id=\"ti-canvas-snap\" style=\"display:none\"><\/canvas>\n            <button type=\"button\" id=\"lbl-cam-snap\" class=\"ti-btn\" style=\"width:100%;margin-top:10px;background:#10b981\" onclick=\"window.snapPhoto()\">Scatta Foto<\/button>\n            <button type=\"button\" onclick=\"window.closeCamModal()\" class=\"ti-btn btn-close\" style=\"width:100%;margin-top:5px;background:#b91c1c\">Chiudi<\/button>\n        <\/div><\/div>\n\n        <div id=\"ti-ateco-ov\" class=\"ti-ov ti-closable-ov\"><div class=\"ti-modal\" style=\"width:90%;max-width:500px;\">\n            <h3 style=\"color:#fff; margin-top:0;\">Ricerca Codice ATECO<\/h3>\n            <p style=\"font-size:12px; color:#aaa;\">Descrivi la tua attivit\u00e0 e l'IA trover\u00e0 i codici pi\u00f9 adatti.<\/p>\n            <div style=\"display:flex; gap:5px; margin-bottom:10px;\">\n                <input type=\"text\" id=\"ateco-in\" class=\"ti-in\" style=\"margin:0; padding:8px;\" placeholder=\"Es: Pizzeria da asporto\">\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;\" onclick=\"window.searchAteco()\">Cerca<\/button>\n            <\/div>\n            <input type=\"hidden\" id=\"ateco-target-id\" value=\"\">\n            <div id=\"ateco-res\" style=\"background:#222; padding:10px; border-radius:8px; border:1px solid #444; color:#ccc; font-size:12px; min-height:60px; max-height:200px; overflow-y:auto; text-align:left;\">\n                <i>Risultati della ricerca...<\/i>\n            <\/div>\n            <button type=\"button\" onclick=\"window.closeModal('ti-ateco-ov')\" class=\"ti-btn btn-close\" style=\"width:100%; margin-top:15px; background:#4b5563;\">Chiudi<\/button>\n        <\/div><\/div>\n\n        <div id=\"ti-kbd-ov\" class=\"ti-ov ti-closable-ov\"><div class=\"ti-modal ti-kbd-modal\" style=\"width:96vw; max-width:760px;\"><h3 id=\"lbl-kbd\" style=\"color:#fff;\">Tastiera<\/h3><input type=\"text\" id=\"ti-kbd-prev\" class=\"ti-in\" style=\"font-size:18px; margin-bottom:10px; background:#000;\" readonly><div id=\"ti-kbd-cnt\" class=\"ti-kbd\"><\/div><div style=\"display:flex; gap:5px; margin-top:10px;\"><button type=\"button\" id=\"ti-kbd-back\" class=\"ti-btn\" style=\"flex:1; background:#b91c1c;\">\u232b<\/button><button type=\"button\" id=\"ti-kbd-ok\" class=\"ti-btn\" style=\"flex:1; background:#059669;\">OK<\/button><button type=\"button\" onclick=\"window.closeModal('ti-kbd-ov')\" class=\"ti-btn btn-close\" style=\"flex:1;\">Chiudi<\/button><\/div><\/div><\/div>\n        \n        <div id=\"ti-help-ov\" class=\"ti-ov ti-closable-ov\"><div class=\"ti-modal\" style=\"text-align:left;\">\n            <h3 id=\"help-title\" style=\"color:#fff; border-bottom:1px solid #555; padding-bottom:10px; margin-top:0;\">Shop & Service<\/h3>\n            <div id=\"ti-help-company\" class=\"ti-help-company-main\" style=\"display:none;text-align:center;color:#e5e7eb;font-size:12px;line-height:1.38;margin:0 0 10px 0;padding:0 0 10px 0;border-bottom:1px solid #374151;\"><\/div>\n            <p style=\"color:#ccc; font-size:13px; margin:5px 0;\"><b>Versione:<\/b> <span id=\"help-ver\"><\/span><\/p>\n            <div id=\"help-email\" style=\"margin-bottom:15px;\"><\/div><div style=\"font-size:12px;color:#9ca3af;margin-bottom:10px;\">Nota manuale AI: tutti i campi Istruzioni nel database sono istruzioni operative dedicate alla AI e devono essere eseguite quando applicabili.<\/div><hr style=\"border:0; border-top:1px solid #444; margin:10px 0;\">\n            <p style=\"color:#38bdf8; font-size:12px; margin-bottom:5px;\"><b>Chiedi all'Assistente Shop & Service:<\/b><\/p>\n            <div style=\"display:flex; gap:5px; margin-bottom:10px;\">\n                <input type=\"text\" id=\"help-bot-in\" class=\"ti-in\" style=\"margin:0; padding:8px;\" placeholder=\"Es: Come prenoto?\">\n                <button type=\"button\" id=\"help-bot-send\" class=\"ti-btn\" style=\"background:#2563eb;\" onclick=\"window.sendHelpChat()\">\u279c<\/button>\n            <\/div>\n            <div id=\"help-bot-reply\" style=\"background:#222; padding:10px; border-radius:8px; border:1px solid #444; margin-bottom:12px; color:#ccc; font-size:12px; min-height:40px; max-height:150px; overflow-y:auto;\">\n                <i>Scrivi la tua domanda qui sopra per ricevere assistenza AI...<\/i>\n            <\/div>\n            <button type=\"button\" id=\"ti-help-support-toggle\" class=\"ti-btn\" style=\"width:100%; background:#0f766e; margin-bottom:10px;\">\ud83d\udee0\ufe0f Segnalazione assistenza tecnica<\/button>\n            <div id=\"ti-help-support-box\" style=\"display:none; margin-bottom:14px; background:#0b1220; border:1px solid #0f766e; border-radius:10px; padding:12px;\">\n                <div style=\"font-size:12px;color:#cbd5e1;margin-bottom:8px;\">Invia una segnalazione all\u2019assistenza tecnica. Se disponibile, verr\u00e0 allegata anche la copia dell\u2019ultima chat in corso.<\/div>\n                <label for=\"ti-support-email\" style=\"display:block;font-size:12px;color:#cbd5e1;margin-bottom:4px;\">Email mittente (opzionale)<\/label>\n                <input type=\"email\" id=\"ti-support-email\" class=\"ti-in\" style=\"margin:0 0 8px 0;\" placeholder=\"nome@dominio.it\">\n                <label for=\"ti-support-subject\" style=\"display:block;font-size:12px;color:#cbd5e1;margin-bottom:4px;\">Oggetto *<\/label>\n                <input type=\"text\" id=\"ti-support-subject\" class=\"ti-in\" style=\"margin:0 0 8px 0;\" placeholder=\"Oggetto della segnalazione\">\n                <label for=\"ti-support-message\" style=\"display:block;font-size:12px;color:#cbd5e1;margin-bottom:4px;\">Segnalazione *<\/label>\n                <textarea id=\"ti-support-message\" class=\"ti-in\" rows=\"5\" style=\"margin:0 0 8px 0;\" placeholder=\"Descrivi il problema o il suggerimento\"><\/textarea>\n                <div style=\"font-size:11px;color:#94a3b8;margin-bottom:10px;\">Destinatario: <b>Shop & Service<\/b>. In copia verr\u00e0 usata la tua e.mail se indicata o rilevata.<\/div>\n                <button type=\"button\" id=\"ti-support-send\" class=\"ti-btn\" style=\"width:100%; background:#2563eb;\">Invia segnalazione<\/button>\n            <\/div>\n            <div style=\"display:flex; gap:10px;\">\n                <a href=\"https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service\/Shop-Service%20Manual.pdf\" target=\"_blank\" class=\"ti-btn\" style=\"flex:1; text-align:center; background:#059669; color:#fff; text-decoration:none; line-height:24px; font-size:13px;\" id=\"btn-manual\">\ud83d\udcc4 MANUALE PDF<\/a>\n                <button type=\"button\" onclick=\"window.closeModal('ti-help-ov')\" class=\"ti-btn btn-close\" style=\"flex:1; background:#4b5563;\">Chiudi<\/button>\n            <\/div>\n        <\/div><\/div>\n\n        <div id=\"ti-img-ov\" class=\"ti-ov ti-closable-ov\" onclick=\"window.closeModal('ti-img-ov')\" style=\"z-index: 2147483647 !important;\">\n            <div class=\"ti-modal\" style=\"background:transparent; border:none; box-shadow:none; padding:0; width:auto; max-height:100%;\">\n                <img decoding=\"async\" id=\"ti-full-img\" style=\"max-width:95vw; max-height:90vh; border-radius:8px; box-shadow:0 0 20px rgba(0,0,0,0.8);\" src=\"\" loading=\"lazy\">\n            <\/div>\n        <\/div>\n\n        <div id=\"ti-order-item-ov\" class=\"ti-ov ti-closable-ov\" style=\"z-index:2147483647 !important;\">\n            <div class=\"ti-modal\" style=\"width:92vw;max-width:820px;\">\n                <h3 id=\"ti-order-item-title\" style=\"color:#fff;margin-top:0;margin-bottom:14px;\">Dettaglio voce<\/h3>\n                <div class=\"ti-order-modal-grid\">\n                    <div><img decoding=\"async\" id=\"ti-order-item-img\" class=\"ti-order-modal-img\" src=\"\" loading=\"lazy\"><\/div>\n                    <div>\n                        <div id=\"ti-order-item-name\" style=\"font-size:18px;font-weight:800;color:#fff;margin-bottom:8px;\"><\/div>\n                        <div id=\"ti-order-item-caption\" class=\"ti-order-modal-caption\"><\/div>\n                        <div class=\"ti-order-modal-line\">Prezzo: <span id=\"ti-order-item-price\"><\/span><\/div>\n                        <div class=\"ti-order-modal-line\">Importo: <b id=\"ti-order-item-total\"><\/b><\/div>\n                        <div class=\"ti-order-modal-line\">Quantit\u00e0: <span class=\"ti-qty-stepper\"><button type=\"button\" class=\"ti-qty-btn\" onclick=\"window.stepQtyInput(this,-1)\">-<\/button><input type=\"text\" inputmode=\"decimal\" id=\"ti-order-item-qty\" class=\"ti-in\" style=\"max-width:140px;display:inline-block;margin-left:0;\"><button type=\"button\" class=\"ti-qty-btn\" onclick=\"window.stepQtyInput(this,1)\">+<\/button><\/span><\/div>\n                        <div id=\"ti-order-item-files\" class=\"ti-order-modal-files\" style=\"display:none;\"><\/div>\n                        <div class=\"ti-order-modal-actions\">\n                            <button type=\"button\" class=\"ti-btn ti-btn-ok\" style=\"flex:1;\" onclick=\"window.confirmOrderItemPopup()\">OK<\/button>\n                            <button type=\"button\" class=\"ti-btn btn-close\" style=\"flex:1;\" onclick=\"window.closeOrderItemPopup()\">Esci<\/button>\n                        <\/div>\n                    <\/div>\n                <\/div>\n            <\/div>\n        <\/div>\n\n        <input type=\"file\" id=\"ti-cam-in\" accept=\"image\/*\" capture=\"environment\" style=\"display:none;\" onchange=\"window.handleFileSelect(event)\">\n    <\/div>\n<script>\n\n\n    \/* 30.9.664 - Guard localStorage: evita blocchi JS se browser\/cache lo nega *\/\n    (function(){\n        try {\n            var t = window.localStorage;\n            if (t) { var k='__ti_ss_ls_test__'; t.setItem(k,'1'); t.removeItem(k); }\n        } catch(e) {\n            try {\n                var mem = {};\n                Object.defineProperty(window, 'localStorage', { configurable:true, value:{\n                    getItem:function(k){ return Object.prototype.hasOwnProperty.call(mem,k) ? mem[k] : null; },\n                    setItem:function(k,v){ mem[k] = String(v); },\n                    removeItem:function(k){ delete mem[k]; },\n                    clear:function(){ mem = {}; },\n                    key:function(i){ return Object.keys(mem)[i] || null; },\n                    get length(){ return Object.keys(mem).length; }\n                }});\n            } catch(ignore) {}\n        }\n    })();\n\n    \/\/ Protezione pagina Shop & Service da errori ripetuti di script esterni di navigazione.\n    \/\/ navigazione.js puo' usare .style su elementi non presenti nella pagina Shop & Service:\n    \/\/ il guard evita che l'errore sporchi la console o interrompa le funzioni del plugin.\n    (function installTiNavigationJsSafetyNet() {\n        if (window.__tiNavigationJsSafetyNetInstalled) return;\n        window.__tiNavigationJsSafetyNetInstalled = true;\n\n        function isNavigationStyleNullError(message, source) {\n            message = String(message || '');\n            source = String(source || '');\n            return \/(?:^|\\\/)navigazione\\.js(?:\\?|$)\/i.test(source)\n                && \/Cannot read properties of null\/i.test(message)\n                && \/\\bstyle\\b\/i.test(message);\n        }\n\n        const previousOnError = window.onerror;\n        window.onerror = function(message, source, lineno, colno, error) {\n            if (isNavigationStyleNullError(message, source)) return true;\n            if (typeof previousOnError === 'function') {\n                return previousOnError.apply(this, arguments);\n            }\n            return false;\n        };\n\n        window.addEventListener('error', function(evt) {\n            const message = evt && (evt.message || (evt.error && evt.error.message));\n            const source = evt && (evt.filename || (evt.error && evt.error.fileName));\n            if (isNavigationStyleNullError(message, source)) {\n                if (evt.preventDefault) evt.preventDefault();\n                if (evt.stopImmediatePropagation) evt.stopImmediatePropagation();\n                return true;\n            }\n        }, true);\n\n        function isSafeIgnoredScrollError(error) {\n            const message = error && error.message ? String(error.message) : String(error || '');\n            return \/Cannot read properties of null\/i.test(message) && \/\\bstyle\\b\/i.test(message);\n        }\n\n        function wrapWindowOnScroll() {\n            const handler = window.onscroll;\n            if (typeof handler !== 'function' || handler.__tiNavigationSafeWrapped) return;\n            const wrapped = function() {\n                try {\n                    return handler.apply(this, arguments);\n                } catch (error) {\n                    if (isSafeIgnoredScrollError(error)) return false;\n                    throw error;\n                }\n            };\n            wrapped.__tiNavigationSafeWrapped = true;\n            wrapped.__tiOriginalScrollHandler = handler;\n            window.onscroll = wrapped;\n        }\n\n        wrapWindowOnScroll();\n        document.addEventListener('DOMContentLoaded', wrapWindowOnScroll);\n        window.addEventListener('load', wrapWindowOnScroll);\n        let navGuardAttempts = 0;\n        const navGuardTimer = window.setInterval(function() {\n            wrapWindowOnScroll();\n            navGuardAttempts++;\n            if (navGuardAttempts >= 30) window.clearInterval(navGuardTimer);\n        }, 500);\n    })();\n\n    \/\/ Inizializzazione variabili globali sicura\n    window.gE = function(id) { return document.getElementById(id); };\n    const gE = window.gE;\n\n    window.tiIsMobileLike = function() {\n        try {\n            return !!(\n                (window.matchMedia && (window.matchMedia('(max-width: 900px)').matches || window.matchMedia('(pointer: coarse)').matches || window.matchMedia('(hover: none)').matches)) ||\n                ('ontouchstart' in window) ||\n                (navigator && navigator.maxTouchPoints && navigator.maxTouchPoints > 0)\n            );\n        } catch(e) {\n            return !!('ontouchstart' in window);\n        }\n    };\n\n    window.tiUrl = window.location.href.split('?')[0]; \n    window.tiAjaxUrl = \"https:\/\/www.tisoft.it\/wp-admin\/admin-ajax.php\";\n    window.updateMobileViewportHeight = function() {\n        try {\n            const vh = (window.visualViewport && window.visualViewport.height ? window.visualViewport.height : window.innerHeight) * 0.01;\n            const root = document.getElementById('ti-ai-outer') || document.documentElement;\n            if (root && root.style) root.style.setProperty('--ti-vh', vh + 'px');\n            if (document.documentElement && document.documentElement.style) document.documentElement.style.setProperty('--ti-vh', vh + 'px');\n            if (document.body && document.body.style) document.body.style.setProperty('--ti-vh', vh + 'px');\n        } catch(e) {}\n    };\n    try { window.updateMobileViewportHeight(); window.addEventListener('resize', window.updateMobileViewportHeight, {passive:true}); if (window.visualViewport) window.visualViewport.addEventListener('resize', window.updateMobileViewportHeight, {passive:true}); } catch(e) {}\n    window.tiForced = \"\"; \n    window.tiForcedActivityQr = \"\"; \n    window.tiUser = \"\"; \n    window.tiRole = \"\"; \n    window.tiConfigSaveLogAllowed = false;\n    window.tiIsPrivilegedUploadUser = false;\n    window.tiDemoReadonly = false;\n    window.tiDemoReadonlyMessage = \"Accesso Demo in sola lettura: l utente ssdemo puo visualizzare la configurazione e usare la chat AI come utente base, ma non puo modificare, aggiungere, cancellare o salvare dati.\";\n    window.tiUserFoto = \"\"; window.tiUserFotoDb = \"\"; window.tiSessionDb = \"\"; \n    window.tiUserDisplay = \"\"; \n    window.tiVersion = \"30.9.947\";\n    window.tiVersionDateTime = \"2026-07-08 09:00 CEST\";\n    window.tiProducerEmails = {\"info\":\"SSInfo@targetinformatica.it\",\"comm\":\"SSComm@targetinformatica.it\",\"support\":\"SSSupport@targetinformatica.it\"};\n    window.tiShopServiceActivationProductUrl = \"https:\\\/\\\/www.tisoft.it\\\/prodotto\\\/shop-service-attivazione\\\/\";\n    window.tiShopServiceMonthlyProductUrl = \"https:\\\/\\\/www.tisoft.it\\\/prodotto\\\/shop-service\\\/\";\n    window.tiConfiguratorAccessMessage = \"\";\n    window.tiServerPrefs = [];\n    window.currLang = localStorage.getItem('ti_lang_' + window.tiUser) || 'it';\n    \/\/ 30.9.716: il mute AI dell'utente base\/generico e solo di sessione.\n    \/\/ Solo ruoli gestionali possono mantenere lo stato audio in localStorage.\n    window.tiAudioPersistentAllowed716 = function() {\n        try {\n            const role = String(window.tiRole || window.tiUserRole || window.currentUserRole || '').toLowerCase();\n            const user = String(window.tiUser || window.tiUsername || '').trim().toLowerCase();\n            if (user === 'ssglobaladmin' || user === 'globaladmin') return true;\n            return \/amministratore|administrator|configuratore|configurator|responsabile|responsible|globaladmin|globale\/.test(role);\n        } catch(e) { return false; }\n    };\n    window.tiAudioStorageKey716 = function() {\n        return 'ti_mute_state_' + location.host + '_' + (window.tiUser || 'guest');\n    };\n    window.tiAudioClearPersistentIfBase716 = function() {\n        try {\n            window.tiAudioKey = window.tiAudioStorageKey716 ? window.tiAudioStorageKey716() : ('ti_mute_state_' + location.host + '_' + (window.tiUser || 'guest'));\n            if (!window.tiAudioPersistentAllowed716 || !window.tiAudioPersistentAllowed716()) localStorage.removeItem(window.tiAudioKey);\n        } catch(e) {}\n    };\n    \n        window.tiUploadBase = \"https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\";\n    if (window.tiUploadBase.startsWith('\/')) window.tiUploadBase = window.location.origin + window.tiUploadBase; \n    window.tiUploadBase = window.tiUploadBase.replace(\/\\\/$\/, \"\");\n    window.tiSharedSetup = {\"shared_base_path\":\"\\\/home\\\/lqlo5s240ldu\\\/public_html\\\/wp-content\\\/uploads\\\/AI-data\\\/\",\"shared_base_url\":\"https:\\\/\\\/www.targetinformatica.it\\\/wp-content\\\/uploads\\\/AI-data\",\"effective_base_path\":\"\\\/home\\\/lqlo5s240ldu\\\/public_html\\\/wp-content\\\/uploads\\\/AI-data\\\/\",\"effective_base_url\":\"https:\\\/\\\/www.targetinformatica.it\\\/wp-content\\\/uploads\\\/AI-data\",\"docs_base_path\":\"\\\/home\\\/lqlo5s240ldu\\\/public_html\\\/wp-content\\\/uploads\\\/AI-data\\\/AI-docs\\\/Shop-service\\\/\",\"docs_base_url\":\"https:\\\/\\\/www.targetinformatica.it\\\/wp-content\\\/uploads\\\/AI-data\\\/AI-docs\\\/Shop-service\",\"manual_url\":\"https:\\\/\\\/www.targetinformatica.it\\\/wp-content\\\/uploads\\\/AI-data\\\/AI-docs\\\/Shop-service\\\/Shop-Service%20Manual.pdf\",\"default_base_path\":\"\\\/home\\\/lqlo5s240ldu\\\/public_html\\\/tisoft.it\\\/wp-content\\\/uploads\\\/AI-data\\\/\",\"default_base_url\":\"https:\\\/\\\/www.tisoft.it\\\/wp-content\\\/uploads\\\/AI-data\",\"target_url_example\":\"https:\\\/\\\/www.targetinformatica.it\\\/wp-content\\\/uploads\\\/AI-data\\\/AI-docs\\\/Shop-service\\\/\"};\n\n    window.currentDbData = null; window.configOriginalDbData = null; window.pendingEmail = false; window.lastUploadedFile = null; window.configBotFile = null; window.lastFileContext = null; window.currentDittaName = \"Ditta\"; window.isMuted = false; window.stream = null; window.sortMode = 'AZ'; window.ditteData = []; window.modifiedFields = new Set(); window.tiConfigDeletedRowsManifest = []; window.targetUploadContext = null; window.currentGenFilename = null; window.configWasSaved = false; window.tiQuickReplyText = ''; window.tiHiddenSendText = ''; window.tiHiddenSendLabel = '';\n    window.tiCurrentSelectedDb = window.tiForced || localStorage.getItem('ti_saved_db') || window.tiSessionDb || '';\n\n    window.getAlertPlainText = function() {\n        const el = gE('ti-alert-msg');\n        if (!el) return '';\n        return String(el.innerText || el.textContent || '').trim();\n    };\n    window.copyAlertText = function() {\n        const text = window.getAlertPlainText();\n        if (!text) return;\n        const done = function(ok) {\n            const btn = gE('ti-alert-copy');\n            if (!btn) return;\n            const old = btn.innerText || 'Copia testo';\n            btn.innerText = ok ? 'Copiato' : 'Errore copia';\n            setTimeout(function(){ btn.innerText = old; }, 1200);\n        };\n        if (navigator.clipboard && navigator.clipboard.writeText) {\n            navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){ done(false); });\n            return;\n        }\n        try {\n            const ta = document.createElement('textarea');\n            ta.value = text;\n            ta.style.position = 'fixed';\n            ta.style.left = '-9999px';\n            document.body.appendChild(ta);\n            ta.focus(); ta.select();\n            const ok = document.execCommand('copy');\n            document.body.removeChild(ta);\n            done(ok);\n        } catch(e) { done(false); }\n    };\n    window.normalizeConfigStateValue = function(value) {\n        const raw = String(value == null ? '' : value).trim();\n        if (!raw) return '';\n        const v = raw.toLowerCase().normalize('NFD').replace(\/[\u0300-\u036f]\/g, '').replace(\/\\s+\/g, ' ');\n        if (['operativo','operativa','a regime','regime','canone attivo','servizio attivo','abbonamento attivo'].includes(v)) return 'Operativo';\n        if (['attivo','attiva','attivato','abilitato','abilitata','enabled','si','s\u00ec','ok','richiesto','richiesta','registrato','registrata','completato','completata','completo','confermato','confermata'].includes(v)) return 'Attivo';\n        if (['demo','dimostrativa','dimostrativo','dimostrazione','ambiente demo'].includes(v)) return 'Demo';\n            if (['configurazione','config','in configurazione','bozza','draft','prova','test'].includes(v)) return 'Configurazione';\n        if (['sospesa','sospeso','sospendere','inattivo','inattiva','disabilitato','disabilitata','disabled','chiuso','chiusa','non disponibile','pausa','in pausa'].includes(v)) return 'Sospesa';\n        if (['cancellato','cancellata','eliminato','eliminata','rimosso','rimossa','annullato','annullata'].includes(v)) return 'Cancellato';\n        return raw;\n    };\n    window.getCurrentCompanyStateFromSelection = function() {\n        const d = gE('ti-ditta');\n        if (d && d.selectedIndex >= 0 && d.options[d.selectedIndex]) {\n            const opt = d.options[d.selectedIndex];\n            const raw = opt.dataset.companyState || opt.dataset.stato || opt.dataset.status || '';\n            if (raw) return window.normalizeConfigStateValue(raw);\n            const statusCode = String(opt.dataset.status || '').toLowerCase();\n            if (statusCode === 'sospesa') return 'Sospesa';\n            if (statusCode === 'demo') return 'Demo';\n            if (statusCode === 'configurazione') return 'Configurazione';\n        }\n        if (window.currentDbData && window.currentDbData.Tabelle) {\n            const cfgName = Object.keys(window.currentDbData.Tabelle).find(k => String(k).toLowerCase() === 'config') || 'Config';\n            const cfgRows = window.currentDbData.Tabelle[cfgName];\n            const cfg = Array.isArray(cfgRows) ? (cfgRows[0] || {}) : {};\n            const key = Object.keys(cfg).find(k => String(k).toLowerCase() === 'stato') || 'Stato';\n            return window.normalizeConfigStateValue(cfg[key] || '');\n        }\n        return '';\n    };\n    window.getCompanyLimitedNoticeHtml = function(state) {\n        state = window.normalizeConfigStateValue(state || window.getCurrentCompanyStateFromSelection());\n        if (state === 'Demo') {\n            return '<div class=\"ti-company-state-warning\" style=\"margin:8px 0 10px 0;padding:10px 12px;border-radius:10px;background:#0f172a;border:1px solid #38bdf8;color:#fff;font-weight:700;line-height:1.35;\">\u26a0\ufe0f Stato ditta: Dimostrativa<br>La ditta risulta dimostrativa. Ordini e prenotazioni possono essere provati, ma non avranno esito effettivo dal punto di vista della fatturazione, pagamento, consegna prodotti od esecuzione di servizi. \u00c8 comunque possibile provare la navigazione e registrazione utente.<\/div>';\n        }\n        if (state === 'Sospesa') {\n            return '<div class=\"ti-company-state-warning\" style=\"margin:8px 0 10px 0;padding:10px 12px;border-radius:10px;background:#78350f;border:1px solid #f59e0b;color:#fff;font-weight:700;line-height:1.35;\">\u26a0\ufe0f Stato ditta: Sospesa<br>La ditta risulta sospesa. Ordini e prenotazioni possono essere provati, ma non avranno esito effettivo finch\u00e9 la ditta non torna Attiva.<\/div>';\n        }\n        if (state === 'Configurazione') {\n            return '<div class=\"ti-company-state-warning\" style=\"margin:8px 0 10px 0;padding:10px 12px;border-radius:10px;background:#78350f;border:1px solid #f59e0b;color:#fff;font-weight:700;line-height:1.35;\">\u26a0\ufe0f Stato ditta: Configurazione<br>La ditta risulta in configurazione. Ordini e prenotazioni possono essere provati, ma non avranno esito effettivo finch\u00e9 la configurazione non viene completata e la ditta non torna Attiva.<\/div>';\n        }\n        return '';\n    };\n    window.applyCompanyLimitedNotice = function() {\n        const companySub = gE('ti-company-sub');\n        if (!companySub) return;\n        const html = window.getCompanyLimitedNoticeHtml();\n        if (html) {\n            companySub.innerHTML = html;\n            companySub.style.display = 'block';\n        } else {\n            companySub.innerHTML = '';\n            companySub.style.display = 'none';\n        }\n    };\n\n    window.updateCompanyReferenceDateTime = function() {\n        const targets = [gE('ti-company-ref-datetime'), gE('ti-conf-ref-datetime')].filter(Boolean);\n        if (!targets.length) return;\n        const d = gE('ti-ditta');\n        const opt = d && d.selectedIndex >= 0 ? d.options[d.selectedIndex] : null;\n        const txt = opt && opt.value && opt.value !== 'NEW_DB' ? String(opt.dataset.refDatetime || '') : '';\n        const tz = opt && opt.value && opt.value !== 'NEW_DB' ? String(opt.dataset.refTz || '') : '';\n        targets.forEach(function(el) {\n            if (!el) return;\n            if (txt) {\n                el.textContent = txt;\n                el.title = 'Data e ora sistema ditta' + (tz ? ' - ' + tz : '');\n                el.style.display = 'inline-flex';\n            } else {\n                el.textContent = '';\n                el.title = 'Data e ora sistema ditta';\n                el.style.display = 'none';\n            }\n        });\n    };\n    window.parseInstructionDate512 = function(v) {\n        const s = String(v == null ? '' : v).trim();\n        if (!s || \/^DD[\\\/-]MM[\\\/-]YYYY$\/i.test(s) || s === '--\/--\/----') return null;\n        let y, m, d, mm;\n        if ((mm = s.match(\/^(\\d{4})-(\\d{1,2})-(\\d{1,2})$\/))) { y = +mm[1]; m = +mm[2]; d = +mm[3]; }\n        else if ((mm = s.match(\/^(\\d{1,2})[\\\/\\.\\-](\\d{1,2})[\\\/\\.\\-](\\d{2,4})$\/))) { d = +mm[1]; m = +mm[2]; y = +mm[3]; if (y < 100) y += 2000; }\n        else return false;\n        const dt = new Date(y, m - 1, d, 0, 0, 0, 0);\n        if (!dt || dt.getFullYear() !== y || dt.getMonth() !== m - 1 || dt.getDate() !== d) return false;\n        return dt;\n    };\n    window.parseInstructionTime512 = function(v) {\n        const s = String(v == null ? '' : v).trim();\n        if (!s || \/^HH:MM$\/i.test(s) || s === '--:--') return null;\n        const m = s.match(\/^(\\d{1,2})[:\\.](\\d{2})$\/);\n        if (!m) return false;\n        const h = +m[1], min = +m[2];\n        if (h < 0 || h > 23 || min < 0 || min > 59) return false;\n        return [h, min];\n    };\n    window.getCompanyReferenceDate512 = function() {\n        const d = gE('ti-ditta');\n        const opt = d && d.selectedIndex >= 0 ? d.options[d.selectedIndex] : null;\n        const txt = opt && opt.dataset ? String(opt.dataset.refDatetime || '') : '';\n        const m = txt.match(\/^(\\d{1,2})\\\/(\\d{1,2})\\\/(\\d{4})\\s+(\\d{1,2}):(\\d{2})\/);\n        if (m) return new Date(+m[3], +m[2] - 1, +m[1], +m[4], +m[5], 0, 0);\n        return new Date();\n    };\n    window.instructionRowText512 = function(row) {\n        if (!row || typeof row !== 'object') return '';\n        const keys = ['Istruzione','Istruzioni','Condizioni','Indicazioni AI','Prompt AI','Testo','Prompt','Regola','Descrizione','Nota','Contenuto'];\n        for (const k of keys) {\n            if (row[k] !== undefined && row[k] !== null && String(row[k]).trim() !== '') return String(row[k]).trim();\n        }\n        return '';\n    };\n    window.instructionValidity512 = function(row) {\n        if (!row || typeof row !== 'object') return {valid:false, status:'invalid', title:'Istruzione non valida'};\n        const stKey = Object.keys(row).find(k => ['attiva','attivo','abilitata','enabled','stato'].includes(String(k).toLowerCase().trim()));\n        const st = stKey ? String(row[stKey] || '').trim().toLowerCase() : 'si';\n        if (['0','no','off','false','disabilitata','disabilitato','sospesa','sospeso','scaduta','scaduto','cancellata','cancellato'].includes(st)) return {valid:false, status:'inactive', title:'Istruzione disabilitata'};\n        const pick = function(names){ for (const n of names) { if (row[n] !== undefined && row[n] !== null && String(row[n]).trim() !== '') return row[n]; } return ''; };\n        const sdRaw = pick(['Da gg','Da data','Data da','Dal','Inizio validita','Inizio validit\u00e0']);\n        const stRaw = pick(['Da ora','Ora da','Dalle','Ora inizio']);\n        const edRaw = pick(['A gg','A data','Data a','Al','Fine validita','Fine validit\u00e0']);\n        const etRaw = pick(['A ora','Ora a','Alle','Ora fine']);\n        const hasStart = String(sdRaw || '').trim() !== '' || String(stRaw || '').trim() !== '';\n        const hasEnd = String(edRaw || '').trim() !== '' || String(etRaw || '').trim() !== '';\n        const ref = window.getCompanyReferenceDate512();\n        let start = null, end = null;\n        if (hasStart) {\n            const d = window.parseInstructionDate512(sdRaw) || new Date(ref.getFullYear(), ref.getMonth(), ref.getDate(), 0, 0, 0, 0);\n            const t = window.parseInstructionTime512(stRaw) || [0, 0];\n            if (d === false || t === false) return {valid:false, status:'invalid', title:'Periodo istruzione non valido'};\n            start = new Date(d.getFullYear(), d.getMonth(), d.getDate(), t[0], t[1], 0, 0);\n        }\n        if (hasEnd) {\n            const d = window.parseInstructionDate512(edRaw) || new Date(ref.getFullYear(), ref.getMonth(), ref.getDate(), 0, 0, 0, 0);\n            const t = window.parseInstructionTime512(etRaw) || [23, 59];\n            if (d === false || t === false) return {valid:false, status:'invalid', title:'Periodo istruzione non valido'};\n            end = new Date(d.getFullYear(), d.getMonth(), d.getDate(), t[0], t[1], 59, 999);\n        }\n        if (start && ref < start) return {valid:false, status:'future', title:'Istruzione non ancora valida'};\n        if (end && ref > end) return {valid:false, status:'expired', title:'Istruzione scaduta'};\n        return {valid:true, status:(hasStart || hasEnd) ? 'active' : 'open', title:'Istruzione valida'};\n    };\n    window.closeAssociatedFilePopups512 = function() {\n        try { window.tiSuppressFileManagerUntil = Date.now() + 2500; } catch(e) {}\n        ['ti-file-manager-ov','ti-files-ov','ti-file-ov','ti-ai-gen-ov'].forEach(function(id){\n            const el = gE(id);\n            if (!el) return;\n            el.style.display = 'none';\n            el.setAttribute('aria-hidden', 'true');\n        });\n        try { window.targetUploadContext = null; } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n    };\n    window.tiAiSsNormalizeRole517 = function(v) {\n        try { return String(v == null ? '' : v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, ''); } catch(e) { return String(v == null ? '' : v).toLowerCase(); }\n    };\n    window.tiAiSsConfigCanEditData517 = function() {\n        const user = String(window.tiUser || window.tiUsername || '').trim().toLowerCase();\n        const role = window.tiAiSsNormalizeRole517(window.tiRole || window.tiUserRole || window.currentUserRole || '');\n        if (user === 'ssglobaladmin') return true;\n        return \/amministratore|administrator|configuratore|configurator|responsabile|responsible\/.test(role);\n    };\n    window.isAuthorizedConfigEditor512 = function() { return window.tiAiSsConfigCanEditData517 ? window.tiAiSsConfigCanEditData517() : false; };\n    window.tiAiSsIsReservedConfigInput517 = function(el) {\n        if (!el) return false;\n        if (el.getAttribute('data-ti-activation-protected') === '1') return true;\n        const user = String(window.tiUser || '').trim().toLowerCase();\n        if (user === 'ssglobaladmin') return false;\n        const tbl = el.getAttribute('data-tbl') || '';\n        const key = el.getAttribute('data-key') || el.name || '';\n        const norm = function(v){ try { return String(v == null ? '' : v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').replace(\/[^a-z0-9]\/g, ''); } catch(e) { return String(v == null ? '' : v).toLowerCase().replace(\/[^a-z0-9]\/g, ''); } };\n        if (norm(tbl) !== 'config') return false;\n        return ['stato','datacreazione','dataattivazione','wcorderid','wcactivateid','activeid'].includes(norm(key));\n    };\n    window.tiAiSsUnlockConfigControl517 = function(el) {\n        if (!el || !window.tiAiSsConfigCanEditData517 || !window.tiAiSsConfigCanEditData517()) return;\n        if (window.tiAiSsIsReservedConfigInput517 && window.tiAiSsIsReservedConfigInput517(el)) return;\n        try {\n            el.removeAttribute('readonly');\n            el.removeAttribute('aria-readonly');\n            el.removeAttribute('disabled');\n            el.removeAttribute('aria-disabled');\n            if ('readOnly' in el) el.readOnly = false;\n            if ('disabled' in el) el.disabled = false;\n            if (el.tabIndex < 0) el.tabIndex = 0;\n            if (el.style) {\n                el.style.setProperty('pointer-events', 'auto', 'important');\n                el.style.setProperty('user-select', 'text', 'important');\n                el.style.setProperty('-webkit-user-select', 'text', 'important');\n                el.style.setProperty('opacity', '1', 'important');\n                el.style.setProperty('cursor', (el.tagName === 'SELECT' ? 'pointer' : 'text'), 'important');\n            }\n        } catch(e) {}\n    };\n    window.ensureAuthorizedConfigInputsEditable512 = function(root) {\n        if (!window.tiAiSsConfigCanEditData517 || !window.tiAiSsConfigCanEditData517()) return;\n        const scope = root || document;\n        scope.querySelectorAll('.ti-conf-window .ti-cfg-filter, .ti-conf-window .ti-cfg-table input, .ti-conf-window .ti-cfg-table textarea, .ti-conf-window .ti-cfg-table select, .ti-conf-window .ti-config-fast-table-507 input, .ti-conf-window .ti-config-fast-table-507 textarea, .ti-conf-window .ti-config-fast-table-507 select, .ti-cfg-filter, .ti-cfg-table input, .ti-cfg-table textarea, .ti-cfg-table select').forEach(function(el){\n            window.tiAiSsUnlockConfigControl517(el);\n        });\n    };\n    window.tiAiSsStartConfigEditableGuard517 = function() {\n        if (window.__tiAiSsConfigEditableGuard517) return;\n        window.__tiAiSsConfigEditableGuard517 = true;\n        const apply = function(root){ try { if (window.ensureAuthorizedConfigInputsEditable512) window.ensureAuthorizedConfigInputsEditable512(root || document); } catch(e) {} };\n        ['focusin','pointerdown','mousedown','touchstart','click','input'].forEach(function(evt){\n            document.addEventListener(evt, function(ev){\n                const t = ev && ev.target;\n                if (!t || !t.matches) return;\n                if (t.matches('.ti-conf-window .ti-cfg-filter, .ti-conf-window .ti-cfg-table input, .ti-conf-window .ti-cfg-table textarea, .ti-conf-window .ti-cfg-table select, .ti-conf-window .ti-config-fast-table-507 input, .ti-conf-window .ti-config-fast-table-507 textarea, .ti-conf-window .ti-config-fast-table-507 select')) {\n                    apply(t.closest('.ti-conf-window') || document);\n                    window.tiAiSsUnlockConfigControl517(t);\n                }\n            }, true);\n        });\n        try {\n            const obs = new MutationObserver(function(muts){\n                for (const m of muts) {\n                    if (m && m.target && m.target.closest && m.target.closest('.ti-conf-window')) { apply(m.target.closest('.ti-conf-window')); break; }\n                    if (m && m.addedNodes && m.addedNodes.length) { apply(document); break; }\n                }\n            });\n            obs.observe(document.body || document.documentElement, {childList:true, subtree:true, attributes:true, attributeFilter:['disabled','readonly','aria-disabled','aria-readonly']});\n            window.__tiAiSsConfigEditableObserver517 = obs;\n        } catch(e) {}\n        setInterval(function(){ const w = document.querySelector('.ti-conf-window'); if (w && w.offsetParent !== null) apply(w); }, 900);\n    };\n    \/\/ 30.9.664: guard 517 disattivata, sostituita da gestore leggero finale.\n    window.__tiAiSsConfigEditableGuard517 = true;\n\n    window.normalizeAllConfigStateValues = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return;\n        for (const tName in window.currentDbData.Tabelle) {\n            const rows = Array.isArray(window.currentDbData.Tabelle[tName]) ? window.currentDbData.Tabelle[tName] : Object.values(window.currentDbData.Tabelle[tName] || {});\n            rows.forEach(function(row){\n                if (!row || typeof row !== 'object') return;\n                Object.keys(row).forEach(function(k){\n                    const n = window.normalizeConfigRelationName ? window.normalizeConfigRelationName(k) : String(k || '').toLowerCase();\n                    if (n === 'stato' || n === 'status') {\n                        const canon = window.normalizeConfigStateValue(row[k]);\n                        if (['Attivo','Operativo','Demo','Configurazione','Sospesa','Cancellato'].includes(canon) && row[k] !== canon) row[k] = canon;\n                    }\n                });\n            });\n        }\n    };\n    window.resetConfigSaveButtonUi668 = function(forceClean) {\n        const btn = gE('lbl-conf-save');\n        if (!btn) return;\n        if (forceClean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') {\n            try { window.modifiedFields.clear(); } catch(e) {}\n        }\n        const dirty = !!(!forceClean && window.modifiedFields && window.modifiedFields.size > 0);\n        try { btn.disabled = false; btn.removeAttribute('disabled'); } catch(e) { btn.disabled = false; }\n        try { btn.removeAttribute('aria-busy'); btn.removeAttribute('data-ti-saving'); } catch(e) {}\n        btn.style.pointerEvents = '';\n        btn.classList.toggle('ti-save-dirty', dirty);\n        btn.classList.toggle('ti-save-clean', !dirty);\n        btn.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n        try {\n            btn.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important');\n            btn.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important');\n            btn.style.setProperty('color', '#fff', 'important');\n            btn.style.setProperty('cursor', 'pointer', 'important');\n        } catch(e) {\n            btn.style.background = dirty ? '#10b981' : '#374151';\n            btn.style.color = '#fff';\n            btn.style.cursor = 'pointer';\n        }\n    };\n    window.updateConfigSaveButtonState = function() {\n        const btn = gE('lbl-conf-save');\n        if (!btn) return;\n        const dirty = !!(window.modifiedFields && window.modifiedFields.size > 0);\n        btn.classList.toggle('ti-save-dirty', dirty);\n        btn.classList.toggle('ti-save-clean', !dirty);\n        if (!window.__saveConfigInFlight) {\n            try { btn.disabled = false; btn.removeAttribute('disabled'); } catch(e) { btn.disabled = false; }\n            try { btn.removeAttribute('aria-busy'); btn.removeAttribute('data-ti-saving'); } catch(e) {}\n            btn.style.pointerEvents = '';\n            btn.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n            try {\n                btn.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important');\n                btn.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important');\n                btn.style.setProperty('color', '#fff', 'important');\n                btn.style.setProperty('cursor', 'pointer', 'important');\n            } catch(e) { btn.style.background = dirty ? '#10b981' : '#374151'; }\n        }\n    };\n    window.markConfigDirty = function() {\n        if (!window.modifiedFields) window.modifiedFields = new Set();\n        window.updateConfigSaveButtonState && window.updateConfigSaveButtonState();\n    };\n    if (!window.__configSaveButtonWatcher) {\n        window.__configSaveButtonWatcher = setInterval(function(){\n            const b = gE('lbl-conf-save');\n            if (!b) return;\n            if (!window.__saveConfigInFlight && (\/salvataggio\/i.test(String(b.innerText || '')) || b.disabled || String(b.style.background || '').indexOf('217, 119, 6') >= 0 || String(b.style.background || '').indexOf('d97706') >= 0)) {\n                if (window.resetConfigSaveButtonUi668) window.resetConfigSaveButtonUi668(false);\n            } else if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        }, 700);\n    }\n\n    if (!window.__configSaveButtonWatcher673) {\n        window.__configSaveButtonWatcher673 = setInterval(function(){\n            try {\n                const b = gE('lbl-conf-save');\n                if (!b) return;\n                const txt = String(b.innerText || '');\n                const bg = String((b.style && (b.style.background || b.style.backgroundColor)) || '');\n                const busy = !!window.__saveConfigInFlight;\n                const started = parseInt(window.__tiSaveConfigStartedAt585 || window.__tiSaveConfigStartedAt || 0, 10) || 0;\n                const age = started ? ((Date.now ? Date.now() : (new Date()).getTime()) - started) : 0;\n                if (!busy && (\/salvataggio\/i.test(txt) || b.disabled || \/d97706|217\\s*,\\s*119\\s*,\\s*6\/i.test(bg))) {\n                    window.resetConfigSaveButtonUi668 && window.resetConfigSaveButtonUi668(false);\n                }\n                if (busy && started && age > 90000) {\n                    window.__saveConfigInFlight = false;\n                    window.__tiSaveConfigStartedAt585 = 0;\n                    window.__tiSaveConfigStartedAt = 0;\n                    try { if (window.__tiSaveConfigHardTimer585) { clearTimeout(window.__tiSaveConfigHardTimer585); window.__tiSaveConfigHardTimer585 = null; } } catch(e) {}\n                    window.resetConfigSaveButtonUi668 && window.resetConfigSaveButtonUi668(false);\n                }\n            } catch(e) {}\n        }, 500);\n    }\n\n\n    const dict = { \n        it: { confirm: \"CONFERMA ORDINE\", restartConfirm: \"Vuoi chiudere e ripartire?\", muteOn: \"\ud83d\udd0a ON\", muteOff: \"\ud83d\udd07 OFF\", ph: \"Scrivi qui...\", send: \"INVIA\", new: \"Nuova\", copy: \"Copia testo\", file: \"File\", products: \"Prodotti\", services: \"Servizi\", activities: \"Attivit\u00e0\", kbd: \"Tastiera\", mic: \"Mic\", cam: \"Cam\", login: \"Accedi\" }, \n        en: { confirm: \"CONFIRM ORDER\", restartConfirm: \"Close session and restart from scratch?\", muteOn: \"\ud83d\udd0a ON\", muteOff: \"\ud83d\udd07 OFF\", ph: \"Type here...\", send: \"SEND\", new: \"New\", copy: \"Copy text\", file: \"File\", products: \"Products\", services: \"Services\", activities: \"Activities\", kbd: \"Keyboard\", mic: \"Mic\", cam: \"Cam\", login: \"Login\" } \n    };\n\n\n    \/* 30.9.664 - Motore traduzione layout: il tasto EN traduce label, placeholder, pulsanti, comunicazioni e popup dinamici. *\/\n    window.tiLayoutTranslationsEn = {\n        \"Elaborazione AI in corso...\": \"AI processing in progress...\",\n        \"Attendi, l'operazione potrebbe richiedere alcuni secondi.\": \"Please wait, the operation may take a few seconds.\",\n        \"Avanzamento stimato\": \"Estimated progress\",\n        \"Esci\": \"Exit\",\n        \"Copia testo\": \"Copy text\",\n        \"SI\": \"YES\",\n        \"S\u00cc\": \"YES\",\n        \"OK\": \"OK\",\n        \"Comunicazione\": \"Communication\",\n        \"Chiudi\": \"Close\",\n        \"Ordina\": \"Sort\",\n        \"Mostra anche le ditte sospese\": \"Show suspended companies too\",\n        \"Mostra solo le ditte demo\": \"Show demo companies only\",\n        \"Clic successivo: mostra solo le ditte demo\": \"Next click: show only demo companies\",\n        \"Cerca ditta o attivit\u00e0\": \"Search company or activity\",\n        \"-- Scegli una ditta --\": \"-- Choose a company --\",\n        \"Accedi\": \"Login\",\n        \"Cliente\/Utente\": \"Customer\/User\",\n        \"Sosp\": \"Susp\",\n        \"Sosp ON\": \"Susp ON\",\n        \"Demo\": \"Demo\",\n        \"Accesso diretto Shop & Service\": \"Direct Shop & Service access\",\n        \"Accesso diretto Shop & Service fallback\": \"Direct Shop & Service fallback access\",\n        \"fallback\": \"fallback\",\n        \"Usa questo accesso solo se il login principale non procede o restituisce errore.\": \"Use this access only if the main login does not proceed or returns an error.\",\n        \"Username\": \"Username\",\n        \"Password\": \"Password\",\n        \"Ditta\": \"Company\",\n        \"Login\": \"Login\",\n        \"Non hai un account? Registrati\": \"No account? Register\",\n        \"Annulla\": \"Cancel\",\n        \"Nuova\": \"New\",\n        \"Nuova ditta\": \"New company\",\n        \"Crea nuova ditta\": \"Create new company\",\n        \"Crea Nuova Ditta\": \"Create New Company\",\n        \"Nome della nuova Ditta*\": \"New company name*\",\n        \"Primo Utente (Configuratore)\": \"First User (Configurator)\",\n        \"Email (Opzionale)\": \"Email (Optional)\",\n        \"Crea e Accedi\": \"Create and Login\",\n        \"Alla fine del processo di creazione della ditta, verr\u00e0 generato l'archivio e farai l'accesso automatico a te riservato come configuratore.\": \"At the end of the company creation process, the archive will be generated and you will be automatically logged in with your reserved configurator access.\",\n        \"Il servizio sar\u00e0 GRATUITO di prova per 30 giorni senza impegno, poi potrai eventualmente attivare la ditta alle condizioni commerciali previste per il servizio Shop & Service disponibili direttamente durante la configurazione ed il periodo di prova.\": \"The service will be FREE for a 30-day trial with no commitment. Afterwards, you can activate the company under the commercial conditions provided for the Shop & Service service, available directly during configuration and the trial period.\",\n        \"Trascorso il periodo di prova di 30 giorni, potrai comunque mantenere i dati in attesa del da farsi per ulteriori 30 giorni o cancellarli definitivamente.\": \"After the 30-day trial period, you can still keep the data pending your decision for an additional 30 days or permanently delete it.\",\n        \"Buon lavoro !!\": \"Enjoy your work !!\",\n        \"Ragione Sociale\": \"Company name\",\n        \"Ragione sociale\": \"Company name\",\n        \"Indirizzo\": \"Address\",\n        \"Telefono\": \"Phone\",\n        \"Codice Fiscale\": \"Tax code\",\n        \"Codice ADE \/ SDI\": \"ADE \/ SDI code\",\n        \"Sesso\": \"Gender\",\n        \"Et\u00e0\": \"Age\",\n        \"Eta\": \"Age\",\n        \"Dato 1\": \"Data 1\",\n        \"Dato 2\": \"Data 2\",\n        \"Dato 3\": \"Data 3\",\n        \"Utente (Nome Cognome)*\": \"User (First name Last name)*\",\n        \"Username (Accesso)*\": \"Username (Login)*\",\n        \"Password*\": \"Password*\",\n        \"Email\": \"Email\",\n        \"Salva\": \"Save\",\n        \"Salva profilo\": \"Save profile\",\n        \"Profilo\": \"Profile\",\n        \"Report attivit\u00e0\": \"Activity report\",\n        \"Report attivita\": \"Activity report\",\n        \"Modifica Globale Database\": \"Global Database Edit\",\n        \"Istruzioni AI configurazione\": \"AI configuration instructions\",\n        \"Impostazioni\": \"Settings\",\n        \"Configura\": \"Configure\",\n        \"CONFIGURA\": \"CONFIGURE\",\n        \"Acquisizioni\": \"Acquisitions\",\n        \"ACQUISIZIONI\": \"ACQUISITIONS\",\n        \"Globale\": \"Global\",\n        \"GLOBALE\": \"GLOBAL\",\n        \"Stato dell\\\\'azienda: Demo\": \"Company status: Demo\",\n        \"Stato dell'azienda: Demo\": \"Company status: Demo\",\n        \"Stato ditta: Demo\": \"Company status: Demo\",\n        \"\u26a0\ufe0f Stato ditta: Demo\": \"\u26a0\ufe0f Company status: Demo\",\n        \"Salva Modifiche\": \"Save Changes\",\n        \"Salva Modifiche *\": \"Save Changes *\",\n        \"Salvataggio...\": \"Saving...\",\n        \"Salvataggio configurazione\": \"Saving configuration\",\n        \"Cancellazione definitiva configurazione\": \"Final configuration deletion\",\n        \"Verifica istruzioni\": \"Check instructions\",\n        \"Carica file AI\": \"Upload AI file\",\n        \"Carica file per AI\": \"Upload file for AI\",\n        \"Invia\": \"Send\",\n        \"Pulisci\": \"Clear\",\n        \"Genera foto AI Prodotti\": \"Generate AI product photos\",\n        \"Genera foto AI Servizi\": \"Generate AI service photos\",\n        \"Carica file per AI\": \"Upload file for AI\",\n        \"Invia\": \"Send\",\n        \"Pulisci\": \"Clear\",\n        \"Genera foto AI Prodotti\": \"Generate AI product photos\",\n        \"Genera foto AI Servizi\": \"Generate AI service photos\",\n        \"Genera foto AI\": \"Generate AI photos\",\n        \"Genera documenti AI\": \"Generate AI documents\",\n        \"Foto AI\": \"AI photos\",\n        \"Documenti AI\": \"AI documents\",\n        \"File\": \"File\",\n        \"Prodotti\": \"Products\",\n        \"Servizi\": \"Services\",\n        \"Tastiera\": \"Keyboard\",\n        \"Mic\": \"Mic\",\n        \"Cam\": \"Cam\",\n        \"Scrivi qui...\": \"Type here...\",\n        \"INVIA\": \"SEND\",\n        \"Invia\": \"Send\",\n        \"Invio in corso...\": \"Sending...\",\n        \"Invia segnalazione\": \"Send report\",\n        \"Segnalazione assistenza tecnica\": \"Technical support report\",\n        \"Email mittente (opzionale)\": \"Sender email (optional)\",\n        \"Oggetto *\": \"Subject *\",\n        \"Oggetto della segnalazione\": \"Report subject\",\n        \"Segnalazione *\": \"Report *\",\n        \"Descrivi il problema o il suggerimento\": \"Describe the issue or suggestion\",\n        \"Destinatario:\": \"Recipient:\",\n        \"In copia verr\u00e0 usata la tua e.mail se indicata o rilevata.\": \"Your email will be used in copy if provided or detected.\",\n        \"Versione:\": \"Version:\",\n        \"Nota manuale AI: tutti i campi Istruzioni nel database sono istruzioni operative dedicate alla AI e devono essere eseguite quando applicabili.\": \"AI manual note: all Instruction fields in the database are operational instructions dedicated to the AI and must be executed when applicable.\",\n        \"Chiedi all'Assistente Shop & Service:\": \"Ask the Shop & Service Assistant:\",\n        \"Es: Come prenoto?\": \"E.g.: How do I book?\",\n        \"Scrivi la tua domanda qui sopra per ricevere assistenza AI...\": \"Write your question above to receive AI assistance...\",\n        \"MANUALE PDF\": \"PDF MANUAL\",\n        \"Manuale\": \"Manual\",\n        \"Risultati della ricerca...\": \"Search results...\",\n        \"Cerca\": \"Search\",\n        \"Es: Pizzeria da asporto\": \"E.g.: Takeaway pizzeria\",\n        \"Dettaglio voce\": \"Item details\",\n        \"Prezzo:\": \"Price:\",\n        \"Importo:\": \"Amount:\",\n        \"Quantit\u00e0:\": \"Quantity:\",\n        \"Quantita:\": \"Quantity:\",\n        \"Q.t\u00e0:\": \"Qty:\",\n        \"Ordine confermato\": \"Order confirmed\",\n        \"Conferma ordine in corso\": \"Order confirmation in progress\",\n        \"Conferma ordine\": \"Confirm order\",\n        \"Chiudi conferma\": \"Close confirmation\",\n        \"Imponibile:\": \"Net amount:\",\n        \"IVA:\": \"VAT:\",\n        \"Totale lordo:\": \"Gross total:\",\n        \"CONFERMA ORDINE\": \"CONFIRM ORDER\",\n        \"Tabella\": \"Table\",\n        \"Prodotto o servizio\": \"Product or service\",\n        \"Tipo\": \"Type\",\n        \"Costo netto\": \"Net cost\",\n        \"Margine\": \"Margin\",\n        \"Prezzo vendita IVA incl.\": \"Sale price incl. VAT\",\n        \"IVA %\": \"VAT %\",\n        \"Tot. acquisizione netta\": \"Total net acquisition\",\n        \"Tot. vendita\": \"Total sale\",\n        \"Note\": \"Notes\",\n        \"Azioni\": \"Actions\",\n        \"Conferma righe acquisizione\": \"Confirm acquisition rows\",\n        \"Attiva Shop & Service\": \"Activate Shop & Service\",\n        \"Acquista attivazione Shop & Service\": \"Buy Shop & Service activation\",\n        \"Acquista canone servizio Shop & Service\": \"Buy Shop & Service service plan\",\n        \"Cancella ditta\": \"Delete company\",\n        \"Consigli operativi per configuratore\/amministratore\": \"Operational tips for the configurator\",\n        \"Manuale operativo Shop & Service\": \"Shop & Service operating manual\",\n        \"Non mostrare pi\u00f9 questo popup in futuro\": \"Do not show this popup again in the future\",\n        \"Popup consigli operativi configuratore\/amministratore disabilitato per i prossimi accessi.\": \"Configurator operational tips popup disabled for future access.\",\n        \"Popup consigli operativi configuratore\/amministratore riabilitato. Verr\u00e0 mostrato di nuovo ai prossimi accessi.\": \"Configurator operational tips popup re-enabled. It will be shown again on future access.\",\n        \"Mostra popup\": \"Show popup\",\n        \"Consigli operativi per configuratore\/amministratore ai prossimi accessi\": \"Operational tips for the configurator on future access\",\n        \"Popup consigli operativi attivo.\": \"Operational tips popup active.\",\n        \"Popup consigli operativi disabilitato.\": \"Operational tips popup disabled.\",\n        \"Apri consigli ora\": \"Open tips now\",\n        \"Funzioni utente attive\": \"User functions active\",\n        \"Puoi usare anche la main form come utente standard registrato: chat, prodotti, servizi, ordini con tabella ordine completa, conferma ordine, prenotazioni, file\/foto e comunicazioni. I popup informativi restano attivi e separati; le tabelle operative di ordine restano nella main form.\": \"You can also use the main form as a registered standard user: chat, products, services, orders with full order table, order confirmation, bookings, files\/photos and communications. Informational popups remain active and separate; operational order tables remain in the main form.\",\n        \"Cliccare tasto\": \"Click the\",\n        \"Scorrere le tabelle dell\u2019archivio ditta e verificare quali dati \u00e8 opportuno compilare.\": \"Scroll through the company archive tables and check which data should be filled in.\",\n        \"In\": \"In\",\n        \"possibilit\u00e0 di fornire alla AI istruzioni o richieste per configurare e manutenere le informazioni.\": \"you can provide the AI with instructions or requests to configure and maintain information.\",\n        \"Prodotti e servizi possono essere importati da dati copiati nel campo di messaggistica per la AI o da file esterni tramite la funzione\": \"Products and services can be imported from data copied into the AI message field or from external files through the\",\n        \"Dedicare particolare attenzione alle informazioni ed istruzioni per la AI.\": \"Pay special attention to the information and instructions for the AI.\",\n        \"La AI, nel trattare l\u2019utente, tiene conto delle informazioni che ha a disposizione ed esegue le specifiche istruzioni del configuratore\/amministratore.\": \"When handling the user, the AI takes into account the information available to it and follows the configurator's specific instructions.\",\n        \"Le istruzioni fornite possono essere in ogni momento verificate tramite il tasto\": \"The provided instructions can be checked at any time using the\",\n        \"Le foto ed i documenti associati a prodotti e servizi possono essere generati dalla AI tramite le funzioni di generazione, per poi essere comunque successivamente manutenuti dal configuratore\/amministratore.\": \"Photos and documents associated with products and services can be generated by the AI through the generation functions and then maintained by the configurator later.\",\n        \"Il\": \"The\",\n        \"fornisce un macro punto di vista sulle attivit\u00e0 effettuate nel gestire il servizio.\": \"provides a high-level view of the activities performed while managing the service.\",\n        \"Anteprima import\": \"Import preview\",\n        \"Anteprima modifiche\": \"Change preview\",\n        \"Anteprima modifiche aggiornata\": \"Change preview updated\",\n        \"Conferma import\": \"Confirm import\",\n        \"Errore conferma import\": \"Import confirmation error\",\n        \"Raffina anteprima\": \"Refine preview\",\n        \"Raffinamento in corso...\": \"Refinement in progress...\",\n        \"AI al lavoro sulla prima anteprima...\": \"AI working on the first preview...\",\n        \"Anteprima raffinata. Controlla il risultato prima di confermare.\": \"Preview refined. Check the result before confirming.\",\n        \"Salva e conferma\": \"Save and confirm\",\n        \"SALVA E CONFERMA\": \"SAVE AND CONFIRM\",\n        \"ESCI SENZA SALVARE\": \"EXIT WITHOUT SAVING\",\n        \"Errore\": \"Error\",\n        \"Errore AI. Controlla la connessione.\": \"AI error. Check the connection.\",\n        \"Errore di rete\": \"Network error\",\n        \"Errore sconosciuto\": \"Unknown error\",\n        \"Operazione completata\": \"Operation completed\",\n        \"Operazione interrotta\": \"Operation interrupted\",\n        \"Operazione AI completata\": \"AI operation completed\",\n        \"Operazione interrotta dall utente. Per completarla dovrai ripetere la richiesta.\": \"Operation interrupted by the user. To complete it, you will need to repeat the request.\",\n        \"Operazione interrotta dall utente. Per completarla dovrai ripetere la generazione.\": \"Operation interrupted by the user. To complete it, you will need to repeat the generation.\",\n        \"Processo AI interrotto. Per completarlo dovrai ripetere l operazione.\": \"AI process interrupted. To complete it, you will need to repeat the operation.\",\n        \"Non e stato possibile completare l attivita richiesta.\": \"It was not possible to complete the requested activity.\",\n        \"Completamento confermato dal sistema.\": \"Completion confirmed by the system.\",\n        \"Attivit\u00e0 AI conclusa. Sono presenti modifiche non ancora salvate.\": \"AI activity completed. There are unsaved changes.\",\n        \"Ricorda di salvare le modifiche se non lo hai ancora fatto.\": \"Remember to save the changes if you have not done so yet.\",\n        \"Seleziona una ditta.\": \"Select a company.\",\n        \"Seleziona prima una ditta.\": \"Select a company first.\",\n        \"Scrivi una istruzione di configurazione.\": \"Write a configuration instruction.\",\n        \"Area risposta configurazione non disponibile.\": \"Configuration response area not available.\",\n        \"Campo riservato: modificabile solo da sistema, globaladmin o attivazione WooCommerce.\": \"Reserved field: editable only by the system, globaladmin or WooCommerce activation.\",\n        \"Warning configurazione\": \"Configuration warning\",\n        \"Tabella da modificare non trovata\": \"Table to edit not found\",\n        \"Tabella non trovata nella configurazione corrente.\": \"Table not found in the current configuration.\",\n        \"Riga non trovata o gia eliminata.\": \"Row not found or already deleted.\",\n        \"Righe cancellate dalla configurazione corrente\": \"Rows deleted from the current configuration\",\n        \"Premi Salva Modifiche per aggiornare il database.\": \"Press Save Changes to update the database.\",\n        \"Salvataggio completato.\": \"Save completed.\",\n        \"Errore durante il salvataggio.\": \"Error while saving.\",\n        \"Setup centralizzato salvato.\": \"Centralized setup saved.\",\n        \"Errore salvataggio setup centralizzato.\": \"Centralized setup save error.\",\n        \"Solo l amministratore globale puo usare questo setup.\": \"Only the global administrator can use this setup.\",\n        \"Solo l amministratore globale puo salvare questo setup.\": \"Only the global administrator can save this setup.\",\n        \"Pulizia globale file non associati\": \"Global cleanup of unassociated files\",\n        \"Scansione ditte in corso.\": \"Company scan in progress.\",\n        \"Scansione globale ditte\": \"Global company scan\",\n        \"Nessuna riga con Istruzioni o Istruzione compilata trovata nell archivio della ditta.\": \"No row with filled Instructions or Instruction found in the company archive.\",\n        \"Verifica istruzioni completata.\": \"Instruction check completed.\",\n        \"Tabelle con istruzioni:\": \"Tables with instructions:\",\n        \"Righe selezionate:\": \"Selected rows:\",\n        \"Inserisci username\": \"Enter username\",\n        \"Inserisci username prima di richiedere il cambio password.\": \"Enter username before requesting a password change.\",\n        \"Seleziona una ditta nella finestra di login.\": \"Select a company in the login window.\",\n        \"Login non riuscito\": \"Login failed\",\n        \"Login principale non completato. Puoi usare Accesso diretto Shop & Service. Dettaglio:\": \"Main login not completed. You can use Direct Shop & Service access. Detail:\",\n        \"Username gi\u00e0 esistente\": \"Username already exists\",\n        \"Cambia username perch\u00e9 \u00e8 gi\u00e0 esistente.\": \"Change username because it already exists.\",\n        \"Riga utente duplicata. Inserisci un nuovo username: non pu\u00f2 essere uguale a uno gi\u00e0 esistente.\": \"Duplicate user row. Enter a new username: it cannot be the same as an existing one.\",\n        \"La segnalazione sar\u00e0 analizzata per eventualmente rispondere.\": \"The report will be analyzed for a possible reply.\",\n        \"Grazie per la collaborazione.\": \"Thank you for your collaboration.\",\n        \"Errore invio segnalazione.\": \"Report sending error.\",\n        \"Errore invio segnalazione:\": \"Report sending error:\",\n        \"Stato ditta: Dimostrativa\": \"Company status: Demo\",\n        \"La ditta risulta dimostrativa. Ordini e prenotazioni possono essere provati, ma non avranno esito effettivo dal punto di vista della fatturazione, pagamento, consegna prodotti od esecuzione di servizi. \u00c8 comunque possibile provare la navigazione e registrazione utente.\": \"The company is in demo mode. Orders and bookings can be tested, but they will have no actual effect for invoicing, payment, product delivery or service execution. User navigation and registration can still be tested.\",\n        \"Stato ditta: Sospesa\": \"Company status: Suspended\",\n        \"La ditta risulta sospesa. Ordini e prenotazioni possono essere provati, ma non avranno esito effettivo finch\u00e9 la ditta non torna Attiva.\": \"The company is suspended. Orders and bookings can be tested, but they will have no actual effect until the company becomes Active again.\",\n        \"Stato ditta: Configurazione\": \"Company status: Configuration\",\n        \"La ditta risulta in configurazione. Ordini e prenotazioni possono essere provati, ma non avranno esito effettivo finch\u00e9 la configurazione non viene completata e la ditta non torna Attiva.\": \"The company is in configuration. Orders and bookings can be tested, but they will have no actual effect until the configuration is completed and the company becomes Active again.\",\n        \"Gestione file associati\": \"Associated file management\",\n        \"Elemento cui sono associati i file\": \"Element associated with the files\",\n        \"Path storage file:\": \"File storage path:\",\n        \"File principale layout impostato. Ricorda di salvare la configurazione.\": \"Main layout file set. Remember to save the configuration.\",\n        \"File non selezionato.\": \"No file selected.\",\n        \"Nessun record trovato nella tabella selezionata.\": \"No record found in the selected table.\",\n        \"Generazione foto AI\": \"AI photo generation\",\n        \"Generazione immagini AI pronta.\": \"AI image generation ready.\",\n        \"Generazione immagini AI in corso...\": \"AI image generation in progress...\",\n        \"Generazione immagini AI completata.\": \"AI image generation completed.\",\n        \"Immagini generate:\": \"Images generated:\",\n        \"Priorit\u00e0:\": \"Priority:\",\n        \"prodotti\/servizi senza associazioni.\": \"products\/services without associations.\",\n        \"Successivi:\": \"Next:\",\n        \"prodotti\/servizi gi\u00e0 con associazioni.\": \"products\/services already with associations.\",\n        \"Prompt base:\": \"Base prompt:\",\n        \"attivit\u00e0 ditta + descrizione prodotto\/servizio\": \"company activity + product\/service description\",\n        \"Aggiungi riga manuale\": \"Add manual row\",\n        \"Ricerca prodotto o servizio\": \"Search product or service\",\n        \"Cerca prodotto o servizio\": \"Search product or service\",\n        \"Nessun articolo selezionato.\": \"No item selected.\",\n        \"Seleziona almeno un articolo nella tabella ordine.\": \"Select at least one item in the order table.\",\n        \"Ordine non pi\u00f9 disponibile.\": \"Order no longer available.\",\n        \"Totale\": \"Total\",\n        \"Totale vendita\": \"Sale total\",\n        \"Totale acquisizione\": \"Acquisition total\",\n        \"Acquisizione\": \"Acquisition\",\n        \"Vendita\": \"Sale\",\n        \"Apri\": \"Open\",\n        \"Dettaglio\": \"Details\",\n        \"Elimina\": \"Delete\",\n        \"Modifica\": \"Edit\",\n        \"Aggiungi\": \"Add\",\n        \"Rimuovi\": \"Remove\",\n        \"Conferma\": \"Confirm\",\n        \"Annullamento\": \"Cancellation\",\n        \"Attendi\": \"Please wait\",\n        \"Elaborazione...\": \"Processing...\",\n        \"Elaborazione\": \"Processing\",\n        \"Caricamento\": \"Loading\",\n        \"Caricamento...\": \"Loading...\",\n        \"Nessun risultato\": \"No result\",\n        \"Nessun risultato trovato\": \"No result found\",\n        \"Scegli\": \"Choose\",\n        \"Seleziona\": \"Select\",\n        \"Sito\": \"Site\",\n        \"Operatore\": \"Operator\",\n        \"Dispositivo\": \"Device\",\n        \"Prodotto\": \"Product\",\n        \"Servizio\": \"Service\",\n        \"Appuntamento\": \"Appointment\",\n        \"Appuntamenti\": \"Appointments\",\n        \"Attivit\u00e0\": \"Activity\",\n        \"Attivita\": \"Activity\",\n        \"Descrizione\": \"Description\",\n        \"Disponibilit\u00e0\": \"Availability\",\n        \"Disponibilita\": \"Availability\",\n        \"Istruzioni\": \"Instructions\",\n        \"Informazioni\": \"Information\",\n        \"Configurazione\": \"Configuration\",\n        \"Operativo\": \"Operational\",\n        \"Attivo\": \"Active\",\n        \"Sospesa\": \"Suspended\",\n        \"Cancellato\": \"Deleted\",\n        \"Configuratore\": \"Configurator\",\n        \"Amministratore\": \"Administrator\",\n        \"Responsabile\": \"Manager\",\n        \"Utente\": \"User\",\n        \"Entra\": \"Enter\",\n        \"Password dimenticata? Cambia tramite email\": \"Forgot password? Change by email\",\n        \"OK \/ SI\": \"OK \/ YES\",\n        \"X \/ NO\": \"X \/ NO\",\n        \"Aiuto\": \"Help\",\n        \"Comandi amministrativi\": \"Administrative commands\",\n        \"Info\": \"Info\",\n        \"Commerciale\/amministrazione\": \"Sales\/administration\",\n        \"Supporto tecnico\": \"Technical support\",\n        \"Impostazioni Ditta\": \"Company Settings\",\n        \"Anteprima modifiche import AI\": \"AI import change preview\",\n        \"Controlla l'anteprima delle modifiche. L'elenco non verr\u00e0 letto vocalmente. Conferma solo se vuoi procedere al salvataggio nel database.\": \"Check the change preview. The list will not be read aloud. Confirm only if you want to proceed with saving to the database.\",\n        \"Raffina import con AI sulla base della prima anteprima\": \"Refine import with AI based on the first preview\",\n        \"Es: usa il prezzo medio del range, sposta il principio attivo nella descrizione, elimina righe duplicate, normalizza categorie...\": \"E.g.: use the average price in the range, move the active ingredient into the description, remove duplicate rows, normalize categories...\",\n        \"Raffina anteprima con AI\": \"Refine preview with AI\",\n        \"OK Conferma e salva\": \"OK Confirm and save\",\n        \"X Annulla import\": \"X Cancel import\",\n        \"Gestione File Associati\": \"Associated File Management\",\n        \"Cancella\": \"Delete\",\n        \"Report Attivit\u00e0\": \"Activity Report\",\n        \"Chiedi all'AI di modificare dati in\": \"Ask the AI to edit data in\",\n        \"TUTTE le ditte esistenti\": \"ALL existing companies\",\n        \"contemporaneamente.\": \"at the same time.\",\n        \"Es: Elimina la parola Terni dai nomi delle ditte...\": \"E.g.: Delete the word Terni from company names...\",\n        \"Esegui Modifica su Tutti i DB\": \"Run Edit on All DBs\",\n        \"In attesa di avvio...\": \"Waiting to start...\",\n        \"Pulisci file non associati - Globale\": \"Clean unassociated files - Global\",\n        \"Carica Immagine\/File\": \"Upload Image\/File\",\n        \"Carica dal Dispositivo\": \"Upload from Device\",\n        \"Cerca \/ Genera con AI\": \"Search \/ Generate with AI\",\n        \"AI Image Generator\": \"AI Image Generator\",\n        \"Cosa ti serve? (es. Un logo per una pizzeria con forno a legna)...\": \"What do you need? (e.g. A logo for a pizzeria with a wood-fired oven)...\",\n        \"Istruzioni di dettaglio per la AI (opzionale). Esempio:\": \"Detailed instructions for the AI (optional). Example:\",\n        \"stile elegante, sfondo chiaro, inquadratura ravvicinata, foto realistica\": \"elegant style, light background, close-up framing, realistic photo\",\n        \"Indica eventuali istruzioni di dettaglio per la generazione della foto\": \"Enter any detailed instructions for generating the photo\",\n        \"Genera Immagine\": \"Generate Image\",\n        \"Usa questa Immagine\": \"Use this Image\",\n        \"Riprova\": \"Try again\",\n        \"Aiuto Colonna\": \"Column Help\",\n        \"Cerco nel manuale...\": \"Searching the manual...\",\n        \"Fotocamera\": \"Camera\",\n        \"Scatta Foto\": \"Take Photo\",\n        \"Ricerca Codice ATECO\": \"ATECO Code Search\",\n        \"Descrivi la tua attivit\u00e0 e l'IA trover\u00e0 i codici pi\u00f9 adatti.\": \"Describe your activity and the AI will find the most suitable codes.\",\n        \"Apri ditta in nuova scheda\": \"Open company in new tab\",\n        \"Formula Prezzo finale\": \"Final price formula\",\n        \"Tabella\": \"Table\",\n        \"Colonna\": \"Column\",\n        \"Riga\": \"Row\",\n        \"Valore precedente\": \"Previous value\",\n        \"Nuovo valore\": \"New value\",\n        \"Nessuna chiamata AI eseguita.\": \"No AI call executed.\",\n        \"Istruzione breve non chiara. Usa formato: campo destinazione = campo origine. Nessuna chiamata AI eseguita.\": \"Short instruction not clear. Use format: destination field = source field. No AI call executed.\",\n        \"Regole locali applicate alla preview e al salvataggio:\": \"Local rules applied to preview and saving:\",\n        \"Path condiviso salvato:\": \"Shared path saved:\",\n        \"URL condiviso salvato:\": \"Shared URL saved:\",\n        \"Percorso effettivo in uso:\": \"Effective path in use:\",\n        \"URL effettivo in uso:\": \"Effective URL in use:\",\n        \"Valori default disponibili:\": \"Available default values:\",\n        \"Esempio URL centralizzato Target Informatica:\": \"Target Informatica centralized URL example:\",\n        \"Annullamento associazioni generate in corso...\": \"Cancellation of generated associations in progress...\",\n        \"Generazione foto AI interrotta.\": \"AI photo generation interrupted.\",\n        \"Preparazione elenco record. Priorit\u00e0 a prodotti e servizi senza associazioni file o immagini.\": \"Preparing record list. Priority to products and services without file or image associations.\",\n        \"Altre fonti informative saranno usate solo successivamente se richieste.\": \"Other information sources will be used only later if requested.\",\n        \"Elaborazione\": \"Processing\",\n        \"Elemento\": \"Element\",\n        \"Selezione\": \"Selection\",\n        \"Disponibile\": \"Available\",\n        \"Non disponibile\": \"Not available\",\n        \"Avviso\": \"Notice\",\n        \"Attenzione\": \"Warning\",\n        \"Conferma definitiva\": \"Final confirmation\",\n        \"Password dimenticata\": \"Forgot password\",\n        \"Cambia tramite email\": \"Change by email\",\n        \"Sono l'assistente virtuale di\": \"I am the virtual assistant of\",\n        \"Come posso aiutarti oggi?\": \"How can I help you today?\",\n        \"Sono l\\'assistente virtuale di\": \"I am the virtual assistant of\",\n        \"Sono l\u2019assistente virtuale di\": \"I am the virtual assistant of\",\n        \"Scegli una ditta dal menu in alto, oppure dimmi cosa stai cercando e ti consiglier\u00f2 l'attivit\u00e0 giusta per te!\": \"Choose a company from the top menu, or tell me what you are looking for and I will recommend the right activity for you!\",\n        \"Scegli una ditta dal menu.\": \"Choose a company from the menu.\",\n        \"Ciao\": \"Hello\",\n        \"Non rilevo attivit\u00e0 da 3 minuti. Vuoi continuare ?\": \"I have not detected activity for 3 minutes. Do you want to continue?\",\n        \"Non rilevo attivita da 3 minuti. Vuoi continuare ?\": \"I have not detected activity for 3 minutes. Do you want to continue?\",\n        \"RIEPILOGO ORDINE\": \"ORDER SUMMARY\",\n        \"RIEPILOGO ORDINE RAGGRUPPATO\": \"GROUPED ORDER SUMMARY\",\n        \"Riepilogo ordine\": \"Order summary\",\n        \"Dettaglio ordine\": \"Order details\",\n        \"Conferma ordine\": \"Confirm order\",\n        \"Conferma righe acquisizione\": \"Confirm acquisition rows\",\n        \"Quantit\u00e0\": \"Quantity\",\n        \"Quantita\": \"Quantity\",\n        \"Prezzo\": \"Price\",\n        \"Prezzo vendita\": \"Sale price\",\n        \"Costo\": \"Cost\",\n        \"Costo acquisizione\": \"Acquisition cost\",\n        \"IVA\": \"VAT\",\n        \"Totale IVA\": \"VAT total\",\n        \"Imponibile\": \"Taxable amount\",\n        \"Importo\": \"Amount\",\n        \"Subtotale\": \"Subtotal\",\n        \"Sconto\": \"Discount\",\n        \"Margine\": \"Margin\",\n        \"Data\": \"Date\",\n        \"Ora\": \"Time\",\n        \"Note\": \"Notes\",\n        \"Cliente\": \"Customer\",\n        \"Telefono\": \"Phone\",\n        \"Email\": \"Email\",\n        \"Indirizzo\": \"Address\",\n        \"Accedi a Shop & Service\": \"Log in to Shop & Service\",\n        \"Accesso diretto Shop & Service\": \"Direct Shop & Service access\",\n        \"Ditta\": \"Company\",\n        \"Seleziona ditta\": \"Select company\",\n        \"Utente \/ username\": \"User \/ username\",\n        \"Username\": \"Username\",\n        \"Password\": \"Password\",\n        \"Ricordami\": \"Remember me\",\n        \"Accedi\": \"Log in\",\n        \"Esci\": \"Exit\",\n        \"Logout\": \"Logout\",\n        \"Chiudi\": \"Close\",\n        \"Non hai un account? Registrati\": \"Don\\'t have an account? Register\",\n        \"Registrati\": \"Register\",\n        \"Registrazione\": \"Registration\",\n        \"Nome\": \"Name\",\n        \"Cognome\": \"Surname\",\n        \"Ripeti password\": \"Repeat password\",\n        \"Conferma password\": \"Confirm password\",\n        \"Crea account\": \"Create account\",\n        \"Form login\": \"Login form\",\n        \"Crea Nuova Ditta\": \"Create New Company\",\n        \"Nome della nuova Ditta*\": \"New company name*\",\n        \"Primo Utente (Configuratore)\": \"First User (Configurator)\",\n        \"Email (Opzionale)\": \"Email (Optional)\",\n        \"Crea e Accedi\": \"Create and Login\",\n        \"Alla fine del processo di creazione della ditta, verr\u00e0 generato l'archivio e farai l'accesso automatico a te riservato come configuratore.\": \"At the end of the company creation process, the archive will be generated and you will be automatically logged in with your reserved configurator access.\",\n        \"Il servizio sar\u00e0 GRATUITO di prova per 30 giorni senza impegno, poi potrai eventualmente attivare la ditta alle condizioni commerciali previste per il servizio Shop & Service disponibili direttamente durante la configurazione ed il periodo di prova.\": \"The service will be FREE for a 30-day trial with no commitment. Afterwards, you can activate the company under the commercial conditions provided for the Shop & Service service, available directly during configuration and the trial period.\",\n        \"Trascorso il periodo di prova di 30 giorni, potrai comunque mantenere i dati in attesa del da farsi per ulteriori 30 giorni o cancellarli definitivamente.\": \"After the 30-day trial period, you can still keep the data pending your decision for an additional 30 days or permanently delete it.\",\n        \"Buon lavoro !!\": \"Enjoy your work !!\",\n        \"Configura\": \"Configure\",\n        \"CONFIGURA\": \"CONFIGURE\",\n        \"Acquisizioni\": \"Acquisitions\",\n        \"ACQUISIZIONI\": \"ACQUISITIONS\",\n        \"Globale\": \"Global\",\n        \"GLOBALE\": \"GLOBAL\",\n        \"Stato dell\\\\'azienda: Demo\": \"Company status: Demo\",\n        \"Stato dell'azienda: Demo\": \"Company status: Demo\",\n        \"Stato ditta: Demo\": \"Company status: Demo\",\n        \"\u26a0\ufe0f Stato ditta: Demo\": \"\u26a0\ufe0f Company status: Demo\",\n        \"Salva Modifiche\": \"Save Changes\",\n        \"Salva modifiche\": \"Save changes\",\n        \"Annulla modifiche\": \"Cancel changes\",\n        \"Verifica istruzioni\": \"Check instructions\",\n        \"Carica file AI\": \"Upload AI file\",\n        \"Chatbot AI configurazione\": \"Configuration AI chatbot\",\n        \"Istruzioni AI configurazione\": \"AI configuration instructions\",\n        \"Fornisci istruzioni di configurazione o ottieni indicazioni tramite AI. Esempio: Aggiungere foto AI a tutti i prodotti o fornisci consigli di configurazione\": \"Provide configuration instructions or get guidance via AI. Example: Add AI photos to all products or provide configuration advice\",\n        \"Modifica Globale Database\": \"Global Database Edit\",\n        \"Modifica globale database\": \"Global database edit\",\n        \"Report attivit\u00e0\": \"Activity report\",\n        \"Report attivita\": \"Activity report\",\n        \"Gestione File Associati\": \"Associated File Management\",\n        \"Pulisci file non associati\": \"Clean unassociated files\",\n        \"Pulisci file non associati - Globale\": \"Clean unassociated files - Global\",\n        \"Cancella ditta\": \"Delete company\",\n        \"Attiva Shop-Service\": \"Activate Shop-Service\",\n        \"Acquista attivazione Shop-Service\": \"Purchase Shop-Service activation\",\n        \"Acquista canone servizio Shop-Service\": \"Purchase Shop-Service service fee\",\n        \"Apri consigli ora\": \"Open tips now\",\n        \"Mostra di nuovo il popup consigli operativi\": \"Show the operational tips popup again\",\n        \"Non mostrare pi\u00f9 questo popup in futuro\": \"Do not show this popup again in the future\",\n        \"Non mostrare piu questo popup in futuro\": \"Do not show this popup again in the future\",\n        \"Popup attesa lettura tabella\": \"Table reading wait popup\",\n        \"Lettura tabelle in corso\": \"Reading tables in progress\",\n        \"Sto leggendo le tabelle della ditta. Attendi la fine del caricamento.\": \"I am reading the company tables. Wait until loading is complete.\",\n        \"Tabelle lette\": \"Tables read\",\n        \"Errore lettura tabelle\": \"Table reading error\",\n        \"Attendi il completamento. Premi Esci per interrompere la richiesta.\": \"Wait for completion. Press Exit to interrupt the request.\",\n        \"Modifica database in corso\": \"Database edit in progress\",\n        \"Consigli operativi per configuratore\/amministratore\": \"Operational tips for the configurator\",\n        \"Manuale operativo Shop & Service\": \"Shop & Service operating manual\",\n        \"Cliccare tasto Configura\": \"Click the Configure button\",\n        \"Scorrere le tabelle dell'archivio ditta e verificare quali dati \u00e8 opportuno compilare\": \"Scroll through the company archive tables and check which data should be filled in\",\n        \"Dedicare particolare attenzione alle informazioni ed istruzioni per la AI\": \"Pay particular attention to the information and instructions for the AI\",\n        \"Tabelle\": \"Tables\",\n        \"Campi\": \"Fields\",\n        \"Record\": \"Records\",\n        \"Righe\": \"Rows\",\n        \"Filtro righe\": \"Row filter\",\n        \"Cerca nella tabella\": \"Search in the table\",\n        \"Nessuna riga\": \"No row\",\n        \"Aggiungi riga\": \"Add row\",\n        \"Elimina riga\": \"Delete row\",\n        \"Duplica riga\": \"Duplicate row\",\n        \"Valore\": \"Value\",\n        \"Valori\": \"Values\",\n        \"Campo\": \"Field\",\n        \"Campo obbligatorio\": \"Required field\",\n        \"Non salvato\": \"Not saved\",\n        \"Salvato\": \"Saved\",\n        \"Salvataggio in corso...\": \"Saving...\",\n        \"Salvataggio\": \"Saving\",\n        \"Email\/SMS\": \"Email\/SMS\",\n        \"Email inviata!\": \"Email sent!\",\n        \"SMS inviato!\": \"SMS sent!\",\n        \"Operazione completata.\": \"Operation completed.\",\n        \"Invio email in corso...\": \"Email sending in progress...\",\n        \"Invio SMS in corso...\": \"SMS sending in progress...\",\n        \"Invio notifica\": \"Notification sending\",\n        \"Comunicazione notifica\": \"Notification communication\",\n        \"Comunicazione \/ report\": \"Communication \/ report\",\n        \"Comunicazione\/report aperto in popup.\": \"Communication\/report opened in popup.\",\n        \"Comunicazione aperta in popup.\": \"Communication opened in popup.\",\n};\n    window.tiLayoutI18nAttrNames = ['placeholder','title','aria-label','data-title'];\n    window.tiI18nApplying = false;\n    window.tiI18nObserver = null;\n    window.tiI18nTextCache = window.tiI18nTextCache || Object.create(null);\n    window.tiI18nTextCacheCount = window.tiI18nTextCacheCount || 0;\n    window.tiI18nTextCacheMax = 6000;\n    window.tiI18nSortedKeysCache = null;\n    window.tiI18nDynamicRulesCache = null;\n    window.tiI18nChangedNodes = window.tiI18nChangedNodes || new Set();\n\n    window.tiI18nClearCache = function() {\n        window.tiI18nTextCache = Object.create(null);\n        window.tiI18nTextCacheCount = 0;\n        window.tiI18nSortedKeysCache = null;\n        window.tiI18nDynamicRulesCache = null;\n    };\n    window.tiI18nCacheSet = function(key, value) {\n        key = String(key == null ? '' : key);\n        if (!key) return value;\n        if (!Object.prototype.hasOwnProperty.call(window.tiI18nTextCache, key)) {\n            window.tiI18nTextCacheCount++;\n            if (window.tiI18nTextCacheCount > window.tiI18nTextCacheMax) {\n                window.tiI18nTextCache = Object.create(null);\n                window.tiI18nTextCacheCount = 1;\n            }\n        }\n        window.tiI18nTextCache[key] = value;\n        return value;\n    };\n    window.tiI18nGetSortedKeys = function() {\n        if (!window.tiI18nSortedKeysCache) {\n            const map = window.tiLayoutTranslationsEn || {};\n            window.tiI18nSortedKeysCache = Object.keys(map).filter(function(k){ return k && k.length >= 3; }).sort(function(a,b){ return b.length - a.length; });\n        }\n        return window.tiI18nSortedKeysCache;\n    };\n    window.tiI18nGetDynamicRules = function() {\n        if (!window.tiI18nDynamicRulesCache) {\n            window.tiI18nDynamicRulesCache = [\n                [\/^\u23f3?\\s*Non rilevo attivit[\u00e0a] da (\\d+) minuti?\\. Vuoi continuare \\?$\/i, '\u23f3 I have not detected activity for $1 minutes. Do you want to continue?'],\n                [\/^Sono l\\\\?[\u2019']assistente virtuale di(?:\\s+(.+))?$\/i, function(m){ return 'I am the virtual assistant of' + (m[1] ? ' ' + m[1] : ''); }],\n                [\/^Sono l[\u2019']assistente virtuale di\\s*$\/i, 'I am the virtual assistant of '],\n                [\/^RIEPILOGO ORDINE(?:\\s+(.+))?$\/i, function(m){ return 'ORDER SUMMARY' + (m[1] ? ' ' + m[1] : ''); }],\n                [\/^Totale\\s+(.+):\\s*(.+)$\/i, 'Total $1: $2'],\n                [\/^(.+)\\s+in corso\\.\\.\\.$\/i, '$1 in progress...'],\n                [\/^Lettura tabella\\s+(.+)$\/i, 'Reading table $1'],\n                [\/^Lettura tabelle\\s+(.+)$\/i, 'Reading tables $1'],\n                [\/^Elemento in lavorazione:\\s*(.+)$\/i, 'Item being processed: $1'],\n                [\/^Impostazioni\\s+(.+)$\/i, 'Settings $1'],\n                [\/^Anteprima modifiche aggiornata alle\\s+(.+)$\/i, 'Change preview updated at $1'],\n                [\/^Errore:\\s*(.+)$\/i, 'Error: $1'],\n                [\/^Errore durante\\s+(.+)$\/i, 'Error during $1'],\n                [\/^Righe cancellate:\\s*(.+)$\/i, 'Deleted rows: $1'],\n                [\/^Tabelle con istruzioni:\\s*(.+)$\/i, 'Tables with instructions: $1'],\n                [\/^Righe selezionate:\\s*(.+)$\/i, 'Selected rows: $1'],\n                [\/^Elaborazione\\s+(.+)$\/i, 'Processing $1'],\n                [\/^Caricamento\\s+(.+)$\/i, 'Loading $1']\n            ];\n        }\n        return window.tiI18nDynamicRulesCache;\n    };\n\n    window.tiCleanVisibleEscapedChars = function(value) {\n        let s = String(value == null ? '' : value);\n        if (!s) return s;\n        let prev = null;\n        for (let i = 0; i < 4 && prev !== s; i++) {\n            prev = s;\n            s = s.replace(\/\\\\([\"'])\/g, '$1');\n            s = s.replace(\/^((?:\\s*[\"'\\\\]+)+)(?=\\s*(?:\u26a0\ufe0f|\u23f3|\u2705|\u274c|Company status|Stato ditta|Stato dell\\\\?'azienda|Sono l\\\\?'assistente|I am))\/u, '');\n            s = s.replace(\/^(\u26a0\ufe0f|\u23f3|\u2705|\u274c)(?:\\s*[\"'\\\\]+)+\\s*\/u, '$1 ');\n            s = s.replace(\/(Demo|Dimostrativa|Sospesa|Configurazione|Attivo|Operativo)(?:[\\s\"'\\\\]+)$\/u, '$1');\n        }\n        return s;\n    };\n    window.tiI18nNormalize = function(txt) {\n        const cleaned = window.tiCleanVisibleEscapedChars ? window.tiCleanVisibleEscapedChars(txt) : String(txt == null ? '' : txt);\n        return String(cleaned == null ? '' : cleaned).replace(\/\\u00a0\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n    };\n    window.tiI18nEscapeRegExp = function(txt) {\n        return String(txt).replace(\/[.*+?^${}()|[\\]\\\\]\/g, '\\\\$&');\n    };\n    window.tiI18nApplyCase = function(src, dst) {\n        src = String(src || ''); dst = String(dst || '');\n        if (!dst) return dst;\n        if (src === src.toUpperCase() && \/[A-Z\u00c0-\u00d6\u00d8-\u00de]\/i.test(src)) return dst.toUpperCase();\n        return dst;\n    };\n    window.tiTranslateLayoutStringToEn = function(value) {\n        const originalRaw = String(value == null ? '' : value);\n        const original = window.tiCleanVisibleEscapedChars ? window.tiCleanVisibleEscapedChars(originalRaw) : originalRaw;\n        if (!original.trim()) return original;\n        const cacheKey = 'en|' + original;\n        const cached = window.tiI18nTextCache && Object.prototype.hasOwnProperty.call(window.tiI18nTextCache, cacheKey) ? window.tiI18nTextCache[cacheKey] : null;\n        if (cached !== null) return cached;\n        const map = window.tiLayoutTranslationsEn || {};\n        const m = original.match(\/^(\\s*)([\\s\\S]*?)(\\s*)$\/);\n        const lead = m ? m[1] : '';\n        const body = m ? m[2] : original;\n        const tail = m ? m[3] : '';\n        const normalized = window.tiI18nNormalize(body);\n        if (!normalized) return window.tiI18nCacheSet(cacheKey, original);\n        if (Object.prototype.hasOwnProperty.call(map, normalized)) return window.tiI18nCacheSet(cacheKey, lead + map[normalized] + tail);\n        let out = body;\n        const dynamicRules = window.tiI18nGetDynamicRules ? window.tiI18nGetDynamicRules() : [];\n        for (const rule of dynamicRules) {\n            const mt = normalized.match(rule[0]);\n            if (mt) {\n                const repl = (typeof rule[1] === 'function') ? rule[1](mt) : normalized.replace(rule[0], rule[1]);\n                return window.tiI18nCacheSet(cacheKey, lead + repl + tail);\n            }\n        }\n        const keys = window.tiI18nGetSortedKeys ? window.tiI18nGetSortedKeys() : Object.keys(map);\n        for (const k of keys) {\n            if (out.indexOf(k) !== -1) out = out.split(k).join(window.tiI18nApplyCase(k, map[k]));\n        }\n        return window.tiI18nCacheSet(cacheKey, lead + out + tail);\n    };\n    window.tiTranslateLayoutString = function(value) {\n        const originalRaw = String(value == null ? '' : value);\n        const original = window.tiCleanVisibleEscapedChars ? window.tiCleanVisibleEscapedChars(originalRaw) : originalRaw;\n        if (window.currLang !== 'en' || !original.trim()) return original;\n        return window.tiTranslateLayoutStringToEn(original);\n    };\n    window.tiI18nShouldSkipNode = function(node) {\n        const el = node && node.nodeType === 1 ? node : (node && node.parentElement ? node.parentElement : null);\n        if (!el) return false;\n        const tag = String(el.tagName || '').toUpperCase();\n        if (['SCRIPT','STYLE','NOSCRIPT','SVG','CANVAS'].includes(tag)) return true;\n        if (el.closest && el.closest('[data-ti-no-i18n=\"1\"], .ti-no-i18n')) return true;\n        return false;\n    };\n    window.tiI18nTranslateTextNode = function(node) {\n        if (!node || node.nodeType !== 3 || window.tiI18nShouldSkipNode(node)) return;\n        const current = String(node.nodeValue || '');\n        if (typeof node.__tiI18nOrigin === 'undefined') {\n            node.__tiI18nOrigin = current;\n        } else {\n            const translatedOrigin = window.tiTranslateLayoutStringToEn ? window.tiTranslateLayoutStringToEn(node.__tiI18nOrigin) : window.tiTranslateLayoutString(node.__tiI18nOrigin);\n            if (current !== node.__tiI18nOrigin && current !== translatedOrigin) node.__tiI18nOrigin = current;\n        }\n        node.nodeValue = (window.currLang === 'en') ? window.tiTranslateLayoutString(node.__tiI18nOrigin) : node.__tiI18nOrigin;\n    };\n    window.tiI18nTranslateAttributes = function(el) {\n        if (!el || el.nodeType !== 1 || window.tiI18nShouldSkipNode(el)) return;\n        const attrs = window.tiLayoutI18nAttrNames || [];\n        if (!el.__tiI18nAttrOrigin) el.__tiI18nAttrOrigin = {};\n        attrs.forEach(function(attr){\n            if (!el.hasAttribute || !el.hasAttribute(attr)) return;\n            const current = el.getAttribute(attr);\n            if (typeof el.__tiI18nAttrOrigin[attr] === 'undefined') {\n                el.__tiI18nAttrOrigin[attr] = current;\n            } else {\n                const translatedOrigin = window.tiTranslateLayoutStringToEn ? window.tiTranslateLayoutStringToEn(el.__tiI18nAttrOrigin[attr]) : window.tiTranslateLayoutString(el.__tiI18nAttrOrigin[attr]);\n                if (current !== el.__tiI18nAttrOrigin[attr] && current !== translatedOrigin) el.__tiI18nAttrOrigin[attr] = current;\n            }\n            const base = el.__tiI18nAttrOrigin[attr];\n            el.setAttribute(attr, window.currLang === 'en' ? window.tiTranslateLayoutString(base) : base);\n        });\n        const tag = String(el.tagName || '').toUpperCase();\n        const type = String(el.getAttribute('type') || '').toLowerCase();\n        if (tag === 'INPUT' && ['button','submit','reset'].includes(type) && el.hasAttribute('value')) {\n            const currentVal = el.getAttribute('value');\n            if (typeof el.__tiI18nAttrOrigin.value === 'undefined') {\n                el.__tiI18nAttrOrigin.value = currentVal;\n            } else {\n                const translatedVal = window.tiTranslateLayoutStringToEn ? window.tiTranslateLayoutStringToEn(el.__tiI18nAttrOrigin.value) : window.tiTranslateLayoutString(el.__tiI18nAttrOrigin.value);\n                if (currentVal !== el.__tiI18nAttrOrigin.value && currentVal !== translatedVal) el.__tiI18nAttrOrigin.value = currentVal;\n            }\n            const baseVal = el.__tiI18nAttrOrigin.value;\n            el.setAttribute('value', window.currLang === 'en' ? window.tiTranslateLayoutString(baseVal) : baseVal);\n        }\n    };\n    window.tiI18nTranslateSubtree = function(root) {\n        if (!root) return;\n        if (root.nodeType === 3) {\n            window.tiI18nTranslateTextNode(root);\n            return;\n        }\n        if (root.nodeType !== 1 || window.tiI18nShouldSkipNode(root)) return;\n        window.tiI18nTranslateAttributes(root);\n        const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {\n            acceptNode: function(node) {\n                if (!node || !node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;\n                return window.tiI18nShouldSkipNode(node) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;\n            }\n        });\n        let node;\n        while ((node = walker.nextNode())) window.tiI18nTranslateTextNode(node);\n        if (root.querySelectorAll) root.querySelectorAll('*').forEach(function(el){ window.tiI18nTranslateAttributes(el); });\n    };\n    window.tiCleanEscapedUiInSubtree = function(root) {\n        if (!root || !window.tiCleanVisibleEscapedChars) return;\n        const cleanTextNode = function(node) {\n            if (!node || node.nodeType !== 3 || window.tiI18nShouldSkipNode(node)) return;\n            const cleaned = window.tiCleanVisibleEscapedChars(node.nodeValue || '');\n            if (cleaned !== node.nodeValue) {\n                node.nodeValue = cleaned;\n                node.__tiI18nOrigin = cleaned;\n            }\n        };\n        if (root.nodeType === 3) { cleanTextNode(root); return; }\n        if (root.nodeType !== 1 || window.tiI18nShouldSkipNode(root)) return;\n        const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {\n            acceptNode: function(node) {\n                if (!node || !node.nodeValue || !node.nodeValue.trim()) return NodeFilter.FILTER_REJECT;\n                return window.tiI18nShouldSkipNode(node) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;\n            }\n        });\n        let node;\n        while ((node = walker.nextNode())) cleanTextNode(node);\n        const attrs = window.tiLayoutI18nAttrNames || [];\n        const scanAttr = function(el) {\n            if (!el || !el.hasAttribute || window.tiI18nShouldSkipNode(el)) return;\n            attrs.forEach(function(attr){\n                if (!el.hasAttribute(attr)) return;\n                const val = el.getAttribute(attr);\n                const cleaned = window.tiCleanVisibleEscapedChars(val);\n                if (cleaned !== val) el.setAttribute(attr, cleaned);\n            });\n        };\n        scanAttr(root);\n        if (root.querySelectorAll) root.querySelectorAll('*').forEach(scanAttr);\n    };\n    window.applyLayoutLanguage = function(force) {\n        const root = document.body || document.getElementById('ti-ai-outer');\n        if (!root || window.tiI18nApplying) return;\n        window.tiI18nApplying = true;\n        try {\n            document.querySelectorAll('.ti-lang-opt').forEach(function(el){\n                el.classList.toggle('active', String(el.dataset.lang || '') === String(window.currLang || 'it'));\n            });\n            if (typeof dict !== 'undefined') {\n                const d = dict[window.currLang] || dict.it || {};\n                if (gE('ti-msg')) gE('ti-msg').placeholder = d.ph || (window.currLang === 'en' ? 'Type here...' : 'Scrivi qui...');\n                if (gE('ti-send')) gE('ti-send').innerText = d.send || (window.currLang === 'en' ? 'SEND' : 'INVIA');\n                if (gE('ti-new')) gE('ti-new').innerText = d.new || (window.currLang === 'en' ? 'New' : 'Nuova');\n                if (gE('ti-copy')) { gE('ti-copy').innerText = d.copy || (window.currLang === 'en' ? 'Copy text' : 'Copia testo'); gE('ti-copy').style.fontSize='11px'; }\n                if (gE('ti-file')) gE('ti-file').innerText = d.file || 'File';\n                if (gE('ti-products-btn')) gE('ti-products-btn').innerText = d.products || (window.currLang === 'en' ? 'Products' : 'Prodotti');\n                if (gE('ti-services-btn')) gE('ti-services-btn').innerText = d.services || (window.currLang === 'en' ? 'Services' : 'Servizi');\n                if (gE('ti-activities-btn')) gE('ti-activities-btn').innerText = d.activities || (window.currLang === 'en' ? 'Activities' : 'Attivit\u00e0');\n                if (gE('ti-kbd-btn')) gE('ti-kbd-btn').innerText = d.kbd || (window.currLang === 'en' ? 'Keyboard' : 'Tastiera');\n                if (gE('ti-mic')) gE('ti-mic').innerText = d.mic || 'Mic';\n                if (gE('ti-cam')) gE('ti-cam').innerText = d.cam || 'Cam';\n                if (gE('ti-login-btn') && gE('ti-login-btn').style.display !== 'none') gE('ti-login-btn').innerText = d.login || (window.currLang === 'en' ? 'Login' : 'Accedi');\n            }\n            window.tiI18nTranslateSubtree(root);\n            if (window.updateActivitiesButtonVisibility) window.updateActivitiesButtonVisibility();\n            if (window.applyMuteState) window.applyMuteState();\n        } catch(e) {\n            try { console.warn('Shop & Service i18n layout error', e); } catch(ignore) {}\n        } finally {\n            window.tiI18nApplying = false;\n        }\n    };\n    window.tiOpenUserActivities = function() {\n        try {\n            const user = String(window.tiUser || '').trim();\n            if (!user) { if (window.tiAlert) window.tiAlert('Accedi prima di consultare le tue attivit\u00e0.'); return false; }\n            window.lastUploadedFile = null;\n            if (window.lastFileContext) {\n                window.lastFileContext.active = false;\n                window.lastFileContext.awaitingReuseChoice = false;\n                window.lastFileContext.pendingReusePrompt = '';\n            }\n            const label = (String(window.currLang || 'it').toLowerCase() === 'en') ? 'Activities' : 'Attivit\u00e0';\n            if (window.tiSendQuickReply) { window.tiSendQuickReply('__TI_USER_ACTIVITIES__', label); }\n            else {\n                window.tiHiddenSendText = '__TI_USER_ACTIVITIES__';\n                window.tiHiddenSendLabel = label;\n                const sendBtn = gE('ti-send');\n                if (sendBtn) sendBtn.click();\n            }\n            return false;\n        } catch(e) { try { console.warn('Shop & Service activities button error', e); } catch(ignore) {} return false; }\n    };\n    window.ensureActivitiesButton = function() {\n        try {\n            let b = gE('ti-activities-btn');\n            const services = gE('ti-services-btn');\n            if (!b) {\n                b = document.createElement('button');\n                b.type = 'button';\n                b.id = 'ti-activities-btn';\n                b.className = 'ti-btn ti-btn-catalog ti-btn-activities';\n                b.textContent = (String(window.currLang || 'it').toLowerCase() === 'en') ? 'Activities' : 'Attivit\u00e0';\n                if (services && services.parentNode) services.parentNode.insertBefore(b, services.nextSibling);\n                else {\n                    const row = document.querySelector('.ti-actions-row');\n                    if (row) row.appendChild(b);\n                }\n            } else if (services && services.parentNode && b.previousElementSibling !== services) {\n                services.parentNode.insertBefore(b, services.nextSibling);\n            }\n            b.onclick = window.tiOpenUserActivities;\n            b.style.alignItems = 'center';\n            b.style.justifyContent = 'center';\n            b.style.gap = '4px';\n            b.title = 'Attivit\u00e0 e appuntamenti dell utente loggato';\n            return b;\n        } catch(e) { return gE('ti-activities-btn'); }\n    };\n    window.updateActivitiesButtonVisibility = function() {\n        try {\n            const b = window.ensureActivitiesButton ? window.ensureActivitiesButton() : gE('ti-activities-btn');\n            if (!b) return;\n            const logged = !!String(window.tiUser || '').trim();\n            if (logged) {\n                b.removeAttribute('hidden');\n                b.disabled = false;\n                b.style.setProperty('display', 'inline-flex', 'important');\n                b.style.visibility = 'visible';\n                b.style.opacity = '1';\n            } else {\n                b.disabled = true;\n                b.style.setProperty('display', 'none', 'important');\n                b.style.visibility = 'hidden';\n                b.style.opacity = '0';\n            }\n        } catch(e) {}\n    };\n\n    window.installLayoutI18nObserver = function() {\n        const root = document.body || document.getElementById('ti-ai-outer');\n        if (!root || window.tiI18nObserver) return;\n        let pending = null;\n        const changedNodes = window.tiI18nChangedNodes || new Set();\n        window.tiI18nChangedNodes = changedNodes;\n        const addChangedNode = function(node) {\n            if (!node) return;\n            if (node.nodeType === 3 || node.nodeType === 1) changedNodes.add(node);\n        };\n        window.tiI18nObserver = new MutationObserver(function(mutations){\n            if (window.tiI18nApplying) return;\n            mutations.forEach(function(m){\n                if (m.type === 'characterData') addChangedNode(m.target);\n                else if (m.type === 'attributes') addChangedNode(m.target);\n                else if (m.type === 'childList') (m.addedNodes || []).forEach(addChangedNode);\n            });\n            clearTimeout(pending);\n            pending = setTimeout(function(){\n                if (window.tiI18nApplying) return;\n                if (window.currLang !== 'en') { changedNodes.clear(); return; }\n                const fullScan = changedNodes.size > 120;\n                const nodes = fullScan ? [] : Array.from(changedNodes);\n                changedNodes.clear();\n                if (fullScan) { window.applyLayoutLanguage(false); return; }\n                window.tiI18nApplying = true;\n                try {\n                    nodes.forEach(function(node){ window.tiI18nTranslateSubtree(node); });\n                    if (window.applyMuteState) window.applyMuteState();\n                } catch(e) {\n                    try { console.warn('Shop & Service i18n incremental error', e); } catch(ignore) {}\n                } finally {\n                    window.tiI18nApplying = false;\n                }\n            }, 120);\n        });\n        window.tiI18nObserver.observe(root, {childList:true, subtree:true, characterData:true, attributes:true, attributeFilter:['placeholder','title','aria-label','data-title','value']});\n    };\n    window.tiApplyEnglishLayoutIfNeeded = function() {\n        try {\n            if (!['it','en'].includes(String(window.currLang || ''))) window.currLang = 'it';\n            window.installLayoutI18nObserver();\n            window.applyLayoutLanguage(true);\n        } catch(e) {}\n    };\n    document.addEventListener('DOMContentLoaded', window.tiApplyEnglishLayoutIfNeeded);\n    setTimeout(window.tiApplyEnglishLayoutIfNeeded, 0);\n\n    window.isConfiguratorUser = function() {\n        return \/amministratore|administrator|configuratore|configurator\/i.test(String(window.tiRole || '')) || String(window.tiUser || '') === 'SSGlobalAdmin';\n    };\n    window.isAdminConfiguratorUser = function() {\n        return \/amministratore|administrator|configuratore|configurator\/i.test(String(window.tiRole || '')) || String(window.tiUser || '') === 'SSGlobalAdmin';\n    };\n    window.isDbModificationWaitRole = function() {\n        return \/amministratore|administrator|configuratore|configurator|responsabile|responsible\/i.test(String(window.tiRole || '')) || String(window.tiUser || '') === 'SSGlobalAdmin';\n    };\n    window.getStoragePathForTable = function(dbName, tblName) {\n        const dbRaw = String(dbName || '').replace(\/\\.json$\/i, '');\n        const tbl = String(tblName || '').trim();\n        if (!dbRaw || !tbl) return '';\n        let p = '\/wp-content\/uploads\/AI-data\/' + dbRaw + '\/Tabelle\/' + tbl;\n        if (!\/(prodott|serviz)\/i.test(tbl)) p += '\/Docs';\n        return p;\n    };\n    window.showDbOperationWait = function(operationLabel, detail) {\n        if (!window.isDbModificationWaitRole || !window.isDbModificationWaitRole()) return false;\n        if (typeof window.showProgressPopup === 'function') {\n            window.showProgressPopup(operationLabel || 'Modifica database in corso', detail || 'Attendi il completamento. Premi Esci per interrompere la richiesta.', {cancellable:true, targetPercent:85, stepMs:450, startPercent:5});\n            return true;\n        }\n        return false;\n    };\n    window.hideDbOperationWait = function(success, finalText, opts) {\n        if (typeof window.hideProgressPopup === 'function') {\n            window.hideProgressPopup(success !== false, finalText || (success === false ? 'Operazione interrotta' : 'Operazione completata'), Object.assign({notifyUser:false}, opts || {}));\n        }\n    };\n\n    window.showTableReadWait = function(title, detail, currentItem) {\n        \/\/ 30.9.664 - Fix chirurgico produzione:\n        \/\/ la semplice lettura tabelle di Configurazione non deve aprire un loader modale.\n        \/\/ In produzione il loader poteva restare a 100% e bloccare la UI; per questa sola operazione\n        \/\/ si preferisce non mostrare il popup di avanzamento.\n        try {\n            window.__tiTableReadWaitSuppressed343 = true;\n            if (typeof window.tiDismissCompletedTableReadLoader343 === 'function') {\n                window.tiDismissCompletedTableReadLoader343('showTableReadWait-suppressed');\n            }\n        } catch(e) {}\n        return false;\n    };\n    window.hideTableReadWait = function(success, finalText) {\n        try {\n            if (typeof window.tiDismissCompletedTableReadLoader343 === 'function') {\n                window.tiDismissCompletedTableReadLoader343('hideTableReadWait');\n            }\n            \/\/ Non richiamare hideProgressPopup per la lettura tabelle: il popup e' stato soppresso.\n        } catch(e) {}\n    };\n    window.getShopServiceManualUrl = function() {\n        try {\n            const setup = window.tiSharedSetup || {};\n            let u = String(setup.manual_url || '').trim();\n            if (!u) {\n                const base = String(setup.docs_base_url || 'https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service').replace(\/\\\/+$\/, '');\n                u = base + '\/Shop-Service%20Manual.pdf';\n            }\n            return u;\n        } catch(e) {\n            return 'https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service\/Shop-Service%20Manual.pdf';\n        }\n    };\n\n    window.escapeAttrValue = window.escapeAttrValue || function(v) {\n        return String(v == null ? '' : v).replace(\/&\/g, '&amp;').replace(\/\"\/g, '&quot;').replace(\/<\/g, '&lt;').replace(\/>\/g, '&gt;');\n    };\n\n    window.getConfiguratorOperationalTipsUserKey = function() {\n        const d = gE('ti-ditta');\n        const dbKey = d && d.value ? String(d.value) : 'no-db';\n        const userKey = String(window.tiUser || window.tiUserDisplay || 'configuratore');\n        return encodeURIComponent(dbKey) + '_' + encodeURIComponent(userKey);\n    };\n\n    window.getConfiguratorOperationalTipsShownKey = function() {\n        return 'ti_cfg_operational_tips_shown_' + window.getConfiguratorOperationalTipsUserKey();\n    };\n\n    window.getConfiguratorOperationalTipsDisabledKey = function() {\n        return 'ti_cfg_operational_tips_disabled_' + window.getConfiguratorOperationalTipsUserKey();\n    };\n\n    window.isConfiguratorOperationalTipsDisabled = function() {\n        try {\n            return !!(window.localStorage && localStorage.getItem(window.getConfiguratorOperationalTipsDisabledKey()) === '1');\n        } catch(e) { return false; }\n    };\n\n    window.setConfiguratorOperationalTipsDisabled = function(disabled, silent) {\n        disabled = !!disabled;\n        try {\n            if (window.localStorage) {\n                if (disabled) localStorage.setItem(window.getConfiguratorOperationalTipsDisabledKey(), '1');\n                else localStorage.removeItem(window.getConfiguratorOperationalTipsDisabledKey());\n            }\n            if (!disabled && window.sessionStorage) sessionStorage.removeItem(window.getConfiguratorOperationalTipsShownKey());\n        } catch(e) {}\n        if (window.syncConfiguratorOperationalTipsFlagUI) window.syncConfiguratorOperationalTipsFlagUI();\n        if (!silent && window.tiAlert) {\n            window.tiAlert(disabled ? 'Popup consigli operativi configuratore\/amministratore disabilitato per i prossimi accessi.' : 'Popup consigli operativi configuratore\/amministratore riabilitato. Verr\u00e0 mostrato di nuovo ai prossimi accessi.');\n        }\n    };\n\n    window.syncConfiguratorOperationalTipsFlagUI = function() {\n        const enabled = !(window.isConfiguratorOperationalTipsDisabled && window.isConfiguratorOperationalTipsDisabled());\n        const cb = gE('ti-cfg-tips-enabled');\n        const status = gE('ti-cfg-tips-status');\n        if (cb) cb.checked = enabled;\n        if (status) status.textContent = enabled ? 'Popup consigli operativi attivo.' : 'Popup consigli operativi disabilitato.';\n    };\n\n    window.handleConfiguratorTipsFlagChange = function(cb) {\n        const enabled = !!(cb && cb.checked);\n        window.setConfiguratorOperationalTipsDisabled(!enabled, false);\n    };\n\n    window.getConfiguratorWelcomeHtml = function() {\n        if (!window.isConfiguratorUser()) return '';\n        let html = String(window.tiConfiguratorAccessMessage || '');\n        const roleNow = String(window.tiRole || '').toLowerCase();\n        const privileged = (!!window.tiUser && window.tiUser === 'SSGlobalAdmin') || \/amministratore|configuratore|responsabile|globale\/.test(roleNow);\n        if (privileged) {\n            const note = [\n                '<div style=\"margin-top:8px;padding:8px 10px;border-radius:10px;background:#0f172a;border:1px solid #334155;color:#bfdbfe;font-size:12px;line-height:1.35;\">',\n                '<b>Funzioni utente attive<\/b><br>',\n                \"Puoi usare anche la main form come utente standard registrato: chat, prodotti, servizi, ordini con tabella ordine completa, conferma ordine, prenotazioni, file\\\/foto e comunicazioni. I popup informativi restano attivi e separati; le tabelle operative di ordine restano nella main form.\",\n                '<\/div>'\n            ].join('');\n            html = html ? (html + note) : note;\n        }\n        return html;\n    };\n\n    window.getConfiguratorOperationalTipsHtml = function() {\n        const manualUrl = window.getShopServiceManualUrl ? window.getShopServiceManualUrl() : 'https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service\/Shop-Service%20Manual.pdf';\n        const disabled = window.isConfiguratorOperationalTipsDisabled && window.isConfiguratorOperationalTipsDisabled();\n        const safeManualUrl = window.escapeAttrValue ? window.escapeAttrValue(manualUrl) : manualUrl;\n        return `\n            <div style=\"text-align:left;line-height:1.45;font-size:13px;color:#e5e7eb;\">\n                <div style=\"font-size:17px;font-weight:800;color:#ffffff;margin-bottom:10px;\">Consigli operativi per configuratore\/amministratore<\/div>\n                <ol style=\"margin:0;padding-left:20px;\">\n                    <li>Cliccare tasto <b>Configura<\/b>.<\/li>\n                    <li>Scorrere le tabelle dell\u2019archivio ditta e verificare quali dati \u00e8 opportuno compilare.<\/li>\n                    <li>In <b>Istruzioni AI configurazione<\/b>, possibilit\u00e0 di fornire alla AI istruzioni o richieste per configurare e manutenere le informazioni.<\/li>\n                    <li>Prodotti e servizi possono essere importati da dati copiati nel campo di messaggistica per la AI o da file esterni tramite la funzione <b>Carica file AI<\/b>.<\/li>\n                    <li>Dedicare particolare attenzione alle informazioni ed istruzioni per la AI.<\/li>\n                    <li>La AI, nel trattare l\u2019utente, tiene conto delle informazioni che ha a disposizione ed esegue le specifiche istruzioni del configuratore\/amministratore.<\/li>\n                    <li>Le istruzioni fornite possono essere in ogni momento verificate tramite il tasto <b>Verifica istruzioni<\/b>.<\/li>\n                    <li>Le foto ed i documenti associati a prodotti e servizi possono essere generati dalla AI tramite le funzioni di generazione, per poi essere comunque successivamente manutenuti dal configuratore\/amministratore.<\/li>\n                    <li>Il <b>Report attivit\u00e0<\/b> fornisce un macro punto di vista sulle attivit\u00e0 effettuate nel gestire il servizio.<\/li>\n                <\/ol>\n                <div style=\"margin-top:14px;padding-top:12px;border-top:1px solid #374151;display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap;\">\n                    <a href=\"${safeManualUrl}\" target=\"_blank\" rel=\"noopener\" class=\"ti-btn\" style=\"background:#059669;color:#fff;text-decoration:none;font-size:12px;padding:8px 10px;border-radius:8px;display:inline-flex;align-items:center;gap:6px;\">\ud83d\udcc4 Manuale operativo Shop & Service<\/a>\n                    <label style=\"display:flex;align-items:center;gap:7px;color:#cbd5e1;font-size:12px;cursor:pointer;\">\n                        <input type=\"checkbox\" id=\"ti-cfg-tips-disable-from-popup\" ${disabled ? 'checked' : ''} onchange=\"if(window.setConfiguratorOperationalTipsDisabled) window.setConfiguratorOperationalTipsDisabled(this.checked, true);\">\n                        Non mostrare pi\u00f9 questo popup in futuro\n                    <\/label>\n                <\/div>\n            <\/div>`;\n    };\n\n    window.showConfiguratorOperationalTipsPopup = function(force) {\n        if (!window.isConfiguratorUser || !window.isConfiguratorUser()) return false;\n        if (!force && window.isConfiguratorOperationalTipsDisabled && window.isConfiguratorOperationalTipsDisabled()) return false;\n        const key = window.getConfiguratorOperationalTipsShownKey ? window.getConfiguratorOperationalTipsShownKey() : 'ti_cfg_operational_tips_shown_config_admin';\n        try {\n            if (!force && window.sessionStorage && sessionStorage.getItem(key) === '1') return false;\n            if (window.sessionStorage) sessionStorage.setItem(key, '1');\n        } catch(e) {}\n        const html = window.getConfiguratorOperationalTipsHtml ? window.getConfiguratorOperationalTipsHtml() : '';\n        if (!html) return false;\n        let shown = false;\n        if (window.showPrivilegedCommunicationPopup) {\n            shown = window.showPrivilegedCommunicationPopup(html, 'Consigli operativi per configuratore\/amministratore', {text: window.tiPlainTextFromHtml ? window.tiPlainTextFromHtml(html) : html});\n        } else if (window.tiAlert) {\n            window.tiAlert(html);\n            shown = true;\n        }\n        if (shown && window.syncConfiguratorOperationalTipsFlagUI) setTimeout(window.syncConfiguratorOperationalTipsFlagUI, 0);\n        return shown;\n    };\n\n    function utoa(str) { return window.btoa(unescape(encodeURIComponent(str))); }\n    \n    window.getRecordLabelForUpload = function(tblName, rowObj, fallback) {\n        const tbl = String(tblName || '').toLowerCase();\n        const row = rowObj || {};\n        const picks = ['Descrizione','Prodotto','Servizio','Username','Utente','Nome','Titolo','Ragione sociale'];\n        for (const k of picks) {\n            if (row && row[k] && String(row[k]).trim()) return String(row[k]).trim();\n            const hit = Object.keys(row || {}).find(x => String(x).toLowerCase() === String(k).toLowerCase());\n            if (hit && String(row[hit] || '').trim()) return String(row[hit]).trim();\n        }\n        if (fallback) return String(fallback).trim();\n        return tbl === 'utenti' ? 'utente' : (tblName || 'record');\n    };\n\n    window.isLegacyUserImagePath = function(val) {\n        return \/^wp-content\\\/uploads\\\/ai-data\\\/.+\\\/utenti\\\/\/i.test(String(val || '').trim());\n    };\n    window.getFullImgUrl = function(val, dbName, tName, recordLabel) {\n        if (!val) return '';\n        val = String(val).trim();\n        if (val.indexOf(',') !== -1) val = String(val.split(',')[0] || '').trim();\n        let dbRaw = dbName ? String(dbName).replace('.json', '') : '';\n        const tableName = String(tName || '').trim();\n        if (!val) return '';\n        if (window.isLegacyUserImagePath(val) && \/^utenti$\/i.test(tableName)) return '';\n        if (val.indexOf('ti_action=ti_ai_get_image') !== -1) return val;\n        if (\/^https?:\\\/\\\/\/i.test(val)) return val;\n        if (\/^\\\/?wp-content\\\/uploads\\\/\/i.test(val)) return window.location.origin + '\/' + val.replace(\/^\\\/+\/, '');\n        if (\/^\\\/\/.test(val)) return window.location.origin + val;\n        if (\/^https?:\\\/\\\/\/i.test(val) && \/\\\/wp-content\\\/uploads\\\/\/i.test(val)) {\n            const pts = val.split('\/');\n            val = pts[pts.length - 1];\n            return window.tiUrl + '?ti_action=ti_ai_get_image&db=' + encodeURIComponent(dbRaw) + '&tbl=' + encodeURIComponent(tableName) + '&file=' + encodeURIComponent(val) + (recordLabel ? '&record=' + encodeURIComponent(recordLabel) : '');\n        }\n        if (\/^wp-content\\\/uploads\\\/\/i.test(val) || \/^uploads\\\/\/i.test(val) || (val.indexOf('AI-data\/') !== -1 && val.indexOf('\/') !== -1)) {\n            const pts = val.split('\/');\n            val = pts[pts.length - 1];\n            return window.tiUrl + '?ti_action=ti_ai_get_image&db=' + encodeURIComponent(dbRaw) + '&tbl=' + encodeURIComponent(tableName) + '&file=' + encodeURIComponent(val) + (recordLabel ? '&record=' + encodeURIComponent(recordLabel) : '');\n        }\n        if (val.indexOf('\/') !== -1) {\n            const pts = val.split('\/');\n            val = pts[pts.length - 1];\n        }\n        return window.tiUrl + '?ti_action=ti_ai_get_image&db=' + encodeURIComponent(dbRaw) + '&tbl=' + encodeURIComponent(tableName) + '&file=' + encodeURIComponent(val) + (recordLabel ? '&record=' + encodeURIComponent(recordLabel) : '');\n    };\n    window.getCompanyFallbackMediaUrl = function(dbName) {\n        const dbVal = dbName || (gE('ti-ditta') ? gE('ti-ditta').value : '');\n        let logo = '';\n        if (window.currentDbData && window.currentDbData.Tabelle && Array.isArray(window.currentDbData.Tabelle.Config) && window.currentDbData.Tabelle.Config[0]) {\n            const cfg = window.currentDbData.Tabelle.Config[0];\n            logo = cfg.Logo || cfg.logo || cfg.Immagine || cfg.immagine || '';\n        }\n        if (!logo && Array.isArray(window.ditteData)) {\n            const hit = window.ditteData.find(d => (d.db || '') === dbVal || (d.db || '') === String(dbVal).replace('.json','') + '.json');\n            if (hit) {\n                logo = hit.logo || '';\n                if (!logo && hit.qr) return hit.qr;\n            }\n        }\n        if (logo) return window.getFullImgUrl(logo, dbVal, 'Config');\n        if (Array.isArray(window.ditteData)) {\n            const hit = window.ditteData.find(d => (d.db || '') === dbVal || (d.db || '') === String(dbVal).replace('.json','') + '.json');\n            if (hit && hit.qr) return hit.qr;\n        }\n        return '';\n    };\n\n    window.getSafePreviewUrl = function(val, dbName, tName, keyName, recordLabel) {\n        const fb = window.getCompanyFallbackMediaUrl(dbName || (gE('ti-ditta') ? gE('ti-ditta').value : '')) || '';\n        const raw = String(val || '').trim();\n        const tbl = String(tName || '').toLowerCase();\n        const key = String(keyName || '').toLowerCase();\n        const url = window.getFullImgUrl(raw, dbName, tName, recordLabel);\n        if (!raw) return fb || url;\n        if ((tbl === 'utenti' || key === 'immagine' || key === 'foto' || key === 'logo') && window.isLegacyUserImagePath(raw)) {\n            return fb || url;\n        }\n        return url || fb;\n    };\n    window.applyFallbackImage = function(imgEl) {\n        if (!imgEl) return;\n        const fb = imgEl.getAttribute('data-fallback') || window.getCompanyFallbackMediaUrl(gE('ti-ditta') ? gE('ti-ditta').value : '');\n        if (fb && imgEl.src !== fb) {\n            imgEl.onerror = null;\n            imgEl.src = fb;\n            return;\n        }\n        imgEl.style.display = 'none';\n    };\n\n    window.fmtNum = function(num) {\n        let n = parseFloat(num) || 0;\n        let parts = n.toFixed(2).split('.');\n        parts[0] = parts[0].replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \".\");\n        return parts.join(',');\n    };\n\n    window.parseIvaRate = function(val, defVal = 22) {\n        if (val == null) return defVal;\n        let raw = String(val).trim();\n        if (!raw) return defVal;\n        raw = raw.replace(\/%\/g, '').replace(',', '.');\n        let n = parseFloat(raw);\n        return isNaN(n) ? defVal : n;\n    };\n\n    window.calcVatAmountFromGross = function(gross, ivaRate) {\n        let g = parseFloat(gross) || 0;\n        let r = window.parseIvaRate(ivaRate, 22);\n        if (r <= 0) return 0;\n        return g - (g \/ (1 + (r \/ 100)));\n    };\n\n    window.calcVatNetFromGross = function(gross, ivaRate) {\n        let g = parseFloat(gross) || 0;\n        let vat = window.calcVatAmountFromGross(g, ivaRate);\n        return g - vat;\n    };\n\n\n    window.fetchJsonSafe = function(url, options) {\n        const opts = Object.assign({}, options || {});\n        let tableReadWait = false;\n        let tableReadWaitShown = false;\n        let tableReadOk = false;\n        let tableReadTitle = '\u23f3 Lettura tabelle in corso';\n        let tableReadDetail = 'Sto leggendo le tabelle della ditta. Attendi la fine del caricamento.';\n        let tableReadItem = '';\n        try {\n            tableReadWait = !!opts.tiShowTableReadWait;\n            if (opts.tiTableReadTitle) tableReadTitle = String(opts.tiTableReadTitle);\n            if (opts.tiTableReadDetail) tableReadDetail = String(opts.tiTableReadDetail);\n            if (opts.tiTableReadItem) tableReadItem = String(opts.tiTableReadItem);\n            delete opts.tiShowTableReadWait;\n            delete opts.tiTableReadTitle;\n            delete opts.tiTableReadDetail;\n            delete opts.tiTableReadItem;\n        } catch(e) {}\n        try {\n            if (opts.body instanceof FormData && opts.body.has('ti_action') && !opts.body.has('action')) {\n                opts.body.append('action', opts.body.get('ti_action'));\n            }\n            if (opts.body instanceof FormData && window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(opts.body);\n        } catch(e) {}\n        const tiNoLongProcessSignal = !!opts.tiNoLongProcessSignal;\n        if (Object.prototype.hasOwnProperty.call(opts, 'tiNoLongProcessSignal')) delete opts.tiNoLongProcessSignal;\n        if (!tiNoLongProcessSignal && !opts.signal && window.tiLongProcessState && window.tiLongProcessState.controller) {\n            opts.signal = window.tiLongProcessState.controller.signal;\n        }\n        if (tableReadWait && typeof window.showTableReadWait === 'function') {\n            tableReadWaitShown = window.showTableReadWait(tableReadTitle, tableReadDetail, tableReadItem);\n        }\n        const requestPromise = fetch(url, opts).then(async (response) => {\n            const raw = await response.text();\n            if (!response.ok) {\n                const httpErr = new Error(`HTTP ${response.status}: ${raw.slice(0, 180) || 'Risposta server vuota'}`);\n                httpErr.httpStatus = response.status;\n                throw httpErr;\n            }\n            try {\n                return JSON.parse(raw);\n            } catch (err) {\n                throw new Error(`Risposta server non valida: ${raw.slice(0, 180) || 'vuota'}`);\n            }\n        }).then((data) => {\n            try {\n                if (data && data.success === false && data.data && data.data.maintenance && data.data.maintenance.active) {\n                    var maint = data.data.maintenance || {};\n                    if (typeof window.tiSsEnterMaintenance496 === 'function') {\n                        window.tiSsEnterMaintenance496(maint.message || (data.data && data.data.message) || 'Shop & Service temporaneamente in manutenzione.', maint.detail || '');\n                    }\n                    var mErr = new Error((data.data && data.data.message) || 'Shop & Service temporaneamente in manutenzione.');\n                    mErr.maintenance = maint;\n                    throw mErr;\n                }\n            } catch(checkErr) {\n                throw checkErr;\n            }\n            tableReadOk = true;\n            return data;\n        }).catch((err) => {\n            if (err && err.name === 'AbortError') {\n                const abortErr = new Error('Processo interrotto dall utente.');\n                abortErr.userCancelled = true;\n                throw abortErr;\n            }\n            throw err;\n        });\n        if (!tableReadWaitShown || typeof requestPromise.finally !== 'function') return requestPromise;\n        return requestPromise.finally(function(){\n            if (typeof window.hideTableReadWait === 'function') window.hideTableReadWait(tableReadOk, tableReadOk ? 'Tabelle lette' : 'Errore lettura tabelle');\n        });\n    };\n\n\n    window.tiAppendCurrentLanguageToFormData = function(fd) {\n        try {\n            if (!(fd instanceof FormData)) return fd;\n            const lang = (String(window.currLang || 'it').toLowerCase().indexOf('en') === 0) ? 'en' : 'it';\n            if (!fd.has('ti_lang')) fd.append('ti_lang', lang);\n            if (!fd.has('ti_ui_lang')) fd.append('ti_ui_lang', lang);\n        } catch(e) {}\n        return fd;\n    };\n    window.buildAjaxFormData = function(actionName, extra) {\n        const fd = new FormData();\n        fd.append('action', actionName);\n        fd.append('ti_action', actionName);\n        if (extra && typeof extra === 'object') {\n            Object.keys(extra).forEach((key) => {\n                if (typeof extra[key] !== 'undefined' && extra[key] !== null) fd.append(key, extra[key]);\n            });\n        }\n        return window.tiAppendCurrentLanguageToFormData(fd);\n    };\n    window.ensureAjaxActionField = function(fd) {\n        if (!(fd instanceof FormData)) return fd;\n        if (!fd.has('action') && fd.has('ti_action')) fd.append('action', fd.get('ti_action'));\n        return window.tiAppendCurrentLanguageToFormData(fd);\n    };\n\n    window.postFormDataJsonSafe = function(url, fd, options) {\n        const formData = window.ensureAjaxActionField(fd);\n        let targetUrl = url || window.tiAjaxUrl || window.tiUrl;\n        \/\/ Le azioni di configurazione usano l'autenticazione interna del plugin.\n        \/\/ Se l'utente non e loggato in WordPress, admin-ajax.php puo rispondere HTTP 400: 0.\n        \/\/ Per queste richieste usiamo quindi l'endpoint pagina\/interceptor, che e lo stesso flusso del chatbot configurazione.\n        try {\n            const act = (formData instanceof FormData) ? String(formData.get('ti_action') || formData.get('action') || '') : '';\n            if (act === 'ti_ai_config_action' && window.tiUrl) targetUrl = window.tiUrl;\n        } catch(e) {}\n        const opts = Object.assign({method:'POST', body:formData, credentials:'same-origin', tiNoLongProcessSignal:true}, options || {});\n        return window.fetchJsonSafe(targetUrl, opts).catch(function(fetchErr){\n            if (!window.jQuery || !(formData instanceof FormData)) throw fetchErr;\n            return new Promise(function(resolve, reject){\n                window.jQuery.ajax({\n                    url: targetUrl,\n                    method: 'POST',\n                    data: formData,\n                    processData: false,\n                    contentType: false,\n                    cache: false,\n                    dataType: 'json',\n                    timeout: 180000,\n                    success: function(data){ resolve(data); },\n                    error: function(xhr, textStatus, errorThrown){\n                        let msg = errorThrown || textStatus || 'errore rete';\n                        try {\n                            const txt = xhr && xhr.responseText ? String(xhr.responseText).trim() : '';\n                            if (txt) msg = txt.slice(0, 220);\n                        } catch(e) {}\n                        const err = new Error('XHR fallback fallito: ' + msg);\n                        err.originalFetchError = fetchErr;\n                        reject(err);\n                    }\n                });\n            });\n        });\n    };\n\n    if (!window.__tiFetchLanguageWrapped && window.fetch) {\n        window.__tiFetchLanguageWrapped = true;\n        window.__tiOriginalFetchForLang = window.fetch.bind(window);\n        window.fetch = function(input, init) {\n            try {\n                if (init && init.body instanceof FormData && window.tiAppendCurrentLanguageToFormData) {\n                    if (init.body.has('ti_action') && !init.body.has('action')) init.body.append('action', init.body.get('ti_action'));\n                    window.tiAppendCurrentLanguageToFormData(init.body);\n                }\n            } catch(e) {}\n            return window.__tiOriginalFetchForLang(input, init);\n        };\n    }\n\n    window.tiLongProcessState = { controller: null, cancellable: false, cancelled: false, label: '' };\n    window.beginLongAIProcess = function(label, cancellable = true) {\n        if (window.tiLongProcessState.controller && !window.tiLongProcessState.cancelled) {\n            try { window.tiLongProcessState.controller.abort(); } catch(e) {}\n        }\n        window.tiLongProcessState = {\n            controller: new AbortController(),\n            cancellable: !!cancellable,\n            cancelled: false,\n            label: label || 'Processo AI'\n        };\n        return window.tiLongProcessState.controller;\n    };\n    window.clearLongAIProcess = function() {\n        window.tiLongProcessState = { controller: null, cancellable: false, cancelled: false, label: '' };\n    };\n    window.isLongAIProcessCancelled = function() {\n        return !!(window.tiLongProcessState && window.tiLongProcessState.cancelled);\n    };\n    window.cancelLongAIProcess = function() {\n        if (typeof window.confirmCancelBatchAIGen === 'function' && window.confirmCancelBatchAIGen()) return;\n        const state = window.tiLongProcessState || {};\n        let handledConfigCancel434 = false;\n        try {\n            if (typeof window.cancelActiveConfigAiRequest434 === 'function') handledConfigCancel434 = !!window.cancelActiveConfigAiRequest434('user_exit');\n        } catch(e) {}\n        if (!state.controller || !state.cancellable) {\n            window.closeModal('ti-ai-loader-ov');\n            try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n            if (!handledConfigCancel434) window.tiAlert('Processo AI interrotto. Per completarlo dovrai ripetere l operazione.');\n            return;\n        }\n        state.cancelled = true;\n        try { state.controller.abort(); } catch(e) {}\n        window.closeModal('ti-ai-loader-ov');\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        if (!handledConfigCancel434) window.tiAlert('Processo AI interrotto. Per completarlo dovrai ripetere l operazione.');\n    };\n\n    window.tiModalBaseZ = 2147482000;\n    window.tiModalMaxZ = 2147483647;\n    window.tiModalCommunicationZ = {\n        'ti-alert-ov': 2147483647,\n        'ti-ai-loader-ov': 2147483646\n    };\n    window.tiModalStack = 0;\n    window.setPluginModalZIndex = function(el, z) {\n        if (!el) return;\n        const zi = Math.max(1, Math.min(window.tiModalMaxZ || 2147483647, parseInt(z, 10) || window.tiModalBaseZ || 2147482000));\n        try { el.style.setProperty('z-index', String(zi), 'important'); }\n        catch(e) { el.style.zIndex = String(zi); }\n    };\n    window.getPluginModalBaseZ = function(el) {\n        if (el && el.id && window.tiModalCommunicationZ && window.tiModalCommunicationZ[el.id]) return window.tiModalCommunicationZ[el.id];\n        return window.tiModalBaseZ || 2147482000;\n    };\n    window.ensurePluginModalRoot = function() {\n        let root = document.getElementById('ti-plugin-modal-root');\n        if (!root) {\n            root = document.createElement('div');\n            root.id = 'ti-plugin-modal-root';\n            root.style.setProperty('z-index', '2147483000', 'important');\n            document.body.appendChild(root);\n        }\n        try { root.style.setProperty('z-index', '2147483000', 'important'); } catch(e) {}\n        return root;\n    };\n    window.getPluginModalClosePanel = function(ov) {\n        if (!ov || !ov.children) return null;\n        const direct = Array.from(ov.children || []).find(function(ch){ return ch && ch.classList && (ch.classList.contains('ti-conf-window') || ch.classList.contains('ti-modal')); });\n        if (direct) return direct;\n        return ov.querySelector ? ov.querySelector('.ti-conf-window, .ti-modal') : null;\n    };\n    window.closePluginFormModal = function(id) {\n        if (id === 'ti-conf-ov' && typeof window.closeConfigModal === 'function') return window.closeConfigModal();\n        if (id === 'ti-cam-ov' && typeof window.closeCamModal === 'function') return window.closeCamModal();\n        if (id === 'ti-order-item-ov' && typeof window.closeOrderItemPopup === 'function') return window.closeOrderItemPopup();\n        return window.closeModal(id);\n    };\n    window.ensureClosableModalControls = function(root) {\n        const scope = root && root.querySelectorAll ? root : document;\n        const overlays = scope.matches && scope.matches('.ti-ov') ? [scope] : Array.from(scope.querySelectorAll ? scope.querySelectorAll('.ti-ov') : []);\n        overlays.forEach(function(ov){\n            if (!ov || !ov.id || ov.id === 'ti-img-ov' || ov.id === 'ti-alert-ov' || ov.id === 'ti-ai-loader-ov') return;\n            const panel = window.getPluginModalClosePanel(ov);\n            if (!panel) return;\n            if (!panel.style.position || panel.style.position === 'static') panel.style.position = 'relative';\n            if (!panel.querySelector('.ti-modal-x-red')) {\n                const x = document.createElement('button');\n                x.type = 'button';\n                x.className = 'ti-modal-x-red';\n                x.setAttribute('aria-label', 'Chiudi');\n                x.title = 'Chiudi';\n                x.textContent = '\u00d7';\n                x.addEventListener('click', function(ev){ ev.preventDefault(); ev.stopPropagation(); window.closePluginFormModal(ov.id); });\n                panel.appendChild(x);\n            }\n            const hasCloseBtn = !!panel.querySelector('.ti-modal-bottom-close-row, [data-ti-bottom-close=\"1\"]') || Array.from(panel.querySelectorAll ? panel.querySelectorAll('button, a') : []).some(function(b){ return \/\\bchiudi\\b\/i.test(String(b.textContent || '')); });\n            if (!hasCloseBtn) {\n                const row = document.createElement('div');\n                row.className = 'ti-modal-bottom-close-row';\n                row.setAttribute('data-ti-bottom-close', '1');\n                const btn = document.createElement('button');\n                btn.type = 'button';\n                btn.className = 'ti-btn btn-close';\n                btn.textContent = 'Chiudi';\n                btn.addEventListener('click', function(ev){ ev.preventDefault(); ev.stopPropagation(); window.closePluginFormModal(ov.id); });\n                row.appendChild(btn);\n                panel.appendChild(row);\n            }\n        });\n    };\n    window.portalizePluginModals = function() {\n        const root = window.ensurePluginModalRoot();\n        document.querySelectorAll('.ti-ov').forEach(function(el){\n            if (el.parentNode !== root) root.appendChild(el);\n            el.style.position = 'fixed';\n            el.style.inset = '0';\n            window.setPluginModalZIndex(el, window.getPluginModalBaseZ(el));\n        });\n        if (window.ensureClosableModalControls) window.ensureClosableModalControls(root);\n        if (window.updateCompanyReferenceDateTime) window.updateCompanyReferenceDateTime();\n    };\n    window.bringPluginModalToFront = function(el) {\n        if (!el) return;\n        if (el.id && window.tiModalCommunicationZ && window.tiModalCommunicationZ[el.id]) {\n            window.setPluginModalZIndex(el, window.tiModalCommunicationZ[el.id]);\n            return;\n        }\n        window.tiModalStack = (window.tiModalStack || 0) + 1;\n        if (window.tiModalStack > 1200) window.tiModalStack = 1;\n        window.setPluginModalZIndex(el, (window.tiModalBaseZ || 2147482000) + window.tiModalStack);\n    };\n    window.keepUserCommunicationPopupsInFront = function() {\n        ['ti-ai-loader-ov', 'ti-admin-comm-ov', 'ti-alert-ov'].forEach(function(id){\n            const el = gE(id);\n            if (!el) return;\n            const disp = String(el.style.display || '').replace(\/\\s+\/g, '').toLowerCase();\n            if (disp === 'flex' || disp === 'block') {\n                window.bringPluginModalToFront(el);\n                if (id === 'ti-alert-ov') {\n                    const btn = gE('ti-alert-no') || gE('ti-alert-yes');\n                    if (btn && typeof btn.focus === 'function') {\n                        setTimeout(function(){ try { btn.focus({preventScroll:true}); } catch(e) { try { btn.focus(); } catch(e2) {} } }, 0);\n                    }\n                }\n            }\n        });\n    };\n    window.preparePluginModal = function(id) {\n        const el = typeof id === 'string' ? gE(id) : id;\n        if (!el) return null;\n        window.portalizePluginModals();\n        const root = window.ensurePluginModalRoot();\n        if (el.parentNode !== root) root.appendChild(el);\n        el.style.position = 'fixed';\n        el.style.inset = '0';\n        window.setPluginModalZIndex(el, window.getPluginModalBaseZ(el));\n        return el;\n    };\n    window.showPluginModal = function(id) {\n        const el = window.preparePluginModal(id);\n        if (!el) return null;\n        if (window.ensureClosableModalControls) window.ensureClosableModalControls(el);\n        window.bringPluginModalToFront(el);\n        el.style.display = 'flex';\n        try { if (window.applyLayoutLanguage) window.applyLayoutLanguage(true); } catch(e) {}\n        window.keepUserCommunicationPopupsInFront();\n        window.updatePluginScrollLock();\n        return el;\n    };\n    window.updatePluginScrollLock = function() {\n        const hasOpen = !!document.querySelector('.ti-ov[style*=\"display: flex\"], .ti-ov[style*=\"display:flex\"], #ti-ai-loader-ov[style*=\"display: flex\"], #ti-ai-loader-ov[style*=\"display:flex\"]');\n        const isMobile = window.tiIsMobileLike ? window.tiIsMobileLike() : !!(window.matchMedia && window.matchMedia('(max-width: 900px)').matches);\n        \/\/ Su smartphone e dispositivi touch non bloccare mai lo scroll del body:\n        \/\/ alcuni browser mobili restano non scorrevoli dopo login\/modal.\n        if (isMobile) {\n            document.documentElement.classList.remove('ti-no-scroll');\n            document.body.classList.remove('ti-no-scroll');\n            document.documentElement.style.overflowY = 'auto';\n            document.body.style.overflowY = 'auto';\n            document.documentElement.style.overflowX = 'hidden';\n            document.body.style.overflowX = 'hidden';\n            document.documentElement.style.height = 'auto';\n            document.body.style.height = 'auto';\n            document.documentElement.style.position = 'static';\n            document.body.style.position = 'static';\n            document.documentElement.style.touchAction = 'pan-y';\n            document.body.style.touchAction = 'pan-y';\n            return;\n        }\n        document.documentElement.classList.toggle('ti-no-scroll', hasOpen);\n        document.body.classList.toggle('ti-no-scroll', hasOpen);\n    };\n    window.keepPluginModalInFront = function(id) {\n        const el = typeof id === 'string' ? gE(id) : id;\n        if (!el) return null;\n        if (el.style.display === 'flex' || el.style.display === 'block') {\n            window.preparePluginModal(el);\n            window.bringPluginModalToFront(el);\n            window.keepUserCommunicationPopupsInFront();\n        }\n        return el;\n    };\n    window.openModal = function(id) {\n        try {\n            if (window.updateMobileViewportHeight) window.updateMobileViewportHeight();\n            window.showPluginModal(id);\n            if (id === 'ti-login-ov') {\n                setTimeout(function(){\n                    if (window.tiMobileUnlockScroll) window.tiMobileUnlockScroll();\n                    if (!(window.tiIsMobileLike && window.tiIsMobileLike())) {\n                        const u = gE('l-user');\n                        if (u && typeof u.focus === 'function') { try { u.focus({preventScroll:true}); } catch(e) {} }\n                    }\n                    if (window.bindMobileLoginButton) window.bindMobileLoginButton();\n                }, 120);\n            }\n        } catch(e) {}\n    };\n    window.closeModal = function(id) {\n        try {\n            const el = typeof id === 'string' ? gE(id) : id;\n            if (el) {\n                el.style.display = 'none';\n                window.updatePluginScrollLock();\n                window.keepUserCommunicationPopupsInFront();\n            }\n        } catch(e) {}\n    };\n    \/\/ 30.9.664: fallback centrale per pulsanti Esci\/Chiudi\/X dentro popup e form modali.\n    if (!window.__tiCloseButtonsFallback664) {\n        window.__tiCloseButtonsFallback664 = true;\n        document.addEventListener('click', function(ev){\n            try {\n                var btn = ev.target && ev.target.closest ? ev.target.closest('button, a, [role=\"button\"]') : null;\n                if (!btn) return;\n                if (btn.id === 'ti-logout-btn') return;\n                var label = String(btn.textContent || btn.getAttribute('aria-label') || btn.title || '').trim().toLowerCase();\n                var isClose = label === '\u00d7' || label === 'x' || label === 'chiudi' || label === 'esci' || label === 'annulla';\n                if (!isClose && !(btn.classList && (btn.classList.contains('btn-close') || btn.classList.contains('ti-modal-x-red')))) return;\n                var ov = btn.closest('.ti-ov, #ti-direct-upload-ov-621, #ti-ai-loader-ov, #ti-alert-ov, #ti-admin-comm-ov');\n                if (!ov || !ov.id) return;\n                if (ov.id === 'ti-direct-upload-ov-621') return; \/\/ gestito da exitUpload per rispettare annullamento upload.\n                setTimeout(function(){\n                    try {\n                        var visible = ov && (ov.style.display === 'flex' || ov.style.display === 'block' || getComputedStyle(ov).display !== 'none');\n                        if (visible && window.closePluginFormModal) window.closePluginFormModal(ov.id);\n                    } catch(e2) {}\n                }, 60);\n            } catch(e) {}\n        }, true);\n    }\n    try { window.portalizePluginModals(); if (window.ensureClosableModalControls) window.ensureClosableModalControls(); } catch(e) {}\n    if (typeof window.initPluginDateInputs !== 'function') {\n        window.initPluginDateInputs = function(root) {\n            const scope = root || document;\n            scope.querySelectorAll('input[type=\"date\"], input[type=\"time\"]').forEach(function(inp){\n                const v = String(inp.value || '').trim();\n                if (inp.type === 'date' && !\/^\\d{4}-\\d{2}-\\d{2}$\/.test(v)) inp.value = '';\n                if (inp.type === 'time' && !\/^\\d{2}:\\d{2}$\/.test(v)) inp.value = '';\n                if (inp.type === 'date') { inp.placeholder = 'gg\/mm\/aaaa'; inp.title = inp.title || 'Seleziona una data'; }\n                if (inp.type === 'time') { inp.placeholder = 'hh:mm'; inp.title = inp.title || 'Seleziona un orario'; }\n            });\n        };\n    }\n\n    window.closeConfigModal = function() {\n        if (window.hasPendingImportAssociations && window.hasPendingImportAssociations()) {\n            const importOv = gE('ti-conf-import-preview-ov');\n            const previewOpen = importOv && String(importOv.style.display || '').indexOf('flex') !== -1;\n            if (previewOpen) {\n                window.closeConfigImportPreview(false);\n                return;\n            }\n        }\n        const hadSaved = !!window.configWasSaved;\n        window.closeModal('ti-conf-ov');\n        if (!hadSaved) {\n            window.currentDbData = null;\n            window.configOriginalDbData = null;\n            if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear();\n            if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n            return;\n        }\n        window.configWasSaved = false;\n        const fd = window.buildAjaxFormData('ti_ai_reset_flow');\n        fetch(window.tiUrl, {method:'POST', body:window.ensureAjaxActionField(fd)}).then(() => location.reload());\n    };\n\n    window.tiLoaderState = { timer: null, percent: 0, target: 90, startedAt: 0, active: false };\n    window.showProgressPopup = function(title, subtitle, opts = {}) {\n        const ov = gE('ti-ai-loader-ov'); if(!ov) return;\n        const titleEl = gE('ti-loader-title'); const subEl = gE('ti-loader-sub'); const itemEl = gE('ti-loader-current-item');\n        const wrap = gE('ti-loader-progress-wrap'); const pct = gE('ti-loader-percent'); const bar = gE('ti-loader-bar');\n        const actions = gE('ti-loader-actions');\n        const cancellable = opts.cancellable !== false;\n        window.beginLongAIProcess(title || 'Elaborazione AI in corso...', cancellable);\n        if (titleEl) titleEl.innerText = title || 'Elaborazione AI in corso...';\n        if (subEl) subEl.innerText = subtitle || \"Attendi, l'operazione potrebbe richiedere alcuni secondi.\";\n        if (itemEl) {\n            const currentItem = String(opts.currentItem || opts.itemName || '').trim();\n            itemEl.style.display = currentItem ? 'block' : 'none';\n            itemEl.innerText = currentItem ? ('Elemento in lavorazione: ' + currentItem) : '';\n        }\n        if (wrap) wrap.style.display = 'block';\n        if (actions) actions.style.display = cancellable ? 'block' : 'none';\n        window.tiLoaderState.active = true;\n        window.tiLoaderState.percent = Math.max(0, Math.min(100, parseInt(opts.startPercent || 0, 10) || 0));\n        window.tiLoaderState.target = Math.max(window.tiLoaderState.percent, Math.min(99, parseInt(opts.targetPercent || 90, 10) || 90));\n        window.tiLoaderState.startedAt = Date.now();\n        if (pct) pct.innerText = window.tiLoaderState.percent + '%';\n        if (bar) bar.style.width = window.tiLoaderState.percent + '%';\n        window.showPluginModal('ti-ai-loader-ov');\n        clearInterval(window.tiLoaderState.timer);\n        const stepMs = Math.max(150, parseInt(opts.stepMs || 500, 10) || 500);\n        window.tiLoaderState.timer = setInterval(() => {\n            if (!window.tiLoaderState.active) return;\n            let inc = 1;\n            if (window.tiLoaderState.percent < 20) inc = 4;\n            else if (window.tiLoaderState.percent < 45) inc = 3;\n            else if (window.tiLoaderState.percent < 70) inc = 2;\n            else inc = 1;\n            window.tiLoaderState.percent = Math.min(window.tiLoaderState.target, window.tiLoaderState.percent + inc);\n            if (pct) pct.innerText = window.tiLoaderState.percent + '%';\n            if (bar) bar.style.width = window.tiLoaderState.percent + '%';\n            if (window.tiLoaderState.percent >= window.tiLoaderState.target) clearInterval(window.tiLoaderState.timer);\n        }, stepMs);\n    };\n    window.updateProgressPopup = function(percent, title, subtitle, currentItem) {\n        const ov = gE('ti-ai-loader-ov'); if(!ov) return;\n        const titleEl = gE('ti-loader-title'); const subEl = gE('ti-loader-sub'); const itemEl = gE('ti-loader-current-item');\n        const wrap = gE('ti-loader-progress-wrap'); const pct = gE('ti-loader-percent'); const bar = gE('ti-loader-bar');\n        window.tiLoaderState.active = true;\n        clearInterval(window.tiLoaderState.timer);\n        if (titleEl && title) titleEl.innerText = title;\n        if (subEl && subtitle) subEl.innerText = subtitle;\n        if (itemEl) {\n            const itemName = String(currentItem || '').trim();\n            itemEl.style.display = itemName ? 'block' : 'none';\n            itemEl.innerText = itemName ? ('Elemento in lavorazione: ' + itemName) : '';\n        }\n        if (wrap) wrap.style.display = 'block';\n        window.tiLoaderState.percent = Math.max(0, Math.min(100, parseInt(percent, 10) || 0));\n        if (pct) pct.innerText = window.tiLoaderState.percent + '%';\n        if (bar) bar.style.width = window.tiLoaderState.percent + '%';\n        window.showPluginModal('ti-ai-loader-ov');\n    };\n    window.hideProgressPopup = function(success = true, finalText = '', opts = {}) {\n        const ov = gE('ti-ai-loader-ov'); if(!ov) return;\n        const titleEl = gE('ti-loader-title'); const subEl = gE('ti-loader-sub'); const itemEl = gE('ti-loader-current-item'); const pct = gE('ti-loader-percent'); const bar = gE('ti-loader-bar');\n        const actions = gE('ti-loader-actions');\n        const wasCancelled = window.isLongAIProcessCancelled();\n        window.tiLoaderState.active = false;\n        clearInterval(window.tiLoaderState.timer);\n        if (actions) actions.style.display = 'none';\n        if (itemEl) { itemEl.style.display = 'none'; itemEl.innerText = ''; }\n        const remindSave = !!opts.remindSave && window.modifiedFields && window.modifiedFields.size > 0;\n        const successMsg = (opts.successMessage || finalText || 'Operazione AI completata') + (remindSave ? '\\n\\nRicorda di salvare le modifiche se non lo hai ancora fatto.' : '');\n        const errorMsg = opts.errorMessage || finalText || 'Operazione interrotta';\n        if (success) {\n            if (titleEl) titleEl.innerText = finalText || 'Operazione completata';\n            if (subEl) subEl.innerText = remindSave ? 'Attivit\u00e0 AI conclusa. Sono presenti modifiche non ancora salvate.' : 'Completamento confermato dal sistema.';\n            if (pct) pct.innerText = '100%';\n            if (bar) bar.style.width = '100%';\n            setTimeout(() => {\n                window.closeModal('ti-ai-loader-ov');\n                window.clearLongAIProcess();\n                if (opts.notifyUser !== false) { window.tiAlert(successMsg); }\n            }, 450);\n        } else {\n            if (titleEl) titleEl.innerText = finalText || 'Operazione interrotta';\n            if (subEl) subEl.innerText = 'Non e stato possibile completare l attivita richiesta.';\n            setTimeout(() => {\n                window.closeModal('ti-ai-loader-ov');\n                if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess();\n                if (opts.notifyUser !== false) { window.tiAlert(errorMsg); }\n            }, 250);\n        }\n    };\n\n    \/\/ 30.9.664: watchdog robusto per avanzamento stimato nelle elaborazioni lunghe.\n    \/\/ Evita che la barra resti a percentuali basse quando il browser rallenta i timer\n    \/\/ o quando il server ha gi\u00e0 concluso ma il client non ha ancora aggiornato la UI.\n    (function installTiRobustLongProgress327(){\n        if (window.__tiRobustLongProgress327Installed) return;\n        window.__tiRobustLongProgress327Installed = true;\n        function clampPct(v) {\n            v = parseInt(v, 10);\n            if (!isFinite(v) || isNaN(v)) v = 0;\n            return Math.max(0, Math.min(100, v));\n        }\n        function completionText(txt) {\n            txt = String(txt || '').toLowerCase();\n            \/\/ 30.9.664: non considerare completato un processo di configurazione se la risposta sta chiedendo specifiche all'utente\n            \/\/ o se non ha aggiornato record. La barra a 100% deve arrivare solo per un esito realmente conclusivo.\n            if (\/chiarimento richiesto|indicami|specifiche|specifica|quali record|criterio piu preciso|criterio pi\u00f9 preciso|record aggiornati:\\s*0\/.test(txt)) return false;\n            return \/completamento confermato|operazione completata|completata|completato|conclusa|concluso|tabelle lette|anteprima import pronta\/.test(txt);\n        }\n        function setLoaderPercent327(percent, title, subtitle, currentItem) {\n            const state = window.tiLoaderState || (window.tiLoaderState = {});\n            const pct = clampPct(percent);\n            const pctEl = gE('ti-loader-percent');\n            const barEl = gE('ti-loader-bar');\n            const titleEl = gE('ti-loader-title');\n            const subEl = gE('ti-loader-sub');\n            const itemEl = gE('ti-loader-current-item');\n            state.percent = pct;\n            if (pctEl) pctEl.innerText = pct + '%';\n            if (barEl) barEl.style.width = pct + '%';\n            if (titleEl && title) titleEl.innerText = String(title);\n            if (subEl && subtitle) subEl.innerText = String(subtitle);\n            if (itemEl && currentItem !== undefined) {\n                const itemName = String(currentItem || '').trim();\n                itemEl.style.display = itemName ? 'block' : 'none';\n                itemEl.innerText = itemName ? ('Elemento in lavorazione: ' + itemName) : '';\n            }\n        }\n        function stopWatch327() {\n            const state = window.tiLoaderState || {};\n            if (state.watchdog327) clearInterval(state.watchdog327);\n            state.watchdog327 = null;\n        }\n        function startWatch327(opts) {\n            opts = opts || {};\n            const state = window.tiLoaderState || (window.tiLoaderState = {});\n            stopWatch327();\n            state.watchStart327 = Date.now();\n            state.watchStartPercent327 = clampPct(state.percent || opts.startPercent || 0);\n            state.watchTarget327 = Math.max(state.watchStartPercent327, Math.min(98, clampPct(opts.targetPercent || state.target || 94)));\n            \/\/ Tempo atteso realistico: se i timer sono sospesi, al tick successivo la percentuale salta in avanti in base al tempo trascorso.\n            state.watchExpectedMs327 = Math.max(30000, Math.min(120000, parseInt(opts.expectedMs || 55000, 10) || 55000));\n            state.watchdog327 = setInterval(function(){\n                try {\n                    if (!state.active) { stopWatch327(); return; }\n                    const elapsed = Math.max(0, Date.now() - (state.watchStart327 || Date.now()));\n                    const start = clampPct(state.watchStartPercent327 || 0);\n                    const target = Math.max(start, Math.min(98, clampPct(state.watchTarget327 || state.target || 94)));\n                    let estimated = start + ((target - start) * Math.min(1, elapsed \/ (state.watchExpectedMs327 || 55000)));\n                    if (elapsed > (state.watchExpectedMs327 || 55000)) {\n                        const extra = Math.min(1, (elapsed - (state.watchExpectedMs327 || 55000)) \/ 90000);\n                        estimated = target + ((99 - target) * extra);\n                    }\n                    if (elapsed > 180000) estimated = 99;\n                    let current = clampPct(state.percent || 0);\n                    if (Math.floor(estimated) > current) {\n                        let sub = '';\n                        if (elapsed > 90000) sub = 'Elaborazione ancora in corso lato server. Attendo la risposta conclusiva e verifico il completamento.';\n                        setLoaderPercent327(Math.floor(estimated), '', sub, undefined);\n                    }\n                    const titleTxt = gE('ti-loader-title') ? gE('ti-loader-title').innerText : '';\n                    const subTxt = gE('ti-loader-sub') ? gE('ti-loader-sub').innerText : '';\n                    if (completionText(titleTxt + ' ' + subTxt)) {\n                        setLoaderPercent327(100, '', 'Completamento confermato dal sistema.', undefined);\n                        stopWatch327();\n                    }\n                } catch(e) {}\n            }, 1000);\n        }\n        if (typeof window.showProgressPopup === 'function' && !window.showProgressPopup.__tiRobustProgress327) {\n            const prevShow = window.showProgressPopup;\n            window.showProgressPopup = function(title, subtitle, opts) {\n                const ret = prevShow.apply(this, arguments);\n                startWatch327(opts || {});\n                return ret;\n            };\n            window.showProgressPopup.__tiRobustProgress327 = true;\n        }\n        if (typeof window.updateProgressPopup === 'function' && !window.updateProgressPopup.__tiRobustProgress327) {\n            const prevUpdate = window.updateProgressPopup;\n            window.updateProgressPopup = function(percent, title, subtitle, currentItem) {\n                const state = window.tiLoaderState || {};\n                let pct = clampPct(percent);\n                const cur = clampPct(state.percent || 0);\n                \/\/ La percentuale non deve regredire per aggiornamenti parziali\/fallback.\n                if (pct < 100 && cur > pct) pct = cur;\n                const ret = prevUpdate.call(this, pct, title, subtitle, currentItem);\n                if (pct >= 100 || completionText(String(title || '') + ' ' + String(subtitle || ''))) {\n                    setLoaderPercent327(100, title || '', subtitle || 'Completamento confermato dal sistema.', currentItem);\n                    stopWatch327();\n                }\n                return ret;\n            };\n            window.updateProgressPopup.__tiRobustProgress327 = true;\n        }\n        if (typeof window.hideProgressPopup === 'function' && !window.hideProgressPopup.__tiRobustProgress327) {\n            const prevHide = window.hideProgressPopup;\n            window.hideProgressPopup = function(success, finalText, opts) {\n                if (success !== false) {\n                    setLoaderPercent327(100, finalText || 'Operazione completata', 'Completamento confermato dal sistema.', undefined);\n                    stopWatch327();\n                }\n                return prevHide.apply(this, arguments);\n            };\n            window.hideProgressPopup.__tiRobustProgress327 = true;\n        }\n        window.tiForceLoaderComplete327 = function(title, subtitle) {\n            setLoaderPercent327(100, title || 'Operazione completata', subtitle || 'Completamento confermato dal sistema.', undefined);\n            stopWatch327();\n        };\n    })();\n\n\n    \/\/ 30.9.664: guardia anti-blocco per popup avanzamento rimasto a 100%.\n    \/\/ Caso reale: semplice accesso a Configurazione, \"Tabelle lette\" e \"Completamento confermato dal sistema\"\n    \/\/ rimanevano visibili anche a processo finito. Questa guardia chiude il loader concluso\n    \/\/ senza uscire dalla Configurazione e senza interferire con popup\/alert utente.\n    (function installTiProgressStallGuard338(){\n        if (window.__tiProgressStallGuard338Installed) return;\n        window.__tiProgressStallGuard338Installed = true;\n        function txt(id) { const el = gE(id); return el ? String(el.innerText || el.textContent || '') : ''; }\n        function pctVal() { const raw = txt('ti-loader-percent').replace(\/[^0-9]\/g, ''); const n = parseInt(raw || '0', 10); return isNaN(n) ? 0 : n; }\n        function loaderEl() { return gE('ti-ai-loader-ov'); }\n        function isVisible(el) { return !!el && (el.style.display === 'flex' || el.style.display === 'block'); }\n        function isCompletionText(v) {\n            v = String(v || '').toLowerCase();\n            if (\/chiarimento richiesto|indicami|specifiche|specifica|quali record|criterio piu preciso|criterio pi\u00f9 preciso|record aggiornati:\\s*0|errore|fallito|interrotto\/.test(v)) return false;\n            return \/completamento confermato|operazione completata|completata|completato|conclusa|concluso|tabelle lette|anteprima import pronta\/.test(v);\n        }\n        function shouldAutoClose() {\n            const ov = loaderEl();\n            if (!isVisible(ov)) return false;\n            const combined = txt('ti-loader-title') + ' ' + txt('ti-loader-sub');\n            return pctVal() >= 100 && isCompletionText(combined);\n        }\n        function forceClose(reason) {\n            try {\n                const state = window.tiLoaderState || {};\n                state.active = false;\n                if (state.timer) clearInterval(state.timer);\n                if (state.watchdog327) clearInterval(state.watchdog327);\n                if (state.stallGuard338) clearInterval(state.stallGuard338);\n                state.timer = null;\n                state.watchdog327 = null;\n                const ov = loaderEl();\n                if (ov) ov.style.display = 'none';\n                if (typeof window.updatePluginScrollLock === 'function') window.updatePluginScrollLock();\n                if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess();\n                if (typeof window.keepUserCommunicationPopupsInFront === 'function') window.keepUserCommunicationPopupsInFront();\n                if (window.console && console.info) console.info('[TI Shop Service] loader auto-closed 30.9.664:', reason || 'completed');\n            } catch(e) {}\n        }\n        function scheduleClose(reason, delay) {\n            try {\n                const state = window.tiLoaderState || (window.tiLoaderState = {});\n                if (state.closeTimer338) clearTimeout(state.closeTimer338);\n                state.closeTimer338 = setTimeout(function(){ if (shouldAutoClose()) forceClose(reason); }, Math.max(250, delay || 900));\n            } catch(e) {}\n        }\n        window.tiForceCloseCompletedLoader338 = function(reason) {\n            if (shouldAutoClose()) forceClose(reason || 'manual-check');\n        };\n        if (typeof window.updateProgressPopup === 'function' && !window.updateProgressPopup.__tiProgressStallGuard338) {\n            const prevUpdate338 = window.updateProgressPopup;\n            window.updateProgressPopup = function(percent, title, subtitle, currentItem) {\n                const ret = prevUpdate338.apply(this, arguments);\n                const combined = String(title || '') + ' ' + String(subtitle || '') + ' ' + txt('ti-loader-title') + ' ' + txt('ti-loader-sub');\n                if ((parseInt(percent, 10) || 0) >= 100 || isCompletionText(combined)) scheduleClose('updateProgressPopup', 900);\n                return ret;\n            };\n            window.updateProgressPopup.__tiProgressStallGuard338 = true;\n        }\n        if (typeof window.hideProgressPopup === 'function' && !window.hideProgressPopup.__tiProgressStallGuard338) {\n            const prevHide338 = window.hideProgressPopup;\n            window.hideProgressPopup = function(success, finalText, opts) {\n                const ret = prevHide338.apply(this, arguments);\n                if (success !== false) scheduleClose('hideProgressPopup', 1400);\n                return ret;\n            };\n            window.hideProgressPopup.__tiProgressStallGuard338 = true;\n        }\n        if (typeof window.hideTableReadWait === 'function' && !window.hideTableReadWait.__tiProgressStallGuard338) {\n            const prevHideTable338 = window.hideTableReadWait;\n            window.hideTableReadWait = function(success, finalText) {\n                const ret = prevHideTable338.apply(this, arguments);\n                if (success !== false) scheduleClose('hideTableReadWait', 750);\n                return ret;\n            };\n            window.hideTableReadWait.__tiProgressStallGuard338 = true;\n        }\n        const state = window.tiLoaderState || (window.tiLoaderState = {});\n        if (state.stallGuard338) clearInterval(state.stallGuard338);\n        state.stallGuard338 = setInterval(function(){\n            try {\n                if (!shouldAutoClose()) return;\n                if (!state.completedVisibleSince338) state.completedVisibleSince338 = Date.now();\n                if ((Date.now() - state.completedVisibleSince338) > 2500) forceClose('periodic-stall-guard');\n            } catch(e) {}\n        }, 1000);\n        document.addEventListener('visibilitychange', function(){\n            if (!document.hidden) scheduleClose('visibility-return', 650);\n        });\n        window.addEventListener('focus', function(){ scheduleClose('window-focus', 650); });\n    })();\n\n\n    \/\/ 30.9.664: rimozione forzata di loader conclusi rimasti visibili in Configurazione.\n    \/\/ Alcuni browser\/sovrapposizioni possono impedire a closeModal() di togliere il box dal DOM;\n    \/\/ se il testo indica chiaramente \"Tabelle lette \/ Completamento confermato \/ 100%\", il box viene nascosto fisicamente.\n    (function installTiCompletedLoaderDomReaper339(){\n        if (window.__tiCompletedLoaderDomReaper339Installed) return;\n        window.__tiCompletedLoaderDomReaper339Installed = true;\n        function byId(id){ return document.getElementById(id); }\n        function textOf(el){ return el ? String(el.innerText || el.textContent || '') : ''; }\n        function norm(s){ return String(s || '').toLowerCase().replace(\/\\s+\/g, ' ').trim(); }\n        function isBadCompletionText(s){\n            s = norm(s);\n            if (!s) return false;\n            if (\/errore|fallito|interrotto|annulla|chiarimento|specifiche|indicami|record aggiornati\\s*0\/.test(s)) return false;\n            return \/tabelle lette\/.test(s) && \/completamento confermato\/.test(s) && \/avanzamento stimato\/.test(s) && \/100\\s*%\/.test(s);\n        }\n        function isVisible(el){\n            if (!el) return false;\n            try {\n                var cs = window.getComputedStyle(el);\n                if (!cs || cs.display === 'none' || cs.visibility === 'hidden' || parseFloat(cs.opacity || '1') === 0) return false;\n                var r = el.getBoundingClientRect();\n                return r.width > 0 && r.height > 0;\n            } catch(e) {\n                return !!(el && el.offsetParent !== null);\n            }\n        }\n        function hardHide(el, reason){\n            if (!el) return false;\n            try {\n                if (window.console && console.info) console.info('[TI Shop Service] completed loader removed 30.9.664:', reason || '', el.id || el.className || el.tagName);\n            } catch(e) {}\n            try { el.classList.add('ti-loader-force-hidden-339'); } catch(e) {}\n            try {\n                el.style.setProperty('display', 'none', 'important');\n                el.style.setProperty('visibility', 'hidden', 'important');\n                el.style.setProperty('pointer-events', 'none', 'important');\n                el.style.setProperty('opacity', '0', 'important');\n                el.setAttribute('aria-hidden', 'true');\n            } catch(e) {}\n            try {\n                var st = window.tiLoaderState || (window.tiLoaderState = {});\n                st.active = false;\n                if (st.timer) clearInterval(st.timer);\n                if (st.watchdog327) clearInterval(st.watchdog327);\n                if (st.stallGuard338) clearInterval(st.stallGuard338);\n                if (st.closeTimer338) clearTimeout(st.closeTimer338);\n                st.timer = null; st.watchdog327 = null; st.closeTimer338 = null;\n            } catch(e) {}\n            try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n            try { if (typeof window.updatePluginScrollLock === 'function') window.updatePluginScrollLock(); } catch(e) {}\n            return true;\n        }\n        function findSmallestCompletedNode(){\n            var nodes = [];\n            try {\n                nodes = Array.from(document.querySelectorAll('#ti-ai-loader-ov, .ti-loader-box, #ti-plugin-modal-root > *, .ti-ov, [id*=\"loader\"], [class*=\"loader\"], [id*=\"progress\"], [class*=\"progress\"], [id*=\"wait\"], [class*=\"wait\"]'));\n            } catch(e) { nodes = []; }\n            var best = null, bestArea = Infinity;\n            nodes.forEach(function(el){\n                if (!el || el.id === 'ti-conf-ov') return;\n                if (!isVisible(el)) return;\n                var tx = textOf(el);\n                if (!isBadCompletionText(tx)) return;\n                var closeTarget = el.closest && el.closest('#ti-ai-loader-ov') ? el.closest('#ti-ai-loader-ov') : el;\n                if (closeTarget && closeTarget.id === 'ti-conf-ov') closeTarget = el;\n                var r = closeTarget.getBoundingClientRect ? closeTarget.getBoundingClientRect() : {width:9999,height:9999};\n                var area = Math.max(1, (r.width || 1) * (r.height || 1));\n                if (area < bestArea) { bestArea = area; best = closeTarget; }\n            });\n            return best;\n        }\n        function reap(reason){\n            var changed = false;\n            try {\n                var ov = byId('ti-ai-loader-ov');\n                if (ov && isBadCompletionText(textOf(ov))) changed = hardHide(ov, reason || 'loader-overlay') || changed;\n            } catch(e) {}\n            try {\n                var box = findSmallestCompletedNode();\n                if (box) changed = hardHide(box, reason || 'completed-node') || changed;\n            } catch(e) {}\n            return changed;\n        }\n        window.tiReapCompletedLoader339 = reap;\n        function schedule(reason, delay){ setTimeout(function(){ reap(reason); }, delay || 120); }\n        if (typeof window.updateProgressPopup === 'function' && !window.updateProgressPopup.__tiCompletedLoaderDomReaper339) {\n            var prevUpdate339 = window.updateProgressPopup;\n            window.updateProgressPopup = function(){\n                var ret = prevUpdate339.apply(this, arguments);\n                schedule('updateProgressPopup', 180);\n                schedule('updateProgressPopup-late', 900);\n                return ret;\n            };\n            window.updateProgressPopup.__tiCompletedLoaderDomReaper339 = true;\n        }\n        if (typeof window.hideProgressPopup === 'function' && !window.hideProgressPopup.__tiCompletedLoaderDomReaper339) {\n            var prevHide339 = window.hideProgressPopup;\n            window.hideProgressPopup = function(){\n                var ret = prevHide339.apply(this, arguments);\n                schedule('hideProgressPopup', 260);\n                schedule('hideProgressPopup-late', 1100);\n                return ret;\n            };\n            window.hideProgressPopup.__tiCompletedLoaderDomReaper339 = true;\n        }\n        if (typeof window.hideTableReadWait === 'function' && !window.hideTableReadWait.__tiCompletedLoaderDomReaper339) {\n            var prevHideTable339 = window.hideTableReadWait;\n            window.hideTableReadWait = function(){\n                var ret = prevHideTable339.apply(this, arguments);\n                schedule('hideTableReadWait', 180);\n                schedule('hideTableReadWait-late', 900);\n                return ret;\n            };\n            window.hideTableReadWait.__tiCompletedLoaderDomReaper339 = true;\n        }\n        \/\/ Controllo continuo leggero: serve quando il loader e' gia' rimasto nel DOM prima dell'installazione del fix.\n        var ticks = 0;\n        var timer = setInterval(function(){\n            ticks++;\n            reap('periodic-' + ticks);\n            if (ticks > 1800) clearInterval(timer); \/\/ 30 minuti, poi basta\n        }, 1000);\n        document.addEventListener('visibilitychange', function(){ if (!document.hidden) schedule('visibilitychange', 100); });\n        window.addEventListener('focus', function(){ schedule('focus', 100); });\n        document.addEventListener('click', function(){ schedule('click', 50); }, true);\n        schedule('install', 250);\n        schedule('install-late', 1500);\n    })();\n\n    \/\/ 30.9.664 - Fix chirurgico produzione: chiusura difensiva immediata dei soli loader\n    \/\/ di lettura tabelle gia' conclusi. Non chiude la Configurazione e non tocca errori\/chiarimenti.\n    (function installTiTableReadNoBlock343(){\n        if (window.__tiTableReadNoBlock343Installed) return;\n        window.__tiTableReadNoBlock343Installed = true;\n        function txt(el){ return el ? String(el.innerText || el.textContent || '') : ''; }\n        function norm(s){ return String(s || '').toLowerCase().replace(\/\\s+\/g, ' ').trim(); }\n        function isCompletedTableReadText(s){\n            s = norm(s);\n            if (!s) return false;\n            if (\/errore|fallito|interrotto|annulla|chiarimento|specifiche|record aggiornati\\s*0\/.test(s)) return false;\n            return (\/tabelle lette|lettura tabelle\/.test(s)) && (\/completamento confermato|100\\s*%|avanzamento stimato\/.test(s));\n        }\n        function hardHide(el){\n            if (!el) return false;\n            try {\n                el.style.setProperty('display', 'none', 'important');\n                el.style.setProperty('visibility', 'hidden', 'important');\n                el.style.setProperty('opacity', '0', 'important');\n                el.style.setProperty('pointer-events', 'none', 'important');\n                el.setAttribute('aria-hidden', 'true');\n            } catch(e) {}\n            return true;\n        }\n        window.tiDismissCompletedTableReadLoader343 = function(reason){\n            var changed = false;\n            try {\n                var ov = document.getElementById('ti-ai-loader-ov');\n                if (ov && isCompletedTableReadText(txt(ov))) changed = hardHide(ov) || changed;\n            } catch(e) {}\n            try {\n                Array.prototype.forEach.call(document.querySelectorAll('.ti-loader-box, #ti-ai-loader-ov'), function(el){\n                    if (el && isCompletedTableReadText(txt(el))) {\n                        var target = (el.closest && el.closest('#ti-ai-loader-ov')) || el;\n                        changed = hardHide(target) || changed;\n                    }\n                });\n            } catch(e) {}\n            try {\n                var st = window.tiLoaderState || (window.tiLoaderState = {});\n                st.active = false;\n                if (st.timer) clearInterval(st.timer);\n                if (st.watchdog327) clearInterval(st.watchdog327);\n                if (st.stallGuard338) clearInterval(st.stallGuard338);\n                if (st.closeTimer338) clearTimeout(st.closeTimer338);\n                st.timer = null; st.watchdog327 = null; st.closeTimer338 = null;\n            } catch(e) {}\n            try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n            try { if (typeof window.updatePluginScrollLock === 'function') window.updatePluginScrollLock(); } catch(e) {}\n            try { if (changed && window.console && console.info) console.info('[TI Shop Service] table-read loader dismissed 30.9.664:', reason || ''); } catch(e) {}\n            return changed;\n        };\n        if (typeof window.showProgressPopup === 'function' && !window.showProgressPopup.__tiNoTableReadLoader343) {\n            var prevShow343 = window.showProgressPopup;\n            window.showProgressPopup = function(title, subtitle, opts){\n                var combined = norm(String(title || '') + ' ' + String(subtitle || ''));\n                if (\/lettura tabelle|sto leggendo.*tabelle\/.test(combined)) {\n                    window.__tiTableReadWaitSuppressed343 = true;\n                    window.tiDismissCompletedTableReadLoader343('showProgressPopup-suppressed');\n                    return null;\n                }\n                return prevShow343.apply(this, arguments);\n            };\n            window.showProgressPopup.__tiNoTableReadLoader343 = true;\n        }\n        if (typeof window.updateProgressPopup === 'function' && !window.updateProgressPopup.__tiNoTableReadLoader343) {\n            var prevUpdate343 = window.updateProgressPopup;\n            window.updateProgressPopup = function(percent, title, subtitle, currentItem){\n                var combined = norm(String(title || '') + ' ' + String(subtitle || '') + ' ' + txt(document.getElementById('ti-loader-title')) + ' ' + txt(document.getElementById('ti-loader-sub')));\n                if ((parseInt(percent, 10) || 0) >= 100 && \/tabelle lette|lettura tabelle\/.test(combined)) {\n                    window.tiDismissCompletedTableReadLoader343('updateProgressPopup');\n                    return null;\n                }\n                return prevUpdate343.apply(this, arguments);\n            };\n            window.updateProgressPopup.__tiNoTableReadLoader343 = true;\n        }\n        [120, 700, 1500, 3000].forEach(function(ms){ setTimeout(function(){ window.tiDismissCompletedTableReadLoader343('install-' + ms); }, ms); });\n        var ticks = 0;\n        var timer = setInterval(function(){\n            ticks++;\n            window.tiDismissCompletedTableReadLoader343('periodic-' + ticks);\n            if (ticks > 240) clearInterval(timer);\n        }, 500);\n        document.addEventListener('visibilitychange', function(){ if (!document.hidden) setTimeout(function(){ window.tiDismissCompletedTableReadLoader343('visibility'); }, 80); });\n        window.addEventListener('focus', function(){ setTimeout(function(){ window.tiDismissCompletedTableReadLoader343('focus'); }, 80); });\n        document.addEventListener('click', function(){ setTimeout(function(){ window.tiDismissCompletedTableReadLoader343('click'); }, 20); }, true);\n    })();\n\n    window.clearLastFileContext = function() {\n        window.lastUploadedFile = null;\n        window.lastFileContext = null;\n    };\n\n    window.rememberLastFileContext = function(url, promptText, isImportFlow) {\n        if (!url) return;\n        window.lastFileContext = {\n            url: url,\n            lastActionPrompt: promptText || '',\n            pendingReusePrompt: '',\n            active: !!isImportFlow,\n            awaitingReuseChoice: false,\n            createdAt: Date.now()\n        };\n    };\n\n    window.extractPromptFromRow = function(row) {\n        if (!row || typeof row !== 'object') return '';\n        let descKey = Object.keys(row).find(k => ['descrizione','dettaglio','note'].includes(k.toLowerCase()));\n        let nameKey = Object.keys(row).find(k => ['nome','prodotto','servizio','articolo','titolo'].includes(k.toLowerCase()));\n        let promptText = (nameKey && row[nameKey] ? row[nameKey] + '. ' : '') + (descKey && row[descKey] ? row[descKey] : '');\n        return promptText.trim();\n    };\n\n    window.populateAIGenPrompt = function(row) {\n        let promptText = '';\n        if (row && typeof row === 'object') {\n            promptText = window.extractPromptFromRow(row);\n        } else if (gE('ti-msg') && gE('ti-msg').value.trim()) {\n            promptText = gE('ti-msg').value.trim();\n        }\n        if (gE('ai-gen-prompt')) gE('ai-gen-prompt').value = promptText;\n        if (gE('ti-msg') && promptText) gE('ti-msg').value = promptText;\n        return promptText;\n    };\n\n    window.isImportFollowupText = function(txt) {\n        if (!txt) return false;\n        return \/^(1|2|3|ok|si|s\u00ec|yes|no|x|annulla)$\/i.test(txt.trim()) || \/(import|reimport|file caricat|ultimo file|riutilizz|ripeti l'ultima azione|ultima azione)\/i.test(txt);\n    };\n\n    window.askReuseLastFile = function(requestText) {\n        if (!window.lastFileContext || !window.lastFileContext.url) return false;\n        window.lastFileContext.awaitingReuseChoice = true;\n        window.lastFileContext.pendingReusePrompt = requestText || window.lastFileContext.lastActionPrompt || 'Importa di nuovo i dati dal file caricato.';\n        window.addMsg('ai', \"Ho ancora disponibile l'ultimo file caricato. Vuoi riutilizzarlo e ripetere l'ultima azione? (SI\/NO)\");\n        return true;\n    };\n\n    window.getOrderViewStorageKey = function() {\n        const dbVal = (gE('ti-ditta') && gE('ti-ditta').value) ? gE('ti-ditta').value : (window.tiForced || 'global');\n        const userKey = window.tiUser || 'guest';\n        return 'ti_order_view_mode::' + userKey + '::' + dbVal;\n    };\n\n    window.isNonEmptyConfigFlag = function(v) { return String(v == null ? '' : v).trim() !== ''; };\n    window.getOrderConfigRow = function() {\n        try {\n            const pickRowFromDb = function(dbObj) {\n                if (!dbObj || !dbObj.Tabelle) return null;\n                const tables = dbObj.Tabelle || {};\n                const keys = Object.keys(tables || {});\n                let cfgKey = keys.find(function(k){ return String(k || '').toLowerCase() === 'config'; });\n                if (!cfgKey) cfgKey = keys.find(function(k){ return String(k || '').toLowerCase().indexOf('config') !== -1; });\n                if (!cfgKey || !Array.isArray(tables[cfgKey])) return null;\n                const firstRow = tables[cfgKey].find(function(r){ return r && typeof r === 'object' && !Array.isArray(r); });\n                return firstRow || null;\n            };\n            let row = pickRowFromDb(window.currentDbViewData || null) || pickRowFromDb(window.currentDbData || null);\n            if (row) return row;\n            const sel = gE('ti-ditta');\n            const opt = sel && sel.options ? sel.options[sel.selectedIndex] : null;\n            if (!opt) return null;\n            return { Tab_tab: opt.getAttribute('data-tab-tab') || '', Tab_sma: opt.getAttribute('data-tab-sma') || '', Tab_med: opt.getAttribute('data-tab-med') || '', Tab_lar: opt.getAttribute('data-tab-lar') || '' };\n        } catch(e) { return null; }\n    };\n    window.getOrderConfigFieldValue = function(fieldName) {\n        const row = window.getOrderConfigRow();\n        if (!row) return '';\n        const norm = function(v){ return String(v == null ? '' : v).toLowerCase().replace(\/[^a-z0-9]+\/g, ''); };\n        const wanted = norm(fieldName);\n        const realKey = Object.keys(row).find(function(k){ return norm(k) === wanted; });\n        return realKey ? row[realKey] : '';\n    };\n    window.getOrderViewAllowedModes = function() {\n        const map = [{ field:'Tab_tab', mode:'table' },{ field:'Tab_sma', mode:'small' },{ field:'Tab_med', mode:'medium' },{ field:'Tab_lar', mode:'large' }];\n        const allowed = map.filter(function(one){ return window.isNonEmptyConfigFlag(window.getOrderConfigFieldValue(one.field)); }).map(function(one){ return one.mode; });\n        return allowed.length ? allowed : ['table'];\n    };\n    window.getOrderViewPreference = function() {\n        const allowed = window.getOrderViewAllowedModes();\n        const storageKey = window.getOrderViewStorageKey();\n        const stored = localStorage.getItem(storageKey);\n        if (stored && allowed.includes(stored)) return stored;\n        const dbVal = (gE('ti-ditta') && gE('ti-ditta').value) ? gE('ti-ditta').value : (window.tiForced || '');\n        if (dbVal && window.tiServerPrefs && window.tiServerPrefs[dbVal] && allowed.includes(window.tiServerPrefs[dbVal].order_view_mode)) return window.tiServerPrefs[dbVal].order_view_mode;\n        return allowed[0] || 'table';\n    };\n\n    window.escapeHtml = function(str) {\n        return String(str || '').replace(\/[&<>\"']\/g, function(ch) {\n            return ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'})[ch] || ch;\n        });\n    };\n\n    window.setOrderView = function(btn, mode) {\n        const wrap = btn.closest('.ti-order-wrap');\n        const allowed = window.getOrderViewAllowedModes ? window.getOrderViewAllowedModes() : ['table','small','medium','large'];\n        if (!wrap || !allowed.includes(mode)) return;\n        wrap.className = wrap.className.replace(\/\\bti-view-(table|small|medium|large)\\b\/g, '').replace(\/\\s{2,}\/g, ' ').trim();\n        wrap.classList.add('ti-view-' + mode);\n        wrap.querySelectorAll('.ti-order-view-btn').forEach(function(b){ b.classList.toggle('active', b.dataset.view === mode); });\n        const storageKey = window.getOrderViewStorageKey();\n        localStorage.setItem(storageKey, mode);\n        const dbVal = (gE('ti-ditta') && gE('ti-ditta').value) ? gE('ti-ditta').value : (window.tiForced || '');\n        if (dbVal) {\n            if (!window.tiServerPrefs || typeof window.tiServerPrefs !== 'object') window.tiServerPrefs = {};\n            if (!window.tiServerPrefs[dbVal] || typeof window.tiServerPrefs[dbVal] !== 'object') window.tiServerPrefs[dbVal] = {};\n            window.tiServerPrefs[dbVal].order_view_mode = mode;\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_save_ui_pref');\n            fd.append('pref_key', 'order_view_mode');\n            fd.append('pref_value', mode);\n            fd.append('db', dbVal);\n            fetch(window.tiUrl, {method:'POST', body:fd}).catch(function(){});\n        }\n        if (window.initConfigTopScrollbars) setTimeout(function(){ window.initConfigTopScrollbars(wrap); }, 20);\n        window.calcOrderWrapTotals(wrap);\n    };\n\n    window.parseQtyInput = function(val) {\n        let raw = String(val == null ? '' : val).trim();\n        if (raw === '') return 0;\n        raw = raw.replace(\/\\s+\/g, '');\n        if (raw.indexOf(',') !== -1 && raw.indexOf('.') !== -1) raw = raw.replace(\/\\.\/g, '').replace(',', '.');\n        else if (raw.indexOf(',') !== -1) raw = raw.replace(',', '.');\n        let n = parseFloat(raw);\n        return isNaN(n) ? 0 : Math.max(0, n);\n    };\n    window.formatQtyInput = function(val) {\n        const n = window.parseQtyInput(val);\n        return window.fmtNum(n);\n    };\n    window.stepQtyInput = function(btn, delta) {\n        const wrap = btn ? btn.closest('.ti-qty-stepper') : null;\n        const inp = wrap ? wrap.querySelector('input') : null;\n        if (!inp) return;\n        const nextVal = Math.max(0, window.parseQtyInput(inp.value) + (parseFloat(delta) || 0));\n        inp.value = window.formatQtyInput(nextVal);\n        if (typeof window.syncOrderQty === 'function') window.syncOrderQty(inp);\n        else if (typeof inp.oninput === 'function') inp.oninput();\n    };\n\n    window.calcOrderWrapTotals = function(wrap) {\n        if (!wrap) return;\n        let totalQty = 0, totalVal = 0;\n        wrap.querySelectorAll('.ti-prod-table tbody tr').forEach(tr => {\n            if (tr.style.display === 'none') return;\n            const qtyInput = tr.querySelector('.ti-q');\n            if (!qtyInput) return;\n            const qty = window.parseQtyInput(qtyInput.value || '0');\n            const price = parseFloat(tr.getAttribute('data-price') || '0') || 0;\n            totalQty += qty;\n            totalVal += qty * price;\n        });\n        wrap.querySelectorAll('.ti-order-total-qty').forEach(el => el.innerText = window.fmtNum(totalQty));\n        wrap.querySelectorAll('.ti-order-total-value').forEach(el => el.innerText = '\u20ac ' + window.fmtNum(totalVal));\n    };\n\n    window.syncOrderQty = function(inp) {\n        const wrap = inp.closest('.ti-order-wrap');\n        if (!wrap) return;\n        const orderId = inp.getAttribute('data-order-id');\n        const qty = window.parseQtyInput(inp.value);\n        const formatted = window.fmtNum(qty);\n        wrap.querySelectorAll('.ti-q[data-order-id=\"' + orderId + '\"]').forEach(el => {\n            if (el !== inp) el.value = formatted;\n            else if (document.activeElement !== el) el.value = formatted;\n        });\n        wrap.querySelectorAll('.p-tot[data-order-id=\"' + orderId + '\"]').forEach(el => {\n            const price = parseFloat(el.getAttribute('data-price') || '0') || 0;\n            el.innerText = window.fmtNum(price * qty);\n        });\n        window.calcOrderWrapTotals(wrap);\n        window.refreshOrderConfirmPanel();\n    };\n\n    window.sortTI = function(th, colIdx) {\n        const table = th.closest('table');\n        if (!table) return;\n        const tbody = table.querySelector('tbody');\n        const rows = Array.from(tbody.querySelectorAll('tr'));\n        const asc = th.dataset.sortDir !== 'asc';\n        table.querySelectorAll('th').forEach(h => delete h.dataset.sortDir);\n        th.dataset.sortDir = asc ? 'asc' : 'desc';\n        rows.sort((a, b) => {\n            const av = window.getComparableCellValue(a.children[colIdx]);\n            const bv = window.getComparableCellValue(b.children[colIdx]);\n            let cmp = 0;\n            if (av.type === 'num' && bv.type === 'num') cmp = av.value - bv.value;\n            else cmp = String(av.value).localeCompare(String(bv.value), 'it');\n            return asc ? cmp : -cmp;\n        });\n        rows.forEach(r => tbody.appendChild(r));\n        const wrap = table.closest('.ti-order-wrap');\n        if (wrap) window.calcOrderWrapTotals(wrap);\n    };\n\n    window.filterTI = function(inp, colIdx) {\n        const table = inp.closest('table');\n        if (!table) return;\n        const val = inp.value.trim().toLowerCase();\n        table.querySelectorAll('tbody tr').forEach(tr => {\n            const cellTxt = (tr.children[colIdx]?.innerText || '').trim().toLowerCase();\n            tr.style.display = !val || cellTxt.includes(val) ? '' : 'none';\n        });\n        const wrap = table.closest('.ti-order-wrap');\n        if (wrap) window.calcOrderWrapTotals(wrap);\n    };\n\n    window.orderToolbarState = window.orderToolbarState || {};\n    window.getOrderWrapState = function(wrap) {\n        if (!wrap) return {filters:{name:'', desc:'', price:''}, sort:'name_asc'};\n        if (!wrap.dataset.orderWrapId) wrap.dataset.orderWrapId = 'ow-' + Math.random().toString(36).slice(2, 10);\n        const id = wrap.dataset.orderWrapId;\n        if (!window.orderToolbarState[id]) window.orderToolbarState[id] = {filters:{name:'', desc:'', price:''}, sort:'name_asc'};\n        return window.orderToolbarState[id];\n    };\n    window.filterOrderWrap = function(inp, key) {\n        const wrap = inp.closest('.ti-order-wrap');\n        if (!wrap) return;\n        const st = window.getOrderWrapState(wrap);\n        st.filters[key] = inp.value || '';\n        window.applyOrderWrapFilters(wrap);\n    };\n    window.sortOrderWrap = function(sel) {\n        const wrap = sel.closest('.ti-order-wrap');\n        if (!wrap) return;\n        const st = window.getOrderWrapState(wrap);\n        st.sort = sel.value || 'name_asc';\n        window.applyOrderWrapFilters(wrap);\n    };\n    window.applyOrderWrapFilters = function(wrap) {\n        if (!wrap) return;\n        const st = window.getOrderWrapState(wrap);\n        const rows = Array.from(wrap.querySelectorAll('.ti-prod-table tbody tr'));\n        const cards = Array.from(wrap.querySelectorAll('.ti-order-card'));\n        const allIds = new Set();\n        rows.forEach(tr => allIds.add(tr.getAttribute('data-order-id') || ''));\n        cards.forEach(card => allIds.add(card.getAttribute('data-order-id') || ''));\n        const visibility = {};\n        allIds.forEach(id => {\n            const src = wrap.querySelector('.ti-prod-table tbody tr[data-order-id=\"' + id + '\"]') || wrap.querySelector('.ti-order-card[data-order-id=\"' + id + '\"]');\n            if (!src) return;\n            const name = src.getAttribute('data-name') || '';\n            const desc = src.getAttribute('data-desc') || '';\n            const price = src.getAttribute('data-price') || '';\n            const okName = window.configFilterMatches(name, st.filters.name || '');\n            const okDesc = window.configFilterMatches(desc, st.filters.desc || '');\n            const okPrice = window.configFilterMatches(price, st.filters.price || '');\n            visibility[id] = !!(okName && okDesc && okPrice);\n        });\n        rows.forEach(tr => { tr.style.display = visibility[tr.getAttribute('data-order-id') || ''] ? '' : 'none'; });\n        cards.forEach(card => { card.style.display = visibility[card.getAttribute('data-order-id') || ''] ? '' : 'none'; });\n        const [sortKey, sortDir] = String(st.sort || 'name_asc').split('_');\n        const sorter = (a, b) => {\n            const getVal = (el) => {\n                const raw = (sortKey === 'price') ? (el.getAttribute('data-price') || '0') : (el.getAttribute('data-' + sortKey) || '');\n                return window.getComparableCellValue({innerText: raw});\n            };\n            const av = getVal(a), bv = getVal(b);\n            let cmp = 0;\n            if (av.type === 'num' && bv.type === 'num') cmp = av.value - bv.value;\n            else cmp = String(av.value).localeCompare(String(bv.value), 'it');\n            return sortDir === 'desc' ? -cmp : cmp;\n        };\n        const tbody = wrap.querySelector('.ti-prod-table tbody');\n        rows.sort(sorter).forEach(tr => tbody.appendChild(tr));\n        const grid = wrap.querySelector('.ti-order-grid-view');\n        cards.sort(sorter).forEach(card => grid.appendChild(card));\n        window.calcOrderWrapTotals(wrap);\n    };\n\n    window.getTemplateRowForTable = function(tName) {\n        if (window.currentDbData && window.currentDbData.__templateTabelle && window.currentDbData.__templateTabelle[tName] && window.currentDbData.__templateTabelle[tName][0] && typeof window.currentDbData.__templateTabelle[tName][0] === 'object') {\n            return window.currentDbData.__templateTabelle[tName][0];\n        }\n        return null;\n    };\n\n    window.getTemplateSchemaKeys = function(tName) {\n        let keys = [];\n        const templateRow = window.getTemplateRowForTable(tName);\n        if (templateRow && typeof templateRow === 'object' && !Array.isArray(templateRow)) {\n            keys = Object.keys(templateRow);\n        }\n        if (!keys.length) {\n            const rows = (window.currentDbData && window.currentDbData.Tabelle && Array.isArray(window.currentDbData.Tabelle[tName])) ? window.currentDbData.Tabelle[tName] : [];\n            rows.forEach(function(row){\n                if (!row || typeof row !== 'object' || Array.isArray(row)) return;\n                Object.keys(row).forEach(function(k){ if (!keys.includes(k)) keys.push(k); });\n            });\n        }\n        const low = String(tName || '').toLowerCase();\n        if (low === 'informazioni') {\n            const desired = ['ID','Tipo','Descrizione','Istruzioni','Data','Ora','Nota','Stato','Immagine','Iva'];\n            keys = desired.filter(k => keys.includes(k)).concat(keys.filter(k => !desired.includes(k)));\n        } else if (low === 'istruzioni') {\n            const desired = ['ID','Istruzione','Da gg','A gg','Da ora','A ora','Nota','Stato','Immagine','Iva','Data','Ora'];\n            keys = desired.filter(k => keys.includes(k)).concat(keys.filter(k => !desired.includes(k)));\n        }\n        if (!keys.includes('ID')) keys.unshift('ID');\n        return keys;\n    };\n\n    window.buildTemplateDefaultValue = function(tplVal) {\n        if (tplVal === undefined || tplVal === null) return '';\n        const str = String(tplVal).trim();\n        if (str === 'DD\/MM\/YYYY' || str === 'HH:MM') return '';\n        return tplVal;\n    };\n\n    window.getTemplateEmptyRow = function(tName, nextId) {\n        const templateRow = window.getTemplateRowForTable(tName);\n        const keys = window.getTemplateSchemaKeys(tName);\n        const row = {};\n        keys.forEach(function(k){\n            if (String(k).toLowerCase() === 'id') {\n                row[k] = nextId;\n                return;\n            }\n            if (templateRow && Object.prototype.hasOwnProperty.call(templateRow, k)) {\n                row[k] = window.buildTemplateDefaultValue(templateRow[k]);\n            } else {\n                row[k] = '';\n            }\n        });\n        if (!('ID' in row)) row.ID = nextId;\n        return row;\n    };\n\n    window.normalizeTableRowsToTemplate = function(tName) {\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tName]) return;\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        const keys = window.getTemplateSchemaKeys(tName);\n        const templateRow = window.getTemplateRowForTable(tName);\n        window.currentDbData.Tabelle[tName] = rows.map(function(srcRow, idx){\n            const safeRow = (srcRow && typeof srcRow === 'object' && !Array.isArray(srcRow)) ? srcRow : {};\n            const clean = {};\n            keys.forEach(function(k){\n                if (Object.prototype.hasOwnProperty.call(safeRow, k)) {\n                    clean[k] = safeRow[k];\n                } else if (window.getConfigFieldAliasCandidates) {\n                    const aliasKey = window.getConfigFieldAliasCandidates(k).find(function(a){ return Object.prototype.hasOwnProperty.call(safeRow, a); });\n                    if (aliasKey) clean[k] = safeRow[aliasKey];\n                    else if (String(k).toLowerCase() === 'id') clean[k] = (safeRow.ID || (idx + 1));\n                    else if (templateRow && Object.prototype.hasOwnProperty.call(templateRow, k)) clean[k] = window.buildTemplateDefaultValue(templateRow[k]);\n                    else clean[k] = '';\n                } else if (String(k).toLowerCase() === 'id') {\n                    clean[k] = (safeRow.ID || (idx + 1));\n                } else if (templateRow && Object.prototype.hasOwnProperty.call(templateRow, k)) {\n                    clean[k] = window.buildTemplateDefaultValue(templateRow[k]);\n                } else {\n                    clean[k] = '';\n                }\n            });\n            Object.keys(safeRow).forEach(function(k){ if (!Object.prototype.hasOwnProperty.call(clean, k)) clean[k] = safeRow[k]; });\n            if (!('ID' in clean)) clean.ID = idx + 1;\n            return clean;\n        });\n    };\n\n    window.isAccountTableName = function(tName) {\n        const low = String(tName || '').toLowerCase();\n        return low.indexOf('utent') !== -1 || low.indexOf('operator') !== -1;\n    };\n\n    window.isUsernameFieldName = function(key) {\n        const low = String(key || '').trim().toLowerCase();\n        return low === 'username' || low === 'user';\n    };\n\n    window.getConfigUsernameValue = function(row) {\n        if (!row || typeof row !== 'object') return '';\n        const key = Object.keys(row).find(function(k){ return window.isUsernameFieldName(k); });\n        return key ? String(row[key] == null ? '' : row[key]).trim() : '';\n    };\n\n    window.normalizeUsernameForCheck = function(v) {\n        return String(v == null ? '' : v).trim().toLowerCase();\n    };\n\n    window.getConfigRowIdValue = function(row) {\n        if (!row || typeof row !== 'object') return '';\n        for (const k of ['ID','Id','id']) {\n            if (Object.prototype.hasOwnProperty.call(row, k)) {\n                const v = String(row[k] == null ? '' : row[k]).trim();\n                if (v) return v;\n            }\n        }\n        return '';\n    };\n\n    window.getOriginalConfigRowsForTable = function(tName) {\n        const tables = window.configOriginalDbData && window.configOriginalDbData.Tabelle ? window.configOriginalDbData.Tabelle : null;\n        if (!tables) return null;\n        if (Array.isArray(tables[tName])) return tables[tName];\n        const wanted = String(tName || '').toLowerCase();\n        for (const oldName in tables) {\n            if (String(oldName).toLowerCase() === wanted && Array.isArray(tables[oldName])) return tables[oldName];\n        }\n        return null;\n    };\n\n    window.getOriginalConfigRow = function(tName, idx, row) {\n        const rows = window.getOriginalConfigRowsForTable(tName);\n        if (!rows) return null;\n        const newId = window.getConfigRowIdValue(row);\n        const indexRow = rows[parseInt(idx, 10)];\n        if (indexRow && typeof indexRow === 'object') {\n            const oldId = window.getConfigRowIdValue(indexRow);\n            if (!newId || !oldId || newId === oldId) return indexRow;\n        }\n        if (newId) {\n            for (const oldRow of rows) {\n                if (oldRow && typeof oldRow === 'object' && window.getConfigRowIdValue(oldRow) === newId) return oldRow;\n            }\n        }\n        return null;\n    };\n\n    window.isConfigUsernameNewOrChanged = function(tName, idx, row) {\n        if (!row || typeof row !== 'object') return false;\n        const username = window.getConfigUsernameValue(row);\n        const key = window.normalizeUsernameForCheck(username);\n        if (!key) return false;\n        const origRow = window.getOriginalConfigRow(tName, idx, row);\n        if (origRow && typeof origRow === 'object') {\n            return window.normalizeUsernameForCheck(window.getConfigUsernameValue(origRow)) !== key;\n        }\n        if (window.configOriginalDbData && window.configOriginalDbData.Tabelle) return true;\n        const uKey = Object.keys(row).find(function(k){ return window.isUsernameFieldName(k); }) || '';\n        return !!(window.modifiedFields && window.modifiedFields.has(`${tName}-${idx}-${uKey}`));\n    };\n\n    window.findDuplicateUsernameInConfig = function(username, excludeTbl, excludeIdx) {\n        const wanted = window.normalizeUsernameForCheck(username);\n        if (!wanted || !window.currentDbData || !window.currentDbData.Tabelle) return null;\n        const tables = window.currentDbData.Tabelle;\n        for (const tName in tables) {\n            if (!window.isAccountTableName(tName)) continue;\n            let rows = tables[tName];\n            if (!Array.isArray(rows)) rows = tables[tName] = Object.values(rows || {});\n            for (let i = 0; i < rows.length; i++) {\n                const row = rows[i] || {};\n                const sameRow = String(tName).toLowerCase() === String(excludeTbl || '').toLowerCase() && parseInt(excludeIdx, 10) === i;\n                if (sameRow) continue;\n                const state = String(row.Stato || row.state || row.status || '').trim().toLowerCase();\n                if (state === 'cancellato' || state.indexOf('cancellat') !== -1) continue;\n                const rowUsername = window.getConfigUsernameValue(row);\n                const cand = window.normalizeUsernameForCheck(rowUsername);\n                if (cand && cand === wanted) return {table:tName, idx:i, username:(rowUsername || username)};\n            }\n        }\n        return null;\n    };\n\n    window.focusConfigUsernameCell = function(tbl, idx) {\n        try {\n            const nodes = document.querySelectorAll('.ti-cfg-table input, .ti-cfg-table textarea, .ti-cfg-table select');\n            for (const el of nodes) {\n                if (!el.dataset) continue;\n                if (String(el.dataset.tbl || '').toLowerCase() !== String(tbl || '').toLowerCase()) continue;\n                if (String(el.dataset.idx || '') !== String(idx)) continue;\n                if (!window.isUsernameFieldName(el.dataset.key || '')) continue;\n                if (el.scrollIntoView) el.scrollIntoView({behavior:'smooth', block:'center', inline:'center'});\n                el.style.setProperty('border-color', '#ef4444', 'important');\n                el.style.setProperty('box-shadow', '0 0 0 2px rgba(239,68,68,.55)', 'important');\n                window.forceWhiteEditStyle && window.forceWhiteEditStyle(el);\n                setTimeout(function(){ try { el.focus(); if (el.select) el.select(); } catch(e) {} }, 120);\n                return true;\n            }\n        } catch(e) {}\n        return false;\n    };\n\n    window.showDuplicateUsernamePopup = function(username, dup) {\n        const u = String(username || '').trim();\n        const where = dup && dup.table ? ('<br><small>Presente in tabella <b>' + window.escapeHtml(dup.table) + '<\/b>, riga ' + (parseInt(dup.idx, 10) + 1) + '.<\/small>') : '';\n        window.tiAlert('Username gi\u00e0 esistente' + (u ? ': <b>' + window.escapeHtml(u) + '<\/b>' : '') + '.<br>Cambia username perch\u00e9 \u00e8 gi\u00e0 esistente.' + where);\n    };\n\n    window.checkConfigUsernameDuplicateInput = function(tbl, idx, key, el) {\n        if (!window.isAccountTableName(tbl) || !window.isUsernameFieldName(key)) return false;\n        const username = el ? String(el.value || '').trim() : '';\n        if (!username) return false;\n        const row = (window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[tbl]) ? window.currentDbData.Tabelle[tbl][idx] : null;\n        if (row && window.isConfigUsernameNewOrChanged && !window.isConfigUsernameNewOrChanged(tbl, idx, row)) return false;\n        const dup = window.findDuplicateUsernameInConfig(username, tbl, idx);\n        if (!dup) return false;\n        if (el) {\n            el.style.setProperty('border-color', '#ef4444', 'important');\n            el.style.setProperty('box-shadow', '0 0 0 2px rgba(239,68,68,.55)', 'important');\n            window.forceWhiteEditStyle && window.forceWhiteEditStyle(el);\n            setTimeout(function(){ try { el.focus(); if (el.select) el.select(); } catch(e) {} }, 80);\n        }\n        window.showDuplicateUsernamePopup(username, dup);\n        return true;\n    };\n\n    window.validateConfigUniqueUsernames = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return true;\n        const tables = window.currentDbData.Tabelle;\n        for (const tName in tables) {\n            if (!window.isAccountTableName(tName)) continue;\n            let rows = tables[tName];\n            if (!Array.isArray(rows)) rows = tables[tName] = Object.values(rows || {});\n            for (let i = 0; i < rows.length; i++) {\n                const row = rows[i] || {};\n                const state = String(row.Stato || row.state || row.status || '').trim().toLowerCase();\n                if (state === 'cancellato' || state.indexOf('cancellat') !== -1) continue;\n                const username = window.getConfigUsernameValue(row);\n                if (!window.normalizeUsernameForCheck(username)) continue;\n                if (window.isConfigUsernameNewOrChanged && !window.isConfigUsernameNewOrChanged(tName, i, row)) continue;\n                const dup = window.findDuplicateUsernameInConfig(username, tName, i);\n                if (dup) {\n                    window.showDuplicateUsernamePopup(username, dup);\n                    window.focusConfigUsernameCell(tName, i);\n                    return false;\n                }\n            }\n        }\n        return true;\n    };\n\n    window.isConfigSingleRowTable379 = function(tName) {\n        try {\n            return String(tName || '').trim().toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').replace(\/[^a-z0-9]\/g, '') === 'config';\n        } catch(e) {\n            return String(tName || '').trim().toLowerCase() === 'config';\n        }\n    };\n\n    window.addTableRow = function(tName) {\n        if (window.isConfigSingleRowTable379 && window.isConfigSingleRowTable379(tName)) {\n            if (window.tiAlert) window.tiAlert('La tabella Config \u00e8 a riga unica: non \u00e8 possibile aggiungere righe.');\n            return false;\n        }\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tName]) return;\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        window.normalizeTableRowsToTemplate(tName);\n        rows = window.currentDbData.Tabelle[tName];\n        const nextId = rows.reduce(function(maxId, row){ const n = parseInt((row && row.ID != null) ? row.ID : 0, 10); return isNaN(n) ? maxId : Math.max(maxId, n); }, 0) + 1;\n        rows.unshift(window.getTemplateEmptyRow(tName, nextId));\n        if (!window.modifiedFields) window.modifiedFields = new Set(); window.modifiedFields.add(`${tName}-__row_added`); if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        window.configForceTop = true;\n        window.renderConfig();\n    };\n\n    window.duplicateTableRow = function(tName, idx) {\n        if (window.isConfigSingleRowTable379 && window.isConfigSingleRowTable379(tName)) {\n            if (window.tiAlert) window.tiAlert('La tabella Config \u00e8 a riga unica: non \u00e8 possibile duplicare righe.');\n            return false;\n        }\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tName] || !window.currentDbData.Tabelle[tName][idx]) return;\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows);\n        const clone = JSON.parse(JSON.stringify(rows[idx]));\n        clone.ID = rows.length + 1;\n        if (window.isAccountTableName(tName)) {\n            Object.keys(clone || {}).forEach(function(k){ if (window.isUsernameFieldName(k)) clone[k] = ''; });\n        }\n        rows.splice(idx + 1, 0, clone);\n        if (!window.modifiedFields) window.modifiedFields = new Set(); window.modifiedFields.add(`${tName}-__row_duplicated`); if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        window.renderConfig();\n        if (window.isAccountTableName(tName)) window.tiAlert('Riga utente duplicata. Inserisci un nuovo username: non pu\u00f2 essere uguale a uno gi\u00e0 esistente.');\n    };\n\n    window.canPhysicallyDeleteUserRows = function() {\n        const r = String(window.tiRole || '').toLowerCase();\n        return r.includes('amministratore') || r.includes('configuratore') || String(window.tiUser || '').toLowerCase() === 'ssglobaladmin';\n    };\n\n    window.reindexTableRows = function(tName) {\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tName]) return [];\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        rows.forEach(function(rowObj, rowIdx) {\n            if (!rowObj || typeof rowObj !== 'object') return;\n            if (Object.prototype.hasOwnProperty.call(rowObj, 'ID')) rowObj.ID = rowIdx + 1;\n            else if (Object.prototype.hasOwnProperty.call(rowObj, 'Id')) rowObj.Id = rowIdx + 1;\n            else if (Object.prototype.hasOwnProperty.call(rowObj, 'id')) rowObj.id = rowIdx + 1;\n        });\n        window.currentDbData.Tabelle[tName] = rows;\n        return rows;\n    };\n\n    window.markTableRowsModified = function(tName) {\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tName]) return;\n        if (!window.modifiedFields) window.modifiedFields = new Set();\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        rows.forEach(function(rowObj, rowIdx) {\n            Object.keys(rowObj || {}).forEach(function(k) {\n                window.modifiedFields.add(`${tName}-${rowIdx}-${k}`); if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n            });\n        });\n    };\n\n    window.mustKeepAtLeastOneConfigRow = function(tName) {\n        const low = String(tName || '').trim().toLowerCase();\n        return low === 'config' || low === 'utenti';\n    };\n\n    window.clearSingleRemainingRow = function(tName, rowIndex) {\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !Object.prototype.hasOwnProperty.call(window.currentDbData.Tabelle, tName)) return false;\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        const idx = parseInt(rowIndex, 10);\n        if (isNaN(idx) || idx < 0 || idx >= rows.length) return false;\n        const oldRow = (rows[idx] && typeof rows[idx] === 'object' && !Array.isArray(rows[idx])) ? rows[idx] : {};\n        const keys = (window.getTemplateSchemaKeys ? window.getTemplateSchemaKeys(tName) : Object.keys(oldRow || {}));\n        const clean = {};\n        keys.forEach(function(k) {\n            if (String(k).toLowerCase() === 'id') clean[k] = 1;\n            else clean[k] = '';\n        });\n        Object.keys(oldRow || {}).forEach(function(k) {\n            if (!Object.prototype.hasOwnProperty.call(clean, k)) clean[k] = (String(k).toLowerCase() === 'id') ? 1 : '';\n        });\n        rows[idx] = clean;\n        window.currentDbData.Tabelle[tName] = rows;\n        if (!window.modifiedFields) window.modifiedFields = new Set();\n        Object.keys(clean || {}).forEach(function(k) { window.modifiedFields.add(`${tName}-${idx}-${k}`); });\n        window.modifiedFields.add(`${tName}-${idx}-__row_cleared`); if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        return true;\n    };\n\n    window.resolveConfigTableName = function(tName) {\n        const wanted = String(tName || '').trim();\n        const tables = (window.currentDbData && window.currentDbData.Tabelle) ? window.currentDbData.Tabelle : null;\n        if (!tables) return wanted;\n        if (Object.prototype.hasOwnProperty.call(tables, wanted)) return wanted;\n        const norm = function(v) {\n            try { v = String(v || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, ''); } catch(e) { v = String(v || ''); }\n            return v.toLowerCase().replace(\/[^a-z0-9]\/g, '');\n        };\n        const nw = norm(wanted);\n        for (const k in tables) {\n            if (String(k) === wanted || norm(k) === nw) return k;\n        }\n        return wanted;\n    };\n\n    window.getConfigRowIdCI = function(row) {\n        if (!row || typeof row !== 'object') return '';\n        const keys = Object.keys(row);\n        for (let i = 0; i < keys.length; i++) {\n            if (String(keys[i]).trim().toLowerCase() === 'id') return String(row[keys[i]] == null ? '' : row[keys[i]]).trim();\n        }\n        return '';\n    };\n\n    window.recordConfigDeletedRowsManifest = function(tName, rows, indexes) {\n        if (!window.tiConfigDeletedRowsManifest) window.tiConfigDeletedRowsManifest = [];\n        rows = Array.isArray(rows) ? rows : [];\n        (indexes || []).forEach(function(i){\n            i = parseInt(i, 10);\n            if (isNaN(i) || i < 0 || i >= rows.length) return;\n            const rowCopy = JSON.parse(JSON.stringify(rows[i] || {}));\n            window.tiConfigDeletedRowsManifest.push({\n                table: String(tName || ''),\n                idx: i,\n                id: window.getConfigRowIdCI ? window.getConfigRowIdCI(rowCopy) : '',\n                row: rowCopy,\n                row_count_before: rows.length,\n                row_count_after: Math.max(0, rows.length - 1),\n                deleted_at: new Date().toISOString()\n            });\n        });\n    };\n\n    window.rememberConfigDeletedTableSnapshot = function(tName) {\n        tName = window.resolveConfigTableName ? window.resolveConfigTableName(tName) : tName;\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !Object.prototype.hasOwnProperty.call(window.currentDbData.Tabelle, tName)) return;\n        if (!window.tiConfigDeletedTableSnapshots) window.tiConfigDeletedTableSnapshots = {};\n        if (!window.tiConfigDeletedTableSnapshotBeforeCounts) window.tiConfigDeletedTableSnapshotBeforeCounts = {};\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        const beforeCount = parseInt(window.tiConfigDeletedTableSnapshotBeforeCounts[tName] || rows.length, 10) || rows.length;\n        window.tiConfigDeletedTableSnapshots[tName] = {\n            table: String(tName || ''),\n            row_count_before: Math.max(beforeCount, rows.length),\n            row_count_after: rows.length,\n            rows: JSON.parse(JSON.stringify(rows || [])),\n            saved_at: new Date().toISOString()\n        };\n    };\n\n    window.refreshConfigDeletedTableSnapshotsFromCurrentData = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.tiConfigDeletedTableSnapshots) return;\n        if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory();\n        Object.keys(window.tiConfigDeletedTableSnapshots || {}).forEach(function(tName){\n            const realTbl = window.resolveConfigTableName ? window.resolveConfigTableName(tName) : tName;\n            if (!window.currentDbData.Tabelle || !Object.prototype.hasOwnProperty.call(window.currentDbData.Tabelle, realTbl)) return;\n            let rows = window.currentDbData.Tabelle[realTbl];\n            if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[realTbl] = Object.values(rows || {});\n            const snap = window.tiConfigDeletedTableSnapshots[tName] || {};\n            const beforeCount = Math.max(parseInt(snap.row_count_before || 0, 10) || 0, parseInt((window.tiConfigDeletedTableSnapshotBeforeCounts || {})[realTbl] || 0, 10) || 0, rows.length);\n            window.tiConfigDeletedTableSnapshots[tName] = {\n                table: String(realTbl || tName || ''),\n                row_count_before: beforeCount,\n                row_count_after: rows.length,\n                rows: JSON.parse(JSON.stringify(rows || [])),\n                saved_at: new Date().toISOString()\n            };\n        });\n    };\n\n    window.applyConfigDeletedTableSnapshotsToCurrentData = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.tiConfigDeletedTableSnapshots) return;\n        Object.keys(window.tiConfigDeletedTableSnapshots || {}).forEach(function(tName){\n            const snap = window.tiConfigDeletedTableSnapshots[tName] || {};\n            const rows = Array.isArray(snap.rows) ? snap.rows : null;\n            if (!rows) return;\n            const realTbl = window.resolveConfigTableName ? window.resolveConfigTableName(tName) : tName;\n            if (!window.currentDbData.Tabelle[realTbl] || !Array.isArray(window.currentDbData.Tabelle[realTbl])) return;\n            \/\/ Se una vecchia ricostruzione ha rimesso righe cancellate, forza la tabella nello stato post-cancellazione.\n            if (window.currentDbData.Tabelle[realTbl].length > rows.length) {\n                window.currentDbData.Tabelle[realTbl] = JSON.parse(JSON.stringify(rows));\n            }\n        });\n    };\n\n    window.syncVisibleConfigInputsToMemory = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return;\n        const root = gE('ti-conf-cnt') || document;\n        root.querySelectorAll('input[data-tbl][data-idx][data-key], textarea[data-tbl][data-idx][data-key], select[data-tbl][data-idx][data-key]').forEach(function(el){\n            const tbl = el.getAttribute('data-tbl') || '';\n            const key = el.getAttribute('data-key') || '';\n            const idx = parseInt(el.getAttribute('data-idx') || '', 10);\n            if (!tbl || !key || isNaN(idx)) return;\n            const realTbl = window.resolveConfigTableName ? window.resolveConfigTableName(tbl) : tbl;\n            if (!window.currentDbData.Tabelle[realTbl] || !window.currentDbData.Tabelle[realTbl][idx]) return;\n            let val = '';\n            if (el.tagName === 'SELECT' && el.multiple) val = Array.from(el.selectedOptions || []).map(function(o){ return String(o.value || '').trim(); }).filter(Boolean).join(', ');\n            else if (el.type === 'checkbox') val = el.checked ? (el.value || '1') : '';\n            else val = el.value;\n            if (window.normalizeInputValueForKey) val = window.normalizeInputValueForKey(key, val);\n            const oldVal = window.currentDbData.Tabelle[realTbl][idx][key];\n            if (String(oldVal == null ? '' : oldVal) !== String(val == null ? '' : val)) {\n                window.currentDbData.Tabelle[realTbl][idx][key] = val;\n                if (!window.modifiedFields) window.modifiedFields = new Set();\n                window.modifiedFields.add(realTbl + '-' + idx + '-' + key);\n                try { if (el && el.style) el.style.borderColor = '#60a5fa'; } catch(e) {}\n                try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n            } else {\n                window.currentDbData.Tabelle[realTbl][idx][key] = val;\n            }\n        });\n    };\n\n    window.syncConfigTableAfterRowMutation = function(tName) {\n        tName = window.resolveConfigTableName ? window.resolveConfigTableName(tName) : tName;\n        try {\n            if (window.currentDbViewData && window.currentDbViewData !== window.currentDbData && window.currentDbViewData.Tabelle && window.currentDbData && window.currentDbData.Tabelle && Object.prototype.hasOwnProperty.call(window.currentDbData.Tabelle, tName)) {\n                window.currentDbViewData.Tabelle[tName] = JSON.parse(JSON.stringify(window.currentDbData.Tabelle[tName] || []));\n            }\n        } catch(e) {}\n        try { window.__configRelationSupportCache = null; } catch(e) {}\n        try { window.__tiLastConfigRowMutation = Date.now(); } catch(e) {}\n    };\n\n    window.deleteConfigRowsByIndexes = function(tName, indexes, opts) {\n        opts = opts || {};\n        tName = window.resolveConfigTableName ? window.resolveConfigTableName(tName) : tName;\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !Object.prototype.hasOwnProperty.call(window.currentDbData.Tabelle, tName)) return false;\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        const ixs = Array.from(new Set((indexes || []).map(function(v){ return parseInt(v, 10); }).filter(function(n){ return !isNaN(n) && n >= 0 && n < rows.length; }))).sort(function(a,b){ return b-a; });\n        if (!ixs.length) { window.tiLastConfigDeleteCount = 0; return false; }\n        const keepOne = window.mustKeepAtLeastOneConfigRow && window.mustKeepAtLeastOneConfigRow(tName);\n        if (keepOne && ixs.length >= rows.length) {\n            window.tiLastConfigDeleteCount = 0;\n            window.tiAlert('La tabella ' + String(tName || '') + ' deve mantenere almeno una riga. Righe cancellate: 0.');\n            return false;\n        }\n        if (!window.tiConfigDeletedTableSnapshotBeforeCounts) window.tiConfigDeletedTableSnapshotBeforeCounts = {};\n        if (!window.tiConfigDeletedTableSnapshotBeforeCounts[tName]) window.tiConfigDeletedTableSnapshotBeforeCounts[tName] = rows.length;\n        if (window.recordConfigDeletedRowsManifest) window.recordConfigDeletedRowsManifest(tName, rows, ixs);\n        const beforeDeleteLen = rows.length;\n        if (!keepOne && rows.length === 1 && ixs.length === 1) {\n            rows.splice(ixs[0], 1);\n        } else {\n            ixs.forEach(function(i){ rows.splice(i, 1); });\n        }\n        window.tiLastConfigDeleteCount = Math.max(0, beforeDeleteLen - rows.length);\n        window.tiConfigDeletedRowsCountTotal = Math.max(0, parseInt(window.tiConfigDeletedRowsCountTotal || 0, 10) || 0) + window.tiLastConfigDeleteCount;\n        window.currentDbData.Tabelle[tName] = rows;\n        window.reindexTableRows(tName);\n        if (window.rememberConfigDeletedTableSnapshot) window.rememberConfigDeletedTableSnapshot(tName);\n        window.markTableRowsModified(tName);\n        if (!window.modifiedFields) window.modifiedFields = new Set();\n        window.modifiedFields.add(`${tName}-__rows_deleted`);\n        window.modifiedFields.add(`${tName}-__table_rewritten`);\n        if (window.syncConfigTableAfterRowMutation) window.syncConfigTableAfterRowMutation(tName);\n        if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        if (opts.render !== false) {\n            window.configForceTop = true;\n            window.renderConfig();\n        }\n        return true;\n    };\n\n    window.delTableRow = function(tName, idx, sourceEl, opts) {\n        opts = opts || {};\n        tName = window.resolveConfigTableName ? window.resolveConfigTableName(tName) : tName;\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !Object.prototype.hasOwnProperty.call(window.currentDbData.Tabelle, tName)) {\n            window.tiAlert && window.tiAlert('Tabella non trovata nella configurazione corrente.');\n            return false;\n        }\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows || {});\n        const rowIndex = parseInt(idx, 10);\n        if (isNaN(rowIndex) || rowIndex < 0 || rowIndex >= rows.length) {\n            window.tiAlert && window.tiAlert('Riga non trovata o gia eliminata.');\n            return false;\n        }\n        const doDelete = function(){\n            if (window.deleteConfigRowsByIndexes) {\n                const ok = window.deleteConfigRowsByIndexes(tName, [rowIndex], {render:true});\n                if (ok) window.tiAlert('Righe cancellate dalla configurazione corrente: ' + (window.tiLastConfigDeleteCount || 1) + '. Premi Salva Modifiche per aggiornare il database.');\n                return !!ok;\n            }\n            rows.splice(rowIndex, 1);\n            window.currentDbData.Tabelle[tName] = rows;\n            window.reindexTableRows(tName);\n            window.markTableRowsModified(tName);\n            if (window.syncConfigTableAfterRowMutation) window.syncConfigTableAfterRowMutation(tName);\n            window.configForceTop = true;\n            window.renderConfig();\n            window.tiAlert && window.tiAlert('Righe cancellate dalla configurazione corrente: 1. Premi Salva Modifiche per aggiornare il database.');\n            return true;\n        };\n        if (opts && opts.noConfirm) return doDelete();\n        const msg = 'Eliminare questa riga dalla tabella ' + String(tName || '') + '?<br><br>La riga verra rimossa dalla configurazione corrente. Per aggiornare il database dovrai premere Salva Modifiche.';\n        if (typeof window.tiConfirmYesNoAction === 'function') window.tiConfirmYesNoAction(msg, doDelete, null, 'ELIMINA', 'ANNULLA');\n        else if (typeof window.tiConfirm === 'function') window.tiConfirm(msg, doDelete);\n        else doDelete();\n        return true;\n    };\n\n    window.addTableCol = function(tName) {\n        const colName = prompt('Nome nuova colonna:');\n        if (!colName || !window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tName]) return;\n        let rows = window.currentDbData.Tabelle[tName];\n        if (!Array.isArray(rows)) rows = window.currentDbData.Tabelle[tName] = Object.values(rows);\n        rows.forEach(r => { if (!(colName in r)) r[colName] = ''; });\n        window.renderConfig();\n    };\n\n    window.getPluginScrollable = function(target) {\n        if (!target) return null;\n        const pluginRoot = target.closest('#ti-ai-outer');\n        if (!pluginRoot) return null;\n        let node = target.nodeType === 1 ? target : target.parentElement;\n        while (node && node !== pluginRoot) {\n            const canScroll = node.classList && (\n                node.classList.contains('ti-scrollable-plugin') ||\n                node.classList.contains('ti-modal') ||\n                node.classList.contains('ti-chat-box') ||\n                node.classList.contains('ti-cfg-table-container') ||\n                node.classList.contains('ti-conf-body') ||\n                node.classList.contains('ti-table-wrap')\n            );\n            if (canScroll) {\n                const style = window.getComputedStyle(node);\n                const overflowY = style.overflowY || '';\n                const reallyScrollable = (overflowY === 'auto' || overflowY === 'scroll' || node.scrollHeight > node.clientHeight + 4);\n                if (reallyScrollable) return node;\n            }\n            node = node.parentElement;\n        }\n        const doc = document.scrollingElement || document.documentElement || document.body;\n        return (doc && doc.scrollHeight > doc.clientHeight + 4) ? doc : pluginRoot;\n    };\n\n    document.addEventListener('wheel', function(e) {\n        const pluginRoot = e.target.closest && e.target.closest('#ti-ai-outer');\n        if (!pluginRoot) return;\n        const scrollable = window.getPluginScrollable(e.target);\n        if (!scrollable || scrollable === pluginRoot) return;\n        const before = scrollable.scrollTop;\n        scrollable.scrollTop += e.deltaY;\n        if (scrollable.scrollTop !== before || e.deltaY !== 0) {\n            e.preventDefault();\n            e.stopPropagation();\n        }\n    }, { passive: false, capture: true });\n\n    document.addEventListener('touchmove', function(e) { \n        const isMobile = window.tiIsMobileLike ? window.tiIsMobileLike() : !!(window.matchMedia && window.matchMedia('(max-width: 900px)').matches);\n        if (isMobile) return; \/\/ su smartphone\/touch lascia sempre libero lo scorrimento verticale a un dito\n        const isModalOpen = document.querySelector('.ti-ov[style*=\"display: flex\"], .ti-ov[style*=\"display:flex\"], #ti-ai-loader-ov[style*=\"display: flex\"], #ti-ai-loader-ov[style*=\"display:flex\"]'); \n        if (isModalOpen) { \n            const t = e.target;\n            const scrollable = t && t.closest ? (t.closest('.ti-conf-body') || t.closest('.ti-table-wrap') || t.closest('.ti-chat-box') || t.closest('.ti-modal') || t.closest('.ti-cfg-table-container') || t.closest('.ti-scrollable-plugin') || t.closest('#help-bot-reply') || t.closest('#col-help-content') || t.closest('#ateco-res') || t.closest('#global-action-res') || t.closest('#fm-grid')) : null; \n            if (!scrollable) e.preventDefault(); \n        } \n    }, { passive: false });\n\n\n    window.tiIsPrivilegedCurrentUser = function() {\n        const roleNow = String(window.tiRole || '').toLowerCase();\n        const userNow = String(window.tiUser || '');\n        return (!!userNow && userNow === 'SSGlobalAdmin') || \/amministratore|configuratore|responsabile|globale\/.test(roleNow);\n    };\n\n    window.tiInformativePopupsEnabled = true;\n    window.tiHasStandardMainFormAccess = function() {\n        return !!String(window.tiUser || '').trim();\n    };\n    window.ensureStandardMainFormForCurrentUser = function() {\n        if (!window.tiHasStandardMainFormAccess || !window.tiHasStandardMainFormAccess()) return;\n        const ids = ['ti-msg','ti-send','ti-new','ti-copy','ti-file','ti-products-btn','ti-services-btn','ti-activities-btn','ti-kbd-btn','ti-mic','ti-cam'];\n        ids.forEach(function(id){\n            const el = gE(id);\n            if (!el) return;\n            el.disabled = false;\n            el.removeAttribute('disabled');\n            el.removeAttribute('aria-hidden');\n            if (el.style && el.style.display === 'none') el.style.display = '';\n            if (el.id === 'ti-msg') { el.readOnly = false; el.removeAttribute('readonly'); }\n        });\n        const wrap = document.querySelector('#ti-ai-outer .ti-input-wrap');\n        if (wrap) { wrap.style.display = ''; wrap.removeAttribute('aria-hidden'); }\n        const actions = document.querySelector('#ti-ai-outer .ti-actions-row');\n        if (actions) { actions.style.display = ''; actions.removeAttribute('aria-hidden'); }\n        document.querySelectorAll('#ti-ai-outer .ti-order-wrap,#ti-ai-outer .ti-order-table-shell,#ti-ai-outer .ti-order-grid-view,#ti-ai-outer .ti-order-confirm-host,#ti-ai-outer table.ti-prod-table').forEach(function(el){\n            el.style.display = '';\n            el.removeAttribute('aria-hidden');\n        });\n        document.querySelectorAll('#ti-ai-outer .ti-order-wrap button,#ti-ai-outer .ti-order-table-shell button,#ti-ai-outer .ti-order-grid-view button,#ti-ai-outer .ti-order-confirm-host button,#ti-ai-outer table.ti-prod-table button').forEach(function(btn){\n            btn.disabled = false;\n            btn.removeAttribute('disabled');\n            btn.removeAttribute('aria-hidden');\n        });\n    };\n\n    window.tiPlainTextFromHtml = function(html) {\n        try {\n            const tmp = document.createElement('div');\n            tmp.innerHTML = String(html == null ? '' : html)\n                .replace(\/<br\\s*\\\/?>\/gi, '\\n')\n                .replace(\/<\\\/p>\/gi, '\\n')\n                .replace(\/<\\\/div>\/gi, '\\n')\n                .replace(\/<\\\/li>\/gi, '\\n');\n            return (tmp.innerText || tmp.textContent || '').replace(\/\\n{3,}\/g, '\\n\\n').trim();\n        } catch(e) {\n            return String(html == null ? '' : html).replace(\/<[^>]+>\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n        }\n    };\n\n    window.tiCopyTextToClipboard = function(text, btn) {\n        text = String(text == null ? '' : text);\n        const done = function(ok){\n            if (!btn) return;\n            const old = btn.textContent;\n            btn.textContent = ok ? 'Copiato' : 'Errore copia';\n            setTimeout(function(){ btn.textContent = old || 'Copia testo'; }, 1200);\n        };\n        if (navigator.clipboard && navigator.clipboard.writeText) {\n            navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){\n                try { const ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.focus(); ta.select(); const ok=document.execCommand('copy'); document.body.removeChild(ta); done(ok); } catch(e){ done(false); }\n            });\n            return;\n        }\n        try { const ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.focus(); ta.select(); const ok=document.execCommand('copy'); document.body.removeChild(ta); done(ok); } catch(e){ done(false); }\n    };\n\n    window.tiSanitizePrivilegedPopupHtml = function(content) {\n        let html = String(content == null ? '' : content);\n        try {\n            if (\/\\[TABLE\\]|ti-order-wrap|ti-prod-table|ti-order-table-shell|ti-order-grid-view\/i.test(html)) {\n                let post = '';\n                if (\/\\[TABLE\\]\/i.test(html)) {\n                    const lastClose = html.lastIndexOf('[\/P]');\n                    if (lastClose >= 0) post = html.slice(lastClose + 4).trim();\n                    html = html.split('[TABLE]')[0] + (post ? '<br><br>' + post : '');\n                }\n                const tmp = document.createElement('div');\n                tmp.innerHTML = html;\n                tmp.querySelectorAll('.ti-order-wrap,.ti-order-table-shell,.ti-order-grid-view,.ti-order-toolbar,.ti-order-filterbar,.ti-order-confirm-host,table.ti-prod-table').forEach(function(el){ el.remove(); });\n                tmp.querySelectorAll('table').forEach(function(tbl){\n                    if (tbl.closest && tbl.closest('#ti-report-cnt')) return;\n                    const txt = (tbl.innerText || tbl.textContent || '').trim();\n                    if (\/voce|quantit|prezzo|totale|descrizione\/i.test(txt)) tbl.remove();\n                });\n                html = tmp.innerHTML;\n            }\n        } catch(e) {}\n        return html;\n    };\n\n    window.tiPopupPlaceholderMessage = function(title) {\n        const t = String(title || 'Comunicazione').replace(\/<[^>]+>\/g, '').trim();\n        return '\ud83d\udccc ' + (t || 'Comunicazione\/report') + ' aperto in popup separato. Usa il pulsante <b>Copia testo<\/b> nel popup per copiare il contenuto.';\n    };\n\n    window.showPrivilegedCommunicationPopup = function(content, title, opts) {\n        if (!window.tiIsPrivilegedCurrentUser || !window.tiIsPrivilegedCurrentUser()) return false;\n        const ov = gE('ti-admin-comm-ov');\n        const box = gE('ti-admin-comm-msg');\n        const titleEl = gE('ti-admin-comm-title');\n        const copyBtn = gE('ti-admin-comm-copy');\n        const closeBtn = gE('ti-admin-comm-close');\n        if (!ov || !box || !titleEl) return false;\n        const html = (window.tiSanitizePrivilegedPopupHtml ? window.tiSanitizePrivilegedPopupHtml(content) : String(content == null ? '' : content)).trim();\n        if (!html) return false;\n        titleEl.textContent = title || 'Comunicazione';\n        box.innerHTML = html;\n        const plain = window.tiPlainTextFromHtml ? window.tiPlainTextFromHtml(html) : String(html).replace(\/<[^>]+>\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n        box.setAttribute('data-copy-text', plain);\n        if (copyBtn) copyBtn.onclick = function(){ window.tiCopyTextToClipboard(plain, copyBtn); };\n        if (closeBtn) closeBtn.onclick = function(){ window.closeModal('ti-admin-comm-ov'); };\n        window.showPluginModal('ti-admin-comm-ov');\n        try { if (window.applyLayoutLanguage) window.applyLayoutLanguage(true); } catch(e) {}\n        window.keepUserCommunicationPopupsInFront();\n        setTimeout(window.keepUserCommunicationPopupsInFront, 0);\n        setTimeout(window.keepUserCommunicationPopupsInFront, 80);\n        return true;\n    };\n\n    window.tiIsFunctionalOrderContent = function(content) {\n        const s = String(content == null ? '' : content);\n        if (!s.trim()) return false;\n        return \/\\[TABLE\\]|\\[P\\]|ti-order-wrap|ti-prod-table|ti-order-table-shell|ti-order-grid-view|ti-order-confirm-host|confirmPendingOrder|Conferma ordine|CONFERMA ORDINE|tabella ordine\/i.test(s);\n    };\n\n    window.shouldShowPrivilegedCommunicationPopup = function(text, force) {\n        if (window.tiIsFunctionalOrderContent && window.tiIsFunctionalOrderContent(text)) return false;\n        if (force) return true;\n        if (!window.tiIsPrivilegedCurrentUser || !window.tiIsPrivilegedCurrentUser()) return false;\n        const plain = String(text == null ? '' : text).replace(\/<[^>]+>\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n        if (!plain) return false;\n        if (plain.length >= 260) return true;\n        return \/(report|riepilogo|comunicaz|avviso|warning|errore|database|configurazione|salvataggio|righe cancellate|modifica globale|anteprima|import|istruzioni|prenotaz|appuntament|ordine concluso|notifica|email)\/i.test(plain);\n    };\n\n    window.maybeShowPrivilegedCommunicationPopup = function(content, title, force) {\n        if (window.tiIsFunctionalOrderContent && window.tiIsFunctionalOrderContent(content)) return false;\n        const plain = window.tiPlainTextFromHtml ? window.tiPlainTextFromHtml(content) : String(content || '');\n        if (!window.shouldShowPrivilegedCommunicationPopup(plain, !!force)) return false;\n        return window.showPrivilegedCommunicationPopup(content, title || 'Comunicazione', {text: plain});\n    };\n\n    window.tiSanitizeUserVisibleUploadMessage636 = window.tiSanitizeUserVisibleUploadMessage636 || function(msg) {\n        var out = String(msg == null ? '' : msg);\n        var neutralUpload = \"Upload file non disponibile con il profilo corrente. Usa la procedura guidata prevista oppure richiedi l'abilitazione della sorgente file.\";\n        var neutralAttach = \"Upload e associazione file non disponibili con il profilo corrente. Usa la procedura guidata prevista oppure richiedi l'abilitazione della sorgente file.\";\n        out = out.replace(\/Errore\\s+aggiunta\\s+file\\s+da\\s+PC\\s*:\\s*Upload\\s+file\\s+disponibile\\s+solo\\s+per[^\\r\\n.<]*\/gi, 'Errore upload file: ' + neutralUpload);\n        out = out.replace(\/Upload\\s+file\\s+disponibile\\s+solo\\s+per[^\\r\\n.<]*\/gi, neutralUpload);\n        out = out.replace(\/Upload\\s+e\\s+associazione\\s+file\\s+disponibili\\s+solo\\s+per[^\\r\\n.<]*\/gi, neutralAttach);\n        out = out.replace(\/Associazione\\s+file\\s+disponibile\\s+solo\\s+per[^\\r\\n.<]*\/gi, 'Associazione file non disponibile con il profilo corrente.');\n        if (\/upload|file|associazione\/i.test(out)) {\n            out = out.replace(\/\\bSSGlobalAdmin\\b|\\bglobal\\s*admin\\b|\\bglobaladmin\\b\/gi, 'profilo abilitato');\n            out = out.replace(\/\\bamministratore\\b|\\bamministratori\\b|\\badministrator\\b\/gi, 'profilo abilitato');\n            out = out.replace(\/profilo abilitato\\s*,\\s*profilo abilitato(?:\\s*,\\s*profilo abilitato)*(?:\\s*o\\s*profilo abilitato)?\/gi, 'profilo abilitato');\n        }\n        return out;\n    };\n\n\n    window.tiResetAlertModalStyle668 = function() {\n        try {\n            const modal = gE('ti-alert-modal') || (gE('ti-alert-ov') ? gE('ti-alert-ov').querySelector('.ti-modal') : null);\n            const msgBox = gE('ti-alert-msg');\n            if (modal) {\n                modal.classList.remove('ti-config-warning-modal-668');\n                modal.style.width = '300px';\n                modal.style.maxWidth = '';\n                modal.style.minWidth = '';\n                modal.style.padding = '20px';\n                modal.style.textAlign = 'center';\n                modal.style.maxHeight = '90vh';\n                modal.style.overflowY = 'auto';\n            }\n            if (msgBox) {\n                msgBox.classList.remove('ti-config-warning-msg-668');\n                msgBox.style.fontSize = '15px';\n                msgBox.style.lineHeight = '1.4';\n                msgBox.style.marginBottom = '20px';\n                msgBox.style.maxHeight = '';\n                msgBox.style.overflowY = '';\n                msgBox.style.textAlign = '';\n                msgBox.style.fontWeight = 'normal';\n                msgBox.style.wordBreak = '';\n            }\n        } catch(e) {}\n    };\n    window.tiApplyConfigWarningAlertStyle668 = function() {\n        try {\n            const modal = gE('ti-alert-modal') || (gE('ti-alert-ov') ? gE('ti-alert-ov').querySelector('.ti-modal') : null);\n            const msgBox = gE('ti-alert-msg');\n            if (modal) {\n                modal.classList.add('ti-config-warning-modal-668');\n                modal.style.width = 'min(96vw, 780px)';\n                modal.style.maxWidth = '780px';\n                modal.style.minWidth = 'min(96vw, 360px)';\n                modal.style.padding = '14px 16px';\n                modal.style.textAlign = 'left';\n                modal.style.maxHeight = '88vh';\n                modal.style.overflowY = 'auto';\n            }\n            if (msgBox) {\n                msgBox.classList.add('ti-config-warning-msg-668');\n                msgBox.style.fontSize = '12px';\n                msgBox.style.lineHeight = '1.25';\n                msgBox.style.marginBottom = '12px';\n                msgBox.style.maxHeight = '72vh';\n                msgBox.style.overflowY = 'auto';\n                msgBox.style.textAlign = 'left';\n                msgBox.style.fontWeight = '600';\n                msgBox.style.wordBreak = 'normal';\n                const ul = msgBox.querySelector('ul');\n                if (ul) { ul.style.marginTop = '7px'; ul.style.paddingLeft = '16px'; }\n                msgBox.querySelectorAll('li').forEach(function(li){ li.style.marginBottom = '8px'; li.style.paddingLeft = '2px'; });\n            }\n        } catch(e) {}\n    };\n\n    window.tiAlert = function(msg) {\n        try { if (window.tiResetAlertModalStyle668) window.tiResetAlertModalStyle668(); } catch(e) {}\n        if (window.tiSanitizeUserVisibleUploadMessage636) msg = window.tiSanitizeUserVisibleUploadMessage636(msg);\n        if (window.tiCleanVisibleEscapedChars) msg = window.tiCleanVisibleEscapedChars(msg);\n        gE('ti-alert-msg').innerHTML = msg;\n        try { if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(gE('ti-alert-msg')); } catch(e) {}\n        if (gE('ti-alert-copy')) { gE('ti-alert-copy').style.display = 'block'; gE('ti-alert-copy').onclick = window.copyAlertText; }\n        gE('ti-alert-yes').style.display = 'none';\n        gE('ti-alert-no').innerText = 'OK';\n        gE('ti-alert-no').onclick = () => { window.closeModal('ti-alert-ov'); };\n        window.showPluginModal('ti-alert-ov');\n        try { if (window.applyLayoutLanguage) window.applyLayoutLanguage(true); } catch(e) {}\n        window.keepUserCommunicationPopupsInFront();\n        setTimeout(window.keepUserCommunicationPopupsInFront, 0);\n        setTimeout(window.keepUserCommunicationPopupsInFront, 80);\n    };\n\n    window.tiConfirm = function(msg, callback) {\n        try { if (window.tiResetAlertModalStyle668) window.tiResetAlertModalStyle668(); } catch(e) {}\n        if (window.tiSanitizeUserVisibleUploadMessage636) msg = window.tiSanitizeUserVisibleUploadMessage636(msg);\n        if (window.tiCleanVisibleEscapedChars) msg = window.tiCleanVisibleEscapedChars(msg);\n        gE('ti-alert-msg').innerHTML = msg;\n        try { if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(gE('ti-alert-msg')); } catch(e) {}\n        if (gE('ti-alert-copy')) { gE('ti-alert-copy').style.display = 'block'; gE('ti-alert-copy').onclick = window.copyAlertText; }\n        gE('ti-alert-yes').style.display = 'block';\n        gE('ti-alert-no').innerText = 'ANNULLA';\n        gE('ti-alert-yes').onclick = () => {\n            window.closeModal('ti-alert-ov');\n            if (typeof callback === 'function') callback();\n        };\n        gE('ti-alert-no').onclick = () => { window.closeModal('ti-alert-ov'); };\n        window.showPluginModal('ti-alert-ov');\n        try { if (window.applyLayoutLanguage) window.applyLayoutLanguage(true); } catch(e) {}\n        window.keepUserCommunicationPopupsInFront();\n        setTimeout(window.keepUserCommunicationPopupsInFront, 0);\n        setTimeout(window.keepUserCommunicationPopupsInFront, 80);\n    };\n\n\n    window.tiConfirmYesNoAction = function(msg, yesCb, noCb, yesLabel = 'OK', noLabel = 'ANNULLA') {\n        try { if (window.tiResetAlertModalStyle668) window.tiResetAlertModalStyle668(); } catch(e) {}\n        gE('ti-alert-msg').innerHTML = msg;\n        if (gE('ti-alert-copy')) { gE('ti-alert-copy').style.display = 'block'; gE('ti-alert-copy').onclick = window.copyAlertText; }\n        gE('ti-alert-yes').style.display = 'block';\n        gE('ti-alert-yes').innerText = yesLabel;\n        gE('ti-alert-no').innerText = noLabel;\n        gE('ti-alert-yes').onclick = () => {\n            window.closeModal('ti-alert-ov');\n            if (typeof yesCb === 'function') yesCb();\n        };\n        gE('ti-alert-no').onclick = () => {\n            window.closeModal('ti-alert-ov');\n            if (typeof noCb === 'function') noCb();\n        };\n        window.showPluginModal('ti-alert-ov');\n        try { if (window.applyLayoutLanguage) window.applyLayoutLanguage(true); } catch(e) {}\n        window.keepUserCommunicationPopupsInFront();\n        setTimeout(window.keepUserCommunicationPopupsInFront, 0);\n        setTimeout(window.keepUserCommunicationPopupsInFront, 80);\n    };\n\n    window.tiBatchAIGenState = { active: false, pendingConfirm: false, generated: [] };\n\n    window.refreshCurrentConfigDb = function(dbValue) {\n        const dbSel = gE('ti-ditta');\n        const db = dbValue || (dbSel ? dbSel.value : '');\n        if (!db || db === 'NEW_DB') return Promise.resolve(false);\n        const fdDb = window.buildAjaxFormData('ti_ai_get_db_data', {db: db});\n        return window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fdDb, tiNoLongProcessSignal:true, tiShowTableReadWait:true, tiTableReadTitle:'\u23f3 Lettura tabelle configurazione', tiTableReadDetail:'Sto aggiornando le tabelle della ditta selezionata.', tiTableReadItem: db}).then(function(dbRes) {\n            if (dbRes && dbRes.success && dbRes.data) {\n                window.currentDbData = dbRes.data;\n                if (window.renderConfig) window.renderConfig();\n                try { if (window.showActivityFromQrIfRequested) window.showActivityFromQrIfRequested(); } catch(e) {}\n                return true;\n            }\n            return false;\n        }).catch(function(){ return false; });\n    };\n\n    window.confirmCancelBatchAIGen = function() {\n        const st = window.tiBatchAIGenState || {};\n        if (!st.active || st.pendingConfirm) return false;\n        const generated = Array.isArray(st.generated) ? st.generated : [];\n        const generatedCount = generated.length;\n        st.pendingConfirm = true;\n        const lp = window.tiLongProcessState || {};\n        lp.cancelled = true;\n        try { if (lp.controller) lp.controller.abort(); } catch(e) {}\n        window.closeModal('ti-ai-loader-ov');\n\n        const finishKeep = function() {\n            st.active = false;\n            st.pendingConfirm = false;\n            if (window.clearLongAIProcess) window.clearLongAIProcess();\n            const msg = generatedCount > 0\n                ? 'Generazione interrotta. Le associazioni gi\u00e0 generate sono state mantenute. Associazioni mantenute: ' + generatedCount + '.'\n                : 'Generazione interrotta. Nessuna associazione era stata ancora generata.';\n            const rb = gE('ti-conf-ai-reply');\n            if (rb) { rb.style.display = 'block'; rb.innerHTML = window.escapeHtml(msg); }\n            window.refreshCurrentConfigDb(st.db).then(function(){ window.tiAlert(msg); });\n        };\n\n        const finishRevert = function() {\n            if (!generatedCount) { finishKeep(); return; }\n            const fd = window.buildAjaxFormData('ti_ai_config_action', {\n                db: st.db || (gE('ti-ditta') ? gE('ti-ditta').value : ''),\n                mode: 'batch_ai_images_revert',\n                items_json: JSON.stringify(generated)\n            });\n            const rb = gE('ti-conf-ai-reply');\n            if (rb) { rb.style.display = 'block'; rb.innerHTML = 'Annullamento associazioni generate in corso...'; }\n            window.postFormDataJsonSafe((window.tiAjaxUrl || window.tiUrl), fd, {tiNoLongProcessSignal:true}).then(function(res){\n                st.active = false;\n                st.pendingConfirm = false;\n                if (window.clearLongAIProcess) window.clearLongAIProcess();\n                const removed = res && res.data && typeof res.data.removed !== 'undefined' ? parseInt(res.data.removed, 10) || 0 : 0;\n                const msg = 'Generazione interrotta. Associazioni generate annullate: ' + removed + '.';\n                if (rb) rb.innerHTML = window.escapeHtml(msg);\n                window.refreshCurrentConfigDb(st.db).then(function(){ window.tiAlert(msg); });\n            }).catch(function(err){\n                st.active = false;\n                st.pendingConfirm = false;\n                if (window.clearLongAIProcess) window.clearLongAIProcess();\n                const msg = 'Generazione interrotta. Non sono riuscito ad annullare automaticamente le associazioni gi\u00e0 generate. Verifica la configurazione. Dettaglio: ' + ((err && err.message) ? err.message : 'errore sconosciuto');\n                if (rb) rb.innerHTML = window.escapeHtml(msg);\n                window.tiAlert(msg);\n            });\n        };\n\n        const question = generatedCount > 0\n            ? 'Sono gi\u00e0 state generate ' + generatedCount + ' associazioni immagine.<br><br>Vuoi mantenerle oppure annullarle?'\n            : 'Vuoi uscire dalla generazione foto AI?<br><br>Nessuna associazione risulta ancora generata.';\n        if (generatedCount > 0 && typeof window.tiConfirmYesNoAction === 'function') {\n            window.tiConfirmYesNoAction(question, finishKeep, finishRevert, 'MANTIENI ASSOCIAZIONI', 'ANNULLA ASSOCIAZIONI');\n        } else if (typeof window.tiConfirmYesNoAction === 'function') {\n            window.tiConfirmYesNoAction(question, finishKeep, function(){\n                st.active = false;\n                st.pendingConfirm = false;\n                if (window.clearLongAIProcess) window.clearLongAIProcess();\n                const rb = gE('ti-conf-ai-reply');\n                if (rb) { rb.style.display = 'block'; rb.innerHTML = 'Generazione foto AI interrotta.'; }\n            }, 'ESCI', 'CONTINUA');\n        } else {\n            finishKeep();\n        }\n        return true;\n    };\n\n\n    window.openFileManager = function(tbl, idx, key) {\n        if (Date.now && Date.now() < (window.tiSuppressFileManagerUntil || 0)) return false;\n        if (!window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tbl] || !window.currentDbData.Tabelle[tbl][idx]) return false;\n        let val = window.currentDbData.Tabelle[tbl][idx][key] || '';\n        window.targetUploadContext = { tbl, idx, key, val };\n        const fmRowObj = (window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[tbl]) ? window.currentDbData.Tabelle[tbl][idx] : null;\n        const fmDescBox = gE('fm-element-description');\n        if (fmDescBox) {\n            try {\n                const recordLabel = window.getRecordLabelForUpload ? window.getRecordLabelForUpload(tbl, fmRowObj, tbl) : (tbl || 'record');\n                let fullDesc = window.extractPromptFromRow ? window.extractPromptFromRow(fmRowObj) : '';\n                fullDesc = String(fullDesc || '').trim();\n                const shownDesc = fullDesc || recordLabel || 'Elemento senza descrizione disponibile';\n                const safeTbl = window.escapeHtml(tbl || '');\n                const safeKey = window.escapeHtml(key || '');\n                const safeDesc = window.escapeHtml(shownDesc);\n                const safeLabel = window.escapeHtml(recordLabel || shownDesc || '');\n                const rowNum = (parseInt(idx, 10) || 0) + 1;\n                fmDescBox.style.display = 'block';\n                fmDescBox.innerHTML = '<div style=\"font-weight:800;color:#93c5fd;margin-bottom:4px;\">Elemento cui sono associati i file<\/div>'\n                    + '<div><b>Descrizione:<\/b> ' + safeDesc + '<\/div>'\n                    + (safeLabel && safeLabel !== safeDesc ? '<div style=\"font-size:12px;color:#bfdbfe;margin-top:2px;\"><b>Voce:<\/b> ' + safeLabel + '<\/div>' : '')\n                    + '<div style=\"font-size:11px;color:#94a3b8;margin-top:4px;\">Tabella: ' + safeTbl + ' \u00b7 Riga rif.: ' + rowNum + ' \u00b7 Campo: ' + safeKey + '<\/div>';\n            } catch(e) {\n                fmDescBox.style.display = 'none';\n                fmDescBox.innerHTML = '';\n            }\n        }\n        let files = window.tiTokenizeFileRefs666 ? window.tiTokenizeFileRefs666(val) : String(val || '').split(\/[,;\\r\\n]+\/).map(f => f.trim()).filter(f=>f);\n        let html = '';\n        files.forEach((f, i) => {\n            let rowObj = (window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[tbl]) ? window.currentDbData.Tabelle[tbl][idx] : null;\n            let dbForMeta676 = gE('ti-ditta') ? (gE('ti-ditta').value || '') : '';\n            let recordLabelForMeta676 = window.getRecordLabelForUpload(tbl, rowObj, tbl);\n            let url = window.getSafePreviewUrl(f, dbForMeta676, tbl, window.targetUploadContext && window.targetUploadContext.key ? window.targetUploadContext.key : '', recordLabelForMeta676);\n            let meta = window.tiFileMetaFromRef666 ? window.tiFileMetaFromRef666(f, url) : {name:f, ext:(String(f).match(\/\\.[a-z0-9]{1,8}$\/i)||[''])[0] || 'n\/d', isImg:\/\\.(jpg|jpeg|png|webp|gif)$\/i.test(f), isPdf:\/\\.pdf$\/i.test(f)};\n            let safeUrl = window.escapeHtml ? window.escapeHtml(url || '') : String(url || '');\n            let safeRef = window.escapeHtml ? window.escapeHtml(f || '') : String(f || '');\n            let safeName = window.escapeHtml ? window.escapeHtml(meta.name || f || 'file') : String(meta.name || f || 'file');\n            let safeExt = window.escapeHtml ? window.escapeHtml(meta.ext || 'n\/d') : String(meta.ext || 'n\/d');\n            let openArg = encodeURIComponent(url || '');\n            let preview = '';\n            if (meta.isImg) {\n                preview = `<div class=\"ti-file-preview-666\"><img decoding=\"async\" src=\"${safeUrl}\" alt=\"Preview ${safeName}\" onclick=\"window.open(decodeURIComponent('${openArg}'), '_blank')\"><\/div>`;\n            } else if (meta.isPdf) {\n                preview = `<div class=\"ti-file-preview-666 ti-file-preview-pdf-666\"><iframe src=\"${safeUrl}#toolbar=0&navpanes=0\" title=\"Preview ${safeName}\" loading=\"lazy\"><\/iframe><\/div>`;\n            } else {\n                preview = `<div class=\"ti-file-preview-666 file-icon\" onclick=\"window.open(decodeURIComponent('${openArg}'), '_blank')\">\ud83d\udcc4<\/div>`;\n            }\n            const primaryBadge = (i === 0) ? `<div style=\"margin-top:4px;padding:3px 5px;border-radius:999px;background:#22c55e;color:#052e16;font-size:10px;font-weight:800;\">Principale layout<\/div>` : `<button type=\"button\" onclick=\"window.setPrimaryFileInManager(${i})\" style=\"margin-top:4px;font-size:9px;padding:2px 5px;border-radius:999px;background:#334155;color:#e5e7eb;border:1px solid #64748b;\">Rendi principale<\/button>`;\n            html += `<div class=\"ti-file-item\" style=\"position:relative;${i === 0 ? 'border-color:#22c55e;box-shadow:0 0 0 1px rgba(34,197,94,.45);' : ''}\">\n                        ${preview}\n                        ${primaryBadge}\n                        <div class=\"ti-file-name\" title=\"${safeRef}\" onclick=\"window.open(decodeURIComponent('${openArg}'), '_blank')\" style=\"cursor:pointer;\">${safeName}<\/div>\n                        <div class=\"ti-file-meta-666\"><b>Tipo:<\/b> ${safeExt}<\/div>\n                        <div class=\"ti-file-meta-666\"><b>Dimensione:<\/b> <span data-ti-file-size-url=\"${safeUrl}\" data-ti-file-ref=\"${safeRef}\" data-ti-file-db=\"${window.escapeHtml ? window.escapeHtml(dbForMeta676 || '') : String(dbForMeta676 || '')}\" data-ti-file-tbl=\"${window.escapeHtml ? window.escapeHtml(tbl || '') : String(tbl || '')}\" data-ti-file-record=\"${window.escapeHtml ? window.escapeHtml(recordLabelForMeta676 || '') : String(recordLabelForMeta676 || '')}\">calcolo...<\/span><\/div>\n                        <button type=\"button\" class=\"ti-file-del\" onclick=\"window.removeFileFromManager(${i})\">X<\/button>\n                        <button type=\"button\" onclick=\"window.open(decodeURIComponent('${openArg}'), '_blank')\" style=\"margin-top:2px; font-size:9px; padding:2px; border-radius:3px;\">Apri<\/button>\n                     <\/div>`;\n        });\n        if(files.length === 0) html = '<p style=\"color:#aaa; font-size:12px; grid-column: 1 \/ -1;\">Nessun file presente.<\/p>';\n        const pathBox = gE('fm-storage-path');\n        if (pathBox) {\n            const showPath = window.isAdminConfiguratorUser && window.isAdminConfiguratorUser();\n            const storagePath = window.getStoragePathForTable(gE('ti-ditta') ? gE('ti-ditta').value : '', tbl);\n            pathBox.style.display = showPath && storagePath ? 'block' : 'none';\n            if (showPath && storagePath) pathBox.innerHTML = '<b>Path storage file:<\/b><br>' + window.escapeHtml(storagePath);\n        }\n        gE('fm-grid').innerHTML = html;\n        try { if (window.tiRefreshFileManagerMeta666) setTimeout(window.tiRefreshFileManagerMeta666, 30); } catch(e) {}\n        try {\n            const fmOv = gE('ti-file-manager-ov');\n            if (fmOv) {\n                fmOv.dataset.tbl = tbl || '';\n                fmOv.dataset.idx = String(idx);\n                fmOv.dataset.key = key || '';\n                fmOv.setAttribute('data-ti-config-child-modal', '1');\n            }\n        } catch(e) {}\n        window.openModal('ti-file-manager-ov');\n        try {\n            const fmOv = gE('ti-file-manager-ov');\n            if (fmOv) {\n                if (window.preparePluginModal) window.preparePluginModal(fmOv);\n                fmOv.style.setProperty('display', 'flex', 'important');\n                fmOv.style.setProperty('z-index', '2147483500', 'important');\n                fmOv.removeAttribute('aria-hidden');\n                fmOv.setAttribute('data-ti-force-front', '1');\n                if (window.updatePluginScrollLock) window.updatePluginScrollLock();\n            }\n        } catch(e) {}\n    };\n\n    window.setPrimaryFileInManager = function(fileIndex) {\n        let ctx = window.targetUploadContext;\n        if (!ctx) return;\n        let val = window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] || '';\n        let files = window.tiTokenizeFileRefs666 ? window.tiTokenizeFileRefs666(val) : String(val || '').split(\/[,;\\r\\n]+\/).map(f => f.trim()).filter(f=>f);\n        if (fileIndex <= 0 || fileIndex >= files.length) return;\n        const picked = files.splice(fileIndex, 1)[0];\n        files.unshift(picked);\n        window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] = files.join(', ');\n        if(!window.modifiedFields) window.modifiedFields = new Set();\n        window.modifiedFields.add(`${ctx.tbl}-${ctx.idx}-${ctx.key}`); if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        window.renderConfig();\n        window.openFileManager(ctx.tbl, ctx.idx, ctx.key);\n        if (window.tiAlert) window.tiAlert('File principale layout impostato. Ricorda di salvare la configurazione.');\n    };\n\n    window.removeFileFromManager = function(fileIndex) {\n        let ctx = window.targetUploadContext;\n        if (!ctx || !window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[ctx.tbl] || !window.currentDbData.Tabelle[ctx.tbl][ctx.idx]) return;\n        const ctxSnapshot = { tbl: ctx.tbl, idx: ctx.idx, key: ctx.key };\n        let val = window.currentDbData.Tabelle[ctxSnapshot.tbl][ctxSnapshot.idx][ctxSnapshot.key] || '';\n        let filesBeforeDelete = window.tiTokenizeFileRefs666 ? window.tiTokenizeFileRefs666(val) : String(val || '').split(\/[,;\\r\\n]+\/).map(f => f.trim()).filter(f=>f);\n        const fileName = filesBeforeDelete[fileIndex] || '';\n        if (!fileName) { window.tiAlert('File non selezionato.'); return; }\n        let filesAfterDelete = filesBeforeDelete.slice();\n        filesAfterDelete.splice(fileIndex, 1);\n        const newVal = filesAfterDelete.join(', ');\n        const rowObj = (window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[ctxSnapshot.tbl]) ? window.currentDbData.Tabelle[ctxSnapshot.tbl][ctxSnapshot.idx] : null;\n        const applyLocalFileRemoval = function(ctx, msg, markUnsaved) {\n            if (!window.currentDbData.Tabelle) window.currentDbData.Tabelle = {};\n            if (!window.currentDbData.Tabelle[ctx.tbl]) window.currentDbData.Tabelle[ctx.tbl] = [];\n            if (!window.currentDbData.Tabelle[ctx.tbl][ctx.idx]) window.currentDbData.Tabelle[ctx.tbl][ctx.idx] = {};\n            window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] = newVal;\n            try { window.currentDbViewData = null; } catch(e) {}\n            try { if (window.syncConfigTableAfterRowMutation) window.syncConfigTableAfterRowMutation(ctx.tbl); } catch(e) {}\n            try {\n                var cssEsc = window.CSS && CSS.escape ? CSS.escape : function(v){ return String(v).replace(\/([\\\"'\\[\\]#.:>+~*^$|=!])\/g, '\\\\$1'); };\n                document.querySelectorAll('#ti-conf-ov [data-tbl=\"' + cssEsc(ctx.tbl) + '\"][data-idx=\"' + cssEsc(ctx.idx) + '\"][data-key=\"' + cssEsc(ctx.key) + '\"]').forEach(function(inp){ if (inp) { inp.value = newVal; inp.setAttribute('data-ti-last-file-removal-676', String(Date.now ? Date.now() : new Date().getTime())); } });\n            } catch(e) {}\n            if (markUnsaved) {\n                if(!window.modifiedFields) window.modifiedFields = new Set();\n                window.modifiedFields.add(`${ctx.tbl}-${ctx.idx}-${ctx.key}`);\n                if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n            }\n            window.renderConfig();\n            window.openFileManager(ctx.tbl, ctx.idx, ctx.key);\n            if (window.tiAlert && msg) window.tiAlert(msg);\n        };\n        window.tiConfirm(\"Rimuovere questa associazione file dal record?\\n\\nIl riferimento verr\u00e0 tolto dalla configurazione corrente. Premi Salva Modifiche per confermare.\", function() {\n            let ctx = ctxSnapshot;\n            try {\n                window.pendingLinkedFileRemovals = window.pendingLinkedFileRemovals || [];\n                window.pendingLinkedFileRemovals.push({\n                    db: (gE('ti-ditta') ? (gE('ti-ditta').value || '') : ''),\n                    tbl: ctx.tbl,\n                    idx: ctx.idx,\n                    key: ctx.key,\n                    file: fileName,\n                    removed_at: new Date().toISOString()\n                });\n            } catch(e) {}\n            applyLocalFileRemoval(ctx, 'Associazione file rimossa dalla configurazione corrente. Premi Salva Modifiche per confermare la modifica. Il file fisico nello storage non viene cancellato automaticamente da questa X rossa.', true);\n        });\n    };\n\n    window.tiCloseFileManagerForAction = function(actionName) {\n        try {\n            window.__tiFileManagerExplicitClose337 = Date.now ? Date.now() : new Date().getTime();\n            window.__tiFileManagerOpening337 = 0;\n            window.__tiFileManagerAction340 = actionName || 'action';\n        } catch(e) {}\n        try { if (window.closeModal) window.closeModal('ti-file-manager-ov'); } catch(e) {}\n        try {\n            const ov = gE('ti-file-manager-ov');\n            if (ov) {\n                ov.style.setProperty('display', 'none', 'important');\n                ov.setAttribute('aria-hidden', 'true');\n                ov.classList.remove('ti-modal-active','ti-ov-active','is-active','active','show');\n            }\n        } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n    };\n\n    window.chooseLocalManager = function() {\n        if (window.tiOpenDirectUpload616) return window.tiOpenDirectUpload616(window.targetUploadContext || 'manager');\n        return false;\n    };\n\n    window.getCompanyActivity = function() {\n        try {\n            if (!window.currentDbData || !window.currentDbData.Tabelle) return '';\n            const cfgRows = window.currentDbData.Tabelle.Config || window.currentDbData.Tabelle.config || [];\n            const row = Array.isArray(cfgRows) && cfgRows.length ? cfgRows[0] : null;\n            if (!row) return '';\n            return row['Attivita'] || row['Attivit\u00e0'] || row['Attivita\\u0300'] || '';\n        } catch(e) { return ''; }\n    };\n\n    window.getInfoLinksForAIGen = function() {\n        try {\n            if (!window.currentDbData || !window.currentDbData.Tabelle) return [];\n            const tables = window.currentDbData.Tabelle;\n            const infoRows = tables.Informazioni || tables.informazioni || [];\n            const links = [];\n            (Array.isArray(infoRows) ? infoRows : []).forEach(row => {\n                if (!row || typeof row !== 'object') return;\n                Object.values(row).forEach(val => {\n                    if (typeof val !== 'string') return;\n                    const matches = val.match(\/https?:\\\/\\\/[^\\s<>'\"]+\/gi);\n                    if (matches) matches.forEach(u => { if (!links.includes(u)) links.push(u); });\n                });\n            });\n            return links.slice(0, 5);\n        } catch(e) { return []; }\n    };\n\n    window.buildAIGenPrompt = function(descriptionText) {\n        const parts = [];\n        const activity = (window.getCompanyActivity() || '').trim();\n        const desc = (descriptionText || '').trim();\n        const infoLinks = window.getInfoLinksForAIGen();\n        if (activity) parts.push('Attivit\u00e0 ditta: ' + activity);\n        if (infoLinks.length) parts.push('Verifica anzitutto questi link informazioni del database: ' + infoLinks.join(' | '));\n        if (desc) parts.push('Descrizione da associare: ' + desc);\n        return parts.join('\\n');\n    };\n\n    window.chooseAIManager = function() {\n        const ctx = window.targetUploadContext;\n        if (!ctx || !ctx.tbl || ctx.idx === undefined || !ctx.key) {\n            if (window.tiAlert) window.tiAlert('Gestione file associati: record\/campo non disponibile. Riapri la form dalla tabella.');\n            return false;\n        }\n        let row = null;\n        try {\n            if (window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[ctx.tbl]) {\n                row = window.currentDbData.Tabelle[ctx.tbl][ctx.idx] || null;\n            }\n        } catch(e) {}\n        if (typeof window.populateAIGenPrompt === 'function') window.populateAIGenPrompt(row);\n        else if (gE('ai-gen-prompt')) gE('ai-gen-prompt').value = window.buildAIGenPrompt ? window.buildAIGenPrompt(row ? (row.Descrizione || row.Prodotto || row.Servizio || row.Nome || '') : '') : '';\n        if (gE('ai-gen-detail')) gE('ai-gen-detail').value = '';\n        if (gE('ai-gen-res')) gE('ai-gen-res').style.display = 'none';\n        if (gE('ai-gen-img')) gE('ai-gen-img').src = '';\n        if (gE('ai-gen-btn')) {\n            gE('ai-gen-btn').disabled = false;\n            gE('ai-gen-btn').innerText = 'Genera Immagine';\n            gE('ai-gen-btn').style.display = 'block';\n        }\n        window.currentGenFilename = null;\n        try { window.openModal('ti-ai-gen-ov'); } catch(e) {}\n        try {\n            const fmOv = gE('ti-file-manager-ov');\n            if (fmOv) {\n                fmOv.style.setProperty('display', 'flex', 'important');\n                fmOv.style.setProperty('z-index', '2147483645', 'important');\n                fmOv.removeAttribute('aria-hidden');\n            }\n            const aiOv = gE('ti-ai-gen-ov');\n            if (aiOv) {\n                aiOv.setAttribute('data-ti-config-child-modal', '1');\n                aiOv.style.setProperty('display', 'flex', 'important');\n                aiOv.style.setProperty('z-index', '2147483646', 'important');\n                aiOv.removeAttribute('aria-hidden');\n                if (window.preparePluginModal) window.preparePluginModal(aiOv);\n            }\n            if (window.updatePluginScrollLock) window.updatePluginScrollLock();\n            setTimeout(function(){ try { if (gE('ai-gen-prompt')) gE('ai-gen-prompt').focus({preventScroll:true}); } catch(e) {} }, 60);\n        } catch(e) {}\n        return false;\n    };\n\n    window.syncLoginDbSelect = function() {\n        try {\n            const loginDb = gE('l-db');\n            const mainDb = gE('ti-ditta');\n            const hiddenDb = gE('l-db-hidden');\n            if (!loginDb) return;\n            if (mainDb && mainDb.options && mainDb.options.length) {\n                loginDb.innerHTML = '';\n                const first = document.createElement('option');\n                first.value = '';\n                first.textContent = '-- Scegli una ditta --';\n                loginDb.appendChild(first);\n                Array.from(mainDb.options || []).forEach(function(opt){\n                if (!opt || opt.disabled) return;\n                const v = String(opt.value || '');\n                if (v === 'NEW_DB') return;\n                const o = document.createElement('option');\n                o.value = v;\n                o.textContent = opt.textContent || v || '-- Scegli una ditta --';\n                Array.from(opt.attributes || []).forEach(function(a){\n                    if (a && a.name && a.name.indexOf('data-') === 0) o.setAttribute(a.name, a.value);\n                });\n                loginDb.appendChild(o);\n                });\n            }\n            let target = (mainDb && mainDb.value && mainDb.value !== 'NEW_DB') ? mainDb.value : '';\n            if (!target) target = window.tiForced || '';\n            if (!target) {\n                try { target = localStorage.getItem('ti_saved_db') || ''; } catch(e) {}\n            }\n            if (target && Array.from(loginDb.options).some(function(o){ return o.value === target; })) loginDb.value = target;\n            else if (loginDb.options.length === 2 && !loginDb.options[0].value) loginDb.selectedIndex = 1;\n            const hasRealOptions = Array.from(loginDb.options).some(function(o){ return o.value; });\n            const shouldShow = !window.tiForced && hasRealOptions;\n            loginDb.style.display = shouldShow ? 'block' : 'none';\n            if (hiddenDb) hiddenDb.value = (loginDb && loginDb.value) ? loginDb.value : (target || hiddenDb.value || '');\n            if (!loginDb.dataset.tiHiddenSyncBound) {\n                loginDb.dataset.tiHiddenSyncBound = '1';\n                loginDb.addEventListener('change', function(){ const h = gE('l-db-hidden'); if (h) h.value = loginDb.value || ''; }, false);\n            }\n        } catch(e) {}\n    };\n\n    window.getLoginDbValue = function() {\n        const modalDb = gE('l-db');\n        const hiddenDb = gE('l-db-hidden');\n        const mainDb = gE('ti-ditta');\n        let v = '';\n        if (modalDb && modalDb.value && modalDb.value !== 'NEW_DB') v = modalDb.value;\n        if (!v && hiddenDb && hiddenDb.value && hiddenDb.value !== 'NEW_DB') v = hiddenDb.value;\n        if (!v && mainDb && mainDb.value && mainDb.value !== 'NEW_DB') v = mainDb.value;\n        if (!v && window.tiForced) v = window.tiForced;\n        if (!v) {\n            try { v = localStorage.getItem('ti_saved_db') || ''; } catch(e) {}\n        }\n        return v;\n    };\n\n    window.tiCloseDirectLoginFallback = function() {\n        try {\n            const dl = gE('ti-mobile-direct-login');\n            if (!dl) return;\n            dl.classList.remove('ti-direct-login-visible');\n            dl.classList.remove('ti-direct-login-manual-open');\n            dl.style.display = 'none';\n            dl.setAttribute('aria-hidden', 'true');\n        } catch(e) {}\n    };\n\n    window.revealDirectLoginFallback = function(reason, detail) {\n        try {\n            const dl = gE('ti-mobile-direct-login');\n            if (!dl) return;\n            dl.classList.add('ti-direct-login-visible');\n            dl.classList.add('ti-direct-login-manual-open');\n            dl.style.display = '';\n            dl.removeAttribute('aria-hidden');\n            const modalUser = gE('l-user');\n            const modalPass = gE('l-pass');\n            const directUser = gE('ti-direct-login-user');\n            const directPass = gE('ti-direct-login-pass');\n            const directDb = gE('ti-direct-login-db');\n            const directBtn = dl.querySelector('button[type=\"submit\"], .ti-mobile-login-submit');\n            if (directUser && modalUser && modalUser.value) directUser.value = modalUser.value;\n            if (directPass && modalPass && modalPass.value) directPass.value = modalPass.value;\n            const dbVal = window.getLoginDbValue ? window.getLoginDbValue() : '';\n            if (directDb && dbVal) directDb.value = dbVal;\n            const box = gE('ti-direct-login-fallback-alert');\n            if (box) {\n                const baseMsg = 'Accesso tramite tasto Accedi non riuscito. Utilizza Accesso diretto Shop & Service per completare il login.';\n                box.textContent = detail ? (baseMsg + ' Dettaglio: ' + detail) : baseMsg;\n                box.style.display = 'block';\n            }\n            if (window.closeModal) window.closeModal('ti-login-ov');\n            try { window.tiScrollIntoViewSafe ? window.tiScrollIntoViewSafe(dl) : dl.scrollIntoView({behavior:'smooth', block:'center'}); } catch(e) { try { dl.scrollIntoView({behavior:'smooth', block:'center'}); } catch(ignore) {} }\n            setTimeout(function(){\n                try {\n                    if (directPass && !directPass.value) directPass.focus();\n                    else if (directBtn) directBtn.focus();\n                    else if (directUser) directUser.focus();\n                } catch(e) {}\n            }, 220);\n        } catch(e) {}\n    };\n\n\n    window.tiPrepareChatLoginAs = function(username) {\n        try {\n            if (window.syncLoginDbSelect) window.syncLoginDbSelect();\n            const u = gE('l-user');\n            const p = gE('l-pass');\n            if (u) u.value = username || '';\n            if (window.openLoginModalSafe) window.openLoginModalSafe(); else if (window.openModal) window.openModal('ti-login-ov');\n            setTimeout(function(){ try { if (p) { p.value = ''; p.focus(); } } catch(e) {} }, 120);\n        } catch(e) {\n            if (window.openLoginModalSafe) window.openLoginModalSafe();\n        }\n    };\n\n    window.openLoginModalSafe = function() {\n        try { if (window.syncLoginDbSelect) window.syncLoginDbSelect(); } catch(e) {}\n        if (window.openModal) window.openModal('ti-login-ov');\n        setTimeout(function(){\n            try {\n                if (window.syncLoginDbSelect) window.syncLoginDbSelect();\n                if (!(window.tiIsMobileLike && window.tiIsMobileLike())) {\n                    const u = gE('l-user'); if (u) u.focus();\n                }\n            } catch(e) {}\n        }, 80);\n    };\n\n    window.mobileLoginXhr = function(url, fd) {\n        return new Promise(function(resolve, reject){\n            try {\n                const xhr = new XMLHttpRequest();\n                xhr.open('POST', url, true);\n                xhr.withCredentials = true;\n                xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n                xhr.onreadystatechange = function(){\n                    if (xhr.readyState !== 4) return;\n                    const raw = xhr.responseText || '';\n                    if (xhr.status < 200 || xhr.status >= 300) {\n                        reject(new Error('HTTP ' + xhr.status + ': ' + raw.slice(0, 180)));\n                        return;\n                    }\n                    try { resolve(JSON.parse(raw)); }\n                    catch(e) { reject(new Error('Risposta server non valida: ' + raw.slice(0, 180))); }\n                };\n                xhr.onerror = function(){ reject(new Error('Errore rete XHR')); };\n                xhr.ontimeout = function(){ reject(new Error('Timeout login')); };\n                xhr.timeout = 60000;\n                xhr.send(fd);\n            } catch(e) { reject(e); }\n        });\n    };\n\n    window.doLogin = function() {\n        const userEl = gE('l-user');\n        const passEl = gE('l-pass');\n        const loginBtn = gE('l-do');\n        try { if (window.syncLoginDbSelect) window.syncLoginDbSelect(); } catch(e) {}\n        const u = userEl ? userEl.value.trim() : '';\n        const dbVal = (window.getLoginDbValue ? window.getLoginDbValue() : '');\n        if (!u) { window.tiAlert('Inserisci username'); if (userEl && !(window.tiIsMobileLike && window.tiIsMobileLike())) userEl.focus(); return; }\n        if (u !== 'SSGlobalAdmin' && !dbVal) { window.tiAlert('Seleziona una ditta nella finestra di login.'); return; }\n        const hiddenDb = gE('l-db-hidden');\n        if (hiddenDb && dbVal) hiddenDb.value = dbVal;\n        if (window.tiLoginInProgress) return;\n        window.tiLoginInProgress = true;\n        if (loginBtn) { loginBtn.disabled = true; loginBtn.textContent = 'Accesso...'; }\n\n        const makeLoginFd = function() {\n            const f = new FormData();\n            f.append('action','ti_ai_login');\n            f.append('ti_action','ti_ai_login');\n            f.append('ti_mobile_login_redirect','0');\n            f.append('redirect','0');\n            f.append('user',u);\n            f.append('username',u);\n            const pwd = passEl ? passEl.value : '';\n            f.append('pass', pwd);\n            f.append('password', pwd);\n            f.append('db', dbVal);\n            f.append('current_db', dbVal);\n            f.append('_ti_mobile_ts', String(Date.now()));\n            return f;\n        };\n\n        const urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n\n        const loginVia = function(url) {\n            const fd = makeLoginFd();\n            if (window.mobileLoginXhr) return window.mobileLoginXhr(url, fd);\n            if (window.postFormDataJsonSafe) return window.postFormDataJsonSafe(url, fd, {tiNoLongProcessSignal:true, cache:'no-store'});\n            return window.fetchJsonSafe(url, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', tiNoLongProcessSignal:true});\n        };\n\n        const trySequence = function(i) {\n            return loginVia(urls[i] || window.tiUrl).catch(function(err){\n                if (i + 1 < urls.length) return trySequence(i + 1);\n                throw err;\n            });\n        };\n\n        trySequence(0).then(function(d){\n            if (d && d.success) {\n                try {\n                    if (dbVal) localStorage.setItem('ti_saved_db', dbVal);\n                    if (dbVal) localStorage.setItem('ti_last_login_db', dbVal);\n                    localStorage.setItem('ti_last_login_user', u || '');\n                    sessionStorage.removeItem('ti_login_failed_clear');\n                    localStorage.setItem('ti_login_ok_ts', String(Date.now()));\n                } catch(e) {}\n                window.closeModal && window.closeModal('ti-login-ov');\n                try { if (window.tiCloseDirectLoginFallback) window.tiCloseDirectLoginFallback(); } catch(e) {}\n                if (window.tiReloadAfterSuccessfulLogin) window.tiReloadAfterSuccessfulLogin(); else location.reload();\n                return;\n            }\n            const failMsg = (((d||{}).data||{}).message || 'Login non riuscito');\n            window.tiAlert('Accesso non riuscito. Verifica username, password e ditta selezionata. Ti propongo Accesso diretto Shop & Service. Dettaglio: ' + failMsg);\n            try {\n                if (window.tiClearLoginFieldsAfterFailure) window.tiClearLoginFieldsAfterFailure(false);\n                else if (passEl) passEl.value = '';\n                if (window.revealDirectLoginFallback) window.revealDirectLoginFallback('login-non-riuscito', failMsg);\n                else if (window.openModal) window.openModal('ti-login-ov');\n            } catch(e) {}\n        }).catch(function(err){\n            const msg = err && err.message ? err.message : 'Errore di connessione durante il login.';\n            window.tiAlert('Accesso tramite tasto Accedi non completato. Ti propongo Accesso diretto Shop & Service. Dettaglio: ' + msg);\n            try {\n                if (window.tiClearLoginFieldsAfterFailure) window.tiClearLoginFieldsAfterFailure(false);\n                if (window.revealDirectLoginFallback) window.revealDirectLoginFallback('login-non-completato', msg);\n                else if (window.openModal) window.openModal('ti-login-ov');\n            } catch(e) {}\n        }).finally(function(){\n            window.tiLoginInProgress = false;\n            if (loginBtn) { loginBtn.disabled = false; loginBtn.textContent = 'Entra'; }\n        });\n    };\n\n    window.tiForgotPasswordFromLogin = function(source) {\n        const modalUser = gE('l-user');\n        const directUser = gE('ti-direct-login-user');\n        const modalDb = gE('l-db');\n        const directDb = gE('ti-direct-login-db');\n        const hiddenDb = gE('l-db-hidden');\n        const user = ((source === 'direct' && directUser) ? directUser.value : (modalUser ? modalUser.value : (directUser ? directUser.value : ''))).trim();\n        const db = (source === 'direct' && directDb && directDb.value) ? directDb.value : ((window.getLoginDbValue ? window.getLoginDbValue() : '') || (modalDb ? modalDb.value : '') || (hiddenDb ? hiddenDb.value : '') || (directDb ? directDb.value : ''));\n        if (!user) { window.tiAlert ? window.tiAlert('Inserisci username prima di richiedere il cambio password.') : alert('Inserisci username prima di richiedere il cambio password.'); return false; }\n        if (user === 'SSGlobalAdmin') { window.tiAlert ? window.tiAlert('Il recupero via email e disponibile per gli utenti registrati della ditta, non per SSGlobalAdmin.') : alert('Il recupero via email e disponibile per gli utenti registrati della ditta, non per SSGlobalAdmin.'); return false; }\n        if (!db) { window.tiAlert ? window.tiAlert('Seleziona la ditta prima di richiedere il cambio password.') : alert('Seleziona la ditta prima di richiedere il cambio password.'); return false; }\n        const ok = window.confirm('Inviare una password temporanea all indirizzo email registrato per l utente ' + user + '?');\n        if (!ok) return false;\n        const btn = gE('ti-forgot-password-btn');\n        const oldText = btn ? btn.textContent : '';\n        if (btn) { btn.disabled = true; btn.textContent = 'Invio email...'; }\n        const fd = new FormData();\n        fd.append('action','ti_ai_forgot_password');\n        fd.append('ti_action','ti_ai_forgot_password');\n        fd.append('user', user);\n        fd.append('username', user);\n        fd.append('db', db);\n        fd.append('db_select', db);\n        fd.append('current_db', db);\n        const urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        const post = function(url) {\n            if (window.postFormDataJsonSafe) return window.postFormDataJsonSafe(url, fd, {tiNoLongProcessSignal:true, cache:'no-store'});\n            return fetch(url, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', headers:{'X-Requested-With':'XMLHttpRequest'}}).then(function(resp){ return resp.text().then(function(raw){ if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + raw.slice(0, 180)); try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta server non valida: ' + raw.slice(0, 180)); } }); });\n        };\n        let chain = Promise.reject(new Error('init'));\n        urls.forEach(function(url){ chain = chain.catch(function(){ return post(url); }); });\n        chain.then(function(res){\n            const msg = res && res.data && res.data.msg ? res.data.msg : (res && res.data && res.data.message ? res.data.message : 'Richiesta completata.');\n            if (res && res.success) {\n                window.tiAlert ? window.tiAlert(msg) : alert(msg);\n                const pass = gE('l-pass') || gE('ti-direct-login-pass');\n                if (pass) try { pass.focus(); } catch(e) {}\n            } else {\n                window.tiAlert ? window.tiAlert('Errore: ' + msg) : alert('Errore: ' + msg);\n            }\n        }).catch(function(err){\n            const msg = err && err.message ? err.message : 'Errore invio richiesta password.';\n            window.tiAlert ? window.tiAlert('Errore recupero password: ' + msg) : alert('Errore recupero password: ' + msg);\n        }).finally(function(){\n            if (btn) { btn.disabled = false; btn.textContent = oldText || 'Password dimenticata? Cambia tramite email'; }\n        });\n        return false;\n    };\n\n\n    window.tiBindStableLoginUsername = function() {\n        try {\n            const bindPair = function(userId, passId) {\n                const u = gE(userId);\n                const p = gE(passId);\n                if (!u || !p || p.dataset.tiStableUserBound) return;\n                p.dataset.tiStableUserBound = '1';\n                const remember = function() {\n                    try {\n                        u.dataset.tiStableUsername = String(u.value || '');\n                    } catch(e) {}\n                };\n                const restore = function() {\n                    try {\n                        const stable = String(u.dataset.tiStableUsername || '');\n                        if (stable !== '' && document.activeElement === p && String(u.value || '') !== stable) {\n                            u.value = stable;\n                            u.dispatchEvent(new Event('input', {bubbles:true}));\n                            u.dispatchEvent(new Event('change', {bubbles:true}));\n                        }\n                    } catch(e) {}\n                };\n                const scheduleRestore = function() {\n                    try {\n                        if (!u.dataset.tiStableUsername) remember();\n                        [0, 40, 120, 300, 700].forEach(function(ms){ setTimeout(restore, ms); });\n                    } catch(e) {}\n                };\n                ['input','change','blur'].forEach(function(ev){\n                    u.addEventListener(ev, remember, false);\n                });\n                ['focus','keydown','input','paste','keyup'].forEach(function(ev){\n                    p.addEventListener(ev, scheduleRestore, false);\n                });\n                remember();\n            };\n            bindPair('l-user', 'l-pass');\n            bindPair('ti-direct-login-user', 'ti-direct-login-pass');\n        } catch(e) {}\n    };\n\n\n    window.bindMobileLoginButton = function() {\n        try {\n            const form = gE('ti-login-form');\n            const btn = gE('l-do');\n            const openBtn = gE('ti-login-btn');\n            const outBtn = gE('ti-logout-btn');\n            if (form && !form.dataset.tiSubmitBound) {\n                form.dataset.tiSubmitBound = '1';\n                form.addEventListener('submit', function(ev){ ev.preventDefault(); ev.stopPropagation(); if (window.doLogin) window.doLogin(); return false; }, false);\n            }\n            if (btn && !btn.dataset.tiClickBound) {\n                btn.dataset.tiClickBound = '1';\n                btn.addEventListener('click', function(ev){ ev.preventDefault(); ev.stopPropagation(); if (window.doLogin) window.doLogin(); return false; }, false);\n            }\n            if (openBtn && !openBtn.dataset.tiClickBound) {\n                openBtn.dataset.tiClickBound = '1';\n                const openPrimaryLogin = function(){ if (window.openLoginModalSafe) window.openLoginModalSafe(); else if (window.openModal) window.openModal('ti-login-ov'); };\n                openBtn.addEventListener('click', function(ev){ ev.preventDefault(); ev.stopPropagation(); openPrimaryLogin(); return false; }, false);\n                openBtn.addEventListener('touchend', function(ev){ ev.preventDefault(); ev.stopPropagation(); openPrimaryLogin(); return false; }, {passive:false});\n            }\n            if (outBtn && !outBtn.dataset.tiClickBound) {\n                outBtn.dataset.tiClickBound = '1';\n                outBtn.addEventListener('click', function(ev){ ev.preventDefault(); ev.stopPropagation(); if (window.doLogout) window.doLogout(); return false; }, false);\n                outBtn.addEventListener('touchend', function(ev){ ev.preventDefault(); ev.stopPropagation(); if (window.doLogout) window.doLogout(); return false; }, {passive:false});\n            }\n            if (window.syncLoginDbSelect) window.syncLoginDbSelect();\n            if (window.tiBindStableLoginUsername) window.tiBindStableLoginUsername();\n        } catch(e) {}\n    };\n    try {\n        if (new URLSearchParams(window.location.search).has('ti_login_error')) { setTimeout(function(){ if (window.openLoginModalSafe) window.openLoginModalSafe(); else if (window.openModal) window.openModal('ti-login-ov'); }, 250); }\n        setTimeout(window.bindMobileLoginButton, 0);\n        setTimeout(window.bindMobileLoginButton, 800);\n        setTimeout(window.bindMobileLoginButton, 1800);\n        document.addEventListener('DOMContentLoaded', window.bindMobileLoginButton, {once:true});\n    } catch(e) {}\n\n    window.tiMobileUnlockScroll = function() {\n        try {\n            if (window.tiIsMobileLike && !window.tiIsMobileLike()) return;\n            document.documentElement.classList.remove('ti-no-scroll');\n            document.body.classList.remove('ti-no-scroll');\n            document.documentElement.style.overflowY = 'auto';\n            document.body.style.overflowY = 'auto';\n            document.documentElement.style.overflowX = 'hidden';\n            document.body.style.overflowX = 'hidden';\n            document.documentElement.style.height = 'auto';\n            document.body.style.height = 'auto';\n            document.documentElement.style.position = 'static';\n            document.body.style.position = 'static';\n            document.documentElement.style.touchAction = 'pan-y';\n            document.body.style.touchAction = 'pan-y';\n            const root = document.getElementById('ti-ai-outer');\n            if (root) {\n                root.style.overflowY = 'visible';\n                root.style.touchAction = 'pan-y';\n                root.style.webkitOverflowScrolling = 'touch';\n            }\n        } catch(e) {}\n    };\n    try {\n        window.addEventListener('pageshow', window.tiMobileUnlockScroll, {passive:true});\n        window.addEventListener('resize', window.tiMobileUnlockScroll, {passive:true});\n        if (window.visualViewport) window.visualViewport.addEventListener('resize', window.tiMobileUnlockScroll, {passive:true});\n        document.addEventListener('touchstart', window.tiMobileUnlockScroll, {passive:true});\n    } catch(e) {}\n\n    window.forceSessionCloseNow = function() {\n        window.idleForceClosing = true;\n        if (window.clearIdleTimers) window.clearIdleTimers();\n        try { document.cookie = 'ti_ai_ss_session=; Max-Age=0; path=\/; SameSite=Lax'; } catch(e) {}\n        try {\n            if (gE('ti-chat-box')) gE('ti-chat-box').innerHTML = '';\n            if (gE('ti-msg')) gE('ti-msg').value = '';\n            if (gE('ti-user-name')) gE('ti-user-name').textContent = 'Cliente\/Utente';\n            if (gE('ti-user-role')) gE('ti-user-role').textContent = '';\n        } catch(e) {}\n        const hardReload = function(){ try { location.reload(); } catch(e) { window.location.href = window.location.href.split('#')[0]; } };\n        const failSafe = setTimeout(hardReload, 1500);\n        try {\n            const fd = new FormData();\n            fd.append('ti_action','ti_ai_logout');\n            fd.append('action','ti_ai_logout');\n            fetch(window.tiUrl, {method:'POST', body:fd, credentials:'same-origin', keepalive:true})\n                .then(function(){ clearTimeout(failSafe); hardReload(); })\n                .catch(function(){ clearTimeout(failSafe); hardReload(); });\n        } catch(e) {\n            clearTimeout(failSafe); hardReload();\n        }\n    };\n\n    window.doLogout = function(silentReset = false) {\n        window.idleForceClosing = true;\n        clearTimeout(window.idleTimer1); clearTimeout(window.idleTimer2); clearTimeout(window.idleTimer3);\n        const hardReload = function(){\n            try {\n                if (gE('ti-chat-box')) gE('ti-chat-box').innerHTML = '';\n                if (gE('ti-msg')) gE('ti-msg').value = '';\n                if (gE('ti-user-name')) gE('ti-user-name').textContent = 'Cliente\/Utente';\n                if (gE('ti-user-role')) gE('ti-user-role').textContent = '';\n                document.cookie = 'ti_ai_ss_session=; Max-Age=0; path=\/; SameSite=Lax';\n            } catch(e) {}\n            try { location.reload(); } catch(e) { window.location.href = window.location.href.split('#')[0]; }\n        };\n        const failSafe = setTimeout(hardReload, 2500);\n        const makeLogoutFd = function(actionName) {\n            const fd = new FormData();\n            fd.append('ti_action', actionName || 'ti_ai_logout');\n            fd.append('action', actionName || 'ti_ai_logout');\n            fd.append('_ti_mobile_ts', String(Date.now()));\n            return fd;\n        };\n        const postSafe = function(actionName) {\n            const urls = [];\n            if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n            if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n            const run = function(i) {\n                const fd = makeLogoutFd(actionName);\n                if (window.postFormDataJsonSafe) {\n                    return window.postFormDataJsonSafe(urls[i] || window.tiUrl, fd, {tiNoLongProcessSignal:true, cache:'no-store'}).catch(function(err){\n                        if (i + 1 < urls.length) return run(i + 1);\n                        throw err;\n                    });\n                }\n                return fetch(urls[i] || window.tiUrl, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'}).catch(function(err){\n                    if (i + 1 < urls.length) return run(i + 1);\n                    throw err;\n                });\n            };\n            return run(0);\n        };\n        postSafe('ti_ai_logout').then(function(){\n            if (silentReset) return postSafe('ti_ai_reset_flow');\n        }).finally(function(){ clearTimeout(failSafe); hardReload(); });\n    };\n\n    window.doRestart = function() { window.tiConfirm(dict[window.currLang].restartConfirm, function(){ window.doLogout(true); }); };\n\n\n    window.resetNewRegistrationProfileForm = function() {\n        const clearIds = ['p-utente','p-user','p-pass','p-ragione_sociale','p-email','p-tel','p-ind','p-piva_vat','p-cod_fisc','p-ade','p-sesso','p-dato_1','p-dato_2','p-dato_3','p-nota','p-foto'];\n        clearIds.forEach(function(id){\n            const el = gE(id);\n            if (!el) return;\n            try {\n                if (el.type === 'checkbox' || el.type === 'radio') el.checked = false;\n                else el.value = '';\n                if (id === 'p-pass' || \/pass\/i.test(id)) el.setAttribute('autocomplete', 'new-password');\n                else el.setAttribute('autocomplete', 'off');\n                if (el.removeAttribute) el.removeAttribute('value');\n                window.forceWhiteEditStyle && window.forceWhiteEditStyle(el);\n            } catch(e) {}\n        });\n        const eta = gE('p-eta');\n        if (eta) { eta.value = ''; eta.setAttribute('autocomplete', 'off'); }\n        ['p-com_mail','p-com_email','p-com_sms','p-com_tel'].forEach(function(id){ const el = gE(id); if (el) el.checked = true; });\n        const user = gE('p-user'); if (user) { user.readOnly = false; user.value = ''; }\n        const pass = gE('p-pass'); if (pass) { pass.required = true; pass.value = ''; }\n        const prev = gE('p-foto-preview'); if (prev) { prev.removeAttribute('src'); prev.style.display = 'none'; }\n        const form = gE('ti-profile-form'); if (form) form.setAttribute('autocomplete', 'off');\n    };\n\n    window.openProfileModal = function(isNew = false) {\n        const ditta = gE('ti-ditta'); if(!ditta || !ditta.value || ditta.value === 'NEW_DB' || ditta.value === '') { window.tiAlert(\"Seleziona una ditta prima di registrarti.\"); return; }\n        gE('lbl-profile-title').innerText = isNew ? \"Crea Profilo\" : \"Modifica Profilo\";\n        gE('lbl-p-pass').innerText = isNew ? \"Password*\" : \"Nuova Password (lascia vuoto per mantenere)\";\n        gE('p-pass').required = isNew;\n        \n        const cancelBtn = gE('ti-profile-cancel-btn');\n        if(isNew) {\n            if (cancelBtn) cancelBtn.style.display = 'none';\n            window.resetNewRegistrationProfileForm();\n            window.openModal('ti-profile-ov');\n            window.resetNewRegistrationProfileForm();\n            setTimeout(window.resetNewRegistrationProfileForm, 80);\n            setTimeout(window.resetNewRegistrationProfileForm, 350);\n            window.bindProfileUsernameDuplicatePopup && window.bindProfileUsernameDuplicatePopup();\n        } else {\n            if (cancelBtn) cancelBtn.style.display = '';\n            gE('p-user').value = window.tiUser; gE('p-user').readOnly = true; gE('p-utente').value = window.tiUserDisplay;\n            if(window.tiUserFoto) { gE('p-foto').value = window.tiUserFoto; const fotoDb = window.tiUserFotoDb || gE('ti-ditta').value; const safeUserFoto = window.getSafePreviewUrl(window.tiUserFoto, fotoDb, 'Utenti', 'Immagine', (gE('p-user').value || window.tiUser || 'utente')); if(safeUserFoto){ gE('p-foto-preview').src = safeUserFoto; gE('p-foto-preview').style.display = 'block'; } else { gE('p-foto-preview').style.display = 'none'; } }\n            \n            const fd = new FormData(); fd.append('ti_action', 'ti_ai_get_db_data'); fd.append('db', ditta.value);\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n                if(d.success && d.data && d.data.Tabelle && d.data.Tabelle.Utenti) {\n                    let myU = d.data.Tabelle.Utenti.find(u => (u.Username||u.user||'').toLowerCase() === window.tiUser.toLowerCase());\n                    if(myU) {\n                        if(gE('p-utente')) gE('p-utente').value = myU['Utente'] || myU['Nome'] || window.tiUserDisplay;\n                        if(gE('p-email')) gE('p-email').value = myU['Email'] || '';\n                        if(gE('p-ragione_sociale')) gE('p-ragione_sociale').value = myU['Ragione sociale'] || '';\n                        if(gE('p-ind')) gE('p-ind').value = myU['Indirizzo'] || '';\n                        if(gE('p-piva_vat')) gE('p-piva_vat').value = myU['PIVA_VAT'] || myU['P.IVA'] || '';\n                        if(gE('p-cod_fisc')) gE('p-cod_fisc').value = myU['COD_FISC'] || '';\n                        if(gE('p-ade')) gE('p-ade').value = myU['ADE'] || '';\n                        if(gE('p-tel')) gE('p-tel').value = myU['Telefono'] || myU['Tel'] || '';\n                        if(gE('p-sesso')) gE('p-sesso').value = myU['Sesso'] || '';\n                        if(gE('p-eta')) gE('p-eta').value = myU['Et\u00e0'] || '0';\n                        if(gE('p-dato_1')) gE('p-dato_1').value = myU['Dato 1'] || '';\n                        if(gE('p-dato_2')) gE('p-dato_2').value = myU['Dato 2'] || '';\n                        if(gE('p-dato_3')) gE('p-dato_3').value = myU['Dato 3'] || '';\n                        if(gE('p-nota')) gE('p-nota').value = myU['Nota'] || '';\n                        if(gE('p-com_mail')) gE('p-com_mail').checked = (myU['Com Mail'] !== 'NO');\n                        if(gE('p-com_email')) gE('p-com_email').checked = (myU['Com Email'] !== 'NO');\n                        if(gE('p-com_sms')) gE('p-com_sms').checked = (myU['Com SMS'] !== 'NO');\n                        if(gE('p-com_tel')) gE('p-com_tel').checked = (myU['Com Tel'] !== 'NO');\n                    }\n                }\n                window.openModal('ti-profile-ov');\n                window.bindProfileUsernameDuplicatePopup && window.bindProfileUsernameDuplicatePopup();\n            });\n        }\n    };\n\n    window.createDbSubmit = function() {\n        const dbName = gE('nd-name').value.trim(); const uName = gE('nd-user').value.trim(); const uPass = gE('nd-pass').value; const uEmail = gE('nd-email').value.trim();\n        if(!dbName || !uName || !uPass) { window.tiAlert(\"Dati obbligatori mancanti!\"); return; }\n        const fd = window.buildAjaxFormData ? window.buildAjaxFormData('ti_ai_create_db') : new FormData(); fd.append('name', dbName); fd.append('username', uName); fd.append('password', uPass); fd.append('nome', uName); fd.append('email', uEmail);\n        if (!fd.has('action')) fd.append('action', 'ti_ai_create_db');\n        if (!fd.has('ti_action')) fd.append('ti_action', 'ti_ai_create_db');\n        window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(d=>{\n            if(d.success) { localStorage.setItem('ti_saved_db', d.data.db); location.href = window.tiUrl; }\n            else {\n                const msg = (d && d.data && d.data.message) ? d.data.message : 'Errore creazione ditta.';\n                if (\/username\/i.test(msg) && \/(gi\u00e0|gia|esistent|uso|duplicat)\/i.test(msg)) {\n                    window.tiAlert('Username gi\u00e0 esistente.<br>Cambia username perch\u00e9 \u00e8 gi\u00e0 esistente.');\n                    const el = gE('nd-user'); if (el) { window.forceWhiteEditStyle && window.forceWhiteEditStyle(el); setTimeout(function(){ try { el.focus(); el.select && el.select(); } catch(e) {} }, 120); }\n                } else window.tiAlert(msg);\n            }\n        });\n    };\n\n    if(gE('p-foto')) gE('p-foto').addEventListener('change', function() { \n        const prev = gE('p-foto-preview'); \n        if(this.value) { \n            prev.src = window.getSafePreviewUrl(this.value, gE('ti-ditta').value, 'Utenti', 'Immagine', (gE('p-user').value || window.tiUser || 'utente')); \n            prev.style.display = 'block'; \n        } else { prev.style.display = 'none'; } \n    });\n\n    window.saveProfile = function() {\n        const submitBtn = gE('ti-profile-save-btn') || document.querySelector('#ti-profile-form button[type=\"submit\"]');\n        if (submitBtn) { submitBtn.disabled = true; submitBtn.innerText = 'Salvataggio...'; }\n        const fd = new FormData(); fd.append('ti_action','ti_ai_save_profile'); fd.append('db', gE('ti-ditta').value); \n        fd.append('utente', gE('p-utente').value); fd.append('username', gE('p-user').value); fd.append('password', gE('p-pass').value); \n        fd.append('ragione_sociale', gE('p-ragione_sociale').value); fd.append('email', gE('p-email').value); fd.append('tel', gE('p-tel').value); \n        fd.append('indirizzo', gE('p-ind').value); fd.append('piva_vat', gE('p-piva_vat').value); fd.append('cod_fisc', gE('p-cod_fisc').value); \n        fd.append('ade', gE('p-ade').value); fd.append('sesso', gE('p-sesso').value); fd.append('eta', gE('p-eta').value); \n        fd.append('dato_1', gE('p-dato_1').value); fd.append('dato_2', gE('p-dato_2').value); fd.append('dato_3', gE('p-dato_3').value); \n        fd.append('com_mail', gE('p-com_mail').checked ? 'SI' : 'NO'); fd.append('com_email', gE('p-com_email').checked ? 'SI' : 'NO'); \n        fd.append('com_sms', gE('p-com_sms').checked ? 'SI' : 'NO'); fd.append('com_tel', gE('p-com_tel').checked ? 'SI' : 'NO'); \n        fd.append('nota', gE('p-nota').value); fd.append('foto', gE('p-foto').value); fd.append('immagine', gE('p-foto').value);\n        window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n            if(d.success) { window.tiAlert(d.data.msg); location.reload(); }\n            else {\n                if (submitBtn) { submitBtn.disabled = false; submitBtn.innerText = 'Salva'; }\n                const msg = (d && d.data && d.data.message) ? d.data.message : 'Errore salvataggio profilo.';\n                if (\/username\/i.test(msg) && \/(gi\u00e0|gia|esistent|uso|duplicat)\/i.test(msg)) {\n                    const userEl = gE('p-user');\n                    window.tiAlert('Username gi\u00e0 esistente.<br>Cambia username perch\u00e9 \u00e8 gi\u00e0 esistente.');\n                    if (userEl) {\n                        userEl.style.setProperty('border-color', '#ef4444', 'important');\n                        userEl.style.setProperty('box-shadow', '0 0 0 2px rgba(239,68,68,.55)', 'important');\n                        window.forceWhiteEditStyle && window.forceWhiteEditStyle(userEl);\n                        setTimeout(function(){ try { userEl.focus(); userEl.select && userEl.select(); } catch(e) {} }, 120);\n                    }\n                } else {\n                    window.tiAlert(msg);\n                }\n            }\n        }).catch(e=>{ if (submitBtn) { submitBtn.disabled = false; submitBtn.innerText = 'Salva'; } window.tiAlert('Errore di rete durante il salvataggio profilo.'); });\n    };\n\n    window.bindProfileUsernameDuplicatePopup = function() {\n        const el = gE('p-user');\n        if (!el || el.dataset.dupBound === '1') return;\n        el.dataset.dupBound = '1';\n        const run = function(){\n            if (el.readOnly) return;\n            const username = String(el.value || '').trim();\n            if (!username) return;\n            if (window.currentDbData && window.currentDbData.Tabelle) {\n                const dup = window.findDuplicateUsernameInConfig ? window.findDuplicateUsernameInConfig(username, '', -1) : null;\n                if (dup) {\n                    window.showDuplicateUsernamePopup(username, dup);\n                    el.style.setProperty('border-color', '#ef4444', 'important');\n                    el.style.setProperty('box-shadow', '0 0 0 2px rgba(239,68,68,.55)', 'important');\n                    window.forceWhiteEditStyle && window.forceWhiteEditStyle(el);\n                }\n            }\n        };\n        el.addEventListener('change', run);\n        el.addEventListener('blur', run);\n        el.addEventListener('input', function(){ window.forceWhiteEditStyle && window.forceWhiteEditStyle(el); });\n    };\n\n    window.bindProfileUsernameDuplicatePopup();\n\n    window.cancelProfile = function() {\n        const username = gE('p-user') ? gE('p-user').value.trim() : '';\n        if (!username) { window.tiAlert('Nessun utente selezionato.'); return; }\n        window.tiConfirm('Confermi la cancellazione logica del profilo? Lo stato verra impostato su Cancellato.', function(){\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_delete_profile');\n            fd.append('db', gE('ti-ditta').value);\n            fd.append('username', username);\n            fd.append('mode', 'soft');\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n                if(d.success) {\n                    window.tiAlert(d.data.msg || 'Profilo cancellato.');\n                    if (d.data.logout) { setTimeout(()=> location.reload(), 400); }\n                    else { window.closeModal('ti-profile-ov'); location.reload(); }\n                } else {\n                    window.tiAlert(d.data.message || 'Errore durante la cancellazione del profilo.');\n                }\n            }).catch(()=> window.tiAlert('Errore di rete durante la cancellazione del profilo.'));\n        });\n    };\n\n    window.tiCompanyHelpEscape = function(v) {\n        return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; });\n    };\n    window.tiGetCompanyConfigRow = function() {\n        const data = window.currentDbData || window.currentDbViewData || null;\n        const tables = data && data.Tabelle ? data.Tabelle : {};\n        const cfgRows = tables.Config || tables.config || tables.CONFIG || [];\n        return Array.isArray(cfgRows) && cfgRows[0] && typeof cfgRows[0] === 'object' ? cfgRows[0] : {};\n    };\n    window.tiPickCompanyVal = function(row, names) {\n        row = row || {};\n        const keys = Object.keys(row);\n        const norm = function(x){ return String(x || '').toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').replace(\/[_\\-]+\/g, ' ').replace(\/\\s+\/g, ' ').trim(); };\n        for (const name of names) {\n            if (Object.prototype.hasOwnProperty.call(row, name) && String(row[name] || '').trim() !== '') return String(row[name]).trim();\n            const wanted = norm(name);\n            const found = keys.find(k => norm(k) === wanted);\n            if (found && String(row[found] || '').trim() !== '') return String(row[found]).trim();\n        }\n        return '';\n    };\n    window.tiBuildCompanyInfoLinksHtml = function() {\n        const data = window.currentDbData || window.currentDbViewData || null;\n        const tables = data && data.Tabelle ? data.Tabelle : {};\n        const infoRows = tables.Informazioni || tables.informazioni || [];\n        if (!Array.isArray(infoRows)) return '';\n        const wanted = [\n            {key:'commerciali', label:'Condizioni commerciali', re:\/commercial|vendita|pagament|offert|listin|prezz|recess|reso|spedizion\/i},\n            {key:'contrattuali', label:'Condizioni contrattuali', re:\/contratt|termini|accord|fornitura|garanzi|clausol\/i},\n            {key:'privacy', label:'Privacy', re:\/privacy|gdpr|dati personal|informativa|cookie\/i}\n        ];\n        const found = {};\n        const clean = function(v){ return String(v == null ? '' : v).trim(); };\n        const firstUrl = function(txt) { const m = String(txt || '').match(\/https?:\\\/\\\/[^\\s<>'\")]+\/i); return m ? m[0].replace(\/[\\],.;]+$\/,'') : ''; };\n        infoRows.forEach(function(row){\n            if (!row || typeof row !== 'object') return;\n            const flat = Object.keys(row).map(k => k + ' ' + clean(row[k])).join(' ');\n            const tipo = clean(row.Tipo || row.tipo || row.Type || '').toUpperCase();\n            const doc = clean(row.Doc || row.Docs || row.File || row.URL || row.Url || row.Link || row.Descrizione || '');\n            const url = firstUrl(doc) || firstUrl(flat);\n            wanted.forEach(function(w){\n                if (found[w.key]) return;\n                if ((tipo === 'DOC' || tipo === 'URL' || tipo === 'WEB' || tipo === '') && w.re.test(flat)) {\n                    found[w.key] = url ? `<a href=\"${window.tiCompanyHelpEscape(url)}\" target=\"_blank\" rel=\"noopener\" style=\"color:#93c5fd;text-decoration:underline;\">${w.label}<\/a>` : `<span style=\"color:#cbd5e1;\">${w.label}<\/span>`;\n                }\n            });\n        });\n        const links = wanted.map(w => found[w.key]).filter(Boolean);\n        return links.length ? `<div>${links.join(' &nbsp;|&nbsp; ')}<\/div>` : '';\n    };\n    window.tiUpdateColumnHelpCompanyHeader = function() {\n        const box = document.getElementById('col-help-company');\n        if (!box) return;\n        const cfg = window.tiGetCompanyConfigRow ? window.tiGetCompanyConfigRow() : {};\n        const pick = window.tiPickCompanyVal || function(){ return ''; };\n        const lines = [];\n        const rag = pick(cfg, ['Ragione sociale','Ragione Sociale','Ditta','Nome ditta']);\n        const indirizzo = pick(cfg, ['Indirizzo','Sede','Indirizzo ditta']);\n        const piva = pick(cfg, ['PIVA_VAT','PIVA VAT','Partita IVA','P. IVA','PIva','PIVA']);\n        const email = pick(cfg, ['Email ditta','Email principale','Email','E-mail','Mail']);\n        const tel = pick(cfg, ['Tel','Telefono','Telefono ditta','Cellulare']);\n        if (rag) lines.push(`<div><b>${window.tiCompanyHelpEscape(rag)}<\/b><\/div>`);\n        if (indirizzo) lines.push(`<div>${window.tiCompanyHelpEscape(indirizzo)}<\/div>`);\n        if (piva) lines.push(`<div>Partita IVA: ${window.tiCompanyHelpEscape(piva)}<\/div>`);\n        if (email) lines.push(`<div>Email: <a href=\"mailto:${window.tiCompanyHelpEscape(email)}\" style=\"color:#93c5fd;\">${window.tiCompanyHelpEscape(email)}<\/a><\/div>`);\n        if (tel) lines.push(`<div>Tel: <a href=\"tel:${window.tiCompanyHelpEscape(tel)}\" style=\"color:#93c5fd;\">${window.tiCompanyHelpEscape(tel)}<\/a><\/div>`);\n        const links = window.tiBuildCompanyInfoLinksHtml ? window.tiBuildCompanyInfoLinksHtml() : '';\n        if (links) lines.push(links);\n        box.innerHTML = lines.join('');\n        box.style.display = lines.length ? 'block' : 'none';\n        try { rewriteLocalDocAnchors(document.getElementById('ti-help-ov') || document); } catch(e) {}\n    };\n\n    window.tiSanitizeColumnHelpText357 = window.tiSanitizeColumnHelpText357 || function(text) {\n        return String(text || '')\n            .replace(\/[#*]+\/g, '')\n            .replace(\/[ \\t]+\\n\/g, '\\n')\n            .replace(\/\\n{3,}\/g, '\\n\\n')\n            .trim();\n    };\n    window.copyColumnHelpText357 = window.copyColumnHelpText357 || function() {\n        const modal = document.querySelector('#ti-col-help-ov .ti-modal');\n        const title = gE('col-help-title');\n        const company = gE('col-help-company');\n        const box = gE('col-help-content');\n        const btn = gE('ti-col-help-copy');\n        const cleanText = function(node) {\n            if (!node) return '';\n            try {\n                const clone = node.cloneNode(true);\n                clone.querySelectorAll('button, .ti-btn, script, style').forEach(function(el){ el.remove(); });\n                return String(clone.innerText || clone.textContent || '').replace(\/\\n{3,}\/g, '\\n\\n').trim();\n            } catch(e) { return String(node.innerText || node.textContent || '').trim(); }\n        };\n        let parts = [];\n        if (title) parts.push(cleanText(title));\n        if (company && String(company.innerText || company.textContent || '').trim() !== '') parts.push(cleanText(company));\n        if (box) parts.push(cleanText(box));\n        let text = parts.filter(Boolean).join('\\n\\n').trim();\n        if (!text && modal) text = cleanText(modal);\n        const done = function(ok){\n            if (!btn) return;\n            const old = btn.innerText;\n            btn.innerText = ok ? 'Testo copiato' : 'Copia non riuscita';\n            setTimeout(function(){ btn.innerText = old || 'Copia testo'; }, 1200);\n        };\n        if (!text) return done(false);\n        if (navigator.clipboard && navigator.clipboard.writeText) {\n            navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){ done(false); });\n        } else {\n            try {\n                const ta = document.createElement('textarea');\n                ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';\n                document.body.appendChild(ta); ta.focus(); ta.select();\n                const ok = document.execCommand('copy'); document.body.removeChild(ta); done(!!ok);\n            } catch(e) { done(false); }\n        }\n    };\n\n    window.tiColumnHelpFallbackHtml436 = function(cleanTbl, cleanCol, helpIsEn, helpRequest) {\n        const esc = window.tiCompanyHelpEscape || function(v){ return String(v || '').replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#039;'}[c]; }); };\n        const norm = function(v){ try { return String(v || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').toLowerCase().replace(\/[^a-z0-9]+\/g,''); } catch(e){ return String(v||'').toLowerCase().replace(\/[^a-z0-9]+\/g,''); } };\n        const tblNorm = norm(cleanTbl), colNorm = norm(cleanCol);\n        const desc = (cleanCol && window.getConfigFieldDescription) ? window.getConfigFieldDescription(cleanTbl, cleanCol) : '';\n        const relationMeta = (cleanCol && window.getConfigRelationMeta) ? window.getConfigRelationMeta(cleanTbl, cleanCol, {}) : null;\n        let relationLine = '';\n        let relationExamples = '';\n        try {\n            if (relationMeta) {\n                const opts = window.getConfigRelationOptions ? window.getConfigRelationOptions(cleanTbl, cleanCol, {}, -1) : [];\n                const sourceLabel = relationMeta.sourceLabel || relationMeta.label || relationMeta.table || 'tabella sorgente';\n                relationLine = helpIsEn ? ('Suggested source: choose from ' + sourceLabel + '.') : ('Scelta consigliata: selezionare un valore da ' + sourceLabel + '.');\n                if (opts && opts.length) {\n                    relationExamples = opts.slice(0, 8).map(function(o){ return String((o && (o.label || o.value)) || '').trim(); }).filter(Boolean).join(', ');\n                } else {\n                    relationExamples = helpIsEn ? ('No active records found in ' + sourceLabel + '.') : ('Nessun record attivo trovato in ' + sourceLabel + '.');\n                }\n            }\n        } catch(e) {}\n        const pickCfg = function(names){\n            try {\n                const cfgTbl = window.findConfigTableName ? window.findConfigTableName('Config') : 'Config';\n                const rows = window.currentDbData && window.currentDbData.Tabelle ? window.currentDbData.Tabelle[cfgTbl] : [];\n                const row = Array.isArray(rows) ? (rows[0] || {}) : (Object.values(rows || {})[0] || {});\n                const keys = Object.keys(row || {});\n                for (const name of names) { const k = keys.find(x => norm(x) === norm(name)); if (k && row[k] != null && String(row[k]).trim() !== '') return String(row[k]).trim(); }\n            } catch(e) {}\n            return '';\n        };\n        const activity = pickCfg(['Attivita','Attivit\u00e0','Tipo attivit\u00e0','Settore']);\n        const exampleFromTable = function(){\n            try {\n                const rows = window.currentDbData && window.currentDbData.Tabelle ? window.currentDbData.Tabelle[cleanTbl] : [];\n                const arr = Array.isArray(rows) ? rows : Object.values(rows || {});\n                for (const r of arr) {\n                    if (!r || typeof r !== 'object') continue;\n                    const k = Object.keys(r).find(x => norm(x) === colNorm);\n                    if (k && String(r[k] == null ? '' : r[k]).trim() !== '') return String(r[k]).trim();\n                }\n            } catch(e) {}\n            return '';\n        };\n        const scrubHelpSensitive = function(v){\n            v = String(v || '').trim();\n            if (!v) return '';\n            v = v.replace(\/[A-Z0-9._%+\\-]+@[A-Z0-9.\\-]+\\.[A-Z]{2,}\/ig, '[email omessa]');\n            v = v.replace(\/\\b(?:https?:\\\/\\\/|www\\.)\\S+\/ig, '[link omesso]');\n            v = v.replace(\/\\b(?:\\+?39[\\s.\\-]?)?(?:\\d[\\s.\\-]?){8,13}\\b\/g, '[telefono omesso]');\n            v = v.replace(\/\\b(?:IT)?\\s*\\d{11}\\b\/ig, '[PIVA omessa]');\n            v = v.replace(\/\\b[A-Z]{6}\\d{2}[A-Z]\\d{2}[A-Z]\\d{3}[A-Z]\\b\/ig, '[codice fiscale omesso]');\n            return v.trim();\n        };\n        const sensitiveField = function(name){\n            const n = norm(name);\n            return \/(ditta|ragionesociale|nome|cognome|utente|username|password|email|tel|telefono|cellulare|indirizzo|piva|partitaiva|codfisc|codicefiscale|iban|bic|beneficiary|bankname|clientid|clientsecret|webhookid|activeid|wcorderid)\/.test(n);\n        };\n        let example = relationExamples || exampleFromTable();\n        if (cleanCol && sensitiveField(cleanCol)) example = '';\n        example = scrubHelpSensitive(example);\n        if (\/\\[(email|telefono|PIVA|codice fiscale|IBAN) omess[ao]\\]\/i.test(example)) example = '';\n        const operationalExample = function(){\n            const key = tblNorm + ':' + colNorm;\n            const gen = helpIsEn ? {\n                'prodotti:prodotto':'Operational example: use a short recognizable product name, then keep description, price, VAT, availability and commercial instructions in their own fields.',\n                'prodotti:istruzioni':'Manual-based example: \"10% discount until 30\/04\/2026\". Keep temporary rules in Instructions and do not change the base price if the discount is temporary.',\n                'prodotti:disponibilita':'Operational example: enter real availability or a coherent quantity; if unavailable, the flow may propose a pre-booking or contact request.',\n                'prodotti:quantita':'Operational example: update the real available quantity and verify that orders and availability use the corrected value.',\n                'servizi:servizio':'Operational example: use a clear service name; complete duration, price, available days\/times and linked resources if the service is bookable.',\n                'servizi:prodotto':'Operational example: link the product or material used by the service from Products; in similar setups, only resources truly needed to deliver the service are linked.',\n                'servizi:prodotti':'Operational example: link one or more products\/materials required by the service, avoiding generic values not present in the catalog.',\n                'servizi:operatore':'Operational example: link an operator only when the service requires a specific skill or resource; otherwise it may remain available for compatible operators.',\n                'servizi:dispositivo':'Operational example: link a technical resource only when it blocks slot availability, such as equipment, workstation or technical room.',\n                'servizi:sito':'Operational example: enter the site when the service can be delivered only in a specific office, room or department.',\n                'servizi:istruzioni':'Manual-based example: add rules such as consent required, temporary promotion, user preparation or booking constraints.'\n            } : {\n                'prodotti:prodotto':'Esempio operativo: usare un nome breve e riconoscibile, poi gestire descrizione, prezzo, IVA, disponibilit\u00e0 e istruzioni commerciali nei rispettivi campi.',\n                'prodotti:istruzioni':'Esempio operativo dal manuale: \"Sconto 10% fino al 30\/04\/2026\". La regola deve restare nel campo Istruzioni e non modificare il prezzo base se \u00e8 temporanea.',\n                'prodotti:disponibilita':'Esempio operativo: indicare una disponibilit\u00e0 reale o una quantit\u00e0 coerente; se il prodotto non \u00e8 disponibile, il flusso pu\u00f2 proporre pre-prenotazione o contatto.',\n                'prodotti:quantita':'Esempio operativo: aggiornare la quantit\u00e0 reale disponibile e verificare che ordini e disponibilit\u00e0 mostrino il dato corretto.',\n                'servizi:servizio':'Esempio operativo: usare un nome servizio chiaro; completare durata, prezzo, giorni\/orari disponibili e risorse collegate se il servizio \u00e8 prenotabile.',\n                'servizi:prodotto':'Esempio operativo: associare il prodotto o materiale usato dal servizio scegliendolo da Prodotti; nei casi analoghi si collega solo ci\u00f2 che serve davvero per erogare il servizio.',\n                'servizi:prodotti':'Esempio operativo: associare uno o pi\u00f9 materiali\/prodotti necessari al servizio, evitando valori generici non presenti in catalogo.',\n                'servizi:operatore':'Esempio operativo: collegare un operatore solo quando il servizio richiede una competenza o una risorsa precisa; altrimenti il servizio pu\u00f2 restare disponibile per gli operatori compatibili.',\n                'servizi:dispositivo':'Esempio operativo: collegare una risorsa tecnica solo se blocca la disponibilit\u00e0 dello slot, ad esempio attrezzatura, postazione o sala tecnica.',\n                'servizi:sito':'Esempio operativo: indicare il sito quando il servizio pu\u00f2 essere eseguito solo in una sede, stanza o reparto specifico.',\n                'servizi:istruzioni':'Esempio operativo dal manuale: inserire regole come consenso informato, promozione temporanea, preparazione utente o vincoli di prenotazione.'\n            };\n            const tableGen = helpIsEn ? {\n                dispositivi:'Operational example: register equipment, workstations, technical rooms or instruments that can enable or restrict a service or appointment.',\n                siti:'Operational example: use this table for offices, rooms, departments or service locations, linking them to services when they affect availability or user directions.',\n                operatori:'Operational example: fill skills, availability and instructions for the operator; do not use this table for non-operational data.',\n                appuntamenti:'Operational example: a slot should include date, time and busy resources; only active or confirmed statuses should block availability.',\n                prenotazioni:'Operational example: store requests or pre-bookings with context, status and notes, confirming them only when data is complete.',\n                istruzioni:'Manual-based example: a temporary rule should state what to do and until when, such as a discount, priority or operational constraint.',\n                informazioni:'Operational example: use information records or documents to give the AI verifiable content about products, services, conditions or procedures.',\n                attivita:'Operational example: track results and operational events, separating customer activity, configuration and management checks.'\n            } : {\n                dispositivi:'Esempio operativo: registrare attrezzature, postazioni, sale tecniche o strumenti che possono limitare o abilitare un servizio o un appuntamento.',\n                siti:'Esempio operativo: usare la tabella per sedi, stanze, reparti o luoghi di erogazione, collegandoli ai servizi quando incidono su disponibilit\u00e0 o indicazioni utente.',\n                operatori:'Esempio operativo: indicare competenze, disponibilit\u00e0 e istruzioni dell\u2019operatore; non usare questa tabella per dati non operativi.',\n                appuntamenti:'Esempio operativo: uno slot deve riportare data, ora e risorse impegnate; solo stati attivi o confermati devono bloccare la disponibilit\u00e0.',\n                prenotazioni:'Esempio operativo: salvare richieste o pre-prenotazioni con contesto, stato e note operative, confermando solo quando i dati sono completi.',\n                istruzioni:'Esempio operativo dal manuale: una regola temporanea deve indicare cosa fare e fino a quando, ad esempio uno sconto, una priorit\u00e0 o un vincolo operativo.',\n                informazioni:'Esempio operativo: usare record informativi o documenti per dare alla AI contenuti verificabili su prodotti, servizi, condizioni o procedure.',\n                attivita:'Esempio operativo: registrare esiti e tracciamenti operativi, distinguendo attivit\u00e0 cliente, configurazione e controlli gestionali.'\n            };\n            if (gen[key]) return gen[key];\n            if (!cleanCol && tableGen[tblNorm]) return tableGen[tblNorm];\n            if (example) return helpIsEn ? ('Operational example: use an existing valid value such as \"' + example + '\" only if it is still active.') : ('Esempio operativo: puoi usare un valore reale gi\u00e0 presente come \"' + example + '\" solo se \u00e8 ancora valido e attivo.');\n            return helpIsEn ? 'Operational example: use real data already present in the company database, avoiding company-identifying details unless the field explicitly requires them.' : 'Esempio operativo: usare dati reali gi\u00e0 presenti nella ditta, evitando dati anagrafici o identificativi se il campo non li richiede espressamente.';\n        };\n        const isProductField = tblNorm === 'prodotti' && colNorm === 'prodotto';\n        const isServiceProduct = tblNorm === 'servizi' && (colNorm === 'prodotto' || colNorm === 'prodotti' || \/^prodotto[123]$\/.test(colNorm));\n        const tableFields = function(){\n            try {\n                const rows = window.currentDbData && window.currentDbData.Tabelle ? window.currentDbData.Tabelle[cleanTbl] : [];\n                const arr = Array.isArray(rows) ? rows : Object.values(rows || {});\n                const r = arr.find(function(x){ return x && typeof x === 'object'; }) || {};\n                return Object.keys(r).map(function(k){ return String(k || '').trim(); }).filter(Boolean).slice(0, 20).join(', ');\n            } catch(e) { return ''; }\n        };\n        const tableExamples = function(){\n            try {\n                const rows = window.currentDbData && window.currentDbData.Tabelle ? window.currentDbData.Tabelle[cleanTbl] : [];\n                const arr = (Array.isArray(rows) ? rows : Object.values(rows || {})).filter(function(r){ return r && typeof r === 'object'; });\n                const out = [];\n                arr.slice(0, 3).forEach(function(r){\n                    const vals = [];\n                    Object.keys(r).forEach(function(k){\n                        if (vals.length >= 3) return;\n                        const v = String(r[k] == null ? '' : r[k]).trim();\n                        if (String(k || '').trim() && v) vals.push(String(k).trim() + ': ' + v);\n                    });\n                    if (vals.length) out.push(vals.join(' | '));\n                });\n                return out.join(' \/ ');\n            } catch(e) { return ''; }\n        };\n        const tableGuide = function(){\n            const it = {\n                config:'contiene parametri principali della ditta: stato, dati identificativi, contatti, attivit\u00e0, istruzioni generali, chiavi e opzioni operative. Va verificata per prima perch\u00e9 orienta AI, login, ordini e messaggi.',\n                utenti:'contiene utenti, ruoli, contatti e preferenze operative. Serve per login, permessi, notifiche email\/SMS e dati utente nei flussi ordine o prenotazione.',\n                operatori:'contiene persone o risorse operative associabili a servizi, appuntamenti e attivit\u00e0. Compilare contatti, competenze, disponibilit\u00e0 e istruzioni quando servono nei flussi di prenotazione.',\n                prodotti:'contiene articoli vendibili o collegabili ai servizi. Verificare nome, descrizione, prezzo, IVA, disponibilit\u00e0, immagine e istruzioni commerciali o operative.',\n                servizi:'contiene prestazioni prenotabili o vendibili. Collegare operatori, siti, dispositivi e prodotti\/materiali quando previsti, indicando durata, disponibilit\u00e0, prezzo, istruzioni e stato.',\n                dispositivi:'contiene attrezzature, strumenti, sale tecniche, postazioni o risorse materiali associabili a servizi, appuntamenti e attivit\u00e0. Serve a evitare conflitti e a proporre solo combinazioni disponibili.',\n                siti:'contiene sedi, reparti, sale, punti vendita o luoghi operativi. Serve per indirizzare prenotazioni, appuntamenti, disponibilit\u00e0 e indicazioni all\u2019utente.',\n                informazioni:'contiene contenuti informativi, documenti, URL e riferimenti che l\u2019AI usa per risposte e assistenza. Compilare Tipo, descrizione, file o link in modo chiaro.',\n                istruzioni:'contiene regole operative per AI e flussi Shop-Service: sconti, priorit\u00e0, comportamenti, vincoli, promozioni e istruzioni tecniche. Le istruzioni devono essere chiare e datate quando temporanee.',\n                appuntamenti:'contiene appuntamenti e impegni fissati. Serve a verificare conflitti su data, ora, operatore, dispositivo, sito e prodotto, e a generare riepiloghi\/notifiche.',\n                prenotazioni:'contiene richieste di prenotazione o pre-prenotazione. Deve riportare servizio\/prodotto, contatto utente, data\/ora quando note, stato e note operative.',\n                attivita:'contiene eventi, report e tracciamenti operativi. Serve per storico, statistiche, controllo esiti e attivit\u00e0 di configurazione.'\n            };\n            const en = {\n                config:'stores the company\u2019s main settings: status, identity data, contacts, activity context, general instructions, keys and behavior options. Review it first because it drives AI, login, orders and messages.',\n                utenti:'stores users, roles, contacts and preferences. It supports login, permissions, email\/SMS notifications and user data in order or booking flows.',\n                operatori:'stores people or operational resources linked to services, bookings and activities. Fill contacts, skills, availability and instructions when used in booking flows.',\n                prodotti:'stores sellable items or items linked to services. Check product name, description, price, VAT, availability, image and commercial or operating instructions.',\n                servizi:'stores bookable or sellable services. Link operators, sites, devices and products\/materials when required, and set duration, availability, price, instructions and status.',\n                dispositivi:'stores equipment, instruments, technical rooms, workstations or material resources linked to services, bookings and activities. It helps avoid conflicts and propose only available combinations.',\n                siti:'stores locations, departments, rooms, shops or operating places. It supports bookings, appointments, availability and user directions.',\n                informazioni:'stores informational content, documents, URLs and references used by the AI for answers and assistance. Fill Type, description, file or link clearly.',\n                istruzioni:'stores operational rules for AI and Shop-Service flows: discounts, priorities, behaviors, constraints, promotions and technical instructions. Instructions should be clear and dated when temporary.',\n                appuntamenti:'stores fixed appointments and commitments. It is used to check conflicts on date, time, operator, device, site and product, and to generate summaries\/notifications.',\n                prenotazioni:'stores booking or pre-booking requests. It should include service\/product, user contact, date\/time when known, status and operational notes.',\n                attivita:'stores events, reports and operational tracking. It supports history, statistics, result control and configuration activities.'\n            };\n            return (helpIsEn ? en : it)[tblNorm] || '';\n        };\n        const fieldList = tableFields();\n        const tableExampleLine = tableExamples();\n        const tableGuideText = tableGuide();\n        const lines = [];\n        if (helpIsEn) {\n            lines.push(cleanCol ? `Field info: ${esc(cleanCol)} [${esc(cleanTbl)}]` : `Table info: ${esc(cleanTbl)}`);\n            if (activity) lines.push('Company context: ' + esc(activity) + '.');\n            if (cleanCol) {\n                lines.push('What it is for: ' + esc(desc || (isProductField ? 'Short product name used in catalog, order flows, search and AI replies.' : (isServiceProduct ? 'Product or material associated with the service.' : 'Field used by this Shop-Service workflow.'))));\n                if (relationLine) lines.push(esc(relationLine));\n                if (isProductField) lines.push('How to fill it in: use a clear, short, recognizable product name. Put details in Description, price in Price\/Prezzo and commercial rules in Instructions\/Istruzioni.');\n                else if (isServiceProduct) lines.push('How to fill it in: select one or more products\/materials used by the service from the Products table, or write a clear manual value if needed.');\n                else lines.push('How to fill it in: use a clear value consistent with the table and the other existing records.');\n                const ex = operationalExample(); if (ex) lines.push(esc(ex));\n                lines.push('Checks: after saving, verify catalog\/search, order or booking flow, and linked images\/documents if used.');\n            } else {\n                lines.push('Operational use: ' + esc(tableGuideText || 'this table feeds configuration, AI replies and Shop-Service workflows.'));\n                const ex = operationalExample(); if (ex) lines.push(esc(ex));\n            }\n            return lines.join('<br>');\n        }\n        lines.push(cleanCol ? `Info campo: ${esc(cleanCol)} [${esc(cleanTbl)}]` : `Info tabella: ${esc(cleanTbl)}`);\n        if (activity) lines.push('Contesto attivit\u00e0 ditta: ' + esc(activity) + '.');\n        if (cleanCol) {\n            lines.push('A cosa serve: ' + esc(desc || (isProductField ? 'Nome breve del prodotto usato in catalogo, ordini, ricerca e risposte AI.' : (isServiceProduct ? 'Prodotto o materiale associato al servizio.' : 'Campo usato dal flusso operativo Shop-Service.'))));\n            if (relationLine) lines.push(esc(relationLine));\n            if (isProductField) lines.push('Come compilarlo: usare un nome prodotto breve, chiaro e riconoscibile. Mettere i dettagli in Descrizione, il prezzo in Prezzo e le regole commerciali in Istruzioni.');\n            else if (isServiceProduct) lines.push('Come compilarlo: selezionare uno o pi\u00f9 prodotti\/materiali usati dal servizio dalla tabella Prodotti, oppure scrivere un valore manuale chiaro se necessario.');\n            else lines.push('Come compilarlo: usare un valore chiaro, coerente con la tabella e con gli altri record presenti.');\n            const ex = operationalExample(); if (ex) lines.push(esc(ex));\n            lines.push('Controlli: dopo il salvataggio verificare ricerca\/catalogo, flusso ordine o prenotazione e immagini\/documenti collegati se presenti.');\n        } else {\n            lines.push('Uso operativo: ' + esc(tableGuideText || 'la tabella alimenta configurazione, risposte AI e flussi Shop-Service.'));\n            const ex = operationalExample(); if (ex) lines.push(esc(ex));\n        }\n        return lines.join('<br>');\n    };\n\n    window.askColumnHelp = function(tbl, col) {\n        const dbVal = gE('ti-ditta').value || 'NEW_DB';\n        const cleanTbl = window.tiSanitizeColumnHelpText357(String(tbl || '').trim());\n        const cleanCol = window.tiSanitizeColumnHelpText357(String(col || '').replace(\/\\\\'\/g, \"'\").replace(\/[\"\u201c\u201d]\/g, '').trim());\n        const helpIsEn = String(window.currLang || 'it').toLowerCase().indexOf('en') === 0;\n        const helpOv = gE('ti-col-help-ov');\n        const helpReqId = String(Date.now()) + '-' + Math.random().toString(36).slice(2);\n        window.tiColumnHelpActiveRequestId = helpReqId;\n        if (helpOv) {\n            helpOv.setAttribute('data-ti-help-request-id', helpReqId);\n            helpOv.setAttribute('data-ti-help-table', cleanTbl);\n            helpOv.setAttribute('data-ti-help-column', cleanCol);\n        }\n        gE('col-help-title').innerText = cleanCol\n            ? (helpIsEn ? `Field info: ${cleanCol} [${cleanTbl}]` : `Info campo: ${cleanCol} [${cleanTbl}]`)\n            : (helpIsEn ? `Table info: ${cleanTbl}` : `Info tabella: ${cleanTbl}`);\n        if (window.tiUpdateColumnHelpCompanyHeader) window.tiUpdateColumnHelpCompanyHeader();\n        const contentBox = gE('col-help-content');\n        contentBox.innerHTML = cleanCol\n            ? (helpIsEn ? `<i>\u23f3 I am checking the manual, schema and available data for field <b>${cleanCol}<\/b> in table <b>${cleanTbl}<\/b>...<\/i>` : `<i>\u23f3 Consulto manuale, schema e dati disponibili per il campo <b>${cleanCol}<\/b> della tabella <b>${cleanTbl}<\/b>...<\/i>`)\n            : (helpIsEn ? `<i>\u23f3 I am checking the manual, schema and available data for table <b>${cleanTbl}<\/b>...<\/i>` : `<i>\u23f3 Consulto manuale, schema e dati disponibili per la tabella <b>${cleanTbl}<\/b>...<\/i>`);\n        window.openModal('ti-col-help-ov');\n        if (window.tiPromoteModalFront355) window.tiPromoteModalFront355('ti-col-help-ov');\n        const fd = new FormData(); fd.append('ti_action', 'ti_ai_chat_start'); fd.append('db', dbVal);\n        const langInstruction = helpIsEn ? ' Rispondi in inglese, senza aggiungere la versione italiana.' : ' Rispondi in italiano, senza aggiungere la versione inglese.';\n        const helpRequest = cleanCol\n            ? `AI_MANUAL_HELP: Dai una risposta operativa reale e specifica sul campo \"${cleanCol}\" nella tabella \"${cleanTbl}\". Indica a cosa serve, come compilarlo e aggiungi se utile un Esempio operativo tratto dal manuale, dai dati non sensibili disponibili o da pattern analoghi aggregati di altre ditte. Non citare mai dati anagrafici o identificativi di altre ditte: no nomi ditta, ragioni sociali, persone, email, telefoni, indirizzi, PIVA, codici fiscali, username, IBAN, link privati o codici attivazione. Non citare campi vuoti, colonne senza nome o frasi di comodo tipo risposta rapida locale. Non citare privacy, anonimizzazione, fonti, prompt o questa istruzione: mostra solo la risposta operativa per l'utente.${langInstruction}`\n            : `AI_MANUAL_HELP: Dai una risposta operativa reale e specifica sulla tabella \"${cleanTbl}\". Indica a cosa serve e aggiungi se utile un Esempio operativo tratto dal manuale, dai dati non sensibili disponibili o da pattern analoghi aggregati di altre ditte. Non citare mai dati anagrafici o identificativi di altre ditte: no nomi ditta, ragioni sociali, persone, email, telefoni, indirizzi, PIVA, codici fiscali, username, IBAN, link privati o codici attivazione. Non citare campi vuoti, colonne senza nome o frasi di comodo tipo risposta rapida locale. Non citare privacy, anonimizzazione, fonti, prompt o questa istruzione: mostra solo la risposta operativa per l'utente.${langInstruction}`;\n        fd.append('text', helpRequest);\n        try {\n            if (contentBox && window.tiColumnHelpFallbackHtml436) {\n                contentBox.innerHTML = window.tiColumnHelpFallbackHtml436(cleanTbl, cleanCol, helpIsEn, helpRequest);\n                if (window.tiPromoteModalFront355) window.tiPromoteModalFront355('ti-col-help-ov');\n            }\n        } catch(e) {}\n        let helpFallbackShown = true;\n        const helpFallbackTimer = setTimeout(function(){\n            try {\n                if (window.tiColumnHelpActiveRequestId !== helpReqId) return;\n                if (helpOv && helpOv.getAttribute('data-ti-help-request-id') !== helpReqId) return;\n                if (!contentBox) return;\n                helpFallbackShown = true;\n                contentBox.innerHTML = window.tiColumnHelpFallbackHtml436 ? window.tiColumnHelpFallbackHtml436(cleanTbl, cleanCol, helpIsEn, helpRequest) : contentBox.innerHTML;\n                if (window.tiPromoteModalFront355) window.tiPromoteModalFront355('ti-col-help-ov');\n            } catch(e) {}\n        }, 2500);\n        const helpAjaxPromise = (window.postFormDataJsonSafe\n            ? window.postFormDataJsonSafe(window.tiUrl, fd, {tiNoLongProcessSignal: true})\n            : window.fetchJsonSafe(window.tiUrl, {method: 'POST', body: fd, tiNoLongProcessSignal: true}));\n        helpAjaxPromise.then(res => {\n            clearTimeout(helpFallbackTimer);\n            if (window.tiColumnHelpActiveRequestId !== helpReqId) return;\n            if (helpOv && helpOv.getAttribute('data-ti-help-request-id') !== helpReqId) return;\n            if(res.success) {\n                gE('col-help-title').innerText = cleanCol\n                    ? (helpIsEn ? `Field info: ${cleanCol} [${cleanTbl}]` : `Info campo: ${cleanCol} [${cleanTbl}]`)\n                    : (helpIsEn ? `Table info: ${cleanTbl}` : `Info tabella: ${cleanTbl}`);\n                const rawReply = String((res.data && res.data.reply) ? res.data.reply : '');\n                const replyHtml = window.tiSanitizeColumnHelpText357(rawReply).replace(\/\\n\/g, '<br>');\n                const aiUsed = !!(res.data && (res.data.field_help_ai_used === true || String(res.data.field_help_source || '').toLowerCase() === 'ai'));\n                if (contentBox) contentBox.setAttribute('data-ti-help-source', aiUsed ? 'ai' : 'fallback');\n                contentBox.innerHTML = replyHtml || (window.tiColumnHelpFallbackHtml436 ? window.tiColumnHelpFallbackHtml436(cleanTbl, cleanCol, helpIsEn, helpRequest) : '');\n                try { if (window.applyLayoutLanguage) window.applyLayoutLanguage(false); } catch(e) {}\n                if (window.tiPromoteModalFront355) window.tiPromoteModalFront355('ti-col-help-ov');\n            } else {\n                if (contentBox) contentBox.setAttribute('data-ti-help-source', 'fallback');\n                contentBox.innerHTML = window.tiColumnHelpFallbackHtml436 ? window.tiColumnHelpFallbackHtml436(cleanTbl, cleanCol, helpIsEn, helpRequest) : (helpIsEn ? \"<span style='color:#ef4444;'>Error while searching the manual.<\/span>\" : \"<span style='color:#ef4444;'>Errore durante la ricerca nel manuale.<\/span>\");\n                if (window.tiPromoteModalFront355) window.tiPromoteModalFront355('ti-col-help-ov');\n            }\n        }).catch(e => {\n            clearTimeout(helpFallbackTimer);\n            if (window.tiColumnHelpActiveRequestId !== helpReqId) return;\n            if (helpOv && helpOv.getAttribute('data-ti-help-request-id') !== helpReqId) return;\n            if (contentBox) contentBox.setAttribute('data-ti-help-source', 'fallback');\n            contentBox.innerHTML = window.tiColumnHelpFallbackHtml436 ? window.tiColumnHelpFallbackHtml436(cleanTbl, cleanCol, helpIsEn, helpRequest) : (helpIsEn ? \"<span style='color:#ef4444;'>Error while searching the manual.<\/span>\" : \"<span style='color:#ef4444;'>Errore durante la ricerca nel manuale.<\/span>\");\n            if (window.tiPromoteModalFront355) window.tiPromoteModalFront355('ti-col-help-ov');\n        });\n\n    };\n\n    window.globalActionProgressState = { timer: null, percent: 0, target: 92, active: false };\n    window.setGlobalActionProgress = function(percent, text) {\n        const wrap = gE('global-action-progress-wrap'); const bar = gE('global-action-progress-bar'); const label = gE('global-action-progress-text');\n        if (wrap) wrap.style.display = 'block';\n        const pct = Math.max(0, Math.min(100, parseInt(percent, 10) || 0));\n        if (bar) bar.style.width = pct + '%';\n        if (label) label.innerText = pct + '% - ' + (text || 'Elaborazione in corso...');\n        window.globalActionProgressState.percent = pct;\n    };\n    window.startGlobalActionProgress = function(text) {\n        clearInterval(window.globalActionProgressState.timer);\n        window.globalActionProgressState.active = true;\n        window.globalActionProgressState.percent = 3;\n        window.globalActionProgressState.target = 94;\n        window.setGlobalActionProgress(3, text || 'Avvio elaborazione globale...');\n        window.globalActionProgressState.timer = setInterval(function(){\n            if (!window.globalActionProgressState.active) return;\n            let pct = window.globalActionProgressState.percent;\n            if (pct >= window.globalActionProgressState.target) return;\n            pct += (pct < 20 ? 5 : (pct < 45 ? 4 : (pct < 70 ? 3 : 1)));\n            if (pct > window.globalActionProgressState.target) pct = window.globalActionProgressState.target;\n            window.setGlobalActionProgress(pct, 'Analisi AI e scrittura sui database in corso...');\n        }, 650);\n    };\n    window.finishGlobalActionProgress = function(success, text) {\n        clearInterval(window.globalActionProgressState.timer);\n        window.globalActionProgressState.active = false;\n        window.setGlobalActionProgress(success ? 100 : Math.max(0, window.globalActionProgressState.percent || 0), text || (success ? 'Operazione completata' : 'Operazione interrotta'));\n    };\n    window.resetGlobalActionProgress = function() {\n        clearInterval(window.globalActionProgressState.timer);\n        window.globalActionProgressState.active = false;\n        window.globalActionProgressState.percent = 0;\n        const wrap = gE('global-action-progress-wrap'); const bar = gE('global-action-progress-bar'); const label = gE('global-action-progress-text');\n        if (wrap) wrap.style.display = 'none';\n        if (bar) bar.style.width = '0%';\n        if (label) label.innerText = '0% - In attesa di avvio...';\n    };\n\n    \/\/ 30.9.664: anche la modifica globale usa avanzamento monotono a tempo trascorso.\n    (function installTiRobustGlobalProgress327(){\n        if (window.__tiRobustGlobalProgress327Installed) return;\n        window.__tiRobustGlobalProgress327Installed = true;\n        function gpClamp(v){ v = parseInt(v, 10); if (!isFinite(v) || isNaN(v)) v = 0; return Math.max(0, Math.min(100, v)); }\n        function gpSet(pct, text){\n            pct = gpClamp(pct);\n            const st = window.globalActionProgressState || (window.globalActionProgressState = {});\n            const cur = gpClamp(st.percent || 0);\n            if (pct < 100 && cur > pct) pct = cur;\n            const wrap = gE('global-action-progress-wrap'); const bar = gE('global-action-progress-bar'); const label = gE('global-action-progress-text');\n            if (wrap) wrap.style.display = 'block';\n            if (bar) bar.style.width = pct + '%';\n            if (label) label.innerText = pct + '% - ' + (text || 'Elaborazione in corso...');\n            st.percent = pct;\n        }\n        if (typeof window.startGlobalActionProgress === 'function' && !window.startGlobalActionProgress.__tiRobustProgress327) {\n            const prevStart = window.startGlobalActionProgress;\n            window.startGlobalActionProgress = function(text){\n                const ret = prevStart.apply(this, arguments);\n                const st = window.globalActionProgressState || (window.globalActionProgressState = {});\n                st.startedAt327 = Date.now();\n                clearInterval(st.watchdog327);\n                st.watchdog327 = setInterval(function(){\n                    if (!st.active) { clearInterval(st.watchdog327); return; }\n                    const elapsed = Math.max(0, Date.now() - (st.startedAt327 || Date.now()));\n                    let pct = 3 + Math.min(1, elapsed \/ 60000) * 91;\n                    if (elapsed > 60000) pct = 94 + Math.min(1, (elapsed - 60000) \/ 90000) * 5;\n                    gpSet(Math.floor(pct), elapsed > 90000 ? 'Elaborazione lunga ancora in corso. Verifico il completamento lato server...' : 'Analisi AI e scrittura sui database in corso...');\n                }, 1000);\n                return ret;\n            };\n            window.startGlobalActionProgress.__tiRobustProgress327 = true;\n        }\n        if (typeof window.setGlobalActionProgress === 'function' && !window.setGlobalActionProgress.__tiRobustProgress327) {\n            const prevSet = window.setGlobalActionProgress;\n            window.setGlobalActionProgress = function(percent, text){\n                const st = window.globalActionProgressState || {};\n                let pct = gpClamp(percent);\n                const cur = gpClamp(st.percent || 0);\n                if (pct < 100 && cur > pct) pct = cur;\n                return prevSet.call(this, pct, text);\n            };\n            window.setGlobalActionProgress.__tiRobustProgress327 = true;\n        }\n        if (typeof window.finishGlobalActionProgress === 'function' && !window.finishGlobalActionProgress.__tiRobustProgress327) {\n            const prevFinish = window.finishGlobalActionProgress;\n            window.finishGlobalActionProgress = function(success, text){\n                const st = window.globalActionProgressState || {};\n                if (st.watchdog327) clearInterval(st.watchdog327);\n                return prevFinish.call(this, success, text || (success ? 'Operazione completata' : 'Operazione interrotta'));\n            };\n            window.finishGlobalActionProgress.__tiRobustProgress327 = true;\n        }\n    })();\n\n    window.doGlobalAction = function() {\n        const prompt = gE('global-action-prompt').value.trim();\n        if(!prompt) { window.tiAlert(\"Inserisci l'operazione che vuoi eseguire.\"); return; }\n        const warning = '<b>Attenzione: modifica globale database<\/b><br><br>' +\n            'Questa operazione puo modificare piu database delle ditte in una sola volta.<br>' +\n            'Prima dell esecuzione viene escluso sempre il template standard <b>shopservicemain.json<\/b>, che non verra modificato.<br><br>' +\n            '<b>Operazione richiesta:<\/b><br><span style=\"color:#fbbf24;white-space:pre-wrap;display:block;margin-top:6px;\">' + window.escapeHtml(prompt) + '<\/span><br>' +\n            'Confermi di voler procedere con cautela?';\n        window.tiConfirmYesNoAction(warning, function(){\n            window.doGlobalActionConfirmed(prompt);\n        }, null, 'CONFERMA MODIFICA', 'ANNULLA');\n    };\n\n    window.doGlobalActionConfirmed = function(prompt, previewToken, definitiveConfirm, tableScope) {\n        const btn = gE('global-action-btn'); const resBox = gE('global-action-res');\n        btn.innerText = definitiveConfirm ? \"\u23f3 Esecuzione definitiva in corso...\" : \"\u23f3 Generazione preview...\";\n        btn.disabled = true; resBox.style.display = 'block';\n        resBox.innerHTML = definitiveConfirm ? \"Esecuzione definitiva su tutti i database dopo preview approvata. Non chiudere la pagina.\" : \"Genero una preview di modifica della struttura del database su un database campione. Nessuna struttura del database viene modificata in questa fase. Sono eventualmente modificati i soli valori dei campi del database.\";\n        window.startGlobalActionProgress(definitiveConfirm ? 'Avvio modifica globale definitiva...' : 'Generazione preview obbligatoria...');\n        const fd = new FormData(); fd.append('ti_action', 'ti_ai_global_action'); fd.append('prompt', prompt); fd.append('global_confirm', '1');\n        if (previewToken) fd.append('global_template_preview_token', previewToken);\n        if (definitiveConfirm) fd.append('global_template_confirm', '1');\n        if (tableScope) fd.append('global_table_scope', tableScope);\n        window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n            btn.innerText = \"Esegui Modifica su Tutti i DB\"; btn.disabled = false;\n            if(d.success) {\n                if (d.data && d.data.requires_table_scope) {\n                    window.finishGlobalActionProgress(false, 'Ambito tabelle richiesto. Nessun database modificato.');\n                    resBox.innerHTML = d.data.reply || 'Indica le tabelle prima di eseguire la modifica globale.';\n                    const answer = window.prompt(d.data.scope_prompt || 'Scrivi TUTTE oppure elenca le tabelle separate da virgola:', '');\n                    if (answer && String(answer).trim()) {\n                        window.doGlobalActionConfirmed(prompt, previewToken, definitiveConfirm, String(answer).trim());\n                    } else {\n                        resBox.innerHTML += '<br><br><span style=\"color:#f59e0b;\">Operazione sospesa. Nessun database e stato modificato.<\/span>';\n                    }\n                    return;\n                }\n                if (d.data && d.data.requires_template_preview) {\n                    window.finishGlobalActionProgress(false, 'Preview generata. In attesa di approvazione.');\n                    resBox.innerHTML = d.data.reply || 'Preview generata. Nessun database modificato.';\n                    const previewHtml = d.data.preview_html || 'Preview generata. Confermi la modifica definitiva?';\n                    const token = d.data.preview_token || '';\n                    window.tiConfirmYesNoAction(previewHtml, function(){\n                        window.doGlobalActionConfirmed(prompt, token, true);\n                    }, function(){\n                        resBox.innerHTML = '<span style=\"color:#f59e0b;\">Operazione annullata. Nessun database e stato modificato.<\/span>';\n                    }, 'APPROVA MODIFICA DEFINITIVA', 'ANNULLA');\n                    return;\n                }\n                let finalMsg = 'Modifica globale completata';\n                if (d.data && d.data.stats) {\n                    const s = d.data.stats;\n                    finalMsg = 'Completato: DB ' + (s.db_modified || 0) + ', tabelle ' + (s.tables_touched || 0) + ', campi ' + (s.fields_touched || 0);\n                }\n                window.finishGlobalActionProgress(true, finalMsg);\n                resBox.innerHTML = d.data.reply;\n            } else {\n                window.finishGlobalActionProgress(false, 'Errore modifica globale');\n                resBox.innerHTML = \"<span style='color:#ef4444;'>Errore: \" + d.data.message + \"<\/span>\";\n            }\n        }).catch(e => {\n            btn.innerText = \"Esegui Modifica su Tutti i DB\"; btn.disabled = false;\n            if (e && (e.userCancelled || window.isLongAIProcessCancelled())) {\n                window.clearLongAIProcess();\n                window.finishGlobalActionProgress(false, 'Operazione interrotta dall utente');\n                resBox.innerHTML = \"<span style='color:#f59e0b;'>Operazione interrotta dall utente.<\/span>\";\n                return;\n            }\n            window.finishGlobalActionProgress(false, 'Errore di rete');\n            resBox.innerHTML = \"<span style='color:#ef4444;'>Errore di rete.<\/span>\";\n        });\n    };\n\n\n    window.activityReportStorageKey = function() {\n        const u = window.tiUser || 'guest';\n        const db = (gE('ti-ditta') && gE('ti-ditta').value) ? gE('ti-ditta').value : (window.tiForced || 'global');\n        return 'ti_activity_report_flags_' + u + '_' + db;\n    };\n    window.activityReportMetricOptions = ['Prezzo','Quantit\u00e0','Totale'];\n    window.getDefaultActivityMetricFlags = function() {\n        return { Prezzo: false, Quantit\u00e0: false, Totale: true };\n    };\n    window.userCanSeeConfigurationActivities = function() {\n        const role = String(window.tiRole || '').toLowerCase();\n        const user = String(window.tiUser || '').toLowerCase();\n        return user === 'ssglobaladmin' || role.includes('amministratore') || role.includes('configuratore');\n    };\n    window.isConfigurationActivityLike = function(row) {\n        if (!row || typeof row !== 'object') return false;\n        const vals = [];\n        Object.keys(row).forEach(function(k){\n            if (\/(descrizione|attivit|attivit\u00e0|dettaglio|note|nota|tipo|stato)\/i.test(String(k || ''))) vals.push(row[k]);\n        });\n        return vals.some(function(v){\n            if (v === null || v === undefined) return false;\n            const txt = String(v).trim().toLowerCase();\n            return \/^configurazione\\b\/.test(txt) || txt.indexOf('attivit\u00e0 di configurazione') !== -1 || txt.indexOf('attivita di configurazione') !== -1;\n        });\n    };\n    window.getDefaultActivityExcludeConfig = function() { return false; };\n    window.loadActivityReportPrefs = function() {\n        try {\n            const raw = localStorage.getItem(window.activityReportStorageKey());\n            if (!raw) return {colFlags:{}, rowFlags:{}, metricFlags: window.getDefaultActivityMetricFlags(), groupField: 'Username', excludeConfigActivities: window.getDefaultActivityExcludeConfig()};\n            const d = JSON.parse(raw);\n            const metricFlags = Object.assign(window.getDefaultActivityMetricFlags(), d && d.metricFlags ? d.metricFlags : {});\n            return { colFlags: d && d.colFlags ? d.colFlags : {}, rowFlags: d && d.rowFlags ? d.rowFlags : {}, metricFlags, groupField: (d && d.groupField ? d.groupField : 'Username'), excludeConfigActivities: !!(d && d.excludeConfigActivities) };\n        } catch(e) { return {colFlags:{}, rowFlags:{}, metricFlags: window.getDefaultActivityMetricFlags(), groupField: 'Username', excludeConfigActivities: window.getDefaultActivityExcludeConfig()}; }\n    };\n    window.saveActivityReportPrefs = function() {\n        try {\n            localStorage.setItem(window.activityReportStorageKey(), JSON.stringify({\n                colFlags: (window.activityReportState && window.activityReportState.colFlags) || {},\n                rowFlags: (window.activityReportState && window.activityReportState.rowFlags) || {},\n                metricFlags: (window.activityReportState && window.activityReportState.metricFlags) || window.getDefaultActivityMetricFlags(),\n                groupField: (window.activityReportState && window.activityReportState.groupField) || 'Username',\n                excludeConfigActivities: !!(window.activityReportState && window.activityReportState.excludeConfigActivities)\n            }));\n        } catch(e) {}\n    };\n    window.activityReportState = { sortCol: 'DataOra', sortDir: 'desc', filters: {}, view: 'table', colFlags: {}, rowFlags: {}, metricFlags: window.getDefaultActivityMetricFlags(), groupField: 'Username', excludeConfigActivities: window.getDefaultActivityExcludeConfig() };\n    window.activityReportCols = ['Username','Descrizione','ID QRcode','Prezzo','Quantit\u00e0','Esito','Operatore','Dispositivo','Sito','Prodotto','Istruzioni','Durata','Data','Ora','Nota','Stato','Totale'];\n    window.activityReportGroupOptions = window.activityReportCols.slice();\n    window.getActivityAvailableGroupFields = function() {\n        const visibleCols = window.getActivityVisibleCols();\n        const allowed = window.activityReportGroupOptions.filter(k => visibleCols.includes(k));\n        return allowed.length ? allowed : ['Username'];\n    };\n    window.getActivitySelectedGroupField = function() {\n        const available = window.getActivityAvailableGroupFields();\n        const current = (window.activityReportState && window.activityReportState.groupField) || 'Username';\n        return available.includes(current) ? current : (available[0] || 'Prodotto');\n    };\n    window.setActivityGroupField = function(field) {\n        window.activityReportState.groupField = field;\n        window.saveActivityReportPrefs();\n        window.renderActivityReport();\n    };\n    window.getActivityAvailableMetrics = function() {\n        const visibleCols = window.getActivityVisibleCols();\n        const allowed = window.activityReportMetricOptions.filter(k => visibleCols.includes(k));\n        return allowed.length ? allowed : ['Totale'];\n    };\n    window.getActivitySelectedMetrics = function() {\n        const flags = (window.activityReportState && window.activityReportState.metricFlags) || window.getDefaultActivityMetricFlags();\n        const available = window.getActivityAvailableMetrics();\n        const selected = available.filter(k => !!flags[k]);\n        return selected.length ? selected : [available[0] || 'Totale'];\n    };\n    window.setActivityMetricFlag = function(metric, checked) {\n        if (!window.activityReportState.metricFlags) window.activityReportState.metricFlags = window.getDefaultActivityMetricFlags();\n        window.activityReportState.metricFlags[metric] = !!checked;\n        const available = window.getActivityAvailableMetrics();\n        const hasSelected = available.some(k => !!window.activityReportState.metricFlags[k]);\n        if (!hasSelected && available.length) window.activityReportState.metricFlags[available[0]] = true;\n        window.saveActivityReportPrefs();\n        window.renderActivityReport();\n    };\n    window.setActivityExcludeConfigActivities = function(checked) {\n        if (!window.activityReportState) window.activityReportState = {};\n        window.activityReportState.excludeConfigActivities = !!checked;\n        window.saveActivityReportPrefs();\n        window.renderActivityReport();\n    };\n    window.parseReportNum = function(v, defVal) {\n        if (v === null || v === undefined || v === '') return defVal || 0;\n        let s = String(v).trim();\n        if (!s) return defVal || 0;\n        s = s.replace(\/[^\\d,\\.\\-]\/g, '');\n        if (s.indexOf(',') !== -1 && s.indexOf('.') !== -1) s = s.replace(\/\\.\/g, '').replace(',', '.');\n        else if (s.indexOf(',') !== -1) s = s.replace(',', '.');\n        const n = parseFloat(s);\n        return isNaN(n) ? (defVal || 0) : n;\n    };\n    window.getActivityRowsNormalized = function() {\n        if(!window.currentDbData || !window.currentDbData.Tabelle) return [];\n        const raw = window.currentDbData.Tabelle['Attivita'] || window.currentDbData.Tabelle['Attivit\u00e0'] || [];\n        const rows = Array.isArray(raw) ? raw : Object.values(raw || {});\n        const normalized = rows.map((r, idx) => {\n            r = r || {};\n            const isConfigActivity = window.isConfigurationActivityLike ? window.isConfigurationActivityLike(r) : false;\n            const prezzo = window.parseReportNum(r.Prezzo || r.Totale || 0, 0);\n            const qta = window.parseReportNum(r['Quantit\u00e0'] || r.Quantita || 1, 1);\n            const data = r.Data || '';\n            const ora = r.Ora || '';\n            const descrizione = r['Descrizione attivit\u00e0'] || r['Descrizione attivita'] || r.Descrizione || r.Dettaglio || r.Note || r.Nota || '';\n            let dataora = 0;\n            const m = String(data).match(\/^(\\d{2})\\\/(\\d{2})\\\/(\\d{4})$\/);\n            const hm = String(ora).match(\/^(\\d{2}):(\\d{2})\/);\n            if (m) dataora = new Date(parseInt(m[3],10), parseInt(m[2],10)-1, parseInt(m[1],10), hm?parseInt(hm[1],10):0, hm?parseInt(hm[2],10):0, 0).getTime();\n            const rowKey = String(r.ID || '') + '|' + String(r.Username || r.Utente || r.user || '') + '|' + String(data) + '|' + String(ora) + '|' + String(descrizione || '') + '|' + idx;\n            return {\n                Username: r.Username || r.Utente || r.user || '',\n                Descrizione: descrizione,\n                Prezzo: prezzo,\n                Quantit\u00e0: qta,\n                Esito: r.Esito || '',\n                Operatore: r.Operatore || '',\n                Dispositivo: r.Dispositivo || '',\n                Sito: r.Sito || '',\n                Prodotto: r.Prodotto || '',\n                Istruzioni: r.Istruzioni || '',\n                Durata: r.Durata || '',\n                Data: data,\n                Ora: ora,\n                Nota: r.Nota || '',\n                Stato: r.Stato || '',\n                Totale: prezzo * qta,\n                'ID QRcode': r['ID QRcode'] || r['ID QR code'] || r.ID_QRcode || r.QRcode_ID || '',\n                __dataora: dataora,\n                __rowKey: rowKey,\n                __isConfigActivity: isConfigActivity\n            };\n        });\n        if (!(window.userCanSeeConfigurationActivities && window.userCanSeeConfigurationActivities())) {\n            return normalized.filter(function(r){ return !r.__isConfigActivity; });\n        }\n        return normalized;\n    };\n    window.findActivityRowByQrCode = function(qrCode) {\n        qrCode = String(qrCode || '').trim();\n        if (!qrCode || !window.currentDbData || !window.currentDbData.Tabelle) return null;\n        const tables = window.currentDbData.Tabelle;\n        const raw = tables['Attivita'] || tables['Attivit\u00e0'] || tables['attivita'] || tables['attivit\u00e0'] || [];\n        const rows = Array.isArray(raw) ? raw : Object.values(raw || {});\n        const norm = function(v){ return String(v == null ? '' : v).trim(); };\n        const keyMatch = function(k){ return \/^(id\\s*qr\\s*code|id\\s*qrcode|id_qrcode|qrcode\\s*id)$\/i.test(String(k || '').replace(\/[_-]+\/g,' ')); };\n        for (let i = 0; i < rows.length; i++) {\n            const r = rows[i] || {};\n            for (const k of Object.keys(r)) {\n                if (!keyMatch(k)) continue;\n                if (norm(r[k]) === qrCode) return {row:r, index:i, key:k};\n            }\n        }\n        return null;\n    };\n    window.showActivityFromQrIfRequested = function() {\n        const qrCode = String(window.tiForcedActivityQr || '').trim();\n        if (!qrCode || window.__tiActivityQrShown) return;\n        const show = function() {\n            const hit = window.findActivityRowByQrCode ? window.findActivityRowByQrCode(qrCode) : null;\n            if (!hit || !hit.row) return false;\n            window.__tiActivityQrShown = true;\n            const esc = window.tiCompanyHelpEscape || function(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); };\n            const r = hit.row;\n            const pick = function(names){ for (const n of names) { if (r[n] !== undefined && String(r[n]).trim() !== '') return String(r[n]); const found = Object.keys(r).find(k => String(k).toLowerCase() === String(n).toLowerCase()); if (found && String(r[found]).trim() !== '') return String(r[found]); } return ''; };\n            const lines = [];\n            lines.push('<b>Attivit\u00e0 richiamata da QRcode<\/b>');\n            lines.push('ID QRcode: ' + esc(qrCode));\n            const id = pick(['ID','Id','id']); if (id) lines.push('ID attivit\u00e0: ' + esc(id));\n            const data = pick(['Data','Data attivit\u00e0','Data attivita']); const ora = pick(['Ora','Ora attivit\u00e0','Ora attivita']); if (data || ora) lines.push('Data\/Ora: ' + esc((data + ' ' + ora).trim()));\n            const desc = pick(['Descrizione attivit\u00e0','Descrizione attivita','Descrizione','Attivita','Attivit\u00e0','Dettaglio','Note','Nota']); if (desc) lines.push('Descrizione: ' + esc(desc));\n            const esito = pick(['Esito','Stato']); if (esito) lines.push('Esito\/Stato: ' + esc(esito));\n            const user = pick(['Username','Utente','user']); if (user) lines.push('Utente: ' + esc(user));\n            const prod = pick(['Prodotto','Servizio']); if (prod) lines.push('Prodotto\/Servizio: ' + esc(prod));\n            if (typeof window.tiAlert === 'function') window.tiAlert(lines.join('<br>'));\n            else alert(lines.map(function(x){ return x.replace(\/<[^>]+>\/g,''); }).join('\\n'));\n            return true;\n        };\n        if (show()) return;\n        const dbSel = document.getElementById('ti-ditta');\n        const db = dbSel ? String(dbSel.value || '').trim() : (window.tiForced || '');\n        if (!db || db === 'NEW_DB' || !window.fetchJsonSafe) return;\n        const fd = new FormData(); fd.append('ti_action','ti_ai_get_db_data'); fd.append('db', db);\n        window.fetchJsonSafe(window.tiUrl || window.location.href, {method:'POST', body:fd, tiNoLongProcessSignal:true}).then(function(res){\n            if (res && res.success && res.data) { window.currentDbData = res.data; show(); }\n        }).catch(function(){});\n    };\n    window.getActivityIncludedRows = function() {\n        return window.getFilteredActivityRows(false);\n    };\n    window.getActivityVisibleCols = function() {\n        const colFlags = (window.activityReportState && window.activityReportState.colFlags) || {};\n        const visible = window.activityReportCols.filter(c => !colFlags[c]);\n        return visible.length ? visible : ['Username','Descrizione','Prezzo','Quantit\u00e0','Totale'];\n    };\n    window.getActivityReportTitle = function(rows) {\n        rows = rows || window.getActivityIncludedRows();\n        let minTs = null, maxTs = null;\n        rows.forEach(r => {\n            if (r && typeof r.__dataora === 'number' && r.__dataora > 0) {\n                if (minTs === null || r.__dataora < minTs) minTs = r.__dataora;\n                if (maxTs === null || r.__dataora > maxTs) maxTs = r.__dataora;\n            }\n        });\n        const ditta = (window.currentDittaName && String(window.currentDittaName).trim()) ? String(window.currentDittaName).trim() : 'Ditta';\n        const fmtDate = function(ts) {\n            if (!ts) return '';\n            const d = new Date(ts);\n            return String(d.getDate()).padStart(2,'0') + '\/' + String(d.getMonth()+1).padStart(2,'0') + '\/' + d.getFullYear();\n        };\n        const fromDate = fmtDate(minTs);\n        const toDate = fmtDate(maxTs);\n        return (fromDate && toDate) ? ('Report attivit\u00e0 \u2013 ' + ditta + ' periodo ' + fromDate + ' - ' + toDate) : ('Report attivit\u00e0 \u2013 ' + ditta);\n    };\n    window.getFilteredActivityRows = function(includeExcludedRows) {\n        let rows = window.getActivityRowsNormalized();\n        const state = window.activityReportState || {filters:{}, rowFlags:{}};\n        const rowFlags = state.rowFlags || {};\n        if (state.excludeConfigActivities && window.userCanSeeConfigurationActivities && window.userCanSeeConfigurationActivities()) {\n            rows = rows.filter(function(r){ return !r.__isConfigActivity; });\n        }\n        rows = rows.filter(r => Object.entries(state.filters || {}).every(([k,v]) => {\n            if (!v) return true;\n            return String(r[k] ?? '').toLowerCase().includes(String(v).toLowerCase());\n        }));\n        if (!includeExcludedRows) rows = rows.filter(r => !rowFlags[r.__rowKey]);\n        const dir = state.sortDir === 'asc' ? 1 : -1;\n        const col = state.sortCol || 'DataOra';\n        rows.sort((a,b) => {\n            const av = col === 'DataOra' ? a.__dataora : a[col];\n            const bv = col === 'DataOra' ? b.__dataora : b[col];\n            if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir;\n            return String(av ?? '').localeCompare(String(bv ?? ''), 'it', {numeric:true, sensitivity:'base'}) * dir;\n        });\n        return rows;\n    };\n    window.setActivityReportFilter = function(col, val, inputEl) {\n        window.activityReportState.filters[col] = val;\n        window.activityReportState.filterFocus = {\n            col,\n            start: inputEl && typeof inputEl.selectionStart === 'number' ? inputEl.selectionStart : null,\n            end: inputEl && typeof inputEl.selectionEnd === 'number' ? inputEl.selectionEnd : null\n        };\n        window.renderActivityReport();\n    };\n    window.sortActivityReport = function(col) {\n        if (window.activityReportState.sortCol === col) window.activityReportState.sortDir = window.activityReportState.sortDir === 'asc' ? 'desc' : 'asc';\n        else { window.activityReportState.sortCol = col; window.activityReportState.sortDir = 'asc'; }\n        window.renderActivityReport();\n    };\n    window.setActivityReportView = function(view) { window.activityReportState.view = view; window.renderActivityReport(); };\n    window.toggleActivityReportColumn = function(col, ev) {\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); }\n        window.activityReportState.colFlags[col] = !window.activityReportState.colFlags[col];\n        window.saveActivityReportPrefs();\n        window.renderActivityReport();\n    };\n    window.toggleActivityReportRow = function(rowKey, ev) {\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); }\n        window.activityReportState.rowFlags[rowKey] = !window.activityReportState.rowFlags[rowKey];\n        window.saveActivityReportPrefs();\n        window.renderActivityReport();\n    };\n    window.exportActivityReportCsv = function() {\n        const rows = window.getFilteredActivityRows(false);\n        const cols = window.getActivityVisibleCols();\n        const csv = cols.join(';') + '\\n' + rows.map(r => cols.map(c => '\"' + String((c === 'Prezzo' || c === 'Quantit\u00e0' || c === 'Totale') ? window.fmtNum(r[c] || 0) : (r[c] ?? '')).replace(\/\"\/g,'\"\"') + '\"').join(';')).join('\\n');\n        const blob = new Blob([csv], {type:'text\/csv;charset=utf-8;'});\n        const a = document.createElement('a');\n        a.href = URL.createObjectURL(blob);\n        a.download = 'report-attivita.csv';\n        a.click();\n        setTimeout(() => URL.revokeObjectURL(a.href), 500);\n    };\n    window.printActivityReport = function() {\n        const view = (window.activityReportState && window.activityReportState.view) || 'table';\n        const w = window.open('', '_blank'); if(!w) return;\n        let bodyHtml = '';\n        if (view === 'table') {\n            const rows = window.getFilteredActivityRows(false);\n            const cols = window.getActivityVisibleCols();\n            const totals = {\n                Prezzo: rows.reduce((s,r)=>s+(r.Prezzo||0),0),\n                Quantit\u00e0: rows.reduce((s,r)=>s+(r.Quantit\u00e0||0),0),\n                Totale: rows.reduce((s,r)=>s+(r.Totale||0),0)\n            };\n            bodyHtml += '<h2>' + window.escapeHtml(window.getActivityReportTitle(rows)) + '<\/h2>';\n            bodyHtml += '<table><thead><tr>' + cols.map(c => '<th>' + window.escapeHtml(c) + '<\/th>').join('') + '<\/tr><\/thead><tbody>';\n            rows.forEach(r => {\n                bodyHtml += '<tr>' + cols.map(c => {\n                    const val = (c === 'Prezzo' || c === 'Quantit\u00e0' || c === 'Totale') ? window.fmtNum(r[c] || 0) : (r[c] || '');\n                    return '<td>' + window.escapeHtml(String(val)) + '<\/td>';\n                }).join('') + '<\/tr>';\n            });\n            bodyHtml += '<\/tbody><tfoot><tr>' + cols.map(c => {\n                if (c === 'Descrizione') return '<td>Righe: ' + rows.length + '<\/td>';\n                if (c === 'Prezzo') return '<td>' + window.fmtNum(totals.Prezzo) + '<\/td>';\n                if (c === 'Quantit\u00e0') return '<td>' + window.fmtNum(totals.Quantit\u00e0) + '<\/td>';\n                if (c === 'Totale') return '<td>' + window.fmtNum(totals.Totale) + '<\/td>';\n                return '<td><\/td>';\n            }).join('') + '<\/tr><\/tfoot><\/table>';\n        } else {\n            const rows = window.getFilteredActivityRows(false);\n            const canvasId = view === 'pie' ? 'ti-report-pie' : 'ti-report-trend';\n            const canvas = gE(canvasId);\n            const dataUrl = canvas ? canvas.toDataURL('image\/png') : '';\n            const title = window.getActivityReportTitle(rows);\n            bodyHtml += '<h2>' + window.escapeHtml(title) + '<\/h2>';\n            bodyHtml += dataUrl ? ('<div style=\"text-align:center;\"><img decoding=\"async\" src=\"' + dataUrl + '\" style=\"max-width:100%;height:auto;\" \/><\/div>') : '<p>Grafico non disponibile.<\/p>';\n        }\n        const printDocTitle = window.escapeHtml(window.getActivityReportTitle(window.getFilteredActivityRows(false)));\n        w.document.write('<html><head><title>' + printDocTitle + '<\/title><style>@page{size:A4 landscape;margin:12mm;} body{font-family:Arial,sans-serif;background:#fff;color:#000;padding:0;margin:0} h2{margin:0 0 12px 0;} table{border-collapse:collapse;width:100%;table-layout:auto} th,td{border:1px solid #bbb;padding:6px;font-size:11px;text-align:left;vertical-align:top;word-break:break-word} th{background:#eee} tfoot td{font-weight:bold;background:#f3f4f6} img{display:block;margin:0 auto;}<\/style><\/head><body>' + bodyHtml + '<\/body><\/html>');\n        w.document.close(); w.focus(); setTimeout(function(){ w.print(); }, 250);\n    };\n    window.getActivityMetricLabel = function(metrics) {\n        const arr = Array.isArray(metrics) && metrics.length ? metrics : ['Totale'];\n        if (arr.length === 1) {\n            if (arr[0] === 'Prezzo') return 'Prezzo';\n            if (arr[0] === 'Quantit\u00e0') return 'Quantit\u00e0';\n            if (arr[0] === 'Totale') return 'Totale';\n            return String(arr[0]);\n        }\n        return 'Somma valori (' + arr.join(', ') + ')';\n    };\n\n    window.drawPie3D = function(canvasId, rows) {\n        const cv = gE(canvasId); if(!cv) return; const ctx = cv.getContext('2d');\n        const selectedMetrics = window.getActivitySelectedMetrics();\n        const groupField = window.getActivitySelectedGroupField();\n        const metrics = (Array.isArray(selectedMetrics) && selectedMetrics.length) ? selectedMetrics : ['Totale'];\n        const cols = metrics.length > 1 ? 2 : 1;\n        const rowsCount = Math.ceil(metrics.length \/ cols);\n        const blockW = 400;\n        const blockH = 280;\n        cv.width = Math.max(860, cols * blockW + 40);\n        cv.height = Math.max(340, rowsCount * blockH + 40);\n        ctx.clearRect(0,0,cv.width,cv.height);\n\n        const colors=['#38bdf8','#22c55e','#f59e0b','#ef4444','#a855f7','#14b8a6','#f97316','#06b6d4','#84cc16','#fb7185','#f97316','#60a5fa'];\n\n        const buildEntriesForMetric = function(metric) {\n            const groups = {};\n            rows.forEach(r => {\n                const label = String(r[groupField] || 'N\/D').trim() || 'N\/D';\n                const value = window.parseReportNum(r[metric], 0) || 0;\n                groups[label] = (groups[label] || 0) + value;\n            });\n            return Object.entries(groups).filter(e => e[1] > 0).sort((a,b) => b[1]-a[1]).slice(0, 10);\n        };\n\n        let drewAtLeastOne = false;\n\n        metrics.forEach((metric, metricIndex) => {\n            const entries = buildEntriesForMetric(metric);\n            const col = metricIndex % cols;\n            const row = Math.floor(metricIndex \/ cols);\n            const originX = 20 + col * blockW;\n            const originY = 20 + row * blockH;\n            const cx = originX + 120;\n            const cy = originY + 120;\n            const rad = 72;\n            const depth = 14;\n\n            ctx.fillStyle = '#fff';\n            ctx.font = 'bold 13px Arial';\n            ctx.fillText(window.getActivityMetricLabel([metric]), originX + 8, originY + 18);\n\n            if (!entries.length) {\n                ctx.fillStyle = '#cbd5e1';\n                ctx.font = '12px Arial';\n                ctx.fillText('Nessun dato per ' + window.getActivityMetricLabel([metric]), originX + 8, originY + 42);\n                return;\n            }\n\n            drewAtLeastOne = true;\n            const total = entries.reduce((a,b)=>a+b[1],0);\n            let start = -Math.PI\/2;\n\n            entries.forEach(([label,val],i) => {\n                const ang = total > 0 ? (val\/total)*Math.PI*2 : 0;\n                ctx.fillStyle='rgba(0,0,0,0.35)';\n                ctx.beginPath(); ctx.moveTo(cx, cy+depth); ctx.arc(cx, cy+depth, rad, start, start+ang); ctx.closePath(); ctx.fill();\n                ctx.fillStyle=colors[i%colors.length];\n                ctx.beginPath(); ctx.moveTo(cx, cy); ctx.arc(cx, cy, rad, start, start+ang); ctx.closePath(); ctx.fill();\n\n                const mid = start + ang\/2;\n                const labelX = cx + Math.cos(mid) * (rad + 18);\n                const labelY = cy + Math.sin(mid) * (rad + 18);\n                const textX = labelX + (Math.cos(mid) >= 0 ? 8 : -150);\n\n                ctx.fillStyle='#fff';\n                ctx.font='12px Arial';\n                ctx.fillText(label, textX, labelY - 10);\n                ctx.fillStyle='#cbd5e1';\n                ctx.font='11px Arial';\n                ctx.fillText(window.getActivityMetricLabel([metric]) + ': ' + window.fmtNum(val), textX, labelY + 4);\n                start += ang;\n            });\n\n            ctx.fillStyle = '#94a3b8';\n            ctx.font = '11px Arial';\n            ctx.fillText('Raggruppa per: ' + groupField, originX + 8, originY + blockH - 12);\n        });\n\n        if (!drewAtLeastOne) {\n            ctx.fillStyle='#fff'; ctx.font='14px Arial'; ctx.fillText('Nessun dato',20,20);\n        }\n    };\n    window.drawTrend3D = function(canvasId, rows) {\n        const cv = gE(canvasId); if(!cv) return; const ctx = cv.getContext('2d');\n        const selectedMetrics = window.getActivitySelectedMetrics();\n        const groupField = window.getActivitySelectedGroupField();\n        const metrics = (Array.isArray(selectedMetrics) && selectedMetrics.length) ? selectedMetrics : ['Totale'];\n        const cols = metrics.length > 1 ? 2 : 1;\n        const rowsCount = Math.ceil(metrics.length \/ cols);\n        const blockW = 460;\n        const blockH = 320;\n        cv.width = Math.max(980, cols * blockW + 40);\n        cv.height = Math.max(380, rowsCount * blockH + 40);\n        ctx.clearRect(0,0,cv.width,cv.height);\n\n        const colors=['#38bdf8','#22c55e','#f59e0b','#ef4444','#a855f7','#14b8a6'];\n\n        const buildMetricTimeline = function(metric) {\n            const timeline = {};\n            const groupTotals = {};\n            rows.forEach(r => {\n                const timeKey = (r.Data || '') + ' ' + (r.Ora || '00:00');\n                const grp = String(r[groupField] || 'N\/D').trim() || 'N\/D';\n                const value = window.parseReportNum(r[metric], 0) || 0;\n                if (!timeline[timeKey]) timeline[timeKey] = {};\n                timeline[timeKey][grp] = (timeline[timeKey][grp] || 0) + value;\n                groupTotals[grp] = (groupTotals[grp] || 0) + value;\n            });\n            const entries = Object.entries(timeline).sort((a,b)=>a[0].localeCompare(b[0])).slice(-12);\n            const groups = Object.entries(groupTotals).sort((a,b)=>b[1]-a[1]).slice(0,4).map(e => e[0]);\n            return { entries, groups };\n        };\n\n        let drewAtLeastOne = false;\n\n        metrics.forEach((metric, metricIndex) => {\n            const data = buildMetricTimeline(metric);\n            const entries = data.entries;\n            const groups = data.groups;\n            const col = metricIndex % cols;\n            const row = Math.floor(metricIndex \/ cols);\n            const originX = 20 + col * blockW;\n            const originY = 20 + row * blockH;\n\n            ctx.fillStyle = '#fff';\n            ctx.font = 'bold 13px Arial';\n            ctx.fillText(window.getActivityMetricLabel([metric]), originX + 8, originY + 18);\n\n            if (!entries.length || !groups.length) {\n                ctx.fillStyle='#cbd5e1';\n                ctx.font='12px Arial';\n                ctx.fillText('Nessun dato per ' + window.getActivityMetricLabel([metric]), originX + 8, originY + 42);\n                return;\n            }\n\n            drewAtLeastOne = true;\n            const max = Math.max(1, ...entries.flatMap(e => groups.map(g => e[1][g] || 0)));\n            const baseY = originY + 240;\n            const left = originX + 52;\n            const step = 32;\n            const barW = Math.max(8, Math.floor(34 \/ Math.max(1, groups.length)));\n\n            ctx.strokeStyle='#94a3b8';\n            ctx.beginPath();\n            ctx.moveTo(left, originY + 32);\n            ctx.lineTo(left, baseY);\n            ctx.lineTo(left + step * (entries.length), baseY);\n            ctx.stroke();\n\n            entries.forEach(([label, metricsAtTime], i)=>{\n                const groupX = left + 10 + i * step;\n                groups.forEach((grp, gi) => {\n                    const val = metricsAtTime[grp] || 0;\n                    const h = (val\/max)*160;\n                    const x = groupX + gi*(barW+3);\n                    ctx.fillStyle='rgba(0,0,0,0.25)';\n                    ctx.fillRect(x+4, baseY-h+4, barW, h);\n                    ctx.fillStyle=colors[gi%colors.length];\n                    ctx.fillRect(x, baseY-h, barW, h);\n                    if (h > 0) {\n                        ctx.fillStyle='#fff';\n                        ctx.font='10px Arial';\n                        ctx.fillText(window.fmtNum(val), x-2, baseY-h-6);\n                    }\n                });\n                ctx.save();\n                ctx.fillStyle='#fff';\n                ctx.font='10px Arial';\n                ctx.translate(groupX, baseY+14);\n                ctx.rotate(-0.6);\n                ctx.fillText(label.slice(0,16),0,0);\n                ctx.restore();\n            });\n\n            groups.forEach((grp, gi) => {\n                const lx = originX + 8 + gi * 104;\n                const ly = originY + 34;\n                ctx.fillStyle = colors[gi%colors.length];\n                ctx.fillRect(lx, ly, 12, 12);\n                ctx.fillStyle = '#fff';\n                ctx.font = '11px Arial';\n                ctx.fillText(grp, lx + 18, ly + 10);\n            });\n\n            ctx.fillStyle = '#cbd5e1';\n            ctx.font = '11px Arial';\n            ctx.fillText('Raggruppa per: ' + groupField, originX + 8, originY + blockH - 12);\n        });\n\n        if (!drewAtLeastOne) {\n            ctx.fillStyle='#fff';\n            ctx.font='14px Arial';\n            ctx.fillText('Nessun dato',20,20);\n        }\n    };\n    window.renderActivityReport = function() {\n        const box = gE('ti-report-cnt'); if(!box) return;\n        const allRows = window.getFilteredActivityRows(true);\n        const rowFlags = (window.activityReportState && window.activityReportState.rowFlags) || {};\n        const includedRows = allRows.filter(r => !rowFlags[r.__rowKey]);\n        const titleEl = gE('ti-report-title');\n        if (titleEl) titleEl.innerText = '\ud83d\udcca ' + window.getActivityReportTitle(includedRows);\n        const allCols = window.activityReportCols;\n        const visibleCols = window.getActivityVisibleCols();\n        const hiddenCols = allCols.filter(c => !visibleCols.includes(c));\n        const view = window.activityReportState.view || 'table';\n        const availableMetrics = window.getActivityAvailableMetrics();\n        const selectedMetrics = window.getActivitySelectedMetrics();\n        const totals = {\n            Prezzo: includedRows.reduce((s,r)=>s+(r.Prezzo||0),0),\n            Quantit\u00e0: includedRows.reduce((s,r)=>s+(r.Quantit\u00e0||0),0),\n            Totale: includedRows.reduce((s,r)=>s+(r.Totale||0),0)\n        };\n        let html = '<div class=\"ti-report-toolbar\">'\n            + '<button type=\"button\" class=\"ti-btn\" onclick=\"window.setActivityReportView(\\'table\\')\">Elenco dati<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" onclick=\"window.setActivityReportView(\\'pie\\')\">Torte<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" onclick=\"window.setActivityReportView(\\'trend\\')\">Andamento<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" onclick=\"window.exportActivityReportCsv()\">Export CSV<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" onclick=\"window.printActivityReport()\">Stampa<\/button>'\n            + ((window.userCanSeeConfigurationActivities && window.userCanSeeConfigurationActivities())\n                ? '<label class=\"ti-report-config-filter\" title=\"Esclude dal report le righe attivit\u00e0 create dalla configurazione\"><input type=\"checkbox\" ' + ((window.activityReportState && window.activityReportState.excludeConfigActivities) ? 'checked' : '') + ' onchange=\"window.setActivityExcludeConfigActivities(this.checked)\"><span>Escludi configurazione<\/span><\/label>'\n                : '<span class=\"ti-report-config-filter\" title=\"Le attivit\u00e0 di configurazione sono visibili solo ad amministratori e configuratori\">Configurazione nascosta<\/span>')\n            + '<\/div>';\n        if (view !== 'table') {\n            const groupFields = window.getActivityAvailableGroupFields();\n            const selectedGroupField = window.getActivitySelectedGroupField();\n            html += '<div class=\"ti-report-hidden-cols\" style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap;\">';\n            if (view === 'pie' || view === 'trend') {\n                html += '<label class=\"ti-report-col-chip\" style=\"display:inline-flex;align-items:center;gap:6px;\">'\n                    + '<span>Raggruppa per<\/span>'\n                    + '<select class=\"ti-in\" style=\"width:auto;min-width:160px;padding:6px 8px;\" onchange=\"window.setActivityGroupField(this.value)\">'\n                    + groupFields.map(function(field){ return '<option value=\"' + window.escapeHtml(field) + '\" ' + (field === selectedGroupField ? 'selected' : '') + '>' + window.escapeHtml(field) + '<\/option>'; }).join('')\n                    + '<\/select>'\n                    + '<\/label>';\n            }\n            availableMetrics.forEach(function(metric){\n                const checked = selectedMetrics.includes(metric);\n                const metricJs = JSON.stringify(String(metric));\n                html += '<label class=\"ti-report-col-chip\" style=\"display:inline-flex;align-items:center;gap:6px;\">'\n                    + '<input type=\"checkbox\" ' + (checked ? 'checked' : '') + ' onchange=\"window.setActivityMetricFlag(' + metricJs.replace(\/\"\/g, '&quot;') + ', this.checked)\">'\n                    + '<span>' + window.escapeHtml(metric) + '<\/span>'\n                    + '<\/label>';\n            });\n            html += '<\/div>';\n        }\n        if (hiddenCols.length) {\n            html += '<div class=\"ti-report-hidden-cols\">' + hiddenCols.map(c => `<span class=\"ti-report-col-chip\"><button type=\"button\" class=\"ti-report-flag-btn is-off\" onclick='window.toggleActivityReportColumn(${JSON.stringify(c)}, event)'>X<\/button>${window.escapeHtml(c)}<\/span>`).join('') + '<\/div>';\n        }\n        if (view === 'table') {\n            html += '<div class=\"ti-report-table-wrap\"><table class=\"ti-report-table\"><thead><tr>';\n            html += '<th class=\"ti-report-row-flag-cell head\">X<\/th>';\n            visibleCols.forEach(c => {\n                const sortKey = (c === 'Data' || c === 'Ora') ? 'DataOra' : c;\n                html += `<th onclick='window.sortActivityReport(${JSON.stringify(sortKey)})'><button type=\"button\" class=\"ti-report-flag-btn\" onclick='window.toggleActivityReportColumn(${JSON.stringify(c)}, event)'>X<\/button>${window.escapeHtml(c)}<br><input class=\"ti-report-filter\" data-col=\"${window.escapeHtml(c)}\" value=\"${window.escapeHtml(window.activityReportState.filters[c] || '')}\" onclick=\"event.stopPropagation()\" oninput='window.setActivityReportFilter(${JSON.stringify(c)}, this.value, this)'><\/th>`;\n            });\n            html += '<\/tr><\/thead><tbody>';\n            allRows.forEach(r => {\n                const rowExcluded = !!rowFlags[r.__rowKey];\n                html += '<tr class=\"' + (rowExcluded ? 'ti-report-row-excluded' : '') + '\">';\n                html += `<td class=\"ti-report-row-flag-cell\"><button type=\"button\" class=\"ti-report-flag-btn ${rowExcluded ? 'is-off' : ''}\" onclick='window.toggleActivityReportRow(${JSON.stringify(r.__rowKey)}, event)'>X<\/button><\/td>`;\n                visibleCols.forEach(c => {\n                    const val = (c === 'Prezzo' || c === 'Quantit\u00e0' || c === 'Totale') ? window.fmtNum(r[c] || 0) : (r[c] || '');\n                    html += '<td class=\"' + ((c === 'Prezzo' || c === 'Quantit\u00e0' || c === 'Totale') ? 'ti-report-num' : '') + '\">' + window.escapeHtml(String(val)) + '<\/td>';\n                });\n                html += '<\/tr>';\n            });\n            html += '<\/tbody><tfoot><tr><td class=\"ti-report-row-flag-cell\">Incl.<\/td>';\n            visibleCols.forEach(c => {\n                if (c === 'Descrizione') html += '<td>Righe: ' + includedRows.length + '<\/td>';\n                else if (c === 'Prezzo') html += '<td class=\"ti-report-num\">' + window.fmtNum(totals.Prezzo) + '<\/td>';\n                else if (c === 'Quantit\u00e0') html += '<td class=\"ti-report-num\">' + window.fmtNum(totals.Quantit\u00e0) + '<\/td>';\n                else if (c === 'Totale') html += '<td class=\"ti-report-num\">' + window.fmtNum(totals.Totale) + '<\/td>';\n                else html += '<td><\/td>';\n            });\n            html += '<\/tr><\/tfoot><\/table><\/div>';\n        } else if (view === 'pie') {\n            html += '<div class=\"ti-chart-wrap\"><canvas id=\"ti-report-pie\" width=\"860\" height=\"340\"><\/canvas><\/div>';\n        } else {\n            html += '<div class=\"ti-chart-wrap\"><canvas id=\"ti-report-trend\" width=\"980\" height=\"380\"><\/canvas><\/div>';\n        }\n        box.innerHTML = html;\n        const focus = window.activityReportState.filterFocus;\n        if (focus && focus.col && view === 'table') {\n            setTimeout(() => {\n                const inp = box.querySelector('.ti-report-filter[data-col=\"' + CSS.escape(focus.col) + '\"]');\n                if (inp) {\n                    inp.focus();\n                    if (typeof focus.start === 'number' && typeof focus.end === 'number' && inp.setSelectionRange) {\n                        try { inp.setSelectionRange(focus.start, focus.end); } catch(e) {}\n                    }\n                }\n            }, 0);\n        }\n        if(view === 'pie') setTimeout(() => window.drawPie3D('ti-report-pie', includedRows), 30);\n        if(view === 'trend') setTimeout(() => window.drawTrend3D('ti-report-trend', includedRows), 30);\n    };\n    window.tiReportPick = function(row, keys, defVal) {\n        row = row || {}; keys = Array.isArray(keys) ? keys : [keys];\n        for (const k of keys) {\n            if (row[k] !== undefined && row[k] !== null && String(row[k]).trim() !== '') return String(row[k]).trim();\n            const found = Object.keys(row).find(x => String(x).toLowerCase() === String(k).toLowerCase());\n            if (found && row[found] !== undefined && row[found] !== null && String(row[found]).trim() !== '') return String(row[found]).trim();\n        }\n        return defVal || '';\n    };\n    window.tiReportBelongsToCurrentUser = function(row) {\n        if (window.tiIsPrivilegedCurrentUser && window.tiIsPrivilegedCurrentUser()) return true;\n        const user = String(window.tiUser || '').trim().toLowerCase();\n        const display = String(window.tiUserDisplay || '').trim().toLowerCase();\n        if (!user && !display) return false;\n        const names = [\n            window.tiReportPick(row, ['Username','Utente','user']),\n            window.tiReportPick(row, ['Nome','Cliente','Display'])\n        ].map(v => String(v || '').trim().toLowerCase()).filter(Boolean);\n        return names.some(v => v === user || v === display || (!!user && v.indexOf(user) !== -1) || (!!display && v.indexOf(display) !== -1));\n    };\n    window.tiReportTableRows = function(tNames) {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return [];\n        for (const t of tNames) {\n            const raw = window.currentDbData.Tabelle[t];\n            if (!raw) continue;\n            return Array.isArray(raw) ? raw : Object.values(raw || {});\n        }\n        return [];\n    };\n    window.tiBuildPublicActivityReportRows = function() {\n        const rows = [];\n        const escNum = function(v, defVal) { const n = window.parseReportNum ? window.parseReportNum(v, defVal || 0) : parseFloat(String(v || '0').replace(',', '.')); return isNaN(n) ? (defVal || 0) : n; };\n        const canFull = window.tiIsPrivilegedCurrentUser && window.tiIsPrivilegedCurrentUser();\n        const appRows = window.tiReportTableRows(['Appuntamenti','Prenotazioni']);\n        appRows.forEach(function(r){\n            r = r || {};\n            if (!canFull && !window.tiReportBelongsToCurrentUser(r)) return;\n            const st = window.tiReportPick(r, ['Stato','Status']);\n            const lowerSt = String(st || '').toLowerCase();\n            if (lowerSt.indexOf('cancell') !== -1 || lowerSt.indexOf('annull') !== -1) return;\n            rows.push({kind:'appointment', title:'Dettaglio appuntamento', fields:{\n                'Username': window.tiReportPick(r, ['Username','Utente','user']),\n                'Servizio': window.tiReportPick(r, ['Servizio','Prestazione','Trattamento','Descrizione']),\n                'Data appuntamento': window.tiReportPick(r, ['Data_appuntamento','Data appuntamento','Data prenotazione','Giorno']),\n                'Ora appuntamento': window.tiReportPick(r, ['Ora_appuntamento','Ora appuntamento','Ora prenotazione','Orario']),\n                'Quantit\u00e0': window.tiReportPick(r, ['Quantit\u00e0','Quantita','Qta','Q.t\u00e0']),\n                'Email': window.tiReportPick(r, ['Email_utente','Email utente','Email','E-mail']),\n                'Tel': window.tiReportPick(r, ['Tel_Utente','Tel Utente','Tel','Telefono','Cellulare']),\n                'Data': window.tiReportPick(r, ['Data']),\n                'Ora': window.tiReportPick(r, ['Ora']),\n                'Nota': window.tiReportPick(r, ['Nota','Note']),\n                'Stato': st\n            }});\n        });\n        const actRows = window.tiReportTableRows(['Attivita','Attivit\u00e0']);\n        actRows.forEach(function(r){\n            r = r || {};\n            if (!canFull && !window.tiReportBelongsToCurrentUser(r)) return;\n            const st = window.tiReportPick(r, ['Stato','Status']);\n            const lowerSt = String(st || '').toLowerCase();\n            if (lowerSt.indexOf('cancell') !== -1 || lowerSt.indexOf('annull') !== -1) return;\n            const prezzo = window.tiReportPick(r, ['Prezzo','Importo','Costo'], '0');\n            const qta = window.tiReportPick(r, ['Quantit\u00e0','Quantita','Qta','Q.t\u00e0'], '1');\n            let totale = window.tiReportPick(r, ['Totale','Importo totale','Totale riga'], '');\n            if (!totale) totale = window.fmtNum ? window.fmtNum(escNum(prezzo, 0) * (escNum(qta, 1) || 1)) : String((escNum(prezzo, 0) * (escNum(qta, 1) || 1)).toFixed(2));\n            rows.push({kind:'activity', title:'Dettaglio attivit\u00e0', fields:{\n                'Username': window.tiReportPick(r, ['Username','Utente','user']),\n                'Descrizione': window.tiReportPick(r, ['Descrizione attivit\u00e0','Descrizione attivita','Descrizione','Dettaglio','Note','Nota','Prodotto','Servizio']),\n                'Prezzo': prezzo,\n                'Quantit\u00e0': qta,\n                'Esito': window.tiReportPick(r, ['Esito']),\n                'Data': window.tiReportPick(r, ['Data','Data attivit\u00e0','Data attivita','Data creazione']),\n                'Ora': window.tiReportPick(r, ['Ora','Ora attivit\u00e0','Ora attivita','Ora creazione']),\n                'Totale': totale\n            }});\n        });\n        return rows;\n    };\n    window.buildActivityReportChatHtml = function() {\n        const esc = window.escapeHtml || function(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); };\n        const rows = window.tiBuildPublicActivityReportRows ? window.tiBuildPublicActivityReportRows() : [];\n        if (!rows.length) return '<b>Report attivit\u00e0<\/b><br>Non trovo attivit\u00e0 o appuntamenti da mostrare per l utente corrente nel database selezionato.';\n        let html = '<b>Report attivit\u00e0<\/b><br>Visualizzazione in chatbot.<br><ol style=\"margin:8px 0 0 20px;padding:0;\">';\n        rows.forEach(function(item){\n            html += '<li style=\"margin:8px 0;\"><b>' + esc(item.title) + '<\/b><br><ol style=\"margin:4px 0 0 18px;padding:0;\">';\n            Object.keys(item.fields || {}).forEach(function(k){\n                html += '<li><b>' + esc(k) + '<\/b>: ' + esc(item.fields[k] || '') + '<\/li>';\n            });\n            html += '<\/ol><\/li>';\n        });\n        html += '<\/ol>';\n        return html;\n    };\n    window.openActivityReport = function() {\n        const prefs = window.loadActivityReportPrefs ? window.loadActivityReportPrefs() : {};\n        window.activityReportState = {\n            sortCol: 'DataOra',\n            sortDir: 'desc',\n            filters: {},\n            view: 'table',\n            colFlags: prefs.colFlags || {},\n            rowFlags: prefs.rowFlags || {},\n            metricFlags: prefs.metricFlags || (window.getDefaultActivityMetricFlags ? window.getDefaultActivityMetricFlags() : {}),\n            groupField: prefs.groupField || 'Username',\n            excludeConfigActivities: !!prefs.excludeConfigActivities\n        };\n        if (window.renderActivityReport) window.renderActivityReport();\n        if (window.openModal) window.openModal('ti-report-ov');\n    };\n\n    window.hasInstructionText = function(row) {\n        if (!row || typeof row !== 'object') return false;\n        const wanted = ['istruzioni','istruzione','istruzioni ai','istruzione ai','indicazioni ai','condizioni','prompt ai','prompt','regola','regole'];\n        return Object.keys(row).some((k) => {\n            const key = String(k).toLowerCase().trim();\n            if (!wanted.includes(key)) return false;\n            const val = row[k];\n            return val !== undefined && val !== null && String(val).trim() !== '';\n        });\n    };\n\n    window.buildInstructionsViewDb = function(fullDb) {\n        if (!fullDb || !fullDb.Tabelle) return null;\n        const view = Object.assign({}, fullDb, { Tabelle: {} });\n        let tableCount = 0;\n        let rowCount = 0;\n        Object.keys(fullDb.Tabelle).forEach((tName) => {\n            const raw = fullDb.Tabelle[tName];\n            const rows = Array.isArray(raw) ? raw : Object.values(raw || {});\n            const matched = [];\n            rows.forEach(function(row, idx){\n                if (!window.hasInstructionText(row)) return;\n                const copy = Object.assign({}, row);\n                try { Object.defineProperty(copy, '__ti_original_idx', {value: idx, enumerable: false, configurable: true}); }\n                catch(e) { copy.__ti_original_idx = idx; }\n                matched.push(copy);\n            });\n            if (matched.length) {\n                view.Tabelle[tName] = matched;\n                tableCount += 1;\n                rowCount += matched.length;\n            }\n        });\n        view.__instructionStats = { tables: tableCount, rows: rowCount };\n        return view;\n    };\n\n\n    window.isAdminUser = function() {\n        return \/amministratore|administrator\/i.test(String(window.tiRole || '')) || String(window.tiUser || '') === 'SSGlobalAdmin';\n    };\n\n    window.canRunLocalOrphanCleanup = function() {\n        const roleNow = String(window.tiRole || '').toLowerCase();\n        const userNow = String(window.tiUser || '').toLowerCase();\n        return userNow === 'ssglobaladmin' || \/amministratore|configuratore|responsabile|globale\/.test(roleNow);\n    };\n    window.canRunGlobalOrphanCleanup = function() {\n        return String(window.tiUser || '').toLowerCase() === 'ssglobaladmin';\n    };\n    window.refreshOrphanCleanupButton = function() {\n        const localBtn = gE('ti-clean-orphans-btn');\n        const globalBtn = gE('ti-clean-orphans-global-btn');\n        if (localBtn) localBtn.style.display = (window.canRunLocalOrphanCleanup && window.canRunLocalOrphanCleanup()) ? 'inline-block' : 'none';\n        if (globalBtn) globalBtn.style.display = (window.canRunGlobalOrphanCleanup && window.canRunGlobalOrphanCleanup()) ? 'block' : 'none';\n        if (window.refreshPrivilegedActionsRow) window.refreshPrivilegedActionsRow();\n    };\n\n    window.orphanCleanupState = window.orphanCleanupState || { active:false, stopRequested:false, total:0, processed:0, deleted:0, currentPath:'' };\n\n    window.restoreDefaultLoaderActions = function() {\n        const actions = gE('ti-loader-actions');\n        if (!actions) return;\n        actions.innerHTML = '<button type=\"button\" id=\"ti-loader-close\" class=\"ti-btn\" style=\"width:100%; background:#b91c1c; color:#fff;\" onclick=\"window.cancelLongAIProcess()\">Esci<\/button>';\n    };\n\n    window.showOrphanCleanupEmergencyButton = function() {\n        const actions = gE('ti-loader-actions');\n        if (!actions) return;\n        actions.style.display = 'block';\n        actions.innerHTML = '<button type=\"button\" id=\"ti-orphan-emergency-stop\" class=\"ti-btn\" style=\"width:100%; background:#dc2626; color:#fff; font-weight:800;\" onclick=\"window.requestStopOrphanFileCleanup()\">\ud83d\uded1 Esci d\\'emergenza - interrompi cancellazione<\/button>'\n            + '<div style=\"margin-top:6px; color:#fecaca; font-size:11px; line-height:1.35;\">Il file eventualmente in cancellazione termina. I file successivi non verranno cancellati.<\/div>';\n    };\n\n    window.requestStopOrphanFileCleanup = function() {\n        const st = window.orphanCleanupState || {};\n        if (!st.active) {\n            window.tiAlert('Nessun processo di pulizia file non associati in corso.');\n            return;\n        }\n        st.stopRequested = true;\n        window.orphanCleanupState = st;\n        const btn = gE('ti-orphan-emergency-stop');\n        if (btn) { btn.disabled = true; btn.innerText = 'Interruzione richiesta...'; }\n        window.updateProgressPopup(\n            Math.max(1, Math.min(99, parseInt(st.lastPercent || 50, 10) || 50)),\n            'Pulizia file non associati',\n            'Esci d emergenza richiesto. Attendo la fine dell eventuale cancellazione in corso.',\n            st.currentPath || 'Interruzione richiesta'\n        );\n    };\n\n    window.runOrphanFileCleanup = function() {\n        if (!(window.canRunLocalOrphanCleanup && window.canRunLocalOrphanCleanup())) {\n            window.tiAlert('Funzione non disponibile con il profilo corrente.');\n            return;\n        }\n        const dbVal = gE('ti-ditta') ? (gE('ti-ditta').value || '') : '';\n        if (!dbVal) { window.tiAlert('Seleziona una ditta prima di avviare la pulizia.'); return; }\n        const ask = 'Questa funzione elimina fisicamente dallo storage i file non associati a nessuna tabella della ditta selezionata e rimuove anche dal database\/schermata le associazioni a file non piu presenti.<br><br>Prima verra eseguita una scansione delle associazioni fantasma e dei file fisici. Durante la cancellazione vedrai il path completo dell elemento eliminato.<br><br>Confermi di procedere?';\n        const start = function() { window.startOrphanFileCleanup(dbVal); };\n        if (typeof window.tiConfirmYesNoAction === 'function') window.tiConfirmYesNoAction(ask, start, null, 'AVVIA PULIZIA', 'ANNULLA');\n        else window.tiConfirm(ask.replace(\/<br\\s*\\\/?>\/gi, '\\n'), start);\n    };\n\n\n\n    window.forceCleanupMissingMediaRefs610 = function(dbVal) {\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_force_cleanup_missing_media_refs');\n        fd.append('db', dbVal);\n        return window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true}).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) ? res.data.message : 'Pulizia diretta riferimenti fantasma non riuscita.');\n            return res.data || {};\n        });\n    };\n\n    window.startOrphanFileCleanup = function(dbVal) {\n        window.orphanCleanupState = { active:true, stopRequested:false, total:0, processed:0, deleted:0, currentPath:'Scansione file della ditta', lastPercent:2, startedAt:Date.now() };\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_scan_orphan_files');\n        fd.append('db', dbVal);\n        window.showProgressPopup('Pulizia file non associati', 'Scansione file della ditta in corso.', {startPercent: 2, targetPercent: 18, stepMs: 350, cancellable:false, currentItem:'Scansione file della ditta'});\n        window.showOrphanCleanupEmergencyButton();\n        window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true}).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) ? res.data.message : 'Scansione non riuscita.');\n            const files = (res.data && Array.isArray(res.data.files)) ? res.data.files : [];\n            if (!files.length) {\n                window.updateProgressPopup(35, 'Pulizia file non associati', 'Nessun file fisico trovato. Verifico associazioni fantasma nei campi database.', 'Scansione diretta campi database');\n                return window.forceCleanupMissingMediaRefs610(dbVal).then(function(forceRes){\n                    const removed = parseInt((forceRes && forceRes.removed) || 0, 10) || 0;\n                    if (window.orphanCleanupState) window.orphanCleanupState.active = false;\n                    window.restoreDefaultLoaderActions();\n                    if (removed > 0) {\n                        const msg = 'Pulizia completata. Rimosse ' + removed + ' associazioni fantasma dai campi database.';\n                        window.updateProgressPopup(100, 'Pulizia file non associati', msg, msg);\n                        window.hideProgressPopup(true, 'Associazioni fantasma rimosse', {notifyUser:true, successMessage:msg});\n                        setTimeout(function(){\n                            window.tiAlert(msg + '<br><br>La Configurazione e la schermata Gestione File Associati vengono aggiornate automaticamente.');\n                        }, 300);\n                    } else {\n                        window.hideProgressPopup(true, 'Nessun file non associato trovato', {notifyUser:true, successMessage:'Pulizia completata. Nessun file non associato o associazione fantasma da eliminare.'});\n                        setTimeout(function(){ window.tiAlert('Pulizia completata.<br>Nessun file non associato o associazione fantasma da eliminare.'); }, 300);\n                    }\n                }).catch(function(err){\n                    if (window.orphanCleanupState) window.orphanCleanupState.active = false;\n                    window.restoreDefaultLoaderActions();\n                    window.hideProgressPopup(false, 'Errore pulizia associazioni fantasma');\n                    window.tiAlert('Errore durante la pulizia diretta delle associazioni fantasma: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n                });\n            }\n            if (window.orphanCleanupState) { window.orphanCleanupState.total = files.length; window.orphanCleanupState.currentPath = 'File trovati: ' + files.length; window.orphanCleanupState.lastPercent = 20; }\n            window.updateProgressPopup(20, 'Pulizia file non associati', 'Trovati ' + files.length + ' file non associati. Avvio cancellazione.', 'File trovati: ' + files.length);\n            window.showOrphanCleanupEmergencyButton();\n            window.deleteOrphanFilesSequential(dbVal, files, 0, 0, [], []);\n        }).catch(function(e){\n            if (window.orphanCleanupState) window.orphanCleanupState.active = false;\n            window.restoreDefaultLoaderActions();\n            window.hideProgressPopup(false, 'Errore pulizia file');\n            window.tiAlert('Errore durante la scansione file non associati: ' + ((e && e.message) ? e.message : 'errore sconosciuto'));\n        });\n    };\n\n    window.showOrphanCleanupFinalReport = function(deletedPaths, errors, total, deleted, interrupted, processed, scopeInfo) {\n        const paths = Array.isArray(deletedPaths) ? deletedPaths : [];\n        const errList = Array.isArray(errors) ? errors : [];\n        const scope = scopeInfo && typeof scopeInfo === 'object' ? scopeInfo : {};\n        const proc = Math.max(0, parseInt(processed || deleted + errList.length, 10) || 0);\n        const scopeLabel = scope.label || ((window.orphanCleanupState && window.orphanCleanupState.globalMode) ? 'Pulizia globale su tutte le ditte' : 'Pulizia ditta selezionata');\n        const ditteProcessed = parseInt(scope.ditteProcessed || 0, 10) || 0;\n        const ditteTotal = parseInt(scope.ditteTotal || 0, 10) || 0;\n        const summary = (interrupted ? 'Pulizia interrotta su richiesta amministratore. ' : '') + 'File eliminati: ' + deleted + ' su ' + total + '. File processati: ' + proc + ' su ' + total + (ditteTotal ? '. Ditte processate: ' + ditteProcessed + ' su ' + ditteTotal : '') + (errList.length ? '. Errori: ' + errList.length : '.');\n        const reportLines = [];\n        reportLines.push('Report pulizia file non associati');\n        reportLines.push('Ambito: ' + scopeLabel);\n        reportLines.push('Versione plugin: ' + (window.tiVersion || '') + ' - ' + (window.tiVersionDateTime || ''));\n        if (interrupted) reportLines.push('Esito: processo interrotto con Esci d emergenza');\n        reportLines.push(summary);\n        reportLines.push('');\n        reportLines.push('Path file cancellati:');\n        if (paths.length) paths.forEach(function(path, idx){ reportLines.push((idx + 1) + '. ' + path); });\n        else reportLines.push('Nessun file cancellato.');\n        if (errList.length) {\n            reportLines.push('');\n            reportLines.push('Errori o file non cancellati:');\n            errList.forEach(function(err, idx){ reportLines.push((idx + 1) + '. ' + err); });\n        }\n        window.lastOrphanCleanupReportText = reportLines.join('\\n');\n        let html = '<div class=\"ti-orphan-report-wide\" style=\"text-align:left; max-height:72vh; overflow:auto; font-size:11px; line-height:1.35;\">';\n        html += '<div style=\"font-weight:800; font-size:14px; margin-bottom:8px;\">Report pulizia file non associati<\/div>';\n        html += '<div style=\"margin-bottom:6px; color:#93c5fd; font-size:11px;\"><b>Ambito:<\/b> ' + window.escapeHtml(scopeLabel) + '<\/div>';\n        if (interrupted) html += '<div style=\"margin-bottom:8px; color:#fbbf24; font-weight:800;\">Processo interrotto con Esci d emergenza.<\/div>';\n        html += '<div style=\"margin-bottom:8px; color:#cbd5e1; font-size:11px;\">' + window.escapeHtml(summary) + '<\/div>';\n        html += '<div style=\"font-weight:700; margin:10px 0 5px; font-size:12px;\">Path file cancellati<\/div>';\n        if (paths.length) {\n            html += '<ol style=\"padding-left:18px; margin:0; color:#e5e7eb; font-size:10.5px; line-height:1.35; word-break:break-all;\">';\n            paths.forEach(function(path){ html += '<li>' + window.escapeHtml(path) + '<\/li>'; });\n            html += '<\/ol>';\n        } else {\n            html += '<div style=\"color:#e5e7eb; font-size:11px;\">Nessun file cancellato.<\/div>';\n        }\n        if (errList.length) {\n            html += '<div style=\"font-weight:700; margin:12px 0 5px; color:#fecaca; font-size:12px;\">Errori o file non cancellati<\/div>';\n            html += '<ol style=\"padding-left:18px; margin:0; color:#fecaca; font-size:10.5px; line-height:1.35; word-break:break-all;\">';\n            errList.forEach(function(err){ html += '<li>' + window.escapeHtml(err) + '<\/li>'; });\n            html += '<\/ol>';\n        }\n        html += '<div style=\"margin-top:10px; font-size:11px; color:#93c5fd;\">Usa il pulsante Copia testo per copiare il report e incollarlo dove serve.<\/div>';\n        html += '<\/div>';\n        window.tiAlert(html);\n        setTimeout(function(){\n            const reportModal = document.querySelector('#ti-alert-ov .ti-modal');\n            const reportMsg = gE('ti-alert-msg');\n            if (reportModal) { reportModal.style.width = 'min(94vw, 1080px)'; reportModal.style.maxWidth = '1080px'; reportModal.style.padding = '16px'; }\n            if (reportMsg) { reportMsg.style.fontSize = '11px'; reportMsg.style.lineHeight = '1.35'; reportMsg.style.marginBottom = '12px'; }\n            const okBtn = gE('ti-alert-no');\n            if (okBtn && !okBtn.dataset.orphanReportWideClose) {\n                okBtn.dataset.orphanReportWideClose = '1';\n                const oldClose = okBtn.onclick;\n                okBtn.onclick = function(){\n                    if (reportModal) { reportModal.style.width = '300px'; reportModal.style.maxWidth = ''; reportModal.style.padding = '20px'; }\n                    if (reportMsg) { reportMsg.style.fontSize = '15px'; reportMsg.style.lineHeight = '1.4'; reportMsg.style.marginBottom = '20px'; }\n                    okBtn.dataset.orphanReportWideClose = '';\n                    if (typeof oldClose === 'function') oldClose.call(this);\n                    else window.closeModal('ti-alert-ov');\n                };\n            }\n            if (gE('ti-alert-copy')) {\n                gE('ti-alert-copy').onclick = function() {\n                    const text = window.lastOrphanCleanupReportText || window.getAlertPlainText();\n                    const done = function(ok) {\n                        const btn = gE('ti-alert-copy');\n                        if (!btn) return;\n                        const old = btn.innerText || 'Copia testo';\n                        btn.innerText = ok ? 'Copiato' : 'Errore copia';\n                        setTimeout(function(){ btn.innerText = old; }, 1200);\n                    };\n                    if (navigator.clipboard && navigator.clipboard.writeText) {\n                        navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){ done(false); });\n                    } else {\n                        try {\n                            const ta = document.createElement('textarea');\n                            ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';\n                            document.body.appendChild(ta); ta.focus(); ta.select();\n                            const ok = document.execCommand('copy'); document.body.removeChild(ta); done(ok);\n                        } catch(e) { done(false); }\n                    }\n                };\n            }\n        }, 0);\n    };\n\n    window.deleteOrphanFilesSequential = function(dbVal, files, index, deleted, errors, deletedPaths) {\n        const total = files.length;\n        deletedPaths = Array.isArray(deletedPaths) ? deletedPaths : [];\n        const st = window.orphanCleanupState || {};\n        if (st.stopRequested) {\n            st.active = false;\n            st.processed = index;\n            window.orphanCleanupState = st;\n            window.restoreDefaultLoaderActions();\n            const msgStop = 'Pulizia interrotta. File eliminati: ' + deleted + ' su ' + total + '. File non ancora elaborati: ' + Math.max(0, total - index) + '.';\n            window.updateProgressPopup(Math.max(20, Math.min(99, parseInt(st.lastPercent || 20, 10) || 20)), 'Pulizia file non associati', msgStop, st.currentPath || 'Interruzione richiesta');\n            window.hideProgressPopup(false, 'Pulizia interrotta', {notifyUser:false});\n            setTimeout(function(){ window.showOrphanCleanupFinalReport(deletedPaths, errors, total, deleted, true, index); }, 500);\n            return;\n        }\n        if (index >= total) {\n            if (window.orphanCleanupState) { window.orphanCleanupState.active = false; window.orphanCleanupState.processed = total; }\n            window.restoreDefaultLoaderActions();\n            const ok = errors.length === 0;\n            const title = ok ? 'Pulizia file completata' : 'Pulizia completata con avvisi';\n            const msg = 'File eliminati: ' + deleted + ' su ' + total + (errors.length ? '. Errori: ' + errors.length : '.');\n            window.updateProgressPopup(100, 'Pulizia file non associati', msg, msg);\n            window.hideProgressPopup(ok, title, {notifyUser:true, successMessage:msg});\n            setTimeout(function(){ window.showOrphanCleanupFinalReport(deletedPaths, errors, total, deleted, false, total); }, 500);\n            return;\n        }\n        const file = files[index] || {};\n        const pathTxt = file.path || file.relative_path || file.name || 'file';\n        const rowInfo = file.table ? ('Tabella ' + file.table + (typeof file.row_index !== 'undefined' ? ', riga ' + (parseInt(file.row_index, 10) + 1) : '') + (file.field ? ', campo ' + file.field : '') + ' - ') : '';\n        const workLine = 'Elemento ' + (index + 1) + ' \/ ' + total + ' - ' + rowInfo + pathTxt;\n        const pct = Math.max(20, Math.min(99, Math.round(20 + ((index) \/ Math.max(1, total)) * 78)));\n        if (window.orphanCleanupState) { window.orphanCleanupState.currentPath = workLine; window.orphanCleanupState.lastPercent = pct; window.orphanCleanupState.processed = index; }\n        window.updateProgressPopup(pct, 'Pulizia file non associati', 'Cancellazione elemento ' + (index + 1) + ' \/ ' + total, workLine);\n        window.showOrphanCleanupEmergencyButton();\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_delete_orphan_file');\n        fd.append('db', dbVal);\n        fd.append('rel', file.rel || '');\n        (function(){\n            const p = window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true});\n            const t = new Promise(function(_, reject){ setTimeout(function(){ reject(new Error('timeout cancellazione elemento: il server non ha risposto entro 25 secondi')); }, 25000); });\n            return Promise.race([p, t]);\n        })().then(function(res){\n            if (res && res.success) {\n                const deletedPath = (res.data && res.data.path) ? res.data.path : pathTxt;\n                const doneLine = 'Elemento ' + (index + 1) + ' \/ ' + total + ' completato - ' + rowInfo + deletedPath;\n                const pct2 = Math.max(pct, Math.min(99, Math.round(20 + ((index + 1) \/ Math.max(1, total)) * 78)));\n                window.updateProgressPopup(pct2, 'Pulizia file non associati', 'Eliminato elemento ' + (index + 1) + ' \/ ' + total, doneLine);\n                if (window.orphanCleanupState) { window.orphanCleanupState.currentPath = doneLine; window.orphanCleanupState.lastPercent = pct2; window.orphanCleanupState.processed = index + 1; window.orphanCleanupState.deleted = deleted + 1; }\n                deletedPaths.push(deletedPath);\n                setTimeout(function(){ window.deleteOrphanFilesSequential(dbVal, files, index + 1, deleted + 1, errors, deletedPaths); }, 80);\n            } else {\n                errors.push(pathTxt + ' - ' + ((res && res.data && res.data.message) ? res.data.message : 'errore eliminazione'));\n                if (window.orphanCleanupState) { window.orphanCleanupState.processed = index + 1; }\n                setTimeout(function(){ window.deleteOrphanFilesSequential(dbVal, files, index + 1, deleted, errors, deletedPaths); }, 80);\n            }\n        }).catch(function(e){\n            errors.push(pathTxt + ' - ' + ((e && e.message) ? e.message : 'errore rete'));\n            if (window.orphanCleanupState) { window.orphanCleanupState.processed = index + 1; }\n            setTimeout(function(){ window.deleteOrphanFilesSequential(dbVal, files, index + 1, deleted, errors, deletedPaths); }, 80);\n        });\n    };\n\n\n    window.getGlobalOrphanCleanupDitte = function() {\n        const normalize = function(list) {\n            return (Array.isArray(list) ? list : []).filter(function(d){\n                const db = d && d.db ? String(d.db) : '';\n                return db && db !== 'Global' && db !== 'NEW_DB' && db !== 'shopservicemain.json';\n            });\n        };\n        const existing = normalize(window.ditteData || []);\n        if (existing.length) return Promise.resolve(existing);\n        const url = window.tiUrl + '?ti_action=ti_ai_list_ditte&current_db=&show_suspended=1';\n        return window.fetchJsonSafe(url, {method:'GET', tiNoLongProcessSignal:true}).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) ? res.data.message : 'Elenco ditte non disponibile.');\n            const list = normalize(res.data && res.data.ditte);\n            window.ditteData = list;\n            return list;\n        });\n    };\n\n    window.scanOrphanFilesForDb = function(dbVal, label) {\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_scan_orphan_files');\n        fd.append('db', dbVal);\n        if (window.orphanCleanupState) window.orphanCleanupState.currentPath = 'Scansione ditta: ' + (label || dbVal);\n        return window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true}).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) ? res.data.message : 'Scansione non riuscita.');\n            return (res.data && Array.isArray(res.data.files)) ? res.data.files : [];\n        });\n    };\n\n    window.deleteSingleOrphanFile = function(dbVal, file) {\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_delete_orphan_file');\n        fd.append('db', dbVal);\n        fd.append('rel', (file && file.rel) ? file.rel : '');\n        const p = window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true});\n        const t = new Promise(function(_, reject){ setTimeout(function(){ reject(new Error('timeout cancellazione elemento: il server non ha risposto entro 25 secondi')); }, 25000); });\n        return Promise.race([p, t]);\n    };\n\n    window.runGlobalOrphanFileCleanup = function() {\n        if (String(window.tiUser || '') !== 'SSGlobalAdmin') {\n            window.tiAlert('Funzione globale non disponibile con il profilo corrente.');\n            return;\n        }\n        const ask = 'Questa funzione elimina fisicamente dallo storage i file non associati di tutte le ditte visibili al Global Admin.<br><br>Prima verra eseguita una scansione globale. Durante la cancellazione vedrai ditta e path completo del file eliminato.<br><br>Confermi di procedere?';\n        const start = function(){ window.startGlobalOrphanFileCleanup(); };\n        if (typeof window.tiConfirmYesNoAction === 'function') window.tiConfirmYesNoAction(ask, start, null, 'AVVIA PULIZIA GLOBALE', 'ANNULLA');\n        else window.tiConfirm(ask.replace(\/<br\\s*\\\/?>\/gi, '\\n'), start);\n    };\n\n    window.startGlobalOrphanFileCleanup = function() {\n        window.orphanCleanupState = { active:true, globalMode:true, stopRequested:false, total:0, processed:0, deleted:0, currentPath:'Scansione globale ditte', lastPercent:2, startedAt:Date.now(), ditteTotal:0, ditteProcessed:0 };\n        window.showProgressPopup('Pulizia globale file non associati', 'Scansione ditte in corso.', {startPercent: 2, targetPercent: 18, stepMs: 350, cancellable:false, currentItem:'Scansione globale ditte'});\n        window.showOrphanCleanupEmergencyButton();\n        window.getGlobalOrphanCleanupDitte().then(function(ditte){\n            if (!ditte.length) throw new Error('Nessuna ditta disponibile per la pulizia globale.');\n            if (window.orphanCleanupState) { window.orphanCleanupState.ditteTotal = ditte.length; window.orphanCleanupState.currentPath = 'Ditte trovate: ' + ditte.length; }\n            window.updateProgressPopup(8, 'Pulizia globale file non associati', 'Ditte trovate: ' + ditte.length + '. Avvio scansione file.', 'Ditte trovate: ' + ditte.length);\n            const allFiles = [];\n            const scanErrors = [];\n            const scanNext = function(i){\n                const st = window.orphanCleanupState || {};\n                if (st.stopRequested) {\n                    st.active = false; window.orphanCleanupState = st; window.restoreDefaultLoaderActions(); window.hideProgressPopup(false, 'Pulizia globale interrotta', {notifyUser:false});\n                    setTimeout(function(){ window.showOrphanCleanupFinalReport([], scanErrors, 0, 0, true, 0, {label:'Pulizia globale su tutte le ditte', ditteProcessed:i, ditteTotal:ditte.length}); }, 400);\n                    return;\n                }\n                if (i >= ditte.length) {\n                    if (!allFiles.length) {\n                        if (window.orphanCleanupState) window.orphanCleanupState.active = false;\n                        window.restoreDefaultLoaderActions();\n                        window.hideProgressPopup(true, 'Nessun file non associato trovato', {notifyUser:true, successMessage:'Pulizia globale completata. Nessun file non associato da eliminare.'});\n                        setTimeout(function(){ window.showOrphanCleanupFinalReport([], scanErrors, 0, 0, false, 0, {label:'Pulizia globale su tutte le ditte', ditteProcessed:ditte.length, ditteTotal:ditte.length}); }, 400);\n                        return;\n                    }\n                    if (window.orphanCleanupState) { window.orphanCleanupState.total = allFiles.length; window.orphanCleanupState.lastPercent = 35; window.orphanCleanupState.currentPath = 'File non associati totali: ' + allFiles.length; }\n                    window.updateProgressPopup(35, 'Pulizia globale file non associati', 'Trovati ' + allFiles.length + ' file non associati. Avvio cancellazione.', 'File non associati totali: ' + allFiles.length);\n                    window.deleteGlobalOrphanFilesSequential(allFiles, 0, 0, scanErrors, [], ditte.length, ditte.length);\n                    return;\n                }\n                const d = ditte[i] || {};\n                const db = d.db || '';\n                const label = d.label || d.nome_pura || db;\n                const pct = Math.max(8, Math.min(34, Math.round(8 + (i \/ Math.max(1, ditte.length)) * 26)));\n                if (window.orphanCleanupState) { window.orphanCleanupState.lastPercent = pct; window.orphanCleanupState.currentPath = 'Scansione: ' + label; window.orphanCleanupState.ditteProcessed = i; }\n                window.updateProgressPopup(pct, 'Pulizia globale file non associati', 'Scansione ditta ' + (i + 1) + ' \/ ' + ditte.length, label + ' - ' + db);\n                window.showOrphanCleanupEmergencyButton();\n                window.scanOrphanFilesForDb(db, label).then(function(files){\n                    files.forEach(function(f){ allFiles.push({db:db, label:label, file:f}); });\n                    if (window.orphanCleanupState) window.orphanCleanupState.ditteProcessed = i + 1;\n                    setTimeout(function(){ scanNext(i + 1); }, 40);\n                }).catch(function(e){\n                    scanErrors.push(label + ' [' + db + '] - scansione: ' + ((e && e.message) ? e.message : 'errore'));\n                    if (window.orphanCleanupState) window.orphanCleanupState.ditteProcessed = i + 1;\n                    setTimeout(function(){ scanNext(i + 1); }, 40);\n                });\n            };\n            scanNext(0);\n        }).catch(function(e){\n            if (window.orphanCleanupState) window.orphanCleanupState.active = false;\n            window.restoreDefaultLoaderActions();\n            window.hideProgressPopup(false, 'Errore pulizia globale');\n            window.tiAlert('Errore durante la pulizia globale file non associati: ' + ((e && e.message) ? e.message : 'errore sconosciuto'));\n        });\n    };\n\n    window.deleteGlobalOrphanFilesSequential = function(items, index, deleted, errors, deletedPaths, ditteProcessed, ditteTotal) {\n        const total = items.length;\n        deletedPaths = Array.isArray(deletedPaths) ? deletedPaths : [];\n        const st = window.orphanCleanupState || {};\n        if (st.stopRequested) {\n            st.active = false; st.processed = index; window.orphanCleanupState = st;\n            window.restoreDefaultLoaderActions();\n            const msgStop = 'Pulizia globale interrotta. File eliminati: ' + deleted + ' su ' + total + '. File non ancora elaborati: ' + Math.max(0, total - index) + '.';\n            window.updateProgressPopup(Math.max(35, Math.min(99, parseInt(st.lastPercent || 35, 10) || 35)), 'Pulizia globale file non associati', msgStop, st.currentPath || 'Interruzione richiesta');\n            window.hideProgressPopup(false, 'Pulizia globale interrotta', {notifyUser:false});\n            setTimeout(function(){ window.showOrphanCleanupFinalReport(deletedPaths, errors, total, deleted, true, index, {label:'Pulizia globale su tutte le ditte', ditteProcessed:ditteProcessed || 0, ditteTotal:ditteTotal || 0}); }, 500);\n            return;\n        }\n        if (index >= total) {\n            if (window.orphanCleanupState) { window.orphanCleanupState.active = false; window.orphanCleanupState.processed = total; }\n            window.restoreDefaultLoaderActions();\n            const ok = errors.length === 0;\n            const title = ok ? 'Pulizia globale completata' : 'Pulizia globale completata con avvisi';\n            const msg = 'File eliminati: ' + deleted + ' su ' + total + (errors.length ? '. Errori: ' + errors.length : '.');\n            window.updateProgressPopup(100, 'Pulizia globale file non associati', msg, msg);\n            window.hideProgressPopup(ok, title, {notifyUser:true, successMessage:msg});\n            setTimeout(function(){ window.showOrphanCleanupFinalReport(deletedPaths, errors, total, deleted, false, total, {label:'Pulizia globale su tutte le ditte', ditteProcessed:ditteProcessed || 0, ditteTotal:ditteTotal || 0}); }, 500);\n            return;\n        }\n        const item = items[index] || {};\n        const file = item.file || {};\n        const db = item.db || '';\n        const label = item.label || db;\n        const pathTxt = file.path || file.relative_path || file.name || 'file';\n        const rowInfo = file.table ? ('Tabella ' + file.table + (typeof file.row_index !== 'undefined' ? ', riga ' + (parseInt(file.row_index, 10) + 1) : '') + (file.field ? ', campo ' + file.field : '') + ' - ') : '';\n        const pct = Math.max(35, Math.min(99, Math.round(35 + (index \/ Math.max(1, total)) * 63)));\n        const current = 'Elemento ' + (index + 1) + ' \/ ' + total + ' - ' + label + ' [' + db + '] - ' + rowInfo + pathTxt;\n        if (window.orphanCleanupState) { window.orphanCleanupState.currentPath = current; window.orphanCleanupState.lastPercent = pct; window.orphanCleanupState.processed = index; }\n        window.updateProgressPopup(pct, 'Pulizia globale file non associati', 'Cancellazione elemento ' + (index + 1) + ' \/ ' + total, current);\n        window.showOrphanCleanupEmergencyButton();\n        window.deleteSingleOrphanFile(db, file).then(function(res){\n            if (res && res.success) {\n                const deletedPath = (res.data && res.data.path) ? res.data.path : pathTxt;\n                const fullLine = 'Elemento ' + (index + 1) + ' \/ ' + total + ' completato - ' + label + ' [' + db + '] - ' + rowInfo + deletedPath;\n                const pct2 = Math.max(pct, Math.min(99, Math.round(35 + ((index + 1) \/ Math.max(1, total)) * 63)));\n                window.updateProgressPopup(pct2, 'Pulizia globale file non associati', 'Eliminato elemento ' + (index + 1) + ' \/ ' + total, fullLine);\n                if (window.orphanCleanupState) { window.orphanCleanupState.currentPath = fullLine; window.orphanCleanupState.lastPercent = pct2; window.orphanCleanupState.processed = index + 1; window.orphanCleanupState.deleted = deleted + 1; }\n                deletedPaths.push(fullLine);\n                setTimeout(function(){ window.deleteGlobalOrphanFilesSequential(items, index + 1, deleted + 1, errors, deletedPaths, ditteProcessed, ditteTotal); }, 80);\n            } else {\n                errors.push(current + ' - ' + ((res && res.data && res.data.message) ? res.data.message : 'errore eliminazione'));\n                if (window.orphanCleanupState) window.orphanCleanupState.processed = index + 1;\n                setTimeout(function(){ window.deleteGlobalOrphanFilesSequential(items, index + 1, deleted, errors, deletedPaths, ditteProcessed, ditteTotal); }, 80);\n            }\n        }).catch(function(e){\n            errors.push(current + ' - ' + ((e && e.message) ? e.message : 'errore rete'));\n            if (window.orphanCleanupState) window.orphanCleanupState.processed = index + 1;\n            setTimeout(function(){ window.deleteGlobalOrphanFilesSequential(items, index + 1, deleted, errors, deletedPaths, ditteProcessed, ditteTotal); }, 80);\n        });\n    };\n\n    window.refreshConfigHeaderButtons = function() {\n        const box = gE('ti-conf-global-btn');\n        if (!box) return;\n        let html = '<div style=\"display:flex; gap:8px; flex-wrap:wrap; justify-content:flex-end;\">';\n        \/\/ 30.9.664: i pulsanti di pulizia file non associati non devono essere mostrati nella parte alta della Configurazione, per nessun ruolo.\n        if (window.configInstructionCheckActive) {\n            html += '<button type=\"button\" class=\"ti-btn\" style=\"background:#4b5563; color:#fff;\" onclick=\"window.showAllConfigTables()\">\u21a9 Mostra tutto<\/button>';\n        }\n        html += '<\/div>';\n        box.innerHTML = html;\n        if (window.refreshOrphanCleanupButton) window.refreshOrphanCleanupButton();\n    };\n\n    window.verifyInstructionsArchive = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return;\n        const view = window.buildInstructionsViewDb(window.currentDbData);\n        window.currentDbViewData = view;\n        window.configInstructionCheckActive = true;\n        window.renderConfig();\n        const stats = (view && view.__instructionStats) ? view.__instructionStats : {tables:0, rows:0};\n        if (!stats.rows) {\n            window.tiAlert('Nessuna riga con Istruzioni o Istruzione compilata trovata nell archivio della ditta.');\n        } else {\n            window.tiAlert('Verifica istruzioni completata.<br>Tabelle con istruzioni: <b>' + stats.tables + '<\/b><br>Righe selezionate: <b>' + stats.rows + '<\/b>');\n        }\n    };\n\n    window.showAllConfigTables = function() {\n        window.currentDbViewData = null;\n        window.configInstructionCheckActive = false;\n        window.renderConfig();\n    };\n\n\n    window.__ensurePluginDateInputs = function() {\n        if (typeof window.initPluginDateInputs === 'function') return;\n        window.initPluginDateInputs = function(root) {\n            const scope = root || document;\n            scope.querySelectorAll('input[type=\"date\"], input[type=\"time\"]').forEach(function(inp){\n                const v = String(inp.value || '').trim();\n                if (inp.type === 'date' && !\/^\\d{4}-\\d{2}-\\d{2}$\/.test(v)) inp.value = '';\n                if (inp.type === 'time' && !\/^\\d{2}:\\d{2}$\/.test(v)) inp.value = '';\n                if (inp.type === 'date') { inp.placeholder = 'gg\/mm\/aaaa'; inp.title = inp.title || 'Seleziona una data'; }\n                if (inp.type === 'time') { inp.placeholder = 'hh:mm'; inp.title = inp.title || 'Seleziona un orario'; }\n            });\n        };\n    };\n\n    window.initConfigTopScrollbars = function(root) {\n        try {\n            const scope = root || document;\n            scope.querySelectorAll('.ti-sync-hscroll-wrap').forEach(function(wrap){\n                if (!wrap || !wrap.querySelector) return;\n                const table = wrap.querySelector('table');\n                if (!table) return;\n                const topBar = wrap.previousElementSibling && wrap.previousElementSibling.classList && wrap.previousElementSibling.classList.contains('ti-sync-hscroll-top') ? wrap.previousElementSibling : null;\n                const bottomBar = wrap.nextElementSibling && wrap.nextElementSibling.classList && wrap.nextElementSibling.classList.contains('ti-sync-hscroll-bottom') ? wrap.nextElementSibling : null;\n                const bars = [topBar, bottomBar].filter(Boolean);\n                const width = Math.max(table.scrollWidth || 0, table.offsetWidth || 0, wrap.scrollWidth || 0, wrap.clientWidth || 0, (table.tHead && table.tHead.scrollWidth) || 0, (table.tBodies && table.tBodies[0] && table.tBodies[0].scrollWidth) || 0);\n                const canScroll = width > ((wrap.clientWidth || 0) + 4);\n                bars.forEach(function(bar){\n                    const inner = bar.querySelector('.ti-cfg-top-scroll-inner, .ti-sync-hscroll-inner');\n                    if (!inner) return;\n                    inner.style.width = width + 'px';\n                    bar.style.display = canScroll ? 'block' : 'none';\n                });\n                if (wrap.dataset.syncBound !== '1') {\n                    const syncBars = function(source){\n                        const left = source && typeof source.scrollLeft === 'number' ? source.scrollLeft : wrap.scrollLeft;\n                        if (wrap.scrollLeft !== left) wrap.scrollLeft = left;\n                        bars.forEach(function(bar){ if (bar && bar.scrollLeft !== left) bar.scrollLeft = left; });\n                    };\n                    wrap.addEventListener('scroll', function(){ syncBars(wrap); }, { passive:true });\n                    bars.forEach(function(bar){ bar.addEventListener('scroll', function(){ syncBars(bar); }, { passive:true }); });\n                    wrap.dataset.syncBound = '1';\n                }\n                bars.forEach(function(bar){ if (bar) bar.scrollLeft = wrap.scrollLeft || 0; });\n            });\n        } catch(e) {}\n    };\n\n    window.bindConfigTableUX = function(root) {\n        try {\n            const scope = root || document;\n            scope.querySelectorAll('details.ti-cfg-details').forEach(function(det){\n                if (det.dataset.uxBound !== '1') {\n                    det.addEventListener('toggle', function(){ setTimeout(function(){ window.initConfigTopScrollbars(scope); }, 60); });\n                    det.dataset.uxBound = '1';\n                }\n            });\n            \/\/ 30.9.664: binding difensivo per filtri intestazione.\n            \/\/ Evita che click\/tasti nel filtro vengano interpretati come sort o comandi della tabella\n            \/\/ e funziona anche quando l'oninput inline viene perso da browser\/HTML complesso.\n            scope.querySelectorAll('.ti-cfg-filter').forEach(function(inp){\n                inp.removeAttribute('readonly'); inp.removeAttribute('disabled'); inp.readOnly = false; inp.disabled = false; if (inp.style) inp.style.setProperty('pointer-events', 'auto', 'important');\n                if (inp.dataset.filterBound435 === '1') return;\n                const read = function(){\n                    return {\n                        tbl: inp.getAttribute('data-tbl') || '',\n                        key: inp.getAttribute('data-key') || '',\n                        value: inp.value || ''\n                    };\n                };\n                inp.addEventListener('click', function(ev){ ev.stopPropagation(); });\n                inp.addEventListener('mousedown', function(ev){ ev.stopPropagation(); });\n                inp.addEventListener('keydown', function(ev){ ev.stopPropagation(); if (ev.key === 'Enter') { ev.preventDefault(); const x = read(); if (window.toggleConfigTableFilterManual522) window.toggleConfigTableFilterManual522(x.tbl, x.key, inp, null, ev); else if (window.applyConfigTableFilterImmediate) window.applyConfigTableFilterImmediate(x.tbl, x.key, x.value, inp); } });\n                inp.addEventListener('input', function(ev){ ev.stopPropagation(); const x = read(); if (window.scheduleConfigTableFilter) window.scheduleConfigTableFilter(x.tbl, x.key, x.value, inp); });\n                inp.addEventListener('change', function(ev){ ev.stopPropagation(); const x = read(); if (window.scheduleConfigTableFilter) window.scheduleConfigTableFilter(x.tbl, x.key, x.value, inp); });\n                inp.dataset.filterBound435 = '1';\n            });\n            if (!window.__cfgResizeBound) {\n                window.addEventListener('resize', function(){ window.initConfigTopScrollbars(document); }, { passive:true });\n                window.__cfgResizeBound = true;\n            }\n        } catch(e) {}\n    };\n\n\n    window.tiConfigSchemaDescriptions = {\"_meta\": {\"source\": \"Shop-Service - Struttura standard database.docx\", \"database\": \"shopservicemain\", \"version\": \"30.9.664\", \"note\": \"Descrizioni operative dei campi principali usate da configurazione, AI, validazioni, alias e relazioni ufficiali. Payments ristrutturata per Metodo.\"}, \"global_fields\": {\"Data\": \"Data di ultima variazione nel formato DD\/MM\/YYYY.\", \"Ora\": \"Ora di ultima variazione nel formato HH:MM.\", \"Nota\": \"Note generali.\", \"Stato\": \"Stato di disponibilit\u00e0 del set dati.\", \"Username\": \"Username univoco che individua l'utente registrato\/loggato.\", \"PIVA_VAT\": \"Partita IVA per fatturazione B2B.\", \"IVA_VAT\": \"Percentuale IVA applicata oppure scorporata dai prezzi comprensivi IVA.\", \"ADE\": \"Codice ADE per fatturazione elettronica Italia.\"}, \"tables\": {\"Config\": {\"Ditta\": \"Nome ditta.\", \"Attivita\": \"Attivit\u00e0 economica ditta con ricerca ATECO.\", \"Ragione sociale\": \"Ragione sociale della ditta.\", \"Logo\": \"Logo della ditta.\", \"COD_FISC\": \"Codice fiscale se necessario.\", \"Sez_ordine\": \"Sezionale per la numerazione ordini.\", \"Num_ordine\": \"Ultimo numero ordine.\", \"Sez_fattura\": \"Sezionale per la numerazione fatture.\", \"Num_fattura\": \"Ultimo numero fattura.\", \"Sez_Corrispettivo\": \"Sezionale per la numerazione corrispettivi.\", \"Num_Corrispettivo\": \"Ultimo numero corrispettivi.\", \"Email ditta\": \"Email principale della ditta.\", \"Email prodotti\": \"Email di riferimento per prodotti.\", \"Email servizi\": \"Email di riferimento per servizi.\", \"Email amministrazione\": \"Email amministrazione.\", \"Tel\": \"Telefono ditta, anche per SMS.\", \"Indirizzo\": \"Indirizzo ditta.\", \"Descrizione\": \"Descrizione breve della ditta e della sua attivit\u00e0.\", \"Accoglienza\": \"Messaggio di accoglienza utente standard.\", \"Istruzioni\": \"Istruzioni per AI relative alla ditta.\", \"Tab_tab\": \"Formato visualizzazione righe tabella ordine: Tabella.\", \"Tab_sma\": \"Formato visualizzazione righe tabella ordine: Small.\", \"Tab_med\": \"Formato visualizzazione righe tabella ordine: Medium.\", \"Tab_lar\": \"Formato visualizzazione righe tabella ordine: Large.\", \"Data creazione\": \"Data creazione ditta. Periodo test 30 giorni successivi.\", \"Stato\": \"Stato Shop & Service della ditta: Attivo, Operativo, Configurazione, Sospesa, Demo.\"}, \"Payments\": {\"Metodo\": \"Metodo di pagamento: bank_transfer, PayPal o altro metodo supportato.\", \"enabled\": \"Abilita\/disabilita il metodo di pagamento.\", \"beneficiary\": \"Beneficiario bonifico.\", \"iban\": \"IBAN bonifico.\", \"bic\": \"BIC\/SWIFT bonifico.\", \"bank_name\": \"Nome banca.\", \"payment_terms\": \"Condizioni di pagamento, es. Bonifico.\", \"instructions\": \"Istruzioni di pagamento per l'utente.\", \"merchant_mode\": \"Modalit\u00e0 merchant PayPal.\", \"environment\": \"Ambiente PayPal: live o sandbox.\", \"client_id\": \"Client ID PayPal.\", \"client_secret_ref\": \"Riferimento sicuro al client secret.\", \"webhook_id\": \"ID webhook PayPal.\", \"currency\": \"Valuta, es. EUR.\", \"intent\": \"Intent PayPal, es. CAPTURE.\", \"ui_mode\": \"Modalit\u00e0 UI pagamento, es. buttons.\", \"brand_name\": \"Nome brand mostrato nel pagamento.\", \"locale\": \"Locale interfaccia, es. it_IT.\", \"invoice_prefix\": \"Prefisso fattura.\", \"debug\": \"Abilita log\/debug pagamento.\"}, \"Informazioni\": {\"Tipo\": \"Tipo di informazione: WEB, URL sito e sotto-URL.\", \"Descrizione\": \"Descrizione o URL del sito, es. https:\/\/www.tisoft.it.\", \"Doc\": \"Documento collegato TXT, PDF ecc.\", \"Istruzioni\": \"Istruzioni per AI relative all'informazione.\"}, \"Istruzioni\": {\"Istruzione\": \"Istruzione particolare per la AI, es. sconto 10% fino al 31\/12\/2026.\", \"Doc\": \"Documento collegato TXT, PDF ecc.\", \"Da gg\": \"Data inizio validit\u00e0 istruzione.\", \"Da ora\": \"Ora inizio validit\u00e0 istruzione.\", \"A gg\": \"Data fine validit\u00e0 istruzione.\", \"A ora\": \"Ora fine validit\u00e0 istruzione.\"}, \"Utenti\": {\"Utente\": \"Cognome nome o username.\", \"Password\": \"Password.\", \"Ruolo\": \"Ruolo utente: Amministratore, Configuratore, Responsabile, Utente.\", \"Ragione sociale\": \"Ragione sociale utente\/cliente.\", \"Indirizzo\": \"Indirizzo utente\/cliente.\", \"Email\": \"Email utente.\", \"Tel\": \"Telefono utente.\", \"Sesso\": \"Sesso utente se utile.\", \"Et\u00e0\": \"Et\u00e0 utente.\", \"Eta\": \"Et\u00e0 utente.\", \"Immagine\": \"Avatar utente.\", \"Istruzioni\": \"Istruzioni particolari ad AI per l'utente.\", \"Dato 1\": \"Informazione utile per gestire il rapporto.\", \"Dato 2\": \"Informazione utile per gestire il rapporto.\", \"Dato 3\": \"Informazione utile per gestire il rapporto.\", \"Esito\": \"Esito della prima interazione con l'utente.\", \"Com Mail\": \"Autorizzazione comunicazione postale.\", \"Com Email\": \"Autorizzazione comunicazione email.\", \"Com SMS\": \"Autorizzazione comunicazione SMS.\", \"Com Tel\": \"Autorizzazione comunicazione telefonica.\", \"COD_FISC\": \"Codice fiscale utente se necessario.\"}, \"Operatori\": {\"Utente\": \"Cognome nome o username operatore.\", \"Ruolo\": \"Ruolo operatore.\", \"Competenze\": \"Competenze professionali dell'operatore.\", \"Disponibilita\": \"Limiti di disponibilit\u00e0 dell'operatore.\", \"Disponibilit\u00e0\": \"Limiti di disponibilit\u00e0 dell'operatore.\", \"Costo_orario\": \"Costo orario per la ditta.\", \"Prezzo_orario\": \"Prezzo orario per l'utente finale.\", \"Istruzioni\": \"Istruzioni AI relative all'operatore.\"}, \"Siti\": {\"Descrizione\": \"Locazione di esecuzione delle attivit\u00e0 della ditta.\", \"Indirizzo\": \"Indirizzo del sito\/sede.\", \"Quantit\u00e0\": \"Quantit\u00e0 disponibile.\", \"Quantita\": \"Quantit\u00e0 disponibile.\", \"Disponibilit\u00e0\": \"Disponibilit\u00e0 del sito, es. SI\/NO.\", \"Disponibilita\": \"Disponibilit\u00e0 del sito, es. SI\/NO.\", \"Istruzioni\": \"Istruzioni AI relative al sito.\"}, \"Dispositivi\": {\"Descrizione\": \"Strumento\/macchinario per l'esecuzione delle attivit\u00e0 della ditta.\", \"Quantit\u00e0\": \"Quantit\u00e0 disponibile.\", \"Quantita\": \"Quantit\u00e0 disponibile.\", \"Disponibilit\u00e0\": \"Disponibilit\u00e0 del dispositivo.\", \"Disponibilita\": \"Disponibilit\u00e0 del dispositivo.\", \"Istruzioni\": \"Istruzioni AI relative al dispositivo.\"}, \"Prodotti\": {\"Prodotto\": \"Nome breve prodotto.\", \"Descrizione\": \"Descrizione estesa prodotto.\", \"Prezzo\": \"Prezzo prodotto.\", \"Costo\": \"Costo prodotto.\", \"Quantit\u00e0\": \"Giacenza\/quantit\u00e0 prodotto.\", \"Quantita\": \"Giacenza\/quantit\u00e0 prodotto.\", \"Un\": \"Unit\u00e0 di misura.\", \"Fascia\": \"Eventuale fascia di riferimento.\", \"Immagine\": \"Immagine prodotto.\", \"Indicazioni\": \"Specifiche indicazioni d'uso del prodotto.\", \"Istruzioni\": \"Istruzioni AI relative al prodotto.\", \"Disponibilita\": \"Giacenza prodotto disponibile.\", \"Disponibilit\u00e0\": \"Giacenza prodotto disponibile.\", \"Riordino\": \"Giorni di riordino o consegna del fornitore.\"}, \"Servizi\": {\"Servizio\": \"Nome breve servizio.\", \"Descrizione\": \"Descrizione estesa servizio.\", \"Prezzo\": \"Prezzo servizio.\", \"Costo\": \"Costo servizio.\", \"Giorni_disponibilita\": \"Periodo di disponibilit\u00e0, es. giorni della settimana.\", \"Ora_disponibilita\": \"Periodo di disponibilit\u00e0, es. orari giornalieri.\", \"Data_variazione\": \"Data dell'ultima variazione del servizio.\", \"Ora_variazione\": \"Ora dell'ultima variazione del servizio.\", \"Durata standard\": \"Durata standard del servizio, es. 00:00:30 mezzora.\", \"Fascia\": \"Eventuale fascia di riferimento.\", \"Operatore1\": \"Operatore standard dedicato al servizio.\", \"Operatore2\": \"Operatore standard dedicato al servizio.\", \"Operatore3\": \"Operatore standard dedicato al servizio.\", \"Dispositivo1\": \"Dispositivo standard dedicato al servizio.\", \"Dispositivo2\": \"Dispositivo standard dedicato al servizio.\", \"Dispositivo3\": \"Dispositivo standard dedicato al servizio.\", \"Prodotto1\": \"Prodotto standard utilizzato per erogare il servizio.\", \"Prodotto2\": \"Prodotto standard utilizzato per erogare il servizio.\", \"Prodotto3\": \"Prodotto standard utilizzato per erogare il servizio.\", \"Sito1\": \"Locazione standard per erogare il servizio.\", \"Sito2\": \"Locazione standard per erogare il servizio.\", \"Sito3\": \"Locazione standard per erogare il servizio.\", \"Istruzioni\": \"Istruzioni AI relative al servizio.\"}, \"Appuntamenti\": {\"Username\": \"Username utente di riferimento.\", \"Servizio\": \"Descrizione breve servizio.\", \"Data_appuntamento\": \"Data dell'appuntamento.\", \"Ora_appuntamento\": \"Ora dell'appuntamento.\", \"Operatore1\": \"Operatore standard o assegnato al servizio.\", \"Operatore2\": \"Operatore standard o assegnato al servizio.\", \"Operatore3\": \"Operatore standard o assegnato al servizio.\", \"Dispositivo1\": \"Dispositivo standard o assegnato al servizio.\", \"Dispositivo2\": \"Dispositivo standard o assegnato al servizio.\", \"Dispositivo3\": \"Dispositivo standard o assegnato al servizio.\", \"Prodotto1\": \"Prodotto standard o assegnato al servizio.\", \"Prodotto2\": \"Prodotto standard o assegnato al servizio.\", \"Prodotto3\": \"Prodotto standard o assegnato al servizio.\", \"Sito1\": \"Locazione standard o assegnata al servizio.\", \"Sito2\": \"Locazione standard o assegnata al servizio.\", \"Sito3\": \"Locazione standard o assegnata al servizio.\", \"Preparazione\": \"Eventuali note all'utente di preparazione al servizio.\", \"Istruzioni\": \"Istruzioni AI relative all'appuntamento.\"}, \"Attivita\": {\"Username\": \"Username utente.\", \"Descrizione\": \"Descrizione dettagliata dell'attivit\u00e0, es. acquisizione prodotti e servizi.\", \"Prezzo\": \"Prezzo dell'attivit\u00e0.\", \"Quantit\u00e0\": \"Quantit\u00e0 dell'attivit\u00e0.\", \"Quantita\": \"Quantit\u00e0 dell'attivit\u00e0.\", \"Esito\": \"Esito dell'attivit\u00e0.\", \"Operatore\": \"Operatore utilizzato.\", \"Dispositivo\": \"Dispositivo utilizzato.\", \"Sito\": \"Locazione dell'attivit\u00e0.\", \"Prodotto\": \"Prodotto fornito.\", \"Istruzioni\": \"Istruzioni AI relative all'attivit\u00e0 svolta.\", \"Durata\": \"Durata complessiva dell'attivit\u00e0 svolta.\"}}, \"aliases\": {\"Disponibilita\": [\"Disponibilita\", \"Disponibilit\u00e0\"], \"Quantita\": [\"Quantita\", \"Quantit\u00e0\"], \"Eta\": [\"Eta\", \"Et\u00e0\"], \"Attivita\": [\"Attivita\", \"Attivit\u00e0\"], \"Email amministrazione\": [\"Email amministrazione\", \"Email amministrativa\"], \"Data creazione\": [\"Data creazione\", \"Data_creazione\"], \"Data_appuntamento\": [\"Data_appuntamento\", \"Data appuntamento\"], \"Ora_appuntamento\": [\"Ora_appuntamento\", \"Ora appuntamento\"]}, \"relations\": {\"Servizi\": {\"Operatore\": \"Operatori\", \"Operatori\": \"Operatori\", \"Operatore1\": \"Operatori\", \"Operatore2\": \"Operatori\", \"Operatore3\": \"Operatori\", \"Dispositivo\": \"Dispositivi\", \"Dispositivi\": \"Dispositivi\", \"Dispositivo1\": \"Dispositivi\", \"Dispositivo2\": \"Dispositivi\", \"Dispositivo3\": \"Dispositivi\", \"Prodotto\": \"Prodotti\", \"Prodotti\": \"Prodotti\", \"Prodotto1\": \"Prodotti\", \"Prodotto2\": \"Prodotti\", \"Prodotto3\": \"Prodotti\", \"Sito\": \"Siti\", \"Siti\": \"Siti\", \"Sito1\": \"Siti\", \"Sito2\": \"Siti\", \"Sito3\": \"Siti\"}, \"Appuntamenti\": {\"Operatore\": \"Operatori\", \"Operatori\": \"Operatori\", \"Operatore1\": \"Operatori\", \"Operatore2\": \"Operatori\", \"Operatore3\": \"Operatori\", \"Dispositivo\": \"Dispositivi\", \"Dispositivi\": \"Dispositivi\", \"Dispositivo1\": \"Dispositivi\", \"Dispositivo2\": \"Dispositivi\", \"Dispositivo3\": \"Dispositivi\", \"Prodotto\": \"Prodotti\", \"Prodotti\": \"Prodotti\", \"Prodotto1\": \"Prodotti\", \"Prodotto2\": \"Prodotti\", \"Prodotto3\": \"Prodotti\", \"Sito\": \"Siti\", \"Siti\": \"Siti\", \"Sito1\": \"Siti\", \"Sito2\": \"Siti\", \"Sito3\": \"Siti\", \"Servizio\": \"Servizi\"}, \"Attivita\": {\"Operatore\": \"Operatori\", \"Dispositivo\": \"Dispositivi\", \"Sito\": \"Siti\", \"Prodotto\": \"Prodotti\"}}};\n    window.normalizeConfigSchemaName = function(v) { return window.compactConfigRelationName ? window.compactConfigRelationName(v) : String(v == null ? '' : v).normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').toLowerCase().replace(\/[^a-z0-9]+\/g,''); };\n    window.getConfigFieldAliasCandidates = function(field) {\n        const schema = window.tiConfigSchemaDescriptions || {};\n        const aliases = schema.aliases || {};\n        const wanted = window.normalizeConfigSchemaName(field);\n        for (const canonical in aliases) {\n            const list = [canonical].concat(Array.isArray(aliases[canonical]) ? aliases[canonical] : []);\n            if (list.some(a => window.normalizeConfigSchemaName(a) === wanted)) return Array.from(new Set(list));\n        }\n        return [String(field || '')];\n    };\n    window.getConfigFieldDescription = function(tbl, key) {\n        const schema = window.tiConfigSchemaDescriptions || {};\n        const aliases = window.getConfigFieldAliasCandidates ? window.getConfigFieldAliasCandidates(key) : [key];\n        const tables = schema.tables || {};\n        const tKey = Object.keys(tables).find(t => window.normalizeConfigSchemaName(t) === window.normalizeConfigSchemaName(tbl)) || tbl;\n        const map = tables[tKey] || {};\n        for (const alias of aliases) {\n            const k = Object.keys(map).find(x => window.normalizeConfigSchemaName(x) === window.normalizeConfigSchemaName(alias));\n            if (k && map[k]) return String(map[k]);\n        }\n        const globals = schema.global_fields || {};\n        for (const alias of aliases) {\n            const k = Object.keys(globals).find(x => window.normalizeConfigSchemaName(x) === window.normalizeConfigSchemaName(alias));\n            if (k && globals[k]) return String(globals[k]);\n        }\n        return '';\n    };\n    window.getOfficialConfigRelationTarget = function(tbl, key) {\n        const schema = window.tiConfigSchemaDescriptions || {};\n        const rels = schema.relations || {};\n        const tKey = Object.keys(rels).find(t => window.normalizeConfigSchemaName(t) === window.normalizeConfigSchemaName(tbl));\n        if (!tKey) return '';\n        const map = rels[tKey] || {};\n        const k = Object.keys(map).find(f => window.normalizeConfigSchemaName(f) === window.normalizeConfigSchemaName(key));\n        return k ? String(map[k] || '') : '';\n    };\n\n    window.normalizeConfigRelationName = function(v) {\n        try {\n            return String(v == null ? '' : v).normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').toLowerCase().replace(\/[_\\-]+\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n        } catch(e) {\n            return String(v == null ? '' : v).toLowerCase().replace(\/[_\\-]+\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n        }\n    };\n\n    window.compactConfigRelationName = function(v) {\n        return window.normalizeConfigRelationName(v).replace(\/[^a-z0-9]+\/g, '');\n    };\n\n    window.getConfigRelationCatalog = function() {\n        return {\n            sito: {type:'sito', label:'Siti', tables:['Siti','Sito','Sedi','Sede'], picks:['Sito','Sede','Nome','Titolo','Descrizione','Indirizzo']},\n            operatore: {type:'operatore', label:'Operatori', tables:['Operatori','Operatore'], picks:['Operatore','Utente','Username','Nome','Cognome','Titolo','Descrizione']},\n            dispositivo: {type:'dispositivo', label:'Dispositivi', tables:['Dispositivi','Dispositivo','Risorse','Risorsa'], picks:['Dispositivo','Risorsa','Nome','Titolo','Descrizione','Codice']},\n            prodotto: {type:'prodotto', label:'Prodotti', tables:['Prodotti','Prodotto'], picks:['Prodotto','Articolo','Nome','Titolo','Descrizione','Codice']},\n            servizio: {type:'servizio', label:'Servizi', tables:['Servizi','Servizio'], picks:['Servizio','Nome','Titolo','Descrizione','Codice']},\n            voce: {type:'voce', label:'Prodotti\/Servizi', tables:['Prodotti','Servizi'], picks:['Prodotto','Servizio','Articolo','Nome','Titolo','Descrizione','Codice']}\n        };\n    };\n\n    window.getConfigRelationTypeFromKey = function(tbl, key) {\n        const nContact = window.normalizeConfigRelationName(key);\n        const cContact = window.compactConfigRelationName(key);\n        \/\/ I campi di contatto\/comunicazione sono campi liberi di edit, anche se nel nome contengono\n        \/\/ parole come prodotti o servizi. Esempio: Config > Email prodotti \/ Email servizi.\n        if (\/^(email|mail|pec|telefono|tel|cellulare|cell|sms|whatsapp)\/.test(cContact) || \/\\b(email|mail|pec|telefono|tel|cellulare|sms|whatsapp)\\b\/.test(nContact)) return '';\n        const officialTarget = window.getOfficialConfigRelationTarget ? window.getOfficialConfigRelationTarget(tbl, key) : '';\n        if (officialTarget) {\n            const oc = window.compactConfigRelationName(officialTarget);\n            if (oc.indexOf('siti') !== -1 || oc.indexOf('sede') !== -1) return 'sito';\n            if (oc.indexOf('operator') !== -1) return 'operatore';\n            if (oc.indexOf('dispositiv') !== -1 || oc.indexOf('risors') !== -1) return 'dispositivo';\n            if (oc.indexOf('prodott') !== -1) return 'prodotto';\n            if (oc.indexOf('serviz') !== -1) return 'servizio';\n        }\n        const n = window.normalizeConfigRelationName(key);\n        const c = window.compactConfigRelationName(key);\n        if (!c || c === 'id') return '';\n        const padded = ' ' + n + ' ';\n        const matchWord = function(words) {\n            return words.some(function(w){ return padded.indexOf(' ' + w + ' ') !== -1 || c === w || c === w + '1' || c === w + '2' || c === w + '3'; });\n        };\n        if (\/^voce[123]?$\/.test(c) || \/^voci[123]?$\/.test(c) || \/^(voce|voci) [123]$\/.test(n)) return 'voce';\n        if (matchWord(['sito','siti','sede','sedi','luogo','location'])) return 'sito';\n        if (matchWord(['operatore','operatori','tecnico','tecnici','addetto','addetti','incaricato','incaricati'])) return 'operatore';\n        if (matchWord(['dispositivo','dispositivi','risorsa','risorse','macchina','macchine','attrezzatura','attrezzature','postazione','postazioni'])) return 'dispositivo';\n        if (matchWord(['prodotto','prodotti','articolo','articoli','materiale','materiali'])) return 'prodotto';\n        if (matchWord(['servizio','servizi','prestazione','prestazioni'])) return 'servizio';\n        return '';\n    };\n\n    window.findConfigTableName = function(names) {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return '';\n        const all = Object.keys(window.currentDbData.Tabelle || {});\n        const wanted = (Array.isArray(names) ? names : [names]).map(function(x){ return window.compactConfigRelationName(x); });\n        return all.find(function(t){ return wanted.includes(window.compactConfigRelationName(t)); }) || '';\n    };\n\n    window.shouldUseMultiConfigRelation = function(tbl, key, row, meta) {\n        const n = window.normalizeConfigRelationName(key);\n        const v = (row && Object.prototype.hasOwnProperty.call(row, key)) ? String(row[key] == null ? '' : row[key]) : '';\n        if (\/[;,\\n]\/.test(v)) return true;\n        if (\/\\b(lista|elenco|ammessi|abilitati|disponibili|associati|selezionabili|multipli|multiplo)\\b\/.test(n)) return true;\n        if (\/\\b(siti|operatori|dispositivi|prodotti|servizi|risorse)\\b\/.test(n)) return true;\n        if (window.compactConfigRelationName(tbl) === 'servizi' && ['sito','operatore','dispositivo','prodotto'].includes(meta.type)) return true;\n        return false;\n    };\n\n    window.getConfigRelationMeta = function(tbl, key, row) {\n        const type = window.getConfigRelationTypeFromKey(tbl, key);\n        if (!type) return null;\n        const catalog = window.getConfigRelationCatalog();\n        const base = catalog[type];\n        if (!base) return null;\n        const currentTable = window.compactConfigRelationName(tbl);\n        const sameTarget = (base.tables || []).some(function(t){ return window.compactConfigRelationName(t) === currentTable; });\n        if (sameTarget && type !== 'voce') return null;\n        const meta = Object.assign({}, base);\n        meta.tables = (base.tables || []).slice();\n        meta.picks = (base.picks || []).slice();\n        meta.table = meta.tables[0] || '';\n        meta.realTables = meta.tables.map(function(t){ return window.findConfigTableName(t); }).filter(Boolean);\n        meta.sourceLabel = meta.realTables.length ? Array.from(new Set(meta.realTables)).join('\/') : (meta.label || meta.table || 'tabella sorgente');\n        meta.missingSource = !meta.realTables.length;\n        meta.multiple = window.shouldUseMultiConfigRelation(tbl, key, row || {}, meta);\n        return meta;\n    };\n\n    window.splitConfigRelationValues = function(raw) {\n        return String(raw == null ? '' : raw)\n            .split(\/[;,\\n]+\/)\n            .map(function(v){ return String(v || '').trim(); })\n            .filter(Boolean);\n    };\n\n    window.getConfigRowValueByNames = function(row, names) {\n        if (!row || typeof row !== 'object') return '';\n        const keys = Object.keys(row);\n        const wanted = (Array.isArray(names) ? names : [names]).map(function(n){ return window.compactConfigRelationName(n); });\n        const realKey = keys.find(function(k){ return wanted.includes(window.compactConfigRelationName(k)); });\n        return realKey ? String(row[realKey] == null ? '' : row[realKey]).trim() : '';\n    };\n\n    window.getConfigRelationRowLabel = function(row, meta) {\n        if (!row || typeof row !== 'object') return '';\n        let value = '';\n        (meta.picks || []).some(function(pick) {\n            const found = window.getConfigRowValueByNames(row, pick);\n            if (found) { value = found; return true; }\n            return false;\n        });\n        if (!value && (meta.type === 'operatore')) {\n            const nome = window.getConfigRowValueByNames(row, ['Nome']);\n            const cognome = window.getConfigRowValueByNames(row, ['Cognome']);\n            value = (nome + ' ' + cognome).trim();\n        }\n        if (!value) {\n            const skip = {id:1, stato:1, status:1, immagine:1, foto:1, file:1, password:1, pass:1};\n            const firstKey = Object.keys(row).find(function(k){ return !skip[window.compactConfigRelationName(k)] && String(row[k] == null ? '' : row[k]).trim() !== ''; });\n            if (firstKey) value = String(row[firstKey]).trim();\n        }\n        return value;\n    };\n\n    window.isConfigRelationRowAvailable = function(row) {\n        if (!row || typeof row !== 'object') return false;\n        const negative = \/(cancellat|eliminat|sospes|disabilit|inattiv|non disponibile|non attiv|scadut|chius|\\bno\\b|\\bfalse\\b|^0$)\/i;\n        let available = true;\n        Object.keys(row).forEach(function(k) {\n            const ck = window.compactConfigRelationName(k);\n            if (!\/(stato|status|attivo|attiva|abilitato|abilitata|disponibile|visibile|sospeso|cancellato)\/.test(ck)) return;\n            const v = String(row[k] == null ? '' : row[k]).trim();\n            if (v !== '' && negative.test(v)) available = false;\n        });\n        return available;\n    };\n\n    window.getConfigRelationSupportText = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return '';\n        const version = String((window.currentDbData && window.currentDbData.__db_version) || '') + '|' + Object.keys(window.currentDbData.Tabelle || {}).length;\n        if (window.__configRelationSupportCache && window.__configRelationSupportCache.version === version) return window.__configRelationSupportCache.text || '';\n        const aliases = ['Informazioni','Istruzioni','Appuntamenti'];\n        let out = '';\n        aliases.forEach(function(alias){\n            const real = window.findConfigTableName(alias);\n            const rows = real && window.currentDbData.Tabelle[real] ? window.currentDbData.Tabelle[real] : [];\n            (Array.isArray(rows) ? rows : Object.values(rows || {})).slice(0, 350).forEach(function(row){\n                if (!row || typeof row !== 'object') return;\n                out += ' ' + Object.keys(row).map(function(k){ return String(row[k] == null ? '' : row[k]); }).join(' ');\n                if (out.length > 60000) out = out.slice(0, 60000);\n            });\n        });\n        out = window.normalizeConfigRelationName(out);\n        window.__configRelationSupportCache = {version: version, text: out};\n        return out;\n    };\n\n    window.countConfigRelationMentions = function(text, value) {\n        const needle = window.normalizeConfigRelationName(value);\n        if (!needle || !text) return 0;\n        let count = 0;\n        let pos = 0;\n        while ((pos = text.indexOf(needle, pos)) !== -1) { count++; pos += Math.max(needle.length, 1); if (count > 20) break; }\n        return count;\n    };\n\n    window.normalizeConfigDateToken = function(v) {\n        const s = String(v == null ? '' : v).trim();\n        let m = s.match(\/^(\\d{4})-(\\d{2})-(\\d{2})\/);\n        if (m) return m[1] + '-' + m[2] + '-' + m[3];\n        m = s.match(\/^(\\d{1,2})\\\/(\\d{1,2})\\\/(\\d{4})$\/);\n        if (m) return m[3] + '-' + String(m[2]).padStart(2, '0') + '-' + String(m[1]).padStart(2, '0');\n        return '';\n    };\n\n    window.normalizeConfigTimeToken = function(v) {\n        const s = String(v == null ? '' : v).trim();\n        const m = s.match(\/^(\\d{1,2})[:.](\\d{2})\/);\n        if (!m) return '';\n        return String(m[1]).padStart(2, '0') + ':' + m[2];\n    };\n\n    window.getConfigRelationRowDate = function(row) {\n        if (!row || typeof row !== 'object') return '';\n        const keys = Object.keys(row);\n        const k = keys.find(function(key){ const c = window.compactConfigRelationName(key); return c === 'data' || c === 'giorno' || c === 'dataappuntamento' || c === 'date'; });\n        return k ? window.normalizeConfigDateToken(row[k]) : '';\n    };\n\n    window.getConfigRelationRowTime = function(row) {\n        if (!row || typeof row !== 'object') return '';\n        const keys = Object.keys(row);\n        const k = keys.find(function(key){ const c = window.compactConfigRelationName(key); return c === 'ora' || c === 'orario' || c === 'daora' || c === 'orainizio' || c === 'time'; });\n        return k ? window.normalizeConfigTimeToken(row[k]) : '';\n    };\n\n    window.getConfigRelationBusyValues = function(meta, currentRow, currentTbl, currentIdx) {\n        const busy = new Set();\n        if (!meta || !['operatore','sito','dispositivo'].includes(meta.type)) return busy;\n        const cDate = window.getConfigRelationRowDate(currentRow);\n        const cTime = window.getConfigRelationRowTime(currentRow);\n        if (!cDate || !cTime) return busy;\n        const real = window.findConfigTableName('Appuntamenti');\n        const rows = real && window.currentDbData && window.currentDbData.Tabelle ? window.currentDbData.Tabelle[real] : [];\n        (Array.isArray(rows) ? rows : Object.values(rows || {})).forEach(function(app, appIdx){\n            if (!app || typeof app !== 'object') return;\n            if (window.compactConfigRelationName(currentTbl) === window.compactConfigRelationName(real) && parseInt(currentIdx,10) === appIdx) return;\n            if (!window.isConfigRelationRowAvailable(app)) return;\n            if (window.getConfigRelationRowDate(app) !== cDate || window.getConfigRelationRowTime(app) !== cTime) return;\n            Object.keys(app).forEach(function(k){\n                if (window.getConfigRelationTypeFromKey(real, k) !== meta.type) return;\n                window.splitConfigRelationValues(app[k]).forEach(function(v){ if (v) busy.add(String(v).toLowerCase()); });\n            });\n        });\n        return busy;\n    };\n\n    window.getConfigRelationOptions = function(tbl, key, row, idx) {\n        const meta = window.getConfigRelationMeta(tbl, key, row || {});\n        const dbData = window.currentDbData;\n        if (!meta || !dbData || !dbData.Tabelle) return [];\n        const supportText = window.getConfigRelationSupportText();\n        const busy = window.getConfigRelationBusyValues(meta, row || {}, tbl, idx);\n        const out = [];\n        const seen = new Set();\n        const addOption = function(value, sourceTable, rowObj, force) {\n            value = String(value == null ? '' : value).trim();\n            if (!value) return;\n            const lower = value.toLowerCase();\n            if (seen.has(lower)) return;\n            if (!force && busy.has(lower)) return;\n            seen.add(lower);\n            const mentions = window.countConfigRelationMentions(supportText, value);\n            out.push({\n                value: value,\n                label: value,\n                sourceTable: sourceTable || '',\n                score: mentions,\n                title: (sourceTable ? ('Tabella: ' + sourceTable + '. ') : '') + (mentions ? ('Citato in Informazioni\/Istruzioni\/Appuntamenti: ' + mentions + ' volte.') : 'Record disponibile.')\n            });\n        };\n        (meta.tables || []).forEach(function(tblAlias){\n            const real = window.findConfigTableName(tblAlias);\n            if (!real) return;\n            let rows = dbData.Tabelle[real];\n            rows = Array.isArray(rows) ? rows : Object.values(rows || {});\n            rows.forEach(function(srcRow){\n                if (!window.isConfigRelationRowAvailable(srcRow)) return;\n                addOption(window.getConfigRelationRowLabel(srcRow, meta), real, srcRow, false);\n            });\n        });\n        window.splitConfigRelationValues(row && Object.prototype.hasOwnProperty.call(row, key) ? row[key] : '').forEach(function(v){ addOption(v, 'Valore attuale', null, true); });\n        out.sort(function(a, b){\n            if ((b.score || 0) !== (a.score || 0)) return (b.score || 0) - (a.score || 0);\n            return String(a.label || a.value).localeCompare(String(b.label || b.value), 'it');\n        });\n        return out;\n    };\n\n    window.updateMemFieldFromRelationSelect = function(tbl, idx, key, sel) {\n        if (!sel) return;\n        const protNorm = v => String(v == null ? '' : v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').replace(\/[^a-z0-9]\/g, '');\n        if (String(window.tiUser || '').toLowerCase() !== 'ssglobaladmin' && protNorm(tbl) === 'config' && ['stato','datacreazione','dataattivazione','wcorderid','wcactivateid'].includes(protNorm(key))) {\n            if (window.tiAlert) window.tiAlert('Campo riservato: modificabile solo da sistema, globaladmin o attivazione WooCommerce.');\n            return;\n        }\n        let values = [];\n        if (sel.multiple) values = Array.from(sel.selectedOptions || []).map(function(opt){ return String(opt.value || '').trim(); }).filter(Boolean);\n        else values = [String(sel.value || '').trim()].filter(Boolean);\n        const joined = values.join(', ');\n        if (window.currentDbData && window.currentDbData.Tabelle[tbl] && window.currentDbData.Tabelle[tbl][idx]) {\n            window.currentDbData.Tabelle[tbl][idx][key] = joined;\n            window.__configRelationSupportCache = null;\n            window.forceWhiteEditStyle && window.forceWhiteEditStyle(sel);\n            sel.style.setProperty('border-color', '#fbbf24', 'important');\n            if (!window.modifiedFields) window.modifiedFields = new Set();\n            window.modifiedFields.add(`${tbl}-${idx}-${key}`);\n            if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        }\n        const hint = sel.parentElement ? sel.parentElement.querySelector('.ti-rel-summary') : null;\n        if (hint) hint.textContent = joined || 'Nessun record associato';\n    };\n\n    window.updateMemFieldFromMultiSelect = window.updateMemFieldFromRelationSelect;\n\n    window.renderConfig = function() {\n        window.__ensurePluginDateInputs();\n        if(!window.currentDbData) return; if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); let dbData = window.currentDbViewData || window.currentDbData; let openDetails = []; const jsEsc = v => encodeURIComponent(String(v == null ? '' : v)); \n        document.querySelectorAll('details.ti-cfg-details').forEach(det => { if(det.open) openDetails.push(det.dataset.tbl); });\n        const cfgScrollState = { mainTop: 0, tables: {} };\n        try {\n            const mainCnt = gE('ti-conf-cnt');\n            if (mainCnt) cfgScrollState.mainTop = mainCnt.scrollTop || 0;\n            document.querySelectorAll('details.ti-cfg-details').forEach(function(det){\n                const tbl = det.dataset ? (det.dataset.tbl || '') : '';\n                const wrap = det.querySelector ? det.querySelector('.ti-cfg-table-container') : null;\n                if (tbl && wrap) cfgScrollState.tables[tbl] = { left: wrap.scrollLeft || 0, top: wrap.scrollTop || 0 };\n            });\n        } catch(e) {}\n        \n        const roleNorm517 = window.tiAiSsNormalizeRole517 ? window.tiAiSsNormalizeRole517(window.tiRole || window.tiUserRole || window.currentUserRole || '') : String(window.tiRole || '').toLowerCase();\n        const isSuperAdmin = roleNorm517.includes('amministratore') || roleNorm517.includes('administrator') || String(window.tiUser || '').toLowerCase() === 'ssglobaladmin'; \n        const canEditRows = window.tiAiSsConfigCanEditData517 ? window.tiAiSsConfigCanEditData517() : (isSuperAdmin || roleNorm517.includes('configuratore') || roleNorm517.includes('configurator') || roleNorm517.includes('responsabile'));\n        const isGlobalAdminConfig = String(window.tiUser || '').toLowerCase() === 'ssglobaladmin';\n        const cfgFieldNorm = v => String(v == null ? '' : v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').replace(\/[^a-z0-9]\/g, '');\n        const isConfigActivationProtectedField = function(tbl, key) {\n            if (isGlobalAdminConfig) return false;\n            if (cfgFieldNorm(tbl) !== 'config') return false;\n            return ['stato','datacreazione','dataattivazione','wcorderid','wcactivateid','activeid'].includes(cfgFieldNorm(key));\n        };\n        \n        gE('ti-conf-title').innerText = \"Impostazioni \" + window.currentDittaName + (window.configInstructionCheckActive ? ' - Verifica istruzioni' : ''); window.refreshConfigHeaderButtons(); let html = ``;\n        const visibleTables = Object.keys((dbData && dbData.Tabelle) ? dbData.Tabelle : {}).filter(tName => tName !== 'undefined');\n        visibleTables.forEach(function(tbl){ window.normalizeTableRowsToTemplate(tbl); });\n        if (!visibleTables.length) {\n            html = `<div style=\"padding:18px; color:#ddd; background:#111827; border:1px solid #374151; border-radius:10px;\">Nessuna tabella o riga con campo <b>Istruzioni\/Istruzione<\/b> compilato trovata nell archivio della ditta.<\/div>`;\n            const confCntEmpty = gE('ti-conf-cnt'); if (confCntEmpty) confCntEmpty.innerHTML = html;\n            return;\n        }\n        \n        for(let tName in dbData.Tabelle) {\n            if (tName === 'undefined') continue; let safeCls = tName.replace(\/[^a-zA-Z0-9]\/g, ''); let isOpen = openDetails.includes(tName) ? 'open' : ''; let isInstructionsTable = (tName.toLowerCase() === 'istruzioni'); let isConfigSingleRowTable = (window.isConfigSingleRowTable379 && window.isConfigSingleRowTable379(tName));\n                        if(dbData.Tabelle[tName] && !Array.isArray(dbData.Tabelle[tName])) dbData.Tabelle[tName] = Object.values(dbData.Tabelle[tName]);\n            html += `<details class=\"ti-cfg-details\" data-tbl=\"${window.escapeHtml(tName)}\" style=\"background:#111; margin-bottom:8px; border-radius:8px; border:1px solid #333;\" ${isOpen}><summary class=\"ti-cfg-summary\"><span class=\"ti-cfg-summary-main\">${tName}<\/span><button type=\"button\" class=\"ti-cfg-summary-help\" onclick=\"event.preventDefault();event.stopPropagation();window.askColumnHelp(decodeURIComponent('${jsEsc(tName)}'), '')\">?<\/button><\/summary>`;\n            \n            if(canEditRows) { \n                html += `<div style=\"padding:10px; display:flex; gap:10px; flex-wrap:wrap; background:#1f2937; border-bottom:1px solid #444; border-radius:5px 5px 0 0;\">`;\n                if (!isConfigSingleRowTable) html += `<button type=\"button\" class=\"ti-btn\" onclick=\"window.addTableRow(decodeURIComponent('${jsEsc(tName)}'))\" style=\"background:#2563eb; color:#fff;\">+ Riga<\/button>`;\n                if (isSuperAdmin) {\n                    html += `<button type=\"button\" class=\"ti-btn\" onclick=\"window.addTableCol(decodeURIComponent('${jsEsc(tName)}'))\" style=\"background:#059669; color:#fff;\">+ Colonna<\/button>`;\n                }\n                html += `<button type=\"button\" class=\"ti-btn ti-config-delete-selected-btn\" data-ti-config-delete-selected=\"1\" data-tbl-enc=\"${jsEsc(tName)}\" data-safe-cls=\"${safeCls}\" onclick=\"return window.handleConfigDeleteSelectedClick(event, this)\" style=\"background:#b91c1c; color:#fff;\">\ud83d\uddd1\ufe0f Elimina Sel.<\/button><\/div>`; \n            }\n\n            window.normalizeTableRowsToTemplate(tName);\n            html += `<div class=\"ti-cfg-top-scroll ti-sync-hscroll-top\"><div class=\"ti-cfg-top-scroll-inner ti-sync-hscroll-inner\"><\/div><\/div><div class=\"ti-cfg-table-container ti-sync-hscroll-wrap\"><table class=\"ti-cfg-table\">`; let rows = dbData.Tabelle[tName];\n            if (rows && rows.length > 0 && rows[0]) {\n                let keys = window.getTemplateSchemaKeys(tName).filter(k => k !== 'ID');\n                if (isInstructionsTable) {\n                    const desired = ['Istruzione','Da gg','A gg','Da ora','A ora','Nota','Stato','Immagine','Iva','Data','Ora'];\n                    keys = desired.filter(k => keys.includes(k)).concat(keys.filter(k => !desired.includes(k)));\n                } else if (tName.toLowerCase() === 'informazioni') {\n                    const desired = ['Tipo','Descrizione','Istruzioni','Data','Ora','Nota','Stato','Immagine','Iva'];\n                    keys = desired.filter(k => keys.includes(k)).concat(keys.filter(k => !desired.includes(k)));\n                }\n                if (!window.configTableState[tName]) window.configTableState[tName] = {filters:{}, filterDrafts:{}, sortKey:'', sortDir:'asc', pinKey:''};\n                if (!window.configTableState[tName].filterDrafts) window.configTableState[tName].filterDrafts = {};\n                const tableState = window.configTableState[tName];\n                let rowEntries = rows.map((row, idx) => {\n                    let realIdx = idx;\n                    if (row && typeof row.__ti_original_idx !== 'undefined') {\n                        const parsedIdx = parseInt(row.__ti_original_idx, 10);\n                        if (!isNaN(parsedIdx) && parsedIdx >= 0) realIdx = parsedIdx;\n                    }\n                    return {row: row, idx: realIdx, viewIdx: idx};\n                });\n                if (\/^attivit[a\u00e0]$\/i.test(String(tName || '')) && !(window.userCanSeeConfigurationActivities && window.userCanSeeConfigurationActivities())) {\n                    rowEntries = rowEntries.filter(function(entry){ return !(window.isConfigurationActivityLike && window.isConfigurationActivityLike(entry.row)); });\n                }\n                rowEntries = rowEntries.filter(entry => keys.every(k => {\n                    const filterVal = ((tableState.filters && tableState.filters[k]) || '').trim();\n                    if (!filterVal) return true;\n                    return window.configFilterMatches(entry.row && entry.row[k] != null ? entry.row[k] : '', filterVal);\n                }));\n                if (!tableState.sortKey) { const defKey = keys.find(k => \/^descrizione$\/i.test(k)) || keys.find(k => \/^(prodotto|servizio|nome|titolo)$\/i.test(k)) || ''; if (defKey) { tableState.sortKey = defKey; tableState.sortDir = 'asc'; } }\n                if (tableState.sortKey) {\n                    const sortKey = tableState.sortKey;\n                    const ascSort = tableState.sortDir !== 'desc';\n                    rowEntries.sort((a, b) => {\n                        let av, bv;\n                        if (sortKey === '__row_index') {\n                            av = {type:'num', value: (parseInt(a.idx, 10) || 0) + 1};\n                            bv = {type:'num', value: (parseInt(b.idx, 10) || 0) + 1};\n                        } else {\n                            av = window.getComparableCellValue({innerText: String(a.row && a.row[sortKey] != null ? a.row[sortKey] : '')});\n                            bv = window.getComparableCellValue({innerText: String(b.row && b.row[sortKey] != null ? b.row[sortKey] : '')});\n                        }\n                        let cmp = 0;\n                        if (av.type === 'num' && bv.type === 'num') cmp = av.value - bv.value;\n                        else cmp = String(av.value).localeCompare(String(bv.value), 'it');\n                        return ascSort ? cmp : -cmp;\n                    });\n                }\n                const rowSortDir = (tableState.sortKey === '__row_index' ? (tableState.sortDir === 'desc' ? '\u25bc' : '\u25b2') : '\u2195');\n                html += `<thead><tr><th class=\"ti-cfg-rownum\" title=\"Numero riga di riferimento\"><span style=\"cursor:pointer;display:inline-block;\" onclick=\"window.sortConfigTable(decodeURIComponent('${jsEsc(tName)}'),'__row_index')\">Riga<br><span style=\"font-size:10px;color:#93c5fd;\">rif.<\/span> <span class=\"ti-cfg-sort-ind\">${rowSortDir}<\/span><\/span><\/th>`;\n                keys.forEach((k, i) => {\n                    const isPinnedCol435 = String(tableState.pinKey || '') === String(k || '');\n                    let stickyClass = isPinnedCol435 ? 'ti-sticky-col' : '';\n                    let safeK = jsEsc(k);\n                    let dir = (tableState.sortKey === k ? (tableState.sortDir === 'desc' ? '\u25bc' : '\u25b2') : '\u2195');\n                    let fRaw = (tableState.filterDrafts && Object.prototype.hasOwnProperty.call(tableState.filterDrafts, k)) ? tableState.filterDrafts[k] : ((tableState.filters && tableState.filters[k]) ? tableState.filters[k] : '');\n                    let fVal = String(fRaw || '').replace(\/\"\/g,'&quot;');\n                    const pinTitle435 = isPinnedCol435 ? 'Sblocca colonna fissata a sinistra' : 'Fissa questa colonna a sinistra durante lo scorrimento orizzontale';\n                    html += `<th class=\"${stickyClass}\" data-ti-col-key=\"${window.escapeHtml(k)}\"><div class=\"ti-cfg-head-label\"><span style=\"cursor:pointer;\" onclick=\"window.sortConfigTable(decodeURIComponent('${jsEsc(tName)}'),decodeURIComponent('${safeK}'))\">${k} <span class=\"ti-cfg-sort-ind\">${dir}<\/span><\/span><span class=\"ti-cfg-head-actions\"><button type=\"button\" class=\"ti-cfg-pin-btn ${isPinnedCol435 ? 'is-active' : ''}\" title=\"${pinTitle435}\" onclick=\"event.preventDefault();event.stopPropagation();window.toggleConfigPinnedColumn(decodeURIComponent('${jsEsc(tName)}'), decodeURIComponent('${safeK}'), event)\">\ud83d\udccc<\/button><button type=\"button\" class=\"ti-cfg-help-btn\" title=\"Spiegazione colonna\" onclick=\"event.preventDefault();event.stopPropagation();window.askColumnHelp(decodeURIComponent('${jsEsc(tName)}'), decodeURIComponent('${safeK}'))\">?<\/button><\/span><\/div><div class=\"ti-cfg-filter-wrap-522\"><input type=\"text\" class=\"ti-cfg-filter\" data-tbl=\"${window.escapeHtml(tName)}\" data-key=\"${window.escapeHtml(k)}\" value=\"${fVal}\" onclick=\"event.stopPropagation()\" onkeydown=\"event.stopPropagation();if(event.key==='Enter'){event.preventDefault();window.toggleConfigTableFilterManual522&&window.toggleConfigTableFilterManual522(decodeURIComponent('${jsEsc(tName)}'),decodeURIComponent('${safeK}'),this,null,event)}\" onkeyup=\"event.stopPropagation()\" oninput=\"event.stopPropagation();window.setConfigTableFilterDraftManual521&&window.setConfigTableFilterDraftManual521(decodeURIComponent('${jsEsc(tName)}'),decodeURIComponent('${safeK}'), this.value, this)\" onchange=\"event.stopPropagation();window.setConfigTableFilterDraftManual521&&window.setConfigTableFilterDraftManual521(decodeURIComponent('${jsEsc(tName)}'),decodeURIComponent('${safeK}'), this.value, this)\" placeholder=\"Filtra...\"><button type=\"button\" class=\"ti-cfg-filter-lens-521 ti-cfg-filter-lens-522 ${String((tableState.filters && tableState.filters[k]) || '').trim() ? 'is-active' : ''}\" title=\"${String((tableState.filters && tableState.filters[k]) || '').trim() ? 'Filtro attivo: clicca per rimuoverlo' : 'Applica filtro righe'}\" aria-label=\"Applica o rimuovi filtro righe\" onclick=\"event.preventDefault();event.stopPropagation();return window.toggleConfigTableFilterManual522(decodeURIComponent('${jsEsc(tName)}'),decodeURIComponent('${safeK}'),this.previousElementSibling,this,event)\">\ud83d\udd0d<\/button><\/div><\/th>`;\n                });\n                if(canEditRows) html += `<th style=\"background:#991b1b; color:#fff; position:sticky; top:0; right:60px; z-index:20;\">SEL<br><input type=\"checkbox\" onclick=\"window.toggleAllTI(this, '${safeCls}', this)\"><\/th><th style=\"background:#991b1b; color:#fff; position:sticky; top:0; right:0; z-index:20;\">AZIONI<\/th>`;\n                html += `<\/tr><\/thead><tbody>`;\n                rowEntries.forEach((entry) => {\n                    const row = entry.row; const idx = entry.idx;\n                    if(!row) return; \n                    let isUtentiTable = (tName.toLowerCase() === 'utenti'); let rowUser = ''; let rowRole = '';\n                    if (isUtentiTable) {\n                        let kUser = Object.keys(row).find(k => k.toLowerCase() === 'username' || k.toLowerCase() === 'user'); let kRole = Object.keys(row).find(k => k.toLowerCase() === 'ruolo' || k.toLowerCase() === 'role');\n                        if(kUser && row[kUser]) rowUser = String(row[kUser]).trim(); if(kRole && row[kRole]) rowRole = String(row[kRole]).trim();\n                        if ((rowUser.toLowerCase() === 'ssglobaladmin' || rowUser.toLowerCase() === 'targetai') && window.tiUser.toLowerCase() !== rowUser.toLowerCase()) return;\n                        if (!isSuperAdmin && rowRole.toLowerCase().includes('amministratore')) return;\n                    }\n                    const instrValidity512 = (window.hasInstructionText && window.hasInstructionText(row) && window.instructionValidity512) ? window.instructionValidity512(row) : null;\n                    let rowNumClass512 = 'ti-cfg-rownum';\n                    let rowNumTitle512 = 'Riga di riferimento ' + (idx + 1);\n                    if (instrValidity512 && instrValidity512.status === 'expired') { rowNumClass512 += ' ti-instr-expired'; rowNumTitle512 += ' - istruzione scaduta\/non valida'; }\n                    else if (instrValidity512 && instrValidity512.status === 'future') { rowNumClass512 += ' ti-instr-future'; rowNumTitle512 += ' - istruzione non ancora valida'; }\n                    else if (instrValidity512 && instrValidity512.status === 'invalid') { rowNumClass512 += ' ti-instr-expired'; rowNumTitle512 += ' - periodo istruzione non valido'; }\n                    html += `<tr data-row-idx=\"${idx}\" data-tbl-enc=\"${jsEsc(tName)}\"><td class=\"${rowNumClass512}\" title=\"${window.escapeHtml(rowNumTitle512)}\">${idx + 1}<\/td>`;\n                    keys.forEach((key, i) => {\n                        let val = row[key]; if (val === undefined || val === null) val = ''; let safeVal = String(val).replace(\/\"\/g, '&quot;');\n                        let stickyClass = (String(tableState.pinKey || '') === String(key || '')) ? 'ti-sticky-col' : ''; let isMod = window.modifiedFields && window.modifiedFields.has(`${tName}-${idx}-${key}`); let bStyle = isMod ? 'border-color:#fbbf24 !important;' : '';\n                        const fieldDesc = window.getConfigFieldDescription ? window.getConfigFieldDescription(tName, key) : '';\n                        const fieldTitle = fieldDesc ? window.escapeHtml(key + ': ' + fieldDesc) : window.escapeHtml(key);\n                        html += `<td class=\"${stickyClass}\" title=\"${fieldTitle}\">`;\n                        let isImg = String(val).match(\/\\.(jpg|jpeg|png|webp|gif)\/i) || String(val).startsWith('http');\n                        let isDataCreazione = (key.toLowerCase() === 'data creazione' || key.toLowerCase() === 'data_creazione');\n                        let isActivationProtectedConfigField = isConfigActivationProtectedField(tName, key);\n                        let readOnlyAttr = '';\n                        if (isActivationProtectedConfigField && !isGlobalAdminConfig) readOnlyAttr = 'readonly disabled data-ti-activation-protected=\"1\" title=\"Campo riservato a sistema, globaladmin o attivazione WooCommerce\"';\n                        if (isInstructionsTable && !canEditRows) readOnlyAttr = 'readonly title=\"Tabella modificabile solo da amministratore, configuratore o responsabile\"';\n                        let isAteco = (key.toLowerCase() === 'attivit\u00e0' || key.toLowerCase() === 'attivita' || key.toLowerCase() === 'codice ateco');\n                        let isPassword = (key.toLowerCase() === 'password' || key.toLowerCase() === 'pass');\n                        let isMyRow = false; let isNewRow = false;\n                        if (isUtentiTable) { let kUser = Object.keys(row).find(k => k.toLowerCase() === 'username' || k.toLowerCase() === 'user'); let rU = kUser && row[kUser] ? String(row[kUser]).trim() : ''; isMyRow = (rU.toLowerCase() === window.tiUser.toLowerCase()); isNewRow = (rU === ''); }\n                        \n                        if (key.toLowerCase() === 'logo' || key.toLowerCase() === 'immagine' || key.toLowerCase().includes('file') || key.toLowerCase() === 'foto' || key.toLowerCase() === 'doc' || key.toLowerCase() === 'docs') {\n                            \n                            let fileList = safeVal.split(',').map(f => f.trim()).filter(f=>f);\n                            let firstImg = fileList.find(f => f.match(\/\\.(jpg|jpeg|png|webp|gif)\/i));\n                            let thumbHtml = '';\n                            if (firstImg) {\n                                let viewUrl = window.getSafePreviewUrl(firstImg, gE('ti-ditta').value, tName, key, window.getRecordLabelForUpload(tName, row, tName));\n                                const fbUrl = window.getCompanyFallbackMediaUrl(gE('ti-ditta').value);\n                                thumbHtml = `<img decoding=\"async\" src=\"${viewUrl}\" data-fallback=\"${window.escapeHtml(fbUrl)}\" onerror=\"window.applyFallbackImage(this)\" style=\"width:24px; height:24px; object-fit:cover; border-radius:4px; cursor:pointer; border:1px solid #444;\" onclick=\"window.openFileManager(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'))\" title=\"Apri Galleria\">`;\n                            } else if (fileList.length > 0) {\n                                thumbHtml = `<span style=\"font-size:18px; cursor:pointer;\" onclick=\"window.openFileManager(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'))\" title=\"Apri Galleria\">\ud83d\udcc4<\/span>`;\n                            } else {\n                                thumbHtml = `<span style=\"font-size:18px; color:#555; cursor:pointer;\" onclick=\"window.openFileManager(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'))\" title=\"Apri galleria file\">\ud83d\udcc1<\/span>`;\n                            }\n\n                            html += `<div style=\"display:flex; align-items:center; gap:5px;\">\n                                        ${thumbHtml}\n                                        <input type=\"text\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" aria-autocomplete=\"none\" data-ti-no-saved-info=\"1\" id=\"cfg-img-${tName}-${idx}-${key}\" data-tbl=\"${tName}\" data-idx=\"${idx}\" data-key=\"${key}\" onchange=\"window.updateMemField(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'), this)\" class=\"ti-in\" style=\"min-width:80px; width:120px; ${bStyle}\" value=\"${safeVal}\" title=\"Campo modificabile; usa File per gestire gli allegati\">\n                                        <button type=\"button\" class=\"ti-btn\" onclick=\"window.openFileManager(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'))\" style=\"padding:4px 6px; font-size:inherit; line-height:inherit; background:#4b5563; white-space:nowrap;\">File (${fileList.length})<\/button>\n                                     <\/div>`;\n\n                        } else { \n                            const relationMeta = window.getConfigRelationMeta(tName, key, row);\n                            if (relationMeta) {\n                                const relOptions = window.getConfigRelationOptions(tName, key, row, idx);\n                                const selectedVals = window.splitConfigRelationValues(val);\n                                const selectedMap = new Set(selectedVals.map(function(v){ return String(v).toLowerCase(); }));\n                                const sourceLabel = window.escapeHtml(relationMeta.sourceLabel || relationMeta.label || relationMeta.table || 'record');\n                                const selectSize = Math.max(1, Math.min(3, relOptions.length || 3));\n                                let optionsHtml = relOptions.map(function(opt){\n                                    const optValue = String(opt && opt.value != null ? opt.value : '').trim();\n                                    const optLower = optValue.toLowerCase();\n                                    const sel = selectedMap.has(optLower) ? ' selected' : '';\n                                    const source = String((opt && opt.sourceTable) || '');\n                                    const suffix = (relationMeta.realTables && relationMeta.realTables.length > 1 && source && source !== 'Valore attuale') ? (' [' + source + ']') : (source === 'Valore attuale' ? ' [attuale]' : '');\n                                    const title = window.escapeHtml((opt && opt.title) ? opt.title : 'Record disponibile.');\n                                    return `<option value=\"${window.escapeHtml(optValue)}\" title=\"${title}\"${sel}>${window.escapeHtml(optValue + suffix)}<\/option>`;\n                                }).join('');\n                                const relEmptyNotice = relOptions.length ? '' : ('Nessun record disponibile da ' + sourceLabel + '. Verifica che la tabella sorgente esista e contenga record attivi.');\n                                if (!relOptions.length) optionsHtml = `<option value=\"\" disabled>${window.escapeHtml(relEmptyNotice)}<\/option>`;\n                                const relSummary = selectedVals.join(', ');\n                                const relHint = relationMeta.multiple ? ('Seleziona una o pi\u00f9 voci disponibili da ' + sourceLabel) : ('Seleziona una voce disponibile da ' + sourceLabel);\n                                if (relationMeta.multiple) {\n                                    html += `<div style=\"display:flex; flex-direction:column; gap:5px; min-width:230px;\">\n                                        <div style=\"font-size:11px; color:#93c5fd;\">${relHint}<\/div>\n                                        <select multiple size=\"${selectSize}\" id=\"cfg-rel-${tName}-${idx}-${key}\" data-tbl=\"${tName}\" data-idx=\"${idx}\" data-key=\"${key}\" onchange=\"window.updateMemFieldFromRelationSelect(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'), this)\" class=\"ti-in ti-rel-select\" title=\"${fieldTitle}\" style=\"min-width:230px; max-height:92px; ${bStyle}\" ${readOnlyAttr}>${optionsHtml}<\/select>\n                                        ${relEmptyNotice ? `<div style=\"font-size:11px;color:#fbbf24;white-space:normal;\">${window.escapeHtml(relEmptyNotice)}<\/div>` : ''}\n                                        <div class=\"ti-rel-summary\" style=\"font-size:11px; color:#cbd5e1; white-space:normal;\">${window.escapeHtml(relSummary || 'Nessun record associato')}<\/div>\n                                    <\/div>`;\n                                } else {\n                                    const blankSel = selectedVals.length ? '' : ' selected';\n                                    html += `<div style=\"display:flex; flex-direction:column; gap:5px; min-width:210px;\">\n                                        <div style=\"font-size:11px; color:#93c5fd;\">${relHint}<\/div>\n                                        <select id=\"cfg-rel-${tName}-${idx}-${key}\" data-tbl=\"${tName}\" data-idx=\"${idx}\" data-key=\"${key}\" onchange=\"window.updateMemFieldFromRelationSelect(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'), this)\" class=\"ti-in ti-rel-select\" title=\"${fieldTitle}\" style=\"min-width:210px; ${bStyle}\" ${readOnlyAttr}><option value=\"\"${blankSel}>-- nessuno --<\/option>${optionsHtml}<\/select>\n                                        ${relEmptyNotice ? `<div style=\"font-size:11px;color:#fbbf24;white-space:normal;\">${window.escapeHtml(relEmptyNotice)}<\/div>` : ''}\n                                        <div class=\"ti-rel-summary\" style=\"font-size:11px; color:#cbd5e1; white-space:normal;\">${window.escapeHtml(relSummary || 'Nessun record associato')}<\/div>\n                                    <\/div>`;\n                                }\n                            } else if (isPassword && isUtentiTable && !isMyRow && !isNewRow) {\n                                html += `<div style=\"display:flex; align-items:center; gap:5px;\"><input type=\"password\" autocomplete=\"new-password\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" aria-autocomplete=\"none\" data-ti-no-saved-info=\"1\" id=\"cfg-txt-${tName}-${idx}-${key}\" data-tbl=\"${tName}\" data-idx=\"${idx}\" data-key=\"${key}\" onchange=\"if(this.value.trim()!=='') window.updateMemField(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'), this)\" class=\"ti-in\" title=\"${fieldTitle}\" style=\"min-width:100px; ${bStyle}\" placeholder=\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\"><\/div>`;\n                            } else {\n                                let isLong = String(val).length > 40 || key.toLowerCase().includes('descrizione') || key.toLowerCase().includes('note');\n                                if(isLong && !isDataCreazione && !isPassword) { html += `<div style=\"display:flex; align-items:center; gap:5px;\"><textarea autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" aria-autocomplete=\"none\" data-ti-no-saved-info=\"1\" id=\"cfg-txt-${tName}-${idx}-${key}\" data-tbl=\"${tName}\" data-idx=\"${idx}\" data-key=\"${key}\" onchange=\"window.updateMemField(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'), this)\" class=\"ti-in\" title=\"${fieldTitle}\" style=\"min-width:200px; height:40px; resize:vertical; ${bStyle}\" ${readOnlyAttr}>${safeVal}<\/textarea><\/div>`; } else {\n                                    const isDateLikeField = window.isDateFieldName(key);\n                                    const isTimeLikeField = window.isTimeFieldName(key);\n                                    let inpType = 'text'; \/\/ 30.9.667: campi edit configurazione sempre testo per evitare menu browser di informazioni salvate \n                                    let inputVal = safeVal;\n                                    let inputTitle = fieldTitle;\n                                    let inputPlaceholder = '';\n                                    if (isDateLikeField) { inputPlaceholder = 'DD\/MM\/YYYY o testo libero'; inputTitle = fieldTitle + ' - Puoi inserire una data precisa o testo libero da interpretare successivamente.'; }\n                                    if (isTimeLikeField) { inputPlaceholder = 'HH:MM o testo libero'; inputTitle = fieldTitle + ' - Puoi inserire un orario preciso o testo libero da interpretare successivamente.'; }\n                                    if ((isDateLikeField || isTimeLikeField) && (\/^D{2}\\\/M{2}\\\/Y{4}$\/i.test(String(val||'')) || \/^H{2}:M{2}$\/i.test(String(val||'')))) inputVal = ''; \n                                    html += `<div style=\"display:flex; align-items:center; gap:5px;\"><input type=\"${inpType}\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" aria-autocomplete=\"none\" data-ti-no-saved-info=\"1\" id=\"cfg-txt-${tName}-${idx}-${key}\" data-tbl=\"${tName}\" data-idx=\"${idx}\" data-key=\"${key}\" onchange=\"window.updateMemField(decodeURIComponent('${jsEsc(tName)}'), ${idx}, decodeURIComponent('${jsEsc(key)}'), this)\" class=\"ti-in ti-date-aware\" title=\"${window.escapeHtml(inputTitle)}\" placeholder=\"${window.escapeHtml(inputPlaceholder)}\" style=\"min-width:130px; ${bStyle}\" value=\"${String(inputVal).replace(\/\"\/g,'&quot;')}\" ${readOnlyAttr}>`;\n                                    if (isDateLikeField && !isActivationProtectedConfigField) html += `<button type=\"button\" class=\"ti-btn\" title=\"Inserisci data di oggi\" onclick=\"const d=new Date(); const v=String(d.getDate()).padStart(2,'0')+'\/'+String(d.getMonth()+1).padStart(2,'0')+'\/'+d.getFullYear(); this.previousElementSibling.value=v; this.previousElementSibling.dispatchEvent(new Event('change'));\" style=\"padding:4px 6px; font-size:11px; background:#374151;\">\ud83d\udcc5<\/button>`;\n                                    if (isAteco) html += `<button type=\"button\" class=\"ti-btn\" onclick=\"window.openAtecoSearch('cfg-txt-${tName}-${idx}-${key}')\" style=\"padding:4px; font-size:10px; background:#2563eb;\">\ud83d\udd0d<\/button>`;\n                                    html += `<\/div>`;\n                                }\n                            }\n                        }\n                        html += `<\/td>`;\n                    });\n                    if(canEditRows) { \n                        html += `<td style=\"padding:5px; text-align:center; background:#111827; position:sticky; right:60px; border-left:1px solid #374151; z-index:5;\"><input type=\"checkbox\" class=\"ti-config-row-select ti-cb-${safeCls}\" data-tbl=\"${window.escapeHtml(tName)}\" data-row-idx=\"${idx}\" value=\"${idx}\"><\/td>\n                        <td style=\"padding:5px; text-align:center; background:#111827; position:sticky; right:0; border-left:1px solid #374151; z-index:5; display:flex; gap:4px;\">\n                            ${isConfigSingleRowTable ? '' : `<button type=\"button\" class=\"ti-btn\" onclick=\"window.duplicateTableRow(decodeURIComponent('${jsEsc(tName)}'), ${idx})\" title=\"Duplica riga\" style=\"background:#10b981;color:#fff;padding:4px 6px;\">\ud83d\udcc4<\/button>`}\n                            <button type=\"button\" class=\"ti-btn ti-config-delete-row-btn\" data-ti-config-delete-row=\"1\" data-tbl-enc=\"${jsEsc(tName)}\" data-row-idx=\"${idx}\" onclick=\"return window.handleConfigDeleteRowClick(event, this)\" title=\"Elimina riga\" style=\"background:#ef4444;color:#fff;padding:4px 6px;\">\u274c<\/button>\n                        <\/td>`; \n                    }\n                    html += `<\/tr>`;\n                }); html += `<\/tbody>`;\n            } else html += `<tr><td style=\"padding:10px; color:#aaa;\">${isConfigSingleRowTable ? 'Nessun record Config disponibile. La tabella Config \u00e8 a riga unica e non consente aggiunta righe manuali.' : 'Nessun record. Usa il pulsante + Riga per iniziare.'}<\/td><\/tr>`; \n            html += `<\/table><\/div><div class=\"ti-cfg-bottom-scroll ti-sync-hscroll-bottom\"><div class=\"ti-cfg-top-scroll-inner ti-sync-hscroll-inner\"><\/div><\/div><\/details>`;\n        }\n        html += `<div style=\"margin-top:18px;padding:14px;border:1px solid #374151;border-radius:10px;background:#111827;\">\n            <h4 style=\"margin:0 0 12px 0;color:#fff;font-size:19px;line-height:1.25;display:flex;align-items:center;gap:10px;\"><img decoding=\"async\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAACAAElEQVR42pT9d5Bl2XkfCH6\/c81zaasyKyursryv7q6q9mg0PNDwoABIICmSIEWRM2KENLuamJg\/JmJnJ2J3R7E7sSNp5HaW4oxEkSMStABBEQJAgN1Ao72rNmW7bFZVevf8u\/ee8+0f55pz7r0vqxeo7s7KfPnMveecz\/0MaPQhYiIwSP8v+W\/xb2x+BULyHRR+izh7MHI\/in+L0x9y7jEofAnrdZOnZtY\/AAMg\/f\/kmdl6JAHZX5myxxXeGpV8eFgfkDn7gMb3kL1D\/QOYr5NdBs5dE86eqeQyWhe9\/PowW49KPikz67ee3KLk7UC\/Yc6uV\/rd9K9K5V6H2bpeKLmt9gXXr0HmXWGCeaGGLA\/rpnD+I8fvI1mr5XfQXFnZUtEvhOyaM9LFkr2UviwYcguY7EdnuwCIn5Bzt4jz9xDJcjG+6dp3ObcDWb+rkpWQfUAk95g4fjAbrzd0RcH8jv4VxNeJOVs5ADEz9AsQzCdB4an06xOXvDaMfYls+cNYV\/alyt9gtg8LmLsMw3dwycYCbbMDQcRDrrh5EZFbDMMeEH9tHQDZ2rEPwvSF9X2I70LyhoqHI3N2PWDfT4qPgCE3vbDhYLwSW5eY073B2T4ZdmXNBWmFDH0smVfeCDkMNo4oDD8B2To6rNvEhSOUt7l7+bUmjIuI7CS13ypQ3Dfm3SCOD5t4LxHS6IEhgTQLM4TkV3QcYS6elZzu1fRSIndQc8lOsU9l87jOFgnnNjXK7615WBR3GWDtz5KFEX9OQrrKrDPMvri5VylZdTxsqRjvMLtp+rxA8ur50z+L4cmdNC5FyQmDYixkIk5fm83XZ+tcy++U9AODjSzC2EXpjorfN3Cf7EUvBs5tgnRJljzYvLDYLjvKngLMSK5W8QKw8U1sd1wkjxK58MvpWjHPaza2e7JvCmEz9x2UvziMBA\/3O9CSfWcHXhByuyD\/blF8a8m248LNAJekidbq5PItYa9B3u5Kp0ssjcpMhXWy7cHJw4\/X+y3LIaGaMHRtFuOVvUo5WXPMua1c\/vZgfoV4qVjPyuYyh51pcnbXkdxGDL0XxXCH0lzVWLRZwOBtY5l5Bm8b6ZAPedveaohtKrb7Fij2m+f7BGPYRZa5gbj4G+DSRWMl3bDrRravVvpiYM4ibfl14HzBGMfnknvI2Y7iZCFut0XsC8LldwT5G8zWrwDZGceFD8D32ZCII40VxoygAgzLaGEHtPSvMPckl4W77OKbsShLGZiIYZYDyF4p3qhsl1YMLkttiyWcUZ4U7zcKNS2SzBelV+J+i3LYMYBh15WLtawoT31gZ+cYfouZ7ns6F7\/OLfjSQ61sB7BRXg9bP+YFQ66rkmyi+AplSWKSpZqbAcM+oZWWo+wGYOiNGbpHUbgPMK+MuZZRSI\/K3pgZCFHS9onfKYBiY6wQ38xTwLxmabbLxvVA8d3BXpVxbhz30+xqMw5ObKY9yTMi\/scquJnLz\/vk0cWlxLmohny7YUgEgRHLUZqHJQ9hpG0F3HfbsDAqeOasD1ZWoAKcT1StFZSe1bh\/RMj\/rfBW40SH7UWcxN0hLVdjtcT\/ZSM34azJhuxDp\/2RwrUZ1j9lNp93aOf3AxxYGLKPUShS7UiKYqfFKsHsFYZ8fZYvBLhYbyJXSxWPMuPXYa+1fDoRbx4uidn5pzWTgPRATLYic0lLL9dDZ7pfTmKkAYUCE4WgaWRZbBwt+sCONxynx1namtZNPyYryxvSRmMWTAxm69As9iCGnCC5O212\/Mz+Cm9bUfF2MZWLRz0P706nPwbbgYoLmUS+FZ4WObkXLXud7RKD\/GonI7MorN60TY6SDD95x1yWVeQ+nJXPc66pRcU0LjvNrKMOhTTKOg24\/AaiUPgVjyZQ6ZYzNl3pX1GeeJt1F9sxA1SoJ7cZYsQ5EQ9LJEoCGZc1aoyKjNODi+9\/GgytCcv3Sb4WTkN9cWqBrBeC8vr2fkHCKojYTErYOH7ZbFfZaV16SsXvCcb6QnGWidLPe5\/DFEbmuW2nwMqdkOvCouycS898mOfOkEQC+ZzLTAiHZOz57W38CuvNhlzLd1j8zr0q7PoO6fFTPADKFgKXtR3YOLqQFY1DVmlap27fsbJPLdyvf7nNDDc+tdJQxvmiumTmaYXgeESBfJVdfsSzLvCz3YeSojd+\/TQgDR1SsDn7KC+u08kp4krdOsbSQiHbnCjZKiW91OR7Rk2Yb90WZkYo22plqzzX+rPLHgzNOs3IBipLE\/Nz+XzX0r75923lF1qfQFn0AcqWLoqTK+tw2r6nh+EtJKM+S7v+WcxLcz97XIHyrKE0\/ber29JO2JDqgIfWEekELC55mJOdzeXTpfhOJj91OX8DYQ5ZkBQh6aQOiP+VtpWyyJvb9zEKx8oQuPz0sgfUMHtRhU2M3N+y9qLdY4YxnUdJ69YKibhv77nkTCJ7mp1NSnMftnRCSsWurj7gstInl\/7jA\/TFkJujY7vmaXadczAQ+37kU6skt2AjcWcqy+XKB5nIYkt+8Fpa0jEXG3R2VgoiFuWRFeXf42H3oLDUrJOPGUg2CLIGSrq5YL493qZUYWMwABe5\/Hl46QQUWjFGgz3tPCMbD+efgVn3gmEWdmVVqznGMd67AWrgYu2K8vduJbFA7qIO23M87CTlYhHK+Sab+c0MdFTat7CODqP5AAyd83D6bDxsg0Ekr8zlqyyPdEneOptHVjJeLRkCpTkOrOY9l4+400Oa80A73HeYBi7cpOTFOVtKJq7LuHTg4ekl3+\/OcvmRZCxLLtvRXLhV2x7iAJHIdanYSrjsA9DouOj+T4xQJLaRGMUGRTqIoTJoKJX2wc03YcU3mNcSw6fNQDFC53ee1e0HUFbwlA0RtpvCmAWBOZ8s7lUuiZNGBx5Wzz6dJmRVFgjbjMaz383NOmDl1WyUz0xgZOAxZto2RRiGzbBS\/HjZoGSbMuVnsmUbhoFc3g1YZW1ZSQ8My\/nzZ2wugUdpD4zJbjIMWRJlg\/RhiZzxP9e825wfnCDtiHN2gCBX3dtzhhjJaqAceJvWQtkpbsU14xhNR6zgQsMDhe3PeeR11kSBkQexkf+V4R1zaRBKU7\/7tba2BdHmfo2HIwrIBFnSfZLMoe3jbTpwyGNVrBiI+2BazZhhVZlcGo9hn6dDEbMAl5RVheKGt7m+GDqizl0loKQyskET5VmX+RQp+BkYkmgZRUeyCS2oKLM9ujSSx4SzUHJJQWBwbrZjZA+8\/RFhlERGDWlOluykyT42E5g5mwsp36ouaZ8w2z\/CfQpALpsXGmsU5dkt0\/2ab2XnNMjEl5ttfyM8lSRQyGVt1taCXRFt00KEkYqyVWrGaOqh6UURZ0PmtYHVZcpnc8ixa8zZkvHmk6CKXKIMa70CxlHMJSUNEqZHcrjGubiBQExWO5A1rWFf3eRotlKBBAGPXGJbPNqIiIR5wbisOM7NqTjJNLLcCRmVgq2sP9lFjCEN6HzbjTltC4Hvs1jNsRU4bUiVPK\/ZJQeVZDLYpvNhDLy374swbxPfymfytN3kbWihVbiXyCWfKB\/5D4mOBpLKTFOZrO9YIxMUh4Fm14aHTfZwnzY\/kpl3SmLJZg3gfFyzqyZrKmP1j0p7vrn2vNXOhAU\/hXUC5nLUktZhllYB2yOCs0gIO7YxzMZw\/o6nIOi0FcM5zAInJ1QuhHLpSZCvlmGcLen5wsZJw7nKuzBnNnpWGLYZMlYGrNmVfQPYBvuitNItOeTYyBZKW008ZOo\/7LcwvLk8FDYAsmKFHX+GnBRcfndQ3DFM9x\/DsUnxLPaG2SpAeNjZxSbBCMy6O2ncDR7SQuMC7zJduFROdsu\/vFmk2PvNRtOWNLyRrNnyM7RwE0Q+V+TyK29EVmOfmdveOIw5JSXlBjDYhtshEoiuwbQZksZuFyR52Pzd5Jwg1zyzo5gxvAS2mb\/aOCuDzMK56XS+q4CSM5LzDRoMqephNuh425iLYQX30LCdm3tRKc9rCIfGnu\/jvpU\/LNwQcjGK7LF\/liQbqG8ur91z1JMUmAk2CIt59EaxRkQRHVjoJ1lQsW2aeGWNNDZqwvy6yRXmRbSAQbAsOSTZ2EPmlK6s2IdR8PKQCMDlw2MupShbTSa2ome+8wUTrWdRxrMDk6yZG3LlOZOVaW+TNsd32xyRZVxoM00oQXrAqkSsKSQY5UlOoUfF9iXNpaf5cQiyji4+wBlo9uvL2z9cWrNaeQEKV1NHPSptlLF1ENmRMS1ak4hsoIrzobIA\/0YJ5JBQNqrMBfdhoBrrOUvRyEzkFnp8Zg+E2Q6fKUM5zjez3kGR0Y5CR75wWjFsrYuSTnNyRuT6MTBCPoZkVsnIBSZydAjRwMLDFeCXhY9l7CUewmE1GOs2kdBo\/7IFLjV6GkzDBA9QsviHBxyzm51yKouMSh46r+PS5CtrRfEwEML92uFU1tmOCfRGH6nkyprpi7GTNWgkrWPSbLYEsIUEUZtLKXibrDgntYBthrTD92EiWZAn\/gs78MOYHrGB\/bNiNGIOHXNhKlbG9E1\/rfieLZhuMb8p58kjxwne\/uNznr1vvdUirBPleHOg8InyZ5YVcWGSl4tho4h3Q5YbZXh842Gcz5NRKl+wTRcom\/LATgqRB7WW9cxiiATSRZ9\/3vsNYEqHmSg7VswDBoUjyR4kMRuDKzMlHs4dK+3eZcQjY4rKQweifJ9WS2m\/eejIVeSvzJB6BpYyS\/qEdiLMZYm8OQmCPU\/FNoAJNvSSigVQToIJOcBknqCmB5gAD1uupR0OtquXRB4inp2gJMG2V1I6KdlmRAG7kAZTKXe1VIUAhRdHnugD+66ymVTArg7LeQtWycqpKg4KvA1sewJsJx3CxeaQge7g3H5gsxjKpjZcwlkrOVqQv3YWy5GNdrrd5TUovyUFCBcbxMQlokWlZH1AD+utgRZgy0qZfHbr+purkIszQ+RG5\/m5Uf5JUrSABduzPngaqTCk1i3cy3wf34bbWP1PlFakVvsurSQBFBqzZRV4Ou60Xo+HDucoVbcyYYglQZjZ4qwMzYiSLQMmU\/oMbOAMyQKAWW1t4xhPRmpkiW7ZeS8ska\/7Iga2+YLzyQzMI2Q4TNsCKFp9cv0RuJRuwGaXgssHv1wcrzKs8ah1GnHZkJmH9LfdQkcHbGdyuayuBHcEW8iqZIaM7b9j7xxzcFogEtgpbjnQFXlcf\/oDtict5fgYMNl9pcJCYR7epId5ZOgSIIdD3D6NNroR9qGbpQ1civMYnhMx58mqnNarWdVtJyEFWEJaY5sILrsmLPbJUL6XP1gVUQZ8twmFGNoRS9cy8nkr2xD\/Qv3LhVdJpi1FDCsNabSUAWPT\/gTbRyQTwSUaBtOhYZl+3B4FD23CZrKFJfpHDNwnSS+b9aCQ1xVaF6J8\/IUMZG4MGrfJ5MFDUDRmPEyfKbeq2JzJITem2r55lp72uVcvqGzm4Ei5Riuo5NTdTmoVhTnSfYocHoqa5OJyyEanMDrDzFS6h4xVmoVfUInqiJUHcdn01VK3LG1B5W8BD515WscgZ7Pyoqgo3XcGaR4jDE4jYalkEnNp1Q1LuQ9WRGIaEpaRF7oyv5FW11zSjrZCMQ9J4oYMo4yuSZFxmKmklmM9UYBeMedp6PZ80fgIZVpbsBKNIYd8cQfBzorz2nAWbKpUu9QuCQzG8FCuDYacE6UzwPtAmxJWGw8ZTgwbsmj4GGwtXxSiENu5Yk6AV0sOD4+YuWk758ZgXDY8xzbn6Pa9ay4NdromZE77tiXPygW1gCQJRVqoF5L0QgGcL95QlADlXJJZqLXY2jM2rxSlqoXbYnSG4Fc5xu7kwBDgcjRDvgmOuNguJC9MJjK3XG6Gh1PbShKdJDnnYfvCLntyDL6hos\/ljdViv6v03g\/v1Oag0ObToFCUgGzJ8JJsLvvgnIdkmUGPPwjk3J4I5md2yE9soTVhhit9lc9J4x1jILbin7tmXYec8EG6oLKMolj7FTsrKOXdFeMAlw++yqaFpV042D3SsgFJkbNkD7vYJjzlRq5l05HsIpbPb5kS+Hy+FcuczGm4kL\/mWKpDsG9DThaUVgUwRl\/Y\/tQuSUJhs1hQtrFz8kvI37PyXDMXqxklUIQ8bNKqzswCO3siDCPzb5ddlxDvUJq\/JX0AIF9lfKCwaHWms8WTLE83uxYld4qL6VtWFJQ00RK58iInqmQpIDfILLsuwyQ0QNtOUpJVAGSSfAasILt9sGwZzFEv0xAIX+6Wo5zwXxCGN9qdBaYyU8lL3F\/od5vKo8DwN+tKbCPXxeUKuDwkphWXf7JVOB+fYPUmyxt29lMW60CwvduRJUdDGxucn+5ZhduwJiLbCXB69HIJ3clu7TMNfcrS4EQmn9BMvQ0YiIGKyapp5FYX2EaAGEWVSXPOYK3GjmAu7cIUi+9yKRcmaJ4rrD4\/EYGElfTYKAyT9GaVIjk8LOfyFQzzwmAqkqktwZSc4UT58hv+LZQMeTIjkvvNxs1xFYbNQy24H3IhGeVxE6XvlwuJy3Da5dB+8TYnXR6CwGU\/RwmO22j1bqtANgz6iiENGI7zRBSZeyYAqLz\/4+beJ5cg9sGlOXuZvw\/n2lwmCbVULCIfN3KDB+RRb\/kJYSpemyalKP1FlIhaFyLPMBeJIYAN+2PEQTU3SYuPJuQHCkNTmfyBfZ90ffgORGFzwJKIzY3UiMrcoTJiTdnuxRCsMnLaqCgZKvC2VdSQj2MwZnNZB5fOvEqRvSbZj4pqz5wPAJaTkJFHwPpEnE\/YhtmWcH5YHaejJU3GErol5fV5soQvY\/XafPMhczGUtBbNBIPKisB8Xgpbq02km5BTTWnLacCYACTWYciYGsp4SGFiUKTKcq63n6PLFvW\/koIIJREzz4cBlye0242Ncu1rprIU0VqUhv4OW1NZzg4SlE7Ht425KJbxtq1TQaSykF\/bbyD\/gUEFGAPT9t1cKvDQOUN\/2135klIWJZMfDC\/Z718sIKd\/lI0o4saM6UdVqtZKFskAsBRsY2JuTt2Yi6uhtEE8DHdSwi4x4x4IIgENCiIAgpGYGcDQaNHdkhjFzMyKmJkVWGXSWUkjqpCUprUijMVWFNy11k5Bz8E4zgwkHhMX6jQMHboUqjTOU8u5JH7C3t48LGBSmQL1B8l02VKNyilc8P3WKuj+Pe38LrBr1txSRZ6yaAdjk+nPqQ4wl6VsXKitE01dmNZ2WboALp\/S5XD\/VjqaRW7Ol71pT49KAQuF0xz59IhLW+flIz9YuhooEyPPeehAy4qBIEg4BJBwCA5BkHD1zgQECxCJ+PkUMzFYsVIgRUpBSVKSWBIzsdKGSUgcLcm0QSiZFZQOFIBh6USRXAAD5TkUJWHXSDAfHwcxttUfcnkXcijCUmQLF0ED208Ay+Z+QzyBABtZkOuMWGM5FGT+c\/116w5Y5p\/52QYPS3uzGoHtyFgOITDbcZwEKwNIkK2HWFEOQxpftgJfVse6ObhfgZmDoUAC5DYdcsQyHtYZh11Zwmhpozh7SI4iwI6ESegDSLgkHIJLwmHHI8cj4ZLjQgAQgCCRVGmsSClWTKyYFViSjEiGpCTJiFiSkmBmKJ2jgu2qL42N+W1oHW+8XSFchvYoiyDDMivjBVHUm+ESoKk5H+Ihg3K2WwDbSpVyWZjnYekYSlj75qfKtW0\/ED8IZguTh04q05kaclzxzAIAmdRXpj7LKK1a4wMONoVqm9qPttU7NH7XNQ6WfC8xD7ZAnuhlvqCdqGNYfl3ARRg9R2DIPCzbgZyZCYk09OmNB8djx4frk+O51drk\/tmpQ7smd0+MTk\/4o1UFUszRIOqstTrLWxt319au3+tuNFlFFIUcBYgCkiGriJQES2KlRR2RNqbNS2JqyoHLGzZUHB9zyWh\/O40ZS3eraEGUgcO5HCqRcuesHhmbU6H7avkYFUradiqO\/u9z0KCM8MvbYrip4MduKlVZwlVl4mlMOS0c2MaVRgJRMhUrEXtku0jk+3gpW20YHjK8TG\/E2Jny5kki+GhrriHRic4rW6MIDcvnZYYhMgrIiUIAtD3uwCnvLS75BIRDjsuOT44Pt0Jexa03po7s3XPm6M4jc\/XxURVE7eXN1nq33+kH\/ZAEudVKdbQ6MTXSmB6VYdRdby5eurXwzo3mnSUOBxwNKAxIBVARKUlKMiswEyvjXnCh517UfvwA5Trfb3Sen3OUN9YxLMu1\/8PmAAapAgzlO788pFZnKqZEhqEMSkYRdpBCuSghtlnHKMo05Tr5bJwzZqZNVH6PzEwlc9Xl8jKJy01t2IplXCqkmh0ORQkc8x1yStUbPZPftABt72sDID+9KGuRFclpjO1see1mpp1MJT1PCAIYDhwdAH24VfKq3ujozOlDRz9+bmL\/7MrN1ZuvXl+7sRQOBioIWEpWOrIxCQcCQjiOX6mO1\/efO3TwkUP10erN165c\/en5rflFHvQ47JMMoEKWIdJfTCRkmFMadtrCYev68vb9sSSucFnvjobBFEtYGFwyfygOj4ev8NxByWznJqb\/OBWmOHnaz\/Z9Gy6ZW2\/3G5xfZShrwA79TIUtZPOI8ttv6K7L7bHCcJ+zqVT+9DKkFHIHJNvpcNzF15uwrCl7vzQDJSVenjiQa8galCIubTygoG6W7UACCA4JQcKF47FbJa8iKrXJA3sf+vondhzc896P3p1\/81pvq0NRyCoiKUkGSbGnCKR\/HXDIcUi45HpOtTq5Z\/qhz56dPLTrwg9eu\/7cW4ONTYr6CPssB5ChLhQ5sydhpCCmTMCEy7PNkgWHIQMGDFlN1mCTP4BOWGETDpNjz2Mmh2FOSkgTPHR+vv0+HAJCYFMLnLNmI+KuZep8UKBGDUfusO12bm48U0eM8wN0tp2ghoRELnm8nR2g7J1QmWUL5zYhaBjuIN8HQynsAjbwBiizdkHOlM8C2pgNGE7rQMSTQMFCkOPGAdCveY2RU888fuyzH7r44vUbL17qbWxwMKAoiHstSpKScW4ZVwRgIQgi3syOS04FXtWp1Sf3737kq486vvvy735\/\/cpNDnoIexT1WYZJiajvWfqFga\/Zpl3KXDJgKEsrjcuQOGBZ9Dsu9jS4bELOBewRsoYcWxpW93fyK\/vR0FJ2+B41lCp46HCi6KGeL1JQVrkMl1jihPuskOUy5lY0vmkYDCb5jbVvcz6teULLUOQFZ09V3qpjInJQmaG8JMX9VGtKrFsNmQBDXsUgOlldVOacagtyFWDW+aRUT8Uh4ZDjkVshr0aVem1i\/KO\/+XOTx468\/Cev3HrtatDcpH6HBl0K+4gGFA0gA0Qh6XRUSVKKWIH1WCIiJaEklGQllZS9zc78u\/PCcc587SkpaeveRmJczLD9Y0z+e6oqAEtjpyCYC+T1YIv6MsX2b8nJiCImpRjW8u8h11QtEQQyxXnvq56P4b5mOaBS9lkSlALKGjGGOkl601NDbaMTDkAfxIBg4ZBwIFwIlxyXhEvCJTjkuAQXwmHhgASJ+LeoABgwYoQ1UAXA5bXSkLzZbsqVKktRIUibDROHKjMFgzxbMB33Q0XkPcSRmfKhTLwFZi2N8spQCMMdPJkEOi7cCrw6qvWR6emP\/dZXA1V58Y9f3Li9yP02BV0KehT2SQYkQ5IRKalHf8ZyYGKOx4N6TM96WhixilQQrt1a6zb7Z770mNdorF5fYqVIn6MFhzLkHBKNlVg2dIYt32SurVIweqLAg1IkGsqADcisHPMMC\/PlilA3NkLNMK1xlKMIkChmw3KWL0LqYepW5c9fu\/in1Jg+lbQSEHrU5BAcEi4cl4RHuifn+HAqcHxyfNIDKseH45Fw4LhJ\/SKoqJ2FHEAPZGoZ5aXPClJlBdg9AAyFpFusF9i8iDgS5jcSoVSB2z5OULI4kOHYgKHgC5QcmdnwnZHlnwRBJEgIFi65FfLrqI7Wp3d94h98tdmSr\/75S921Neq3EHQR9igKoEId5Yhl\/ErCIeGT40E4yRmokj+sv0DybyZqrXa2Vjpnv\/hIFPLmnVVWiogpSWmKok7WGDO\/GYsbJveRsa0OkjkULU9VYOydTAOyzO8aQwExVtQ1DNJQuN0lkKayQaax4beDtlojX07MW2GioEgAgiAYjm6Gk+OR65NbgVclrwavRn6N3Bo7VXL1JvTJrcL34fokfHJcEh4jfh77CDDPPRQF9Qhl79beBciBKFIdfzPZZwxlr8ZzwhjrwPYAlG1PgjKr9dL6fhhEwH4EF4eH1rKCbbEg4jTD8cmrVcbHH\/n5T\/cC8dp3XhtsblG\/TWGXwwAyTIYKiohIOAwB4ThetdoY9fxqFPZ7na4MB0wBqSjZfvHgPgmbxISlC7de+pb66K99oru+de+Ni8xMLIkVM6cNDAOrl8zv88x9LkCWyw62Eqt3lJEnCgxXygBNedsWq2gsXHPw8J5KjAc29FZS+SkuQxNTifIZcL8uFNncAhhD\/dRNOstCGYLgsOPC8Vl45LgQnnB9f7TRmJoYnR6v7hivjNZ8zxHMkZKDftDf6nTWmq2l9d5mU\/YDyIhlQHIAnSVBT4MVkWJStrp\/0XUAQxz\/EHc\/7RF3XoUkryLFBbRKSuolEA+p0GGKpRWn8Pme+TZqHSXkapRYT2dROBkJkoh3oPCrxz79aGPv7mf\/1x8EmxvUb9Ggg2hAMmQlwSq2KxAuOb7renP7Z7\/wc08\/ee7QeKPWGwRvX1789refv3blRjTocRQg3opJmhon7JJUtPiefOmPX3ry1555rtldv3KdVcRKklIAx7ct23rM1g6z13ieoG3fRgvewFQmJDlko5iaF0DWTjAgqRiGgyvOLwzAefG45kQ5EUzliEh7EMfMRRn\/kvQWhZFDDhElWOgWmqcDILtV+BV\/pDF1aM+JZx4d2TslgNZSa+XaUndtq93pEZGo+u5ofeLE7ImDU\/URT3Z6l1+6cvvli53ldTnocdSjqE9RQAihIo3bZ9KjYOS7ThaPaxs4Wt70quTCMg9pdOpzbuxsyc4vQ7iwlToX2MhkuwuAywHZnFUfeV8kLdYU+w0AujKEIOGyTkQro3sePvnYr37xJ7\/z7Obtu9xv06BN0QBpL1Q\/u+OSU\/XrI9\/4xid+\/uc\/6o\/WLlxeWJ5f2zE9fvr0XCSjv\/zzl37\/P3x\/0GlS1CMVJaMbQcIh4bLrkVulyogzMn7u558eGan87F\/\/edTeoqBN4YBUEA8P42mt\/ofBZeEf23Y4hg3ccyiPbQbzRR175g8ii4xiv7wwWLdmXNhu1nifHh6XA0kLR3AyiIqzIQHhsND5Z1Xnn9WJ8VOfeWTuiZO9ZnD3rRvL15a6qxtBuyuDkJSMP7sQJAQcx6tV6jtGdx7YNXtybteJ3ZsLa29\/+4W1KzfVoEdRj6IByTR70shhhmkokpttkE73mY2jjkvUtGAYrBonk2WsnRv5sIPKbL4AsaoNyusRolhwlPU581oveftZLkHJJKQksyGm+zGOT36jOrnzyW9+\/sbrt+68dZX7bRp0EA4gA46BZhzjSN2qW61\/\/Ruf\/C9+83NvvnPnX\/yr7\/3xHz737LPnf\/LT91559Uq1Xv\/qlx4leO+9d1tJmYBIrWQteaNidX5j\/9OnQdi8tUSsdFKaQZW3Mz8p9lQKTjKFqhi5yoqGN2LSe8GFZY3tdx+GYsRKt9A2L122gYc8idWOYsPwVV9nJmGUoCLpw3nkVcmrk9\/wRsePfPLRx37xU8KpXPzRuxf+8+tLF250l1fC1pYadCiObwOSA1IDivoc9dWg39tqbcwvz799c+HiXadWP\/KZh3ed3Ndc2go6AeXY2WwJGxlOmZagEUqKxmF2D7gP09P4u4PK7ryRMsxwx5ZKMYSNS0SujwQT5Zy2FJDr+5jiC8jzkpLGDDRAVDgkPPaq8Osnnnli4uDe1\/7kBdltY9ChsJfM4hWxnsULCI\/8xrETh\/+7\/\/Zv\/+yV6\/\/8X\/zFzet3w84WhW056K5tdM6\/uzA+1vjqzz325pu3V5a3iCMolRAMzTYIEUFGHIXy+KfO3nnzuhwEcSHBaggFpCRBp0LbhvMa+SAM2auUs7ZGjo1+n6xvG3X8nFy8vUWNpnapGdMH7AigxIYJyBZzVviZRaCI57euT16NvDoq9cmDex\/+hU9PnTr03vfPX\/j+G5vzi7LT4qBLod57aT88IBWSDKF0bzwkFVIUcBQOttrL7y8uXVqaOTZ37JmHo5CbS81MlNN2MODCVNKu3Ysq3hgieDvsNuRvpd6EOfc+k0MBzkzPBOVcWSysWYmXQBo587Wt6aOQWk8aPpEJPcJh4ZLrw697o+MP\/fynLz53afPGXdI7MBro\/gpYISb4CziuV63\/yi985tEH9v2Tf\/ad+Zt3VdClqE8q1Hl\/JMXicufLz5wZGW28\/PKlKOjHHRfTryh7+05nvbfn7KHqaH316l1SITT4ZjvDUxQ6yCjrUlqN8iFqK9uUU+CSBh74fqEwaaTDOv7y1qKMD7KWzPMAVNiupRWg\/alTgZJ0GYgkBrpV8uuiUt\/70OFHf\/nzzdXeq7\/\/3MbNe6rfpqCLqEdhHzKAPoVlRCnyXulet0b\/xhhgPZeKuv277853toJzP\/dEbcfY+q1VGaoYgGH0xGAia3NbMYs7xQmSoddicJpL5UcAaz2IXLM102mybZ45SU6GdVyQn3+QDVLY\/k4azjOZhJ8eTjgkPHIrs+dO9Ad8982rHPQp1LCYeP6umzF6dEtCjDS8D3\/oyKXbm7cX2nHfRLd2hEsEUtHC0tbPXr528tTe+kiVHI819UkTgolJKZaSZEgypChQQXT7tevHnz7l1WvkeAyXhcNmC9fQj8+x6ZFr2eeHE0bRvT0OjUtRZyWsE8A2zyi75kzbiUghdm4axmDaJgBiyHlfTIARrz3oRFSwHqabO7DSEJXGoScffOJXvnDl2ctvfusng40NHrQp6FDQ5aBP0YCjgOPtF5GKoEKSIamQdWCMQopCkgHJgMIegg4Pmqq7tfDahWf\/1X+aPjL3xK9\/sTIxQV6N3Co5XkxJTSb7mcGzPVllyzSUSxrdOR\/50vtpN3h0JDQYIyjMngHkfaqy2GUe5jaeHiXutgkcCeYkNC0CjamRZsdDz4W8mlMbfervfX7h4r2VS7f0UB5ZIsoEQHjwqvDrwquwcHoT08\/95PL87WV9VYXjCLcivKrwasKtMPP71++9+vrVhXurrCEXcYsl7pQmObSAcBhuvxvtf+LY+o3F7lqTVAQVQROdsngXQ4CQFTtlXtwlcWKbuqu87jZ2F9tDnZyep\/miJXZF+a9A5Rp2sQ1O6lNkyPkMrTNLR8xmTQLLW0r\/R+j6X8fAEVEbOfTU2bM\/95EX\/+DF+beuqH6bwi6FPcSYRN2JyWa8KbYJzNA3kRlIT3dFSiXTYxX2woWLd2cfOjT3+PGFt2+qMErnxsimPoy8TiDKsDEo3EkU7bYK8\/DcnNBOLDhvB4bMUaToDJF8zqJEbkmFhBzABGX2o8bH0QekcOE49cmR8V2TK5df5ZiDG7HUdHgVl\/KO59XGP\/LRh5947IjjO+xXTuwa\/fiTBxGP+JKNT0ISRYqZpVD0pc8+HEX8yqvvv\/yzt4KuFryIOMYcMikdDweDza07794+\/pmHV67OU+hmM9\/Y0TwmVMT\/QWmKkoNLZioEeeVMLiJ9DeKc2QrHMIUklOGz85MPtlWWDRRlcSZJ5SgBTp\/J1KTJwRjy740TXSYTCMqxBoIDR9f\/td2nDp35+kde\/P2fLV66xYMOwi5HA5IRpzIImRiFrbZkiHYnH1CxYkJKSSMm6q2pN771k0d+9dOn\/tbH3vvjH8kEHsyKIYiU4kzywk4GUGT92qzbTK+kIMyU19uPod9u\/gUyU7Ksj2fKkXNenxtmuC7veVveDqCCiHnpVEPHIhaChTu2e+fGcmvz3qrGoyFuK6fsJNf3\/V\/71c\/+2q98otOPWv0wYI6YQeQmh69KXi5ilkyC4ANgrvjOZz9\/7lvfmvm93\/lOqOvGRHuGWJGK9CuuXls49AtPe4160G9xUlMhCxHMuQCFgpLgkISch+GhUYJpNkAdTNvmi\/ZIkXJmS5ofzqVjkRJ4ky2Gy7nNiJz5dhHvyPH7RYolhpWQg0gwHBIeOxW4tfG56Ud+5Zk3v\/vW0sVbHHQo6rEMdemRnFYOIAyWGZVOCzhNTZAogOl7GvaYVW8N5\/\/wp5\/6R1\/sL61d\/dEr8R0nRYoZ0MJlifOoWYPz9nMf2xjaVi7NRopZlYZM\/De+vpweH0Z9jhwVkYfyo0vXRC4JMYa6lmkzF5FBDBBcOO7EvpmVG8tRr0dRGDMkWMWcAAhy\/Nl9s1\/9+lPff+7i737rhU5nQEqyDIgVwFqILSF+cTzYgwvhKuZqRfziL3z0S3\/ryR9+76X5mwNmBRkQkdah4aS4by61hO\/VxhvButD5c\/6exOW0gavOk1rLrg8KbIISbOcQqOd9mqEoSUNQLGUK0JyCS3HeTAgGZt3UJE3kHoBMnGiYtqcBeQf09oiL\/6o\/OnrmKx9eurR455WLqt+ioEsyJMWAww4ydCtbCqcx7Mue1lqZc2KTyyxJamtfp7288fq3X33o555cubGwce0WWGr5LyRg+8QPo+BYbMXIbfjcKBVczEGMXFNyvHS2nJ4Fw7sGpfpiZTR52\/a9zLEpBYBxPDMUjvCrO04fmj9\/lxTHsGzdj9HLAwKOv2NqanSk9txPL965cVuFeqNGWb6aU0GgpBUOB271hecvff0Lj+w+sPfOnUWSIUmw\/i1WmmPBSgXtfr8XVSZGNCBYB0PO6xzCEGW7b5ezTFHGmlVxqnlaPgCh7cmx+ECnZf6OlbnEFMRLTKn+Eqi6TaZh4xNwlhiAC8hEOD65\/oEPnRrbP\/vK\/\/u7Ub9DQZeiATFDuPCqnu\/5vh8TI8yQYCLsmTiFUtgsKBmFg06Ho0FcTMqQw\/6989enT8898PWPvfgv\/0xKqZMssMp06XMiGNYVs+RChsQjDNOPSl3qXDszMs+uUiOkITLsxorJ1LVzurnAsJSVhzm+A4BwK5Ude3de+ZvLWqApq6GZY6CKfpgGZktJQZuiAWe4FrbxxgZRw6mwWyFyKqCK7xOcFEbIMc6biRUpKXuD5dtrbqOWNFFhluIpNqmg8zjMIKEwDSpuDAzxmkgs2m1RCTKMQmwh96GDD84Dx0rKemvzlOSswDZTQuua53sYyDrMQsMSPXYqjcnxIx87e\/7bbwyaGp8UD5aE6+07sO8bX3vqkQdmhSvMfkWW3yQvo+KhLxvyViDg7krzT\/\/shTdfvRgOeqxFvcKBgrj0n17\/6D\/6wuzZ43dfeYfkgBAQZApkKxU+QAaZQWnvv1SYlE3vW8PE1M11ezJV77ynDSiP0OXymzrM\/I\/Tub\/p5cxljgQJPxGCSTiuI1wnbHfSvRcrC1ACp2ZmxVHcTVEsI4oGBriM2Ww7JW0AYpfgatRSxCSVsj6t1nhKnlNFYXtl06t5evVwwhKJi22jL1OOAStmiUNjJYbNcjJ7hBLrG1sFHcPbM5ahgXGco6Tey1Nu7M\/DwwbToG3lG8EWcCPegeTW4FePf+aR7lawcP4aB12SASmlZ79z+3b9k\/\/bz++cHH397VuDMCSBuAsKEkKkkqGat6wTGY17ZE2NARiYndv5P\/w\/fvmf\/k\/f+fEPX+ewyyokHhDQX9u8\/vzlB7\/29NK7NyLZJzkgFREJIs7A+Wnine18WzzZ2Kiw5HryMRR5GzZ2iyvFgvei+B0MZUxgm5iZo46U1DBMZQAOEARYQEaRKffCFqgcTJBErCLiJAtVkdG5TptLCU8KTCSYFThSKoqYlS4W80qPnMoEq37gVryEgGvqoCSaZyjRQ78Pkr30opXLCtrG4fcZ5g8ZNtr7uaShWk57MOy7851wHgLPsrujabmY6tVnMypNkfHJq1XGxmfPHnv7L96SYR8yYBkSKwgXTuWRR0\/s3T353\/2T7732+mUV9uNbzCrPyYyvqjJaYnpI5UKI6V3j\/6\/\/8e9+9atPvvzy5fZ6jzUOMRLsBHfeuLbvqeOTR\/asvL1FwmU4gIzn9nGoZUvJnbmk9WVtzTIv4zzLhEwF7oKVYVGvg82CeJhUtuEPPnRxsK0MmT9nU463eShzBg7i\/OeNJzNJnRBHyyRr1ZcvhdQaYBFmBaWS8pIsxq1Ja0msUiEEG3O5+PE2uqhMAZm3w9Uw55L\/fNM75xppao2WmStZiO4h4\/UC82bI9i2jiFOp3c2Q7V6g94ish4W0Y6lxFB4cf+rwnl4zWLw4zykIhhUTkXCnpyeDTnDr7noYhjzokUzIaCUJO1uOWtDprkeuv7aC1krr6L6dIw2\/vc4xkQ0RR4PB+ubd168d+fxjqxevc+gRAoaIpdkzW1wuVHq5LnSabxp+CuV9GVPjlN0Pphtk+fAg6xqA+QPirUpGnJyl9jBBH2zr5ylFYHY8P9a3p5JaJsZ9pys7\/ZPq3ueXpqYdKjP5Tf8pTrgghDc+Mlhfz5uApywiSyDYVv4jo6uBkvZjIeewm9qFh5T1XLNMkk1dyrJ+Kcrcp7mUPGPJfJdqn1kYEGxnu56RXjmxDIFujwmHHE+4ztGPP7h8ZUEOBokqQnrRhGLEbBeh73oyJdZ3QuWOpNxucXTlxU4FJJw4S2JiZqUACRmyDFcv3zv+zEPVyfHuoENCQCEz7ykknJSPdOXmFCizqi62ZtztkyfkPVGY0kIoTdiAXPmfKoRnfXsqUcsqv5tG00F\/LaWiUHq1KkEgy1rN\/qFililqCxZmjkvMX0uPbqMlgzywQ8BxRnaNr74TUsbf3QbYyeXSZshRa616q0zRncrlP0rTDJSqJhU2g\/H0dnWfqsqXUu8pr7BUIiem7HQsp\/RkHzXZCog5E36jPn5o5sJfX2SpQdiR0W2BZBroVaGB3QRK9LvKxh+28Yvm1jgVuL4kRIpitQT9iZVkFUGG7aX1sDUY37Ozt7TIcAyxaUNbtdTn2PIFst7GfQzsE99715paAakYnE31toVAORsIcWmsQ04pF3nqMaziBJmLreHtGddjrIJwc6lZHa+lba4UgcUZri7bg0wljl0mM9JsoRhhmTN6OqdIjpjQ6FT86ZnR99sdbWeRwUXt5EebFXAxXTER3VkWkebhJrXcxkHR9rh8A8iUoo45Nwoqs+6Gje2B7dllqsLlf0tXcQnaM\/6Bxo6BWBGUjTsG2y8Ua96lKD49nBBOY3rccZzOypYGgrLW4EoG+kLA95zZmbGqB4pCjsIYtEgpsz1m+hmqxtkcn4UQXqVSHx2peiRSBSMdLhQpyUqGnf6t19+fOLZ38e3LsSxD1vcuzdxhdWXMO27Rrnl7UHBSE+bs2JGLuBi6CGKwb+mMuLQ9aFpvZjVVkkMbPZ+ky0wsZRgtXrjVmBoFEQuHIECCIYy3KywonAl\/heFRnBz7gCgBQsOGCugJsm4bQFQaVQgEmy1dajJnhGnLgrFwx8pUagEbcW2hYPIAGhghC6WQlHjXC7I1SUy9Lsv2DJRaaVsuWVYSbFgisCl8ZFZxwiEI1vg+FbCKoDILznxqlKq227NEACwECbexc3R9sT1oD0hKyIg1KIpELAooMDs79k\/\/+6+wUnFfLlkhKiEPcKGbASLFFDExkSvgCjG2o3Hv7ga0a1AaxVlqaFRzfvXAZ86K7z4vB4ISjY8Ef8r2WKko3limh8BUUO0v2dJuacltkYQ5myrmNcdgBNGhvuDpoNw0qrVqwmLPXf887q9IuTm\/cvAzj4tKVQZdEi6JkJQgw59X2\/KilEeLYTMAmIWKMPzeMhC5EFq0a2x2MgzC3lZH85j0mMSargxJ5FEc3aAsQMOMweCc0Edi2wgzniSjMC4O8+z2qt1TT8fBKCBEYeW1mQRSgliM0bwOOVV4DXLr5HqQIYc9DttE\/cRYRVFZ+84uapFKKGi6TGNqcv36sgwGWigongMbKq\/Njd7v\/9n5VqutopClVEqlewgEbfnDSpl6+IDQPCUIF67nOOLnvny25juslKHjrFhju5Xsbvb8nSNOtSJbCSmIzTecmS1yPu+lcjFZ5JyebNXY5Ca4ZsBLdrQpZWR0MrkIEc5FAtq2W81lom0oI9QxKD2oJKmotbg2sWdidO\/U5tUOiT4jIBFBCS7pVOSw\/0XfYZNbFH\/ivIoroEWikhGWt+vI7Ma1RdkfGKjx3FgbOQSv5URoRiiy3FTJVhlM4QRsss7SJQArVCVHNec+CAouT6YvIWc2M2wGVi7B1Gu4lEihvInYT51qO2ePHTl47uDq\/NK1l9+T3UTDTqrYw5vzw5b0LTDyVTcc0di9Y\/PWRsqN0CLLiEFITMytZu+vfvDWwsISBT2SUTwMJGVhP5J0JG4Yihh0Ra4Pt+p6\/mOP7D9yYGeqp86s4mOJFbMMuwN2HKfia3EjS0DesHa5H0atxNBie+0Fl\/KofTMlNDD+hRtkA1VtawKUNuRzhvXYtl3CccNFKZJhf70VrG\/tOrF368YCuz5FA1IhW+ieROoYArE+lyBhVEkw25XgWEk2XsOKE9BFTCcj0vKyjkdetTYxOvvgvlf\/w4\/Srh0nEmvmteDM28vupyFtSQJW4zKnD5pX4MwCICyOCZVEORNGztlpANMvw0DsMpvgYgNeY6fx1jvRAz0PbpX90erUni\/\/48\/uf3Rua7Xzrf+LnH\/lTRY9VgFBgFXMk+AMgzzUhESfd45Tm6itXFyIUdrMpBKUL7OG8UpiFfZZkwlVGO8H7dsT3zgHgI5sTFrw0oFwWTgkA0QBixEP5MW5OVuyY8zESgUREzm+p1uABuw87yTA1kSqZLI6RF3O7j4mX7uFMrHUFOqDuLailD5eTMAKzhRlw2K9K2IeQ6QGwa0XLx546qHrz70XhT1yXJJOopIRX1QVgwaJNRSYUng\/yqD\/SCWcFURIrBEznPTQIRwWHrk+PH\/m9JxwsHVnJcY6JWTCFL9mlWDIua2yOfZkU3M1Y1fm8mcg04HPdSZz68FCP5FpA8R2kc\/2AA2GnBEMWIJ5YoqkeNTSr0jyAq\/mTe3aeerQ+IEpdpzxnY3dx2bnX3sbcIgEpyVnmg\/AaskkEtewIJgAeY4MoxzMMDPNZJJMHHdEFYTwfSccBErF40QSjnAd4fly0CcVEYdEgAuv4ktJUkmigGTgEIs4S7LdlY1IDSHSYgz2CJdNMDgXOqTZG+Y8gpfLQRiIWRSFQa8VvNhSwLH3vu29DBrK9TD9s5jz0qjWkAuZ+WyC2yQZcBjMv3xl7pPndp87dPeFNjs+OWHcFofQA5\/sjBIu+XWN8Ca2XTVT22idAQqXnAoJj5miKJVdA0Gw45LrkVsR1dr+J09eff5y2O3G42PmjEsXU0htRhJgS65ZAjycaV1nOWe6QNniN4usCQwUFICQx2RkmEOda5mWKMl3mG3gf+whnmDPGakLXeqCTC5BCM916g1vdrf3wMmo67R7fOPKxpmp+r3rm1devsGkKFHOZhYEPQc3MnAuymJliTIrJQchBGfYRoA4GUfFcTFx8oSYO3P46d\/4\/Pm\/fOny955XKiIIp1o7+rc\/u\/P0wbf\/1z\/v3LnLUpIQO44d\/vBvffX6y5cu\/PmPEywbUEY9SnT6hBKCFXMmso4URpXXrigBxsDMOlBKmjHMIVM4pZvfyfaIhe0AxfeD7peNIwtztSQ4wezWpMkcGzFE70MZUtQLW+3bP313\/8cfWHrzehiFpCSBSEl9g1udsDcITzy4\/403rgSOD72AEtgapyWNNfsSRMKt+A8+sF9Gamtli1kSU0yodzzyavBrBz500hvxb\/70HZYBRWGCr8+KKs6Ia7aLaGZ9nCqSIGcCFzfrs30lYl5P7IKa5NWU2FGR4cqQgBxgl0OprDglXJAESKQMR1pO8neGOU1lUx1AABCe6x7Y6+6YJHZVL5SdcPDiFal8Z2zs+SutGyMb61cWWqN1YtKiafH7Z4NTkpOpRm7kzUTMUrXX2l6jbpw7Ih5BA\/lUjnnm7JHpJ04cZ1z78ctB0CMIb2x0zxc\/XN+9c+R7L3Xu3CEATmXvU4\/u+NADTeGJv3hORpKsNc5sjYcEhBC+J5hkENnu9ZzXfyoOn4bJuJro0eE8TbckUbQiIyyDimFZJlDssBVMP3KTcZSxeWwEHCtSEohI9jns3P3p+T0PHtz\/8Qev\/+AN5ogCRaRR9vLO3aUf\/Oitr3ztyTNnDkbdgQASgGlmMqsXtpkDMQMV78iRmZ8+9969u7HiPUNoKCP8RmXnjgMfP\/3+j94O2u2Ux2guiDhwZ5QWqzfLWevXCmKwOkPxLISzyXU8v2btgZN6SMHR85JYFZdEOuuyRu6sOIVWJsrilCoMJCERxKmmeFxMphIdSvkTI5WpSarXpV+R8AcL63J5jZlj1l9lxDu4f\/2tO826krfvqrEa1aoUNAlgEoAy\/c1ysAzOIUqZNG63s7hemZ0m4Wnbc1JC9zXTZDabS8K58dL71ekXbv\/0najXJxUBCDY2r\/yHvxqdm968eI1kCFbE8uazr7sj7uL5a2oQkiOy+8BstVwSYbFKzaV+JPtBwZzwA1lWZYOLUuwOD\/tFdgn3IZwZtxcfCCi8XYTkwniMS0aLVrKilbMCivphq\/3+91976JufWbl4p3lLswpZq2j32s3\/z7\/8s9u3V86ePex7jtLpLFsjERBI5QZ6POj1\/v2\/e+f7f\/lSr9Miinn67NXgj4jG2Om\/9WR7oTn\/8hWOApIRyDAMzepCpLDUcpwgbMnN2Oc0yTYhMqVNJKWXcFi40CaK2mwIDoTruK7jOo7nOJ4HzxOu6zhxI4aZZMQcRSoKZRjJKJKhlDECUxJrp1RO42TsBB5\/QcQKDsRowx2tVevV2o7x7upm9+Zi1I9Y6bugI6oglqjVIxVF169HBzwSgq\/exr7d3FojOJkUaokON5gKuFVmUkxKtVe2Djz+gPB9GbiAw5l6iK3HAjCr9Ss3X\/ifr3M0YBmCFCvmQffeD18g4VDUJ46ImWTYmr\/91m\/fTfWADHeUrPJk4xZUx6qte+tRP0iyBrJnr7ah2tDteF\/RLqv3TJSQegGzpQoTD1jOG7bSL9s0O+fUWwpjsiIgW3CRXBWsFJEkGQF9Ft7Ku9dvPXv+3Dc\/9vK\/+t5gIyJWHPWgIlaD9lbwx\/\/Hf\/72H\/sQbpwLwUg\/OC4n7FOZWckoCJVeqSQgHHYr5DdQH9v7xMmdx3a\/\/G++L8NBInbIaeiwxe6RCH3YLZNMzjjF3+ig56S7DkKwcLWiFDkOCQ+OR44Lx3MrVb9Rq4816iPVkYnG6MzE9J6x6ZmR2ni1UnXJddlxGFCKJZECC8VCSkQy7EWdje7yYnPlzlZrcbPX7nbag26rHw5CFUlWilNou5LkkluvCp+cuV1yIOX7N1tLS1sXbrCWF4itGgw8NBTt3RFdukmqgvUWzU7Q6oY4ekQtTvJyD3FvxpoyG+tPwBTLZwYpbcPaXuuOTtWrY\/VOt81wUgBG2h9QiS+GBpoRh5QobsX5tgwTaRIQMakIUY+Vw+SQcKEPOEoZ+BwbaKY5P9yxg7s3L9\/mMCAlkekgmiWfKmt5GLgNHpalboOqZdcwoGcD24\/i2LeURAhbRMXaRsk4ikvZO2wP+XIUj7hboH9Psfb6DPtKuLeePT9xcNeDf\/ejb\/\/+c+EWETGHPaiQWClWgyhMmX5W8zi\/N9L0wcAiCEfLXaJS33ly38Nff\/SV332+tbSVRA9JpEAqzTsNgFsekk6ZtZBIIIgaT+CQcJCEOxIuOx45Hrs+hAfH9evV+mhjcm76yLm5uaO7KhN133MrDrGirXa0uti89v5me70bdMIwkGFEiqEYpCRxJAR5nlNtVEYmquPTjfHZnYfO7B+pO8w0YIp64cZK+9rFlfmLy51mPySWAmLvBHmeurUiV1fC87dUrx9LxOvLTsru5YEgMDlGwqX1DapPkO5mVSrq\/A3sneLVxTihTet9pu0TLWatJBIN1lthKEf2THdXNrSRK3MGqEi6yo7+JccV9fHJfrMZ9hQcl5jheF5j1KvVepttpi5USCA4TmNyYtALgiCedurZh1EOaQaOy47nNypz5w69+e9\/oEXZOFXF\/wAhzTAAzn2r1OQAtsAL3OwZhprplLtaso1CtrCr+fQsNzxOUEyWTEIqxyByHxP6SJYR0YBCETbFe3\/47Id+68uPfOPDb\/zxz8LN5JKajmiZISuXYThh9UjgaJ0vcivs11Fp7Hzo0IO\/+KHzf\/nu1r69PrmD184jbdUyw0I8mF1lGPADwZpEAwexLxdify\/hsdb2Fz65nnArwq9Xx0f3nJw9cm5u6uDOsYm6VFi\/u3XjvZXVxXZzrdNvdvvdftCPwkGkkSIEwfH03GHhQClK4JSZhqArfN+r1v3aaK0+Vh+brIzPju4\/M\/vgZ4+zpMgRSwu9yy8sLr93L2oyBy47DaqIpOOUyubGznAp3Jr2zfGV2wRBSkIOOAgxs5Nv3KCOS3t38a15wCFw0sFKwMYYQuYibYkVRb3e1tWFXadml995P66BY\/ZDPHX3QUI4RAyO9nz03Ln\/6hvLP337jX\/9R2G\/D4I3Nvbkf\/PN6aN7f\/bP\/mT5jXc4YoD2ffTJR\/\/BV2+9dPHN3\/5zpQNv3CURWU0eZ\/7+2L4Zr+53Flc5VpTmhMbBpQBMexRclO5BqeG2HX\/sxgxQwvSGZS7PaQvQPPw5exhiOj6bGTTSzJStVu9wKBmM+TvSfi+DJauIogFD9FbWXv2d\/\/zEb3z2oW9+\/MIf\/nSwSiwcigbIVC2UYXBdyp\/Vt8HR4ShR2m+I6siBD5868rkz1\/\/63YWlbu0fflTdXA3efZ86y6T96\/MCVmzODMi0VRTaztIjPS8GoA3ehK+\/L1x\/bHri4Jn9hx4+uPvgFLvO2u3N919fWrm5urGw1VrvRP1BLAEW05QVGwNu7YkpiAFmxfri6LpQvz8JEZHorid4ZSkB8kfq47snduybmjsxM3d08sgvHG5t7Lnz\/vrNt24tXFoM2j326wg6FA44GlBm9pgg0fbOEBwKiLwakaAwRC\/A3ARf7fONLZyeo9UGt1uAMmUV7WE9svIkXuWKOGIZ3f7ZhbN\/+2l\/pD4IuiS9JPvQlSj5qTS7cP3x8ZG9O9p7Z+DXKWIiiEq9tm\/G3zPl1CoaVEBAdefU6L5d1etLul9FMiJmoVvNSM9K7brn7n7wwNqVe0GznTAVlT0SYOSQopzukQRMU6aVlYtQyfOwOb9yjT2Lkkk9F1gAeTqEMWvPc4MN3DCXWFcOYQZwNpDJhhmKFIEiAnEEJrTvLb7wb7578pc+9fhvfubSX7y2euUuD7os3FiVNF06SYVtA6gSoSfhJbaSFfJq\/sTY0WfO7X740KX\/9Pq9129gcix45WK41GbHIa8G4lg\/KuwRh9lmtlBmIlVMgVNht8JuBW6VhEPMDAG3AscfmRydOzl77Ozc7pOzQmDp2sYr333v5qWlzcVNKVWsqqokkTJgXGz4RerEDI2ZyWNfedgbr9386ysr52+QDCnKgHXIoNqJBCDQ3xwMWu2Va0uXf3KpUnGm90\/OnZnbe2bvwV9\/vLvam39n+ebb91auLcpum4I2RX1SAamI4vAL2r2bLtwl4VC1TmHEoeLlNXKIOCKleGUTh\/fSO+8TyWQRl6tHpVsU+nBhSSpsza8Jl+YePXT9R1ssPOKIVMrf1EY8ioRLXuPuS9de+b9\/a+XifBgSOVUmGrQHr\/6zPxmZGV95+yoJQaiwcG4893bYGaxdu6MUkXCYpJSKM\/snfZZ55HiNXZMzDx9+\/be\/p6KQVARlSZsaSgt5kkRZDThM\/KUINctMQnPjDBtjYe+srOEILjO7hAHaKAMAWHhvKmGslsHnODbIY2JJMon9zL1V+c7vfO\/UF5988tc\/deW5S3deuthb3+QopCggOYhFmhWn6Jss2ArEAdDxyavArTr12tSh2VOff5hd5\/Xf+eHW7UWWirrN3r+9xWCanqFN5iiCcMivkFMhOSAZZvO32MdXo+Eccnz2qvBHUBmtjI7Xp3d0uzzo9hyoyd2Thx6YO\/2Rw2M762u3N1\/5T5evv3Gru9mR4YBN44REHQcx5IDt1A4kFDnezNm5fZ84SQBqtY2Lt6JekEhTJ4cuGydz0o\/laKCDdq+L+c31Oxduu98Zndg3dfojh089vvfMpw9ffWvx6vNXV67cGTRbenuQksTA4VnaOUP+OmSfKlUebPIg5FYLrkO+Q4OQlrZorEFTO3l5KW5sWN6NuSIjqUH0IEqGUbdz7cVLcx85efulq6EMSAUEqdOMrWZPVLzxiZGl5QozggHd\/OklZoZXQ5yeYOvW6tbNZVKAVwOIIKKQb75wgeRAeD45bq1RrY1W76w0e91AjzpICHJ8uJU9T5zobbWbt+7EKyfVaysQcM3vFznamZJzGeVweGMmP9InSrRWUKKcgIyRZ0gwG3R0ZHwSLozoc5NIDFPkywkSpXx5IqFIRcl7VlFHvfcXz9977\/bDv\/zJE584ffl7b919+0Zvo8nhgE2d\/FjiPqEjxnZrHrlVb2RkfN\/08Y+d2nVi9sL3zt944b2g3aKoR1LGCYyKKGjh4DG6c4+7TfS3SIXxIIEccnxyHFJhrMynZaRdH16dahPVyZnP\/OZHTj6679p7a6\/\/5PaRh3Yce2xP2AwvvXjz8ks3mivNqN8jGZEMOYVfKZmrnlOBd6NJJiDAzCqKIsUKkDIiFcTmRMm5UxDlFUQSpr4zgeGwikKlVq50fvL+3Vf+tDFzbOaBL5z++D\/6aGuhdfH7F+9eXu02Oxz0SBDN7OR35jGzj+\/dpUGPZMBRSAoUhjTSoCAgYr63RkfmaH2DIxUnxcZIIMOWGzxILXbOMqBwcPeFS3OPHjvw9KlrP3iNZZgYlPJ7F24NwvAf\/tYz3\/6zV7rdkGNNkkSjmSiBZ5juLrqQV8wSAn7Fe\/TxI\/uO7P5X\/+w77c0NVkzC0TtwdPfUgSeOv\/m7P1D9buoyRMxD9xFZOMAyKUNLg8YEByLfmdT7YPwR2Oj+UsBNJkxgz\/FB4EKSatEh8lP4nEpCQbISBX9w5HSBkmJauOz45Prwat7I+OwDR8995XFnvHHjrdu3nn+3u9oM2l2WUUrDZ6WIFCDIcZxKtTZe333m8NGPnayM+LdeuHzt+Qut5XUO+xT2SQaxvrBSTIrgUK2OwyfoxlXqteFVKRzoJZIMmgDhEByGQ24FlVHUxqi2Y8+Dh\/\/2\/\/VT9bonmDtrvdX5rVf\/6srdC3e66xuxmUkGAFAWqMW6+ZyxK5NNSI5LXq0yNXng8496MxPz33m5dXU+qQlVTC1mzoAB6dlpjr90USQE4MaAdeFAeG6ttmP\/zKnPnDz91IF+K\/zZ96\/dfO1GdGKXakZ8ZYHGRmn5LnW3KOzj9H6Gh06Xui2+c4fkgFjS9DhB4O4CqZA5QlLX5TOjdFgqXBIuHJ+9Oipjex4\/\/cA3nvrZ\/\/PbneV1kn1SIQGe6z75xPF\/\/I++tGvXRKin+IYxCzLpErYQMRxfAgFygUE\/+P0\/fuHb3\/rrTqtNAAmP\/IZTbTz5m58Nu\/3X\/7e\/UIMmhZ3YUtu6HWz0F9ho+5ENHMyhc43OvFEVmk6jyZUYfyQXgIbq1iJn6wyrRkSZH302sMhttrIK0eQWsNGYzhkYJp3GtPoixyenIry6PzY2sW\/3zpNzM2cOjE00Olud9uLm5nK7t9WT3QCC\/NFaY+dIdXq0MT3mONi8trB64c7qtYXO2iYHPY4GsRlTgjKJBx1aa7Tq0cEjPH+H+iEclxyXoh6H3Rit6nqAS47HXh3+CNV3OAeO7nv0+Fe+ecSrOL324PnfffP9V+d7W23ut5J9HiWkgUzKMdfSR4IssycugIhJHsJxAKggYFZIxvEG0zFBwBHp0ksrABqXOzGmjhP12JsaTsWt1nYe2H3yY0cPf\/LQ+mZw+Wbn\/WdXIwhqr\/L1qxR2qNehBw8ReVhtwg3VtWsUtElF5Ao6fgg37nJ7i2QAknZ7zERoCc16iUc1bpX8MacxcexrT47N7nzzt58NO00K2xQNSCnHcaanJ3ftnnJdx1jNyPDu2ewhlbnkNAmXMtpcX1+4txoO+syS4JJXQ3X0yKce3v+hwy\/9i293lxYobFPUh+7AKZUBJ80xPbMtGzHE1jfde7lbag79YUTCsr4qhpEkjP6oOSFETkLC2oFFN9k8tQUozEgyYGQmZhFnHZxaWMXtTSfdinA8cjx\/bHTHvunG7NTY7kl\/tAbfESAmKKnCdn9rYa1zd621tNFd3WTdTtRBKd5+CTYtnhQlYEiWqDdo7iCtN7m5SSy1LTM7euLnw62RVyHHJ7828tDps584ffLo6PJKp9vu33ln\/tpPr0aDAUcBhYPEzUvfaUUJ+tyG2ZpgNGVIV9liflkMYFs+BMlAUhAJYhl3lWJzxRxtKdFTMS+pcOHXndrIrhMzpz976vC5uXvXuq+f31p646raWCW\/SpurdHyG4OLmstg\/Kl9+hfpNkhGBaHwEu3fxpasUDcCx51EC02eDOyKy++i47FTIb1BlvD499cRvPrN2ffXiX74pW+sctCgKEklscw7HtnhPTmEw+VoI0hQnGVJ61rg+3MqOowc\/9I+++Ma\/\/\/7Cq+8YOzA5HMlosJvnSImfNuUDYwlQgSzAjTHfcguspfIQWCDsm2pFMObeZSLdQxWdLNZvYdOzrThsIHHMo17FhQFUxHLAwiPhDYLu4voqv+sBDohIKWtqk6Ir40aINpeUpDRDQqWykrpy0ZwLhqB+n+7dEQcOMJRqtUlFLCNAUHWcq6NUn0B9vD4xcuDJ\/cc\/fsRd7Tz\/p+9cf3cl6ra516Sgw9rKJs0\/lUTsT6oMPRhr0hhX3rGeKhvycHl0tCVCoXeg49dmZ8ZPHWzdWurcvMuswDLpdFmksdQ\/jZkBxUrGOb9SkYwW3u4uX1m89ejhRz5\/4pkvz7xd7199w+tuME+P4eAUrW2xWhezO5TvceDFiN9mlw4I7JzglXVWAMWUF5TNrPUVgFIkJFTEKuxu9d\/60\/NP\/oOPdlr9W3\/9hoxCckBqEBfPmrzPdkhhNpsZbLAQACdOffVg3HHJqZBXGT+w+9yvfOzyd15ceuNS3GmLIX4WLNGccVqNyqGEwRwmjAtEihxGmhxUZws7DqUIyNxDUsWlHEAyXxDaNPB8NQiUwOFSDBjKZNQNhivSa60Sq7rYwiUkGSEKWAas\/y0HLAccDVgGFA30H2gHSRlBx8PY7y4VCGcLCSMEEbFkarcxM03skGRdkZJXJ6\/ieM6eI9Of+XtPHjw9c\/H7l1\/4\/ZeXri3IXou1uWzYRxTEhrJKIhZH4iTepqDLeAQPt1LbMemNjUUhJe6zMp8CUdKz0Y58SaUHIUh43vj4yd\/6yu4vPDZyct\/GW7dku5OBuVMOdqaTypmMMWUuYhqTqoJg497W+6\/d67b6Zz9\/9NiTc63lXmcjEE\/uF1VXvb8sdvq8ukmDkFJniM0m7d+DzQ4phQzPb7rBm5E4MSoUArWGOHZ6QNW1d2+f+NoZEk7rziYrECloMqEJTE\/+DdaHqeIErY5EEDHryGvmmleHX588su+xv\/\/MvdeuXPv+Sypok4wbAUiVFNnQmObkImNIkZaLivfHUVu7pGwT2icyk+25buiymDJheXyT3YnhPEsTBbJ1CV4HVOJVk6Kms9Eo238SOGKyG0PL0zw2NI9IhVBRHANzVXhiV2t3lZIpvGLu9py9s\/AbzBUa34Pqjvrkjkf+1qNP\/uLjC1fWn\/v3r956\/WbY2aKgQ4MOhf3YV1jbets9GDDbfHpAuOTVZh458eg\/\/vLBT59pLXS6SxssA2IJUihggLJWW2o4D0HCq+zYufeLT6BeU4rWX74abm6RChG7i5u6G0ylMiBxOqKSzDmSQbR2c2P+rcWxqcZjXzlcmahszo5Gh3bytQ2+vUgVlzpB1oiWTJ5HO3fQZjOZc6qSszg+dWNQEYhodj++8DXya\/3X3ly\/MH\/sc2cmDsxsXF9WobSI8LlEjwtKNvGlcDiVKdE2stWR2SdOnvu1T80\/+9bVv\/qZHLS16z3inIgzwBonrqPD4Nd24bCt7noua7V+w0F1D+XtVQGjRIHlDJMtFc5pHKKw1QoOtChX84NtjWLRb3OWpfaPshojR0FESt6JNbbTE05HywSGQtkAwEblwSaQJSWo8GIDExIUCeeBB2himgaYPnv007\/xod2Hp174gzff\/v6Fzsq6jn6kOz2cwqCSd5VEMKMXkx5pgtyKqI6c\/vmnph455E423Kq\/+PJVDnuxzCab79byvMsiDQSEKyNE7dA\/OLP0w7e2zl\/jQBc8yjjjYVb3qXMgTIh+0joCK+aIZTho9e++t7i12D3z8UNzs6OrP7nbnW\/jxC6xc5zvbMQ2BHoRd3s0O02dPoWDNOBT5gbtUEzGF6neD1yfKmO0uEA3rvHWetBsrV++O3ty7tAzZ5pLzUGzn0gtam0nkRm2xjTLVNxEdwpcjSsi1ye\/Br\/ujU2c\/tqH93\/4xOU\/+cntn7wu+\/EOJBnpHRiDAljZJx2X6yaVILFwv92Yl6xAugktzQ+yNLGM0xIZ3bjMXtlsmrJpUgLThBsWSyMLacXAyHlvQyqxduIcXzT9ePr01buOEp6rUhkdyQC1wVI8SzQYUs18ZAor8ZTfr1N1grla++ITJ546+PDnjm4ud5\/\/o8v33r4VdZo0aFPYpShINGlUYvETV3hW282cSWgojOejOrLr1L7pk7tDxQvPX157+waHKRSGswMxxsfp5WhIJ2gpQUJvo1M9smvtr98I1jdIBuCQlAags3XcALAEXbPmR0b51VdPSVKRCoKNO827F9amxvyTj80MJG89NCcOT\/OVVWp34\/6WhhNFIXbtpPVNJDx9rYIRt7g5k3shUvAbNLaLmuu0fIeba6QGJIOg3Vm9chcQx7\/06Pj+Xe3VbtiNCC7BJeHGAt56bBvD4j1y9L99cqvkVsmrwq16I6NzTz344C9+jMPwvT98duXdqyroUNQjGUCXgrpJluYmVj0HyotmcSFvyMVLDM9EOaN2MZXUhACKpWBSahhe85nwV0bTAhXUY0rso5FLaFGyA8t4GkactuaZqWoSTIvhrK5LJ8LItlyOspkdMOm74iwGkqY4EVyCQ65PboVqY6hNNqZ2ffgzx08+Mnv5zdVX\/\/RCZ32Lu1sUTyAMZ1kignAqvj8+KoMUEp0lPEZCAIJDbsUfa+x5+FB\/vXfruQs3v\/eabG9RlIwuU+UWISBcOD6cKgsnq\/Qy1zYhPH\/H40fb790ON7f0aos1ArMcOIkhwsl2YCqFlDquZb0KPQjRAoGd+bfvuUKc+8xRty3XyIuaRI5HnS7JKBYE6AdUr5Jfo36od1qS7cd0qjSXQW2CxqZ5a5mCLjgiDqBCcAQVykGwcXN5\/erSzOm5\/R9\/qDG7U0YIe0qRq5vhyZ8k6LlV8qrkVeHX4NcbM1N7Hj916qtPTR2dvfmjt65892e9lSUKu7oOhIzi4sWQHciEQmxPRnwQlm958YehD9LdUUs\/gMnSfk2HdUPsY7PCAvc1xkTp17z9Hky7vYDd8LE68mndbDYbwGXN2qHS5LHILjMZp0qaiArN9yWvzn5DVEemD8184ptPjEz6f\/3b5+fnW6rZpPYm9VoU9iiKs764HS8EhDvzmUf2fPzchX\/+ne69hbhtoFR+iqTVJQSd+vTJRtV54X\/5YdDZ4oEelIVIGnexPo1wRa2x48kH6\/v2Lv\/4jf7de6x0tNSdKiYpSUWJ9FKmeYFY2SFRc0uw5klPRSJG7aSUmIRlw0xQsfm7kizDQTR4609ead5tffzXn5jyKy8FvKWqfGg\/3ZK0FWtp0717dGA\/bayRJIJIMNkwlftR30mNKbV+i1RIBGaQ0pc9IuGQiFhFWzeD1\/6\/i2P7Z488c+6R3\/hUe7G1dmlh6+5yd7UZdnoqGKgwIlZwHOF7TrVSHauPzU1NHJ6ZODwju\/35n1y498p7UbfDsh9jAGSIBGOMWEGPCxxCMpm2Bcu7gsBMDh\/KlkQ+5zPVeNLpkq1ia0mEGZBR5HuvbDqLcFG5EKWelAU7iu10A3LitIZYZi4n5UT9PFUxilVUgILoaPID4zOyua2THc+Jc5AWGoNLwiOvSpURMTa179TeZ37jY+1m\/9v\/\/Kcb97Y46MT5ZzigKDRpBwQBInJcb2aHNzvpVNy0l2S0WDjRjCEC4Lj7D03fvXgvaG1w2KOorwkcsUhm\/D4FCa+2b3bf3\/uiP9YIW917dxfIlFfQwnOaEZjI6RrLKdW8S6REHZ+EIClJhky6T0iGnwcSmnSiDhTv6kiyuvbCpeZK+9P\/5Yc+\/bcPPvc7S2s3ujR9gKprvHiTBkwh08oW7dzNK3dJ6fxWskbb6u7C2DS7dVq9ThzFp2iqtsiKlSQhSUhSoVLe1vXBm\/\/7oj86Oj63a\/9HT+39yFFV8QiIAtnf6pJUXr3ij\/quCwqjcKN779Ur1\/\/q5d7aphz0WPb1cRYTYqTkrBeqkoWQkII5Fo9Eua6EvSbLGkSmCF4phj3dzC7lmTkfSLmCU67RfeItiolurpYsmGYPAe2YPQiLAVwwO+CM68+xXU0GF+ZMNjBh\/iL+JRPklRBKRUa9dStUGUFt7PRHTn3ilx6\/9vbCT37\/xe5mm6MAUR9RSEpyLOWirfOysSQptXn+xvix3d2ltTSvSyRobbECAIBP5CoJFbAcUBSQipB5mKZqdYgCFXUGolLpLawli4nS7i6xVlEj1m2V7OSK1R5iQXuv4o6M7f7MY+5offGv3+ovrMSWElnanApDxcK6CaVBT2iJWS1fiv7yf2o+\/fc\/9Mz\/+cM\/\/Z3X7r5zj9mnuYO0cJt6A+oT7ZikRpe6TYYDzZAiRQAm93DQ57WbSIfjEJlWpWLo4Kkki4hUyHJAUX8waC9vrCy\/d9Wt+l6j5o2OePUKhH6fHAVR2OpGrU7Q7XM0IBVxjMQIKZnFa9h3Uh6nKvGco0qgdPJesLAuMbROTUVNSlSpYyqTa\/hfDeUN58HYzJZp8zDEKYqcYBRJH+UiQNsN982\/sp0YZEwOWBh3HRVNq9UMLps6oaRGJUwxLTDW4XY8cmvkN0R17NyXHvnQVx99+9lrr373rf7GBoc9kiGzJKl594lcKqXqfSo+0YMQEWsHt3Q4wWZSnaowKWVo4EpSEjGKgC3\/CaUGS+t3\/uynYw+fbL77fuaOqksvDVJRUkllYT6YM\/UvEIRDbmXP55\/Y89WnyXPcqbHr\/\/q7Sg30XB2miFumV6GIwCzAksDEgX6h1p3gJ\/\/muUe\/8fjnfutDL\/7h+Ut\/c0Gt9Wh8F006tNGkZod2ztIgIgSsGdgCNLmPO1vc3aC0WxYHQKnjbpwAEwhaaiiCcAhhrMYPJxyIsCloUWjly2y3pCTMjJ0cJWeZzBSukv6cecDHdSCTiRS1wWCcV3Rluk8ByGV7N3lKt+yXUHSDNP0q8xyJYn5YLPJyfHarUcT3k44qQhBgiyOa+5cNUlUqUl14fP6NmrYwqdygA93j9mrwR9zG+Lmf+9C5L5976S8vvvPDd8PWFgddigZQUYossws8w+RNa72DYjUnsvR2E0Z0Ou1LlcU4UWhPnEzjzy70LJSjKFxvR82OCgYp3DFpo8RjGCVjVoXlmpoBqACIyq5J4XsKVBlvQDgZCZDZXGW6RZNIV+iVqrQzC5RkGXWX5Sv\/8WXZDz\/xzXNKhVd+\/K7q9Kju0\/Ruam2QX6XJXdTcJAgSoNEd3NqgfjPrJ7FWOkxp3OYJriBBkKSEJbJeAvpPQ7ayJeeS8b1mXaTzTE75EGyLrHH5INK6yVwmf1bwqEhtJ7g8uLgFMIwNIssmeDAaqwUqbq53wlyAxaSCAiLWjmKCBbQzS0sMxc7BLm3TRQJr1DhUkZ\/zXVz7h7EAIWJ713gHUmXUG508+4VHH\/vy2ef\/6PyF5y7KXhtBV8Pt0y6fPfbUUnqpbq8QQrgOjB4WJ1pymbhrahEoHAAo1voJK0CBBCcTUaGUGUiNtaKI2YFhdM2x2xxnVkPMrJaee2\/s3KHBZm\/px++qKEigJwwTi1hETWSxQjELXXkO1vnVP3o16gVP\/\/KjzOr956\/JwYCCNtUb1GuJub1esDNa25BRxBv3KOqTEJRm2lkYVOX3kaGTWGM55d4VDAC3MnaalQ7YaqullpG87Wxv+yIM+aDH+XWck0l0C+ovtlQfGUOlXDLIZNP6KS9VasG1oYMA66lO2qzTPQxD9hem4JupQ2xHTkPHO+MYZ2R+lCbhGd43dcFlczRPif9ePOf12KuiMuqN7jj7uYef\/Oojf\/P7b1z66SXZb1PY40jPl5LaKVMW1f5OImESxysGghxwku1k4GBDzYNN4SikoECYDZL0E+uhlooxNDlWtXGoOMI4GWNZbhVPHxJ4Wn+thebmnf\/w4tbVOywDUiGnOZuZpwhBcPS4XOfDGfmVOVbdZhVuytf\/9BUBevqbj8P1r\/7kquwp2mpR3a195vGxUwe6L1zY+oPvaMtPIplA5BOtUiktNDGz4TjASW4oC1ZCKCigMWdgDFNNI9t+hhWAIQdczj82Wyecm46bJxWzOQM0NF5sU57UAJkQ22VnyEWrV8KmJVR50svWuxo2DEnldCpwfBYuMUOGLAOiAAzWQCpwoUNU4v1mHVqWXSxsiiKbyuG2LWeKxrPtjzOGm0NCM2tGRGPyzBcfe\/Ir537wO69de\/Wq6rco6CEapC3QpD\/FMQvBcUm4iDt7EWnMdHaLZCLozgZwNDngk+ZKxjfS6z1DM5orM2EMZqq+hq1NiizNCGiUSjYleB29gRVAVZcpHLAaQCYcf21+nIzyWTjkVMitkHCJFaKQKSSpMmcOJoYCK1YqUuqVP3q5348++UvnPNC7P3qXlYIkZ2REVqrRWM3GP+qVJiy7RDaUpDPyARUcbVN8qGUSCZvZZJGPkiZzMhLkzL0j0XBFjl+Q7BmUpKm5\/JTKWBS8bcMTbl56wiRMwCrgeOgkPbMczDcsYYzanAr5DXdyurFvb\/fOYrCxRmGHIk6Y8ipnHFaYYHC+lOMiYM9Q9ipLGkqZ\/JyypUDQ+mWOp7NQUZs487nHHvny2We\/dV7vQA57JLUhpmKyAwUQA693T0+dPbJ1Y6l59RaH\/QTLn2juZagXXaGpON6xYIM\/ktX7ccc8EQtLm8oZXNSE5JsohRzKlGHgjJkUSMSiwMSe3swq4nj7KUslEoDjkd9ozO3Zde7A2qV7zWt3adAmFSUlVWLFCqUzXtmhd777Rq3mfujnz3Rb\/esvXOEdOzo\/erd\/\/kp05x45IKUSAI1K3rwwFjobDUNkrjVZ5WOFnxRGnK16LhN6YcOlkYzrzZkytzX2ztPthmJCh4Db2JgZsrWjDIK8W2qnXKa8VA5HNUZsXACXpdFVa1p53o6p0\/+nX546d7J56cb5f\/p7g+WIWDIxFBuElBIRjvt1bsoaqTwEAWDaASdIoLgpGg\/lXThV8kcxuuvYZx997OvnXv\/+5Ys\/uSj7TT0MhIy0fm7m7aN7rwAJx6tXTv3qM1OPHG0vrL31P3+7M3+HVBQLH2YTgpTIm4wBWBguLoniZhzyshqSc4esfk1XpK6biUId0u4fG4e9OdPNtCFYxvyMGFWbNQ85ww855FZqM9MP\/8PPTT00tzW\/\/ur\/+KetG92kgaA4bYCxluJiJpJtev1PX3Mblad+9bHNTrC+Hsrzl6QvqKJo1xSFfeozsWLFoIiUJLdCYZdNQFa6Yzh3qLKBKimqTLCJac8BNZCPXGxuWhT303aGZ8UAVZCSsVZ0bmIYfwKR34GZs08+xb5PAxPlI8sknwcgqtM7xh87JcYbk2eOVvfvh1slRwtmp6YLogj7LtKdSngXsKHexr2xHBfzKO2EFwuQQOwD41bIr9PE9NRXPvrErz556eU7b3\/\/gsrIEAnjiVLWjEpsLxQp5Y3UqvunSYjGron67p0GXsCM2CkFyRS3zOxrFEFlujhp1mo2Ejhjo4jUp8T+KUyIX2YiaoLmUmF8pUcjGdEpXZ1ggByXXL+xZ2rs+CwJUds9UdsxapwpacNDxU4YKiIZcNgLms3X\/\/i1pasrj\/0XH66HbZIhDUJyx2lxHQePUG2EhZvo\/CoSDkjZAH429lJ6sexdxOb3bdp7ZkRlJ\/NGjZ1p7m43HKMSAdvth+Sm63OhE5\/7nxhS58Vng0XmZrKcGLaPyznSMTMr1VtcW37hne7qVrcb4IEH6cBJTO6h6ihrOWptipJ6A5h7q+jGS8WXI1vqo\/gD08QT1uBEpA6YPvl12jXbePqRT3z51Or5hde+82bU3qKgRzKI+3isEo10zUtisIKSUBE4Gqw3V9++0dpodTba3eWtGNKq+xhKWRx562IysRknU4S3ShNLJPBXGNCqxLs9045h2wMI+aKCs0WbXRNWirPhpN3XAwQ5HtyKGG90u\/1Os7d5ZaF9b504YpU4gnC6A1UG+OaQo353ZePlf\/ditd175GvnKhNjxIJCJuXz0pKYnSInkVyByzK6H4w4vYOcNVpiV4KMn2ki47UzkNGJYSMdJUMUn2D2b\/Ib0LZ9pG2WH7Z96yUCaJT3JyzJA5HhJADKSwrkuUkWQyONR\/rSqDDcWr\/yr\/7A3znlnzjsHt5X3TM9aG7Q3hO0sUzddR60EAacRolk4GUk6kUdZ9wPQ1uYEGaTjsSWLOYiuOT45Dew94B3\/ORTZ2Yrzd73\/+OrvdVVGnS0zAnSEzfLEg1WkGKSUdTvX\/vd781\/9\/mdnzg79tDe7vydxKOCldErT4HUhn53to2U6S5odca5AFrgeIJoofitQ6ggyG7Gw\/gXY4tVW+pPZwksXLiV8WN7d3\/y+MV\/++POnZVgozNYXdcsWE7FqjMZ6OwqkQo56LbmF1\/8N89+8b\/\/Un\/l4Tf+\/LxUTCOjtHqdUcWuab4zH1MiODJOXrbWH2y\/QzaXLGeQYDDnuuicFdG2n12KkhoumrYNhfA+q46HfpPzVH3SBh15EUIMM7KwFjcXAgyXokH1GcMSKkTUk8213vytrZ+9tvnSe07YHz9xiLY2OYrIrWHHfuw6iOoIHJ91Kzz2iNU8MUCIAiej1L6qmK+CrRlhloVyMhJkx+dKA6dPY3L3Q4cmjj+854f\/7uXmvSUKOhT2kWWhqbl7wioghlZnUFJbKUatrd783fbr7+46NUOObn0pUpHK4GyANUjl3FkWMalsVpbPezijXIAsChJMOzbr4AUVzbb1b3E+dU9Z7qk\/qXBrlWO\/9OTy31xeeulS6\/r8YPkeBU2SgS4mmVN6FMPS5tKInwGH7dUrd179P15\/+Iunjz51CEGPpneQYF5rEjyx\/zC5DXLrMZYV4Bi0ZBCsmAs323DmKWjZIsUdkulvGPerOOvbcAntCMXhMbYfHg4PfSVPDsOEXH84N68FBbtPirLuzLboMhigSaTYm9j4uh8rDkWhWrzT9fyGzzsePLTx1gUZBNRvEZiqdYzuJNej9QUOeqBcOWQPUHh4+IdZqaYrE0azQZAQsTeL49PoBI4d483gwCH3Q58+8eJ3Ly2\/f4+DXgyetuo3A9duQM\/ATCqiiMlRWtrcFRyXjopZzyrSRa7\/CGRqqMmih+b5sCHXa\/A8s8lFrJJGBqs1BglwRi1PJpdCQ0xSB09KrIigIZpR9nKJ+FIc3UAAXKEEmjeXORpQjCmPjPQ16e8TQKnJkUhkmiUp4rB\/+W\/e23lwx6N\/5+zipdvN9XUe2UntJi+3ad805vby4mp8UzhzOMy6vuaozTiz2CSqszWlMJWyQGbKYoW+wmwQ9lwhpw2Tw4cNCXtZ7C0d3yNT1WUiZDUh212DIh7u\/8+ozAb\/NjO+DiEDivoUdvjuTa6I9uL64Pb8wQcO1BpVcjxm4k5LbSxQe41qI2LnHkzPoT5GRlSMie0kLB6qGVpgTxORquLF+isMsKYmQYOzK7Rnrzh6lBY7dXI\/\/POP37i0\/Pb331bdFqIASqVOriTAQmjjrgRfr2B2TVLIIoHhKlaxnLMOKHq3C4eFyEw\/tbRZbACabEVTM1tow1CRiqCRcOOrQQJCK8roeB4\/BvprTYVkirnI8UdIOsDCSTF0uiTkWJtcsL4s+oV0rc4cRpLDLskBcYRkjIG4g8lxHhg7C\/hwPOh5qe4aEZMMZKf1+h+\/vLHafuzXPuy6Po3PklslErywhqlxzO5IWgOIEba5pVaGJkMW91I2YDGYcS44IcsR7qsMw1TCXLfj59BmZbETA0u7LNnq7v1JtNtF3lJ0WIqWhn2uKFJEHJFIfCqX7mJqd\/vmbSfozh07uHj9Vns9iBP5bjt+vcYY6qOojXAYIuhyv5vkwpzTJLdBqhRDNK3ujqGDoP0AhSCvgn37aWRc3dpwvfqH\/84Tbr32wp+9HLWbGSyGANerTO0YO7yne2+lc+MORz0tv2L4mybGznEkcZggmVlFcWPDqTiuS14VostwSDDFqJokPxGCnFiUQTguHIcgyHFI+UREcDLqZbaNyXEc39M6yA6xm5hewPA\/1C40sfFoDHTW2EuROrQJIUCuT45HMiATWa77VUCkFMtAgwpYCLAgVpZGZsJI9sYnxk8ekEHUvHhD9frx\/ENFHPW6K+tvfuu1T\/43nzn2yVOXLvVUc4M6axQE6uZd58GDamONmyFBMNLZKVtjNR4CZiQeXpOkLq4F+Jn17HbyaNEJuaQ0w7AysCxL3H66SOzS0L4rhlCEkbeNycndY4gPXYLYgEqcylcWqVrjkerm8mYwUPuO7V++rjaXVtm8au1NxeuAoPo4du5F0OdBj\/ttCgcEZc4pkQPK2dLgnCWBAlryWbhUb4hDB3kg1fVFVEb2PDR37OkjP\/69NzfvrWnyC7RQpxDOaGP\/3\/tb40f2Dda3rv7LP+jdvJ4wXVK5wMRqN9bPdZkoYCLHZccht8p+TRI4nYKwy4rMFQTtkOH55FXh6i984ohApLwEoENMqZ+2S15F+L5X8cjzKfKTcydZCsIjv6YcQcQkBLl+fsQNxPJHlYbnOcKrkFchjijm6af7yiPHIyHgeuRWkhmsYC3eoUDat0Vn146z61OP7\/vaJyIpb\/z2t9dfeJNJo88VlOLIWb5w5\/JfvvvE1x9c\/Oc\/Wx8dp+46AdQN1cXbzpH98mKPuxFlWJgMHlwCIGMuupxYiH6+7+ChlGNORcguhvDu7tOV4fs+CkzsAjn0P+ebH3niHpfoapcQKPJ+vUaebRBMVxbo4HFqNrubGzfPNw+ePug4WFtYSc3xErAxUbcpuy24HlUa2H2U2uvUb3PQg4ziNgnn619kXZksBsbpnHDFxAQdPapur3CnD69W2zH2xDc\/fOHVO++\/eo3DXqyPFg+vyWnUnJmdSggaH\/N27ujfuqVpbwSHiQHBjqPDHQmXHAeOR46QEPBrJCrwKuR6gWSA4Prxr2aoxSR9Fw78OjwvAKTnwa8KUux6iIGaMp5aCwE47DjwqvAryhHwfKHqMfhGTwuIyK2iNsJCMEsSgF8lruZF3V2fKg1URpQQ7Aj4FYBZhmkBysIj14dXFcKBX0N1jMIBOwMOA6DPMiQkCmW64HSEf2C3ch3HE9U906QkkfbTBQmH5EAF3cs\/vnTsQwfOfOnYz\/5IhusLMXFpq6s4EPv3yms3SSvdCzZQ69b0rIBhifkdZOpuF2l2bIUEbL+PkG31JJaW7PkyLhMKgtzI47ntb7jM21EOSwaAQB6kZvZjMIyDiHzcZEUK1O3Swl2qN2hrLVDOjfMX9x0\/MDoxdvvydRUxS8vSDaQ4lAgH3N0kv0Z+DY1x+FW1uUyDDilJRRG3NCFJzEC17YHYN4vGmLx0naVAdcRpjJ\/58hP1Rv2tv3o+6jZJFz8qpNhZGuHq5soPXxz\/8NlwoPp9JrdGHFFcLeoBoye8Sn3nxO6HDk0dnKrUfXe8Wj24a\/bvf1K4PoRwJmojh3ZO\/PrHhZLCkD1I0WcE0l55LDB1bHpk19j03lFSETSQRim2ZfDhuHAcsWPE3TU+\/Rsfd5RKpoCafSjI8ZTr1k9Mz37jSer2nbhToruZiuKk1RF+1a1XJ\/eO1796Nmr1nJgVqZRiJibhwPXgueNHpqd\/4an+Vq+10lp4+\/bW\/KrstSnsIhpoAiQDEB4cp7PRqq2t80Zr87WLDILUPWE9h4847HVXV5\/9tz\/95P\/wpT2PydtLa7x8g6M+Kak2OqLmeyePhBfeN7oSyrLj5AJ1zYRspacuWy1fNtThLWB0uWdbYWjHBSQ3bxM4iwSo+7RVgfFHh+SjKPB9MATCQjZrKWf2klPvNDGlgrV00r7jtHgbMiKG6zt7Du8Tnnvn4vthECRqvJzAQbIWS4zxq42QXyO3SqSo2+Rex+TzJT1RxELrwoXricMHSDlyfpGIyKujNj730LGv\/7ef\/+v\/+PbFZ99RvU09moeK4tFzTCx03PFRVWnQiZPq+m26fY3UgCHg+qjUJvfteuiLD+8\/d0D4Xnd5M2h1JDMT0v0WMQkBj5UgcpJLo0i7N1uSrEwcMTkg17L3iAs1lSDPHQEQDRSHiiogJ3kVxawHOkSImEPFDpEv4hfVDXpllEtCAEQREzE70FbxMGSY4AAKkExgcqr+yO6JWsW7e3np7e+dv\/vm+6rXibWrSYjqqPf4Y6LfkteuqEEomz3WfPZUVk\/XpW7NqY8\/\/M2P7P3YyR\/\/9hutF39GQZuiHnGfiGuHZwHRu3JD\/y4SjTmOTRdzPVLOUjdmKxHNmfBxzkCCyd68yFGVTDuX8r3E+U4pU+Elts2Ek0e6QwBiKMNGD9uBKB+JlIOmmTIDXiZSFAW0eJNGJnltiYAwUPNXbswdmdt\/8sidqzeD3oApNsGGKR6eKsX02tTrkC4aGxOiMcFhyEGP+u1MQwVg4UA4YmTM2b9HbvXU4iIJh1yPXN9v1B7\/4pn1e63rr99QYT8WiUn16oliPkRIYdAnZxODAT34AAcdWlogxxWV6sGnTj31d5+SzO\/+4J1br99o3luTg15m3M3K6OaxRnamkKREXAAMYdxOZYz7YKpTGdKMiaI9GziKrKeKWLqXjckhZ2eZVRlm7HkRt9S0K7CeNBjjE+F6jZ3j+x6aO\/HJU5\/+rz\/7xnemL3339bDVoqhPfk08+kTUbMo3XiPZpdjUjBM8DTEAxSBiGaigf+kv3z3x9NHDnz7x9qXLvNon5ZAUxHJwa6l+4oC\/Zya4u8CkkaWcUSMy9gQSBgWXM2zK1mrq6MdD3OFzCg3bFXbW0C5nQz+kGYN8wqqvu1vgrdPwgSCXwTh5yPsiWw8GJpA9llNJjjfqtWliN9XGqLdFUiqF+Ss3p\/bN7Tt17N6VG91OhxQRSQsDy6mhdtIr6ra414YQVG3Q5B70tPSgJBbkuBDkjtTcffuDO4tqc4sgSGjKfOPQ2cOHHtrzR\/\/sp\/3mFmmOkkpVg5WVVKiIVMSrS\/RWiIdOMZFo9w8+dezpv\/\/xxXfnX\/y9n7aXNyjqUxTGbCCydCViCThmynFzTH5qvpYYcieRy5fMA1GUD5c5RwiigkhRnDhwhhPIuLMa4K6E2+w131tYvPqziw997fGHvv6YqPjvfOtnHLnOuTNqbVW++zbJHskQZuVhOJfEev4y6q633\/ve5eO\/9NjNR45v\/WiFVEAsSCklVffa3dEzR6nTCTa2WHDc99FJAOdNYGDqk+QmCkMbMwYdpeA6eL9isXSXlAKwi48vjiYBYrdkEjIcIl7QlbEFUVHAiad6LigI4VMCgyDFBFq5TTv3Ub8Z+ycDq\/N31KB77Nyp99+52m01WSHj76UAKSsUK2KwVNTZpM4WvApVx7DrIAWhOzbiH52l9fXuuxfUIEqoVQ559crY5JOfO3P3wuri1SWOBiQjZGTWzBwmC0aKiRVtrPJ7F8WnPrZjq\/nYNx+\/8cq1l\/+3HwadDoV9wyRYkelWyznDIIMwV2x5FwW+SlS42LrZ8cOFHQGM7IrLUG+U58Wy7pzF38jGPOnYg4VLwhsE\/Tf\/4KeDXnj6G08113p3+2509aq6eFF\/\/NgTIpHtSZs8iXEikYo47F\/6wXtHPnLwzOdP\/ezld1VzQCrQoA4eBN13r049dGTl\/FXZbmbJZSaflQquGBTa1Ewcdl2HIb1\/tpufQEk\/n4tLnAsoMs4zLVLzRKsVwsNmng6qe1METVZx5VHStp4vFdqnKDywBHiNEkXFjF6lqN5AbZR77UQiFv1e0G11D5w+Ouj0giBkw1KPrUzJLoJTDJdi7g\/I8ca++rH6F5\/c+quX1dYWyZAgyPXJr6My+tDHHjz30ePf\/d03t5bXYslQFWbaJAbcHuagVwhi4Qb0iX\/4SdHu\/vRff6+\/uU6DDkVdkgPE2l4BVAQZQUVaApRUhNgGI\/2OFtKNSAuQxa4P6dcRYkeNxDkj+T6pCPFPZfzN2H5DUvywECqEtnNKvkMyIk7eg1ZA0t9REZRE\/KNQW9ak+kjxqyRi+IlWkmTG+u2NHbOTZ77yyPVn3++\/d4nCDmScAoASEXRKCsJMZVjjLiAjKRSd+8Kp29c2u\/dWwRGUBClSUoZh0O40jh+INppKqsKBATJZ6Om6hK0ljWJ6x0VVM1jPDhrK2QFgLHgUhvIZExL2vjDgP2XoAAe1PakZRNHOs6xVQ9upbJRsQgNumvj6mkCyjBc96GJkknrdeLwLIlAwCDpbzQPHD8lI9XuDmGln7ODcjTFGdg45rvD8yvG56O5K59n3opUmuS7qI6iNElzyRypjOz75Kx+av77x5t9cVmEPUZApZ2dIfBNnkaRnwiOvsnN28qNfevD1P3n97jvXKOhQ2IldDdKdE0s7y9T2EIkNhmUtFPu0Jew+ZRr3yuSPJq3LTFJRW52xbfRLklThm5bpTap9pK25k2cmw+Qo82NIuVoqJSuw4dmkyAl79MgnTtx97cbG\/CprVU82GCGpsRZShFDiqwMi4uZKZ8+pPdMHd9585TYnSQSzJJaqH7ig+sE9\/ZWtNPPUKH59+CZE\/HQBIS9QVOY4lCfI87AE8L7E2mFoHsunExlsNffQ1Ac3M4QpsV0qAFKR31ug4dw\/FLxckqI4RfhabrJJSjG5l9obQEYxD8OotdHac2S\/63mddp9gyK+kJkQGBwqOC8eFcN1atXHiULi6Mbg+r7baFPQp7FPYh1\/B5JzYfeDBTzxw5NG5v\/mTd5pLa4nOPJuwcNOZIsF8CopHZ\/7eh\/bPPbjv5T94qbe+RkE7sX9Joke2plNBndSSiWGrD6Wqm+A8mwkG6892vCl9TCrLm1B7iEmpmAZVZFGZbyMhJWVu2+k7JzLcEZODSYAcb9Dn3Ud2jcxO3njlGkcxpBsZ\/iBxehMuhEeOC+GSk8DTIKRyyfHOfvbEjdfu9TZb8T6MT2GEnQGNNKozO8K1LWQwxEwaL41NuSW1vag7CqVdWfFoaMsPA67kyLbFncN5JZyCuD1zYghjegzCxj3nQhxyeCBknhQ0xJXe+k6qqoQ8KxdERGGfag0o5igw5HdIKtpab+0+OFep19qbbc7ugZP0IVJUpAfhkuN5UztrRw\/2b98LlpZjERdWJAQ5HokK3LHq2M5nfvWROzfabz97nQddkhGIoIGXEECGn06eWQAOOY62+BHVxqFPPNDvBZe+\/wb3WxR2DXutJNTkjV9SOV0Fy4uCDZw6JT7BafDJEXCLfyzIb8JyJKstREl\/P7OjsTekSW5MZFHjP2YbKclQ4qmPcBU7tanxg08cvvzsxWgQIZXSjYG+DqA3nk9OhdwqHB+OD8dLGWS9Dh16dN\/IRP32+TvZJkwEwqOtTn1qwhmphZvt2BiDYELe7cLGWEtly5iJSrVPsM0gISdUZB3Qw0JiCXFiG4SprgkLpwfsrDoX+HNxbxjzPV9AosR60OQ66MsqQ5rZR63NWDY32eySaWt1Y3J2ujY20m12OM1tROrO45Lw4Hjk+pXdu\/xjB7sXbkVb7dS2EsLR2k3U2OFM7zr41PGzT+55\/gd3NxY2qd8B0+jhmUOffXRkz1R3taOkHg9q2TWX9PnteuT45NepOupUG0efPtFZ2rzzykWKuqlYve12GHeSoP2hzJ0WyzQVKfM5m8TcAwobJuatkv3ItDhhFJ8ha\/kW2fqWqzPSHoMhTZfccUEx2dcf271j\/6OHrzz3fjhg42GChJfguSvkVMitw6uPnjq093NP1aZ3dJeaLBUBMiJB4pHPHb\/80o1Bu00qivPheF2JwWbb2buLCdwfpCb1mZOUOY6HHXhQQiQqLFQwCLYBZ0Gjdlu8dq7xgQIZKmfHhDw93c3vfVhvOEPLlaIMLC3+7Qj\/vL0YgKlFGPS4uUaNSbRWDRsORSqKQp6\/dG32yP49p44sXLsnJVuyGnq1OE5t\/253Zrr12hUVBiQc3c8EMRyXJicxOSUO7Pendz\/68QP33l+\/d\/EO9zukpNuoHP6VT0wd3eMo6U2Nrbz5vpAhsgxQ51ia\/lUhvyY8tzpe6y2uxua7abGUma6otBlouZGZHG3eFuq7LdfUbk3zNlNhpE6TzDZ0OfFRM5immXyeHhhyUidocwA96mfoGTqriJUiqXzfHT2yFzsmadBD0KcoZBmBde8sTkfZ8VGrzv7Cx8YP78Zg0FnY3Lp0g1Sowt6t87ef\/MZDex+cay4sslOJndVi72TJkepfvV1\/9GQvjORmM+bva40s\/QCliBRDUCzbNwwEA0MbOvH\/iW1xjDEDm5YNKWyebShYTmWKh80TbAZTCQjWEHrK39n0P8NJtPnthbLKMF8Up0YQnLJJYOfWIGqtY99J6rc4HKQ\/0mMiFWHhxsLkoQPT586sXrkdKZccjwBSkqIAiOon9qlu2Hz9KksVFwuuh6qHHRM4cUKudrnL8srCSJf3TZ78q7+6GiwvUL8DGRG57LoBEYQYO3uwsndMgAVIxN558dAeCQ3KccTYwanNK\/Oc6SnFwpscb0ht9EOJLGKiPMHp7cgGTEiP5Lw6xdCqw0KOoIzrY65B2HNC05g0gedm3X4F08qeGbGPDnRNKBIlKF33RlDKaVR2ffxkrRNQpCCVUIoj1mWjiGt4Qa5Qru\/uHI8UK4AcSTIgYhJoLW299f1LZ77w4I2XL\/fWB6Qi1uZQMd+fuD\/ovnXVOXdavXOVO70kw9fycBFRRIqIFSAy2T5rwbExxIfJUYTFUUXWLxk2RTQuOVuTi8R\/ASkqICENZzEGXACja\/XVYVpmXBAs4+2SZ3ABZ1PWSuKcA0Vh6siKpKTVuzQySRtLhv5KnEQpprV7G6OiMf3IQ2sL3YjqEI4KAxE2Kwd2BKtbwd01ggOhMLNT7Jggx+HegJst9foljlzyR0StfuSxvb1A3bm4yL02hX1WMtqK7vzpc4MPnx5stBd\/8GawvpVVd6Sycw+CXZfcqtuo7\/oHn3LZ7CIm+Sel7G1O3nKscm9OpzLxBUO2ly1QVSkWEShOrDgvyJc3V06XQ5FqkKOnGq4JehYbw5djbXZO8adZs5RYbfVu\/t7zrcUlHvTTKUWS9yZcYccj15947Njkkyf7t5c6798iGRCIIqhI3nxz8dzPnZn58Nlb529we4NW7lAQpRNLAKof4v3bIw8e716al\/2QQKRCjgYUDUgO4oNYSdL7MH8hgFxAydmCI\/YRKui0oECsjb9pm0SkWvf57WnEpCKQIN7yLlvcJNhCpR9A89uwSfuADA8uNQZNJUT0Te+0aNck1cao3yJSzEwwmeZorXXlBO360LlDcxMzUyOX3t+4s9HuXLvLbuQePUSqDxUqYt7qqaUNlhFIsXDIqxNU7cDMA585\/taz11pL6xT2KRqQjDiktVcubI35FWAAAIAASURBVLx+hWXEMsyGB0omNzWl2LrkRwCUjBLDA1NzzYh+BjiJTZoXWyctLNOVtJ9dYskT36xSzhrD8N+4rx6zAQmwax82ypCUKJbu1RQrFiMHExFxh1mFgRx0qd\/R2jNkChbHMwmHhLP+7MbG829yFLLU4uuCAJaDzYWt1t2tYydnbr9+j+o7eIaov8lBn\/ptdLtEIOHKZhCs97yTJ9StdYJDQY97TQpaFIKigCggIlIqHjunegdxwg0LugCwJVmEvOi1tbRtlj1Zm5OphCNRSFzTM898aPy1O6Qm4aImVHlrx5CUH7r9Snn5oKERWMeQ9UWanKGgmzgQiFhFX\/NQ\/Wo3qDx6cOYLXz\/kOTj05Ozv\/cWtQc0Xg97g2m159Q631qEUwdNgzeTJJUarU0dmRqGuvXRNDXokA0QDVpG+AyoSsRKHsidsCQ+D2CGiZJ6uhElKyUSyC2MAW08hy4IoXekwbz+Mg4lzuAc2NrUx+EFOR2iIADLDhE9R5lVij65yMh6xjkbaJ0gEk1O9QKGlPeIrloIQVKKPLWKrRggWHkmHNDmLEjp01A9bW2\/\/+Orjf+fc2GRt69YqdTY47NBonfbso26HggjsMruD1YE3VZn40LmdirvrraXr87LlEkDUoYg0hZOYQUk8zLwobYEJNuTmze0Du9KGYY\/CdtZmYm5pWAwkMnN92KVp8rtukYBEnPMIzcVhq+rjQtGXIQbi+wQLWYVtB6MpTJcURSE5jB27eWuDVESgxM7SI9clr4Lq2MzMqCcQEchzaLweXF7le+u01YUgch2SREwQLkvJxBAuV6pidu+hqZFos7+xsMEypChgGZDehOmSjnV4Zap1H3crBWcfX7iO4wig4LaTqoNo1Fsa2DLdL1s61MgADdwfJ4JwJdAJzhaLqUdtI3SR7sscxjn1c4BNnzNSU87MwThmpiPu5BbUO9N2lAyR6eFl5D5iYq0mGlcVUXzkJbw6EBH6FPp33rr10V955PATB968c49cQZFAs0VbTXJAfpVmZsTsLG90qaU+9fOHn356v2r2f+dfvHD5VU514jiKPzEnZsOcWSwiM9QxE\/5UYzdnKlTkOSFXxVuGcSjqElpIVFMkJ231xN48pfIWQ4Mg833Kw9wUI+ciivwIsTSV5bTwoLVl3nec2n0iLSbvxPJBUQgeTBysrrFqNiPp0qXF3sZbt9X8EqIAxBgbpZ3j8CvKr6m1dWy1SBL5NXH6uDuoHD0+9c5rdwbtHskwAbgoxMIwqSO8k9EYk\/KA47FhIvTiOpo0xDB4nLHrUaY5n9qOIJdIgoy2JPLTpizxQb6YQNbEtAARzCUVvBEZjfWCHNIYRqLEWW\/U9CRILNHShWwsBqWL9nSmykLrrhEL4\/05EC5z2vZU2oWONcCIZacZbt7Y2PfI\/vPPXpGDLfJCjoiUJCaEkhaWeaNN9ZHRIwdPnpoebXhOwz388IGrlxZU0CEZMEkiRZJISQKgPVszw5x4XVkRzuyEFtO5NOYYXTRs16rOUSjMgpLNjlAqIKJvrjucGbgtPK0ox1Z4Bsb9jXwNrr4JaGNiRYJIStpap6lZWpknInJcEhUSrrtrhze3a9Dvvnl+5d1XNyoj0erFG\/zwQWclku\/Nq34PYYfUQPfmyBFUGaFqjfbN0c7pPWOV6tz4hX\/3kooGpGOgUokDkW6oOsKtOdVaFPRjCEgqQ6HFkdwKaXY8SJkDEmQpQFpdGEo4uS4mDFIRCnQxUy4VJW3R7EUK9Gqj8cX2qJZze8+oSzij+SczYgO1ZJDy0sYMGx25ZBMLAcdnxUSChDIiEAAB13MqjWgQctAm7hsqw4pIQUUqDBffW37g81OVPXu6zbU42EcBEZNTget447XKwV1wsHht\/ejekU6oljvByMnZEXens7W2fuFKsLYcDkCIiIWeWMRnov4Cqe9AFgwL1oGwDD1jl\/AMqGAZghRSoOL30yqAeWhYc4eOHcuB9pRVMUOn80R5m8GihVVSByNn7EUJYN+J3+LWOlXHqDJCTOS4zuS4c3iOt1rRyoZw6735C+w0KrtrgwvzbtAVDUEHxuTNAYcACY3GZlboDzA6SbUKv\/7uzM+dW19vN5c3MsC04R1JEKJSn\/3yJyfOHlt98Z3W9dsgmeQGDG0v5VXIq7q1qluvstn31tNEo2BnonIZolJrVBQvZqIWZ1EpkaVPtmCD5RbHqXCodVstuhyz5Vucfcn2gkhDIxeFIvSjhOc29u\/msQbJEFFEUqZnvRZTh3AnP3a2cWRu5cdvrvzwBY2DSncCWOlW57X3Fk5\/4cT49Ei\/c0TduU28Qip0J0bcfXuEUtRs9y5fawXet0PM3+vV9k68\/+p88\/a9geiNVMP6eH1y5yEICKjW+mbr7lI0GLC2x0qwfpw10ijOtiwpXsudjlIJiNRaNJtiDKHWF3ofzNvIIsbfdu2xiXmjSueHGAI7yC+jvE1ZGRDO1iiMcYAxZUbr\/+nI0+1g9gj6XX+qoUbrcn6F+j3vxP7Be1fZqZPboPE9rGR4YV6cnqV2lw7P0GqN7y1T6h+oIt63jzcCV4mZo7vuXd0IOgOKNKsgA7iAiCHcicmJD5\/19+ycGmvUb+\/TumQJvYQJpISjHCE8tzI7pt6zvGxhyZIzyvNykyJTkiAkFm9gG6JlNMATOmXWH0EGFAZZwStBluQ5p4l0CkzJvPzcn7P8hLmM9Bif8mKkOv2psyOdAVSshKBb2qnKq\/ArlQcOo+I3NjurP3qBSRIZets6z1dy6fbG6mL70FN7Bqd3d3\/syK0GqEUso6VVeXeRlSTXo8rY+mb\/+R9eGTs+SV5E3Wa\/t9Hvb1HQETJwK9XxuV31yfHR8RFIKYMw6A9aa1u9ZovhGOB4zsykTCFgWLhoHUVZxSbkaW0Pu6KwD10LKV4y4zANoQBicm16KPIM0eFUQ06TnWIIzdv0FptKKIfQQKvHOlpXgR0PboVcv\/H1D3tVv\/dnz4XnrzoNv3Jif+\/tKxwyuX1Cm7sjFHWo3+ULHefIHnX9Fp0+wdUK3bwBCZKSJid4Zi9dW6\/PzO7ZM3Hj+ZvMMhVTS31dGACrcHNr7bnXRh84uPSXL3au3tKWOVnHRQhyPHZ9t944+V99VljOgUw5xR62c27KMyC5kIcm\/4cBbY+PAaOVw0mCo4zUN39UcgI85xiya+E1mJTZGkoJClmAZTObsfJmRqFVsdW9\/R9\/0lpepbBPkYyh8OmyhiPqjdm\/86nqwd3Nl85z2COODL0vQUykQlZhFKqFa5uH\/u6Dm4QNGax9Zy18\/x73mnHLmhRFRBXmTmewtbo1rbyKcioy2upQ1CcVKaJg0F+9dksfLp4nRnZM7Do8t\/vQXGdto9vpdjrdQbMTdPtKSjJ8Q7MbpzmZECxEbDIpI0JEiqAoqWMp3xfNjexzFP9c8MznmOwWJAwN+8ztZBnzlUapVKIZXtnqzZTZvBAS6XtOofei3nAPzlV2NsJbK\/35BbcqGsf3tS\/Nq15AYJIhKeLOJIIWB10OpLPAtRNz7dcu4tRxsX+\/ujnPbg1HTgv4ND6668RuFli4vsxyQBym5puZm58KaNBc\/t6zKz9wVBBwRiBIVCQcl1yfFCvHkWHIylRjya4LjElvUU7Psr\/MKzbCorRDMCXcn6T7klqvgMCs4kmzOXA3bINji08IMsCWnEHM9eJCvLwK3jqxfgFQIAckhSExQA6RiiIZBDEn0+JGgyBU2L37e38BB7LTJtk3bSeQygXJgIPWytU7p+XpflNt3GkGCyukmIBYbE6\/ifoobS2rsB25U+rt92vTI+17fVaSSCVgOo0ipIESweLKxtIqgSrVaqXqjeye2v\/IA73V9dbiSr8fDTqdQbefxDbEHXBth+r4BIdkSGEPUY8i41ayyrddSun2Vj5hyoEjc3nP0lGr1QWUUXmpDHqdpf1l9g92aGfkPaKQR4FDgIR2qYJw4Lj1udnK3Fzz2t31\/+XPsGtW7Jn15ya6V+Zlp5MRXpRSgxbJHkU9IhosrKpG3dk3Hb13hUbrzo6dMgwpgnrnfWdmx5Gzkxv31jfuLVFkjCUInHALWDJYKRkinoyZEz\/NNJcEItfXe1NJlXThOJNBKQrBMg\/XT2AjK4u9HxImhxbedgFHZwcxu1yHBY2ZhKTU5zgbtSOW99XD1VjeW3AsIcOap8vQbclYM8JotRk25ZwK41BmqyKYyeR8pYwqSSpkFZDWyFIqtTwhFaion3CjYpFyPb5n4RAAZlYhhf31O2veamfwn+4Ga33s2sHtdRJuqhZFJEhGtLHAUPARdpW\/p+qMjURr\/cRwQYIlaztepRgxtb\/XDnttsbW2uXDxul+r1EZqtVpt5vjB2mh96eqt1lorCGQ06DNceDWujovaqF9vRJ1W1Frj3lYM8JMMM28vlZNJnAhgnbYGRgOMrBhnTkYU5tCYyqaERTxrJi9gmxUXqVxF5TXYHVSdL8UEM83ZcxuN0QdOBFv9zbcvy0BRjWlrzfulz\/Wfe09t9SgMYikEImKSSjJnmi7RrXv+yYM8WZWrG6h4ePws396gIESzMzNZmb+ypMJIa10mTUoLhslKZXYOWZJp4Jx1riU8x\/Xy4EK2sQ5W\/VAED9mMm0SBDvHmcdnRvJAKCZ+Em+i\/aFePgGQAhKwAipgUsQDYwufHIldOKk1Pwo23jgxJ9iEDJj3LlMh8i4xGWV662mbzpPFQa8apUJuBx\/4cyqi0STHr90kWkA1ucsSI5HyRW6vt1dube0echa0+ju6j5jovajSMICJU69zfoqhLjksra1Rx+zfv+ft3ybWVrIHL2XwIbEEbmIQUot+K+q02EVZuL1VGR9ya35io7xwZHZ2eavbl2lI3oJHHnjnzyCdOLN1e+8+\/8zfte9dJBrH0m1QZPd2c6VuDwUSp1AaYArnUPh74usURnhXNuLTjatR2GDbPMLug6fFMhjZ2ug9FTErSfg2eO374gKrV2tfvhFtddnxyKqDQ3TcpX7uq7q3SoB2LuLAiARIeMYMkqwggVor7HFy6jgdP0ECy71GgaGSUon697k\/tbJy\/vRUbelLioKKPZK0PTzkVpvQyxDEqU9rWqGTH0THTdA40knfAloVFqjubO7CQ6LjEr+KS48Gtktcgv8FOhVyfhQsQRSGFfQp6FHYp6oI4MbhXVp85CYPk+uTW4TfYq5FbgXCIiYMuBk0OOvEvKua4\/86WLxeX5NRGdZFwf7XRsoridC5Wr+Bc5cTWQeNoxwto\/FOCyyFC2FcL1zf2Htr52nfX2R2hU4dpbYuiHsEhkjwySmt3iCNih1bWhCvDe8ve5Jw7PhJutZCKxWhx9hRynG0IRUokwGmKIhVthrTJTTDBaUztrswerB46tPf4oU\/\/0tmxnbXdJ3e9+\/Kty6vLPNhC5BA5ujQE2xOALFMoNkY48zjN5ZVJ7uJSHiRnyBrnFgqXSQbYdR3nJDh0MiBsghdMcke8ppkAIWqTY6N7dstIbr53VSmlN5jjUOPAVO\/addkMaGon9TeJQI4Tv6gQUMyQ8TEMYiW5p\/jqLRw\/SV6d7zZJCszM1icdVJzNhY2YKZOi3YUnKlUlFctUx0EmbUND4kJ3ax2P3CoJTwshkjAMjOzWIUqQhGzYitsupYl2TeLH4pDjk1dHY6oyMeM1GlypkFch4SDoc2ezv74Rtdeox4YpJ2eDo9RZSbjkVFCbqO2YcUbHuVKHVyEmbq71l+5FmjqsJEEBzCyLIEMgjzjO+b\/qj+5oU3vhwvGZNdswsxxNCd0xAljoHeg5lerIoT3kuK2bS2rQiw9oxavz6\/sfnvVlr39P4ZE5OnWE3u6RknAc1geQDuerm6hViCRvtCfPnFh54TzLMCU0QqUsahMSrchALhEUBJHw9InQ6cnORggR1fdEoedKgJhUrUFebAfCOk9JscSchy3Z0NAsXTBEJ5N7zlmp6LIlXjPM0BcFJlM5VQIoMPENRyQ24GzGPBAE4deqY\/t2u\/X6+pXrYS\/gGLOiBIWN\/XvDxeVwtUUgahKNjFFrjagSWyZo7jmEJhMxM6CYBbV71O\/T3n10t0cEXlkZf\/zsVjfaXG6yUpmRJdHI8SN7v\/FM5\/27qz97i6WM4b9K5fVb9VtyK+RW2HEdz2EHkVTEhb6WCWQxQ6BpNgKyz8mUqyogHBY+3CpVRsXI9Jd\/+eMPP70\/cBwF4YA8VhyG3\/2DN1\/5z69wgtKEUoRse8QgHjgkPLgVpz7+i3\/\/mRNPHowcxwGIadAb\/Md\/\/teXX3kDKmQZEEtG0pjhHGuNC6055KyYmRV5bmX3ropwKOiTlKQkSZmm8dbIHwJCMFwId+rph3Z\/6SkQ3frTny3+8GWwZGaSwebCunRFdcQfrPT4vWVx8hDPr\/AG0fgIdbYIiKviXgfjDYLbX+s6uwJnclwudxNhRRO4S5lGE4lEdVr32CoMN6FfgKUUvXbFD6SrXnnu9qG5+nJzcPvCbeyYpME6qxAyIkgiQaSYRKzEo+8d5yQGs0EhDGZ0CkcxCCrsmmTDrJkJE6qTnyizBYgrjYqZSDay9oDIaP5x+Sd0460xPbnjyNz6+\/Pda7eVVLGUCxPA9UOzg6Xlwcqm1vCl5hZN76Nuk1jFNmD6LHc9wEmALxRTbKTD61s4NceXFsDR7kl3bb7ZaQUaXKgt2sEYfeT0yJljIycOjD14kFgS2Ca5ctyFcPQ2ESSEckXkCO\/QjvDqbXPolCsPc+rMKB3hEDiWvU5KQXjk+Ow1UB0b3bXr4Y8c2n9oIhGogAt2iT\/8ydNvvXwnCPsc9mNAgtET4jjB1sZvVVEdnZybOnh4Z2plFoZyZHqGKmMctkm4UIEeabBVgWR1jtlB5xSMmnxexRyO1w\/\/0ke7vTAKQ1JMUhl2jHFTQp9CTizFLch1\/L0zTr0Wsaof3YsfuRxFpCIK+73VTccR1dHq5mKLllZpR939+Nnor9+kqUnaWE5yGMWhJL9KJJhVb2HVO3dC\/ngtFoaLg5WWk3LIrZJbJWYKO9qUm1hROKBooAXBSPhwPLde3\/3h48227K+svnF78fmlbuXAjmBjUa0ukpLYMUtBjzYWKOhy0k0wHLZNYf48DpTYZE3kZDLgptIAjKJSVdIRi9s5bJ+Fw+S6zUMztoNn4Wn2AyDiBhMEMblVb+zoAdnuLbx1Jep2OTUL0Dvw+OFgfT1YWo2julIEQZ1NGt1JrVVipvoYEzO55FeZWrHbnu6h+R6vrdL8XX7yURyYdu+t798\/sfD+hookKZWpawux8dJ7RBjcW2tduh0LAXJKX4r7v5xaBepPVGm49caRX3\/aQ4kMpcnz42yCX0TGGA2qbGYt4ozXq1N1fN\/xucnZEUXkkD54iQkKeOjszOGHjlx6YQ2DLZYBdPs+ZV2lBaFwyatRdYRcL6GZapAYtJkHCZ90cojEVpGKQJxS3GT2\/gFEG+2r\/+7H7bUWD7oUasErm7jDRlyFQ44Pv77zyQdnv\/JkNIg2Xr5EGiGoiAj9Zi9SauTQXmwS9Tp8bR5TR0Y\/\/1j7J29wFCXaqg5HgsdHAOZwIFf6TnjQOXxM3pqnaEDRgGNhBEVKUdSjsBsPk9IBQ9w0JmIWQjTmdlf37Vm\/eIP9GsmoeeUuM2RviZwBBT2K+txZhevQyCRVZrG+wIMukUmbEta8Mb1ayPCpyJ3PSZbskgmnQEGT2ZjYMsyhnykSY7OZYLjkasiL45FbI69Ojs\/CISKQQhSM7p9yRuq9u6v9pRVSUaLuGtMHxo4fDpqtYHEl\/kRRQKRIONRZp137qeOQEDh0mPbvx7XLFEVEnNweQXBpZoYWF4hdunyNn\/mYMzo6Otm4un6XErhwKsXfv3174fZtYsl6RKm0zZAyRPuypiULQY5Lfp8GAxGEbmYNXTKNZbNjxjkmTO78Sl\/FYeGRW0Wl4Y+OP\/mxw9Wqow1BdRWhw3JtzHvosT2X3xpT3ZF4+ZLK2rz6XWlnQscjr+oIR6dQmnYEokSVxyE4SUqClDNhrKe0hCEUPRmYiEhAOJKD9a1gdZXDPmnlyBTnlR6rKQNKu9M5lcXvr2+9c4Gl6i2ssiZnkmAg7EeDldbOSe\/65hYHTQq7tDiy47\/+\/GBxdbC2RF1FxPA8JvBoheoeNUNiFTUj\/8h+eeu2thOGCo3boEyQEggsHMAlxyFmr17dcexwwM76W5eIeOLsqebl97nTJxVxl\/zHTgT37nEQkgw4GFBHoT5KI+NiYid129xcixtB4By\/YaiNGkxmG4hYe77CHB6z4QplkAAKOBCgjCBhWucKbTHJbg3VMartQHWMhGAV+VXRmKlzt7l54RoPAo7nsA4JQawAqh\/eF7U7\/TsLWf6mr6liCpnWF6gxTmHkHT\/kfOhMsLFBHJAQsVsKCTQa3Nykfpeq49Tt4+qdyt\/9RHW63mn2mKMEzhbTcJiDWPAvEVnilL6UdZxFUqYKIkXS1Yc9MvSrrYPAFlCGOaMOsXUwGoP1NHbpM6syPrVv9qGz007qW5tcb8UcER15YKq6Y7rb2aCoT2pAKgQUGx0vxGrZbjreSAwRkxaCyLpiyfeQsHHiEIjcYIILzK3UW0pFHA0o6mfikQYehTNVAOi7yXLAodO53iQSRAylGKSHFhxx505zdm7UoTDsNilsR+8Nlr8zrRyHZo9Tp01bdynoUNDhKKB6jTbaREq9fZ4e3C9GPNmN4rOYDTmDrF5OZGmFA+GMzu6qT+\/sLG+0VprkOI0HjrSv3wzXVjRCgImwsemO1cNuMzm4mTtN6jTJr2B8CpUaBX1uabadLSGJAqM29TlMIIf6cHONRZDodqbCRHFhrdLfyMOxUUa8SHOhxDETXo1qE1Mnjh37\/CO9gbr96g14aF28HCwucWToncTkVnj7dslBOJi\/yymsSW+bNDEKApraQRsb0bvvy1bAmx0xOUF+nQZdYgWStHMHLd5L5A99Wu\/X2332R9utAcf60Nq9nY1qh2NpXau6S5E+EhBMikifFExwdHgpWDoYhAkmG+ZFScfYYLWkMZDASeOevCrq40dO7p0Yr4hUbTpJJnUtMndg7OiZA++srdCgxVpwkSVSY3btIB9P6kUqg5P5oIgkH6MUE5d1BMCw51lpuQP7eKF0UoFEcoY4gg4+KYaBDBv5RHw2TghjQztdDwkSYJZKqtU7zQdP7haQpEKSgWoG3b982Xn6NOoeyzHqNbm7QTLERpsadRaCVETBYHDhinP4EFbXOAZ8MnITosSfGHDcamV8357\/H2P\/HSRZkp13oue4XxEqtc6sLK2rq7Xu6elR6JnBDDAASJAAl1wsCWLJJddoNFuzR67xj\/fM+N4aV3C5giRIglyAAAkSA4BDAKMwWrWWpasyq1LrzMgMfZW7n\/eH+1URUQ2O1bR1V1WqiOvux8\/5vt\/HS+X9u6sijADJGStLvxNsbYKU8YFpiZVtd3JEbEWkLyk6\/BwRQk\/trSG3cGyGV05Qq6E6TYh804PtmSgkySlag2+US6RPwpTawIHZgNzcy0jGvOcEEtSTY4DYJyIX42R5tEyr3Smx4ujVLz518dUrIQDa7rX\/8KZsRVpyksKnkQGz+PwcOba\/cD8WVTFDDUXMIeVqOzAwopaX4KDOyw6fHhNSAnKAEEpFEgIiBXYBeAHLZXZ2lt\/dhKvloOWZnZ4x1LpZ5IhIpEAGeqqMOSd+wihJfk6m6zdAjpyxNPa9q9ygPnimnC4wA+czQ0KOqJPDHHBK7uDwM0\/POjaT5uYBrbZyLHBc1EOfSom\/+OLcnbfvi\/YB+A4wKzbLxi9aIlqiDNUw5qbFTKJUp4r9GX1d4sQ8HTm+AbPkjsIYKE5MI0J1N4ZMOx67obuU9jCRkqktECnR2WuUyhbHuGyxXGi31Nv3+BPn5bVVijxETm4J9g6wPJDEB5DHuevgzES0sQWoA72Td42MDBA5Y1ZpbHjg5PHm6mZ7aUtf2O2CPXRmvvr2NRBhSschks0mmxlGRiRiyLKuyPTZJgXtbRC3sDSAUyepsQ+dhgGUIWbjdykVc6h4QAgx3sKUJRZyF+wicBsISEmSIcowlUFkMEGUGWebw51zYExLjdEqYKHCKiPkVpRQFPgkApKR54cCwCaQAxV4\/jy7EcJylfT+wmygCEjxuSkaGhN37hmBWKx4yshj4x\/Db8OxWVAMgAHjaPP4tOQ0Mgn7u8AdsBwcH8OLJ9TqJlejTJwJ215SrZFloe0WJ8YJ0N8\/pLAFyPUPm3QReqZmDLgDvEBWARhX8S26K3EuTfH6M2g7pneYqEMJOXAbeAGd0vGTY489MqL17ApAAvz4\/fpQmX3sySF9oDGAC+eHhibHq7VtsFyIbMAIUKX1cx5kQUmGSlwpmhobkqUKMbiTMnLkDI0ytf5myy1CBIsxsFzkjpFhJRZNosTAmjV7xPdjhqZoVKQxdqbwVe1WR3HkTiyscSvgHdKhhI0NdmxANqvk20BStQlHbNRCIg3tvrtcODUjtvdI6nhICboZa8oNsBxr8vI5ANy7uRD5gdGXcxw5MdO6dVcFXqbJDKAIBImgY89PB\/dXDeU6xlvFZSWCDFXzENo1LJTZ1DyFIdX2QURamp6mbWckfqBkkn5lxeEKFtlFa2h87rMv8oK7+e13woN9iloQgQHg6U0OGVgOK1SgWAFug5AUeSbDRH\/nUpAiajVUq55Oh7hDEm985fUmkdjubFV9qNfo6JCVh6BUwkIRIh8O96xzJ1UQyXsLsX6f5fWlia1Gb8cKtrdh6iTUQnKLUCiZH3NwBLlDioNls9lpnJ9VC1sgyZ4fQqlUGBm+DzK03NnPPD\/3088JRWv\/+bXae7dBBZgjFJnVSFnoLbPBKYJbZq4jCSJjwoDeUEgEoj6O23Q6H2t5WbZMAu6AXUS3fOHSpF22YpQ8+L66cfOgVGLPPTZgc\/PSTIy5jz018\/3VZbILENogLdDB1Dm0GGnZWD5qOr7l5uHo0O9Mz\/NNKdsBSJSjETI+MMg7IYQeSGHoMil3h3paOubr2sNDAxdPtVY2vOUNgmRWqUQYKACr4CB3idnAGIgQyBe3F6znHmXzc7QUUacBjbZlTYa8AMAhkkBKeUGkAOfmaH0dWOwdMSUoVsYGR4\/P1DZ2mntVfeMDAmSqMj1DSnqHtTSkwBz\/CkgGq+vs5HFkFskMyzw7KNU\/kVTUaUi\/jYUyDExAoQz1PfTbhGRuGRBPOJUAwJhgpMtRM1ayhp+8MvfXvuTalqqMrX7rbexUzWuq2WFKkJAoJUhJjSMQkU53IB1YpdXGmMUZADJO3AbmEEL7oHHvrQVxc51kyEZdNjqo1neo2QLWZCWHP3oFnrgiv\/06DA9Dsw1SACpABkog40aThdlBJSMvBIugiMA5lEvgVkAEMDJGOwdgFfHkLNmOurMMvIiOa1uopJSRTCTFaLsDj56FkWFgOPMLL08\/d45r1iVkQihVbJNIdH+IknFlWwqZc2pSLG0YdT88xHbSB\/+BGTV9ehtH08RywCkVR0auPD4h0cTrcoD19fbyg8NyhVdrczPjLtM1nM2ef2H69e8Mec0ScBdYEMujKbOFxcruVLqIeZ4b9gZt5ltwlFV+QA6bCgCoAMKh0om\/+EroRyAilDrdMUPbyQaNxZYFBQDI7MkR+8xs9cP7G\/\/k31PQNk0ckiQFB3LLZbBLiECtvRiuBbSwaD\/3QtgOYKMDxOxCKVKSkINTgaAJMhIrG3j2JGy7GTEd5647fOGkDbi7sOI36iZrABGArFKRD5UOby3EiZQpMNZM6HxFocBSiVrNDOuD8mQ8ikWaEvw2+D6UBrE0AANDEATk+WCXkHEkoUIfhG\/KUlSQ4Y4SEhDjgjGGrHxisjIz7N3dEYc1kBGowNCOdedQ6\/cTgaWpIpKYiYSzwuLihVCGrITkt6i5BzJS9YhNTbFT82r7gPb2yLGU56l\/8x9IKRwcxNFxUGMQhSgFNWsQeqQVRmn4Xjz6392BmVkKQhgoQWEQhytkM2CIp+aAO7S6DbwABQvAdYouECgzoCcAIiFqt1ftkzORUAdfe7t9cwkpSoJZKEEIphG2hptCAGA7vFi6MPAxK4ORpx5CaFY7jD0TVMoIOOLBugN2EYuDlx6ZO3duWF\/wGQAC3VloeS0\/9OTmRuvYmKsVuQpgdn5g6vjU6v428RpwB0hpj2Ea1QCApmqmnHQkIY1h18ghxjWlB18mXhlTp32y2zLGWMvb+Np7ncMWiQCiEESYCbHJwqRSeJTuGznjQ86JCW9tl0Iv\/TskSUkH0SkNQLGtl5+BbRPI\/RpfW7auXIj2d8nvMMbZQEUeHQBIsF2QgtptaLfx2HHarZKKQAl3bKAwMxm2\/KPlTdKXf610AUQE9+Rce31LRpFmN+czm3QHnFMnZKeOq5u3iTDJX0HoxwpO3Gd+h8IAnQKMTOFIwXKLx195vDQ7vvon32\/cvKGDg\/SObJkXS0lSUf39W1v\/4Vv2YFluVMslt\/zkFW91w68ehY0WiZBkgDIkJeJiR2UQYVomK5NJfgwCUKCkNmVZg9xQIpUCBLWzA80mv3CWJoZgwJFvvw1+B5hF+7tAEpBhaRjHpvnwhGodqnYdIo+0GgOMvAsYB1+ARKgMkuViqYSTk7S6jmfmQSEtbQAAoIlTtwtFi9DsILokiLy9b73ZvL+poshfWiEZgJLxVEZlhsuQ9o0Rte8GuG2VysrzeJe2Idd76MvryohF00gTrWewwHLQKfPS0NUnZgoFSz+xEjHw1e17RyrwFMk33q0+fnnEso0KeWDAfuSJmfU7S7JTAuGZizEofd2ih2MoifpRTzDPZeziCGOeP5z2WZkrZLiz7+1XSQQgQpAhajQ9USYfPa+2AkaI\/jbANUVKICZzSxM7yRjykguVQWrsZ0g\/CEqJxWX37Glx7hzdfaAsh08Oy8YhSAEyBLsMUIRayK6cVz6i9IsTA3bFad68p7xAh64Z2iIQIBRPzquWFx4cZeJD8hJ8\/feOGjA5gZZFkSTdRIwT2zPWkHg2jpYZNTllKAwRFNEdGHz58alf+gxnTA5U7v1\/V6TwQZrnwYpx4hEITx7ubv72fyJm2eXK8Ol5b7cqg3ZpfHjq0TNHdx94ewdCGr2CKUgwE9mT1Ubmw+VBCYo8ijy130QRkDaro4RGoNbX2EvPwf0lVETcBeQISJGHaJHXkev3FRIwBKcAg+NsfJIaNWgcUSiAOHAXnCL4wK4eLxQH6NS8IsFOz8rdBu3upbxQUsAsi9vM9Ai0FSgCJZWS7Tt3gRQIHRMrKeeRpywhgvSkXgczAepQeyKl8WNISN1gEMqP5DF7nULAxPxuIqW0baIwMDwzcfHKmALggIQkAT5caCwt7lCngYzfW6jv7PszsyXUXxXh5ZdnX\/\/O2FFzn4KWAe8CmOkfsEwLKHdTxHzKLFKaU5Gnm2J69GnoI3b1loAMfxVAhhB2dOQ4xRQJ6hOwEU849L2UYrMdKQQLuAXcAVaIbEs9ew7ad+FoBVRISr8kAACqEwavvcu\/8Dm5u6faPhuugF0AxkDnnzoVYoOF8eGRv\/6r7e\/d7vzwg87KCgUBKD9efhohS7xSoIFB\/\/Z9REz82VkwZ0o1lEoe1KFShnoEaBtXJ1KOAKq36Vinhwi85LDRAX72ohoalpw1251y0e2s75NSGUw4WfESkqhCiBTJABiPonbt1tHwibmA7PriprdzWB4pjDxyOvT86uKS6Hjx9TTj+knptZjRjjIDFFIRhL7a2clkjDMcKuNwRX7ta7w84Fy+FK3vqWYLwAIlySqA8AEEAQOF4HXA86HVgWIZBkaYxWF4nCIETyIvnpsuP\/+ZcztPHHvnT2\/X3l+gZgjM0iQ1BA4Wh4KjCrbSLs+kVaAPDUM5MDIaTMKxYwQQJV4QRAROuv1IBMiR8RRR0aUTzeyiGfR\/Jikck5hFjcS1gDvgFLFQeeaZubnpUvJpmKJr72z51QPwmoBWY7\/2YKk5O1tKfLhTU8WT56aPNrfQq5Hwgcl41s7MMsVuSkVGSJ79tgl7FPqEXT6QbNgkGvuTkubBJYUaK6qH9eb17O4J5akZsVogawliNjCbR5LW9nHEgVNT4Pq0sQGCkgus2q\/jgyX29IXo7iIbHYTCEERtEKFmdaNtlS6eGLow1\/rTu2GHqDgGjENrFykEAGAO8AI4tnPpdHD7AUVhDlCSUO4x3\/tut\/HMBbx+nYhieaNpyWLmmWcWYqngnDrBLYdankAVLq2yiU5Q4kv\/8F\/ZZbt1b0UFHVQyiVewjJhVAUBCUGYALIq8o\/ve+JmTxWNj+2vbQXW\/bkFlamz8zHFLydbefn2\/FqOs0gCGPJkkpyYlbpFQhFy\/0nxkgM9NRgv3odORfkTo8DOnWaMpNnbBLoIQaLkgA9LZtxrHJwS0W9hBBYQBwMQczo3Pnpn54l94dHJq8PyxgY0Pdw\/Hp3BoCNo19OuIQHaZCmMwMCwdS4JCEwOaaboy28hxSKLSN2NFGaNApt7XC4mRid1jjDNmJGV5nhPm2tcZInb2NmiWB2rjErPAcsEp2wNDjz06adss6SAFHbGydEhBB4I2AVPN4vvX9p95etwtaBIPFlz+5LOzN959EDWLwDugBCmKufjpL8qqN\/tDKLGbBoeYS+9JrsTdKCJUQKaO0NcEAiRJWt6QYoiTmoDFBSlH5hC3gcAEEpu7InJuOYC4ukf3Vqi5i2UHz55FBrR9QPWaXjPy2rL1K18E31cKYGACfAfCNojQnR2DmZn6999vr+8H796lThXCFkgPCIgXkCFwl9yK+\/Fn5V5V+ZmpIOS2gvyupcD30A9pZAY8D6VPwgcZxc4I5OWSPTVOjClE6nSig1ZQa6kgRLfonD8Lqu2\/t0ydpnFjC5\/MPkVA+iTU4mwldUM2HvUy6av9xeX5cyenj43srmyIUNXWN+sky4PFsbmZoenpTsur7+6FHQ8AiGQsqMBUXIoxzNKyCBywinqzxKJjzU+K+6vk+QAMSKlGnZZW3ScuDf3si\/7ybudHN6jThE5gGj9EBvoAAEBsbAzHJ2GwzIcrlcnhgUpRVxnuxBhOdWh3HRo1tb8NioM7BCWB5SiYLQoYA6bbSByYhQBgO5X5eeB2a2OHAkuberKuiJwaU8N\/uA2WHqhyxpAxZuC81EONzV0qMVOtm8tDDKHQ69ACywW3Mj49duzEgEqInIj3Vtt7ey2SAoQAAIq8uwtHGzv+6ZNlFle+Tz458c35yc3DHQpbJiYxJwmI3xXEFG2UX269VP2YcNh1EczMHMEAfxkz2dfmp0AGKEhlX8lsEcziyoIDs+yh4dLMZKfaCKuHIGPEMyAiRAyEkuB7EETUagMRjA+w6REaKigvgGYIkYL3F9wXL8uthtwmaBYwPHKmBhgo\/9YdFVK4ugWWA8KHqKNJUCbiLvL4UAGLVrSxA3YZuGvG4CIyrHdSJHxQgrIDVGZB0yufuRAdHIZH++jXmQV8sMwstIoFkiQjFW5uURABkR75om3b8+MIneCD2xCGoAQqSSqKecc6Qg+s+DWS5oqBSFqegoyIZKDWbi8cv3hm7uz89tJqFAoi1aq12o0Htm0Pz03NXz7v1WrNo2a7VpNCUup1jDEnWglQGQBC4DaoCEsl59yJ8O4itT2DUSEFIqAoAsTRVx9vr9X8W+vSa6LtEBICMcdihQIrFsG1sVyU41OqrdTOodptr9TEG2fHT54cbJWLW0sHdHcdo0O0XTUyDsQBi8RsUFHQ8SRnzHFNoihYwPj4s49c\/NXPWZa1+OXX99+7QyKIu3AGwYbZFClE4By5RdwFu2gVXct1HpYgmAQQYEbGFhcuSMAy7Rn9KtlguVgcePzp+cqwK+MVK6X64Rt7HS8wVkYAAui02otLjXMnyya2jaAyZJ+9NLV5bxC8GggfSI9DWa72w7xSpQtmnxluYgYA0MNoTtnymM214dweHnRCCSIEITEjkc\/HAyZdLk7cYrZ99pc\/PfnMuf2ba3f+76+IhkwXIYOQYejYpiUDBErSXk1uB2ApPD4Hx4+hh7Rbnx9wLn3uSn165PrNLY8Fwd0HYm0NZABE0PBxfJo0PlivIo2WddCaGgv+6OsURTHoTQAioIXcAWcA3ALqhmJMUSG7iG5l4ur5p\/7Wzze2Dq\/99rf8Rp0XbWrVxepy0GmTUnE8uH5hFGPcOTMHNguu3SQVYZJqrqSBoMb9Pysj9U87skaiiaRHZWt3FmfPnjh5+czKjbuRBABGBGEQ7i+tVVc3ysODxy6eCdr+\/uZOp9GWUaSn4bGA2wLLZTMzgBahhSXOH7kQ3rir9CkHhJyDW6TSAIzOhku7h7\/9nXCvWbh0MhofEOsrrgrcyQkYHfIXVuVRXR01KJQEW8As4BxLFTg9+4Pfexc6wvqpi+7ZUX6\/JO4sQ+sAkKFbhHIJBgeAKGQcbJsXisg4ISIydArjH3sMxkeI4alf\/tjsS+eVEqQMe8a0CwzXxXwEY4wYSkTijFmscnK8urhCmRSImBhI0Gs4iOs5ivVkZnSHZkaPTqk0PPL8s1OcM2Wsd+Q1xNq2D66D5Qo4BT0dUZZ9b9X\/aUFoo7ngM\/boE1Ovf3c4bO1D2AaMAGQm6SH+whniPebpi5SMqYi6dNf5GLuU\/BwnTZEiwuHShb\/y6bYXkpCcFIOYeZG\/ZMbHPwJDiUxaTunkbORYpYvHi9OTzVYLSCAiEDKLK2ZFM9M0tg0cIHRNmx0cYAhNBULAxGjlifOvXp66enoILw2O\/WjwP\/3Gm7IF4I5A2AIZQNgAEOA60O4A8nhyw50rF8TeLoUdoz5TKibuCZBEXgQeASUeIA4I6BSs42fnvvgJZ25seHyoMjXWvrckWlWIPJBBGgWHDIAD48zm7rkTikR4fUEfsETS7H9GRaQSkLmVVhppxgAZuaZ+KVEpUpuLy+Mzk+eeuHr\/xt0wMoFhRCCFbBwc3nnt0C2XisMjx5883dyvNnf3g0BSUmU5JWv+uNhpssEh+8xkuLBOAQAvAip0CjQ0DmGASrLaDlp2\/a3beP6UOjwCGeLJeXH7XnRnTYWSRKilbYAckECFoLAwO0OH+\/56HewCfC8ILhxzHpuVG6tkcWjWKQgg2IdmGwZGhB8BAnedGFtGIKW3vkf+mRBg+5vv7v3wGigBUmYi5ql7\/KCho8wCxqyCM\/vfvMJ6oFjY\/ehl\/F8p6Trm+Wo3PbPAKkBhYPbExNRMGZPWFeBRA4aGh93ToIIAlAJEtC1WLIVQ2DkUM5M2xZroK5eHTl2cXKhuQuCCDI1tCVN9QMYVkmjX0n0h4Sn29ytlUV5IuaaObpTUO\/e\/\/EbzoEZhB2VkrEyU5eEmhGdm7sPM5qWBs3\/t1dLZGW\/7MDysJ6AtVKHroBMIcXMHOgSBwNAjikBJ4Ax4mawyWgXyEJfroyVL6jZfESH0QAQQtsFvQtQBGdDOGhQroAiQA2cA6B6bAD8Sm1VjiNE3GYZJ4Ju51ur2WcnBQpGfPUPMoVp74\/u3KrOD7dX92of3VKcBwkfpA6kMEIaQMVYoDjxyztvaCje2SYbGZ6xl7hDnx2ayyq3Y+gQpWR2TxMoEREmk1MH2Lklx9rGLD27eDz2fdHpujM0NWp2gE7YO6+WRwdETx4ojozsrO52GT26Zl4cLE5NBnZynHmvdewC+wMoQjk5Q2AbvyB4Ca2Sa+1J0hOx05MEubW9REEEU4kCZj01DyYKNVVARgIwbfQqUtKbGZOiHy2vAXXDKsE1ytAx7R9bLl6KvvQdD09DYh8gDkqAo6kgAdF3bqCKUoAg2vvZG\/d46SdFcWJGdts6lMD9UL9sCGTCGlgvcIW5bBSdq+2D0yXlVWu4OlA\/FQswotpNiwQa7yMtDjzwxWyhberCg47bn5+y\/96tzUs0KMtNlC9FCBIaWg8konxBKZfvqE9OL7w8qvwGRj9rZHk\/au+AKsTsUCT8i4KTLqJxGxUAXRQeASeXtVTs7u2ZEEfsJ87NIzAF1kKNduvfrf1ScGfW298PDAwOhRAUqKlTsUCjZ6ECkwOtQUAcZAulrp4BAkBdYI5Fz7OTijj8+Ujg46Lz9wY4zU\/KrIWmVDONAFvgeDI7EZ6DNSwU+NOTfXdJCfAO51RG\/yWXYYlgpF06fUIyJo5pqedGdRQoV8OK+Ktb\/3m1ZPxCNGggfTBMh2WwZMOQFt\/ToeX9lNdje1Qo1TGS0qWYo63JO74Rdb1R2WKQQDZuyulcVUp26eHr9\/lqn2TJkpKSHoUD4Xm0nqO8dVqY7zvi0e3LIGhh64qcenXr0xOKN\/Wvfvk9z56yL58BrYuipo7ZStqx15PYhtTpECKSAImPPRaSGEJ0ARydh9hjsbUCnAdwBboGSbKACIyPR4hpEEiwBoKChIJwQu002UGCPn1dv30O3SCC0WzdQFil0S7Y2baCSBFLUD4\/ePwSN65ORviundt7sUsLYoUcSSe8FriSSZjKOfSf0iH1S6DFn5OXJcGJoYuS5ZyZyvmggsNC2GAAmjl2Ghtkg8\/59hXDlkYlvjo21OjUIWqRC1EoZzM67MrAAhr1xWKZxRH0so2nEmsnGyfyBUkoRkACpWyCB8fjnh6WZIDHjYyIlvFW\/s7qSEOkpZhyXx8v7R75IHvEsOwcIQRZPjbHhofrq7n\/+T8H7x0erH64erG6yASyfGuMNq7m8oSIGzAKSUCjDwCj4Hjq2c+VisLCkhAIE4Lae9ZkrXLHozs0oi5NSKgjD7V1RrVEQmSsVAjCkdi042ADlgwhQPzPZ\/YVxPjBQuXiyvfgg2j8AJVBvK2Z8kFt4lHFWWBmzDuZwKNkMbq1jUkgg69UjIDV\/Zn7jwWq7Xov90\/onkaRMoGRrfx\/qHh+eOvvZM1d\/6hGrYBdKxZXVtljfpO0HcmWZDquk9MxUAYmMFy52OzL9R5L21tB2cGIWRyeo44PlsqjpnDsV3FuiSAERyEgLKWhtDafm1Hv3rVefpLUSrazgwCBwm1QUBQIjMTDo6IY76fsxhJg0Qo1vSyYhu4bdBgiEwDDhNZBkYClAzhiPLS1djY5sPkwuSCmj4Qbjo+c2WC46pdNnxqcmCiy2ozFAhqCSyVkuXCmRPSZ5X8QRT85XLj5+7L39Heq4IAMAhZjWo3HjmhAyQW5d33wak9JLv39IphYSIUVKkQnojfkgaV+UEvhCEtgBxIARSQIZ6tQ0Yw9Ajb10RudHDlZqyrEAwQAZFBIwYA4fGbanx0iF7ev34fgx2Wkthe1wcZn8htprdFQ4eGJq8NLp4Kjpb+0RWuAO4fwIba45xybEQVu2BXBbo6iQMxybYY7NChYjIIBofVe1WrFHMubWadJ0eRD2lkF4QAJ0+8DEeGl5JrNHhgqnj7fv3Bf1GiqRaDlS\/W6a5UqZeHSyzG9l0pBTkmkuKFiB3pCB6od1BHby3Mn1+w8aR01KS39NWAIkAYIBC6TXObi32d5rlqeHGrvNaGMPl7eirQPyJDAXmDabKJCmNM83xNFwU5AgIthZg5mT9qdesZ89Q2\/cCN\/4UHnSsJn1nVgGtLHJJsZVx5M\/vMafPCmqO9Sq49QsFCsiCHwvLA+5SdaFJkYTqYyuWudOKMrc4vL4iXjAAISk9IgiAxfpDhxAxK5k7LyW2yAJwSny0uDVx6YLDlPGuYuIYCEggUxN7phqechMy1WcJIFExQJ\/7pnZD18fFM0CCJ9I5JBSqRRNb8AqzZROrLnJdkGpkBSSGMU+kcrG7yKBQIXGysh47D\/NLvJkZceQddN\/SMRi2ugAhNwqlkbmx5a\/vaLCAFwbnCKENjCJHO1jk3xiLFhYVe0OnjhFWzv8zATtrFN9C2QIIlSk6vfXmMVKp+cHL53yGyIKWfHkCTYyShW7\/fZ1sB0sMbCKaHOsVJBbamNLbjUhCglUAm4yMdugSEWADJ0iFStQ30MwYiBzh2PmnlIcH3FnJxvXb6tOh0iPnU3WbLL2kKivxNjKrMgepmieQ53gZYmgVq1GoX\/i0tmNheVGrUHaQWM+lSJCJAkyoqh9uLL2lf\/pD0cvnTjaPGrc3S7ODbtTp737S\/JAqSAEpUCKlGmffh8MGAAxje5Dqwh2kY6aVLLgsTmxsatuVDAQ1I6fL0NqQPLaOF1Wq3v0dtu+fEpeW1KtEAeLcmBkpxaWpoeBW8llwFSeCX3YVA4yBWZ2LUKmKUyO6ZjpEZn+wO5Hs7syTfUzkGN\/oO1CYWDi+NTlR8Zj7icqgHZb1PY9UipSWoFJLAMOUiYMkE9PF12bMSQAlAAnzgwOToweHu5C5KEEo3TNnkd5ll6\/aC\/q0tRgjiOG1C9FymaI3DI8Ie2XVz2HodldGSS9MaVfbcJUBM+QsUK5UB4qdA4adLgPbhGYC7xgVVznzIw4OPI\/vE1SQWWIjo6g3bGiQX95CbwakNEwE4CU0Lz3wBoaL129Mnpi\/uoXrvKC\/f6\/+XZnZAiGBwBDWHtAhzW1uaV9M\/FQSqXOEX08GHARUXkAazukAsoh7RgiQ8YrsxOFqfHDD+9ILYXRfPe8FjxpR6eE33gdWl2g83xuMqaN6cTFp7RjmLUbrQc3Fk5eOGltWUf7NdOhiYmDoG3BfkvRzs611u6dpcL5Y9H+Rri7zmxljVVKj57GMOrcWZJtD9KZiYqrHAkSTHIdEkQt4i4wR77\/oX9UU+\/dBr8JwxN44iTsbVO9joUSACcRqd0mu3ycHqzSTscaLDiPXmjfO6RqQ1F5c827cHHcKbu+rw89A3IlbhempwZPzx7dXpVRJiwt600y+CkdyeSCXWAFlxBENpIJenSSWW8sdXVKWYLAwkLliafmp6dLCpIMKPrR6zt\/9MeLFHgQBiT0JhXDgxgnboFTcIaH\/vIvX3zxiTEjvSMYGneuPjP7o9V1CppAEmOPbgYEj+kuQaq7CZwJw+whN2YlNfnFq5RtWVZliJd8iBwgBM3qT52WWdURAjBeLNgTg97KmvI6qET6FZEDs8rDBRcgaAVIRCChVCqcPM258O8+kLUjIAVuGewiVLeQM\/Q6qnoIFKVjWSPKsWS707y2dPK5y+MnRiNJxz92Zf+tO7RwBzo1Mys3qRhE2VAgw\/sSKSWPMbQdONxEpcxpqVUWjCOzRi+cQMet3liQgYemTE0iTJLCgjJVJSU\/r\/6\/lVlkGc4o5RFbmfwKQGVmYQqCTmflzvKJi2csy93f2VMqBkLqr6EECR9IgQwpKkt\/mI0V5eae8lXYbEbr2874gHNsHPlUtLMnqkdEytx90iDYTMyRinB0GKSU33sNiyUSANtb0Gji8CCeOe389DNwbS34xg+h1VZ7VShw8KW3UnWem+KPnJR3dqDdPFiqPv3iVHmk5B8x0ysnJORoOce+9PHpzz9bu7YUeL7SgXvZFBdM5CegU3u4YzOLuWenopWtxE+Yg7NSt3kindJhirsnbqFTsMtDjzwywZnp\/Sugji+vf7DV3tmkVh0iH6SAbAYbMuAWuGWvOfnOW0MvXh1Bm2l7ScFmLz838\/a3hrzOIckwXv\/6MM1cNTQ\/UqsUIfP8Ze+GCZ+JEuYM9iEeAihSNFQ89Zc+VW8FIopQSEaEipKZWOZcRVKIhNbYAJ+f3Pxnv9d878P4mGBGy8CtgYmhSEDHk8Q4mxjAkzPi3q3g\/gMKOsZVaI\/CwSaQAi5l4MUkZ+3HVaBhaoSgBDjWwYF\/sFIVnO0uH1EgQFLcUY77t6TSmQ1iouNP5Yaj0xAFyTkZk4E4s+yh86eI1NHNeyrUK1Bh0pHCLG+PsqRWzO6IYOC\/1K2dSyCVmLNVZ6HAusANfG\/p9uK5K2dtG7fWdhRxyEZeJzGdBGJtg81Nw7rUjV0KRbBWA1LWyFBpboqPD0bNjndQV5EytzUlEYCY8RBiuYQT42ppGcKQ2ocwOIauRY19ajU4oTM5EJ6YsidGxWGN6m04MQsLG8B4uLhvPX+Vd5TaaTT3G4pBZWr4cHWHGKcYLkFS7vz4Wqdab95aFX6Q2uH1Rs4yGCuM47Kdgu1Yp37pOSup3rAnuUqTVZCynVNKc8Bi6KBTmjk+Nn9qSG90epEs3a8v3Vyj+gG0DkGzC7KnllbYuBUAXFscqdfPDo0XAFEf7qfmK9PHxlaq2xS2EzeD0opYNHxMJCCl4h6moswnR+jJH8kTn7I\/LcUoDdH0N799o1FtUeBR4KEIgCRBPpKFFBAQcATkg0V7asx\/sBonDqB+cTRnfmRutOHa4WPHrbESk75Y2pRrOxB6oCeQAyMQtEF4wC1WdIXXiPdBSkJ5AZA4A86tydLejYUf\/fh9dIAcZ+DMZOvmvgq0u4YylXB8czM9gkSBgQAMLJt2V2LKvWGRWI49cvZYJML6whJpvVuSMgTdQwgTyYp9IlaTzHrM1BoIvaFMaU2fmeXqolmJKISFGwunL5+emZ\/Z3thTxNDcXA0QB5RAEajqEU6Po21R5IEKYlsXieph8\/CIu87g+dPu9FTnoC4O66LdSjQlpBTanJ87LVe2wGubcrG6RuPHoFAEz5O7u53\/7fcpVNDu4PgkyA7MT8HmEUkOoZIfLvPHT0Iz8JpeqyVGj42tvW8bnrfJNwubdxZbd+7Hp4HKSiQzViYExtBywHKBu1GxIL2A5TwBRuGftMJStnkPmdSsOGYxp\/jcC8eHxgoxjo8YwcL1be9gH9pH4NVABEASlaSUg8qAOwgEXqG6XV14UHtyfDqWacPQgH350vjq7SIgkgYoKInpQZgIkkWSq5NPremJg0XsDXQzzyhp4x9nkfQ297zdKgWdWKgp88ionDwAAOA2pH1UM5lAYtxyrJmz49Wiq37uKv3um+F3r4NtGY88SSwPEXLoHAFjQJzPTqmjA1ACuAWK4oQvIqcCtutUOLeVd\/uWkKCv+mxmqHx61rsfCE+fDSK\/2yjoMYhioUhBG\/U0AhEZB2Zx1xk5MyfbnfrSOimRUFEoxSBlMUUpV6B3TSV+M+qm3acknz4dsbjRpilpEpUQUbh0a7FQsudOz3PLAWbpcAhKmDa6img2rNkRJIlJWLxSIAUpIfzg6Obi0Y17IMLRJy+WTszaQ8PolMkqo1NwT82rzT1qtICEGSQQwf46qBArZegcyaUFOtynVkttrFKtAa2Afe4lNjQIQFQ9kNfu8MtTAri\/354\/Nc5sFywb4h4pKAkiJOFD5EHUgciDyAPRAeGB8ED4EHmk\/0X4FHkUdkj4JCMlpVIU2wpSqW\/WcUfdfl7KBcQClsqlq1cnESHuUaDwxeKNTfIa4DchbGPUgbBNUSf+3nwUAQifog75jeho9zvfvtv2BJnWGRDD55+bqVRcUARSao8fk4Ij6CgTw1SVIYjAcJBBkkGA5dcKYpZI2x0Vm6pBkQOCEhS1IWqAaIP0QWoGqWd+SR+l\/s8OiE486RbmPDJlAgPGK2ODM+cm1t86sA582mmAkNBpAAMQAXJGhUFoVGOfqsSxYepIMBm6BMjRKVNlGgDtAiscPx7cvAOtA\/Br4NfBq3WWV6P9\/YnLZ5xSRT\/8qY3SjJvS0E3U8PiZE9hpUNKtYdwqlUYfPd+p1o8erJMZL0tIQ1EhN5PKwPL7CiKSk\/AhUJQssy8L1jQ+TzTpmigBSUS0urg2e\/r4mStnl+48EBHFL4022UtSgrZ33cunBYl4X1SYouAUKKF85W1s+du79vCwOz\/nuKXIE7zoUKuu6prHmi2aCI62YXAcxybpaJ86DeQOSAntCO4u47njMDPCLU73V9T+gVpZxdMX92rw5PkJd2i447cNElfb7c1dLOnam1B4zGHq9d8SiEzn4zKGQIoyEUvGsUDZD8Os7lLvdxhTQkBFc+PW\/FgBIxkCKACb6N7N3ZV76+Q19OCb4oc1rUWJo\/ZbIYM6rH5g7axcOnVmnCEqhIBofr48P1u4vRKayr99KDttjARDFIQhkQgFBS2IOijDFIWspyx9\/PbZ\/m5sI8w\/KAx0anJkKEbZTiP1\/FN3PuJWVZI5p11d43Ojluts\/mDBizpQLGLBpaMq2A5wGybnYW\/T7CREiKDabVXvAOfgOFgcBMGI2RAKq1JxTp1s3b6nggCUAAjiqQPz96q1IBy+eKp+dzFo1AEpqWAN\/NKwK5CQgVuCKIDA1713RHQqxcHLZxsLy0H1ECg5A1V3MFr6w2I3\/rRnOVrdf4R9S5LkXk6GDJvSpLW6DQhQRmJraX325LELV84s3FyMROZuS4QkVKeDBwdO2Q2bUXcr0WDklEa9R0eNqCP4yGjlb\/xiQYjOH\/0gdIrKk7kgKv3It+uIFpueU7sbJAKwHACbmh5tVZUXQr3Bh8o4UFFNiUW2LtxXxivHjg8v7u0Qs4hZGaWocdkk5xmkTIccJJtYPCpEZJyhJrWm5r3MwAcpY3OKe42kQCGiAhlB5Fe3tv7ZP\/kOlQYFoIwCLvyDjcPO7gYEDYg6OpvS0IqSRYgKAEAYEFVrC3\/n\/\/jW8OwMc0sEpIIOdBo7D5Yp7IDwgHHRwT\/+j2++9voGFEqKUIYeefXl6\/chbIPwyVxmFPREfFNP8AhlEn2yMEVJClSApOLQDgSQGQlu1qAYEz10NHxCoNSkcG6NnRqrVzve1paqVXGQ4\/wEVbchjHD2HNT2QATx8SLR5lCvURQAt9hTz9LHPw5f\/gqsLznj45XPvFT\/0zeV34ll3xDn+jAg6R2GMuyMnj5W32DewSEYcLmJB0iDHgigOAD1KukJFmOlocrIufn9O4tBrQEquwIpAbr2DH3oITe7tAFm9d3yqOdjKLMM86epAUPrb0BGsLm0PntCnL1yevneahDoekPf+yUABI22e3IuvLGQExWbJzTVHxBjoCRHqX70\/uHSll0qFC+fitoNsbyqOn4a6a47RJ0GIOHoGB3sgkRwCmBZ6vYSDJRoZ0sUy9iOYHoWAmLnB2pD7uSZ0cV3UfPhdTCTIUAidFXzlEvzRgA08AVuAzJEYIxleUpJaAD2vPqY6fGTxlaoiMLOwerawUED3EEANNWvCCkKIGyDCOKzJUl6MfE8cTNQ6qH70vs34e4GOGUgBUEL\/DppKbMS+kF\/cHPhwf1tsIsABJEHYRuCDkRtkgEqkeTAIebGepiLPEDK5iwkkjQiJOJJ7gizwNI37biySDUAmKoUwGRy5fq9nDOLz56f3FmthfUaeA1qduDMLB6fp1oLuE1BmIlwJeYWMCTgHAtF8CLYrEIQ8sGBgScud4482WyYw9nwaUzqgd5jwnpYvRcNnDvNGWvvHWgzd\/e9zHFgdBJW7mqKwsDk6PCxid0bi2GzlXPWQ+bV6HPW4UMr0fjPrMxUOtE09cIMkihu7IfFQ3MkKkUIEmhzeWMe1dnLp5YXNzttLy62AUCJRrt4rsQKBen7SfMDuwLEkAGzLLcweGy8fv1a2AxDQGTEZ8adM8eV58ujtqq3THa0fhA6TYgCGBwCXwBn4Ldps8GfuiCLBeAOBQoOGsD2lQi2sTJ+cdpy7EhYIDkoDsickYo7MujtHlEuljDF4Zr2HWDsCylw1yHGVCaEnuJhokl\/RIQ+CgkwsS5KAgqUHgQAKqROzZxBmn8jQ6NlNVR5lbkJaKiNzognAoCQSAqIOsBtAAIRogxBhEl0LpAiGUJQNyFEui8qAhCeZu0YIlMK9U0jmLC3LMKuHZ2IJDLGiwNY9JAjKhUPTRPMDCBmpdvMKrgDZ4611vb8g0O9UyCzyXKH58ZGj4\/d\/L0bIAKIfJABLa3jxXNYHKT798EuQ9RMpYFTEzJkODhBIqJb1+DBAybDwcfPte8s+NtViAIEIi3fAdQMobSjgTzqePW7i7NXzjil4tH6rmliZf9XGYH6gd5th+amKlMjO9fvRp6v+R1IyevWter6eKbzlFZ4SDmazfQF6stEp353yjx+RBnAGsLa8vakYKcvnXpw877naSGYAgAVCbG+VRga6gSCEEhjtokom2YBjFv22JUzra2t8KgWQ4xRrKxLRDYyyM+fhXqLqjU4OiKlgFkIDPSOPD5O1S0II+AF7HjWsQmxH+qgeQVh9U9urrbPPHlipDg2LAKf0AIUwNnMn\/\/k5KefbL33QIRh7vwDnSCMyBAIJBERKsaY7ViOVTk\/U1vbybbUHlr5Z0+ChM2uhBkHyQhinL5pFKlEMyBNoZ5CwWNjLdNB2cYUgtIj8xmIDF7WbAagIpRJ7Re\/WYZ2JZJYYiLqm\/zaFcfcc+cBBRAOFCd+4cVSOwARolAcgWvNsdK6XIWAyJm5ZyssDJVGnruw8ZXX1v79Nw1IkttoF449ckIh27y9Q5pFTwAigiCA4TJwhCCKfaoAnLvnT3rX1ynwQUbAGQ\/rw+fmOyub\/u6B3sVICQCGzCJnEPwjEO0MyUIBCBni1u2VyScujRTKteU1pbO9kqStkXHY3UXbHZkddUcHdz64LUIf9fKLbzFZYWdXtUndCfJp17xrTVrdTN10xA\/ddCDsTsxGyn+gbmboF17B3vpWFIanr55fvH4v8INkWiLb\/ujJea9aIyBU6TqM9bucMRg+PR1UDzpbe\/qJjC+jDBDVYU29cw1KBX58Dk\/MycOWaobgBxA0odWgwMOJCTqoAnC1dug8dk7sHQICkMBR17+7tqzk1f\/mqamz0839GkQGeXB4bTnsyMYbdygKcltXon\/HmA2jJ+xuxSpXzo5WEIH6WXfjBIZ+dDPzWqm4AiJQEhiLoVhJFZeE6XUFbpn4W1KEuqmgFDCZFsx6DJ2Zkptc5OybqD+\/0j09hRSvwN6uTPf8s7dbjoQsaHoH3\/6gXfNAhBAFIMNkkm96roowEesBWhWntldtvHMbVGTYZ8xmdnH+ysz+yqHfbseGeoYTw7Sxjq7NZ4bkSgvsEiIHzrFUYkC0vwkCwSpwmw2fngsPqv52FbVVTUWglAZFgH8EMkJmAylKdjcEACUV7d24P37+xPil8wd3l5RSwC1gNo6OwMAg7u0Pnpzjttq9dltFIUihPxwzHe6eNHnqFxWaowvkT0eyEPrl0fftr+ccGKmQP1OyJAEgqHexo92DKIzOPH5x5eZip+0BASEL2hFUKs7AgF9vEKM0RwoR0ALGirPjilR9eV3LtTDWWJNpCUpQEhq+uN3GkXF85nkGIT5YUnsBRARK0d4ujk9DxJRyIreIw0WqtZFbcHDgjBb8VrR+e\/fiJ88uv78qQg9kQCJovnWt+daHwFjcmEg7CpgmN5q8ALRccFpWqRy1PUaQz2XCh3Wa09DMtIRXZlmhRGIZuJDCJM07N\/DN1rSZ6G3tNcmMADNzyqzwJ0MPI9MSxLxgEvq30jGbRNmdxoXIkblKRdXDYK9GUajLSFSyZziGJg4REEi1dN4BcmA2cAu5XRooTpybePubD5RSQAIQcGgYwgj2d4hz6+IJGCzKkBMiRJFlAW0cqlACs5DjyLm5qNlobVdJhtpBi8iIMVISVZCUGGTYdjYg0y5+UlJG0f7C2sCJuYlnHzu4viBZAawSTs3A5vLg2WMkZHVhUUVRolvCTOwk9UlAw36K3J5LSQZ4Z3X\/7d4e6UcCZBHzSqf0RVdavtM6rK3fXpg\/f3p3fa922NBVz9H9dWd0JGi2SLG0X4EckBcnRkrT49VrdykygslE\/Y+pttshtACRGk36\/vfQAhwdss8eZ3Yh2j6Q+1Wqd9jcSVAuVQU7PSuvrUKxYHGLFYvBTmv95vbLf\/Hq4MTgUadJJgBdJjGx6cjeaJcoB0dhUvu7KGJxLDv0psRRl84SUxkTYnaRqFhWozKdD8qz3qjnTTYeKTKdeiTCTAQzZlXAOZBh9pYRJ+lRbN3K32Cz8NiH7CuxBIQhOjpEVYZ6rIIyIiX7fUJm4hIyiWEm5oTzuUszkvH1vZDGRmCvDRRByaW1Va03iJY37JefpaqiB8ukQmtgiDoE9gAyGDk1RaHXXNkAKWKkAKNEvwpK9xo1dIeIkIRW\/JsAabSUhMbqrhBs4Pnngs2jytRkOFTBYTfa2u0sLVMUQRKb16fo6bl3ZHM8CD76f0l3lLp03h\/hIEvSDR6iq8mQukDpSVvrqL5+9\/70hbOS2c1qDQCDVlg4cQw3t0goMIcAAWPuQGno9NzBtbsqjBIZPiaZUowDc4DZBADSN2k7ACSY3I1ktWNNjrOpITZSVj5Thw04NkcB8VJJjYzzs9NciWBxT0Vqf78oXWfqwtTR2lYK54wbaAiZmF4zXNLnsE4QkhBHbZtWZbobImYxa8mEO7sK8m9XmsFJGQMwEfXCMrCr5U2JSz\/RGCZHLULOEkg5HnP85mnBfSwrw4c+LPkbIXbNbIwdyahqY11LP4Zy4sRSiMYUnLaymGUXC8++evFw8bC1tAcooVzAizNWvaEcLr0I3IIqDlNblqeH2geDFPlW0ZHSxsrEwHwZlHd0c1lpPwTjRpGnHaoIQCzRMkHmtQNAJEmEgDZym+xSu4FDbfbpv\/OluSszB9uNH\/3rH9S37xhot9aHxK1QTDxD2KVyR8wfhj2i20SplUhVycr3QjMlTB\/wQdda7Q9FyM6ZSOmkLuk1Wxt37o1duii57VVrSihu8eL0ZHvnIPlsls2HT83X7m8IX3TbargNaBO3NVE\/nhOydLClJMhA7OzAzjYWC+zCRWt+VB36MD02cWp6+Ox8q1jc\/t4dKNo4abeHKqu3D658+uzyG7eCkJPmICgTUkL6TCbSrztlPAWZ\/BbEmNaeycDJzhX1U571UeRU0WlKRy7oIU8Lzl4l0ni7eEWlhzBl57E9TnnKWOoxpaSlBsgupRX0Pdszmy7mJ35IRBERSZG+LywDZDN5jyxhjZNOJtXfhRkP2kPTI1Onx979rfeodQheA2QLOlWw0b1wSh4Oi04kOyJa3Bn41FR4+XT4Tscemwz3guFzg4y3Dt+9F2PdIQ0CMKwunUzIjfLTbK9opBqMmM3BsaBYgYFxHJksnZife\/yYO+BOFyxXz58MGS2zfrt12P3ACtizBKl3EEgxbS2lh2TmE4h9ClrMZfRgP98TplG0RlJD2mSjIGz7+7cWxq5esmze3D9q3l+vHJvu1CNSCpRCkONXTvpHR8FRI2k4oU4atV0CBiKIlx+k6HhkqbVZVwvMJsHlwpYqt\/nU2MTFuU99\/vzEaGF\/1\/va+lHj\/apaOZLr7fud6OrffnryzPR6s4VSmDkhKWSIrkuKSMpY35wxxaHh1QOzdVHIMpAIhFyuX9Yci1lRfbIUMRu7kivmMZe6TZnJuVZBYQaqmHnvMJ+mFH95zDEHM\/GUGb9ptjXalYiW87JgwixO\/CUIyISu\/ZDpfMsYmAD5kxCBceQWMltJAhJAANwC7qJlz1+Z6YTRxt018hogPRibpNXFSEZie9caGy6fnI12a0EAtQ9WrM8+U+4EoydnS4Ujjo2Nt1cIOTCVBC0hA+OEYczIfm2bOzZzCsgRSDCLsUKBDwzhUFkWi5EXRp0AIouCTmd772i3Oeaww5Xd+uoaRR6YxyNvC8xRKDB9m\/u1YjDzvufe7WREkdxeEPK2id6GD\/XmZBNkI4DTKjk26lOSLKRACRH41VuLI5fOYbHQOvCssWmrhpHnoQiGzk+HgV9fPyAdD8ot4CVCjjICKQEi3ZWOPfdoYkNIB2YS2ha4BbAYMAcHBuxjs2x6whkemRkqjBctBChVLD5UYPMzJEBJ6+DAOzzwLrx0YWthX0gZS1LY6FOXjn3xpaM7G1vffhuiMO5eJOlCQPo5swvMdmJpJXbdpikjuomPnu7hBaY5SZTuXJCTHGZL2h40dHIqUZyyRlk5WbLiTbWJveqN7LaQafHlskGxa\/dFyMtiTTwPWoxx17UKRbIYShH3tro2f2SOM\/Xp50qnZtf+4Cf+5iaA0nmMpZHhRz535e3X11oRQqEArAC7e6AIgBGwqBGKO+tW0bKmZ3Fo7Ayoz\/\/9z5U4f+OHGz\/+8o8kFgAD5ICOjYyIBCOBjmWNjvDJUas4iDKUnof1lmo0pJCkUASS2g21c6SEMgMgHTtZHm6PTXz7H399cMBqbGy31lch0DBlhTlMMnXfL9KgtG5\/JmZdNV0jjKQGzMj7Hzqmz3d++q1PyGjZkgCRVHlJBihMUgR+9d7K8JXzxdH50B0qPTLRXFgvjDhUcmpL98kaAAzBAYPWkoKQoWZUAyBjHJFx4BbjAwOFsUFrZAQcLkMhOn4QRFEkpQAACFe34MGmh+6dgbEhFZx++sS9e\/vt9xflQQM4xyKPhq33H9Q+8cLJmz94sHPX13FZVsGe\/YVPDl49W3nkzPgTJ5kMLSNQyRDkESSiQsYsNnputrm5m+2GYLbSSPdAQsyJcLKxf5gRO2F359NUkNnOCvUWnPFf62rQYYIcyWsw8pNgzDb6ssFpGVIbYo7dCNl0NcMdHStf+BuvhmHISTEiJFLKfNn47qmR\/UV+8oRw7PFIbfzL\/wRgoV3EQuXY1ZMDw+UH722pSECxYqCptgUkwS7AzHGqDEVFB0vl8szUx185e3J+kANefGLqvfWLcDhKzSZ4LXW4D74PUQBCkZRRtRnuVCEMSSoiBmFAIBKyY0z4ZIAcGUMFBBbOjYh712u+rBFR2DKGKSUghhtgxuOqdz1zWUHoriby9Sc+VDqTGdYnGo98lBzCwy+GXYdm1x\/n9d7xm0wKSCohjh7sFh+fLh6bvnhprnN1fr0a7L73QA0dA1aE1iEGDQbEygVnoGgPVgpFR1\/RQJJLpJT0QiGEjDqhX9+KvEC02jIQQGRkU4wZEoxd8Dz\/x7\/9vXf+eMD3QjgxSn6NDj1gFtjO6n61cXHozNPzu\/e3yXJBSaXA2zkaOh827m1sfuU1EpHGxqNSZokwrR1lxC2rUDz2i0VSRFmAZ2YFxjTr3IUwOXUI+iCUKONX6I3sjNcWdqWRpkFPfUFMOQ9EZuCSVUdQV1e1qyWTYhoNKzXb+kVQpPyGt\/anH3oND6UxTyWp7qlDiHFnaHj6iyWYGvaXtxGBmA12sTA0\/PynL+7drTY2DqHTgYIDu6sQtkB4UCjgzBRVBiCUIHz0oTBBFcdyAIjIa0Xh6n60tU2He9Q4Aq9hgEOadaRiyzKzwClpmFjmXh2bwvQeVXRgbobWVqDRNLwSFYGIQIaoMg6JfAR9XgSaL0MeLlXL9E3NJ7HS1xv7Gcq6UBfY78Le1c5OTmfzvGB2jqF9k6RUsHrw4l946YVPnvIU\/OEfLx\/4yGoHrOm40YBFIY8CIiWJUIqo0\/ZrjU6tLTqeVggTQjZPNx3CkQAVURInSIKUEFGnVa8CEBxu2RePK4Gy5gOD4NC798ba4y+dvv39e0drDUKgSKz++28d\/PhDb2svOKglDdJEcJgimS3XKlfa1cuUlZXqzniS7Z7epeNJeG6tIPYpa7KoNuy6miHmpEyYs5NkTf2YP5WhRwSFPY9FX9Ma5kWvGGvPUi27thdb3OahaC5sNg8bICIQIUiZweDHjSvGkRfqS1vWUKmzvAXANJ391OXZY2dGf\/N\/\/4lo1cFh0D4E0QEZQrECA6N0UIetI14olOdG+ZAtDne\/8xUpPnfOP2y\/986m0252Dg7Ia4EIYneFSrZ7AwUtDsLwMdq6iUqm9PAUlqDALcKxY7C\/B4cHiMmbTobSQhK7s9Cp\/8QuN1DvevcIMqT2nF6G+gm4H+rufOjMCPt8M9ibcRNbrEkBSUQYqTgSwbFwbMzhXiPc3ZHbG1GrDkGLooCiSO9ACLGqA7KIgH6HNLLUgIscSKFSJHxASx+S4vYyv3QaVYeaAVnuvR8vP\/7CyYsvnXpzbZ2QEUVRrVY7OtSa6QRtHMsJtJuWg3IAOUVB1AncwTLaDkUWMAZSe2Ew\/+ND0jQxG0UfqFYXFyNz2+96xTMhEXo+SDnfbWYfzqZAdck3khgyYpj6RRCzSoCEaZHOSOPlB0igg\/i4brQUhyoWMFJkBJ8iNLH1pmFkNgVCRlwE22GwgwQMuAOM26595aUTD5YPd5c2QQbALWjXgHEYGgdmQ73GSgVndqw0MQKeV792R\/LizY2ZtWqndWtNerWxk0MDU5XWUlWZW71+hbRnwDiMqH0IUqAMUognQmLIQteB+eN6BRqzQTJcTbSvqQ+Leh0MidUEHy4ZpYTvjQa1nVUWWz3D\/ofIB3vmgtnIy7wkADNMDMr3dSgx0apO\/fqPbg9PDahI3v2jd7zbS9SuglfHyKPIBxVhLMOnWNnYtcqzXQvzn2QsTuY\/9HYo0XD4mA1E4s4Sv3BCiZD8llfl139y\/9xLZ67\/6QetPd\/E+iYUYCUxq9s0j5MFyEBJRayx17z88vnC6JAXtE3ODGmfkf4GelKNspqTnNIIs3RO7MdBo65rPuTZ34iZfixmQgXpI3Q8afgBpQHQWYFqSjpK+qExiTzxTCCjkdnhxn4r9EKQkcZvo5SkFKY\/K6KWy+gnm9vAHGAMkZ1+7Nj8lenf\/413ZRCxyQk63IdimYoD2G5a5Nlzo87xGX\/joHbjnooEIALzrMJQZ3stPNyAqH1wbXPwxNTIhfna7QUpM7hxopwBL6YGpy84MgBE28EL52h1gw4P9V6f2LkMDY2IEospdOEgc3znLAYGsJsxh1147VQ9RQTA0Z3L9zp7y87euQV2YzAw+yUQuotbTPbDuJJBUrSzvH\/z9YUPvnPtYHGZmnvgHUHYRuGh0oV4nPYKCkkhEMaDp3gIq2JuHGVYShnzGkFG+xLz56SiWgOPT0AgKVT1vfapF06Xh8qbt7ZAaVmz8dchZamYGRCQZhxw1ykVrn726saNlcZWFZREJZI0sUxqpG4sMZNKi8lYJX6akSX93uSfmHyUhgBnwLgmIBEYMP3XWCz+Tj4biz3xacp3+p9M\/475a2T+AqPkN7H7W8L0G2OUpik6aBfBLlQmR5\/7y5+89u2bW7eXIdSWeQFKolJJ\/h6amJP4xzfkyIJTKv3Ur7x4uO+\/8901QokWACgol61ObfjYwPDZGW+32llYjqqHJExqHZJypobC+8vk1SHySIRB9QhlOHbmWHBUk0LGhXpme0JEu4DCjx9QrWVn6Lhw9hysrlLtUNeu8WMGKf8g08vMNsEzTcf4uO\/xCvZspIm4N1ONIACClZ2uY16XC32UqXlWysOaN\/mrPWEmE8SkZ\/tAJEVQa1URgJSM3TdRYvTW\/aikbqa8CiVRHWTmZbq9xAxhSTML47oNlIngBVLgCbq\/wq6cp13RqXmLP1h8\/meu3vvB4uHaFsgQMAIQmZ0049EFIiVRcZICIn9\/aXO\/3jn9qatb1x5IGcdrSQlKUGqlyyWD0UfW9tnuP\/YMPPKj4O5bet7pmc8+w774xbwy52FlPqYPUGzYNaRGckrolk6\/cMkeKa\/d2TQ6FZl0ZTIaLmO\/Stg0DLiFlnXuhfODZyb\/6J+\/K5HB1ARJHy1ZGEB3bDzY2Kvfvi8D33R19E7EbGY5tgi82oHOI9FforPTUp3W9KXTh8ubrXrTZI0CoRbxEJCUGGtWzc5YKOK5M7S2TvU6xC2cRE\/RrfAkyAKzsZc5kZ1M9GRRPez6lNzTORZm+xvqH4IT6XOf+ajCNdPcTk9DvSAlKF29aBhJiDLKohaywHB4uJI\/31PIlHyZFAaEHl6fAmp08MQ0kNXe65x4ZHby+OjytU0VhSZkM44fzpzjZtysfb1oO3T1HBTsS8+dDRt+dXmXEmp6st+bmi3\/yxwmDE0vN8nfMn8Bufl9NPWepfmWce\/X\/NKzmvQzIEfO0XxU9gvpT24Bt+JPwpKPQmZlP7P+WnGOKgf9nXAr\/k5s4DZwB6wCOCUsVuYeOf3yX\/3U\/Vs79xfr6ugQQtPQz55C8XGqrRIcmYW2C265PDb+6q99bPHO4d2lNoyWaXIUR1weNNXWVufuUnhUoyiAhEWkdYKMl6dHmIqCgwNjtoxRGsIPw1Zn6sy8kirseBkJJyIgWC4ogRrfDAjFIp44STu7+h6oKx3MXsfz3u54VGCeB8j3xvr4aLoa0zltf\/ovcWSzvhP2zhq6gymwfxsGe26nXcjm7sWhNwwJCogUMImxuibXh4QcrLKPKJ2S2GaEZBBHSbpsRqOQbm2xdUj\/hmLQ6dD9ZX7hbLum3v76nc\/+2nOzF2fX3m8Tj3SWlRk\/JFIGjMH1lstKA85zV2TBufeb3x31XnrhL72sovDBD24KzzMIIyVif3DPrZlyBHnqgZRm0rXzQsJsgdE1ssUU1Is5tFl+18KHzapyv530wExv06S4GaMZcActlxUKM1dOfepvvFrfqb\/7G9+QPmGxDJ0GQQCMp99tJuc43hFssIu8NPTE5x9zKsXbd3fKv3DVHinWvnNXfLAiFhfJO4IoQQoYnIwp5JQYPjFVW1jW4mxMnJlAAMxvtTfuLM+cPV5wsLq5K3VnzjgqELhNRIgKbJsdn6eDfaoeIGj+b6+YIm1dZlV+0LUC6b+gp4nd2OReV6YFfRn48dWm58sCfgSxJquf6VqFMco0LhwloEJiqSc9noenVmXK3aMzUI6uyXNSX8U1dCIJ65IJGZwUACDKiIBBq6XuLcOli+trRws3tp\/\/5WcP1g7auxGoiEBhtjrRxw5yYAyL5cqnnwHOm3\/6GqH15r\/7sfLDF3\/t8zMXTtz8+jv1rarw2iQioDiPBXLM8zztP5dXkRFkdlU0WTkbdnVbcnpUo1PL8mfR9I0pl9ubnz\/mPqmJcU7McowB4+bGyCzLLVTGBs+\/eP6JP\/fCzoO97\/+zb7R3qyAC4BYWB1AJkmGco0I6dDXOWLWAOWQV0ClPnpz+2OcvfvOb60dSVUZc6fvq7eu0tgZBG7QGNeuciaEynIPyw85ePD3SJpI4uoVIRp7cvL04c+HE1KWh3cV1KRSRQBkBQ+A2AIBl4ekTan2Vaoc6iQTj5mfXaZPSsjMdeTIZIIB95uaZN5HoI8cLlFe1IeLwM9Btisso5XvOWOqyp2bvL9ini0O5bg0m7fWkeQ7pPh5Pjql76A\/pgsAk0Rl6BQmQfHuYtiOTKTMY4TUwRowDd4C7YJdheJxfvTyI9s\/9rRdvfOPW+195W3XqJupVaSU+A9tBp8BcBwCKn3+JvKjznbcobAMpYNwuFo8\/deGlv\/zx0miltX20sbhb29hnJBljoOPblWIm7Y0kKb2Da04UAJIOw9GBoVqiGkdXKC03yb1hcXiZ6QODIiKlA0Q1\/06BUrprQ7GPH5ElSza+KWnFHyJDZvr6yYFDyfhZaaEStxAZAhZHB4+dmxo9NoKMvf2Vd299+4POwRFEHT2WQLsIg5MAyrIQAKNmC6IQVKTVTsBsslxwy4WxiS\/93VcrM8O\/9xsftqVi\/hHUq3L7EGrboCKQgYFuUDKFIUAE7hRHB0vTM4d3HpAyR2UaOq0ZTciBW8x2huZnhuZm1z+4L0MfRIiOQ8JHp4AXL9DCXaodauYqZuJys1CPRHSZ4OtjeWDXJIC61lbP8RirCnN9Gur6p9UPHJLrx+QWdjp\/oMx3RAj9Insw2wuA3qg+zHeRksEJ9lPKZVQn2Ns6oO49gLKyLOoSn2s3OkQADNCHVl3dX2k8\/fSbP157\/otXdxa2N68FZCiDCEDALVasTH3q2ZEXrgZBdPDOzeaP3ie\/ZTIiESMZLr9+Y29hY\/bK6WOXj42fmznx5KlCwSkMlTr7zUBKAWQX7OLIQLTXFFIqJXXAC0uvrcbNpIAE4uDkoApV1PSVksoIwLTPHLXXQ1DM3Sg4UC5Ghy0TFaRfBGVWFzBmjQ34hy0II33N5pkZSEyzRYtzPjXU2W9CJFhMbdCqX6kAGNoWtyYHg4OG3wqP1vbv\/ujOxq21o8195Xcg8kFf3vRuIsT0S49f+NknZShv\/D9\/Wr+3AoJMZKfloF1Ep3DxhdMzp0b+4B\/\/pH0gyKvK\/U0IGsA5kADRgawRKX1qGaiwND7oV\/dJRQbazWxtR4qdEyaPSElV29wPQyo\/frm9sCq9iCxEZeP8KXqwSq1WWo9QkprU5YWnLFsCU\/wZPbyKT2ZSub4OPtRuRHnuKPbqQrG\/Kzi52mHm6KW+oETKlpI98T7pBBPhYQapzM6U2hep7wUVu41Xugjq8h\/rF16ZIlshYASSQeTBYVXdeXB\/5Mm5hnzsF5463DzwDhSRAuETKUBuDw\/O\/vSLhfFxX4rqN14jr40iIJ1HiwBKkhKt3XDxsL74+i27XHEK9uCZqdnPPnn3X3+foggQhs\/Pnvm55z785z8U7ToEHZBRrB8infdEaGkgJ7OtF3\/tMwcrjYUf3FFhGyLf5PvEu6IWDREi2tbQpfmx564u\/+6PVNAGKdDESykAAsu2BioX\/+rHl7\/8Zmt9z8C2uyQBjKPt2iMjl\/\/WK4u\/\/UZ780CfQiYzQx+knDsD5WO\/9onNf\/eTztZh1GyB3yIZkIx0GjsoCUrHtSuGYv7Fs0OXTyhF40+ea9xbJcYRlA7SwUJ5+uKJ5770+Btfu71dC6hRhcMtiNoQ+eBHUBmDoKmBIwiZnDZkgIxZfOT4zNqP3wUVYIruTtpglqGqI0fOiaCzd8AlFc+dc47NOuODzYWVzvW71G7HeRWUhW72MekhZBGsaThF38tftvykj1S\/UK\/9N\/UTYs+nxT4tgawngzBTafYuYMzpPhLjE3Udj73feuoxwJxqv8u78dABZrYvlauvDQLHMBoBEJQEiFAEFHVgZyt6x3p3wP30J+ce\/Zmn3vny20oRkQIZAoAKQ++oVZwYszxPtTsgIxBRPBg0\/DLSiEvuhFEQcs5G3I7Xae\/vkwKw3EJHyihqH+yLRtxFJMIkx4oxk9NkuaxYQaFku9Pe31WBBh8ap5V5JfQ8kHGwHHduLPL8zv6e8pqgpBlCaoCK5VjBqIgiv17r7O\/p+WcqrzFf1wLbtZWUYRjUa+39PRAhKAkmDpgAECxHiEgK6TU74dERhS2IfJBam5ajBpOSFPmdgxoBUST8g4Zm52h9DDql8vjYi7\/49P5u+8P3tmV9H\/ZWQHogQlN\/ejUoDEL7ADJxsYk4zh2otDd3RKdj+G6ZdFddRqAKARlyV\/eBidsissK2PPEzL7ujg4cz46vvXEfUehroZQNlNdbUw9rRSbm5ZJEufsifoS7D3jB1zJajXTBRwt7xVR+9cELhfogJOIHBm4ZJ11USM2bTrEcm87ew58qXlgapb7ELOpX9KOzC5mBu7gcKTI5iCFEHggasr9XfHbo+7n7sk5cOHtQevH7HjCsAokZ75be+1nnpkdbieuvBOpn8vRg6qIExSoGUxCXwCC2HkXIISCmSEpmFJFEI8BsQtCDyzemReEz1cJxzAAIq24AYBeS3INT83ywdHEyX0nKAMSSiMKSgHS\/sDKhbSdB0MBmB0EWjzGuNkZgFJCAs21Jh6IFZ8xnyHzJQRaCyi8CkgLCDYRtESDKlbppKDQlIURQufeXHou0FTX\/37buEzGgGrSIrFJ\/46Ucqc8N\/\/NsfhJ0m7K5A1ALhmy4oEXgNGJoEuwxRK8GC6a0CmGUPVNrVGpnNKAeX17reGI+giCSijYhYKtoTIyoQTCrmBQikTPBG\/MhhP4Ag9bUtYLfZrNuf23VLJOjmp0HfFWvwFt1sPsLeW1ffWQh2qa96v3NKIlJyiJQu\/37OupMhBHSXoqlQLp389WiQkfIFKuXl6Zid72j2lkIQAYgAmE\/37i0rNle8+tIvP7G3tN3c8IgkyAgUeSvrq0vLQJJEBFK3BLLptrHZXQ9aEEEpTojASEbEQiUCEpGOFtImSTJjjPh7YgzIAi5ARUgKRQAyBClAivgxVYmBj7RKhgikVCICRaCUKQ61a0yXZ3q1KBWLgaJUjG6CZRRwC0iSDq4QEURhTFvU11ALmAVKcSIQAYkAhDBWibiiM2Rr\/fNL4e3sLfzON4FxYhYAQ8sBuwRO4eQL5898\/PxPvrd6tLRJ60sgPJAhmJDteHzUqsLgNBz5QCKm5SMgQ87Y9Hj7ziJlIuAJTFspxYvoUsIp8pFRPjWhRkaD5bW1f\/77hfmJ5od3KN6niJKsqC5MC2HXksmMCh+O8HrICuy1wVD\/VWL1zN\/6fpWHjea7bBXU07ylrKUns2wy\/iZEyBOJKXvaYu\/ph10TesoxF3IflROgGFpM3hita0IlQATAXAg6Ynnp3a9alV++\/NyvvPijX\/9+cIQGFy8iczAqAUqmga3Z+D09AtE8OClVEtAnBWj2kZKgyIy5tGVb31YRgZg5vmSklFQJYocUSAkkkumdueyapB2BiT9eLzNQoPkRSoIUKma8m1BkSsKSEDkH4rqSlHpDUQSkUBoYKSED4DpVkCmJ8ZEVQ8ApA2aPX2wlIQpIa2I4kOWSXWDFobHTUx\/\/lRduLR4tf+tt2lyFoAlREG8KyixpRJAByg6NzkB1C0DGfArk5QKfGJHvBcnENr4rck0fMnI823JGRipXzgf1tr+9Gy0sg1JiS3Vu69RaH2VEht\/Xl5LUXT32mVH3DN8A+yDL+yxXeug5ZRmHTF7e1I2ceWilS93phpmeTX4+0fUj54w+2bUdVyBdXVeMBzSImYsf9TZpMHMjNXUCpglnmJMLmfpfKQBEJUkDbJqivcjf+Kr1mV99+slfePrd339XNIhUBBpHq5SpnbrepKxfSNdg3MY82izl9sYemfiyB8BYnOFO5nqZvAtaVKVUBlzLzNpWAklypttjMgn91FWZPgB1SChmGIfx9s9AMeDm7BakQb3x5dYk\/mVmPECYxqEmOLl83Bsl2XJozFjcRrswODny03\/9xeaRf\/t3fyw318BvQqRbU7EgJo2OIWoewugc2CUQHQAEzgHRrrj+wooyb6pRuiLjpmRFjrZdmZuqHJ\/xdqqND25FjWYK7dHzQKWQJJnhh8qoQQgpJ3noiXtIiE7ULXboZ7CGLhZwcvfp\/SgTa01Wd6HZfx7fg9x6GJQNHy6Xy6\/qLo1GPJ\/OzfcwjXqPsVyZYWPSgKFuPV3mJMQ4aTb30qQlcqw5VZT4JyybartHN+x3fzD96VcvhHX\/wz95V0Y+cGGe5jjtFru3QY0GdsAugFMizpWe2sWMDJ60JnUXXmXKKnO4mPGJQbulTugM9N64us0tFEHpcCggiZBRkMSiGYbxga+PMA0dznbJCUCRkjpKlDKC9VhpiCYnFZkRi+t3Ju439+Q0GXGfA1YR7GJhaOBjf\/Fxe8j54f\/67fbyBghfOy1AGdthBhYQQ1\/bdZiYh90Hpv9EipXccOfQEPf0bwLXyxwtXjhxzB4oYatdfe9m1GyRUaKp9Naj17leh6RSCzw9xJOeYSBkXq6HlKPYNQDsrVO7MrCzvlETEtqDtuz9\/Ji948FD8KYI\/ZuXfdnqaWkYgzep64jDHtlqYheMXyhEfR3PLM6ssy65sHVlQXdPV\/U6NFIPAlDALdU4XHlj5cOK9dQXLzcPG4s\/upXMZ5BUzH9VueoBEdBCu8SHxgYvzFfOTLX36iR9IAXEzEOsL3KZpiJk\/PLJpIszYMgyVW4qo828bAZtpPShqp+teA6TfBRLP223Fw4NwU9X0ZnvitK5PqGpH5AxcEpoF0CEpAQCIah8OmqCouP6AAR3wB4ceubnn5y6MPHNX\/\/hwf0NitompsaIQjXkPwMm0vtO6EGnAeVRCH1gnDvMGhvvLG0DIqCNBt2rrKLtTo\/zSll4fuvGPQp8IH2MK+h9x5PLJOXAu13zCezW21N2++5PqegdrvftpjwEJJpNZaK+q5B6NPZ9vizif4kXOMfgwJyZI+6cYGbqh5TxkuqnABNDTRLeZHw33MCzzbYd80IpqQNjlChBwrrNE7AUKQUgARUAgbBQeGp15f1vkWPD83\/pmaAdrX+wYlKLdFmoRPzKZhhQjKHlHHv12WO\/+HEvlA\/+j\/9AUpq1wOzYyaL61S4EacK7dsLqIlMbmlVX5ZOOeoz8RUHO8ZyehAryAKIMey12JRIQMaScgC472SFVGCkVRwYnrp719lsi9FFGhAqBgWGiIxqbL0PkxCzgLjhlq1x56guPXXr5zPf+9Y83371DQQPCDqg4FY+6aAJEKWROQvsQZi9CswZKOsenoDAEvAQQGUhpueCMDhbGB\/3d\/dbNeyoMTPpSQhw21+nsAkuUMZDP\/KE+sukeYsVHSG372eupe8PvPYkycjer63DIfa6ka0Lw0Wu1z6mI+LAPwT6\/n3HlGO82ZsZ9iXqY5fwBmBgCLF2lpJuxUnFStIrjFvRxQaBUbFPUs3JMEK5maSGADCBsAzKxLt7+TsGx3S\/+rVe+9k\/56rVFUBEoZRpmSmjaZa5BizhweobZjsNV+dRc\/cO7BAjIMcl7oR6ZRU7+brYbTCAM+VZ3vhBJDISpxpx6L6tEvc9P5lpM8ddkuS60kYiQXXSu\/NWfGpqfefRXx6Mg2PjqPhmQh0LgMUhMW42spBpnbuHJn7769Ocvff933nzwxl0KmxA0tNsQlSClEICIIbNIz\/1ylBsCAGjWYOokto7c2WPe5iENzqLftBwxeukEMFZfXD9cWafIo0TqnYa0ZCKHu11IBPleJuZ1Hd1NToKHX71yMtE\/Y0zf52hL3V5Wz8ruHayZoQL1dlER6KG6nC6ffmb0gNmyNjueT3LXMWNDYaTb92hsNWAMNXZq5+E2tx1mcWSxK1opGYYqiuJ7jjTdSBVpv1+Sb9zd6dIwfBHXLMiieu3t+5F92nr1v3vhq\/+nt30zNEe0wK77NgADQIr8nR+9Vzw2FjY6R2\/dJr1iiYCIhAIZGX5pLAPtDSrAuALMtrCo9+03vUFmjllmAUaAKm6LZK3buZFSXhxCMU81NpvHSYXpoNAtFMZHCFDatlUumMEdIgCjrE2WMeAW2AVwB6zS4KOfe\/SJLzzyk\/\/49p0fXFNeDcIWCD\/Oe4sR5gBxSZtAa7i5+FlFQMuenD714qXBycr1\/bvW\/JA7W8C9ncbKZrBfpdAHGVFsQEUigpiBkiJAKF9wUOYOkghGCPHhSwuzML3eg4qw6+aH6RGa32kzG33cC02+iPXRCBmCbAsDu0b92HspTSxF+YWdWclJnkaX87fLpx\/nFSeiJJ6Y2RzkDlqOVS6Nn50dOzM9fHyiPFRhjiWFFJFEzizLYkHYrrWP1vb3HuwerOxKP9DJuHFXwOzHOVuD6YuYCRgyRlYBRMu7OPbaB5vBCLz6N1\/+\/m\/wtetLhAygHeNvMbZIIRKRjA7euta4v0oSwloLRASAwG2pZKQ0wkSfXSw2amDPC4sqMSFnFfNJrUNxA5ZZuoYGywFmAwbUcxXPVFUYd6gQ05aDVqQyru9yestLJzgKSEXN9tJ\/fmPqY1fae4e7r11DreTUZTASmfGAFa\/Ail0Zfuyzjz7\/84+99uV3bnzrXdnSwATfjHaUIspfUDGJqSJgNjCbuAO2y4oDLz498+m\/\/FRENDQ28Mb3FoP7S3J9hcKmTlbErNswvv\/3o1bH5r2cRYmyKbX40Udcn5Mu5yTErPXuITrp\/GmHmVv+R4CeMDca6cZkdLU5u0vLPuqZfj9tTysWk3Z4vAKNCc0BywW7iHaRu4Xx09PHnjg7eHyyUHTb1dbB4s767j2v0QlanowEt1mhUqiMDg7Ojs6cmz39zAXPC3fub6+9\/6C+uq0CT4\/mSXLtcDCILozXkp4KoASlUAo62FV\/8p12I3yrPFz60qUv\/I2X\/\/S3nKV37sTyeIYSiUQqclcCAhlu+5QqyDiQsAoWc13uuNILTGhFHugSk3IQACQRJQkZmN2LKYPG0HU4gsXRconbwDgoFvdvcoSG5DPnYxIwCWnhWvAd73oYO81JCfI6q19\/ff07b1EUKKFAKjPhwFiawyzkDtgFckpOZeCxV69+7M8\/8drvv3\/z2x\/I1hEETYh8UiKOBFbxKIO61VFmOq8QlJannzg3WijbFYDjo8UfPtgRh3sgApAi9u9LNBCG7IWYsoOHPo3KvDQMoW8k2cMFLvgRg4mH\/sHDxvt6rVi9yhvIWwIx\/X4x07aN5dXpYJC66RM5OXU+5Af7NT\/zTBpATnG1CZaLTomXBibOzZ976cr4qZmte1vXvvx6c2NfhBHJCKQgzdsiw9Pa1a4Wp+BUKmPnjp392Pn5\/\/bVjesrq6\/frq\/uqqBjjkRj5weTbJF0dglMPH19n96vg1P2RsLvf7X8yheOf+KvPc9stfTGXcksCD1CBImghLFbpbBbQGDEGDAsDJfnf+5j7tnjkx9\/YvNrP0n7L+mLEvd1TQdB+yrjDrC5epIG1WDcmuJFd\/D07ODUiDtc9tq17LQAs6swZ\/BNrt66+ckRuenJIwLnyK2kVgcgVFKjXKSPueIq3Qs4cgfsIhQGC0NDT37+kSc\/\/8hrX373va+\/Kzv6DAxIRkjm2pbcyjAH7MeUahPnN0ohrr+2NHZyzEW8+fqqCtooA1IRxqqXzNNEsUKf8h68Prhl7Eqk7s+DgS5KWfegLZ\/3mR1V4Z91N8y3UTFdhP0VNRkOXrrTpEq0rMGKsqg9QuoS13QLR\/MXkzgjJ7b766ugOQAdsAvoFEoT44987pnjT5xd+NGdd37vjeCoRpFP2kwUm18yibg6+J4r4fl+a\/OouvXezcrU+OUvPfux\/\/6Ld797c+WH16JmEyIfzBLiwKz4XVPaggxuERp7wDjYRQCAetVjxe\/+fvDsT5\/45K+9XBos3f7ebdGsmxmJZJQYapLATdRtGOUMlgvHptBxh6+c2frGa7pVixl7pFY5YxwbJlWcOaGxTvEYBs3fRH1uj189fvEvfcp2Hf9Lz9\/5F3+gDBwtjVhLQhApn3VHhkDFkXFinAAiAuIcuAMsAIz0CNy0gpUAkPGrylKZBOPALWQ22EVwy8Wx8U\/90jPnXzjx7d9+49a33lN+XcekGddfugIhE19FmSxR1FFN5rlRkoL29e9fe3BtBRlrHbVU5wgiH1SUefX6NBzxYe196IdV\/ujeCXatXugKi8zQRTF7V+hrGvyISfzDylHTNaCHfoeUP5i72zndX64nELjHu5F7PoBZwGywXHBKrFiZOH\/82b\/y6er97T\/9n\/+guVvVhFnSEyeSqFTahTc3YIM2I6Gbflwxq7nWfvtf7g4en7n8516ae+xn3\/u3329t7RGzjIuUmPawgYrAdqlQhtoWaDyJDCFEUAhRGAUTb38fOoAv\/5Xnx6dHfvzlt8I6Qsi1sQCVICW1ntv8qIoAVWtt9\/5vfXPo8unNP\/y2CjxMki0S9o5Zb4ZlJgAlmcDGxG4fG3NjAQNRaXLELbiEOHTxOC8VVKeRy2GLzec95q\/Y3KzHCcxCxiVZUKyAXSQtowXjGI4zqTCjuY2BMeaOUEKnWJoYefXXPj53cvyrv\/7a\/TdvqaCptX7JtQ0z01pKS45sEYSpyJcUyQjCpiLR9BtACmRg9L3aZxhT7BNNfspszBTg0I1jwgQZbYYk1HfI0NVMpi5hZ08oPX3EJ+g7VEz3RCRIQU99z0zMVyDYLUnB7MgPMx30nnkEpWjojNEeYt1zPGUyIkB9BtpFvQLPvvToc7\/48evfuHbnm+94B1UIOiA8MPrmKKb9CEy1XclsTcbpn0L\/k2QU1Fr7d7fsSunKn3uxXe20awEiNz+Akqh8sF1wK9A+ApIpHZAAVAQqABHQi5cOzp9q3K8\/9\/zs6LHRndVa5BtvXmbwS9nBJwnZXto4fPtGuH8AIoyFbxmkBWPIOFo22AUslE8+f6l52N5f2kUpYidHmm1kylTLUYRjl04SyQdf\/nHt1oM4\/Vsll0ZWKI6\/dLV2bSWqNXQvKn5PmElQ4gWrXDnzX3167LHzA9PjtcWtqN4wvg2jGoxDb5J5LLPAcsBy0S6CU8HSyPSF45\/9O5+kkvOtf\/nj9Rsr5DcwbBt7hOZHapt8HtgJGSUhdqMWU3EsqMQCEsTUmZzOIaOgSspN7HLJUdYCkNmKHtaNhI\/Kr4beJYi9RWzfSR51Oc7Nf3MszPakTmtelwAAV\/5JREFU82I6osuWxXHR2PPzYI6PEm+XeeEMYnbp6mFgip\/IQso4cAecArhlVqpc+uwzl155\/O0vv7723oLstCDyQAQo4uWnxbhmycUTgyTpmghNvLiKuegKgEQoj5arIsRHfv6ZwFONnRopQCSUAbglcCrk1YEglmhxQAZWwQxIbBeLZQh49cbe+odrV14+ee6ZU42DoFH1475C79YIWpZNoWZyGrhlTLXPCE0sB5yiPTZ65pOPBQL3bm2o0AMZJr6BjD0EASCstXbfvr35vferH96jwIs5cXHKAndYoTT+wuXajZWo1kDzp+nsB5gFdqE0N3Pmr\/yUWy4VJoeaKzvNxRWzZ6V7teGdGjycVYDCALgVdMq8VL78yuWf\/tXnd4X1gy+\/f\/DebRU0IWhC5KVbJCkEBd0rMJnvYzbfDmNQKiRlqoo302QcRLn6MMt8wVywd\/oYd3NhehZVjmqBea\/8Q\/srfWVm2LUssidoz0De\/AtHd7a\/rbfbzpTPKce+izD3reTNQ5hZjJgKsgEzrFu90dpgu+CUsTh44aeeOfPS1bd\/98fbt+6T34rfWoFKYjY8Nf78lAR3UhePk3K1OhEpqm\/Vgrp\/9rOP+fVWe79BVhFtG6wCeHUEhlYBzK8iAAMZQtiGsA0ygr0aLWxSu90KcfPG5szpsaufuaCAH+60pMxkhkK++0DxXpCJ0shEO6A26bJi+fIvfWr2lavlU1PBYbO+sALCLC3s9ZgpGTVb4VGNQl9DU+JsYwTkYLu8ODD14uXajeXwqGGMsynVgiHjyB1eKk89fdEZGujUWzvfedfb2gEl4m54wg7mwCxiNnIX3ApWxnFoamBi5JkvPfrUF67cv7b9gy9fb1cbtLdmbPIy1OWJAQp2hR8CpiMoyBKHORoson4SspRkhMzdOEYw625WJn0oQ9fM2ta6DDf4EEtQP+98XpP8UbDPPi7gfLxq\/7ZLqh3NOuUf4uzoVuY8XKSGvX+p5+ePg2\/THwwpoXpaLrql2SfOnv\/EY+\/+hzd2765Q0MLQ048RkSSD09bPbnyQ5iqKDMMjG8pIBCRQEoUKSG68fk0xOvczz3U81hg9A0rQ69+EwrDZa8MWRG2QEpT2BzHgDkQ+Uo0iD\/xDiqYO\/PFv\/PpPHvvsped\/4bGp85PvfuXa0dquYjaEHRR6miwA4l2cVPp9UJaclgoVOLdGzswS46zIhi\/N4Z\/kLig5NYxK1OSpTCQWoiEgQ2axQskpFJhjQwx1yvC0AEiBDP396q1\/+dWJx08fLawfXbsLSsUPMcPE18s4MQe4TVYBnQovDx1\/4vQrP3OODznf\/70P7r++KMMQZADIUYQkwsyZnILf48YFppCODI9cU8CJcWCctBwq15GCuEukzGxQKSIBJAFj2wdpR2\/cJU2JcakmJvMKInQjYbJGiq6XvFtl2EeClBG3dLdvHnYCxxkX+TthahTqWz1Tl30B+9nec3l4SYpmho2f3jSSg0uPiw3a2QW3XJ6bfuGvf+HWN26uv3eXgiZEHqrIuHVMTcyQW3pqz2wXbQetAloF5A5YLuMO6tkGMzxcs22ax4ESmmVrt10YGjz\/sy9sNgpKKFhfBL8FYRuCJsggZyBKzliSQAJAQejDQEki276+vr1UvfyxM1c+daHVFM1DXyo9wk664tinqDCLITkJGTKukLtjw5Vj40G9vfLVt5tLGzqdK+nlxPkQ2as1ZnJi4vsbt5A7488\/NvFTz1GpVH\/\/LoUBKJFJ8kheDxlU64c37nsbO6BJjYmuliEgJ70t2iV0SuiWS+NjT3z+0S\/+148eNsQ3\/p\/31j94oNp18JsQeGC72KmDDIEEKBXfBSCNtDCTEdOIwvQCovtwjr5tgu2CXdRdcbSLaLlguWi5aDmo\/47RTlkpbTlTasXVflJmaFksYrYB2NOr6ne09RkS9lefddnFU0UK5ArwfqdVt2IG8\/fKnuES9g+o7PJPUG87FvNyGMzdydPeNwdmg12wK5Wn\/\/JnVt9ZXn3jJmkYRBz+agCYjAOzGGOOU6wMDEzOjM7Ojo6ODFRKBQASgjp+cHBQX1\/bre7XOp4nokAJQVKkAAgTcx9S5C9\/+8OhycHzo4XbX\/8eBV5qXMgE9IJWhwEHgHgUgQAMNpZhZlbZ7ub1ta\/8o\/r5j51++ZeeeOTjZ976yo2dO+uqYxPGvgEUiIyUQCUBMVbLUDakk6SAwLv\/B9\/f\/MkNktTZPSBljpRYWZO4t\/Qi15JUNELZhPyvZ45SWgPFiPPi\/CRzXdVI4FqYggCUNO77rsaEDl1CBsxG7pBdAKdslSonnjn99M89Wjw29rWvLCx8eBjsVMlrg9+CqA1KgjUIrgthU3usUq18aj4EgjRUwzThkBstFHeA22hZwG3mOJbjugPFganh8mjJLlhEFLTD9n6zvVvzWy3p+0oIc\/NMMhT0xNjoCTXYVgExUx9kiQ55\/UpP\/kDfgTv16GLSINcul+x\/6SgkYcxkB9QPqVrxIzXk1KPKpowQpF8qV4YOmqSXEOPAbbScmUfPFoaHH\/z2j6Wnr\/gmI80knluu7ZYuXjrx5DPnr1w+NndsfHCoxAAiLwyCMJQSEQtFt1h0hZD1o\/bW5sG1m2vvvn1vaWFVhEGsNiQgBTKCsB3V4M5X33zk73xpcLJcazvA9QSfAYg0706\/1MTNMJNxkBwgBADY2oSJKXLczu7u9T852ru\/9\/SXnvjS331l4a3VD75+\/XBli0xaWEAyRImk7fPM3A\/TqS8hKAUilO1Ga6mNCKAUyoiUIsq38szjaxl0KjIQmhMRAUkTzASEJA5eey8Skbe2K2p1iuvDbmOlZguketTkmNWFSQEslzul6YvzT\/301akrs\/eubd763upRxNXeNngN8JsQeaYX2vB13GoiV8ipKjMtAGKcjBrRiDHAKqLtMscdOjYxffX45IUZt+RSO1CRFEopUojIbW4BoG1Jm7Wrjf27m9vXlr39KkUh8RCiADEgDA0QBBBAArA0ZML497Af2D3\/m0QPb+J0o+8zFSw9DATz0cNIxKGn8pQWTIvmh4VZ5OCD2PWd5FCHicAfMxK6bMiW3hQZJz0VtMvFyYlP\/L\/+qxtfv7759k3yGvEilACIlmMVKpcfOffzX3r2uRfO79X9B4s7yw92Nreqewf1Zq0V+KEUgiEWSsXh0YGp8aHj82PnzkyfOjvtuvaPf3Trq199d\/HeigraJEMghciIW2AVsVA+9vnnx8\/M3fidH4rGEWjTjVECyNgIk8YAojm3LWI2cAecIszMQcuDkLA04IxNXvn8o4+9MG+R\/OAbN++9ttjaO6TQM8EbMlb9G3YtpfdlRGQs7RtrH5Z2hGRHsloqbRV4ZWj40Yt2pXL4we3w4ACEBzKI3QEsDqvg5oxNGzOYMmaSkCaIhQGMITJiFjAbrQI6xZFjExc\/efHyx8\/XdtvvfWdx9d0l5TVpbgiEhI1tCAIIdax0BDICpwJhB0Q7HsGnxXes\/I7nHDweBdsu2gW7MjDz6MljT512hsrtg0b13nZtabe1dSC8QMUTHURAxpyBYuXY+NiZmdlLc9y169vVtbfu7t9dl74HkR+ja8zsStOBMQWNmpc1USf1eCa6F2GWKJUV41Ifnm\/vbZD6XgWzX4WAEIafwuysM\/EudTOukfr9Zxwlma5MyvZLc9AX6GqKxnIQFm+HRXQrJ15+8uLPvvy9f\/SHYa0KfhOEbx4dbluFyqc+9czf+ztfrLX8P\/z6h9\/97vXDw7qSoZIRaY92XDHo9iAyhpxxyx2fGH31s09+4dVHLc7\/t3\/yx2\/+5H0ZdrShARknqwBupTA19cLf\/8Ubv\/Pawe0H4B9B0DLcsZiul0vqTNxJjBNawGxwXDh+Eg6agBydCkzNVmZnn3x8+MIrp5r7retfv7n6\/nL74IiiwIRVSJFPO8mEqORGUJr9rHnvZnKntUTolMdefPLsf\/8LzLK2vvn22r\/7Jnk1iDwyvJbEfmmE6XE2BsZvWbze4sqQ0OTP6Ekgc4qDU2Onnj712BceiUJx\/TsL915bCtttCloQtEB4cGIaGIOVXYhCiNogI1ARoAWFEWhtmSIi3tu1\/i7+KnH9aRfQKVrlytSF42c\/8UhhpLz84zurr98NG02lP1saNpoIPTX3yQJuoe2WxobOfOLKzOOnDpZ2l757rba6pfy2mWPJgJJ1aKIBYkqiEVJSP+8v5H+z5+7X6w4n6AL+drmiMEe4BspFOCkC4FiYy00He7Fk+LB2bB4umHPkQvcizF0tE3NAqj8EboNdsCqDj\/2FTz74ycLh4hqEHY0GRCBEhpZz\/tLZf\/gP\/uLS2uHf\/4d\/+Obr19u1Qxl2KPJMOaRFFTIEGaIKSQYgQ5KRlKLV7ty4uf7G28tPP336lVcuv\/XmQqPeyEwLEJDLCJ2RgcG5sYNbK+YWF7fX030Lu9R\/8bulx8rtFszOQKsFUQQdL1Swfm1raaMzfHr8qY+dPPvC2TBifluJiAjjMyq5FJk+Icu2i3tkLvGQXcuJuDv23CNDj58nRABefesOeW0z38NML9CATCDORUqu3yYjSUvPyHLAKaFTBqfMiuXhuZlHPvv4y3\/1xdGzEze+t\/iT3\/rJ5vUl0ToCvw5BE8IORAEcHoFr4\/gQVA9BqXgqKAA5gAKKMt97krKou98OWC44RXRLlemJx\/7cC+c\/fmXhh\/eu\/cfX9m8tiVaNAj0Qzr2tKENUIcgIVUQyBBlSFITN1t6t1c33V8aOjT\/5518UAht7LSWy0BroilIi\/AhHLD2kjdkXf\/uQIX7iWqKHDhUzDhdEAI6FmXzHrhveCV2AsoeGh3aBHrKsl4wkLe5tQjzoiUUYLjrFifMnz3\/6qWu\/\/1rUNIUoggQiYhwt5\/NfePHqYyf\/P\/\/L11bXdlXYgqgDSld3ESqBJJEkKqWjxjEJGDUXEtby1cFR8AtfeHxlef\/uwkYm1geRMULLr3vnvvjU\/rWVqNUGyhGBMI7OwswFJ\/lN884qCX4HZqag0QLhQxTC6JB\/0FrdbN\/\/7j0VRc998ZErn7pYnh5u16MwYmRyyOLYs3SFMN2XTFuy8cygq6OoBA6em1Oh2Prj19sLy6CCTCcpSbdOa\/74q1jIbbAc4DZYDloOWS7aRSwMOoOj46dmnvnFZz\/2l54dOzn23tfvvPm7b62\/\/yBq6hK9DWEboo4ZAyoJjSaSwNlJqB6akbp+GIpDEDTjbyIOOdVVtL7HOkXmlsfOH3\/pVz\/TOei89Zs\/2Lu9JDs1iNoQdUD4KAPQeDsdGqHHwsY5IYEEKoHmei+FH+4tbu\/d2zn7+ceHr5yqLe8JP8qrSbNOwuQR71VMY49ZCPu3SfsNxLuWQy55AfNitQyyk9LuaFfaBfWueaI\/y4jU7\/6IfTulkHH4xo8aJ+6MXjq5v3XkH9VBhCgFkUld1s7RYrkYIQoVkvQNZJp6reP5gpkAlAROwDnabqlcKFjMcd00uhkASIIUIEJ\/vy6CYOj0VGdnl4xos\/tNoFxFoWtyCaDMw99pwd4WjI3Dzj40I1ayFbnyCKqre7Wl1YWfLJx49PiFT5699OKZ6srR7deXV96779frFAmUunmbmMRlwubIBifkbE1KtJc3bv+j32U2D\/erJALN0UCGQIxyvRDIDcRZKlJF5gC3LYuXxoYvfOzC2edOVqYHdtZrP\/r965u3t9q7e8pvgQhA6HMpw0eL9cN0cABhACfnYGUFyNIMOJACuIsqMILEbPebu+CUmFs6\/vylCz\/91J1vXl97+55oNyA0qXLJ+w6gNHojw1oyuqs49BcTXI1SUXUxeOP\/PLrws88\/9zc\/\/8G\/\/V59ZTNtapoIXpn4x7JDwjQxHFLyVk7DnQv2i\/uk2Ot8yPVsqLtj+lBbVTInxIfkhGZ4gdB3wgJ9a85cAyDdMmKAWgyPAYZJ8itzyxc\/\/\/z29fXDxRUjjkna7sxCt\/zMs4+89PELY5ODS\/e3m\/U2MQtYfPNhLAu\/MHcGbiO3kFtou3Zx8Mknzv3tX31lZmbohz++e\/vOSnzbpES9CcArJyYL44MH11d0s1vL4ghUToaYF5xjLk6EIAiBMxgbhUYdRIQjo2ADtNvU6QQtb3dpf+nt5epmfXBy5PIrZ0997NzI8QnHLQaRJSUDywVmmwkYj3+lAaNxMnbyi0D5gWy19XwvPv942pLRn8fS3X8XrQLY+lcJ3RJ3KoMzU+deOPfYl5545peeOfbI3N796rt\/9OEHX72292AnrFepUwNdcQg\/o4NJNIDS1OGdNsgQJqchVCBjCJVTAenFb0qS71sAp8QKleMvXj7\/2adv\/P7bG+\/eU55Re+uuFeoRvJF9q6RBHSc3GxoVxSQhAJUckiKIDhb33KHy2VefbGwc+o1OIldMezNZkBNlwjofOlzAHp3ZQzqsGTZCn1iWnHEhd1JZPb\/VNW\/ojmf+CKDTR8w5sqCUnsaq0THalQobKO3cWKaMZZPiaCREbjEetsNjsxP\/8P\/3X3\/7mx9+8N6DldXdZr0OMiTQ8JiEXQY6lAgYHx4pnzpz7PkXH\/mZVx\/dqwe1TiRjspgZ3GsapxKgor0PV0585lHGUTKeihHyfu1cmZEYuBAI42FmdR8cDpPjVG2CjMAZxLFBOjwgRGDMC6z7bzSWbuwNHZs4\/pmLc6dnLjx1Unpyc3FvfWH3cG2\/tnUYNNpAAqQifUqTyvjHVcYoEMdQm9j6ZECUqWMZR8YpzfG1i0Ol4emRydOTx85PTp+dRIdtbDWuPWhv3N88euuBahxC4IHfhKCJUYd0NIXucGg8D0DucAYCRXB0BJLY9DTtt6jTAulBoQJWCVVESUg915aL8twT5x750rNv\/9ZP9m8tU9iKr\/SGgJj+sJlbN2W84XFGfCxUVslQVwIp2cGFr7\/DAJ75lU+\/9k\/\/uLMjKKGZpMok\/ZkUwsNdgPSQZziLG+mbQoFEvSrRrux5yulprL6Z6R9BlSLoGUbkANvUpYbtHhV2R8tg0isvz45z1wmP6sY3nYueA1ISSR3tNP\/x\/\/LNV1459ZnPXP3Fn392aePo1rXlzY3dWr3TbodhKAiAM+Y41kDZHR0pnTgx\/ehjJycnBrYOmr\/3x++\/f2P\/H\/6Dz0nTljRBiGhUnZKUbG3sW65jDZRl6BugWzbCgrrK0pgBj4AkjUNHR3Tt7MDxkzg1Snt7QAwmChC1NCmMlAUAEnntkDWuL9\/8nY2hE1MXPn5xZmrwmUdm7AI\/2m\/v3NmqrVYbe63WYTNotCM\/kFE8qMjgEhMnVOoqMGpMMElqnFuOZRfdwmBpYLw8PDU0enJs5sL04HAx6IS19cN3vnZj+dp6q+GrUoU6dWgcAkkolMFyUNqgbFRC97dAScqkpmlossn3099Dq0lSwtRxtDjVA\/DqUBiEqKXVeMA1DL80dmb+sS89d\/0P3tm\/vUxhGyIPIh9kZFZ4nFWW6xJnpEKUsz4l5QmZ1x8RREcF7N63r1uDlYu\/9Ikbv\/n1sCa0zE2nL8e1C+X5K3+WKRf\/y6Z\/mMVIPOzA6la0WdkvQbmJH\/YVquJHTx+zWPQsXzdTXFO3Sk+3DVh5tEKRlJHMbYSJ+0xDuSRtbOz+0\/\/7xvBg8cojp1555fJLT58sf+qyU3ItzhkQRyDEiDAUMmh6fqNz6+72P\/9X3753d7XpiWPH5+McP8o8UikiUfqhXSoUxoaDajXfHqOsrp6yL2Xsy9VtHiRhRlpbW3DuFLQlbK\/j3CVyLAglaKY9+oAOTLriJx\/g0Mght9\/8vTeZF7guGxovzz1+\/NhTxy++fK7IkSnlNYKD7frO0lF9+8hrelEQhoGMQqkipaSM8+GJMWScc9tyHMt2mO3yQqU4PD8+e250fHrArrgSIGoHtb3m3Z\/c37y+1jhoBh1PRcLgkg6lMeypCOsKlM4gUlAow+AEdhomtExEIKIMNSXZIwmQKIhgZ5NNTQEfp9oRuIOEDFRgbChOsTg89PjPPbv23vLGe\/cp9DKuX90oZIRgBC4aspO5ZVFv\/y8R9yQxs0rH+3iqY9\/7xvvP\/90vzL\/yxPI331TaEqWtGBq1k0M5YVYR+FADU57z3FMtUr+hBnUJXrAXR2MWYR5PneFApr76zL8S9neF5GxdmROwt5tD2Idgj1bJEV6ghIQEBU2U2Ch1JRKClNITUXiw1\/7hd\/df\/8E7xaJbGhislIulou3anDOMJAWR7HSCZrPd6XT8gKTenDgH4YEiRA2MAMzOGEgBKRIRV7JQcuq5QzvbZDPi63S2SpmygBQhoB5shgEtrsCls7BxCF4EVy7AtUUAAhIgBc7M09o2Tk+ACOm9N0mCAiZsuxNUtkP88EcLDlGpwAcGnaGpwYmT4yefmB785ClusZBhBIyMNItUHLjL9C9CjsSAOBEJVT\/0DtaqD968X9+tt+pep9nxm20ZRKAi0rN1lZgtFZBOehFEEhUBSSKJfh1qW+ZuiRaUxqA0CJ0j7ByBIhJSx6QaMr8SEHhqcw2n5nD6BNWa4A5C2ADGwCowy778+SelgNvfvKYCL7326ydNUwUyDGQz7s9QlDDTP8mAC\/WtIlEjChQBQSussmu\/+YPn\/7vP1Jd3qjcXSWrQnkRQ3RAMpB5KIP2ZB95HVKxA\/VxL1N+mAXn4L+aFZ30ibjO3oj6XTchp0jJJND13Tuqm9wEAMJtTKGI7aZpQl+WQS\/3QG2p9GEUy8puN2iEgR8wjP\/R4mpQWQILtIrfMBYolrM401cXIYqREIRhnSf57EhgQB5tSShgxvOuErZP5URUQRhAiPFjDc6ep3sSL83RnUz92bH4GCiVwB2n\/kHa2QUUwMASujcjIa+DCgZBSILYBDgAQGeojzrXsgmuXC9x1LMfiNuOOhZzpzVIpICFlIEUQRUEo\/DDygtAPRRiRlKAnPYlNVndiE9MzmWCcpGzLtkMACFUEERAQeEfQsJBbwBk4FeRO7DhpQxjodQvAaH8XZ07h1Cw1OmCXgDEoDIyenpm9Mv\/Gb\/1E+G2DPyQFCKjja7iFlgtopdU1ZWez+eopvVkRAoESerhvKPcyAkAKrcbq9tpbi+c\/+8xbD7ak1DPGGLEHEg3KOa\/26n6eP2o1IuVaqN3nJvaVv1HP1RCsHBW+e4CC6SFIee1nFh7X3QzKxCCl6zGNScuNQzDzahIxzG1N+vSjxP1DVKq4o+Mj1cMmMAkqNK2LbIeLCLJua\/N1OCBHqzhzbMIuOcrELWR+grTQYIyxbrF8Xi2ISYOaqGdgarrhZhMHBh2ftg7g7Bk6bMBAERoSR0bpiUdg44iWNiH0sFwEXiEiONgh3wPGMoxCA3RSiBJZmAgbUutW4gaixBcDyWzDLDCZJkzoUlwpo0GnLC8QUnmX6T0mHSDKNkdARCT0l28CEVgFKI6AMwBWCZT2lzBQRDsbOHMSxichikBE3B248nPPr9\/eqa7uxXprAUSIDLnN3NL02fnTj58Znx3hnGHKyEmvh9TDINei7NALNu5v3X\/vfnNvn4QPKorLDZ8i+\/63Pnz5f\/jS3BPn19+4RiLQgnhEBcB0RM\/DHEf9opb6jdv6kiywyzCcP3QybZk+LorMKZUO76l3QIiGcYy9y6\/\/9DC7GvP1REatF\/kBc23kLPZ6YgamahBm07PDf\/tvvvrP\/sV3lpfWhS8BAKR+NZPGiTKVBtOmeNTYT8ctXrxy6m\/+9U8OjpSU7rt2v0IIyNCywLGjSOWTGyh7B+oVImV3qLhdI5EAlCAZwMEBFCvwsSd4oYgPtuhzz6nDFuxWcaAAZNPmOvgemvYdoVDU7XzFlFqQM7Yy6t5usvdblVl+SSiAZn9QJiRY5fqcqekhVVqm+QxE+cE3AiCIAFp7KY\/PLmJ5DJhDkujwEE+Nw6ufh44\/uLo6MjN07Q\/eARGiiEAJ02hlllMZfPbzL7z6Kx8PpOzUO5i+1Bl1e1qJxhkG8fbvlJznf+6ZlXvbX\/2\/\/nj73jJFhCQMrFEEUbO5+e7imY9f2Xz\/now8EH4WNA796U8P02B3mw8fut6oTy5vZgbfTTW1ehLRIC6\/MBPb2zUziQ\/x7vzPLqFAHuebr1PzOTUEpLx6Bwsus20V8Nh9n5vTcADRDs8eH\/sX\/\/Mv\/c5X3n3tJx\/s7hy024GSESiVAhGMu5wjd5njVkr2zMzoy594\/Be++Ey9I4JOlOn2QALY1WIUbjuBVJ1aE0GSQWfHL0RS\/GTzIzLjgvTEN7wwBUyAQlA27NWcnaPJv\/ozGEQHC7vB\/V2KOrC7TZ2m0UbqQCUNINPbG2RNmInCIQkIwG5xL6bvGWTSKbJTteygjJJVlyoBKI1AjH\/2ZNH1oXclYuhE1EkIUQfqATEb7DI4A2DZOD0NIpydxZX31hrrO6T5y5pPiQy5\/dgnHv35v\/Gp9390\/09\/76127QhklBRjlMB4MGtSj60QyACZ7RROPnbqs\/\/tKz\/3P\/7iv\/97v1nf2aGIUNfVMiIRrb52Z+6xk5Xp8fpyC5gFjIFKLYeZHgdl8EAPvRkiZtKxMi7l7kEi5SVqlMWgdTO6rT9LAfPwMxm7TlikDJLyoYNH7G0kKZ3H0t49lEpyx5GMU3KIZaaojKi+0\/h\/\/6NvvPjC8S\/93BM\/+\/NP1g7bK8v7t26vLy\/vNmrNMFKgiIGyHV4ZGThzevaxK8fPnZqojJbrgfi3f\/DuvXsH\/9M\/+BxPCRQx2jOe+DtDFemFYbVmOtqUArmSYCPM9IGxqzOcq26UqUhJgpIlbg8XS34R7Ttv+G9+AF4Vwjaq0KQCxjFo2PUmZpPh0mBLTNOMM7PctHmmW0fZ+KFMJl42IDUGj8XoMVN4Eub28+TJ79peifpgFGLhCQkiARur8K3vOKPDs3\/+wvXf+IDC2F2pP5RZlbHRl\/7ci3eurf\/hr3\/L63QobKMMYrsmxrB4hT3dQ\/NNMO5x9+aPm4EI\/uL\/+HOXX3n8rT\/4nhIhEQFJUhHK0K+1DjcPT3386rXVDWKcgHUJTnLnUGI1TGse6uI6YQaTgr0Nz3RKQV2WJepPXSQLs1\/8IXEvmA3RQOzfKO5iiuZdvvlhdxoDGhdOCkgF+0fg+aWZibDRABbXXZSaepRSUqmNzb1\/9W9u\/fEf\/eDMmWNXHz392CPHn3n6FNgWAEghkQgYA86kosAL97eqP3rz3q3bGwv3t\/aPvPn5WUbEkUH6TmAahMDY+MU52QmlH8bfVRIglEkSIurrMOtizZv\/IElKoPD8B6udhRVFFNy6BUGdRBBTYWJxlvkqWYwBdlUhmCZ950AGyaLNrMOu8oag2wWQg3RjOhSnbI8gzw3M1uPZGiWjWMi1DhQFbbh7s3zltFJn6xt7hnxhhFCIyGZPz8ycnPxP3\/i+1zjSED3TOMGUWgKZhInEiWCOemTAI0ly\/dZqY7fx6MuX3v\/q60HYNuAvRA0L3r27ef6zjzPHkQFPsW7pyZcloFJPS\/NhN788pAIePlDPLAXqct5CJp8wvXqlOnPql+P0UVbefkAZSgAjiBlfOMSRGGhiMUFJGcjmQWPmydO1hRU0h6GM73gERJJUoKQQ7dDvrK3W1la2f\/T99wcGiyOjQwNDlUrJKRY4AkgFHV81Wn7j6Kh+WOt4UjEHGAKzlOiIODE39gemlDdkfOLKfH1tnzTxNkn5yaqdiHpegMwAgzCjKwRSBIxAhRQ1g5WFtf\/1XyKSqNfT2HqSoFQ+sgtjtHn8CmGX\/j+9JlDP25mk6vb4brIxCFm9BgGlBOK0GM3w4buSQrD72chszV19RdLNs2BkuhLsNsKmn0xEEBQBB2RuweWI7YZHSmDkg4wAOXIbGQKz8hMLSre\/uPlESoIUxKLIjzqNoFR2GedxLaCITGZ4c6PKi44zUPDaHJBTL27ko+QylGvSdl8d+5rpqe+yoIe4Da2H6rAzM3Xq7rHinxGO1qUqSaYQ+spPGROi6QQY+d\/BvfWzn3n6nltQURsZJ4WZQSpKglCRSTxWEpQQkTrab9YOqjoaIc4iZ4QWAZA212hAk11CtJSIQkXSbKJJSWMCZ9xKcXRudPW712K\/X14zkNmMsfs1yhGek+a0SUqQ2s4XiYOOnumnyPAkyDpeWClpV7e+YkVO\/tKfqV1zTQRzL8wsqD63CcpM19K2Uz9wQ16XQnkKRO+MKvfBSClpsjI1uP3+GgmRxoPqL4mAANKU3VzvfWjhiafOn3j2\/9\/Zl\/Zacl3X7VX33nff0COH7iZFcWzRHERziCSSJiXZim0hUmw6spUJSAzbMAIEQRAHyE9IkA9BYiROgnxwEngI7NixTSuQZQ2WJTu0RHGmJJJqstndHHruft39pjtUrXyoaZ999qm6zwQ\/sJvv3aGqzjl7r7X2WvcKssbSqzkPq3a2YCayc23rtS\/+1eTSevXXSlRXbS5VIvJ86\/z6tfPXlg+sbZ\/NpIkZbuAusmv0nf1+FW30RQyFJtxKQ56Q0TJzY3ctl4hgO\/R\/owzhtUULarqiflbK+fG55LP1Y++uPPX4nsP7r2xdqf2n22VwbWN7Zc\/yDYcPXF5fFw5rUz2SMwo1sQIZNMPpkNJlJMuy0QdvvRnj0dX1K5LPpE1EGWAwlOHo4F0376xvr594n\/lMONdjoOEwGK0IlxoTCw1Lirp7Z+naBgpRVLAkahsIu88xLuebza9QJ5H53Xp\/awea\/HiQClukotu0U1SAlcPXNreMDQPCu6W0qkmFbDxeOXLde8+8wFoTX5lfMKuOS7KtiElIcctDd973kw8XxAyVILya0meLjGUik2tbJ\/\/qu5OLF6sgyqKCk5oPUDKfLObFzs6105dXPnjk8hsnQudCduvTyAbmcM\/KGjSOtdWa7Uws4uYSDhNnmuP+5oE30algXKV07ga1mKYFcSrynbkUk80z58+8\/NZdn\/zwi++eZj6SYgbWmsli\/vILr\/\/s333iF3\/50\/\/pV79w9szFIstkPqh8dfV8jYAoSm5QBmW8QTbIiruO3vTLv\/hj779z\/pUXX5N8CtaxXtmQg6XBeHz0Mx9959k3phsbUszQpi5LEJ9m07aD3Eal3FNkbRnLXhpjN\/uOGDGqc6NUK15o1AAwkesM4kWaYtYsbJ2wRr2GGQrT1Ux2c8eCesyrzGi2qBZGWVpdXrl+72Rro+qB2zTPOlCpkCKv7YYhzOXZ3\/raK3\/8nAzGIhlrfwqGdttSzIvp9vali2Rjss46H4C1+TSlKMqiZrK+efD2I6chAc4rqHBph3EnqOYPVRHhdI\/0hNUpMbbOPEQTEkrPWNR1oFJQAWJNju0Vkpoe7aYoQikKFLkUM86n7zz7+mP\/7Gd+8OXnN96fymBWdk0iheSzN77\/9n\/71T\/6lX\/5uV\/7d7\/wu08\/+\/\/+4tULF65Od3YknxT5VFrqrxSzDGWwJNnSyji78bo9P\/rpR3\/6qUc5nf3af\/j9C+cvsjRmLxXVwyUZjg7efnjPDfvPvvJW7VCSs5la0NPZqkoHxRA87aNYF2x1fViAMCvOJrOya94bQeCOmUxRAx2Nlt7REzIkhlqfFRi9Fc3cTKojattYSPgGCrPNxgPJkE9moTZAdaTlOVcKaEhKMd28Nt3exmCpMsiRQgWJ149ePpV8R4q5YFAKfQpSp4WzdQYppMhnm9s3HD0UnPNtiBOhcSYGY3wW+Q1IPtrGkpGrN0wQY50QyTaodKhYkZDO18atusCUCN1BYpO0idnlY99WqI03I0mUHXY+vXz8vVMvv\/WBTz547A+uFpURSyGSs5gX0+0\/\/\/oLZ89c+dnPf\/yf\/Pwnfv7nHj1x6tKbb519\/djp9947t355YzKd5bmMhoOVleG+A2sf+OCNd995891HD33gluu2JfviF5792p8+e+rk+7WcrfJ3k9HyaO+ee37qseNfeXHr\/AXOJ5LPWeRo1mFVr2nDH5oAV7sg9QwAbVIrXZanRXQa0FzZVDRokphQdOplAsSyDep6EQn9lKNrNA+RAYO9bQNKGlUfGhThbJYX87zprMEakNP4XTlQ1vTeRU7MGoCrERLXoyS55DMppiAlq4OQ85YXRVNjV0wjWRRltnctimxVp00ULsOuWcL9K+l8IcmxJhMXk8odHSI0UKFLFRIUN0upNSALuTI94hQfslamXhpSsJjLfJJvbx7\/0rd\/5Ff+3oWXj196bcJ8KlkhLPGM2Xyy8d1XX3vzrXee\/sPbH3zorvvvv\/VHHj\/6qc88XAyyyWSeT2eSc2k4GC8POcim0\/mV81dOnjz\/tW++9sJzb5x88\/h8Nm1WIJAxG8loGaOVmx+5e++R61\/4718uZo0LU16nyrCRtgUPLmPAgnHSJFqMu+H94QnbHTFfUGcyOZjdRHuHkomY4khMqmr5qw866O40OICBUNMPg+sXpVoV1cRjawrSWCJI7XTSQBPIhoPltdHeNWDAbCSCKsqiRKry2WxrY765wdZ5pCb2Gax8s21kwwELWhLGdgOEbe09ZAWhJsZlMsgwNxjuOEZ5VYedXH0zQ0ICqZ80bx6sQ01vwLT8jQypnK0sUMxZTGW+vfnu2Xe\/8fI9T33suXOXJ+dLbVqp+it9OOdbVycvPX\/lpRdfG42G+6\/bf8Oh6264Ye\/a3pXl8WiQZXmeT7Yn61e2z124cuHMhc2NSS7CMkqN85KaIDIOlmRpGUsrB247cu+n\/8Z3f\/8vty9frpJDqUPtibjDZlvXaaoWYR4xqDsmapNmGpLV8P6dkLMSW9HUhQmInEGVoxR4CAb16Iy+tDsHoxEi1UIawoLt2HsxnUNkOB4Cg6ppM1HwlRdxVr39YHD\/5z5x799+lMS8EtwLUc\/uFjz9+qnnf\/V3puvTanKy5kyKKu+mULqGMlshAzBeXSo2t0PZUMzjhZujfYSdQhQ+tsNYIeMLXiAiGEa3MLkWJSKKwnnnDggHxkgqmHdAbYhQ5MjngimnOz\/40rcfvuvI0c9+7I3f++Z8W2QGmRHFtGLVOC8wFexMZoNz29fOv\/+eoNxSA3FLRbir0c\/6LMtKs1CM18YH9j\/4+Scv\/uC99154jfOdxsKoKY0qQE5Xm9QYqRaJqPXAJvUAWgONdvTf4J\/enWe\/c0G8AzL6bHHlFFaPXr9O1beQDl3cqMnaM7QZem+mU0lyujXZXt8YroxLgYR678oHp6gTSyUbsCiEcu39y5ffPldQ5vNcgCzLKMjL+GIp1k+cyacz5QaTNU1pUWsq2vVdJ14s7Vu7dOq8VSOQ6ETEAqiTthNhE1dt1dvoCNw2l5tKMYM+\/VoAvlCH8hKMiMGI1KBq4DWM0LxyUcWV55DZ9nxz8Oqvf+nRf\/pTg6ce+97T38oFwpyzQjBvm3sUIvOmFmALz6lqpOmYTPTaaFnGa8O9ex\/5hz\/G6eyVP\/iLYrJVzdfkVRozGptK1gxeZxA56pQXzaw32hQ952kOTAefISXdYbQ6VX0YIsAYbBsSD5QFlTURo\/YB9BpmpEhIHurDo5kOKstDFsX2zvb7F9Y+cN36sVPNxGB1Whb5fDqdiGCUiWQyGJWoxYlnXjnxrVer64nKig4k80k5hMXZDMgqRU02wGAJo+XR0rCYl0SF2vyzAbMM49HqTdedfe77Si7byvEkFEkjNWKvk+H17qnma0ADTMcvRcv8VwHF\/UR\/WhbQOhnrHw5djRmlZwuNiUijm5FiLvmEs63tixdf+o2vHr7n5gf+zuOD1TUZrcloRQbjSoNbNqPl+VnkkueSz5HPUTsgSvmXCosDKm9FWVrFyt6l\/fsf\/fkfHw0Hz\/\/mV6dX12W2LXWcEFoYXX28tLtrs6lHwtiAY4RzAQm1ZUA0wqYjMcKrWjY3OihTMdTSDEy0g9HtS2vtT\/Mv3INXK1RSj4NSeAdgbLV\/VZdx++z6Bx++QwaVw2oZCEMRKebn3z138eK1Wx64ORsNZGlVllZlOCpYFPN5ns+LeV7MZ5xNOdvJZzvFbFrMp0WRS5bV0TGrMt4jSysHDl935NaDb796cj7ZqcZHSntbQLJs5bq9+w\/vn1y80qgRm7IoZDndeM+QGLZjSaquplbA0RtKZPgWtUxBxjfZthzxidlm3gQ7HxJxbo3Sv62i9ECAHr6ACfzVe8xkY+fSOxePPvnh\/XfedPHti\/lcqvwdGwUVWpS3gz+VLLvKexqMZLQsSytY2bvnphsf+6WfyOb58\/\/rzzbOnZfZVpN3XcbKNjQ9SBMLbgtIX8zXYNC21lSIMzudaOmWqKm8PIh0EXnmAYJdXXDdTGylFbg7EApiitopCKpQ0cF4sLp6+yfuO\/XMsXxnR4rSN7FCZabb832HDtzzqQeunt+6dGab2bIMRtUQ12BJBktVSCuGyEaVf0DpmzgcY7SM8V4s7d1\/5IZP\/qNPXv+Bg1\/5z3989ewZyadllB2zgQyXZLR8\/T23Xn\/PB49\/6dliZxP5BLUmEYFJghGIR671dgulX0SKSErfEA0ale8+RAyVB\/K\/eJBXBaLVlnEtgNbmwEiizmWo5GA4C1VI0Vjyg3NcPv7Ot379T+7\/Bz\/60C\/95JtffP7yW+9yOqrs98qMCn1qKXhB0JagzAZ17MFytrxy+IePPvDUY1feOffq735969Kl2u2rtD+oqKqawqL3iNPubqqUAT3lvGnyyEDeYLZMJjk5zwda3ZI6lA+R+MXWt7RvSg0K0H0oVKqRo8BRQ+\/NgV5KcPPZtXfPz\/Niz5GDl66sSzaQPBOUdjvzfLr9nf\/zzI23HnnqX3zmtcdPXDx1bjadTWfTKVlwIIXMZ\/PxUFZXhsPKx6BgXZIjG2TZcHnf2p0P37bv0J4\/+x9fPXPsRGkPVyWQZkMZjDAc3XjPrVfePjvf2W7wNm8KUE+vu\/0b01hZePvJxDaGUIDVjL\/sezi9f7qbbJTLi+D4rYWh8I4sRGy9qLzo1nmNVSTQkgzHGK4MVvfc99NP3Pqxe9\/+1psnnvne1pnznE2kqLiEmgVmq5wsg90rl85BafycLS3vvfmGW5+8\/\/BDd5786kvHv\/Fivn1N5jtVFdoUoqJsHcSd8OQC+KWhdBg9sD4dBytjQ8dbUHGtzbVlkq1N4az0hB2Nd4ckZFPKZhZNqnatIMoyYMAybmlpD1YO3vv5T3LC159+hpOrMt2Q+aQyp8mGGK2s3XjkI5978o7HP7R2YHXGLL9u79asuPLelT0H9l5348qgmM4vXFkqOBRBq2OqOI18ll88deGFp7\/95jMv55PqlZENWHf+SwcOPv7Pn\/re733j4qvf52QDZdRPRUHV6XfB2JfbDMcaNBcjiOVKCYRG\/ZxahPExGPJJNoNJorpUe2zHLsA25kmxiNUKzKgDm5oacrAkw2UsrR2845YP\/9zHlw9dd\/zPXz734rHN8+vFdMKikDxHmcxcUEfAVvby2WAwHq8cOnDzI3ff9Yn7L7x1+rU\/\/MuN02c4LZGYeb38KrdvqhlzWBrA1CdwBO+UDoVXwK1jsWXmtBZWZxqmMocLGG7hLvEwt3kA4MNDdHqT+qZX9YsIsqw+iJZktILx\/gM\/dOeDv\/AT3\/r3T++cPSeTJuenAMDBEMMVLO1Z2r9vae\/KnkPXP\/GvnjrxnVPX3j730b\/\/2Ejy5\/7oOye\/8YrMths1L1uOsijy2c7Vzfn2NotpbYIMDIYcjmVpVZbX7vzkg7c\/ce83\/\/VvzDevlmneyGvnNdXzV9suXESK4nuJOoPz0HF3SRgleGaGXlCb9ERi+2OcVvsrfeQhW\/ax6pFaab4UUkBkzsqIISfzy28df+Y\/njt4xwc+9KmHHvzJh69duPaDb37v0rH3J9c25zs7nM9Lf1dggGyQjYajlZXxgdXDP3zHLY\/eVYyXzr14\/Nv\/5Y+vnDqd72xytlOVoI2akU0hqsjXkJJtfat8yaeeDYUFEuEpIQLXK6SLECZP2Y6rHtFOjERzKt+ATlpJMKcc7xntL1JbH1az56Uuv1Qj7lx99+zOZHrko3ef\/JPLHI6FeU3mF8IZSSnynXwy2Rjf8pGja9ftuemOGx944q5jX3vl1S986+rp85xscb5TpwhTKfiaedRciqL6NM0WMBwPl1fueOK+k3\/5ar5TQd9opvUl0gDZoSXquZUItYYLXCkVEGMmEu1UdWB5SO+ZQM+QEtpOpBSiRUA3rXKNekbcyC7CmaBKJZ9LwdoCuFQAzuazyYXvbVw+\/u7ew4duvOumGz90yx2ffoSjbL61IzuT+WTOghgNBuPR0p6V5fHSdGPn0lun3\/zicxePvbt1\/iKnO2Wgj8ynKOYsjJoxnHBVzVvoKMPWQdaI130JCk1VAqVfTzBCqREYJR+F0U7HCSdgSs7LAKC3KV+BGk9SKF\/dgMLExrXVQ2WpPJdiUmxvvvn0M3d+7uNnnn9r50zRTDNXNzefli398v6VO3\/sgfF4MNvY+sq\/+cqZ774x37jMMhymVS\/RXlYqdWYVtjeS4TJG49sfvzefzN955nssA31rKWI140qlUKu9PXTZhwYbF0TgSGdPgpjSh\/gIjwwbuYaZICPTWmIlKPZ6JGqb36j+TPa1Yc1UsPkgRQ3lF7lgznyazyfrW9fW3z6Br4\/GB\/ctH9yzfGB1Zd\/K0toKMhR5MdnamV7b2bm8tXVhfXJlUzhnGWTbhq6Ubn9UMZ2N0UN7RCBmddQIL10Re9vFV\/QgbEVJQ6HXpTp7ekw7c2GsDGI\/rrCEhl8ceTeXCe9b5+dR1zNQJChBNNb05ZjLbOfy90\/sPHn\/HZ\/9yOu\/+XXmpfdp3vZGzFHkRz76Q\/lg+Of\/9g\/ffub7s\/WLrDIqpmAuhZ5\/DyrwejhQRDJkTS+6vHLD\/qOffvi7v\/P16bWrpRYKRc5mcitSz0KX9xCvA+QCWIB5EqIRw5hGrHtCtWaBCDUKc1CgCys3\/RvQ7DlSbaFYSY2xNqqTDMuoejZsOwYqS2wo2QBVDLp+WstSJ0eRlxb3pRYc5X9TxVHULsAa\/HOoWO9USkx7STKvignNXwo+SVon9EJophSNPWkSNEZajWWJa4sRoJofqpvDUh5YxaENV2R5z97bbvnhX\/pbb33h+bPPvV6lUJQLTEjJMBjsuf2DUgw2zpxnPpWKmp+jmEtRGH18jZDWIWM6QnxpRcZ7Rvv2PfqPP7V9deOl3\/pyvnmlRIPKyKeqt1SQDBswPDAXpFG6q2kF9mXxuuMyzg800Wj0FGoIkRmExRR9BJaxNI1BpcRI7R8aLwBWG40ywgxFCUuzDMGrzP8GyDKWI7yN9Shqbr1y+CtqE8667CyKdqhPpfBCHYQI0LD0bkezxdnRZob9JGNpJhrBW\/TirbKGEUbNnhUo3cYo9sBkK0CMPVUCo6n2zCOUsL86RWpsvCnzCilykZkgk+lg49SZU19+7r7PPbp19tLVk\/PqBpdDTEIW843jJ6sJ3jIjuSha6Nt++\/pyNzaQ9UyMjNey1bXbHr9v783Xv\/Q7f5bvbMl8okT5KjS7floQpjWJNyzkFAWRo6ELoNFZjcFtG2B8pHtfrNBnt9BhpERrjMAMVQU4wD0kmtVS52H9EIaIT5ksXYAEi9JxuTZNKEvNGfKZFHOUrEOZlVcVn1otIQYChRmEICXlN+BCpokuOqwTxB8WQtRv63hkBvYu7opazG6kU5SIiIOkUf8Gj2Bjy1vdJ1aBd+0tbInN1nSewMaZK4Lsw59\/8tzr78+2Zw2WDhFUC2+OKgm0qJNegyZHubCijkwtE+BKOHQPVtZuf+y++z\/9yDP\/9f9ee\/e0TDcxrxDRata+pumdGw1fQdYMqwN9V9uXGVHvVeZuDDA+Eu\/PZhHC2QEknL9Wt4WmxIpzDZHQCMUBFghXuTT+R9WAH1leWbRnXQ7WbkIlYlZ6uig\/aakPWNR0PEjnBtjjh76OpXeQOQBv0AnAOJnkVeXlJuIlw8xdzZQvyUopli0z3HGMtjcJaj0jjFGvrQYoG6fXx2vje37miYvHL0w3JsISh6Ow0KNfsa69pOBRZYZnaGMPR6gJiWxl7faPP\/BDf\/PBl377qxfeeLtM3kY+lWJe170SnKtKhht2H7SyGLa0aB2vGQHjpJWnSezJZNdCcxJGz0fQGSJZ+USEYUjjh+ahSOir4CdAtXhSwEmzIakhbcZgCQOgjfIrmqwlkULxB0Sro2W8F3aZyCWfwgXOGwRDSABkV2s43Zb1w6qdhSt288OJisnmoovdHPTBThbFpXcuDlaXj372I5sXNrYvbQQyw8bpsFnfyFgFy2YotRyDOmZ8MKpI+aVVLO8d7tl772c\/dutDd373f3\/9zCuvc3JNZluoLPdzpUPUQEtrLWtNTBi2cDBtBP1W0Ldmc7VK1e8MMD6igxPRnvhJOqr7QIN4Yhp0LDl461M6WEpY9aO2DGSzOI3vLZTMmupOGNp60RVBV2pr\/j7a19onFrt\/SyNBdMVM7HpJxgvQjERAnP6z81PanOYIZEKsamCRF5ffPgvg6Gc+OtyzZ\/PclXwuko2q2d8yOLnhG1BHFGd15ZkNMViS4ZKMVmS0IuMVLK\/uv\/Xmj37+4\/uPHHzxt79y\/rU3Od2U6Rbq2bTIgLyupzpGyYypDLoRUcPmpKNinJ5w6YjToQHSE0iPSMAExFUtwv3SOUDgHNiIlN8S6LKbrQiBOq7+IxpER7R8jiraWoL\/WMQZx1FKLrCEVDQPvPMVMVPogqsUGwmWvk2maNV8A9IHL8VhBI2LqbuaY4zU9rf6FKn3RxYsiisnzl97\/\/KHfvzBmx85OtmcTzbmZFYvtkELg2fDWns4qqK\/h2OMlksUFMtrK4euv\/2J+x\/+2R+5durMd\/7nn15973SlyJ9PUcxaMYaaC4ETKa+HS2rzf1dtFFwseDdVH5vx1haP9u59MLlzq5FUlYWQrEhDftIcC\/CXMeA\/HWod1uhPcGBS\/Y+WlUTQBtPqp1tQNnJcjldatzyUIBwyoJs\/gGfu4apnqMy2tPNnmlQQj0IMoRbobZOLLMLkW3g4oSDWyrElq2p5cHOaDcYYLstodbR3\/x1PPnD3Zx55\/8TlU197+eqJ96YbG5LPWfkjKk+6rDoSkQ0kG2Tj8coNBw\/df9uHPvXAbGPz1d\/+2qVjJ\/PpThW+XczKLDQJrSvrWBGHgVBzeeZ\/wcpk4hBCJhVqAANGI5qxcBahSqyo9opKOYB4CcWFqA7NNntwsFMqvTeikdPovSJjR7abQjwO1EoP4D+GgYpHNVfwyUAX8+CuVqCEoLEmkZQuLNb4iuut0LcIU59HDf22YRax5L\/JZrSdAhPoDyzYAG0H2TY4bPu6AcuTbTCWwTJGqyvXH7jx\/tvv\/omHRytLZ157\/73vHNu6cHWysZlPp8zr1PQsywbD4eryyv7V\/XfddMdjH1o9uPfiD9478c1XLh5\/d75xldVQ6BStHiNXPl1txquVhkoQENY+QaT3WMWbUUwbKhaF9KwxlO8F9j3o+d4nsDV\/HcJ53hi1HGL9cksXepixDONMlphgZDrFm8FTU383iwAzObMitV9AssJbDLLpIe6hFiGNkKKnNGakQ\/ITnukDP0Y1QS6KPFlFMINyxngI6Wqs4a7Kbr5y2SqrzWGl0R8sYTAa799\/4LYjh47ecvi+2264+eDOLL+2ubOzMynmOTPJRkuj5fHa6hKmswtvnr7wg3cuHHtn48z5fGe7UqXldfww5zUhwcYBMTCzMEuC0X\/4tqKJioD0qflerUVlCLLvQV+d69xzPYiExGhvWX0gdLJyhy2kNnhGl3TZbrG6LlCniXM+m0K9zkX3VQ5IhlIxeUYujGHKwiQGFhPHpIGk5FKu40CcJQaSu1n5UX8J225YBjWUQ5U2PwBYRiY3jV82ksEI2VAwGK7u2XfowPjA3tG+1cHaEgYoCuY78+nWZL6+sX1xfeviVeG8XXvFvBEDl4p\/0PJSNqDKXWNqodJu1glxDHSqmrewnQvH8DDa91DcViZ83uIhHQRFDpIrQHSOPQPxUYL\/1PcYafZbL0LYwi+BVsAoIcyvk85Is2XpDIsYV8UMS3J0LjP0IbCdYHH0w9aXNAkiAfUQMK0cAbvgT6DuvuudWE+o6amrZv4QNQTKGIwRhLgEKo\/uJui7SZgp8tLMEnXyaWWDH\/d+seGq7p1JuuPwTsREYqowkOkad16GSdjV3wx74fdAjdO\/WauYvmCZ+DqTSJqB9ONlZs9hQothzzS6e76E6w361RqzdapdIvbjcoggIl7sSDQPkgoBQtT3xdR8ag0HLXBae99sDdBYD+NNExLHATrL0rQnjB7rwClT96NsvfVZlEygMEeRERnKP5YGSNWrBmr0eqqmxDwLFT7DykHTLDa7AlO3ot2QLLiRaDMYNx0wHTgN6U4T+VtrR6E9NugpNqIxOuo0xFDhwBi0IMwWkuKLnVXuOf2JtY6Go02hYVTjukKbYjMx7yeR6Q78yr21oPerRaZm7ROIrFuLdO+CiTG5qHaJJKKMQXggzvLqVM+YfUY37\/RtcNWTmIMgqnlQtjItOMVNE7pUBcuw8SKh8kqHRN1d\/HxGzrENa0V0l+eBuXna\/IIL9PS1gJv2DbSVZCskjqJ\/hQuJRTyuTTwHviZ8D8aTyDDUrgF7+MoM5u669ClcgBw3CuZ4o7GJDj4MEz36hs1FNDfaW1lHhXYwHOhcaUQyWY2KqhzejqvDIN4CXu0aCoQUl60dlUqBSBkxXoXQ0zmBwRiFVodb8yQ21nVRalWUcCj6AbcVpmnt9F2BY3yf9Kf0KcR2QLV6RodpPEFdgop2a6PzLBfZljpIEG9gzz4BD4YyJ7SZNghblxas84yYkqcbEk6PcI45ekcT00w+2o1VoPdLL0TNXPZgmAqdRx9d2E2fhWSX4CPC+MK5CR\/i7QSsaPp188FQZ78qbyRR4y\/xHgajSCnliNBzgGpxRnbmpjRFdMAHwal2D9QrkIE7zG41h7RFLEUgA4wPJ\/CQNFoNq31poBU0PwBzN6ULrGeHrLTj78U4vyRpM8RdCjrwq\/B3UhfDPyKg3wzpAT86Vj6ee14KuHbyJ7xaNPayIYIhcavUhiPqjr4LPYhu0acRqi01WlO9t1O5ubZWBo3WrFls0IedbkAsCcFAMOwCnuFAM+IBlyS\/xQSQEavnQdtfyEDGRzw3NaebkGBAJWIlTOEJCN1K1F3mQHLYtw+ad7gMdMdlSJOikOymujFJjzti2iPG3WT8l0\/Ir8NkPrE+dnAuE1oVcBD2BWP209JNKUi0vTfBsAR7ZKUITw7Yde9Ia2P1vBZOg+pcCurTWpFiXIztmjH+MV0tnIlWhXjimF0qJbyqtPynXISM+Adnc1XzKdrS0JuMAUB0Quvw71lipXlLFOlTSBLwXfq06SLuGAE\/TF5xILW2et7FjcPTXoZ6ZrgNr48vmZqqJp00dmiJr6OFQXdRgbjoQPdXpENdIYBgJVAlajAA3hVsNMMhics6n4Ex1aSuGtOMX7RLx8467GYSErVRh65ELUK4j38UpNRssvCsKVq6iCnGgfAeSziDhS44YT4mE0OyzkdbgGFjjCPBEwwx1UJDXAoBQD\/LVj+GSPw00iUK4NSjaBJpKSkBN\/TmhRY0YXoXM\/mLqeXnVP9GyV2fx4FCvJEIQwTd+yN0LA8Q80juZK37RzbD9VGd7++8uk\/vF214Yw32n3KekJ0sbAzTBzeBkjL1QzuUi9BsVoOcvQWn84kCZTNb8Zu2ycAujqKuy9\/\/i9GRDdspYcE5DSDeDkRciUJYS6oHJdCZMWgKEf+KREG7qYsRzkl5TSx7WsOIY1Have6Lbl1Snd48YALpsJxU3AtCpLPTK8TljUBLF1rmwFEYQcRFIoYBKSndB8uCJ3HoqwJGjFQUBUdX35ymExCGT9jWTMmyXUl0YgXCQTXa5GL3s3SexBDZ1dy9uIMSTZhmV7nnK7GcTDSg+4Awl4z1DEDfrt\/cCPQIgMIVaFBUpk5U2Lz42ISx9i3U2eaGVEfIX\/Q3hw26UTTlKBAF1NMV8cNdvhUlE77tMNxT43VAbWZnUdtWii+BMbO554s8f3ShbxcyRtrmrMm3gES8eW1K67r0uAB++BBHnRIQbrjwwEkm7kXS4jpxhrATAAgdRGmzYHV+L1LQoPrONOOISbyIf21fgESgir2T8SUIfcOCxz\/hSmgVTilSytdCxeVvmhg0miXHe9RtK7M+5qfNcdUHvSlWNfNiH8Xk06P9NKk8ydnoV0wQQMJ+XL9NG+jZbt8tpk3phscS15aphp6pOrPXH7b2cu\/5BJ1VjWj5SzclANvB0Ar8geT3p0vetZGotDRg4tYk1XT6NGMdMEZjOxJdeV340SxKBB9SdCQcrEu628DpqxRbfgUVHEObPKZQH4r7MAuFw2ApdcHkXtHmCR3V5UC65GYIDKR8kPxfh4mcroJUNTkSWbiYOtYf1KrHvyw8wnQjmJzn6p2Wjz5A36mSkjM4OEQPZGeUocoSrb\/w9DZTv1KnQTtgu6gg5cmvi1L\/nTDdMvADPJipbQ6le0dO79lMrZOQA2YS6wiboNptzWZ0BXCwSOwgAwsYoJtbE4ddjG8kNIgWsc+xnUmaeqiG7gkE\/g6w8IFnO4BuszH0EYNRTR+kHGkUBG7fmLJgY2Sd0KnZprllbfctEYCTWAD1LunD5+yGL3sHMWB75yZNwXDmuvVdrIDpWsmqvEVg9G9oRqRLEAuMMYCenD2Z8Y7Z2tAMnV0hfJjVr2nSLNJ9JG1\/vT8C6YsFRYVJlxmE22aoMW7xJCsWZoEL+zC24UwIlKN+AA5eQ92pMUbSe6cVGQ+RVPsJmewnYJlDjau3WjZLe2qQWw\/Ii+fX7KJWjFRmnYdpi+ka0WYoeSMXW3upM1MHqDA4R9ofYXpkKfxjXMHYLm0XE9rDmAaM+vlQYBx9RATWKIsh\/FR7H0D\/dGMoisYCL9iHbsQmdsazpxybcaaxYvDRSE\/KHYDO8coQ3aeLnCF9uPiUF+O9C105lnCffUbRI6EnT2Kah0HOArpJnz6yL2A4UjA7o6uvsnFSRQpjRsH6i3bbJaTHUmjLQURAku9Ra5oqKmAmiUY2BalD6JX8nB3AgdOc0D1iQQkUWFQFcJAFz46SwNvA\/AoxAbUGllmQNDRCDx5oVBuED0uEwqJg2oOqTeVCm2d89DEYCoy9YJTIwJpEKmMjlwrrXkYMqJzeO5J4wuvZI7q7LJWCtEFaoAqdYEbOqFJIC+S0xnspyCdA89LAWPWQo5qZYkps2t7kVEcGlALuI2FlFcUnge7EX9sdho1cm9iu3JngqmFcf5rYnCjuQmnrZdsyxWHAyeNBc2fs6jOT+1T0R8T1I6XzsAsbNYrtArTAGWbWQhIO3Ck9roIAetSAiM5XxMbSsfmBa6oQdGCIAUqXrfefXadni6XCNHohtodJcm+A9JQ\/irhkY4ZXlgt0jSxiYWfcsAHDaqaLZlY1bXAAH0ZC2BgS7VAoCKY5XHjMJwNJUdsvaIeqCNdBWJ+gk31h2O7HVmdmPKojnilZResHgtYTIAKQ4aoFxBm4MDtkBArG8B7dQwzODzjgtuXRApMQOBxF3CPQ7dPswtC9DNWEYzxdyLiIpXdIexoQhGUNwmVNBw2kukO0FrCW+apsdKJm2YeegcqBG7Edg7WR7h4DgKMXbdGLYEeOhKzwI6V1moEG0lBbtJnjFB7vA3huxdJpKpfeKNC59PpbcCRwNnuNdvu6qrZhH0ZNvzjw7GERdbYRTAsP\/Uff5aRRsFhvTKT5Vfg0aNpvvPNkQ7BgJMV3+CAMPF9g9tyiKMmzrGyGbrcZTonHlzz2vkMLVtD2eezpcpL8mUJI6G2zsfYFqR9JAdbUUQRmlRrOhtzFCmTKYybxvOzClM2eutEWiz50vuPhZtCOKSbPK3ijBypAUIgurjPhPwWmBEbJt7bSKPSFkTGJb3XntFqbskYq18+uMsjhtWfnUDzSO6K2vZ49haGrPTRyHbZMWTRExl4mAm30NsVldCxCEBodInH9k5wkA1i4ewXaqh6RZ1t82WgfTexqy2qlBXR7VDJ5jNOhzR3MshpNbB6jRKvC3kNLImFqoutLQQQdBw5Tb8EuOICS9vn3yY4IEKY44zYGhG1wIjp9C1FP1qfE0\/DH9HprI0+Oax7GOBYm9ujvSlSzz3r6I1VgeyTeSs42aBP+wIeeiXkl80xHjzskSnHpHPJAYrSkY6wRSaLAJ2XRF\/cLq3D1sIFEEsNCBLo78N1bg0bjEZFuK9EF0z5sQdQS03iBWnyAnlprEtJ6Nstw72hGfJqfGGDpcHoHCrvK\/r4lNL23NRI8rKRvyAepBzTsIzobtrC37G1awhEu6LkYwkMDo8cynoxViKQ7oUcJNotUUpDz5DFK9UA8DeoJJ3rvI+0UYLLUly5l3i4K4+QHriKc7WXtou8DNSwkHu2P9+\/Aqyb5AaEeqfSgWoTFUkd4tlMIoCBTfk1h6JE5ibGLgZyQfwrHowN4Ah1HaYjbxbcfTQqa1UK7OxPFoVkX6\/7RqrbSK9B+biCEhWifhFoTXxJ9yk8sumyURIazO3oobu0fFALsu4WR5IKSGApjULU4eE2sq+4mjRhovpvzCi5S0tmFkwu1UoGxHGPUjPbGsk7LZOveBgKxfjXgz5CuAeqTEH7H7JWB2M2xJV68bzhVicQrmSESJM0voj\/Sl5aiT8iI5HcM3bTRXwikiKj2IGcaQ+2xJ40XgHvtA7fXRfrMvobU+61FXheL3gN2Xz2v72SHmXLvGRwa3tGFeXSv3NG7gI5eAP3mFyLSKGbQ1SQxhJc7cFgmNExcCPf3NiDTwSB2s9MVANiDnHh+li2ynzwkiLjodS6WVwioHRLQJ6FzUieG2dxHkTHOZ\/wdnAtFJveufsg3PKPgyCwTTw7hXSDv6oVZEZRu8cpCc\/HJTZXiTENTXOtDKzVnVKXTlgGtlDtqIyLQZxg52YKpeVl2DjWqrckZA4WPBtTCRfrNtmNjhA7kPwwYX6BXNoa9iFmVpg6looU7CtokzgbYFSfRe6dO8t1AkEzcO7ZA2+KTStJD8CgL9ggEsMEdEnt+J69b10ekLPYI7frbIZb3W\/9CW5K3eriKp0bX4VdqWJyjK+u8yVGTltIbhjuz8TFJB03We48PBjL9Wz4WEndR4WbPaEwzMkYnnT6LbjQPF392GSc9mCIU\/vAZhLt7ltjR6OlPwt7Do\/dFFchIlHRr7LDiClGZ2ji44I4Wrlftq02fsPK4Rzh9IiTwEmzrILQtPYK3VtcTpFPJ9sFVlJKiiAMtKgcwSJepVuwNZnJGTcy1R3\/oPiwlI3Ud05g8KZCQbMezuvqLJMyXET9EcC5u\/KnUfCaJoB\/0k0C9brPpR3plPbsYn+lbdr5UeAFuo+vRjzph9wN3NtPRh0SvDMTBhxhiEm5fhOCJsSNxTDymbqOCLk69llkPvYvf5mr2TfI1LFpzMoeqpMi5PqhRQjaRrYIPauwIETcKicKVImc926Ex5W7Zc6pFjlLJx5WOh2LUGNAWcf5MU+BIgvg1F0b\/ta5XFsRRJIkGmRCgDlwlFl1wkQ\/M3Xw2plyvo09O46Pl0TX0LFoDZ5kaegEdXCdVArFr92tSwONKYPFczNYP2YOD0QaOeb003GaDUfHGCJdmOA3EFkM3biXBrgnbfDcMh\/a0gXhRkolnJRzUQFy8MT6pGDQTHarFYINWWxwZZc0ml58E3BfSfEBnSnBTdkEnWNHRZjNB7C9URrOfrkjb1CgOBaFJLGvcjc6vhCNdYifb0IzVtS78cCEys+1TmS+axW4WGtlEwCV6+pCOBP3DpoferJE0Eqofg9WVMw06VJFXoLpoLs4ZJz\/STcbR9B3cHZvON3QR0WCot+N\/WqoPKTiRDuRMr2wLa9tgC0gYtaB7XjIemIwpO3oBmi0y0wyqcgGvVUlAh9pZKoUP0+FrIikZ2hfVP886ypTwyiaa9a92V0q\/xX3QKjanqGc0UCvukXVBbozPJHqIjbh9mrI8CyY74Lr90SG5gJTrvfV8g0QqZKTB65BChQXfTclA9mDr6eYgeGEusKc3PEh8QLGrSjRzsaByGgshlOD1QlYgABrYhaSkTNCclI5F0xR9p28GAZNwpj2EZAAWtipp208h\/M7+R6S65U3zgERpQzVXnZTa1KAo49Oj3A5kgPGhJHCHdvg3+cgYdEJH7bhgMqosuirDM4myoAN2QNcygJfsDR9EwQKIBQUgesNPunqehQ8ExBgP\/Y6xuw0CPLsL7ArBYThxSyc9CbsvO5EWTKOrbRJJDEz3OM2YSIOIB0NSA7sQq2EY9u6h8DZBA6HjWtb3UDCMoQnvQTgGFAw1MF1xaKhwocqFgYcGooJBnx9BVkg4CM4FmSiLZDMuZrEbJeaCyw8BfNcHHuo6nG5dF8KtZgWy90Ojs4T4a35Htd78Cirp+wbbH9C5ifAbNtZ2QOjcR5O3Bb0bS7JbSV4IzZRlfmLuQiA4XUczuhU8DdJYG7zGlYuzm4BED0JkHJy6rjOTZZZqPMUJhPO3wuhLu+IrYgFkv2mS2e2a0xJi7DSBoXM12PGCfUAl6Zi7udcluLWscAFqF23XFrdjAtUpYhmp\/8nUU0sxy5YJAKgLMkr8aEfLwC5Ko\/muQzt8ic5GzdYV0Tdv\/fUQhaQ2nr2wIKnrKRhNvUXofqKsJKMkLlBinSG8naxDr8m+fYpdzpzssDpF4F6+oAoA7rOANAq1wPIPenr65q6syziEJg7WFZbOkRQwJ2ahcIFggMAjrydWw\/kzgRTbwy4H196IqI6DBIjuVuO7WP1VBn3k+vF6xmS6d\/go5LVUuUiJhArggjVOVxPkDAcEsIRRNiRukAanGfe+7k1PElOO71p4WgZMAxv3oFAwuXhfSQVTREi4aeyQrjf7QCAyxHl7YxuZ6NNiFa7lw9nxHdOsRjfuGvpEGCSPIr4ZlIQTcIn74tlV18WIX9o33znrW\/NmcWLR1cIoCaO9U7ApNoxXd0uKIaB0AtomWjMMX5SJbiG6+pG6goZpZA+pl8zJ6BB+kCqonRbbjj9Z0hmH7sbZOd4Vi7NM\/RgPf8KmZgZ3NUJ+0wcLabwAF9XQAd3YrPNO0J\/bnKf0ryaSOy0WsCBBZJjTU+tKKVuzo0qIijSyg5hw6hp4WjP4mU2g0rLEbhTpe4Pk5FSHHWzP3Wbci0NcT1obv0O4lmnQfxlZNdcIsZmBr1EEc8W6h2jDAk\/vUnBQFdb3iCEpJM4IEpy\/0BUxomklx7wOpjsLfyFUFCK5\/NyFGe5BOqA3iLFMOosppB+LJOD1FCPO7gMX\/q3pOmSddXRYpgbVKMIK0UAdsHMo0CYtRMj8NEhNoLReAENPmY2APVksCczUgzyJiEBjsG0GFz2MTacSktBK3O0VQ+TGaQaRnTKM7rBqwpaVjsaolXdIqxlq2gQgVmgqg1Qm7wo7ZO7ufEvg+8zEI+k2gQxCmKJiOwwrhPi1cLh44PE9Hm4uydZoV\/9ksdOWLwkAnEYWLsykpqZUkLan3xBGdaN03GO65Z\/TIVDnQdEF1rpelw4oHtrvpj1kFXesjW2iN0K9u8EEfkgCtmN6I2LAQtcQOAI0MkkZwRFz0PniiQ2QnfCdxUuJhFS69EdI+Supcp3+GyA+DZC6XmQHMIzIXTqJxJghNYSqf9hNNl2HZV1DNskTyffwjMZXYIWf7JZK68Q\/1F5+TEfZp8ts3XIgjX+j3lzgFF+SagxMyetK7+q4lgTQFYxLovYMSqPbdDadQBDCtvAE67kNqobTJxzELR1tu+xXikwTHWyvEpgkEGBvWXdf2CnWCh7e3pMorGZEJTInrIIXKUO9aqEXeASkTWUK8nqM3ocWfkU4JdB12ZAgOKjNq6yllEkLoqYn+qFiOj8XCzK9exkGH6WegjZ+I6oaYbwb\/SzheHK0I3dE6f7Brp3QIPxONhMMKIRYjuPEVaXqSTb9q25yo29KpOrKfnNU+shzFPlmHrLIGiVlz9v6btGmG\/tYg008DDFH9252dLPNhRpg+VCaLEr9LpR9W+\/beOpBGAUdEm12tDchzCiFg7sEoyZI5xgaY\/EmX0pBCG2WVpi7HQ86qutQXhNfUENjtRPsSzrkiJLKNqC+hhojEv+7UhD5HJBEmHAUKRZDM30b3cQorsAkdzmtUag6MzsRFzpu4qg8hLseUrF3jLR3lKRbYcS09S8QptpKd4EYcv7\/A5V66wHRWfq9AAAAAElFTkSuQmCC\" alt=\"Shop &amp; Service\" class=\"ti-shop-service-inline-icon\" style=\"width:28px;height:28px;border-radius:8px;object-fit:cover;box-shadow:0 0 0 1px rgba(255,255,255,.08);\"><span>Istruzioni AI configurazione<\/span><\/h4>\n            <div class=\"ti-conf-ai-desc-label\" style=\"font-size:15px;line-height:1.45;color:#cbd5e1;margin-bottom:10px;font-weight:600;\">Fornisci istruzioni di configurazione o ottieni indicazioni tramite AI. Esempio: <i>Aggiungere foto AI a tutti i prodotti<\/i> o <i>fornisci consigli di configurazione<\/i><\/div>\n            <textarea id=\"ti-conf-ai-prompt\" class=\"ti-in\" rows=\"9\" placeholder=\"Scrivi qui la richiesta per la AI di configurazione. La risposta comparira nella stessa area, come unica chat.\" style=\"min-height:210px;line-height:1.45;\"><\/textarea>\n            <div id=\"ti-conf-ai-chat-actions\" style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:10px;margin-bottom:10px;padding:10px;border:1px solid #334155;border-radius:10px;background:#0b1220;\">\n                <button type=\"button\" id=\"ti-conf-ai-send\" class=\"ti-btn\" style=\"background:#10b981;color:#fff;font-weight:800;\" onclick=\"window.runConfigBot()\">Invia<\/button>\n                <button type=\"button\" id=\"ti-conf-ai-copy\" class=\"ti-btn ti-conf-ai-copy-btn\" data-copy-target=\"#ti-conf-ai-prompt\" style=\"background:#334155;color:#fff;font-weight:800;font-size:10px;line-height:1.05;white-space:normal;\" title=\"Copia testo istruzioni AI configurazione\">Copia testo<\/button>\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;\" onclick=\"window.verifyInstructionsArchive()\">\ud83d\udd0e Verifica istruzioni<\/button>\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;\" onclick=\"window.pickConfigBotFile()\">Carica file per AI<\/button>\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#1d4ed8;color:#fff;\" onclick=\"window.openActivityReport()\">Report Attivit\u00e0<\/button>\n                <button type=\"button\" class=\"ti-btn\" onclick=\"window.clearConfigBot()\">Pulisci<\/button>\n            <\/div>\n            <div id=\"ti-conf-ai-file\" style=\"display:none;margin-top:10px;margin-bottom:10px;font-size:14px;line-height:1.45;color:#bfdbfe;background:#0b1220;padding:10px 12px;border-radius:8px;border:1px solid #334155;\"><\/div>\n            <div id=\"ti-conf-ai-reply\" style=\"display:none;margin-top:10px;background:#000;padding:10px;border-radius:8px;border:1px solid #374151;color:#e5e7eb;white-space:pre-wrap;\"><\/div>\n            <div class=\"ti-conf-ai-desc-label\" style=\"font-size:13px;line-height:1.45;color:#9ca3af;margin-top:8px;margin-bottom:10px;font-weight:500;\">La AI usa il database della ditta corrente ed in genere la sua esperienza Shop &amp; Service.<\/div>\n            <div class=\"ti-conf-ai-desc-label\" style=\"font-size:15px;line-height:1.45;color:#cbd5e1;margin-top:10px;margin-bottom:10px;font-weight:600;\">Per le funzioni <b>Genera foto AI<\/b> puoi indicare eventuali istruzioni di dettaglio per ottenere immagini piu coerenti con le intenzioni dell utente. Esempio: <i>sfondo chiaro, stile elegante, foto ravvicinata, ambientazione realistica<\/i>.<\/div>\n            <textarea id=\"ti-conf-ai-photo-detail\" class=\"ti-in\" rows=\"3\" placeholder=\"Istruzioni di dettaglio per la generazione foto AI (opzionale)\"><\/textarea>\n            <div id=\"ti-conf-ai-image-actions\" style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:10px;\">\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#059669;color:#fff;\" onclick=\"window.runBatchAIGen('Prodotti')\">Genera foto AI Prodotti<\/button>\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#0f766e;color:#fff;\" onclick=\"window.runBatchAIGen('Servizi')\">Genera foto AI Servizi<\/button>\n            <\/div>\n            <div id=\"ti-configurator-tips-control\" style=\"${(window.isConfiguratorUser && window.isConfiguratorUser()) ? 'display:flex' : 'display:none'};gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap;margin-top:12px;padding:10px;border:1px solid #334155;border-radius:10px;background:#0f172a;color:#cbd5e1;font-size:12px;\">\n                <label style=\"display:flex;align-items:center;gap:7px;cursor:pointer;\"><input type=\"checkbox\" id=\"ti-cfg-tips-enabled\" onchange=\"window.handleConfiguratorTipsFlagChange(this)\"> Mostra popup <b>Consigli operativi per configuratore\/amministratore<\/b> ai prossimi accessi<\/label>\n                <span id=\"ti-cfg-tips-status\" style=\"color:#93c5fd;\">Popup consigli operativi attivo.<\/span>\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#475569;color:#fff;font-size:12px;padding:7px 10px;\" onclick=\"window.showConfiguratorOperationalTipsPopup(true)\">Apri consigli ora<\/button>\n            <\/div>\n        <\/div>`;\n        const confCnt = gE('ti-conf-cnt'); if (confCnt) { confCnt.innerHTML = html; window.__ensurePluginDateInputs(); window.initPluginDateInputs(confCnt); if (window.ensureAuthorizedConfigInputsEditable512) window.ensureAuthorizedConfigInputsEditable512(confCnt); window.initConfigTopScrollbars(confCnt); window.bindConfigTableUX(confCnt); if (window.ensureAuthorizedConfigInputsEditable512) window.ensureAuthorizedConfigInputsEditable512(confCnt); if (!window.__configRenderedOnce || window.configForceTop) { confCnt.scrollTop = 0; document.querySelectorAll('.ti-cfg-table-container').forEach(el => { el.scrollTop = 0; el.scrollLeft = 0; }); document.querySelectorAll('.ti-cfg-top-scroll, .ti-cfg-bottom-scroll').forEach(el => { el.scrollLeft = 0; }); window.__configRenderedOnce = true; window.configForceTop = false; } else { try { confCnt.scrollTop = cfgScrollState.mainTop || 0; document.querySelectorAll('details.ti-cfg-details').forEach(function(det){ const tbl = det.dataset ? (det.dataset.tbl || '') : ''; const st = cfgScrollState.tables && cfgScrollState.tables[tbl]; const wrap = det.querySelector ? det.querySelector('.ti-cfg-table-container') : null; if (st && wrap) { wrap.scrollLeft = st.left || 0; wrap.scrollTop = st.top || 0; const topBar = wrap.previousElementSibling && wrap.previousElementSibling.classList && wrap.previousElementSibling.classList.contains('ti-sync-hscroll-top') ? wrap.previousElementSibling : null; const bottomBar = wrap.nextElementSibling && wrap.nextElementSibling.classList && wrap.nextElementSibling.classList.contains('ti-sync-hscroll-bottom') ? wrap.nextElementSibling : null; if (topBar) topBar.scrollLeft = wrap.scrollLeft; if (bottomBar) bottomBar.scrollLeft = wrap.scrollLeft; } }); } catch(e) {} } if (window.restoreConfigFilterFocus) setTimeout(window.restoreConfigFilterFocus, 0); if (window.syncConfiguratorOperationalTipsFlagUI) setTimeout(window.syncConfiguratorOperationalTipsFlagUI, 0); }\n        if (window.keepUserCommunicationPopupsInFront) window.keepUserCommunicationPopupsInFront();\n    };\n\n\n    window.isValidConfigDate = function(v) { const s = String(v == null ? '' : v).trim(); if (!s || \/^DD\\\/MM\\\/YYYY$\/i.test(s) || s === '--\/--\/----') return true; const m = s.match(\/^(\\d{1,2})\\\/(\\d{1,2})\\\/(\\d{4})$\/); if (m) { const d = new Date(Number(m[3]), Number(m[2])-1, Number(m[1])); return d && d.getFullYear()===Number(m[3]) && d.getMonth()===Number(m[2])-1 && d.getDate()===Number(m[1]); } if (\/^\\d{1,2}[\\\/.-]\\d{1,2}[\\\/.-]\\d{2,4}$\/.test(s)) return false; return true; };\n    window.isValidConfigTime = function(v) { const s = String(v == null ? '' : v).trim(); if (!s || \/^HH:MM$\/i.test(s) || s === '--:--') return true; const m = s.match(\/^(\\d{1,2})[:.](\\d{2})$\/); if (m) return Number(m[1]) >= 0 && Number(m[1]) <= 23 && Number(m[2]) >= 0 && Number(m[2]) <= 59; return true; };\n    window.isValidConfigNumber = function(v) { const s = String(v == null ? '' : v).trim().replace(\/[\u20ac\\s]\/g,'').replace(',', '.'); return !s || !isNaN(Number(s)); };\n    window.isEmptyOrStandardDayConfigTime = function(v) { const s = String(v == null ? '' : v).trim(); return !s || \/^HH:MM$\/i.test(s) || s === '--:--'; };\n    window.isEmptyConfigPeriodBoundary = function(v, kind) { const s = String(v == null ? '' : v).trim(); if (!s) return true; if (kind === 'ora' && (\/^HH:MM$\/i.test(s) || s === '--:--')) return true; if (kind === 'data' && (\/^DD\\\/MM\\\/YYYY$\/i.test(s) || s === '--\/--\/----')) return true; return false; };\n    window.getConfigPeriodPairMeta = function(key) {\n        const n = window.normalizeConfigRelationName ? window.normalizeConfigRelationName(key) : String(key || '').toLowerCase();\n        const c = String(n || '').replace(\/[^a-z0-9]+\/g, '');\n        const dateStart = ['dagg','dadata','datada','datainizio','inizioperiodo','iniziovalidita','dal'];\n        const dateEnd = ['agg','adata','dataa','datafine','fineperiodo','finevalidita','al'];\n        const timeStart = ['daora','orainizio','inizioora','orarioda','dalleore','dalle'];\n        const timeEnd = ['aora','orafine','fineora','orarioa','alleore','alle'];\n        if (dateStart.includes(c)) return {kind:'data', side:'start', counter:['agg','a gg','a data','data a','data fine','fine periodo','fine validita','al']};\n        if (dateEnd.includes(c)) return {kind:'data', side:'end', counter:['dagg','da gg','da data','data da','data inizio','inizio periodo','inizio validita','dal']};\n        if (timeStart.includes(c)) return {kind:'ora', side:'start', counter:['aora','a ora','ora fine','fine ora','orario a','alle ore','alle']};\n        if (timeEnd.includes(c)) return {kind:'ora', side:'end', counter:['daora','da ora','ora inizio','inizio ora','orario da','dalle ore','dalle']};\n        return null;\n    };\n    window.getConfigPeriodCounterpartKey = function(row, meta) {\n        if (!row || !meta) return '';\n        const counters = (meta.counter || []).map(x => String(window.normalizeConfigRelationName ? window.normalizeConfigRelationName(x) : x).replace(\/[^a-z0-9]+\/g, ''));\n        for (const k in row) { const c = String(window.normalizeConfigRelationName ? window.normalizeConfigRelationName(k) : k).replace(\/[^a-z0-9]+\/g, ''); if (counters.includes(c)) return k; }\n        return '';\n    };\n    window.getConfigEmptyTimeWarnings = function() {\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return '';\n        const labels = [];\n        for (const tName in window.currentDbData.Tabelle) {\n            const rows = Array.isArray(window.currentDbData.Tabelle[tName]) ? window.currentDbData.Tabelle[tName] : Object.values(window.currentDbData.Tabelle[tName] || {});\n            for (let i = 0; i < rows.length; i++) {\n                const row = rows[i] || {};\n                for (const key in row) {\n                    const val = row[key];\n                    if (val && typeof val === 'object') continue;\n                    const n = window.normalizeConfigRelationName ? window.normalizeConfigRelationName(key) : String(key || '').toLowerCase();\n                    const isHourlyAmount = \/(^| )(costo|prezzo|tariffa|importo) orari[oa]( |$)\/.test(n)\n                        || \/(^| )(costo|prezzo|tariffa|importo) ora( |$)\/.test(n)\n                        || \/(^| )(costo|prezzo|tariffa|importo) per ora( |$)\/.test(n);\n                    const isTime = !isHourlyAmount && (\/(^| )(ora|orario)( |$)\/.test(n) || n.includes('ora appuntamento') || n.includes('ora variazione'));\n                    if (isTime && window.isEmptyOrStandardDayConfigTime(val)) labels.push('tabella ' + tName + ', riga ' + (i + 1) + ', campo ' + key);\n                }\n            }\n        }\n        if (!labels.length) return '';\n        const shown = labels.slice(0, 8).join('\\n- ');\n        const more = labels.length > 8 ? '\\n- ... altri ' + (labels.length - 8) + ' campi orari' : '';\n        return 'Seguono informazioni su campi non necessari, ma che sarebbe utile compilare.\\n\\nAvviso campi orari vuoti o --:--\\n- ' + shown + more + '\\n\\nOrario eventualmente riportato in note config, informazioni o istruzioni. In mancanza viene considerato l orario standard 09:00 - 18:00.\\nVerificare direttamente con la ditta la disponibilita specifica.';\n    };\n    window.extractConfigWarningTarget = function(issueOrMessage) {\n        const issue = (issueOrMessage && typeof issueOrMessage === 'object') ? issueOrMessage : {message:String(issueOrMessage || '')};\n        const msg = String(issue.message || issue.msg || issueOrMessage || '');\n        let table = String(issue.table || '');\n        let row = (typeof issue.row !== 'undefined') ? parseInt(issue.row, 10) : 0;\n        let field = String(issue.field || '');\n        if (!table) {\n            const mt = msg.match(\/tabella\\s+([^,\\.]+)\/i);\n            if (mt && mt[1]) table = mt[1].trim();\n        }\n        if (!row) {\n            const mr = msg.match(\/riga\\s+(\\d+)\/i);\n            if (mr && mr[1]) row = parseInt(mr[1], 10) || 0;\n        }\n        if (!field) {\n            const mf = msg.match(\/campo\\s+([^\\.]+?)(?:\\.|$)\/i);\n            if (mf && mf[1]) field = mf[1].trim();\n        }\n        return {table:table, row:row, field:field, message:msg};\n    };\n\n    window.openConfigTableForWarning = function(tableName, rowNum, fieldName) {\n        const tbl = String(tableName || '').trim();\n        if (!tbl) return false;\n        try { window.closeModal && window.closeModal('ti-alert-ov'); } catch(e) {}\n        if (window.openModal) window.openModal('ti-conf-ov');\n        if (window.renderConfig) window.renderConfig();\n        let det = null;\n        document.querySelectorAll('details.ti-cfg-details').forEach(function(d){\n            if (!det && d.dataset && String(d.dataset.tbl || '') === tbl) det = d;\n        });\n        if (!det) {\n            const nt = window.normalizeConfigRelationName ? window.normalizeConfigRelationName(tbl) : tbl.toLowerCase();\n            document.querySelectorAll('details.ti-cfg-details').forEach(function(d){\n                const dt = d && d.dataset ? String(d.dataset.tbl || '') : '';\n                const nd = window.normalizeConfigRelationName ? window.normalizeConfigRelationName(dt) : dt.toLowerCase();\n                if (!det && nd === nt) det = d;\n            });\n        }\n        if (!det) { window.tiAlert && window.tiAlert('Tabella da modificare non trovata: ' + window.escapeHtml(tbl)); return false; }\n        det.open = true;\n        det.style.boxShadow = '0 0 0 3px rgba(251,191,36,.9)';\n        setTimeout(function(){ det.style.boxShadow = ''; }, 4000);\n        let target = det;\n        const wantedIdx = rowNum ? (parseInt(rowNum, 10) - 1) : -1;\n        const wantedField = String(fieldName || '').trim();\n        if (wantedIdx >= 0) {\n            const inputs = det.querySelectorAll('[data-idx]');\n            inputs.forEach(function(el){\n                if (target !== det) return;\n                const idxOk = parseInt(el.getAttribute('data-idx') || '-1', 10) === wantedIdx;\n                const keyOk = !wantedField || String(el.getAttribute('data-key') || '').toLowerCase() === wantedField.toLowerCase();\n                if (idxOk && keyOk) target = el;\n            });\n            if (target === det) {\n                const rowEl = det.querySelector('tr[data-row-idx=\"' + wantedIdx + '\"]');\n                if (rowEl) target = rowEl;\n            }\n        }\n        setTimeout(function(){\n            try { target.scrollIntoView({behavior:'smooth', block:'center', inline:'center'}); } catch(e) { target.scrollIntoView && target.scrollIntoView(); }\n            if (target && target.focus) target.focus({preventScroll:true});\n            if (target && target.style) {\n                target.style.setProperty('outline', '3px solid #fbbf24', 'important');\n                target.style.setProperty('outline-offset', '2px', 'important');\n                setTimeout(function(){ target.style.removeProperty('outline'); target.style.removeProperty('outline-offset'); }, 5000);\n            }\n        }, 120);\n        return true;\n    };\n\n    window.showConfigValidationWarning = function(issues, partialSaveMode) {\n        const arr = Array.isArray(issues) ? issues : (issues ? [issues] : []);\n        if (!arr.length) return;\n        const first = window.extractConfigWarningTarget(arr[0]);\n        const shown = arr.slice(0, 6).map(function(it){\n            const t = window.extractConfigWarningTarget(it);\n            return '<li style=\"margin-bottom:8px;\">' + window.escapeHtml(t.message || String(it || 'Warning configurazione')) + '<\/li>';\n        }).join('');\n        const more = arr.length > 6 ? '<li style=\"margin-bottom:8px;\">... altri ' + (arr.length - 6) + ' warning<\/li>' : '';\n        const encTable = encodeURIComponent(first.table || '');\n        const encField = encodeURIComponent(first.field || '');\n        const btn = first.table ? '<br><button type=\"button\" class=\"ti-btn\" style=\"margin-top:10px;background:#2563eb;color:#fff;\" onclick=\"window.openConfigTableForWarning(decodeURIComponent(\\'' + encTable + '\\'),' + String(first.row || 0) + ',decodeURIComponent(\\'' + encField + '\\'))\">Apri tabella ' + window.escapeHtml(first.table) + '<\/button>' : '';\n        const intro = partialSaveMode ? 'Le tabelle valide sono state salvate o verranno salvate. La tabella indicata dal warning resta da correggere e non viene sovrascritta.' : 'Correggi il valore indicato prima di salvare quella tabella.';\n        window.tiAlert('<div class=\"ti-config-warning-content-668\" style=\"text-align:left;font-size:12px;line-height:1.25;\"><b style=\"font-size:13px;display:block;margin-bottom:4px;\">Warning configurazione<\/b><div style=\"font-size:12px;line-height:1.25;color:#e5e7eb;margin-bottom:6px;\">' + window.escapeHtml(intro) + '<\/div><ul style=\"margin-top:7px;padding-left:16px;font-size:12px;line-height:1.25;\">' + shown + more + '<\/ul>' + btn + '<\/div>');\n        try { if (window.tiApplyConfigWarningAlertStyle668) window.tiApplyConfigWarningAlertStyle668(); } catch(e) {}\n        setTimeout(function(){ try { if (window.tiApplyConfigWarningAlertStyle668) window.tiApplyConfigWarningAlertStyle668(); } catch(e) {} }, 0);\n    };\n\n    window.collectConfigValidationIssues = function() {\n        const issues = [];\n        if (!window.currentDbData || !window.currentDbData.Tabelle) return issues;\n        const allowedState = \/^(attivo|operativo|demo|configurazione|sospesa|cancellato)?$\/i;\n        const pushIssue = function(tName, rowNumber, key, message) {\n            issues.push({table:String(tName || ''), row:rowNumber || 0, field:String(key || ''), message:String(message || '')});\n        };\n        if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues();\n        window.configTimeFallbackWarning = window.getConfigEmptyTimeWarnings ? window.getConfigEmptyTimeWarnings() : '';\n        for (const tName in window.currentDbData.Tabelle) {\n            const rows = Array.isArray(window.currentDbData.Tabelle[tName]) ? window.currentDbData.Tabelle[tName] : Object.values(window.currentDbData.Tabelle[tName] || {});\n            for (let i = 0; i < rows.length; i++) {\n                const row = rows[i] || {};\n                for (const key in row) {\n                    const val = row[key];\n                    if (val && typeof val === 'object') continue;\n                    const n = window.normalizeConfigRelationName ? window.normalizeConfigRelationName(key) : String(key || '').toLowerCase();\n                    const label = `tabella ${tName}, riga ${i + 1}, campo ${key}`;\n                    const periodMeta = window.getConfigPeriodPairMeta ? window.getConfigPeriodPairMeta(key) : null;\n                    if (periodMeta) {\n                        const counterKey = window.getConfigPeriodCounterpartKey ? window.getConfigPeriodCounterpartKey(row, periodMeta) : '';\n                        if (!counterKey) {\n                            const needed = periodMeta.kind === 'data' ? (periodMeta.side === 'start' ? 'A data \/ A gg' : 'Da data \/ Da gg') : (periodMeta.side === 'start' ? 'A ora' : 'Da ora');\n                            pushIssue(tName, i + 1, key, 'Periodo non completo in ' + label + '. I periodi devono avere due campi: da data a data e\/o da ora ad ora. Campo richiesto: ' + needed + '.');\n                            continue;\n                        }\n                        const thisEmpty = window.isEmptyConfigPeriodBoundary ? window.isEmptyConfigPeriodBoundary(val, periodMeta.kind) : !String(val || '').trim();\n                        const otherEmpty = window.isEmptyConfigPeriodBoundary ? window.isEmptyConfigPeriodBoundary(row[counterKey], periodMeta.kind) : !String(row[counterKey] || '').trim();\n                        if (thisEmpty !== otherEmpty) {\n                            pushIssue(tName, i + 1, key, 'Periodo non completo in tabella ' + tName + ', riga ' + (i + 1) + '. Compila entrambi i campi collegati: ' + key + ' e ' + counterKey + ', oppure lascia entrambi vuoti. Puoi indicare anche testo libero, che verra interpretato successivamente.');\n                            continue;\n                        }\n                    }\n                    const isDate = \/(^| )(data|gg)( |$)\/.test(n) || n.includes('data appuntamento') || n.includes('data variazione');\n                    const isHourlyAmount = \/(^| )(costo|prezzo|tariffa|importo) orari[oa]( |$)\/.test(n)\n                        || \/(^| )(costo|prezzo|tariffa|importo) ora( |$)\/.test(n)\n                        || \/(^| )(costo|prezzo|tariffa|importo) per ora( |$)\/.test(n);\n                    const isTime = !isHourlyAmount && (\/(^| )(ora|orario)( |$)\/.test(n) || n.includes('ora appuntamento') || n.includes('ora variazione'));\n                    if (isDate && !window.isValidConfigDate(val)) { pushIssue(tName, i + 1, key, 'Formato data non valido in ' + label + '. Usa DD\/MM\/YYYY per una data precisa, oppure testo libero descrittivo da interpretare successivamente.'); continue; }\n                    if (isTime && !window.isValidConfigTime(val)) { pushIssue(tName, i + 1, key, 'Formato ora non valido in ' + label + '. Usa HH:MM per un orario preciso, oppure testo libero descrittivo da interpretare successivamente.'); continue; }\n                    if (isHourlyAmount || \/^(prezzo|costo|iva vat|iva|quantita|num ordine|num fattura|num corrispettivo)$\/.test(n) || \/(^| )(prezzo|costo|iva vat|quantita|num ordine|num fattura|num corrispettivo)( |$)\/.test(n)) {\n                        if (!window.isValidConfigNumber(val)) { pushIssue(tName, i + 1, key, 'Valore numerico non valido in ' + label + '. Usa un numero.'); continue; }\n                    }\n                    if (n === 'stato' || n === 'status') {\n                        const canonState = window.normalizeConfigStateValue ? window.normalizeConfigStateValue(val) : String(val || '').trim();\n                        if (canonState && ['Attivo','Operativo','Demo','Configurazione','Sospesa','Cancellato'].includes(canonState)) row[key] = canonState;\n                        if (!allowedState.test(String(row[key] || '').trim())) { pushIssue(tName, i + 1, key, 'Stato non valido in ' + label + '. Usa Attivo, Operativo, Demo, Configurazione, Sospesa o Cancellato.'); continue; }\n                    }\n                }\n            }\n        }\n        return issues;\n    };\n\n    window.validateConfigValues = function() {\n        const issues = window.collectConfigValidationIssues ? window.collectConfigValidationIssues() : [];\n        if (issues.length) { window.showConfigValidationWarning && window.showConfigValidationWarning(issues, false); return false; }\n        return true;\n    };\n\n    window.getConfigChangedTables = function() {\n        const map = {};\n        const add = function(t) { t = String(t || '').trim(); if (t) map[t] = true; };\n        try {\n            if (window.modifiedFields && typeof window.modifiedFields.forEach === 'function') {\n                window.modifiedFields.forEach(function(k) {\n                    k = String(k || '');\n                    const p = k.indexOf('-');\n                    if (p > 0) add(k.slice(0, p));\n                });\n            }\n        } catch(e) {}\n        try {\n            (window.tiConfigDeletedRowsManifest || []).forEach(function(it){ if (it && it.table) add(it.table); });\n        } catch(e) {}\n        try {\n            Object.keys(window.tiConfigDeletedTableSnapshots || {}).forEach(add);\n        } catch(e) {}\n        return Object.keys(map);\n    };\n\n    window.saveConfig = function() {\n        const saveNow585 = Date.now ? Date.now() : (new Date()).getTime();\n        if (window.__saveConfigInFlight) {\n            const started585 = parseInt(window.__tiSaveConfigStartedAt585 || window.__tiSaveConfigStartedAt || 0, 10) || 0;\n            if (started585 && (saveNow585 - started585) > 45000) {\n                try { if (window.tiForceUnlockConfigSave585) window.tiForceUnlockConfigSave585('Salvataggio precedente sbloccato automaticamente.'); } catch(e) {}\n            } else {\n                return;\n            }\n        }\n        if(!window.currentDbData) return;\n        if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory();\n        for(let t in window.currentDbData.Tabelle) { if(!Array.isArray(window.currentDbData.Tabelle[t])) window.currentDbData.Tabelle[t] = Object.values(window.currentDbData.Tabelle[t]); }\n        if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues();\n        if (window.validateConfigUniqueUsernames && !window.validateConfigUniqueUsernames()) return;\n        const configValidationIssues = window.collectConfigValidationIssues ? window.collectConfigValidationIssues() : [];\n        if (configValidationIssues.length) {\n            window.tiConfigValidationIssues = configValidationIssues;\n            if (window.showConfigValidationWarning) window.showConfigValidationWarning(configValidationIssues, true);\n        }\n        const btn = gE('lbl-conf-save');\n        let oldText = btn ? String(btn.innerText || '').trim() : 'Salva Modifiche';\n        if (!oldText || \/salvataggio\/i.test(oldText)) oldText = 'Salva Modifiche';\n        let oldBg = btn ? btn.style.background : '';\n        if (\/d97706|217\\s*,\\s*119\\s*,\\s*6\/i.test(String(oldBg || ''))) oldBg = '';\n        const restoreBtn = function(renderAfter){\n            window.__saveConfigInFlight = false;\n            window.__tiSaveConfigStartedAt585 = 0;\n            window.__tiSaveConfigStartedAt = 0;\n            try { if (window.__tiSaveConfigHardTimer585) { clearTimeout(window.__tiSaveConfigHardTimer585); window.__tiSaveConfigHardTimer585 = null; } } catch(e) {}\n            if (btn) {\n                try { btn.disabled = false; btn.removeAttribute('disabled'); } catch(e) { btn.disabled = false; }\n                try { btn.removeAttribute('aria-busy'); btn.removeAttribute('data-ti-saving'); } catch(e) {}\n                btn.style.pointerEvents = '';\n                btn.innerText = oldText;\n                try { btn.style.setProperty('background', '#374151', 'important'); btn.style.setProperty('background-color', '#374151', 'important'); btn.style.setProperty('color', '#fff', 'important'); } catch(e) { btn.style.background = oldBg || '#374151'; }\n            }\n            try { if (window.resetConfigSaveButtonUi668 && !window.__saveConfigInFlight) window.resetConfigSaveButtonUi668(false); } catch(e) {}\n            if (renderAfter) {\n                window.renderConfig();\n                window.keepPluginModalInFront('ti-conf-ov');\n                window.keepUserCommunicationPopupsInFront();\n            }\n        };\n        const hardUnlockSaveUi568 = function(reason){\n            try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n            try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, reason || 'Salvataggio interrotto', {notifyUser:false}); } catch(e) {}\n            try {\n                document.querySelectorAll('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(el){\n                    if (el && el.parentNode) el.parentNode.removeChild(el);\n                });\n            } catch(e) {}\n            try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n            restoreBtn(false);\n            try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n        };\n        window.tiForceUnlockConfigSave585 = hardUnlockSaveUi568;\n        window.tiForceUnlockConfigSave587 = hardUnlockSaveUi568;\n        window.tiForceUnlockConfigSave588 = hardUnlockSaveUi568;\n        const saveDiag588 = function(msg){\n            try {\n                window.__tiLastConfigSaveDiag588 = '[' + (new Date()).toLocaleTimeString() + '] ' + String(msg || '');\n                if (window.console && console.log) console.log('TI save config 588:', msg);\n            } catch(e) {}\n        };\n        const finishSaveUi588 = function(reason, renderAfter){\n            try { saveDiag588(reason || 'fine salvataggio'); } catch(e) {}\n            try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n            try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(true, reason || 'Salvataggio configurazione concluso', {notifyUser:false}); } catch(e) {}\n            try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n            try { restoreBtn(!!renderAfter); } catch(e) { hardUnlockSaveUi568(reason || 'Fine salvataggio configurazione'); }\n            try {\n                const b588 = gE('lbl-conf-save');\n                if (b588) {\n                    try { b588.disabled = false; b588.removeAttribute('disabled'); } catch(e) { b588.disabled = false; }\n                    try { b588.removeAttribute('aria-busy'); b588.removeAttribute('data-ti-saving'); } catch(e) {}\n                    b588.style.pointerEvents = '';\n                    b588.innerText = 'Salva Modifiche';\n                    try { b588.style.setProperty('background', '#374151', 'important'); b588.style.setProperty('background-color', '#374151', 'important'); b588.style.setProperty('color', '#fff', 'important'); } catch(e) { b588.style.background = '#374151'; }\n                }\n                try { if (window.resetConfigSaveButtonUi668 && !window.__saveConfigInFlight) window.resetConfigSaveButtonUi668(false); } catch(e) {}\n            } catch(e) {}\n            try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n        };\n        window.__saveConfigInFlight = true;\n        window.__tiSaveConfigStartedAt585 = saveNow585;\n        window.__tiSaveConfigStartedAt = saveNow585;\n        try { if (window.__tiSaveConfigHardTimer585) clearTimeout(window.__tiSaveConfigHardTimer585); } catch(e) {}\n        window.__tiSaveConfigHardTimer585 = setTimeout(function(){\n            if (!window.__saveConfigInFlight) return;\n            hardUnlockSaveUi568('Salvataggio configurazione sbloccato');\n            try { if (window.tiAlert) window.tiAlert('Il salvataggio non ha restituito una chiusura corretta entro il tempo previsto. Il blocco e stato rimosso. Verifica se le modifiche risultano salvate e, se necessario, ripeti Salva Modifiche.'); } catch(e) {}\n        }, 75000);\n        if (btn) {\n            btn.disabled = true;\n            try { btn.setAttribute('aria-busy', 'true'); btn.setAttribute('data-ti-saving', '1'); } catch(e) {}\n            btn.style.pointerEvents = 'none';\n            btn.innerText = 'Salvataggio...';\n            try { btn.style.setProperty('background', '#d97706', 'important'); btn.style.setProperty('background-color', '#d97706', 'important'); btn.style.setProperty('color', '#fff', 'important'); } catch(e) { btn.style.background = '#d97706'; }\n        }\n        if (window.refreshConfigDeletedTableSnapshotsFromCurrentData) window.refreshConfigDeletedTableSnapshotsFromCurrentData();\n        const pendingDeletedManifest = JSON.parse(JSON.stringify(window.tiConfigDeletedRowsManifest || []));\n        const pendingDeletedSnapshots = JSON.parse(JSON.stringify(window.tiConfigDeletedTableSnapshots || {}));\n        const pendingDeletedClientTotal = Math.max(parseInt(window.tiConfigDeletedRowsCountTotal || 0, 10) || 0, pendingDeletedManifest.length || 0);\n        const pendingDefinitiveDelete = pendingDeletedClientTotal > 0 || pendingDeletedManifest.length > 0 || Object.keys(pendingDeletedSnapshots || {}).length > 0;\n        const changedTablesForSave = window.getConfigChangedTables ? window.getConfigChangedTables() : [];\n        const hasRealConfigChanges587 = function(){\n            try {\n                if (!changedTablesForSave || !changedTablesForSave.length) return false;\n                if (!window.configOriginalDbData || !window.configOriginalDbData.Tabelle || !window.currentDbData || !window.currentDbData.Tabelle) return true;\n                for (let i587 = 0; i587 < changedTablesForSave.length; i587++) {\n                    const t587 = changedTablesForSave[i587];\n                    const cur587 = window.currentDbData.Tabelle ? window.currentDbData.Tabelle[t587] : undefined;\n                    const old587 = window.configOriginalDbData.Tabelle ? window.configOriginalDbData.Tabelle[t587] : undefined;\n                    if (JSON.stringify(cur587 === undefined ? null : cur587) !== JSON.stringify(old587 === undefined ? null : old587)) return true;\n                }\n                return false;\n            } catch(e) {\n                return true;\n            }\n        };\n        const realChangesForSave587 = hasRealConfigChanges587();\n        \/\/ 30.9.664: se non ci sono modifiche reali e non ci sono cancellazioni pendenti, non inviare il database al server.\n        \/\/ Evita il blocco \"Salvataggio...\" quando l'utente preme Salva Modifiche senza aver cambiato dati o con campi marcati sporchi per errore.\n        if (!pendingDefinitiveDelete && !realChangesForSave587) {\n            try { if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); } catch(e) {}\n            try { window.configWasSaved = true; } catch(e) {}\n            try { window.__saveConfigInFlight = false; window.__tiSaveConfigStartedAt585 = 0; window.__tiSaveConfigStartedAt = 0; } catch(e) {}\n            try { if (window.__tiSaveConfigHardTimer585) { clearTimeout(window.__tiSaveConfigHardTimer585); window.__tiSaveConfigHardTimer585 = null; } } catch(e) {}\n            try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n            try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n            try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Nessuna modifica da salvare', {notifyUser:false}); } catch(e) {}\n            try {\n                document.querySelectorAll('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(el){\n                    if (el && el.parentNode) el.parentNode.removeChild(el);\n                });\n            } catch(e) {}\n            try {\n                if (btn) {\n                    btn.disabled = false;\n                    btn.style.pointerEvents = '';\n                    btn.innerText = 'Salva Modifiche';\n                    try { btn.style.removeProperty('background'); btn.style.removeProperty('background-color'); } catch(e) { btn.style.background = ''; }\n                }\n                try { if (window.resetConfigSaveButtonUi668) window.resetConfigSaveButtonUi668(true); } catch(e) {}\n            } catch(e) {}\n            try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n            try { if (window.tiAlert) window.tiAlert('Nessuna modifica da salvare.'); } catch(e) {}\n            return;\n        }\n        const postConfigForm = function(fd, customSignal) {\n            \/\/ 30.9.664: richiesta isolata per il salvataggio configurazione.\n            \/\/ Non eredita controller\/abort di altri processi lunghi e non lascia la UI bloccata se il server non risponde.\n            return new Promise(function(resolve, reject){\n                const ctrl = (typeof AbortController !== 'undefined') ? new AbortController() : null;\n                let finished = false;\n                const timeoutMs = 65000;\n                const timer = setTimeout(function(){\n                    if (finished) return;\n                    finished = true;\n                    try { if (ctrl) ctrl.abort(); } catch(e) {}\n                    reject(new Error('Timeout salvataggio configurazione. La richiesta e stata interrotta lato browser: verifica se i dati risultano salvati, poi riapri la Configurazione.'));\n                }, timeoutMs);\n                try { saveDiag588('richiesta server avviata'); } catch(e) {}\n                fetch(window.tiUrl, {method:'POST', body:fd, signal: ctrl ? ctrl.signal : undefined, credentials:'same-origin', cache:'no-store'}).then(function(r){\n                    return r.text().then(function(txt){\n                        try { saveDiag588('risposta server HTTP ' + r.status + ', bytes=' + String(txt || '').length); } catch(e) {}\n                        if (r.status === 413) throw new Error('Errore 413: Limite superato.');\n                        if (!r.ok) throw new Error('Errore Server ' + r.status + (txt ? ': ' + txt.slice(0, 220) : ''));\n                        try { return JSON.parse(txt); }\n                        catch(e) { throw new Error('Risposta salvataggio non valida: ' + String(txt || '').slice(0, 220)); }\n                    });\n                }).then(function(data){\n                    if (finished) return;\n                    finished = true;\n                    clearTimeout(timer);\n                    resolve(data);\n                }).catch(function(err){\n                    if (finished) return;\n                    finished = true;\n                    clearTimeout(timer);\n                    reject(err);\n                });\n            });\n        };\n        const dbName = gE('ti-ditta') ? gE('ti-ditta').value : '';\n        const currentVersion = function(){ return (window.currentDbData && window.currentDbData.__db_version) ? window.currentDbData.__db_version : ''; };\n        const makeSaveForm = function(dbVersion, includeDeleteManifest) {\n            const fdSave = new FormData();\n            fdSave.append('ti_action', 'ti_ai_save_full_db');\n            fdSave.append('db', dbName);\n            const changedForPayload570 = window.getConfigChangedTables ? window.getConfigChangedTables() : [];\n            let savePayload570 = window.currentDbData || {};\n            if (changedForPayload570.length && window.currentDbData && window.currentDbData.Tabelle) {\n                const compactTables570 = {};\n                changedForPayload570.forEach(function(t){ if (window.currentDbData.Tabelle && window.currentDbData.Tabelle[t]) compactTables570[t] = window.currentDbData.Tabelle[t]; });\n                if (Object.keys(compactTables570).length) {\n                    savePayload570 = {Database: window.currentDbData.Database || dbName, __db_version: window.currentDbData.__db_version || '', Tabelle: compactTables570};\n                    fdSave.append('compact_save', '1');\n                }\n            }\n            fdSave.append('payload', utoa(JSON.stringify(savePayload570 || {})));\n            fdSave.append('light_save', '1');\n            fdSave.append('response_light', '1');\n            fdSave.append('save_mode', 'light');\n            fdSave.append('db_version', dbVersion || currentVersion() || '');\n            fdSave.append('changed_tables', utoa(JSON.stringify(changedForPayload570)));\n            if (includeDeleteManifest) {\n                fdSave.append('deleted_rows', utoa(JSON.stringify(pendingDeletedManifest)));\n                fdSave.append('deleted_table_snapshots', utoa(JSON.stringify(pendingDeletedSnapshots)));\n                fdSave.append('deleted_rows_client_total', String(pendingDeletedClientTotal || 0));\n            } else {\n                fdSave.append('deleted_rows', utoa(JSON.stringify([])));\n                fdSave.append('deleted_table_snapshots', utoa(JSON.stringify({})));\n                fdSave.append('deleted_rows_client_total', '0');\n                fdSave.append('delete_already_applied', '1');\n            }\n            return fdSave;\n        };\n        const makeDeleteForm = function() {\n            const fdDelete = new FormData();\n            fdDelete.append('ti_action', 'ti_ai_config_delete_rows_definitive');\n            fdDelete.append('db', dbName);\n            fdDelete.append('db_version', currentVersion() || '');\n            fdDelete.append('deleted_rows', utoa(JSON.stringify(pendingDeletedManifest)));\n            fdDelete.append('deleted_table_snapshots', utoa(JSON.stringify(pendingDeletedSnapshots)));\n            fdDelete.append('deleted_rows_client_total', String(pendingDeletedClientTotal || 0));\n            return fdDelete;\n        };\n\n        if (pendingDefinitiveDelete && window.showProgressPopup) {\n            window.showProgressPopup('Cancellazione definitiva configurazione', 'Clessidra attiva. Avvio la cancellazione definitiva dei record selezionati dal database ditta, poi salvo le modifiche residue. Attendi la fine del processo.', {startPercent: 8, targetPercent: 92, stepMs: 350, cancellable:false});\n        }\n\n        const saveSignal = undefined; \/\/ 30.9.664: salvataggio isolato, senza ereditare controller di progress\/AI\n        const finishSaveOk = function(saveData, definitiveDeletedTotal){\n            saveData = saveData || {};\n            const saveDeletedTotal = (typeof saveData.deleted_rows_total !== 'undefined') ? (parseInt(saveData.deleted_rows_total, 10) || 0) : 0;\n            const deletedTotal = Math.max(0, parseInt(definitiveDeletedTotal || 0, 10) || 0) + Math.max(0, saveDeletedTotal);\n            const saveMsg = deletedTotal > 0 ? ('Configurazione salvata con successo. Cancellazione definitiva completata. Righe cancellate: ' + deletedTotal + '.') : 'Configurazione salvata con successo.';\n            if (pendingDefinitiveDelete && window.hideProgressPopup) window.hideProgressPopup(true, 'Configurazione salvata', {notifyUser:true, remindSave:false, successMessage:saveMsg});\n            const serverWarnings = (saveData && Array.isArray(saveData.value_warnings)) ? saveData.value_warnings : [];\n            setTimeout(function(){ if (serverWarnings.length && window.showConfigValidationWarning) window.showConfigValidationWarning(serverWarnings, true); else if (window.tiAlert) window.tiAlert(deletedTotal > 0 ? ('\u2705 Salvataggio completato. Righe cancellate: ' + deletedTotal + '.') : '\u2705 Salvataggio completato.'); }, 250);\n            const cfgTimeWarn = String(window.configTimeFallbackWarning || '');\n            if (cfgTimeWarn) setTimeout(function(){ window.tiAlert(cfgTimeWarn); }, 1000);\n            window.configWasSaved = true;\n            if (saveData && saveData.db && saveData.db.Tabelle) {\n                window.currentDbData = saveData.db;\n                window.currentDbViewData = null;\n            } else if (window.currentDbData && saveData && saveData.db_version) {\n                window.currentDbData.__db_version = saveData.db_version;\n            }\n            if (window.currentDbData && saveData && saveData.db_version) window.currentDbData.__db_version = saveData.db_version;\n            if (window.currentDbData) window.configOriginalDbData = JSON.parse(JSON.stringify(window.currentDbData || {}));\n            if(window.modifiedFields) window.modifiedFields.clear();\n            try { if (window.resetConfigSaveButtonUi668) window.resetConfigSaveButtonUi668(true); } catch(e) {}\n            window.tiConfigDeletedRowsManifest = [];\n            window.tiConfigDeletedTableSnapshots = {};\n            window.tiConfigDeletedTableSnapshotBeforeCounts = {};\n            window.tiConfigDeletedRowsCountTotal = 0;\n            window.tiLastConfigDeleteCount = 0;\n            const mustRenderAfterSave = !!(serverWarnings.length || deletedTotal > 0 || (saveData && saveData.force_render_after_save));\n            \/\/ 30.9.664: chiudi immediatamente il processo di salvataggio appena il server conferma la scrittura.\n            \/\/ Non aspettare render, alert o popup: prima sblocca UI, poi mostra eventuali messaggi.\n            try { finishSaveUi588('Salvataggio configurazione concluso', mustRenderAfterSave); }\n            catch(e) { try { restoreBtn(mustRenderAfterSave); } catch(x) {} }\n        };\n        const runFinalSave = function(versionAfterDelete, definitiveDeletedTotal, includeDeleteManifest) {\n            if (pendingDefinitiveDelete && window.updateProgressPopup) window.updateProgressPopup(78, 'Salvataggio finale configurazione', 'Cancellazione definitiva conclusa. Salvo ora il database aggiornato.', 'Righe cancellate: ' + Math.max(0, definitiveDeletedTotal || 0));\n            return postConfigForm(makeSaveForm(versionAfterDelete || currentVersion(), includeDeleteManifest), saveSignal).then(function(d){\n                if (d && d.success) {\n                    try { saveDiag588('salvataggio confermato dal server'); } catch(e) {}\n                    finishSaveOk((d && d.data) ? d.data : {}, definitiveDeletedTotal || 0);\n                    return d;\n                }\n                try { saveDiag588('salvataggio rifiutato dal server'); } catch(e) {}\n                throw new Error((d && d.data && d.data.message) ? d.data.message : 'salvataggio non riuscito');\n            });\n        };\n\n        const buildDeltaChanges589 = function(){\n            try {\n                if (pendingDefinitiveDelete) return null;\n                if (!window.modifiedFields || typeof window.modifiedFields.forEach !== 'function') return null;\n                if (!window.currentDbData || !window.currentDbData.Tabelle || !window.configOriginalDbData || !window.configOriginalDbData.Tabelle) return null;\n                const tables589 = Object.keys(window.currentDbData.Tabelle || {}).sort(function(a,b){ return String(b).length - String(a).length; });\n                const changes589 = [];\n                const seen589 = {};\n                window.modifiedFields.forEach(function(rawKey){\n                    rawKey = String(rawKey || '');\n                    if (!rawKey) return;\n                    let parsed589 = null;\n                    for (let i589 = 0; i589 < tables589.length; i589++) {\n                        const t589 = tables589[i589];\n                        const prefix589 = String(t589) + '-';\n                        if (rawKey.indexOf(prefix589) !== 0) continue;\n                        const rest589 = rawKey.slice(prefix589.length);\n                        const p589 = rest589.indexOf('-');\n                        if (p589 <= 0) continue;\n                        const idxStr589 = rest589.slice(0, p589);\n                        const key589 = rest589.slice(p589 + 1);\n                        if (!\/^\\d+$\/.test(idxStr589) || !key589 || key589.indexOf('__') === 0) { parsed589 = false; break; }\n                        parsed589 = {table:t589, row:parseInt(idxStr589,10), key:key589};\n                        break;\n                    }\n                    if (parsed589 === false) { changes589.__invalid = true; return; }\n                    if (!parsed589) { changes589.__invalid = true; return; }\n                    const curRows589 = window.currentDbData.Tabelle[parsed589.table];\n                    const oldRows589 = window.configOriginalDbData.Tabelle[parsed589.table];\n                    if (!Array.isArray(curRows589) || !curRows589[parsed589.row] || typeof curRows589[parsed589.row] !== 'object') { changes589.__invalid = true; return; }\n                    if (!Array.isArray(oldRows589) || !oldRows589[parsed589.row] || typeof oldRows589[parsed589.row] !== 'object') { changes589.__invalid = true; return; }\n                    const curVal589 = curRows589[parsed589.row][parsed589.key];\n                    const oldVal589 = oldRows589[parsed589.row][parsed589.key];\n                    if (JSON.stringify(curVal589 == null ? '' : curVal589) === JSON.stringify(oldVal589 == null ? '' : oldVal589)) return;\n                    const sig589 = parsed589.table + '\\u0001' + parsed589.row + '\\u0001' + parsed589.key;\n                    if (seen589[sig589]) return;\n                    seen589[sig589] = true;\n                    changes589.push({table:parsed589.table, row:parsed589.row, key:parsed589.key, value:(curVal589 == null ? '' : curVal589)});\n                });\n                if (changes589.__invalid) return null;\n                if (changes589.length > 300) return null;\n                return changes589;\n            } catch(e) { return null; }\n        };\n        const runDeltaSave589 = function(deltaChanges589){\n            const fdDelta589 = new FormData();\n            fdDelta589.append('ti_action', 'ti_ai_save_config_delta');\n            fdDelta589.append('db', dbName);\n            fdDelta589.append('db_version', currentVersion() || '');\n            fdDelta589.append('changes', utoa(JSON.stringify(deltaChanges589 || [])));\n            try { saveDiag588('salvataggio delta 589 avviato; modifiche=' + String((deltaChanges589 || []).length)); } catch(e) {}\n            return postConfigForm(fdDelta589, saveSignal).then(function(d){\n                if (d && d.success) {\n                    try { saveDiag588('salvataggio delta 589 confermato dal server'); } catch(e) {}\n                    finishSaveOk((d && d.data) ? d.data : {}, 0);\n                    return d;\n                }\n                throw new Error((d && d.data && d.data.message) ? d.data.message : 'salvataggio rapido non riuscito');\n            });\n        };\n        const deltaChanges589 = buildDeltaChanges589();\n        if (deltaChanges589 && deltaChanges589.length > 0) {\n            runDeltaSave589(deltaChanges589).catch(function(e){\n                hardUnlockSaveUi568('Errore salvataggio rapido');\n                const msg589 = (e && e.message) ? e.message : 'Errore durante il salvataggio rapido.';\n                if (window.tiAlert) window.tiAlert(msg589); else alert(msg589);\n            }).finally(function(){\n                setTimeout(function(){\n                    if (window.__saveConfigInFlight) finishSaveUi588('Fine salvataggio rapido configurazione', false);\n                    const b589 = gE('lbl-conf-save');\n                    if (b589 && \/salvataggio\/i.test(String(b589.innerText || ''))) {\n                        b589.disabled = false;\n                        b589.style.pointerEvents = '';\n                        b589.innerText = 'Salva Modifiche';\n                        try { b589.style.removeProperty('background'); b589.style.removeProperty('background-color'); } catch(e) {}\n                    }\n                    try { if (!window.__saveConfigInFlight && window.resetConfigSaveButtonUi668) window.resetConfigSaveButtonUi668(false); } catch(e) {}\n                }, 120);\n            });\n            return;\n        }\n\n        let flowPromise;\n        if (pendingDefinitiveDelete) {\n            if (window.updateProgressPopup) window.updateProgressPopup(20, 'Cancellazione definitiva in corso', 'Il server sta rileggendo il database reale e cancellando i record selezionati.', 'Righe da cancellare: ' + pendingDeletedClientTotal);\n            flowPromise = postConfigForm(makeDeleteForm(), saveSignal).then(function(delRes){\n                if (!(delRes && delRes.success)) throw new Error((delRes && delRes.data && delRes.data.message) ? delRes.data.message : 'cancellazione definitiva non riuscita');\n                const delData = (delRes && delRes.data) ? delRes.data : {};\n                const deletedByEndpoint = (typeof delData.deleted_rows_total !== 'undefined') ? (parseInt(delData.deleted_rows_total, 10) || 0) : 0;\n                const versionAfterDelete = delData.db_version || currentVersion();\n                if (window.updateProgressPopup) window.updateProgressPopup(62, 'Cancellazione definitiva verificata', 'Il processo server di cancellazione definitiva e terminato. Proseguo con il salvataggio finale.', 'Righe cancellate: ' + deletedByEndpoint);\n                return runFinalSave(versionAfterDelete, deletedByEndpoint, false);\n            }).catch(function(firstErr){\n                \/\/ Fallback controllato: se l endpoint separato non riesce a trovare righe ma il payload corrente e gia ridotto,\n                \/\/ il salvataggio finale usa il payload ridotto come fonte autorevole e verifica la scrittura su file.\n                if (window.updateProgressPopup) window.updateProgressPopup(55, 'Cancellazione tramite salvataggio finale', 'Endpoint separato non conclusivo. Applico la tabella ridotta direttamente nel salvataggio finale verificato.', firstErr && firstErr.message ? firstErr.message : 'fallback');\n                return runFinalSave(currentVersion(), 0, true);\n            });\n        } else {\n            flowPromise = runFinalSave(currentVersion(), 0, true);\n        }\n\n        flowPromise.catch(function(e) {\n            const msg = (e && e.message) ? e.message : 'Errore durante il salvataggio.';\n            if (e && (e.name === 'AbortError' || e.userCancelled || (window.isLongAIProcessCancelled && window.isLongAIProcessCancelled()))) {\n                hardUnlockSaveUi568('Salvataggio interrotto');\n                return;\n            }\n            hardUnlockSaveUi568('Errore salvataggio');\n            if (window.tiAlert) window.tiAlert(msg); else alert(msg);\n        }).finally(function(){\n            \/\/ 30.9.664: rete conclusa. Se per qualunque motivo finishSaveOk non ha sbloccato,\n            \/\/ sblocca subito senza attendere altri timer.\n            setTimeout(function(){\n                if (window.__saveConfigInFlight) finishSaveUi588('Fine salvataggio configurazione', false);\n                const b585 = gE('lbl-conf-save');\n                if (b585 && \/salvataggio\/i.test(String(b585.innerText || ''))) {\n                    b585.disabled = false;\n                    b585.style.pointerEvents = '';\n                    b585.innerText = 'Salva Modifiche';\n                    try { b585.style.removeProperty('background'); b585.style.removeProperty('background-color'); } catch(e) {}\n                }\n                try { if (!window.__saveConfigInFlight && window.resetConfigSaveButtonUi668) window.resetConfigSaveButtonUi668(false); } catch(e) {}\n            }, 120);\n        });\n    };\n\n    window.updateMemField = function(tbl, idx, key, el) { \n        const protNorm = v => String(v == null ? '' : v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').replace(\/[^a-z0-9]\/g, '');\n        if (String(window.tiUser || '').toLowerCase() !== 'ssglobaladmin' && protNorm(tbl) === 'config' && ['stato','datacreazione','dataattivazione','wcorderid','wcactivateid'].includes(protNorm(key))) {\n            if (el && typeof el.blur === 'function') el.blur();\n            if (window.tiAlert) window.tiAlert('Campo riservato: modificabile solo da sistema, globaladmin o attivazione WooCommerce.');\n            return;\n        }\n        if(window.currentDbData && window.currentDbData.Tabelle[tbl] && window.currentDbData.Tabelle[tbl][idx]) { \n            let normalizedValue = window.normalizeInputValueForKey(key, el.value);\n            const fieldNorm = window.normalizeConfigRelationName ? window.normalizeConfigRelationName(key) : String(key || '').toLowerCase();\n            if (fieldNorm === 'stato' || fieldNorm === 'status') {\n                normalizedValue = window.normalizeConfigStateValue ? window.normalizeConfigStateValue(normalizedValue) : normalizedValue;\n                if (el && ['Attivo','Operativo','Demo','Configurazione','Sospesa','Cancellato'].includes(normalizedValue)) el.value = normalizedValue;\n            }\n            window.currentDbData.Tabelle[tbl][idx][key] = normalizedValue;\n            window.__configRelationSupportCache = null;\n            window.forceWhiteEditStyle && window.forceWhiteEditStyle(el);\n            el.style.setProperty('border-color', '#fbbf24', 'important');\n            if(!window.modifiedFields) window.modifiedFields = new Set();\n            window.modifiedFields.add(`${tbl}-${idx}-${key}`);\n            if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n            if (window.checkConfigUsernameDuplicateInput) window.checkConfigUsernameDuplicateInput(tbl, idx, key, el);\n        } \n    };\n\n    window.ensureSharedSetupModal = function() {\n        if (gE('ti-shared-setup-ov')) return;\n        const wrap = document.createElement('div');\n        wrap.id = 'ti-shared-setup-ov';\n        wrap.className = 'ti-ov';\n        wrap.innerHTML = `<div class=\"ti-modal\" style=\"width:96%;max-width:760px;text-align:left;\">\n            <h3 style=\"margin-top:0;color:#fff;\">Setup storage centralizzato<\/h3>\n            <div style=\"font-size:12px;color:#9ca3af;margin-bottom:12px;\">Disponibile solo per l amministratore globale. Definisce percorso filesystem condiviso e URL pubblico del repository AI-data usato dal plugin su siti diversi. PATH e URL non devono essere identici come stringa: il PATH e un percorso server, l URL e un indirizzo web pubblico.<\/div>\n            <label style=\"display:block;font-size:12px;color:#bfdbfe;margin-bottom:6px;\">TI_AI_SS_SHARED_BASE_PATH<\/label>\n            <input id=\"ti-shared-base-path\" class=\"ti-in\" placeholder=\"\/percorso\/condiviso\/wp-content\/uploads\/AI-data\/\">\n            <label style=\"display:block;font-size:12px;color:#bfdbfe;margin:12px 0 6px 0;\">TI_AI_SS_SHARED_BASE_URL<\/label>\n            <input id=\"ti-shared-base-url\" class=\"ti-in\" placeholder=\"https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\">\n            <div id=\"ti-shared-setup-info\" style=\"margin-top:12px;font-size:12px;line-height:1.45;color:#cbd5e1;background:#020617;border:1px solid #334155;border-radius:8px;padding:10px;\"><\/div>\n            <div style=\"display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap;margin-top:14px;\">\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#334155;color:#fff;\" onclick=\"window.fillSharedSetupDefaults()\">Usa valori default<\/button>\n                <button type=\"button\" class=\"ti-btn\" onclick=\"window.closeModal('ti-shared-setup-ov')\">Chiudi<\/button>\n                <button type=\"button\" class=\"ti-btn\" style=\"background:#a16207;color:#fff;\" onclick=\"window.saveSharedSetup()\">Salva setup centralizzato<\/button>\n            <\/div>\n        <\/div>`;\n        document.body.appendChild(wrap);\n        if (window.portalizePluginModals) window.portalizePluginModals();\n    };\n\n    window.refreshSharedSetupInfo = function() {\n        const info = gE('ti-shared-setup-info');\n        if (!info) return;\n        const payload = window.tiSharedSetup || {};\n        info.innerHTML = 'Path condiviso salvato:<br><b>' + window.escapeHtml(payload.shared_base_path || '(non impostato)') + '<\/b><br><br>URL condiviso salvato:<br><b>' + window.escapeHtml(payload.shared_base_url || '(non impostato)') + '<\/b><br><br>Percorso effettivo in uso:<br><b>' + window.escapeHtml(payload.effective_base_path || '') + '<\/b><br><br>URL effettivo in uso:<br><b>' + window.escapeHtml(payload.effective_base_url || '') + '<\/b><br><br>Valori default disponibili:<br><b>' + window.escapeHtml(payload.default_base_path || '') + '<\/b><br><b>' + window.escapeHtml(payload.default_base_url || '') + '<\/b><br><br>Esempio URL centralizzato Target Informatica:<br><b>' + window.escapeHtml(payload.target_url_example || '') + '<\/b>';\n    };\n\n    window.fillSharedSetupDefaults = function() {\n        const payload = window.tiSharedSetup || {};\n        const pathInp = gE('ti-shared-base-path');\n        const urlInp = gE('ti-shared-base-url');\n        if (pathInp) pathInp.value = payload.default_base_path || '';\n        if (urlInp) urlInp.value = payload.default_base_url || '';\n    };\n\n    window.openSharedSetupModal = function() {\n        if (window.tiUser !== 'SSGlobalAdmin') { window.tiAlert('Solo l amministratore globale puo usare questo setup.'); return; }\n        window.ensureSharedSetupModal();\n        const payload = window.tiSharedSetup || {};\n        const pathInp = gE('ti-shared-base-path');\n        const urlInp = gE('ti-shared-base-url');\n        if (pathInp) pathInp.value = payload.shared_base_path || payload.default_base_path || '';\n        if (urlInp) urlInp.value = payload.shared_base_url || payload.default_base_url || '';\n        window.refreshSharedSetupInfo();\n        window.openModal('ti-shared-setup-ov');\n    };\n\n    window.saveSharedSetup = function() {\n        if (window.tiUser !== 'SSGlobalAdmin') { window.tiAlert('Solo l amministratore globale puo salvare questo setup.'); return; }\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_config_action');\n        fd.append('mode', 'shared_setup_save');\n        fd.append('shared_base_path', (gE('ti-shared-base-path') && gE('ti-shared-base-path').value) ? gE('ti-shared-base-path').value : '');\n        fd.append('shared_base_url', (gE('ti-shared-base-url') && gE('ti-shared-base-url').value) ? gE('ti-shared-base-url').value : '');\n        window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n            if (!res || !res.success) { window.tiAlert((res && res.data && res.data.message) ? res.data.message : 'Errore salvataggio setup centralizzato.'); return; }\n            window.tiSharedSetup = (res.data && res.data.shared_setup) ? res.data.shared_setup : (window.tiSharedSetup || {});\n            window.tiUploadBase = String((window.tiSharedSetup && window.tiSharedSetup.effective_base_url) || window.tiUploadBase || '').replace(\/\\\/$\/, '');\n            const manualBtn = gE('btn-manual');\n            const sharedManualUrl = String((window.tiSharedSetup && window.tiSharedSetup.manual_url) || '');\n            const sharedDocsBaseUrl = String((window.tiSharedSetup && window.tiSharedSetup.docs_base_url) || 'https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service');\n            if (manualBtn && sharedManualUrl) manualBtn.href = sharedManualUrl;\n            else if (manualBtn && sharedDocsBaseUrl) manualBtn.href = sharedDocsBaseUrl.replace(\/\\\/+$\/, '') + '\/Shop-Service%20Manual.pdf';\n            window.refreshSharedSetupInfo();\n            const replyBox = gE('ti-conf-ai-reply');\n            if (replyBox && res.data && res.data.reply) { replyBox.style.display = 'block'; replyBox.innerHTML = window.escapeHtml(res.data.reply); }\n            window.tiAlert('Setup centralizzato salvato.');\n        }).catch(function(err){\n            window.tiAlert('Errore salvataggio setup centralizzato: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n        });\n    };\n\n\n    window.ensureConfigBotLoadingStyle = function() {\n        if (gE('ti-conf-ai-loading-style')) return;\n        const st = document.createElement('style');\n        st.id = 'ti-conf-ai-loading-style';\n        st.innerHTML = '@keyframes tiConfHourglassSpin{0%{transform:rotate(0deg);}50%{transform:rotate(180deg);}100%{transform:rotate(360deg);}}' +\n            '@keyframes tiConfDots{0%,20%{opacity:.2;}50%{opacity:1;}100%{opacity:.2;}}' +\n            '.ti-conf-wait{display:flex;align-items:center;gap:12px;padding:2px 0;}' +\n            '.ti-conf-wait-icon{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:999px;background:#1f2937;border:1px solid #475569;font-size:18px;animation:tiConfHourglassSpin 1.25s ease-in-out infinite;}' +\n            '.ti-conf-wait-text{display:flex;flex-direction:column;gap:2px;line-height:1.35;}' +\n            '.ti-conf-wait-title{font-weight:700;color:#f8fafc;}' +\n            '.ti-conf-wait-sub{font-size:12px;color:#93c5fd;}' +\n            '.ti-conf-wait-dots span{display:inline-block;animation:tiConfDots 1.2s infinite;}' +\n            '.ti-conf-wait-dots span:nth-child(2){animation-delay:.2s;}' +\n            '.ti-conf-wait-dots span:nth-child(3){animation-delay:.4s;}';\n        document.head.appendChild(st);\n    };\n\n    window.getConfigBotWaitingHtml = function(message, detail) {\n        window.ensureConfigBotLoadingStyle();\n        const msg = window.escapeHtml(message || 'Fammi pensare');\n        const sub = window.escapeHtml(detail || 'Sto verificando database, documenti AI e istruzioni disponibili.');\n        return '<div class=\"ti-conf-wait\" aria-live=\"polite\" aria-busy=\"true\">'\n            + '<div class=\"ti-conf-wait-icon\" title=\"Attendere\">\u23f3<\/div>'\n            + '<div class=\"ti-conf-wait-text\">'\n            + '<div class=\"ti-conf-wait-title\">' + msg + ' <span class=\"ti-conf-wait-dots\"><span>.<\/span><span>.<\/span><span>.<\/span><\/span><\/div>'\n            + '<div class=\"ti-conf-wait-sub\">' + sub + '<\/div>'\n            + '<\/div>'\n            + '<\/div>';\n    };\n\n\n    window.getUserWaitingHtml = function(message, detail) {\n        window.ensureConfigBotLoadingStyle();\n        const msg = window.escapeHtml(message || 'Fammi pensare...');\n        const sub = window.escapeHtml(detail || 'Sto elaborando la richiesta e verifico i dati disponibili.');\n        return '<div class=\"ti-conf-wait ti-user-wait\" aria-live=\"polite\" aria-busy=\"true\">'\n            + '<div class=\"ti-conf-wait-icon\" title=\"Attendere\">\u23f3<\/div>'\n            + '<div class=\"ti-conf-wait-text\">'\n            + '<div class=\"ti-conf-wait-title\">' + msg + '<\/div>'\n            + '<div class=\"ti-conf-wait-sub\">' + sub + '<\/div>'\n            + '<\/div>'\n            + '<\/div>';\n    };\n\n\n    \/* 30.9.664 - Fallback OpenAI obbligatorio per richieste informative\/comparative senza DOC\/catalogo *\/\n    window.tiIsInfoVerificationRequest = function(text) {\n        const raw = String(text || '').trim();\n        if (!raw) return false;\n        const t = raw.toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '');\n        if (\/\\b(conferma\\s+ordine|conferma\\s+righe|salva\\s+ordine|annulla|login|logout|accedi|importa|carica\\s+file|scatta|foto)\\b\/i.test(t)) return false;\n        if (\/\\b(che\\s+mi\\s+dici|cosa\\s+mi\\s+dici|cosa\\s+sai|dimmi|spiegami|spiega|informazioni?\\s+su|info\\s+su|a\\s+cosa\\s+serve|come\\s+funziona|condizioni|contrattual|commercial|privacy|gdpr|documento|documenti|scheda|bugiardino|foglietto|aspirina|farmaco|farmaci|prodotto|servizio)\\b\/i.test(t)) return true;\n        const wordCount = t.split(\/\\s+\/).filter(Boolean).length;\n        return wordCount >= 2 && wordCount <= 8 && !\/[0-9]+\\s*(pz|pezzi|unita|unit\u00e0|conf|confezioni|kg|g|ml|litri?)\\b\/i.test(t);\n    };\n\n    window.getInfoVerificationWaitingHtml = function(message, detail) {\n        return window.getUserWaitingHtml(\n            message || '\u23f3 Verifica informazioni in corso...',\n            detail || 'Sto cercando informazioni in database ditta, prodotti\/servizi, righe ordine e documenti collegati.'\n        );\n    };\n\n    window.clearConfigBot = function() {\n        const p = gE('ti-conf-ai-prompt'); const r = gE('ti-conf-ai-reply'); const f = gE('ti-conf-ai-file');\n        if (p) p.value = '';\n        if (r) { r.style.display = 'none'; r.innerHTML = ''; }\n        window.configBotFile = null;\n        if (f) { f.style.display = 'none'; f.innerHTML = ''; }\n    };\n\n    window.showConfigImportPreview = function(previewText, importToken) {\n        if (importToken) window.tiConfigImportToken = importToken;\n        const body = gE('ti-conf-import-preview-body');\n        if (body) {\n            body.innerHTML = (previewText || '').replace(\/\\[NO_VOICE\\]\/g, '').trim();\n            if (window.tiConfigImportToken) body.dataset.importToken = window.tiConfigImportToken;\n            if (window.initConfigTopScrollbars) setTimeout(function(){ window.initConfigTopScrollbars(body); }, 40);\n        }\n        const refineBox = gE('ti-conf-import-refine-prompt');\n        const refineStatus = gE('ti-conf-import-refine-status');\n        if (refineBox) refineBox.value = '';\n        if (refineStatus) refineStatus.innerHTML = '';\n        window.tiConfigImportRefineRules = [];\n        window.markConfigImportPreviewUpdated('Anteprima pronta. Verifica i dati e conferma solo se corretti.');\n        window.openModal('ti-conf-import-preview-ov');\n    };\n\n    window.hasPendingImportAssociations = function() {\n        const body = gE('ti-conf-import-preview-body');\n        if (!body) return false;\n        if (window.tiConfigImportToken) return true;\n        if (Array.isArray(window.tiConfigImportRefineRules) && window.tiConfigImportRefineRules.length) return true;\n        try {\n            const selects = Array.from(body.querySelectorAll('.ti-import-map-select'));\n            if (selects.some(function(sel){ return String(sel.value || '').trim() !== '' && String(sel.value || '').trim() !== '--'; })) return true;\n            const formulas = Array.from(body.querySelectorAll('.ti-import-price-formula'));\n            if (formulas.some(function(inp){ return String(inp.value || '').trim() !== ''; })) return true;\n            const targets = Array.from(body.querySelectorAll('.ti-import-group-target'));\n            if (targets.some(function(sel){ return String(sel.value || '').trim() !== '' && String(sel.value || '').trim() !== 'Prodotti'; })) return true;\n        } catch(e) {}\n        return false;\n    };\n\n    window.closeConfigImportPreview = function(forceClose) {\n        if (!forceClose && window.hasPendingImportAssociations && window.hasPendingImportAssociations()) {\n            const msg = '<b>Associazioni import non ancora salvate<\/b><br><br>' +\n                'Hai gi\u00e0 fatto associazioni o regole di import nella preview.<br>' +\n                'Vuoi salvarle adesso con la conferma definitiva oppure uscire senza salvarle?<br><br>' +\n                '<span style=\"color:#bbf7d0;font-weight:700;\">SALVA E CONFERMA<\/span> salva l import nel database.<br>' +\n                '<span style=\"color:#fecaca;font-weight:700;\">ESCI SENZA SALVARE<\/span> chiude la finestra e non rende definitive le associazioni.';\n            window.tiConfirmYesNoAction(msg, function(){\n                window.confirmConfigImportPreview(true);\n            }, function(){\n                window.closeConfigImportPreview(true);\n            }, 'SALVA E CONFERMA', 'ESCI SENZA SALVARE');\n            return;\n        }\n        window.closeModal('ti-conf-import-preview-ov');\n    };\n\n    window.markConfigImportPreviewUpdated = function(message) {\n        const note = gE('ti-conf-import-preview-last-update');\n        const body = gE('ti-conf-import-preview-body');\n        const text = message || 'Anteprima modifiche aggiornata. Controlla il risultato e conferma solo se corretto.';\n        if (note) {\n            const now = new Date();\n            let hh = String(now.getHours()).padStart(2, '0');\n            let mm = String(now.getMinutes()).padStart(2, '0');\n            note.style.display = 'block';\n            note.innerHTML = 'Anteprima modifiche aggiornata alle ' + hh + ':' + mm + '. ' + window.escapeHtml(text);\n        }\n        if (body) {\n            try {\n                const active = document.activeElement;\n                const editingImport = !!(active && body.contains(active) && active.matches && active.matches('.ti-import-map-select, .ti-import-group-target, .ti-import-price-formula'));\n                if (!editingImport) body.scrollTop = 0;\n            } catch(e) {}\n            body.setAttribute('data-preview-updated', '1');\n        }\n    };\n\n    window.redisplayConfigImportPreviewForFinalConfirm = function(message) {\n        window.markConfigImportPreviewUpdated(message || 'Rivedi questa anteprima prima della conferma definitiva.');\n        try { window.openModal('ti-conf-import-preview-ov'); } catch(e) {}\n        const body = gE('ti-conf-import-preview-body');\n        if (body && typeof body.focus === 'function') {\n            try { body.setAttribute('tabindex', '-1'); body.focus({preventScroll:true}); } catch(e) {}\n        }\n    };\n\n    window.normalizeImportRefineToken = function(v) {\n        return String(v || '').toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').replace(\/[^a-z0-9]+\/g, ' ').trim();\n    };\n\n    window.tiConfigImportRefineRules = Array.isArray(window.tiConfigImportRefineRules) ? window.tiConfigImportRefineRules : [];\n    window.rememberImportLocalRefineRule = function(dest, src) {\n        const d = String(dest || '').trim();\n        const s = String(src || '').trim();\n        if (!d || !s) return false;\n        window.tiConfigImportRefineRules = Array.isArray(window.tiConfigImportRefineRules) ? window.tiConfigImportRefineRules : [];\n        const nd = window.normalizeImportRefineToken(d);\n        const ns = window.normalizeImportRefineToken(s);\n        const exists = window.tiConfigImportRefineRules.some(function(r){ return window.normalizeImportRefineToken(r.dest) === nd && window.normalizeImportRefineToken(r.src) === ns; });\n        if (!exists) window.tiConfigImportRefineRules.push({dest:d, src:s});\n        return true;\n    };\n\n    window.getImportRefineAssignment = function(prompt) {\n        let txt = String(prompt || '').trim();\n        if (!txt) return null;\n        txt = txt.replace(\/[\\r\\n]+\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n        txt = txt.replace(\/^(?:ut|utente|richiesta|istruzione|comando)\\s*[:\\-]\\s*\/i, '').trim();\n        txt = txt.replace(\/^['\"`]+|['\"`]+$\/g, '').trim();\n        const m = txt.match(\/^(.+?)\\s*(?:=|->|\\u2192|come| in | da )\\s*(.+?)$\/i);\n        if (!m) return null;\n        let leftRaw = String(m[1] || '').replace(\/^campo\\s+\/i, '').trim();\n        let rightRaw = String(m[2] || '').replace(\/^campo\\s+\/i, '').trim();\n        leftRaw = leftRaw.replace(\/^['\"`]+|['\"`]+$\/g, '').trim();\n        rightRaw = rightRaw.replace(\/^['\"`]+|['\"`]+$\/g, '').trim();\n        const left = window.normalizeImportRefineToken ? window.normalizeImportRefineToken(leftRaw) : leftRaw.toLowerCase();\n        const right = window.normalizeImportRefineToken ? window.normalizeImportRefineToken(rightRaw) : rightRaw.toLowerCase();\n        if (!left || !right || left.length > 80 || right.length > 80) return null;\n        return {dest: leftRaw, src: rightRaw, destNorm: left, srcNorm: right, raw: txt};\n    };\n\n    window.isSimpleImportRefineInstruction = function(prompt) {\n        const txt = String(prompt || '').trim();\n        if (!txt) return false;\n        if (window.getImportRefineAssignment && window.getImportRefineAssignment(txt)) return true;\n        return txt.length <= 180 && \/(?:=|->|\\u2192)\/.test(txt);\n    };\n\n    window.getImportTableHeaderTexts = function(table) {\n        const ths = Array.from((table || document).querySelectorAll('thead th'));\n        return ths.map(function(th){\n            let label = '';\n            const span = th.querySelector('span');\n            if (span) label = span.textContent || '';\n            if (!label) label = th.childNodes && th.childNodes.length ? Array.from(th.childNodes).map(function(n){ return n.nodeType === 3 ? n.textContent : ''; }).join(' ') : th.textContent;\n            if (!label) label = th.textContent || '';\n            return String(label || '').replace(\/\\?\/g, '').trim();\n        });\n    };\n\n    window.findImportPreviewRowsTable = function(root, destNorm, srcNorm) {\n        const box = root || gE('ti-conf-import-preview-body') || document;\n        const tables = Array.from(box.querySelectorAll('table'));\n        let best = null;\n        tables.forEach(function(table){\n            if (best) return;\n            if (table.querySelector('.ti-import-map-select, .ti-import-group-target')) return;\n            const headers = window.getImportTableHeaderTexts(table);\n            if (!headers.length) return;\n            const norms = headers.map(function(h){ return window.normalizeImportRefineToken ? window.normalizeImportRefineToken(h) : String(h).toLowerCase(); });\n            const hasSrc = norms.some(function(n){ return n === srcNorm || n.indexOf(srcNorm) !== -1 || srcNorm.indexOf(n) !== -1; });\n            const hasDest = norms.some(function(n){ return n === destNorm || n.indexOf(destNorm) !== -1 || destNorm.indexOf(n) !== -1; });\n            if (hasSrc || hasDest) best = table;\n        });\n        return best;\n    };\n\n    window.ensureImportPreviewLocalRulesNote = function(root) {\n        const box = root || gE('ti-conf-import-preview-body');\n        if (!box) return null;\n        let note = box.querySelector('#ti-import-local-rules-note');\n        if (!note) {\n            note = document.createElement('div');\n            note.id = 'ti-import-local-rules-note';\n            note.style.cssText = 'margin:10px 0;padding:8px 10px;border-radius:8px;border:1px solid #22c55e;background:#052e16;color:#bbf7d0;font-size:12px;font-weight:700;';\n            box.insertBefore(note, box.firstChild);\n        }\n        return note;\n    };\n\n    window.renderImportPreviewLocalRulesNote = function(root) {\n        const note = window.ensureImportPreviewLocalRulesNote(root);\n        if (!note) return;\n        const rules = Array.isArray(window.tiConfigImportRefineRules) ? window.tiConfigImportRefineRules : [];\n        if (!rules.length) { note.style.display = 'none'; return; }\n        note.style.display = 'block';\n        note.innerHTML = 'Regole locali applicate alla preview e al salvataggio: ' + rules.map(function(r){\n            return window.escapeHtml(String(r.dest || '').trim() + ' = ' + String(r.src || '').trim());\n        }).join(' ; ') + '. Controlla l anteprima aggiornata e conferma solo se corretta.';\n    };\n\n    window.findImportColumnIndexByToken = function(headers, tokenNorm) {\n        const norms = headers.map(function(h){ return window.normalizeImportRefineToken ? window.normalizeImportRefineToken(h) : String(h).toLowerCase(); });\n        let idx = norms.findIndex(function(n){ return n === tokenNorm; });\n        if (idx < 0) idx = norms.findIndex(function(n){ return n.indexOf(tokenNorm) !== -1 || tokenNorm.indexOf(n) !== -1; });\n        return idx;\n    };\n\n    window.updateImportPreviewRowsLocal = function(dest, src, root) {\n        const destNorm = window.normalizeImportRefineToken ? window.normalizeImportRefineToken(dest) : String(dest || '').toLowerCase();\n        const srcNorm = window.normalizeImportRefineToken ? window.normalizeImportRefineToken(src) : String(src || '').toLowerCase();\n        const box = root || gE('ti-conf-import-preview-body');\n        if (!box || !destNorm || !srcNorm) return false;\n        const table = window.findImportPreviewRowsTable(box, destNorm, srcNorm);\n        window.renderImportPreviewLocalRulesNote(box);\n        if (!table) return false;\n        let headers = window.getImportTableHeaderTexts(table);\n        let srcIdx = window.findImportColumnIndexByToken(headers, srcNorm);\n        let destIdx = window.findImportColumnIndexByToken(headers, destNorm);\n        if (srcIdx < 0) return false;\n        if (destIdx < 0) {\n            const headRow = table.querySelector('thead tr');\n            if (headRow) {\n                const th = document.createElement('th');\n                th.style.cssText = 'border:1px solid #475569; padding:5px 6px; background:#0f172a; text-align:left; white-space:nowrap;';\n                th.innerHTML = '<div style=\"display:flex;align-items:center;justify-content:space-between;gap:6px;\"><span>' + window.escapeHtml(dest) + '<\/span><\/div>';\n                headRow.appendChild(th);\n                headers.push(dest);\n                destIdx = headers.length - 1;\n                Array.from(table.querySelectorAll('tbody tr')).forEach(function(tr){\n                    const td = document.createElement('td');\n                    td.style.cssText = 'border:1px solid #475569; padding:5px 6px; text-align:left; vertical-align:top; background:#052e16;';\n                    tr.appendChild(td);\n                });\n            }\n        }\n        if (destIdx < 0) return false;\n        let changed = 0;\n        Array.from(table.querySelectorAll('tbody tr')).forEach(function(tr){\n            const cells = Array.from(tr.children || []);\n            if (!cells[srcIdx] || !cells[destIdx]) return;\n            const value = (cells[srcIdx].textContent || '').trim();\n            cells[destIdx].textContent = value;\n            cells[destIdx].style.background = '#052e16';\n            changed++;\n        });\n        return changed > 0;\n    };\n\n    window.tryApplyImportRefineMappingInstruction = function(prompt, root, statusEl) {\n        const parsed = window.getImportRefineAssignment ? window.getImportRefineAssignment(prompt) : null;\n        if (!parsed) {\n            if (window.isSimpleImportRefineInstruction && window.isSimpleImportRefineInstruction(prompt)) {\n                if (statusEl) statusEl.innerHTML = '<span style=\"color:#fbbf24;\">Istruzione breve non chiara. Usa formato: campo destinazione = campo origine. Nessuna chiamata AI eseguita.<\/span>';\n                if (window.redisplayConfigImportPreviewForFinalConfirm) window.redisplayConfigImportPreviewForFinalConfirm('Chiarimento richiesto per il raffinamento. La preview resta invariata fino a istruzione chiara.');\n                return true;\n            }\n            return false;\n        }\n        const box = root || document;\n        const destRaw = parsed.dest;\n        const srcRaw = parsed.src;\n        window.rememberImportLocalRefineRule(destRaw, srcRaw);\n        const updatedRows = window.updateImportPreviewRowsLocal ? window.updateImportPreviewRowsLocal(destRaw, srcRaw, box) : false;\n        const selects = Array.from(box.querySelectorAll('.ti-import-map-select'));\n        if (selects.length) {\n            function optionValueFor(sel, wanted) {\n                const opts = Array.from(sel.options || []);\n                let hit = opts.find(function(o){ return window.normalizeImportRefineToken(o.value) === wanted || window.normalizeImportRefineToken(o.text) === wanted; });\n                if (!hit) hit = opts.find(function(o){ return window.normalizeImportRefineToken(o.value).indexOf(wanted) !== -1 || window.normalizeImportRefineToken(o.text).indexOf(wanted) !== -1; });\n                return hit ? hit.value : '';\n            }\n            selects.forEach(function(sel){\n                const hdr = window.normalizeImportRefineToken(sel.getAttribute('data-source-header') || '');\n                if (hdr === parsed.srcNorm || hdr.indexOf(parsed.srcNorm) !== -1 || parsed.srcNorm.indexOf(hdr) !== -1) {\n                    const val = optionValueFor(sel, parsed.destNorm);\n                    if (val && val !== '--') sel.value = val;\n                }\n            });\n        }\n        if (statusEl) {\n            statusEl.innerHTML = updatedRows\n                ? 'Anteprima aggiornata localmente. Non chiamo il server. Controlla il risultato e conferma per salvare.'\n                : 'Regola locale memorizzata. Non chiamo il server. Verr\u00e0 applicata al salvataggio. Controlla la preview prima di confermare.';\n        }\n        if (window.redisplayConfigImportPreviewForFinalConfirm) window.redisplayConfigImportPreviewForFinalConfirm(updatedRows ? 'Anteprima modifiche aggiornata localmente. Conferma solo se corretta.' : 'Regola locale registrata. Conferma solo se corretta.');\n        return true;\n    };\n\n    window.refineConfigImportPreview = function() {\n        const dbSel = gE('ti-ditta');\n        if (!dbSel || !dbSel.value || dbSel.value === 'NEW_DB') { window.tiAlert('Seleziona una ditta.'); return; }\n        const body = gE('ti-conf-import-preview-body');\n        const promptEl = gE('ti-conf-import-refine-prompt');\n        const statusEl = gE('ti-conf-import-refine-status');\n        const btn = gE('ti-conf-import-refine-btn');\n        const refinePrompt = promptEl ? String(promptEl.value || '').trim() : '';\n        if (!refinePrompt) { window.tiAlert('Scrivi cosa vuoi far raffinare alla AI.'); if (promptEl) promptEl.focus(); return; }\n        const mapping = (window.collectImportMappingFromUI ? window.collectImportMappingFromUI(body || document) : (window.tiConfigImportLastMapping || {}));\n        const fd = new FormData();\n        fd.append('action', 'ti_ai_config_action');\n        fd.append('ti_action', 'ti_ai_config_action');\n        fd.append('db', dbSel.value);\n        fd.append('mode', 'refine_import_preview');\n        fd.append('prompt', 'raffina import preview');\n        fd.append('refine_prompt', refinePrompt);\n        if (window.tiConfigImportToken) fd.append('import_token', window.tiConfigImportToken);\n        fd.append('mapping_json', JSON.stringify(mapping));\n        const oldText = btn ? btn.innerText : '';\n        if (btn) { btn.disabled = true; btn.innerText = 'Raffinamento in corso...'; }\n        if (window.tryApplyImportRefineMappingInstruction && window.tryApplyImportRefineMappingInstruction(refinePrompt, body || document, statusEl)) {\n            if (btn) { btn.disabled = false; btn.innerText = oldText || 'Raffina anteprima con AI'; }\n            return;\n        }\n        if (statusEl) statusEl.innerHTML = 'AI al lavoro sulla prima anteprima...';\n        if (body) body.style.opacity = '0.72';\n        window.postFormDataJsonSafe(window.tiUrl, fd, {tiNoLongProcessSignal:true}).then(function(res){\n            if (btn) { btn.disabled = false; btn.innerText = oldText || 'Raffina anteprima con AI'; }\n            if (body) body.style.opacity = '';\n            if (res && res.success) {\n                if (res.data && (res.data.import_token || res.data.preview_token)) window.tiConfigImportToken = res.data.import_token || res.data.preview_token;\n                const rawReply = ((res.data || {}).reply || '').replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                if (rawReply && body) {\n                    body.innerHTML = rawReply;\n                    if (window.tiConfigImportToken) body.dataset.importToken = window.tiConfigImportToken;\n                    if (window.initConfigTopScrollbars) setTimeout(function(){ window.initConfigTopScrollbars(body); }, 40);\n                }\n                if (window.redisplayConfigImportPreviewForFinalConfirm) window.redisplayConfigImportPreviewForFinalConfirm('Anteprima raffinata. Controlla il risultato prima di confermare.');\n                if (statusEl) statusEl.innerHTML = 'Anteprima raffinata. Controlla il risultato prima di confermare.';\n            } else {\n                const msg = (res && res.data && res.data.message) ? res.data.message : 'Raffinamento non riuscito.';\n                if (statusEl) statusEl.innerHTML = '<span style=\"color:#fca5a5;\">' + window.escapeHtml(msg) + '<\/span>';\n                else window.tiAlert(msg);\n            }\n        }).catch(function(err){\n            if (btn) { btn.disabled = false; btn.innerText = oldText || 'Raffina anteprima con AI'; }\n            if (body) body.style.opacity = '';\n            const msg = 'Errore raffinamento import: ' + ((err && err.message) ? err.message : 'errore sconosciuto');\n            if (statusEl) statusEl.innerHTML = '<span style=\"color:#fca5a5;\">' + window.escapeHtml(msg) + '<\/span>';\n            else window.tiAlert(msg);\n        });\n    };\n\n    window.confirmConfigImportPreview = function(skipClosePrompt) {\n        const dbSel = gE('ti-ditta');\n        if (!dbSel || !dbSel.value || dbSel.value === 'NEW_DB') { window.tiAlert('Seleziona una ditta.'); return; }\n        const body = gE('ti-conf-import-preview-body');\n        const statusEl = gE('ti-conf-import-refine-status');\n        const mapping = (window.collectImportMappingFromUI ? window.collectImportMappingFromUI(body || document) : {});\n\n        const fd = new FormData();\n        fd.append('action', 'ti_ai_config_action');\n        fd.append('ti_action', 'ti_ai_config_action');\n        fd.append('db', dbSel.value);\n        fd.append('mode', 'confirm_import_preview');\n        fd.append('prompt', 'conferma import preview');\n        if (window.tiConfigImportToken) fd.append('import_token', window.tiConfigImportToken);\n        fd.append('mapping_json', JSON.stringify(mapping));\n\n        const buttons = Array.from(document.querySelectorAll('#ti-conf-import-preview-ov button'));\n        buttons.forEach(function(btn){ btn.disabled = true; });\n        if (statusEl) statusEl.innerHTML = 'Salvataggio import in corso...';\n        if (body) body.style.opacity = '0.72';\n\n        \/\/ Usare window.tiUrl evita HTTP 400: 0 di admin-ajax.php quando l'accesso e quello interno del plugin.\n        window.postFormDataJsonSafe(window.tiUrl, fd, {tiNoLongProcessSignal:true, cache:'no-store'}).then(function(res){\n            buttons.forEach(function(btn){ btn.disabled = false; });\n            if (body) body.style.opacity = '';\n            if (res && res.success) {\n                const rawReply = ((res.data || {}).reply || '').trim();\n                const cleanReply = rawReply.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                const finalImportMsg = cleanReply || '\u2705 Import completato e salvato nel database.';\n                window.closeConfigImportPreview(true);\n                window.addMsg('ai', window.formatTable(finalImportMsg), finalImportMsg);\n                setTimeout(function(){ if (window.tiAlert) window.tiAlert('\u2705 Salvataggio import completato con successo.'); }, 250);\n                window.tiConfigImportToken = null;\n                if (res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); }, 1800);\n            } else {\n                const msg = (res && res.data && res.data.message) ? res.data.message : 'Errore conferma import';\n                if (statusEl) statusEl.innerHTML = '<span style=\"color:#fca5a5;\">' + window.escapeHtml(msg) + '<\/span>';\n                window.tiAlert(msg);\n            }\n        }).catch(function(err){\n            buttons.forEach(function(btn){ btn.disabled = false; });\n            if (body) body.style.opacity = '';\n            const msg = 'Errore conferma import: ' + ((err && err.message) ? err.message : 'errore sconosciuto');\n            if (statusEl) statusEl.innerHTML = '<span style=\"color:#fca5a5;\">' + window.escapeHtml(msg) + '<\/span>';\n            window.tiAlert(msg);\n        });\n    };\n\n    window.cancelConfigImportPreview = function() {\n        const box = gE('ti-conf-ai-prompt');\n        if (box) box.value = 'ANNULLA';\n        window.closeConfigImportPreview(true);\n        window.runConfigBot();\n    };\n\n    window.pickConfigBotFile = function() {\n        window.targetUploadContext = 'config-bot-file';\n        if (window.tiOpenDirectUpload616) return window.tiOpenDirectUpload616('config-bot-file');\n        return false;\n    };\n\n    window.runBatchAIGen = function(tableName) {\n        const dbSel = gE('ti-ditta'); const replyBox = gE('ti-conf-ai-reply');\n        if (!dbSel || !dbSel.value || dbSel.value === 'NEW_DB') { window.tiAlert('Seleziona prima una ditta.'); return; }\n        if (!replyBox) { window.tiAlert('Area risposta configurazione non disponibile.'); return; }\n        const photoDetail = ((gE('ti-conf-ai-photo-detail') && gE('ti-conf-ai-photo-detail').value) ? gE('ti-conf-ai-photo-detail').value : '').trim();\n        replyBox.style.display = 'block';\n        replyBox.innerHTML = 'Preparazione elenco record. Priorit\u00e0 a prodotti e servizi senza associazioni file o immagini.<br>Prompt base: attivit\u00e0 ditta + descrizione prodotto\/servizio.';\n        const initFd = window.buildAjaxFormData('ti_ai_config_action', {\n            db: dbSel.value,\n            mode: 'batch_ai_images',\n            tables: tableName\n        });\n        window.showProgressPopup('Generazione foto AI', 'Sto preparando l elenco dei record. Prompt base: attivit\u00e0 ditta e descrizione prodotto o servizio.', {startPercent: 2, targetPercent: 18, stepMs: 350});\n        window.fetchJsonSafe(window.tiUrl, {method:'POST', body:initFd}).then(d => {\n            if (!d.success) {\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                window.hideProgressPopup(false, 'Errore preparazione batch');\n                replyBox.innerHTML = 'Errore: ' + (d.data && d.data.message ? d.data.message : 'Errore sconosciuto');\n                return;\n            }\n            const targets = (d.data && Array.isArray(d.data.targets)) ? d.data.targets : [];\n            if (!targets.length) {\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                window.hideProgressPopup(true, 'Nessun elemento da elaborare', {notifyUser:false, remindSave:false});\n                replyBox.innerHTML = 'Nessun record trovato nella tabella selezionata.';\n                return;\n            }\n            let index = 0;\n            let okCount = 0;\n            const logs = [];\n            const total = targets.length;\n            window.tiBatchAIGenState = {\n                active: true,\n                pendingConfirm: false,\n                generated: [],\n                db: dbSel.value,\n                tableName: tableName,\n                total: total,\n                startedAt: Date.now()\n            };\n            const withoutAssoc = (d.data && typeof d.data.without_associations !== 'undefined') ? parseInt(d.data.without_associations, 10) : targets.filter(function(x){ return !x.has_associations; }).length;\n            const withAssoc = (d.data && typeof d.data.with_associations !== 'undefined') ? parseInt(d.data.with_associations, 10) : Math.max(0, total - withoutAssoc);\n            const detailMode = (d.data && d.data.detail_prompt_mode) ? String(d.data.detail_prompt_mode) : '';\n            const detailInfo = (photoDetail && detailMode === 'leaflet')\n                ? '<br>Istruzione aggiuntiva riconosciuta: <b>foglietto illustrativo \/ materiale informativo<\/b>.'\n                : (photoDetail ? '<br>Istruzione aggiuntiva: <b>' + window.escapeHtml(photoDetail) + '<\/b>.' : '');\n            replyBox.innerHTML = 'Generazione immagini AI pronta.<br>Prompt base: <b>attivit\u00e0 ditta + descrizione prodotto\/servizio<\/b>.<br>Altre fonti informative saranno usate solo successivamente se richieste.' + detailInfo + '<br>Priorit\u00e0: <b>' + withoutAssoc + '<\/b> prodotti\/servizi senza associazioni.<br>Successivi: <b>' + withAssoc + '<\/b> prodotti\/servizi gi\u00e0 con associazioni.';\n            const refreshDb = () => {\n                if (window.refreshCurrentConfigDb) return window.refreshCurrentConfigDb(dbSel.value);\n                const fdDb = window.buildAjaxFormData('ti_ai_get_db_data', {db: dbSel.value});\n                return window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fdDb, tiNoLongProcessSignal:true, tiShowTableReadWait:true, tiTableReadTitle:'\u23f3 Lettura tabelle configurazione', tiTableReadDetail:'Sto aggiornando le tabelle dopo l operazione.', tiTableReadItem: dbSel.value}).then(dbRes => {\n                    if (dbRes.success && dbRes.data) { window.currentDbData = dbRes.data; window.renderConfig(); }\n                }).catch(() => {});\n            };\n            const runNext = () => {\n                if (window.isLongAIProcessCancelled()) {\n                    if (window.tiBatchAIGenState && window.tiBatchAIGenState.pendingConfirm) return;\n                    replyBox.innerHTML = 'Operazione interrotta dall utente. Per completarla dovrai ripetere la generazione.';\n                    if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                    window.clearLongAIProcess();\n                    return;\n                }\n                if (index >= total) {\n                    if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                    window.updateProgressPopup(100, 'Generazione foto AI', 'Completamento e verifica salvataggio associazioni.');\n                    Promise.resolve(refreshDb()).finally(function(){\n                        window.hideProgressPopup(true, 'Generazione foto AI completata', {\n                            remindSave: false,\n                            successMessage: 'Attivita AI completata. Immagini associate e verificate: ' + okCount + ' su ' + total + '.'\n                        });\n                        replyBox.innerHTML = '\u2705 Generazione immagini AI completata.<br>Immagini associate e verificate: <b>' + okCount + '<\/b> \/ ' + total + '<br><br>' + logs.slice(0, 40).join('<br>');\n                    });\n                    return;\n                }\n                const item = targets[index];\n                const currentStep = index + 1;\n                const pct = Math.max(5, Math.min(98, Math.round((index \/ total) * 100)));\n                const priorityText = item.has_associations ? 'Elemento gi\u00e0 con associazioni' : 'Priorit\u00e0 senza associazioni';\n                window.updateProgressPopup(pct, 'Generazione foto AI', priorityText + '. Elaborazione ' + currentStep + ' \/ ' + total + ': ' + (item.name || 'Elemento'), (item.name || 'Elemento'));\n                replyBox.innerHTML = 'Generazione immagini AI in corso...<br>' + priorityText + '<br>Elaborazione ' + currentStep + ' \/ ' + total + ': <b>' + (item.name || 'Elemento') + '<\/b>';\n                const fd = window.buildAjaxFormData('ti_ai_config_action', {\n                    db: dbSel.value,\n                    mode: 'batch_ai_image_one',\n                    table: item.table || tableName,\n                    row_index: item.row_index,\n                    detail_prompt: photoDetail\n                });\n                window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(one => {\n                    const data = one && one.data ? one.data : {};\n                    if (one.success && data.generated) {\n                        okCount++;\n                        if (window.tiBatchAIGenState && Array.isArray(window.tiBatchAIGenState.generated) && (data.added_value || data.filename || data.url)) {\n                            window.tiBatchAIGenState.generated.push({\n                                table: data.table || item.table || tableName,\n                                row_index: (typeof data.row_index !== 'undefined') ? data.row_index : item.row_index,\n                                field: data.image_field || 'Immagine',\n                                value: data.added_value || data.filename || data.url,\n                                name: data.name || item.name || 'Elemento'\n                            });\n                        }\n                        if (data.fallback_logo) logs.push('\u2705 ' + (data.name || item.name || 'Elemento') + ': foto non disponibile, usato logo ditta');\n                        else logs.push('\u2705 ' + (data.name || item.name || 'Elemento') + ': immagine generata e associazione salvata');\n                    } else if (one.success && data.skipped) {\n                        logs.push('\u23ed\ufe0f ' + (data.name || item.name || 'Elemento') + ': immagine gia presente');\n                    } else {\n                        logs.push('\u274c ' + (data.name || item.name || 'Elemento') + ': ' + (data.message || 'errore'));\n                    }\n                    index++;\n                    setTimeout(runNext, 350);\n                }).catch(err => {\n                    if (err && (err.userCancelled || window.isLongAIProcessCancelled())) {\n                        if (window.tiBatchAIGenState && window.tiBatchAIGenState.pendingConfirm) return;\n                        replyBox.innerHTML = 'Operazione interrotta dall utente. Per completarla dovrai ripetere la generazione.';\n                        if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                        window.clearLongAIProcess();\n                        return;\n                    }\n                    logs.push('\u274c ' + (item.name || 'Elemento') + ': ' + err.message);\n                    index++;\n                    setTimeout(runNext, 700);\n                });\n            };\n            runNext();\n        }).catch(err => {\n            if (err && (err.userCancelled || window.isLongAIProcessCancelled())) {\n                if (window.tiBatchAIGenState && window.tiBatchAIGenState.pendingConfirm) return;\n                replyBox.innerHTML = 'Operazione interrotta dall utente. Per completarla dovrai ripetere la generazione.';\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                window.clearLongAIProcess();\n                return;\n            }\n            if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n            window.hideProgressPopup(false, 'Errore batch immagini');\n            replyBox.innerHTML = 'Errore di rete: ' + err.message;\n        });\n    };\n\n    window.formatConfigAiErrorMessage = function(payload, fallbackTitle, promptText) {\n        const data = payload && payload.data ? payload.data : {};\n        const rawMsg = String(data.message || (payload && payload.message) || '').trim();\n        const lower = rawMsg.toLowerCase();\n        let reason = rawMsg || 'Errore sconosciuto durante la configurazione AI.';\n        let hint = '';\n        if (!rawMsg) {\n            hint = 'Possibile motivo: errore di interpretazione dei dati o lista troppo complessa.';\n        } else if (lower.includes('nessuna anteprima import')) {\n            hint = 'Possibile motivo: hai confermato con OK ma non esiste piu una anteprima import valida. Rigenera l anteprima dal listino e poi conferma.';\n        } else if (lower.includes('json') || lower.includes('incomplet') || lower.includes('interpretazione') || lower.includes('nessun record') || lower.includes('parser')) {\n            hint = 'Possibile motivo: dati troppo complessi, colonne non chiare, descrizioni con molte virgole\/virgolette o fasce prezzo non omogenee.';\n        } else if (lower.includes('accesso') || lower.includes('ruolo')) {\n            hint = 'Possibile motivo: il ruolo utente non consente questa modifica.';\n        }\n        const simplify = 'Semplifica la lista: usa poche colonne chiare, ad esempio Nome\/Servizio, Prezzo, Descrizione; una sola voce per riga; evita testi troppo lunghi o colonne non necessarie.';\n        let html = '<b>' + window.escapeHtml(fallbackTitle || 'Errore configurazione AI') + '<\/b>';\n        if (String(promptText || '').trim()) html += '<br><br><b>Istruzione inviata alla Configurazione AI:<\/b><br>' + window.escapeHtml(String(promptText || '').trim());\n        html += '<br><br><b>Dettaglio:<\/b><br>' + window.escapeHtml(reason);\n        if (hint) html += '<br><br><b>Possibile motivo:<\/b><br>' + window.escapeHtml(hint);\n        html += '<br><br><b>Come correggere:<\/b><br>' + window.escapeHtml(simplify);\n        return html;\n    };\n\n    window.tiConfigAiHtmlToPlainText482 = function(html) {\n        try {\n            const tmp = document.createElement('div');\n            tmp.innerHTML = String(html || '').replace(\/<br\\s*\\\/?\\s*>\/gi, '\\n');\n            return String(tmp.innerText || tmp.textContent || '').trim();\n        } catch(e) {\n            return String(html || '').replace(\/<br\\s*\\\/?\\s*>\/gi, '\\n').replace(\/<[^>]+>\/g, '').trim();\n        }\n    };\n    window.tiAppendConfigAiToUnifiedChat482 = function(text) {\n        const promptBox = gE('ti-conf-ai-prompt');\n        if (!promptBox) return false;\n        let aiText = String(text || '').replace(\/\\[NO_VOICE\\]\/g, '').trim();\n        if (!aiText) return false;\n        let current = String(promptBox.value || '').trim();\n        current = current.replace(\/\\n+AI:\\s*(Sto elaborando[^\\n]*|Elaborazione in corso[^\\n]*)\\s*$\/i, '').trim();\n        const tail = current.slice(Math.max(0, current.length - aiText.length - 40));\n        if (tail.indexOf(aiText) !== -1) return true;\n        if (current && !\/^Utente:\/i.test(current) && current.indexOf('\\nAI:') === -1) current = 'Utente:\\n' + current;\n        const next = (current ? current + '\\n\\n' : '') + 'AI:\\n' + aiText + '\\n\\nUtente:\\n';\n        promptBox.value = next;\n        try { promptBox.dispatchEvent(new Event('input', {bubbles:true})); } catch(e) {}\n        setTimeout(function(){ try { promptBox.focus({preventScroll:true}); promptBox.selectionStart = promptBox.selectionEnd = promptBox.value.length; } catch(e) {} }, 60);\n        return true;\n    };\n    window.setConfigAiReplyHtml = function(replyBox, html, plainText) {\n        const safeHtml = String(html || '');\n        const rawText = plainText != null ? String(plainText) : window.tiConfigAiHtmlToPlainText482(safeHtml);\n        window.tiAppendConfigAiToUnifiedChat482(rawText);\n        if (replyBox) {\n            replyBox.style.display = 'none';\n            replyBox.innerHTML = '';\n        }\n    };\n\n    \/\/ 30.9.664: stato persistente dei processi AI di configurazione.\n    \/\/ Se la AI chiede specifiche o non aggiorna record, non e un completamento: la richiesta resta visibile anche dopo F5.\n    window.tiConfigAiStateKey330 = function(db) {\n        return 'ti_ai_ss_config_ai_state_330_' + String(db || '').replace(\/[^a-zA-Z0-9_.-]\/g, '_');\n    };\n    window.saveConfigAiState330 = function(db, prompt, reply, status) {\n        try {\n            if (!db) return;\n            const payload = {\n                db: String(db || ''),\n                prompt: String(prompt || ''),\n                reply: String(reply || ''),\n                status: String(status || ''),\n                ts: Date.now()\n            };\n            localStorage.setItem(window.tiConfigAiStateKey330(db), JSON.stringify(payload));\n            sessionStorage.setItem('ti_ai_ss_last_config_db_330', String(db || ''));\n        } catch(e) {}\n    };\n    window.clearConfigAiState330 = function(db) {\n        try { if (db) localStorage.removeItem(window.tiConfigAiStateKey330(db)); } catch(e) {}\n    };\n    window.getConfigAiState330 = function(db) {\n        try {\n            if (!db) return null;\n            const raw = localStorage.getItem(window.tiConfigAiStateKey330(db));\n            if (!raw) return null;\n            const payload = JSON.parse(raw);\n            if (!payload || payload.db !== String(db || '')) return null;\n            if (payload.ts && (Date.now() - payload.ts) > 86400000) {\n                localStorage.removeItem(window.tiConfigAiStateKey330(db));\n                return null;\n            }\n            if (payload.status === 'running' && payload.ts && (Date.now() - payload.ts) > 180000) {\n                localStorage.removeItem(window.tiConfigAiStateKey330(db));\n                return null;\n            }\n            return payload;\n        } catch(e) { return null; }\n    };\n    window.tiConfigAiPendingShownKey446 = function(db) {\n        return 'ti_ai_ss_config_ai_pending_shown_446_' + String(db || '').replace(\/[^a-zA-Z0-9_.-]\/g, '_');\n    };\n    window.showConfigAiPendingClarification446 = function(db, payload, replyBox, promptBox) {\n        try {\n            if (!payload || payload.status !== 'awaiting_user_details') return false;\n            const reply = String(payload.reply || '').trim();\n            if (!reply) return false;\n            if (window.hideProgressPopup) window.hideProgressPopup(false, 'Chiarimento richiesto', {notifyUser:false});\n            if (replyBox && window.setConfigAiReplyHtml) {\n                const html = '<b>Chiarimento AI richiesto<\/b><br><br>' + window.escapeHtml(reply).replace(\/\\n\/g, '<br>') + '<br><br><span style=\"color:#cbd5e1;font-size:12px;\">Rispondi nel campo Configurazione AI e premi Invia. Non viene avviato nessun nuovo processo finche questa richiesta non viene gestita.<\/span>';\n                replyBox.style.display = 'block';\n                window.setConfigAiReplyHtml(replyBox, html, reply);\n            }\n            if (promptBox) {\n                const draft = String(promptBox.value || '').trim();\n                if (draft && draft !== String(payload.prompt || '').trim()) {\n                    try { sessionStorage.setItem('ti_ai_ss_config_ai_blocked_draft_446_' + String(db || ''), draft); } catch(e) {}\n                }\n                if (!String(promptBox.value || '').trim()) promptBox.value = 'Utente:\\n';\n                promptBox.placeholder = 'Rispondi al chiarimento richiesto dalla AI e premi Invia...';\n                setTimeout(function(){ try { promptBox.focus({preventScroll:true}); promptBox.selectionStart = promptBox.selectionEnd = promptBox.value.length; } catch(e) {} }, 80);\n            }\n            try { sessionStorage.setItem(window.tiConfigAiPendingShownKey446(db), String(Date.now())); } catch(e) {}\n            try { if (window.showPluginModal) window.showPluginModal('ti-conf-ov'); } catch(e) {}\n            return true;\n        } catch(e) { return false; }\n    };\n    window.configAiPendingClarificationNeedsDisplay446 = function(db, payload, prompt) {\n        try {\n            if (!payload || payload.status !== 'awaiting_user_details' || !String(payload.reply || '').trim()) return false;\n            const p = String(prompt || '').trim();\n            const oldPrompt = String(payload.prompt || '').trim();\n            const shownRaw = sessionStorage.getItem(window.tiConfigAiPendingShownKey446(db));\n            const shownAt = shownRaw ? parseInt(shownRaw, 10) : 0;\n            const shownRecently = shownAt && (Date.now() - shownAt) < 3600000;\n            if (!shownRecently) return true;\n            if (!p) return true;\n            if (oldPrompt && p === oldPrompt) return true;\n            return false;\n        } catch(e) { return true; }\n    };\n    window.buildConfigAiPromptWithPendingAnswer446 = function(db, payload, prompt) {\n        try {\n            if (!payload || payload.status !== 'awaiting_user_details') return String(prompt || '');\n            const p = String(prompt || '').trim();\n            if (!p) return p;\n            const oldPrompt = String(payload.prompt || '').trim();\n            const clarification = String(payload.reply || '').replace(\/<[^>]+>\/g, ' ').trim();\n            if (!oldPrompt || p === oldPrompt) return p;\n            try { window.clearConfigAiState330(db); } catch(e) {}\n            try { sessionStorage.removeItem(window.tiConfigAiPendingShownKey446(db)); } catch(e) {}\n            return 'Richiesta precedente: ' + oldPrompt + '\\nChiarimento richiesto dalla AI: ' + clarification + '\\nRisposta utente: ' + p;\n        } catch(e) { return String(prompt || ''); }\n    };\n    window.restoreConfigAiState330 = function(force) {\n        try {\n            const dbSel = gE('ti-ditta');\n            const db = dbSel ? String(dbSel.value || '') : String(sessionStorage.getItem('ti_ai_ss_last_config_db_330') || '');\n            if (!db || db === 'NEW_DB') return false;\n            const payload = window.getConfigAiState330 ? window.getConfigAiState330(db) : null;\n            if (!payload) return false;\n            const replyBox = gE('ti-conf-ai-reply');\n            const promptBox = gE('ti-conf-ai-prompt');\n            if (!replyBox) return false;\n            const currentText = String(replyBox.innerText || replyBox.textContent || '').trim();\n            if (!force && currentText && !\/fammi pensare|elaborando|attendere\/i.test(currentText)) return false;\n            if (promptBox && !String(promptBox.value || '').trim() && payload.prompt) promptBox.value = payload.prompt;\n            let label = '';\n            if (payload.status === 'awaiting_user_details') label = '<b>Richiesta AI in attesa di specifiche utente<\/b><br><br>';\n            else if (payload.status === 'running') label = '<b>Elaborazione precedente non verificata dopo refresh<\/b><br><br>';\n            replyBox.style.display = 'block';\n            window.setConfigAiReplyHtml(replyBox, label + window.escapeHtml(String(payload.reply || '')).replace(\/\\n\/g, '<br>'), String(payload.reply || ''));\n            return true;\n        } catch(e) { return false; }\n    };\n    window.finishConfigAiAwaitingUser330 = function(replyBox, db, prompt, replyText) {\n        if (window.hideProgressPopup) window.hideProgressPopup(false, 'Chiarimento richiesto', {notifyUser:false});\n        if (replyBox) {\n            replyBox.style.display = 'block';\n            window.setConfigAiReplyHtml(replyBox, window.escapeHtml(String(replyText || '')).replace(\/\\n\/g, '<br>'), String(replyText || ''));\n        }\n        window.saveConfigAiState330(db, prompt, replyText, 'awaiting_user_details');\n        try { window.showPluginModal('ti-conf-ov'); } catch(e) {}\n    };\n    (function installConfigAiRestore330(){\n        if (window.__tiConfigAiRestore330Installed) return;\n        window.__tiConfigAiRestore330Installed = true;\n        const prevOpen = window.openModal;\n        if (typeof prevOpen === 'function') {\n            window.openModal = function(id) {\n                const ret = prevOpen.apply(this, arguments);\n                if (id === 'ti-conf-ov') setTimeout(function(){ window.restoreConfigAiState330(false); }, 250);\n                return ret;\n            };\n        }\n        document.addEventListener('DOMContentLoaded', function(){ setTimeout(function(){ window.restoreConfigAiState330(false); }, 700); });\n        setTimeout(function(){ window.restoreConfigAiState330(false); }, 1200);\n    })();\n\n    \/\/ 30.9.664: gestione robusta processo Configurazione AI.\n    \/\/ Usa il tasto Esci gia esistente: non aggiunge nuovi pulsanti, ma ferma UI, stato locale e token server.\n    window.tiActiveConfigAiRequest434 = null;\n    window.isConfigAiFastAdvicePrompt434 = function(prompt) {\n        const rawPrompt434 = String(prompt || '');\n        const p = rawPrompt434.toLowerCase().normalize ? rawPrompt434.toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '') : rawPrompt434.toLowerCase();\n        if (!p.trim()) return false;\n        const hasDiscount = \/\\b(sconto|sconti|scontare|scontato|promozione|promozioni|promo|offerta|offerte|coupon|ribasso|riduzione|saldo|saldi)\\b\/.test(p);\n        const hasScope = \/\\b(prodotto|prodotti|articolo|articoli|servizio|servizi|catalogo|listino|prezzo|prezzi|tutti|tutto)\\b\/.test(p);\n        const hasPeriod = \/\\b(fino|fine|mese|scadenza|validita|temporaneo|periodo)\\b\/.test(p);\n        return !!(hasDiscount && (hasScope || hasPeriod));\n    };\n    window.configAiTraceHtml434 = function(prompt, isFast) {\n        const safe = window.escapeHtml ? window.escapeHtml(String(prompt || '')) : String(prompt || '').replace(\/[&<>]\/g, function(c){ return ({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]); });\n        const mode = isFast ? 'Percorso veloce: nessun import e nessuna lettura file allegati; se il comando e applicativo puo salvare solo dati esistenti.' : 'Elaborazione Configurazione AI.';\n        return '<div class=\"ti-conf-ai-trace\" style=\"margin-top:10px;padding:8px 10px;border-radius:10px;background:#0f172a;border:1px solid #334155;color:#dbeafe;font-size:12px;line-height:1.35;text-align:left;\">'\n            + '<b>Istruzione ricevuta dalla Configurazione AI<\/b><br>'\n            + '<span style=\"display:block;margin-top:4px;white-space:pre-wrap;\">' + safe + '<\/span>'\n            + '<div style=\"margin-top:6px;color:#bfdbfe;\">' + mode + '<\/div>'\n            + '<\/div>';\n    };\n    window.startConfigAiRequest434 = function(db, prompt, isFast) {\n        const token = 'cfgai-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10);\n        window.tiActiveConfigAiRequest434 = { token: token, db: String(db || ''), prompt: String(prompt || ''), fast: !!isFast, cancelled: false, startedAt: Date.now() };\n        return token;\n    };\n    window.isConfigAiRequestActive434 = function(token) {\n        const st = window.tiActiveConfigAiRequest434;\n        return !!(st && st.token === token && !st.cancelled);\n    };\n    window.finishConfigAiRequest434 = function(token) {\n        const st = window.tiActiveConfigAiRequest434;\n        if (st && st.token === token) window.tiActiveConfigAiRequest434 = null;\n    };\n    window.notifyConfigAiCancelServer434 = function(st) {\n        try {\n            if (!st || !st.token || !st.db || !window.buildAjaxFormData) return;\n            const fdCancel = window.buildAjaxFormData('ti_ai_config_action', {db: st.db, mode: 'cancel_config_ai', request_token: st.token, prompt: ''});\n            if (navigator.sendBeacon) {\n                try { navigator.sendBeacon(window.tiUrl, fdCancel); return; } catch(e) {}\n            }\n            fetch(window.tiUrl, {method:'POST', body:window.ensureAjaxActionField(fdCancel), keepalive:true, credentials:'same-origin'}).catch(function(){});\n        } catch(e) {}\n    };\n    window.cancelActiveConfigAiRequest434 = function(reason) {\n        const st = window.tiActiveConfigAiRequest434;\n        if (!st || st.cancelled) return false;\n        st.cancelled = true;\n        window.notifyConfigAiCancelServer434(st);\n        try { if (st.db && window.clearConfigAiState330) window.clearConfigAiState330(st.db); } catch(e) {}\n        try { if (window.tiConfigAiFastAdviceTimeout434) clearTimeout(window.tiConfigAiFastAdviceTimeout434); } catch(e) {}\n        const replyBox = gE('ti-conf-ai-reply');\n        const msg = 'Processo Configurazione AI interrotto con Esci. Nessuna modifica e stata salvata.\\n\\nIstruzione ricevuta: ' + String(st.prompt || '');\n        try { if (window.tiLongProcessState && window.tiLongProcessState.controller) window.tiLongProcessState.controller.abort(); } catch(e) {}\n        const sendBtn435 = gE('ti-conf-ai-send');\n        if (sendBtn435) { sendBtn435.disabled = false; sendBtn435.innerText = 'Invia'; }\n        if (replyBox && window.setConfigAiReplyHtml) {\n            replyBox.style.display = 'block';\n            window.setConfigAiReplyHtml(replyBox, msg);\n        }\n        try { if (window.hideProgressPopup) window.hideProgressPopup(false, 'Configurazione AI interrotta', {notifyUser:false, errorMessage:'Configurazione AI interrotta.'}); } catch(e) {}\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        window.tiActiveConfigAiRequest434 = null;\n        return true;\n    };\n\n    window.runConfigBot = function() {\n        const box = gE('ti-conf-ai-prompt'); const replyBox = gE('ti-conf-ai-reply'); const btn = gE('ti-conf-ai-send');\n        const dbSel = gE('ti-ditta');\n        if (!box || !replyBox || !btn || !dbSel || !dbSel.value || dbSel.value === 'NEW_DB') { window.tiAlert('Seleziona prima una ditta.'); return; }\n        let prompt = (box.value || '').trim();\n        if (!prompt) {\n            const pendingEmpty446 = window.getConfigAiState330 ? window.getConfigAiState330(dbSel.value) : null;\n            if (pendingEmpty446 && pendingEmpty446.status === 'awaiting_user_details') {\n                window.showConfigAiPendingClarification446 && window.showConfigAiPendingClarification446(dbSel.value, pendingEmpty446, replyBox, box);\n                return;\n            }\n            window.tiAlert('Scrivi una istruzione di configurazione.'); return;\n        }\n        const pendingState446 = window.getConfigAiState330 ? window.getConfigAiState330(dbSel.value) : null;\n        if (pendingState446 && pendingState446.status === 'awaiting_user_details') {\n            if (window.configAiPendingClarificationNeedsDisplay446 && window.configAiPendingClarificationNeedsDisplay446(dbSel.value, pendingState446, prompt)) {\n                window.showConfigAiPendingClarification446 && window.showConfigAiPendingClarification446(dbSel.value, pendingState446, replyBox, box);\n                return;\n            }\n            if (window.buildConfigAiPromptWithPendingAnswer446) prompt = window.buildConfigAiPromptWithPendingAnswer446(dbSel.value, pendingState446, prompt);\n        }\n        const roleTxt = String(window.tiRole || '').toLowerCase();\n        const isDbAdmin = roleTxt.includes('amministratore') || window.tiUser === 'SSGlobalAdmin';\n        const wantsStructure = \/struttura\\s+database|adegua\\s+struttura|allinea\\s+struttura|schema\\s+template|aggiungi\\s+campo|rimuovi\\s+campo|nuova\\s+tabella|elimina\\s+tabella\/i.test(prompt);\n        if (wantsStructure && !isDbAdmin) {\n            replyBox.style.display = 'block';\n            replyBox.innerHTML = 'Le modifiche della struttura del database non sono possibili per il tuo ruolo. Richiedi l intervento di un amministratore.';\n            return;\n        }\n        btn.disabled = true; const oldText = btn.innerText; btn.innerText = '\u23f3 Elaborazione...';\n        const isFastAdvicePrompt434 = !!(window.isConfigAiFastAdvicePrompt434 && window.isConfigAiFastAdvicePrompt434(prompt));\n        const requestToken434 = (window.startConfigAiRequest434 ? window.startConfigAiRequest434(dbSel.value, prompt, isFastAdvicePrompt434) : ('cfgai-' + Date.now()));\n        replyBox.style.display = 'none';\n        replyBox.innerHTML = '';\n        if (isFastAdvicePrompt434) {\n            try { if (window.clearConfigAiState330) window.clearConfigAiState330(dbSel.value); } catch(e) {}\n        } else {\n            window.saveConfigAiState330(dbSel.value, prompt, 'Elaborazione avviata. Se hai aggiornato la pagina prima della risposta finale, rientra in Configurazione e ripeti o verifica i record: la richiesta resta qui come promemoria.', 'running');\n        }\n        window.showProgressPopup('Configurazione AI', isFastAdvicePrompt434 ? 'Percorso veloce promozioni: nessun file allegato in lettura; eventuale salvataggio solo su campi esistenti.' : 'Sto elaborando le istruzioni e l eventuale file allegato. La percentuale \u00e8 stimata.', {startPercent: 2, targetPercent: isFastAdvicePrompt434 ? 60 : 94, stepMs: isFastAdvicePrompt434 ? 350 : 800, expectedMs: isFastAdvicePrompt434 ? 8000 : 55000});\n        const fd = window.buildAjaxFormData('ti_ai_config_action', {\n            db: dbSel.value,\n            mode: 'ai_ops',\n            prompt: prompt,\n            request_token: requestToken434\n        });\n        if (window.configBotFile) fd.append('file_url', window.configBotFile);\n        if (window.tiConfigImportToken) fd.append('import_token', window.tiConfigImportToken);\n        if (isFastAdvicePrompt434) {\n            try {\n                if (window.tiConfigAiFastAdviceTimeout434) clearTimeout(window.tiConfigAiFastAdviceTimeout434);\n                window.tiConfigAiFastAdviceTimeout434 = setTimeout(function(){\n                    if (!window.isConfigAiRequestActive434 || !window.isConfigAiRequestActive434(requestToken434)) return;\n                    try { if (window.tiLongProcessState && window.tiLongProcessState.controller) window.tiLongProcessState.controller.abort(); } catch(e) {}\n                    if (window.cancelActiveConfigAiRequest434) window.cancelActiveConfigAiRequest434('fast_advice_timeout');\n                }, 20000);\n            } catch(e) {}\n        }\n        window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d => {\n            if (window.isConfigAiRequestActive434 && !window.isConfigAiRequestActive434(requestToken434)) {\n                btn.disabled = false; btn.innerText = oldText;\n                try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n                return;\n            }\n            try { if (window.tiConfigAiFastAdviceTimeout434) clearTimeout(window.tiConfigAiFastAdviceTimeout434); } catch(e) {}\n            btn.disabled = false; btn.innerText = oldText;\n            if (!d.success) {\n                const maybeData = d && d.data ? d.data : {};\n                if (maybeData && maybeData.clarify_request && maybeData.reply) {\n                    window.finishConfigAiAwaitingUser330(replyBox, dbSel.value, prompt, String(maybeData.reply));\n                    return;\n                }\n                window.hideProgressPopup(false, 'Errore configurazione AI');\n                if (window.finishConfigAiRequest434) window.finishConfigAiRequest434(requestToken434);\n                try { if (window.clearConfigAiState330 && isFastAdvicePrompt434) window.clearConfigAiState330(dbSel.value); } catch(e) {}\n                replyBox.style.display = 'block';\n                window.setConfigAiReplyHtml(replyBox, window.formatConfigAiErrorMessage(d, 'Errore configurazione AI', prompt));\n                return;\n            }\n            const replyText = d.data && d.data.reply ? d.data.reply : 'Operazione completata.';\n            const processStatusEarly434 = d.data && d.data.process_status ? String(d.data.process_status) : '';\n            if (processStatusEarly434 === 'cancelled') {\n                if (window.cancelActiveConfigAiRequest434) window.cancelActiveConfigAiRequest434('server_cancelled');\n                return;\n            }\n            if (d.data && d.data.awaiting_confirm) {\n                window.hideProgressPopup(true, 'Anteprima import pronta', {notifyUser:false, remindSave:false, successMessage:'Anteprima pronta.'});\n                replyBox.style.display = 'none';\n                window.tiConfigImportToken = d.data.import_token || d.data.preview_token || window.tiConfigImportToken || null;\n                if (window.finishConfigAiRequest434) window.finishConfigAiRequest434(requestToken434);\n                window.showConfigImportPreview(replyText, window.tiConfigImportToken);\n                return;\n            }\n            if (d.data && d.data.needs_reload) window.tiConfigImportToken = null;\n            let cleanReplyText = replyText.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n            if (d.data && d.data.ai_instruction_echo && cleanReplyText.indexOf('Istruzione ricevuta\/inviata alla Configurazione AI') === -1) {\n                cleanReplyText = 'Istruzione ricevuta\/inviata alla Configurazione AI: \"' + String(d.data.ai_instruction_echo) + '\".\\n\\n' + cleanReplyText;\n            }\n            const updatedCount = (d.data && typeof d.data.updated_count !== 'undefined') ? (parseInt(d.data.updated_count, 10) || 0) : null;\n            const processStatus = d.data && d.data.process_status ? String(d.data.process_status) : '';\n            const replyLower330 = cleanReplyText.toLowerCase();\n            const isAdviceCompleted330 = (processStatus === 'completed_advice' || processStatus === 'completed_operational_advice');\n            const needsUserDetails330 = !isAdviceCompleted330 && (!!(d.data && d.data.clarify_request)\n                || processStatus === 'awaiting_user_details'\n                || (updatedCount === 0 && !d.data.needs_reload && \/(indicami|specific|quali record|quale record|criterio piu preciso|criterio pi\u00f9 preciso|non ho trovato|record aggiornati:\\s*0)\/i.test(replyLower330)));\n            if (needsUserDetails330) {\n                if (window.finishConfigAiRequest434) window.finishConfigAiRequest434(requestToken434);\n                window.finishConfigAiAwaitingUser330(replyBox, dbSel.value, prompt, cleanReplyText);\n                return;\n            } else if (isAdviceCompleted330) {\n                window.hideProgressPopup(true, 'Consiglio operativo AI completato', {notifyUser:false, remindSave:false, successMessage:'Consiglio operativo AI completato.'});\n            } else {\n                window.hideProgressPopup(true, 'Configurazione AI completata', {remindSave:true, successMessage:'Attivita AI di configurazione completata.'});\n            }\n            replyBox.style.display = 'block';\n            window.setConfigAiReplyHtml(replyBox, cleanReplyText);\n            window.saveConfigAiState330(dbSel.value, prompt, cleanReplyText, isAdviceCompleted330 ? 'completed_advice' : ((d.data && d.data.needs_reload) ? 'completed_with_updates' : 'completed'));\n            if (window.finishConfigAiRequest434) window.finishConfigAiRequest434(requestToken434);\n            if (d.data && d.data.needs_reload) {\n                const fdDb = window.buildAjaxFormData('ti_ai_get_db_data', {db: dbSel.value});\n                fetch(window.tiUrl, {method:'POST', body:window.ensureAjaxActionField(fdDb)}).then(r2 => r2.json()).then(dbRes => {\n                    if (dbRes.success && dbRes.data) { window.currentDbData = dbRes.data; window.renderConfig(); }\n                });\n            }\n        }).catch(err => {\n            if (window.isConfigAiRequestActive434 && !window.isConfigAiRequestActive434(requestToken434)) {\n                btn.disabled = false; btn.innerText = oldText;\n                try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n                return;\n            }\n            try { if (window.tiConfigAiFastAdviceTimeout434) clearTimeout(window.tiConfigAiFastAdviceTimeout434); } catch(e) {}\n            btn.disabled = false; btn.innerText = oldText;\n            if (err && (err.userCancelled || window.isLongAIProcessCancelled())) {\n                if (window.cancelActiveConfigAiRequest434) { window.cancelActiveConfigAiRequest434('abort_catch'); return; }\n                replyBox.style.display = 'block'; window.setConfigAiReplyHtml(replyBox, 'Operazione interrotta dall utente. Per completarla dovrai ripetere la richiesta.');\n                try { if (window.clearConfigAiState330) window.clearConfigAiState330(dbSel.value); } catch(e) {}\n                window.clearLongAIProcess(); return;\n            }\n            if (window.finishConfigAiRequest434) window.finishConfigAiRequest434(requestToken434);\n            window.hideProgressPopup(false, 'Errore configurazione AI');\n            replyBox.style.display = 'block';\n            window.setConfigAiReplyHtml(replyBox, window.formatConfigAiErrorMessage({message: 'Errore di comunicazione o risposta server non valida: ' + (err && err.message ? err.message : 'errore sconosciuto')}, 'Errore configurazione AI', prompt));\n        });\n    };\n\n    \/\/ 30.9.710: conserva la funzione base non avvolta; i wrapper successivi non devono creare catene ricorsive.\n    try { if (typeof window.runConfigBot === 'function' && !window.__tiRunConfigBotCore710) window.__tiRunConfigBotCore710 = window.runConfigBot; } catch(e) {}\n\n    window.openAtecoSearch = function(targetId) {\n        const target = gE('ateco-target-id');\n        const input = gE('ateco-in');\n        const res = gE('ateco-res');\n        const ov = gE('ti-ateco-ov');\n        if (!target || !input || !res || !ov) {\n            if (window.tiAlert) window.tiAlert('Ricerca ATECO non disponibile: ricarica la configurazione e riprova.');\n            return false;\n        }\n        target.value = targetId || '';\n        input.value = '';\n        res.innerHTML = '<i>Scrivi l\\'attivit\u00e0 da cercare...<\/i>';\n        try { window.openModal('ti-ateco-ov'); } catch(e) {}\n        try {\n            ov.setAttribute('data-ti-config-child-modal', '1');\n            if (window.preparePluginModal) window.preparePluginModal(ov);\n            ov.style.setProperty('display', 'flex', 'important');\n            ov.style.setProperty('z-index', '2147483500', 'important');\n            ov.removeAttribute('aria-hidden');\n            ov.setAttribute('data-ti-force-front', '1');\n            if (window.updatePluginScrollLock) window.updatePluginScrollLock();\n            setTimeout(function(){ try { input.focus({preventScroll:true}); } catch(e) {} }, 60);\n        } catch(e) {}\n        return false;\n    };\n    window.searchAteco = function() {\n        const query = gE('ateco-in').value; if (!query) return;\n        const resBox = gE('ateco-res'); resBox.innerHTML = '<i>Ricerca in corso con l\\'IA... \u23f3<\/i>';\n        const fd = new FormData(); fd.append('ti_action', 'ti_ai_chat_start'); fd.append('db', gE('ti-ditta').value || 'NEW_DB'); fd.append('text', \"CERCA ATECO: \" + query);\n        fetch(window.tiUrl, {method: 'POST', body: fd}).then(r=>r.json()).then(res=>{ if (res.success) resBox.innerHTML = res.data.reply; else resBox.innerHTML = \"<span style='color:#ef4444;'>Errore ricerca ATECO.<\/span>\"; });\n    };\n    window.selectAteco = function(codice, desc) { const targetId = gE('ateco-target-id').value; if (targetId) { const inputEl = gE(targetId); if (inputEl) { inputEl.value = desc; inputEl.dispatchEvent(new Event('change')); } } window.closeModal('ti-ateco-ov'); };\n\n    window.showUploadChoice = function(target) { window.targetUploadContext = target || 'chat'; window.openModal('ti-up-choice-ov'); };\n    window.chooseLocal = function() { if (window.tiOpenDirectUpload616) return window.tiOpenDirectUpload616(window.targetUploadContext || 'chat'); return false; };\n    \n    window.chooseAI = function() { \n        window.closeModal('ti-up-choice-ov'); \n        let row = null;\n        if (window.targetUploadContext && typeof window.targetUploadContext === 'object' && window.targetUploadContext.tbl) {\n            row = window.currentDbData.Tabelle[window.targetUploadContext.tbl][window.targetUploadContext.idx] || null;\n        }\n        window.populateAIGenPrompt(row);\n        if (gE('ai-gen-detail')) gE('ai-gen-detail').value = '';\n        gE('ai-gen-res').style.display = 'none'; \n        gE('ai-gen-btn').style.display = 'block'; \n        window.openModal('ti-ai-gen-ov'); \n    };\n    \n    window.doAIGen = function() {\n        let prompt = gE('ai-gen-prompt').value.trim();\n        const detailInstructions = (gE('ai-gen-detail') && gE('ai-gen-detail').value) ? gE('ai-gen-detail').value.trim() : '';\n        if(!prompt && !detailInstructions) { window.tiAlert(\"Inserisci una descrizione!\"); return; }\n        if (!prompt && detailInstructions) prompt = detailInstructions;\n        if (!\/^Attivit\u00e0 ditta:\/i.test(prompt) && !\/^Verifica anzitutto\/i.test(prompt)) { prompt = window.buildAIGenPrompt(prompt); gE('ai-gen-prompt').value = prompt; }\n        if (detailInstructions) {\n            const detailBlock = ' Istruzioni di dettaglio per la generazione foto: ' + detailInstructions + '.';\n            if (prompt.indexOf(detailInstructions) === -1) prompt += detailBlock;\n        }\n        const btn = gE('ai-gen-btn'); btn.innerText = \"\u23f3 Generazione in corso... (10-20 sec)\"; btn.disabled = true;\n        window.showProgressPopup(\"AI Image Generator\", \"Sto generando l'immagine richiesta. La percentuale e stimata fino alla risposta del servizio.\", {startPercent: 6, targetPercent: 93, stepMs: 650});\n        const dbVal = gE('ti-ditta').value || 'Global'; const fd = new FormData(); fd.append('ti_action', 'ti_ai_generate_media'); fd.append('db', dbVal); fd.append('prompt', prompt);\n        window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n            btn.innerText = \"Genera Immagine\"; btn.disabled = false;\n            if(d.success) { window.hideProgressPopup(true, 'Immagine generata', {remindSave:true, successMessage:'Attivita AI completata. Immagine generata correttamente.'}); window.currentGenFilename = d.data.filename; gE('ai-gen-img').src = d.data.url; gE('ai-gen-btn').style.display = 'none'; gE('ai-gen-res').style.display = 'block'; } else { window.hideProgressPopup(false, 'Errore generazione immagine'); window.tiAlert((d && d.data && d.data.message) ? d.data.message : 'Errore generazione immagine.'); } \n        }).catch(e => { btn.innerText = \"Genera Immagine\"; btn.disabled = false; if (e && (e.userCancelled || window.isLongAIProcessCancelled())) { window.clearLongAIProcess(); return; } window.hideProgressPopup(false, 'Errore generazione immagine'); const msg = (e && e.message) ? e.message : 'Errore di rete.'; window.tiAlert(msg); });\n    };\n    \n    window.resetAIGen = function() { gE('ai-gen-res').style.display = 'none'; gE('ai-gen-btn').style.display = 'block'; gE('ai-gen-img').src = ''; if (gE('ai-gen-detail')) gE('ai-gen-detail').value = ''; window.currentGenFilename = null; };\n    \n    window.useAIGen = function() {\n        if(!window.currentGenFilename) return; const dbVal = gE('ti-ditta').value || 'Global'; const tgt = window.targetUploadContext; window.closeModal('ti-ai-gen-ov');\n        if(tgt === 'chat') {\n            if(window.addMsg) window.addMsg('ai',\"\u23f3 Elaborazione file AI...\"); \n            const fd = new FormData(); fd.append('ti_action', 'ti_ai_media_upload'); fd.append('db', dbVal); fd.append('tbl', 'Chat'); fd.append('temp_file', window.currentGenFilename);\n            window.fetchJsonSafe(window.tiUrl,{method:'POST',body:fd}).then(d=>{ if(d.success) { window.lastUploadedFile = d.data.url; const promptText = `Ho generato questa immagine. Analizzala.`; window.rememberLastFileContext(window.lastUploadedFile, promptText, false); gE('ti-msg').value = promptText; gE('ti-send').click(); } else if(window.addMsg) window.addMsg('ai',`\u26a0\ufe0f Errore salvataggio file AI.`); });\n        } else if (typeof tgt === 'object') {\n            let ctx = tgt;\n            const fd = new FormData(); fd.append('ti_action', 'ti_ai_media_upload'); fd.append('db', dbVal); fd.append('tbl', ctx.tbl); fd.append('temp_file', window.currentGenFilename); fd.append('record_label', window.getRecordLabelForUpload(ctx.tbl, window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[ctx.tbl] ? window.currentDbData.Tabelle[ctx.tbl][ctx.idx] : null, ctx.tbl));\n            window.fetchJsonSafe(window.tiUrl,{method:'POST',body:fd}).then(d=>{ \n                if(d.success) { \n                    let oldVal = window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] || '';\n                    let arr = oldVal.split(',').map(f=>f.trim()).filter(f=>f);\n                    arr.push(d.data.name);\n                    window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] = arr.join(', ');\n                    if(!window.modifiedFields) window.modifiedFields = new Set();\n                    window.modifiedFields.add(`${ctx.tbl}-${ctx.idx}-${ctx.key}`); if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n                    window.renderConfig();\n                    window.openFileManager(ctx.tbl, ctx.idx, ctx.key);\n                } else window.tiAlert(\"Errore: \" + d.data.message); \n            });\n        } else {\n            let tblName = 'Config'; if(tgt.startsWith('cfg-img-')) { const match = tgt.match(\/cfg-img-([^-]+)-\/); if(match) tblName = match[1]; } else if(tgt === 'p-foto') tblName = 'Utenti'; \n            const fd = new FormData(); fd.append('ti_action', 'ti_ai_media_upload'); fd.append('db', dbVal); fd.append('tbl', tblName); fd.append('temp_file', window.currentGenFilename); const recordLabel = (tgt === 'p-foto') ? (gE('p-user').value || gE('p-utente').value || 'utente') : tblName; fd.append('record_label', recordLabel);\n            window.fetchJsonSafe(window.tiUrl,{method:'POST',body:fd}).then(d=>{ if(d.success) { const inp = gE(tgt); if(inp) { inp.value = d.data.name; inp.dispatchEvent(new Event('change')); if(tgt.startsWith('cfg-')) window.renderConfig(); } window.tiAlert(\"Immagine salvata!\"); } else window.tiAlert(\"Errore: \" + d.data.message); });\n        }\n    };\n\n    window.handleFileSelect = function(e) {\n        if(!e.target.files.length) return;\n        const tgt = e.target.dataset.target;\n        const dbVal = gE('ti-ditta').value || 'Global';\n        const fileObj = e.target.files[0];\n\n        if (tgt === 'manager') {\n            let ctx = window.targetUploadContext;\n            const fd = new FormData(); fd.append('ti_action', 'ti_ai_media_upload'); fd.append('db', dbVal); fd.append('file', fileObj); fd.append('tbl', ctx.tbl); fd.append('record_label', window.getRecordLabelForUpload(ctx.tbl, window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[ctx.tbl] ? window.currentDbData.Tabelle[ctx.tbl][ctx.idx] : null, ctx.tbl));\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n                if(d.success) {\n                    let oldVal = window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] || '';\n                    let arr = oldVal.split(',').map(f=>f.trim()).filter(f=>f);\n                    arr.push(d.data.name);\n                    window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] = arr.join(', ');\n                    if(!window.modifiedFields) window.modifiedFields = new Set();\n                    window.modifiedFields.add(`${ctx.tbl}-${ctx.idx}-${ctx.key}`); if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n                    window.renderConfig();\n                    window.openFileManager(ctx.tbl, ctx.idx, ctx.key);\n                } else {\n                    window.tiAlert('Err: ' + (d.data && d.data.message ? d.data.message : 'Errore upload.'));\n                }\n                e.target.dataset.target = ''; e.target.value = '';\n            }).catch(err => {\n                window.tiAlert('Errore di rete: ' + err.message);\n                e.target.dataset.target = ''; e.target.value = '';\n            });\n\n        } else if (tgt === 'config-bot-file') {\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_media_upload');\n            fd.append('db', dbVal);\n            fd.append('file', fileObj);\n            fd.append('tbl', 'Chat');\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n                if (d.success) {\n                    window.configBotFile = d.data.url;\n                    const fileBox = gE('ti-conf-ai-file');\n                    const promptBox = gE('ti-conf-ai-prompt');\n                    const replyBox = gE('ti-conf-ai-reply');\n                    const fileName = d.data.name || fileObj.name || 'file';\n                    if (fileBox) {\n                        fileBox.style.display = 'block';\n                        fileBox.innerHTML = `\ud83d\udcce <b>File caricato per AI:<\/b> ${fileName}<br><span style=\"font-size:13px;color:#cbd5e1;\">Adesso indica cosa deve fare la AI con il contenuto del file.<\/span>`;\n                    }\n                    if (replyBox) {\n                        replyBox.style.display = 'block';\n                        replyBox.innerHTML = '\u2705 File caricato correttamente.';\n                    }\n                    if (promptBox && !promptBox.value.trim()) {\n                        promptBox.value = 'Ho caricato un file per la AI. Analizza il contenuto del file e applica la mia prossima istruzione operativa.';\n                    }\n                    if (promptBox) promptBox.focus();\n                } else {\n                    window.tiAlert('Err: ' + (d.data && d.data.message ? d.data.message : 'Errore upload file.'));\n                }\n                e.target.dataset.target = ''; e.target.value = '';\n            }).catch(err => {\n                window.tiAlert('Errore di rete: ' + err.message);\n                e.target.dataset.target = ''; e.target.value = '';\n            });\n\n        } else if (tgt === 'purchase-import-file') {\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_media_upload');\n            fd.append('db', dbVal);\n            fd.append('file', fileObj);\n            fd.append('tbl', 'Chat');\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n                if (d.success) {\n                    const fileName = d.data.name || fileObj.name || 'file';\n                    if (window.tiAlert) window.tiAlert('File acquisizioni caricato: ' + fileName + '. Preparo import acquisizioni.');\n                    window.startPurchaseFileImport(d.data.url, fileName);\n                } else {\n                    window.tiAlert('Err: ' + (d.data && d.data.message ? d.data.message : 'Errore upload file acquisizioni.'));\n                }\n                e.target.dataset.target = ''; e.target.value = '';\n            }).catch(err => {\n                window.tiAlert('Errore di rete: ' + err.message);\n                e.target.dataset.target = ''; e.target.value = '';\n            });\n\n        } else if (tgt && tgt !== 'chat') {\n            const tblMatch = tgt.match(\/cfg-img-([^-]+)-\/);\n            const tblName = tblMatch ? tblMatch[1] : (tgt === 'p-foto' ? 'Utenti' : 'Config');\n            const fd = new FormData(); fd.append('ti_action', 'ti_ai_media_upload'); fd.append('db', dbVal); fd.append('file', fileObj); fd.append('tbl', tblName); const recordLabel = (tgt === 'p-foto') ? (gE('p-user').value || gE('p-utente').value || 'utente') : tblName; fd.append('record_label', recordLabel);\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n                if(d.success) {\n                    const inp = gE(tgt);\n                    if(inp) {\n                        inp.value = d.data.name;\n                        inp.dispatchEvent(new Event('change'));\n                        if(tgt.startsWith('cfg-')) window.renderConfig();\n                    }\n                    window.tiAlert('Memoria aggiornata!');\n                } else {\n                    window.tiAlert('Err: ' + (d.data && d.data.message ? d.data.message : 'Errore upload.'));\n                }\n                e.target.dataset.target = ''; e.target.value = '';\n            }).catch(err => {\n                window.tiAlert('Errore di rete: ' + err.message);\n                e.target.dataset.target = ''; e.target.value = '';\n            });\n        } else {\n            const runUpload = function(uploadFile){\n                const realFile = (uploadFile instanceof File) ? uploadFile : fileObj;\n                const fd = new FormData(); fd.append('ti_action', 'ti_ai_media_upload'); fd.append('db', dbVal); fd.append('file', realFile, realFile.name || fileObj.name || 'upload'); fd.append('tbl', 'Chat');\n                if(window.addMsg) window.addMsg('ai', '\u23f3 Analisi in corso...');\n                window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(d=>{\n                    if(d.success) {\n                        window.lastUploadedFile = d.data.url;\n                        window.startUserUploadedFileAI(window.lastUploadedFile, realFile.name || fileObj.name || '');\n                    } else {\n                        window.tiAlert('Err: ' + (d.data && d.data.message ? d.data.message : 'Errore upload.'));\n                    }\n                    e.target.dataset.target = ''; e.target.value = '';\n                }).catch(err => {\n                    window.tiAlert('Errore di rete: ' + err.message);\n                    e.target.dataset.target = ''; e.target.value = '';\n                });\n            };\n            if (typeof window.prepareUploadFileForAI === 'function') {\n                window.prepareUploadFileForAI(fileObj).then(runUpload);\n            } else {\n                runUpload(fileObj);\n            }\n        }\n    };\n\n    window.getConfigButtonTableName = function(btn) {\n        let tbl = '';\n        try {\n            const enc = btn && btn.getAttribute ? (btn.getAttribute('data-tbl-enc') || '') : '';\n            if (enc) tbl = decodeURIComponent(enc);\n        } catch(e) { tbl = ''; }\n        try {\n            if (!tbl && btn && btn.closest) {\n                const det = btn.closest('details.ti-cfg-details');\n                if (det && det.dataset) tbl = det.dataset.tbl || '';\n            }\n        } catch(e) {}\n        return window.resolveConfigTableName ? window.resolveConfigTableName(tbl) : tbl;\n    };\n\n    window.handleConfigDeleteRowClick = function(ev, btn) {\n        try { if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } } catch(e) {}\n        const tbl = window.getConfigButtonTableName ? window.getConfigButtonTableName(btn) : '';\n        let idx = NaN;\n        try { idx = parseInt((btn && btn.getAttribute && (btn.getAttribute('data-row-idx') || btn.getAttribute('data-idx'))) || '', 10); } catch(e) { idx = NaN; }\n        if (isNaN(idx) && btn && btn.closest) {\n            try { const tr = btn.closest('tr[data-row-idx]'); if (tr) idx = parseInt(tr.getAttribute('data-row-idx') || '', 10); } catch(e) {}\n        }\n        if (!tbl) { window.tiAlert && window.tiAlert('Tabella non riconosciuta. Riapri la configurazione e riprova.'); return false; }\n        if (isNaN(idx)) { window.tiAlert && window.tiAlert('Riga non riconosciuta. Riapri la configurazione e riprova.'); return false; }\n        if (typeof window.delTableRow === 'function') window.delTableRow(tbl, idx, btn);\n        return false;\n    };\n\n    window.handleConfigDeleteSelectedClick = function(ev, btn) {\n        try { if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } } catch(e) {}\n        const tbl = window.getConfigButtonTableName ? window.getConfigButtonTableName(btn) : '';\n        const safeCls = (btn && btn.getAttribute) ? (btn.getAttribute('data-safe-cls') || '') : '';\n        if (!tbl) { window.tiAlert && window.tiAlert('Tabella non riconosciuta. Riapri la configurazione e riprova.'); return false; }\n        if (typeof window.delBulk === 'function') window.delBulk(tbl, safeCls, btn);\n        return false;\n    };\n\n    window.getConfigTableScope = function(tName, sourceEl) {\n        let scope = null;\n        try {\n            if (sourceEl && sourceEl.closest) scope = sourceEl.closest('details.ti-cfg-details');\n            if (scope && scope.dataset && String(scope.dataset.tbl || '') === String(tName || '')) return scope;\n            document.querySelectorAll('details.ti-cfg-details').forEach(function(det){\n                if (!scope && det.dataset && String(det.dataset.tbl || '') === String(tName || '')) scope = det;\n            });\n        } catch(e) {}\n        return scope || document;\n    };\n\n    window.getSelectedConfigRowIndexes = function(tName, sC, sourceEl) {\n        const scope = window.getConfigTableScope ? window.getConfigTableScope(tName, sourceEl) : document;\n        let checks = [];\n        try { checks = Array.from(scope.querySelectorAll('input.ti-config-row-select:checked')); } catch(e) { checks = []; }\n        if (!checks.length && sC) {\n            try { checks = Array.from(scope.querySelectorAll('.ti-cb-' + sC + ':checked')); } catch(e) { checks = []; }\n        }\n        return checks.map(function(x){\n            const raw = (x && x.dataset && x.dataset.rowIdx != null) ? x.dataset.rowIdx : (x ? x.value : '');\n            return parseInt(raw, 10);\n        }).filter(function(n){ return !isNaN(n); });\n    };\n\n    window.toggleAllTI = function(ck, sC, sourceEl) {\n        const tbl = (ck && ck.closest && ck.closest('details.ti-cfg-details') && ck.closest('details.ti-cfg-details').dataset) ? ck.closest('details.ti-cfg-details').dataset.tbl : '';\n        const scope = window.getConfigTableScope ? window.getConfigTableScope(tbl, sourceEl || ck) : document;\n        let boxes = [];\n        try { boxes = Array.from(scope.querySelectorAll('input.ti-config-row-select')); } catch(e) { boxes = []; }\n        if (!boxes.length && sC) {\n            try { boxes = Array.from(scope.querySelectorAll('.ti-cb-' + sC)); } catch(e) { boxes = []; }\n        }\n        boxes.forEach(function(c){ if (c && !c.disabled) c.checked = !!(ck && ck.checked); });\n    };\n    \n    window.delBulk = function(tN, sC, sourceEl) { \n        tN = window.resolveConfigTableName ? window.resolveConfigTableName(tN) : tN;\n        const low = String(tN || '').toLowerCase();\n        if ((low === 'utenti' || low === 'operatori') && !window.canPhysicallyDeleteUserRows()) {\n            window.tiAlert('Solo amministratore o configuratore possono cancellare fisicamente utenti o operatori dal database.');\n            return;\n        }\n        const selectedIndexes = window.getSelectedConfigRowIndexes ? window.getSelectedConfigRowIndexes(tN, sC, sourceEl) : [];\n        if(selectedIndexes.length===0) { window.tiAlert('Seleziona almeno una riga da eliminare nella tabella corrente.'); return; }\n        const doDelete = function(){ \n            if (window.deleteConfigRowsByIndexes) {\n                const ok = window.deleteConfigRowsByIndexes(tN, selectedIndexes, {render:true});\n                if (ok) window.tiAlert('Righe cancellate dalla configurazione corrente: ' + (window.tiLastConfigDeleteCount || selectedIndexes.length) + '. Premi Salva Modifiche per aggiornare il database.');\n                else window.tiAlert('Nessuna riga eliminata. Righe cancellate: 0. Verifica che le righe selezionate siano ancora presenti e che la tabella possa essere svuotata.');\n                return;\n            }\n            if(!Array.isArray(window.currentDbData.Tabelle[tN])) window.currentDbData.Tabelle[tN]=Object.values(window.currentDbData.Tabelle[tN] || {}); \n            if (window.recordConfigDeletedRowsManifest) window.recordConfigDeletedRowsManifest(tN, window.currentDbData.Tabelle[tN], selectedIndexes);\n            selectedIndexes.sort((a,b)=>b-a).forEach(i=>{ window.currentDbData.Tabelle[tN].splice(i,1); }); \n            window.reindexTableRows(tN);\n            window.markTableRowsModified(tN);\n            if (window.syncConfigTableAfterRowMutation) window.syncConfigTableAfterRowMutation(tN);\n            window.renderConfig(); \n        };\n        if (typeof window.tiConfirmYesNoAction === 'function') window.tiConfirmYesNoAction('Eliminare ' + selectedIndexes.length + ' righe selezionate dalla tabella ' + String(tN || '') + '?', doDelete, null, 'ELIMINA', 'ANNULLA');\n        else window.tiConfirm('Eliminare ' + selectedIndexes.length + ' righe?', doDelete);\n    };\n\n    window.bindConfigDeleteActions = function() {\n        if (window.__tiConfigDeleteActionsBound) return;\n        window.__tiConfigDeleteActionsBound = true;\n        document.addEventListener('click', function(ev){\n            const target = ev && ev.target && ev.target.closest ? ev.target : null;\n            if (!target) return;\n            const rowBtn = target.closest('[data-ti-config-delete-row=\"1\"], .ti-config-delete-row-btn');\n            if (rowBtn && rowBtn.closest && rowBtn.closest('details.ti-cfg-details')) {\n                ev.preventDefault();\n                ev.stopPropagation();\n                if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                if (typeof window.handleConfigDeleteRowClick === 'function') window.handleConfigDeleteRowClick(ev, rowBtn);\n                return false;\n            }\n            const bulkBtn = target.closest('[data-ti-config-delete-selected=\"1\"], .ti-config-delete-selected-btn');\n            if (bulkBtn && bulkBtn.closest && bulkBtn.closest('details.ti-cfg-details')) {\n                ev.preventDefault();\n                ev.stopPropagation();\n                if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                if (typeof window.handleConfigDeleteSelectedClick === 'function') window.handleConfigDeleteSelectedClick(ev, bulkBtn);\n                return false;\n            }\n        }, true);\n    };\n    window.bindConfigDeleteActions();\n\n    \/* 30.9.664 - Stato sessione pesante lato server per prenotazioni multi-servizio: evita cookie troppo grandi\/ERR_HTTP2_PROTOCOL_ERROR. *\/\n    \/* 30.9.664 - Comunicazioni e report per amministratori\/configuratori\/responsabili in popup separato in primo piano con pulsante Copia testo. *\/\n    \/* 30.9.664 - Popup informativi attivi ma non intercettano le tabelle operative ordine; ruoli privilegiati usano anche ordine e tabella ordine. *\/\n    \/* 30.9.664 - Salvataggio parziale tabelle valide con warning mirato; pulsante per aprire la prima tabella da correggere. *\/\n\n\n    window.lastUserPromptText = '';\n    window.restoreLastUserPromptText = function() {\n        const m = gE('ti-msg');\n        if (m && window.lastUserPromptText) {\n            m.value = window.lastUserPromptText;\n            try { m.focus(); } catch(e) {}\n        }\n    };\n\n    \/\/ v30.9.664 - Il campo input del chatbot utente viene sempre svuotato dopo ogni invio utente e dopo ogni risposta AI.\n    window.tiClearUserChatInput = function() {\n        try {\n            const msgInp = gE('ti-msg');\n            if (!msgInp) return;\n            msgInp.value = '';\n            msgInp.removeAttribute('data-ti-pending-text');\n            try { msgInp.dispatchEvent(new Event('input', { bubbles: true })); } catch(_e) {}\n            try { msgInp.dispatchEvent(new Event('change', { bubbles: true })); } catch(_e) {}\n        } catch(_e) {}\n    };\n    window.addMsg = function(r,h,rawSource) {\n        const chat = window.tiEnsureUserChatBelowAiForm717 ? window.tiEnsureUserChatBelowAiForm717() : gE('ti-chat-box');\n        if(!chat) return null;\n        const d = document.createElement('div');\n        d.className = 'ti-bubble ' + r;\n        const baseHtml = String((typeof rawSource === 'string' && rawSource !== '') ? rawSource : (h || ''));\n        d.innerHTML = String(h || '');\n        if(r === 'ai') {\n            d.__tiChatHtmlByLang = d.__tiChatHtmlByLang || {};\n            d.__tiChatHtmlByLang.it = baseHtml;\n            d.setAttribute('data-ti-it-html', baseHtml);\n            if(typeof rawSource === 'string' && rawSource !== '') {\n                d.setAttribute('data-raw-source', rawSource);\n                window.lastAiReplySource = rawSource;\n            }\n        }\n        chat.appendChild(d);\n        try {\n            if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(d);\n            if (window.tiI18nTranslateSubtree) window.tiI18nTranslateSubtree(d);\n            if (r === 'ai' && window.tiExtractOrderAiResponseFromBubble637) window.tiExtractOrderAiResponseFromBubble637(d);\n            if (r === 'ai' && d.__tiChatHtmlByLang) {\n                const langNow = String(window.currLang || 'it').toLowerCase() === 'en' ? 'en' : 'it';\n                d.__tiChatHtmlByLang[langNow] = d.innerHTML;\n                if (langNow === 'it') {\n                    d.__tiChatHtmlByLang.it = d.innerHTML;\n                    d.setAttribute('data-ti-it-html', d.innerHTML);\n                }\n            }\n        } catch(e) {}\n        chat.scrollTop = chat.scrollHeight;\n        if (r === 'user' || r === 'ai') { window.tiClearUserChatInput(); }\n        if (r === 'ai') {\n            setTimeout(function(){\n                window.tiClearUserChatInput();\n                const hint = d.querySelector('.ti-order-hint');\n                const toolbar = d.querySelector('.ti-order-toolbar');\n                const target = hint || toolbar;\n                if (target && target.scrollIntoView) {\n                    try { target.scrollIntoView({behavior:'smooth', block:'start'}); } catch(e) { target.scrollIntoView(true); }\n                }\n                if (window.tiExtractOrderAiResponseFromBubble637) window.tiExtractOrderAiResponseFromBubble637(d);\n                window.refreshOrderConfirmPanel();\n                if (window.tiPendingOrderAiResponseHtml637 && window.tiRenderExternalAiResponse637) window.tiRenderExternalAiResponse637(window.tiPendingOrderAiResponseHtml637);\n            }, 80);\n        }\n        const msgInp = gE('ti-msg');\n        if(r === 'ai' && msgInp) { msgInp.value=''; msgInp.focus(); }\n        return d;\n    };\n\n    window.collectImportMappingFromUI = function(root) {\n        const box = root || document;\n        let mapping = {};\n        try {\n            if (window.tiConfigImportLastMapping && typeof window.tiConfigImportLastMapping === 'object') mapping = Object.assign({}, window.tiConfigImportLastMapping);\n            if (box && box.dataset && box.dataset.importMapping) {\n                try { mapping = Object.assign(mapping, JSON.parse(decodeURIComponent(box.dataset.importMapping))); } catch(_e) {}\n            }\n            box.querySelectorAll('.ti-import-group-target').forEach(function(sel){\n                const idx = parseInt(sel.getAttribute('data-group-index') || '0', 10);\n                if (!isNaN(idx)) mapping['__group_target_' + idx] = sel.value || 'Prodotti';\n            });\n            box.querySelectorAll('.ti-import-map-select').forEach(function(sel){\n                const hdr = sel.getAttribute('data-source-header') || '';\n                const idx = parseInt(sel.getAttribute('data-group-index') || '0', 10);\n                if (hdr) {\n                    if (!isNaN(idx)) mapping['__map_' + idx + '__' + hdr] = sel.value || '--';\n                    else mapping[hdr] = sel.value || '--';\n                }\n            });\n            box.querySelectorAll('.ti-import-price-formula').forEach(function(inp){\n                const idx = parseInt(inp.getAttribute('data-group-index') || '0', 10);\n                const val = String(inp.value || '').trim();\n                if (val !== '') {\n                    if (!isNaN(idx)) mapping['__price_formula_' + idx] = val;\n                    else mapping['__price_formula'] = val;\n                }\n            });\n            const pdfImg = box.querySelector('.ti-import-pdf-image-link');\n            if (pdfImg && pdfImg.checked) mapping['__link_pdf_images'] = '1';\n            if (Array.isArray(window.tiConfigImportRefineRules) && window.tiConfigImportRefineRules.length) {\n                mapping['__local_refine_copy_rules'] = JSON.stringify(window.tiConfigImportRefineRules);\n            }\n            try { window.tiConfigImportLastMapping = Object.assign({}, mapping); } catch(_e) {}\n        } catch(e) {}\n        return mapping;\n    };\n\n    window.reapplyImportMappingToUI = function(root, mapping) {\n        const box = root || document;\n        mapping = mapping || {};\n        try {\n            box.querySelectorAll('.ti-import-group-target').forEach(function(sel){\n                const idx = parseInt(sel.getAttribute('data-group-index') || '0', 10);\n                const key = '__group_target_' + idx;\n                if (!isNaN(idx) && Object.prototype.hasOwnProperty.call(mapping, key)) sel.value = mapping[key] || 'Prodotti';\n            });\n            box.querySelectorAll('.ti-import-map-select').forEach(function(sel){\n                const hdr = sel.getAttribute('data-source-header') || '';\n                const idx = parseInt(sel.getAttribute('data-group-index') || '0', 10);\n                const key = '__map_' + idx + '__' + hdr;\n                if (!isNaN(idx) && hdr && Object.prototype.hasOwnProperty.call(mapping, key)) sel.value = mapping[key] || '--';\n            });\n            box.querySelectorAll('.ti-import-price-formula').forEach(function(inp){\n                const idx = parseInt(inp.getAttribute('data-group-index') || '0', 10);\n                const key = '__price_formula_' + idx;\n                if (!isNaN(idx) && Object.prototype.hasOwnProperty.call(mapping, key)) inp.value = mapping[key] || '';\n            });\n            try { window.tiConfigImportLastMapping = Object.assign({}, mapping); } catch(_e) {}\n        } catch(e) {}\n    };\n\n    window.captureImportFocusState = function(sourceEl) {\n        try {\n            const el = sourceEl || document.activeElement;\n            if (!el) return null;\n            const bubble = el.closest ? el.closest('.ti-bubble.ai') : null;\n            const chatBox = gE('ti-chat-box');\n            const state = { bubbleScrollTop: bubble ? bubble.scrollTop : 0 };\n            state.windowScrollX = window.scrollX || window.pageXOffset || 0;\n            state.windowScrollY = window.scrollY || window.pageYOffset || 0;\n            state.chatScrollTop = chatBox ? chatBox.scrollTop : 0;\n            if (chatBox && el.getBoundingClientRect) {\n                try {\n                    const er = el.getBoundingClientRect();\n                    const cr = chatBox.getBoundingClientRect();\n                    state.elementTopInChat = er.top - cr.top;\n                } catch(e) {}\n            }\n            const sc = el.closest ? el.closest('.ti-cfg-table-container, .ti-import-table-container') : null;\n            state.containerScrollLeft = sc ? sc.scrollLeft : 0;\n            state.containerScrollTop = sc ? sc.scrollTop : 0;\n            if (el.classList && el.classList.contains('ti-import-map-select')) {\n                state.kind = 'map';\n                state.groupIndex = el.getAttribute('data-group-index') || '';\n                state.header = el.getAttribute('data-source-header') || '';\n                state.value = el.value || '--';\n                return state;\n            }\n            if (el.classList && el.classList.contains('ti-import-group-target')) {\n                state.kind = 'target';\n                state.groupIndex = el.getAttribute('data-group-index') || '';\n                state.value = el.value || 'Prodotti';\n                return state;\n            }\n            if (el.classList && el.classList.contains('ti-import-price-formula')) {\n                state.kind = 'formula';\n                state.groupIndex = el.getAttribute('data-group-index') || '';\n                state.value = el.value || '';\n                try { state.selStart = el.selectionStart; state.selEnd = el.selectionEnd; } catch(e) {}\n                return state;\n            }\n        } catch(e) {}\n        return null;\n    };\n\n    window.restoreImportFocusState = function(root, state) {\n        if (!state) return;\n        try {\n            const box = root || document;\n            let el = null;\n            if (state.kind === 'map') el = box.querySelector('.ti-import-map-select[data-group-index=\"' + state.groupIndex + '\"][data-source-header=\"' + CSS.escape(state.header || '') + '\"]');\n            else if (state.kind === 'target') el = box.querySelector('.ti-import-group-target[data-group-index=\"' + state.groupIndex + '\"]');\n            else if (state.kind === 'formula') el = box.querySelector('.ti-import-price-formula[data-group-index=\"' + state.groupIndex + '\"]');\n            if (el) {\n                if (typeof el.focus === 'function') el.focus({preventScroll:true});\n                if (state.kind === 'formula') {\n                    try { el.selectionStart = state.selStart || 0; el.selectionEnd = state.selEnd || state.selStart || 0; } catch(e) {}\n                }\n                const sc = el.closest ? el.closest('.ti-cfg-table-container, .ti-import-table-container') : null;\n                if (sc) {\n                    sc.scrollLeft = state.containerScrollLeft || 0;\n                    sc.scrollTop = state.containerScrollTop || 0;\n                }\n                const bubble = el.closest ? el.closest('.ti-bubble.ai') : null;\n                if (bubble) bubble.scrollTop = state.bubbleScrollTop || bubble.scrollTop || 0;\n                const chatBox = gE('ti-chat-box');\n                if (chatBox) {\n                    try {\n                        if (typeof state.elementTopInChat === 'number' && el.getBoundingClientRect) {\n                            const er = el.getBoundingClientRect();\n                            const cr = chatBox.getBoundingClientRect();\n                            chatBox.scrollTop += ((er.top - cr.top) - state.elementTopInChat);\n                        } else if (typeof state.chatScrollTop === 'number') {\n                            chatBox.scrollTop = state.chatScrollTop;\n                        }\n                    } catch(e) {}\n                }\n                try {\n                    if (typeof state.windowScrollY === 'number') window.scrollTo(state.windowScrollX || 0, state.windowScrollY || 0);\n                } catch(e) {}\n            }\n        } catch(e) {}\n    };\n\n    window.refreshImportPreviewFromUI = function(sourceEl) {\n        const previewBody = gE('ti-conf-import-preview-body');\n        const isModalPreview = !!(previewBody && sourceEl && previewBody.contains && previewBody.contains(sourceEl));\n        const bubble = (!isModalPreview && sourceEl && sourceEl.closest) ? sourceEl.closest('.ti-bubble.ai') : null;\n        const targetBox = isModalPreview ? previewBody : bubble;\n        const root = targetBox || document;\n        const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n        if (!dbVal || !targetBox) return;\n        const mapping = window.collectImportMappingFromUI(root);\n        const focusState = window.captureImportFocusState(sourceEl);\n        try { targetBox.dataset.importMapping = encodeURIComponent(JSON.stringify(mapping)); } catch(e) {}\n        const fd = new FormData();\n        fd.append('action', 'ti_ai_config_action');\n        fd.append('ti_action', 'ti_ai_config_action');\n        fd.append('db', dbVal);\n        fd.append('mode', 'refresh_import_preview');\n        fd.append('prompt', 'refresh import preview');\n        if (window.tiConfigImportToken) fd.append('import_token', window.tiConfigImportToken);\n        fd.append('mapping_json', JSON.stringify(mapping));\n        targetBox.style.opacity = '0.75';\n        window.postFormDataJsonSafe(window.tiUrl, fd, {tiNoLongProcessSignal:true}).then(function(res){\n            targetBox.style.opacity = '';\n            if (res && res.success && targetBox) {\n                if (res.data && (res.data.import_token || res.data.preview_token)) window.tiConfigImportToken = res.data.import_token || res.data.preview_token;\n                const rawReply = ((res.data || {}).reply || '').replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                try { window.tiConfigImportLastMapping = Object.assign({}, mapping); } catch(_e) {}\n                if (rawReply) {\n                    targetBox.innerHTML = rawReply;\n                    let applyMap = mapping;\n                    try {\n                        if (targetBox.dataset.importMapping) applyMap = JSON.parse(decodeURIComponent(targetBox.dataset.importMapping));\n                    } catch(e) {}\n                    window.reapplyImportMappingToUI(targetBox, applyMap);\n                    if (window.initConfigTopScrollbars) window.initConfigTopScrollbars(targetBox);\n                    window.restoreImportFocusState(targetBox, focusState);\n                    setTimeout(function(){ window.restoreImportFocusState(targetBox, focusState); }, 0);\n                    if (window.requestAnimationFrame) window.requestAnimationFrame(function(){ window.restoreImportFocusState(targetBox, focusState); });\n                    if (isModalPreview && window.redisplayConfigImportPreviewForFinalConfirm) {\n                        window.redisplayConfigImportPreviewForFinalConfirm('Anteprima modifiche aggiornata dopo la variazione richiesta. Conferma solo se corretta.');\n                    }\n                }\n            }\n        }).catch(function(){\n            targetBox.style.opacity = '';\n            if (isModalPreview && window.markConfigImportPreviewUpdated) {\n                window.markConfigImportPreviewUpdated('Modifica registrata. Se l anteprima non si aggiorna automaticamente, controlla le associazioni visibili prima di confermare.');\n            }\n        });\n    };\n\n    window.scheduleImportPreviewRefresh = function(sourceEl) {\n        clearTimeout(window.importPreviewRefreshTimer);\n        window.importPreviewRefreshTimer = setTimeout(function(){ window.refreshImportPreviewFromUI(sourceEl); }, 180);\n    };\n    window.showFullImg = function(url) { const fullImg = gE('ti-full-img'); if(fullImg) { fullImg.src = url; window.openModal('ti-img-ov'); } };\n    window.getFileNameFromUrlOrValue = function(value) {\n        const raw = String(value || '').trim();\n        if (!raw) return 'file';\n        try {\n            const u = new URL(raw, window.location.origin);\n            const qf = u.searchParams.get('file');\n            if (qf) return decodeURIComponent(qf);\n            const pts = u.pathname.split('\/').filter(Boolean);\n            return decodeURIComponent(pts.pop() || raw);\n        } catch(e) {\n            const clean = raw.split('?')[0].split('#')[0];\n            const pts = clean.split('\/').filter(Boolean);\n            return decodeURIComponent(pts.pop() || raw);\n        }\n    };\n    window.isImageFileName = function(name) { return \/\\.(jpg|jpeg|png|webp|gif|bmp|svg)$\/i.test(String(name || '')); };\n    window.isPdfFileName = function(name) { return \/\\.pdf$\/i.test(String(name || '')); };\n    window.getFilePreviewIcon = function(name) {\n        const n = String(name || '').toLowerCase();\n        if (\/\\.pdf$\/.test(n)) return '\ud83d\udcd5';\n        if (\/\\.(doc|docx)$\/.test(n)) return '\ud83d\udcd8';\n        if (\/\\.(xls|xlsx|csv)$\/.test(n)) return '\ud83d\udcca';\n        if (\/\\.(txt|json|xml)$\/.test(n)) return '\ud83d\udcc4';\n        if (\/\\.(mp4|mov|avi|webm)$\/.test(n)) return '\ud83c\udfac';\n        return '\ud83d\udcce';\n    };\n    window.ensureFilePreviewModal = function() {\n        let ov = gE('ti-file-preview-ov');\n        if (ov) return ov;\n        ov = document.createElement('div');\n        ov.id = 'ti-file-preview-ov';\n        ov.className = 'ti-ov ti-closable-ov';\n        ov.style.zIndex = '2147483647';\n        ov.innerHTML = '<div class=\"ti-modal\" style=\"width:94vw;max-width:920px;text-align:left;\">'\n            + '<button type=\"button\" class=\"ti-modal-x-red\" onclick=\"window.closeFilePreviewModal()\" title=\"Chiudi\">X<\/button>'\n            + '<h3 id=\"ti-file-preview-title\" style=\"color:#fff;margin-top:0;margin-bottom:12px;padding-right:38px;\">Preview file<\/h3>'\n            + '<div id=\"ti-file-preview-content\" style=\"min-height:120px;color:#e5e7eb;\"><\/div>'\n            + '<div class=\"ti-modal-bottom-close-row\"><button type=\"button\" class=\"ti-btn btn-close\" onclick=\"window.closeFilePreviewModal()\">Chiudi<\/button><\/div>'\n            + '<\/div>';\n        document.body.appendChild(ov);\n        return ov;\n    };\n    window.closeFilePreviewModal = function() { window.closeModal('ti-file-preview-ov'); };\n    window.openExternalLinkedFile = function(url) { if (url) window.open(String(url), '_blank', 'noopener'); };\n    window.openFilePreviewModal = function(file, title) {\n        const obj = (typeof file === 'string') ? {url:file, name:window.getFileNameFromUrlOrValue(file)} : (file || {});\n        const url = String(obj.url || obj.raw || '').trim();\n        if (!url) return;\n        const name = String(obj.name || window.getFileNameFromUrlOrValue(url) || 'file');\n        const safeName = window.escapeHtml(name);\n        const safeUrl = window.escapeHtml(url);\n        const ov = window.ensureFilePreviewModal();\n        const titleEl = gE('ti-file-preview-title');\n        const content = gE('ti-file-preview-content');\n        if (titleEl) titleEl.textContent = title || name;\n        let html = '';\n        if (window.isImageFileName(name) || window.isImageFileName(url)) {\n            const fb = window.getCompanyFallbackMediaUrl(gE('ti-ditta') ? gE('ti-ditta').value : '');\n            html = '<div style=\"text-align:center;\"><img decoding=\"async\" src=\"' + safeUrl + '\" data-fallback=\"' + window.escapeHtml(fb) + '\" onerror=\"window.applyFallbackImage(this)\" alt=\"' + safeName + '\" style=\"max-width:100%;max-height:72vh;object-fit:contain;border-radius:10px;background:#000;border:1px solid #334155;\"><\/div>';\n        } else if (window.isPdfFileName(name) || window.isPdfFileName(url)) {\n            html = '<iframe src=\"' + safeUrl + '#toolbar=1&navpanes=0\" title=\"' + safeName + '\" style=\"width:100%;height:72vh;border:1px solid #334155;border-radius:10px;background:#000;\"><\/iframe>';\n        } else {\n            html = '<div style=\"text-align:center;padding:28px 12px;background:#020617;border:1px solid #334155;border-radius:12px;\">'\n                + '<div style=\"font-size:48px;margin-bottom:10px;\">' + window.getFilePreviewIcon(name) + '<\/div>'\n                + '<div style=\"font-weight:800;margin-bottom:12px;word-break:break-all;\">' + safeName + '<\/div>'\n                + '<button type=\"button\" class=\"ti-btn\" onclick=\"window.openExternalLinkedFile(decodeURIComponent(\\'' + encodeURIComponent(url) + '\\'))\">Apri originale<\/button>'\n                + '<\/div>';\n        }\n        if (content) content.innerHTML = html;\n        window.openModal('ti-file-preview-ov');\n        if (window.keepUserCommunicationPopupsInFront) window.keepUserCommunicationPopupsInFront();\n    };\n    window.openOriginalLinkedFile = function(url) {\n        if (!url) return;\n        window.openFilePreviewModal({url:String(url), name:window.getFileNameFromUrlOrValue(url)});\n    };\n    window.openOrderLinkedFilesChoice = function(payload) {\n        const files = Array.isArray(payload && payload.files) ? (window.tiDedupeOrderLinkedFiles483 ? window.tiDedupeOrderLinkedFiles483(payload.files.filter(function(f){ return f && f.url; })) : payload.files.filter(function(f){ return f && f.url; })) : [];\n        if (!files.length) { window.openOrderItemPopup(payload); return; }\n        if (files.length === 1) { window.openFilePreviewModal(files[0], payload && payload.name ? payload.name : files[0].name); return; }\n        const ov = window.ensureFilePreviewModal();\n        const titleEl = gE('ti-file-preview-title');\n        const content = gE('ti-file-preview-content');\n        if (titleEl) titleEl.textContent = 'Scegli file da vedere (' + files.length + ' file collegati)';\n        const cards = files.map(function(f, i){\n            const url = String(f.url || '');\n            const name = String(f.name || window.getFileNameFromUrlOrValue(url) || ('file ' + (i + 1)));\n            const safeName = window.escapeHtml(name);\n            const safeUrl = window.escapeHtml(url);\n            const enc = encodeURIComponent(url);\n            const encName = encodeURIComponent(name);\n            let preview = '';\n            if (window.isImageFileName(name) || window.isImageFileName(url)) {\n                const fb = window.getCompanyFallbackMediaUrl(gE('ti-ditta') ? gE('ti-ditta').value : '');\n                preview = '<img decoding=\"async\" src=\"' + safeUrl + '\" data-fallback=\"' + window.escapeHtml(fb) + '\" onerror=\"window.applyFallbackImage(this)\" alt=\"' + safeName + '\">';\n            } else if (window.isPdfFileName(name) || window.isPdfFileName(url)) {\n                preview = '<iframe src=\"' + safeUrl + '#toolbar=0&navpanes=0\" title=\"' + safeName + '\"><\/iframe>';\n            } else {\n                preview = '<div class=\"ti-order-file-preview-icon\">' + window.getFilePreviewIcon(name) + '<\/div>';\n            }\n            return '<button type=\"button\" class=\"ti-order-file-preview\" onclick=\"window.openFilePreviewModal({url:decodeURIComponent(\\'' + enc + '\\'),name:decodeURIComponent(\\'' + encName + '\\')}, decodeURIComponent(\\'' + encName + '\\'))\" title=\"Apri: ' + safeName + '\">' + preview + '<div class=\"ti-order-file-preview-name\">' + safeName + '<\/div><div class=\"ti-order-file-preview-open\">Vedi file<\/div><\/button>';\n        }).join('');\n        if (content) content.innerHTML = '<div class=\"ti-order-modal-files-grid\">' + cards + '<\/div>';\n        window.openModal('ti-file-preview-ov');\n    };\n    window.openOrderLinkedFilesFromEl = function(el) {\n        if (!el) return;\n        const raw = el.getAttribute('data-popup') || '';\n        if (!raw) return;\n        try { window.openOrderLinkedFilesChoice(JSON.parse(decodeURIComponent(raw))); }\n        catch(e) { window.openOrderItemPopupFromEl(el); }\n    };\n    window.renderOrderItemLinkedFiles = function(payload) {\n        const box = gE('ti-order-item-files');\n        if (!box) return;\n        const files = Array.isArray(payload && payload.files) ? payload.files : [];\n        const usable = window.tiDedupeOrderLinkedFiles483 ? window.tiDedupeOrderLinkedFiles483(files.filter(function(f){ return f && f.url; })) : files.filter(function(f){ return f && f.url; });\n        if (!usable.length) { box.style.display = 'none'; box.innerHTML = ''; return; }\n        const title = usable.length === 1 ? 'File collegato' : ('File collegati (' + usable.length + ')');\n        const cards = usable.map(function(f, i){\n            const url = String(f.url || '');\n            const name = String(f.name || window.getFileNameFromUrlOrValue(url) || ('file ' + (i + 1)));\n            const safeName = window.escapeHtml(name);\n            const safeUrl = window.escapeHtml(url);\n            const encUrl = encodeURIComponent(url);\n            let preview = '';\n            if (window.isImageFileName(name) || window.isImageFileName(url)) {\n                const fb = window.getCompanyFallbackMediaUrl(gE('ti-ditta') ? gE('ti-ditta').value : '');\n                preview = `<img decoding=\"async\" src=\"${safeUrl}\" data-fallback=\"${window.escapeHtml(fb)}\" onerror=\"window.applyFallbackImage(this)\" alt=\"${safeName}\">`;\n            } else if (window.isPdfFileName(name) || window.isPdfFileName(url)) {\n                preview = `<iframe src=\"${safeUrl}#toolbar=0&navpanes=0\" title=\"${safeName}\"><\/iframe>`;\n            } else {\n                preview = `<div class=\"ti-order-file-preview-icon\">${window.getFilePreviewIcon(name)}<\/div>`;\n            }\n            return `<button type=\"button\" class=\"ti-order-file-preview\" onclick=\"window.openOriginalLinkedFile(decodeURIComponent('${encUrl}'))\" title=\"Apri versione originale: ${safeName}\">${preview}<div class=\"ti-order-file-preview-name\">${safeName}<\/div><div class=\"ti-order-file-preview-open\">Apri originale<\/div><\/button>`;\n        }).join('');\n        box.innerHTML = `<div class=\"ti-order-modal-files-title\">Seleziona documento da vedere<\/div><div class=\"ti-order-modal-files-grid\">${cards}<\/div>`;\n        box.style.display = '';\n    };\n    window.orderItemPopupState = null;\n    window.openOrderItemPopup = function(payload) {\n        window.orderItemPopupState = payload || null;\n        if (!payload) return;\n        const qty = window.parseQtyInput(payload.qty);\n        const img = gE('ti-order-item-img'); if (img) { const fb = window.getCompanyFallbackMediaUrl(gE('ti-ditta') ? gE('ti-ditta').value : ''); img.setAttribute('data-fallback', fb || ''); img.onerror = function(){ window.applyFallbackImage(this); }; img.src = payload.img || fb || ''; img.style.display = (payload.img || fb) ? '' : 'none'; }\n        const titleEl = gE('ti-order-item-title'); if (titleEl) { const tblLow = String(payload.tblName || payload.table || '').toLowerCase(); titleEl.textContent = tblLow.indexOf('serviz') !== -1 ? 'Dettaglio servizio' : (tblLow.indexOf('prodott') !== -1 ? 'Dettaglio articolo' : 'Dettaglio voce'); }\n        const nameEl = gE('ti-order-item-name'); if (nameEl) nameEl.textContent = payload.name || '';\n        const capEl = gE('ti-order-item-caption'); if (capEl) capEl.textContent = payload.desc || payload.name || '';\n        const priceEl = gE('ti-order-item-price'); if (priceEl) priceEl.innerHTML = payload.priceHtml || ('\u20ac ' + window.fmtNum(payload.price || 0));\n        const qtyEl = gE('ti-order-item-qty'); if (qtyEl) qtyEl.value = window.formatQtyInput(qty);\n        const totalEl = gE('ti-order-item-total'); if (totalEl) totalEl.textContent = '\u20ac ' + window.fmtNum((payload.price || 0) * qty);\n        const noteEl = gE('ti-order-item-note'); if (noteEl) noteEl.textContent = payload.note || '';\n        window.renderOrderItemLinkedFiles(payload);\n        if (qtyEl) { qtyEl.oninput = function(){ const q = window.parseQtyInput(this.value); const tot = gE('ti-order-item-total'); if (tot) tot.textContent = '\u20ac ' + window.fmtNum((payload.price || 0) * q); }; }\n        window.openModal('ti-order-item-ov');\n    };\n    window.openOrderItemPopupFromEl = function(el) {\n        if (!el) return;\n        const raw = el.getAttribute('data-popup') || '';\n        if (!raw) return;\n        try {\n            const payload = JSON.parse(decodeURIComponent(raw));\n            window.openOrderItemPopup(payload);\n        } catch(e) {\n            console.error('Popup ordine non valido', e, raw);\n        }\n    };\n    window.confirmOrderItemPopup = function() {\n        const st = window.orderItemPopupState || {};\n        const qtyEl = gE('ti-order-item-qty');\n        const qty = qtyEl ? window.parseQtyInput(qtyEl.value) : 0;\n        if (st.orderId) window.setOrderQty(st.orderId, qty);\n        window.closeOrderItemPopup();\n    };\n    window.closeOrderItemPopup = function() { window.orderItemPopupState = null; window.closeModal('ti-order-item-ov'); };\n    window.setOrderQty = function(orderId, qty) {\n        const val = window.formatQtyInput(qty);\n        document.querySelectorAll('.ti-q[data-order-id=\"' + orderId + '\"]').forEach(inp => { inp.value = val; window.syncOrderQty(inp); });\n    };\n    window.canViewSystemInstructions = function() {\n        const role = String(window.tiRole || '').toLowerCase();\n        const user = String(window.tiUser || '').toLowerCase();\n        return user === 'ssglobaladmin' || role.includes('amministratore') || role.includes('configuratore');\n    };\n    window.stripSystemInstructionSegments = function(instr) {\n        let txt = String(instr || '');\n        if (!txt) return '';\n        txt = txt.replace(\/\\\\\\$sys\\s*=\/gi, '$sys=');\n        txt = txt.replace(\/&dollar;\\s*sys\\s*=\/gi, '$sys=');\n        function stripSysFromLine(line) {\n            let rawLine = String(line || '');\n            if (\/^\\s*\\$sys\\s*=\/i.test(rawLine)) return '';\n            let idx = rawLine.search(\/\\$sys\\s*=\/i);\n            if (idx < 0) return rawLine;\n            const before = rawLine.slice(0, idx).trim();\n            const after = rawLine.slice(idx).replace(\/^\\$sys\\s*=\\s*\/i, '');\n            \/\/ Se dopo l'istruzione di sistema esiste un'altra regola commerciale concatenata\n            \/\/ (es. \"... $sys=... sconto 50%\"), conserva solo quella regola commerciale.\n            const nextCommercial = after.search(\/\\b(?:sconto|prezzo\\s*(?:applicato|finale|vendita)?|rincaro|aumento|maggiorazione)\\b|[-+]\\s*\\d+[\\.,]?\\d*\\s*%\/i);\n            const tail = nextCommercial >= 0 ? after.slice(nextCommercial).trim() : '';\n            return [before, tail].filter(Boolean).join(' ');\n        }\n        const lines = txt.split(\/\\r?\\n\/).map(stripSysFromLine).filter(function(line){ return String(line || '').trim() !== ''; });\n        return lines.join('\\n').replace(\/[ \\t]{2,}\/g, ' ').trim();\n    };\n    window.extractVisibleInstructionDiscountLabels = function(instr) {\n        const txt = window.stripSystemInstructionSegments(instr);\n        const labels = [];\n        const seen = {};\n        function push(label) { if (label && !seen[label]) { seen[label] = true; labels.push(label); } }\n        let m;\n        const pctRe = \/(?:sconto\\s*(?:del|di)?\\s*|-\\s*)(\\d+[\\.,]?\\d*)\\s*%\/gi;\n        while ((m = pctRe.exec(txt)) !== null) push('Sconto ' + String(m[1]).replace('.', ',') + '%');\n        const euroRe = \/sconto\\s*(?:di)?\\s*(?:\u20ac\\s*)?(\\d+[\\.,]?\\d*)\\s*(?:euro|eur)\\b\/gi;\n        while ((m = euroRe.exec(txt)) !== null) push('Sconto \u20ac ' + String(m[1]).replace('.', ','));\n        const addPctRe = \/(?:(?:rincaro|aumento|maggiorazione)\\s*(?:del|di)?\\s*|\\+\\s*)(\\d+[\\.,]?\\d*)\\s*%\/gi;\n        while ((m = addPctRe.exec(txt)) !== null) push('Maggiorazione ' + String(m[1]).replace('.', ',') + '%');\n        return labels;\n    };\n    window.summarizeVisibleOrderInstruction = function(instr) {\n        const txt = window.stripSystemInstructionSegments(instr);\n        if (!txt) return '';\n        const labels = window.extractVisibleInstructionDiscountLabels(txt);\n        if (labels.length) return labels.join(' + ');\n        let m = txt.match(\/prezzo\\s*(?:applicato|finale|vendita)?\\s*[:=]?\\s*\u20ac?\\s*(\\d+[\\.,]?\\d*)\/i);\n        if (m) return 'Prezzo finale \u20ac ' + String(m[1]).replace('.', ',');\n        return txt;\n    };\n    window.getOrderInstructionTextsForDisplay = function(instr) {\n        const raw = String(instr || '').trim();\n        const commercial = window.stripSystemInstructionSegments(raw);\n        const summary = window.summarizeVisibleOrderInstruction(commercial);\n        const canViewSys = window.canViewSystemInstructions();\n        return {\n            raw: raw,\n            commercial: commercial,\n            display: canViewSys ? raw : summary,\n            popup: canViewSys ? raw : summary,\n            summary: summary\n        };\n    };\n    window.extractInstructionPriceAdjustments = function(instr, basePrice) {\n        const originalTxt = String(instr || '');\n        const txt = window.stripSystemInstructionSegments ? window.stripSystemInstructionSegments(originalTxt) : originalTxt;\n        const low = txt.toLowerCase();\n        let finalPrice = parseFloat(basePrice) || 0;\n        const listPrice = finalPrice;\n        let defaultQty = null;\n        const mQty = low.match(\/(?:q\\.?t\u00e0|qt\u00e0|qta|quantit\u00e0|quantita|minimo|minimum)\\s*[:=]?\\s*(\\d+[\\.,]?\\d*)\/i);\n        if (mQty) defaultQty = parseFloat(String(mQty[1]).replace(',', '.')) || null;\n\n        const mSetPrice = low.match(\/prezzo\\s*(?:applicato|finale|vendita)?\\s*[:=]?\\s*\u20ac?\\s*(\\d+[\\.,]?\\d*)\/i);\n        if (mSetPrice) {\n            finalPrice = parseFloat(String(mSetPrice[1]).replace(',', '.')) || finalPrice;\n        } else {\n            \/\/ Applica tutte le variazioni in cascata, nell'ordine in cui compaiono.\n            const re = \/(?:sconto\\s*(?:del|di)?\\s*(\\d+[\\.,]?\\d*)\\s*%|-(\\d+[\\.,]?\\d*)\\s*%|(?:rincaro|aumento|maggiorazione)\\s*(?:del|di)?\\s*(\\d+[\\.,]?\\d*)\\s*%|\\+(\\d+[\\.,]?\\d*)\\s*%|sconto\\s*(?:di)?\\s*\u20ac\\s*(\\d+[\\.,]?\\d*)\\b|sconto\\s*(?:di)?\\s*(\\d+[\\.,]?\\d*)\\s*(?:euro|eur)\\b)\/gi;\n            let m;\n            while ((m = re.exec(low)) !== null) {\n                if (m[1] || m[2]) {\n                    const pct = parseFloat(String(m[1] || m[2]).replace(',', '.')) || 0;\n                    finalPrice = finalPrice * (1 - pct \/ 100);\n                } else if (m[3] || m[4]) {\n                    const pct = parseFloat(String(m[3] || m[4]).replace(',', '.')) || 0;\n                    finalPrice = finalPrice * (1 + pct \/ 100);\n                } else if (m[5] || m[6]) {\n                    const val = parseFloat(String(m[5] || m[6]).replace(',', '.')) || 0;\n                    finalPrice = Math.max(0, finalPrice - val);\n                }\n            }\n        }\n\n        finalPrice = Math.max(0, Math.round(finalPrice * 100) \/ 100);\n        return { finalPrice: finalPrice, listPrice: listPrice, isModified: Math.abs(finalPrice - listPrice) > 0.009, note: txt.trim(), defaultQty: defaultQty };\n    };\n    window.isDateFieldName = function(key) {\n        const k = String(key || '').toLowerCase().trim();\n        return k === 'data' || k === 'data creazione' || k === 'data_appuntamento' || k === 'data appuntamento' || \/(^|\\s)(da gg|a gg)$\/.test(k) || \/(^|_)(data)(_|$)\/.test(k);\n    };\n    window.isTimeFieldName = function(key) {\n        const k = String(key || '').toLowerCase().trim();\n        return k === 'ora' || k === 'ora appuntamento' || k === 'ora_appuntamento' || \/(^|\\s)(da ora|a ora)$\/.test(k) || \/(^|_)(ora)(_|$)\/.test(k);\n    };\n    window.ddmmyyyyToInputDate = function(v) {\n        const s = String(v || '').trim();\n        const m = s.match(\/^(\\d{2})\\\/(\\d{2})\\\/(\\d{4})$\/);\n        return m ? `${m[3]}-${m[2]}-${m[1]}` : '';\n    };\n\n    window.hhmmToInputTime = function(v) {\n        const s = String(v || '').trim();\n        if (!s) return '';\n        if (\/^\\d{2}:\\d{2}$\/.test(s)) return s;\n        if (\/^\\d{1,2}:\\d{2}$\/.test(s)) { const p = s.split(':'); return p[0].padStart(2,'0') + ':' + p[1]; }\n        return '';\n    };\n\n    window.initPluginDateInputs = function(root) {\n        const scope = root || document;\n        scope.querySelectorAll('input[type=\"date\"], input[type=\"time\"]').forEach(function(inp){\n            if (inp.type === 'time' && (!inp.value || !\/^\\d{2}:\\d{2}$\/.test(inp.value))) inp.value = '';\n            if (inp.type === 'date' && (!inp.value || !\/^\\d{4}-\\d{2}-\\d{2}$\/.test(inp.value))) inp.value = '';\n            if (inp.type === 'date') { inp.placeholder = 'gg\/mm\/aaaa'; inp.title = inp.title || 'Seleziona una data'; }\n            if (inp.type === 'time') { inp.placeholder = 'hh:mm'; inp.title = inp.title || 'Seleziona un orario'; }\n        });\n    };\n    window.inputDateToDDMMYYYY = function(v) {\n        const s = String(v || '').trim();\n        const m = s.match(\/^(\\d{4})-(\\d{2})-(\\d{2})$\/);\n        return m ? `${m[3]}\/${m[2]}\/${m[1]}` : s;\n    };\n    window.normalizeInputValueForKey = function(key, value) {\n        if (window.isDateFieldName(key)) return window.inputDateToDDMMYYYY(value);\n        return value;\n    };\n\n    window.tiConfigCellText700 = function(cell) {\n        try {\n            if (!cell) return '';\n            if (cell.matches && cell.matches('input,textarea,select')) {\n                if (cell.tagName && String(cell.tagName).toLowerCase() === 'select') {\n                    return Array.prototype.slice.call(cell.selectedOptions || []).map(function(o){ return o.textContent || o.value || ''; }).join(' ');\n                }\n                return String(('value' in cell) ? cell.value : (cell.textContent || ''));\n            }\n            if (cell.querySelectorAll) {\n                var parts = [];\n                cell.querySelectorAll('input,textarea,select').forEach(function(el){\n                    if (!el) return;\n                    if (el.tagName && String(el.tagName).toLowerCase() === 'select') {\n                        parts.push(Array.prototype.slice.call(el.selectedOptions || []).map(function(o){ return o.textContent || o.value || ''; }).join(' '));\n                    } else if ('value' in el) parts.push(el.value);\n                });\n                var visible = String(cell.innerText || cell.textContent || '').trim();\n                if (visible) parts.push(visible);\n                var joined = parts.map(function(v){ return String(v == null ? '' : v).trim(); }).filter(Boolean).join(' ');\n                if (joined) return joined;\n            }\n            return String(cell.innerText || cell.textContent || '').trim();\n        } catch(e) { return String(cell && (cell.innerText || cell.textContent) || '').trim(); }\n    };\n    window.tiParseLocaleAmount700 = function(raw) {\n        try {\n            var s = String(raw == null ? '' : raw).trim();\n            if (!s) return null;\n            s = s.replace(\/\\u00a0\/g, ' ').replace(\/[\u20ac$\u00a3]\/g, '').replace(\/\\s+\/g, '');\n            s = s.replace(\/[^0-9,.'\\-+]\/g, '');\n            if (!\/[0-9]\/.test(s)) return null;\n            var sign = \/^-\/.test(s) ? -1 : 1;\n            s = s.replace(\/[+\\-]\/g, '');\n            var lastComma = s.lastIndexOf(','), lastDot = s.lastIndexOf('.');\n            var decSep = '';\n            if (lastComma >= 0 && lastDot >= 0) decSep = lastComma > lastDot ? ',' : '.';\n            else if (lastComma >= 0) {\n                var dc = s.length - lastComma - 1;\n                decSep = (dc > 0 && dc <= 2) ? ',' : '';\n            } else if (lastDot >= 0) {\n                var dd = s.length - lastDot - 1;\n                decSep = (dd > 0 && dd <= 2) ? '.' : '';\n            }\n            var normalized;\n            if (decSep) {\n                var p = s.split(decSep);\n                var dec = p.pop();\n                var intp = p.join('').replace(\/[,.'']\/g, '');\n                normalized = intp + '.' + dec.replace(\/[,.'']\/g, '');\n            } else {\n                normalized = s.replace(\/[,.'']\/g, '');\n            }\n            if (!normalized || !\/^\\d+(?:\\.\\d+)?$\/.test(normalized)) return null;\n            var n = parseFloat(normalized) * sign;\n            return isNaN(n) ? null : n;\n        } catch(e) { return null; }\n    };\n    window.getComparableCellValue = function(cell) {\n        const txt = (window.tiConfigCellText700 ? window.tiConfigCellText700(cell) : (cell ? (cell.innerText || cell.textContent || '') : '')).trim();\n        const dataPrice = cell && cell.getAttribute ? cell.getAttribute('data-price') : '';\n        if (dataPrice !== null && dataPrice !== undefined && String(dataPrice).trim() !== '') {\n            const dp = window.tiParseLocaleAmount700 ? window.tiParseLocaleAmount700(dataPrice) : parseFloat(String(dataPrice).replace(',', '.'));\n            if (dp !== null && !isNaN(dp)) return {type:'num', value: dp};\n        }\n        const amount = window.tiParseLocaleAmount700 ? window.tiParseLocaleAmount700(txt) : null;\n        if (amount !== null && !isNaN(amount)) return {type:'num', value: amount};\n        const dmy = txt.match(\/^(\\d{2})\\\/(\\d{2})\\\/(\\d{4})$\/);\n        if (dmy) return {type:'date', value: dmy[3] + dmy[2] + dmy[1]};\n        return {type:'str', value: txt.toLowerCase()};\n    };\n    window.configTableState = window.configTableState || {};\n    window.__configFilterTimers = window.__configFilterTimers || {};\n    window.ensureConfigTableState = function(tbl) {\n        const t = String(tbl || '');\n        if (!window.configTableState[t]) window.configTableState[t] = {filters:{}, filterDrafts:{}, sortKey:'', sortDir:'asc', pinKey:''};\n        const st = window.configTableState[t];\n        if (!st.filters) st.filters = {};\n        if (!st.filterDrafts) st.filterDrafts = {};\n        if (!st.sortKey) st.sortKey = '';\n        if (!st.sortDir) st.sortDir = 'asc';\n        if (typeof st.pinKey === 'undefined') st.pinKey = '';\n        return st;\n    };\n    window.rememberConfigFilterFocus = function(tbl, key, value, inputEl, pending) {\n        try {\n            const active = inputEl || document.activeElement;\n            const val = String(value == null ? '' : value);\n            window.__configFilterFocus = {\n                tbl: String(tbl || ''),\n                key: String(key || ''),\n                value: val,\n                start: active && typeof active.selectionStart === 'number' ? active.selectionStart : val.length,\n                end: active && typeof active.selectionEnd === 'number' ? active.selectionEnd : val.length,\n                pending: pending !== false\n            };\n        } catch(e) {}\n    };\n    if (!window.__configEditableGuard515) {\n        window.__configEditableGuard515 = true;\n        document.addEventListener('focusin', function(ev){\n            const el = ev.target;\n            if (!el || !el.matches) return;\n            if (!el.matches('.ti-conf-window .ti-cfg-filter, .ti-conf-window .ti-cfg-table input, .ti-conf-window .ti-cfg-table textarea, .ti-conf-window .ti-cfg-table select')) return;\n            if (window.ensureAuthorizedConfigInputsEditable512) window.ensureAuthorizedConfigInputsEditable512(el.closest('.ti-conf-window') || document);\n        }, true);\n        document.addEventListener('pointerdown', function(ev){\n            const el = ev.target;\n            if (!el || !el.matches) return;\n            if (!el.matches('.ti-conf-window .ti-cfg-filter, .ti-conf-window .ti-cfg-table input, .ti-conf-window .ti-cfg-table textarea, .ti-conf-window .ti-cfg-table select')) return;\n            if (window.ensureAuthorizedConfigInputsEditable512) window.ensureAuthorizedConfigInputsEditable512(el.closest('.ti-conf-window') || document);\n        }, true);\n    }\n\n    window.toggleConfigPinnedColumn = function(tbl, key, ev) {\n        try { if (ev) { ev.preventDefault && ev.preventDefault(); ev.stopPropagation && ev.stopPropagation(); } } catch(e) {}\n        const st = window.ensureConfigTableState ? window.ensureConfigTableState(tbl) : (window.configTableState[tbl] || (window.configTableState[tbl] = {filters:{}, filterDrafts:{}, sortKey:'', sortDir:'asc', pinKey:''}));\n        st.pinKey = (String(st.pinKey || '') === String(key || '')) ? '' : String(key || '');\n        window.renderConfig();\n    };\n    window.sortConfigTable = function(tbl, key) {\n        const st = window.ensureConfigTableState ? window.ensureConfigTableState(tbl) : (window.configTableState[tbl] || (window.configTableState[tbl] = {filters:{}, filterDrafts:{}, sortKey:'', sortDir:'asc', pinKey:''}));\n        st.sortDir = (st.sortKey === key && st.sortDir === 'asc') ? 'desc' : 'asc';\n        st.sortKey = key;\n        window.renderConfig();\n    };\n    window.setConfigTableFilterDraftOnly522 = function(tbl, key, value, inputEl) {\n        const t = String(tbl || '');\n        const k = String(key || '');\n        const val = String(value == null ? '' : value);\n        window.__configFilterTimers = window.__configFilterTimers || {};\n        const timerKey = encodeURIComponent(t) + '|' + encodeURIComponent(k);\n        if (window.__configFilterTimers[timerKey]) {\n            clearTimeout(window.__configFilterTimers[timerKey]);\n            delete window.__configFilterTimers[timerKey];\n        }\n        const st = window.ensureConfigTableState ? window.ensureConfigTableState(t) : (window.configTableState[t] || (window.configTableState[t] = {filters:{}, filterDrafts:{}, sortKey:'', sortDir:'asc', pinKey:''}));\n        if (!st.filters) st.filters = {};\n        if (!st.filterDrafts) st.filterDrafts = {};\n        st.filterDrafts[k] = val;\n        try { window.__configFilterFocus = null; } catch(e) {}\n        if (inputEl) {\n            inputEl.readOnly = false;\n            inputEl.disabled = false;\n            inputEl.removeAttribute('readonly');\n            inputEl.removeAttribute('disabled');\n            if (inputEl.style) inputEl.style.setProperty('pointer-events', 'auto', 'important');\n        }\n    };\n    window.applyConfigTableFilterImmediate = function(tbl, key, value, inputEl) {\n        window.setConfigTableFilterDraftOnly522(tbl, key, value, inputEl);\n    };\n    window.scheduleConfigTableFilter = function(tbl, key, value, inputEl) {\n        window.setConfigTableFilterDraftOnly522(tbl, key, value, inputEl);\n    };\n    window.filterConfigTable = function(tbl, key, value, inputEl) {\n        window.setConfigTableFilterDraftOnly522(tbl, key, value, inputEl);\n    };\n    window.toggleConfigTableFilterManual522 = function(tbl, key, inputEl, buttonEl, ev) {\n        try { if (ev) { ev.preventDefault && ev.preventDefault(); ev.stopPropagation && ev.stopPropagation(); } } catch(e) {}\n        const t = String(tbl || '');\n        const k = String(key || '');\n        const inp = inputEl || document.querySelector('.ti-cfg-filter[data-tbl=\"' + (window.CSS && CSS.escape ? CSS.escape(t) : t.replace(\/[^a-zA-Z0-9_-]\/g, function(ch){ return '\\\\' + ch; })) + '\"][data-key=\"' + (window.CSS && CSS.escape ? CSS.escape(k) : k.replace(\/[^a-zA-Z0-9_-]\/g, function(ch){ return '\\\\' + ch; })) + '\"]');\n        const val = inp && 'value' in inp ? String(inp.value || '') : '';\n        window.setConfigTableFilterDraftOnly522(t, k, val, inp);\n        const st = window.ensureConfigTableState ? window.ensureConfigTableState(t) : (window.configTableState[t] || (window.configTableState[t] = {filters:{}, filterDrafts:{}, sortKey:'', sortDir:'asc', pinKey:''}));\n        if (!st.filters) st.filters = {};\n        if (!st.filterDrafts) st.filterDrafts = {};\n        const active = String(st.filters[k] || '').trim();\n        if (active) {\n            st.filters[k] = '';\n            st.filterDrafts[k] = '';\n            if (inp) inp.value = '';\n        } else {\n            st.filterDrafts[k] = val;\n            st.filters[k] = val.trim() ? val : '';\n        }\n        try { window.__configFilterFocus = null; } catch(e) {}\n        const oldAllow = window.__tiAllowConfigRender520;\n        window.__tiAllowConfigRender520 = true;\n        try { if (typeof window.renderConfig === 'function') window.renderConfig(); }\n        finally { window.__tiAllowConfigRender520 = oldAllow; }\n        return false;\n    };\n    window.restoreConfigFilterFocus = function() {\n        try {\n            const f = window.__configFilterFocus;\n            if (!f || !f.pending) return;\n            const esc = (window.CSS && typeof window.CSS.escape === 'function') ? window.CSS.escape : function(v){ return String(v).replace(\/[^a-zA-Z0-9_-]\/g, function(ch){ return '\\\\' + ch; }); };\n            const q = '.ti-cfg-filter[data-tbl=\"' + esc(String(f.tbl)) + '\"][data-key=\"' + esc(String(f.key)) + '\"]';\n            const inp = document.querySelector(q);\n            f.pending = false;\n            if (!inp) return;\n            const det = inp.closest ? inp.closest('details.ti-cfg-details') : null;\n            if (det) det.open = true;\n            const confCnt = gE('ti-conf-cnt');\n            const beforeTop = confCnt ? confCnt.scrollTop : null;\n            const wrap = inp.closest ? inp.closest('.ti-cfg-table-container') : null;\n            const beforeWrapTop = wrap ? wrap.scrollTop : null;\n            const beforeWrapLeft = wrap ? wrap.scrollLeft : null;\n            inp.focus({preventScroll:true});\n            const len = String(inp.value || '').length;\n            const s = Math.max(0, Math.min(typeof f.start === 'number' ? f.start : len, len));\n            const e = Math.max(0, Math.min(typeof f.end === 'number' ? f.end : s, len));\n            if (inp.setSelectionRange) inp.setSelectionRange(s, e);\n            if (confCnt && beforeTop !== null) confCnt.scrollTop = beforeTop;\n            if (wrap) {\n                if (beforeWrapTop !== null) wrap.scrollTop = beforeWrapTop;\n                if (beforeWrapLeft !== null) wrap.scrollLeft = beforeWrapLeft;\n                const topBar = wrap.previousElementSibling && wrap.previousElementSibling.classList && wrap.previousElementSibling.classList.contains('ti-sync-hscroll-top') ? wrap.previousElementSibling : null;\n                const bottomBar = wrap.nextElementSibling && wrap.nextElementSibling.classList && wrap.nextElementSibling.classList.contains('ti-sync-hscroll-bottom') ? wrap.nextElementSibling : null;\n                if (topBar) topBar.scrollLeft = wrap.scrollLeft;\n                if (bottomBar) bottomBar.scrollLeft = wrap.scrollLeft;\n            }\n        } catch(e) {}\n    };\n    window.configFilterMatches = function(cellVal, filterVal) {\n        const rawCell = String(cellVal == null ? '' : cellVal).trim();\n        const f = String(filterVal == null ? '' : filterVal).trim();\n        if (!f) return true;\n        const cmpCell = window.getComparableCellValue({innerText: rawCell});\n        const cmpFilter = String(f).match(\/^\\s*(<=|>=|<|>|=)?\\s*(.+?)\\s*$\/);\n        const filterAmount = cmpFilter ? (window.tiParseLocaleAmount700 ? window.tiParseLocaleAmount700(cmpFilter[2]) : null) : null;\n        if (cmpFilter && filterAmount !== null && cmpCell.type === 'num') {\n            const op = cmpFilter[1] || '';\n            if (op === '>') return cmpCell.value > filterAmount;\n            if (op === '<') return cmpCell.value < filterAmount;\n            if (op === '>=') return cmpCell.value >= filterAmount;\n            if (op === '<=') return cmpCell.value <= filterAmount;\n            if (op === '=') return Math.abs(cmpCell.value - filterAmount) < 0.0001;\n            if (Math.abs(cmpCell.value - filterAmount) < 0.0001) return true;\n        }\n        if (\/^\\s*[<>]=?\/.test(f) && cmpCell.type === 'date') {\n            const m = f.match(\/^\\s*(<=|>=|<|>)\\s*(\\d{2}\\\/\\d{2}\\\/\\d{4})\\s*$\/);\n            if (m) {\n                const cv = cmpCell.value;\n                const dv = window.getComparableCellValue({innerText: m[2]}).value;\n                if (m[1] === '>') return cv > dv;\n                if (m[1] === '<') return cv < dv;\n                if (m[1] === '>=') return cv >= dv;\n                if (m[1] === '<=') return cv <= dv;\n            }\n        }\n        const norm = function(v){\n            try { return String(v == null ? '' : v).normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').toLowerCase(); }\n            catch(e) { return String(v == null ? '' : v).toLowerCase(); }\n        };\n        const normLoose = function(v){ return norm(v).replace(\/[^a-z0-9]+\/g, ''); };\n        const rawNorm = norm(rawCell), fNorm = norm(f);\n        if (rawNorm.indexOf(fNorm) >= 0) return true;\n        const rawLoose = normLoose(rawCell), fLoose = normLoose(f);\n        if (fLoose && rawLoose.indexOf(fLoose) >= 0) return true;\n        const rawDigits = String(rawCell).replace(\/\\D+\/g, '');\n        const fDigits = String(f).replace(\/\\D+\/g, '');\n        if (fDigits && rawDigits && rawDigits.indexOf(fDigits) >= 0) return true;\n        return false;\n    };\n\n    window.formatTable = function(t) { \n        t = String(t || '');\n        let h = t.split('[TABLE]')[0];\n        const tiHasOrderTable631 = t.includes('[TABLE]');\n        const tiPortraitAiFirst631 = tiHasOrderTable631 && (function(){ try { return (window.innerHeight || 0) > (window.innerWidth || 0) || (window.matchMedia && window.matchMedia('(orientation: portrait)').matches); } catch(e) { return false; } })();\n        let tiPostTableInfoHtml = '';\n        if (tiHasOrderTable631) {\n            const lastProductClose = t.lastIndexOf('[\/P]');\n            if (lastProductClose >= 0) {\n                let postRaw = t.slice(lastProductClose + 4).trim();\n                \/\/ Mantiene visibile la risposta informativa AI scritta dopo la tabella ordine.\n                \/\/ Prima delle v30.9.664 formatTable mostrava solo il testo prima di [TABLE]\n                \/\/ e ignorava il testo successivo all'ultima riga [P].\n                if (postRaw) {\n                    postRaw = postRaw.replace(\/^(?:<br\\s*\\\/?\\s*>|\\s|&nbsp;)+\/ig, '').trim();\n                    if (postRaw) {\n                        tiPostTableInfoHtml = '<div class=\"ti-info-after-order\" style=\"margin-top:12px;padding:10px 12px;border-radius:12px;background:#111827;border:1px solid rgba(148,163,184,.35);color:#e5e7eb;line-height:1.5;font-size:15.5px;\">' + postRaw.replace(\/\\n+\/g, '<br>') + '<\/div>';\n                    }\n                }\n            }\n        }\n        if (tiHasOrderTable631) {\n            let tiAiInfo637 = '';\n            if (String(h || '').trim() !== '') {\n                tiAiInfo637 += '<div class=\"ti-ai-file-order-note-637\">' + String(h || '').replace(\/\\n+\/g, '<br>') + '<\/div>';\n            }\n            if (tiPostTableInfoHtml) {\n                tiAiInfo637 += tiPostTableInfoHtml.replace('ti-info-after-order', 'ti-ai-post-order-info-637').replace('margin-top:12px;', 'margin-top:10px;');\n                tiPostTableInfoHtml = '';\n            }\n            if (tiAiInfo637 && !\/verific[a-z\u00e0\u00e8\u00e9\u00ec\u00f2\u00f9 ]{0,80}(prodott|serviz|tabella ordine)\/i.test(tiAiInfo637)) {\n                tiAiInfo637 += '<div class=\"ti-ai-order-final-check-637\">Verifica prodotti e servizi proposti nella tabella ordine prima di confermare.<\/div>';\n            }\n            if (tiAiInfo637) {\n                try { if (window.tiRegisterOrderAiExternalResponse) window.tiRegisterOrderAiExternalResponse(tiAiInfo637); } catch(_e) {}\n                h = '<span class=\"ti-ai-order-response-source\" data-ai-html=\"' + encodeURIComponent(tiAiInfo637) + '\" style=\"display:none!important;\"><\/span>';\n            } else {\n                h = '';\n            }\n        }\n        if (!t.includes('[TABLE]')) return h.split('\\n').join('<br>');\n        const d = dict[window.currLang] || dict['it'];\n        const rows = t.match(\/\\[P\\](.*?)\\[\\\/P\\]\/g) || [];\n        const allowedViews = window.getOrderViewAllowedModes ? window.getOrderViewAllowedModes() : ['table','small','medium','large'];\n        let preferredView = window.getOrderViewPreference ? window.getOrderViewPreference() : 'table';\n        if (!allowedViews.includes(preferredView)) preferredView = allowedViews[0] || 'table';\n        const layoutLabels = (window.currLang === 'en')\n            ? { table:'Table', small:'Small', medium:'Medium', large:'Large' }\n            : { table:'Tabella', small:'Small', medium:'Medium', large:'Large' };\n        const viewButtonsHtml = [\n            allowedViews.includes('table')  ? `<button type=\"button\" class=\"ti-order-view-btn ${preferredView === 'table' ? 'active' : ''}\" data-view=\"table\" onclick=\"window.setOrderView(this, 'table')\">${layoutLabels.table}<\/button>` : '',\n            allowedViews.includes('small')  ? `<button type=\"button\" class=\"ti-order-view-btn ${preferredView === 'small' ? 'active' : ''}\" data-view=\"small\" onclick=\"window.setOrderView(this, 'small')\">${layoutLabels.small}<\/button>` : '',\n            allowedViews.includes('medium') ? `<button type=\"button\" class=\"ti-order-view-btn ${preferredView === 'medium' ? 'active' : ''}\" data-view=\"medium\" onclick=\"window.setOrderView(this, 'medium')\">${layoutLabels.medium}<\/button>` : '',\n            allowedViews.includes('large')  ? `<button type=\"button\" class=\"ti-order-view-btn ${preferredView === 'large' ? 'active' : ''}\" data-view=\"large\" onclick=\"window.setOrderView(this, 'large')\">${layoutLabels.large}<\/button>` : ''\n        ].filter(Boolean).join('');\n        const showLayoutButtons = viewButtonsHtml !== '' && allowedViews.length > 1;\n        let items = rows.map((r, idx) => {\n            const p = r.replace('[P]','').replace('[\/P]','').split(' ## ');\n            const n = p[0] || '';\n            const priceStr = p[1] || '0';\n            let desc = p[4] || '';\n            const descEn = p[18] ? p[18].trim() : '';\n            if (String(window.currLang || 'it').toLowerCase() === 'en' && descEn) desc = descEn;\n            const ivaRate = p[5] || '22';\n            const tblName = p[6] || '';\n            const imgUrl = p[8] || '';\n            const istr = p[15] ? p[15].trim() : '';\n            const istrEn = p[17] ? p[17].trim() : '';\n            let linkedFileValues = String(imgUrl || '').split(',').map(v => v.trim()).filter(v => v);\n            if (window.tiDedupeOrderFileValues483) linkedFileValues = window.tiDedupeOrderFileValues483(linkedFileValues);\n            let firstImg = linkedFileValues[0] || '';\n            const linkedFiles = linkedFileValues.map(function(fileVal){\n                const url = window.getFullImgUrl(fileVal, gE('ti-ditta').value, tblName, n || tblName);\n                return {name: window.getFileNameFromUrlOrValue(fileVal), raw: fileVal, url: url};\n            }).filter(function(f){ return f && f.url; });\n            let fullImgUrl = firstImg ? window.getFullImgUrl(firstImg, gE('ti-ditta').value, tblName, n || tblName) : '';\n            let pFinal = 0, pOrig = 0, isMod = false;\n            if (priceStr.includes('|||')) {\n                let pts = priceStr.split('|||');\n                pFinal = parseFloat(pts[0]) || 0;\n                pOrig = parseFloat(pts[1]) || 0;\n                isMod = pFinal !== pOrig;\n            } else {\n                pFinal = parseFloat(priceStr) || 0;\n                pOrig = pFinal;\n            }\n            const instrTexts = window.getOrderInstructionTextsForDisplay ? window.getOrderInstructionTextsForDisplay(istr) : {raw:String(istr||''), commercial:String(istr||''), display:String(istr||''), popup:String(istr||''), summary:String(istr||'')};\n            if (String(window.currLang || 'it').toLowerCase() === 'en' && istrEn) {\n                instrTexts.display = istrEn;\n                instrTexts.popup = istrEn;\n                instrTexts.summary = istrEn;\n            }\n            const cleanIstr = instrTexts.display ? String(instrTexts.display).replace(\/\"\/g, '&quot;') : '';\n            const commercialIstr = instrTexts.commercial ? String(instrTexts.commercial).replace(\/\"\/g, '&quot;') : '';\n            const popupIstr = instrTexts.popup ? String(instrTexts.popup).replace(\/\"\/g, '&quot;') : '';\n            const localAdj = window.extractInstructionPriceAdjustments(commercialIstr, pFinal);\n            const hasPriceRule = !!(commercialIstr && localAdj && localAdj.isModified && \/(sconto|rincaro|aumento|maggiorazione|prezzo\\s*(applicato|finale|vendita)?)\/i.test(commercialIstr));\n            \/\/ 30.9.664: se il PHP ha gia inviato prezzo finale|||prezzo originale, non riapplicare lo stesso sconto lato JS.\n            if (hasPriceRule && !isMod) {\n                const lp = parseFloat(localAdj.listPrice);\n                const fp = parseFloat(localAdj.finalPrice);\n                pOrig = isNaN(lp) ? pFinal : lp;\n                pFinal = isNaN(fp) ? pFinal : fp;\n                isMod = Math.abs(pFinal - pOrig) > 0.009;\n            }\n            const encodedIstr = encodeURIComponent(`DATI BASE:\nPrezzo Base: \u20ac${window.fmtNum(pOrig)}\n\nISTRUZIONI E VARIAZIONI:\n${popupIstr || cleanIstr}`);\n            const isImgFile = firstImg && (\/\\.(jpg|jpeg|png|webp|gif)(?:$|[?#])\/i.test(firstImg) || window.isImageFileName(window.getFileNameFromUrlOrValue(firstImg)));\n            const orderSource = String(tblName || '').toLowerCase();\n            const vocePrefix = orderSource.includes('serviz') ? 'S' : 'P';\n            const voceBaseName = String(n || '').replace(\/^\\s*[PS]\\s*-\\s*\/i, '').trim();\n            const voceName = (vocePrefix + ' - ' + (voceBaseName || n || 'Voce')).trim();\n            const orderId = 'ord-' + idx;\n            let qtyDefault = (localAdj && localAdj.defaultQty != null) ? localAdj.defaultQty : 0;\n            const istrLower = String(commercialIstr || '').toLowerCase();\n            let mQty = istrLower.match(\/(?:q\\.t\u00e0|qt\u00e0|qta|quantit\u00e0|quantita|minimo|minimum)\\s*[:=]?\\s*(\\d+[\\.,]?\\d*)\/i);\n            if (mQty) qtyDefault = parseFloat(String(mQty[1]).replace(',', '.')) || qtyDefault;\n            const priceHtml = isMod\n                ? `<span style=\"display:block;font-size:14px;font-weight:800;color:#fff;\">\u20ac ${window.fmtNum(pFinal)}<\/span><span style=\"display:block;font-size:11px;color:#ef4444;text-decoration:line-through;\">\u20ac ${window.fmtNum(pOrig)}<\/span>`\n                : `<span style=\"display:block;font-size:13px;color:#fff;font-weight:700;\">\u20ac ${window.fmtNum(pFinal)}<\/span>`;\n            return {\n                orderId, n, voceName, vocePrefix, desc, tblName, firstImg, fullImgUrl, linkedFiles, cleanIstr, encodedIstr, isImgFile, pFinal, pOrig, isMod, hasPriceRule, priceHtml, qtyDefault, ivaRate,\n                popupJson: encodeURIComponent(JSON.stringify({orderId: orderId, name: voceName, rawName: n, desc: (desc || n), tblName: tblName, img: fullImgUrl, files: linkedFiles, note: (popupIstr || cleanIstr), price: pFinal, priceHtml: priceHtml, qty: qtyDefault || 0, ivaRate: ivaRate})),\n                nameFilter: window.escapeHtml(voceName + ' ' + n),\n                descFilter: window.escapeHtml(desc || ''),\n                priceFilter: String(pFinal)\n            };\n        }).filter(item => item && String(item.voceName || item.n || '').trim() !== '');\n\n        const tableRows = items.map(item => {\n            const fbUrl = window.getCompanyFallbackMediaUrl(gE('ti-ditta').value);\n            const popupTriggerAttrs = `data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderItemPopupFromEl(this)\" title=\"Apri dettaglio voce\"`;\n            const fileTriggerAttrs = `data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderLinkedFilesFromEl(this)\" title=\"Apri file collegati\"`;\n            const warnIcon = item.hasPriceRule ? `<span class=\"ti-order-warn ti-order-popup-trigger\" ${popupTriggerAttrs} data-istr=\"${window.escapeHtml(item.encodedIstr)}\" style='color:#22c55e;font-weight:900;cursor:pointer;font-size:16px;margin-right:5px;'>\u2757<\/span>` : '';\n            const fileIcon = `<span class=\"ti-order-file-icon ti-order-popup-trigger\" ${fileTriggerAttrs} style=\"cursor:pointer;\">\ud83d\udcc4<\/span>`;\n            const fileCountBadge = item.linkedFiles && item.linkedFiles.length > 1 ? `<span class=\"ti-order-file-count ti-order-popup-trigger\" ${fileTriggerAttrs} style=\"display:inline-flex;align-items:center;justify-content:center;margin-left:3px;min-width:18px;min-height:18px;padding:0 5px;border-radius:999px;background:#2563eb;color:#fff;font-size:inherit;line-height:inherit;font-weight:800;vertical-align:middle;\">${item.linkedFiles.length}<\/span>` : '';\n            const imgHtml = item.fullImgUrl && item.isImgFile\n                ? `${warnIcon}<img decoding=\"async\" src=\"${item.fullImgUrl}\" data-fallback=\"${window.escapeHtml(fbUrl)}\" class=\"ti-row-img ti-order-popup-trigger\" data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderLinkedFilesFromEl(this)\" title=\"Apri file collegati\" onerror=\"window.applyFallbackImage(this)\" data-no-lazy=\"1\">${fileCountBadge}`\n                : (item.hasPriceRule ? warnIcon + fileIcon + fileCountBadge : fileIcon + fileCountBadge);\n            return `<tr data-order-id=\"${item.orderId}\" data-name=\"${item.nameFilter}\" data-desc=\"${item.descFilter}\" data-price=\"${item.priceFilter}\" data-iva=\"${window.escapeHtml(String(item.ivaRate || '22'))}\">\n                <td class=\"ti-col-img\">${imgHtml}<\/td>\n                <td class=\"ti-col-art\"><div style=\"display:flex;align-items:center;\"><span class=\"ti-item-name\" style=\"font-size:11px;font-weight:bold;\">${window.escapeHtml(item.voceName || item.n)}<\/span><\/div><\/td>\n                <td style=\"font-size:10px;color:#ccc;\">${window.escapeHtml(item.desc)}<\/td>\n                <td><\/td>\n                <td class=\"ti-col-eur ti-order-popup-trigger\" data-price=\"${item.pFinal}\" data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderItemPopupFromEl(this)\" title=\"Apri dettaglio voce\" style=\"cursor:pointer;\">${item.priceHtml}<\/td>\n                <td class=\"ti-col-qta\"><span class=\"ti-qty-stepper\"><button type=\"button\" class=\"ti-qty-btn\" onclick=\"window.stepQtyInput(this,-1)\">-<\/button><input type=\"text\" inputmode=\"decimal\" class=\"ti-inp ti-q ti-num-step\" data-order-id=\"${item.orderId}\" style=\"width:100%;min-width:64px;text-align:right;padding:2px;font-size:11px;\" value=\"${window.fmtNum(item.qtyDefault || 0)}\" placeholder=\"0,00\" aria-label=\"Quantit\u00e0\" onfocus=\"if(this.value==='0,00') this.value='';\" onblur=\"if(this.value==='') this.value='0,00'; else this.value=window.formatQtyInput(this.value); window.syncOrderQty(this);\" oninput=\"window.syncOrderQty(this)\"><button type=\"button\" class=\"ti-qty-btn\" onclick=\"window.stepQtyInput(this,1)\">+<\/button><\/span><\/td>\n                <td class=\"ti-col-tot p-tot\" data-order-id=\"${item.orderId}\" data-price=\"${item.pFinal}\">0,00<\/td>\n            <\/tr>`;\n        }).join('');\n\n        const cardsHtml = items.map(item => {\n            const fbUrl = window.getCompanyFallbackMediaUrl(gE('ti-ditta').value);\n            const cardFileBadge = item.linkedFiles && item.linkedFiles.length > 1 ? `<span class=\"ti-order-card-file-count ti-order-popup-trigger\" data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderLinkedFilesFromEl(this)\" title=\"${item.linkedFiles.length} file collegati\" style=\"position:absolute;right:7px;top:7px;min-width:22px;min-height:22px;padding:0 5px;border-radius:999px;background:#2563eb;color:#fff;font-size:inherit;line-height:inherit;font-weight:800;display:flex;align-items:center;justify-content:center;cursor:pointer;\">${item.linkedFiles.length}<\/span>` : '';\n            const mediaHtml = item.fullImgUrl && item.isImgFile\n                ? `<div style=\"position:relative;width:100%;height:100%;\"><img decoding=\"async\" src=\"${item.fullImgUrl}\" alt=\"${window.escapeHtml(item.voceName || item.n)}\" class=\"ti-order-popup-trigger\" data-fallback=\"${window.escapeHtml(fbUrl)}\" data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderLinkedFilesFromEl(this)\" title=\"Apri file collegati\" onerror=\"window.applyFallbackImage(this)\">${cardFileBadge}<\/div>`\n                : `<div style=\"position:relative;width:100%;height:100%;\"><div class=\"ti-order-file-icon ti-order-popup-trigger\" data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderLinkedFilesFromEl(this)\" title=\"Apri file collegati\" style=\"cursor:pointer;\">\ud83d\udcc4<\/div>${cardFileBadge}<\/div>`;\n            const warnBtn = item.hasPriceRule ? `<button type=\"button\" class=\"ti-btn ti-order-warn ti-order-popup-trigger\" data-popup=\"${window.escapeHtml(item.popupJson)}\" data-istr=\"${window.escapeHtml(item.encodedIstr)}\" onclick=\"window.openOrderItemPopupFromEl(this)\" title=\"Apri dettaglio voce\" style=\"padding:4px 8px;font-size:10px;background:#14532d;cursor:pointer;\">\u2757 Prezzo<\/button>` : '';\n            return `<div class=\"ti-order-card\" data-order-id=\"${item.orderId}\" data-name=\"${item.nameFilter}\" data-desc=\"${item.descFilter}\" data-price=\"${item.priceFilter}\" data-iva=\"${window.escapeHtml(String(item.ivaRate || '22'))}\">\n                <div class=\"ti-order-card-media\">${mediaHtml}<\/div>\n                <div class=\"ti-order-card-body\">\n                    <div class=\"ti-order-card-title\">${window.escapeHtml(item.voceName || item.n || 'Voce')}<\/div>\n                    <div class=\"ti-order-card-desc\">${window.escapeHtml(item.desc || item.n || 'Nessuna descrizione disponibile')}<\/div>\n                    <div>${warnBtn}<\/div>\n                    <div class=\"ti-order-card-line\">Q.t\u00e0: <span class=\"ti-qty-stepper\"><button type=\"button\" class=\"ti-qty-btn\" onclick=\"window.stepQtyInput(this,-1)\">-<\/button><input type=\"text\" inputmode=\"decimal\" class=\"ti-q\" data-order-id=\"${item.orderId}\" value=\"${window.fmtNum(item.qtyDefault || 0)}\" placeholder=\"0,00\" aria-label=\"Quantit\u00e0\" onfocus=\"if(this.value==='0,00') this.value='';\" onblur=\"if(this.value==='') this.value='0,00'; else this.value=window.formatQtyInput(this.value); window.syncOrderQty(this);\" oninput=\"window.syncOrderQty(this)\"><button type=\"button\" class=\"ti-qty-btn\" onclick=\"window.stepQtyInput(this,1)\">+<\/button><\/span><\/div>\n                    <div class=\"ti-order-card-line\">Prezzo: <span class=\"ti-order-inline-price ti-order-popup-trigger\" data-popup=\"${window.escapeHtml(item.popupJson)}\" onclick=\"window.openOrderItemPopupFromEl(this)\" title=\"Apri dettaglio voce\" style=\"cursor:pointer;display:inline-block;\">${item.priceHtml}<\/span> <span class=\"ti-order-inline-total-wrap\">Totale: <span class=\"ti-order-card-total p-tot\" data-order-id=\"${item.orderId}\" data-price=\"${item.pFinal}\">0,00<\/span><\/span><\/div>\n                <\/div>\n            <\/div>`;\n        }).join('');\n\n        h += `<div class=\"ti-order-wrap ti-view-${preferredView}\">\n            <div class=\"ti-order-toolbar\">\n                <div class=\"ti-order-toolbar-left\">${showLayoutButtons ? viewButtonsHtml : ''}<\/div>\n                <div class=\"ti-order-toolbar-right\"><span class=\"ti-order-hint\">Descrizione \/ quantit\u00e0 \/ prezzo \/ totale<\/span><\/div>\n            <\/div>\n            <div class=\"ti-order-filterbar\">\n                <div class=\"ti-order-filtercell\"><span class=\"ti-order-filterlabel\">Voce<\/span><input type=\"text\" class=\"ti-filter-input\" oninput=\"window.filterOrderWrap(this,'name')\" placeholder=\"Filtra voce\"><\/div>\n                <div class=\"ti-order-filtercell\"><span class=\"ti-order-filterlabel\">Descrizione<\/span><input type=\"text\" class=\"ti-filter-input\" oninput=\"window.filterOrderWrap(this,'desc')\" placeholder=\"Filtra descrizione\"><\/div>\n                <div class=\"ti-order-filtercell\"><span class=\"ti-order-filterlabel\">Prezzo<\/span><input type=\"text\" class=\"ti-filter-input\" oninput=\"window.filterOrderWrap(this,'price')\" placeholder=\"> 10 \/ < 20 \/ 15\"><\/div>\n                <div class=\"ti-order-filtercell\"><span class=\"ti-order-filterlabel\">Ordina<\/span><select class=\"ti-in\" onchange=\"window.sortOrderWrap(this)\"><option value=\"name_asc\">Voce A-Z<\/option><option value=\"name_desc\">Voce Z-A<\/option><option value=\"desc_asc\">Descrizione A-Z<\/option><option value=\"desc_desc\">Descrizione Z-A<\/option><option value=\"price_asc\">Prezzo crescente<\/option><option value=\"price_desc\">Prezzo decrescente<\/option><\/select><\/div>\n            <\/div>\n            <div class=\"ti-order-table-shell\"><div class=\"ti-order-top-scroll ti-sync-hscroll-top\"><div class=\"ti-cfg-top-scroll-inner ti-sync-hscroll-inner\"><\/div><\/div><div class=\"ti-order-table-view ti-table-wrap ti-sync-hscroll-wrap\"><table class=\"ti-prod-table\"><thead><tr><th class=\"ti-col-img\">Doc<\/th><th onclick=\"window.sortTI(this,1)\">Voce<\/th><th style=\"text-align:left;\" onclick=\"window.sortTI(this,2)\">Descrizione<\/th><th class=\"ti-col-disp\">Disp<\/th><th class=\"ti-col-eur\" onclick=\"window.sortTI(this,4)\">Prezzo<\/th><th class=\"ti-col-qta\">Qt\u00e0<\/th><th class=\"ti-col-tot\">Tot<\/th><\/tr><\/thead><tbody>${tableRows}<\/tbody><\/table><\/div><div class=\"ti-order-bottom-scroll ti-sync-hscroll-bottom\"><div class=\"ti-cfg-top-scroll-inner ti-sync-hscroll-inner\"><\/div><\/div><\/div>\n            <div class=\"ti-order-grid-view\">${cardsHtml}<\/div>\n        <\/div>`;\n        if (!tiHasOrderTable631 && tiPostTableInfoHtml) {\n            h += tiPostTableInfoHtml;\n        }\n        return h;\n    };\n\n    window.tiPendingOrderAiResponseHtml637 = window.tiPendingOrderAiResponseHtml637 || '';\n    window.tiGetAiResponseCompanyTitle638 = function() {\n        let ditta = '';\n        try {\n            ditta = String(window.currentDittaName || '').trim();\n            if (!ditta || \/^ditta$\/i.test(ditta)) {\n                const cfg = (window.currentDbData && window.currentDbData.Tabelle && Array.isArray(window.currentDbData.Tabelle.Config)) ? (window.currentDbData.Tabelle.Config[0] || {}) : {};\n                ditta = String(cfg.Ditta || cfg['Ragione sociale'] || cfg['Ragione Sociale'] || cfg.Nome || cfg.Attivita || cfg['Attivit\u00e0'] || '').trim();\n            }\n            if (!ditta) {\n                const sel = document.getElementById('ti-ditta');\n                if (sel) {\n                    const opt = sel.options && sel.selectedIndex >= 0 ? sel.options[sel.selectedIndex] : null;\n                    ditta = String((opt && (opt.dataset.name || opt.textContent || opt.value)) || sel.value || '').trim();\n                }\n            }\n        } catch(e) { ditta = ''; }\n        ditta = String(ditta || 'Ditta').replace(\/\\.json$\/i, '').trim() || 'Ditta';\n        return 'AI ' + ditta;\n    };\n    window.tiOrderAiResponseTextFromHtml637 = function(html) {\n        const tmp = document.createElement('div');\n        tmp.innerHTML = String(html || '');\n        return String(tmp.innerText || tmp.textContent || '').replace(\/\\n{3,}\/g, '\\n\\n').trim();\n    };\n    window.ensureExternalAiResponseHost637 = function() {\n        const inputWrap = document.querySelector('#ti-ai-outer .ti-input-wrap');\n        if (!inputWrap || !inputWrap.parentNode) return null;\n        let host = gE('ti-ai-response-host');\n        if (!host) {\n            host = document.createElement('div');\n            host.id = 'ti-ai-response-host';\n            host.className = 'ti-ai-response-host';\n        }\n        let orderHost = gE('ti-order-confirm-host');\n        if (!orderHost && window.ensureExternalOrderConfirmHost) orderHost = window.ensureExternalOrderConfirmHost();\n        if (orderHost && orderHost.parentNode === inputWrap.parentNode) {\n            if (host.parentNode !== inputWrap.parentNode || host.previousElementSibling !== orderHost) {\n                inputWrap.parentNode.insertBefore(host, orderHost.nextSibling);\n            }\n        } else if (host.parentNode !== inputWrap.parentNode || host.nextElementSibling !== inputWrap) {\n            inputWrap.parentNode.insertBefore(host, inputWrap);\n        }\n        return host;\n    };\n    window.tiRenderExternalAiResponse637 = function(html) {\n        html = String(html || '').trim();\n        const host = window.ensureExternalAiResponseHost637 ? window.ensureExternalAiResponseHost637() : null;\n        if (!host) return;\n        if (!html) { host.style.display = 'none'; host.innerHTML = ''; host.removeAttribute('data-copy-text'); return; }\n        const copyText = window.tiOrderAiResponseTextFromHtml637 ? window.tiOrderAiResponseTextFromHtml637(html) : html.replace(\/<[^>]+>\/g, ' ');\n        host.setAttribute('data-copy-text', encodeURIComponent(copyText));\n        const titleText638 = (window.tiGetAiResponseCompanyTitle638 ? window.tiGetAiResponseCompanyTitle638() : 'AI Ditta');\n        const titleHtml638 = (window.escapeHtml ? window.escapeHtml(titleText638) : String(titleText638).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }));\n        host.innerHTML = '<div class=\"ti-ai-response-head\"><div class=\"ti-ai-response-title\">' + titleHtml638 + '<\/div><\/div><div class=\"ti-ai-response-body\">' + html + '<\/div><div class=\"ti-ai-response-actions\"><button type=\"button\" class=\"ti-btn ti-ai-response-copy\" onclick=\"window.tiCopyExternalAiResponse637(this)\">Copia testo<\/button><\/div>';\n        host.style.display = 'block';\n        try { if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(host); } catch(e) {}\n        try { if (window.tiI18nTranslateSubtree) window.tiI18nTranslateSubtree(host); } catch(e) {}\n    };\n    window.tiRegisterOrderAiExternalResponse = function(html) {\n        window.tiPendingOrderAiResponseHtml637 = String(html || '').trim();\n        if (window.tiPendingOrderAiResponseHtml637) {\n            setTimeout(function(){ try { window.tiRenderExternalAiResponse637(window.tiPendingOrderAiResponseHtml637); } catch(e) {} }, 0);\n        }\n        return '';\n    };\n    window.tiExtractOrderAiResponseFromBubble637 = function(bubble) {\n        if (!bubble) return;\n        try {\n            const nodes = bubble.querySelectorAll ? bubble.querySelectorAll('.ti-ai-order-response-source[data-ai-html]') : [];\n            if (!nodes || !nodes.length) return;\n            let html = '';\n            nodes.forEach(function(n){\n                const enc = n.getAttribute('data-ai-html') || '';\n                if (enc) { try { html = decodeURIComponent(enc); } catch(_e) { html = enc; } }\n            });\n            if (html) window.tiRegisterOrderAiExternalResponse(html);\n        } catch(e) {}\n    };\n    window.tiCopyExternalAiResponse637 = function(btn) {\n        const host = gE('ti-ai-response-host');\n        let text = '';\n        try { text = decodeURIComponent((host && host.getAttribute('data-copy-text')) || ''); } catch(e) { text = ''; }\n        if (!text && host) {\n            const body = host.querySelector('.ti-ai-response-body');\n            text = String(body ? (body.innerText || body.textContent || '') : (host.innerText || host.textContent || '')).trim();\n        }\n        const done = function(ok){\n            if (!btn) return;\n            const old = btn.textContent;\n            btn.textContent = ok ? 'Copiato' : 'Copia non riuscita';\n            setTimeout(function(){ btn.textContent = old || 'Copia testo'; }, 1400);\n        };\n        if (!text) { done(false); return false; }\n        if (navigator.clipboard && navigator.clipboard.writeText) {\n            navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){\n                try { const ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.focus(); ta.select(); const ok=document.execCommand('copy'); document.body.removeChild(ta); done(!!ok); } catch(e) { done(false); }\n            });\n        } else {\n            try { const ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.focus(); ta.select(); const ok=document.execCommand('copy'); document.body.removeChild(ta); done(!!ok); } catch(e) { done(false); }\n        }\n        return false;\n    };\n    window.tiHideExternalAiResponse637 = function() {\n        const host = gE('ti-ai-response-host');\n        if (host) { host.style.display = 'none'; host.innerHTML = ''; host.removeAttribute('data-copy-text'); }\n        window.tiPendingOrderAiResponseHtml637 = '';\n    };\n\n    window.pendingOrderState = null;\n    window.confirmedOrderTokens = window.confirmedOrderTokens || {};\n\n    window.ensureExternalOrderConfirmHost = function() {\n        const inputWrap = document.querySelector('#ti-ai-outer .ti-input-wrap');\n        const chatBox = gE('ti-chat-box');\n        let host = gE('ti-order-confirm-host');\n        if (!inputWrap || !chatBox || !inputWrap.parentNode) return null;\n        if (!host) {\n            host = document.createElement('div');\n            host.id = 'ti-order-confirm-host';\n            host.className = 'ti-order-confirm-host';\n        }\n        const aiHost = gE('ti-ai-response-host');\n        const targetNode = (aiHost && aiHost.parentNode === inputWrap.parentNode) ? aiHost : inputWrap;\n        if (host.parentNode !== inputWrap.parentNode || host.nextElementSibling !== targetNode) {\n            inputWrap.parentNode.insertBefore(host, targetNode);\n        }\n        return host;\n    };\n\n    window.getLastVisibleOrderWrap = function() {\n        const chatBox = gE('ti-chat-box');\n        if (!chatBox) return null;\n        const wraps = Array.from(chatBox.querySelectorAll('.ti-order-wrap'));\n        let last = null;\n        wraps.forEach(function(w){\n            if (window.isOrderWrapVisibleInChat(w)) last = w;\n        });\n        return last;\n    };\n\n    window.isOrderWrapVisibleInChat = function(wrap) {\n        const chatBox = gE('ti-chat-box');\n        if (!wrap || !chatBox || !document.body.contains(wrap)) return false;\n        const wrapRect = wrap.getBoundingClientRect();\n        const chatRect = chatBox.getBoundingClientRect();\n        return wrapRect.bottom > chatRect.top + 20 && wrapRect.top < chatRect.bottom - 20;\n    };\n\n    window.ensureOrderWrapToken = function(wrap) {\n        if (!wrap) return '';\n        if (!wrap.dataset.orderWrapToken) wrap.dataset.orderWrapToken = 'ow_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);\n        return wrap.dataset.orderWrapToken;\n    };\n\n    window.buildOrderSummaryFromWrap = function(wrap) {\n        if (!wrap) return null;\n        const table = wrap.querySelector('table');\n        if (!table) return null;\n        let orderText = \"RIEPILOGO ORDINE:\\n\";\n        let totalGross = 0;\n        let totalVat = 0;\n        let hasItems = false;\n        table.querySelectorAll('tbody tr').forEach(tr => {\n            if (tr.style.display === 'none') return;\n            const qtyInput = tr.querySelector('.ti-q');\n            if (!qtyInput) return;\n            let qty = window.parseQtyInput(qtyInput.value);\n            if (qty > 0) {\n                let name = tr.querySelector('.ti-item-name') ? tr.querySelector('.ti-item-name').innerText.trim() : '';\n                let price = parseFloat(tr.getAttribute('data-price') || (tr.querySelector('.ti-col-eur') ? tr.querySelector('.ti-col-eur').getAttribute('data-price') : '0')) || 0;\n                let ivaRate = window.parseIvaRate(tr.getAttribute('data-iva') || 22, 22);\n                let rowGross = price * qty;\n                let rowVat = window.calcVatAmountFromGross(rowGross, ivaRate);\n                orderText += `- ${window.fmtNum(qty)}x ${name} (\u20ac ${window.fmtNum(rowGross)})\\n`;\n                totalGross += rowGross;\n                totalVat += rowVat;\n                hasItems = true;\n            }\n        });\n        const totalNet = totalGross - totalVat;\n        if (!hasItems) {\n            orderText += \"- Nessun articolo selezionato\\n\";\n        }\n        orderText += `\\nIMPONIBILE: \u20ac ${window.fmtNum(totalNet)}\\nIVA SCORPORATA: \u20ac ${window.fmtNum(totalVat)}\\nTOTALE LORDO: \u20ac ${window.fmtNum(totalGross)}`;\n        return { text: orderText, totalGross: totalGross, totalVat: totalVat, totalNet: totalNet, hasItems: hasItems };\n    };\n\n    window.hideOrderConfirmPanel = function() {\n        window.pendingOrderState = null;\n        const host = window.ensureExternalOrderConfirmHost();\n        if (host) { host.style.display = 'none'; host.innerHTML = ''; }\n    };\n\n    window.refreshOrderConfirmPanel = function() {\n        const host = window.ensureExternalOrderConfirmHost();\n        if (!host) return;\n        const visibleWraps = Array.from(document.querySelectorAll('#ti-chat-box .ti-order-wrap')).filter(function(w){\n            return window.isOrderWrapVisibleInChat(w);\n        });\n        const wrap = visibleWraps.length ? visibleWraps[visibleWraps.length - 1] : null;\n        if (!wrap) { host.style.display = 'none'; host.innerHTML = ''; window.pendingOrderState = null; return; }\n        const token = window.ensureOrderWrapToken(wrap);\n        window.pendingOrderState = { token: token };\n        const summary = window.buildOrderSummaryFromWrap(wrap);\n        if (!summary) { host.style.display = 'none'; host.innerHTML = ''; return; }\n        const alreadyConfirmed = !!window.confirmedOrderTokens[token];\n        host.style.display = 'block';\n        const stateNoticeHtml = window.getCompanyLimitedNoticeHtml ? window.getCompanyLimitedNoticeHtml() : '';\n        host.innerHTML = `<div class=\"ti-order-confirm-title\">${alreadyConfirmed ? 'Ordine confermato' : 'Conferma ordine in corso'}<\/div>\n            ${stateNoticeHtml}\n            <div class=\"ti-order-confirm-box\">${window.escapeHtml(summary.text)}<\/div>\n            <div class=\"ti-order-confirm-meta\">\n                <div>Imponibile: <b>\u20ac ${window.fmtNum(summary.totalNet)}<\/b><\/div>\n                <div>IVA: <b>\u20ac ${window.fmtNum(summary.totalVat)}<\/b><\/div>\n                <div>Totale lordo: <b>\u20ac ${window.fmtNum(summary.totalGross)}<\/b><\/div>\n            <\/div>\n            <div class=\"ti-order-confirm-actions\">\n                <button type=\"button\" class=\"ti-btn ti-btn-ok\" ${(summary.hasItems && !alreadyConfirmed) ? '' : 'disabled style=\"opacity:.55;cursor:not-allowed;\"'} onclick=\"window.confirmPendingOrder()\">${alreadyConfirmed ? 'Ordine confermato' : 'Conferma ordine'}<\/button>\n                <button type=\"button\" class=\"ti-btn btn-close\" onclick=\"window.hideOrderConfirmPanel()\">Chiudi conferma<\/button>\n            <\/div>`;\n    };\n\n    window.confirmPendingOrder = function() {\n        const st = window.pendingOrderState || null;\n        if (!st || !st.token) { window.hideOrderConfirmPanel(); return; }\n        if (window.confirmedOrderTokens[st.token]) { window.refreshOrderConfirmPanel(); return; }\n        const wrap = document.querySelector('.ti-order-wrap[data-order-wrap-token=\"' + st.token + '\"]');\n        if (!wrap || !document.body.contains(wrap)) { window.hideOrderConfirmPanel(); window.tiAlert('Ordine non pi\u00f9 disponibile.'); return; }\n        const summary = window.buildOrderSummaryFromWrap(wrap);\n        if (!summary || !summary.hasItems) { window.tiAlert('Seleziona almeno un articolo nella tabella ordine.'); return; }\n        window.confirmedOrderTokens[st.token] = true;\n        window.tiHiddenSendText = summary.text;\n        window.tiHiddenSendLabel = 'Conferma ordine';\n        if (gE('ti-msg')) gE('ti-msg').value = '';\n        window.refreshOrderConfirmPanel();\n        if (gE('ti-send')) gE('ti-send').click();\n    };\n\n    window.sendOrderInChat = function(btn) { \n        const wrap = btn.closest('.ti-order-wrap') || btn.closest('.ti-table-wrap');\n        if (!wrap) return;\n        const token = window.ensureOrderWrapToken(wrap);\n        const summary = window.buildOrderSummaryFromWrap(wrap);\n        if (!summary) { window.tiAlert(\"Nessun articolo selezionato.\"); return; }\n        window.pendingOrderState = { token: token };\n        window.refreshOrderConfirmPanel();\n        const host = window.ensureExternalOrderConfirmHost();\n        if (host && host.scrollIntoView) {\n            try { host.scrollIntoView({behavior:'smooth', block:'nearest'}); } catch(e) { host.scrollIntoView(true); }\n        }\n    };\n\n    window.bindOrderConfirmHostSync = function() {\n        const chatBox = gE('ti-chat-box');\n        if (!chatBox || chatBox.dataset.orderConfirmSyncBound === '1') return;\n        chatBox.dataset.orderConfirmSyncBound = '1';\n        chatBox.addEventListener('scroll', function(){\n            window.refreshOrderConfirmPanel();\n        }, { passive: true });\n    };\n    window.bindOrderConfirmHostSync();\n    document.addEventListener('DOMContentLoaded', function(){ window.bindOrderConfirmHostSync(); });\n\n    window.changePage = function(p) { const d = gE('ti-ditta'); if(d && d.value) { const fd = new FormData(); fd.append('ti_action','ti_ai_chat_start'); fd.append('text','lista'); fd.append('page_num',p); fd.append('db',d.value); fetch(window.tiUrl, {method:'POST',body:fd}).then(r=>r.json()).then(res=>{ const bx = gE('ti-chat-box'); if(bx) { bx.innerHTML += res.data.reply; bx.scrollTop=bx.scrollHeight; } }); } };\n    window.openCamModal = function() { const ov = gE('ti-cam-ov'); const video = gE('ti-video-feed'); if(ov && video && navigator.mediaDevices) { window.showPluginModal ? window.showPluginModal(ov) : (ov.style.display = 'flex'); navigator.mediaDevices.getUserMedia({ video: { facingMode: \"environment\" } }).then(s => { window.stream = s; video.srcObject = s; }).catch(e => { gE('ti-cam-in').click(); }); } else { gE('ti-cam-in').click(); } };\n    window.closeCamModal = function() { const ov = gE('ti-cam-ov'); if(ov) { window.closeModal ? window.closeModal(ov) : (ov.style.display = 'none'); } if(window.stream) { window.stream.getTracks().forEach(t=>t.stop()); window.stream = null; } };\n    window.snapPhoto = function() { const video = gE('ti-video-feed'); const canvas = gE('ti-canvas-snap'); if(video && canvas) { canvas.width = video.videoWidth; canvas.height = video.videoHeight; canvas.getContext('2d').drawImage(video, 0, 0); canvas.toBlob(blob => { const file = new File([blob], \"snap.jpg\", { type: \"image\/jpeg\" }); const dt = new DataTransfer(); dt.items.add(file); const camIn = gE('ti-cam-in'); if(camIn) { camIn.files = dt.files; camIn.dispatchEvent(new Event('change')); } window.closeCamModal(); }, 'image\/jpeg', 0.85); } };\n\n\n    window.tiSpeechPrimed = false;\n    window.primeSpeech = function() {\n        if (!('speechSynthesis' in window) || window.tiSpeechPrimed) return;\n        try {\n            const u = new SpeechSynthesisUtterance(' ');\n            u.volume = 0;\n            window.speechSynthesis.cancel();\n            window.speechSynthesis.speak(u);\n            window.tiSpeechPrimed = true;\n        } catch(e) {}\n    };\n    ['click','touchstart','keydown'].forEach(function(evt){\n        document.addEventListener(evt, function(){ window.primeSpeech(); }, { once:true, passive:true });\n    });\n\n    window.tiIsNoPopupVoiceRole = function() {\n        const role = String(window.tiRole || window.tiUserRole || window.currentUserRole || '').toLowerCase();\n        const user = String(window.tiUser || window.tiUsername || '').toLowerCase();\n        return user === 'ssglobaladmin' || \/amministratore|configuratore|responsabile\/.test(role);\n    };\n    window.tiHasVisiblePopupOrAlert = function() {\n        try {\n            return !!Array.from(document.querySelectorAll('.ti-ov, #ti-alert-ov, #ti-ai-loader-ov')).find(function(el){\n                if (!el) return false;\n                const cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n                const disp = cs ? cs.display : el.style.display;\n                const vis = cs ? cs.visibility : el.style.visibility;\n                return disp !== 'none' && vis !== 'hidden' && el.offsetParent !== null;\n            });\n        } catch(e) { return false; }\n    };\n    window.tiShouldSuppressPopupSpeech = function(text) {\n        if (!window.tiIsNoPopupVoiceRole || !window.tiIsNoPopupVoiceRole()) return false;\n        const raw = String(text || '');\n        const clean = raw.replace(\/<[^>]+>\/g, ' ').replace(\/\\s+\/g, ' ').trim().toLowerCase();\n        if (!clean) return false;\n        if (window.tiHasVisiblePopupOrAlert && window.tiHasVisiblePopupOrAlert()) return true;\n        return \/^(\u26a0|\u2757|attenzione|avviso|errore|conferma|operazione|seleziona|inserisci|nessun|solo|sessione|elaborazione|processo|salvataggio|login|accesso|richiesta)\/i.test(clean);\n    };\n\n    \/* 30.9.664 - La sintesi vocale non deve pronunciare punteggiatura o simboli; protegge anche gli apostrofi italiani, es. l'AI non diventa elle AI. *\/\n    window.tiPrepareSpeechPunctuation = function(text) {\n        let s = String(text || '');\n        if (!s.trim()) return s;\n\n        \/\/ Protegge l'articolo eliso prima della pulizia della punteggiatura.\n        \/\/ Senza questa normalizzazione \"l'AI\" diventava \"l AI\" e alcune voci leggevano \"elle AI\".\n        s = s\n            .replace(\/\\b([Ll])[\\u2019']\\s*(AI|A\\.?I\\.?|IA)\\b\/g, function(m, art, acr) { return (art === 'L' ? 'La ' : 'la ') + acr.replace(\/\\.\/g, ''); })\n            .replace(\/\\b([Ll])[\\u2019']\\s*([A-Za-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff])\/g, function(m, art, chr) { return (art === 'L' ? 'L' : 'l') + chr; });\n\n        \/\/ Non trasformare mai i segni in parole (niente \"punto\", \"virgola\", \"segno di spunta\").\n        \/\/ I segni di fine frase diventano a-capo per ottenere una pausa vocale senza pronunciare il carattere.\n        s = s\n            .replace(\/\\.\\.\\.\/g, '\\n')\n            .replace(\/[.!?\u00bf\u00a1]+\/g, '\\n')\n            .replace(\/[,;:]+\/g, ' ')\n            .replace(\/[()\\[\\]{}<>]\/g, ' ')\n            .replace(\/[\"\u201c\u201d\u2018\u2019'`\u00b4]\/g, ' ')\n            .replace(\/[\\\/\\\\|]\/g, ' ')\n            .replace(\/\\s+[\u2013\u2014-]\\s+\/g, ' ')\n            .replace(\/[\u2013\u2014]\/g, ' ');\n\n        \/\/ Rimuove caratteri grafici\/simboli che alcune voci leggono letteralmente,\n        \/\/ ad esempio \"segno di spunta\", \"croce\", \"triangolo di attenzione\".\n        s = s\n            .replace(\/[\u2713\u2714\u2611\u2705\u2717\u2718\u274c\u2b55\u26d4\u26a0\u2757\u2753\u2b50\u2605\u2606\u25c6\u25c7\u25a0\u25a1\u25b2\u25b3\u25bc\u25bd\u25cf\u25cb\u2022\u00b7]\/g, ' ')\n            .replace(\/[\\u2600-\\u27BF\\u2B00-\\u2BFF]\/g, ' ')\n            .replace(\/[\\uFE0E\\uFE0F\\u200D]\/g, ' ')\n            .replace(\/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]\/g, ' ')\n            .replace(\/[ \\t]+\/g, ' ')\n            .replace(\/\\n\\s+\/g, '\\n')\n            .replace(\/\\n{2,}\/g, '\\n')\n            .trim();\n\n        return s;\n    };\n\n\n    window.tiItalianNumberToWords498 = function(n){\n        n = Math.max(0, Math.floor(Number(n) || 0));\n        var ones = ['zero','uno','due','tre','quattro','cinque','sei','sette','otto','nove','dieci','undici','dodici','tredici','quattordici','quindici','sedici','diciassette','diciotto','diciannove'];\n        var tens = ['', '', 'venti','trenta','quaranta','cinquanta','sessanta','settanta','ottanta','novanta'];\n        function under100(x){\n            if (x < 20) return ones[x];\n            var t = Math.floor(x\/10), u = x % 10;\n            var w = tens[t];\n            if (u === 1 || u === 8) w = w.slice(0, -1);\n            return w + (u ? ones[u] : '');\n        }\n        function under1000(x){\n            if (x < 100) return under100(x);\n            var h = Math.floor(x\/100), r = x % 100;\n            var w = (h === 1 ? 'cento' : ones[h] + 'cento');\n            if (r >= 80 && r < 90) w = w.slice(0, -1);\n            return w + (r ? under100(r) : '');\n        }\n        function words(x){\n            if (x < 1000) return under1000(x);\n            if (x < 1000000) {\n                var k = Math.floor(x\/1000), r = x % 1000;\n                var wk = (k === 1 ? 'mille' : words(k) + 'mila');\n                return wk + (r ? under1000(r) : '');\n            }\n            if (x < 1000000000) {\n                var m = Math.floor(x\/1000000), r2 = x % 1000000;\n                var wm = (m === 1 ? 'un milione' : words(m) + ' milioni');\n                return wm + (r2 ? ' ' + words(r2) : '');\n            }\n            return String(x);\n        }\n        return words(n);\n    };\n\n    window.tiPrepareEuroSpeech498 = function(text){\n        var s = String(text || '');\n        function normalizeNum(num){\n            num = String(num || '').replace(\/\\s\/g, '');\n            var parts = num.split(',');\n            var intPart = parts[0].replace(\/\\.\/g, '');\n            var cents = parts.length > 1 ? (parts[1] || '').replace(\/\\D\/g, '').slice(0,2) : '';\n            var value = parseInt(intPart, 10);\n            if (!isFinite(value) || isNaN(value)) return '';\n            \/\/ Per importi con migliaia, la voce deve evidenziare le migliaia e non leggere il punto come decimale.\n            \/\/ Esempio richiesto: \u20ac 46.800,00 -> quarantaseimila euro.\n            if (value >= 1000 && \/\\.\/.test(num)) {\n                var thousandsOnly = Math.floor(value \/ 1000) * 1000;\n                return window.tiItalianNumberToWords498(thousandsOnly) + ' euro';\n            }\n            var out = window.tiItalianNumberToWords498(value) + (value === 1 ? ' euro' : ' euro');\n            var c = cents ? parseInt(cents.padEnd(2, '0'), 10) : 0;\n            if (c > 0) out += ' e ' + window.tiItalianNumberToWords498(c) + ' centesimi';\n            return out;\n        }\n        s = s.replace(\/\u20ac\\s*([0-9]{1,3}(?:\\.[0-9]{3})+(?:,[0-9]{1,2})?|[0-9]+(?:,[0-9]{1,2})?)\/g, function(m, n){ return normalizeNum(n) || m; });\n        s = s.replace(\/([0-9]{1,3}(?:\\.[0-9]{3})+(?:,[0-9]{1,2})?|[0-9]+(?:,[0-9]{1,2})?)\\s*\u20ac\/g, function(m, n){ return normalizeNum(n) || m; });\n        return s;\n    };\n\n    window.speakText = function(text) {\n        if (!('speechSynthesis' in window) || window.isMuted) return; \n        let raw = String(text || '');\n        if (window.tiShouldSuppressPopupSpeech && window.tiShouldSuppressPopupSpeech(raw)) return;\n        window.primeSpeech();\n        window.speechSynthesis.cancel();\n\n        raw = raw.replace(\/<!--NOAUDIO_START-->[\\s\\S]*?<!--NOAUDIO_END-->\/g, '');\n        raw = raw.replace(\/<div[^>]*class=[\"'][^\"']*ti-company-state-warning[^\"']*[\"'][^>]*>[\\s\\S]*?<\\\/div>\/gi, '');\n        raw = raw.replace(\/<div[^>]*>\\s*\u26a0\ufe0f\\s*Stato ditta:[\\s\\S]*?<\\\/div>\/gi, '');\n        raw = raw.replace(\/\u26a0\ufe0f\\s*Stato ditta:\\s*(Configurazione|Sospesa)[\\s\\S]*?(?=(Sono l'assistente|Come posso|$))\/gi, '');\n        raw = raw.replace(\/Attenzione\\.\\s*La ditta risulta (sospesa|in configurazione)\\.[\\s\\S]*?(?=(Sono l'assistente|Come posso|$))\/gi, '');\n        raw = raw.replace(\/<div[^>]*>\\s*<b>\\s*\u2139\ufe0f\\s*Report esecuzione istruzioni AI<\\\/b>[\\s\\S]*?<\\\/div>\/gi, '');\n        let clean = raw.replace(\/\\[TABLE\\][\\s\\S]*?\\[\\\/TABLE\\]\/g, '').replace(\/\\[P\\][\\s\\S]*?\\[\\\/P\\]\/g, '').replace(\/<\\s*br\\s*\\\/?\\s*>\/gi, '\\n').replace(\/<\\\/(p|div|li|tr|h[1-6])>\/gi, '\\n').replace(\/<[^>]+>\/g, ' ').trim();\n        clean = clean.replace(\/[*_#~`]\/g, ''); \n        clean = clean.replace(\/(\\d{1,2})\\\/(\\d{1,2})\\\/(\\d{4})\/g, \"$1 del $2 $3\");\n        clean = clean.replace(\/(\\d{1,2})\\\/(\\d{1,2})\/g, \"$1 del $2\");\n        clean = clean.replace(\/(\\d{1,2}):(\\d{2})\/g, \"$1 e $2 minuti\");\n        clean = clean.replace(\/tasto\\s+X\\b\/gi, \"tasto ixs\");\n        clean = clean.replace(\/\\bX\\b(?=\\s*per\\s+annullare)\/gi, \"ixs\");\n        if (window.tiPrepareEuroSpeech498) clean = window.tiPrepareEuroSpeech498(clean);\n        if (window.tiPrepareSpeechPunctuation) clean = window.tiPrepareSpeechPunctuation(clean);\n\n        if (clean) { \n            let u = new SpeechSynthesisUtterance(clean); \n            u.lang = window.currLang === 'en' ? 'en-US' : 'it-IT'; u.rate = 1.0; u.pitch = 1.2; \n            let voices = window.speechSynthesis.getVoices();\n            if(voices.length > 0) {\n                let femaleVoice = voices.find(v => v.lang.includes(u.lang) && (v.name.includes('Google') || v.name.includes('Elsa') || v.name.includes('Bianca') || v.name.includes('Female') || v.name.includes('Samantha') || v.name.includes('Victoria') || v.name.includes('Jenny') || v.name.includes('Zira') || v.name.includes('Hazel')));\n                if(femaleVoice) { u.voice = femaleVoice; } else { let langVoice = voices.find(v => v.lang.includes(u.lang)); if(langVoice) u.voice = langVoice; }\n            }\n            setTimeout(() => { window.speechSynthesis.speak(u); }, 150);\n        }\n    };\n    if ('speechSynthesis' in window) { window.speechSynthesis.onvoiceschanged = function() { window.speechSynthesis.getVoices(); }; }\n\n    window.extractCurrentUserEmailFromData = function(data) {\n        try {\n            if (!data || !data.Tabelle || !window.tiUser || window.tiUser === 'SSGlobalAdmin') return '';\n            const tables = ['Utenti', 'Operatori'];\n            for (const tbl of tables) {\n                const rows = Array.isArray(data.Tabelle[tbl]) ? data.Tabelle[tbl] : Object.values((data.Tabelle && data.Tabelle[tbl]) || {});\n                for (const row of rows) {\n                    if (!row || typeof row !== 'object') continue;\n                    const userKey = Object.keys(row).find(k => String(k).toLowerCase() === 'username' || String(k).toLowerCase() === 'user');\n                    if (!userKey) continue;\n                    if (String(row[userKey] || '').trim().toLowerCase() !== String(window.tiUser || '').trim().toLowerCase()) continue;\n                    const emailKey = Object.keys(row).find(k => String(k).toLowerCase() === 'email');\n                    const email = emailKey ? String(row[emailKey] || '').trim() : '';\n                    if (email) return email;\n                }\n            }\n        } catch(e) {}\n        return '';\n    };\n\n    window.prefillSupportFeedbackForm = function(forceRefresh) {\n        const emailInput = gE('ti-support-email');\n        const infoBox = gE('help-email');\n        if (!emailInput) return Promise.resolve('');\n        if (!forceRefresh && String(emailInput.value || '').trim() !== '') {\n            const keepVal = String(emailInput.value || '').trim();\n            if (infoBox) infoBox.innerHTML = keepVal ? '<div style=\"font-size:12px;color:#93c5fd;\">Email utente proposta: <b>' + window.escapeHtml(keepVal) + '<\/b><\/div>' : '';\n            return Promise.resolve(keepVal);\n        }\n        const localEmail = window.extractCurrentUserEmailFromData(window.currentDbData);\n        if (localEmail) {\n            emailInput.value = localEmail;\n            if (infoBox) infoBox.innerHTML = '<div style=\"font-size:12px;color:#93c5fd;\">Email utente proposta: <b>' + window.escapeHtml(localEmail) + '<\/b><\/div>';\n            return Promise.resolve(localEmail);\n        }\n        const dbSel = gE('ti-ditta');\n        if (!dbSel || !dbSel.value || dbSel.value === 'NEW_DB' || !window.tiUser || window.tiUser === 'SSGlobalAdmin') {\n            if (infoBox) infoBox.innerHTML = '';\n            return Promise.resolve('');\n        }\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_get_db_data');\n        fd.append('db', dbSel.value);\n        return window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n            const email = (res && res.success && res.data) ? window.extractCurrentUserEmailFromData(res.data) : '';\n            if (email) {\n                window.currentDbData = res.data;\n                emailInput.value = email;\n                if (infoBox) infoBox.innerHTML = '<div style=\"font-size:12px;color:#93c5fd;\">Email utente proposta: <b>' + window.escapeHtml(email) + '<\/b><\/div>';\n            } else if (infoBox) {\n                infoBox.innerHTML = '';\n            }\n            return email;\n        }).catch(function(){ if (infoBox) infoBox.innerHTML = ''; return ''; });\n    };\n\n    window.toggleSupportFeedbackBox = function(forceOpen) {\n        const box = gE('ti-help-support-box');\n        const toggleBtn = gE('ti-help-support-toggle');\n        if (!box) return;\n        const shouldOpen = (typeof forceOpen === 'boolean') ? forceOpen : (box.style.display === 'none' || box.style.display === '');\n        box.style.display = shouldOpen ? 'block' : 'none';\n        if (toggleBtn) toggleBtn.innerText = shouldOpen ? '\u2716\ufe0f Chiudi segnalazione assistenza' : '\ud83d\udee0\ufe0f Segnalazione assistenza tecnica';\n        if (shouldOpen) {\n            window.prefillSupportFeedbackForm(true).then(function(){ const sbj = gE('ti-support-subject'); if (sbj && !sbj.value) sbj.focus(); });\n        }\n    };\n\n    window.getSupportFeedbackChatHistory = function() {\n        const chat = gE('ti-chat-box');\n        if (!chat) return '';\n        const lines = [];\n        const bubbles = Array.from(chat.querySelectorAll('.ti-bubble')).slice(-30);\n        bubbles.forEach(function(b){\n            const clone = b.cloneNode(true);\n            clone.querySelectorAll('table, .ti-order-grid-view, .ti-order-toolbar, .ti-order-filterbar, .ti-cfg-top-scroll, .ti-cfg-bottom-scroll, .ti-order-top-scroll, .ti-order-bottom-scroll').forEach(function(n){ n.remove(); });\n            const txt = String(clone.innerText || '').trim();\n            if (!txt || txt.indexOf('Fammi pensare') !== -1) return;\n            lines.push((b.classList.contains('user') ? 'Utente' : 'Assistente') + ': ' + txt);\n        });\n        let out = lines.join('\\n\\n').trim();\n        if (out.length > 15000) out = out.slice(out.length - 15000);\n        return out;\n    };\n\n    window.sendSupportFeedback = function() {\n        const dbSel = gE('ti-ditta');\n        const email = (gE('ti-support-email') ? gE('ti-support-email').value : '').trim();\n        const subject = (gE('ti-support-subject') ? gE('ti-support-subject').value : '').trim();\n        const message = (gE('ti-support-message') ? gE('ti-support-message').value : '').trim();\n        const sendBtn = gE('ti-support-send');\n        if (!subject) { window.tiAlert('Oggetto obbligatorio.'); return; }\n        if (!message) { window.tiAlert('Inserisci il testo della segnalazione.'); return; }\n        if (email && !\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(email)) { window.tiAlert('Email mittente non valida.'); return; }\n        const fd = new FormData();\n        fd.append('ti_action', 'ti_ai_send_email');\n        fd.append('mode', 'support_feedback');\n        fd.append('db', dbSel && dbSel.value ? dbSel.value : 'NEW_DB');\n        fd.append('sender_email', email);\n        fd.append('feedback_subject', subject);\n        fd.append('feedback_message', message);\n        fd.append('chat_history', window.getSupportFeedbackChatHistory());\n        if (sendBtn) { sendBtn.disabled = true; sendBtn.innerText = 'Invio in corso...'; }\n        window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n            if (sendBtn) { sendBtn.disabled = false; sendBtn.innerText = 'Invia segnalazione'; }\n            if (res && res.success) {\n                if (gE('ti-support-subject')) gE('ti-support-subject').value = '';\n                if (gE('ti-support-message')) gE('ti-support-message').value = '';\n                window.toggleSupportFeedbackBox(false);\n                window.tiAlert((res.data && res.data.reply) ? res.data.reply : 'La segnalazione sar\u00e0 analizzata per eventualmente rispondere.<br><br>Grazie per la collaborazione.');\n            } else {\n                window.tiAlert((res && res.data && res.data.message) ? res.data.message : 'Errore invio segnalazione.');\n            }\n        }).catch(function(err){ if (sendBtn) { sendBtn.disabled = false; sendBtn.innerText = 'Invia segnalazione'; } window.tiAlert('Errore invio segnalazione: ' + ((err && err.message) ? err.message : 'errore sconosciuto')); });\n    };\n\n    window.openHelpModal = function() {\n        window.prefillSupportFeedbackForm(true);\n        window.openModal('ti-help-ov');\n    };\n\n    window.sendHelpChat = () => {\n        const msg = gE('help-bot-in').value; if(!msg) return;\n        const replyBox = gE('help-bot-reply');\n        replyBox.innerHTML = \"<i>Analizzo il manuale... \u23f3<\/i>\";\n        gE('help-bot-in').value = '';\n        \n        const fd = new FormData(); fd.append('ti_action','ti_ai_chat_start'); fd.append('db', gE('ti-ditta').value || 'NEW_DB');\n        fd.append('text', \"DOMANDA SUL MANUALE CONFIGURAZIONE: Rispondi solo con informazioni operative chiare ed esempi di compilazione basati su manuale, schema standard e dati disponibili. Non riportare frasi su campi vuoti, colonne senza nome o valori \\\"\\\". Domanda: \" + msg);\n        \n        fetch(window.tiUrl,{method:'POST',body:fd}).then(r=>r.json()).then(res=>{\n            if(res.success) { replyBox.innerHTML = res.data.reply.replace(\/\\n\/g, '<br>'); } \n            else { replyBox.innerHTML = \"<span style='color:#ef4444;'>Errore AI. Controlla la connessione.<\/span>\"; }\n        });\n    };\n\n    window.applyMuteState = function() {\n        const muteBtn = gE('ti-mute-btn'); if(!muteBtn) return;\n        const d = dict[window.currLang] || dict['it'];\n        if(window.muteState === 0) {\n            window.isMuted = false;\n            muteBtn.innerText = d.muteOn;\n            muteBtn.style.background = '#222';\n            muteBtn.style.borderColor = '#444';\n        } else if (window.muteState === 1) {\n            window.isMuted = true;\n            muteBtn.innerText = d.muteOff;\n            muteBtn.style.background = '#b91c1c';\n            muteBtn.style.borderColor = '#991b1b';\n        } else if (window.muteState === 2) {\n            window.isMuted = true;\n            muteBtn.innerText = d.muteOff;\n            muteBtn.style.background = '#b91c1c';\n            muteBtn.style.borderColor = '#991b1b';\n        }\n    };\n\n    window.tiTranslateHtmlLocallyToCurrentLanguage = function(html) {\n        html = String(html || '');\n        if (!html.trim() || String(window.currLang || 'it').toLowerCase() !== 'en') return html;\n        const box = document.createElement('div');\n        box.innerHTML = html;\n        try {\n            if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(box);\n            if (window.tiI18nTranslateSubtree) window.tiI18nTranslateSubtree(box);\n        } catch(e) {}\n        return box.innerHTML || html;\n    };\n    window.tiRemoteTranslateHtmlForCurrentLanguage = function(html, dbValue) {\n        \/\/ v30.9.664: non usare fetch di traduzione durante inserimento\/switch lingua.\n        \/\/ Il fetch poteva ritardare o bloccare IT\/EN; la traduzione immediata usa dizionario\/cache DOM locale.\n        return Promise.resolve(window.tiTranslateHtmlLocallyToCurrentLanguage ? window.tiTranslateHtmlLocallyToCurrentLanguage(html) : String(html || ''));\n    };\n    window.tiAddAiMessageTranslatedIfNeeded = function(html, voice, dbValue) {\n        const originalHtml = String(html || '');\n        const originalVoice = String(voice || originalHtml || '');\n        const bubble = window.addMsg ? window.addMsg('ai', originalHtml, originalHtml) : null;\n        let voiceSource = originalVoice;\n        if (String(window.currLang || 'it').toLowerCase() === 'en') {\n            voiceSource = bubble ? String(bubble.innerText || bubble.textContent || '') : (window.tiTranslateHtmlLocallyToCurrentLanguage ? window.tiTranslateHtmlLocallyToCurrentLanguage(originalVoice) : originalVoice);\n            voiceSource = String(voiceSource || '').replace(\/<[^>]+>\/g, ' ').replace(\/\\s+\/g, ' ').trim();\n            if (window.tiQueueRemoteEnglishChatTranslation && bubble) window.tiQueueRemoteEnglishChatTranslation(bubble, originalHtml, dbValue);\n        }\n        if (voiceSource && window.speakText) window.speakText(voiceSource);\n        return bubble;\n    };\n\n\n    window.tiChatTranslationCache = window.tiChatTranslationCache || Object.create(null);\n    window.tiChatTranslationPending = window.tiChatTranslationPending || Object.create(null);\n    window.tiHashString = function(str) {\n        str = String(str == null ? '' : str);\n        let h = 0;\n        for (let i = 0; i < str.length; i++) { h = ((h << 5) - h + str.charCodeAt(i)) | 0; }\n        return String(h >>> 0) + '_' + String(str.length);\n    };\n    window.tiRememberVisibleChatLanguage = function(lang) {\n        lang = String(lang || window.currLang || 'it').toLowerCase();\n        document.querySelectorAll('.ti-bubble.ai').forEach(function(b){\n            if (!b.__tiChatHtmlByLang) b.__tiChatHtmlByLang = {};\n            if (!b.__tiChatHtmlByLang[lang]) b.__tiChatHtmlByLang[lang] = b.innerHTML;\n        });\n    };\n    window.tiRemoteTranslateHtmlToLanguage = function(html, targetLang, dbValue, fallback) {\n        targetLang = String(targetLang || 'it').toLowerCase() === 'en' ? 'en' : 'it';\n        html = String(html || '');\n        const localFallback = (fallback !== undefined) ? fallback : html;\n        if (!html.trim()) return Promise.resolve(html);\n        const cacheKey = targetLang + ':' + window.tiHashString(html);\n        if (window.tiChatTranslationCache && Object.prototype.hasOwnProperty.call(window.tiChatTranslationCache, cacheKey)) {\n            return Promise.resolve(window.tiChatTranslationCache[cacheKey]);\n        }\n        if (window.tiChatTranslationPending && window.tiChatTranslationPending[cacheKey]) return window.tiChatTranslationPending[cacheKey];\n        try {\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_chat_start');\n            fd.append('db', dbValue || (gE('ti-ditta') ? gE('ti-ditta').value : '') || '');\n            fd.append('text', 'TRADUCI_ESATTAMENTE_QUESTO_HTML_IN_' + targetLang.toUpperCase() + ': ' + html);\n            if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd);\n            const pending = fetch(window.tiUrl, {method:'POST', body:fd})\n                .then(function(r){ return r.json(); })\n                .then(function(res){\n                    const out = (res && res.success && res.data && res.data.reply) ? String(res.data.reply || '') : localFallback;\n                    window.tiChatTranslationCache[cacheKey] = out;\n                    delete window.tiChatTranslationPending[cacheKey];\n                    return out;\n                })\n                .catch(function(){ delete window.tiChatTranslationPending[cacheKey]; return localFallback; });\n            window.tiChatTranslationPending[cacheKey] = pending;\n            return pending;\n        } catch(e) { return Promise.resolve(localFallback); }\n    };\n    window.tiQueueRemoteEnglishChatTranslation = function(bubble, sourceHtml, dbValue) {\n        if (!bubble || !sourceHtml || String(window.currLang || 'it').toLowerCase() !== 'en') return;\n        sourceHtml = String(sourceHtml || '');\n        if (!sourceHtml.trim() || sourceHtml.length > 24000) return;\n        if (!window.tiRemoteTranslateHtmlToLanguage) return;\n        const sourceKey = window.tiHashString ? window.tiHashString(sourceHtml) : String(sourceHtml.length);\n        if (bubble.__tiRemoteEnSourceKey === sourceKey) return;\n        bubble.__tiRemoteEnSourceKey = sourceKey;\n        const timeout = new Promise(function(resolve){ setTimeout(function(){ resolve(null); }, 6500); });\n        Promise.race([window.tiRemoteTranslateHtmlToLanguage(sourceHtml, 'en', dbValue || (gE('ti-ditta') ? gE('ti-ditta').value : ''), null), timeout]).then(function(outHtml){\n            if (!outHtml || String(window.currLang || 'it').toLowerCase() !== 'en') return;\n            const currentIt = bubble.getAttribute('data-ti-it-html') || (bubble.__tiChatHtmlByLang && bubble.__tiChatHtmlByLang.it) || '';\n            if (currentIt && currentIt !== sourceHtml) return;\n            bubble.innerHTML = String(outHtml || '');\n            bubble.__tiChatHtmlByLang = bubble.__tiChatHtmlByLang || {};\n            bubble.__tiChatHtmlByLang.en = bubble.innerHTML;\n            try { if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(bubble); } catch(e) {}\n        }).catch(function(){});\n    };\n    window.tiTranslateChatBubblesForLanguage = function(targetLang, force) {\n        targetLang = String(targetLang || window.currLang || 'it').toLowerCase() === 'en' ? 'en' : 'it';\n        const bubbles = Array.from(document.querySelectorAll('.ti-bubble.ai')).slice(-80);\n        bubbles.forEach(function(bubble){\n            if (!bubble) return;\n            if (!bubble.__tiChatHtmlByLang) bubble.__tiChatHtmlByLang = {};\n            const rawSource = bubble.getAttribute('data-raw-source') || '';\n            const storedIt = bubble.getAttribute('data-ti-it-html') || bubble.__tiChatHtmlByLang.it || rawSource || bubble.innerHTML;\n            if (targetLang === 'it') {\n                \/\/ v30.9.664: ritorno a IT sempre immediato e locale, senza chiamate AI\/fetch.\n                bubble.innerHTML = storedIt;\n                bubble.__tiChatHtmlByLang.it = storedIt;\n                bubble.setAttribute('data-ti-it-html', storedIt);\n                try { if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(bubble); } catch(e) {}\n                return;\n            }\n            if (!force && bubble.__tiChatHtmlByLang.en) {\n                bubble.innerHTML = bubble.__tiChatHtmlByLang.en;\n                try { if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(bubble); } catch(e) {}\n                return;\n            }\n            if (rawSource && \/\\[TABLE\\]|\\[P\\]\/.test(rawSource) && window.formatTable) {\n                bubble.innerHTML = window.formatTable(rawSource);\n                try {\n                    if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(bubble);\n                    if (window.tiI18nTranslateSubtree) window.tiI18nTranslateSubtree(bubble);\n                    if (window.tiExtractOrderAiResponseFromBubble637) window.tiExtractOrderAiResponseFromBubble637(bubble);\n                } catch(e) {}\n                bubble.__tiChatHtmlByLang.en = bubble.innerHTML;\n                return;\n            }\n            bubble.innerHTML = storedIt;\n            try {\n                if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(bubble);\n                if (window.tiI18nTranslateSubtree) window.tiI18nTranslateSubtree(bubble);\n            } catch(e) {}\n            bubble.__tiChatHtmlByLang.en = bubble.innerHTML;\n            if (window.tiQueueRemoteEnglishChatTranslation) window.tiQueueRemoteEnglishChatTranslation(bubble, storedIt, (gE('ti-ditta') ? gE('ti-ditta').value : ''));\n        });\n    };\n    window.forceApplyCurrentLanguageEverywhere = function(reason) {\n        try {\n            if (!['it','en'].includes(String(window.currLang || '').toLowerCase())) window.currLang = 'it';\n            if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(document.body || document.documentElement);\n            if (window.applyLayoutLanguage) window.applyLayoutLanguage(true);\n            if (window.tiTranslateChatBubblesForLanguage) window.tiTranslateChatBubblesForLanguage(window.currLang, true);\n            [60, 180, 420, 900].forEach(function(ms){\n                setTimeout(function(){\n                    try {\n                        if (window.applyLayoutLanguage) window.applyLayoutLanguage(true);\n                        if (window.tiTranslateChatBubblesForLanguage) window.tiTranslateChatBubblesForLanguage(window.currLang, true);\n                    } catch(e) {}\n                }, ms);\n            });\n        } catch(e) { try { console.warn('Shop & Service forced language apply error', e); } catch(ignore) {} }\n    };\n    window.setShopServiceLanguage = function(lang) {\n        const dittaSel = gE('ti-ditta');\n        const selectedDbBefore = dittaSel && dittaSel.value ? dittaSel.value : (window.tiCurrentSelectedDb || localStorage.getItem('ti_saved_db') || '');\n        const previousLang = String(window.currLang || 'it').toLowerCase() === 'en' ? 'en' : 'it';\n        if (window.tiRememberVisibleChatLanguage) window.tiRememberVisibleChatLanguage(previousLang);\n        window.currLang = String(lang || 'it').toLowerCase() === 'en' ? 'en' : 'it';\n        if (window.tiI18nClearCache) window.tiI18nClearCache();\n        localStorage.setItem('ti_lang_' + window.tiUser, window.currLang);\n        if (selectedDbBefore) window.tiCurrentSelectedDb = selectedDbBefore;\n        try { document.querySelectorAll('.ti-lang-opt').forEach(function(e){ e.classList.toggle('active', String(e.dataset.lang || '') === window.currLang); }); } catch(e) {}\n        try {\n            if (dittaSel && selectedDbBefore && Array.from(dittaSel.options || []).some(function(o){ return o.value === selectedDbBefore; })) {\n                dittaSel.value = selectedDbBefore;\n                if (selectedDbBefore !== 'NEW_DB') localStorage.setItem('ti_saved_db', selectedDbBefore);\n            }\n            if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n            if (window.syncDittaTrigger) window.syncDittaTrigger();\n            if (window.applyCompanyLimitedNotice) window.applyCompanyLimitedNotice();\n        } catch(e) {}\n        try {\n            const d = dict[window.currLang] || dict['it'];\n            if(gE('ti-msg')) gE('ti-msg').placeholder = d.ph;\n            if(gE('ti-send')) gE('ti-send').innerText = d.send;\n            if(gE('ti-new')) gE('ti-new').innerText = d.new;\n            if(gE('ti-copy')) { gE('ti-copy').innerText = d.copy; gE('ti-copy').style.fontSize='11px'; }\n            if(gE('ti-file')) gE('ti-file').innerText = d.file;\n            if(gE('ti-products-btn')) gE('ti-products-btn').innerText = d.products || 'Prodotti';\n            if(gE('ti-services-btn')) gE('ti-services-btn').innerText = d.services || 'Servizi';\n            if(gE('ti-activities-btn')) gE('ti-activities-btn').innerText = d.activities || (window.currLang === 'en' ? 'Activities' : 'Attivit\u00e0');\n            if(gE('ti-kbd-btn')) gE('ti-kbd-btn').innerText = d.kbd;\n            if(gE('ti-mic')) gE('ti-mic').innerText = d.mic;\n            if(gE('ti-cam')) gE('ti-cam').innerText = d.cam;\n            if(gE('ti-login-btn') && gE('ti-login-btn').style.display !== 'none') gE('ti-login-btn').innerText = d.login;\n        } catch(e) {}\n        try { if (window.applyMuteState) window.applyMuteState(); } catch(e) {}\n        window.forceApplyCurrentLanguageEverywhere('language-switch');\n        if (window.currLang === 'en' && typeof window.fetchDitteList === 'function') {\n            try {\n                window.fetchDitteList({silent:true}).then(function(){\n                    try {\n                        const dsNow = gE('ti-ditta');\n                        if (dsNow && selectedDbBefore && Array.from(dsNow.options || []).some(function(o){ return o.value === selectedDbBefore; })) {\n                            dsNow.value = selectedDbBefore;\n                            window.tiCurrentSelectedDb = selectedDbBefore;\n                            if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n                        }\n                        if (window.forceApplyCurrentLanguageEverywhere) window.forceApplyCurrentLanguageEverywhere('language-switch-ditta-refresh');\n                    } catch(e) {}\n                }).catch(function(){});\n            } catch(e) {}\n        }\n        setTimeout(function(){\n            try {\n                const ds = gE('ti-ditta');\n                if (ds && selectedDbBefore && Array.from(ds.options || []).some(function(o){ return o.value === selectedDbBefore; })) {\n                    ds.value = selectedDbBefore;\n                    window.tiCurrentSelectedDb = selectedDbBefore;\n                    if (selectedDbBefore !== 'NEW_DB') localStorage.setItem('ti_saved_db', selectedDbBefore);\n                    if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n                }\n            } catch(e) {}\n        }, 250);\n    };\n\n    if (!window.__tiLangSwitchDelegatedBound) {\n        window.__tiLangSwitchDelegatedBound = true;\n        document.addEventListener('click', function(ev) {\n            const target = ev.target && ev.target.closest ? ev.target.closest('.ti-lang-opt') : null;\n            if (!target) return;\n            ev.preventDefault();\n            ev.stopPropagation();\n            window.setShopServiceLanguage(target.dataset.lang || 'it');\n        }, true);\n    }\n    document.querySelectorAll('.ti-lang-opt').forEach(function(el) {\n        el.onclick = function(ev) {\n            if (ev) { ev.preventDefault(); ev.stopPropagation(); }\n            window.setShopServiceLanguage(this.dataset.lang || 'it');\n        };\n    });\n\n    document.addEventListener(\"DOMContentLoaded\", function() {\n        const confAiBtn = gE('ti-conf-ai-send');\n        if (confAiBtn) confAiBtn.onclick = window.runConfigBot;\n        const dom = { chat: gE('ti-chat-box'), ditta: gE('ti-ditta'), msg: gE('ti-msg'), send: gE('ti-send'), btnNew: gE('ti-new') };\n\n\n        window.tiEnsureUserChatBelowAiForm717 = function() {\n            try {\n                const chat = gE('ti-chat-box');\n                const form = document.querySelector('#ti-ai-outer .ti-input-wrap');\n                if (!chat || !form || !form.parentNode) return chat || null;\n                if (form.previousElementSibling !== chat) form.parentNode.insertBefore(chat, form);\n                if (chat.classList) chat.classList.add('ti-chat-after-ai-form-717');\n                chat.setAttribute('data-ti-placement', 'after-ai-form');\n                return chat;\n            } catch(e) { return gE('ti-chat-box'); }\n        };\n        if (window.tiEnsureUserChatBelowAiForm717) {\n            window.tiEnsureUserChatBelowAiForm717();\n            setTimeout(window.tiEnsureUserChatBelowAiForm717, 0);\n            setTimeout(window.tiEnsureUserChatBelowAiForm717, 500);\n        }\n\n        document.addEventListener('change', function(ev){\n            const t = ev.target;\n            if (t && (t.classList.contains('ti-import-map-select') || t.classList.contains('ti-import-group-target'))) window.scheduleImportPreviewRefresh(t);\n        });\n        document.addEventListener('input', function(ev){\n            const t = ev.target;\n            if (t && t.classList && t.classList.contains('ti-import-price-formula')) window.scheduleImportPreviewRefresh(t);\n        });\n        \n        if (gE('help-ver')) gE('help-ver').innerText = window.tiVersion + (window.tiVersionDateTime ? ' - ' + window.tiVersionDateTime : '');\n        window.tiAudioKey = window.tiAudioStorageKey716 ? window.tiAudioStorageKey716() : ('ti_mute_state_' + location.host + '_' + (window.tiUser || 'guest'));\n        const tiAudioPersistent716 = !!(window.tiAudioPersistentAllowed716 && window.tiAudioPersistentAllowed716());\n        let tiStoredMute716 = null;\n        try {\n            if (tiAudioPersistent716) tiStoredMute716 = localStorage.getItem(window.tiAudioKey);\n            else localStorage.removeItem(window.tiAudioKey);\n        } catch(e) {}\n        window.muteState = tiStoredMute716 ? parseInt(tiStoredMute716, 10) : 0;\n        if (isNaN(window.muteState) || window.muteState < 0 || (!tiAudioPersistent716 && window.muteState !== 1)) window.muteState = 0;\n        window.applyMuteState();\n        \n        const muteBtn = gE('ti-mute-btn');\n        if(muteBtn) {\n            muteBtn.onclick = () => {\n                const canPersistMute716 = !!(window.tiAudioPersistentAllowed716 && window.tiAudioPersistentAllowed716());\n                if (canPersistMute716) window.muteState = (window.muteState + 1) % 3;\n                else window.muteState = (window.muteState === 1 ? 0 : 1);\n                try {\n                    if(canPersistMute716 && window.muteState === 2) { localStorage.setItem(window.tiAudioKey, '2'); }\n                    else { localStorage.removeItem(window.tiAudioKey); }\n                } catch(e) {}\n                window.applyMuteState();\n                if(window.isMuted && window.speechSynthesis) window.speechSynthesis.cancel();\n            };\n        }\n        \n        window.ditteStateFilter = localStorage.getItem('ti_ditte_state_filter') || 'all';\n        if (window.ditteStateFilter === 'normal') window.ditteStateFilter = 'all';\n        if (!['all','active','suspended','demo'].includes(window.ditteStateFilter)) window.ditteStateFilter = 'all';\n        window.showSuspendedDitte = true;\n        window.showDemoDitteOnly = (window.ditteStateFilter === 'demo');\n        window.renderSuspendedToggle = function() {\n            const b = gE('ti-show-sospese-btn'); if(!b) return;\n            if (window.ditteStateFilter === 'active') {\n                b.textContent = 'Attive';\n                b.style.background = '#059669';\n                b.title = 'Attive: mostra solo le ditte con Stato Attivo\/Operativo. Clic successivo: solo sospese.';\n            } else if (window.ditteStateFilter === 'demo') {\n                b.textContent = 'Demo';\n                b.style.background = '#0369a1';\n                b.title = 'Demo: mostra solo le ditte demo. Clic successivo: Tutte.';\n            } else if (window.ditteStateFilter === 'suspended') {\n                b.textContent = 'Sosp';\n                b.style.background = '#b91c1c';\n                b.title = 'Sosp: mostra solo le ditte sospese. Clic successivo: Demo.';\n            } else {\n                b.textContent = 'Tutte';\n                b.style.background = '#374151';\n                b.title = 'Tutte: mostra tutte le ditte, incluse sospese e demo. Clic successivo: solo attive.';\n            }\n            window.showSuspendedDitte = true;\n            window.showDemoDitteOnly = (window.ditteStateFilter === 'demo');\n            window.showActiveDitteOnly = (window.ditteStateFilter === 'active');\n        };\n        window.normalizeSearchText = function(str) {\n            return String(str || '')\n                .toLowerCase()\n                .normalize('NFD')\n                .replace(\/[\u0300-\u036f]\/g, '')\n                .replace(\/[^a-z0-9\\s]\/g, ' ')\n                .replace(\/\\s+\/g, ' ')\n                .trim();\n        };\n        window.filterDitteData = function(list, query) {\n            list = Array.isArray(list) ? list : [];\n            const q = window.normalizeSearchText(query || '');\n            if (!q) return list;\n            const tokens = q.split(\/\\s+\/).filter(Boolean);\n            const synonyms = {\n                farmacia: ['farmacia', 'parafarmacia', 'sanitario', 'farmaci', 'salute'],\n                ferramenta: ['ferramenta', 'bricolage', 'utensili', 'viti', 'fissaggi'],\n                fiori: ['fiori', 'fioraio', 'bouquet', 'piante', 'floreale'],\n                auto: ['auto', 'autofficina', 'carrozzeria', 'gomme', 'ricambi'],\n                capelli: ['capelli', 'hairstyle', 'parrucchiere', 'barbiere', 'acconciature'],\n                estetica: ['estetica', 'beauty', 'benessere', 'trattamenti', 'viso'],\n                ristorante: ['ristorante', 'pizzeria', 'cucina', 'menu', 'piatti'],\n                medico: ['medico', 'sanitario', 'visite', 'esami', 'specialistica']\n            };\n            const expandToken = function(tok) {\n                let out = [tok];\n                Object.keys(synonyms).forEach(key => {\n                    const vals = synonyms[key];\n                    if (key === tok || vals.includes(tok)) out = out.concat(vals);\n                });\n                return Array.from(new Set(out));\n            };\n            const scored = list.map(d => {\n                const nome = window.normalizeSearchText(d.nome_pura || d.label || '');\n                const settore = window.normalizeSearchText(d.settore || '');\n                const accoglienza = window.normalizeSearchText(d.accoglienza || '');\n                const db = window.normalizeSearchText(d.db || '');\n                const hay = [nome, settore, accoglienza, db].join(' | ');\n                let score = 0;\n                let matchedAll = true;\n                tokens.forEach(tok => {\n                    const expanded = expandToken(tok);\n                    let tokenMatched = false;\n                    expanded.forEach(v => {\n                        if (!v) return;\n                        if (nome === v) { score += 120; tokenMatched = true; }\n                        else if (nome.startsWith(v)) { score += 90; tokenMatched = true; }\n                        else if (nome.includes(v)) { score += 60; tokenMatched = true; }\n                        if (settore === v) { score += 110; tokenMatched = true; }\n                        else if (settore.startsWith(v)) { score += 85; tokenMatched = true; }\n                        else if (settore.includes(v)) { score += 55; tokenMatched = true; }\n                        if (db === v) { score += 80; tokenMatched = true; }\n                        else if (db.includes(v)) { score += 45; tokenMatched = true; }\n                        if (accoglienza.includes(v)) { score += 20; tokenMatched = true; }\n                        if (!tokenMatched && v.length >= 3) {\n                            const prefix = v.slice(0, 4);\n                            if (nome.includes(prefix) || settore.includes(prefix) || db.includes(prefix)) {\n                                score += 18; tokenMatched = true;\n                            }\n                        }\n                    });\n                    if (!tokenMatched) matchedAll = false;\n                });\n                if (nome.includes(q)) score += 100;\n                if (settore.includes(q)) score += 90;\n                if (db.includes(q)) score += 70;\n                return { item: d, score, matchedAll };\n            }).filter(x => x.score > 0 || x.matchedAll);\n            scored.sort((a,b) => {\n                if ((b.score||0) !== (a.score||0)) return (b.score||0) - (a.score||0);\n                return String(a.item.nome_pura || '').localeCompare(String(b.item.nome_pura || ''));\n            });\n            return scored.map(x => x.item);\n        };\n\n        window.closeDittaPopup = function() {\n            const pop = gE('ti-ditta-popup');\n            if (pop) pop.style.display = 'none';\n        };\n\n        window.syncDittaTrigger = function() {\n            const sel = gE('ti-ditta');\n            const btn = gE('ti-ditta-trigger');\n            if (!sel || !btn) return;\n            const opt = sel.options && sel.selectedIndex >= 0 ? sel.options[sel.selectedIndex] : null;\n            let label = '-- Scegli una ditta --';\n            let color = '#ffffff';\n            let borderColor = '#444';\n            if (opt) {\n                label = String(opt.text || label);\n                const status = String(opt.getAttribute('data-status') || '').toLowerCase();\n                if (status === 'sospesa' || status === 'sviluppo') { color = '#ef4444'; borderColor = '#7f1d1d'; }\n                else if (status === 'demo') { color = '#38bdf8'; borderColor = '#0e7490'; }\n                else if (status === 'configurazione') { color = '#facc15'; borderColor = '#a16207'; }\n                else if (opt.value === 'NEW_DB') { color = '#10b981'; borderColor = '#065f46'; }\n            }\n            btn.textContent = label;\n            btn.style.color = color;\n            btn.style.borderColor = borderColor;\n            btn.style.background = '#000000';\n        };\n\n        window.renderDittePopup = function() {\n            const sel = gE('ti-ditta');\n            const pop = gE('ti-ditta-popup');\n            if (!sel || !pop) return;\n            const options = Array.from(sel.options || []);\n            pop.innerHTML = '';\n            options.forEach(function(opt){\n                const status = String(opt.getAttribute('data-status') || '').toLowerCase() || 'attivo';\n                const effectiveStatus = (opt.value === 'NEW_DB') ? 'nuova' : status;\n                const fullText = String(opt.text || '').trim();\n                let badgeText = '';\n                let baseText = fullText;\n\n                if (\/ - Sosp$\/i.test(fullText)) { baseText = fullText.replace(\/ - Sosp$\/i, ''); badgeText = 'Sosp'; }\n                else if (\/ - Conf$\/i.test(fullText)) { baseText = fullText.replace(\/ - Conf$\/i, ''); badgeText = 'Conf'; }\n                else if (\/ - Demo$\/i.test(fullText)) { baseText = fullText.replace(\/ - Demo$\/i, ''); badgeText = 'Demo'; }\n                else if (\/ - Svil$\/i.test(fullText)) { baseText = fullText.replace(\/ - Svil$\/i, ''); badgeText = 'Svil'; }\n                else if (opt.value === 'NEW_DB') { badgeText = 'Nuova'; }\n\n                const row = document.createElement('div');\n                row.className = 'ti-ditta-row ' + effectiveStatus + (sel.value === opt.value ? ' active' : '');\n                row.setAttribute('role', 'button');\n                row.tabIndex = opt.disabled ? -1 : 0;\n                if (!opt.value) row.classList.add('empty');\n                if (opt.disabled) row.classList.add('none');\n\n                const main = document.createElement('span');\n                main.className = 'ti-ditta-row-main';\n                main.textContent = baseText || fullText || '--';\n                if (row.classList.contains('empty') || row.classList.contains('none')) {\n                    main.style.setProperty('color', '#cbd5e1', 'important');\n                } else {\n                    main.style.setProperty('color', '#ffffff', 'important');\n                }\n                row.appendChild(main);\n\n                if (badgeText) {\n                    const badge = document.createElement('span');\n                    badge.className = 'ti-ditta-row-badge ' + effectiveStatus;\n                    badge.textContent = badgeText;\n                    row.appendChild(badge);\n                }\n\n                const pickRow = function(ev){\n                    if (ev) ev.preventDefault();\n                    if (opt.disabled) return;\n                    sel.value = opt.value;\n                    if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n                    window.closeDittaPopup();\n                    sel.dispatchEvent(new Event('change', {bubbles:true}));\n                };\n                row.onclick = pickRow;\n                row.onkeydown = function(ev){\n                    if (ev.key === 'Enter' || ev.key === ' ') pickRow(ev);\n                };\n                pop.appendChild(row);\n            });\n        };\n\n        window.toggleDittaPopup = function() {\n            const pop = gE('ti-ditta-popup');\n            if (!pop) return;\n            const willOpen = pop.style.display === 'none' || !pop.style.display;\n            if (willOpen) {\n                if (window.renderDittePopup) window.renderDittePopup();\n            if (window.syncLoginDbSelect) window.syncLoginDbSelect();\n                pop.style.display = 'block';\n            } else {\n                pop.style.display = 'none';\n            }\n        };\n\nwindow.forceWhiteEditStyle = function(el) {\n            if (!el) return;\n            try {\n                const inConf = !!(el.closest && (el.closest('.ti-conf-window') || el.closest('.ti-conf-body') || el.closest('.ti-cfg-table') || el.closest('.ti-import-map-table') || el.closest('#ti-profile-ov') || el.closest('#ti-newdb-ov') || el.closest('#ti-new-db-ov')));\n                if (!inConf) return;\n                el.style.setProperty('color', '#ffffff', 'important');\n                el.style.setProperty('-webkit-text-fill-color', '#ffffff', 'important');\n                el.style.setProperty('caret-color', '#ffffff', 'important');\n                el.style.setProperty('background', '#000000', 'important');\n                el.style.setProperty('background-color', '#000000', 'important');\n                el.style.setProperty('background-image', 'none', 'important');\n                el.style.setProperty('text-shadow', 'none', 'important');\n                el.style.setProperty('color-scheme', 'dark', 'important');\n                if (el.tagName === 'SELECT' && el.options) {\n                    Array.from(el.options).forEach(function(opt){\n                        opt.style.setProperty('color', '#ffffff', 'important');\n                        opt.style.setProperty('-webkit-text-fill-color', '#ffffff', 'important');\n                        opt.style.setProperty('background', '#000000', 'important');\n                        opt.style.setProperty('background-color', '#000000', 'important');\n                    });\n                }\n            } catch(e) {}\n        };\n\n        window.bindConfigWhiteEditHandlers = function() {\n            if (window.__tiConfigWhiteEditBound) return;\n            window.__tiConfigWhiteEditBound = true;\n\n            document.addEventListener('focusin', function(ev) {\n                const el = ev.target;\n                if (!el) return;\n                window.forceWhiteEditStyle(el);\n            }, true);\n\n            document.addEventListener('input', function(ev) {\n                const el = ev.target;\n                if (!el) return;\n                window.forceWhiteEditStyle(el);\n            }, true);\n\n            document.addEventListener('change', function(ev) {\n                const el = ev.target;\n                if (!el) return;\n                window.forceWhiteEditStyle(el);\n            }, true);\n\n            document.addEventListener('click', function(ev) {\n                const el = ev.target;\n                if (!el) return;\n                if (el.matches && (el.matches('.ti-conf-window input, .ti-conf-window textarea, .ti-conf-window select, .ti-cfg-table input, .ti-cfg-table textarea, .ti-cfg-table select, .ti-import-map-table input, .ti-import-map-table textarea, .ti-import-map-table select, #ti-profile-ov input, #ti-profile-ov textarea, #ti-profile-ov select, #ti-newdb-ov input, #ti-newdb-ov textarea, #ti-newdb-ov select, #ti-new-db-ov input, #ti-new-db-ov textarea, #ti-new-db-ov select, .ti-conf-window [contenteditable=\"true\"], .ti-cfg-table [contenteditable=\"true\"], .ti-import-map-table [contenteditable=\"true\"]'))) {\n                    window.forceWhiteEditStyle(el);\n                }\n            }, true);\n        };\n\n        window.bindConfigWhiteEditHandlers();\n\nwindow.applyDittaSelectVisual = function() {\n            const sel = gE('ti-ditta');\n            if (!sel) return;\n            const opt = sel.options && sel.selectedIndex >= 0 ? sel.options[sel.selectedIndex] : null;\n            let color = '#ffffff', weight = '400';\n            if (opt) {\n                const status = String(opt.getAttribute('data-status') || '').toLowerCase();\n                if (status === 'sospesa' || status === 'sviluppo') { color = '#ef4444'; weight = '700'; }\n                else if (status === 'demo') { color = '#38bdf8'; weight = '700'; }\n                else if (status === 'configurazione') { color = '#facc15'; weight = '700'; }\n                else if (opt.value === 'NEW_DB') { color = '#10b981'; weight = '700'; }\n            }\n            sel.style.color = color;\n            sel.style.fontWeight = weight;\n            sel.style.background = '#000000';\n            if (window.syncDittaTrigger) window.syncDittaTrigger();\n            if (window.updateCompanyReferenceDateTime) window.updateCompanyReferenceDateTime();\n        };\n\n        window.renderDitteDropdown = function() {\n            const dittaInput = gE('ti-ditta'); if(!dittaInput) return;\n            const prevSelectedDb = dittaInput.value || window.tiCurrentSelectedDb || localStorage.getItem('ti_saved_db') || '';\n            if (prevSelectedDb) window.tiCurrentSelectedDb = prevSelectedDb;\n            const filterInp = gE('ti-filter-ditte');\n            const q = filterInp ? filterInp.value : '';\n            if(!window.ditteData || !Array.isArray(window.ditteData)) window.ditteData = [];\n            let sorted = window.filterDitteData([...window.ditteData], q);\n            if(window.sortMode === 'AZ') { sorted.sort((a,b) => (a.nome_pura||'').localeCompare(b.nome_pura||'')); } else { sorted.sort((a,b) => (a.settore || '').localeCompare(b.settore || '')); }\n\n            dittaInput.innerHTML = '';\n\n            const optEmpty = document.createElement('option');\n            optEmpty.value = '';\n            optEmpty.textContent = '-- Scegli una ditta --';\n            optEmpty.setAttribute('data-status', 'attivo');\n            optEmpty.style.color = '#ffffff';\n            optEmpty.style.background = '#000000';\n            dittaInput.appendChild(optEmpty);\n\n            const optNew = document.createElement('option');\n            optNew.value = 'NEW_DB';\n            optNew.textContent = '+ Nuova ditta ...';\n            optNew.setAttribute('data-status', 'nuova');\n            optNew.style.color = '#10b981';\n            optNew.style.fontWeight = '700';\n            optNew.style.background = '#000000';\n            dittaInput.appendChild(optNew);\n\n            sorted.forEach(function(d){\n                const opt = document.createElement('option');\n                const safeName = String(d.nome_pura || '');\n                const safeSettore = String(d.settore || '');\n                let statusCode = 'attivo';\n                let suffix = '';\n                let color = '#ffffff';\n                if (d.is_dev) {\n                    statusCode = 'sviluppo';\n                    suffix = ' - Svil';\n                    color = '#ef4444';\n                } else if (d.is_sosp) {\n                    statusCode = 'sospesa';\n                    suffix = ' - Sosp';\n                    color = '#ef4444';\n                } else if (d.is_demo) {\n                    statusCode = 'demo';\n                    suffix = ' - Demo';\n                    color = '#38bdf8';\n                } else if (d.is_conf) {\n                    statusCode = 'configurazione';\n                    suffix = ' - Conf';\n                    color = '#facc15';\n                }\n                opt.value = d.db;\n                opt.textContent = safeName + (safeSettore ? ' - ' + safeSettore : '') + suffix;\n                opt.setAttribute('data-status', statusCode);\n                opt.setAttribute('data-stato', String(d.stato || d.company_state || statusCode || ''));\n                opt.setAttribute('data-company-state', String(d.company_state || d.stato || statusCode || ''));\n                opt.setAttribute('data-acc', String(d.accoglienza || ''));\n                opt.setAttribute('data-acc-en', String(d.accoglienza_en || ''));\n                opt.setAttribute('data-ref-datetime', String(d.ref_datetime || ''));\n                opt.setAttribute('data-ref-tz', String(d.ref_timezone || ''));\n                opt.setAttribute('data-logo', String(d.logo || ''));\n                opt.setAttribute('data-qr', String(d.qr || ''));\n                opt.setAttribute('data-demo-login-available', d.demo_login_available ? '1' : '0');\n                opt.setAttribute('data-demo-login-user', String(d.demo_login_user || ''));\n                opt.setAttribute('data-demo-login-pass', String(d.demo_login_pass || ''));\n                opt.setAttribute('data-demo-login-notice', String(d.demo_login_notice || ''));\n                opt.setAttribute('data-name', safeName);\n                opt.setAttribute('data-tab-tab', String(d.tab_tab || ''));\n                opt.setAttribute('data-tab-sma', String(d.tab_sma || ''));\n                opt.setAttribute('data-tab-med', String(d.tab_med || ''));\n                opt.setAttribute('data-tab-lar', String(d.tab_lar || ''));\n                opt.style.color = color;\n                opt.style.background = '#000000';\n                if (statusCode !== 'attivo') opt.style.fontWeight = '700';\n                dittaInput.appendChild(opt);\n            });\n\n            if (!sorted.length && q) {\n                const optNone = document.createElement('option');\n                optNone.value = '';\n                optNone.disabled = true;\n                optNone.textContent = 'Nessuna ditta trovata';\n                optNone.style.color = '#9ca3af';\n                optNone.style.background = '#000000';\n                dittaInput.appendChild(optNone);\n            }\n\n            if (prevSelectedDb && !Array.from(dittaInput.options || []).some(function(o){ return o.value === prevSelectedDb; })) {\n                const savedRecord = (window.ditteData || []).find(function(d){ return String(d && d.db || '') === String(prevSelectedDb); });\n                if (savedRecord) {\n                    const opt = document.createElement('option');\n                    const safeName = String(savedRecord.nome_pura || '');\n                    const safeSettore = String(savedRecord.settore || '');\n                    let statusCode = 'attivo', suffix = '', color = '#ffffff';\n                    if (savedRecord.is_dev) { statusCode = 'sviluppo'; suffix = ' - Svil'; color = '#ef4444'; }\n                    else if (savedRecord.is_sosp) { statusCode = 'sospesa'; suffix = ' - Sosp'; color = '#ef4444'; }\n                    else if (savedRecord.is_demo) { statusCode = 'demo'; suffix = ' - Demo'; color = '#38bdf8'; }\n                    else if (savedRecord.is_conf) { statusCode = 'configurazione'; suffix = ' - Conf'; color = '#facc15'; }\n                    opt.value = savedRecord.db;\n                    opt.textContent = safeName + (safeSettore ? ' - ' + safeSettore : '') + suffix;\n                    opt.setAttribute('data-status', statusCode);\n                    opt.setAttribute('data-stato', String(savedRecord.stato || savedRecord.company_state || statusCode || ''));\n                    opt.setAttribute('data-company-state', String(savedRecord.company_state || savedRecord.stato || statusCode || ''));\n                    opt.setAttribute('data-acc', String(savedRecord.accoglienza || ''));\n                    opt.setAttribute('data-acc-en', String(savedRecord.accoglienza_en || ''));\n                    opt.setAttribute('data-ref-datetime', String(savedRecord.ref_datetime || ''));\n                    opt.setAttribute('data-ref-tz', String(savedRecord.ref_timezone || ''));\n                    opt.setAttribute('data-logo', String(savedRecord.logo || ''));\n                    opt.setAttribute('data-qr', String(savedRecord.qr || ''));\n                    opt.setAttribute('data-name', safeName);\n                    opt.setAttribute('data-tab-tab', String(savedRecord.tab_tab || ''));\n                    opt.setAttribute('data-tab-sma', String(savedRecord.tab_sma || ''));\n                    opt.setAttribute('data-tab-med', String(savedRecord.tab_med || ''));\n                    opt.setAttribute('data-tab-lar', String(savedRecord.tab_lar || ''));\n                    opt.style.color = color;\n                    opt.style.background = '#000000';\n                    if (statusCode !== 'attivo') opt.style.fontWeight = '700';\n                    dittaInput.appendChild(opt);\n                }\n            }\n            if (prevSelectedDb) {\n                const hasPrev = Array.from(dittaInput.options || []).some(function(o){ return o.value === prevSelectedDb; });\n                if (hasPrev) dittaInput.value = prevSelectedDb;\n            }\n            if (dittaInput.value) {\n                window.tiCurrentSelectedDb = dittaInput.value;\n                if (dittaInput.value !== 'NEW_DB') localStorage.setItem('ti_saved_db', dittaInput.value);\n            }\n            if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n            if (window.renderDittePopup) window.renderDittePopup();\n            if(window.tiForced) {\n                dittaInput.style.display = 'none';\n                const picker = document.querySelector('.ti-ditta-picker'); if (picker) picker.style.display = 'none';\n                const sBtn = gE('ti-sort-btn'); if(sBtn) sBtn.style.display = 'none';\n                const fInp = gE('ti-filter-ditte'); if(fInp) fInp.style.display = 'none';\n            }\n        };\n\n        window.fetchDitteList = function(opts){\n            opts = opts || {};\n            const silent = !!opts.silent;\n            const prevVal = dom.ditta ? dom.ditta.value : '';\n            const stateFilter = ['all','active','suspended','demo'].includes(window.ditteStateFilter) ? window.ditteStateFilter : 'all';\n            const url = window.tiUrl + '?ti_action=ti_ai_list_ditte&current_db=' + encodeURIComponent(window.tiForced || '') + '&state_filter=' + encodeURIComponent(stateFilter) + '&show_suspended=' + (stateFilter === 'all' ? '1' : '0') + '&show_demo_only=' + (stateFilter === 'demo' ? '1' : '0') + '&ti_lang=' + encodeURIComponent((String(window.currLang || 'it').toLowerCase().indexOf('en') === 0) ? 'en' : 'it');\n            return window.fetchJsonSafe(url, {method:'GET'}).then(function(res){\n                if(res.success && dom.ditta) {\n                    window.ditteData = Array.isArray(res.data && res.data.ditte) ? res.data.ditte : [];\n                    window.renderDitteDropdown();\n                    if(window.tiForced) {\n                        dom.ditta.value = window.tiForced;\n                        const fdReset = new FormData(); fdReset.append('ti_action', 'ti_ai_reset_flow'); fetch(window.tiUrl, {method:'POST', body:fdReset}).catch(function(){});\n                    } else {\n                        let targetDb = prevVal || localStorage.getItem('ti_saved_db');\n                        if (targetDb && Array.from(dom.ditta.options).find(o => o.value === targetDb)) { dom.ditta.value = targetDb; window.tiCurrentSelectedDb = targetDb; }\n                    }\n                    if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n                    window.updCtx(silent);\n                }\n                return res;\n            }).catch(function(err){\n                console.error('Errore fetchDitteList:', err);\n                window.ditteData = [];\n                if (dom.ditta) dom.ditta.innerHTML = '<option value=\"\">Errore caricamento ditte<\/option>';\n                if (err && err.maintenance) {\n                    throw err;\n                }\n                if (!silent && window.tiAlert) window.tiAlert('Errore caricamento ditte: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n                throw err;\n            });\n        };\n\n        window.fetchDitteList({silent:false});\n\n        const dittaTrigger = gE('ti-ditta-trigger');\n        if (dittaTrigger) {\n            dittaTrigger.onclick = function(ev){\n                ev.preventDefault();\n                ev.stopPropagation();\n                window.toggleDittaPopup();\n            };\n        }\n        document.addEventListener('click', function(ev){\n            const picker = document.querySelector('.ti-ditta-picker');\n            if (!picker) return;\n            if (!picker.contains(ev.target)) window.closeDittaPopup();\n        });\n\n        const sospBtn = gE('ti-show-sospese-btn');\n        if(sospBtn) {\n            window.renderSuspendedToggle();\n            sospBtn.onclick = () => {\n                if (window.ditteStateFilter === 'all') window.ditteStateFilter = 'active';\n                else if (window.ditteStateFilter === 'active') window.ditteStateFilter = 'suspended';\n                else if (window.ditteStateFilter === 'suspended') window.ditteStateFilter = 'demo';\n                else window.ditteStateFilter = 'all';\n                localStorage.setItem('ti_ditte_state_filter', window.ditteStateFilter);\n                localStorage.setItem('ti_show_suspended_ditte', window.ditteStateFilter === 'all' ? '1' : '0');\n                window.renderSuspendedToggle();\n                window.fetchDitteList({silent:true});\n            };\n        }\n\n        const sortBtn = gE('ti-sort-btn');\n        if(sortBtn) { sortBtn.onclick = () => { window.sortMode = (window.sortMode === 'AZ') ? 'ATT' : 'AZ'; sortBtn.textContent = window.sortMode; window.renderDitteDropdown(); }; }\n\n        const filterInp = gE('ti-filter-ditte');\n        if(filterInp) {\n            filterInp.oninput = () => {\n                const prevVal = dom.ditta && dom.ditta.value ? dom.ditta.value : '';\n                window.renderDitteDropdown();\n                if (dom.ditta && prevVal && Array.from(dom.ditta.options).some(o => o.value === prevVal)) dom.ditta.value = prevVal;\n                if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n                if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n            };\n            filterInp.onkeydown = (e) => {\n                if (e.key === 'Enter') {\n                    e.preventDefault();\n                    const realOptions = Array.from(dom.ditta.options).filter(o => o.value && o.value !== 'NEW_DB' && !o.disabled);\n                    if (realOptions.length === 1) {\n                        dom.ditta.value = realOptions[0].value;\n                        dom.ditta.dispatchEvent(new Event('change'));\n                    }\n                }\n            };\n        }\n\n        if (dom.ditta) {\n            dom.ditta.onchange = () => {\n                 try { if (window.closeAssociatedFilePopups512) window.closeAssociatedFilePopups512(); else window.tiSuppressFileManagerUntil = Date.now() + 2500; } catch(e) {}\n                 let v = dom.ditta.value;\n                 if (v === 'NEW_DB') { \n                     window.openModal('ti-new-db-ov'); dom.ditta.value = window.tiForced || localStorage.getItem('ti_saved_db') || ''; return; \n                 }\n\n                 if(v !== '') { localStorage.setItem('ti_saved_db', v); window.tiCurrentSelectedDb = v; } else { localStorage.removeItem('ti_saved_db'); window.tiCurrentSelectedDb = ''; }\n                 if (window.applyDittaSelectVisual) window.applyDittaSelectVisual();\n                 const mustLogoutForCompanySwitch = !!(window.tiUser && window.tiUser !== 'SSGlobalAdmin' && window.tiSessionDb && v && v !== window.tiSessionDb);\n                 if (mustLogoutForCompanySwitch) {\n                     if(dom.chat) dom.chat.innerHTML = '';\n                     const fdReset = new FormData(); fdReset.append('ti_action', 'ti_ai_reset_flow');\n                     fetch(window.tiUrl, {method:'POST', body:fdReset}).catch(()=>{});\n                     const fdLogout = new FormData(); fdLogout.append('ti_action','ti_ai_logout');\n                     fetch(window.tiUrl,{method:'POST',body:fdLogout}).finally(()=>{ location.reload(); });\n                     return;\n                 }\n                 if(dom.chat) dom.chat.innerHTML = '';\n                 window.hideOrderConfirmPanel();\n                 const fd = new FormData(); fd.append('ti_action', 'ti_ai_reset_flow');\n                 fetch(window.tiUrl, {method:'POST', body:fd}).finally(function(){ window.updCtx(false); });\n            };\n        }\n\n        window.loadRegisteredUserProfileWelcome = function(dbValue) {\n            try {\n                if (!window.tiUser || window.tiUser === 'SSGlobalAdmin' || String(window.tiUser || '').toLowerCase() === 'ssdemo') return;\n                if (!dbValue || dbValue === 'NEW_DB') return;\n                window.tiProfileWelcomeShown = window.tiProfileWelcomeShown || {};\n                const key = String(dbValue) + '|' + String(window.tiUser || '');\n                if (window.tiProfileWelcomeShown[key]) return;\n                window.tiProfileWelcomeShown[key] = true;\n                const fdProfile = new FormData();\n                fdProfile.append('ti_action', 'ti_ai_chat_start');\n                fdProfile.append('db', dbValue);\n                fdProfile.append('text', '__TI_PROFILE_WELCOME__');\n                fetch(window.tiUrl, {method:'POST', body:fdProfile})\n                    .then(function(r){ return r.json(); })\n                    .then(function(d){\n                        const reply = d && d.success && d.data ? String(d.data.reply || '') : '';\n                        if (!reply) return;\n                        if (window.tiAddAiMessageTranslatedIfNeeded) window.tiAddAiMessageTranslatedIfNeeded(reply, String(d.data.voice || reply), dbValue);\n                        else { if (window.addMsg) window.addMsg('ai', reply, reply); if (d.data.voice && window.speakText) window.speakText(String(d.data.voice || '')); }\n                    })\n                    .catch(function(err){ console.warn('Profilazione utente non disponibile:', err); });\n            } catch(e) { console.warn('Profilazione utente non disponibile:', e); }\n        };\n\n        window.tiGetCompanyWelcomeTextForLanguage = function(optionEl, lang) {\n            if (!optionEl) return '';\n            lang = String(lang || window.currLang || 'it').toLowerCase() === 'en' ? 'en' : 'it';\n            const raw = String(optionEl.dataset && optionEl.dataset.acc ? optionEl.dataset.acc : '');\n            if (lang !== 'en') return raw;\n            const en = String(optionEl.dataset && optionEl.dataset.accEn ? optionEl.dataset.accEn : '');\n            if (en) return en;\n            return (window.tiTranslateLayoutStringToEn ? window.tiTranslateLayoutStringToEn(raw) : raw);\n        };\n        window.tiQueueCompanyWelcomeAccTranslation = function(optionEl, bubble, sourceHtml, dbValue) {\n            try {\n                if (!optionEl || !bubble || String(window.currLang || 'it').toLowerCase() !== 'en') return;\n                const rawAcc = String(optionEl.dataset && optionEl.dataset.acc ? optionEl.dataset.acc : '');\n                if (!rawAcc.trim()) return;\n                const accKey = window.tiHashString ? window.tiHashString(rawAcc) : String(rawAcc.length);\n                if (optionEl.dataset.accEn && optionEl.dataset.accEnKey === accKey) return;\n                if (!window.tiRemoteTranslateHtmlToLanguage) return;\n                window.tiRemoteTranslateHtmlToLanguage(rawAcc, 'en', dbValue || '', rawAcc).then(function(accEn){\n                    accEn = String(accEn || '').replace(\/<[^>]+>\/g, ' ').trim();\n                    if (!accEn || String(window.currLang || 'it').toLowerCase() !== 'en') return;\n                    optionEl.dataset.accEn = accEn;\n                    optionEl.dataset.accEnKey = accKey;\n                    if (bubble && sourceHtml && bubble.getAttribute('data-ti-it-html') === sourceHtml) {\n                        const translatedHtml = sourceHtml.replace(rawAcc, accEn);\n                        bubble.innerHTML = translatedHtml;\n                        if (window.tiI18nTranslateSubtree) window.tiI18nTranslateSubtree(bubble);\n                        bubble.__tiChatHtmlByLang = bubble.__tiChatHtmlByLang || {};\n                        bubble.__tiChatHtmlByLang.en = bubble.innerHTML;\n                    }\n                }).catch(function(){});\n            } catch(e) {}\n        };\n\n        window.updCtx = function(silent) {\n             silent = !!silent;\n             const d = gE('ti-ditta');\n             if(!d || !d.value || d.value === 'NEW_DB') {\n                 gE('ti-company-name').textContent = \"Shop & Service\"; const companySub = gE('ti-company-sub'); if (companySub) { companySub.innerHTML = ''; companySub.style.display = 'none'; } if (window.updateCompanyReferenceDateTime) window.updateCompanyReferenceDateTime();\n                 if(!silent) {\n                     let welcomeName = window.tiUserDisplay ? \" Ciao <b>\" + window.tiUserDisplay + \"<\/b>!\" : \"\";\n                     let cfgWelcome = window.getConfiguratorWelcomeHtml ? window.getConfiguratorWelcomeHtml() : '';\n                     let htmlMsg = cfgWelcome || `Sono l'assistente virtuale di <b>Shop & Service<\/b>.${welcomeName}<br><br>Scegli una ditta dal menu in alto, oppure dimmi cosa stai cercando e ti consiglier\u00f2 l'attivit\u00e0 giusta per te!`;\n                     const welcomeVoice = `Sono l'assistente virtuale di Shop & Service. Ciao ${window.tiUserDisplay || ''}! Scegli una ditta dal menu.`;\n                     if (window.tiAddAiMessageTranslatedIfNeeded) window.tiAddAiMessageTranslatedIfNeeded(htmlMsg, welcomeVoice, '');\n                     else { if(window.addMsg) window.addMsg('ai', htmlMsg, htmlMsg); window.speakText(welcomeVoice); }\n                     if (cfgWelcome && window.showConfiguratorOperationalTipsPopup) setTimeout(function(){ window.showConfiguratorOperationalTipsPopup(false); }, 350);\n                 }\n                 return;\n             }\n             const s = d.options[d.selectedIndex];\n             if(s) {\n                 window.currentDittaName = s.dataset.name; gE('ti-company-name').textContent = s.dataset.name; if (window.updateCompanyReferenceDateTime) window.updateCompanyReferenceDateTime(); if (window.applyCompanyLimitedNotice) window.applyCompanyLimitedNotice(); \n                 const l = gE('ti-logo-img'); \n\n                 let directLink = window.location.href.split('?')[0] + \"?ti_ai_db=\" + s.value;\n                 if(l && s.dataset.logo && s.dataset.logo !== \"undefined\") { \n                     let finalLogo = window.getFullImgUrl(s.dataset.logo, s.value, 'Config');\n                     l.src = finalLogo; \n                     l.style.display = 'block'; \n                     l.title = 'Copia link diretto della ditta';\n                     l.onclick = () => { navigator.clipboard.writeText(directLink).then(() => window.tiAlert(\"Link copiato:\\n\" + directLink)); };\n                 } else if(l) { l.style.display = 'none'; l.onclick = null; }\n\n                 const q = gE('ti-qr-img'); if(q && s.value && s.value !== \"NEW_DB\") { q.src=\"https:\/\/api.qrserver.com\/v1\/create-qr-code\/?size=100x100&data=\"+encodeURIComponent(directLink); q.style.display='block'; q.onclick = () => window.open(directLink, '_blank'); } else if (q) { q.style.display='none'; }\n\n                 const uAva = gE('ti-user-avatar');\n                 if(window.tiUser && window.tiUserFoto) { const fotoDb = window.tiUserFotoDb || s.value; const avaUrl = window.getSafePreviewUrl(window.tiUserFoto, fotoDb, 'Utenti', 'Immagine', window.tiUser || 'utente'); if(uAva && avaUrl) { uAva.src = avaUrl; uAva.style.display = 'block'; } } else if(uAva) { uAva.style.display = 'none'; }\n\n                 if(!silent) {\n                     let accTextForLang = window.tiGetCompanyWelcomeTextForLanguage ? window.tiGetCompanyWelcomeTextForLanguage(s, window.currLang) : (s.dataset.acc || '');\n                     let acc = accTextForLang && accTextForLang !== 'undefined' && accTextForLang !== '' ? `<br><br><b>${accTextForLang}<\/b>` : '';\n                     let welcomeName = window.tiUserDisplay ? \" Ciao <b>\" + window.tiUserDisplay + \"<\/b>!\" : \"\";\n                     let plainAcc = accTextForLang && accTextForLang !== 'undefined' ? accTextForLang : '';\n                     let plainWelcomeName = window.tiUserDisplay ? \" Ciao \" + window.tiUserDisplay + \"!\" : \"\";\n\n                     let cfgWelcome = window.getConfiguratorWelcomeHtml ? window.getConfiguratorWelcomeHtml() : '';\n                     const stateNoticeHtml = window.getCompanyLimitedNoticeHtml ? window.getCompanyLimitedNoticeHtml() : '';\n                     let htmlMsg = cfgWelcome || `Sono l'assistente virtuale di <b>${s.dataset.name}<\/b>.${welcomeName}${acc}<br><br>${stateNoticeHtml}Come posso aiutarti oggi?`;\n                     let voiceMsg = cfgWelcome ? `Ciao ${window.tiUserDisplay || 'configuratore'}, puoi configurare la ditta oppure usare la main form come utente registrato standard.` : `Sono l'assistente virtuale di ${s.dataset.name}. ${plainWelcomeName} ${plainAcc} Come posso aiutarti oggi?`;\n\n                     let welcomeBubble = null;\n                     if (window.tiAddAiMessageTranslatedIfNeeded) welcomeBubble = window.tiAddAiMessageTranslatedIfNeeded(htmlMsg, voiceMsg, s.value);\n                     else { if(window.addMsg) welcomeBubble = window.addMsg('ai', htmlMsg, htmlMsg); window.speakText(voiceMsg); }\n                     if (window.tiQueueCompanyWelcomeAccTranslation) window.tiQueueCompanyWelcomeAccTranslation(s, welcomeBubble, htmlMsg, s.value);\n                     if (cfgWelcome && window.showConfiguratorOperationalTipsPopup) setTimeout(function(){ window.showConfiguratorOperationalTipsPopup(false); }, 350);\n                     if (window.loadRegisteredUserProfileWelcome) window.loadRegisteredUserProfileWelcome(s.value);\n                     window.resetIdleTimers(); \n                 }\n             }\n        };\n\nwindow.idleTimer1 = null; window.idleTimer2 = null; window.idleTimer3 = null;\n        window.idleIgnoreUntil = 0;\n        window.idleForceClosing = false;\n        window.idleAwaitingChoice = false;\n        window.idleLastUserActivityAt = Date.now();\n        window.idleFirstWarningAt = 0;\n        window.idleFinalWarningAt = 0;\n        window.idleCloseCheckAt = 0;\n        window.IDLE_FIRST_WARNING_MS = 180000;   \/\/ 3 minuti senza attivita utente\n        window.IDLE_SECOND_WARNING_MS = 120000;  \/\/ ulteriore attesa dopo il primo avviso per utenti standard\/responsabili\n        window.IDLE_CLOSE_AFTER_MS = 60000;      \/\/ chiusura finale per utenti standard\/responsabili\n        window.IDLE_ADMIN_CONFIG_CLOSE_AFTER_WARNING_MS = 3600000; \/\/ 1 ora dall ultimo avviso per amministratori\/configuratori\n        window.isIdleSessionActive = function() {\n            return !!(dom.ditta && dom.ditta.value && dom.ditta.value !== 'NEW_DB');\n        };\n        window.isIdleAdminConfiguratorRole = function() {\n            const role = String(window.tiRole || '').toLowerCase();\n            const user = String(window.tiUser || '').trim();\n            return user === 'SSGlobalAdmin' || \/amministratore|administrator|configuratore|configurator\/.test(role);\n        };\n        window.clearIdleTimers = function() {\n            clearTimeout(window.idleTimer1);\n            clearTimeout(window.idleTimer2);\n            clearTimeout(window.idleTimer3);\n            window.idleTimer1 = null;\n            window.idleTimer2 = null;\n            window.idleTimer3 = null;\n        };\n        window.scheduleIdleFirstWarning = function() {\n            if (window.idleForceClosing) return;\n            window.clearIdleTimers();\n            if (!window.isIdleSessionActive()) return;\n            const elapsed = Date.now() - (window.idleLastUserActivityAt || Date.now());\n            const wait = Math.max(0, window.IDLE_FIRST_WARNING_MS - elapsed);\n            window.idleTimer1 = setTimeout(window.showIdleFirstWarning, wait);\n        };\n        window.showIdleFirstWarning = function() {\n            if (window.idleForceClosing || !window.isIdleSessionActive()) return;\n            const elapsed = Date.now() - (window.idleLastUserActivityAt || Date.now());\n            if (elapsed < window.IDLE_FIRST_WARNING_MS - 250) {\n                window.scheduleIdleFirstWarning();\n                return;\n            }\n            window.idleFirstWarningAt = Date.now();\n            window.idleAwaitingChoice = true;\n            window.idleIgnoreUntil = Date.now() + 8000;\n            if(window.addMsg) window.addMsg('ai', \"\u23f3 Non rilevo attivit\u00e0 da 3 minuti. Vuoi continuare ?\");\n            window.speakText(\"Non rilevo attivit\u00e0 da 3 minuti. Vuoi continuare?\");\n            clearTimeout(window.idleTimer2);\n            clearTimeout(window.idleTimer3);\n            if (window.isIdleAdminConfiguratorRole && window.isIdleAdminConfiguratorRole()) {\n                window.idleCloseCheckAt = window.idleFirstWarningAt;\n                window.idleTimer3 = setTimeout(window.closeIdleSessionIfStillInactive, window.IDLE_ADMIN_CONFIG_CLOSE_AFTER_WARNING_MS);\n            } else {\n                window.idleTimer2 = setTimeout(window.showIdleFinalWarning, window.IDLE_SECOND_WARNING_MS);\n            }\n        };\n        window.showIdleFinalWarning = function() {\n            if (window.idleForceClosing || !window.isIdleSessionActive()) return;\n            if ((window.idleLastUserActivityAt || 0) > (window.idleFirstWarningAt || 0)) {\n                window.scheduleIdleFirstWarning();\n                return;\n            }\n            window.idleFinalWarningAt = Date.now();\n            window.idleCloseCheckAt = window.idleFinalWarningAt;\n            window.idleIgnoreUntil = Date.now() + 8000;\n            if(window.addMsg) window.addMsg('ai', \"\u23f3 Se non ricevo istruzioni chiuder\u00f2 la sessione a breve.\");\n            window.speakText(\"Se non ricevo istruzioni, chiuder\u00f2 la sessione a breve per proteggere i tuoi dati.\");\n            clearTimeout(window.idleTimer3);\n            window.idleTimer3 = setTimeout(window.closeIdleSessionIfStillInactive, window.IDLE_CLOSE_AFTER_MS);\n        };\n        window.closeIdleSessionIfStillInactive = function() {\n            if (window.idleForceClosing || !window.isIdleSessionActive()) return;\n            const checkAt = window.idleCloseCheckAt || window.idleFinalWarningAt || window.idleFirstWarningAt || 0;\n            if ((window.idleLastUserActivityAt || 0) > checkAt) {\n                window.scheduleIdleFirstWarning();\n                return;\n            }\n            window.idleForceClosing = true;\n            if(window.addMsg) window.addMsg('ai', \"\ud83d\udd12 Sessione chiusa per inattivit\u00e0.\");\n            window.speakText(\"Sessione chiusa per inattivit\u00e0.\");\n            window.doLogout(true);\n        };\n        window.resetIdleTimers = function(evt) {\n            if (window.idleForceClosing) return;\n            if (evt && evt.isTrusted === false) return;\n            if (window.idleIgnoreUntil && Date.now() < window.idleIgnoreUntil) return;\n            window.idleLastUserActivityAt = Date.now();\n            window.idleFirstWarningAt = 0;\n            window.idleFinalWarningAt = 0;\n            window.idleCloseCheckAt = 0;\n            window.scheduleIdleFirstWarning();\n        };\n        ['mousemove', 'keydown', 'wheel', 'touchstart', 'click'].forEach(evt => {\n            document.addEventListener(evt, function(e){ window.resetIdleTimers(e); }, {passive: true});\n        });\n\n        window.placeMainHelpButtonRight = function() {\n            const row = document.querySelector('#ti-ai-outer .ti-actions-row');\n            const btn = gE('ti-help-btn');\n            if (!row || !btn) return;\n            if (btn.parentNode !== row || row.lastElementChild !== btn) row.appendChild(btn);\n        };\n        window.getPrivilegedActionsRow = function() {\n            let row = gE('ti-admin-actions-row');\n            const host = document.querySelector('#ti-ai-outer .ti-input-wrap');\n            if (!row && host) {\n                row = document.createElement('div');\n                row.id = 'ti-admin-actions-row';\n                row.className = 'ti-admin-actions-row';\n                row.setAttribute('aria-label', 'Comandi amministrativi');\n                host.appendChild(row);\n            }\n            return row;\n        };\n        window.refreshPrivilegedActionsRow = function() {\n            const row = window.getPrivilegedActionsRow ? window.getPrivilegedActionsRow() : gE('ti-admin-actions-row');\n            if (!row) return;\n            const ids = ['ti-conf-toggle', 'ti-activation-toggle', 'ti-purchase-toggle', 'ti-clean-orphans-btn', 'ti-global-action-toggle', 'ti-shared-setup-toggle'];\n            let visible = false;\n            ids.forEach(id => {\n                const b = gE(id);\n                if (b && b.parentNode !== row) row.appendChild(b);\n                if (b && b.style.display !== 'none') visible = true;\n            });\n            row.style.display = visible ? 'flex' : 'none';\n        };\n\n        window.tiGetConfigRow = function(data) {\n            data = data || window.currentDbData || {};\n            const tables = data && data.Tabelle ? data.Tabelle : {};\n            const keys = Object.keys(tables || {});\n            let k = keys.find(x => String(x).toLowerCase() === 'config') || keys.find(x => \/config\/i.test(String(x))) || 'Config';\n            const rows = tables[k];\n            return Array.isArray(rows) && rows[0] && typeof rows[0] === 'object' ? rows[0] : {};\n        };\n        window.tiCiField = function(row, names, def) {\n            row = row || {}; names = Array.isArray(names) ? names : [names];\n            const keys = Object.keys(row || {});\n            const norm = function(v){ return String(v || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').toLowerCase().replace(\/[^a-z0-9]\/g,''); };\n            for (const n of names) {\n                const wn = norm(n);\n                for (const k of keys) if (norm(k) === wn) return String(row[k] == null ? '' : row[k]).trim();\n            }\n            return def || '';\n        };\n        window.tiCompanyActivationState = function(data) {\n            const cfg = window.tiGetConfigRow(data);\n            const companyKey = window.tiCiField(cfg, ['ActiveID'], '');\n            const stato = window.tiCiField(cfg, ['Stato'], '');\n            const activeId = window.tiCiField(cfg, ['active_id','wc_activate_id','wc_activation_id'], '');\n            const monthlyId = window.tiCiField(cfg, ['canone_id','active_canone_id','wc_canone_order_id','wc_monthly_order_id','wc_subscription_id','subscription_id','abbonamento_id'], '');\n            const monthlyState = window.tiCiField(cfg, ['Stato canone','Canone stato','stato_canone','Abbonamento stato'], '');\n            const activationActive = \/^(attivo|operativo)$\/i.test(stato) && activeId !== '';\n            const monthlyActive = \/^operativo$\/i.test(stato) || \/^(attivo|operativo)$\/i.test(monthlyState);\n            return {companyKey, stato, activeId, monthlyId, monthlyState, activationActive, monthlyActive};\n        };\n        window.tiNormalizeShopServiceProductUrl = function(base, kind) {\n            kind = kind === 'monthly' ? 'monthly' : 'activation';\n            const fallback = kind === 'monthly' ? 'https:\/\/www.tisoft.it\/prodotto\/shop-service\/' : 'https:\/\/www.tisoft.it\/prodotto\/shop-service-attivazione\/';\n            base = String(base || '').trim() || fallback;\n            try {\n                const u = new URL(base, window.location.origin);\n                const host = String(u.hostname || '').toLowerCase();\n                const pathNorm = '\/' + String(u.pathname || '').replace(\/^\\\/+|\\\/+$\/g, '') + '\/';\n                if (host === 'www.tisoft.it' || host === 'tisoft.it') {\n                    if (kind === 'monthly' && (pathNorm === '\/shop-service\/' || pathNorm === '\/prodotto\/shop-service-canone-mensile\/')) {\n                        u.pathname = '\/prodotto\/shop-service\/';\n                    }\n                    if (kind === 'activation' && (pathNorm === '\/shop-service-attivazione\/' || pathNorm === '\/attivazione-shop-service\/')) {\n                        u.pathname = '\/prodotto\/shop-service-attivazione\/';\n                    }\n                }\n                return u.toString();\n            } catch(e) {\n                return fallback;\n            }\n        };\n        window.tiBuildShopServiceBuyUrl = function(kind, companyKey) {\n            const rawBase = kind === 'monthly' ? (window.tiShopServiceMonthlyProductUrl || 'https:\/\/www.tisoft.it\/prodotto\/shop-service\/') : (window.tiShopServiceActivationProductUrl || 'https:\/\/www.tisoft.it\/prodotto\/shop-service-attivazione\/');\n            const base = window.tiNormalizeShopServiceProductUrl(rawBase, kind);\n            try {\n                const url = new URL(base, window.location.origin);\n                url.searchParams.set('ss_company', companyKey || '');\n                url.searchParams.set('ss_service', kind === 'monthly' ? 'monthly' : 'activation');\n                return url.toString();\n            } catch(e) {\n                const sep = String(base).indexOf('?') === -1 ? '?' : '&';\n                return String(base) + sep + 'ss_company=' + encodeURIComponent(companyKey || '') + '&ss_service=' + (kind === 'monthly' ? 'monthly' : 'activation');\n            }\n        };\n        window.ensureShopServiceActivationModal = function() {\n            let ov = gE('ti-shopservice-activation-ov');\n            if (ov) return ov;\n            ov = document.createElement('div');\n            ov.id = 'ti-shopservice-activation-ov';\n            ov.className = 'ti-modal-overlay';\n            ov.style.display = 'none';\n            ov.style.zIndex = '99992';\n            ov.style.alignItems = 'center';\n            ov.style.justifyContent = 'center';\n            ov.innerHTML = `<div class=\"ti-modal\" style=\"width:min(520px,94vw);background:#0f172a;color:#fff;border-radius:16px;padding:18px;box-shadow:0 18px 60px rgba(0,0,0,.45);\">\n                <div style=\"display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px;\">\n                    <div><h2 style=\"margin:0;font-size:20px;\">Attiva Shop & Service<\/h2><div id=\"ti-shopservice-activation-sub\" style=\"font-size:13px;color:#cbd5e1;margin-top:5px;\"><\/div><\/div>\n                    <button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"window.closeShopServiceActivationPopup()\">X<\/button>\n                <\/div>\n                <div id=\"ti-shopservice-activation-body\" style=\"display:grid;gap:10px;margin-top:12px;\"><\/div>\n                <div style=\"font-size:12px;color:#cbd5e1;margin-top:14px;line-height:1.35;\">Eventuali variazioni del servizio\/canone saranno gestite dal profilo WooCommerce richiamando l ordine attivo.<\/div>\n            <\/div>`;\n            document.body.appendChild(ov);\n            return ov;\n        };\n        window.closeShopServiceActivationPopup = function() {\n            const ov = gE('ti-shopservice-activation-ov');\n            if (!ov) return;\n            if (window.closeModal) window.closeModal(ov); else ov.style.display = 'none';\n        };\n        window.openShopServiceActivationPopup = function() {\n            const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Seleziona prima una ditta.'); return; }\n            const openWithData = function(data) {\n                window.currentDbData = data || window.currentDbData || {};\n                const st = window.tiCompanyActivationState(window.currentDbData);\n                if (!st.companyKey) { window.tiAlert('Codice ActiveID ditta non disponibile. Riapri Configura o ricarica la ditta per generarlo.'); return; }\n                const ov = window.ensureShopServiceActivationModal();\n                const sub = gE('ti-shopservice-activation-sub');\n                const body = gE('ti-shopservice-activation-body');\n                if (sub) sub.textContent = 'Codice ditta: ' + st.companyKey;\n                const actions = [];\n                if (!st.activationActive) {\n                    actions.push('<a class=\"ti-btn\" style=\"display:block;text-align:center;background:#22c55e;color:#052e16;font-weight:800;padding:12px;border-radius:10px;text-decoration:none;\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"' + window.escapeHtml(window.tiBuildShopServiceBuyUrl('activation', st.companyKey)) + '\">Acquista attivazione Shop & Service<\/a>');\n                }\n                if (st.activationActive && !st.monthlyActive) {\n                    actions.push('<a class=\"ti-btn\" style=\"display:block;text-align:center;background:#38bdf8;color:#082f49;font-weight:800;padding:12px;border-radius:10px;text-decoration:none;\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"' + window.escapeHtml(window.tiBuildShopServiceBuyUrl('monthly', st.companyKey)) + '\">Acquista canone servizio Shop & Service<\/a>');\n                } else if (!st.activationActive) {\n                    actions.push('<a class=\"ti-btn\" style=\"display:block;text-align:center;background:#38bdf8;color:#082f49;font-weight:800;padding:12px;border-radius:10px;text-decoration:none;\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"' + window.escapeHtml(window.tiBuildShopServiceBuyUrl('monthly', st.companyKey)) + '\">Acquista canone servizio Shop & Service<\/a>');\n                }\n                if (!actions.length) {\n                    if (body) body.innerHTML = '<div style=\"padding:12px;border-radius:10px;background:#052e16;border:1px solid #16a34a;color:#bbf7d0;font-weight:700;\">Attivazione e canone risultano gi\u00e0 attivi. Non sono necessarie altre azioni.<\/div>';\n                } else if (body) body.innerHTML = actions.join('');\n                if (window.showPluginModal) window.showPluginModal(ov); else ov.style.display = 'flex';\n            };\n            if (window.currentDbData && window.currentDbData.Tabelle) { openWithData(window.currentDbData); return; }\n            const fd = new FormData(); fd.append('ti_action','ti_ai_get_db_data'); fd.append('db', dbVal);\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiShowTableReadWait:true, tiTableReadTitle:'Lettura stato attivazione', tiTableReadDetail:'Sto leggendo ActiveID, stato ditta e canone.', tiTableReadItem:dbVal}).then(function(res){\n                if (res && res.success) openWithData(res.data); else window.tiAlert((res && res.data && res.data.message) ? res.data.message : 'Errore lettura database.');\n            }).catch(function(err){ window.tiAlert('Errore lettura attivazione: ' + err.message); });\n        };\n        window.refreshShopServiceActivationButton = function() {\n            const btn = gE('ti-activation-toggle');\n            if (!btn) return;\n            const role = String(window.tiRole || '').toLowerCase();\n            const can = String(window.tiUser || '').toLowerCase() === 'ssglobaladmin' || role.includes('amministratore') || role.includes('configuratore') || role.includes('responsabile');\n            if (!can) { btn.style.display = 'none'; return; }\n            const st = window.tiCompanyActivationState(window.currentDbData || {});\n            btn.style.display = (st.activationActive && st.monthlyActive) ? 'none' : 'inline-block';\n        };\n\n\n        window.ensureDeleteCompanyModal = function() {\n            let ov = gE('ti-delete-company-ov');\n            if (ov) return ov;\n            ov = document.createElement('div');\n            ov.id = 'ti-delete-company-ov';\n            ov.className = 'ti-modal-overlay';\n            ov.style.display = 'none';\n            ov.style.zIndex = '99994';\n            ov.style.alignItems = 'center';\n            ov.style.justifyContent = 'center';\n            ov.innerHTML = `<div class=\"ti-modal\" style=\"width:min(560px,94vw);background:#111827;color:#fff;border-radius:16px;padding:18px;box-shadow:0 18px 60px rgba(0,0,0,.55);border:1px solid #dc2626;\">\n                <div style=\"display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px;\">\n                    <div><h2 style=\"margin:0;font-size:20px;color:#fecaca;\">Cancella ditta<\/h2><div id=\"ti-delete-company-sub\" style=\"font-size:13px;color:#e5e7eb;margin-top:5px;\"><\/div><\/div>\n                    <button type=\"button\" class=\"ti-btn\" style=\"background:#374151;color:#fff;\" onclick=\"window.closeDeleteCompanyPopup()\">X<\/button>\n                <\/div>\n                <div style=\"background:#7f1d1d;border:1px solid #ef4444;border-radius:12px;padding:12px;line-height:1.4;font-size:14px;\">\n                    <b>Avviso definitivo.<\/b><br>\n                    La ditta verra cancellata da Shop & Service ed eliminati tutti gli archivi finora trattati nelle path del plugin. Prima della cancellazione viene creato un ultimo backup ZIP nella path condivisa e inviato via email il link al file.\n                <\/div>\n                <div style=\"margin-top:12px;font-size:13px;color:#e5e7eb;\">Per confermare inserisci la password dell utente loggato e premi <b>OK, cancella ditta<\/b>.<\/div>\n                <input id=\"ti-delete-company-password\" type=\"password\" class=\"ti-in\" autocomplete=\"current-password\" placeholder=\"Password\" style=\"margin-top:10px;width:100%;box-sizing:border-box;\">\n                <div id=\"ti-delete-company-status\" style=\"margin-top:10px;font-size:13px;color:#fde68a;min-height:18px;\"><\/div>\n                <div style=\"display:flex;gap:8px;justify-content:flex-end;margin-top:14px;flex-wrap:wrap;\">\n                    <button type=\"button\" class=\"ti-btn\" style=\"background:#374151;color:#fff;\" onclick=\"window.closeDeleteCompanyPopup()\">Annulla<\/button>\n                    <button type=\"button\" id=\"ti-delete-company-confirm-btn\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;border:1px solid #991b1b;font-weight:800;\" onclick=\"window.confirmDeleteCompany()\">OK, cancella ditta<\/button>\n                <\/div>\n            <\/div>`;\n            document.body.appendChild(ov);\n            return ov;\n        };\n        window.openDeleteCompanyPopup = function() {\n            const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Seleziona prima la ditta da cancellare.'); return; }\n            const ov = window.ensureDeleteCompanyModal();\n            const sub = gE('ti-delete-company-sub');\n            const pwd = gE('ti-delete-company-password');\n            const st = gE('ti-delete-company-status');\n            if (sub) sub.textContent = 'Ditta\/database selezionato: ' + dbVal;\n            if (pwd) pwd.value = '';\n            if (st) st.textContent = '';\n            if (window.showPluginModal) window.showPluginModal(ov); else ov.style.display = 'flex';\n            setTimeout(function(){ if (pwd) pwd.focus(); }, 150);\n        };\n        window.closeDeleteCompanyPopup = function() {\n            const ov = gE('ti-delete-company-ov');\n            if (!ov) return;\n            if (window.closeModal) window.closeModal(ov); else ov.style.display = 'none';\n        };\n        window.finishDeleteCompanySuccess = function(data) {\n            const m = (data && data.message) ? data.message : 'Ditta cancellata definitivamente.';\n            const st = gE('ti-delete-company-status');\n            if (st) st.textContent = m;\n            window.tiAlert(m);\n            window.closeDeleteCompanyPopup();\n            try { if (gE('ti-ditta')) gE('ti-ditta').value = ''; } catch(e) {}\n            if (typeof window.loadDitte === 'function') window.loadDitte();\n            if (data && data.logout) setTimeout(function(){ window.location.reload(); }, 900);\n        };\n        window.retryDeleteCompanyWithEmailChoice = function(opts) {\n            opts = opts || {};\n            const dbVal = opts.db || (gE('ti-ditta') ? gE('ti-ditta').value : '');\n            const pwd = opts.password || (gE('ti-delete-company-password') ? gE('ti-delete-company-password').value : '');\n            const st = gE('ti-delete-company-status');\n            const btn = gE('ti-delete-company-confirm-btn');\n            if (!dbVal || !pwd) { window.tiAlert('Ditta o password non disponibili per continuare la cancellazione.'); return; }\n            if (btn) btn.disabled = true;\n            if (st) st.textContent = opts.skipEmail ? 'Proseguo senza invio email dopo conferma finale...' : 'Riprovo invio backup all indirizzo indicato...';\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_delete_company');\n            fd.append('db', dbVal);\n            fd.append('password', pwd);\n            fd.append('confirm', 'OK');\n            if (opts.email) fd.append('delete_email', opts.email);\n            if (opts.skipEmail) { fd.append('skip_email', '1'); fd.append('confirm_no_email', 'OK'); }\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(function(res){\n                if (btn) btn.disabled = false;\n                if (!res || !res.success) {\n                    const m = (res && res.data && res.data.message) ? res.data.message : 'Errore cancellazione ditta.';\n                    if (st) st.textContent = m;\n                    window.tiAlert(m);\n                    return;\n                }\n                const data = res.data || {};\n                if (data.needs_email_choice) { window.handleDeleteCompanyEmailChoice(data, dbVal, pwd); return; }\n                window.finishDeleteCompanySuccess(data);\n            }).catch(function(err){\n                if (btn) btn.disabled = false;\n                const m = 'Errore cancellazione ditta: ' + (err && err.message ? err.message : err);\n                if (st) st.textContent = m;\n                window.tiAlert(m);\n            });\n        };\n        window.handleDeleteCompanyEmailChoice = function(data, dbVal, pwd) {\n            const st = gE('ti-delete-company-status');\n            const msg = (data && data.message) ? data.message : 'Backup finale creato, ma email non inviata.';\n            if (st) st.textContent = msg;\n            const chooseEmail = window.confirm(msg + '\\n\\nPremi OK per indicare un altro indirizzo email.\\nPremi Annulla per proseguire senza invio email.');\n            if (chooseEmail) {\n                const email = window.prompt('Indica indirizzo email a cui inviare il backup finale ZIP:', (data && data.email) ? data.email : '');\n                if (!email) { if (st) st.textContent = 'Cancellazione sospesa. Nessuna email indicata.'; return; }\n                window.retryDeleteCompanyWithEmailChoice({db:dbVal, password:pwd, email:String(email).trim()});\n                return;\n            }\n            const finalConfirm = window.confirm('Confermi definitivamente di cancellare la ditta SENZA inviare via email il link al backup finale?\\n\\nIl backup ZIP e stato creato nello storage del plugin. Questa scelta elimina comunque database e archivi ditta, conservando la cartella Backups.');\n            if (!finalConfirm) { if (st) st.textContent = 'Cancellazione sospesa. Backup creato, ditta non cancellata.'; return; }\n            window.retryDeleteCompanyWithEmailChoice({db:dbVal, password:pwd, skipEmail:true});\n        };\n\n        window.confirmDeleteCompany = function() {\n            const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n            const pwdEl = gE('ti-delete-company-password');\n            const pwd = pwdEl ? pwdEl.value : '';\n            const st = gE('ti-delete-company-status');\n            const btn = gE('ti-delete-company-confirm-btn');\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Ditta non valida.'); return; }\n            if (!pwd) { if (st) st.textContent = 'Inserisci la password.'; return; }\n            const msg = 'Confermi definitivamente la cancellazione della ditta ' + dbVal + '?\\n\\nVerr\u00e0 creato un ultimo backup ZIP, inviato via email come link nella path condivisa, poi saranno eliminati database e cartelle archivio ditta conservando Backups.';\n            if (!window.confirm(msg)) return;\n            if (btn) btn.disabled = true;\n            if (st) st.textContent = 'Attendere: creo backup finale, invio email con link al backup e cancello gli archivi ditta...';\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_delete_company');\n            fd.append('db', dbVal);\n            fd.append('password', pwd);\n            fd.append('confirm', 'OK');\n            window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd}).then(function(res){\n                if (btn) btn.disabled = false;\n                if (!res || !res.success) {\n                    const m = (res && res.data && res.data.message) ? res.data.message : 'Errore cancellazione ditta.';\n                    if (st) st.textContent = m;\n                    window.tiAlert(m);\n                    return;\n                }\n                const data = res.data || {};\n                if (data.needs_email_choice) { window.handleDeleteCompanyEmailChoice(data, dbVal, pwd); return; }\n                window.finishDeleteCompanySuccess(data);\n            }).catch(function(err){\n                if (btn) btn.disabled = false;\n                const m = 'Errore cancellazione ditta: ' + (err && err.message ? err.message : err);\n                if (st) st.textContent = m;\n                window.tiAlert(m);\n            });\n        };\n\n        window.isPurchaseRoleEnabled = function(role, user) {\n            const r = String(role || '').toLowerCase();\n            const u = String(user || window.tiUser || '').toLowerCase();\n            const hasPurchaseRole = r.includes('acquisizioni') || r.includes('buyer') || r.includes('procurement');\n            return u === 'ssglobaladmin'\n                || r.includes('amministratore')\n                || r.includes('configuratore')\n                || r.includes('responsabile')\n                || hasPurchaseRole;\n        };\n\n        window.setAdminUI = function(user, role) {\n            const uName = gE('ti-user-name'); const uRole = gE('ti-user-role'); const uArea = gE('ti-user-area');\n            if(uName) uName.textContent = (user || ''); if(uRole) uRole.textContent = role ? (' - ' + role) : ''; if(uArea) uArea.style.display = 'flex';\n            if (window.tiCloseDirectLoginFallback) window.tiCloseDirectLoginFallback();\n            if (window.closeModal) window.closeModal('ti-login-ov');\n            gE('ti-login-btn').style.display = 'none'; gE('ti-logout-btn').style.display = 'inline-block';\n            if (typeof window.ensureStandardMainFormForCurrentUser === 'function') window.ensureStandardMainFormForCurrentUser();\n            const roleLower = String(role || '').toLowerCase();\n            const canOpenConfig = String(window.tiUser || user || '').toLowerCase() === 'ssglobaladmin' || roleLower.includes('amministratore') || roleLower.includes('configuratore') || roleLower.includes('responsabile');\n            const canOpenPurchases = window.isPurchaseRoleEnabled(role, window.tiUser || user);\n            if(canOpenConfig) {\n                let confBtn = gE('ti-conf-toggle');\n                if(!confBtn) { \n                    confBtn = document.createElement('button'); confBtn.type=\"button\"; confBtn.id = 'ti-conf-toggle'; confBtn.className = 'ti-btn ti-btn-conf-toggle'; confBtn.innerHTML = '\u2699\ufe0f <b>CONFIGURA<\/b>'; confBtn.style.background = '#10b981'; confBtn.style.color = '#ffffff'; confBtn.style.border = '1px solid #047857'; const adminRow = window.getPrivilegedActionsRow(); if (adminRow) adminRow.appendChild(confBtn); \n                }\n                confBtn.style.display = 'inline-block';\n                confBtn.onclick = () => {\n                    const fd = new FormData(); fd.append('ti_action', 'ti_ai_get_db_data'); fd.append('db', gE('ti-ditta').value);\n                    window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiShowTableReadWait:true, tiTableReadTitle:'\u23f3 Lettura tabelle configurazione', tiTableReadDetail:'Sto leggendo tutte le tabelle della ditta prima di aprire la configurazione.', tiTableReadItem:(gE('ti-ditta') ? gE('ti-ditta').value : '')}).then(d=>{ if(d.success) { window.currentDbData = d.data; window.configOriginalDbData = JSON.parse(JSON.stringify(d.data || {})); window.configWasSaved = false; window.tiConfigDeletedRowsManifest = []; if(window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); if(window.refreshShopServiceActivationButton) window.refreshShopServiceActivationButton(); window.renderConfig(); window.openModal('ti-conf-ov'); } });\n                };\n\n                let actBtn = gE('ti-activation-toggle');\n                if (!actBtn) {\n                    actBtn = document.createElement('button');\n                    actBtn.type = 'button';\n                    actBtn.id = 'ti-activation-toggle';\n                    actBtn.className = 'ti-btn';\n                    actBtn.innerHTML = '\u26a1 <b>Attiva Shop & Service<\/b>';\n                    actBtn.title = 'Acquista attivazione o canone mensile Shop & Service tramite WooCommerce';\n                    actBtn.style.background = '#f59e0b';\n                    actBtn.style.color = '#111827';\n                    actBtn.style.border = '1px solid #b45309';\n                    const adminRow = window.getPrivilegedActionsRow();\n                    if (adminRow) adminRow.appendChild(actBtn);\n                }\n                actBtn.style.display = 'inline-block';\n                actBtn.onclick = () => window.openShopServiceActivationPopup();\n                if (window.refreshShopServiceActivationButton) window.refreshShopServiceActivationButton();\n\n                let purchBtn = gE('ti-purchase-toggle');\n                if(canOpenPurchases) {\n                    if(!purchBtn) {\n                        purchBtn = document.createElement('button');\n                        purchBtn.type = 'button';\n                        purchBtn.id = 'ti-purchase-toggle';\n                        purchBtn.className = 'ti-btn';\n                        purchBtn.innerHTML = '\ud83d\uded2 <b>ACQUISIZIONI<\/b>';\n                        purchBtn.title = 'Acquisizioni - disponibile per amministratore, configuratore, responsabile, ruolo acquisizioni o SSGlobalAdmin';\n                        purchBtn.style.background = '#0ea5e9';\n                        purchBtn.style.color = '#fff';\n                        purchBtn.style.border = '1px solid #0369a1';\n                        const adminRow = window.getPrivilegedActionsRow();\n                        if (adminRow) adminRow.appendChild(purchBtn);\n                    }\n                    purchBtn.style.display = 'inline-block';\n                    purchBtn.onclick = () => window.openPurchaseManualFlow();\n                } else if (purchBtn) {\n                    purchBtn.style.display = 'none';\n                    purchBtn.onclick = null;\n                }\n                let cleanBtn = gE('ti-clean-orphans-btn');\n                if (window.canRunLocalOrphanCleanup && window.canRunLocalOrphanCleanup()) {\n                    if(!cleanBtn) {\n                        cleanBtn = document.createElement('button');\n                        cleanBtn.type = 'button';\n                        cleanBtn.id = 'ti-clean-orphans-btn';\n                        cleanBtn.className = 'ti-btn';\n                        cleanBtn.innerHTML = '\ud83e\uddf9 <b>Pulisci file non associati<\/b>';\n                        cleanBtn.title = 'Elimina fisicamente dallo storage i file non associati alla ditta selezionata';\n                        cleanBtn.style.background = '#475569';\n                        cleanBtn.style.color = '#fff';\n                        cleanBtn.style.border = '1px solid #334155';\n                        cleanBtn.style.fontSize = '11px';\n                        const adminRow = window.getPrivilegedActionsRow();\n                        if (adminRow) adminRow.appendChild(cleanBtn);\n                    }\n                    cleanBtn.style.display = 'inline-block';\n                    cleanBtn.onclick = () => window.runOrphanFileCleanup();\n                } else if (cleanBtn) {\n                    cleanBtn.style.display = 'none';\n                    cleanBtn.onclick = null;\n                }\n                let delCompanyBtn = gE('ti-delete-company-toggle');\n                const canDeleteCompany = String(window.tiUser || user || '').toLowerCase() === 'ssglobaladmin' || roleLower.includes('amministratore') || roleLower.includes('configuratore');\n                if (canDeleteCompany) {\n                    if (!delCompanyBtn) {\n                        delCompanyBtn = document.createElement('button');\n                        delCompanyBtn.type = 'button';\n                        delCompanyBtn.id = 'ti-delete-company-toggle';\n                        delCompanyBtn.className = 'ti-btn';\n                        delCompanyBtn.innerHTML = '\ud83d\uddd1 <b>Cancella ditta<\/b>';\n                        delCompanyBtn.title = 'Cancellazione definitiva della ditta con backup finale e conferma password';\n                        delCompanyBtn.style.background = '#dc2626';\n                        delCompanyBtn.style.color = '#fff';\n                        delCompanyBtn.style.border = '1px solid #991b1b';\n                        delCompanyBtn.style.fontSize = '11px';\n                        const adminRow = window.getPrivilegedActionsRow();\n                        if (adminRow) adminRow.appendChild(delCompanyBtn);\n                    }\n                    delCompanyBtn.style.display = 'inline-block';\n                    delCompanyBtn.onclick = () => window.openDeleteCompanyPopup();\n                } else if (delCompanyBtn) {\n                    delCompanyBtn.style.display = 'none';\n                    delCompanyBtn.onclick = null;\n                }\n            } else {\n                const confBtnOff = gE('ti-conf-toggle');\n                if (confBtnOff) confBtnOff.style.display = 'none';\n                const actBtnOff = gE('ti-activation-toggle');\n                if (actBtnOff) { actBtnOff.style.display = 'none'; actBtnOff.onclick = null; }\n                const purchBtnOff = gE('ti-purchase-toggle');\n                if (purchBtnOff) { purchBtnOff.style.display = 'none'; purchBtnOff.onclick = null; }\n                const cleanBtnOff = gE('ti-clean-orphans-btn');\n                if (cleanBtnOff) { cleanBtnOff.style.display = 'none'; cleanBtnOff.onclick = null; }\n                const delCompanyBtnOff = gE('ti-delete-company-toggle');\n                if (delCompanyBtnOff) { delCompanyBtnOff.style.display = 'none'; delCompanyBtnOff.onclick = null; }\n            }\n            if (window.tiUser === 'SSGlobalAdmin') {\n                let gb = gE('ti-global-action-toggle');\n                if(!gb) {\n                    gb = document.createElement('button'); gb.type=\"button\"; gb.id='ti-global-action-toggle'; gb.className='ti-btn'; gb.innerHTML='\ud83e\ude84 <b>GLOBALE<\/b>'; gb.style.background='#8b5cf6'; gb.style.color='#fff'; gb.style.border='1px solid #5b21b6'; const adminRow = window.getPrivilegedActionsRow(); if (adminRow) adminRow.appendChild(gb);\n                }\n                gb.style.display = 'inline-block';\n                gb.onclick = () => window.openModal('ti-global-action-ov');\n\n                let sb = gE('ti-shared-setup-toggle');\n                if(!sb) {\n                    sb = document.createElement('button'); sb.type='button'; sb.id='ti-shared-setup-toggle'; sb.className='ti-btn'; sb.innerHTML='\ud83d\uddc2 <b>STORAGE<\/b>'; sb.style.background='#a16207'; sb.style.color='#fff'; sb.style.border='1px solid #78350f'; const adminRow = window.getPrivilegedActionsRow(); if (adminRow) adminRow.appendChild(sb);\n                }\n                sb.style.display = 'inline-block';\n                sb.onclick = () => window.openSharedSetupModal();\n\n                let gclean = gE('ti-clean-orphans-global-btn');\n                if (gclean) {\n                    gclean.style.display = 'block';\n                    gclean.onclick = () => window.runGlobalOrphanFileCleanup();\n                }\n            } else {\n                const gcleanOff = gE('ti-clean-orphans-global-btn');\n                if (gcleanOff) { gcleanOff.style.display = 'none'; gcleanOff.onclick = null; }\n            }\n            if (window.refreshOrphanCleanupButton) window.refreshOrphanCleanupButton();\n            window.refreshPrivilegedActionsRow();\n            window.placeMainHelpButtonRight();\n            try { if (window.updateActivitiesButtonVisibility) window.updateActivitiesButtonVisibility(); } catch(e) {}\n            try { if (window.tiCleanEscapedUiInSubtree) window.tiCleanEscapedUiInSubtree(window.getPrivilegedActionsRow ? window.getPrivilegedActionsRow() : document.body); if (window.tiI18nTranslateSubtree) window.tiI18nTranslateSubtree(window.getPrivilegedActionsRow ? window.getPrivilegedActionsRow() : document.body); } catch(e) {}\n        };\n\n        if(window.tiUser) { window.setAdminUI(window.tiUserDisplay || window.tiUser, window.tiRole); if (typeof window.ensureStandardMainFormForCurrentUser === 'function') window.ensureStandardMainFormForCurrentUser(); if (window.updateActivitiesButtonVisibility) window.updateActivitiesButtonVisibility(); } else { const uName = gE('ti-user-name'); const uRole = gE('ti-user-role'); const uArea = gE('ti-user-area'); if (uName) uName.textContent = 'Cliente\/Utente'; if (uRole) uRole.textContent = ''; if (uArea) uArea.style.display = 'flex'; ['ti-conf-toggle','ti-activation-toggle','ti-purchase-toggle','ti-clean-orphans-btn','ti-delete-company-toggle','ti-clean-orphans-global-btn','ti-global-action-toggle','ti-shared-setup-toggle'].forEach(function(id){ const b=gE(id); if(b){ b.style.display='none'; b.onclick=null; } }); const adminRow = gE('ti-admin-actions-row'); if (adminRow) adminRow.style.display = 'none'; }\n\n        window.isStructuredListinoFileName = function(fileName) {\n            const nm = String(fileName || '').toLowerCase().trim();\n            return \/\\.(txt|csv|tsv|xlsx|pdf)$\/i.test(nm);\n        };\n\n\n\n        window.ensurePurchaseModal = function() {\n            let ov = gE('ti-purchase-ov');\n            if (ov) return ov;\n            ov = document.createElement('div');\n            ov.id = 'ti-purchase-ov';\n            ov.className = 'ti-modal-overlay';\n            ov.style.display = 'none';\n            ov.style.zIndex = '99990';\n            ov.style.alignItems = 'center';\n            ov.style.justifyContent = 'center';\n            ov.innerHTML = `\n                <div class=\"ti-modal ti-purchase-modal\" style=\"width:min(1460px,98vw);max-width:98vw;max-height:92vh;overflow:auto;background:#0f172a;color:#fff;border-radius:16px;padding:18px;box-shadow:0 18px 60px rgba(0,0,0,.45);margin:0 auto;\">\n                    <div style=\"display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;\">\n                        <div>\n                            <h2 style=\"margin:0;font-size:22px;\">Acquisizioni<\/h2>\n                            <div style=\"font-size:14px;color:#cbd5e1;margin-top:4px;\">Inserisci manualmente le righe nella tabella ordine acquisizione. L'import file resta disponibile come funzione aggiuntiva.<\/div>\n                        <\/div>\n                        <button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"window.closePurchaseManualFlow()\">X<\/button>\n                    <\/div>\n                    <div style=\"display:flex;gap:8px;flex-wrap:wrap;margin:10px 0 14px 0;\">\n                        <button type=\"button\" class=\"ti-btn\" style=\"background:#22c55e;color:#052e16;font-weight:700;\" onclick=\"window.addPurchaseManualRow()\">+ Aggiungi riga manuale<\/button>\n                        <button type=\"button\" class=\"ti-btn\" style=\"background:#f59e0b;color:#111827;font-weight:700;\" onclick=\"window.pickPurchaseImportFile()\">Import file acquisizioni<\/button>\n                        <button type=\"button\" class=\"ti-btn\" style=\"background:#38bdf8;color:#082f49;font-weight:700;\" onclick=\"window.purchaseSendToChat()\">Invia righe acquisizione alla chat<\/button>\n                    <\/div>\n                    <div style=\"border:1px solid #334155;background:#111827;border-radius:12px;padding:10px;margin-bottom:12px;\">\n                        <label style=\"display:block;font-weight:700;margin-bottom:6px;\">Ricerca prodotti e servizi gi\u00e0 presenti nel database<\/label>\n                        <input id=\"ti-purchase-search\" type=\"text\" placeholder=\"Cerca prodotto o servizio...\" style=\"width:100%;box-sizing:border-box;padding:10px;border-radius:10px;border:1px solid #475569;background:#020617;color:#fff;font-size:15px;\" oninput=\"window.searchPurchaseCatalog(this.value)\">\n                        <div id=\"ti-purchase-search-results\" style=\"margin-top:8px;display:grid;gap:6px;\"><\/div>\n                    <\/div>\n                    <div style=\"overflow:auto;border:1px solid #334155;border-radius:12px;\">\n                        <table id=\"ti-purchase-table\" style=\"width:100%;border-collapse:collapse;min-width:1120px;background:#020617;\">\n                            <thead>\n                                <tr style=\"background:#1e293b;\">\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Voce<\/th>\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Tipo<\/th>\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Q.t\u00e0<\/th>\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Costo acquisizione<\/th>\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Prezzo vendita<\/th>\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">IVA<\/th>\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Note<\/th>\n                                    <th style=\"padding:8px;border-bottom:1px solid #334155;\">Azioni<\/th>\n                                <\/tr>\n                            <\/thead>\n                            <tbody id=\"ti-purchase-body\"><\/tbody>\n                        <\/table>\n                    <\/div>\n                    <div id=\"ti-purchase-total\" style=\"margin-top:10px;text-align:right;font-weight:700;color:#bbf7d0;\"><\/div>\n                    <div style=\"display:flex;justify-content:center;gap:8px;margin-top:14px;\">\n                        <button type=\"button\" class=\"ti-btn\" style=\"background:#38bdf8;color:#082f49;font-weight:700;\" onclick=\"window.purchaseSendToChat()\">Conferma righe in chat<\/button>\n                        <button type=\"button\" class=\"ti-btn btn-close\" onclick=\"window.closePurchaseManualFlow()\">Chiudi<\/button>\n                    <\/div>\n                <\/div>`;\n            document.body.appendChild(ov);\n            return ov;\n        };\n\n        window.purchaseGetCatalogItems = function() {\n            const data = window.currentDbData || {};\n            const tables = (data && data.Tabelle) ? data.Tabelle : {};\n            const out = [];\n            Object.keys(tables).forEach(function(tn){\n                if (!\/(prodott|serviz)\/i.test(tn)) return;\n                const rows = Array.isArray(tables[tn]) ? tables[tn] : [];\n                rows.forEach(function(r, idx){\n                    if (!r || typeof r !== 'object') return;\n                    const isServ = \/(serviz)\/i.test(tn);\n                    const name = String(r.Prodotto || r.Servizio || r.Nome || r.Titolo || r.Descrizione || '').trim();\n                    if (!name) return;\n                    out.push({\n                        table: tn,\n                        index: idx,\n                        type: isServ ? 'S' : 'P',\n                        name: name,\n                        desc: String(r.Descrizione || r.Note || '').trim(),\n                        cost: parseFloat(String(r.Costo || r.Costo_acquisto || r.Prezzo || '0').replace(',','.')) || 0,\n                        sale: parseFloat(String(r.Prezzo || r.Prezzo_vendita || '0').replace(',','.')) || 0,\n                        iva: parseFloat(String(r.IVA_VAT || r.IVA || '22').replace(',','.')) || 22\n                    });\n                });\n            });\n            return out;\n        };\n\n        window.openPurchaseManualFlow = function() {\n            if (!window.isPurchaseRoleEnabled || !window.isPurchaseRoleEnabled(window.tiRole, window.tiUser)) {\n                window.tiAlert('Funzione Acquisizioni non disponibile con il profilo corrente.');\n                return;\n            }\n            const dbSel = gE('ti-ditta');\n            const dbVal = dbSel ? dbSel.value : '';\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Seleziona prima una ditta.'); return; }\n            const openIt = function(){\n                const ov = window.ensurePurchaseModal();\n                if (window.showPluginModal) window.showPluginModal(ov); else ov.style.display = 'flex';\n                if (!gE('ti-purchase-body') || !gE('ti-purchase-body').children.length) window.addPurchaseManualRow();\n                window.refreshPurchaseTotals();\n            };\n            if (!window.currentDbData || !window.currentDbData.Tabelle) {\n                const fd = new FormData();\n                fd.append('ti_action', 'ti_ai_get_db_data');\n                fd.append('db', dbVal);\n                window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiShowTableReadWait:true, tiTableReadTitle:'\u23f3 Lettura prodotti e servizi', tiTableReadDetail:'Sto leggendo il database per preparare acquisizioni e ricerca.', tiTableReadItem:dbVal}).then(function(res){\n                    if (res && res.success) { window.currentDbData = res.data; openIt(); }\n                    else window.tiAlert((res && res.data && res.data.message) ? res.data.message : 'Errore lettura database.');\n                }).catch(function(err){ window.tiAlert('Errore lettura database: ' + err.message); });\n            } else {\n                openIt();\n            }\n        };\n\n        window.closePurchaseManualFlow = function() {\n            const ov = gE('ti-purchase-ov');\n            if (!ov) return;\n            if (window.closeModal) window.closeModal(ov); else ov.style.display = 'none';\n        };\n\n        window.addPurchaseManualRow = function(item) {\n            const body = gE('ti-purchase-body');\n            if (!body) return;\n            const tr = document.createElement('tr');\n            tr.className = 'ti-purchase-row';\n            const safe = item || {};\n            tr.innerHTML = `\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-name\" value=\"${window.escapeHtml(safe.name || '')}\" placeholder=\"Prodotto o servizio\" style=\"width:100%;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><select class=\"ti-pur-type\" style=\"width:70px;padding:8px;border-radius:8px;background:#0f172a;color:#fff;border:1px solid #334155;\"><option value=\"P\" ${safe.type === 'S' ? '' : 'selected'}>P<\/option><option value=\"S\" ${safe.type === 'S' ? 'selected' : ''}>S<\/option><\/select><\/td>\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-qty\" value=\"${safe.qty || '1'}\" inputmode=\"decimal\" style=\"width:80px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\" oninput=\"window.refreshPurchaseTotals()\"><\/td>\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-cost\" value=\"${safe.cost ? window.fmtNum(safe.cost) : ''}\" inputmode=\"decimal\" style=\"width:120px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\" oninput=\"window.refreshPurchaseTotals()\"><\/td>\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-sale\" value=\"${safe.sale ? window.fmtNum(safe.sale) : ''}\" inputmode=\"decimal\" style=\"width:120px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-iva\" value=\"${safe.iva || '22'}\" inputmode=\"decimal\" style=\"width:70px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-note\" value=\"${window.escapeHtml(safe.desc || '')}\" placeholder=\"Note\" style=\"width:100%;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:center;\"><button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"this.closest('tr').remove();window.refreshPurchaseTotals();\">X<\/button><\/td>`;\n            body.appendChild(tr);\n            window.refreshPurchaseTotals();\n        };\n\n        window.searchPurchaseCatalog = function(q) {\n            const box = gE('ti-purchase-search-results');\n            if (!box) return;\n            const query = String(q || '').trim().toLowerCase();\n            if (!query) { box.innerHTML = ''; return; }\n            const items = window.purchaseGetCatalogItems().filter(function(it){\n                return (it.name + ' ' + it.desc + ' ' + it.table).toLowerCase().indexOf(query) !== -1;\n            }).slice(0, 12);\n            if (!items.length) {\n                box.innerHTML = '<div style=\"color:#fca5a5;font-size:13px;\">Nessun prodotto o servizio trovato nel database.<\/div>';\n                return;\n            }\n            box.innerHTML = items.map(function(it, idx){\n                return `<button type=\"button\" class=\"ti-btn\" style=\"text-align:left;background:#1e293b;color:#fff;border:1px solid #334155;\" onclick=\"window.addPurchaseManualRow(window.purchaseSearchCache[${idx}])\"><b>${it.type} - ${window.escapeHtml(it.name)}<\/b><br><span style=\"font-size:12px;color:#cbd5e1;\">${window.escapeHtml(it.table)} \u00b7 costo ${window.fmtNum(it.cost)} \u00b7 prezzo vendita ${window.fmtNum(it.sale)}<\/span><\/button>`;\n            }).join('');\n            window.purchaseSearchCache = items;\n        };\n\n        window.refreshPurchaseTotals = function() {\n            let total = 0;\n            document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                const qty = window.parseQtyInput ? window.parseQtyInput((tr.querySelector('.ti-pur-qty') || {}).value || '0') : (parseFloat(String((tr.querySelector('.ti-pur-qty') || {}).value || '0').replace(',','.')) || 0);\n                const cost = parseFloat(String((tr.querySelector('.ti-pur-cost') || {}).value || '0').replace(\/\\.\/g,'').replace(',','.')) || 0;\n                total += qty * cost;\n            });\n            const out = gE('ti-purchase-total');\n            if (out) out.innerHTML = 'Totale acquisizione indicativo: \u20ac ' + window.fmtNum(total);\n        };\n\n        window.purchaseCollectRows = function() {\n            const rows = [];\n            document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                const name = String((tr.querySelector('.ti-pur-name') || {}).value || '').trim();\n                if (!name) return;\n                rows.push({\n                    type: String((tr.querySelector('.ti-pur-type') || {}).value || 'P'),\n                    name: name,\n                    qty: String((tr.querySelector('.ti-pur-qty') || {}).value || '1').trim(),\n                    cost: String((tr.querySelector('.ti-pur-cost') || {}).value || '').trim(),\n                    sale: String((tr.querySelector('.ti-pur-sale') || {}).value || '').trim(),\n                    iva: String((tr.querySelector('.ti-pur-iva') || {}).value || '22').trim(),\n                    note: String((tr.querySelector('.ti-pur-note') || {}).value || '').trim()\n                });\n            });\n            return rows;\n        };\n\n        window.purchaseSendToChat = function() {\n            const rows = window.purchaseCollectRows();\n            if (!rows.length) { window.tiAlert('Inserisci almeno una riga acquisizione.'); return; }\n            let txt = 'ACQUISTO - righe inserite manualmente\\\\n';\n            txt += 'Usa queste righe come tabella ordine acquisizione. Il campo prezzo rappresenta il costo di acquisizione; se presente riporta anche il prezzo di vendita nel prodotto.\\\\n\\\\n';\n            rows.forEach(function(r, i){\n                txt += (i+1) + '. ' + r.type + ' - ' + r.name + ' | Quantita: ' + r.qty + ' | Costo acquisizione: ' + r.cost + ' | Prezzo vendita: ' + r.sale + ' | IVA: ' + r.iva + (r.note ? ' | Note: ' + r.note : '') + '\\n';\n            });\n            window.closePurchaseManualFlow();\n            if (window.addMsg) window.addMsg('user', txt);\n            const msg = gE('ti-msg');\n            if (msg) {\n                msg.value = txt;\n                if (gE('ti-send')) gE('ti-send').click();\n            }\n        };\n\n        window.pickPurchaseImportFile = function() {\n            window.targetUploadContext = 'purchase-import-file';\n            if (window.tiOpenDirectUpload616) return window.tiOpenDirectUpload616('purchase-import-file');\n            return false;\n        };\n\n        window.startPurchaseFileImport = function(fileUrl, fileName) {\n            const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Seleziona una ditta prima di importare acquisizioni.'); return; }\n            const prompt = 'Import acquisizioni da file. Prima usa la tabella ordine acquisizione per anteprima e modifica manuale delle righe. Interpreta il file come acquisizione: il campo Prezzo rappresenta il costo di acquisizione; se presente un prezzo di vendita riportalo nel prodotto. Rendere disponibili ricerche su prodotti o servizi gi\u00e0 presenti.';\n            window.rememberLastFileContext(fileUrl, prompt, true);\n            window.lastUploadedFile = null;\n            if (window.addMsg) window.addMsg('user', prompt);\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_config_action');\n            fd.append('db', dbVal);\n            fd.append('mode', 'ai_ops');\n            fd.append('prompt', prompt);\n            fd.append('file_url', fileUrl);\n            let waitBubble = null;\n            try {\n                waitBubble = document.createElement('div');\n                waitBubble.className = 'ti-bubble ai';\n                waitBubble.innerHTML = window.getUserWaitingHtml('Import acquisizioni in corso...', 'Sto leggendo il file e preparo la tabella ordine acquisizione modificabile.');\n                if (dom.chat) { dom.chat.appendChild(waitBubble); dom.chat.scrollTop = dom.chat.scrollHeight; }\n            } catch(e) {}\n            window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                if (res && res.success) {\n                    const rawReply = ((res.data || {}).reply || '').trim();\n                    const cleanReply = rawReply.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                    window.addMsg('ai', window.formatTable(cleanReply || 'Import acquisizioni pronto.'), cleanReply || 'Import acquisizioni pronto.');\n                } else {\n                    window.addMsg('ai', '\u26a0\ufe0f ' + ((res && res.data && res.data.message) ? res.data.message : 'Import acquisizioni non riuscito'));\n                }\n            }).catch(function(err){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                window.addMsg('ai', '\u26a0\ufe0f Errore import acquisizioni: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n            });\n        };\n\n\n        window.startAdminGenericFileAI = function(fileUrl, fileName) {\n            const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n            const prompt = 'Ho allegato un file che pu\u00f2 essere PDF con testo e immagini oppure un file tecnico. Analizza solo il testo leggibile o estraibile automaticamente; non inventare contenuti da immagini, scansioni o parti binarie non leggibili. Riporta una sintesi comprensibile e, se il contenuto \u00e8 parziale, chiedimi di precisarne il contenuto o cosa devo farne prima di modificare il database.';\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Seleziona una ditta prima di analizzare il file.'); return; }\n            window.rememberLastFileContext(fileUrl, prompt, false);\n            window.lastUploadedFile = null;\n            if (window.addMsg) window.addMsg('user', prompt);\n\n            let waitBubble = null;\n            try {\n                waitBubble = document.createElement('div');\n                waitBubble.className = 'ti-bubble ai';\n                waitBubble.innerHTML = window.getUserWaitingHtml('Analisi file in corso...', 'Sto leggendo il file allegato e preparo la risposta.');\n                if (dom.chat) { dom.chat.appendChild(waitBubble); dom.chat.scrollTop = dom.chat.scrollHeight; }\n            } catch(e) {}\n\n            let chatHist = [];\n            try {\n                const bubbles = dom.chat ? dom.chat.querySelectorAll('.ti-bubble') : [];\n                const startIdx = Math.max(0, (bubbles ? bubbles.length : 0) - 8);\n                for (let i = startIdx; i < (bubbles ? bubbles.length : 0); i++) {\n                    const bubble = bubbles[i];\n                    const role = bubble.classList.contains('user') ? 'user' : 'assistant';\n                    const clone = bubble.cloneNode(true);\n                    const tw = clone.querySelector('.ti-table-wrap'); if (tw) tw.remove();\n                    const msgTxt = (clone.innerText || '').trim();\n                    if (msgTxt && !msgTxt.includes('Fammi pensare...')) chatHist.push({role: role, content: msgTxt});\n                }\n            } catch(e) {}\n\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_chat_start');\n            fd.append('db', dbVal);\n            fd.append('analyze_file_only', '1');\n            fd.append('text', prompt + ` [FILE_ALLEGATO: ${fileUrl}]`);\n            fd.append('history_context', JSON.stringify(chatHist));\n            fd.append('analyze_file_only', '1');\n            fd.append('analyze_file_only', '1');\n\n            window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                if (res && res.success) {\n                    const rawReply = ((res.data || {}).reply || '').trim();\n                    const cleanReply = rawReply.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                    window.addMsg('ai', window.formatTable(cleanReply || 'Analisi completata.'), cleanReply || 'Analisi completata.');\n                    try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(true, 'Analisi file completata', {notifyUser:true, successMessage:'Analisi file completata. Risultato disponibile nella chat AI.'}); } catch(eProgressOk792) {}\n                    if (res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); }, 1200);\n                } else {\n                    const msg = (res && res.data && res.data.message) ? res.data.message : 'Analisi file non riuscita';\n                    window.addMsg('ai', '\u26a0\ufe0f ' + msg);\n                }\n            }).catch(function(err){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                window.addMsg('ai', '\u26a0\ufe0f Errore analisi file: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n            });\n        };\n\n        window.askAdminUploadedFileAction = function(fileUrl, fileName) {\n            const looksStructured = window.isStructuredListinoFileName(fileName || '');\n            if (!looksStructured) {\n                window.startAdminGenericFileAI(fileUrl, fileName);\n                return;\n            }\n            const msg = `Il file caricato pu\u00f2 essere trattato come <b>listino<\/b> da importare nel database oppure come <b>documento generico<\/b> da leggere con la AI.<br><br>Premi <b>IMPORTA LISTINO<\/b> per creare l'anteprima di import.<br>Premi <b>LEGGI FILE<\/b> per trattarlo come file utente e decidere dopo cosa farne.`;\n            window.tiConfirmYesNoAction(msg,\n                function(){ window.startAdminListinoImport(fileUrl, fileName); },\n                function(){ window.startAdminGenericFileAI(fileUrl, fileName); },\n                'IMPORTA LISTINO',\n                'LEGGI FILE'\n            );\n        };\n\n        window.startAdminListinoImport = function(fileUrl, fileName) {\n            const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Seleziona una ditta prima di importare il listino.'); return; }\n            const prompt = 'Tratta il file allegato come listino da importare nel database della ditta, preferibilmente per creare prodotti. Se il file e PDF, ricerca le righe utili, proponi una prima struttura di import prodotti modificabile e, se trovi immagini disponibili per le righe, chiedi se collegarle ai singoli prodotti. Mostra sempre l anteprima finale prima del salvataggio.';\n            window.rememberLastFileContext(fileUrl, prompt, true);\n            window.lastUploadedFile = null;\n            if (window.addMsg) window.addMsg('user', prompt);\n\n            let waitBubble = null;\n            try {\n                waitBubble = document.createElement('div');\n                waitBubble.className = 'ti-bubble ai';\n                waitBubble.innerHTML = window.getUserWaitingHtml('Avvio import listino in corso...', 'Sto interpretando il listino e preparo l anteprima.');\n                if (dom.chat) { dom.chat.appendChild(waitBubble); dom.chat.scrollTop = dom.chat.scrollHeight; }\n            } catch(e) {}\n\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_config_action');\n            fd.append('db', dbVal);\n            fd.append('mode', 'ai_ops');\n            fd.append('prompt', prompt);\n            fd.append('file_url', fileUrl);\n\n            window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                if (res && res.success) {\n                    const rawReply = ((res.data || {}).reply || '').trim();\n                    const noVoice = !!((res.data || {}).no_voice) || rawReply.indexOf('[NO_VOICE]') !== -1;\n                    const cleanReply = rawReply.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                    window.addMsg('ai', window.formatTable(cleanReply || 'Import pronto.'), cleanReply || 'Import pronto.');\n                    if (!noVoice && cleanReply) window.speakText(cleanReply);\n                    if (res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); }, 1200);\n                } else {\n                    const msg = (res && res.data && res.data.message) ? res.data.message : 'Import non riuscito';\n                    window.addMsg('ai', '\u26a0\ufe0f ' + msg);\n                }\n            }).catch(function(err){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                window.addMsg('ai', '\u26a0\ufe0f Errore import listino: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n            });\n        };\n\n\n        window.prepareUploadFileForAI = function(file) {\n            return new Promise(function(resolve){\n                try {\n                    if (!(file instanceof File)) { resolve(file); return; }\n                    const fileName = String(file.name || '').toLowerCase();\n                    const mimeType = String(file.type || '').toLowerCase();\n                    const isImage = mimeType.indexOf('image\/') === 0 || \/\\.(jpg|jpeg|png|webp|gif|heic|heif)$\/i.test(fileName);\n                    if (!isImage) { resolve(file); return; }\n\n                    const reader = new FileReader();\n                    reader.onerror = function(){ resolve(file); };\n                    reader.onload = function(ev) {\n                        const img = new Image();\n                        img.onerror = function(){ resolve(file); };\n                        img.onload = function() {\n                            try {\n                                const maxSide = 1600;\n                                const srcW = img.naturalWidth || img.width || 0;\n                                const srcH = img.naturalHeight || img.height || 0;\n                                if (!srcW || !srcH) { resolve(file); return; }\n                                const scale = Math.min(1, maxSide \/ Math.max(srcW, srcH));\n                                const outW = Math.max(1, Math.round(srcW * scale));\n                                const outH = Math.max(1, Math.round(srcH * scale));\n                                const canvas = document.createElement('canvas');\n                                canvas.width = outW;\n                                canvas.height = outH;\n                                const ctx = canvas.getContext('2d');\n                                if (!ctx) { resolve(file); return; }\n                                ctx.drawImage(img, 0, 0, outW, outH);\n                                canvas.toBlob(function(blob){\n                                    if (!blob || blob.size >= file.size) { resolve(file); return; }\n                                    const baseName = String(file.name || 'camera').replace(\/\\.[^.]+$\/, '') || 'camera';\n                                    resolve(new File([blob], baseName + '.jpg', { type: 'image\/jpeg' }));\n                                }, 'image\/jpeg', 0.82);\n                            } catch(e) {\n                                resolve(file);\n                            }\n                        };\n                        img.src = String(ev.target && ev.target.result ? ev.target.result : '');\n                    };\n                    reader.readAsDataURL(file);\n                } catch(e) {\n                    resolve(file);\n                }\n            });\n        };\n\n        window.startUserUploadedFileAI = function(fileUrl, fileName) {\n            const dbVal = gE('ti-ditta') ? gE('ti-ditta').value : '';\n            const lowerName = String(fileName || '').toLowerCase();\n            const isImg = \/\\.(jpg|jpeg|png|webp|gif|heic|heif)$\/i.test(lowerName) || \/ti_action=ti_ai_get_image\/i.test(String(fileUrl || ''));\n            const prompt = isImg\n                ? 'Ho allegato una foto\/immagine. Analizzala, leggi eventuali testi o dati visibili, riportami una sintesi e chiedimi cosa voglio fare con il file o con i dati letti. Se il testo non \u00e8 leggibile o il contenuto \u00e8 ambiguo, chiedimi di precisarlo. Se riconosci prodotti o servizi presenti nel database, proponili automaticamente nella tabella ordine come righe da verificare e confermare.'\n                : 'Ho allegato un file che pu\u00f2 essere PDF con testo e immagini oppure un file tecnico. Usa per l interpretazione solo il testo leggibile o estraibile automaticamente; non inventare contenuti da immagini, scansioni o parti binarie non leggibili. Riporta una sintesi, chiedimi cosa voglio fare con il file o con i dati letti e, se il contenuto \u00e8 parziale, chiedimi di precisarlo. Se riconosci prodotti o servizi presenti nel database, proponili automaticamente nella tabella ordine come righe da verificare e confermare.';\n\n            if (!dbVal || dbVal === 'NEW_DB') { window.tiAlert('Seleziona una ditta prima di analizzare il file.'); return; }\n\n            try {\n                const shownName792 = String(fileName || 'file');\n                if (typeof window.updateProgressPopup === 'function') window.updateProgressPopup(94, 'Analisi file AI', 'File caricato. La AI sta leggendo il contenuto: attendi l esito.', shownName792);\n                else if (typeof window.showProgressPopup === 'function') window.showProgressPopup('Analisi file AI', 'File caricato. La AI sta leggendo il contenuto: attendi l esito.', {startPercent:94, targetPercent:98, stepMs:650, cancellable:true, currentItem:shownName792});\n            } catch(eProgress792) {}\n\n            window.rememberLastFileContext(fileUrl, prompt, false);\n            window.lastUploadedFile = null;\n            if (window.addMsg) window.addMsg('user', prompt);\n\n            let waitBubble = null;\n            try {\n                waitBubble = document.createElement('div');\n                waitBubble.className = 'ti-bubble ai';\n                waitBubble.innerHTML = window.getUserWaitingHtml('Analisi file in corso...', 'Sto leggendo il file allegato e preparo la risposta.');\n                if (dom.chat) { dom.chat.appendChild(waitBubble); dom.chat.scrollTop = dom.chat.scrollHeight; }\n            } catch(e) {}\n\n            let chatHist = [];\n            try {\n                const bubbles = dom.chat ? dom.chat.querySelectorAll('.ti-bubble') : [];\n                const startIdx = Math.max(0, (bubbles ? bubbles.length : 0) - 8);\n                for (let i = startIdx; i < (bubbles ? bubbles.length : 0); i++) {\n                    const bubble = bubbles[i];\n                    const role = bubble.classList.contains('user') ? 'user' : 'assistant';\n                    const clone = bubble.cloneNode(true);\n                    const tw = clone.querySelector('.ti-table-wrap'); if (tw) tw.remove();\n                    const msgTxt = (clone.innerText || '').trim();\n                    if (msgTxt && !msgTxt.includes('Fammi pensare...')) chatHist.push({role: role, content: msgTxt});\n                }\n            } catch(e) {}\n\n            const fd = new FormData();\n            fd.append('ti_action', 'ti_ai_chat_start');\n            fd.append('db', dbVal);\n            fd.append('analyze_file_only', '1');\n            fd.append('reset_flow_for_file_analysis', '1');\n            fd.append('text', prompt + ` [FILE_ALLEGATO: ${fileUrl}]`);\n            fd.append('history_context', JSON.stringify(chatHist));\n\n            window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                if (res && res.success) {\n                    const rawReply = ((res.data || {}).reply || '').trim();\n                    const cleanReply = rawReply.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                    window.addMsg('ai', window.formatTable(cleanReply || 'Analisi completata.'), cleanReply || 'Analisi completata.');\n                    if (res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); }, 1200);\n                } else {\n                    const msg = (res && res.data && res.data.message) ? res.data.message : 'Analisi file non riuscita';\n                    window.addMsg('ai', '\u26a0\ufe0f ' + msg);\n                    try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Analisi file non riuscita', {notifyUser:true, errorMessage:msg}); } catch(eProgressErr792) {}\n                }\n            }).catch(function(err){\n                if (waitBubble && waitBubble.remove) waitBubble.remove();\n                const errMsg792 = 'Errore analisi file: ' + ((err && err.message) ? err.message : 'errore sconosciuto');\n                window.addMsg('ai', '\u26a0\ufe0f ' + errMsg792);\n                try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Errore analisi file', {notifyUser:true, errorMessage:errMsg792}); } catch(eProgressCatch792) {}\n            });\n        };\n\n        window.handleFileUpload = function(file, typeName) {\n            let tbl = 'Chat'; const dbVal = gE('ti-ditta').value;\n            if(!dbVal) { window.tiAlert(\"Seleziona una ditta prima di caricare file.\"); return; }\n            window.prepareUploadFileForAI(file).then(function(uploadFile){\n                const realFile = (uploadFile instanceof File) ? uploadFile : file;\n                const fd=new FormData(); fd.append('ti_action','ti_ai_media_upload'); fd.append('db',dbVal); fd.append('file', realFile, realFile.name || file.name || 'upload'); fd.append('tbl', tbl);\n                if(window.addMsg) window.addMsg('ai',\"\u23f3 Analisi file in corso...\"); \n                window.fetchJsonSafe(window.tiUrl,{method:'POST',body:fd}).then(d=>{\n                    if(d.success) { \n                        window.lastUploadedFile = d.data && d.data.url ? d.data.url : (d.url ? d.url : ''); \n                        window.startUserUploadedFileAI(window.lastUploadedFile, realFile.name || file.name || ''); \n                    } else { if(window.addMsg) window.addMsg('ai',`\u26a0\ufe0f Errore caricamento.`); }\n                }).catch(function(err){\n                    if(window.addMsg) window.addMsg('ai', '\u26a0\ufe0f Errore caricamento file: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n                });\n            });\n        };\n\n        if(dom.btnNew) dom.btnNew.onclick = () => { window.clearLastFileContext(); window.resetClarifyCount(); const fd = new FormData(); fd.append('ti_action', 'ti_ai_reset_flow'); fetch(window.tiUrl, {method:'POST', body:fd}).then(() => { if(dom.chat) dom.chat.innerHTML=''; window.updCtx(); }); }; \n        if(gE('ti-cam')) gE('ti-cam').onclick = (e) => { e.preventDefault(); window.openCamModal(); };\n        const camIn = gE('ti-cam-in'); if(camIn) camIn.onchange = (e) => { if(e.target.files.length) window.handleFileUpload(e.target.files[0], 'Immagine'); };\n        if(gE('ti-file')) gE('ti-file').onclick = (e) => { e.preventDefault(); window.showUploadChoice('chat'); }; \n        if(gE('ti-products-btn')) gE('ti-products-btn').onclick = () => { if(gE('ti-msg')) { window.lastUploadedFile = null; if (window.lastFileContext) { window.lastFileContext.active = false; window.lastFileContext.awaitingReuseChoice = false; window.lastFileContext.pendingReusePrompt = ''; } gE('ti-msg').value = (String(window.currLang || 'it').toLowerCase() === 'en') ? 'Show all available products in order table' : 'Mostra tutti i prodotti disponibili in tabella ordine'; gE('ti-send').click(); } };\n        if(gE('ti-services-btn')) gE('ti-services-btn').onclick = () => { if(gE('ti-msg')) { window.lastUploadedFile = null; if (window.lastFileContext) { window.lastFileContext.active = false; window.lastFileContext.awaitingReuseChoice = false; window.lastFileContext.pendingReusePrompt = ''; } gE('ti-msg').value = (String(window.currLang || 'it').toLowerCase() === 'en') ? 'Show all available services in order table' : 'Mostra tutti i servizi disponibili in tabella ordine'; gE('ti-send').click(); } };\n        if (window.ensureActivitiesButton) window.ensureActivitiesButton();\n        if(gE('ti-activities-btn')) gE('ti-activities-btn').onclick = window.tiOpenUserActivities;\n        if (window.updateActivitiesButtonVisibility) {\n            window.updateActivitiesButtonVisibility();\n            setTimeout(window.updateActivitiesButtonVisibility, 250);\n            setTimeout(window.updateActivitiesButtonVisibility, 900);\n        }\n        \n        \/\/ 30.9.664: ti-file-in rimosso. Upload da PC gestito da input diretti 616.\n\n        window.invalidClarifyCount = 0;\n        window.clarifyWarnTimer = null;\n        window.clarifyCloseTimer = null;\n        window.pendingClarifyClosure = false;\n        window.cancelClarifyClosure = function() {\n            if (window.clarifyWarnTimer) { clearTimeout(window.clarifyWarnTimer); window.clarifyWarnTimer = null; }\n            if (window.clarifyCloseTimer) { clearTimeout(window.clarifyCloseTimer); window.clarifyCloseTimer = null; }\n            window.pendingClarifyClosure = false;\n        };\n        window.resetClarifyCount = function() {\n            window.invalidClarifyCount = 0;\n            window.cancelClarifyClosure();\n        };\n        window.startFreshChatAfterClarifyLimit = function() {\n            window.cancelClarifyClosure();\n            window.invalidClarifyCount = 0;\n            if (window.doLogout) {\n                window.doLogout(true);\n                return;\n            }\n            const fd = new FormData(); fd.append('ti_action', 'ti_ai_reset_flow');\n            fetch(window.tiUrl, {method:'POST', body:fd}).finally(() => {\n                if(dom.chat) dom.chat.innerHTML = '';\n                window.updCtx();\n            });\n        };\n        window.scheduleClarifyClosure = function() {\n            window.cancelClarifyClosure();\n            window.pendingClarifyClosure = true;\n            window.clarifyWarnTimer = setTimeout(() => {\n                if (!window.pendingClarifyClosure) return;\n                const warn = \"Se non ricevo istruzioni chiuder\u00f2 la sessione a breve.\";\n                window.addMsg('ai', warn, warn);\n                window.speakText(\"Se non ricevo istruzioni chiuder\u00f2 la sessione a breve.\");\n                window.clarifyCloseTimer = setTimeout(() => {\n                    if (!window.pendingClarifyClosure) return;\n                    window.startFreshChatAfterClarifyLimit();\n                }, 60000);\n            }, 60000);\n        };\n        window.handleClarifyReply = function(replyText, rawSource, noVoice) {\n            const safeReply = String(replyText || 'Mi dispiace, non ho capito bene la richiesta. Puoi scriverla in un altro modo o aggiungere un dettaglio in pi\u00f9?').trim();\n            window.invalidClarifyCount = (window.invalidClarifyCount || 0) + 1;\n            window.addMsg('ai', window.formatTable(safeReply), rawSource || safeReply);\n            if (!noVoice) window.speakText(safeReply);\n            if (window.invalidClarifyCount >= 3) {\n                window.scheduleClarifyClosure();\n            }\n            return false;\n        };\n\n        if(gE('ti-copy')) gE('ti-copy').onclick = () => { if(dom.chat) { navigator.clipboard.writeText(dom.chat.innerText).then(() => { window.pendingEmail = 'manual'; window.addMsg('ai', \"Chat copiata. A quale email vuoi inviarla?\"); }); } };\n\n        \/\/ 30.9.664 - Guardia invio vuoto chatbot utente.\n        window.tiSsUserChatText543 = function() {\n            const el = document.getElementById('ti-msg');\n            return el ? String(el.value || '').replace(\/\u00a0\/g, ' ').trim() : '';\n        };\n        window.tiSsIsBareYesNoOk543 = function(v) {\n            const t = String(v || '').replace(\/\u00a0\/g, ' ').trim().toLowerCase();\n            return \/^(ok|si|s\u00ec|s|yes|y|no|n|annulla|annullo|x)$\/i.test(t);\n        };\n        window.tiSsBlockEmptyUserChat543 = function(source) {\n            window.tiQuickReplyText = '';\n            window.tiHiddenSendText = '';\n            window.tiHiddenSendLabel = '';\n            const el = document.getElementById('ti-msg');\n            if (el) el.value = '';\n            return false;\n        };\n\n        \/\/ v30.9.664 - invio rapido robusto per pulsanti chat generati dinamicamente (booking avanti\/indietro, OK\/X).\n        window.tiSendQuickReply = function(text, label) {\n            const msgEl = document.getElementById('ti-msg');\n            const sendBtn = document.getElementById('ti-send');\n            const quickText = String(text || '').replace(\/\u00a0\/g, ' ').trim();\n            if (!quickText || !sendBtn) return false;\n            const typedText543 = window.tiSsUserChatText543 ? window.tiSsUserChatText543() : (msgEl ? String(msgEl.value || '').trim() : '');\n            if (!typedText543 && window.tiSsIsBareYesNoOk543 && window.tiSsIsBareYesNoOk543(quickText)) {\n                return window.tiSsBlockEmptyUserChat543 ? window.tiSsBlockEmptyUserChat543('quick-reply') : false;\n            }\n            window.tiQuickReplyText = quickText;\n            if (label && String(label).trim() && String(label).trim() !== quickText) {\n                window.tiHiddenSendText = quickText;\n                window.tiHiddenSendLabel = String(label).trim();\n                window.tiQuickReplyText = '';\n            }\n            if (msgEl) msgEl.value = '';\n            try { sendBtn.dispatchEvent(new MouseEvent('click', {bubbles:true, cancelable:true, view:window})); }\n            catch(e) { try { sendBtn.click(); } catch(_e) {} }\n            return false;\n        };\n        if (!window.tiBookingNavDelegated210) {\n            window.tiBookingNavDelegated210 = true;\n            document.addEventListener('click', function(ev) {\n                const btn = ev.target && ev.target.closest ? ev.target.closest('[data-ti-booking-nav]') : null;\n                if (!btn) return;\n                ev.preventDefault();\n                ev.stopPropagation();\n                const cmd = String(btn.getAttribute('data-ti-booking-nav') || '').trim();\n                if (cmd && window.tiSendQuickReply) window.tiSendQuickReply(cmd, btn.textContent || cmd);\n            }, true);\n        }\n\n        if(dom.send) dom.send.onclick = () => {\n            if(!dom.msg) return;\n            const rawInput = (dom.msg.value || '');\n            const typedInput543 = String(rawInput || '').replace(\/\u00a0\/g, ' ').trim();\n            let txt = typedInput543;\n            if (window.tiQuickReplyText) {\n                txt = String(window.tiQuickReplyText || '').replace(\/\u00a0\/g, ' ').trim();\n                window.tiQuickReplyText = '';\n            }\n            let displayTxt = txt;\n            let textToSend = txt;\n            if (window.tiHiddenSendText) {\n                textToSend = String(window.tiHiddenSendText || '').replace(\/\u00a0\/g, ' ').trim();\n                displayTxt = String(window.tiHiddenSendLabel || 'Conferma ordine').replace(\/\u00a0\/g, ' ').trim();\n                window.tiHiddenSendText = '';\n                window.tiHiddenSendLabel = '';\n            }\n            if(!textToSend) { if (window.tiSsBlockEmptyUserChat543) window.tiSsBlockEmptyUserChat543('send-empty'); return false; }\n            if(!typedInput543 && window.tiSsIsBareYesNoOk543 && window.tiSsIsBareYesNoOk543(textToSend)) {\n                return window.tiSsBlockEmptyUserChat543 ? window.tiSsBlockEmptyUserChat543('send-bare-yes-no') : false;\n            }\n            if (window.cancelClarifyClosure) window.cancelClarifyClosure();\n            if (window.idleAwaitingChoice) {\n                const idleAns = String(textToSend || '').trim().toLowerCase();\n                if (\/^(no|n)$\/i.test(idleAns)) {\n                    window.idleAwaitingChoice = false;\n                    window.idleForceClosing = true;\n                    if (window.clearIdleTimers) window.clearIdleTimers();\n                    window.addMsg('user', displayTxt || textToSend);\n                    dom.msg.value = '';\n                    if(window.addMsg) window.addMsg('ai', '\ud83d\udd12 Sessione chiusa su richiesta. Avvio una nuova chat.');\n                    window.speakText('Sessione chiusa su richiesta. Avvio una nuova chat.');\n                    if (window.forceSessionCloseNow) window.forceSessionCloseNow(); else if (window.doLogout) window.doLogout(true);\n                    return;\n                }\n                if (\/^(si|s\u00ec|s|yes|ok|continua|continuare)$\/i.test(idleAns)) {\n                    window.idleAwaitingChoice = false;\n                    window.idleForceClosing = false;\n                    window.idleLastUserActivityAt = Date.now();\n                    window.idleFirstWarningAt = 0;\n                    window.idleFinalWarningAt = 0;\n                    window.idleCloseCheckAt = 0;\n                    if (window.clearIdleTimers) window.clearIdleTimers();\n                    if (window.scheduleIdleFirstWarning) window.scheduleIdleFirstWarning();\n                    window.addMsg('user', displayTxt || textToSend);\n                    dom.msg.value = '';\n                    if(window.addMsg) window.addMsg('ai', 'Ok, continuo la chat.');\n                    window.speakText('Ok, continuo la chat.');\n                    return;\n                }\n            }\n            const dittaInput = gE('ti-ditta'); const dbVal = dittaInput ? dittaInput.value : '';\n            const lastImportBubble = dom.chat ? Array.from(dom.chat.querySelectorAll('.ti-bubble.ai')).reverse().find(b => b.querySelector('.ti-import-map-select, .ti-import-group-target, #ti-import-price-formula, .ti-import-pdf-image-link')) : null;\n            if (lastImportBubble && \/^(ok|conferma|procedi|salva|importa|vai)$\/i.test(txt)) {\n                const mapping = window.collectImportMappingFromUI(lastImportBubble) || window.tiConfigImportLastMapping || {};\n                window.addMsg('user', txt); dom.msg.value='';\n                let b = document.createElement('div'); b.className='ti-bubble ai'; b.innerHTML = window.getUserWaitingHtml('Importazione in corso...', 'Sto salvando i dati confermati nel database.'); if(dom.chat){ dom.chat.appendChild(b); dom.chat.scrollTop=dom.chat.scrollHeight; }\n                const fdImp = new FormData();\n                fdImp.append('action','ti_ai_config_action');\n                fdImp.append('ti_action','ti_ai_config_action');\n                fdImp.append('db', dbVal);\n                fdImp.append('mode', 'confirm_import_preview');\n                fdImp.append('prompt', txt);\n                if (window.tiConfigImportToken) fdImp.append('import_token', window.tiConfigImportToken);\n                fdImp.append('mapping_json', JSON.stringify(mapping));\n                window.postFormDataJsonSafe(window.tiUrl, fdImp, {tiNoLongProcessSignal:true, cache:'no-store'}).then(res=>{\n                    if (b) b.remove();\n                    if (res.success) {\n                        const rawReply = res.data.reply || '';\n                        const noVoice = !!(res.data && res.data.no_voice) || rawReply.includes('[NO_VOICE]');\n                        const cleanReply = rawReply.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                        window.addMsg('ai', cleanReply);\n                        window.restoreLastUserPromptText();\n                        if (!noVoice) window.speakText(cleanReply);\n                        if (res.data.needs_reload) { window.tiConfigImportToken = null; setTimeout(() => { location.reload(); }, 1200); }\n                    } else {\n                        window.addMsg('ai', '\u26a0\ufe0f Errore import: ' + ((res.data && res.data.message) ? res.data.message : 'Sconosciuto'));\n                    }\n                }).catch((e)=>{ if (b) b.remove(); window.addMsg('ai', '\u26a0\ufe0f ' + e.message); });\n                return;\n            }\n\n            if(window.pendingEmail === 'manual') {\n                window.addMsg('ai', \"\u23f3 Invio email in corso...\");\n                const fd=new FormData(); fd.append('ti_action','ti_ai_send_email'); fd.append('db',dbVal); fd.append('email',txt); fd.append('history',dom.chat.innerText);\n                fetch(window.tiUrl,{method:'POST',body:fd}).then(r=>r.json()).then(res=>{ window.addMsg('ai',res.data.reply, res.data.reply); window.speakText(res.data.reply); });\n                window.pendingEmail=false; dom.msg.value=''; return;\n            }\n\n            const txtNorm = txt.toLowerCase();\n            if (window.lastFileContext && window.lastFileContext.awaitingReuseChoice) {\n                if (\/^(si|s\u00ec|yes|ok|ripeti|riutilizza)$\/i.test(txt)) {\n                    txt = window.lastFileContext.pendingReusePrompt || window.lastFileContext.lastActionPrompt || 'Importa di nuovo i dati dal file caricato.';\n                    window.lastUploadedFile = window.lastFileContext.url;\n                    window.lastFileContext.awaitingReuseChoice = false;\n                    window.lastFileContext.pendingReusePrompt = '';\n                    dom.msg.value = txt;\n                } else if (\/^(no|annulla|x)$\/i.test(txt)) {\n                    window.clearLastFileContext();\n                    window.addMsg('ai', \"Ok, non riutilizzo l'ultimo file. Puoi caricarne un altro quando vuoi.\");\n                    dom.msg.value='';\n                    return;\n                }\n            } else if (!window.lastUploadedFile && window.lastFileContext && window.lastFileContext.url) {\n                const isImportFollowup = window.isImportFollowupText(txtNorm);\n                const asksAboutLastFile = \/(import|reimport|file caricat|ultimo file|riutilizz|ripeti l'ultima azione|ultima azione)\/i.test(txtNorm);\n                if (asksAboutLastFile && !\/^(1|2|3|ok|si|s\u00ec|yes|no|x|annulla)$\/i.test(txt)) {\n                    if (window.askReuseLastFile(txt)) { dom.msg.value=''; return; }\n                } else if (window.lastFileContext.active && isImportFollowup) {\n                    window.lastUploadedFile = window.lastFileContext.url;\n                }\n            }\n\n            const asksCatalogOnly = \/\b(mostra|vedi|tabella|ordine|prodotti|servizi|catalogo|elenco|lista)\b\/i.test(txt) && !\/\b(file|allegat|pdf|document|referto|immagine|listino)\b\/i.test(txt);\n            if (asksCatalogOnly) {\n                window.lastUploadedFile = null;\n                if (window.lastFileContext) {\n                    window.lastFileContext.active = false;\n                    window.lastFileContext.awaitingReuseChoice = false;\n                    window.lastFileContext.pendingReusePrompt = '';\n                }\n            }\n            if(window.lastUploadedFile) { textToSend += ` [FILE_ALLEGATO: ${window.lastUploadedFile}]`; window.lastUploadedFile = null; }\n            window.lastUserPromptText = displayTxt;\n            window.addMsg('user',displayTxt); dom.msg.value='';\n            \n            let chatHist = []; const bubbles = dom.chat.querySelectorAll('.ti-bubble'); let startIdx = Math.max(0, bubbles.length - 8); \n            for(let i=startIdx; i<bubbles.length; i++) {\n                let b = bubbles[i]; let role = b.classList.contains('user') ? 'user' : 'assistant'; let clone = b.cloneNode(true);\n                let tw = clone.querySelector('.ti-table-wrap'); if(tw) tw.remove(); \n                let msgTxt = clone.innerText.trim(); if(msgTxt && !msgTxt.includes(\"Fammi pensare...\")) { chatHist.push({role: role, content: msgTxt}); }\n            }\n\n            const fd=new FormData(); fd.append('ti_action','ti_ai_chat_start'); fd.append('text',textToSend); fd.append('db',dbVal); fd.append('history_context', JSON.stringify(chatHist)); fd.append('ti_direct_db', (window.tiForced ? '1' : '0'));\n\n            const tiInfoWait196 = window.tiIsInfoVerificationRequest ? window.tiIsInfoVerificationRequest(textToSend) : false;\n            let b = document.createElement('div');\n            b.className = 'ti-bubble ai' + (tiInfoWait196 ? ' ti-info-wait-bubble' : '');\n            b.innerHTML = tiInfoWait196\n                ? window.getInfoVerificationWaitingHtml('\u23f3 Verifica informazioni in corso...', 'Sto elaborando la richiesta e verifico le informazioni disponibili.')\n                : window.getUserWaitingHtml('Fammi pensare...', 'Sto elaborando la richiesta e verifico i dati disponibili.');\n            if(dom.chat){ dom.chat.appendChild(b); dom.chat.scrollTop=dom.chat.scrollHeight; }\n            \n            fetch(window.tiUrl,{method:'POST',body:fd}).then(r=> {\n                if (r.status === 413) throw new Error(\"File\/Testo troppo grande per i limiti del server (Errore 413).\"); if (!r.ok) throw new Error(\"Errore Server \" + r.status); return r.json();\n            }).then(res=>{\n                if(b) b.remove();\n                if(res.success) {\n                    if(res.data && res.data.empty_ignored) { window.restoreLastUserPromptText && window.restoreLastUserPromptText(); return; }\n                    if(res.data.clear_chat && dom.chat) dom.chat.innerHTML = '';\n                    if(res.data.needs_reload) {\n                        window.clearLastFileContext();\n                        setTimeout(() => { location.reload(); }, 2500);\n                    }\n                    if(res.data && res.data.admin_popup) {\n                        if (window.showPrivilegedCommunicationPopup) window.showPrivilegedCommunicationPopup(res.data.admin_popup, 'Comunicazione amministrativa', {text: (window.tiPlainTextFromHtml ? window.tiPlainTextFromHtml(res.data.admin_popup) : String(res.data.admin_popup || ''))});\n                        else if (window.tiAlert) window.tiAlert(res.data.admin_popup);\n                    }\n                    \n                    if(res.data.reply && res.data.reply.includes('[UPDATE_DB]')) {\n                        let aiReplyText = res.data.reply;\n                        let start = aiReplyText.indexOf('[UPDATE_DB]') + 11;\n                        let end = aiReplyText.indexOf('[\/UPDATE_DB]');\n                        if (end !== -1 && end > start) {\n                            let jsonStr = aiReplyText.substring(start, end);\n                            let cleanJson = jsonStr.replace(\/```json|```\/ig, '').trim();\n                            try {\n                                let newTabelle = JSON.parse(cleanJson);\n                                if (typeof newTabelle === 'object') {\n                                    let currentData = window.currentDbData || { Tabelle: {} };\n                                    \n                                    let incTabs = newTabelle.Tabelle || newTabelle;\n                                    for(let t in incTabs) {\n                                        if (!currentData.Tabelle[t]) currentData.Tabelle[t] = [];\n                                        currentData.Tabelle[t] = currentData.Tabelle[t].concat(incTabs[t]);\n                                    }\n\n                                    const fdUpdate = new FormData();\n                                    fdUpdate.append('ti_action', 'ti_ai_chat_start');\n                                    fdUpdate.append('db', dbVal);\n                                    fdUpdate.append('text', \"FULL_DB_UPDATE_B64|||\" + utoa(JSON.stringify(currentData)));\n                                    fdUpdate.append('db_version', (currentData && currentData.__db_version) ? currentData.__db_version : '');\n                                    fetch(window.tiUrl, {method:'POST', body:window.ensureAjaxActionField(fdUpdate)}).then(ru=>ru.json()).then(du=>{\n                                        if(du.success) {\n                                            if (currentData && du.data && du.data.db_version) currentData.__db_version = du.data.db_version;\n                                            window.clearLastFileContext();\n                                            let userFeedback = aiReplyText.substring(0, aiReplyText.indexOf('[UPDATE_DB]')) + \"\\n\\n\u2705 **Database aggiornato con successo dall'AI!**\";\n                                            window.addMsg('ai', userFeedback); window.speakText(\"Database aggiornato con successo.\");\n                                            setTimeout(() => { location.reload(); }, 2500);\n                                        }\n                                    });\n                                    return;\n                                }\n                            } catch(e) {\n                                window.addMsg('ai', aiReplyText.substring(0, aiReplyText.indexOf('[UPDATE_DB]')) + \"\\n\\n\u26a0\ufe0f L'AI ha provato a modificare i dati ma ha generato un formato JSON non valido.\"); return;\n                            }\n                        }\n                    }\n                    \n                    if(res.data.reply && res.data.reply.includes('[RESET_SESSION]')) {\n                        let cln = res.data.reply.replace('[RESET_SESSION]', '').trim();\n                        if(cln) { window.addMsg('ai', cln, cln); window.speakText(cln); }\n                        setTimeout(() => { window.doLogout(true); }, 2500); return;\n                    }\n                    \n                    if(res.data.reply && res.data.reply.includes('[SWITCH_DB:')) {\n                        let match = res.data.reply.match(\/\\[SWITCH_DB:(.*?)\\]\/);\n                        if(match) {\n                            let targetDb = match[1]; window.addMsg('ai', \"\u23f3 Ti sto trasferendo alla ditta selezionata...\");\n                            setTimeout(() => { localStorage.setItem('ti_saved_db', targetDb); location.href = window.tiUrl; }, 1500); return;\n                        }\n                    }\n\n                    if(res.data.trigger_email) {\n                        const fd2=new FormData(); fd2.append('ti_action','ti_ai_send_email'); fd2.append('db',dbVal); fd2.append('email',res.data.trigger_email); fd2.append('subject',res.data.trigger_subj); \n                        fd2.append('history',res.data.trigger_hist); if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd2); if(res.data.admin_copy) fd2.append('admin_copy','1');\n                        if (res.data.trigger_cc) fd2.append('cc_email', res.data.trigger_cc);\n                        if (res.data.trigger_admin_to_email) fd2.append('admin_to_email', res.data.trigger_admin_to_email);\n                        if (res.data.trigger_admin_to_tel) fd2.append('admin_to_tel', res.data.trigger_admin_to_tel);\n                        if (res.data.trigger_send_email) fd2.append('send_email_opt', res.data.trigger_send_email);\n                        if (res.data.trigger_send_sms) fd2.append('send_sms_opt', res.data.trigger_send_sms);\n                        fetch(window.tiUrl,{method:'POST',body:window.ensureAjaxActionField(fd2)}).then(rr=>{ if(!rr.ok) throw new Error('Errore Server ' + rr.status); return rr.json(); }).then(rr=>{\n                            if (rr && rr.data && rr.data.admin_popup) {\n                                if (window.showPrivilegedCommunicationPopup) window.showPrivilegedCommunicationPopup(rr.data.admin_popup, 'Fattura elettronica', {text: (window.tiPlainTextFromHtml ? window.tiPlainTextFromHtml(rr.data.admin_popup) : String(rr.data.admin_popup || ''))});\n                                else if (window.tiAlert) window.tiAlert(rr.data.admin_popup);\n                            }\n                            const replyMsg = (rr && rr.data && rr.data.reply) ? rr.data.reply : ((rr && rr.success) ? 'Operazione completata.' : 'Errore invio notifica');\n                            const shownNotifyPopup = window.maybeShowPrivilegedCommunicationPopup ? window.maybeShowPrivilegedCommunicationPopup(replyMsg, 'Comunicazione notifica') : false;\n                            if (shownNotifyPopup) window.addMsg('ai', window.tiPopupPlaceholderMessage ? window.tiPopupPlaceholderMessage('Comunicazione notifica') : 'Comunicazione aperta in popup.');\n                            else { window.addMsg('ai', replyMsg, replyMsg); window.speakText(replyMsg); }\n                            window.restoreLastUserPromptText();\n                            if(res.data.force_refresh) { const fd3=new FormData(); fd3.append('ti_action','ti_ai_chat_start'); fd3.append('text',''); fd3.append('force_refresh_flow','1'); fd3.append('db',dbVal); fetch(window.tiUrl,{method:'POST',body:window.ensureAjaxActionField(fd3)}).then(r3=>{ if(!r3.ok) throw new Error('Errore Server ' + r3.status); return r3.json(); }).then(r3=>{ const r3msg = (r3 && r3.data && r3.data.reply) ? r3.data.reply : ''; if (r3msg) { window.addMsg('ai', r3msg, r3msg); window.speakText(r3msg); } }); }\n                        }).catch((err)=>{ const em = (err && err.message) ? err.message : 'Errore invio notifica'; window.addMsg('ai', '\u26a0\ufe0f ' + em); window.restoreLastUserPromptText(); });\n                    } else {\n                        const rawReply = res.data.reply || '';\n                        const noVoice = !!(res.data && res.data.no_voice) || rawReply.includes('[NO_VOICE]');\n                        const cleanReply = rawReply.replace(\/\\[NO_VOICE\\]\/g, '').trim();\n                        if (res.data && res.data.clarify_request) {\n                            window.handleClarifyReply(cleanReply, cleanReply, noVoice);\n                            if (res.data.reset_chat) {\n                                setTimeout(() => { window.startFreshChatAfterClarifyLimit(); }, 1200);\n                            }\n                        } else {\n                            window.resetClarifyCount();\n                            const formattedReply = window.formatTable(cleanReply);\n                            const forceChatReply = !!(res.data && (res.data.force_chat || res.data.no_admin_popup || res.data.activity_chat));\n                            const shownPrivPopup = forceChatReply ? false : (window.maybeShowPrivilegedCommunicationPopup ? window.maybeShowPrivilegedCommunicationPopup(formattedReply, 'Comunicazione \/ report') : false);\n                            if (shownPrivPopup) {\n                                window.addMsg('ai', window.tiPopupPlaceholderMessage ? window.tiPopupPlaceholderMessage('Comunicazione \/ report') : 'Comunicazione\/report aperto in popup.');\n                            } else {\n                                window.addMsg('ai', formattedReply, cleanReply);\n                                if (!noVoice) window.speakText(cleanReply);\n                            }\n                        }\n                        window.restoreLastUserPromptText();\n                    }\n                } else {\n                    const roleNow = String(window.tiRole || '').toLowerCase();\n                    const isPrivilegedNow = (!!window.tiUser && window.tiUser === 'SSGlobalAdmin') || \/amministratore|configuratore|responsabile|globale\/.test(roleNow);\n                    if (isPrivilegedNow) {\n                        const errHtml = \"\u26a0\ufe0f Errore AI: \" + (res.data && res.data.message ? res.data.message : 'Sconosciuto');\n                        const shownErrPopup = window.showPrivilegedCommunicationPopup ? window.showPrivilegedCommunicationPopup(errHtml, 'Errore \/ comunicazione', {text: errHtml}) : false;\n                        window.addMsg('ai', shownErrPopup && window.tiPopupPlaceholderMessage ? window.tiPopupPlaceholderMessage('Errore \/ comunicazione') : errHtml);\n                    } else {\n                        window.handleClarifyReply(\"Mi dispiace, non ho capito bene la richiesta. Puoi scriverla in un altro modo o aggiungere un dettaglio in pi\u00f9?\", '', true);\n                    }\n                    window.restoreLastUserPromptText();\n                }\n            }).catch((e)=>{ \n                const msg = (e && e.message) ? e.message : 'Errore richiesta AI';\n                const roleNow = String(window.tiRole || '').toLowerCase();\n                const isPrivilegedNow = (!!window.tiUser && window.tiUser === 'SSGlobalAdmin') || \/amministratore|configuratore|responsabile|globale\/.test(roleNow);\n                window.hideProgressPopup(false, 'Errore richiesta AI', {notifyUser:isPrivilegedNow, errorMessage:msg}); \n                if (isPrivilegedNow) {\n                    const errHtml = \"\u26a0\ufe0f \" + msg;\n                    const shownErrPopup = window.showPrivilegedCommunicationPopup ? window.showPrivilegedCommunicationPopup(errHtml, 'Errore \/ comunicazione', {text: errHtml}) : false;\n                    if (!shownErrPopup && window.tiAlert && \/API Key|documenti AI|formato|500\/i.test(msg)) window.tiAlert(msg);\n                    window.addMsg('ai', shownErrPopup && window.tiPopupPlaceholderMessage ? window.tiPopupPlaceholderMessage('Errore \/ comunicazione') : errHtml);\n                } else {\n                    window.handleClarifyReply(\"Mi dispiace, non ho capito bene la richiesta. Puoi scriverla in un altro modo o aggiungere un dettaglio in pi\u00f9?\", '', true);\n                }\n                window.restoreLastUserPromptText(); \n            });\n        };\n\n        if(gE('ti-ok')) { gE('ti-ok').onclick=()=>{ const curr = dom.msg ? String(dom.msg.value || '').replace(\/\u00a0\/g, ' ').trim() : ''; if(!curr) return window.tiSsBlockEmptyUserChat543 ? window.tiSsBlockEmptyUserChat543('ok-empty') : false; if(dom.msg) dom.msg.value=''; if(window.tiSendQuickReply) return window.tiSendQuickReply(curr, curr); window.tiQuickReplyText = curr; if(dom.send) dom.send.click(); return false; }; }\n        if(gE('ti-x')) { gE('ti-x').onclick=()=>{ const curr = dom.msg ? String(dom.msg.value || '').replace(\/\u00a0\/g, ' ').trim() : ''; if(!curr) return window.tiSsBlockEmptyUserChat543 ? window.tiSsBlockEmptyUserChat543('no-empty') : false; if(dom.msg) dom.msg.value=''; if(window.tiSendQuickReply) return window.tiSendQuickReply('ANNULLA', 'ANNULLA'); window.tiQuickReplyText = 'ANNULLA'; if(dom.send) dom.send.click(); return false; }; }\n        if(window.placeMainHelpButtonRight) window.placeMainHelpButtonRight();\n        if(gE('ti-help-btn')) { gE('ti-help-btn').onclick = () => window.openHelpModal(); }\n        if(gE('ti-help-support-toggle')) { gE('ti-help-support-toggle').onclick = () => window.toggleSupportFeedbackBox(); }\n        if(gE('ti-support-send')) { gE('ti-support-send').onclick = () => window.sendSupportFeedback(); }\n        if(gE('ti-kbd-btn')) {\n            gE('ti-kbd-btn').onclick = () => { \n                const k = gE('ti-kbd-cnt'); \n                if(k && k.innerHTML === '') { \n                    [\"1234567890\", \"QWERTYUIOP\", \"ASDFGHJKL\", \"ZXCVBNM\", \"@_-.\u20ac?!+=\"].forEach(row => { \n                        const rDiv = document.createElement('div'); rDiv.className = 'ti-kbd-row'; \n                        row.split('').forEach(c=>{ const b=document.createElement('button'); b.type=\"button\"; b.innerText=c; b.onclick=()=>{ if(gE('ti-kbd-prev')) { gE('ti-kbd-prev').value+=c; } }; rDiv.appendChild(b); }); k.appendChild(rDiv); \n                    }); \n                } \n                if(gE('ti-kbd-prev')) { gE('ti-kbd-prev').value = ''; }\n                window.openModal('ti-kbd-ov'); \n            };\n        }\n\n        if(gE('ti-mic')) {\n            gE('ti-mic').onclick = () => {\n                const SR = window.SpeechRecognition || window.webkitSpeechRecognition; \n                if(!SR) return window.tiAlert(\"Il microfono non \u00e8 supportato dal browser in uso (usa Chrome, Edge o Safari).\");\n                const r = new SR(); r.lang = 'it-IT'; gE('ti-mic').style.background = '#b91c1c'; gE('ti-mic').style.color = '#fff';\n                r.onresult = e => { if(dom.msg) dom.msg.value = e.results[0][0].transcript; if(dom.send) dom.send.click(); };\n                r.onend = () => { gE('ti-mic').style.background = '#222'; gE('ti-mic').style.color = '#ccc'; }; \n                r.start(); \n                setTimeout(() => { r.stop(); gE('ti-mic').style.background = '#222'; gE('ti-mic').style.color = '#ccc'; }, 10000);\n            };\n        }\n    }); \n    \/* 30.9.664 - Fix persi reintegrati sul backup stabile: Acquisizioni, Report, Esiti, input scuri *\/\n    (function(){\n        function tiInstallFixPersi181(){\n        try {\n            const byId = function(id){ return document.getElementById(id); };\n            const escapeSafe = function(v){\n                if (window.escapeHtml) return window.escapeHtml(String(v == null ? '' : v));\n                return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; });\n            };\n            const parseNumSafe = function(v){\n                if (v === null || v === undefined) return 0;\n                let s = String(v).trim();\n                if (!s) return 0;\n                s = s.replace(\/[\u20ac\\s]\/g, '');\n                if (s.indexOf(',') !== -1 && s.indexOf('.') !== -1) s = s.replace(\/\\.\/g, '').replace(',', '.');\n                else if (s.indexOf(',') !== -1) s = s.replace(',', '.');\n                const n = parseFloat(s.replace(\/[^0-9.\\-]\/g, ''));\n                return isNaN(n) ? 0 : n;\n            };\n            const fmtSafe = function(n){\n                const num = Math.round((parseFloat(n) || 0) * 100) \/ 100;\n                if (window.fmtNum) return window.fmtNum(num);\n                return num.toLocaleString('it-IT', {minimumFractionDigits:2, maximumFractionDigits:2});\n            };\n            const normKey = function(s){\n                return String(s || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').toLowerCase().replace(\/[^a-z0-9]\/g, '');\n            };\n            const realTableName = function(data, wanted, fallback){\n                const tables = data && data.Tabelle ? data.Tabelle : {};\n                const want = normKey(wanted);\n                const keys = Object.keys(tables || {});\n                for (let i=0;i<keys.length;i++) if (normKey(keys[i]) === want) return keys[i];\n                for (let i=0;i<keys.length;i++) if (normKey(keys[i]).indexOf(want) !== -1 || want.indexOf(normKey(keys[i])) !== -1) return keys[i];\n                return fallback || wanted;\n            };\n            const getField = function(row, names, fallback){\n                row = row || {};\n                const keys = Object.keys(row);\n                for (let i=0;i<names.length;i++) {\n                    const want = normKey(names[i]);\n                    for (let j=0;j<keys.length;j++) if (normKey(keys[j]) === want) return row[keys[j]];\n                }\n                return fallback;\n            };\n            const setField = function(row, names, value){\n                row = row || {};\n                const keys = Object.keys(row);\n                for (let i=0;i<names.length;i++) {\n                    const want = normKey(names[i]);\n                    for (let j=0;j<keys.length;j++) if (normKey(keys[j]) === want) { row[keys[j]] = value; return; }\n                }\n                row[names[0]] = value;\n            };\n            const marginAdjustedNet = function(costNet, marginText){\n                const cost = parseNumSafe(costNet);\n                let m = String(marginText || '').trim();\n                if (!m) return Math.max(0, cost);\n                const isPct = m.indexOf('%') !== -1;\n                m = m.replace(\/[%\u20ac\\s]\/g, '');\n                let sign = 1;\n                if (m.charAt(0) === '-') { sign = -1; m = m.slice(1); }\n                else if (m.charAt(0) === '+') { m = m.slice(1); }\n                const val = parseNumSafe(m);\n                const net = isPct ? (cost + (cost * sign * val \/ 100)) : (cost + sign * val);\n                return Math.max(0, net);\n            };\n            const saleGross = function(costNet, marginText, ivaText){\n                let iva = parseNumSafe(ivaText);\n                if (iva <= 0) iva = 22;\n                return Math.round(marginAdjustedNet(costNet, marginText) * (1 + iva \/ 100) * 100) \/ 100;\n            };\n            const todayParts = function(){\n                const d = new Date();\n                const dd = String(d.getDate()).padStart(2,'0');\n                const mm = String(d.getMonth()+1).padStart(2,'0');\n                const yyyy = d.getFullYear();\n                const hh = String(d.getHours()).padStart(2,'0');\n                const mi = String(d.getMinutes()).padStart(2,'0');\n                return {date: dd + '\/' + mm + '\/' + yyyy, time: hh + ':' + mi};\n            };\n            const base64Utf8 = function(str){\n                try { return btoa(unescape(encodeURIComponent(str))); }\n                catch(e) { return btoa(str); }\n            };\n            window.tiPurchaseCalcSaleGross = saleGross;\n            window.tiPurchaseMarginAdjustedNet = marginAdjustedNet;\n\n            window.purchaseGetCatalogItems = function() {\n                const data = window.currentDbData || {};\n                const tables = data && data.Tabelle ? data.Tabelle : {};\n                const out = [];\n                Object.keys(tables || {}).forEach(function(tn){\n                    if (!\/(prodott|serviz)\/i.test(tn)) return;\n                    const rows = Array.isArray(tables[tn]) ? tables[tn] : [];\n                    rows.forEach(function(r, idx){\n                        if (!r || typeof r !== 'object') return;\n                        const isServ = \/(serviz)\/i.test(tn);\n                        const name = String(getField(r, isServ ? ['Servizio','Descrizione','Nome','Titolo'] : ['Prodotto','Descrizione','Nome','Titolo'], '') || '').trim();\n                        if (!name) return;\n                        out.push({\n                            table: tn,\n                            index: idx,\n                            type: isServ ? 'S' : 'P',\n                            typeLabel: isServ ? 'servizio' : 'prodotto',\n                            name: name,\n                            desc: String(getField(r, ['Descrizione','Note','Nota'], '') || '').trim(),\n                            cost: parseNumSafe(getField(r, ['Costo','Costo_acquisto','Costo acquisizione'], 0)),\n                            sale: parseNumSafe(getField(r, ['Prezzo','Prezzo_vendita','Prezzo vendita'], 0)),\n                            iva: parseNumSafe(getField(r, ['IVA_VAT','IVA','Iva','VAT'], 22)) || 22\n                        });\n                    });\n                });\n                return out;\n            };\n\n            window.ensurePurchaseModal = function() {\n                let ov = byId('ti-purchase-ov');\n                if (ov && ov.dataset && ov.dataset.v181 === '1') return ov;\n                if (ov && ov.parentNode) ov.parentNode.removeChild(ov);\n                ov = document.createElement('div');\n                ov.id = 'ti-purchase-ov';\n                ov.className = 'ti-modal-overlay ti-ov ti-closable-ov';\n                ov.dataset.v181 = '1';\n                ov.style.display = 'none';\n                ov.style.zIndex = '2147482000';\n                ov.style.alignItems = 'center';\n                ov.style.justifyContent = 'center';\n                ov.innerHTML = `\n                    <div class=\"ti-modal ti-purchase-modal\" style=\"width:min(1460px,98vw);max-width:98vw;max-height:92vh;overflow:auto;background:#0f172a;color:#fff;border-radius:16px;padding:18px;box-shadow:0 18px 60px rgba(0,0,0,.45);margin:0 auto;\">\n                        <div style=\"display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;\">\n                            <div>\n                                <h2 style=\"margin:0;font-size:22px;\">Acquisizioni<\/h2>\n                                <div style=\"font-size:14px;color:#cbd5e1;margin-top:4px;\">Inserisci o modifica le righe acquisizione. Il costo \u00e8 netto IVA; il prezzo vendita proposto include margine e IVA.<\/div>\n                            <\/div>\n                            <button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"window.closePurchaseManualFlow()\">X<\/button>\n                        <\/div>\n                        <div style=\"display:flex;gap:8px;flex-wrap:wrap;margin:10px 0 14px 0;\">\n                            <button type=\"button\" class=\"ti-btn ti-ss-add-manual-row\" style=\"background:#22c55e;color:#052e16;font-weight:700;\" onclick=\"window.addPurchaseManualRow()\">+ Aggiungi riga manuale<\/button>\n                            <button type=\"button\" class=\"ti-btn\" style=\"background:#f59e0b;color:#111827;font-weight:700;\" onclick=\"window.pickPurchaseImportFile()\">Import file acquisizioni<\/button>\n                        <\/div>\n                        <div class=\"ti-purchase-table-wrap\" style=\"overflow:auto;border:1px solid #334155;border-radius:12px;\">\n                            <table id=\"ti-purchase-table\" style=\"width:100%;border-collapse:collapse;min-width:1060px;background:#020617;\">\n                                <thead>\n                                    <tr style=\"background:#1e293b;\">\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Prodotto o servizio<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Tipo<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Q.t\u00e0<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Costo netto<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Margine<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Prezzo vendita ivato<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">IVA %<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Note<\/th>\n                                        <th style=\"padding:8px;border-bottom:1px solid #334155;\">Azioni<\/th>\n                                    <\/tr>\n                                <\/thead>\n                                <tbody id=\"ti-purchase-body\"><\/tbody>\n                            <\/table>\n                        <\/div>\n                        <div id=\"ti-purchase-total\" style=\"margin-top:10px;text-align:right;font-weight:700;color:#bbf7d0;\"><\/div>\n                        <div style=\"display:flex;justify-content:center;gap:8px;margin-top:14px;flex-wrap:wrap;\">\n                            <button type=\"button\" class=\"ti-btn ti-btn-ok\" style=\"background:#22c55e;color:#052e16;font-weight:900;\" onclick=\"window.confirmPurchaseRows()\">Conferma righe acquisizione<\/button>\n                            <button type=\"button\" class=\"ti-btn btn-close\" onclick=\"window.closePurchaseManualFlow()\">Chiudi<\/button>\n                        <\/div>\n                    <\/div>`;\n                document.body.appendChild(ov);\n                return ov;\n            };\n\n            window.closePurchaseSearchPopover = function(){\n                const p = byId('ti-purchase-row-search-popover');\n                if (p && p.parentNode) p.parentNode.removeChild(p);\n            };\n            window.selectPurchaseCatalogItemForRow = function(item, row){\n                try {\n                    row = row || window.__tiPurchaseActiveRow;\n                    if (!item || !row) return;\n                    const name = row.querySelector('.ti-pur-name');\n                    const type = row.querySelector('.ti-pur-type');\n                    const cost = row.querySelector('.ti-pur-cost');\n                    const sale = row.querySelector('.ti-pur-sale');\n                    const iva = row.querySelector('.ti-pur-iva');\n                    const note = row.querySelector('.ti-pur-note');\n                    if (name) name.value = item.name || '';\n                    if (type) type.value = item.type || 'P';\n                    if (cost) cost.value = item.cost ? String(item.cost).replace('.', ',') : '';\n                    if (iva) iva.value = item.iva || '22';\n                    if (note && !note.value) note.value = item.desc || '';\n                    row.dataset.catalogTable = item.table || '';\n                    row.dataset.catalogIndex = String(item.index != null ? item.index : -1);\n                    row.dataset.targetType = item.typeLabel || (item.type === 'S' ? 'servizio' : 'prodotto');\n                    window.recalcPurchaseRow(row, true);\n                    if (sale && item.sale && !sale.value) sale.value = String(item.sale).replace('.', ',');\n                    window.closePurchaseSearchPopover();\n                    window.refreshPurchaseTotals();\n                } catch(e) {}\n            };\n            window.showPurchaseRowSearch = function(input){\n                try {\n                    if (!input) return;\n                    const row = input.closest('tr');\n                    window.__tiPurchaseActiveRow = row;\n                    const q = String(input.value || '').trim().toLowerCase();\n                    window.closePurchaseSearchPopover();\n                    if (q.length < 1) return;\n                    const items = window.purchaseGetCatalogItems().filter(function(it){\n                        return (String(it.name||'') + ' ' + String(it.desc||'') + ' ' + String(it.table||'')).toLowerCase().indexOf(q) !== -1;\n                    }).slice(0, 16);\n                    const pop = document.createElement('div');\n                    pop.id = 'ti-purchase-row-search-popover';\n                    pop.className = 'ti-purchase-row-search-popover';\n                    if (!items.length) {\n                        pop.innerHTML = '<div style=\"color:#fca5a5;font-size:11px;padding:7px;\">Nessun prodotto o servizio trovato nel database.<\/div>';\n                    } else {\n                        pop.innerHTML = items.map(function(it, idx){\n                            return '<button type=\"button\" class=\"ti-purchase-result-row\" data-idx=\"' + idx + '\"><b>' + escapeSafe(it.typeLabel) + ' - ' + escapeSafe(it.name) + '<\/b><small>' + escapeSafe(it.table) + ' \u00b7 costo ' + escapeSafe(fmtSafe(it.cost)) + ' \u00b7 prezzo ' + escapeSafe(fmtSafe(it.sale)) + ' \u00b7 IVA ' + escapeSafe(it.iva) + '%<\/small><\/button>';\n                        }).join('');\n                    }\n                    document.body.appendChild(pop);\n                    const rect = input.getBoundingClientRect();\n                    const margin = 8;\n                    const topBelow = rect.bottom + margin;\n                    const estimatedH = Math.min(320, Math.max(70, items.length * 48 + 18));\n                    const top = (topBelow + estimatedH > window.innerHeight && rect.top > estimatedH) ? Math.max(margin, rect.top - estimatedH - margin) : topBelow;\n                    pop.style.left = Math.max(margin, Math.min(rect.left, window.innerWidth - Math.min(520, Math.max(240, rect.width)) - margin)) + 'px';\n                    pop.style.top = top + 'px';\n                    pop.style.width = Math.min(520, Math.max(240, rect.width)) + 'px';\n                    pop.querySelectorAll('.ti-purchase-result-row').forEach(function(btn){\n                        const handler = function(ev){\n                            ev.preventDefault(); ev.stopPropagation();\n                            const idx = parseInt(btn.getAttribute('data-idx'), 10);\n                            window.selectPurchaseCatalogItemForRow(items[idx], row);\n                        };\n                        btn.addEventListener('mousedown', handler);\n                        btn.addEventListener('touchstart', handler, {passive:false});\n                        btn.addEventListener('click', handler);\n                    });\n                } catch(e) {}\n            };\n            if (!window.__tiPurchaseSearchCloseBound) {\n                document.addEventListener('pointerdown', function(ev){\n                    const p = byId('ti-purchase-row-search-popover');\n                    if (!p) return;\n                    if (p.contains(ev.target)) return;\n                    if (ev.target && ev.target.closest && ev.target.closest('.ti-pur-name')) return;\n                    window.closePurchaseSearchPopover();\n                }, true);\n                window.addEventListener('resize', function(){ window.closePurchaseSearchPopover(); }, {passive:true});\n                window.__tiPurchaseSearchCloseBound = true;\n            }\n\n            window.recalcPurchaseRow = function(row, force){\n                try {\n                    if (!row) return;\n                    const cost = row.querySelector('.ti-pur-cost');\n                    const margin = row.querySelector('.ti-pur-margin');\n                    const sale = row.querySelector('.ti-pur-sale');\n                    const iva = row.querySelector('.ti-pur-iva');\n                    if (!sale) return;\n                    const gross = saleGross(cost ? cost.value : 0, margin ? margin.value : '', iva ? iva.value : '22');\n                    if (force || document.activeElement !== sale) sale.value = gross ? String(gross).replace('.', ',') : '';\n                } catch(e) {}\n            };\n\n            window.addPurchaseManualRow = function(item) {\n                try {\n                    const ov = byId('ti-purchase-ov') || window.ensurePurchaseModal();\n                    const body = byId('ti-purchase-body');\n                    if (!body) return;\n                    if (ov) { ov.style.display = 'flex'; }\n                    const safe = item || {};\n                    const tr = document.createElement('tr');\n                    tr.className = 'ti-purchase-row';\n                    tr.dataset.catalogTable = safe.table || '';\n                    tr.dataset.catalogIndex = String(safe.index != null ? safe.index : -1);\n                    tr.dataset.targetType = safe.type === 'S' ? 'servizio' : 'prodotto';\n                    const iva = safe.iva || '22';\n                    const cost = safe.cost ? String(safe.cost).replace('.', ',') : '';\n                    const sale = safe.sale ? String(safe.sale).replace('.', ',') : '';\n                    tr.innerHTML = `\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-name ti-purchase-row-search\" value=\"${escapeSafe(safe.name || '')}\" placeholder=\"Cerca prodotto o servizio\" autocomplete=\"off\" style=\"width:100%;min-width:230px;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><select class=\"ti-pur-type\" style=\"width:76px;padding:8px;border-radius:8px;background:#0f172a;color:#fff;border:1px solid #334155;\"><option value=\"P\" ${safe.type === 'S' ? '' : 'selected'}>P<\/option><option value=\"S\" ${safe.type === 'S' ? 'selected' : ''}>S<\/option><\/select><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-qty\" value=\"${escapeSafe(safe.qty || '1')}\" inputmode=\"decimal\" style=\"width:72px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-cost\" value=\"${escapeSafe(cost)}\" inputmode=\"decimal\" style=\"width:100px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-margin\" value=\"${escapeSafe(safe.margin || '')}\" placeholder=\"+10%\" style=\"width:86px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-sale\" value=\"${escapeSafe(sale)}\" inputmode=\"decimal\" style=\"width:112px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-iva\" value=\"${escapeSafe(iva)}\" inputmode=\"decimal\" style=\"width:68px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-note\" value=\"${escapeSafe(safe.desc || '')}\" placeholder=\"Note\" style=\"width:100%;min-width:170px;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                        <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:center;\"><button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"this.closest('tr').remove();window.refreshPurchaseTotals();\">X<\/button><\/td>`;\n                    body.appendChild(tr);\n                    const name = tr.querySelector('.ti-pur-name');\n                    if (name) {\n                        name.addEventListener('input', function(){ window.showPurchaseRowSearch(name); });\n                        name.addEventListener('focus', function(){ if (String(name.value||'').trim()) window.showPurchaseRowSearch(name); });\n                    }\n                    ['.ti-pur-qty','.ti-pur-cost','.ti-pur-margin','.ti-pur-iva'].forEach(function(sel){\n                        const el = tr.querySelector(sel);\n                        if (el) el.addEventListener('input', function(){ window.recalcPurchaseRow(tr); window.refreshPurchaseTotals(); });\n                    });\n                    window.recalcPurchaseRow(tr, !sale);\n                    window.refreshPurchaseTotals();\n                } catch(e) { if (window.tiAlert) window.tiAlert('Errore aggiunta riga acquisizione: ' + (e.message || e)); }\n            };\n\n            window.refreshPurchaseTotals = function() {\n                try {\n                    let total = 0;\n                    document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                        const qty = parseNumSafe((tr.querySelector('.ti-pur-qty') || {}).value || '0');\n                        const cost = parseNumSafe((tr.querySelector('.ti-pur-cost') || {}).value || '0');\n                        total += qty * cost;\n                    });\n                    const out = byId('ti-purchase-total');\n                    if (out) out.innerHTML = 'Totale acquisizione netta indicativo: \u20ac ' + fmtSafe(total);\n                } catch(e) {}\n            };\n            window.purchaseCollectRows = function() {\n                const rows = [];\n                try {\n                    document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                        const name = String((tr.querySelector('.ti-pur-name') || {}).value || '').trim();\n                        if (!name) return;\n                        rows.push({\n                            type: String((tr.querySelector('.ti-pur-type') || {}).value || 'P'),\n                            name: name,\n                            qty: String((tr.querySelector('.ti-pur-qty') || {}).value || '1').trim(),\n                            cost: String((tr.querySelector('.ti-pur-cost') || {}).value || '').trim(),\n                            margin: String((tr.querySelector('.ti-pur-margin') || {}).value || '').trim(),\n                            sale: String((tr.querySelector('.ti-pur-sale') || {}).value || '').trim(),\n                            iva: String((tr.querySelector('.ti-pur-iva') || {}).value || '22').trim(),\n                            note: String((tr.querySelector('.ti-pur-note') || {}).value || '').trim(),\n                            table: tr.dataset.catalogTable || '',\n                            index: parseInt(tr.dataset.catalogIndex || '-1', 10)\n                        });\n                    });\n                } catch(e) {}\n                return rows;\n            };\n            window.confirmPurchaseRows = function(){\n                try {\n                    const rows = window.purchaseCollectRows();\n                    if (!rows.length) { if (window.tiAlert) window.tiAlert('Inserisci almeno una riga acquisizione.'); return; }\n                    const dbEl = byId('ti-ditta');\n                    const dbVal = dbEl ? dbEl.value : '';\n                    if (!dbVal || dbVal === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona prima una ditta.'); return; }\n                    const source = window.currentDbData;\n                    if (!source || !source.Tabelle) { if (window.tiAlert) window.tiAlert('Database non caricato. Riapri Acquisizioni dopo aver selezionato la ditta.'); return; }\n                    const data = JSON.parse(JSON.stringify(source));\n                    const prodTable = realTableName(data, 'Prodotti', 'Prodotti');\n                    const servTable = realTableName(data, 'Servizi', 'Servizi');\n                    const actTable = realTableName(data, 'Attivita', 'Attivita');\n                    if (!data.Tabelle[prodTable]) data.Tabelle[prodTable] = [];\n                    if (!data.Tabelle[servTable]) data.Tabelle[servTable] = [];\n                    if (!data.Tabelle[actTable]) data.Tabelle[actTable] = [];\n                    const now = todayParts();\n                    const makeId = function(table){ return (Array.isArray(data.Tabelle[table]) ? data.Tabelle[table].length : 0) + 1; };\n                    rows.forEach(function(r){\n                        const isServ = String(r.type || 'P') === 'S';\n                        const t = isServ ? servTable : prodTable;\n                        const nameFields = isServ ? ['Servizio','Nome','Descrizione'] : ['Prodotto','Nome','Descrizione'];\n                        const tableRows = Array.isArray(data.Tabelle[t]) ? data.Tabelle[t] : (data.Tabelle[t] = []);\n                        let idx = (r.table === t && r.index >= 0 && tableRows[r.index]) ? r.index : -1;\n                        if (idx < 0) {\n                            const wanted = normKey(r.name);\n                            idx = tableRows.findIndex(function(row){ return normKey(getField(row, nameFields, '')) === wanted; });\n                        }\n                        let rec;\n                        if (idx >= 0) rec = tableRows[idx];\n                        else { rec = {}; setField(rec, ['ID','Id','id'], makeId(t)); setField(rec, nameFields, r.name); tableRows.push(rec); }\n                        setField(rec, nameFields, r.name);\n                        setField(rec, ['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'], parseNumSafe(r.cost));\n                        const gross = parseNumSafe(r.sale) || saleGross(r.cost, r.margin, r.iva);\n                        setField(rec, ['Prezzo','Prezzo vendita','Prezzo_vendita'], gross);\n                        setField(rec, ['IVA_VAT','IVA','Iva','VAT'], parseNumSafe(r.iva) || 22);\n                        if (r.note) setField(rec, ['Note','Nota','Descrizione'], r.note);\n                        if (!isServ) {\n                            const oldQty = parseNumSafe(getField(rec, ['Quantit\u00e0','Quantita','Disponibilita','Disponibilit\u00e0'], 0));\n                            setField(rec, ['Quantit\u00e0','Quantita'], oldQty + parseNumSafe(r.qty));\n                        }\n                        const act = {};\n                        setField(act, ['ID','Id','id'], makeId(actTable));\n                        setField(act, ['Username','Utente','user'], window.tiUser || '');\n                        setField(act, ['Descrizione attivit\u00e0','Descrizione attivita','Descrizione','Dettaglio','Note'], 'Acquisizione ' + r.name);\n                        setField(act, ['Prodotto'], r.name);\n                        setField(act, ['Prezzo'], parseNumSafe(r.cost));\n                        setField(act, ['Quantit\u00e0','Quantita'], parseNumSafe(r.qty));\n                        setField(act, ['Totale'], Math.round(parseNumSafe(r.cost) * parseNumSafe(r.qty) * 100) \/ 100);\n                        setField(act, ['Esito'], 'acquisizione');\n                        setField(act, ['Data'], now.date);\n                        setField(act, ['Ora'], now.time);\n                        setField(act, ['Stato'], 'Attivo');\n                        data.Tabelle[actTable].push(act);\n                    });\n                    const fd = new FormData();\n                    fd.append('ti_action', 'ti_ai_save_full_db');\n                    fd.append('db', dbVal);\n                    fd.append('payload', base64Utf8(JSON.stringify(data)));\n                    const done = function(res){\n                        if (res && res.success) {\n                            window.currentDbData = data;\n                            window.closePurchaseManualFlow();\n                            if (window.tiAlert) window.tiAlert('\u2705 Righe acquisizione confermate e salvate nel database.');\n                            if (res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); }, 900);\n                        } else {\n                            const msg = (res && res.data && res.data.message) ? res.data.message : 'Salvataggio acquisizione non riuscito.';\n                            if (window.tiAlert) window.tiAlert('\u26a0\ufe0f ' + msg);\n                        }\n                    };\n                    if (window.fetchJsonSafe) window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(done).catch(function(e){ if (window.tiAlert) window.tiAlert('Errore salvataggio acquisizione: ' + (e.message || e)); });\n                    else fetch((window.tiAjaxUrl || window.tiUrl), {method:'POST', body:fd, credentials:'same-origin'}).then(r=>r.json()).then(done).catch(function(e){ if (window.tiAlert) window.tiAlert('Errore salvataggio acquisizione: ' + (e.message || e)); });\n                } catch(e) { if (window.tiAlert) window.tiAlert('Errore conferma acquisizione: ' + (e.message || e)); }\n            };\n            window.purchaseSendToChat = function(){ window.confirmPurchaseRows(); };\n            if (!window.__tiPurchaseDelegated181) {\n                document.addEventListener('click', function(ev){\n                    const btn = ev.target && ev.target.closest ? ev.target.closest('.ti-ss-add-manual-row') : null;\n                    if (!btn) return;\n                    ev.preventDefault();\n                    window.addPurchaseManualRow();\n                });\n                window.__tiPurchaseDelegated181 = true;\n            }\n\n            window.normalizeActivityOutcome = function(row){\n                row = row || {};\n                const current = String(row.Esito || '').trim();\n                if (\/vendit\/i.test(current)) return 'vendita';\n                if (\/acquist\/i.test(current)) return 'acquisizione';\n                if (\/configur\/i.test(current)) return current || 'configurazione';\n                if (\/appunt\/i.test(current)) return current || 'appuntamento';\n                const txt = String(row.Descrizione || row.Dettaglio || row.Note || row.Nota || '').toLowerCase();\n                if (\/acquist|fornitor\/.test(txt)) return current || 'acquisizione';\n                if (\/appuntament|prenotaz\/.test(txt)) return current || 'appuntamento';\n                if (\/configur|setup|modifica database\/.test(txt)) return current || 'configurazione';\n                const price = parseNumSafe(row.Prezzo || row.Totale || 0);\n                const hasProduct = String(row.Prodotto || row.Servizio || '').trim() !== '';\n                if (!current && (hasProduct || price > 0)) return 'vendita';\n                return current;\n            };\n            if (typeof window.getActivityRowsNormalized === 'function' && !window.__tiActivityOutcomeWrap181) {\n                const oldRowsNorm = window.getActivityRowsNormalized;\n                window.getActivityRowsNormalized = function(){\n                    const arr = oldRowsNorm.apply(this, arguments) || [];\n                    return arr.map(function(r){ if (r && typeof r === 'object') r.Esito = window.normalizeActivityOutcome(r); return r; });\n                };\n                window.__tiActivityOutcomeWrap181 = true;\n            }\n            window.activityOutcomeStorageKey = function(){\n                const u = window.tiUser || 'guest';\n                const db = (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : (window.tiForced || 'global');\n                return 'ti_activity_report_outcome_' + u + '_' + db;\n            };\n            window.loadActivityOutcomeFilter = function(){ try { return localStorage.getItem(window.activityOutcomeStorageKey()) || ''; } catch(e) { return ''; } };\n            window.saveActivityOutcomeFilter = function(v){ try { localStorage.setItem(window.activityOutcomeStorageKey(), String(v || '')); } catch(e) {} };\n            window.setActivityOutcomeFilter = function(v){\n                if (!window.activityReportState) window.activityReportState = {};\n                window.activityReportState.outcomeFilter = String(v || '');\n                window.saveActivityOutcomeFilter(window.activityReportState.outcomeFilter);\n                window.renderActivityReport();\n            };\n            if (typeof window.getFilteredActivityRows === 'function' && !window.__tiActivityFilterWrap181) {\n                const oldFilteredRows = window.getFilteredActivityRows;\n                window.getFilteredActivityRows = function(includeExcludedRows){\n                    let rows = oldFilteredRows.apply(this, arguments) || [];\n                    const f = window.activityReportState ? String(window.activityReportState.outcomeFilter || '') : '';\n                    if (f) rows = rows.filter(function(r){ return String(r.Esito || '') === f; });\n                    return rows;\n                };\n                window.__tiActivityFilterWrap181 = true;\n            }\n            window.injectActivityOutcomeFilter = function(){\n                try {\n                    const toolbar = document.querySelector('#ti-report-cnt .ti-report-toolbar');\n                    if (!toolbar || toolbar.querySelector('.ti-report-outcome-filter-wrap')) return;\n                    const rawRows = (window.getActivityRowsNormalized ? window.getActivityRowsNormalized() : []);\n                    const opts = [];\n                    rawRows.forEach(function(r){ const e = String(r && r.Esito ? r.Esito : '').trim(); if (e && opts.indexOf(e) === -1) opts.push(e); });\n                    opts.sort(function(a,b){ return a.localeCompare(b, 'it'); });\n                    const current = window.activityReportState ? String(window.activityReportState.outcomeFilter || '') : '';\n                    const label = document.createElement('label');\n                    label.className = 'ti-report-outcome-filter-wrap';\n                    let html = '<span>Esito<\/span><select class=\"ti-report-outcome-filter\" onchange=\"window.setActivityOutcomeFilter(this.value)\"><option value=\"\">Tutti<\/option><option value=\"__empty__\" ' + (current === '__empty__' ? 'selected' : '') + '>Senza esito<\/option>';\n                    opts.forEach(function(o){ html += '<option value=\"' + escapeSafe(o) + '\" ' + (o === current ? 'selected' : '') + '>' + escapeSafe(o) + '<\/option>'; });\n                    if (current && current !== '__empty__' && opts.indexOf(current) === -1) html += '<option value=\"' + escapeSafe(current) + '\" selected>' + escapeSafe(current) + '<\/option>';\n                    html += '<\/select>';\n                    label.innerHTML = html;\n                    toolbar.appendChild(label);\n                } catch(e) {}\n            };\n            if (typeof window.renderActivityReport === 'function' && !window.__tiActivityRenderWrap181) {\n                const oldRender = window.renderActivityReport;\n                window.renderActivityReport = function(){ oldRender.apply(this, arguments); window.injectActivityOutcomeFilter(); };\n                window.__tiActivityRenderWrap181 = true;\n            }\n            if (typeof window.openActivityReport === 'function' && !window.__tiActivityOpenWrap181) {\n                const oldOpenReport = window.openActivityReport;\n                window.openActivityReport = function(){\n                    oldOpenReport.apply(this, arguments);\n                    if (!window.activityReportState) window.activityReportState = {};\n                    window.activityReportState.outcomeFilter = window.loadActivityOutcomeFilter();\n                    window.renderActivityReport();\n                };\n                window.__tiActivityOpenWrap181 = true;\n            }\n\n            window.drawPie3D = function(canvasId, rows) {\n                const cv = byId(canvasId); if(!cv) return; const ctx = cv.getContext('2d');\n                const selectedMetrics = window.getActivitySelectedMetrics ? window.getActivitySelectedMetrics() : ['Totale'];\n                const groupField = window.getActivitySelectedGroupField ? window.getActivitySelectedGroupField() : 'Username';\n                const metrics = (Array.isArray(selectedMetrics) && selectedMetrics.length) ? selectedMetrics : ['Totale'];\n                const cols = 1;\n                const blockW = 780;\n                const blockH = 310;\n                cv.width = Math.max(900, blockW + 40);\n                cv.height = Math.max(360, metrics.length * blockH + 40);\n                ctx.clearRect(0,0,cv.width,cv.height);\n                const colors=['#38bdf8','#22c55e','#f59e0b','#ef4444','#a855f7','#14b8a6','#f97316','#06b6d4','#84cc16','#fb7185','#60a5fa','#e879f9'];\n                const buildEntriesForMetric = function(metric) {\n                    const groups = {};\n                    rows.forEach(function(r){\n                        const label = String(r[groupField] || 'N\/D').trim() || 'N\/D';\n                        const value = parseNumSafe(r[metric]) || 0;\n                        groups[label] = (groups[label] || 0) + value;\n                    });\n                    return Object.entries(groups).filter(function(e){ return e[1] > 0; }).sort(function(a,b){ return b[1]-a[1]; }).slice(0, 14);\n                };\n                let drew = false;\n                metrics.forEach(function(metric, metricIndex){\n                    const entries = buildEntriesForMetric(metric);\n                    const originX = 20;\n                    const originY = 20 + metricIndex * blockH;\n                    const cx = originX + 145;\n                    const cy = originY + 145;\n                    const rad = 82;\n                    const depth = 16;\n                    const legendX = originX + 280;\n                    ctx.fillStyle = '#fff'; ctx.font = 'bold 14px Arial'; ctx.fillText(window.getActivityMetricLabel ? window.getActivityMetricLabel([metric]) : metric, originX + 8, originY + 20);\n                    if (!entries.length) { ctx.fillStyle = '#cbd5e1'; ctx.font = '12px Arial'; ctx.fillText('Nessun dato', originX + 8, originY + 48); return; }\n                    drew = true;\n                    const total = entries.reduce(function(a,b){ return a+b[1]; },0);\n                    let start = -Math.PI\/2;\n                    entries.forEach(function(entry,i){\n                        const ang = total > 0 ? (entry[1]\/total)*Math.PI*2 : 0;\n                        ctx.fillStyle='rgba(0,0,0,0.35)'; ctx.beginPath(); ctx.moveTo(cx, cy+depth); ctx.arc(cx, cy+depth, rad, start, start+ang); ctx.closePath(); ctx.fill();\n                        ctx.fillStyle=colors[i%colors.length]; ctx.beginPath(); ctx.moveTo(cx, cy); ctx.arc(cx, cy, rad, start, start+ang); ctx.closePath(); ctx.fill();\n                        start += ang;\n                    });\n                    entries.forEach(function(entry,i){\n                        const pct = total > 0 ? (entry[1] \/ total * 100) : 0;\n                        const ly = originY + 44 + i * 18;\n                        ctx.fillStyle = colors[i%colors.length]; ctx.fillRect(legendX, ly - 10, 11, 11);\n                        ctx.fillStyle = '#fff'; ctx.font = '11px Arial';\n                        const label = String(entry[0]).length > 30 ? String(entry[0]).slice(0, 29) + '\u2026' : String(entry[0]);\n                        ctx.fillText(label + ' - ' + fmtSafe(entry[1]) + ' (' + fmtSafe(pct) + '%)', legendX + 18, ly);\n                    });\n                    ctx.fillStyle = '#94a3b8'; ctx.font = '11px Arial'; ctx.fillText('Raggruppa per: ' + groupField, originX + 8, originY + blockH - 12);\n                });\n                if (!drew) { ctx.fillStyle='#fff'; ctx.font='14px Arial'; ctx.fillText('Nessun dato',20,20); }\n            };\n            window.drawTrend3D = function(canvasId, rows) {\n                const cv = byId(canvasId); if(!cv) return; const ctx = cv.getContext('2d');\n                const selectedMetrics = window.getActivitySelectedMetrics ? window.getActivitySelectedMetrics() : ['Totale'];\n                const groupField = window.getActivitySelectedGroupField ? window.getActivitySelectedGroupField() : 'Username';\n                const metrics = (Array.isArray(selectedMetrics) && selectedMetrics.length) ? selectedMetrics : ['Totale'];\n                const blockW = 860;\n                const blockH = 330;\n                cv.width = Math.max(980, blockW + 40);\n                cv.height = Math.max(380, metrics.length * blockH + 40);\n                ctx.clearRect(0,0,cv.width,cv.height);\n                const colors=['#38bdf8','#22c55e','#f59e0b','#ef4444','#a855f7','#14b8a6','#60a5fa','#e879f9'];\n                const buildMetricTimeline = function(metric) {\n                    const timeline = {}; const groupTotals = {};\n                    rows.forEach(function(r){\n                        const timeKey = (r.Data || '') + ' ' + (r.Ora || '00:00');\n                        const grp = String(r[groupField] || 'N\/D').trim() || 'N\/D';\n                        const value = parseNumSafe(r[metric]) || 0;\n                        if (!timeline[timeKey]) timeline[timeKey] = {};\n                        timeline[timeKey][grp] = (timeline[timeKey][grp] || 0) + value;\n                        groupTotals[grp] = (groupTotals[grp] || 0) + value;\n                    });\n                    const entries = Object.entries(timeline).sort(function(a,b){ return a[0].localeCompare(b[0]); }).slice(-12);\n                    const groups = Object.entries(groupTotals).sort(function(a,b){ return b[1]-a[1]; }).slice(0,6).map(function(e){ return e[0]; });\n                    return {entries:entries, groups:groups, totals:groupTotals};\n                };\n                let drew = false;\n                metrics.forEach(function(metric, metricIndex){\n                    const data = buildMetricTimeline(metric);\n                    const entries = data.entries; const groups = data.groups;\n                    const originX = 20; const originY = 20 + metricIndex * blockH;\n                    const chartW = 610;\n                    const legendX = originX + chartW + 28;\n                    ctx.fillStyle = '#fff'; ctx.font = 'bold 14px Arial'; ctx.fillText(window.getActivityMetricLabel ? window.getActivityMetricLabel([metric]) : metric, originX + 8, originY + 20);\n                    if (!entries.length || !groups.length) { ctx.fillStyle='#cbd5e1'; ctx.font='12px Arial'; ctx.fillText('Nessun dato', originX+8, originY+48); return; }\n                    drew = true;\n                    const max = Math.max(1, ...entries.flatMap(function(e){ return groups.map(function(g){ return e[1][g] || 0; }); }));\n                    const baseY = originY + 250;\n                    const left = originX + 52;\n                    const step = Math.max(36, Math.floor((chartW - 70) \/ Math.max(1, entries.length)));\n                    const barW = Math.max(6, Math.floor(28 \/ Math.max(1, groups.length)));\n                    ctx.strokeStyle='#94a3b8'; ctx.beginPath(); ctx.moveTo(left, originY + 38); ctx.lineTo(left, baseY); ctx.lineTo(left + step * entries.length, baseY); ctx.stroke();\n                    entries.forEach(function(entry, i){\n                        const label = entry[0]; const metricsAtTime = entry[1];\n                        const groupX = left + 10 + i * step;\n                        groups.forEach(function(grp, gi){\n                            const val = metricsAtTime[grp] || 0;\n                            const h = (val\/max)*170;\n                            const x = groupX + gi*(barW+3);\n                            ctx.fillStyle='rgba(0,0,0,0.25)'; ctx.fillRect(x+4, baseY-h+4, barW, h);\n                            ctx.fillStyle=colors[gi%colors.length]; ctx.fillRect(x, baseY-h, barW, h);\n                            if (h > 0) { ctx.fillStyle='#fff'; ctx.font='9px Arial'; ctx.fillText(fmtSafe(val), x-2, baseY-h-6); }\n                        });\n                        ctx.save(); ctx.fillStyle='#fff'; ctx.font='9px Arial'; ctx.translate(groupX, baseY+14); ctx.rotate(-0.6); ctx.fillText(String(label).slice(0,16),0,0); ctx.restore();\n                    });\n                    groups.forEach(function(grp, gi){\n                        const ly = originY + 46 + gi * 20;\n                        ctx.fillStyle = colors[gi%colors.length]; ctx.fillRect(legendX, ly - 10, 12, 12);\n                        ctx.fillStyle = '#fff'; ctx.font = '11px Arial';\n                        const label = String(grp).length > 25 ? String(grp).slice(0,24) + '\u2026' : String(grp);\n                        ctx.fillText(label + ' - ' + fmtSafe(data.totals[grp] || 0), legendX + 18, ly);\n                    });\n                    ctx.fillStyle = '#cbd5e1'; ctx.font = '11px Arial'; ctx.fillText('Raggruppa per: ' + groupField, originX + 8, originY + blockH - 12);\n                });\n                if (!drew) { ctx.fillStyle='#fff'; ctx.font='14px Arial'; ctx.fillText('Nessun dato',20,20); }\n            };\n        } catch(e) {\n            try { console.error('Shop & Service fix 30.9.664 non applicato:', e); } catch(ignore) {}\n        }\n        }\n        if (document.readyState === 'loading') {\n            document.addEventListener('DOMContentLoaded', function(){ setTimeout(tiInstallFixPersi181, 0); });\n        } else {\n            setTimeout(tiInstallFixPersi181, 0);\n        }\n    })();\n\n\n    \/* 30.9.664 - Fix chirurgici: gestione tabella acquisizioni + Report attivita *\/\n    (function(){\n        function tiInstallPurchaseReport182(){\n            try {\n                const byId = function(id){ return document.getElementById(id); };\n                const esc = function(v){\n                    if (window.escapeHtml) return window.escapeHtml(String(v == null ? '' : v));\n                    return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; });\n                };\n                const parseNum = function(v){\n                    if (v === null || v === undefined) return 0;\n                    let s = String(v).trim();\n                    if (!s) return 0;\n                    s = s.replace(\/[\u20ac\\s]\/g, '');\n                    if (s.indexOf(',') !== -1 && s.indexOf('.') !== -1) s = s.replace(\/\\.\/g, '').replace(',', '.');\n                    else if (s.indexOf(',') !== -1) s = s.replace(',', '.');\n                    const n = parseFloat(s.replace(\/[^0-9.\\-]\/g, ''));\n                    return isNaN(n) ? 0 : n;\n                };\n                const fmt = function(n){\n                    const num = Math.round((parseFloat(n) || 0) * 100) \/ 100;\n                    if (window.fmtNum) return window.fmtNum(num);\n                    return num.toLocaleString('it-IT', {minimumFractionDigits:2, maximumFractionDigits:2});\n                };\n                const norm = function(s){\n                    return String(s || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').toLowerCase().replace(\/[^a-z0-9]\/g, '');\n                };\n                const realTableName = function(data, wanted, fallback){\n                    const tables = data && data.Tabelle ? data.Tabelle : {};\n                    const want = norm(wanted);\n                    const keys = Object.keys(tables || {});\n                    for (let i=0;i<keys.length;i++) if (norm(keys[i]) === want) return keys[i];\n                    for (let i=0;i<keys.length;i++) if (norm(keys[i]).indexOf(want) !== -1 || want.indexOf(norm(keys[i])) !== -1) return keys[i];\n                    return fallback || wanted;\n                };\n                const getField = function(row, names, fallback){\n                    row = row || {};\n                    const keys = Object.keys(row);\n                    for (let i=0;i<names.length;i++) {\n                        const want = norm(names[i]);\n                        for (let j=0;j<keys.length;j++) if (norm(keys[j]) === want) return row[keys[j]];\n                    }\n                    return fallback;\n                };\n                const setField = function(row, names, value){\n                    row = row || {};\n                    const keys = Object.keys(row);\n                    for (let i=0;i<names.length;i++) {\n                        const want = norm(names[i]);\n                        for (let j=0;j<keys.length;j++) if (norm(keys[j]) === want) { row[keys[j]] = value; return; }\n                    }\n                    row[names[0]] = value;\n                };\n                const marginNet = function(costNet, marginText){\n                    const cost = parseNum(costNet);\n                    let m = String(marginText || '').trim();\n                    if (!m) return Math.max(0, cost);\n                    const isPct = m.indexOf('%') !== -1;\n                    m = m.replace(\/[%\u20ac\\s]\/g, '');\n                    let sign = 1;\n                    if (m.charAt(0) === '-') { sign = -1; m = m.slice(1); }\n                    else if (m.charAt(0) === '+') { m = m.slice(1); }\n                    const val = parseNum(m);\n                    return Math.max(0, isPct ? (cost + (cost * sign * val \/ 100)) : (cost + sign * val));\n                };\n                const grossSale = function(costNet, marginText, ivaText){\n                    let iva = parseNum(ivaText);\n                    if (iva <= 0) iva = 22;\n                    return Math.round(marginNet(costNet, marginText) * (1 + iva \/ 100) * 100) \/ 100;\n                };\n                const dateParts = function(){\n                    const d = new Date();\n                    return {\n                        date:String(d.getDate()).padStart(2,'0') + '\/' + String(d.getMonth()+1).padStart(2,'0') + '\/' + d.getFullYear(),\n                        time:String(d.getHours()).padStart(2,'0') + ':' + String(d.getMinutes()).padStart(2,'0')\n                    };\n                };\n                const base64Utf8 = function(str){\n                    try { return btoa(unescape(encodeURIComponent(str))); }\n                    catch(e) { return btoa(str); }\n                };\n                window.tiPurchaseCalcSaleGross = grossSale;\n                window.tiPurchaseMarginAdjustedNet = marginNet;\n\n                window.purchaseGetCatalogItems = function(){\n                    const data = window.currentDbData || {};\n                    const tables = data && data.Tabelle ? data.Tabelle : {};\n                    const out = [];\n                    Object.keys(tables || {}).forEach(function(tn){\n                        if (!\/(prodott|serviz)\/i.test(tn)) return;\n                        const rows = Array.isArray(tables[tn]) ? tables[tn] : [];\n                        rows.forEach(function(r, idx){\n                            if (!r || typeof r !== 'object') return;\n                            const isServ = \/(serviz)\/i.test(tn);\n                            const name = String(getField(r, isServ ? ['Servizio','Descrizione','Nome','Titolo'] : ['Prodotto','Descrizione','Nome','Titolo'], '') || '').trim();\n                            if (!name) return;\n                            out.push({\n                                table:tn,\n                                index:idx,\n                                type:isServ ? 'S' : 'P',\n                                typeLabel:isServ ? 'servizio' : 'prodotto',\n                                name:name,\n                                desc:String(getField(r, ['Descrizione','Note','Nota'], '') || '').trim(),\n                                cost:parseNum(getField(r, ['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'], 0)),\n                                sale:parseNum(getField(r, ['Prezzo','Prezzo vendita','Prezzo_vendita'], 0)),\n                                iva:parseNum(getField(r, ['IVA_VAT','IVA','Iva','VAT'], 22)) || 22\n                            });\n                        });\n                    });\n                    return out;\n                };\n\n                window.ensurePurchaseModal = function(){\n                    let ov = byId('ti-purchase-ov');\n                    if (ov && ov.dataset && ov.dataset.v182 === '1') return ov;\n                    if (ov && ov.parentNode) ov.parentNode.removeChild(ov);\n                    ov = document.createElement('div');\n                    ov.id = 'ti-purchase-ov';\n                    ov.className = 'ti-modal-overlay ti-ov ti-closable-ov';\n                    ov.dataset.v182 = '1';\n                    ov.style.display = 'none';\n                    ov.style.zIndex = '2147482000';\n                    ov.style.alignItems = 'center';\n                    ov.style.justifyContent = 'center';\n                    ov.innerHTML = `\n                        <div class=\"ti-modal ti-purchase-modal\" style=\"width:min(1480px,98vw);max-width:98vw;max-height:92vh;overflow:auto;background:#0f172a;color:#fff;border-radius:16px;padding:18px;box-shadow:0 18px 60px rgba(0,0,0,.45);margin:0 auto;\">\n                            <div style=\"display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;\">\n                                <div>\n                                    <h2 style=\"margin:0;font-size:22px;\">Acquisizioni<\/h2>\n                                    <div style=\"font-size:14px;color:#cbd5e1;margin-top:4px;\">Gestisci le righe acquisizione. Il costo \u00e8 netto IVA; il prezzo vendita proposto include margine e IVA.<\/div>\n                                <\/div>\n                                <button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"window.closePurchaseManualFlow()\">X<\/button>\n                            <\/div>\n                            <div style=\"display:flex;gap:8px;flex-wrap:wrap;margin:10px 0 14px 0;\">\n                                <button type=\"button\" class=\"ti-btn ti-purchase-add-row\" style=\"background:#22c55e;color:#052e16;font-weight:700;\">+ Aggiungi riga manuale<\/button>\n                                <button type=\"button\" class=\"ti-btn\" style=\"background:#f59e0b;color:#111827;font-weight:700;\" onclick=\"window.pickPurchaseImportFile()\">Import file acquisizioni<\/button>\n                                <button type=\"button\" class=\"ti-btn\" style=\"background:#64748b;color:#fff;font-weight:700;\" onclick=\"window.clearPurchaseRows()\">Svuota righe<\/button>\n                            <\/div>\n                            <div class=\"ti-purchase-table-wrap\" style=\"overflow:auto;border:1px solid #334155;border-radius:12px;\">\n                                <table id=\"ti-purchase-table\" style=\"width:100%;border-collapse:collapse;min-width:1240px;background:#020617;\">\n                                    <thead>\n                                        <tr style=\"background:#1e293b;\">\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Prodotto o servizio<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Tipo<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Q.t\u00e0<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Costo netto<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Margine<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Prezzo vendita IVA incl.<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">IVA %<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Tot. acquisizione netta<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Tot. vendita<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Note<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:center;\">Azioni<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody id=\"ti-purchase-body\"><\/tbody>\n                                <\/table>\n                            <\/div>\n                            <div id=\"ti-purchase-total\" class=\"ti-purchase-summary-grid\" aria-live=\"polite\"><\/div>\n                            <div class=\"ti-purchase-toolbar-fixed\" style=\"display:flex;justify-content:center;gap:8px;margin-top:14px;flex-wrap:wrap;\">\n                                <button type=\"button\" id=\"ti-purchase-confirm-btn\" class=\"ti-btn\" style=\"background:#38bdf8;color:#082f49;font-weight:800;\" onclick=\"window.confirmPurchaseRows()\">Conferma righe acquisizione<\/button>\n                                <button type=\"button\" class=\"ti-btn btn-close\" onclick=\"window.closePurchaseManualFlow()\">Chiudi<\/button>\n                            <\/div>\n                        <\/div>`;\n                    document.body.appendChild(ov);\n                    return ov;\n                };\n\n                window.clearPurchaseRows = function(){\n                    const body = byId('ti-purchase-body');\n                    if (!body) return;\n                    if (body.children.length && window.confirm && !window.confirm('Svuotare tutte le righe acquisizione?')) return;\n                    body.innerHTML = '';\n                    window.addPurchaseManualRow();\n                    window.refreshPurchaseTotals();\n                };\n\n                window.recalcPurchaseRow = function(row, force){\n                    try {\n                        if (!row) return;\n                        const qty = parseNum((row.querySelector('.ti-pur-qty') || {}).value || '0');\n                        const costEl = row.querySelector('.ti-pur-cost');\n                        const marginEl = row.querySelector('.ti-pur-margin');\n                        const saleEl = row.querySelector('.ti-pur-sale');\n                        const ivaEl = row.querySelector('.ti-pur-iva');\n                        const cost = parseNum(costEl ? costEl.value : 0);\n                        const sale = grossSale(cost, marginEl ? marginEl.value : '', ivaEl ? ivaEl.value : '22');\n                        if (saleEl && (force || document.activeElement !== saleEl || saleEl.dataset.auto === '1')) {\n                            saleEl.value = sale ? String(sale).replace('.', ',') : '';\n                            saleEl.dataset.auto = '1';\n                        }\n                        const rowSale = saleEl ? (parseNum(saleEl.value) || sale) : sale;\n                        const netTot = Math.round(qty * cost * 100) \/ 100;\n                        const grossTot = Math.round(qty * rowSale * 100) \/ 100;\n                        const netOut = row.querySelector('.ti-pur-row-total-net');\n                        const grossOut = row.querySelector('.ti-pur-row-total-gross');\n                        if (netOut) netOut.textContent = fmt(netTot);\n                        if (grossOut) grossOut.textContent = fmt(grossTot);\n                    } catch(e) {}\n                };\n\n                window.validatePurchaseRows = function(mark){\n                    const errors = [];\n                    document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr, idx){\n                        tr.classList.remove('is-invalid');\n                        const n = String((tr.querySelector('.ti-pur-name') || {}).value || '').trim();\n                        const q = parseNum((tr.querySelector('.ti-pur-qty') || {}).value || '0');\n                        const c = parseNum((tr.querySelector('.ti-pur-cost') || {}).value || '0');\n                        const ivaEl = tr.querySelector('.ti-pur-iva');\n                        if (ivaEl && !String(ivaEl.value || '').trim()) ivaEl.value = '22';\n                        const bad = !n || q <= 0 || c < 0;\n                        if (bad) {\n                            if (mark) tr.classList.add('is-invalid');\n                            if (!n) errors.push('Riga ' + (idx + 1) + ': prodotto\/servizio mancante');\n                            if (q <= 0) errors.push('Riga ' + (idx + 1) + ': quantit\u00e0 non valida');\n                            if (c < 0) errors.push('Riga ' + (idx + 1) + ': costo non valido');\n                        }\n                    });\n                    return errors;\n                };\n\n                window.addPurchaseManualRow = function(item){\n                    try {\n                        const ov = byId('ti-purchase-ov') || window.ensurePurchaseModal();\n                        const body = byId('ti-purchase-body');\n                        if (!body) return;\n                        if (ov) ov.style.display = 'flex';\n                        const safe = item || {};\n                        const tr = document.createElement('tr');\n                        tr.className = 'ti-purchase-row';\n                        tr.dataset.catalogTable = safe.table || '';\n                        tr.dataset.catalogIndex = String(safe.index != null ? safe.index : -1);\n                        tr.dataset.targetType = safe.type === 'S' ? 'servizio' : 'prodotto';\n                        const iva = safe.iva || '22';\n                        const cost = safe.cost ? String(safe.cost).replace('.', ',') : '';\n                        const sale = safe.sale ? String(safe.sale).replace('.', ',') : '';\n                        tr.innerHTML = `\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-name ti-purchase-row-search\" value=\"${esc(safe.name || '')}\" placeholder=\"Cerca prodotto o servizio\" autocomplete=\"off\" style=\"width:100%;min-width:230px;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><select class=\"ti-pur-type\" style=\"width:76px;padding:8px;border-radius:8px;background:#0f172a;color:#fff;border:1px solid #334155;\"><option value=\"P\" ${safe.type === 'S' ? '' : 'selected'}>P<\/option><option value=\"S\" ${safe.type === 'S' ? 'selected' : ''}>S<\/option><\/select><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-qty\" value=\"${esc(safe.qty || '1')}\" inputmode=\"decimal\" style=\"width:72px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-cost\" value=\"${esc(cost)}\" inputmode=\"decimal\" style=\"width:100px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-margin\" value=\"${esc(safe.margin || '')}\" placeholder=\"+10%\" style=\"width:86px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-sale\" data-auto=\"${sale ? '0' : '1'}\" value=\"${esc(sale)}\" inputmode=\"decimal\" style=\"width:112px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-iva\" value=\"${esc(iva)}\" inputmode=\"decimal\" style=\"width:68px;text-align:right;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:right;\"><span class=\"ti-pur-row-total-net\">0,00<\/span><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:right;\"><span class=\"ti-pur-row-total-gross\">0,00<\/span><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-note\" value=\"${esc(safe.desc || '')}\" placeholder=\"Note\" style=\"width:100%;min-width:160px;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #334155;background:#0f172a;color:#fff;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:center;white-space:nowrap;\"><button type=\"button\" class=\"ti-btn ti-pur-dup\" title=\"Duplica riga\" style=\"background:#2563eb;color:#fff;margin-right:4px;\">\u2197<\/button><button type=\"button\" class=\"ti-btn ti-pur-del\" title=\"Elimina riga\" style=\"background:#dc2626;color:#fff;\">X<\/button><\/td>`;\n                        body.appendChild(tr);\n                        const name = tr.querySelector('.ti-pur-name');\n                        if (name) {\n                            name.addEventListener('input', function(){ window.__tiPurchaseActiveRow = tr; window.showPurchaseRowSearch(name); });\n                            name.addEventListener('focus', function(){ window.__tiPurchaseActiveRow = tr; if (String(name.value || '').trim()) window.showPurchaseRowSearch(name); });\n                        }\n                        const typeEl = tr.querySelector('.ti-pur-type');\n                        if (typeEl) typeEl.addEventListener('change', function(){ tr.dataset.targetType = this.value === 'S' ? 'servizio' : 'prodotto'; });\n                        ['.ti-pur-qty','.ti-pur-cost','.ti-pur-margin','.ti-pur-iva'].forEach(function(sel){\n                            const el = tr.querySelector(sel);\n                            if (el) el.addEventListener('input', function(){\n                                const saleEl = tr.querySelector('.ti-pur-sale');\n                                if (saleEl) saleEl.dataset.auto = '1';\n                                window.recalcPurchaseRow(tr, true);\n                                window.refreshPurchaseTotals();\n                            });\n                        });\n                        const saleEl = tr.querySelector('.ti-pur-sale');\n                        if (saleEl) saleEl.addEventListener('input', function(){ saleEl.dataset.auto = '0'; window.recalcPurchaseRow(tr, false); window.refreshPurchaseTotals(); });\n                        const del = tr.querySelector('.ti-pur-del');\n                        if (del) del.addEventListener('click', function(){ tr.remove(); if (!body.children.length) window.addPurchaseManualRow(); window.refreshPurchaseTotals(); });\n                        const dup = tr.querySelector('.ti-pur-dup');\n                        if (dup) dup.addEventListener('click', function(){ window.addPurchaseManualRow(window.purchaseRowToItem(tr)); });\n                        window.recalcPurchaseRow(tr, !sale);\n                        window.refreshPurchaseTotals();\n                        if (!safe.name && name) { try { name.focus(); } catch(ignore) {} }\n                    } catch(e) { if (window.tiAlert) window.tiAlert('Errore aggiunta riga acquisizione: ' + (e.message || e)); }\n                };\n\n                window.purchaseRowToItem = function(tr){\n                    tr = tr || {};\n                    return {\n                        table:tr.dataset ? (tr.dataset.catalogTable || '') : '',\n                        index:tr.dataset ? parseInt(tr.dataset.catalogIndex || '-1', 10) : -1,\n                        type:String(((tr.querySelector && tr.querySelector('.ti-pur-type')) || {}).value || 'P'),\n                        name:String(((tr.querySelector && tr.querySelector('.ti-pur-name')) || {}).value || ''),\n                        qty:String(((tr.querySelector && tr.querySelector('.ti-pur-qty')) || {}).value || '1'),\n                        cost:String(((tr.querySelector && tr.querySelector('.ti-pur-cost')) || {}).value || ''),\n                        margin:String(((tr.querySelector && tr.querySelector('.ti-pur-margin')) || {}).value || ''),\n                        sale:String(((tr.querySelector && tr.querySelector('.ti-pur-sale')) || {}).value || ''),\n                        iva:String(((tr.querySelector && tr.querySelector('.ti-pur-iva')) || {}).value || '22'),\n                        desc:String(((tr.querySelector && tr.querySelector('.ti-pur-note')) || {}).value || '')\n                    };\n                };\n\n                window.refreshPurchaseTotals = function(){\n                    try {\n                        let rows = 0, qtyTot = 0, netTot = 0, saleTot = 0;\n                        document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                            window.recalcPurchaseRow(tr, false);\n                            const qty = parseNum((tr.querySelector('.ti-pur-qty') || {}).value || '0');\n                            const cost = parseNum((tr.querySelector('.ti-pur-cost') || {}).value || '0');\n                            const sale = parseNum((tr.querySelector('.ti-pur-sale') || {}).value || '0');\n                            rows += 1;\n                            qtyTot += qty;\n                            netTot += qty * cost;\n                            saleTot += qty * sale;\n                        });\n                        const out = byId('ti-purchase-total');\n                        if (out) out.innerHTML = '<div class=\"ti-purchase-summary-card\">Righe<b>' + rows + '<\/b><\/div>'\n                            + '<div class=\"ti-purchase-summary-card\">Quantit\u00e0<b>' + fmt(qtyTot) + '<\/b><\/div>'\n                            + '<div class=\"ti-purchase-summary-card\">Totale acquisizione netta<b>\u20ac ' + fmt(netTot) + '<\/b><\/div>'\n                            + '<div class=\"ti-purchase-summary-card\">Totale vendita IVA incl.<b>\u20ac ' + fmt(saleTot) + '<\/b><\/div>';\n                    } catch(e) {}\n                };\n\n                window.selectPurchaseCatalogItemForRow = function(item, row){\n                    try {\n                        item = item || {};\n                        row = row || window.__tiPurchaseActiveRow;\n                        if (!row) return;\n                        row.dataset.catalogTable = item.table || '';\n                        row.dataset.catalogIndex = String(item.index != null ? item.index : -1);\n                        row.dataset.targetType = item.type === 'S' ? 'servizio' : 'prodotto';\n                        const setVal = function(sel, val){ const el = row.querySelector(sel); if (el) el.value = val == null ? '' : String(val).replace('.', ','); };\n                        setVal('.ti-pur-name', item.name || '');\n                        setVal('.ti-pur-type', item.type === 'S' ? 'S' : 'P');\n                        setVal('.ti-pur-cost', item.cost || '');\n                        setVal('.ti-pur-iva', item.iva || '22');\n                        setVal('.ti-pur-note', item.desc || '');\n                        const sale = row.querySelector('.ti-pur-sale');\n                        if (sale) { sale.dataset.auto = '1'; sale.value = item.sale ? String(item.sale).replace('.', ',') : ''; }\n                        window.recalcPurchaseRow(row, !item.sale);\n                        window.refreshPurchaseTotals();\n                        window.closePurchaseSearchPopover && window.closePurchaseSearchPopover();\n                    } catch(e) {}\n                };\n\n                window.purchaseCollectRows = function(){\n                    const rows = [];\n                    document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                        const item = window.purchaseRowToItem(tr);\n                        item.table = tr.dataset.catalogTable || '';\n                        item.index = parseInt(tr.dataset.catalogIndex || '-1', 10);\n                        if (String(item.name || '').trim()) rows.push(item);\n                    });\n                    return rows;\n                };\n\n                window.confirmPurchaseRows = function(){\n                    const btn = byId('ti-purchase-confirm-btn');\n                    try {\n                        const errors = window.validatePurchaseRows(true);\n                        if (errors.length) { if (window.tiAlert) window.tiAlert('Correggi le righe acquisizione:\\n' + errors.slice(0, 8).join('\\n')); return; }\n                        const rows = window.purchaseCollectRows();\n                        if (!rows.length) { if (window.tiAlert) window.tiAlert('Inserisci almeno una riga acquisizione.'); return; }\n                        const dbEl = byId('ti-ditta');\n                        const dbVal = dbEl ? dbEl.value : '';\n                        if (!dbVal || dbVal === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona prima una ditta.'); return; }\n                        const source = window.currentDbData;\n                        if (!source || !source.Tabelle) { if (window.tiAlert) window.tiAlert('Database non caricato. Riapri Acquisizioni dopo aver selezionato la ditta.'); return; }\n                        if (btn) { btn.disabled = true; btn.dataset.oldText = btn.innerHTML; btn.innerHTML = 'Salvataggio...'; }\n                        const data = JSON.parse(JSON.stringify(source));\n                        const prodTable = realTableName(data, 'Prodotti', 'Prodotti');\n                        const servTable = realTableName(data, 'Servizi', 'Servizi');\n                        const actTable = realTableName(data, 'Attivita', 'Attivita');\n                        if (!data.Tabelle[prodTable]) data.Tabelle[prodTable] = [];\n                        if (!data.Tabelle[servTable]) data.Tabelle[servTable] = [];\n                        if (!data.Tabelle[actTable]) data.Tabelle[actTable] = [];\n                        const now = dateParts();\n                        const nextId = function(table){ return (Array.isArray(data.Tabelle[table]) ? data.Tabelle[table].length : 0) + 1; };\n                        rows.forEach(function(r){\n                            const isServ = String(r.type || 'P') === 'S';\n                            const t = isServ ? servTable : prodTable;\n                            const nameFields = isServ ? ['Servizio','Nome','Descrizione'] : ['Prodotto','Nome','Descrizione'];\n                            const tableRows = Array.isArray(data.Tabelle[t]) ? data.Tabelle[t] : (data.Tabelle[t] = []);\n                            let idx = (r.table === t && r.index >= 0 && tableRows[r.index]) ? r.index : -1;\n                            if (idx < 0) {\n                                const wanted = norm(r.name);\n                                idx = tableRows.findIndex(function(row){ return norm(getField(row, nameFields, '')) === wanted; });\n                            }\n                            let rec;\n                            if (idx >= 0) rec = tableRows[idx];\n                            else { rec = {}; setField(rec, ['ID','Id','id'], nextId(t)); tableRows.push(rec); }\n                            setField(rec, nameFields, r.name);\n                            setField(rec, ['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'], parseNum(r.cost));\n                            setField(rec, ['Margine'], String(r.margin || ''));\n                            const gross = parseNum(r.sale) || grossSale(r.cost, r.margin, r.iva);\n                            setField(rec, ['Prezzo','Prezzo vendita','Prezzo_vendita'], gross);\n                            setField(rec, ['IVA_VAT','IVA','Iva','VAT'], parseNum(r.iva) || 22);\n                            if (r.note) setField(rec, ['Note','Nota','Descrizione'], r.note);\n                            if (!isServ) {\n                                const oldQty = parseNum(getField(rec, ['Quantit\u00e0','Quantita','Disponibilita','Disponibilit\u00e0'], 0));\n                                setField(rec, ['Quantit\u00e0','Quantita'], Math.round((oldQty + parseNum(r.qty)) * 100) \/ 100);\n                            }\n                            const act = {};\n                            setField(act, ['ID','Id','id'], nextId(actTable));\n                            setField(act, ['Username','Utente','user'], window.tiUser || '');\n                            setField(act, ['Descrizione attivit\u00e0','Descrizione attivita','Descrizione','Dettaglio','Note'], 'Acquisizione ' + r.name);\n                            setField(act, ['Prodotto'], r.name);\n                            setField(act, ['Prezzo'], parseNum(r.cost));\n                            setField(act, ['Quantit\u00e0','Quantita'], parseNum(r.qty));\n                            setField(act, ['Totale'], Math.round(parseNum(r.cost) * parseNum(r.qty) * 100) \/ 100);\n                            setField(act, ['Margine'], String(r.margin || ''));\n                            setField(act, ['IVA_VAT','IVA','Iva','VAT'], parseNum(r.iva) || 22);\n                            setField(act, ['Prezzo vendita','Prezzo_vendita'], gross);\n                            setField(act, ['Esito'], 'acquisizione');\n                            setField(act, ['Data'], now.date);\n                            setField(act, ['Ora'], now.time);\n                            setField(act, ['Stato'], 'Attivo');\n                            data.Tabelle[actTable].push(act);\n                        });\n                        const fd = new FormData();\n                        fd.append('ti_action', 'ti_ai_save_full_db');\n                        fd.append('db', dbVal);\n                        fd.append('payload', base64Utf8(JSON.stringify(data)));\n                        const done = function(res){\n                            if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText || 'Conferma righe acquisizione'; }\n                            if (res && res.success) {\n                                window.currentDbData = data;\n                                window.closePurchaseManualFlow && window.closePurchaseManualFlow();\n                                if (window.tiAlert) window.tiAlert('\u2705 Righe acquisizione confermate e salvate nel database.');\n                                if (res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); }, 900);\n                            } else {\n                                const msg = (res && res.data && res.data.message) ? res.data.message : 'Salvataggio acquisizione non riuscito.';\n                                if (window.tiAlert) window.tiAlert('\u26a0\ufe0f ' + msg);\n                            }\n                        };\n                        const fail = function(e){\n                            if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText || 'Conferma righe acquisizione'; }\n                            if (window.tiAlert) window.tiAlert('Errore salvataggio acquisizione: ' + ((e && e.message) || e));\n                        };\n                        const url = window.tiAjaxUrl || window.tiUrl;\n                        if (window.fetchJsonSafe) window.fetchJsonSafe(url, {method:'POST', body:fd, credentials:'same-origin'}).then(done).catch(fail);\n                        else fetch(url, {method:'POST', body:fd, credentials:'same-origin'}).then(function(r){ return r.json(); }).then(done).catch(fail);\n                    } catch(e) {\n                        if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText || 'Conferma righe acquisizione'; }\n                        if (window.tiAlert) window.tiAlert('Errore conferma acquisizione: ' + (e.message || e));\n                    }\n                };\n                window.purchaseSendToChat = function(){ window.confirmPurchaseRows(); };\n\n                if (!window.__tiPurchaseDelegated182) {\n                    document.addEventListener('click', function(ev){\n                        const btn = ev.target && ev.target.closest ? ev.target.closest('.ti-purchase-add-row,.ti-ss-add-manual-row') : null;\n                        if (!btn) return;\n                        ev.preventDefault();\n                        ev.stopPropagation();\n                        if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                        window.addPurchaseManualRow();\n                    }, true);\n                    window.__tiPurchaseDelegated182 = true;\n                }\n\n                window.ti182NormalizeActivityOutcome = function(row){\n                    row = row || {};\n                    const current = String(row.Esito || '').trim();\n                    if (\/vendit\/i.test(current)) return 'vendita';\n                    if (\/acquist\/i.test(current)) return 'acquisizione';\n                    if (\/configur\/i.test(current)) return current || 'configurazione';\n                    if (\/appunt|prenot\/i.test(current)) return current || 'appuntamento';\n                    const txt = String(row.Descrizione || row.Dettaglio || row.Note || row.Nota || row.Istruzioni || '').toLowerCase();\n                    if (\/acquist|fornitor\/.test(txt)) return current || 'acquisizione';\n                    if (\/appuntament|prenotaz\/.test(txt)) return current || 'appuntamento';\n                    if (\/configur|setup|modifica database\/.test(txt)) return current || 'configurazione';\n                    const price = parseNum(row.Prezzo || row.Totale || 0);\n                    const hasProduct = String(row.Prodotto || row.Servizio || '').trim() !== '';\n                    if (!current && (hasProduct || price > 0)) return 'vendita';\n                    return current;\n                };\n                if (typeof window.getActivityRowsNormalized === 'function' && !window.__tiActivityOutcomeWrap182) {\n                    const prevRowsNorm = window.getActivityRowsNormalized;\n                    window.getActivityRowsNormalized = function(){\n                        const arr = prevRowsNorm.apply(this, arguments) || [];\n                        return arr.map(function(r){ if (r && typeof r === 'object') r.Esito = window.ti182NormalizeActivityOutcome(r); return r; });\n                    };\n                    window.__tiActivityOutcomeWrap182 = true;\n                }\n                window.ti182OutcomeMatches = function(row, filter){\n                    filter = String(filter || '');\n                    const val = String(row && row.Esito ? row.Esito : '').trim();\n                    if (!filter) return true;\n                    if (filter === '__empty__') return !val;\n                    return val === filter;\n                };\n                if (typeof window.getActivityRowsNormalized === 'function') {\n                    window.getFilteredActivityRows = function(includeExcludedRows){\n                        let rows = window.getActivityRowsNormalized() || [];\n                        const state = window.activityReportState || {filters:{}, rowFlags:{}};\n                        const rowFlags = state.rowFlags || {};\n                        if (state.excludeConfigActivities && window.userCanSeeConfigurationActivities && window.userCanSeeConfigurationActivities()) rows = rows.filter(function(r){ return !r.__isConfigActivity; });\n                        rows = rows.filter(function(r){\n                            return Object.entries(state.filters || {}).every(function(pair){\n                                const k = pair[0], v = pair[1];\n                                if (!v) return true;\n                                return String(r[k] == null ? '' : r[k]).toLowerCase().indexOf(String(v).toLowerCase()) !== -1;\n                            });\n                        });\n                        const outcomeFilter = String(state.outcomeFilter || '');\n                        if (outcomeFilter) rows = rows.filter(function(r){ return window.ti182OutcomeMatches(r, outcomeFilter); });\n                        if (!includeExcludedRows) rows = rows.filter(function(r){ return !rowFlags[r.__rowKey]; });\n                        const dir = state.sortDir === 'asc' ? 1 : -1;\n                        const col = state.sortCol || 'DataOra';\n                        rows.sort(function(a,b){\n                            const av = col === 'DataOra' ? a.__dataora : a[col];\n                            const bv = col === 'DataOra' ? b.__dataora : b[col];\n                            if (typeof av === 'number' && typeof bv === 'number') return (av - bv) * dir;\n                            return String(av == null ? '' : av).localeCompare(String(bv == null ? '' : bv), 'it', {numeric:true, sensitivity:'base'}) * dir;\n                        });\n                        return rows;\n                    };\n                    window.getActivityIncludedRows = function(){ return window.getFilteredActivityRows(false); };\n                }\n                window.injectActivityOutcomeFilter = function(){\n                    try {\n                        const toolbar = document.querySelector('#ti-report-cnt .ti-report-toolbar');\n                        if (!toolbar) return;\n                        const old = toolbar.querySelector('.ti-report-outcome-filter-wrap');\n                        if (old) old.remove();\n                        const rawRows = window.getActivityRowsNormalized ? window.getActivityRowsNormalized() : [];\n                        const opts = [];\n                        rawRows.forEach(function(r){ const e = String(r && r.Esito ? r.Esito : '').trim(); if (e && opts.indexOf(e) === -1) opts.push(e); });\n                        opts.sort(function(a,b){ return a.localeCompare(b, 'it'); });\n                        const current = window.activityReportState ? String(window.activityReportState.outcomeFilter || '') : '';\n                        const label = document.createElement('label');\n                        label.className = 'ti-report-outcome-filter-wrap';\n                        let html = '<span>Esito<\/span><select class=\"ti-report-outcome-filter\" onchange=\"window.setActivityOutcomeFilter(this.value)\"><option value=\"\">Tutti<\/option><option value=\"__empty__\" ' + (current === '__empty__' ? 'selected' : '') + '>Senza esito<\/option>';\n                        opts.forEach(function(o){ html += '<option value=\"' + esc(o) + '\" ' + (o === current ? 'selected' : '') + '>' + esc(o) + '<\/option>'; });\n                        if (current && current !== '__empty__' && opts.indexOf(current) === -1) html += '<option value=\"' + esc(current) + '\" selected>' + esc(current) + '<\/option>';\n                        html += '<\/select>';\n                        label.innerHTML = html;\n                        toolbar.appendChild(label);\n                    } catch(e) {}\n                };\n                window.setActivityOutcomeFilter = function(v){\n                    if (!window.activityReportState) window.activityReportState = {};\n                    window.activityReportState.outcomeFilter = String(v || '');\n                    try { localStorage.setItem((window.activityOutcomeStorageKey ? window.activityOutcomeStorageKey() : 'ti_activity_report_outcome'), window.activityReportState.outcomeFilter); } catch(e) {}\n                    if (typeof window.renderActivityReport === 'function') window.renderActivityReport();\n                };\n                if (typeof window.renderActivityReport === 'function' && !window.__tiActivityRenderWrap182) {\n                    const oldRender182 = window.renderActivityReport;\n                    window.renderActivityReport = function(){ oldRender182.apply(this, arguments); window.injectActivityOutcomeFilter(); };\n                    window.__tiActivityRenderWrap182 = true;\n                }\n                if (typeof window.openActivityReport === 'function' && !window.__tiActivityOpenWrap182) {\n                    const oldOpen182 = window.openActivityReport;\n                    window.openActivityReport = function(){\n                        oldOpen182.apply(this, arguments);\n                        if (!window.activityReportState) window.activityReportState = {};\n                        try { window.activityReportState.outcomeFilter = localStorage.getItem(window.activityOutcomeStorageKey ? window.activityOutcomeStorageKey() : 'ti_activity_report_outcome') || ''; } catch(e) {}\n                        if (typeof window.renderActivityReport === 'function') window.renderActivityReport();\n                    };\n                    window.__tiActivityOpenWrap182 = true;\n                }\n                if (typeof window.exportActivityReportCsv === 'function' && !window.__tiActivityExportWrap182) {\n                    window.exportActivityReportCsv = function(){\n                        const rows = window.getFilteredActivityRows(false);\n                        const cols = window.getActivityVisibleCols ? window.getActivityVisibleCols() : Object.keys(rows[0] || {});\n                        const csv = cols.join(';') + '\\n' + rows.map(function(r){ return cols.map(function(c){\n                            const raw = (c === 'Prezzo' || c === 'Quantit\u00e0' || c === 'Totale') ? fmt(r[c] || 0) : (r[c] == null ? '' : r[c]);\n                            return '\"' + String(raw).replace(\/\"\/g,'\"\"') + '\"';\n                        }).join(';'); }).join('\\n');\n                        const blob = new Blob([csv], {type:'text\/csv;charset=utf-8;'});\n                        const a = document.createElement('a');\n                        a.href = URL.createObjectURL(blob);\n                        a.download = 'report-attivita.csv';\n                        a.click();\n                        setTimeout(function(){ URL.revokeObjectURL(a.href); }, 500);\n                    };\n                    window.__tiActivityExportWrap182 = true;\n                }\n            } catch(e) {\n                try { console.error('Shop & Service fix 30.9.664 non applicato:', e); } catch(ignore) {}\n            }\n        }\n        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(tiInstallPurchaseReport182, 0); });\n        else setTimeout(tiInstallPurchaseReport182, 0);\n    })();\n\n    \/* 30.9.664 - Fix attivazione Acquisizioni base e tabella manuale *\/\n    (function(){\n        function ti183InstallPurchaseBaseFix(){\n            try {\n                if (window.__tiPurchaseBaseFix183) return;\n                window.__tiPurchaseBaseFix183 = true;\n\n                const byId = function(id){ return document.getElementById(id); };\n                const esc = function(v){\n                    if (window.escapeHtml) return window.escapeHtml(String(v == null ? '' : v));\n                    return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; });\n                };\n                const parseNum = function(v){\n                    if (v === null || v === undefined) return 0;\n                    let s = String(v).trim();\n                    if (!s) return 0;\n                    s = s.replace(\/[\u20ac\\s]\/g, '');\n                    if (s.indexOf(',') !== -1 && s.indexOf('.') !== -1) s = s.replace(\/\\.\/g, '').replace(',', '.');\n                    else if (s.indexOf(',') !== -1) s = s.replace(',', '.');\n                    const n = parseFloat(s.replace(\/[^0-9.\\-]\/g, ''));\n                    return isNaN(n) ? 0 : n;\n                };\n                const fmt = function(n){\n                    const num = Math.round((parseFloat(n) || 0) * 100) \/ 100;\n                    if (window.fmtNum) return window.fmtNum(num);\n                    return num.toLocaleString('it-IT', {minimumFractionDigits:2, maximumFractionDigits:2});\n                };\n                const norm = function(s){\n                    return String(s || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g, '').toLowerCase().replace(\/[^a-z0-9]\/g, '');\n                };\n                const getField = function(row, names, fallback){\n                    row = row || {};\n                    const keys = Object.keys(row);\n                    for (let i=0;i<names.length;i++) {\n                        const wanted = norm(names[i]);\n                        for (let j=0;j<keys.length;j++) if (norm(keys[j]) === wanted) return row[keys[j]];\n                    }\n                    return fallback;\n                };\n                const setField = function(row, names, value){\n                    row = row || {};\n                    const keys = Object.keys(row);\n                    for (let i=0;i<names.length;i++) {\n                        const wanted = norm(names[i]);\n                        for (let j=0;j<keys.length;j++) if (norm(keys[j]) === wanted) { row[keys[j]] = value; return; }\n                    }\n                    row[names[0]] = value;\n                };\n                const realTableName = function(data, wanted, fallback){\n                    const tables = data && data.Tabelle ? data.Tabelle : {};\n                    const wantedNorm = norm(wanted);\n                    const keys = Object.keys(tables || {});\n                    for (let i=0;i<keys.length;i++) if (norm(keys[i]) === wantedNorm) return keys[i];\n                    for (let i=0;i<keys.length;i++) if (norm(keys[i]).indexOf(wantedNorm) !== -1 || wantedNorm.indexOf(norm(keys[i])) !== -1) return keys[i];\n                    return fallback || wanted;\n                };\n                const marginNet = function(costNet, marginText){\n                    const cost = parseNum(costNet);\n                    let m = String(marginText || '').trim();\n                    if (!m) return Math.max(0, cost);\n                    const isPct = m.indexOf('%') !== -1;\n                    m = m.replace(\/[%\u20ac\\s]\/g, '');\n                    let sign = 1;\n                    if (m.charAt(0) === '-') { sign = -1; m = m.slice(1); }\n                    else if (m.charAt(0) === '+') { m = m.slice(1); }\n                    const val = parseNum(m);\n                    return Math.max(0, isPct ? cost + (cost * sign * val \/ 100) : cost + sign * val);\n                };\n                const saleGross = function(costNet, marginText, ivaText){\n                    let iva = parseNum(ivaText);\n                    if (iva <= 0) iva = 22;\n                    return Math.round(marginNet(costNet, marginText) * (1 + iva \/ 100) * 100) \/ 100;\n                };\n                const dateParts = function(){\n                    const d = new Date();\n                    return {\n                        date:String(d.getDate()).padStart(2,'0') + '\/' + String(d.getMonth()+1).padStart(2,'0') + '\/' + d.getFullYear(),\n                        time:String(d.getHours()).padStart(2,'0') + ':' + String(d.getMinutes()).padStart(2,'0')\n                    };\n                };\n                const base64Utf8 = function(str){\n                    try { return btoa(unescape(encodeURIComponent(str))); }\n                    catch(e) { return btoa(str); }\n                };\n\n                function injectPurchaseStyle(){\n                    if (byId('ti-purchase-base-fix-183-css')) return;\n                    const st = document.createElement('style');\n                    st.id = 'ti-purchase-base-fix-183-css';\n                    st.textContent = `\n#ti-purchase-ov.ti-purchase-open{display:flex!important;align-items:center!important;justify-content:center!important;pointer-events:auto!important;}\n#ti-purchase-ov .ti-purchase-modal{width:min(1480px,98vw)!important;max-width:98vw!important;max-height:92vh!important;overflow:auto!important;}\n#ti-purchase-ov .ti-purchase-table-wrap{display:block!important;width:100%!important;max-width:100%!important;overflow-x:auto!important;overflow-y:visible!important;-webkit-overflow-scrolling:touch!important;touch-action:pan-x pan-y!important;border:1px solid #334155!important;border-radius:12px!important;}\n#ti-purchase-ov #ti-purchase-table{display:table!important;width:100%!important;min-width:1180px!important;border-collapse:collapse!important;background:#020617!important;}\n#ti-purchase-ov #ti-purchase-table thead{display:table-header-group!important;}\n#ti-purchase-ov #ti-purchase-table tbody{display:table-row-group!important;}\n#ti-purchase-ov #ti-purchase-table tr{display:table-row!important;}\n#ti-purchase-ov #ti-purchase-table th,#ti-purchase-ov #ti-purchase-table td{display:table-cell!important;white-space:nowrap!important;}\n#ti-purchase-ov .ti-purchase-row input,#ti-purchase-ov .ti-purchase-row select{background:#0f172a!important;color:#fff!important;-webkit-text-fill-color:#fff!important;caret-color:#fff!important;border:1px solid #334155!important;}\n#ti-purchase-ov .ti-purchase-toolbar-fixed{position:sticky!important;bottom:0!important;z-index:5!important;background:#0f172a!important;padding-top:10px!important;border-top:1px solid #334155!important;}\n#ti-purchase-ov .ti-purchase-summary-grid{display:grid!important;grid-template-columns:repeat(4,minmax(120px,1fr))!important;gap:8px!important;margin-top:10px!important;}\n#ti-purchase-ov .ti-purchase-summary-card{background:#020617!important;border:1px solid #334155!important;border-radius:10px!important;padding:8px!important;color:#e5e7eb!important;font-size:12px!important;}\n#ti-purchase-ov .ti-purchase-summary-card b{display:block!important;color:#bbf7d0!important;font-size:15px!important;margin-top:2px!important;}\n.ti-purchase-row-search-popover{position:fixed!important;z-index:2147483647!important;background:#020617!important;border:1px solid #38bdf8!important;border-radius:10px!important;box-shadow:0 16px 44px rgba(0,0,0,.65)!important;max-height:min(320px,52vh)!important;overflow:auto!important;padding:6px!important;min-width:230px!important;max-width:min(92vw,560px)!important;color:#fff!important;pointer-events:auto!important;-webkit-overflow-scrolling:touch!important;}\n.ti-purchase-row-search-popover .ti-purchase-result-row{display:block!important;width:100%!important;text-align:left!important;margin:0 0 5px 0!important;padding:7px 8px!important;border-radius:8px!important;border:1px solid #334155!important;background:#1e293b!important;color:#fff!important;font-size:12px!important;line-height:1.25!important;cursor:pointer!important;}\n.ti-purchase-row-search-popover .ti-purchase-result-row small{display:block!important;color:#cbd5e1!important;font-size:10.5px!important;margin-top:2px!important;}\n@media (max-width:767px) and (orientation:portrait){#ti-purchase-ov .ti-purchase-modal{width:98vw!important;max-width:98vw!important;padding:10px!important;}#ti-purchase-ov #ti-purchase-table{min-width:980px!important;}#ti-purchase-ov #ti-purchase-table th,#ti-purchase-ov #ti-purchase-table td{font-size:10.5px!important;padding:4px!important;}#ti-purchase-ov #ti-purchase-table input,#ti-purchase-ov #ti-purchase-table select,#ti-purchase-ov #ti-purchase-table button{font-size:10.5px!important;padding:4px!important;min-height:27px!important;}#ti-purchase-ov .ti-pur-name{min-width:190px!important;}.ti-purchase-row-search-popover .ti-purchase-result-row{font-size:10.5px!important;padding:6px!important;}.ti-purchase-row-search-popover .ti-purchase-result-row small{font-size:9.5px!important;}}\n@media (max-width:920px) and (orientation:landscape){#ti-purchase-ov .ti-purchase-modal{width:98vw!important;max-width:98vw!important;padding:10px!important;}#ti-purchase-ov #ti-purchase-table{min-width:900px!important;}#ti-purchase-ov #ti-purchase-table th,#ti-purchase-ov #ti-purchase-table td{font-size:10px!important;padding:3px!important;}#ti-purchase-ov #ti-purchase-table input,#ti-purchase-ov #ti-purchase-table select,#ti-purchase-ov #ti-purchase-table button{font-size:10px!important;padding:3px!important;min-height:25px!important;}}\n`;\n                    document.head.appendChild(st);\n                }\n\n\n                function injectPurchaseLandscapeCompact189(){\n                    try {\n                        if (document.getElementById('ti-purchase-landscape-compact-189-css')) return;\n                        const st = document.createElement('style');\n                        st.id = 'ti-purchase-landscape-compact-189-css';\n                        st.textContent = `\n@media (max-width:980px) and (orientation:landscape){\n#ti-purchase-ov.ti-purchase-open{align-items:flex-start!important;padding:2px!important;}\n#ti-purchase-ov .ti-purchase-modal{width:99vw!important;max-width:99vw!important;max-height:96vh!important;margin:2px auto!important;padding:5px!important;border-radius:10px!important;overflow:auto!important;}\n#ti-purchase-ov .ti-purchase-modal h3,#ti-purchase-ov .ti-purchase-modal h4{font-size:11px!important;line-height:1.05!important;margin:0 0 4px 0!important;}\n#ti-purchase-ov .ti-purchase-table-wrap{max-height:58vh!important;overflow:auto!important;}\n#ti-purchase-ov #ti-purchase-table{min-width:790px!important;table-layout:auto!important;}\n#ti-purchase-ov #ti-purchase-table th,#ti-purchase-ov #ti-purchase-table td{font-size:8.5px!important;line-height:1.05!important;padding:1px 2px!important;height:22px!important;max-height:22px!important;vertical-align:middle!important;}\n#ti-purchase-ov #ti-purchase-table input,#ti-purchase-ov #ti-purchase-table select,#ti-purchase-ov #ti-purchase-table textarea,#ti-purchase-ov #ti-purchase-table button{font-size:8.5px!important;line-height:1.05!important;padding:1px 2px!important;height:20px!important;min-height:20px!important;max-height:20px!important;border-radius:4px!important;box-sizing:border-box!important;}\n#ti-purchase-ov .ti-pur-name{min-width:145px!important;max-width:165px!important;}#ti-purchase-ov .ti-pur-type{width:42px!important;min-width:42px!important;}#ti-purchase-ov .ti-pur-qty{width:46px!important;min-width:46px!important;}#ti-purchase-ov .ti-pur-cost{width:62px!important;min-width:62px!important;}#ti-purchase-ov .ti-pur-margin{width:56px!important;min-width:56px!important;}#ti-purchase-ov .ti-pur-sale{width:70px!important;min-width:70px!important;}#ti-purchase-ov .ti-pur-iva{width:42px!important;min-width:42px!important;}#ti-purchase-ov .ti-pur-note{min-width:96px!important;max-width:115px!important;}\n#ti-purchase-ov .ti-pur-row-total-net,#ti-purchase-ov .ti-pur-row-total-gross{display:inline-block!important;min-width:42px!important;font-size:8.5px!important;line-height:1!important;}\n#ti-purchase-ov .ti-pur-dup,#ti-purchase-ov .ti-pur-del{width:21px!important;min-width:21px!important;height:20px!important;min-height:20px!important;padding:0!important;margin:0 1px!important;font-size:9px!important;line-height:1!important;}\n#ti-purchase-ov .ti-purchase-summary-grid{grid-template-columns:repeat(4,minmax(70px,1fr))!important;gap:4px!important;margin-top:4px!important;}#ti-purchase-ov .ti-purchase-summary-card{font-size:8.5px!important;line-height:1.05!important;padding:4px!important;border-radius:7px!important;}#ti-purchase-ov .ti-purchase-summary-card b{font-size:10px!important;line-height:1.05!important;}#ti-purchase-ov .ti-purchase-toolbar-fixed{padding-top:4px!important;gap:4px!important;}#ti-purchase-ov .ti-purchase-toolbar-fixed .ti-btn,#ti-purchase-ov .ti-purchase-toolbar-fixed button{font-size:9px!important;line-height:1.05!important;padding:4px 6px!important;min-height:23px!important;height:23px!important;}\n.ti-purchase-row-search-popover .ti-purchase-result-row{font-size:9px!important;line-height:1.08!important;padding:4px 5px!important;margin-bottom:3px!important;}.ti-purchase-row-search-popover .ti-purchase-result-row small{font-size:8px!important;line-height:1.05!important;}\n}`;\n                        document.head.appendChild(st);\n                    } catch(e) {}\n                }\n                injectPurchaseLandscapeCompact189();\n                document.addEventListener('DOMContentLoaded', injectPurchaseLandscapeCompact189);\n\n                window.tiPurchaseCalcSaleGross = saleGross;\n                window.tiPurchaseMarginAdjustedNet = marginNet;\n\n                window.purchaseGetCatalogItems = function(){\n                    const data = window.currentDbData || {};\n                    const tables = data && data.Tabelle ? data.Tabelle : {};\n                    const out = [];\n                    Object.keys(tables || {}).forEach(function(tn){\n                        if (!\/(prodott|serviz)\/i.test(tn)) return;\n                        const rows = Array.isArray(tables[tn]) ? tables[tn] : [];\n                        rows.forEach(function(r, idx){\n                            if (!r || typeof r !== 'object') return;\n                            const isServ = \/(serviz)\/i.test(tn);\n                            const name = String(getField(r, isServ ? ['Servizio','Descrizione','Nome','Titolo'] : ['Prodotto','Descrizione','Nome','Titolo'], '') || '').trim();\n                            if (!name) return;\n                            out.push({\n                                table:tn,\n                                index:idx,\n                                type:isServ ? 'S' : 'P',\n                                typeLabel:isServ ? 'servizio' : 'prodotto',\n                                name:name,\n                                desc:String(getField(r, ['Descrizione','Note','Nota'], '') || '').trim(),\n                                cost:parseNum(getField(r, ['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'], 0)),\n                                sale:parseNum(getField(r, ['Prezzo','Prezzo vendita','Prezzo_vendita'], 0)),\n                                iva:parseNum(getField(r, ['IVA_VAT','IVA','Iva','VAT'], 22)) || 22\n                            });\n                        });\n                    });\n                    return out;\n                };\n\n                window.closePurchaseSearchPopover = function(){\n                    const p = byId('ti-purchase-row-search-popover');\n                    if (p) p.remove();\n                };\n                window.selectPurchaseCatalogItemForRow = function(item, row){\n                    try {\n                        item = item || {};\n                        row = row || window.__tiPurchaseActiveRow;\n                        if (!row) return;\n                        row.dataset.catalogTable = item.table || '';\n                        row.dataset.catalogIndex = String(item.index != null ? item.index : -1);\n                        row.dataset.targetType = item.type === 'S' ? 'servizio' : 'prodotto';\n                        const setVal = function(sel, val){ const el = row.querySelector(sel); if (el) el.value = val == null ? '' : String(val).replace('.', ','); };\n                        setVal('.ti-pur-name', item.name || '');\n                        setVal('.ti-pur-type', item.type === 'S' ? 'S' : 'P');\n                        setVal('.ti-pur-cost', item.cost || '');\n                        setVal('.ti-pur-iva', item.iva || '22');\n                        setVal('.ti-pur-note', item.desc || '');\n                        const sale = row.querySelector('.ti-pur-sale');\n                        if (sale) { sale.dataset.auto = '1'; sale.value = item.sale ? String(item.sale).replace('.', ',') : ''; }\n                        window.recalcPurchaseRow(row, !item.sale);\n                        window.refreshPurchaseTotals();\n                        window.closePurchaseSearchPopover();\n                    } catch(e) {}\n                };\n                window.showPurchaseRowSearch = function(input){\n                    try {\n                        if (!input) return;\n                        const row = input.closest('tr');\n                        window.__tiPurchaseActiveRow = row;\n                        const q = String(input.value || '').trim().toLowerCase();\n                        const all = window.purchaseGetCatalogItems ? window.purchaseGetCatalogItems() : [];\n                        const filtered = q ? all.filter(function(it){ return (String(it.name || '') + ' ' + String(it.desc || '') + ' ' + String(it.typeLabel || '')).toLowerCase().indexOf(q) !== -1; }).slice(0, 40) : all.slice(0, 20);\n                        window.closePurchaseSearchPopover();\n                        if (!filtered.length) return;\n                        const pop = document.createElement('div');\n                        pop.id = 'ti-purchase-row-search-popover';\n                        pop.className = 'ti-purchase-row-search-popover';\n                        filtered.forEach(function(it){\n                            const b = document.createElement('button');\n                            b.type = 'button';\n                            b.className = 'ti-purchase-result-row';\n                            b.innerHTML = '<b>' + esc(it.name) + '<\/b><small>' + esc(it.typeLabel || it.type || '') + ' - ' + esc(it.table || '') + ' - costo ' + fmt(it.cost || 0) + ' - prezzo ' + fmt(it.sale || 0) + ' - IVA ' + fmt(it.iva || 22) + '%<\/small>';\n                            const choose = function(ev){ if (ev) { ev.preventDefault(); ev.stopPropagation(); } window.selectPurchaseCatalogItemForRow(it, row); };\n                            b.addEventListener('pointerdown', choose);\n                            b.addEventListener('click', choose);\n                            pop.appendChild(b);\n                        });\n                        document.body.appendChild(pop);\n                        const r = input.getBoundingClientRect();\n                        const width = Math.max(r.width, Math.min(560, window.innerWidth - 24));\n                        let left = Math.max(8, Math.min(r.left, window.innerWidth - width - 8));\n                        let top = r.bottom + 6;\n                        setTimeout(function(){\n                            const ph = pop.offsetHeight || 260;\n                            if (top + ph > window.innerHeight - 8) top = Math.max(8, r.top - ph - 6);\n                            pop.style.left = left + 'px';\n                            pop.style.top = top + 'px';\n                            pop.style.width = width + 'px';\n                        }, 0);\n                    } catch(e) {}\n                };\n\n                window.ensurePurchaseModal = function(){\n                    injectPurchaseStyle();\n                    let ov = byId('ti-purchase-ov');\n                    if (ov && ov.dataset && ov.dataset.v183 === '1') return ov;\n                    if (ov && ov.parentNode) ov.parentNode.removeChild(ov);\n                    ov = document.createElement('div');\n                    ov.id = 'ti-purchase-ov';\n                    ov.className = 'ti-modal-overlay ti-ov ti-closable-ov';\n                    ov.dataset.v183 = '1';\n                    ov.style.display = 'none';\n                    ov.style.zIndex = '2147482000';\n                    ov.innerHTML = `\n                        <div class=\"ti-modal ti-purchase-modal\" style=\"background:#0f172a;color:#fff;border-radius:16px;padding:18px;box-shadow:0 18px 60px rgba(0,0,0,.45);margin:0 auto;\">\n                            <div style=\"display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;\">\n                                <div>\n                                    <h2 style=\"margin:0;font-size:22px;\">Acquisizioni<\/h2>\n                                    <div style=\"font-size:14px;color:#cbd5e1;margin-top:4px;\">Gestisci le righe acquisizione manuale. Il costo \u00e8 netto IVA; il prezzo vendita proposto include margine e IVA.<\/div>\n                                <\/div>\n                                <button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"window.closePurchaseManualFlow()\">X<\/button>\n                            <\/div>\n                            <div style=\"display:flex;gap:8px;flex-wrap:wrap;margin:10px 0 14px 0;\">\n                                <button type=\"button\" class=\"ti-btn ti-purchase-add-row ti-ss-add-manual-row\" style=\"background:#22c55e;color:#052e16;font-weight:700;\" onclick=\"window.ti183ForceAddPurchaseRow(event)\">+ Aggiungi riga manuale<\/button>\n                                <button type=\"button\" class=\"ti-btn\" style=\"background:#f59e0b;color:#111827;font-weight:700;\" onclick=\"window.pickPurchaseImportFile && window.pickPurchaseImportFile()\">Import file acquisizioni<\/button>\n                                <button type=\"button\" class=\"ti-btn\" style=\"background:#64748b;color:#fff;font-weight:700;\" onclick=\"window.clearPurchaseRows()\">Svuota righe<\/button>\n                            <\/div>\n                            <div class=\"ti-purchase-table-wrap\">\n                                <table id=\"ti-purchase-table\">\n                                    <thead>\n                                        <tr style=\"background:#1e293b;\">\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Prodotto o servizio<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Tipo<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Q.t\u00e0<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Costo netto<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Margine<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Prezzo vendita IVA incl.<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">IVA %<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Tot. acquisizione netta<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:right;\">Tot. vendita<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:left;\">Note<\/th>\n                                            <th style=\"padding:8px;border-bottom:1px solid #334155;text-align:center;\">Azioni<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody id=\"ti-purchase-body\"><\/tbody>\n                                <\/table>\n                            <\/div>\n                            <div id=\"ti-purchase-total\" class=\"ti-purchase-summary-grid\" aria-live=\"polite\"><\/div>\n                            <div class=\"ti-purchase-toolbar-fixed\" style=\"display:flex;justify-content:center;gap:8px;margin-top:14px;flex-wrap:wrap;\">\n                                <button type=\"button\" id=\"ti-purchase-confirm-btn\" class=\"ti-btn\" style=\"background:#38bdf8;color:#082f49;font-weight:800;\" onclick=\"window.confirmPurchaseRows()\">Conferma righe acquisizione<\/button>\n                                <button type=\"button\" class=\"ti-btn btn-close\" onclick=\"window.closePurchaseManualFlow()\">Chiudi<\/button>\n                            <\/div>\n                        <\/div>`;\n                    document.body.appendChild(ov);\n                    return ov;\n                };\n                window.closePurchaseManualFlow = function(){\n                    const ov = byId('ti-purchase-ov');\n                    window.closePurchaseSearchPopover();\n                    if (!ov) return;\n                    ov.classList.remove('ti-purchase-open', 'ti-modal-open');\n                    if (window.closeModal) window.closeModal(ov); else ov.style.display = 'none';\n                };\n                function openPurchaseOverlay(){\n                    const ov = window.ensurePurchaseModal();\n                    ov.classList.add('ti-purchase-open', 'ti-modal-open');\n                    ov.style.display = 'flex';\n                    ov.style.pointerEvents = 'auto';\n                    ov.setAttribute('aria-hidden', 'false');\n                    return ov;\n                }\n                window.ti183ForcePurchaseTableActive = function(){\n                    const ov = openPurchaseOverlay();\n                    const wrap = ov.querySelector('.ti-purchase-table-wrap');\n                    const table = byId('ti-purchase-table');\n                    const body = byId('ti-purchase-body');\n                    if (wrap) wrap.style.display = 'block';\n                    if (table) table.style.display = 'table';\n                    if (body && !body.children.length) window.addPurchaseManualRow();\n                    window.refreshPurchaseTotals();\n                    return body;\n                };\n                window.ti183ForceAddPurchaseRow = function(ev){\n                    if (ev) { ev.preventDefault(); ev.stopPropagation(); }\n                    openPurchaseOverlay();\n                    window.addPurchaseManualRow();\n                };\n                window.openPurchaseManualFlow = function(){\n                    const dbSel = byId('ti-ditta');\n                    const dbVal = dbSel ? dbSel.value : '';\n                    const openNow = function(){ window.ti183ForcePurchaseTableActive(); };\n                    if (!dbVal || dbVal === 'NEW_DB') { openNow(); return; }\n                    if (window.currentDbData && window.currentDbData.Tabelle) { openNow(); return; }\n                    const fd = new FormData();\n                    fd.append('ti_action', 'ti_ai_get_db_data');\n                    fd.append('db', dbVal);\n                    const url = window.tiAjaxUrl || window.tiUrl;\n                    if (url && window.fetchJsonSafe) {\n                        window.fetchJsonSafe(url, {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n                            if (res && res.success) window.currentDbData = res.data;\n                            openNow();\n                        }).catch(function(){ openNow(); });\n                    } else openNow();\n                };\n\n                window.recalcPurchaseRow = function(row, force){\n                    try {\n                        if (!row) return;\n                        const qty = parseNum((row.querySelector('.ti-pur-qty') || {}).value || '0');\n                        const costEl = row.querySelector('.ti-pur-cost');\n                        const marginEl = row.querySelector('.ti-pur-margin');\n                        const saleEl = row.querySelector('.ti-pur-sale');\n                        const ivaEl = row.querySelector('.ti-pur-iva');\n                        const cost = parseNum(costEl ? costEl.value : 0);\n                        const sale = saleGross(cost, marginEl ? marginEl.value : '', ivaEl ? ivaEl.value : '22');\n                        if (saleEl && (force || document.activeElement !== saleEl || saleEl.dataset.auto === '1')) {\n                            saleEl.value = sale ? String(sale).replace('.', ',') : '';\n                            saleEl.dataset.auto = '1';\n                        }\n                        const rowSale = saleEl ? (parseNum(saleEl.value) || sale) : sale;\n                        const netTot = Math.round(qty * cost * 100) \/ 100;\n                        const grossTot = Math.round(qty * rowSale * 100) \/ 100;\n                        const netOut = row.querySelector('.ti-pur-row-total-net');\n                        const grossOut = row.querySelector('.ti-pur-row-total-gross');\n                        if (netOut) netOut.textContent = fmt(netTot);\n                        if (grossOut) grossOut.textContent = fmt(grossTot);\n                    } catch(e) {}\n                };\n                window.refreshPurchaseTotals = function(){\n                    try {\n                        let rows = 0, qtyTot = 0, netTot = 0, saleTot = 0;\n                        document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                            window.recalcPurchaseRow(tr, false);\n                            const qty = parseNum((tr.querySelector('.ti-pur-qty') || {}).value || '0');\n                            const cost = parseNum((tr.querySelector('.ti-pur-cost') || {}).value || '0');\n                            const sale = parseNum((tr.querySelector('.ti-pur-sale') || {}).value || '0');\n                            rows += 1; qtyTot += qty; netTot += qty * cost; saleTot += qty * sale;\n                        });\n                        const out = byId('ti-purchase-total');\n                        if (out) out.innerHTML = '<div class=\"ti-purchase-summary-card\">Righe<b>' + rows + '<\/b><\/div>'\n                            + '<div class=\"ti-purchase-summary-card\">Quantit\u00e0<b>' + fmt(qtyTot) + '<\/b><\/div>'\n                            + '<div class=\"ti-purchase-summary-card\">Totale acquisizione netta<b>\u20ac ' + fmt(netTot) + '<\/b><\/div>'\n                            + '<div class=\"ti-purchase-summary-card\">Totale vendita IVA incl.<b>\u20ac ' + fmt(saleTot) + '<\/b><\/div>';\n                    } catch(e) {}\n                };\n                window.addPurchaseManualRow = function(item){\n                    try {\n                        const body = byId('ti-purchase-body') || window.ti183ForcePurchaseTableActive();\n                        if (!body) return;\n                        const now = Date.now();\n                        if (!item && window.__tiLastPurchaseManualRowAt && now - window.__tiLastPurchaseManualRowAt < 180) return;\n                        if (!item) window.__tiLastPurchaseManualRowAt = now;\n                        const safe = item || {};\n                        const tr = document.createElement('tr');\n                        tr.className = 'ti-purchase-row';\n                        tr.dataset.catalogTable = safe.table || '';\n                        tr.dataset.catalogIndex = String(safe.index != null ? safe.index : -1);\n                        tr.dataset.targetType = safe.type === 'S' ? 'servizio' : 'prodotto';\n                        const iva = safe.iva || '22';\n                        const cost = safe.cost ? String(safe.cost).replace('.', ',') : '';\n                        const sale = safe.sale ? String(safe.sale).replace('.', ',') : '';\n                        tr.innerHTML = `\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-name ti-purchase-row-search\" value=\"${esc(safe.name || '')}\" placeholder=\"Cerca prodotto o servizio\" autocomplete=\"off\" style=\"width:100%;min-width:230px;box-sizing:border-box;padding:8px;border-radius:8px;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><select class=\"ti-pur-type\" style=\"width:76px;padding:8px;border-radius:8px;\"><option value=\"P\" ${safe.type === 'S' ? '' : 'selected'}>P<\/option><option value=\"S\" ${safe.type === 'S' ? 'selected' : ''}>S<\/option><\/select><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-qty\" value=\"${esc(safe.qty || '1')}\" inputmode=\"decimal\" style=\"width:72px;text-align:right;padding:8px;border-radius:8px;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-cost\" value=\"${esc(cost)}\" inputmode=\"decimal\" style=\"width:100px;text-align:right;padding:8px;border-radius:8px;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-margin\" value=\"${esc(safe.margin || '')}\" placeholder=\"+10%\" style=\"width:86px;text-align:right;padding:8px;border-radius:8px;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-sale\" data-auto=\"${sale ? '0' : '1'}\" value=\"${esc(sale)}\" inputmode=\"decimal\" style=\"width:112px;text-align:right;padding:8px;border-radius:8px;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-iva\" value=\"${esc(iva)}\" inputmode=\"decimal\" style=\"width:68px;text-align:right;padding:8px;border-radius:8px;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:right;\"><span class=\"ti-pur-row-total-net\">0,00<\/span><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:right;\"><span class=\"ti-pur-row-total-gross\">0,00<\/span><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;\"><input class=\"ti-pur-note\" value=\"${esc(safe.desc || '')}\" placeholder=\"Note\" style=\"width:100%;min-width:160px;box-sizing:border-box;padding:8px;border-radius:8px;\"><\/td>\n                            <td style=\"padding:6px;border-bottom:1px solid #1f2937;text-align:center;white-space:nowrap;\"><button type=\"button\" class=\"ti-btn ti-pur-dup\" title=\"Duplica riga\" style=\"background:#2563eb;color:#fff;margin-right:4px;\">\u2197<\/button><button type=\"button\" class=\"ti-btn ti-pur-del\" title=\"Elimina riga\" style=\"background:#dc2626;color:#fff;\">X<\/button><\/td>`;\n                        body.appendChild(tr);\n                        const name = tr.querySelector('.ti-pur-name');\n                        if (name) {\n                            name.addEventListener('input', function(){ window.__tiPurchaseActiveRow = tr; window.showPurchaseRowSearch(name); });\n                            name.addEventListener('focus', function(){ window.__tiPurchaseActiveRow = tr; if (String(name.value || '').trim()) window.showPurchaseRowSearch(name); });\n                        }\n                        const typeEl = tr.querySelector('.ti-pur-type');\n                        if (typeEl) typeEl.addEventListener('change', function(){ tr.dataset.targetType = this.value === 'S' ? 'servizio' : 'prodotto'; });\n                        ['.ti-pur-qty','.ti-pur-cost','.ti-pur-margin','.ti-pur-iva'].forEach(function(sel){\n                            const el = tr.querySelector(sel);\n                            if (el) el.addEventListener('input', function(){ const saleEl = tr.querySelector('.ti-pur-sale'); if (saleEl) saleEl.dataset.auto = '1'; window.recalcPurchaseRow(tr, true); window.refreshPurchaseTotals(); });\n                        });\n                        const saleEl = tr.querySelector('.ti-pur-sale');\n                        if (saleEl) saleEl.addEventListener('input', function(){ saleEl.dataset.auto = '0'; window.recalcPurchaseRow(tr, false); window.refreshPurchaseTotals(); });\n                        const del = tr.querySelector('.ti-pur-del');\n                        if (del) del.addEventListener('click', function(){ tr.remove(); if (!body.children.length) window.addPurchaseManualRow(); window.refreshPurchaseTotals(); });\n                        const dup = tr.querySelector('.ti-pur-dup');\n                        if (dup) dup.addEventListener('click', function(){ window.addPurchaseManualRow(window.purchaseRowToItem(tr)); });\n                        window.recalcPurchaseRow(tr, !sale);\n                        window.refreshPurchaseTotals();\n                        if (!safe.name && name) { try { name.focus(); } catch(ignore) {} }\n                    } catch(e) { if (window.tiAlert) window.tiAlert('Errore aggiunta riga acquisizione: ' + (e.message || e)); }\n                };\n                window.purchaseRowToItem = function(tr){\n                    tr = tr || {};\n                    return {\n                        table:tr.dataset ? (tr.dataset.catalogTable || '') : '',\n                        index:tr.dataset ? parseInt(tr.dataset.catalogIndex || '-1', 10) : -1,\n                        type:String(((tr.querySelector && tr.querySelector('.ti-pur-type')) || {}).value || 'P'),\n                        name:String(((tr.querySelector && tr.querySelector('.ti-pur-name')) || {}).value || ''),\n                        qty:String(((tr.querySelector && tr.querySelector('.ti-pur-qty')) || {}).value || '1'),\n                        cost:String(((tr.querySelector && tr.querySelector('.ti-pur-cost')) || {}).value || ''),\n                        margin:String(((tr.querySelector && tr.querySelector('.ti-pur-margin')) || {}).value || ''),\n                        sale:String(((tr.querySelector && tr.querySelector('.ti-pur-sale')) || {}).value || ''),\n                        iva:String(((tr.querySelector && tr.querySelector('.ti-pur-iva')) || {}).value || '22'),\n                        desc:String(((tr.querySelector && tr.querySelector('.ti-pur-note')) || {}).value || '')\n                    };\n                };\n                window.clearPurchaseRows = function(){\n                    const body = byId('ti-purchase-body') || window.ti183ForcePurchaseTableActive();\n                    if (!body) return;\n                    if (body.children.length && window.confirm && !window.confirm('Svuotare tutte le righe acquisizione?')) return;\n                    body.innerHTML = '';\n                    window.addPurchaseManualRow();\n                    window.refreshPurchaseTotals();\n                };\n                window.validatePurchaseRows = function(mark){\n                    const errors = [];\n                    document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr, idx){\n                        tr.classList.remove('is-invalid');\n                        const n = String((tr.querySelector('.ti-pur-name') || {}).value || '').trim();\n                        const q = parseNum((tr.querySelector('.ti-pur-qty') || {}).value || '0');\n                        const ivaEl = tr.querySelector('.ti-pur-iva');\n                        if (ivaEl && !String(ivaEl.value || '').trim()) ivaEl.value = '22';\n                        if (!n || q <= 0) {\n                            if (mark) tr.classList.add('is-invalid');\n                            if (!n) errors.push('Riga ' + (idx + 1) + ': prodotto\/servizio mancante');\n                            if (q <= 0) errors.push('Riga ' + (idx + 1) + ': quantit\u00e0 non valida');\n                        }\n                    });\n                    return errors;\n                };\n                window.purchaseCollectRows = function(){\n                    const rows = [];\n                    document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n                        const item = window.purchaseRowToItem(tr);\n                        if (String(item.name || '').trim()) rows.push(item);\n                    });\n                    return rows;\n                };\n                window.confirmPurchaseRows = function(){\n                    const btn = byId('ti-purchase-confirm-btn');\n                    try {\n                        const errors = window.validatePurchaseRows(true);\n                        if (errors.length) { if (window.tiAlert) window.tiAlert('Correggi le righe acquisizione:\\n' + errors.slice(0, 8).join('\\n')); return; }\n                        const rows = window.purchaseCollectRows();\n                        if (!rows.length) { if (window.tiAlert) window.tiAlert('Inserisci almeno una riga acquisizione.'); return; }\n                        const dbEl = byId('ti-ditta');\n                        const dbVal = dbEl ? dbEl.value : '';\n                        if (!dbVal || dbVal === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona prima una ditta.'); return; }\n                        const source = window.currentDbData;\n                        if (!source || !source.Tabelle) { if (window.tiAlert) window.tiAlert('Database non caricato. Seleziona la ditta e riapri Acquisizioni.'); return; }\n                        if (btn) { btn.disabled = true; btn.dataset.oldText = btn.innerHTML; btn.innerHTML = 'Salvataggio...'; }\n                        const data = JSON.parse(JSON.stringify(source));\n                        const prodTable = realTableName(data, 'Prodotti', 'Prodotti');\n                        const servTable = realTableName(data, 'Servizi', 'Servizi');\n                        const actTable = realTableName(data, 'Attivita', 'Attivita');\n                        if (!data.Tabelle[prodTable]) data.Tabelle[prodTable] = [];\n                        if (!data.Tabelle[servTable]) data.Tabelle[servTable] = [];\n                        if (!data.Tabelle[actTable]) data.Tabelle[actTable] = [];\n                        const now = dateParts();\n                        const nextId = function(table){ return (Array.isArray(data.Tabelle[table]) ? data.Tabelle[table].length : 0) + 1; };\n                        rows.forEach(function(r){\n                            const isServ = String(r.type || 'P') === 'S';\n                            const t = isServ ? servTable : prodTable;\n                            const nameFields = isServ ? ['Servizio','Nome','Descrizione'] : ['Prodotto','Nome','Descrizione'];\n                            const tableRows = Array.isArray(data.Tabelle[t]) ? data.Tabelle[t] : (data.Tabelle[t] = []);\n                            let idx = (r.table === t && r.index >= 0 && tableRows[r.index]) ? r.index : -1;\n                            if (idx < 0) {\n                                const wanted = norm(r.name);\n                                idx = tableRows.findIndex(function(row){ return norm(getField(row, nameFields, '')) === wanted; });\n                            }\n                            let rec;\n                            if (idx >= 0) rec = tableRows[idx];\n                            else { rec = {}; setField(rec, ['ID','Id','id'], nextId(t)); tableRows.push(rec); }\n                            setField(rec, nameFields, r.name);\n                            setField(rec, ['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'], parseNum(r.cost));\n                            setField(rec, ['Margine'], String(r.margin || ''));\n                            const gross = parseNum(r.sale) || saleGross(r.cost, r.margin, r.iva);\n                            setField(rec, ['Prezzo','Prezzo vendita','Prezzo_vendita'], gross);\n                            setField(rec, ['IVA_VAT','IVA','Iva','VAT'], parseNum(r.iva) || 22);\n                            if (!isServ) {\n                                const oldQty = parseNum(getField(rec, ['Quantit\u00e0','Quantita','Disponibilita','Disponibilit\u00e0'], 0));\n                                setField(rec, ['Quantit\u00e0','Quantita'], Math.round((oldQty + parseNum(r.qty)) * 100) \/ 100);\n                            }\n                            const act = {};\n                            setField(act, ['ID','Id','id'], nextId(actTable));\n                            setField(act, ['Username','Utente','user'], window.tiUser || '');\n                            setField(act, ['Descrizione attivit\u00e0','Descrizione attivita','Descrizione','Dettaglio','Note'], 'Acquisizione ' + r.name);\n                            setField(act, ['Prodotto'], r.name);\n                            setField(act, ['Prezzo'], parseNum(r.cost));\n                            setField(act, ['Quantit\u00e0','Quantita'], parseNum(r.qty));\n                            setField(act, ['Totale'], Math.round(parseNum(r.cost) * parseNum(r.qty) * 100) \/ 100);\n                            setField(act, ['Margine'], String(r.margin || ''));\n                            setField(act, ['IVA_VAT','IVA','Iva','VAT'], parseNum(r.iva) || 22);\n                            setField(act, ['Prezzo vendita','Prezzo_vendita'], gross);\n                            setField(act, ['Esito'], 'acquisizione');\n                            setField(act, ['Data'], now.date);\n                            setField(act, ['Ora'], now.time);\n                            setField(act, ['Stato'], 'Attivo');\n                            data.Tabelle[actTable].push(act);\n                        });\n                        const fd = new FormData();\n                        fd.append('ti_action', 'ti_ai_save_full_db');\n                        fd.append('db', dbVal);\n                        fd.append('payload', base64Utf8(JSON.stringify(data)));\n                        const done = function(res){\n                            if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText || 'Conferma righe acquisizione'; }\n                            if (res && res.success) {\n                                window.currentDbData = data;\n                                window.closePurchaseManualFlow();\n                                if (window.tiAlert) window.tiAlert('\u2705 Righe acquisizione confermate e salvate nel database.');\n                                if (res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); }, 900);\n                            } else if (window.tiAlert) window.tiAlert('\u26a0\ufe0f Salvataggio acquisizione non riuscito.');\n                        };\n                        const fail = function(e){\n                            if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText || 'Conferma righe acquisizione'; }\n                            if (window.tiAlert) window.tiAlert('Errore salvataggio acquisizione: ' + ((e && e.message) || e));\n                        };\n                        const url = window.tiAjaxUrl || window.tiUrl;\n                        if (window.fetchJsonSafe) window.fetchJsonSafe(url, {method:'POST', body:fd, credentials:'same-origin'}).then(done).catch(fail);\n                        else fetch(url, {method:'POST', body:fd, credentials:'same-origin'}).then(function(r){ return r.json(); }).then(done).catch(fail);\n                    } catch(e) {\n                        if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText || 'Conferma righe acquisizione'; }\n                        if (window.tiAlert) window.tiAlert('Errore conferma acquisizione: ' + (e.message || e));\n                    }\n                };\n                window.purchaseSendToChat = function(){ window.confirmPurchaseRows(); };\n\n                document.addEventListener('click', function(ev){\n                    const target = ev.target && ev.target.closest ? ev.target.closest('#ti-purchase-toggle,.ti-purchase-add-row,.ti-ss-add-manual-row') : null;\n                    if (!target) return;\n                    if (target.id === 'ti-purchase-toggle') {\n                        ev.preventDefault();\n                        if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                        window.openPurchaseManualFlow();\n                        return;\n                    }\n                    if (target.classList.contains('ti-purchase-add-row') || target.classList.contains('ti-ss-add-manual-row')) {\n                        ev.preventDefault();\n                        if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                        window.ti183ForceAddPurchaseRow();\n                    }\n                }, true);\n                document.addEventListener('click', function(ev){\n                    const pop = byId('ti-purchase-row-search-popover');\n                    if (!pop) return;\n                    if (pop.contains(ev.target)) return;\n                    if (ev.target && ev.target.closest && ev.target.closest('.ti-purchase-row-search')) return;\n                    window.closePurchaseSearchPopover();\n                });\n                const pbtn = byId('ti-purchase-toggle');\n                if (pbtn) pbtn.onclick = function(ev){ if (ev) ev.preventDefault(); window.openPurchaseManualFlow(); };\n            } catch(e) {\n                try { console.error('Shop & Service purchase base fix 30.9.664 non applicato:', e); } catch(ignore) {}\n            }\n        }\n        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(ti183InstallPurchaseBaseFix, 0); });\n        else setTimeout(ti183InstallPurchaseBaseFix, 0);\n    })();\n\n\n    <\/script>\n\n<script id=\"ti-purchase-manual-rescue-184\">\n(function(){\n    \"use strict\";\n    var AJAX_URL = \"https:\/\/www.tisoft.it\/wp-admin\/admin-ajax.php\";\n    if (window.__tiPurchaseManualRescue184) return;\n    window.__tiPurchaseManualRescue184 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function norm(v){ return String(v || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').toLowerCase().replace(\/[^a-z0-9]\/g,''); }\n    function num(v){\n        if (v == null) return 0;\n        var s = String(v).trim().replace(\/[\u20ac\\s]\/g,'');\n        if (!s) return 0;\n        if (s.indexOf(',') !== -1 && s.indexOf('.') !== -1) s = s.replace(\/\\.\/g,'').replace(',', '.');\n        else if (s.indexOf(',') !== -1) s = s.replace(',', '.');\n        var n = parseFloat(s.replace(\/[^0-9.\\-]\/g,''));\n        return isNaN(n) ? 0 : n;\n    }\n    function fmt(n){\n        n = Math.round((parseFloat(n) || 0) * 100) \/ 100;\n        try { return n.toLocaleString('it-IT', {minimumFractionDigits:2, maximumFractionDigits:2}); }\n        catch(e){ return String(n.toFixed(2)).replace('.', ','); }\n    }\n    function field(row, names, fallback){\n        row = row || {}; var keys = Object.keys(row);\n        for (var i=0;i<names.length;i++) { var w = norm(names[i]); for (var j=0;j<keys.length;j++) if (norm(keys[j]) === w) return row[keys[j]]; }\n        return fallback;\n    }\n    function setField(row, names, value){\n        row = row || {}; var keys = Object.keys(row);\n        for (var i=0;i<names.length;i++) { var w = norm(names[i]); for (var j=0;j<keys.length;j++) if (norm(keys[j]) === w) { row[keys[j]] = value; return; } }\n        row[names[0]] = value;\n    }\n    function realTable(data, wanted, fallback){\n        var tables = data && data.Tabelle ? data.Tabelle : {}; var keys = Object.keys(tables); var w = norm(wanted);\n        for (var i=0;i<keys.length;i++) if (norm(keys[i]) === w) return keys[i];\n        for (var j=0;j<keys.length;j++) { var k = norm(keys[j]); if (k.indexOf(w) !== -1 || w.indexOf(k) !== -1) return keys[j]; }\n        return fallback || wanted;\n    }\n    function marginNet(cost, margin){\n        cost = num(cost); var m = String(margin || '').trim();\n        if (!m) return Math.max(0, cost);\n        var pct = m.indexOf('%') !== -1; m = m.replace(\/[%\u20ac\\s]\/g,'');\n        var sign = 1; if (m.charAt(0) === '-') { sign = -1; m = m.slice(1); } else if (m.charAt(0) === '+') m = m.slice(1);\n        var v = num(m); return Math.max(0, pct ? cost + cost * sign * v \/ 100 : cost + sign * v);\n    }\n    function grossSale(cost, margin, iva){ iva = num(iva); if (iva <= 0) iva = 22; return Math.round(marginNet(cost, margin) * (1 + iva\/100) * 100) \/ 100; }\n    function base64Utf8(str){ try { return btoa(unescape(encodeURIComponent(str))); } catch(e) { return btoa(str); } }\n    function ajaxForm(tiAction, extra){\n        var fd = new FormData(); fd.append('ti_action', tiAction);\n        Object.keys(extra || {}).forEach(function(k){ fd.append(k, extra[k]); });\n        var url = window.tiAjaxUrl || window.tiUrl || AJAX_URL;\n        if (window.fetchJsonSafe) return window.fetchJsonSafe(url, {method:'POST', body:fd, credentials:'same-origin'});\n        return fetch(url, {method:'POST', body:fd, credentials:'same-origin'}).then(function(r){ return r.json(); });\n    }\n    function selectedDb(){ var el = byId('ti-ditta'); return el ? String(el.value || '') : ''; }\n    function loadDb(){\n        if (window.currentDbData && window.currentDbData.Tabelle) return Promise.resolve(window.currentDbData);\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') return Promise.resolve(window.currentDbData || {Tabelle:{}});\n        return ajaxForm('ti_ai_get_db_data', {db:db}).then(function(res){ if (res && res.success) window.currentDbData = res.data; return window.currentDbData || {Tabelle:{}}; }).catch(function(){ return window.currentDbData || {Tabelle:{}}; });\n    }\n\n    function injectCss(){\n        if (byId('ti-purchase-rescue-184-css')) return;\n        var st = document.createElement('style'); st.id = 'ti-purchase-rescue-184-css';\n        st.textContent = '#ti-purchase-ov.ti-purchase-rescue-open{display:flex!important;align-items:center!important;justify-content:center!important;pointer-events:auto!important;z-index:2147482000!important}'\n        + '#ti-purchase-ov .ti-purchase-modal{width:min(1480px,98vw)!important;max-width:98vw!important;max-height:92vh!important;overflow:auto!important;background:#0f172a!important;color:#fff!important;border-radius:16px!important;padding:18px!important;box-sizing:border-box!important}'\n        + '#ti-purchase-ov .ti-purchase-table-wrap{display:block!important;width:100%!important;max-width:100%!important;overflow-x:auto!important;overflow-y:visible!important;-webkit-overflow-scrolling:touch!important;touch-action:pan-x pan-y!important;border:1px solid #334155!important;border-radius:12px!important}'\n        + '#ti-purchase-ov #ti-purchase-table{display:table!important;width:100%!important;min-width:1160px!important;border-collapse:collapse!important;background:#020617!important}'\n        + '#ti-purchase-ov #ti-purchase-table thead{display:table-header-group!important}#ti-purchase-ov #ti-purchase-table tbody{display:table-row-group!important}#ti-purchase-ov #ti-purchase-table tr{display:table-row!important}#ti-purchase-ov #ti-purchase-table th,#ti-purchase-ov #ti-purchase-table td{display:table-cell!important;white-space:nowrap!important;vertical-align:middle!important}'\n        + '#ti-purchase-ov input,#ti-purchase-ov select,#ti-purchase-ov textarea{background:#0f172a!important;color:#fff!important;-webkit-text-fill-color:#fff!important;caret-color:#fff!important;border:1px solid #334155!important}'\n        + '#ti-purchase-row-pop-184{position:fixed!important;z-index:2147483647!important;background:#020617!important;border:1px solid #38bdf8!important;border-radius:10px!important;box-shadow:0 16px 44px rgba(0,0,0,.65)!important;max-height:min(320px,52vh)!important;overflow:auto!important;padding:6px!important;min-width:230px!important;max-width:min(92vw,560px)!important;color:#fff!important;pointer-events:auto!important;-webkit-overflow-scrolling:touch!important}'\n        + '#ti-purchase-row-pop-184 button{display:block!important;width:100%!important;text-align:left!important;margin:0 0 5px!important;padding:7px 8px!important;border-radius:8px!important;border:1px solid #334155!important;background:#1e293b!important;color:#fff!important;font-size:12px!important;line-height:1.25!important;cursor:pointer!important}'\n        + '#ti-purchase-row-pop-184 small{display:block!important;color:#cbd5e1!important;font-size:10.5px!important;margin-top:2px!important}'\n        + '#ti-purchase-total{display:grid!important;grid-template-columns:repeat(4,minmax(120px,1fr))!important;gap:8px!important;margin-top:10px!important}.ti-purchase-summary-card{background:#020617!important;border:1px solid #334155!important;border-radius:10px!important;padding:8px!important;color:#e5e7eb!important;font-size:12px!important}.ti-purchase-summary-card b{display:block!important;color:#bbf7d0!important;font-size:15px!important;margin-top:2px!important}'\n        + '@media (max-width:767px) and (orientation:portrait){#ti-purchase-ov .ti-purchase-modal{width:98vw!important;max-width:98vw!important;padding:10px!important}#ti-purchase-ov #ti-purchase-table{min-width:980px!important}#ti-purchase-ov #ti-purchase-table th,#ti-purchase-ov #ti-purchase-table td{font-size:10.5px!important;padding:4px!important}#ti-purchase-ov #ti-purchase-table input,#ti-purchase-ov #ti-purchase-table select,#ti-purchase-ov #ti-purchase-table button{font-size:10.5px!important;padding:4px!important;min-height:27px!important}#ti-purchase-ov .ti-pur-name{min-width:190px!important}#ti-purchase-row-pop-184 button{font-size:10.5px!important;padding:6px!important}#ti-purchase-row-pop-184 small{font-size:9.5px!important}}'\n        + '@media (max-width:920px) and (orientation:landscape){#ti-purchase-ov .ti-purchase-modal{width:98vw!important;max-width:98vw!important;padding:10px!important}#ti-purchase-ov #ti-purchase-table{min-width:900px!important}#ti-purchase-ov #ti-purchase-table th,#ti-purchase-ov #ti-purchase-table td{font-size:10px!important;padding:3px!important}#ti-purchase-ov #ti-purchase-table input,#ti-purchase-ov #ti-purchase-table select,#ti-purchase-ov #ti-purchase-table button{font-size:10px!important;padding:3px!important;min-height:25px!important}}';\n        document.head.appendChild(st);\n    }\n\n    function ensureModal(){\n        injectCss();\n        var ov = byId('ti-purchase-ov');\n        if (ov && ov.dataset && ov.dataset.rescue184 === '1') return ov;\n        if (ov && ov.parentNode) ov.parentNode.removeChild(ov);\n        ov = document.createElement('div');\n        ov.id = 'ti-purchase-ov'; ov.className = 'ti-modal-overlay ti-ov ti-closable-ov'; ov.dataset.rescue184 = '1';\n        ov.style.display = 'none'; ov.style.zIndex = '2147482000';\n        ov.innerHTML = '<div class=\"ti-modal ti-purchase-modal\">'\n            + '<div style=\"display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px\"><div><h2 style=\"margin:0;font-size:22px\">Acquisizioni<\/h2><div style=\"font-size:14px;color:#cbd5e1;margin-top:4px\">Tabella acquisizione prodotti e servizi. Il costo \u00e8 netto IVA; il prezzo vendita proposto include margine e IVA.<\/div><\/div><button type=\"button\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff\" onclick=\"window.closePurchaseManualFlow()\">X<\/button><\/div>'\n            + '<div style=\"display:flex;gap:8px;flex-wrap:wrap;margin:10px 0 14px 0\"><button type=\"button\" class=\"ti-btn ti-purchase-add-row ti-ss-add-manual-row\" style=\"background:#22c55e;color:#052e16;font-weight:700\">+ Aggiungi riga manuale<\/button><button type=\"button\" class=\"ti-btn\" style=\"background:#f59e0b;color:#111827;font-weight:700\" onclick=\"window.pickPurchaseImportFile&&window.pickPurchaseImportFile()\">Import file acquisizioni<\/button><button type=\"button\" class=\"ti-btn\" style=\"background:#64748b;color:#fff;font-weight:700\" onclick=\"window.clearPurchaseRows()\">Svuota righe<\/button><\/div>'\n            + '<div class=\"ti-purchase-table-wrap\"><table id=\"ti-purchase-table\"><thead><tr style=\"background:#1e293b\"><th>Prodotto o servizio<\/th><th>Tipo<\/th><th>Q.t\u00e0<\/th><th>Costo netto<\/th><th>Margine<\/th><th>Prezzo vendita IVA incl.<\/th><th>IVA %<\/th><th>Tot. acquisizione netta<\/th><th>Tot. vendita<\/th><th>Note<\/th><th>Azioni<\/th><\/tr><\/thead><tbody id=\"ti-purchase-body\"><\/tbody><\/table><\/div>'\n            + '<div id=\"ti-purchase-total\" aria-live=\"polite\"><\/div><div style=\"display:flex;justify-content:center;gap:8px;margin-top:14px;flex-wrap:wrap;position:sticky;bottom:0;background:#0f172a;padding-top:10px;border-top:1px solid #334155\"><button type=\"button\" id=\"ti-purchase-confirm-btn\" class=\"ti-btn\" style=\"background:#38bdf8;color:#082f49;font-weight:800\" onclick=\"window.confirmPurchaseRows()\">Conferma righe acquisizione<\/button><button type=\"button\" class=\"ti-btn btn-close\" onclick=\"window.closePurchaseManualFlow()\">Chiudi<\/button><\/div><\/div>';\n        document.body.appendChild(ov);\n        return ov;\n    }\n    function showModal(){ var ov = ensureModal(); ov.classList.add('ti-purchase-rescue-open','ti-modal-open'); ov.style.display='flex'; ov.style.pointerEvents='auto'; ov.setAttribute('aria-hidden','false'); return ov; }\n    function closeModal(){ closePopover(); var ov=byId('ti-purchase-ov'); if(ov){ ov.classList.remove('ti-purchase-rescue-open','ti-modal-open'); ov.style.display='none'; ov.style.pointerEvents='none'; } }\n    function getBody(){ return ensureModal().querySelector('#ti-purchase-body'); }\n\n    function catalog(){\n        var data = window.currentDbData || {}; var tables = data.Tabelle || {}; var out = [];\n        function add(table, type, rows){ if (!Array.isArray(rows)) return; rows.forEach(function(r, idx){ var name = field(r, type === 'S' ? ['Servizio','Nome','Descrizione'] : ['Prodotto','Nome','Descrizione'], ''); if (!String(name||'').trim()) return; out.push({table:table,index:idx,type:type,typeLabel:type==='S'?'Servizio':'Prodotto',name:String(name),desc:String(field(r,['Descrizione','Note','Nota'], '')||''),cost:num(field(r,['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'],0)),sale:num(field(r,['Prezzo','Prezzo vendita','Prezzo_vendita'],0)),iva:num(field(r,['IVA_VAT','IVA','Iva','VAT'],22))||22}); }); }\n        Object.keys(tables).forEach(function(t){ var nt = norm(t); if (nt.indexOf('serviz') !== -1) add(t,'S',tables[t]); else if (nt.indexOf('prodott') !== -1 || nt === 'listino') add(t,'P',tables[t]); });\n        return out;\n    }\n    function closePopover(){ var p = byId('ti-purchase-row-pop-184'); if (p) p.remove(); }\n    function selectItem(item, row){\n        row = row || window.__tiPurchaseActiveRow184; if (!row || !item) return;\n        row.dataset.catalogTable = item.table || ''; row.dataset.catalogIndex = String(item.index != null ? item.index : -1); row.dataset.targetType = item.type === 'S' ? 'servizio' : 'prodotto';\n        function val(sel,v){ var el=row.querySelector(sel); if(el) el.value = v == null ? '' : String(v).replace('.', ','); }\n        val('.ti-pur-name', item.name || ''); val('.ti-pur-type', item.type === 'S' ? 'S' : 'P'); val('.ti-pur-cost', item.cost || ''); val('.ti-pur-iva', item.iva || 22); val('.ti-pur-note', item.desc || '');\n        var sale = row.querySelector('.ti-pur-sale'); if (sale) { sale.value = item.sale ? String(item.sale).replace('.', ',') : ''; sale.dataset.auto = item.sale ? '0' : '1'; }\n        recalcRow(row, !item.sale); totals(); closePopover();\n    }\n    function showSearch(input){\n        if (!input) return; var row = input.closest('tr'); window.__tiPurchaseActiveRow184 = row;\n        var q = String(input.value || '').toLowerCase().trim(); var list = catalog().filter(function(it){ return !q || (it.name + ' ' + it.desc + ' ' + it.typeLabel).toLowerCase().indexOf(q) !== -1; }).slice(0, q ? 40 : 20);\n        closePopover(); if (!list.length) return;\n        var pop = document.createElement('div'); pop.id = 'ti-purchase-row-pop-184';\n        list.forEach(function(it){ var b = document.createElement('button'); b.type='button'; b.innerHTML = '<b>' + esc(it.typeLabel) + ' - ' + esc(it.name) + '<\/b><small>' + esc(it.table) + ' - costo ' + fmt(it.cost) + ' - prezzo ' + fmt(it.sale) + ' - IVA ' + fmt(it.iva) + '%<\/small>'; var choose=function(ev){ if(ev){ev.preventDefault();ev.stopPropagation();} selectItem(it,row); }; b.addEventListener('pointerdown', choose); b.addEventListener('click', choose); pop.appendChild(b); });\n        document.body.appendChild(pop); var r = input.getBoundingClientRect(); var w = Math.max(r.width, Math.min(560, window.innerWidth - 24)); var left = Math.max(8, Math.min(r.left, window.innerWidth - w - 8)); var top = r.bottom + 6; setTimeout(function(){ var h=pop.offsetHeight||260; if(top+h>window.innerHeight-8) top=Math.max(8,r.top-h-6); pop.style.left=left+'px'; pop.style.top=top+'px'; pop.style.width=w+'px'; },0);\n    }\n    function rowItem(tr){ return {table:tr.dataset.catalogTable||'', index:parseInt(tr.dataset.catalogIndex||'-1',10), type:(tr.querySelector('.ti-pur-type')||{}).value||'P', name:(tr.querySelector('.ti-pur-name')||{}).value||'', qty:(tr.querySelector('.ti-pur-qty')||{}).value||'1', cost:(tr.querySelector('.ti-pur-cost')||{}).value||'', margin:(tr.querySelector('.ti-pur-margin')||{}).value||'', sale:(tr.querySelector('.ti-pur-sale')||{}).value||'', iva:(tr.querySelector('.ti-pur-iva')||{}).value||'22', desc:(tr.querySelector('.ti-pur-note')||{}).value||''}; }\n    function recalcRow(row, force){\n        if (!row) return; var qty=num((row.querySelector('.ti-pur-qty')||{}).value||0); var cost=num((row.querySelector('.ti-pur-cost')||{}).value||0); var margin=(row.querySelector('.ti-pur-margin')||{}).value||''; var iva=(row.querySelector('.ti-pur-iva')||{}).value||'22'; var saleEl=row.querySelector('.ti-pur-sale'); var sale=grossSale(cost, margin, iva);\n        if (saleEl && (force || saleEl.dataset.auto === '1' || document.activeElement !== saleEl)) { saleEl.value = sale ? String(sale).replace('.', ',') : ''; saleEl.dataset.auto='1'; }\n        var saleVal = saleEl ? (num(saleEl.value) || sale) : sale; var net=row.querySelector('.ti-pur-row-total-net'); var gross=row.querySelector('.ti-pur-row-total-gross'); if(net) net.textContent=fmt(qty*cost); if(gross) gross.textContent=fmt(qty*saleVal);\n    }\n    function totals(){\n        var rows=0, qty=0, net=0, gross=0; document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){ recalcRow(tr,false); rows++; qty += num((tr.querySelector('.ti-pur-qty')||{}).value||0); net += num((tr.querySelector('.ti-pur-qty')||{}).value||0) * num((tr.querySelector('.ti-pur-cost')||{}).value||0); gross += num((tr.querySelector('.ti-pur-qty')||{}).value||0) * num((tr.querySelector('.ti-pur-sale')||{}).value||0); });\n        var out=byId('ti-purchase-total'); if(out) out.innerHTML='<div class=\"ti-purchase-summary-card\">Righe<b>'+rows+'<\/b><\/div><div class=\"ti-purchase-summary-card\">Quantit\u00e0<b>'+fmt(qty)+'<\/b><\/div><div class=\"ti-purchase-summary-card\">Totale acquisizione netta<b>\u20ac '+fmt(net)+'<\/b><\/div><div class=\"ti-purchase-summary-card\">Totale vendita IVA incl.<b>\u20ac '+fmt(gross)+'<\/b><\/div>';\n    }\n    function addRow(item){\n        var body = getBody(); if (!body) return; showModal(); item = item || {}; var tr=document.createElement('tr'); tr.className='ti-purchase-row'; tr.dataset.catalogTable=item.table||''; tr.dataset.catalogIndex=String(item.index!=null?item.index:-1); tr.dataset.targetType=item.type==='S'?'servizio':'prodotto';\n        var cost=item.cost?String(item.cost).replace('.', ','):''; var sale=item.sale?String(item.sale).replace('.', ','):''; var iva=item.iva||'22';\n        tr.innerHTML='<td><input class=\"ti-pur-name ti-purchase-row-search\" value=\"'+esc(item.name||'')+'\" placeholder=\"Cerca prodotto o servizio\" autocomplete=\"off\" style=\"width:100%;min-width:230px;box-sizing:border-box;padding:8px;border-radius:8px\"><\/td><td><select class=\"ti-pur-type\" style=\"width:76px;padding:8px;border-radius:8px\"><option value=\"P\" '+(item.type==='S'?'':'selected')+'>P<\/option><option value=\"S\" '+(item.type==='S'?'selected':'')+'>S<\/option><\/select><\/td><td><input class=\"ti-pur-qty\" value=\"'+esc(item.qty||'1')+'\" inputmode=\"decimal\" style=\"width:72px;text-align:right;padding:8px;border-radius:8px\"><\/td><td><input class=\"ti-pur-cost\" value=\"'+esc(cost)+'\" inputmode=\"decimal\" style=\"width:100px;text-align:right;padding:8px;border-radius:8px\"><\/td><td><input class=\"ti-pur-margin\" value=\"'+esc(item.margin||'')+'\" placeholder=\"+10%\" style=\"width:86px;text-align:right;padding:8px;border-radius:8px\"><\/td><td><input class=\"ti-pur-sale\" data-auto=\"'+(sale?'0':'1')+'\" value=\"'+esc(sale)+'\" inputmode=\"decimal\" style=\"width:112px;text-align:right;padding:8px;border-radius:8px\"><\/td><td><input class=\"ti-pur-iva\" value=\"'+esc(iva)+'\" inputmode=\"decimal\" style=\"width:68px;text-align:right;padding:8px;border-radius:8px\"><\/td><td style=\"text-align:right\"><span class=\"ti-pur-row-total-net\">0,00<\/span><\/td><td style=\"text-align:right\"><span class=\"ti-pur-row-total-gross\">0,00<\/span><\/td><td><input class=\"ti-pur-note\" value=\"'+esc(item.desc||'')+'\" style=\"width:180px;padding:8px;border-radius:8px\"><\/td><td style=\"text-align:center\"><button type=\"button\" class=\"ti-btn ti-pur-dup\" style=\"background:#64748b;color:#fff;margin-right:4px\">Duplica<\/button><button type=\"button\" class=\"ti-btn ti-pur-del\" style=\"background:#dc2626;color:#fff\">X<\/button><\/td>';\n        body.appendChild(tr); var name=tr.querySelector('.ti-pur-name'); if(name){ name.addEventListener('input', function(){ showSearch(name); }); name.addEventListener('focus', function(){ showSearch(name); }); }\n        tr.querySelectorAll('input,select').forEach(function(el){ if(!el.classList.contains('ti-purchase-row-search')) el.addEventListener('input', function(){ if(el.classList.contains('ti-pur-sale')) el.dataset.auto='0'; recalcRow(tr,false); totals(); }); });\n        var del=tr.querySelector('.ti-pur-del'); if(del) del.addEventListener('click', function(){ tr.remove(); if(!body.children.length) addRow(); totals(); }); var dup=tr.querySelector('.ti-pur-dup'); if(dup) dup.addEventListener('click', function(){ addRow(rowItem(tr)); }); recalcRow(tr,!sale); totals(); if(!item.name && name) setTimeout(function(){ try{name.focus();}catch(e){} }, 50);\n    }\n    function openFlow(){ loadDb().then(function(){ showModal(); var body=getBody(); if(body && !body.children.length) addRow(); totals(); }); }\n    function clearRows(){ var body=getBody(); if(!body) return; if(body.children.length && window.confirm && !window.confirm('Svuotare tutte le righe acquisizione?')) return; body.innerHTML=''; addRow(); totals(); }\n    function collect(){ var rows=[]; document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){ var it=rowItem(tr); if(String(it.name||'').trim()) rows.push(it); }); return rows; }\n    function validate(){ var err=[]; document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr,i){ var n=String((tr.querySelector('.ti-pur-name')||{}).value||'').trim(); var q=num((tr.querySelector('.ti-pur-qty')||{}).value||0); var iva=tr.querySelector('.ti-pur-iva'); if(iva && !String(iva.value||'').trim()) iva.value='22'; if(!n) err.push('Riga '+(i+1)+': prodotto\/servizio mancante'); if(q<=0) err.push('Riga '+(i+1)+': quantit\u00e0 non valida'); }); return err; }\n    function confirmRows(){\n        var btn=byId('ti-purchase-confirm-btn'); var err=validate(); if(err.length){ if(window.tiAlert) window.tiAlert('Correggi le righe acquisizione:\\n'+err.slice(0,8).join('\\n')); else alert(err.join('\\n')); return; }\n        var rows=collect(); if(!rows.length){ if(window.tiAlert) window.tiAlert('Inserisci almeno una riga acquisizione.'); else alert('Inserisci almeno una riga acquisizione.'); return; }\n        var db=selectedDb(); if(!db || db==='NEW_DB'){ if(window.tiAlert) window.tiAlert('Seleziona prima una ditta.'); else alert('Seleziona prima una ditta.'); return; }\n        loadDb().then(function(source){ if(!source || !source.Tabelle){ throw new Error('Database non caricato'); } if(btn){ btn.disabled=true; btn.dataset.oldText=btn.innerHTML; btn.innerHTML='Salvataggio...'; }\n            var data=JSON.parse(JSON.stringify(source)); var prod=realTable(data,'Prodotti','Prodotti'); var serv=realTable(data,'Servizi','Servizi'); var act=realTable(data,'Attivita','Attivita'); if(!data.Tabelle[prod]) data.Tabelle[prod]=[]; if(!data.Tabelle[serv]) data.Tabelle[serv]=[]; if(!data.Tabelle[act]) data.Tabelle[act]=[];\n            var d=new Date(); var date=String(d.getDate()).padStart(2,'0')+'\/'+String(d.getMonth()+1).padStart(2,'0')+'\/'+d.getFullYear(); var time=String(d.getHours()).padStart(2,'0')+':'+String(d.getMinutes()).padStart(2,'0'); var nextId=function(t){ return (Array.isArray(data.Tabelle[t])?data.Tabelle[t].length:0)+1; };\n            rows.forEach(function(r){ var isS=String(r.type||'P')==='S'; var t=isS?serv:prod; var names=isS?['Servizio','Nome','Descrizione']:['Prodotto','Nome','Descrizione']; var arr=Array.isArray(data.Tabelle[t])?data.Tabelle[t]:(data.Tabelle[t]=[]); var idx=(r.table===t && r.index>=0 && arr[r.index])?r.index:-1; if(idx<0){ var wanted=norm(r.name); idx=arr.findIndex(function(x){ return norm(field(x,names,''))===wanted; }); } var rec=idx>=0?arr[idx]:{}; if(idx<0){ setField(rec,['ID','Id','id'],nextId(t)); arr.push(rec); } setField(rec,names,r.name); setField(rec,['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'],num(r.cost)); setField(rec,['Margine'],String(r.margin||'')); var sale=num(r.sale)||grossSale(r.cost,r.margin,r.iva); setField(rec,['Prezzo','Prezzo vendita','Prezzo_vendita'],sale); setField(rec,['IVA_VAT','IVA','Iva','VAT'],num(r.iva)||22); if(!isS){ var old=num(field(rec,['Quantit\u00e0','Quantita','Disponibilita','Disponibilit\u00e0'],0)); setField(rec,['Quantit\u00e0','Quantita'],Math.round((old+num(r.qty))*100)\/100); } var a={}; setField(a,['ID','Id','id'],nextId(act)); setField(a,['Username','Utente','user'],window.tiUser||''); setField(a,['Descrizione attivit\u00e0','Descrizione attivita','Descrizione','Dettaglio','Note'],'Acquisizione '+r.name); setField(a,['Prodotto'],r.name); setField(a,['Prezzo'],num(r.cost)); setField(a,['Quantit\u00e0','Quantita'],num(r.qty)); setField(a,['Totale'],Math.round(num(r.cost)*num(r.qty)*100)\/100); setField(a,['Margine'],String(r.margin||'')); setField(a,['IVA_VAT','IVA','Iva','VAT'],num(r.iva)||22); setField(a,['Prezzo vendita','Prezzo_vendita'],sale); setField(a,['Esito'],'acquisizione'); setField(a,['Data'],date); setField(a,['Ora'],time); setField(a,['Stato'],'Attivo'); data.Tabelle[act].push(a); });\n            return ajaxForm('ti_ai_save_full_db', {db:db, payload:base64Utf8(JSON.stringify(data))}).then(function(res){ if(btn){btn.disabled=false;btn.innerHTML=btn.dataset.oldText||'Conferma righe acquisizione';} if(res && res.success){ window.currentDbData=data; closeModal(); if(window.tiAlert) window.tiAlert('\u2705 Righe acquisizione confermate e salvate nel database.'); else alert('Righe acquisizione confermate.'); if(res.data && res.data.needs_reload) setTimeout(function(){ location.reload(); },900); } else { if(window.tiAlert) window.tiAlert('\u26a0\ufe0f Salvataggio acquisizione non riuscito.'); else alert('Salvataggio acquisizione non riuscito.'); } });\n        }).catch(function(e){ if(btn){btn.disabled=false;btn.innerHTML=btn.dataset.oldText||'Conferma righe acquisizione';} if(window.tiAlert) window.tiAlert('Errore conferma acquisizione: '+(e.message||e)); else alert('Errore conferma acquisizione: '+(e.message||e)); });\n    }\n\n    window.ensurePurchaseModal = ensureModal;\n    window.openPurchaseManualFlow = openFlow;\n    window.addPurchaseManualRow = addRow;\n    window.ti183ForceAddPurchaseRow = function(ev){ if(ev){ ev.preventDefault(); ev.stopPropagation(); } addRow(); };\n    window.ti183ForcePurchaseTableActive = function(){ showModal(); var body=getBody(); if(body && !body.children.length) addRow(); totals(); return body; };\n    window.closePurchaseManualFlow = closeModal;\n    window.clearPurchaseRows = clearRows;\n    window.confirmPurchaseRows = confirmRows;\n    window.purchaseSendToChat = confirmRows;\n    window.refreshPurchaseTotals = totals;\n    window.recalcPurchaseRow = recalcRow;\n\n    function isAddButton(el){ return el && el.closest && el.closest('.ti-purchase-add-row,.ti-ss-add-manual-row'); }\n    function isPurchaseButton(el){\n        if (!el || !el.closest) return null;\n        var b = el.closest('#ti-purchase-toggle,button,a'); if(!b) return null;\n        var txt = String(b.textContent || '').toLowerCase();\n        if (b.id === 'ti-purchase-toggle') return b;\n        if (txt.indexOf('aggiungi riga manuale') !== -1) return null;\n        if (txt.indexOf('import') !== -1) return null;\n        if (txt.indexOf('acquisizioni') !== -1 || txt.indexOf('acquisizione') !== -1 || txt.indexOf('acquisti') !== -1 || txt.indexOf('acquisto') !== -1) return b;\n        return null;\n    }\n    document.addEventListener('click', function(ev){\n        var add = isAddButton(ev.target); if(add){ ev.preventDefault(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); addRow(); return; }\n        var p = isPurchaseButton(ev.target); if(p){ ev.preventDefault(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); openFlow(); }\n    }, true);\n    document.addEventListener('click', function(ev){ var pop=byId('ti-purchase-row-pop-184'); if(pop && !pop.contains(ev.target) && !(ev.target.closest && ev.target.closest('.ti-purchase-row-search'))) closePopover(); });\n    function bindButtons(){\n        var b=byId('ti-purchase-toggle'); if(b) b.onclick=function(ev){ if(ev) ev.preventDefault(); openFlow(); return false; };\n        document.querySelectorAll('.ti-purchase-add-row,.ti-ss-add-manual-row').forEach(function(x){ x.onclick=function(ev){ if(ev) ev.preventDefault(); addRow(); return false; }; });\n    }\n    if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(bindButtons,0); setTimeout(bindButtons,800); }); else { setTimeout(bindButtons,0); setTimeout(bindButtons,800); }\n    try { new MutationObserver(function(){ bindButtons(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n})();\n<\/script>\n\n\n<style id=\"ti-dark-input-and-purchase-close-185\">\n#ti-ai-outer input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n#ti-ai-outer textarea,\n#ti-ai-outer select,\n#ti-plugin-modal-root input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n#ti-plugin-modal-root textarea,\n#ti-plugin-modal-root select,\n#ti-mobile-direct-login input,\n#ti-mobile-direct-login select,\n.ti-ov input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n.ti-ov textarea,\n.ti-ov select,\n.ti-in,\n.ti-msg {\n    background:#020617 !important;\n    color:#ffffff !important;\n    -webkit-text-fill-color:#ffffff !important;\n    caret-color:#ffffff !important;\n    border-color:#334155 !important;\n}\n#ti-ai-outer input:focus,\n#ti-ai-outer textarea:focus,\n#ti-ai-outer select:focus,\n#ti-plugin-modal-root input:focus,\n#ti-plugin-modal-root textarea:focus,\n#ti-plugin-modal-root select:focus,\n#ti-mobile-direct-login input:focus,\n#ti-mobile-direct-login select:focus,\n.ti-ov input:focus,\n.ti-ov textarea:focus,\n.ti-ov select:focus,\n.ti-in:focus,\n.ti-msg:focus {\n    background:#000000 !important;\n    color:#ffffff !important;\n    -webkit-text-fill-color:#ffffff !important;\n    caret-color:#ffffff !important;\n    outline:2px solid rgba(56,189,248,.45) !important;\n    outline-offset:1px !important;\n}\n#ti-ai-outer input::placeholder,\n#ti-ai-outer textarea::placeholder,\n#ti-plugin-modal-root input::placeholder,\n#ti-plugin-modal-root textarea::placeholder,\n#ti-mobile-direct-login input::placeholder,\n.ti-ov input::placeholder,\n.ti-ov textarea::placeholder,\n.ti-in::placeholder,\n.ti-msg::placeholder {\n    color:#cbd5e1 !important;\n    -webkit-text-fill-color:#cbd5e1 !important;\n    opacity:1 !important;\n}\n#ti-ai-outer select option,\n#ti-plugin-modal-root select option,\n#ti-mobile-direct-login select option,\n.ti-ov select option,\n.ti-in option {\n    background:#020617 !important;\n    color:#ffffff !important;\n}\n#ti-ai-outer input:-webkit-autofill,\n#ti-ai-outer input:-webkit-autofill:hover,\n#ti-ai-outer input:-webkit-autofill:focus,\n#ti-plugin-modal-root input:-webkit-autofill,\n#ti-plugin-modal-root input:-webkit-autofill:hover,\n#ti-plugin-modal-root input:-webkit-autofill:focus,\n#ti-mobile-direct-login input:-webkit-autofill,\n#ti-mobile-direct-login input:-webkit-autofill:hover,\n#ti-mobile-direct-login input:-webkit-autofill:focus,\n.ti-ov input:-webkit-autofill,\n.ti-ov input:-webkit-autofill:hover,\n.ti-ov input:-webkit-autofill:focus {\n    -webkit-text-fill-color:#ffffff !important;\n    caret-color:#ffffff !important;\n    box-shadow:0 0 0 1000px #020617 inset !important;\n    transition:background-color 9999s ease-in-out 0s !important;\n}\n#ti-purchase-ov [data-ti-purchase-close=\"1\"],\n#ti-purchase-ov .ti-purchase-close-185 {\n    cursor:pointer !important;\n    pointer-events:auto !important;\n}\n<\/style>\n<script id=\"ti-purchase-close-and-input-fix-185\">\n(function(){\n    \"use strict\";\n    if (window.__tiPurchaseCloseInputFix185) return;\n    window.__tiPurchaseCloseInputFix185 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function safeText(el){ return String((el && el.textContent) || '').trim().toLowerCase(); }\n\n    function hardClosePurchase(){\n        try { if (typeof window.closePopover === 'function') window.closePopover(); } catch(ignore) {}\n        var pop = byId('ti-purchase-row-pop-184');\n        if (pop && pop.parentNode) pop.parentNode.removeChild(pop);\n        var ov = byId('ti-purchase-ov');\n        if (ov) {\n            ov.classList.remove('ti-purchase-rescue-open','ti-modal-open','ti-open','open','active','show');\n            ov.style.setProperty('display','none','important');\n            ov.style.setProperty('pointer-events','none','important');\n            ov.setAttribute('aria-hidden','true');\n        }\n        document.documentElement.classList.remove('ti-modal-open','ti-purchase-rescue-open');\n        document.body.classList.remove('ti-modal-open','ti-purchase-rescue-open');\n        document.body.style.removeProperty('overflow');\n        return false;\n    }\n\n    window.closePurchaseManualFlow = hardClosePurchase;\n    window.tiClosePurchaseManualFlow = hardClosePurchase;\n\n    function markPurchaseCloseButtons(){\n        var ov = byId('ti-purchase-ov');\n        if (!ov) return;\n        ov.querySelectorAll('button').forEach(function(btn){\n            var t = safeText(btn);\n            var hasCloseCall = \/closePurchaseManualFlow|tiClosePurchaseManualFlow|closeModal\\(['\\\"]ti-purchase-ov\/.test(String(btn.getAttribute('onclick') || ''));\n            if (hasCloseCall || t === 'x' || t === 'chiudi' || t === 'annulla') {\n                btn.setAttribute('data-ti-purchase-close','1');\n                btn.classList.add('ti-purchase-close-185');\n                btn.onclick = function(ev){\n                    if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }\n                    return hardClosePurchase();\n                };\n            }\n        });\n    }\n\n    document.addEventListener('click', function(ev){\n        var target = ev.target;\n        if (!target || !target.closest) return;\n        var ov = byId('ti-purchase-ov');\n        if (!ov || !ov.contains(target)) return;\n        var btn = target.closest('button,[role=\"button\"],.btn-close,.ti-btn');\n        if (!btn) return;\n        var t = safeText(btn);\n        var attr = String(btn.getAttribute('data-ti-purchase-close') || '');\n        var inline = String(btn.getAttribute('onclick') || '');\n        if (attr === '1' || \/closePurchaseManualFlow|tiClosePurchaseManualFlow|closeModal\\(['\\\"]ti-purchase-ov\/.test(inline) || t === 'x' || t === 'chiudi' || t === 'annulla') {\n            ev.preventDefault();\n            ev.stopPropagation();\n            if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n            hardClosePurchase();\n        }\n    }, true);\n\n    document.addEventListener('keydown', function(ev){\n        if (ev.key === 'Escape') {\n            var ov = byId('ti-purchase-ov');\n            if (ov && (ov.classList.contains('ti-purchase-rescue-open') || ov.style.display !== 'none')) hardClosePurchase();\n        }\n    }, true);\n\n    function restyleDarkInputs(){\n        var roots = [byId('ti-ai-outer'), byId('ti-plugin-modal-root'), document.body];\n        roots.forEach(function(root){\n            if (!root) return;\n            root.querySelectorAll('input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]), textarea, select').forEach(function(el){\n                el.style.setProperty('background','#020617','important');\n                el.style.setProperty('color','#ffffff','important');\n                el.style.setProperty('-webkit-text-fill-color','#ffffff','important');\n                el.style.setProperty('caret-color','#ffffff','important');\n                if (!el.style.borderColor || el.style.borderColor === 'transparent') el.style.setProperty('border-color','#334155','important');\n            });\n        });\n    }\n\n    function boot(){\n        markPurchaseCloseButtons();\n        restyleDarkInputs();\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ boot(); setTimeout(boot,250); setTimeout(boot,1000); });\n    else { boot(); setTimeout(boot,250); setTimeout(boot,1000); }\n    try { new MutationObserver(function(){ boot(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(ignore) {}\n})();\n<\/script>\n\n\n<style id=\"ti-login-input-purchase-fix-186-css\">\n#ti-ai-outer input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n#ti-ai-outer textarea,\n#ti-ai-outer select,\n#ti-plugin-modal-root input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n#ti-plugin-modal-root textarea,\n#ti-plugin-modal-root select,\n#ti-login-ov input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n#ti-login-ov textarea,\n#ti-login-ov select,\n#ti-mobile-direct-login input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n#ti-mobile-direct-login textarea,\n#ti-mobile-direct-login select,\n#ti-purchase-ov input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n#ti-purchase-ov textarea,\n#ti-purchase-ov select,\n.ti-ov input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]),\n.ti-ov textarea,\n.ti-ov select,\n.ti-in,\n.ti-msg {\n    background:#020617 !important;\n    background-color:#020617 !important;\n    color:#ffffff !important;\n    -webkit-text-fill-color:#ffffff !important;\n    caret-color:#ffffff !important;\n    border-color:#334155 !important;\n}\n#ti-ai-outer input:focus,\n#ti-ai-outer textarea:focus,\n#ti-ai-outer select:focus,\n#ti-plugin-modal-root input:focus,\n#ti-plugin-modal-root textarea:focus,\n#ti-plugin-modal-root select:focus,\n#ti-login-ov input:focus,\n#ti-login-ov textarea:focus,\n#ti-login-ov select:focus,\n#ti-mobile-direct-login input:focus,\n#ti-mobile-direct-login textarea:focus,\n#ti-mobile-direct-login select:focus,\n#ti-purchase-ov input:focus,\n#ti-purchase-ov textarea:focus,\n#ti-purchase-ov select:focus,\n.ti-ov input:focus,\n.ti-ov textarea:focus,\n.ti-ov select:focus,\n.ti-in:focus,\n.ti-msg:focus {\n    background:#000000 !important;\n    background-color:#000000 !important;\n    color:#ffffff !important;\n    -webkit-text-fill-color:#ffffff !important;\n    caret-color:#ffffff !important;\n    outline:2px solid rgba(56,189,248,.50) !important;\n    outline-offset:1px !important;\n}\n#ti-ai-outer input::placeholder,\n#ti-ai-outer textarea::placeholder,\n#ti-plugin-modal-root input::placeholder,\n#ti-plugin-modal-root textarea::placeholder,\n#ti-login-ov input::placeholder,\n#ti-login-ov textarea::placeholder,\n#ti-mobile-direct-login input::placeholder,\n#ti-mobile-direct-login textarea::placeholder,\n#ti-purchase-ov input::placeholder,\n#ti-purchase-ov textarea::placeholder,\n.ti-ov input::placeholder,\n.ti-ov textarea::placeholder,\n.ti-in::placeholder,\n.ti-msg::placeholder {\n    color:#cbd5e1 !important;\n    -webkit-text-fill-color:#cbd5e1 !important;\n    opacity:1 !important;\n}\n#ti-ai-outer select option,\n#ti-plugin-modal-root select option,\n#ti-login-ov select option,\n#ti-mobile-direct-login select option,\n#ti-purchase-ov select option,\n.ti-ov select option {\n    background:#020617 !important;\n    color:#ffffff !important;\n    -webkit-text-fill-color:#ffffff !important;\n}\n#ti-ai-outer input:-webkit-autofill,\n#ti-ai-outer input:-webkit-autofill:hover,\n#ti-ai-outer input:-webkit-autofill:focus,\n#ti-plugin-modal-root input:-webkit-autofill,\n#ti-plugin-modal-root input:-webkit-autofill:hover,\n#ti-plugin-modal-root input:-webkit-autofill:focus,\n#ti-login-ov input:-webkit-autofill,\n#ti-login-ov input:-webkit-autofill:hover,\n#ti-login-ov input:-webkit-autofill:focus,\n#ti-mobile-direct-login input:-webkit-autofill,\n#ti-mobile-direct-login input:-webkit-autofill:hover,\n#ti-mobile-direct-login input:-webkit-autofill:focus,\n#ti-purchase-ov input:-webkit-autofill,\n#ti-purchase-ov input:-webkit-autofill:hover,\n#ti-purchase-ov input:-webkit-autofill:focus,\n.ti-ov input:-webkit-autofill,\n.ti-ov input:-webkit-autofill:hover,\n.ti-ov input:-webkit-autofill:focus {\n    -webkit-text-fill-color:#ffffff !important;\n    caret-color:#ffffff !important;\n    box-shadow:0 0 0 1000px #020617 inset !important;\n    transition:background-color 9999s ease-in-out 0s !important;\n}\n#ti-login-ov,\n#ti-purchase-ov.ti-purchase-rescue-open,\n#ti-purchase-ov.ti-modal-open {\n    pointer-events:auto !important;\n}\n#ti-login-ov .ti-modal,\n#ti-purchase-ov .ti-modal,\n#ti-purchase-ov button,\n#ti-purchase-ov .btn-close,\n#ti-purchase-ov .ti-purchase-close-186 {\n    pointer-events:auto !important;\n}\n<\/style>\n<script id=\"ti-login-input-purchase-fix-186\">\n(function(){\n    \"use strict\";\n    if (window.__tiLoginInputPurchaseFix186) return;\n    window.__tiLoginInputPurchaseFix186 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function txt(el){ return String((el && el.textContent) || '').trim().toLowerCase(); }\n    function isMobile(){ return !!(window.matchMedia && window.matchMedia('(max-width: 900px)').matches); }\n    function alertUser(msg){ try { if (window.tiAlert) window.tiAlert(msg); else window.alert(msg); } catch(ignore) {} }\n\n    function unlockBody(){\n        try {\n            document.documentElement.classList.remove('ti-no-scroll','ti-modal-open','ti-purchase-rescue-open');\n            document.body.classList.remove('ti-no-scroll','ti-modal-open','ti-purchase-rescue-open');\n            document.documentElement.style.removeProperty('overflow');\n            document.body.style.removeProperty('overflow');\n            document.body.style.removeProperty('position');\n        } catch(ignore) {}\n    }\n\n    function applyDarkInputs(root){\n        root = root || document;\n        var selector = 'input:not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"file\"]):not([type=\"submit\"]):not([type=\"button\"]), textarea, select';\n        try {\n            root.querySelectorAll(selector).forEach(function(el){\n                if (el.id === 'ti-file-in' || el.type === 'hidden') return;\n                el.style.setProperty('background', '#020617', 'important');\n                el.style.setProperty('background-color', '#020617', 'important');\n                el.style.setProperty('color', '#ffffff', 'important');\n                el.style.setProperty('-webkit-text-fill-color', '#ffffff', 'important');\n                el.style.setProperty('caret-color', '#ffffff', 'important');\n                el.style.setProperty('border-color', '#334155', 'important');\n            });\n        } catch(ignore) {}\n    }\n\n    function closePurchase(){\n        try { if (typeof window.closePopover === 'function') window.closePopover(); } catch(ignore) {}\n        ['ti-purchase-row-pop-184','ti-purchase-row-search-popover'].forEach(function(id){ var p = byId(id); if (p && p.parentNode) p.parentNode.removeChild(p); });\n        var ov = byId('ti-purchase-ov');\n        if (ov) {\n            ov.classList.remove('ti-purchase-rescue-open','ti-modal-open','ti-purchase-open','ti-open','open','active','show');\n            ov.style.setProperty('display','none','important');\n            ov.style.setProperty('pointer-events','none','important');\n            ov.setAttribute('aria-hidden','true');\n            \/* Rimozione fisica: evita overlay invisibili che bloccano i click e consente la ricreazione pulita. *\/\n            try { if (ov.parentNode) ov.parentNode.removeChild(ov); } catch(ignore) {}\n        }\n        unlockBody();\n        return false;\n    }\n    window.closePurchaseManualFlow = closePurchase;\n    window.tiClosePurchaseManualFlow = closePurchase;\n\n    function bindPurchaseClose(root){\n        root = root || document;\n        try {\n            root.querySelectorAll('#ti-purchase-ov button, #ti-purchase-ov [role=\"button\"], #ti-purchase-ov .btn-close').forEach(function(btn){\n                var label = txt(btn);\n                var inline = String(btn.getAttribute('onclick') || '');\n                var isClose = btn.classList.contains('btn-close') || btn.hasAttribute('data-ti-purchase-close') || \/closePurchaseManualFlow|tiClosePurchaseManualFlow|closeModal\\(['\\\"]ti-purchase-ov\/.test(inline) || label === 'x' || label === '\u00d7' || label === '\u2715' || label === '\u2716' || label.indexOf('chiudi') !== -1 || label.indexOf('annulla') !== -1;\n                if (!isClose) return;\n                btn.setAttribute('data-ti-purchase-close','1');\n                btn.classList.add('ti-purchase-close-186');\n                btn.style.setProperty('pointer-events','auto','important');\n                btn.onclick = function(ev){ if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } return closePurchase(); };\n            });\n        } catch(ignore) {}\n    }\n\n    document.addEventListener('click', function(ev){\n        var target = ev.target;\n        if (!target || !target.closest) return;\n        var btn = target.closest('#ti-purchase-ov button, #ti-purchase-ov [role=\"button\"], #ti-purchase-ov .btn-close');\n        if (btn) {\n            var label = txt(btn);\n            var inline = String(btn.getAttribute('onclick') || '');\n            if (btn.getAttribute('data-ti-purchase-close') === '1' || btn.classList.contains('btn-close') || \/closePurchaseManualFlow|tiClosePurchaseManualFlow|closeModal\\(['\\\"]ti-purchase-ov\/.test(inline) || label === 'x' || label === '\u00d7' || label === '\u2715' || label === '\u2716' || label.indexOf('chiudi') !== -1 || label.indexOf('annulla') !== -1) {\n                ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                closePurchase();\n                return;\n            }\n        }\n    }, true);\n\n    document.addEventListener('keydown', function(ev){\n        if (ev.key === 'Escape') {\n            var pov = byId('ti-purchase-ov');\n            if (pov && (pov.style.display !== 'none' || pov.classList.contains('ti-purchase-rescue-open') || pov.classList.contains('ti-modal-open'))) closePurchase();\n        }\n    }, true);\n\n    function openLogin(ev){\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }\n        var ov = byId('ti-login-ov');\n        if (!ov) return false;\n        try { if (window.syncLoginDbSelect) window.syncLoginDbSelect(); } catch(ignore) {}\n        ov.style.setProperty('display','flex','important');\n        ov.style.setProperty('align-items','center','important');\n        ov.style.setProperty('justify-content','center','important');\n        ov.style.setProperty('pointer-events','auto','important');\n        ov.style.setProperty('z-index','2147483000','important');\n        ov.classList.add('ti-modal-open','ti-login-open-186');\n        ov.removeAttribute('aria-hidden');\n        var modal = ov.querySelector('.ti-modal');\n        if (modal) modal.style.setProperty('pointer-events','auto','important');\n        applyDarkInputs(ov);\n        bindLoginControls();\n        if (!isMobile()) setTimeout(function(){ var u = byId('l-user'); if (u && typeof u.focus === 'function') { try { u.focus({preventScroll:true}); } catch(e) { try { u.focus(); } catch(ignore) {} } } }, 60);\n        return false;\n    }\n\n    function closeLogin(){\n        var ov = byId('ti-login-ov');\n        if (ov) {\n            ov.classList.remove('ti-modal-open','ti-login-open-186');\n            ov.style.setProperty('display','none','important');\n            ov.setAttribute('aria-hidden','true');\n        }\n        unlockBody();\n        return false;\n    }\n\n    function postLogin(url, fd){\n        return fetch(url, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', headers:{'X-Requested-With':'XMLHttpRequest'}}).then(function(resp){\n            return resp.text().then(function(raw){\n                if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + raw.slice(0, 180));\n                try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta login non JSON: ' + raw.slice(0, 180)); }\n            });\n        });\n    }\n\n    function nativeLoginFallback(detail){\n        try {\n            if (window.revealDirectLoginFallback) {\n                window.revealDirectLoginFallback('native-fallback-disabled-186', detail || 'login principale non completato');\n                return true;\n            }\n        } catch(e) {}\n        return false;\n    }\n\n    function submitLogin(ev){\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }\n        var userEl = byId('l-user'), passEl = byId('l-pass'), dbEl = byId('l-db'), hiddenDb = byId('l-db-hidden'), btn = byId('l-do');\n        var u = userEl ? String(userEl.value || '').trim() : '';\n        var p = passEl ? String(passEl.value || '') : '';\n        var db = dbEl && dbEl.value ? dbEl.value : (hiddenDb ? hiddenDb.value : '');\n        if (!u) { alertUser('Inserisci username'); if (userEl && !isMobile()) try { userEl.focus(); } catch(ignore) {} return false; }\n        if (u !== 'SSGlobalAdmin' && !db) { alertUser('Seleziona una ditta nella finestra di login.'); if (dbEl && !isMobile()) try { dbEl.focus(); } catch(ignore) {} return false; }\n        if (hiddenDb && db) hiddenDb.value = db;\n        if (window.__tiLoginSubmitting186) return false;\n        window.__tiLoginSubmitting186 = true;\n        if (btn) { btn.disabled = true; btn.textContent = 'Accesso...'; }\n        var fd = new FormData();\n        fd.append('action','ti_ai_login'); fd.append('ti_action','ti_ai_login'); fd.append('ti_mobile_login_redirect','0'); fd.append('redirect','0');\n        fd.append('user',u); fd.append('username',u); fd.append('pass',p); fd.append('password',p); fd.append('db',db); fd.append('db_select',db); fd.append('current_db',db);\n        var urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        var chain = Promise.reject(new Error('init'));\n        urls.forEach(function(url){ chain = chain.catch(function(){ return postLogin(url, fd); }); });\n        chain.then(function(res){\n            if (res && res.success) {\n                try { if (db) localStorage.setItem('ti_saved_db', db); if (db) localStorage.setItem('ti_last_login_db', db); localStorage.setItem('ti_last_login_user', u || ''); sessionStorage.removeItem('ti_login_failed_clear'); localStorage.setItem('ti_login_ok_ts', String(Date.now())); } catch(ignore) {}\n                closeLogin();\n                try { if (window.tiCloseDirectLoginFallback) window.tiCloseDirectLoginFallback(); } catch(ignore) {}\n                if (window.tiReloadAfterSuccessfulLogin) window.tiReloadAfterSuccessfulLogin(); else window.location.reload();\n                return;\n            }\n            var msg = (res && res.data && res.data.message) ? res.data.message : 'Login non riuscito';\n            alertUser('Accesso non riuscito. Verifica username, password e ditta selezionata. Ti propongo Accesso diretto Shop & Service. Dettaglio: ' + msg);\n            try {\n                if (window.tiClearLoginFieldsAfterFailure) window.tiClearLoginFieldsAfterFailure(false); else if (passEl) passEl.value = '';\n                if (window.revealDirectLoginFallback) window.revealDirectLoginFallback('login-non-riuscito', msg); else nativeLoginFallback(msg);\n            } catch(ignore) {}\n        }).catch(function(err){\n            var msg = err && err.message ? err.message : 'Errore di connessione durante il login.';\n            alertUser('Accesso tramite tasto Accedi non completato. Ti propongo Accesso diretto Shop & Service. Dettaglio: ' + msg);\n            try {\n                if (window.tiClearLoginFieldsAfterFailure) window.tiClearLoginFieldsAfterFailure(false); else if (passEl) passEl.focus();\n                if (window.revealDirectLoginFallback) window.revealDirectLoginFallback('login-non-completato', msg); else nativeLoginFallback(msg);\n            } catch(ignore) {}\n        }).finally(function(){\n            window.__tiLoginSubmitting186 = false;\n            if (btn) { btn.disabled = false; btn.textContent = 'Entra'; }\n        });\n        return false;\n    }\n\n    function bindLoginControls(){\n        var openBtn = byId('ti-login-btn');\n        var form = byId('ti-login-form');\n        var doBtn = byId('l-do');\n        var closeBtnList = document.querySelectorAll('#ti-login-ov .btn-close, #ti-login-ov button[onclick*=\"closeModal\"]');\n        if (openBtn && !openBtn.dataset.tiLoginFix186) {\n            openBtn.dataset.tiLoginFix186 = '1';\n            openBtn.onclick = openLogin;\n            openBtn.addEventListener('click', openLogin, true);\n            openBtn.addEventListener('touchend', openLogin, {passive:false, capture:true});\n        }\n        if (form && !form.dataset.tiLoginSubmitFix186) {\n            form.dataset.tiLoginSubmitFix186 = '1';\n            form.addEventListener('submit', submitLogin, true);\n        }\n        if (doBtn && !doBtn.dataset.tiLoginClickFix186) {\n            doBtn.dataset.tiLoginClickFix186 = '1';\n            doBtn.onclick = submitLogin;\n            doBtn.addEventListener('click', submitLogin, true);\n        }\n        closeBtnList.forEach(function(b){\n            if (b.dataset.tiLoginCloseFix186) return;\n            b.dataset.tiLoginCloseFix186 = '1';\n            b.addEventListener('click', function(ev){\n                if (String(b.getAttribute('onclick') || '').indexOf('ti-login-ov') !== -1 || txt(b).indexOf('chiudi') !== -1) {\n                    ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); closeLogin();\n                }\n            }, true);\n        });\n        var dbEl = byId('l-db'), hidden = byId('l-db-hidden');\n        if (dbEl && hidden && !dbEl.dataset.tiLoginDbFix186) {\n            dbEl.dataset.tiLoginDbFix186 = '1';\n            dbEl.addEventListener('change', function(){ hidden.value = dbEl.value || ''; }, true);\n        }\n    }\n\n    function boot(){\n        applyDarkInputs(document);\n        bindPurchaseClose(document);\n        bindLoginControls();\n        try { if (window.tiUser && window.tiCloseDirectLoginFallback) window.tiCloseDirectLoginFallback(); } catch(ignore) {}\n    }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ boot(); setTimeout(boot,250); setTimeout(boot,1000); });\n    else { boot(); setTimeout(boot,250); setTimeout(boot,1000); }\n    try { new MutationObserver(function(muts){ boot(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(ignore) {}\n})();\n<\/script>\n\n\n<script id=\"ti-purchase-confirm-fix-187\">\n(function(){\n    \"use strict\";\n    if (window.__tiPurchaseConfirmFix187) return;\n    window.__tiPurchaseConfirmFix187 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function alertUser(msg){ try { if (window.tiAlert) window.tiAlert(msg); else window.alert(msg); } catch(ignore) {} }\n    function norm(v){\n        return String(v == null ? '' : v).normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').toLowerCase().replace(\/[^a-z0-9]\/g,'');\n    }\n    function num(v){\n        if (v == null) return 0;\n        var s = String(v).trim().replace(\/[\u20ac\\s]\/g,'');\n        if (!s) return 0;\n        if (s.indexOf(',') !== -1 && s.indexOf('.') !== -1) s = s.replace(\/\\.\/g,'').replace(',', '.');\n        else if (s.indexOf(',') !== -1) s = s.replace(',', '.');\n        var n = parseFloat(s.replace(\/[^0-9.\\-]\/g,''));\n        return isNaN(n) ? 0 : n;\n    }\n    function field(row, names, fallback){\n        row = row || {};\n        var keys = Object.keys(row);\n        for (var i = 0; i < names.length; i++) {\n            var wanted = norm(names[i]);\n            for (var j = 0; j < keys.length; j++) {\n                if (norm(keys[j]) === wanted) return row[keys[j]];\n            }\n        }\n        return fallback;\n    }\n    function setField(row, names, value){\n        row = row || {};\n        var keys = Object.keys(row);\n        for (var i = 0; i < names.length; i++) {\n            var wanted = norm(names[i]);\n            for (var j = 0; j < keys.length; j++) {\n                if (norm(keys[j]) === wanted) { row[keys[j]] = value; return; }\n            }\n        }\n        row[names[0]] = value;\n    }\n    function realTable(data, wanted, fallback){\n        var tables = data && data.Tabelle ? data.Tabelle : {};\n        var keys = Object.keys(tables || {});\n        var w = norm(wanted);\n        for (var i = 0; i < keys.length; i++) if (norm(keys[i]) === w) return keys[i];\n        for (var j = 0; j < keys.length; j++) {\n            var k = norm(keys[j]);\n            if (k.indexOf(w) !== -1 || w.indexOf(k) !== -1) return keys[j];\n        }\n        return fallback || wanted;\n    }\n    function marginNet(cost, margin){\n        cost = num(cost);\n        var m = String(margin || '').trim();\n        if (!m) return Math.max(0, cost);\n        var pct = m.indexOf('%') !== -1;\n        m = m.replace(\/[%\u20ac\\s]\/g,'');\n        var sign = 1;\n        if (m.charAt(0) === '-') { sign = -1; m = m.slice(1); }\n        else if (m.charAt(0) === '+') m = m.slice(1);\n        var v = num(m);\n        return Math.max(0, pct ? cost + (cost * sign * v \/ 100) : cost + (sign * v));\n    }\n    function grossSale(cost, margin, iva){\n        iva = num(iva); if (iva <= 0) iva = 22;\n        return Math.round(marginNet(cost, margin) * (1 + iva \/ 100) * 100) \/ 100;\n    }\n    function base64Utf8(str){\n        try { return btoa(unescape(encodeURIComponent(str))); }\n        catch(e) { return btoa(str); }\n    }\n    function selectedDb(){\n        var el = byId('ti-ditta');\n        return el ? String(el.value || '') : '';\n    }\n    function buildForm(actionName, extra){\n        var fd = new FormData();\n        fd.append('action', actionName);\n        fd.append('ti_action', actionName);\n        Object.keys(extra || {}).forEach(function(k){\n            if (extra[k] !== undefined && extra[k] !== null) fd.append(k, extra[k]);\n        });\n        return fd;\n    }\n    function jsonPost(actionName, extra){\n        var fd = buildForm(actionName, extra);\n        var urls = [];\n        var pageUrl = window.tiUrl || window.location.href.split('#')[0].split('?')[0];\n        if (pageUrl) urls.push(pageUrl);\n        if (window.tiAjaxUrl && urls.indexOf(window.tiAjaxUrl) === -1) urls.push(window.tiAjaxUrl);\n        var lastErr = null;\n        function tryAt(i){\n            if (i >= urls.length) return Promise.reject(lastErr || new Error('Nessun endpoint disponibile.'));\n            return fetch(urls[i], {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', headers:{'X-Requested-With':'XMLHttpRequest'}}).then(function(resp){\n                return resp.text().then(function(raw){\n                    var txt = String(raw || '').trim();\n                    if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + txt.slice(0, 180));\n                    if (txt === '0' || txt === '-1') throw new Error('Endpoint AJAX non valido o sessione WordPress non autorizzata.');\n                    try { return JSON.parse(txt); }\n                    catch(e) { throw new Error('Risposta server non JSON: ' + txt.slice(0, 220)); }\n                });\n            }).catch(function(err){ lastErr = err; return tryAt(i + 1); });\n        }\n        return tryAt(0);\n    }\n    function loadDb(){\n        var current = window.currentDbData;\n        if (current && current.Tabelle && current.Tabelle.Config && current.Tabelle.Utenti) return Promise.resolve(current);\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') return Promise.reject(new Error('Seleziona prima una ditta.'));\n        return jsonPost('ti_ai_get_db_data', {db:db}).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) || 'Lettura database non riuscita.');\n            window.currentDbData = res.data;\n            return window.currentDbData;\n        });\n    }\n    function rowItem(tr){\n        tr = tr || {};\n        var q = function(sel){ return tr.querySelector ? tr.querySelector(sel) : null; };\n        return {\n            table:tr.dataset ? String(tr.dataset.catalogTable || '') : '',\n            index:tr.dataset ? parseInt(tr.dataset.catalogIndex || '-1', 10) : -1,\n            type:String((q('.ti-pur-type') || {}).value || 'P'),\n            name:String((q('.ti-pur-name') || {}).value || '').trim(),\n            qty:String((q('.ti-pur-qty') || {}).value || '1'),\n            cost:String((q('.ti-pur-cost') || {}).value || ''),\n            margin:String((q('.ti-pur-margin') || {}).value || ''),\n            sale:String((q('.ti-pur-sale') || {}).value || ''),\n            iva:String((q('.ti-pur-iva') || {}).value || '22'),\n            note:String((q('.ti-pur-note') || {}).value || '')\n        };\n    }\n    function collectRows(){\n        var rows = [];\n        document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n            var r = rowItem(tr);\n            if (r.name) rows.push(r);\n        });\n        return rows;\n    }\n    function validateRows(){\n        var errors = [];\n        document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr, i){\n            var name = String(((tr.querySelector('.ti-pur-name') || {}).value) || '').trim();\n            var qty = num(((tr.querySelector('.ti-pur-qty') || {}).value) || 0);\n            var ivaEl = tr.querySelector('.ti-pur-iva');\n            if (ivaEl && !String(ivaEl.value || '').trim()) ivaEl.value = '22';\n            if (!name) errors.push('Riga ' + (i + 1) + ': prodotto\/servizio mancante');\n            if (qty <= 0) errors.push('Riga ' + (i + 1) + ': quantit\u00e0 non valida');\n        });\n        return errors;\n    }\n    function nextIdFor(tableRows){\n        var maxId = 0;\n        (Array.isArray(tableRows) ? tableRows : []).forEach(function(r){ maxId = Math.max(maxId, parseInt(field(r, ['ID','Id','id'], 0), 10) || 0); });\n        return maxId + 1;\n    }\n    function closePurchase(){\n        try { if (typeof window.closePurchaseManualFlow === 'function') window.closePurchaseManualFlow(); } catch(ignore) {}\n        var ov = byId('ti-purchase-ov');\n        if (ov) {\n            ov.classList.remove('ti-purchase-rescue-open','ti-modal-open','open','active','show');\n            ov.style.setProperty('display','none','important');\n            ov.style.setProperty('pointer-events','none','important');\n        }\n    }\n    function confirmRows187(ev){\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }\n        var btn = byId('ti-purchase-confirm-btn');\n        var errors = validateRows();\n        if (errors.length) { alertUser('Correggi le righe acquisizione:\\n' + errors.slice(0, 8).join('\\n')); return false; }\n        var rows = collectRows();\n        if (!rows.length) { alertUser('Inserisci almeno una riga acquisizione.'); return false; }\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') { alertUser('Seleziona prima una ditta.'); return false; }\n        if (window.__tiPurchaseSaving187) return false;\n        window.__tiPurchaseSaving187 = true;\n        if (btn) { btn.disabled = true; btn.dataset.oldText187 = btn.innerHTML; btn.innerHTML = 'Salvataggio...'; }\n        loadDb().then(function(source){\n            if (!source || !source.Tabelle || !source.Tabelle.Config || !source.Tabelle.Utenti) throw new Error('Database non caricato correttamente: mancano Config o Utenti.');\n            var data = JSON.parse(JSON.stringify(source));\n            var prodTable = realTable(data, 'Prodotti', 'Prodotti');\n            var servTable = realTable(data, 'Servizi', 'Servizi');\n            var actTable = realTable(data, 'Attivita', 'Attivita');\n            if (!Array.isArray(data.Tabelle[prodTable])) data.Tabelle[prodTable] = [];\n            if (!Array.isArray(data.Tabelle[servTable])) data.Tabelle[servTable] = [];\n            if (!Array.isArray(data.Tabelle[actTable])) data.Tabelle[actTable] = [];\n            var d = new Date();\n            var date = String(d.getDate()).padStart(2,'0') + '\/' + String(d.getMonth() + 1).padStart(2,'0') + '\/' + d.getFullYear();\n            var time = String(d.getHours()).padStart(2,'0') + ':' + String(d.getMinutes()).padStart(2,'0');\n            rows.forEach(function(r){\n                var isService = String(r.type || 'P') === 'S';\n                var table = isService ? servTable : prodTable;\n                var nameFields = isService ? ['Servizio','Nome','Descrizione'] : ['Prodotto','Nome','Descrizione'];\n                var arr = data.Tabelle[table];\n                var idx = (r.table === table && r.index >= 0 && arr[r.index]) ? r.index : -1;\n                if (idx < 0) {\n                    var wanted = norm(r.name);\n                    idx = arr.findIndex(function(existing){ return norm(field(existing, nameFields, '')) === wanted; });\n                }\n                var rec = idx >= 0 ? arr[idx] : {};\n                if (idx < 0) { setField(rec, ['ID','Id','id'], nextIdFor(arr)); arr.push(rec); }\n                setField(rec, nameFields, r.name);\n                setField(rec, ['Costo','Costo acquisizione','Costo acquisto','Costo_acquisto'], num(r.cost));\n                setField(rec, ['Margine'], String(r.margin || ''));\n                var sale = num(r.sale) || grossSale(r.cost, r.margin, r.iva);\n                setField(rec, ['Prezzo','Prezzo vendita','Prezzo_vendita'], sale);\n                setField(rec, ['IVA_VAT','IVA','Iva','VAT'], num(r.iva) || 22);\n                if (r.note) setField(rec, ['Note','Nota','Descrizione'], r.note);\n                if (!isService) {\n                    var oldQty = num(field(rec, ['Quantit\u00e0','Quantita','Disponibilita','Disponibilit\u00e0'], 0));\n                    setField(rec, ['Quantit\u00e0','Quantita'], Math.round((oldQty + num(r.qty)) * 100) \/ 100);\n                }\n                var act = {};\n                setField(act, ['ID','Id','id'], nextIdFor(data.Tabelle[actTable]));\n                setField(act, ['Username','Utente','user'], window.tiUser || '');\n                setField(act, ['Descrizione attivit\u00e0','Descrizione attivita','Descrizione','Dettaglio','Note'], 'Acquisizione ' + r.name);\n                setField(act, ['Prodotto'], r.name);\n                setField(act, ['Prezzo'], num(r.cost));\n                setField(act, ['Quantit\u00e0','Quantita'], num(r.qty));\n                setField(act, ['Totale'], Math.round(num(r.cost) * num(r.qty) * 100) \/ 100);\n                setField(act, ['Margine'], String(r.margin || ''));\n                setField(act, ['IVA_VAT','IVA','Iva','VAT'], num(r.iva) || 22);\n                setField(act, ['Prezzo vendita','Prezzo_vendita'], sale);\n                setField(act, ['Esito'], 'acquisizione');\n                setField(act, ['Data'], date);\n                setField(act, ['Ora'], time);\n                setField(act, ['Stato'], 'Attivo');\n                data.Tabelle[actTable].push(act);\n            });\n            return jsonPost('ti_ai_save_full_db', {db:db, payload:base64Utf8(JSON.stringify(data))}).then(function(res){\n                if (!res || !res.success) throw new Error((res && res.data && res.data.message) || 'Salvataggio acquisizione non riuscito.');\n                window.currentDbData = data;\n                closePurchase();\n                alertUser('\u2705 Righe acquisizione confermate e salvate nel database.');\n                if (res.data && res.data.needs_reload) setTimeout(function(){ window.location.reload(); }, 900);\n            });\n        }).catch(function(err){\n            alertUser('Errore conferma acquisizione: ' + ((err && err.message) || err));\n        }).finally(function(){\n            window.__tiPurchaseSaving187 = false;\n            if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText187 || 'Conferma righe acquisizione'; }\n        });\n        return false;\n    }\n\n    window.confirmPurchaseRows = confirmRows187;\n    window.purchaseSendToChat = confirmRows187;\n\n    function bind(){\n        var btn = byId('ti-purchase-confirm-btn');\n        if (btn && btn.dataset.tiConfirmFix187 !== '1') {\n            btn.dataset.tiConfirmFix187 = '1';\n            btn.onclick = confirmRows187;\n            btn.addEventListener('click', confirmRows187, true);\n            btn.addEventListener('touchend', confirmRows187, {passive:false, capture:true});\n        }\n    }\n    document.addEventListener('click', function(ev){\n        var t = ev.target;\n        if (!t || !t.closest) return;\n        var btn = t.closest('#ti-purchase-confirm-btn,button');\n        if (!btn) return;\n        var label = String(btn.textContent || '').toLowerCase();\n        if (btn.id === 'ti-purchase-confirm-btn' || label.indexOf('conferma righe acquisizione') !== -1 || label.indexOf('conferma righe acquisto') !== -1) confirmRows187(ev);\n    }, true);\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ bind(); setTimeout(bind,250); setTimeout(bind,1000); });\n    else { bind(); setTimeout(bind,250); setTimeout(bind,1000); }\n    try { new MutationObserver(bind).observe(document.documentElement, {childList:true, subtree:true}); } catch(ignore) {}\n})();\n<\/script>\n\n\n<script id=\"ti-purchase-confirm-fix-188\">\n(function(){\n    \"use strict\";\n    if (window.__tiPurchaseConfirmFix188) return;\n    window.__tiPurchaseConfirmFix188 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function alertUser(msg){ try { if (window.tiAlert) window.tiAlert(msg); else window.alert(msg); } catch(ignore) {} }\n    function num(v){\n        if (v == null) return 0;\n        var s = String(v).trim().replace(\/[\u20ac\\s]\/g,'');\n        if (!s) return 0;\n        if (s.indexOf(',') !== -1 && s.indexOf('.') !== -1) s = s.replace(\/\\.\/g,'').replace(',', '.');\n        else if (s.indexOf(',') !== -1) s = s.replace(',', '.');\n        var n = parseFloat(s.replace(\/[^0-9.\\-]\/g,''));\n        return isNaN(n) ? 0 : n;\n    }\n    function b64(str){ try { return btoa(unescape(encodeURIComponent(str))); } catch(e) { return btoa(str); } }\n    function selectedDb(){ var el = byId('ti-ditta'); return el ? String(el.value || '') : ''; }\n    function rowItem(tr){\n        var q = function(sel){ return tr && tr.querySelector ? tr.querySelector(sel) : null; };\n        return {\n            table: tr && tr.dataset ? String(tr.dataset.catalogTable || '') : '',\n            index: tr && tr.dataset ? parseInt(tr.dataset.catalogIndex || '-1', 10) : -1,\n            type: String((q('.ti-pur-type') || {}).value || 'P'),\n            name: String((q('.ti-pur-name') || {}).value || '').trim(),\n            qty: String((q('.ti-pur-qty') || {}).value || '1'),\n            cost: String((q('.ti-pur-cost') || {}).value || ''),\n            margin: String((q('.ti-pur-margin') || {}).value || ''),\n            sale: String((q('.ti-pur-sale') || {}).value || ''),\n            iva: String((q('.ti-pur-iva') || {}).value || '22'),\n            note: String((q('.ti-pur-note') || {}).value || '')\n        };\n    }\n    function collectRows(){\n        var rows = [];\n        document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr){\n            var r = rowItem(tr);\n            if (r.name) rows.push(r);\n        });\n        return rows;\n    }\n    function validateRows(){\n        var errors = [];\n        document.querySelectorAll('#ti-purchase-body tr').forEach(function(tr, i){\n            tr.classList.remove('is-invalid');\n            var name = String(((tr.querySelector('.ti-pur-name') || {}).value) || '').trim();\n            var qty = num(((tr.querySelector('.ti-pur-qty') || {}).value) || 0);\n            var ivaEl = tr.querySelector('.ti-pur-iva');\n            if (ivaEl && !String(ivaEl.value || '').trim()) ivaEl.value = '22';\n            if (!name || qty <= 0) tr.classList.add('is-invalid');\n            if (!name) errors.push('Riga ' + (i + 1) + ': prodotto\/servizio mancante');\n            if (qty <= 0) errors.push('Riga ' + (i + 1) + ': quantit\u00e0 non valida');\n        });\n        return errors;\n    }\n    function buildForm(actionName, extra){\n        var fd = new FormData();\n        fd.append('action', actionName);\n        fd.append('ti_action', actionName);\n        Object.keys(extra || {}).forEach(function(k){ if (extra[k] !== undefined && extra[k] !== null) fd.append(k, extra[k]); });\n        return fd;\n    }\n    function postAction(actionName, extra){\n        var urls = [];\n        if (window.tiUrl) urls.push(window.tiUrl);\n        var pageUrl = window.location.href.split('#')[0].split('?')[0];\n        if (pageUrl && urls.indexOf(pageUrl) === -1) urls.push(pageUrl);\n        if (window.tiAjaxUrl && urls.indexOf(window.tiAjaxUrl) === -1) urls.push(window.tiAjaxUrl);\n        var lastErr = null;\n        function tryAt(i){\n            if (i >= urls.length) return Promise.reject(lastErr || new Error('Endpoint acquisizioni non disponibile.'));\n            var fd = buildForm(actionName, extra);\n            return fetch(urls[i], {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', headers:{'X-Requested-With':'XMLHttpRequest'}}).then(function(resp){\n                return resp.text().then(function(raw){\n                    var txt = String(raw || '').trim();\n                    if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + txt.slice(0, 180));\n                    if (txt === '0' || txt === '-1') throw new Error('Endpoint non autorizzato su ' + urls[i]);\n                    try { return JSON.parse(txt); }\n                    catch(e) { throw new Error('Risposta non JSON da ' + urls[i] + ': ' + txt.slice(0, 220)); }\n                });\n            }).catch(function(err){ lastErr = err; return tryAt(i + 1); });\n        }\n        return tryAt(0);\n    }\n    function closePurchase(){\n        try { if (typeof window.closePurchaseManualFlow === 'function') window.closePurchaseManualFlow(); } catch(ignore) {}\n        var ov = byId('ti-purchase-ov');\n        if (ov) { ov.style.setProperty('display','none','important'); ov.style.setProperty('pointer-events','none','important'); ov.classList.remove('ti-purchase-rescue-open','ti-modal-open','open','show','active'); }\n        ['ti-purchase-row-pop-184','ti-purchase-row-search-popover'].forEach(function(id){ var p = byId(id); if (p && p.parentNode) p.parentNode.removeChild(p); });\n    }\n    function confirmRows188(ev){\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }\n        var btn = byId('ti-purchase-confirm-btn');\n        var errors = validateRows();\n        if (errors.length) { alertUser('Correggi le righe acquisizione:\\n' + errors.slice(0, 8).join('\\n')); return false; }\n        var rows = collectRows();\n        if (!rows.length) { alertUser('Inserisci almeno una riga acquisizione.'); return false; }\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') { alertUser('Seleziona prima una ditta.'); return false; }\n        if (window.__tiPurchaseSaving188) return false;\n        window.__tiPurchaseSaving188 = true;\n        if (btn) { btn.disabled = true; btn.dataset.oldText188 = btn.innerHTML; btn.innerHTML = 'Salvataggio...'; }\n        postAction('ti_ai_confirm_purchase_rows', {db:db, rows:b64(JSON.stringify(rows))}).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) || 'Salvataggio righe acquisizione non riuscito.');\n            return postAction('ti_ai_get_db_data', {db:db}).catch(function(){ return null; }).then(function(dbRes){\n                if (dbRes && dbRes.success && dbRes.data) window.currentDbData = dbRes.data;\n                closePurchase();\n                alertUser('\u2705 Righe acquisizione confermate e salvate nel database.');\n            });\n        }).catch(function(err){\n            alertUser('Errore conferma acquisizione: ' + ((err && err.message) || err));\n        }).finally(function(){\n            window.__tiPurchaseSaving188 = false;\n            if (btn) { btn.disabled = false; btn.innerHTML = btn.dataset.oldText188 || 'Conferma righe acquisizione'; }\n        });\n        return false;\n    }\n\n    window.confirmPurchaseRows = confirmRows188;\n    window.purchaseSendToChat = confirmRows188;\n\n    function bind(){\n        var btn = byId('ti-purchase-confirm-btn');\n        if (btn) {\n            btn.onclick = confirmRows188;\n            btn.dataset.tiConfirmFix188 = '1';\n        }\n    }\n    function capture(ev){\n        var t = ev.target;\n        if (!t || !t.closest) return;\n        var btn = t.closest('#ti-purchase-confirm-btn,button');\n        if (!btn) return;\n        var label = String(btn.textContent || '').toLowerCase();\n        if (btn.id === 'ti-purchase-confirm-btn' || label.indexOf('conferma righe acquisizione') !== -1 || label.indexOf('conferma righe acquisto') !== -1) confirmRows188(ev);\n    }\n    window.addEventListener('click', capture, true);\n    window.addEventListener('touchend', capture, {passive:false, capture:true});\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ bind(); setTimeout(bind,250); setTimeout(bind,1000); });\n    else { bind(); setTimeout(bind,250); setTimeout(bind,1000); }\n    try { new MutationObserver(bind).observe(document.documentElement, {childList:true, subtree:true}); } catch(ignore) {}\n})();\n<\/script>\n\n\n<script id=\"ti-help-company-main-193\">\n(function(){\n    if (window.__tiHelpCompanyMain193) return;\n    window.__tiHelpCompanyMain193 = true;\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){\n        if (window.tiCompanyHelpEscape) return window.tiCompanyHelpEscape(v);\n        return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; });\n    }\n    function norm(v){\n        return String(v == null ? '' : v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').replace(\/[_\\-]+\/g,' ').replace(\/\\s+\/g,' ').trim();\n    }\n    function getTables(data){ return data && data.Tabelle && typeof data.Tabelle === 'object' ? data.Tabelle : {}; }\n    function getRows(data, names){\n        var tables = getTables(data);\n        var keys = Object.keys(tables || {});\n        for (var i=0; i<names.length; i++) {\n            var wanted = norm(names[i]);\n            var key = keys.find(function(k){ return norm(k) === wanted; });\n            if (key) return Array.isArray(tables[key]) ? tables[key] : Object.values(tables[key] || {});\n        }\n        return [];\n    }\n    function pick(row, names){\n        if (window.tiPickCompanyVal) return window.tiPickCompanyVal(row || {}, names || []);\n        row = row || {};\n        var keys = Object.keys(row);\n        for (var i=0; i<names.length; i++) {\n            var nm = names[i];\n            if (Object.prototype.hasOwnProperty.call(row, nm) && String(row[nm] || '').trim() !== '') return String(row[nm]).trim();\n            var wanted = norm(nm);\n            var found = keys.find(function(k){ return norm(k) === wanted; });\n            if (found && String(row[found] || '').trim() !== '') return String(row[found]).trim();\n        }\n        return '';\n    }\n    function currentDb(){ return (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : ''; }\n    function firstUrl(txt){\n        var m = String(txt || '').match(\/https?:\\\/\\\/[^\\s<>'\")]+\/i);\n        return m ? m[0].replace(\/[\\],.;]+$\/,'') : '';\n    }\n    function encodePath(path){ return String(path || '').split('\/').map(function(x){ return encodeURIComponent(x); }).join('\/'); }\n    function basenamePath(v){\n        var s = String(v || '').trim().replace(\/\\\\\/g,'\/');\n        s = s.split(\/[?#]\/)[0];\n        var p = s.split('\/');\n        return (p[p.length - 1] || '').trim();\n    }\n    function slugRecordLabel(row){\n        row = row || {};\n        var names = ['Descrizione','Titolo','Nome','Informazione','Documento','Doc','URL','Note'];\n        var keys = Object.keys(row || {});\n        var val = '';\n        for (var i=0; i<names.length && !val; i++) {\n            var wanted = norm(names[i]);\n            var hit = keys.find(function(k){ return norm(k) === wanted; });\n            if (hit && String(row[hit] || '').trim()) val = String(row[hit]).trim();\n        }\n        val = val || 'informazioni';\n        return val.toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').replace(\/\\.[a-z0-9]{2,5}$\/i,'').replace(\/[^a-z0-9]+\/g,'-').replace(\/^-+|-+$\/g,'') || 'informazioni';\n    }\n    function docFileNameFromText(txt){\n        var s = String(txt || '');\n        try { s = decodeURIComponent(s); } catch(e) {}\n        var m = s.match(\/([A-Za-z0-9][A-Za-z0-9._ -]*\\.(?:pdf|docx?|xlsx?|txt|csv|html?|jpg|jpeg|png|webp))\/i);\n        if (m) return basenamePath(m[1]);\n        return '';\n    }\n    function isSharedDocsUrl(v){\n        var s = String(v || '');\n        try { s = decodeURIComponent(s); } catch(e) {}\n        return \/AI-data\\\/AI-docs\\\/Shop-service\/i.test(s) || \/AI-docs\\\/Shop-service\/i.test(s);\n    }\n    function sharedDocsManualUrl(){\n        var setup = window.tiSharedSetup || {};\n        var u = String(setup.manual_url || '').trim();\n        if (u) return u;\n        var base = String(setup.docs_base_url || 'https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service').replace(\/\\\/+$\/, '');\n        return base + '\/Shop-Service%20Manual.pdf';\n    }\n    function buildLocalDocRoute(fileName, row){\n        var file = basenamePath(fileName);\n        if (!file) return '';\n        var db = currentDb();\n        if (!db || db === 'NEW_DB') return '';\n        var base = String(window.tiUrl || window.location.href).split('#')[0];\n        var sep = base.indexOf('?') >= 0 ? '&' : '?';\n        return base + sep + 'ti_action=ti_ai_get_image&db=' + encodeURIComponent(db.replace(\/\\.json$\/i,'')) + '&tbl=Informazioni&file=' + encodeURIComponent(file) + '&record=' + encodeURIComponent(slugRecordLabel(row));\n    }\n    function docToUrl(val, row){\n        var v = String(val || '').trim();\n        if (!v) return '';\n        if (isSharedDocsUrl(v)) return v;\n        var direct = firstUrl(v);\n        if (direct) return direct;\n        var file = docFileNameFromText(v) || (\/\\.(pdf|docx?|xlsx?|txt|csv|html?|jpg|jpeg|png|webp)(\\?.*)?$\/i.test(v) ? basenamePath(v) : '');\n        if (file) return buildLocalDocRoute(file, row) || '';\n        if (\/^\\\/[^\/]\/.test(v)) return v;\n        if (\/^(wp-content\\\/uploads\\\/|uploads\\\/|AI-data\\\/)\/i.test(v)) {\n            var f = basenamePath(v);\n            return f ? buildLocalDocRoute(f, row) : ('\/' + v.replace(\/^\\\/+\/, ''));\n        }\n        return '';\n    }\n    function infoLinksHtml(data){\n        var rows = getRows(data, ['Informazioni','Informazioni ditta']);\n        var wanted = [\n            {key:'commerciali', label:'Condizioni commerciali', re:\/commercial|vendita|pagament|offert|listin|prezz|recess|reso|spedizion|fattur|ordine\/i},\n            {key:'contrattuali', label:'Condizioni contrattuali', re:\/contratt|termini|accord|fornitura|garanzi|clausol|condizion\/i},\n            {key:'privacy', label:'Privacy', re:\/privacy|gdpr|dati personal|informativa|cookie|trattamento dati\/i}\n        ];\n        var found = {};\n        rows.forEach(function(row){\n            if (!row || typeof row !== 'object') return;\n            var flat = Object.keys(row).map(function(k){ return k + ' ' + String(row[k] == null ? '' : row[k]); }).join(' ');\n            var tipo = String(pick(row, ['Tipo','type']) || '').toUpperCase();\n            var ref = pick(row, ['URL','Url','Link','Doc','Docs','File','Files','PDF','Pdf','Documento','Documenti','Allegato','Allegati','File associato','File associati','Nome file','Path','Percorso','Descrizione','Istruzioni','Note']);\n            var url = docToUrl(ref, row) || docToUrl(flat, row);\n            wanted.forEach(function(w){\n                if (found[w.key]) return;\n                if (w.re.test(flat) && (\/^(DOC|URL|WEB|RIC)?$\/i.test(tipo) || !tipo)) {\n                    if (url) found[w.key] = '<a href=\"' + esc(url) + '\" target=\"_blank\" rel=\"noopener\" style=\"color:#93c5fd;text-decoration:underline;\">' + esc(w.label) + '<\/a>';\n                    else found[w.key] = '<span style=\"color:#cbd5e1;\">' + esc(w.label) + '<\/span>';\n                }\n            });\n        });\n        var links = wanted.map(function(w){ return found[w.key]; }).filter(Boolean);\n        return links.length ? '<div style=\"margin-top:4px;\">' + links.join(' &nbsp;|&nbsp; ') + '<\/div>' : '';\n    }\n    function render(data){\n        var box = byId('ti-help-company');\n        if (!box) return;\n        data = data || window.currentDbData || window.currentDbViewData || null;\n        var cfgRows = getRows(data, ['Config','Configurazione']);\n        var cfg = cfgRows && cfgRows[0] && typeof cfgRows[0] === 'object' ? cfgRows[0] : {};\n        var lines = [];\n        var rag = pick(cfg, ['Ragione sociale','Ragione Sociale','Ditta','Nome ditta','Azienda']);\n        var indirizzo = pick(cfg, ['Indirizzo','Sede','Indirizzo ditta','Indirizzo sede']);\n        var piva = pick(cfg, ['PIVA_VAT','PIVA VAT','Partita IVA','Partita iva','P. IVA','PIva','PIVA','VAT']);\n        var email = pick(cfg, ['Email principale','Email ditta','Email','E-mail','Mail']);\n        var tel = pick(cfg, ['Tel','Telefono','Telefono ditta','Cellulare','Phone']);\n        if (rag) lines.push('<div><b>' + esc(rag) + '<\/b><\/div>');\n        if (indirizzo) lines.push('<div>' + esc(indirizzo) + '<\/div>');\n        if (piva) lines.push('<div>Partita IVA: ' + esc(piva) + '<\/div>');\n        if (email) lines.push('<div>Email: <a href=\"mailto:' + esc(email) + '\" style=\"color:#93c5fd;\">' + esc(email) + '<\/a><\/div>');\n        if (tel) lines.push('<div>Telefono: <a href=\"tel:' + esc(tel) + '\" style=\"color:#93c5fd;\">' + esc(tel) + '<\/a><\/div>');\n        var links = infoLinksHtml(data);\n        if (links) lines.push(links);\n        box.innerHTML = lines.join('');\n        box.style.display = lines.length ? 'block' : 'none';\n        try { rewriteLocalDocAnchors(document.getElementById('ti-help-ov') || document); } catch(e) {}\n    }\n    function loadDataAndRender(){\n        render(window.currentDbData || window.currentDbViewData || null);\n        var db = currentDb();\n        if (!db || db === 'NEW_DB') return Promise.resolve(null);\n        if (window.currentDbData && window.currentDbData.Tabelle) return Promise.resolve(window.currentDbData);\n        if (!window.fetchJsonSafe) return Promise.resolve(null);\n        var fd = new FormData();\n        fd.append('ti_action','ti_ai_get_db_data');\n        fd.append('db', db);\n        return window.fetchJsonSafe((window.tiAjaxUrl || window.tiUrl || window.location.href), {method:'POST', body:fd, credentials:'same-origin'}).then(function(res){\n            if (res && res.success && res.data) { window.currentDbData = res.data; render(res.data); }\n            return res && res.data ? res.data : null;\n        }).catch(function(){ return null; });\n    }\n    function rewriteLocalDocAnchors(root){\n        root = root || document;\n        if (!root.querySelectorAll) return;\n        root.querySelectorAll('a[href]').forEach(function(a){\n            var raw = a.getAttribute('href') || '';\n            if (a.id === 'btn-manual') { a.setAttribute('href', sharedDocsManualUrl()); return; }\n            if (!raw || isSharedDocsUrl(raw) || \/ti_action=ti_ai_get_image\/i.test(raw) || \/^mailto:|^tel:|^javascript:\/i.test(raw)) return;\n            var file = docFileNameFromText(raw);\n            if (!file) return;\n            var abs = a.href || '';\n            var isBare = raw === file || raw.indexOf('\/') === -1;\n            var isSamePageRelative = abs && abs.indexOf(window.location.origin) === 0 && abs.indexOf('\/wp-content\/uploads\/') === -1;\n            if (isBare || isSamePageRelative || \/AI-data|wp-content\\\/uploads\/i.test(raw)) {\n                var fixed = buildLocalDocRoute(file, null);\n                if (fixed) a.setAttribute('href', fixed);\n            }\n        });\n    }\n    window.tiUpdateMainHelpCompanyHeader = loadDataAndRender;\n    var prevOpen = window.openHelpModal;\n    window.openHelpModal = function(){\n        try { loadDataAndRender(); } catch(e) {}\n        if (typeof prevOpen === 'function') return prevOpen.apply(this, arguments);\n        if (window.openModal) window.openModal('ti-help-ov');\n    };\n    document.addEventListener('change', function(ev){ if (ev.target && ev.target.id === 'ti-ditta') setTimeout(loadDataAndRender, 80); }, true);\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ render(); setTimeout(loadDataAndRender, 300); try { rewriteLocalDocAnchors(document); } catch(e) {} });\n    else { render(); setTimeout(loadDataAndRender, 300); try { rewriteLocalDocAnchors(document); } catch(e) {} }\n    try { new MutationObserver(function(muts){ muts.forEach(function(m){ (m.addedNodes || []).forEach(function(n){ if (n && n.nodeType === 1) rewriteLocalDocAnchors(n); }); }); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n})();\n<\/script>\n\n\n\n<script id=\"ti-activity-qrcode-direct-299\">\n(function(){\n    \"use strict\";\n    if (window.__tiActivityQrDirect299) return;\n    window.__tiActivityQrDirect299 = true;\n    function tryShow(){ try { if (window.showActivityFromQrIfRequested) window.showActivityFromQrIfRequested(); } catch(e) {} }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(tryShow, 600); setTimeout(tryShow, 1600); });\n    else { setTimeout(tryShow, 600); setTimeout(tryShow, 1600); }\n    document.addEventListener('change', function(ev){ if (ev && ev.target && ev.target.id === 'ti-ditta') setTimeout(tryShow, 700); }, true);\n})();\n<\/script>\n\n\n<script id=\"ti-config-open-rescue-218\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigOpenRescue218) return;\n    window.__tiConfigOpenRescue218 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){\n        return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){\n            return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#039;'}[c];\n        });\n    }\n    function selectedDb(){\n        var sel = byId('ti-ditta');\n        return sel ? String(sel.value || '').trim() : '';\n    }\n    function currentSessionDb(){\n        var db = '';\n        try { db = String(window.tiSessionDb || window.tiForced || window.tiCurrentSelectedDb || '').trim(); } catch(e) {}\n        if (!db) db = selectedDb();\n        return db;\n    }\n    function markCurrentDbData(db){\n        try {\n            db = String(db || selectedDb() || '').trim();\n            if (db) {\n                window.__tiCurrentDbDataDb = db;\n                window.__tiConfigLoadedDb = db;\n                if (window.currentDbData && typeof window.currentDbData === 'object' && !window.currentDbData.__db_name) window.currentDbData.__db_name = db;\n            }\n        } catch(e) {}\n    }\n    function cachedConfigDataFor(db){\n        try {\n            db = String(db || '').trim();\n            var data = window.currentDbData || null;\n            if (!db || !data || !data.Tabelle) return null;\n            var known = String(data.__db_name || window.__tiCurrentDbDataDb || window.__tiConfigLoadedDb || '').trim();\n            if (known && known !== db) return null;\n            if (!known) {\n                var sel = selectedDb();\n                var sessDb = currentSessionDb();\n                \/* 30.9.664: se la ditta selezionata coincide, usa i dati gia presenti.\n                   In molte sessioni gia loggate __db_name non era valorizzato e la Configurazione\n                   rileggeva inutilmente tutto il DB, restando su \"Caricamento configurazione\". *\/\n                if (sel && sel === db) {\n                    try { data.__db_name = db; window.__tiCurrentDbDataDb = db; window.__tiConfigLoadedDb = db; } catch(e) {}\n                    return data;\n                }\n                if (!sessDb || sessDb !== db) return null;\n            }\n            return data;\n        } catch(e) { return null; }\n    }\n    function prepareConfigStateFromData(data, db){\n        window.currentDbData = data;\n        markCurrentDbData(db);\n        try { window.configOriginalDbData = JSON.parse(JSON.stringify(data || {})); } catch(e) { window.configOriginalDbData = data || {}; }\n        window.configWasSaved = false;\n        window.tiConfigDeletedRowsManifest = [];\n        if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear();\n        try { if (window.refreshShopServiceActivationButton) window.refreshShopServiceActivationButton(); } catch(e) {}\n    }\n    function renderConfigNow(db, sourceLabel){\n        if (typeof window.renderConfig !== 'function') throw new Error('Funzione renderConfig non disponibile.');\n        window.__tiConfigOpeningFast459 = true;\n        window.renderConfig();\n        window.__tiConfigOpeningFast459 = false;\n        openConfigOverlay();\n        try { if (typeof window.updateConfigSaveButtonState === 'function') window.updateConfigSaveButtonState(); } catch(e) {}\n        try { if (sourceLabel && window.console) console.info('Shop & Service configurazione aperta: ' + sourceLabel + ' - ' + db); } catch(e) {}\n    }\n    function showConfigSoftNotice(html, type){\n        try {\n            var cnt = byId('ti-conf-cnt');\n            if (!cnt) return;\n            var old = byId('ti-config-soft-notice-459');\n            if (old && old.parentNode) old.parentNode.removeChild(old);\n            var div = document.createElement('div');\n            div.id = 'ti-config-soft-notice-459';\n            var border = type === 'error' ? '#ef4444' : '#f59e0b';\n            var bg = type === 'error' ? '#450a0a' : '#422006';\n            var color = type === 'error' ? '#fecaca' : '#fde68a';\n            div.setAttribute('style','position:sticky;top:0;z-index:5;margin:8px 8px 12px 8px;padding:10px 12px;border-radius:10px;border:1px solid '+border+';background:'+bg+';color:'+color+';font-size:12px;line-height:1.35;');\n            div.innerHTML = html;\n            cnt.insertBefore(div, cnt.firstChild || null);\n        } catch(e) {}\n    }\n    function openConfigOverlay(){\n        var ov = byId('ti-conf-ov');\n        try {\n            if (window.showPluginModal) window.showPluginModal(ov || 'ti-conf-ov');\n            else if (window.openModal) window.openModal('ti-conf-ov');\n            else if (ov) { ov.style.display = 'flex'; ov.style.position = 'fixed'; ov.style.inset = '0'; ov.style.zIndex = '2147483000'; }\n        } catch(e) {\n            if (ov) { ov.style.display = 'flex'; ov.style.position = 'fixed'; ov.style.inset = '0'; ov.style.zIndex = '2147483000'; }\n        }\n    }\n    function setConfigMessage(html, type){\n        var cnt = byId('ti-conf-cnt');\n        if (!cnt) return;\n        var border = type === 'error' ? '#ef4444' : '#22c55e';\n        var bg = type === 'error' ? '#450a0a' : '#052e16';\n        var color = type === 'error' ? '#fecaca' : '#bbf7d0';\n        cnt.innerHTML = '<div style=\"margin:14px;padding:14px;border-radius:12px;border:1px solid ' + border + ';background:' + bg + ';color:' + color + ';font-size:14px;line-height:1.45;\">' + html + '<\/div>';\n    }\n    function buildFd(action, extra){\n        var fd;\n        if (typeof window.buildAjaxFormData === 'function') fd = window.buildAjaxFormData(action, extra || {});\n        else {\n            fd = new FormData();\n            fd.append('action', action);\n            fd.append('ti_action', action);\n            Object.keys(extra || {}).forEach(function(k){ if (extra[k] !== undefined && extra[k] !== null) fd.append(k, extra[k]); });\n        }\n        if (fd instanceof FormData) {\n            if (!fd.has('action')) fd.append('action', action);\n            if (!fd.has('ti_action')) fd.append('ti_action', action);\n        }\n        return fd;\n    }\n    function postJson(fd, db, cfgOpts){\n        cfgOpts = cfgOpts || {};\n        var url = window.tiAjaxUrl || window.ajaxurl || window.tiUrl || window.location.href;\n        var form = (window.ensureAjaxActionField ? window.ensureAjaxActionField(fd) : fd);\n        var timeoutMs = cfgOpts.timeoutMs || 15000;\n        var controller = null;\n        var timer = null;\n        var opts = { method:'POST', body:form, credentials:'same-origin', cache:'no-store' };\n        try {\n            if (window.AbortController) {\n                controller = new AbortController();\n                opts.signal = controller.signal;\n            }\n        } catch(e) {}\n        return new Promise(function(resolve, reject){\n            timer = setTimeout(function(){\n                try { if (controller) controller.abort(); } catch(e) {}\n                reject(new Error('Lettura database non completata entro ' + Math.round(timeoutMs\/1000) + ' secondi. Riprova oppure ricarica la pagina.'));\n            }, timeoutMs);\n            fetch(url, opts).then(function(r){\n                return r.text().then(function(t){\n                    if(!r.ok) throw new Error('HTTP ' + r.status + ': ' + String(t || '').slice(0,180));\n                    try { return JSON.parse(t); }\n                    catch(e) { throw new Error('Risposta server non valida: ' + String(t || '').slice(0,180)); }\n                });\n            }).then(function(json){ clearTimeout(timer); resolve(json); }).catch(function(err){ clearTimeout(timer); reject(err); });\n        });\n    }\n    function withConfigTimeout(promise, ms, label){\n        var done = false;\n        return new Promise(function(resolve, reject){\n            var timer = setTimeout(function(){\n                if (done) return;\n                done = true;\n                reject(new Error((label || 'Caricamento configurazione') + ' non completato entro ' + Math.round(ms\/1000) + ' secondi. Il server potrebbe essere occupato o la risposta troppo lenta.'));\n            }, ms);\n            Promise.resolve(promise).then(function(v){\n                if (done) return;\n                done = true; clearTimeout(timer); resolve(v);\n            }).catch(function(e){\n                if (done) return;\n                done = true; clearTimeout(timer); reject(e);\n            });\n        });\n    }\n\n    window.openConfigSafe218 = function(ev){\n        if (ev && ev.preventDefault) ev.preventDefault();\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') {\n            var msg = 'Seleziona prima una ditta valida per aprire la configurazione.';\n            if (typeof window.tiAlert === 'function') window.tiAlert(msg); else alert(msg);\n            return Promise.resolve(false);\n        }\n        if (window.__tiConfigOpeningPromise459 && window.__tiConfigOpeningDb459 === db) return window.__tiConfigOpeningPromise459;\n        openConfigOverlay();\n        var cached = cachedConfigDataFor(db);\n        var usedCache = false;\n        var cachedVersion = cached && cached.__db_version ? String(cached.__db_version) : '';\n        if (cached) {\n            try {\n                usedCache = true;\n                prepareConfigStateFromData(cached, db);\n                renderConfigNow(db, 'dati gia in memoria');\n                showConfigSoftNotice('<b>Configurazione aperta subito.<\/b> Verifico in background se il database e stato modificato.', 'warn');\n            } catch(cacheErr) {\n                usedCache = false;\n                setConfigMessage('<b>Caricamento configurazione in corso...<\/b><br><span style=\"font-size:12px;\">I dati in memoria non sono utilizzabili, rileggo il database.<\/span>', 'ok');\n            }\n        } else {\n            setConfigMessage('<b>Caricamento configurazione in corso...<\/b><br><span style=\"font-size:12px;\">Lettura database in corso. Se il server non risponde, comparira automaticamente il pulsante Riprova.<\/span>', 'ok');\n        }\n        var slowTimer = setTimeout(function(){\n            try {\n                if (usedCache) showConfigSoftNotice('<b>Verifica database ancora in corso.<\/b> Puoi continuare a consultare la configurazione; se il server conferma modifiche, la form viene aggiornata automaticamente.', 'warn');\n                else setConfigMessage('<b>Caricamento configurazione ancora in corso...<\/b><br>Il server sta impiegando piu del previsto. <button type=\"button\" class=\"ti-btn\" style=\"margin-left:8px;background:#2563eb;color:#fff;\" onclick=\"window.__tiConfigOpeningPromise459=null;window.openConfigSafe218(event);return false;\">Riprova<\/button> <button type=\"button\" class=\"ti-btn\" style=\"margin-left:8px;background:#4b5563;color:#fff;\" onclick=\"window.__tiConfigOpeningPromise459=null;if(window.closeModal)window.closeModal(&quot;ti-conf-ov&quot;);return false;\">Chiudi<\/button>', 'ok');\n            } catch(e) {}\n        }, 5000);\n        var fd = buildFd('ti_ai_get_db_data', {db: db, ti_config_open: '1'});\n        if (usedCache) {\n            \/* 30.9.664: non considerare il refresh in background come apertura bloccante. *\/\n            window.__tiConfigOpeningPromise459 = null;\n            window.__tiConfigOpeningDb459 = '';\n        }\n        var promise = withConfigTimeout(postJson(fd, db, {silentWait: true, timeoutMs: usedCache ? 9000 : 12000}), usedCache ? 10000 : 13000, 'Caricamento configurazione').then(function(res){\n            clearTimeout(slowTimer);\n            window.__tiConfigOpeningPromise459 = null;\n            if (!res || !res.success || !res.data) {\n                var serverMsg = res && res.data && res.data.message ? res.data.message : (res && res.message ? res.message : 'lettura database non riuscita');\n                throw new Error(serverMsg);\n            }\n            var freshVersion = res.data && res.data.__db_version ? String(res.data.__db_version) : '';\n            prepareConfigStateFromData(res.data, db);\n            if (usedCache && cachedVersion && freshVersion && cachedVersion === freshVersion) {\n                showConfigSoftNotice('<b>Database verificato.<\/b> La configurazione era gia aggiornata.', 'warn');\n                setTimeout(function(){ var n = byId('ti-config-soft-notice-459'); if (n && n.parentNode) n.parentNode.removeChild(n); }, 2500);\n                return true;\n            }\n            try {\n                renderConfigNow(db, usedCache ? 'refresh database completato' : 'lettura database completata');\n            } catch(renderErr) {\n                setConfigMessage('<b>Errore apertura form configurazione.<\/b><br>' + esc(renderErr && renderErr.message ? renderErr.message : renderErr), 'error');\n                throw renderErr;\n            }\n            setTimeout(function(){\n                var cnt = byId('ti-conf-cnt');\n                if (cnt && !String(cnt.innerHTML || '').trim()) {\n                    setConfigMessage('<b>Form configurazione aperto, ma nessuna tabella renderizzata.<\/b><br>Verifica che il database contenga la sezione Tabelle.', 'error');\n                }\n                if (typeof window.updateConfigSaveButtonState === 'function') window.updateConfigSaveButtonState();\n            }, 80);\n            return true;\n        }).catch(function(err){\n            clearTimeout(slowTimer);\n            window.__tiConfigOpeningPromise459 = null;\n            openConfigOverlay();\n            var m = err && err.message ? err.message : String(err || 'errore sconosciuto');\n            if (usedCache) {\n                showConfigSoftNotice('<b>Verifica database non completata.<\/b> La configurazione aperta e quella gia disponibile in memoria.<br>' + esc(m), 'error');\n                try { console.error('Shop & Service CONFIGURA background refresh error', err); } catch(e) {}\n                return true;\n            }\n            setConfigMessage('<b>Configurazione non caricata.<\/b><br>' + esc(m) + '<br><br><button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\" onclick=\"window.__tiConfigOpeningPromise459=null;window.openConfigSafe218(event);return false;\">Riprova<\/button><button type=\"button\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\" onclick=\"window.closeModal&&window.closeModal(\\'ti-conf-ov\\');return false;\">Chiudi<\/button><br><br>La finestra resta aperta per mostrare il problema invece di chiudersi senza messaggi.', 'error');\n            try { console.error('Shop & Service CONFIGURA open error', err); } catch(e) {}\n            return false;\n        });\n        window.__tiConfigOpeningDb459 = db;\n        window.__tiConfigOpeningPromise459 = promise;\n        return promise;\n    };\n\n    function bindConfigButton(){\n        var btn = byId('ti-conf-toggle');\n        if (!btn) return;\n        btn.onclick = window.openConfigSafe218;\n        btn.setAttribute('data-ti-config-open-rescue', '218');\n        btn.style.display = 'inline-block';\n    }\n\n    document.addEventListener('click', function(ev){\n        var target = ev.target;\n        if (!target || !target.closest) return;\n        var btn = target.closest('#ti-conf-toggle');\n        if (!btn) return;\n        ev.preventDefault();\n        if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n        else ev.stopPropagation();\n        window.openConfigSafe218(ev);\n    }, true);\n\n    var prevSetAdminUI = window.setAdminUI;\n    if (typeof prevSetAdminUI === 'function' && !prevSetAdminUI.__tiConfigOpenRescue218) {\n        window.setAdminUI = function(){\n            var ret = prevSetAdminUI.apply(this, arguments);\n            try { bindConfigButton(); } catch(e) {}\n            return ret;\n        };\n        window.setAdminUI.__tiConfigOpenRescue218 = true;\n    }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bindConfigButton);\n    else bindConfigButton();\n    var tries = 0;\n    var timer = setInterval(function(){\n        tries += 1;\n        bindConfigButton();\n        if (tries > 20 || byId('ti-conf-toggle')) clearInterval(timer);\n    }, 500);\n})();\n<\/script>\n\n\n\n\n\n<script id=\"ti-demo-readonly-695\">\n(function(){\n    \"use strict\";\n    if (window.__tiDemoReadonly695) return;\n    window.__tiDemoReadonly695 = true;\n    function byId(id){ return document.getElementById(id); }\n    function esc(s){ return String(s == null ? '' : s).replace(\/[&<>\"']\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c];}); }\n    function alertHtml(html){ try { if (window.tiAlert) window.tiAlert(html); else window.alert(String(html || '').replace(\/<[^>]+>\/g,' ')); } catch(e) {} }\n    window.tiIsDemoReadonly = function(){ return !!window.tiDemoReadonly || String(window.tiUser || '').trim().toLowerCase() === 'ssdemo'; };\n    window.tiShowDemoCompanyAccessNotice695 = function(optionEl){\n        try {\n            if (!optionEl) return;\n            var isDemo = String((optionEl.dataset && (optionEl.dataset.status || optionEl.dataset.companyState || optionEl.dataset.stato)) || '').toLowerCase().indexOf('demo') !== -1 || String(optionEl.getAttribute('data-demo-login-available') || '') === '1';\n            if (!isDemo) return;\n            if (String(window.tiUser || '').trim().toLowerCase() === 'ssdemo') return;\n            var db = String(optionEl.value || '');\n            var key = 'ti_demo_login_notice_695_' + db;\n            if (sessionStorage.getItem(key) === '1') return;\n            sessionStorage.setItem(key, '1');\n            var msg = String(optionEl.getAttribute('data-demo-login-notice') || 'Ditta Demo: puoi accedere con username ssdemo e password ssdemo per visualizzare la configurazione in sola lettura.');\n            alertHtml('<b>Ditta Demo<\/b><br><br>' + esc(msg) + '<br><br><b>Username:<\/b> ssdemo<br><b>Password:<\/b> ssdemo');\n        } catch(e) {}\n    };\n    window.tiShowSsdemoAccessPopup695 = function(){\n        try {\n            if (!window.tiIsDemoReadonly || !window.tiIsDemoReadonly()) return;\n            if (window.__tiSsdemoPopupShown695) return;\n            window.__tiSsdemoPopupShown695 = true;\n            alertHtml('<b>Accesso Demo sola lettura<\/b><br><br>Sei entrato come <b>ssdemo<\/b>.<br>Puoi visualizzare la configurazione della ditta Demo e usare la chat AI come utente base. Non puoi modificare, aggiungere, cancellare o salvare dati.');\n        } catch(e) {}\n    };\n    window.tiApplyDemoReadonlyConfig695 = function(root){\n        try {\n            if (!window.tiIsDemoReadonly || !window.tiIsDemoReadonly()) return;\n            root = root || document;\n            var conf = byId('ti-conf-cnt');\n            var scope = conf && conf.contains(root) ? conf : (root === document ? conf || document : root);\n            if (!scope) return;\n            if (conf && !byId('ti-demo-readonly-banner-695')) {\n                var b = document.createElement('div');\n                b.id = 'ti-demo-readonly-banner-695';\n                b.style.cssText = 'margin:8px 0 12px 0;padding:10px 12px;border-radius:10px;background:#082f49;border:1px solid #38bdf8;color:#e0f2fe;font-size:13px;font-weight:700;line-height:1.35;';\n                b.innerHTML = 'Modalit\u00e0 Demo sola lettura: puoi consultare i dati, ma non puoi modificare, aggiungere, cancellare o salvare.';\n                conf.insertBefore(b, conf.firstChild);\n            }\n            var editable = scope.querySelectorAll ? scope.querySelectorAll('input, textarea, select') : [];\n            Array.prototype.forEach.call(editable, function(el){\n                if (!el || el.id === 'ti-cfg-tips-enabled') return;\n                if (el.type === 'hidden') return;\n                if (el.type === 'checkbox' || el.type === 'radio' || el.tagName === 'SELECT') el.disabled = true;\n                else el.readOnly = true;\n                el.setAttribute('data-ti-demo-readonly','1');\n                el.style.setProperty('opacity','0.85','important');\n            });\n            function isMutatingDemoButton(btn){\n                var txt = String((btn && (btn.textContent || btn.value)) || '').replace(\/\\s+\/g,' ').trim().toLowerCase();\n                var inline = String((btn && btn.getAttribute && btn.getAttribute('onclick')) || '').toLowerCase();\n                if (txt === 'x' || txt.indexOf('chiudi') !== -1 || txt.indexOf('esci') !== -1 || txt.indexOf('?') !== -1 || txt.indexOf('info') !== -1 || txt.indexOf('copia') !== -1) return false;\n                if (inline.indexOf('close') !== -1 || inline.indexOf('openconfigtableforwarning') !== -1 || inline.indexOf('showconfiguratoroperationaltipspopup') !== -1) return false;\n                if (\/^(config|prodotti|servizi|utenti|informazioni|istruzioni|operatori|siti|dispositivi|appuntamenti|attivita|attivit\u00e0|apri tabella|elenco dati|<<|>>|pag\\s*\\d*)$\/i.test(txt)) return false;\n                var mut = \/(salva|salvataggio|aggiung|nuov|crea|elimina|cancella|rimuov|pulisci|import|carica|upload|genera|applica|modifica|sposta|ordina|associa|dissocia|backup|ripristina|attiva|disattiva|verifica istruzioni|acquisizion|globale|storage|password|email|sms)\/i;\n                return mut.test(txt) || mut.test(inline);\n            }\n            window.tiDemoReadonlyIsMutatingButton695 = isMutatingDemoButton;\n            var buttons = scope.querySelectorAll ? scope.querySelectorAll('button, [role=\"button\"]') : [];\n            Array.prototype.forEach.call(buttons, function(btn){\n                if (isMutatingDemoButton(btn)) {\n                    btn.disabled = true;\n                    btn.setAttribute('data-ti-demo-readonly','1');\n                    btn.style.setProperty('opacity','0.45','important');\n                    btn.title = 'Disabilitato in accesso Demo sola lettura';\n                }\n            });\n            var save = byId('ti-save-config');\n            if (save) { save.disabled = true; save.textContent = 'Sola lettura Demo'; save.title = 'Accesso ssdemo: salvataggio disabilitato'; }\n        } catch(e) {}\n    };\n    document.addEventListener('change', function(ev){\n        try { if (ev && ev.target && ev.target.id === 'ti-ditta') window.tiShowDemoCompanyAccessNotice695(ev.target.options[ev.target.selectedIndex]); } catch(e) {}\n    }, true);\n    document.addEventListener('click', function(ev){\n        try {\n            var t = ev.target;\n            if (!t || !t.closest) return;\n            if ((window.tiIsDemoReadonly && window.tiIsDemoReadonly()) && t.closest('#ti-conf-cnt button:not([data-ti-demo-readonly-allow])')) {\n                var b = t.closest('button');\n                if (!b) return;\n                if (b.disabled) return;\n                var mut = window.tiDemoReadonlyIsMutatingButton695 ? window.tiDemoReadonlyIsMutatingButton695(b) : false;\n                if (mut) {\n                    ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                    alertHtml('<b>Accesso Demo sola lettura<\/b><br><br>Con l utente <b>ssdemo<\/b> puoi solo visualizzare i dati. Modifiche, aggiunte, cancellazioni e salvataggi sono disabilitati.');\n                    return false;\n                }\n            }\n        } catch(e) {}\n    }, true);\n    if (typeof window.renderConfig === 'function' && !window.renderConfig.__tiDemoReadonly695) {\n        var prevRenderConfig695 = window.renderConfig;\n        window.renderConfig = function(){\n            var ret = prevRenderConfig695.apply(this, arguments);\n            setTimeout(function(){ window.tiApplyDemoReadonlyConfig695(document); }, 0);\n            return ret;\n        };\n        window.renderConfig.__tiDemoReadonly695 = true;\n    }\n    if (typeof window.updCtx === 'function' && !window.updCtx.__tiDemoNotice695) {\n        var prevUpdCtx695 = window.updCtx;\n        window.updCtx = function(){\n            var ret = prevUpdCtx695.apply(this, arguments);\n            try { var d = byId('ti-ditta'); if (d && d.value) window.tiShowDemoCompanyAccessNotice695(d.options[d.selectedIndex]); } catch(e) {}\n            return ret;\n        };\n        window.updCtx.__tiDemoNotice695 = true;\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(window.tiShowSsdemoAccessPopup695, 350); setTimeout(function(){ window.tiApplyDemoReadonlyConfig695(document); }, 700); });\n    else { setTimeout(window.tiShowSsdemoAccessPopup695, 350); setTimeout(function(){ window.tiApplyDemoReadonlyConfig695(document); }, 700); }\n    try { new MutationObserver(function(){ if (window.tiIsDemoReadonly && window.tiIsDemoReadonly()) window.tiApplyDemoReadonlyConfig695(document); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n})();\n<\/script>\n\n<script id=\"ti-login-memory-fix-312\">\n(function(){\n    \"use strict\";\n    if (window.__tiLoginMemoryFix312) return;\n    window.__tiLoginMemoryFix312 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function safeGet(storage, key){ try { return storage.getItem(key) || ''; } catch(e) { return ''; } }\n    function safeSet(storage, key, val){ try { storage.setItem(key, String(val || '')); } catch(e) {} }\n    function safeRemove(storage, key){ try { storage.removeItem(key); } catch(e) {} }\n    function hasOption(sel, value){\n        if (!sel || !value) return false;\n        for (var i = 0; i < sel.options.length; i++) if (sel.options[i].value === value) return true;\n        return false;\n    }\n    function isBadCredentialClearActive(){\n        return safeGet(sessionStorage, 'ti_login_failed_clear') === '1';\n    }\n    function clearStableUsername(userEl){\n        try { if (userEl && userEl.dataset) delete userEl.dataset.tiStableUsername; } catch(e) {}\n    }\n\n    window.tiRememberSuccessfulLogin = function(user, db){\n        try {\n            var u = String(user || '').trim();\n            var d = String(db || '').trim();\n            if (u) safeSet(localStorage, 'ti_last_login_user', u);\n            if (d) {\n                safeSet(localStorage, 'ti_last_login_db', d);\n                safeSet(localStorage, 'ti_saved_db', d);\n            }\n            safeRemove(sessionStorage, 'ti_login_failed_clear');\n        } catch(e) {}\n    };\n\n    window.tiClearLoginFieldsAfterFailure = function(clearUserToo){\n        try {\n            safeSet(sessionStorage, 'ti_login_failed_clear', '1');\n            var ids = clearUserToo ? ['l-user','ti-direct-login-user'] : [];\n            ids.forEach(function(id){ var el = byId(id); if (el) { el.value = ''; clearStableUsername(el); } });\n            ['l-pass','ti-direct-login-pass'].forEach(function(id){ var el = byId(id); if (el) el.value = ''; });\n            var target = clearUserToo ? (byId('l-user') || byId('ti-direct-login-user')) : (byId('l-pass') || byId('ti-direct-login-pass'));\n            if (target && typeof target.focus === 'function') {\n                setTimeout(function(){ try { target.focus({preventScroll:true}); } catch(e) { try { target.focus(); } catch(ignore) {} } }, 30);\n            }\n        } catch(e) {}\n    };\n\n    window.tiApplyLastSuccessfulLoginToForm = function(force){\n        try {\n            var userEl = byId('l-user');\n            var passEl = byId('l-pass');\n            var dbEl = byId('l-db');\n            var hiddenDb = byId('l-db-hidden');\n            if (isBadCredentialClearActive() && !force) {\n                if (userEl && userEl.value) { userEl.value = ''; clearStableUsername(userEl); }\n                if (passEl && passEl.value) passEl.value = '';\n                return;\n            }\n            var lastUser = safeGet(localStorage, 'ti_last_login_user');\n            var lastDb = safeGet(localStorage, 'ti_last_login_db') || safeGet(localStorage, 'ti_saved_db');\n            if (userEl && lastUser && (!userEl.value || force)) {\n                userEl.value = lastUser;\n                try { userEl.dataset.tiStableUsername = lastUser; } catch(e) {}\n            }\n            if (dbEl && lastDb && hasOption(dbEl, lastDb) && (!dbEl.value || force)) {\n                dbEl.value = lastDb;\n                if (hiddenDb) hiddenDb.value = lastDb;\n                try { dbEl.dispatchEvent(new Event('change', {bubbles:true})); } catch(e) {}\n            } else if (hiddenDb && lastDb && (!hiddenDb.value || force)) {\n                hiddenDb.value = lastDb;\n            }\n            if (passEl) passEl.value = '';\n        } catch(e) {}\n    };\n\n    function bindManualTypingReset(){\n        try {\n            ['l-user','l-pass','ti-direct-login-user','ti-direct-login-pass'].forEach(function(id){\n                var el = byId(id);\n                if (!el || el.dataset.tiLoginMemoryResetBound312) return;\n                el.dataset.tiLoginMemoryResetBound312 = '1';\n                el.addEventListener('input', function(){ safeRemove(sessionStorage, 'ti_login_failed_clear'); }, false);\n            });\n        } catch(e) {}\n    }\n\n    function patchOpenLogin(){\n        try {\n            if (typeof window.openLoginModalSafe === 'function' && !window.openLoginModalSafe.__tiLoginMemory312) {\n                var prevOpen = window.openLoginModalSafe;\n                window.openLoginModalSafe = function(){\n                    var ret = prevOpen.apply(this, arguments);\n                    setTimeout(function(){ try { window.tiApplyLastSuccessfulLoginToForm(false); bindManualTypingReset(); } catch(e) {} }, 90);\n                    setTimeout(function(){ try { window.tiApplyLastSuccessfulLoginToForm(false); bindManualTypingReset(); } catch(e) {} }, 260);\n                    return ret;\n                };\n                window.openLoginModalSafe.__tiLoginMemory312 = true;\n            }\n            if (typeof window.syncLoginDbSelect === 'function' && !window.syncLoginDbSelect.__tiLoginMemory312) {\n                var prevSync = window.syncLoginDbSelect;\n                window.syncLoginDbSelect = function(){\n                    var ret = prevSync.apply(this, arguments);\n                    try { window.tiApplyLastSuccessfulLoginToForm(false); } catch(e) {}\n                    return ret;\n                };\n                window.syncLoginDbSelect.__tiLoginMemory312 = true;\n            }\n        } catch(e) {}\n    }\n\n    function boot(){\n        try {\n            if (String(window.tiUser || '').trim() && window.tiRememberSuccessfulLogin) {\n                window.tiRememberSuccessfulLogin(window.tiUser, window.tiSessionDb || window.tiForced || '');\n            }\n        } catch(e) {}\n        bindManualTypingReset();\n        patchOpenLogin();\n        try { window.tiApplyLastSuccessfulLoginToForm(false); } catch(e) {}\n    }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ boot(); setTimeout(boot, 300); setTimeout(boot, 1200); });\n    else { boot(); setTimeout(boot, 300); setTimeout(boot, 1200); }\n    try { new MutationObserver(function(){ bindManualTypingReset(); patchOpenLogin(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n})();\n<\/script>\n\n\n<script id=\"ti-plugin-version-access-memory-314\">\n(function(){\n    \"use strict\";\n    if (window.__tiPluginVersionAccessMemory314) return;\n    window.__tiPluginVersionAccessMemory314 = true;\n\n    function safeGet(storage, key){ try { return storage.getItem(key) || ''; } catch(e) { return ''; } }\n    function safeSet(storage, key, val){ try { storage.setItem(key, String(val || '')); } catch(e) {} }\n    function esc(s){\n        return String(s == null ? '' : s).replace(\/[&<>\"']\/g, function(c){\n            return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c] || c;\n        });\n    }\n    function isPrivilegedVersionUser(){\n        var role = String(window.tiRole || '').toLowerCase();\n        var user = String(window.tiUser || '').toLowerCase();\n        return !!String(window.tiUser || '').trim() && (user === 'ssglobaladmin' || role.indexOf('configuratore') >= 0 || role.indexOf('configurator') >= 0 || role.indexOf('amministratore') >= 0 || role.indexOf('administrator') >= 0);\n    }\n    function selectedDbKey(){\n        var db = String(window.tiSessionDb || window.tiForced || window.tiCurrentSelectedDb || '').trim();\n        if (!db) { try { db = String((document.getElementById('ti-ditta') || {}).value || '').trim(); } catch(e) {} }\n        if (!db) { try { db = String(localStorage.getItem('ti_saved_db') || '').trim(); } catch(e) {} }\n        return db || 'no_db';\n    }\n    function storageKey(){\n        var user = String(window.tiUser || 'guest').trim() || 'guest';\n        var db = selectedDbKey();\n        return 'ti_plugin_last_version_access_' + location.host + '_' + db + '_' + user;\n    }\n    function readPrevious(key){\n        var raw = safeGet(localStorage, key);\n        if (!raw) return null;\n        try {\n            var obj = JSON.parse(raw);\n            if (obj && typeof obj === 'object') return obj;\n        } catch(e) {}\n        return {version: raw, datetime: '', ts: ''};\n    }\n    function currentPayload(){\n        return {\n            version: String(window.tiVersion || '').trim(),\n            datetime: String(window.tiVersionDateTime || '').trim(),\n            header: String(window.tiVersion || '').trim() + (window.tiVersionDateTime ? ' - ' + String(window.tiVersionDateTime).trim() : ''),\n            user: String(window.tiUser || '').trim(),\n            role: String(window.tiRole || '').trim(),\n            db: selectedDbKey(),\n            ts: new Date().toISOString()\n        };\n    }\n    window.tiCheckPluginVersionOnAccess = function(){\n        try {\n            if (!String(window.tiUser || '').trim()) return;\n            var key = storageKey();\n            var cur = currentPayload();\n            if (!cur.version) return;\n            var prev = readPrevious(key);\n            var changed = !!(prev && String(prev.version || '').trim() && String(prev.version || '').trim() !== cur.version);\n            safeSet(localStorage, key, JSON.stringify(cur));\n            safeSet(localStorage, 'ti_plugin_last_version_access_global_' + location.host, JSON.stringify(cur));\n            if (!changed || !isPrivilegedVersionUser()) return;\n            var prevHeader = String(prev.header || '').trim() || (String(prev.version || '').trim() + (prev.datetime ? ' - ' + String(prev.datetime).trim() : ''));\n            var html = '<div style=\"text-align:left;line-height:1.45;\">' +\n                '<b>Aggiornamento versione Shop &amp; Service rilevato<\/b><br><br>' +\n                'La versione del plugin e diversa da quella memorizzata al precedente accesso.<br><br>' +\n                '<b>Versione precedente:<\/b><br>' + esc(prevHeader || 'non disponibile') + '<br><br>' +\n                '<b>Versione attuale:<\/b><br>' + esc(cur.header || cur.version) + '<br><br>' +\n                '<span style=\"font-size:12px;color:#ccc;\">La nuova versione e stata ora memorizzata per i prossimi accessi.<\/span>' +\n                '<\/div>';\n            setTimeout(function(){\n                try {\n                    if (typeof window.tiAlert === 'function') window.tiAlert(html);\n                    else alert('Aggiornamento versione Shop & Service rilevato\\nVersione precedente: ' + (prevHeader || 'non disponibile') + '\\nVersione attuale: ' + (cur.header || cur.version));\n                } catch(e) {}\n            }, 900);\n        } catch(e) {}\n    };\n    function boot(){ try { window.tiCheckPluginVersionOnAccess(); } catch(e) {} }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(boot, 250); setTimeout(boot, 1400); });\n    else { setTimeout(boot, 250); setTimeout(boot, 1400); }\n})();\n<\/script>\n\n\n\n<script id=\"ti-plugin-server-version-check-457\">\n(function(){\n    \"use strict\";\n    if (window.__tiPluginServerVersionCheck457) return;\n    window.__tiPluginServerVersionCheck457 = true;\n\n    var pending = false;\n    var lastCheckAt = 0;\n    var lastSourceAt = {};\n    var endpoint = \"https:\/\/www.tisoft.it\/wp-admin\/admin-ajax.php\";\n    var pageVersion = String(window.tiVersion || \"30.9.947\" || '').trim();\n    var pageDateTime = String(window.tiVersionDateTime || \"2026-07-08 09:00 CEST\" || '').trim();\n\n    function txt(v){ return String(v == null ? '' : v).trim(); }\n    function byId(id){ return document.getElementById(id); }\n    function esc(s){ return String(s == null ? '' : s).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c] || c; }); }\n    function fmt(n){\n        n = parseInt(n, 10) || 0;\n        try { return n.toLocaleString('it-IT'); } catch(e) { return String(n); }\n    }\n    function isPrivileged(){\n        var role = txt(window.tiRole).toLowerCase();\n        var user = txt(window.tiUser).toLowerCase();\n        if (!user) return false;\n        return user === 'ssglobaladmin' || role.indexOf('configuratore') >= 0 || role.indexOf('configurator') >= 0 || role.indexOf('amministratore') >= 0 || role.indexOf('administrator') >= 0;\n    }\n    function selectedDb(){\n        var db = txt(window.tiSessionDb || window.tiForced || window.tiCurrentSelectedDb || '');\n        if (!db) { var sel = byId('ti-ditta'); if (sel) db = txt(sel.value); }\n        return db || 'no_db';\n    }\n    function currentHeader(){ return pageVersion + (pageDateTime ? ' - ' + pageDateTime : ''); }\n    function disabled(){ try { return window.localStorage && localStorage.getItem('ti_disable_plugin_version_check') === '1'; } catch(e) { return false; } }\n    function warnedKey(v){ return 'ti_plugin_version_warned_457_' + location.host + '_' + selectedDb() + '_' + txt(window.tiUser || 'user') + '_' + txt(v || ''); }\n    function wasWarned(v){ try { return window.sessionStorage && sessionStorage.getItem(warnedKey(v)) === '1'; } catch(e) { return false; } }\n    function markWarned(v){ try { if (window.sessionStorage) sessionStorage.setItem(warnedKey(v), '1'); } catch(e) {} }\n    function showWarn(data, source){\n        data = data || {};\n        var serverVersion = txt(data.version);\n        if (!serverVersion || serverVersion === pageVersion) return;\n        if (wasWarned(serverVersion)) return;\n        markWarned(serverVersion);\n        var serverHeader = txt(data.header) || (serverVersion + (data.datetime ? ' - ' + txt(data.datetime) : ''));\n        var html = '<div style=\"text-align:left;line-height:1.45;\">' +\n            '<b>\u26a0\ufe0f Aggiornamento plugin rilevato<\/b><br><br>' +\n            'La pagina aperta sta usando una versione precedente di <b>Shop &amp; Service<\/b>, mentre sul server risulta attiva una versione diversa.<br><br>' +\n            '<b>Versione caricata nella pagina:<\/b><br>' + esc(currentHeader() || 'non disponibile') + '<br><br>' +\n            '<b>Versione attiva sul server:<\/b><br>' + esc(serverHeader || serverVersion) + '<br><br>' +\n            'Prima di proseguire in <b>Configurazione<\/b> o nella <b>main form<\/b>, aggiorna la pagina per usare il plugin aggiornato.' +\n            '<br><br><button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;border:0;border-radius:8px;padding:9px 12px;\" onclick=\"location.reload();return false;\">Aggiorna ora<\/button>' +\n            '<button type=\"button\" class=\"ti-btn\" style=\"margin-left:8px;background:#374151;color:#fff;border:0;border-radius:8px;padding:9px 12px;\" onclick=\"try{var m=document.querySelector(\\'.ti-modal.show,.ti-overlay.show,#ti-alert-ov\\');if(m)m.classList.remove(\\'show\\');}catch(e){} return false;\">Continua<\/button>' +\n            '<div style=\"margin-top:8px;font-size:12px;color:#cbd5e1;\">Controllo: ' + esc(source || 'accesso') + '<\/div>' +\n            '<\/div>';\n        try {\n            if (typeof window.tiAlert === 'function') window.tiAlert(html);\n            else alert('Aggiornamento plugin rilevato. Versione pagina: ' + (currentHeader() || 'non disponibile') + '\\nVersione server: ' + (serverHeader || serverVersion) + '\\nAggiorna la pagina prima di proseguire.');\n        } catch(e) {}\n    }\n    function parseJson(resp){ return resp.text().then(function(t){ try { return JSON.parse(t); } catch(e) { return null; } }); }\n    window.tiCheckPluginServerVersion457 = function(source, force){\n        try {\n            source = source || 'main form';\n            if (disabled() || !isPrivileged()) return Promise.resolve(false);\n            var now = Date.now();\n            \/\/ Anti-loop: mai piu di una chiamata globale ogni 60s; la Configurazione puo forzare ma non piu di una volta ogni 15s.\n            var minGlobal = force ? 15000 : 60000;\n            if (pending || (now - lastCheckAt < minGlobal)) return Promise.resolve(false);\n            if (lastSourceAt[source] && now - lastSourceAt[source] < 15000) return Promise.resolve(false);\n            pending = true; lastCheckAt = now; lastSourceAt[source] = now;\n            var fd = new FormData();\n            fd.append('action', 'ti_ai_plugin_version_check');\n            fd.append('ti_action', 'ti_ai_plugin_version_check');\n            fd.append('_ts', String(now));\n            fd.append('loaded_version', pageVersion);\n            fd.append('db', selectedDb());\n            var opts = {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'};\n            var ctl = null; var timer = null;\n            if (window.AbortController) {\n                ctl = new AbortController(); opts.signal = ctl.signal;\n                timer = setTimeout(function(){ try { ctl.abort(); } catch(e) {} }, 4000);\n            }\n            return fetch(endpoint, opts).then(parseJson).then(function(res){\n                pending = false; if (timer) clearTimeout(timer);\n                if (!res || res.success === false) return false;\n                showWarn(res.data || {}, source);\n                return true;\n            }).catch(function(){ pending = false; if (timer) clearTimeout(timer); return false; });\n        } catch(e) { pending = false; return Promise.resolve(false); }\n    };\n    function checkSoon(source, force, delay){ setTimeout(function(){ try { window.tiCheckPluginServerVersion457(source, !!force); } catch(e) {} }, delay == null ? 150 : delay); }\n    function patchFn(name, source){\n        try {\n            var fn = window[name];\n            if (typeof fn !== 'function' || fn.__tiVersionCheck457) return;\n            var wrapped = function(){\n                var ret = fn.apply(this, arguments);\n                checkSoon(source, true, 250);\n                return ret;\n            };\n            wrapped.__tiVersionCheck457 = true; wrapped.__tiOriginal = fn; window[name] = wrapped;\n        } catch(e) {}\n    }\n    function patchAll(){ patchFn('openConfigSafe218','Configurazione'); patchFn('setAdminUI','main form'); }\n    document.addEventListener('click', function(ev){\n        try {\n            var t = ev && ev.target; if (!t || !t.closest) return;\n            if (t.closest('#ti-conf-toggle')) checkSoon('Configurazione', true, 200);\n            else if (t.closest('#ti-send, #ti-ditta-trigger, #ti-ditta')) checkSoon('main form', false, 300);\n        } catch(e) {}\n    }, true);\n    document.addEventListener('change', function(ev){ try { if (ev && ev.target && ev.target.id === 'ti-ditta') checkSoon('main form', true, 300); } catch(e) {} }, true);\n    function boot(){ patchAll(); checkSoon('main form', false, 1200); setTimeout(patchAll, 1500); }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot); else boot();\n})();\n<\/script>\n\n<script id=\"ti-login-singleton-fix-315\">\n(function(){\n    \"use strict\";\n    if (window.__tiLoginSingletonFix315) return;\n    window.__tiLoginSingletonFix315 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function all(sel){ try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch(e) { return []; } }\n    function isVisible(el){\n        if (!el) return false;\n        try {\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            return el.style.display !== 'none' && (!cs || cs.display !== 'none') && el.getAttribute('aria-hidden') !== 'true';\n        } catch(e) { return el.style.display !== 'none'; }\n    }\n    function hideOverlay(el){\n        if (!el) return;\n        try {\n            el.classList.remove('ti-modal-open','ti-login-open-186','ti-login-open-315','open','active','show');\n            el.style.setProperty('display','none','important');\n            el.setAttribute('aria-hidden','true');\n        } catch(e) {}\n    }\n    function preferredLoginOverlay(){\n        var list = all('#ti-login-ov');\n        if (!list.length) return null;\n        var visible = list.filter(isVisible);\n        if (visible.length) return visible[0];\n        var inOuter = list.filter(function(el){ return !!(el.closest && el.closest('#ti-ai-outer')); });\n        return inOuter[0] || list[0];\n    }\n    function dedupeLoginDom(){\n        try {\n            var keep = preferredLoginOverlay();\n            all('#ti-login-ov').forEach(function(el){ if (el !== keep) hideOverlay(el); });\n            var keepForm = keep ? keep.querySelector('#ti-login-form') : null;\n            all('#ti-login-form').forEach(function(form){ if (form !== keepForm && form.closest && form.closest('#ti-login-ov')) hideOverlay(form.closest('#ti-login-ov')); });\n            var keepDirect = byId('ti-mobile-direct-login');\n            all('#ti-mobile-direct-login').forEach(function(el, idx){ if (idx > 0 || el !== keepDirect) { try { el.style.display='none'; el.setAttribute('aria-hidden','true'); } catch(e) {} } });\n            return keep;\n        } catch(e) { return byId('ti-login-ov'); }\n    }\n    function closeDirectFallbackUnlessManual(){\n        try {\n            var dl = byId('ti-mobile-direct-login');\n            if (!dl) return;\n            if (!dl.classList.contains('ti-direct-login-manual-open')) {\n                dl.classList.remove('ti-direct-login-visible');\n                dl.style.display = 'none';\n                dl.setAttribute('aria-hidden','true');\n            }\n        } catch(e) {}\n    }\n    function openLoginSingleton(ev){\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }\n        var now = Date.now();\n        if (window.__tiLoginOpenTs315 && now - window.__tiLoginOpenTs315 < 450) return false;\n        window.__tiLoginOpenTs315 = now;\n        var ov = dedupeLoginDom();\n        if (!ov) return false;\n        closeDirectFallbackUnlessManual();\n        try { if (window.syncLoginDbSelect) window.syncLoginDbSelect(); } catch(e) {}\n        try {\n            ov.style.setProperty('display','flex','important');\n            ov.style.setProperty('align-items','center','important');\n            ov.style.setProperty('justify-content','center','important');\n            ov.style.setProperty('pointer-events','auto','important');\n            ov.style.setProperty('z-index','2147483000','important');\n            ov.classList.add('ti-modal-open','ti-login-open-315');\n            ov.removeAttribute('aria-hidden');\n            var modal = ov.querySelector('.ti-modal');\n            if (modal) modal.style.setProperty('pointer-events','auto','important');\n            if (window.applyLayoutLanguage) window.applyLayoutLanguage(true);\n            if (window.tiApplyLastSuccessfulLoginToForm) window.tiApplyLastSuccessfulLoginToForm(false);\n            if (window.bindMobileLoginButton) window.bindMobileLoginButton();\n        } catch(e) {}\n        setTimeout(function(){\n            try {\n                dedupeLoginDom();\n                if (!(window.tiIsMobileLike && window.tiIsMobileLike())) {\n                    var u = byId('l-user'); if (u && typeof u.focus === 'function') u.focus({preventScroll:true});\n                }\n            } catch(e) {}\n        }, 80);\n        return false;\n    }\n    function closeLoginSingleton(ev){\n        if (ev) { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }\n        all('#ti-login-ov').forEach(hideOverlay);\n        try {\n            document.documentElement.classList.remove('ti-no-scroll');\n            document.body.classList.remove('ti-no-scroll');\n            if (window.updatePluginScrollLock) window.updatePluginScrollLock();\n        } catch(e) {}\n        return false;\n    }\n    function markManualDirectOpen(){\n        try {\n            var dl = byId('ti-mobile-direct-login');\n            if (dl) dl.classList.add('ti-direct-login-manual-open');\n        } catch(e) {}\n    }\n    function bindLoginSingleton(){\n        try {\n            dedupeLoginDom();\n            var openBtn = byId('ti-login-btn');\n            if (openBtn && !openBtn.dataset.tiLoginSingleton315) {\n                openBtn.dataset.tiLoginSingleton315 = '1';\n                openBtn.onclick = openLoginSingleton;\n                openBtn.addEventListener('click', openLoginSingleton, true);\n                openBtn.addEventListener('touchend', openLoginSingleton, {passive:false, capture:true});\n            }\n            all('#ti-login-ov .btn-close, #ti-login-ov button[onclick*=\"closeModal\"]').forEach(function(btn){\n                if (btn.dataset.tiLoginSingletonClose315) return;\n                btn.dataset.tiLoginSingletonClose315 = '1';\n                btn.onclick = closeLoginSingleton;\n                btn.addEventListener('click', closeLoginSingleton, true);\n            });\n            var manual = byId('ti-show-direct-login-fallback');\n            if (manual && !manual.dataset.tiDirectManual315) {\n                manual.dataset.tiDirectManual315 = '1';\n                manual.addEventListener('click', markManualDirectOpen, true);\n            }\n        } catch(e) {}\n    }\n\n    var previousOpenLogin = window.openLoginModalSafe;\n    window.openLoginModalSafe = function(){ return openLoginSingleton(); };\n    window.openLoginModalSafe.__tiLoginSingleton315 = true;\n\n    if (typeof window.openModal === 'function' && !window.openModal.__tiLoginSingleton315) {\n        var prevOpenModal = window.openModal;\n        window.openModal = function(id){\n            if (id === 'ti-login-ov') return openLoginSingleton();\n            return prevOpenModal.apply(this, arguments);\n        };\n        window.openModal.__tiLoginSingleton315 = true;\n    }\n    if (typeof window.closeModal === 'function' && !window.closeModal.__tiLoginSingleton315) {\n        var prevCloseModal = window.closeModal;\n        window.closeModal = function(id){\n            if (id === 'ti-login-ov') return closeLoginSingleton();\n            return prevCloseModal.apply(this, arguments);\n        };\n        window.closeModal.__tiLoginSingleton315 = true;\n    }\n\n    function suppressRepeatedLoginErrorOpen(){\n        try {\n            var params = new URLSearchParams(window.location.search || '');\n            if (!params.has('ti_login_error')) return;\n            var key = 'ti_login_error_open_seen_' + location.pathname + '_' + params.get('ti_login_error');\n            if (sessionStorage.getItem(key) === '1') return;\n            sessionStorage.setItem(key, '1');\n            setTimeout(function(){\n                try {\n                    var detail = params.get('ti_login_error') || 'login non riuscito';\n                    if (window.revealDirectLoginFallback) window.revealDirectLoginFallback('login-error-url', decodeURIComponent(detail));\n                    else openLoginSingleton();\n                } catch(e) { openLoginSingleton(); }\n            }, 260);\n        } catch(e) {}\n    }\n\n    function bindDelegatedLoginInterceptors(){\n        if (window.__tiLoginDelegatedInterceptors315) return;\n        window.__tiLoginDelegatedInterceptors315 = true;\n        document.addEventListener('click', function(ev){\n            var t = ev.target;\n            if (!t || !t.closest) return;\n            if (t.closest('#ti-login-btn')) { openLoginSingleton(ev); return; }\n            if (t.closest('#ti-login-ov .btn-close, #ti-login-ov button[onclick*=\\\"closeModal\\\"]')) { closeLoginSingleton(ev); return; }\n        }, true);\n        document.addEventListener('touchend', function(ev){\n            var t = ev.target;\n            if (!t || !t.closest) return;\n            if (t.closest('#ti-login-btn')) { openLoginSingleton(ev); return; }\n        }, {passive:false, capture:true});\n    }\n\n    function boot(){\n        bindDelegatedLoginInterceptors();\n        bindLoginSingleton();\n        dedupeLoginDom();\n        suppressRepeatedLoginErrorOpen();\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ boot(); setTimeout(boot, 400); setTimeout(boot, 1200); });\n    else { boot(); setTimeout(boot, 400); setTimeout(boot, 1200); }\n    try { new MutationObserver(function(){ bindLoginSingleton(); dedupeLoginDom(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n})();\n<\/script>\n\n\n<script id=\"ti-login-success-nonblocking-fix-318\">\n(function(){\n    \"use strict\";\n    if (window.__tiLoginSuccessNonBlocking318) return;\n    window.__tiLoginSuccessNonBlocking318 = true;\n\n    function hasLoginParam(){\n        try {\n            var p = new URLSearchParams(window.location.search || '');\n            return p.has('ti_login_error') || p.has('ti_login_ok') || p.has('ti_action') || p.has('action') || p.has('redirect') || p.has('ti_mobile_login_redirect');\n        } catch(e) { return false; }\n    }\n    function cleanLoginUrl(){\n        try {\n            var url = new URL(window.location.href);\n            ['ti_login_error','ti_login_ok','ti_action','action','redirect','ti_mobile_login_redirect','_wpnonce'].forEach(function(k){ url.searchParams.delete(k); });\n            var clean = url.pathname + (url.search ? url.search : '') + (url.hash || '');\n            if (clean !== (window.location.pathname + window.location.search + window.location.hash)) {\n                window.history.replaceState({}, document.title, clean);\n            }\n            return clean || window.location.pathname || '\/';\n        } catch(e) { return window.location.href.split('#')[0]; }\n    }\n    function hideLoginIfVisible(){\n        try {\n            document.querySelectorAll('#ti-login-ov').forEach(function(ov){\n                var cs = window.getComputedStyle ? window.getComputedStyle(ov) : null;\n                var visible = ov && ov.getAttribute('aria-hidden') !== 'true' && (!cs || cs.display !== 'none') && ov.style.display !== 'none';\n                if (!visible) return;\n                ov.classList.remove('ti-modal-open','ti-login-open-186','ti-login-open-315','open','active','show');\n                ov.style.setProperty('display','none','important');\n                ov.setAttribute('aria-hidden','true');\n            });\n            var dl = document.getElementById('ti-mobile-direct-login');\n            if (dl && !dl.classList.contains('ti-direct-login-manual-open')) {\n                dl.classList.remove('ti-direct-login-visible');\n                dl.style.display = 'none';\n                dl.setAttribute('aria-hidden','true');\n            }\n            if (window.updatePluginScrollLock) window.updatePluginScrollLock();\n        } catch(e) {}\n    }\n    function markSuccess(){\n        try {\n            sessionStorage.setItem('ti_login_success_no_autoopen_318', String(Date.now()));\n            sessionStorage.removeItem('ti_login_failed_clear');\n        } catch(e) {}\n    }\n    function recentSuccess(){\n        try {\n            var ts = parseInt(sessionStorage.getItem('ti_login_success_no_autoopen_318') || '0', 10);\n            return ts && (Date.now() - ts < 15000);\n        } catch(e) { return false; }\n    }\n\n    window.tiCleanLoginUrl = cleanLoginUrl;\n    window.tiReloadAfterSuccessfulLogin = function(){\n        markSuccess();\n        var clean = cleanLoginUrl();\n        try {\n            var target = new URL(clean, window.location.origin).href;\n            window.location.replace(target);\n        } catch(e) {\n            window.location.href = clean || window.location.href.split('#')[0];\n        }\n    };\n\n    function boot(){\n        try {\n            var logged = !!String(window.tiUser || '').trim();\n            if (logged || hasLoginParam() || recentSuccess()) cleanLoginUrl();\n            if (logged || recentSuccess()) {\n                hideLoginIfVisible();\n                setTimeout(hideLoginIfVisible, 180);\n            }\n        } catch(e) {}\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, {once:true});\n    else boot();\n})();\n<\/script>\n\n\n<script id=\"ti-login-direct-proposal-336\">\n(function(){\n    \"use strict\";\n    if (window.__tiLoginDirectProposal336) return;\n    window.__tiLoginDirectProposal336 = true;\n    function propose(detail){\n        try {\n            if (String(window.tiUser || '').trim()) return;\n            if (window.revealDirectLoginFallback) {\n                window.revealDirectLoginFallback('login-failed-336', detail || 'login non riuscito');\n            }\n        } catch(e) {}\n    }\n    function boot(){\n        try {\n            var params = new URLSearchParams(window.location.search || '');\n            if (params.has('ti_login_error')) {\n                var detail = params.get('ti_login_error') || 'login non riuscito';\n                setTimeout(function(){ propose(detail); }, 360);\n            }\n        } catch(e) {}\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, {once:true});\n    else boot();\n})();\n<\/script>\n\n\n<script id=\"ti-config-stay-open-324\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigStayOpen324Installed) return;\n    window.__tiConfigStayOpen324Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function getConfigOverlay(){ return byId('ti-conf-ov'); }\n    function targetId(x){\n        if (typeof x === 'string') return x;\n        if (x && x.id) return x.id;\n        return '';\n    }\n    function isVisible(el){\n        if (!el) return false;\n        try {\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            return el.style.display !== 'none' && (!cs || (cs.display !== 'none' && cs.visibility !== 'hidden' && cs.opacity !== '0'));\n        } catch(e) { return el.style.display !== 'none'; }\n    }\n    function markConfigOpen(reason){\n        window.__tiConfigStayOpen324 = true;\n        window.__tiConfigExplicitClose324 = false;\n        window.__tiConfigStayOpenReason324 = reason || 'config-open';\n    }\n    function allowExplicitConfigClose(reason){\n        window.__tiConfigExplicitClose324 = true;\n        window.__tiConfigStayOpen324 = false;\n        window.__tiConfigStayOpenReason324 = reason || 'explicit-close';\n    }\n    function shouldKeepConfigOpen(){\n        var ov = getConfigOverlay();\n        if (!ov) return false;\n        if (window.__tiConfigExplicitClose324) return false;\n        if (!window.__tiConfigStayOpen324) return false;\n        return true;\n    }\n    function showConfigOverlay(reason){\n        var ov = getConfigOverlay();\n        if (!ov || !shouldKeepConfigOpen()) return false;\n        try {\n            if (window.preparePluginModal) window.preparePluginModal(ov);\n            if (window.showPluginModal && !window.__tiConfigStayOpenShowing324) {\n                window.__tiConfigStayOpenShowing324 = true;\n                try { window.showPluginModal(ov); } finally { window.__tiConfigStayOpenShowing324 = false; }\n            } else {\n                ov.style.display = 'flex';\n                ov.removeAttribute('aria-hidden');\n            }\n            if (window.keepPluginModalInFront) window.keepPluginModalInFront(ov);\n            if (window.updatePluginScrollLock) window.updatePluginScrollLock();\n            window.__tiConfigStayOpenReason324 = reason || window.__tiConfigStayOpenReason324 || 'ensure';\n            return true;\n        } catch(e) {\n            try { ov.style.display = 'flex'; } catch(ignore) {}\n            return false;\n        }\n    }\n    function ensureConfigOpen(reason){\n        if (!shouldKeepConfigOpen()) return;\n        setTimeout(function(){ if (shouldKeepConfigOpen()) showConfigOverlay(reason || 'after-process'); }, 80);\n        setTimeout(function(){ if (shouldKeepConfigOpen()) showConfigOverlay(reason || 'after-process'); }, 350);\n    }\n\n    function wrapOpenModal(){\n        if (typeof window.openModal === 'function' && !window.openModal.__tiConfigStayOpen324) {\n            var prevOpen = window.openModal;\n            window.openModal = function(id){\n                if (targetId(id) === 'ti-conf-ov') markConfigOpen('openModal');\n                var ret = prevOpen.apply(this, arguments);\n                if (targetId(id) === 'ti-conf-ov') ensureConfigOpen('openModal');\n                return ret;\n            };\n            window.openModal.__tiConfigStayOpen324 = true;\n        }\n    }\n    function wrapShowPluginModal(){\n        if (typeof window.showPluginModal === 'function' && !window.showPluginModal.__tiConfigStayOpen324) {\n            var prevShow = window.showPluginModal;\n            window.showPluginModal = function(elOrId){\n                if (targetId(elOrId) === 'ti-conf-ov') markConfigOpen('showPluginModal');\n                var ret = prevShow.apply(this, arguments);\n                if (targetId(elOrId) === 'ti-conf-ov') ensureConfigOpen('showPluginModal');\n                return ret;\n            };\n            window.showPluginModal.__tiConfigStayOpen324 = true;\n        }\n    }\n    function wrapCloseModal(){\n        if (typeof window.closeModal === 'function' && !window.closeModal.__tiConfigStayOpen324) {\n            var prevClose = window.closeModal;\n            window.closeModal = function(id){\n                var tid = targetId(id);\n                if (tid === 'ti-conf-ov' && shouldKeepConfigOpen() && !window.__tiConfigExplicitClose324) {\n                    ensureConfigOpen('blocked-closeModal');\n                    return false;\n                }\n                var ret = prevClose.apply(this, arguments);\n                if (tid && tid !== 'ti-conf-ov') ensureConfigOpen('closed-' + tid);\n                return ret;\n            };\n            window.closeModal.__tiConfigStayOpen324 = true;\n        }\n    }\n    function wrapCloseConfigModal(){\n        if (typeof window.closeConfigModal === 'function' && !window.closeConfigModal.__tiConfigStayOpen324) {\n            var prevCloseConfig = window.closeConfigModal;\n            window.closeConfigModal = function(){\n                allowExplicitConfigClose('closeConfigModal');\n                return prevCloseConfig.apply(this, arguments);\n            };\n            window.closeConfigModal.__tiConfigStayOpen324 = true;\n        }\n    }\n    function wrapProcessFunctions(){\n        if (typeof window.hideProgressPopup === 'function' && !window.hideProgressPopup.__tiConfigStayOpen324) {\n            var prevHideProgress = window.hideProgressPopup;\n            window.hideProgressPopup = function(){\n                var ret = prevHideProgress.apply(this, arguments);\n                ensureConfigOpen('hideProgressPopup');\n                return ret;\n            };\n            window.hideProgressPopup.__tiConfigStayOpen324 = true;\n        }\n        if (typeof window.tiAlert === 'function' && !window.tiAlert.__tiConfigStayOpen324) {\n            var prevAlert = window.tiAlert;\n            window.tiAlert = function(){\n                var ret = prevAlert.apply(this, arguments);\n                ensureConfigOpen('tiAlert');\n                return ret;\n            };\n            window.tiAlert.__tiConfigStayOpen324 = true;\n        }\n        if (typeof window.closeConfigImportPreview === 'function' && !window.closeConfigImportPreview.__tiConfigStayOpen324) {\n            var prevCloseImport = window.closeConfigImportPreview;\n            window.closeConfigImportPreview = function(){\n                var ret = prevCloseImport.apply(this, arguments);\n                ensureConfigOpen('closeConfigImportPreview');\n                return ret;\n            };\n            window.closeConfigImportPreview.__tiConfigStayOpen324 = true;\n        }\n    }\n\n    function installExplicitCloseCapture(){\n        if (window.__tiConfigExplicitCloseCapture324) return;\n        window.__tiConfigExplicitCloseCapture324 = true;\n        document.addEventListener('click', function(ev){\n            var t = ev.target;\n            if (!t || !t.closest) return;\n            var ov = t.closest('#ti-conf-ov');\n            if (!ov) return;\n            var btn = t.closest('button, a');\n            if (!btn) return;\n            var txt = String(btn.textContent || btn.value || '').trim().toLowerCase();\n            var isClose = btn.classList.contains('ti-modal-x-red') || btn.classList.contains('btn-close') || \/^(x|\u00d7|chiudi|close)$\/i.test(txt) || \/\\bchiudi\\b\/i.test(txt);\n            if (isClose) allowExplicitConfigClose('user-button');\n        }, true);\n    }\n\n    function installObserver(){\n        if (window.__tiConfigStayObserver324) return;\n        var ov = getConfigOverlay();\n        if (!ov || typeof MutationObserver === 'undefined') return;\n        window.__tiConfigStayObserver324 = new MutationObserver(function(){\n            if (shouldKeepConfigOpen() && !isVisible(ov)) ensureConfigOpen('observer');\n        });\n        try { window.__tiConfigStayObserver324.observe(ov, {attributes:true, attributeFilter:['style','class','aria-hidden']}); } catch(e) {}\n    }\n\n    function boot(){\n        wrapShowPluginModal();\n        wrapOpenModal();\n        wrapCloseModal();\n        wrapCloseConfigModal();\n        wrapProcessFunctions();\n        installExplicitCloseCapture();\n        installObserver();\n        var ov = getConfigOverlay();\n        if (ov && isVisible(ov)) markConfigOpen('boot-visible');\n    }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, {once:true});\n    else boot();\n    setTimeout(boot, 300);\n    setTimeout(boot, 1200);\n})();\n<\/script>\n<script id=\"ti-file-manager-ateco-front-347\">\n(function(){\n    \/\/ Disattivato dalla 30.9.664: sostituito da ti-modal-stack-353.\n})();\n<\/script>\n\n<script id=\"ti-file-manager-ateco-native-fallback-346\">\n(function(){\n    \"use strict\";\n    if (window.__tiFileManagerAtecoNativeFallback346) return;\n    window.__tiFileManagerAtecoNativeFallback346 = true;\n    function byId(id){ return document.getElementById(id); }\n    function isVisible(el){\n        if (!el) return false;\n        try { var cs = window.getComputedStyle ? window.getComputedStyle(el) : null; return !!(cs && cs.display !== 'none' && cs.visibility !== 'hidden'); }\n        catch(e) { return el.style && el.style.display !== 'none'; }\n    }\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var atecoBtn = t.closest('button[onclick*=\"openAtecoSearch\"]');\n        if (atecoBtn) {\n            setTimeout(function(){\n                if (isVisible(byId('ti-ateco-ov'))) return;\n                var oc = String(atecoBtn.getAttribute('onclick') || '');\n                var m = oc.match(\/openAtecoSearch\\((['\"])(.*?)\\1\\)\/);\n                if (m && typeof window.openAtecoSearch === 'function') window.openAtecoSearch(m[2] || '');\n            }, 80);\n            return;\n        }\n        var fmBtn = t.closest('#ti-file-manager-ov button');\n        if (!fmBtn) return;\n        var oc = String(fmBtn.getAttribute('onclick') || '');\n        if (oc.indexOf('chooseAIManager') !== -1) {\n            setTimeout(function(){ if (!isVisible(byId('ti-ai-gen-ov')) && typeof window.chooseAIManager === 'function') window.chooseAIManager(); }, 120);\n        }\n    }, false);\n})();\n<\/script>\n<script id=\"ti-operational-popups-front-350\">\n(function(){\n    \/\/ Disattivato dalla 30.9.664: sostituito da ti-modal-stack-353.\n})();\n<\/script>\n<script id=\"ti-operational-popups-front-persistent-351\">\n(function(){\n    \/\/ Disattivato dalla 30.9.664: sostituito da ti-modal-stack-353.\n})();\n<\/script>\n<script id=\"ti-ai-gen-interactive-front-352\">\n(function(){\n    \/\/ Disattivato dalla 30.9.664: sostituito da ti-modal-stack-353.\n})();\n<\/script>\n\n\n<script id=\"ti-modal-front-354\">\n(function(){\n    \/\/ Disattivato dalla 30.9.664: il gestore con MutationObserver poteva causare flash\/loop e perdita del contesto help.\n})();\n<\/script>\n\n<script id=\"ti-modal-front-355\">\n(function(){\n    \"use strict\";\n    if (window.__tiModalFront355) return;\n    window.__tiModalFront355 = true;\n    var MAIN_Z = 2147480800;\n    var CHILD_BASE_Z = 2147483000;\n    var ALERT_Z = 2147483647;\n    var LOADER_Z = 2147483646;\n    var seq = 0;\n    var order = [];\n    var mainIds = {'ti-conf-ov':true,'ti-login-ov':true,'ti-direct-login-ov':true,'ti-purchase-ov':true};\n    function byId(id){ return typeof id === 'string' ? document.getElementById(id) : id; }\n    function isEl(el){ return !!(el && el.nodeType === 1); }\n    function isOverlay(el){ return isEl(el) && ((el.classList && el.classList.contains('ti-ov')) || !!(el.id && \/^ti-.*-ov$\/.test(el.id))); }\n    function closestOverlay(node){ try { return node && node.closest ? node.closest('.ti-ov, [id^=\"ti-\"][id$=\"-ov\"]') : null; } catch(e) { return null; } }\n    function visible(el){\n        if (!isOverlay(el)) return false;\n        try {\n            if (el.hasAttribute && el.hasAttribute('hidden')) return false;\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden')) return false;\n            return true;\n        } catch(e) { return !!(el.style && el.style.display !== 'none'); }\n    }\n    function setZ(el, z){\n        if (!isEl(el) || !el.style) return;\n        try { el.style.setProperty('z-index', String(z), 'important'); } catch(e) { try { el.style.zIndex = String(z); } catch(e2) {} }\n        try { el.style.setProperty('pointer-events', 'auto', 'important'); } catch(e) {}\n    }\n    function removeOrder(el){ order = order.filter(function(x){ return x !== el && visible(x); }); }\n    function promote(el){\n        el = byId(el);\n        if (!isOverlay(el)) return null;\n        if (el.id === 'ti-alert-ov') { setZ(el, ALERT_Z); return el; }\n        if (el.id === 'ti-ai-loader-ov') { setZ(el, LOADER_Z); return el; }\n        removeOrder(el);\n        order.push(el);\n        seq++;\n        if (seq > 20000) seq = 1;\n        if (mainIds[el.id]) {\n            setZ(el, MAIN_Z);\n        } else {\n            setZ(el, CHILD_BASE_Z + seq);\n            var conf = document.getElementById('ti-conf-ov');\n            if (visible(conf)) setZ(conf, MAIN_Z);\n        }\n        return el;\n    }\n    function promoteVisibleTop(){ order = order.filter(visible); if (order.length) promote(order[order.length - 1]); }\n    function afterOpen(id){ setTimeout(function(){ promote(id); }, 0); setTimeout(function(){ promote(id); }, 80); setTimeout(function(){ promote(id); }, 240); }\n    window.tiPromoteModalFront355 = function(id){ return promote(id); };\n    window.tiPromoteLastVisibleModal355 = promoteVisibleTop;\n    if (typeof window.openModal === 'function' && !window.openModal.__tiFront355) {\n        var prevOpen = window.openModal;\n        window.openModal = function(id){ var ret = prevOpen.apply(this, arguments); afterOpen(id); return ret; };\n        window.openModal.__tiFront355 = true;\n    }\n    if (typeof window.showPluginModal === 'function' && !window.showPluginModal.__tiFront355) {\n        var prevShow = window.showPluginModal;\n        window.showPluginModal = function(id){ var ret = prevShow.apply(this, arguments); afterOpen(id); return ret; };\n        window.showPluginModal.__tiFront355 = true;\n    }\n    if (typeof window.closeModal === 'function' && !window.closeModal.__tiFront355) {\n        var prevClose = window.closeModal;\n        window.closeModal = function(id){ var el = byId(id); var ret = prevClose.apply(this, arguments); if (el) removeOrder(el); setTimeout(promoteVisibleTop, 40); return ret; };\n        window.closeModal.__tiFront355 = true;\n    }\n    ['mousedown','focusin','touchstart'].forEach(function(evt){ document.addEventListener(evt, function(ev){ var ov = closestOverlay(ev && ev.target); if (ov) promote(ov); }, false); });\n    setTimeout(function(){ ['ti-col-help-ov','ti-ateco-ov','ti-file-manager-ov','ti-ai-gen-ov','ti-help-ov','ti-report-ov','ti-global-action-ov'].forEach(function(id){ var el=byId(id); if (visible(el)) promote(el); }); }, 300);\n})();\n<\/script>\n\n\n<script id=\"ti-modal-context-front-356\">\n(function(){\n    \"use strict\";\n    if (window.__tiModalContextFront356) return;\n    window.__tiModalContextFront356 = true;\n\n    var MAIN_Z = 2147480900;\n    var CHILD_BASE_Z = 2147483200;\n    var ALERT_Z = 2147483647;\n    var LOADER_Z = 2147483646;\n    var mainIds = {\n        'ti-conf-ov': true,\n        'ti-login-ov': true,\n        'ti-direct-login-ov': true,\n        'ti-purchase-ov': true\n    };\n    var topSystemIds = {\n        'ti-alert-ov': ALERT_Z,\n        'ti-ai-loader-ov': LOADER_Z\n    };\n    var order = [];\n    var seq = 0;\n\n    function byId(id){ return (typeof id === 'string') ? document.getElementById(id) : id; }\n    function isEl(el){ return !!(el && el.nodeType === 1); }\n    function overlayOf(el){\n        if (!el) return null;\n        if (typeof el === 'string') return document.getElementById(el);\n        try {\n            if (el.classList && el.classList.contains('ti-ov')) return el;\n            return el.closest ? el.closest('.ti-ov, [id^=\"ti-\"][id$=\"-ov\"]') : null;\n        } catch(e) { return null; }\n    }\n    function overlayId(idOrEl){ var el = byId(idOrEl); return el && el.id ? el.id : String(idOrEl || ''); }\n    function isOverlay(el){ return isEl(el) && ((el.classList && el.classList.contains('ti-ov')) || (el.id && \/^ti-.*-ov$\/.test(el.id))); }\n    function isVisible(el){\n        if (!isOverlay(el)) return false;\n        try {\n            if (el.hasAttribute && el.hasAttribute('hidden')) return false;\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0')) return false;\n            if (el.style && (el.style.display === 'none' || el.style.visibility === 'hidden')) return false;\n            return true;\n        } catch(e) { return !!(el.style && el.style.display !== 'none'); }\n    }\n    function setZ(el, z){\n        if (!isEl(el) || !el.style) return;\n        try { el.style.setProperty('z-index', String(z), 'important'); } catch(e) { try { el.style.zIndex = String(z); } catch(e2) {} }\n        try { el.style.setProperty('pointer-events', 'auto', 'important'); } catch(e) {}\n        if (isVisible(el)) {\n            try { el.removeAttribute('aria-hidden'); } catch(e) {}\n        }\n    }\n    function visibleChildren(){\n        var nodes = Array.prototype.slice.call(document.querySelectorAll('.ti-ov, [id^=\"ti-\"][id$=\"-ov\"]'));\n        return nodes.filter(function(el){\n            if (!isVisible(el) || !el.id) return false;\n            if (mainIds[el.id] || topSystemIds[el.id]) return false;\n            return true;\n        });\n    }\n    function visibleMain(){\n        return Object.keys(mainIds).map(function(id){ return document.getElementById(id); }).filter(isVisible);\n    }\n    function normalizeOrder(){\n        var children = visibleChildren();\n        order = order.filter(function(id){ return children.some(function(el){ return el.id === id; }); });\n        children.forEach(function(el){ if (order.indexOf(el.id) < 0) order.push(el.id); });\n    }\n    function lowerMainIfNeeded(){\n        var children = visibleChildren();\n        if (!children.length) return;\n        visibleMain().forEach(function(el){ setZ(el, MAIN_Z); });\n    }\n    function topChild(){\n        normalizeOrder();\n        for (var i = order.length - 1; i >= 0; i--) {\n            var el = document.getElementById(order[i]);\n            if (isVisible(el) && !mainIds[el.id] && !topSystemIds[el.id]) return el;\n        }\n        var children = visibleChildren();\n        return children.length ? children[children.length - 1] : null;\n    }\n    function applyStack(){\n        normalizeOrder();\n        lowerMainIfNeeded();\n        order.forEach(function(id, idx){\n            var el = document.getElementById(id);\n            if (isVisible(el)) setZ(el, CHILD_BASE_Z + idx + 1);\n        });\n        Object.keys(topSystemIds).forEach(function(id){ var el = document.getElementById(id); if (isVisible(el)) setZ(el, topSystemIds[id]); });\n    }\n    function promote(idOrEl){\n        var el = overlayOf(idOrEl) || byId(idOrEl);\n        if (!isOverlay(el)) return null;\n        if (topSystemIds[el.id]) { setZ(el, topSystemIds[el.id]); return el; }\n        if (mainIds[el.id]) {\n            var child = topChild();\n            setZ(el, MAIN_Z);\n            if (child) promote(child);\n            else setZ(el, CHILD_BASE_Z);\n            return el;\n        }\n        order = order.filter(function(id){ return id !== el.id; });\n        order.push(el.id);\n        seq++;\n        applyStack();\n        return el;\n    }\n    function promoteSoon(idOrEl){\n        var delays = [0, 60, 180, 420, 900, 1600];\n        delays.forEach(function(ms){ setTimeout(function(){ promote(idOrEl); applyStack(); }, ms); });\n    }\n\n    \/\/ Esponi un solo punto leggero di promozione. Mantiene compatibilita con il codice gia esistente.\n    window.tiPromoteModalFront356 = function(idOrEl){ return promote(idOrEl); };\n    window.tiPromoteLastVisibleModal356 = function(){ var el = topChild(); if (el) promote(el); else applyStack(); };\n    window.tiPromoteModalFront355 = window.tiPromoteModalFront356;\n    window.tiPromoteLastVisibleModal355 = window.tiPromoteLastVisibleModal356;\n\n    if (typeof window.bringPluginModalToFront === 'function' && !window.bringPluginModalToFront.__tiFront356) {\n        var prevBring = window.bringPluginModalToFront;\n        window.bringPluginModalToFront = function(elOrId){\n            var el = overlayOf(elOrId) || byId(elOrId);\n            if (el && mainIds[el.id] && topChild()) { setZ(el, MAIN_Z); promote(topChild()); return el; }\n            var ret = prevBring.apply(this, arguments);\n            var target = overlayOf(ret) || el;\n            if (target) promote(target);\n            return ret;\n        };\n        window.bringPluginModalToFront.__tiFront356 = true;\n    }\n    if (typeof window.keepPluginModalInFront === 'function' && !window.keepPluginModalInFront.__tiFront356) {\n        var prevKeep = window.keepPluginModalInFront;\n        window.keepPluginModalInFront = function(idOrEl){\n            var el = overlayOf(idOrEl) || byId(idOrEl);\n            var ret = prevKeep.apply(this, arguments);\n            if (el && mainIds[el.id]) {\n                setZ(el, MAIN_Z);\n                var child = topChild();\n                if (child) promoteSoon(child);\n            } else if (el) {\n                promoteSoon(el);\n            }\n            return ret;\n        };\n        window.keepPluginModalInFront.__tiFront356 = true;\n    }\n    if (typeof window.showPluginModal === 'function' && !window.showPluginModal.__tiFront356) {\n        var prevShow = window.showPluginModal;\n        window.showPluginModal = function(idOrEl){\n            var ret = prevShow.apply(this, arguments);\n            var el = overlayOf(idOrEl) || overlayOf(ret) || byId(idOrEl);\n            if (el) promoteSoon(el);\n            return ret;\n        };\n        window.showPluginModal.__tiFront356 = true;\n    }\n    if (typeof window.openModal === 'function' && !window.openModal.__tiFront356) {\n        var prevOpen = window.openModal;\n        window.openModal = function(id){\n            var ret = prevOpen.apply(this, arguments);\n            var el = byId(id);\n            if (el) promoteSoon(el);\n            return ret;\n        };\n        window.openModal.__tiFront356 = true;\n    }\n    if (typeof window.closeModal === 'function' && !window.closeModal.__tiFront356) {\n        var prevClose = window.closeModal;\n        window.closeModal = function(id){\n            var tid = overlayId(id);\n            var ret = prevClose.apply(this, arguments);\n            if (tid) order = order.filter(function(x){ return x !== tid; });\n            setTimeout(function(){ var child = topChild(); if (child) promote(child); else applyStack(); }, 60);\n            return ret;\n        };\n        window.closeModal.__tiFront356 = true;\n    }\n    ['mousedown','mouseup','click','focusin','touchstart'].forEach(function(evt){\n        document.addEventListener(evt, function(ev){\n            var ov = overlayOf(ev && ev.target);\n            if (!ov) return;\n            if (mainIds[ov.id] && topChild()) { setTimeout(function(){ var child = topChild(); if (child) promote(child); }, 0); return; }\n            if (!mainIds[ov.id]) promote(ov);\n        }, false);\n    });\n\n    function cloneCtx(ctx){\n        if (!ctx || typeof ctx !== 'object') return null;\n        return {tbl: ctx.tbl, idx: ctx.idx, key: ctx.key, val: ctx.val};\n    }\n    function validCtx(ctx){ return !!(ctx && typeof ctx === 'object' && ctx.tbl && ctx.idx !== undefined && ctx.idx !== null && ctx.key); }\n    function saveUploadCtx(ctx){\n        if (!validCtx(ctx)) return null;\n        var c = cloneCtx(ctx);\n        window.tiLastUploadContext356 = c;\n        try { sessionStorage.setItem('tiLastUploadContext356', JSON.stringify(c)); } catch(e) {}\n        return c;\n    }\n    function restoreUploadCtx(){\n        if (validCtx(window.targetUploadContext)) return window.targetUploadContext;\n        if (validCtx(window.tiLastUploadContext356)) { window.targetUploadContext = cloneCtx(window.tiLastUploadContext356); return window.targetUploadContext; }\n        try {\n            var raw = sessionStorage.getItem('tiLastUploadContext356');\n            if (raw) {\n                var parsed = JSON.parse(raw);\n                if (validCtx(parsed)) { window.targetUploadContext = cloneCtx(parsed); window.tiLastUploadContext356 = cloneCtx(parsed); return window.targetUploadContext; }\n            }\n        } catch(e) {}\n        return window.targetUploadContext;\n    }\n    function stampOverlayCtx(id, ctx){\n        var el = document.getElementById(id);\n        if (!el || !validCtx(ctx)) return;\n        try {\n            el.setAttribute('data-ti-context-table', ctx.tbl || '');\n            el.setAttribute('data-ti-context-row', String(ctx.idx));\n            el.setAttribute('data-ti-context-field', ctx.key || '');\n        } catch(e) {}\n    }\n\n    if (typeof window.openFileManager === 'function' && !window.openFileManager.__tiCtx356) {\n        var prevOpenFM = window.openFileManager;\n        window.openFileManager = function(tbl, idx, key){\n            var ret = prevOpenFM.apply(this, arguments);\n            var ctx = saveUploadCtx(window.targetUploadContext || {tbl:tbl, idx:idx, key:key});\n            stampOverlayCtx('ti-file-manager-ov', ctx);\n            promoteSoon('ti-file-manager-ov');\n            return ret;\n        };\n        window.openFileManager.__tiCtx356 = true;\n    }\n    if (typeof window.chooseAIManager === 'function' && !window.chooseAIManager.__tiCtx356) {\n        var prevChooseAIManager = window.chooseAIManager;\n        window.chooseAIManager = function(){\n            var ctx = restoreUploadCtx();\n            if (validCtx(ctx)) saveUploadCtx(ctx);\n            var ret = prevChooseAIManager.apply(this, arguments);\n            ctx = restoreUploadCtx();\n            if (validCtx(ctx)) {\n                window.targetUploadContext = cloneCtx(ctx);\n                stampOverlayCtx('ti-ai-gen-ov', ctx);\n                stampOverlayCtx('ti-file-manager-ov', ctx);\n            }\n            \/\/ La form AI e l'ultima operativa: deve stare sopra Gestione File Associati.\n            promoteSoon('ti-ai-gen-ov');\n            return ret;\n        };\n        window.chooseAIManager.__tiCtx356 = true;\n    }\n    if (typeof window.chooseAI === 'function' && !window.chooseAI.__tiCtx356) {\n        var prevChooseAI = window.chooseAI;\n        window.chooseAI = function(){\n            if (validCtx(window.targetUploadContext)) saveUploadCtx(window.targetUploadContext);\n            var ret = prevChooseAI.apply(this, arguments);\n            var ctx = restoreUploadCtx();\n            if (validCtx(ctx)) stampOverlayCtx('ti-ai-gen-ov', ctx);\n            promoteSoon('ti-ai-gen-ov');\n            return ret;\n        };\n        window.chooseAI.__tiCtx356 = true;\n    }\n    if (typeof window.useAIGen === 'function' && !window.useAIGen.__tiCtx356) {\n        var prevUseAI = window.useAIGen;\n        window.useAIGen = function(){\n            var ctx = restoreUploadCtx();\n            if (validCtx(ctx)) window.targetUploadContext = cloneCtx(ctx);\n            var ret = prevUseAI.apply(this, arguments);\n            setTimeout(function(){ if (validCtx(ctx)) { window.targetUploadContext = cloneCtx(ctx); promote('ti-file-manager-ov'); } }, 160);\n            return ret;\n        };\n        window.useAIGen.__tiCtx356 = true;\n    }\n    if (typeof window.handleFileSelect === 'function' && !window.handleFileSelect.__tiCtx356) {\n        var prevHandleFileSelect = window.handleFileSelect;\n        window.handleFileSelect = function(e){\n            try {\n                if (e && e.target && e.target.dataset && e.target.dataset.target === 'manager') {\n                    var ctx = restoreUploadCtx();\n                    if (validCtx(ctx)) window.targetUploadContext = cloneCtx(ctx);\n                }\n            } catch(ignore) {}\n            return prevHandleFileSelect.apply(this, arguments);\n        };\n        window.handleFileSelect.__tiCtx356 = true;\n    }\n\n    if (typeof window.openAtecoSearch === 'function' && !window.openAtecoSearch.__tiCtx356) {\n        var prevAteco = window.openAtecoSearch;\n        window.openAtecoSearch = function(targetId){\n            window.tiAtecoTargetId356 = targetId || window.tiAtecoTargetId356 || '';\n            var ret = prevAteco.apply(this, arguments);\n            try { var hidden = document.getElementById('ateco-target-id'); if (hidden && targetId) hidden.value = targetId; } catch(e) {}\n            promoteSoon('ti-ateco-ov');\n            return ret;\n        };\n        window.openAtecoSearch.__tiCtx356 = true;\n    }\n    if (typeof window.searchAteco === 'function' && !window.searchAteco.__tiFront356) {\n        var prevSearchAteco = window.searchAteco;\n        window.searchAteco = function(){ var ret = prevSearchAteco.apply(this, arguments); promoteSoon('ti-ateco-ov'); return ret; };\n        window.searchAteco.__tiFront356 = true;\n    }\n    if (typeof window.selectAteco === 'function' && !window.selectAteco.__tiCtx356) {\n        var prevSelectAteco = window.selectAteco;\n        window.selectAteco = function(codice, desc){\n            try { var hidden = document.getElementById('ateco-target-id'); if (hidden && !hidden.value && window.tiAtecoTargetId356) hidden.value = window.tiAtecoTargetId356; } catch(e) {}\n            return prevSelectAteco.apply(this, arguments);\n        };\n        window.selectAteco.__tiCtx356 = true;\n    }\n\n    if (typeof window.askColumnHelp === 'function' && !window.askColumnHelp.__tiCtx356) {\n        var prevAskColumnHelp = window.askColumnHelp;\n        window.askColumnHelp = function(tbl, col){\n            var ctx = {tbl:String(tbl || ''), col:String(col || ''), ts:Date.now()};\n            window.tiColumnHelpContext356 = ctx;\n            var ret = prevAskColumnHelp.apply(this, arguments);\n            var ov = document.getElementById('ti-col-help-ov');\n            if (ov) {\n                try {\n                    ov.setAttribute('data-ti-help-table', ctx.tbl);\n                    ov.setAttribute('data-ti-help-column', ctx.col);\n                    ov.setAttribute('data-ti-help-context-ts', String(ctx.ts));\n                } catch(e) {}\n            }\n            promoteSoon('ti-col-help-ov');\n            return ret;\n        };\n        window.askColumnHelp.__tiCtx356 = true;\n    }\n\n    \/\/ Primo riallineamento non invasivo per form gia aperte al caricamento dello script.\n    setTimeout(function(){ var child = topChild(); if (child) promote(child); else applyStack(); }, 300);\n})();\n<\/script>\n\n\n\n<script id=\"ti-help-copy-loader-close-357\">\n(function(){\n    \"use strict\";\n    if (window.__tiHelpCopyLoaderClose357Installed) return;\n    window.__tiHelpCopyLoaderClose357Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function textOf(el){ return el ? String(el.innerText || el.textContent || '') : ''; }\n    function clearLoaderTimers(){\n        try {\n            var st = window.tiLoaderState || (window.tiLoaderState = {});\n            st.active = false;\n            ['timer','watchdog327','stallGuard338'].forEach(function(k){ if (st[k]) { clearInterval(st[k]); st[k] = null; } });\n            ['closeTimer338','closeTimer357'].forEach(function(k){ if (st[k]) { clearTimeout(st[k]); st[k] = null; } });\n        } catch(e) {}\n    }\n    function hardCloseLoader(reason){\n        clearLoaderTimers();\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        var ov = byId('ti-ai-loader-ov');\n        if (ov) {\n            try {\n                ov.style.setProperty('display','none','important');\n                ov.style.setProperty('visibility','hidden','important');\n                ov.style.setProperty('opacity','0','important');\n                ov.style.setProperty('pointer-events','none','important');\n                ov.setAttribute('aria-hidden','true');\n            } catch(e) {}\n        }\n        try { if (typeof window.updatePluginScrollLock === 'function') window.updatePluginScrollLock(); } catch(e) {}\n        return true;\n    }\n    function looksCompleted(title, sub){\n        var s = String(title || '') + ' ' + String(sub || '') + ' ' + textOf(byId('ti-loader-title')) + ' ' + textOf(byId('ti-loader-sub')) + ' ' + textOf(byId('ti-loader-percent'));\n        s = s.toLowerCase();\n        if (\/errore|fallito|interrotto|annulla|chiarimento|specifiche|record aggiornati\\s*0\/.test(s)) return false;\n        return \/100\\s*%|completamento confermato|configurazione salvata|immagine generata|attivit[a\u00e0] ai conclusa|operazione completata\/.test(s);\n    }\n    function scheduleHardClose(reason, delay){\n        try {\n            var st = window.tiLoaderState || (window.tiLoaderState = {});\n            if (st.closeTimer357) clearTimeout(st.closeTimer357);\n            st.closeTimer357 = setTimeout(function(){ hardCloseLoader(reason); }, Math.max(80, delay || 700));\n        } catch(e) { setTimeout(function(){ hardCloseLoader(reason); }, Math.max(80, delay || 700)); }\n    }\n    window.tiHardCloseCompletedLoader357 = function(reason){\n        if (looksCompleted('', '')) return hardCloseLoader(reason || 'manual');\n        return false;\n    };\n\n    if (typeof window.hideProgressPopup === 'function' && !window.hideProgressPopup.__tiClose357) {\n        var prevHide357 = window.hideProgressPopup;\n        window.hideProgressPopup = function(success, finalText, opts){\n            var ret = prevHide357.apply(this, arguments);\n            if (success !== false) {\n                scheduleHardClose('hideProgressPopup-success', 650);\n                scheduleHardClose('hideProgressPopup-success-late', 1600);\n            }\n            return ret;\n        };\n        window.hideProgressPopup.__tiClose357 = true;\n    }\n    if (typeof window.updateProgressPopup === 'function' && !window.updateProgressPopup.__tiClose357) {\n        var prevUpdate357 = window.updateProgressPopup;\n        window.updateProgressPopup = function(percent, title, subtitle, currentItem){\n            var ret = prevUpdate357.apply(this, arguments);\n            if ((parseInt(percent,10) || 0) >= 100 && looksCompleted(title, subtitle)) {\n                scheduleHardClose('updateProgressPopup-100', 650);\n                scheduleHardClose('updateProgressPopup-100-late', 1600);\n            }\n            return ret;\n        };\n        window.updateProgressPopup.__tiClose357 = true;\n    }\n\n    \/\/ Garanzia non invasiva: se la risposta help viene aggiornata da vecchi handler, ripulisci solo # e * e mantieni il pulsante copia.\n    function sanitizeHelpNow(){\n        try {\n            var title = byId('col-help-title');\n            var box = byId('col-help-content');\n            if (title && \/[#*]\/.test(title.innerText || title.textContent || '')) title.innerText = String(title.innerText || title.textContent || '').replace(\/[#*]+\/g,'').trim();\n            if (box && \/[#*]\/.test(box.innerHTML || '')) {\n                box.innerHTML = String(box.innerHTML || '').replace(\/[#*]+\/g,'').replace(\/(<br\\s*\\\/?>(\\s*)){3,}\/gi,'<br><br>');\n            }\n        } catch(e) {}\n    }\n    if (typeof MutationObserver !== 'undefined') {\n        var box = byId('col-help-content');\n        if (box) {\n            try {\n                var busy = false;\n                var mo = new MutationObserver(function(){ if (busy) return; busy = true; setTimeout(function(){ sanitizeHelpNow(); busy = false; }, 20); });\n                mo.observe(box, {childList:true, subtree:true});\n            } catch(e) {}\n        }\n    }\n    setTimeout(sanitizeHelpNow, 300);\n})();\n<\/script>\n\n<!-- ti-ai-file-manager-stability-358 disattivato in 30.9.664: sostituito da ti-ai-media-safe-fix-455 per evitare blocchi UI. -->\n\n<!-- ti-file-manager-genera-ai-click-360 disattivato in 30.9.664: sostituito da ti-ai-media-safe-fix-455 per evitare blocchi UI. -->\n\n\n\n<script id=\"ti-plugin-audio-mic-internal-363\">\n(function(){\n    \"use strict\";\n    if (window.__tiPluginAudioMic363Installed) return;\n    window.__tiPluginAudioMic363Installed = true;\n\n    var audioCtx = null;\n    var audioUnlocked = false;\n    var speechPrimed = false;\n    var lastUnlockError = '';\n    var micRecognition = null;\n    var micStoppingTimer = null;\n\n    function byId(id){ return document.getElementById(id); }\n    function root(){ return byId('ti-ai-outer') || document.querySelector('.ti-ai-shop-service,[data-ti-shop-service]'); }\n    function isMuted(){ return !!(window.isMuted || window.muteState === 1 || window.muteState === 2); }\n    function safeAlert(msg){ try { if (window.tiAlert) window.tiAlert(msg); else alert(msg); } catch(e) { try { alert(msg); } catch(ignore) {} } }\n    function setMicUi(active){\n        var btn = byId('ti-mic');\n        if (!btn) return;\n        try {\n            btn.style.background = active ? '#b91c1c' : '#222';\n            btn.style.color = active ? '#fff' : '#ccc';\n            btn.textContent = active ? 'Mic...' : ((window.currLang === 'en') ? 'Mic' : 'Mic');\n            btn.disabled = false;\n        } catch(e) {}\n    }\n    function secureContextOk(){\n        try { return window.isSecureContext || location.protocol === 'https:' || location.hostname === 'localhost' || location.hostname === '127.0.0.1'; } catch(e) { return false; }\n    }\n    function unlockAudioInternal(){\n        var ok = false;\n        lastUnlockError = '';\n        try {\n            var AC = window.AudioContext || window.webkitAudioContext;\n            if (AC) {\n                audioCtx = audioCtx || new AC();\n                if (audioCtx.state === 'suspended' && audioCtx.resume) {\n                    var r = audioCtx.resume();\n                    if (r && typeof r.catch === 'function') r.catch(function(e){ lastUnlockError = e && e.message ? e.message : String(e || ''); });\n                }\n                try {\n                    var osc = audioCtx.createOscillator();\n                    var gain = audioCtx.createGain();\n                    gain.gain.value = 0.00001;\n                    osc.connect(gain);\n                    gain.connect(audioCtx.destination);\n                    osc.start(0);\n                    osc.stop(audioCtx.currentTime + 0.04);\n                } catch(ignore) {}\n                ok = true;\n            }\n        } catch(e) {\n            lastUnlockError = e && e.message ? e.message : String(e || 'AudioContext non disponibile');\n        }\n        try {\n            if ('speechSynthesis' in window) {\n                window.speechSynthesis.resume();\n                if (!speechPrimed) {\n                    var u = new SpeechSynthesisUtterance(' ');\n                    u.volume = 0;\n                    u.rate = 1;\n                    u.pitch = 1;\n                    u.lang = (window.currLang === 'en') ? 'en-US' : 'it-IT';\n                    window.speechSynthesis.speak(u);\n                    speechPrimed = true;\n                }\n                ok = true;\n            }\n        } catch(e) {\n            lastUnlockError = e && e.message ? e.message : String(e || 'speechSynthesis non disponibile');\n        }\n        try {\n            var r = root();\n            (r ? r.querySelectorAll('audio,video') : document.querySelectorAll('#ti-ai-outer audio,#ti-ai-outer video')).forEach(function(el){\n                try { el.muted = false; el.volume = 1; } catch(ignore) {}\n            });\n        } catch(e) {}\n        if (ok) audioUnlocked = true;\n        window.tiPluginAudioUnlocked363 = audioUnlocked;\n        return ok;\n    }\n    window.tiEnsurePluginAudioUnlocked = function(){ return unlockAudioInternal(); };\n    window.tiPluginAudioStatus = function(){\n        return {\n            audioUnlocked: audioUnlocked,\n            audioContextState: audioCtx ? audioCtx.state : 'not-created',\n            speechSynthesis: ('speechSynthesis' in window),\n            speechPrimed: speechPrimed,\n            muted: isMuted(),\n            secureContext: secureContextOk(),\n            mediaDevices: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),\n            speechRecognition: !!(window.SpeechRecognition || window.webkitSpeechRecognition),\n            rootFound: !!root(),\n            lastUnlockError: lastUnlockError\n        };\n    };\n\n    var oldPrime = window.primeSpeech;\n    window.primeSpeech = function(){\n        if (audioUnlocked && speechPrimed) return true;\n        return unlockAudioInternal() || (typeof oldPrime === 'function' ? oldPrime.apply(this, arguments) : false);\n    };\n\n    if (typeof window.speakText === 'function' && !window.speakText.__tiAudio363) {\n        var oldSpeakText = window.speakText;\n        window.speakText = function(text){\n            if (!isMuted()) unlockAudioInternal();\n            return oldSpeakText.apply(this, arguments);\n        };\n        window.speakText.__tiAudio363 = true;\n    }\n\n    function startMic(){\n        unlockAudioInternal();\n        var btn = byId('ti-mic');\n        var msg = byId('ti-msg');\n        var send = byId('ti-send');\n        if (!secureContextOk()) {\n            safeAlert('Microfono non disponibile. Apri la pagina in HTTPS e autorizza il microfono per questo sito.');\n            return false;\n        }\n        if (!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) {\n            safeAlert('Microfono non disponibile nel browser. Verifica permessi sito oppure usa Chrome, Edge o Safari aggiornato.');\n            return false;\n        }\n        var SR = window.SpeechRecognition || window.webkitSpeechRecognition;\n        if (!SR) {\n            safeAlert('Riconoscimento vocale non supportato dal browser. Il microfono esiste, ma questo browser non supporta SpeechRecognition. Usa Chrome o Edge quando possibile.');\n            return false;\n        }\n        if (micRecognition) {\n            try { micRecognition.stop(); } catch(e) {}\n            micRecognition = null;\n            setMicUi(false);\n            return false;\n        }\n        setMicUi(true);\n        navigator.mediaDevices.getUserMedia({audio:true}).then(function(stream){\n            try { stream.getTracks().forEach(function(t){ t.stop(); }); } catch(e) {}\n            var r = new SR();\n            micRecognition = r;\n            r.lang = (window.currLang === 'en') ? 'en-US' : 'it-IT';\n            r.continuous = false;\n            r.interimResults = false;\n            r.maxAlternatives = 1;\n            r.onresult = function(e){\n                try {\n                    var transcript = e && e.results && e.results[0] && e.results[0][0] ? e.results[0][0].transcript : '';\n                    if (transcript && msg) {\n                        msg.value = transcript;\n                        msg.dispatchEvent(new Event('input', {bubbles:true}));\n                        msg.dispatchEvent(new Event('change', {bubbles:true}));\n                    }\n                    if (transcript && send) setTimeout(function(){ try { send.click(); } catch(ignore) {} }, 80);\n                } catch(err) {}\n            };\n            r.onerror = function(e){\n                var code = e && e.error ? e.error : 'errore';\n                var detail = 'Microfono non disponibile o non autorizzato.';\n                if (code === 'not-allowed' || code === 'service-not-allowed') detail = 'Microfono non autorizzato. Abilita il permesso microfono nelle impostazioni del browser per questo sito.';\n                else if (code === 'no-speech') detail = 'Non ho rilevato voce. Riprova parlando dopo aver premuto Mic.';\n                else if (code === 'audio-capture') detail = 'Nessun microfono rilevato dal browser.';\n                safeAlert(detail + ' Codice: ' + code);\n            };\n            r.onend = function(){\n                micRecognition = null;\n                if (micStoppingTimer) { clearTimeout(micStoppingTimer); micStoppingTimer = null; }\n                setMicUi(false);\n            };\n            try { r.start(); } catch(e) { micRecognition = null; setMicUi(false); safeAlert('Impossibile avviare il microfono: ' + (e && e.message ? e.message : 'errore browser')); return; }\n            micStoppingTimer = setTimeout(function(){\n                try { if (micRecognition) micRecognition.stop(); } catch(e) {}\n                setMicUi(false);\n            }, 10000);\n        }).catch(function(e){\n            setMicUi(false);\n            var msgErr = e && e.name ? e.name : '';\n            safeAlert('Microfono non autorizzato o non disponibile. Verifica i permessi del browser per questo sito. ' + (msgErr ? 'Dettaglio: ' + msgErr : ''));\n        });\n        return false;\n    }\n    window.tiStartPluginMic = startMic;\n\n    function installMicButton(){\n        var btn = byId('ti-mic');\n        if (!btn || btn.__tiMic363) return;\n        btn.__tiMic363 = true;\n        btn.onclick = function(ev){\n            if (ev) { try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {} }\n            return startMic();\n        };\n    }\n    function installGestureUnlock(){\n        var r = root();\n        if (!r || r.__tiAudioGesture363) return;\n        r.__tiAudioGesture363 = true;\n        ['pointerdown','touchstart','mousedown','click','keydown'].forEach(function(evt){\n            r.addEventListener(evt, function(){ unlockAudioInternal(); }, {capture:true, passive:true});\n        });\n    }\n    function installAll(){ installGestureUnlock(); installMicButton(); }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', installAll);\n    installAll();\n    setTimeout(installAll, 300);\n    setTimeout(installAll, 1200);\n})();\n<\/script>\n\n\n\n<script id=\"ti-ateco-config-field-restore-364\">\n(function(){\n    \"use strict\";\n    if (window.__tiAtecoConfigFieldRestore364Installed) return;\n    window.__tiAtecoConfigFieldRestore364Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function norm(s){\n        try { s = String(s || '').normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,''); } catch(e) { s = String(s || ''); }\n        return s.toLowerCase().replace(\/[^a-z0-9]+\/g,'');\n    }\n    function isAtecoFieldName(key){\n        var n = norm(key);\n        return n === 'attivita' || n === 'attivitaditta' || n === 'codiceateco' || n === 'codateco' || n.indexOf('ateco') !== -1;\n    }\n    function visible(el){\n        if (!el) return false;\n        try {\n            var st = window.getComputedStyle ? getComputedStyle(el) : null;\n            if (st && (st.display === 'none' || st.visibility === 'hidden' || st.opacity === '0')) return false;\n            return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n        } catch(e) { return false; }\n    }\n    function css(el, prop, val){ try { if (el) el.style.setProperty(prop, val, 'important'); } catch(e) {} }\n    function ensureId(el){\n        if (!el) return '';\n        if (el.id) return el.id;\n        var tbl = el.getAttribute('data-tbl') || 'Config';\n        var idx = el.getAttribute('data-idx') || '0';\n        var key = el.getAttribute('data-key') || 'Attivita';\n        var id = 'ti-ateco-target-' + norm(tbl) + '-' + String(idx).replace(\/[^0-9a-z_-]\/gi,'') + '-' + norm(key) + '-' + Math.random().toString(36).slice(2,7);\n        el.id = id;\n        return id;\n    }\n    function frontAteco(){\n        var ov = byId('ti-ateco-ov');\n        if (!ov) return;\n        try { if (window.preparePluginModal) window.preparePluginModal(ov); } catch(e) {}\n        css(ov, 'display', 'flex');\n        css(ov, 'visibility', 'visible');\n        css(ov, 'opacity', '1');\n        css(ov, 'pointer-events', 'auto');\n        css(ov, 'z-index', '2147483647');\n        try { ov.removeAttribute('aria-hidden'); ov.setAttribute('data-ti-config-child-modal','1'); ov.setAttribute('data-ti-force-front','1'); } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n        setTimeout(function(){\n            try {\n                var input = byId('ateco-in');\n                if (input) input.focus({preventScroll:true});\n            } catch(e) {}\n        }, 80);\n    }\n    function openForTarget(el){\n        if (!el) return false;\n        var id = ensureId(el);\n        window.__tiAtecoTargetElement364 = el;\n        window.__tiAtecoTargetId364 = id;\n        try {\n            var h = byId('ateco-target-id');\n            if (h) h.value = id;\n        } catch(e) {}\n        try {\n            if (typeof window.__tiNativeOpenAtecoSearch364 === 'function') {\n                window.__tiNativeOpenAtecoSearch364(id);\n            } else if (typeof window.openAtecoSearch === 'function') {\n                window.openAtecoSearch(id);\n            }\n        } catch(e) {\n            try { if (window.tiAlert) window.tiAlert('Ricerca ATECO non disponibile: ' + (e && e.message ? e.message : 'errore')); } catch(ignore) {}\n        }\n        try {\n            var h2 = byId('ateco-target-id');\n            if (h2) h2.value = id;\n            var q = byId('ateco-in');\n            if (q && !q.value && el.value) q.value = String(el.value || '').trim();\n            var res = byId('ateco-res');\n            if (res && !String(res.innerHTML || '').trim()) res.innerHTML = '<i>Scrivi l\\'attivit\u00e0 da cercare...<\/i>';\n        } catch(e) {}\n        frontAteco();\n        return false;\n    }\n    function findFieldFromButton(btn){\n        if (!btn) return null;\n        var wrap = btn.parentElement || btn.closest('td');\n        var field = wrap ? wrap.querySelector('input[data-key], textarea[data-key], select[data-key]') : null;\n        if (field) return field;\n        var oc = String(btn.getAttribute('onclick') || '');\n        var m = oc.match(\/openAtecoSearch\\((['\"])(.*?)\\1\\)\/);\n        if (m && m[2]) return byId(m[2]);\n        var targetId = btn.getAttribute('data-ti-ateco-target');\n        return targetId ? byId(targetId) : null;\n    }\n    function addButtonForField(el){\n        if (!el || !isAtecoFieldName(el.getAttribute('data-key') || '')) return;\n        var parent = el.parentElement;\n        if (!parent) return;\n        if (parent.querySelector('[data-ti-ateco-btn=\"364\"], button[onclick*=\"openAtecoSearch\"]')) return;\n        var btn = document.createElement('button');\n        btn.type = 'button';\n        btn.className = 'ti-btn ti-ateco-restore-btn';\n        btn.setAttribute('data-ti-ateco-btn', '364');\n        btn.setAttribute('data-ti-ateco-target', ensureId(el));\n        btn.title = 'Ricerca ATECO';\n        btn.textContent = '\ud83d\udd0d';\n        btn.style.cssText = 'padding:4px 6px;font-size:10px;background:#2563eb;color:#fff;white-space:nowrap;';\n        parent.appendChild(btn);\n    }\n    function ensureAtecoButtons(){\n        try {\n            var scope = byId('ti-conf-ov') || document;\n            var fields = scope.querySelectorAll('input[data-key], textarea[data-key], select[data-key]');\n            Array.prototype.forEach.call(fields, addButtonForField);\n        } catch(e) {}\n    }\n\n    if (typeof window.openAtecoSearch === 'function' && !window.openAtecoSearch.__tiAteco364) {\n        window.__tiNativeOpenAtecoSearch364 = window.openAtecoSearch;\n        window.openAtecoSearch = function(targetId){\n            var el = targetId ? byId(targetId) : null;\n            if (el) {\n                window.__tiAtecoTargetElement364 = el;\n                window.__tiAtecoTargetId364 = targetId;\n            }\n            var ret = window.__tiNativeOpenAtecoSearch364.apply(this, arguments);\n            try { var h = byId('ateco-target-id'); if (h && targetId) h.value = targetId; } catch(e) {}\n            frontAteco();\n            return ret;\n        };\n        window.openAtecoSearch.__tiAteco364 = true;\n    }\n    if (typeof window.selectAteco === 'function' && !window.selectAteco.__tiAteco364) {\n        var prevSelect364 = window.selectAteco;\n        window.selectAteco = function(codice, desc){\n            try {\n                var h = byId('ateco-target-id');\n                var id = (h && h.value) || window.__tiAtecoTargetId364 || '';\n                var el = id ? byId(id) : null;\n                if (!el && window.__tiAtecoTargetElement364) el = window.__tiAtecoTargetElement364;\n                if (el) {\n                    el.value = desc || codice || '';\n                    try { el.dispatchEvent(new Event('input', {bubbles:true})); } catch(e) {}\n                    try { el.dispatchEvent(new Event('change', {bubbles:true})); } catch(e) {}\n                }\n            } catch(e) {}\n            return prevSelect364.apply(this, arguments);\n        };\n        window.selectAteco.__tiAteco364 = true;\n    }\n    document.addEventListener('click', function(ev){\n        var btn = ev.target && ev.target.closest ? ev.target.closest('[data-ti-ateco-btn=\"364\"], button[onclick*=\"openAtecoSearch\"]') : null;\n        if (!btn) return;\n        var field = findFieldFromButton(btn);\n        if (!field) return;\n        if (!isAtecoFieldName(field.getAttribute('data-key') || '')) return;\n        ev.preventDefault();\n        ev.stopPropagation();\n        openForTarget(field);\n        return false;\n    }, true);\n\n    if (typeof window.renderConfig === 'function' && !window.renderConfig.__tiAteco364) {\n        var prevRender364 = window.renderConfig;\n        window.renderConfig = function(){\n            var ret = prevRender364.apply(this, arguments);\n            setTimeout(ensureAtecoButtons, 80);\n            setTimeout(ensureAtecoButtons, 350);\n            return ret;\n        };\n        window.renderConfig.__tiAteco364 = true;\n    }\n    if (typeof MutationObserver !== 'undefined') {\n        try {\n            var cfg = byId('ti-conf-ov');\n            if (cfg) {\n                var mo = new MutationObserver(function(){ setTimeout(ensureAtecoButtons, 60); });\n                mo.observe(cfg, {childList:true, subtree:true});\n            }\n        } catch(e) {}\n    }\n    setTimeout(ensureAtecoButtons, 400);\n})();\n<\/script>\n\n\n<script id=\"ti-global-template-quick-button-368\">\n(function(){\n    \"use strict\";\n    if (window.__tiGlobalTemplateQuickButton368Installed) return;\n    window.__tiGlobalTemplateQuickButton368Installed = true;\n\n    var CMD = \"Adegua struttura database a template shopservicemain.json\";\n\n    function byId(id){ return document.getElementById(id); }\n\n    function fillGlobalTemplateCommand(){\n        var prompt = byId('global-action-prompt');\n        if (!prompt) return false;\n        prompt.value = CMD;\n        try { prompt.dispatchEvent(new Event('input', {bubbles:true})); } catch(e) {}\n        try { prompt.dispatchEvent(new Event('change', {bubbles:true})); } catch(e) {}\n        try { prompt.focus({preventScroll:true}); } catch(e) { try { prompt.focus(); } catch(ignore) {} }\n        return false;\n    }\n\n    window.tiFillGlobalTemplateCommand368 = fillGlobalTemplateCommand;\n\n    function ensureButton(){\n        var prompt = byId('global-action-prompt');\n        if (!prompt) return;\n\n        var btn = byId('global-action-template-btn');\n        if (!btn) {\n            btn = document.createElement('button');\n            btn.type = 'button';\n            btn.id = 'global-action-template-btn';\n            btn.className = 'ti-btn';\n            btn.textContent = 'Template';\n            btn.title = 'Scrivi comando: ' + CMD;\n            btn.onclick = fillGlobalTemplateCommand;\n\n            var parent = prompt.parentNode;\n            if (parent) {\n                try {\n                    parent.style.display = 'flex';\n                    parent.style.gap = parent.style.gap || '6px';\n                    parent.style.alignItems = 'stretch';\n                } catch(e) {}\n                parent.appendChild(btn);\n            }\n        } else {\n            btn.onclick = fillGlobalTemplateCommand;\n            btn.textContent = 'Template';\n            btn.title = 'Scrivi comando: ' + CMD;\n        }\n\n        try {\n            btn.style.setProperty('display', 'inline-flex', 'important');\n            btn.style.setProperty('align-items', 'center', 'important');\n            btn.style.setProperty('justify-content', 'center', 'important');\n            btn.style.setProperty('width', '58px', 'important');\n            btn.style.setProperty('min-width', '58px', 'important');\n            btn.style.setProperty('padding', '6px 4px', 'important');\n            btn.style.setProperty('background', '#0f766e', 'important');\n            btn.style.setProperty('color', '#fff', 'important');\n            btn.style.setProperty('font-size', '10px', 'important');\n            btn.style.setProperty('line-height', '1.1', 'important');\n            btn.style.setProperty('font-weight', '800', 'important');\n            btn.style.setProperty('white-space', 'normal', 'important');\n            btn.style.setProperty('cursor', 'pointer', 'important');\n            prompt.style.setProperty('flex', '1 1 auto', 'important');\n            prompt.style.setProperty('min-width', '0', 'important');\n        } catch(e) {}\n    }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ensureButton);\n    else ensureButton();\n\n    if (typeof window.openModal === 'function' && !window.openModal.__tiGlobalTpl368) {\n        var prevOpen = window.openModal;\n        window.openModal = function(id){\n            var ret = prevOpen.apply(this, arguments);\n            if (id === 'ti-global-action-ov') setTimeout(ensureButton, 40);\n            return ret;\n        };\n        window.openModal.__tiGlobalTpl368 = true;\n    }\n\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t) return;\n        if (t.id === 'global-action-template-btn' || (t.closest && t.closest('#global-action-template-btn'))) {\n            ev.preventDefault();\n            ev.stopPropagation();\n            fillGlobalTemplateCommand();\n        }\n    }, true);\n\n    setTimeout(ensureButton, 300);\n})();\n<\/script>\n\n\n<script id=\"ti-contact-num-main-access-375\">\n(function(){\n    \"use strict\";\n    if (window.__tiContactNumMainAccess375Installed) return;\n    window.__tiContactNumMainAccess375Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function selectedDb(){\n        try {\n            var d = byId('ti-ditta');\n            var v = d && d.value ? String(d.value).trim() : '';\n            if (!v || v === 'NEW_DB') {\n                try { v = String(window.tiForced || localStorage.getItem('ti_saved_db') || '').trim(); } catch(e) {}\n            }\n            return (!v || v === 'NEW_DB') ? '' : v;\n        } catch(e) { return ''; }\n    }\n    function fmt(n){\n        n = parseInt(n, 10) || 0;\n        try { return n.toLocaleString('it-IT'); } catch(e) { return String(n).replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, '.'); }\n    }\n    function escHtml(s){ return String(s == null ? '' : s).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c] || c; }); }\n    function clientContactId584(){\n        var k = 'ti_contact_client_id_584';\n        var id = '';\n        try { id = sessionStorage.getItem(k) || ''; } catch(e) {}\n        if (!id) {\n            id = 'cc_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 12);\n            try { sessionStorage.setItem(k, id); } catch(e) {}\n        }\n        return id;\n    }\n    function isPrivilegedClient(){\n        var role = String(window.tiRole || '').toLowerCase();\n        var user = String(window.tiUser || '').toLowerCase();\n        return user === 'ssglobaladmin' || user === 'targetai' || \/amministratore|administrator|configuratore|configurator|responsabile|responsible|operatore|operator|globaladmin|sysadmin\/.test(role);\n    }\n    function getConfigRow(){\n        try {\n            var data = window.currentDbData || {};\n            var t = data && data.Tabelle ? data.Tabelle : null;\n            if (!t) return null;\n            var cfgName = Object.keys(t).find(function(k){ return String(k).toLowerCase() === 'config'; }) || 'Config';\n            var rows = t[cfgName];\n            if (Array.isArray(rows)) return rows[0] || null;\n            if (rows && typeof rows === 'object') return Object.values(rows)[0] || null;\n        } catch(e) {}\n        return null;\n    }\n    function setLocalContactsNum(n, field){\n        n = parseInt(n, 10) || 0;\n        try {\n            if (!window.currentDbData) window.currentDbData = {};\n            window.currentDbData.__contacts_num = n;\n            var row = getConfigRow();\n            if (row) {\n                var k = Object.keys(row).find(function(x){\n                    var s = String(x || '').toLowerCase().replace(\/[^a-z0-9]\/g,'');\n                    return s === 'contactsnum' || s === 'contactnum';\n                }) || field || 'Contacts_num';\n                row[k] = n;\n            }\n        } catch(e) {}\n        return n;\n    }\n    function currentContactsNum(){\n        try {\n            if (window.currentDbData && typeof window.currentDbData.__contacts_num !== 'undefined') return parseInt(window.currentDbData.__contacts_num, 10) || 0;\n            var row = getConfigRow();\n            if (!row) return 0;\n            var k = Object.keys(row).find(function(x){\n                var s = String(x || '').toLowerCase().replace(\/[^a-z0-9]\/g,'');\n                return s === 'contactsnum' || s === 'contactnum';\n            });\n            return k ? (parseInt(String(row[k] || '0').replace(\/[^0-9\\-]\/g,''), 10) || 0) : 0;\n        } catch(e) { return 0; }\n    }\n    function addAdminInfoOnce(db, n){\n        if (!isPrivilegedClient()) return;\n        window.__tiContactInfoShown375 = window.__tiContactInfoShown375 || {};\n        var key = String(db || '');\n        if (window.__tiContactInfoShown375[key]) return;\n        window.__tiContactInfoShown375[key] = true;\n        var plain = 'Totale contatti : ' + fmt(n || 0);\n        var html = '<a href=\"#\" class=\"ti-contact-log-link\" data-ti-db=\"' + escHtml(db || selectedDb() || '') + '\" style=\"color:#93c5fd;text-decoration:underline;font-weight:700;\">Totale contatti<\/a> : <b>' + fmt(n || 0) + '<\/b>';\n        try {\n            if (window.tiAddAiMessageTranslatedIfNeeded) window.tiAddAiMessageTranslatedIfNeeded(html, plain, db || '');\n            else if (window.addMsg) window.addMsg('ai', html, plain);\n        } catch(e) {}\n    }\n    function updateReportTitle(n){\n        try {\n            var title = byId('ti-report-title');\n            if (!title) return;\n            if (typeof n === 'undefined' || n === null) n = currentContactsNum();\n            var txt = String(title.innerText || title.textContent || '').replace(\/\\s*[\\-\u2013\u2014]?\\s*(?:Contatti(?: main form)?|Totale contatti)\\s*:\\s*[0-9.]+\\s*$\/i, '').trim();\n            title.innerHTML = escHtml(txt) + ' - <a href=\"#\" class=\"ti-contact-log-link\" data-ti-db=\"' + escHtml(selectedDb() || '') + '\" style=\"color:#93c5fd;text-decoration:underline;font-weight:700;\">Totale contatti<\/a> : <b>' + fmt(n || 0) + '<\/b>';\n        } catch(e) {}\n    }\n    function requestContacts(db, mode, opts){\n        opts = opts || {};\n        db = String(db || selectedDb() || '').trim();\n        if (!db || db === 'NEW_DB') return Promise.resolve({contacts_num: currentContactsNum(), skipped:true});\n        mode = mode || 'read';\n        try {\n            var fd = new FormData();\n            fd.append('action', 'ti_ai_track_contact_access');\n            fd.append('ti_action', 'ti_ai_track_contact_access');\n            fd.append('db', db);\n            fd.append('mode', mode);\n            fd.append('client_contact_id', clientContactId584());\n            var url = window.tiAjaxUrl || window.tiUrl || window.location.href;\n            var p = window.fetchJsonSafe\n                ? window.fetchJsonSafe(url, {method:'POST', body:fd, credentials:'same-origin', tiNoLongProcessSignal:true})\n                : fetch(url, {method:'POST', body:fd, credentials:'same-origin'}).then(function(r){ return r.json(); });\n            return p.then(function(res){\n                var payload = res && res.success ? (res.data || {}) : {};\n                var n = typeof payload.contacts_num !== 'undefined' ? (parseInt(payload.contacts_num, 10) || 0) : currentContactsNum();\n                setLocalContactsNum(n, payload.field || 'Contacts_num');\n                try {\n                    var flagKey = 'ti_contact_counted_before_role_login_' + String(db || '');\n                    if (payload.incremented) sessionStorage.setItem(flagKey, '1');\n                    if (payload.decremented) sessionStorage.removeItem(flagKey);\n                } catch(ignore) {}\n                if (opts.showChat) addAdminInfoOnce(db, n);\n                if (opts.updateReport) updateReportTitle(n);\n                return payload;\n            }).catch(function(){\n                if (opts.updateReport) updateReportTitle(currentContactsNum());\n                return {contacts_num: currentContactsNum(), error:true};\n            });\n        } catch(e) {\n            if (opts.updateReport) updateReportTitle(currentContactsNum());\n            return Promise.resolve({contacts_num: currentContactsNum(), error:true});\n        }\n    }\n    function trackMainAccess(db){\n        db = String(db || selectedDb() || '').trim();\n        if (!db || db === 'NEW_DB') return;\n        window.__tiContactMainAccessDone375 = window.__tiContactMainAccessDone375 || {};\n        if (window.__tiContactMainAccessDone375[db]) {\n            requestContacts(db, 'read', {showChat:true, updateReport:true});\n            return;\n        }\n        window.__tiContactMainAccessDone375[db] = true;\n        requestContacts(db, 'access', {showChat:true, updateReport:true});\n    }\n    function compensateAfterRoleLogin(db){\n        db = String(db || selectedDb() || '').trim();\n        if (!db || db === 'NEW_DB') return Promise.resolve(false);\n        var flagKey = 'ti_contact_counted_before_role_login_' + db;\n        var hadCount = false;\n        try { hadCount = sessionStorage.getItem(flagKey) === '1'; } catch(e) {}\n        if (!hadCount) return Promise.resolve(false);\n        return requestContacts(db, 'role_login_compensate', {showChat:false, updateReport:true}).then(function(payload){\n            try { if (payload && payload.decremented) sessionStorage.removeItem(flagKey); } catch(e) {}\n            return !!(payload && payload.decremented);\n        });\n    }\n\n    function refreshForReport(){\n        requestContacts(selectedDb(), 'report', {showChat:false, updateReport:true});\n        setTimeout(function(){ requestContacts(selectedDb(), 'report', {showChat:false, updateReport:true}); }, 600);\n    }\n    function scheduleTrack(){\n        var tries = 0;\n        var tm = setInterval(function(){\n            tries++;\n            var db = selectedDb();\n            if (db) { clearInterval(tm); trackMainAccess(db); }\n            if (tries > 60) clearInterval(tm);\n        }, 300);\n    }\n    function install(){\n        if (typeof window.updCtx === 'function' && !window.updCtx.__tiContact375) {\n            var oldUpd = window.updCtx;\n            window.updCtx = function(){\n                var ret = oldUpd.apply(this, arguments);\n                setTimeout(function(){ trackMainAccess(selectedDb()); }, 700);\n                return ret;\n            };\n            window.updCtx.__tiContact375 = true;\n        }\n        if (typeof window.renderActivityReport === 'function' && !window.renderActivityReport.__tiContact375) {\n            var oldRender = window.renderActivityReport;\n            window.renderActivityReport = function(){ var ret = oldRender.apply(this, arguments); updateReportTitle(); return ret; };\n            window.renderActivityReport.__tiContact375 = true;\n        }\n        if (typeof window.openActivityReport === 'function' && !window.openActivityReport.__tiContact375) {\n            var oldOpen = window.openActivityReport;\n            window.openActivityReport = function(){\n                var ret = oldOpen.apply(this, arguments);\n                refreshForReport();\n                return ret;\n            };\n            window.openActivityReport.__tiContact375 = true;\n        }\n        try {\n            var d = byId('ti-ditta');\n            if (d && !d.__tiContact375Change) {\n                d.__tiContact375Change = true;\n                d.addEventListener('change', function(){\n                    var db = selectedDb();\n                    if (db) {\n                        window.__tiContactInfoShown375 = {};\n                        setTimeout(function(){ trackMainAccess(db); }, 900);\n                    }\n                }, false);\n            }\n        } catch(e) {}\n    }\n    document.addEventListener('DOMContentLoaded', function(){ install(); scheduleTrack(); }, false);\n    setTimeout(function(){ install(); scheduleTrack(); }, 800);\n    window.tiTrackCompanyContactAccess375 = trackMainAccess;\n    window.tiReadCompanyContactAccess375 = function(db){ return requestContacts(db || selectedDb(), 'read', {showChat:false, updateReport:true}); };\n    window.tiForceCompanyContactsNum375 = function(n, field){\n        n = setLocalContactsNum(n, field || 'Contacts_num');\n        updateReportTitle(n);\n        return n;\n    };\n    window.tiCompensateContactAfterRoleLogin377 = compensateAfterRoleLogin;\n    window.tiUpdateActivityReportContactsTitle375 = function(){ refreshForReport(); };\n    function touchContactOnExit584(){\n        try {\n            var db = selectedDb();\n            if (!db || isPrivilegedClient()) return;\n            var fd = new FormData();\n            fd.append('action', 'ti_ai_track_contact_access');\n            fd.append('ti_action', 'ti_ai_track_contact_access');\n            fd.append('db', db);\n            fd.append('mode', 'contact_touch');\n            fd.append('client_contact_id', clientContactId584());\n            var url = window.tiAjaxUrl || window.tiUrl || window.location.href;\n            if (navigator.sendBeacon) navigator.sendBeacon(url, fd);\n            else fetch(url, {method:'POST', body:fd, credentials:'same-origin', keepalive:true});\n        } catch(e) {}\n    }\n    if (!window.__tiContactTouchExit584Installed) {\n        window.__tiContactTouchExit584Installed = true;\n        window.addEventListener('pagehide', touchContactOnExit584, false);\n        window.addEventListener('beforeunload', touchContactOnExit584, false);\n    }\n    try {\n        if (!window.__tiContactLoginCompensate377Installed) {\n            window.__tiContactLoginCompensate377Installed = true;\n            var prevReload = window.tiReloadAfterSuccessfulLogin;\n            window.tiReloadAfterSuccessfulLogin = function(){\n                var db = selectedDb();\n                var done = false;\n                function go(){ if (done) return; done = true; if (typeof prevReload === 'function') prevReload(); else window.location.reload(); }\n                try {\n                    compensateAfterRoleLogin(db).then(go).catch(go);\n                    setTimeout(go, 900);\n                } catch(e) { go(); }\n            };\n        }\n    } catch(e) {}\n})();\n<\/script>\n\n<script id=\"ti-contact-log-popup-584\">\n(function(){\n    \"use strict\";\n    if (window.__tiContactLogPopup584Installed) return;\n    window.__tiContactLogPopup584Installed = true;\n\n    function esc(s){ return String(s == null ? '' : s).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c] || c; }); }\n    function fmt(n){\n        n = parseInt(n, 10) || 0;\n        try { return n.toLocaleString('it-IT'); } catch(e) { return String(n).replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, '.'); }\n    }\n    function selectedDb(){\n        try {\n            var d = document.getElementById('ti-ditta');\n            var v = d && d.value ? String(d.value).trim() : '';\n            if (!v || v === 'NEW_DB') v = String(window.tiForced || localStorage.getItem('ti_saved_db') || '').trim();\n            return (!v || v === 'NEW_DB') ? '' : v;\n        } catch(e) { return ''; }\n    }\n    function ajaxLog(db, mode){\n        var fd = new FormData();\n        fd.append('action', 'ti_ai_contact_log');\n        fd.append('ti_action', 'ti_ai_contact_log');\n        fd.append('db', db || selectedDb());\n        fd.append('mode', mode || 'read');\n        var url = window.tiAjaxUrl || window.tiUrl || window.location.href;\n        var p = window.fetchJsonSafe\n            ? window.fetchJsonSafe(url, {method:'POST', body:fd, credentials:'same-origin', tiNoLongProcessSignal:true})\n            : fetch(url, {method:'POST', body:fd, credentials:'same-origin'}).then(function(r){ return r.json(); });\n        return p.then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && (res.data.message || res.data.error)) || 'Errore log contatti');\n            return res.data || {};\n        });\n    }\n    function plainRows(rows, total){\n        rows = Array.isArray(rows) ? rows : [];\n        var out = ['Log contatti', 'Totale contatti: ' + fmt(total || rows.length), 'Righe dettaglio log: ' + fmt(rows.length), 'Data e ora\\tIP\\tUsername\\tDurata hh.mm'];\n        rows.forEach(function(r){ out.push([r.datetime || '', r.ip || '', r.username || '', r.duration || '00.00'].join('\\t')); });\n        \/\/ 30.9.856: copia testo senza frasi descrittive quando non ci sono righe.\n        return out.join('\\n');\n    }\n    function copyText(text, btn){\n        text = String(text || '');\n        if (window.tiAiSafeCopyText459) return window.tiAiSafeCopyText459(text, btn || null);\n        function done(ok){ if (btn) { var old = btn.getAttribute('data-old') || btn.textContent || 'Copia testo'; btn.setAttribute('data-old', old); btn.textContent = ok ? 'Copiato' : 'Copia manuale'; setTimeout(function(){ btn.textContent = old; }, 1200); } }\n        try { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){ done(false); }); return true; } } catch(e) {}\n        try { var ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.select(); var ok=document.execCommand('copy'); ta.remove(); done(ok); return ok; } catch(e) { done(false); }\n        return false;\n    }\n    function ensureModal(){\n        var old = document.getElementById('ti-contact-log-modal-584');\n        if (old) return old;\n        var div = document.createElement('div');\n        div.id = 'ti-contact-log-modal-584';\n        div.style.cssText = 'position:fixed;inset:0;z-index:1000002;background:rgba(15,23,42,.74);display:none;align-items:center;justify-content:center;padding:14px;box-sizing:border-box;';\n        div.innerHTML = '<div style=\"width:min(860px,96vw);max-height:88vh;overflow:auto;background:#111827;color:#e5e7eb;border:1px solid #334155;border-radius:14px;box-shadow:0 18px 50px rgba(0,0,0,.45);padding:14px;font-family:Arial,sans-serif;\">'\n            + '<div style=\"display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;\">'\n            + '<div style=\"font-size:16px;font-weight:700;\">Log contatti<\/div>'\n            + '<button type=\"button\" data-ti-contact-close=\"1\" data-ti-no-global-exit=\"1\" style=\"background:#374151;color:#fff;border:0;border-radius:8px;padding:7px 10px;cursor:pointer;\">Chiudi lista<\/button>'\n            + '<\/div>'\n            + '<div id=\"ti-contact-log-status-584\" aria-hidden=\"true\" style=\"display:none!important;\"><\/div>'\n            + '<div style=\"overflow:auto;border:1px solid #334155;border-radius:10px;\">'\n            + '<table style=\"width:100%;border-collapse:collapse;font-size:12px;min-width:560px;\">'\n            + '<thead><tr style=\"background:#1f2937;\"><th style=\"text-align:left;padding:7px;border-bottom:1px solid #334155;\">Data e ora<\/th><th style=\"text-align:left;padding:7px;border-bottom:1px solid #334155;\">IP<\/th><th style=\"text-align:left;padding:7px;border-bottom:1px solid #334155;\">Username<\/th><th style=\"text-align:left;padding:7px;border-bottom:1px solid #334155;\">Durata hh.mm<\/th><\/tr><\/thead>'\n            + '<tbody id=\"ti-contact-log-body-584\"><\/tbody>'\n            + '<\/table><\/div>'\n            + '<div style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;justify-content:flex-end;\">'\n            + '<button type=\"button\" id=\"ti-contact-log-copy-584\" style=\"background:#2563eb;color:#fff;border:0;border-radius:8px;padding:8px 10px;cursor:pointer;font-size:12px;\">Copia testo<\/button>'\n            + '<button type=\"button\" id=\"ti-contact-log-clear-584\" style=\"background:#b91c1c;color:#fff;border:0;border-radius:8px;padding:8px 10px;cursor:pointer;font-size:12px;\">Cancella lista \/ Azzera totale<\/button>'\n            + '<button type=\"button\" data-ti-contact-close=\"1\" data-ti-no-global-exit=\"1\" style=\"background:#374151;color:#fff;border:0;border-radius:8px;padding:8px 10px;cursor:pointer;font-size:12px;\">Chiudi lista<\/button>'\n            + '<\/div><\/div>';\n        document.body.appendChild(div);\n        div.addEventListener('click', function(ev){\n            if (ev.target === div || (ev.target && ev.target.getAttribute && ev.target.getAttribute('data-ti-contact-close') === '1')) {\n                try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n                div.style.display = 'none';\n                try {\n                    document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked','ti-no-scroll');\n                    document.documentElement.classList.remove('ti-no-scroll');\n                } catch(e) {}\n                return false;\n            }\n        }, true);\n        return div;\n    }\n    function renderRows(rows, total){\n        var body = document.getElementById('ti-contact-log-body-584');\n        if (!body) return;\n        rows = Array.isArray(rows) ? rows : [];\n        total = parseInt(total, 10) || 0;\n        if (!rows.length) {\n            \/\/ 30.9.856: nessun commento\/riga informativa sotto la barra del popup.\n            body.innerHTML = '';\n            return;\n        }\n        body.innerHTML = rows.map(function(r){\n            return '<tr>'\n                + '<td style=\"padding:7px;border-top:1px solid #334155;\">' + esc(r.datetime || '') + '<\/td>'\n                + '<td style=\"padding:7px;border-top:1px solid #334155;\">' + esc(r.ip || '') + '<\/td>'\n                + '<td style=\"padding:7px;border-top:1px solid #334155;\">' + esc(r.username || '') + '<\/td>'\n                + '<td style=\"padding:7px;border-top:1px solid #334155;\">' + esc(r.duration || '00.00') + '<\/td>'\n                + '<\/tr>';\n        }).join('');\n    }\n    function openContactLog(db){\n        db = String(db || selectedDb() || '').trim();\n        if (!db) { try { if (window.tiAlert) window.tiAlert('Ditta non selezionata.'); else alert('Ditta non selezionata.'); } catch(e) {} return; }\n        var modal = ensureModal();\n        var status = document.getElementById('ti-contact-log-status-584');\n        var copyBtn = document.getElementById('ti-contact-log-copy-584');\n        var clearBtn = document.getElementById('ti-contact-log-clear-584');\n        var currentRows = [];\n        var currentTotal = 0;\n        modal.style.display = 'flex';\n        if (status) { status.textContent = ''; status.style.display = 'none'; status.setAttribute('aria-hidden','true'); }\n        renderRows([], 0);\n        function refresh(){\n            return ajaxLog(db, 'read').then(function(data){\n                currentRows = Array.isArray(data.rows) ? data.rows : [];\n                currentTotal = parseInt(data.total_contacts || data.contacts_num || currentRows.length, 10) || 0;\n                renderRows(currentRows, currentTotal);\n                if (status) { status.textContent = ''; status.style.display = 'none'; }\n                try { if (window.tiForceCompanyContactsNum375) window.tiForceCompanyContactsNum375(currentTotal, data.field || 'Contacts_num'); } catch(e) {}\n                return data;\n            }).catch(function(err){\n                if (status) { status.textContent = ''; status.style.display = 'none'; }\n                renderRows([], currentTotal);\n            });\n        }\n        if (copyBtn && !copyBtn.__tiContactCopy584) {\n            copyBtn.__tiContactCopy584 = true;\n            copyBtn.addEventListener('click', function(){ copyText(plainRows(currentRows, currentTotal), copyBtn); }, false);\n        }\n        if (clearBtn && !clearBtn.__tiContactClear584) {\n            clearBtn.__tiContactClear584 = true;\n            clearBtn.addEventListener('click', function(){\n                var ok = false;\n                try { ok = confirm('Cancellare la lista log contatti e azzerare anche il totale contatti della ditta corrente?'); } catch(e) { ok = true; }\n                if (!ok) return;\n                if (status) { status.textContent = ''; status.style.display = 'none'; }\n                ajaxLog(db, 'clear').then(function(data){\n                    currentRows = [];\n                    currentTotal = parseInt(data && (data.total_contacts || data.contacts_num), 10) || 0;\n                    renderRows([], currentTotal);\n                    if (status) { status.textContent = ''; status.style.display = 'none'; }\n                    try { if (window.tiForceCompanyContactsNum375) window.tiForceCompanyContactsNum375(currentTotal, (data && data.field) || 'Contacts_num'); } catch(e) {}\n                    try { if (window.tiReadCompanyContactAccess375) setTimeout(function(){ window.tiReadCompanyContactAccess375(db); }, 350); } catch(e) {}\n                }).catch(function(err){ if (status) { status.textContent = ''; status.style.display = 'none'; } });\n            }, false);\n        }\n        refresh();\n    }\n    document.addEventListener('click', function(ev){\n        var a = ev.target && ev.target.closest ? ev.target.closest('.ti-contact-log-link') : null;\n        if (!a) return;\n        ev.preventDefault();\n        ev.stopPropagation();\n        openContactLog(a.getAttribute('data-ti-db') || selectedDb());\n    }, true);\n    window.tiOpenContactLogPopup584 = openContactLog;\n})();\n<\/script>\n\n\n\n\n<script id=\"ti-contact-log-close-isolation-586\">\n(function(){\n    \"use strict\";\n    if (window.__tiContactLogCloseIsolation586Installed) return;\n    window.__tiContactLogCloseIsolation586Installed = true;\n    function closeOnly(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var modal = t.closest('#ti-contact-log-modal-584');\n        if (!modal) return;\n        var isClose = (t === modal) || (t.closest && t.closest('[data-ti-contact-close=\"1\"]'));\n        if (!isClose) return;\n        try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        try { modal.style.display = 'none'; } catch(e) {}\n        try {\n            document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked','ti-no-scroll');\n            document.documentElement.classList.remove('ti-no-scroll');\n        } catch(e) {}\n        return false;\n    }\n    try { window.addEventListener('click', closeOnly, true); } catch(e) {}\n    try { window.addEventListener('touchend', closeOnly, true); } catch(e) {}\n})();\n<\/script>\n\n\n<script id=\"ti-plugin-audio-mic-fix-380\">\n(function(){\n    \"use strict\";\n    if (window.__tiPluginAudioMicFix380Installed) return;\n    window.__tiPluginAudioMicFix380Installed = true;\n\n    var audioCtx380 = null;\n    var audioUnlocked380 = false;\n    var speechPrimed380 = false;\n    var micRecognition380 = null;\n    var micTimer380 = null;\n    var lastError380 = '';\n\n    function byId(id){ return document.getElementById(id); }\n    function root(){ return byId('ti-ai-outer') || document.querySelector('.ti-ai-shop-service,[data-ti-shop-service]'); }\n    function muted(){ return !!(window.isMuted || window.muteState === 1 || window.muteState === 2); }\n    function alertSafe(msg){ try { if (window.tiAlert) window.tiAlert(msg); else alert(msg); } catch(e) { try { alert(msg); } catch(ignore){} } }\n    function secureOk(){ try { return !!(window.isSecureContext || location.protocol === 'https:' || location.hostname === 'localhost' || location.hostname === '127.0.0.1'); } catch(e) { return false; } }\n\n    function unlockAudio380(){\n        var ok = false;\n        lastError380 = '';\n        try {\n            var AC = window.AudioContext || window.webkitAudioContext;\n            if (AC) {\n                audioCtx380 = audioCtx380 || new AC();\n                if (audioCtx380.state === 'suspended' && audioCtx380.resume) {\n                    try {\n                        var pr = audioCtx380.resume();\n                        if (pr && typeof pr.catch === 'function') pr.catch(function(e){ lastError380 = e && e.message ? e.message : String(e || ''); });\n                    } catch(e) { lastError380 = e && e.message ? e.message : String(e || ''); }\n                }\n                try {\n                    var osc = audioCtx380.createOscillator();\n                    var gain = audioCtx380.createGain();\n                    gain.gain.value = 0.00001;\n                    osc.frequency.value = 880;\n                    osc.connect(gain);\n                    gain.connect(audioCtx380.destination);\n                    osc.start(0);\n                    osc.stop(audioCtx380.currentTime + 0.04);\n                } catch(ignore) {}\n                ok = true;\n            }\n        } catch(e) { lastError380 = e && e.message ? e.message : String(e || 'AudioContext non disponibile'); }\n        try {\n            if ('speechSynthesis' in window) {\n                window.speechSynthesis.resume();\n                if (!speechPrimed380) {\n                    var u = new SpeechSynthesisUtterance(' ');\n                    u.lang = (window.currLang === 'en') ? 'en-US' : 'it-IT';\n                    u.volume = 0.01;\n                    u.rate = 1;\n                    u.pitch = 1;\n                    window.speechSynthesis.speak(u);\n                    speechPrimed380 = true;\n                }\n                ok = true;\n            }\n        } catch(e) { lastError380 = e && e.message ? e.message : String(e || 'speechSynthesis non disponibile'); }\n        try {\n            var rr = root();\n            (rr ? rr.querySelectorAll('audio,video') : document.querySelectorAll('#ti-ai-outer audio,#ti-ai-outer video')).forEach(function(el){\n                try { el.muted = false; el.volume = 1; } catch(ignore) {}\n            });\n        } catch(e) {}\n        if (ok) audioUnlocked380 = true;\n        window.tiPluginAudioUnlocked380 = audioUnlocked380;\n        return ok;\n    }\n\n    window.tiEnsurePluginAudioUnlocked = unlockAudio380;\n    window.tiPluginAudioStatus = function(){\n        return {\n            version: '30.9.664',\n            audioUnlocked: audioUnlocked380,\n            audioContextState: audioCtx380 ? audioCtx380.state : 'not-created',\n            speechSynthesis: ('speechSynthesis' in window),\n            speechPrimed: speechPrimed380,\n            muted: muted(),\n            secureContext: secureOk(),\n            mediaDevices: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),\n            speechRecognition: !!(window.SpeechRecognition || window.webkitSpeechRecognition),\n            rootFound: !!root(),\n            micBound: !!(byId('ti-mic') && byId('ti-mic').__tiMic380),\n            lastUnlockError: lastError380\n        };\n    };\n\n    if (typeof window.speakText === 'function' && !window.speakText.__tiAudio380) {\n        var prevSpeakText380 = window.speakText;\n        window.speakText = function(text){\n            if (!muted()) unlockAudio380();\n            return prevSpeakText380.apply(this, arguments);\n        };\n        window.speakText.__tiAudio380 = true;\n    }\n    var prevPrime380 = window.primeSpeech;\n    window.primeSpeech = function(){\n        return unlockAudio380() || (typeof prevPrime380 === 'function' ? prevPrime380.apply(this, arguments) : false);\n    };\n\n    function setMicUi(active){\n        var btn = byId('ti-mic');\n        if (!btn) return;\n        try {\n            btn.disabled = false;\n            btn.style.background = active ? '#b91c1c' : '#222';\n            btn.style.color = active ? '#fff' : '#ccc';\n            btn.textContent = active ? 'Mic...' : 'Mic';\n        } catch(e) {}\n    }\n    function startMic380(ev){\n        if (ev) { try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {} }\n        unlockAudio380();\n        var msg = byId('ti-msg');\n        var send = byId('ti-send');\n        if (!secureOk()) { alertSafe('Microfono non disponibile. Apri la pagina in HTTPS e autorizza il microfono per questo sito.'); return false; }\n        if (!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) { alertSafe('Microfono non disponibile nel browser. Verifica i permessi del sito oppure usa Chrome, Edge o Safari aggiornato.'); return false; }\n        var SR = window.SpeechRecognition || window.webkitSpeechRecognition;\n        if (!SR) { alertSafe('Riconoscimento vocale non supportato dal browser. Usa Chrome o Edge quando possibile.'); return false; }\n        if (micRecognition380) {\n            try { micRecognition380.stop(); } catch(e) {}\n            micRecognition380 = null;\n            if (micTimer380) { clearTimeout(micTimer380); micTimer380 = null; }\n            setMicUi(false);\n            return false;\n        }\n        setMicUi(true);\n        navigator.mediaDevices.getUserMedia({audio:true}).then(function(stream){\n            try { stream.getTracks().forEach(function(t){ t.stop(); }); } catch(e) {}\n            var r = new SR();\n            micRecognition380 = r;\n            r.lang = (window.currLang === 'en') ? 'en-US' : 'it-IT';\n            r.continuous = false;\n            r.interimResults = false;\n            r.maxAlternatives = 1;\n            r.onresult = function(e){\n                try {\n                    var transcript = e && e.results && e.results[0] && e.results[0][0] ? e.results[0][0].transcript : '';\n                    if (transcript && msg) {\n                        msg.value = transcript;\n                        msg.dispatchEvent(new Event('input', {bubbles:true}));\n                        msg.dispatchEvent(new Event('change', {bubbles:true}));\n                    }\n                    if (transcript && send) setTimeout(function(){ try { send.click(); } catch(ignore){} }, 80);\n                } catch(ignore) {}\n            };\n            r.onerror = function(e){\n                var code = e && e.error ? e.error : 'errore';\n                var detail = 'Microfono non disponibile o non autorizzato.';\n                if (code === 'not-allowed' || code === 'service-not-allowed') detail = 'Microfono non autorizzato. Abilita il permesso microfono nelle impostazioni del browser per questo sito.';\n                else if (code === 'no-speech') detail = 'Non ho rilevato voce. Riprova parlando dopo aver premuto Mic.';\n                else if (code === 'audio-capture') detail = 'Nessun microfono rilevato dal browser.';\n                alertSafe(detail + ' Codice: ' + code);\n            };\n            r.onend = function(){ micRecognition380 = null; if (micTimer380) { clearTimeout(micTimer380); micTimer380 = null; } setMicUi(false); };\n            try { r.start(); } catch(e) { micRecognition380 = null; setMicUi(false); alertSafe('Impossibile avviare il microfono: ' + (e && e.message ? e.message : 'errore browser')); }\n            micTimer380 = setTimeout(function(){ try { if (micRecognition380) micRecognition380.stop(); } catch(e) {} setMicUi(false); }, 10000);\n        }).catch(function(e){\n            setMicUi(false);\n            alertSafe('Microfono non autorizzato o non disponibile. Verifica i permessi del browser per questo sito.' + (e && e.name ? ' Dettaglio: ' + e.name : ''));\n        });\n        return false;\n    }\n    window.tiStartPluginMic = startMic380;\n\n    function bindMic380(){\n        var btn = byId('ti-mic');\n        if (!btn) return;\n        btn.__tiMic380 = true;\n        btn.disabled = false;\n        btn.onclick = startMic380;\n    }\n    function bindMute380(){\n        var btn = byId('ti-mute-btn');\n        if (!btn || btn.__tiMuteUnlock380) return;\n        btn.__tiMuteUnlock380 = true;\n        btn.addEventListener('click', function(){ setTimeout(function(){ if (!muted()) unlockAudio380(); }, 30); }, false);\n    }\n    function bindGestures380(){\n        var rr = root();\n        if (!rr || rr.__tiAudioGesture380) return;\n        rr.__tiAudioGesture380 = true;\n        ['pointerdown','touchstart','mousedown','click','keydown'].forEach(function(evt){\n            rr.addEventListener(evt, function(){ if (!muted()) unlockAudio380(); }, {capture:true, passive:true});\n        });\n    }\n    function install380(){ bindGestures380(); bindMic380(); bindMute380(); }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install380);\n    install380();\n    setTimeout(install380, 100);\n    setTimeout(install380, 400);\n    setTimeout(install380, 1200);\n    setInterval(bindMic380, 2500);\n})();\n<\/script>\n\n\n<script id=\"ti-profile-batch-ai-fix-381\">\n(function(){\n    \"use strict\";\n    if (window.__tiProfileBatchAiFix381Installed) return;\n    window.__tiProfileBatchAiFix381Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return (window.escapeHtml ? window.escapeHtml(String(v == null ? '' : v)) : String(v == null ? '' : v).replace(\/[&<>'\"]\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;',\"'\":'&#39;','\"':'&quot;'}[c];})); }\n    function resetNewProfileOnce(){\n        var ids = ['p-utente','p-user','p-pass','p-ragione_sociale','p-email','p-tel','p-ind','p-piva_vat','p-cod_fisc','p-ade','p-sesso','p-dato_1','p-dato_2','p-dato_3','p-nota','p-foto'];\n        ids.forEach(function(id){\n            var el = byId(id); if (!el) return;\n            try {\n                if (el.type === 'checkbox' || el.type === 'radio') el.checked = false;\n                else el.value = '';\n                el.disabled = false;\n                el.readOnly = false;\n                el.removeAttribute('readonly');\n                el.removeAttribute('disabled');\n                el.setAttribute('autocomplete', id === 'p-pass' ? 'new-password' : 'off');\n                if (window.forceWhiteEditStyle) window.forceWhiteEditStyle(el);\n            } catch(e) {}\n        });\n        ['p-com_mail','p-com_email','p-com_sms','p-com_tel'].forEach(function(id){ var el = byId(id); if (el) el.checked = true; });\n        var eta = byId('p-eta'); if (eta) { eta.value = ''; eta.disabled = false; eta.readOnly = false; }\n        var pass = byId('p-pass'); if (pass) { pass.required = true; pass.disabled = false; pass.readOnly = false; }\n        var user = byId('p-user'); if (user) {\n            user.required = true;\n            user.disabled = false;\n            user.readOnly = false;\n            user.removeAttribute('readonly');\n            user.removeAttribute('disabled');\n            user.style.setProperty('pointer-events','auto','important');\n            user.style.setProperty('user-select','text','important');\n            user.style.setProperty('-webkit-user-select','text','important');\n        }\n        var prev = byId('p-foto-preview'); if (prev) { prev.removeAttribute('src'); prev.style.display = 'none'; }\n    }\n\n    var legacyOpenProfile381 = window.openProfileModal;\n    window.openProfileModal = function(isNew){\n        var ditta = byId('ti-ditta');\n        if (!ditta || !ditta.value || ditta.value === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona una ditta prima di registrarti.'); return false; }\n        if (isNew === true) {\n            var title = byId('lbl-profile-title'); if (title) title.innerText = 'Crea Profilo';\n            var lbl = byId('lbl-p-pass'); if (lbl) lbl.innerText = 'Password*';\n            var cancelBtn = byId('ti-profile-cancel-btn'); if (cancelBtn) cancelBtn.style.display = 'none';\n            resetNewProfileOnce();\n            if (window.openModal) window.openModal('ti-profile-ov');\n            setTimeout(function(){\n                var u = byId('p-user');\n                if (u) { u.readOnly = false; u.disabled = false; try { u.focus({preventScroll:true}); } catch(e) { try { u.focus(); } catch(e2) {} } }\n                if (window.bindProfileUsernameDuplicatePopup) window.bindProfileUsernameDuplicatePopup();\n            }, 80);\n            return false;\n        }\n        if (!window.tiUser) {\n            if (window.openLoginModalSafe) window.openLoginModalSafe();\n            else if (window.openModal) window.openModal('ti-login-ov');\n            return false;\n        }\n        if (typeof legacyOpenProfile381 === 'function') return legacyOpenProfile381.apply(this, arguments);\n        return false;\n    };\n\n    function bindUserArea381(){\n        var area = byId('ti-user-area');\n        if (!area || area.__tiUserArea381) return;\n        area.__tiUserArea381 = true;\n        area.onclick = function(ev){\n            if (ev) { ev.preventDefault(); ev.stopPropagation(); }\n            if (window.tiUser) window.openProfileModal(false);\n            else if (window.openLoginModalSafe) window.openLoginModalSafe();\n            else if (window.openModal) window.openModal('ti-login-ov');\n            return false;\n        };\n    }\n    bindUserArea381();\n    setTimeout(bindUserArea381, 500);\n\n    document.addEventListener('focusin', function(ev){\n        var el = ev.target;\n        if (!el || el.id !== 'p-user') return;\n        if (byId('ti-profile-ov') && !window.tiUser) {\n            try { el.readOnly = false; el.disabled = false; el.removeAttribute('readonly'); el.removeAttribute('disabled'); } catch(e) {}\n        }\n    }, true);\n\n    function ajaxConfig381(fd){\n        if (window.postFormDataJsonSafe) return window.postFormDataJsonSafe(window.tiUrl, fd, {tiNoLongProcessSignal:true, cache:'no-store'});\n        if (window.fetchJsonSafe) return window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true, cache:'no-store'});\n        return fetch(window.tiUrl, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'}).then(function(r){ return r.json(); });\n    }\n    function withTimeout381(promise, ms, label){\n        var tid;\n        var timeout = new Promise(function(_, reject){ tid = setTimeout(function(){ reject(new Error(label || 'Richiesta non completata nei tempi previsti.')); }, ms || 45000); });\n        return Promise.race([promise, timeout]).finally(function(){ clearTimeout(tid); });\n    }\n\n    window.runBatchAIGen = function(tableName) {\n        var dbSel = byId('ti-ditta');\n        var replyBox = byId('ti-conf-ai-reply');\n        if (!dbSel || !dbSel.value || dbSel.value === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona prima una ditta.'); return; }\n        if (!replyBox) { if (window.tiAlert) window.tiAlert('Area risposta configurazione non disponibile.'); return; }\n        var photoDetail = ((byId('ti-conf-ai-photo-detail') && byId('ti-conf-ai-photo-detail').value) ? byId('ti-conf-ai-photo-detail').value : '').trim();\n        replyBox.style.display = 'block';\n        replyBox.innerHTML = 'Preparazione elenco record. Priorita a prodotti e servizi senza associazioni file o immagini.<br>Prompt base: attivita ditta + descrizione prodotto\/servizio.';\n        var initFd = window.buildAjaxFormData ? window.buildAjaxFormData('ti_ai_config_action', {db: dbSel.value, mode: 'batch_ai_images', tables: tableName, detail_prompt: photoDetail}) : new FormData();\n        if (!initFd.has('ti_action')) initFd.append('ti_action','ti_ai_config_action');\n        if (!initFd.has('action')) initFd.append('action','ti_ai_config_action');\n        initFd.set('db', dbSel.value); initFd.set('mode','batch_ai_images'); initFd.set('tables', tableName); initFd.set('detail_prompt', photoDetail);\n        if (window.clearLongAIProcess) window.clearLongAIProcess();\n        if (window.showProgressPopup) window.showProgressPopup('Generazione foto AI', 'Sto preparando l elenco dei record. Prompt base: attivita ditta e descrizione prodotto o servizio.', {startPercent:2, targetPercent:18, stepMs:350, cancellable:false});\n        withTimeout381(ajaxConfig381(initFd), 45000, 'Preparazione elenco record non completata: verifica connessione o riprova.').then(function(d){\n            if (!d || !d.success) {\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                if (window.hideProgressPopup) window.hideProgressPopup(false, 'Errore preparazione batch');\n                replyBox.innerHTML = 'Errore: ' + esc(d && d.data && d.data.message ? d.data.message : 'Errore sconosciuto');\n                return;\n            }\n            var targets = (d.data && Array.isArray(d.data.targets)) ? d.data.targets : [];\n            if (!targets.length) {\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                if (window.hideProgressPopup) window.hideProgressPopup(true, 'Nessun elemento da elaborare', {notifyUser:false, remindSave:false});\n                replyBox.innerHTML = 'Nessun record trovato nella tabella selezionata.';\n                return;\n            }\n            var index = 0, okCount = 0, logs = [], total = targets.length;\n            window.tiBatchAIGenState = {active:true, pendingConfirm:false, generated:[], db:dbSel.value, tableName:tableName, total:total, startedAt:Date.now()};\n            var withoutAssoc = (d.data && typeof d.data.without_associations !== 'undefined') ? parseInt(d.data.without_associations,10) : targets.filter(function(x){return !x.has_associations;}).length;\n            var withAssoc = (d.data && typeof d.data.with_associations !== 'undefined') ? parseInt(d.data.with_associations,10) : Math.max(0,total-withoutAssoc);\n            var detailMode = (d.data && d.data.detail_prompt_mode) ? String(d.data.detail_prompt_mode) : '';\n            var detailInfo = (photoDetail && detailMode === 'leaflet') ? '<br>Istruzione aggiuntiva riconosciuta: <b>foglietto illustrativo \/ materiale informativo<\/b>.' : (photoDetail ? '<br>Istruzione aggiuntiva: <b>' + esc(photoDetail) + '<\/b>.' : '');\n            replyBox.innerHTML = 'Generazione immagini AI pronta.<br>Prompt base: <b>attivita ditta + descrizione prodotto\/servizio<\/b>.' + detailInfo + '<br>Priorita: <b>' + withoutAssoc + '<\/b> prodotti\/servizi senza associazioni.<br>Successivi: <b>' + withAssoc + '<\/b> prodotti\/servizi gia con associazioni.';\n            var refreshDb = function(){\n                if (window.refreshCurrentConfigDb) return window.refreshCurrentConfigDb(dbSel.value);\n                var fdDb = window.buildAjaxFormData ? window.buildAjaxFormData('ti_ai_get_db_data', {db: dbSel.value}) : new FormData();\n                if (!fdDb.has('ti_action')) fdDb.append('ti_action','ti_ai_get_db_data');\n                if (!fdDb.has('action')) fdDb.append('action','ti_ai_get_db_data');\n                fdDb.set('db', dbSel.value);\n                return ajaxConfig381(fdDb).then(function(dbRes){ if (dbRes && dbRes.success && dbRes.data) { window.currentDbData = dbRes.data; if (window.renderConfig) window.renderConfig(); } }).catch(function(){});\n            };\n            var runNext = function(){\n                if (index >= total) {\n                    if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                    if (window.updateProgressPopup) window.updateProgressPopup(100, 'Generazione foto AI', 'Completamento e verifica salvataggio associazioni.');\n                    Promise.resolve(refreshDb()).finally(function(){\n                        if (window.hideProgressPopup) window.hideProgressPopup(true, 'Generazione foto AI completata', {remindSave:false, successMessage:'Attivita AI completata. Immagini associate e verificate: ' + okCount + ' su ' + total + '.'});\n                        replyBox.innerHTML = '\u2705 Generazione immagini AI completata.<br>Immagini associate e verificate: <b>' + okCount + '<\/b> \/ ' + total + '<br><br>' + logs.slice(0,40).join('<br>');\n                    });\n                    return;\n                }\n                var item = targets[index];\n                var currentStep = index + 1;\n                var pct = Math.max(5, Math.min(98, Math.round((index \/ total) * 100)));\n                var priorityText = item.has_associations ? 'Elemento gia con associazioni' : 'Priorita senza associazioni';\n                if (window.updateProgressPopup) window.updateProgressPopup(pct, 'Generazione foto AI', priorityText + '. Elaborazione ' + currentStep + ' \/ ' + total + ': ' + (item.name || 'Elemento'), (item.name || 'Elemento'));\n                replyBox.innerHTML = 'Generazione immagini AI in corso...<br>' + priorityText + '<br>Elaborazione ' + currentStep + ' \/ ' + total + ': <b>' + esc(item.name || 'Elemento') + '<\/b>';\n                var fd = window.buildAjaxFormData ? window.buildAjaxFormData('ti_ai_config_action', {db: dbSel.value, mode:'batch_ai_image_one', table:item.table || tableName, row_index:item.row_index, detail_prompt:photoDetail}) : new FormData();\n                if (!fd.has('ti_action')) fd.append('ti_action','ti_ai_config_action');\n                if (!fd.has('action')) fd.append('action','ti_ai_config_action');\n                fd.set('db', dbSel.value); fd.set('mode','batch_ai_image_one'); fd.set('table', item.table || tableName); fd.set('row_index', item.row_index); fd.set('detail_prompt', photoDetail);\n                ajaxConfig381(fd).then(function(one){\n                    var data = one && one.data ? one.data : {};\n                    if (one && one.success && data.generated) {\n                        okCount++;\n                        if (window.tiBatchAIGenState && Array.isArray(window.tiBatchAIGenState.generated) && (data.added_value || data.filename || data.url)) {\n                            window.tiBatchAIGenState.generated.push({table:data.table || item.table || tableName, row_index:(typeof data.row_index !== 'undefined') ? data.row_index : item.row_index, field:data.image_field || 'Immagine', value:data.added_value || data.filename || data.url, name:data.name || item.name || 'Elemento'});\n                        }\n                        logs.push('\u2705 ' + esc(data.name || item.name || 'Elemento') + ': ' + (data.fallback_logo ? 'foto non disponibile, usato logo ditta' : 'immagine generata e associazione salvata'));\n                    } else if (one && one.success && data.skipped) {\n                        logs.push('\u23ed\ufe0f ' + esc(data.name || item.name || 'Elemento') + ': immagine gia presente');\n                    } else {\n                        logs.push('\u274c ' + esc(data.name || item.name || 'Elemento') + ': ' + esc(data.message || 'errore'));\n                    }\n                    index++;\n                    setTimeout(runNext, 350);\n                }).catch(function(err){\n                    logs.push('\u274c ' + esc(item.name || 'Elemento') + ': ' + esc(err && err.message ? err.message : 'errore rete'));\n                    index++;\n                    setTimeout(runNext, 700);\n                });\n            };\n            runNext();\n        }).catch(function(err){\n            if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n            if (window.hideProgressPopup) window.hideProgressPopup(false, 'Errore batch immagini');\n            replyBox.innerHTML = 'Errore preparazione generazione foto AI: ' + esc(err && err.message ? err.message : 'errore rete');\n        });\n    };\n})();\n<\/script>\n\n\n<script id=\"ti-report-controls-charts-fields-382\">\n(function(){\n    \"use strict\";\n    if (window.__tiReportControlsChartsFields382Installed) return;\n    window.__tiReportControlsChartsFields382Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){\n        return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){\n            return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c];\n        });\n    }\n    function safeCols(){\n        var cols = Array.isArray(window.activityReportCols) ? window.activityReportCols.slice() : [];\n        if (!cols.length) cols = ['Username','Descrizione','Prezzo','Quantit\u00e0','Esito','Data','Ora','Stato','Totale'];\n        return cols;\n    }\n    function ensureStyle(){\n        if (byId('ti-report-controls-charts-fields-382-css')) return;\n        var st = document.createElement('style');\n        st.id = 'ti-report-controls-charts-fields-382-css';\n        st.textContent = ''+\n        '#ti-report-cnt .ti-report-toolbar{display:flex!important;visibility:visible!important;opacity:1!important;gap:8px!important;align-items:center!important;flex-wrap:wrap!important;overflow-x:auto!important;margin-bottom:8px!important;}'+\n        '#ti-report-cnt .ti-report-382-panel{display:block!important;visibility:visible!important;opacity:1!important;background:#0f172a!important;border:1px solid #334155!important;border-radius:10px!important;padding:8px!important;margin:8px 0!important;color:#e5e7eb!important;}'+\n        '#ti-report-cnt .ti-report-382-row{display:flex!important;gap:8px!important;align-items:center!important;flex-wrap:wrap!important;margin:5px 0!important;}'+\n        '#ti-report-cnt .ti-report-382-row label{display:inline-flex!important;gap:5px!important;align-items:center!important;font-size:12px!important;background:#111827!important;border:1px solid #334155!important;border-radius:999px!important;padding:4px 8px!important;}'+\n        '#ti-report-cnt .ti-report-382-btn{border:1px solid #475569!important;border-radius:8px!important;background:#1f2937!important;color:#fff!important;padding:6px 9px!important;font-weight:800!important;cursor:pointer!important;}'+\n        '#ti-report-cnt .ti-report-382-btn.is-active{background:#1d4ed8!important;border-color:#60a5fa!important;}'+\n        '#ti-report-cnt .ti-report-382-select{background:#111827!important;color:#fff!important;border:1px solid #475569!important;border-radius:8px!important;padding:5px 8px!important;}'+\n        '#ti-report-cnt .ti-chart-wrap{display:block!important;visibility:visible!important;opacity:1!important;min-height:360px!important;}'+\n        '#ti-report-cnt .ti-chart-wrap canvas{display:block!important;visibility:visible!important;opacity:1!important;background:#020617!important;max-width:100%!important;}';\n        document.head.appendChild(st);\n    }\n    function ensureState(){\n        if (!window.activityReportState) window.activityReportState = {};\n        if (!window.activityReportState.colFlags) window.activityReportState.colFlags = {};\n        if (!window.activityReportState.metricFlags) {\n            window.activityReportState.metricFlags = (typeof window.getDefaultActivityMetricFlags === 'function') ? window.getDefaultActivityMetricFlags() : {Prezzo:false, Quantit\u00e0:false, Totale:true};\n        }\n        if (!window.activityReportState.view) window.activityReportState.view = 'table';\n        if (!window.activityReportState.groupField) window.activityReportState.groupField = 'Username';\n    }\n    function setView(view){\n        ensureState();\n        window.activityReportState.view = view || 'table';\n        if (typeof window.saveActivityReportPrefs === 'function') window.saveActivityReportPrefs();\n        if (typeof window.renderActivityReport === 'function') window.renderActivityReport();\n    }\n    function setColumnVisible(col, checked){\n        ensureState();\n        window.activityReportState.colFlags[col] = !checked;\n        if (typeof window.saveActivityReportPrefs === 'function') window.saveActivityReportPrefs();\n        if (typeof window.renderActivityReport === 'function') window.renderActivityReport();\n    }\n    function setMetric(metric, checked){\n        ensureState();\n        window.activityReportState.metricFlags[metric] = !!checked;\n        var metrics = ['Prezzo','Quantit\u00e0','Totale'];\n        var has = metrics.some(function(m){ return !!window.activityReportState.metricFlags[m]; });\n        if (!has) window.activityReportState.metricFlags[metric] = true;\n        if (typeof window.saveActivityReportPrefs === 'function') window.saveActivityReportPrefs();\n        if (typeof window.renderActivityReport === 'function') window.renderActivityReport();\n    }\n    function setGroup(field){\n        ensureState();\n        window.activityReportState.groupField = field || 'Username';\n        if (typeof window.saveActivityReportPrefs === 'function') window.saveActivityReportPrefs();\n        if (typeof window.renderActivityReport === 'function') window.renderActivityReport();\n    }\n    window.tiReport382SetView = setView;\n    window.tiReport382SetColumnVisible = setColumnVisible;\n    window.tiReport382SetMetric = setMetric;\n    window.tiReport382SetGroup = setGroup;\n\n    function addControls(){\n        var box = byId('ti-report-cnt');\n        if (!box) return;\n        ensureStyle();\n        ensureState();\n        var old = byId('ti-report-controls-382');\n        if (old) old.remove();\n\n        var view = window.activityReportState.view || 'table';\n        var cols = safeCols();\n        var colFlags = window.activityReportState.colFlags || {};\n        var metricFlags = window.activityReportState.metricFlags || {};\n        var groupFields = (typeof window.getActivityAvailableGroupFields === 'function') ? window.getActivityAvailableGroupFields() : cols;\n        if (!Array.isArray(groupFields) || !groupFields.length) groupFields = cols;\n        var group = (typeof window.getActivitySelectedGroupField === 'function') ? window.getActivitySelectedGroupField() : (window.activityReportState.groupField || 'Username');\n        var html = '<div id=\"ti-report-controls-382\" class=\"ti-report-382-panel\">';\n        html += '<div class=\"ti-report-382-row\"><b>Vista report<\/b>';\n        [['table','Elenco dati'],['pie','Grafico torte'],['trend','Grafico andamento']].forEach(function(v){\n            html += '<button type=\"button\" class=\"ti-report-382-btn '+(view===v[0]?'is-active':'')+'\" onclick=\"window.tiReport382SetView(\\''+v[0]+'\\')\">'+esc(v[1])+'<\/button>';\n        });\n        html += '<\/div>';\n        html += '<div class=\"ti-report-382-row\"><b>Campi<\/b>';\n        cols.forEach(function(c){\n            var checked = !colFlags[c];\n            html += '<label><input type=\"checkbox\" '+(checked?'checked':'')+' onchange=\"window.tiReport382SetColumnVisible('+JSON.stringify(c).replace(\/\"\/g,'&quot;')+', this.checked)\"><span>'+esc(c)+'<\/span><\/label>';\n        });\n        html += '<\/div>';\n        if (view === 'pie' || view === 'trend') {\n            html += '<div class=\"ti-report-382-row\"><b>Grafico<\/b><span>Raggruppa per<\/span><select class=\"ti-report-382-select\" onchange=\"window.tiReport382SetGroup(this.value)\">';\n            groupFields.forEach(function(g){ html += '<option value=\"'+esc(g)+'\" '+(g===group?'selected':'')+'>'+esc(g)+'<\/option>'; });\n            html += '<\/select>';\n            ['Prezzo','Quantit\u00e0','Totale'].forEach(function(m){\n                html += '<label><input type=\"checkbox\" '+(metricFlags[m]?'checked':'')+' onchange=\"window.tiReport382SetMetric(\\''+m+'\\', this.checked)\"><span>'+esc(m)+'<\/span><\/label>';\n            });\n            html += '<\/div>';\n        }\n        html += '<\/div>';\n\n        var toolbar = box.querySelector('.ti-report-toolbar');\n        if (toolbar && toolbar.parentNode) toolbar.insertAdjacentHTML('afterend', html);\n        else box.insertAdjacentHTML('afterbegin', html);\n    }\n    function ensureChart(){\n        var box = byId('ti-report-cnt');\n        if (!box || !window.activityReportState) return;\n        var view = window.activityReportState.view || 'table';\n        if (view !== 'pie' && view !== 'trend') return;\n        var id = view === 'pie' ? 'ti-report-pie' : 'ti-report-trend';\n        if (!byId(id)) {\n            box.insertAdjacentHTML('beforeend', '<div class=\"ti-chart-wrap\"><canvas id=\"'+id+'\" width=\"980\" height=\"380\"><\/canvas><\/div>');\n        }\n        var rows = (typeof window.getFilteredActivityRows === 'function') ? window.getFilteredActivityRows(false) : [];\n        setTimeout(function(){\n            try {\n                if (view === 'pie' && typeof window.drawPie3D === 'function') window.drawPie3D(id, rows);\n                if (view === 'trend' && typeof window.drawTrend3D === 'function') window.drawTrend3D(id, rows);\n            } catch(e) {\n                try {\n                    var cv = byId(id), ctx = cv && cv.getContext ? cv.getContext('2d') : null;\n                    if (ctx) { ctx.fillStyle='#fff'; ctx.font='14px Arial'; ctx.fillText('Grafico non disponibile: '+(e && e.message ? e.message : 'errore'), 20, 30); }\n                } catch(ignore) {}\n            }\n        }, 80);\n    }\n    function enhance(){ addControls(); ensureChart(); }\n\n    if (typeof window.renderActivityReport === 'function' && !window.renderActivityReport.__ti382) {\n        var oldRender = window.renderActivityReport;\n        window.renderActivityReport = function(){\n            var ret = oldRender.apply(this, arguments);\n            setTimeout(enhance, 0);\n            setTimeout(enhance, 160);\n            return ret;\n        };\n        window.renderActivityReport.__ti382 = true;\n    }\n    if (typeof window.openActivityReport === 'function' && !window.openActivityReport.__ti382) {\n        var oldOpen = window.openActivityReport;\n        window.openActivityReport = function(){\n            var ret = oldOpen.apply(this, arguments);\n            setTimeout(enhance, 120);\n            setTimeout(enhance, 450);\n            return ret;\n        };\n        window.openActivityReport.__ti382 = true;\n    }\n    setTimeout(enhance, 500);\n})();\n<\/script>\n\n\n\n\n<script id=\"ti-auto-audio-unlock-383\">\n(function(){\n    \"use strict\";\n    if (window.__tiAutoAudioUnlock383Installed) return;\n    window.__tiAutoAudioUnlock383Installed = true;\n\n    var audioCtx = null;\n    var speechPrimed = false;\n    var unlocked = false;\n    var lastError = \"\";\n    var gestureCount = 0;\n\n    function byId(id){ return document.getElementById(id); }\n    function hasPluginRoot(){ return !!(byId('ti-ai-outer') || document.querySelector('.ti-ai-shop-service,[data-ti-shop-service]')); }\n    function isPluginPage(){\n        try {\n            return hasPluginRoot() || (document.body && document.body.classList && document.body.classList.contains('page-id-1165'));\n        } catch(e) { return false; }\n    }\n    function isMuted(){ return !!(window.isMuted || window.muteState === 1 || window.muteState === 2); }\n    function mediaEls(){\n        var root = byId('ti-ai-outer');\n        try { return root ? root.querySelectorAll('audio,video') : document.querySelectorAll('#ti-ai-outer audio,#ti-ai-outer video'); }\n        catch(e) { return []; }\n    }\n    function unlockAudio383(force){\n        if (!force && !isPluginPage()) return false;\n        var ok = false;\n        lastError = \"\";\n        try {\n            var AC = window.AudioContext || window.webkitAudioContext;\n            if (AC) {\n                audioCtx = audioCtx || new AC();\n                if (audioCtx.state === 'suspended' && audioCtx.resume) {\n                    try {\n                        var pr = audioCtx.resume();\n                        if (pr && typeof pr.catch === 'function') pr.catch(function(e){ lastError = e && e.message ? e.message : String(e || ''); });\n                    } catch(e) { lastError = e && e.message ? e.message : String(e || ''); }\n                }\n                try {\n                    var osc = audioCtx.createOscillator();\n                    var gain = audioCtx.createGain();\n                    gain.gain.value = 0.00001;\n                    osc.frequency.value = 880;\n                    osc.connect(gain);\n                    gain.connect(audioCtx.destination);\n                    osc.start(0);\n                    osc.stop(audioCtx.currentTime + 0.04);\n                } catch(ignore) {}\n                ok = true;\n            }\n        } catch(e) { lastError = e && e.message ? e.message : String(e || 'AudioContext non disponibile'); }\n        try {\n            if ('speechSynthesis' in window) {\n                window.speechSynthesis.resume();\n                if (!speechPrimed && !isMuted()) {\n                    var u = new SpeechSynthesisUtterance(' ');\n                    u.lang = (window.currLang === 'en') ? 'en-US' : 'it-IT';\n                    u.volume = 0;\n                    u.rate = 1;\n                    u.pitch = 1;\n                    window.speechSynthesis.speak(u);\n                    speechPrimed = true;\n                }\n                ok = true;\n            }\n        } catch(e) { lastError = e && e.message ? e.message : String(e || 'speechSynthesis non disponibile'); }\n        try { mediaEls().forEach(function(el){ try { el.muted = false; el.volume = 1; } catch(ignore) {} }); } catch(e) {}\n        if (ok) unlocked = true;\n        window.tiAudioAutoUnlocked383 = unlocked;\n        return ok;\n    }\n\n    window.tiEnsurePluginAudioUnlocked = function(){ return unlockAudio383(true); };\n    window.tiPluginAudioStatus = function(){\n        var prev = {};\n        try { if (typeof window.__tiPreviousPluginAudioStatus383 === 'function') prev = window.__tiPreviousPluginAudioStatus383() || {}; } catch(e) {}\n        return Object.assign({}, prev, {\n            version: '30.9.664',\n            audioUnlocked: unlocked,\n            audioAutoUnlock383: unlocked,\n            gestureCount383: gestureCount,\n            audioContextState383: audioCtx ? audioCtx.state : 'not-created',\n            speechSynthesis: ('speechSynthesis' in window),\n            speechPrimed383: speechPrimed,\n            muted: isMuted(),\n            pluginRootFound: hasPluginRoot(),\n            pageId1165: !!(document.body && document.body.classList && document.body.classList.contains('page-id-1165')),\n            lastUnlockError383: lastError\n        });\n    };\n\n    \/\/ Conserva eventuale diagnostica esistente, ma sovrascrive lo sblocco con il nuovo fallback.\n    try {\n        if (typeof window.tiPluginAudioStatus === 'function' && !window.__tiPreviousPluginAudioStatus383) {\n            window.__tiPreviousPluginAudioStatus383 = window.tiPluginAudioStatus;\n        }\n    } catch(e) {}\n\n    function onRealGesture(){\n        if (!isPluginPage()) return;\n        gestureCount++;\n        unlockAudio383(true);\n    }\n    function bindDocumentGestures(){\n        if (document.__tiAudioAutoGestures383) return;\n        document.__tiAudioAutoGestures383 = true;\n        ['pointerdown','touchstart','mousedown','click','keydown'].forEach(function(evt){\n            document.addEventListener(evt, onRealGesture, {capture:true, passive:true});\n        });\n    }\n    function wrapSpeakText(){\n        if (typeof window.speakText === 'function' && !window.speakText.__tiAutoAudio383) {\n            var prevSpeakText = window.speakText;\n            window.speakText = function(text){\n                if (!isMuted()) unlockAudio383(true);\n                return prevSpeakText.apply(this, arguments);\n            };\n            window.speakText.__tiAutoAudio383 = true;\n        }\n        if (typeof window.primeSpeech === 'function' && !window.primeSpeech.__tiAutoAudio383) {\n            var prevPrime = window.primeSpeech;\n            window.primeSpeech = function(){\n                return unlockAudio383(true) || prevPrime.apply(this, arguments);\n            };\n            window.primeSpeech.__tiAutoAudio383 = true;\n        }\n    }\n    function wrapNativeSpeak(){\n        try {\n            if (!('speechSynthesis' in window) || !window.speechSynthesis || !window.speechSynthesis.speak || window.speechSynthesis.speak.__tiAutoAudio383) return;\n            var nativeSpeak = window.speechSynthesis.speak.bind(window.speechSynthesis);\n            var wrappedSpeak = function(utterance){\n                if (!isMuted()) unlockAudio383(true);\n                return nativeSpeak(utterance);\n            };\n            wrappedSpeak.__tiAutoAudio383 = true;\n            window.speechSynthesis.speak = wrappedSpeak;\n        } catch(e) {}\n    }\n    function wrapMediaPlay(){\n        try {\n            if (!window.HTMLMediaElement || !HTMLMediaElement.prototype || !HTMLMediaElement.prototype.play || HTMLMediaElement.prototype.play.__tiAutoAudio383) return;\n            var nativePlay = HTMLMediaElement.prototype.play;\n            var wrappedPlay = function(){\n                unlockAudio383(true);\n                try { this.muted = false; this.volume = 1; } catch(e) {}\n                return nativePlay.apply(this, arguments);\n            };\n            wrappedPlay.__tiAutoAudio383 = true;\n            HTMLMediaElement.prototype.play = wrappedPlay;\n        } catch(e) {}\n    }\n    function bindMute(){\n        var btn = byId('ti-mute-btn');\n        if (!btn || btn.__tiAutoAudio383) return;\n        btn.__tiAutoAudio383 = true;\n        btn.addEventListener('click', function(){ setTimeout(function(){ if (!isMuted()) unlockAudio383(true); }, 40); }, false);\n    }\n    function install(){\n        if (!isPluginPage()) return;\n        bindDocumentGestures();\n        wrapSpeakText();\n        wrapNativeSpeak();\n        wrapMediaPlay();\n        bindMute();\n        \/\/ Tentativo non sonoro: se il browser lo permette, lo sblocco avviene subito; altrimenti avverra al primo gesto reale.\n        if (!isMuted()) unlockAudio383(false);\n    }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install);\n    install();\n    setTimeout(install, 250);\n    setTimeout(install, 900);\n    setTimeout(install, 1800);\n    if (typeof MutationObserver !== 'undefined') {\n        try { new MutationObserver(function(){ install(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n    }\n})();\n<\/script>\n\n\n\n<script id=\"ti-profile-registration-384\">\n(function(){\n    \"use strict\";\n    if (window.__tiProfileRegistration384Installed) return;\n    window.__tiProfileRegistration384Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function visible(el){\n        if (!el) return false;\n        try {\n            var st = window.getComputedStyle ? getComputedStyle(el) : null;\n            return !(st && (st.display === 'none' || st.visibility === 'hidden' || st.opacity === '0')) && !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n        } catch(e) { return false; }\n    }\n    function inRegistration(){ return window.__tiProfileRegistrationMode384 === true; }\n    function setUserAreaVisibility384(){\n        var area = byId('ti-user-area');\n        if (!area) return;\n        var allow = !!window.tiUser || inRegistration();\n        area.style.setProperty('display', allow ? 'flex' : 'none', 'important');\n        if (!window.tiUser && !inRegistration()) {\n            var name = byId('ti-user-name');\n            var role = byId('ti-user-role');\n            if (name) name.textContent = 'Cliente\/Utente';\n            if (role) role.textContent = '';\n        }\n    }\n    function setEditableRegistrationFields384(){\n        ['p-utente','p-user','p-pass','p-email','p-ragione_sociale','p-ind','p-piva_vat','p-cod_fisc','p-ade','p-tel','p-sesso','p-eta','p-dato_1','p-dato_2','p-dato_3','p-nota'].forEach(function(id){\n            var el = byId(id); if (!el) return;\n            try {\n                el.disabled = false;\n                el.readOnly = false;\n                el.removeAttribute('disabled');\n                el.removeAttribute('readonly');\n                el.style.setProperty('pointer-events','auto','important');\n                el.style.setProperty('user-select','text','important');\n                el.style.setProperty('-webkit-user-select','text','important');\n                el.setAttribute('autocomplete', id === 'p-pass' ? 'new-password' : 'off');\n                if (id === 'p-user') {\n                    el.required = true;\n                    el.setAttribute('inputmode','text');\n                    el.setAttribute('autocapitalize','none');\n                    el.setAttribute('autocorrect','off');\n                    el.setAttribute('spellcheck','false');\n                }\n            } catch(e) {}\n        });\n        var pass = byId('p-pass'); if (pass) pass.required = true;\n        var title = byId('lbl-profile-title'); if (title) title.innerText = 'Crea Profilo Cliente\/Utente';\n        var lbl = byId('lbl-p-pass'); if (lbl) lbl.innerText = 'Password*';\n        var cancelBtn = byId('ti-profile-cancel-btn'); if (cancelBtn) cancelBtn.style.display = 'none';\n    }\n    function clearRegistrationFields384(){\n        ['p-utente','p-user','p-pass','p-email','p-ragione_sociale','p-ind','p-piva_vat','p-cod_fisc','p-ade','p-tel','p-sesso','p-eta','p-dato_1','p-dato_2','p-dato_3','p-nota','p-foto'].forEach(function(id){\n            var el = byId(id); if (!el) return;\n            try { if (el.type === 'checkbox' || el.type === 'radio') el.checked = false; else el.value = ''; } catch(e) {}\n        });\n        ['p-com_mail','p-com_email','p-com_sms','p-com_tel'].forEach(function(id){ var el = byId(id); if (el) el.checked = true; });\n        var prev = byId('p-foto-preview'); if (prev) { prev.removeAttribute('src'); prev.style.display = 'none'; }\n        setEditableRegistrationFields384();\n    }\n\n    var prevOpen384 = window.openProfileModal;\n    window.openProfileModal = function(isNew){\n        if (isNew === true) {\n            var ditta = byId('ti-ditta');\n            if (!ditta || !ditta.value || ditta.value === 'NEW_DB' || ditta.value === '') { if (window.tiAlert) window.tiAlert('Seleziona una ditta prima di registrarti.'); return false; }\n            window.__tiProfileRegistrationMode384 = true;\n            clearRegistrationFields384();\n            if (typeof prevOpen384 === 'function') prevOpen384.call(this, true);\n            else if (window.openModal) window.openModal('ti-profile-ov');\n            setTimeout(function(){ window.__tiProfileRegistrationMode384 = true; setEditableRegistrationFields384(); setUserAreaVisibility384(); var u = byId('p-user'); if (u) { try { u.focus({preventScroll:true}); } catch(e) { try { u.focus(); } catch(e2) {} } } }, 60);\n            setTimeout(function(){ setEditableRegistrationFields384(); setUserAreaVisibility384(); }, 250);\n            return false;\n        }\n        if (!window.tiUser) {\n            window.__tiProfileRegistrationMode384 = false;\n            setUserAreaVisibility384();\n            if (window.openLoginModalSafe) window.openLoginModalSafe();\n            else if (window.openModal) window.openModal('ti-login-ov');\n            return false;\n        }\n        window.__tiProfileRegistrationMode384 = false;\n        return (typeof prevOpen384 === 'function') ? prevOpen384.apply(this, arguments) : false;\n    };\n\n    var prevClose384 = window.closeModal;\n    if (typeof prevClose384 === 'function' && !prevClose384.__tiProfile384) {\n        window.closeModal = function(id){\n            var ret = prevClose384.apply(this, arguments);\n            if (id === 'ti-profile-ov') {\n                window.__tiProfileRegistrationMode384 = false;\n                setTimeout(setUserAreaVisibility384, 40);\n            }\n            return ret;\n        };\n        window.closeModal.__tiProfile384 = true;\n    }\n\n    function bindRegistrationButton384(){\n        try {\n            document.querySelectorAll('button').forEach(function(btn){\n                var txt = String(btn.textContent || '').trim().toLowerCase();\n                if (txt.indexOf('registrati') === -1) return;\n                if (btn.__tiReg384) return;\n                btn.__tiReg384 = true;\n                btn.addEventListener('click', function(ev){\n                    window.__tiProfileRegistrationMode384 = true;\n                    setTimeout(function(){ setEditableRegistrationFields384(); setUserAreaVisibility384(); }, 80);\n                }, true);\n            });\n        } catch(e) {}\n    }\n    function bindUserArea384(){\n        var area = byId('ti-user-area');\n        if (!area) return;\n        area.onclick = function(ev){\n            if (ev) { ev.preventDefault(); ev.stopPropagation(); }\n            if (window.tiUser) return window.openProfileModal(false);\n            if (inRegistration() && visible(byId('ti-profile-ov'))) return window.openProfileModal(true);\n            return false;\n        };\n    }\n    document.addEventListener('focusin', function(ev){\n        var el = ev && ev.target;\n        if (!el) return;\n        if (inRegistration() && \/^p-\/.test(el.id || '')) setEditableRegistrationFields384();\n    }, true);\n    document.addEventListener('input', function(ev){\n        var el = ev && ev.target;\n        if (el && el.id === 'p-user' && inRegistration()) setEditableRegistrationFields384();\n    }, true);\n\n    function tick384(){ bindRegistrationButton384(); bindUserArea384(); setUserAreaVisibility384(); }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', tick384);\n    tick384();\n    setTimeout(tick384, 200);\n    setTimeout(tick384, 800);\n    setTimeout(tick384, 1600);\n})();\n<\/script>\n\n\n\n<script id=\"ti-registration-start-385\">\n(function(){\n    \"use strict\";\n    if (window.__tiRegistrationStart385Installed) return;\n    window.__tiRegistrationStart385Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function setImp(el, prop, val){ try { if (el) el.style.setProperty(prop, val, 'important'); } catch(e) {} }\n    function selectedDb(){ var d = byId('ti-ditta') || byId('l-db') || byId('l-db-hidden'); return d ? String(d.value || '').trim() : ''; }\n    function hideLogin(){\n        var ov = byId('ti-login-ov');\n        if (!ov) return;\n        setImp(ov, 'display', 'none');\n        setImp(ov, 'visibility', 'hidden');\n        setImp(ov, 'opacity', '0');\n        setImp(ov, 'pointer-events', 'none');\n        try { ov.setAttribute('aria-hidden','true'); } catch(e) {}\n    }\n    function showProfile(){\n        var ov = byId('ti-profile-ov');\n        if (!ov) return;\n        setImp(ov, 'display', 'flex');\n        setImp(ov, 'visibility', 'visible');\n        setImp(ov, 'opacity', '1');\n        setImp(ov, 'pointer-events', 'auto');\n        setImp(ov, 'z-index', '2147483647');\n        try { ov.removeAttribute('aria-hidden'); ov.classList.add('ti-modal-active','ti-ov-active','is-active','show'); } catch(e) {}\n        try { if (window.preparePluginModal) window.preparePluginModal(ov); } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n    }\n    function enableProfileInputs(){\n        var ids = ['p-utente','p-user','p-pass','p-email','p-ragione_sociale','p-ind','p-piva_vat','p-cod_fisc','p-ade','p-tel','p-sesso','p-eta','p-dato_1','p-dato_2','p-dato_3','p-nota','p-foto'];\n        ids.forEach(function(id){\n            var el = byId(id); if (!el) return;\n            try {\n                el.disabled = false;\n                el.readOnly = false;\n                el.removeAttribute('disabled');\n                el.removeAttribute('readonly');\n                el.style.setProperty('pointer-events','auto','important');\n                el.style.setProperty('user-select','text','important');\n                el.style.setProperty('-webkit-user-select','text','important');\n                el.setAttribute('autocomplete', id === 'p-pass' ? 'new-password' : 'off');\n                el.setAttribute('autocorrect','off');\n                el.setAttribute('spellcheck','false');\n                if (id === 'p-user') {\n                    el.required = true;\n                    el.setAttribute('inputmode','text');\n                    el.setAttribute('autocapitalize','none');\n                }\n                if (window.forceWhiteEditStyle) window.forceWhiteEditStyle(el);\n            } catch(e) {}\n        });\n        var pass = byId('p-pass'); if (pass) pass.required = true;\n        var user = byId('p-user'); if (user) user.required = true;\n    }\n    function clearProfileFields(){\n        var ids = ['p-utente','p-user','p-pass','p-email','p-ragione_sociale','p-ind','p-piva_vat','p-cod_fisc','p-ade','p-tel','p-sesso','p-eta','p-dato_1','p-dato_2','p-dato_3','p-nota','p-foto'];\n        ids.forEach(function(id){\n            var el = byId(id); if (!el) return;\n            try { if (el.type === 'checkbox' || el.type === 'radio') el.checked = false; else el.value = ''; } catch(e) {}\n        });\n        ['p-com_mail','p-com_email','p-com_sms','p-com_tel'].forEach(function(id){ var el = byId(id); if (el) el.checked = true; });\n        var prev = byId('p-foto-preview'); if (prev) { try { prev.removeAttribute('src'); prev.style.display = 'none'; } catch(e) {} }\n    }\n    function setProfileRegistrationLayout(){\n        window.__tiProfileRegistrationMode384 = true;\n        window.__tiProfileRegistrationMode385 = true;\n        var title = byId('lbl-profile-title'); if (title) title.innerText = 'Crea Profilo Cliente\/Utente';\n        var passLbl = byId('lbl-p-pass'); if (passLbl) passLbl.innerText = 'Password*';\n        var cancelBtn = byId('ti-profile-cancel-btn'); if (cancelBtn) cancelBtn.style.display = 'none';\n        var area = byId('ti-user-area'); if (area) area.style.setProperty('display','flex','important');\n        var name = byId('ti-user-name'); if (name) name.textContent = 'Cliente\/Utente';\n        var role = byId('ti-user-role'); if (role) role.textContent = 'Registrazione';\n        enableProfileInputs();\n    }\n\n    window.tiStartUserRegistration385 = function(ev){\n        if (ev) {\n            try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        }\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona una ditta prima di registrarti.'); return false; }\n        window.__tiProfileRegistrationMode384 = true;\n        window.__tiProfileRegistrationMode385 = true;\n        hideLogin();\n        clearProfileFields();\n        setProfileRegistrationLayout();\n        try { if (window.openModal) window.openModal('ti-profile-ov'); } catch(e) {}\n        showProfile();\n        setTimeout(function(){ setProfileRegistrationLayout(); showProfile(); var u = byId('p-user'); if (u) { try { u.focus({preventScroll:true}); } catch(e) { try { u.focus(); } catch(e2) {} } } }, 40);\n        setTimeout(function(){ setProfileRegistrationLayout(); showProfile(); }, 180);\n        setTimeout(function(){ setProfileRegistrationLayout(); showProfile(); }, 500);\n        try { if (window.bindProfileUsernameDuplicatePopup) window.bindProfileUsernameDuplicatePopup(); } catch(e) {}\n        return false;\n    };\n\n    function isRegisterButton(el){\n        if (!el) return false;\n        var btn = el.closest ? el.closest('button,a,input[type=\"button\"],input[type=\"submit\"]') : null;\n        if (!btn) return false;\n        var txt = String(btn.textContent || btn.value || '').toLowerCase();\n        return txt.indexOf('registrati') !== -1 || txt.indexOf('register') !== -1 || btn.id === 'ti-register-user-btn';\n    }\n    document.addEventListener('click', function(ev){\n        if (!isRegisterButton(ev.target)) return;\n        window.tiStartUserRegistration385(ev);\n    }, true);\n\n    function bindButtons(){\n        try {\n            document.querySelectorAll('button,a,input[type=\"button\"],input[type=\"submit\"]').forEach(function(btn){\n                var txt = String(btn.textContent || btn.value || '').toLowerCase();\n                if (txt.indexOf('registrati') === -1 && txt.indexOf('register') === -1 && btn.id !== 'ti-register-user-btn') return;\n                btn.setAttribute('data-ti-registration-button','1');\n                btn.onclick = function(ev){ return window.tiStartUserRegistration385(ev || window.event); };\n            });\n        } catch(e) {}\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bindButtons);\n    bindButtons();\n    setTimeout(bindButtons, 250);\n    setTimeout(bindButtons, 900);\n    setTimeout(bindButtons, 1800);\n    if (typeof MutationObserver !== 'undefined') {\n        try { new MutationObserver(function(){ bindButtons(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n    }\n})();\n<\/script>\n\n\n<script id=\"ti-batch-ai-timeout-386\">\n(function(){\n    \"use strict\";\n    if (window.__tiBatchAiTimeout386Installed) return;\n    window.__tiBatchAiTimeout386Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){\n        return (window.escapeHtml ? window.escapeHtml(String(v == null ? '' : v)) : String(v == null ? '' : v).replace(\/[&<>'\"]\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;',\"'\":'&#39;','\"':'&quot;'}[c];}));\n    }\n    function buildFd(action, data){\n        var fd = window.buildAjaxFormData ? window.buildAjaxFormData(action, data || {}) : new FormData();\n        if (!fd.has('ti_action')) fd.append('ti_action', action);\n        if (!fd.has('action')) fd.append('action', action);\n        Object.keys(data || {}).forEach(function(k){ fd.set(k, data[k]); });\n        return fd;\n    }\n    function ajaxWithTimeout(fd, ms, label){\n        ms = ms || 90000;\n        var controller = null;\n        var timer = null;\n        var opts = {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'};\n        try {\n            if (window.AbortController) {\n                controller = new AbortController();\n                opts.signal = controller.signal;\n            }\n        } catch(e) {}\n        return new Promise(function(resolve, reject){\n            timer = setTimeout(function(){\n                try { if (controller) controller.abort(); } catch(e) {}\n                reject(new Error(label || 'Richiesta non completata nei tempi previsti.'));\n            }, ms);\n            fetch(window.tiUrl, opts).then(function(r){\n                if (!r.ok) throw new Error('HTTP ' + r.status);\n                return r.json();\n            }).then(resolve).catch(function(err){\n                if (err && err.name === 'AbortError') reject(new Error(label || 'Richiesta interrotta per timeout.'));\n                else reject(err);\n            }).finally(function(){ clearTimeout(timer); });\n        });\n    }\n    function shouldCancel(){\n        try { return !!(window.isLongAIProcessCancelled && window.isLongAIProcessCancelled()); } catch(e) { return false; }\n    }\n    function clearCancel(){ try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {} }\n\n    window.runBatchAIGen = function(tableName) {\n        var dbSel = byId('ti-ditta');\n        var replyBox = byId('ti-conf-ai-reply');\n        if (!dbSel || !dbSel.value || dbSel.value === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona prima una ditta.'); return; }\n        if (!replyBox) { if (window.tiAlert) window.tiAlert('Area risposta configurazione non disponibile.'); return; }\n        var photoDetail = ((byId('ti-conf-ai-photo-detail') && byId('ti-conf-ai-photo-detail').value) ? byId('ti-conf-ai-photo-detail').value : '').trim();\n        replyBox.style.display = 'block';\n        replyBox.innerHTML = 'Preparazione elenco record. Priorita a prodotti e servizi senza associazioni file o immagini.<br>Prompt base: attivita ditta + descrizione prodotto\/servizio.';\n        clearCancel();\n        if (window.showProgressPopup) window.showProgressPopup('Generazione foto AI', 'Sto preparando l elenco dei record. Prompt base: attivita ditta e descrizione prodotto o servizio.', {startPercent:2, targetPercent:18, stepMs:350, cancellable:true});\n\n        var initFd = buildFd('ti_ai_config_action', {db:dbSel.value, mode:'batch_ai_images', tables:tableName, detail_prompt:photoDetail});\n        ajaxWithTimeout(initFd, 45000, 'Preparazione elenco record non completata entro 45 secondi. Riprova o riduci il numero di record.').then(function(d){\n            if (!d || !d.success) {\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                if (window.hideProgressPopup) window.hideProgressPopup(false, 'Errore preparazione batch');\n                replyBox.innerHTML = 'Errore preparazione generazione foto AI: ' + esc(d && d.data && d.data.message ? d.data.message : 'Errore sconosciuto');\n                return;\n            }\n            var targets = (d.data && Array.isArray(d.data.targets)) ? d.data.targets : [];\n            if (!targets.length) {\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                if (window.hideProgressPopup) window.hideProgressPopup(true, 'Nessun elemento da elaborare', {notifyUser:false, remindSave:false});\n                replyBox.innerHTML = 'Nessun record trovato nella tabella selezionata.';\n                return;\n            }\n            var index = 0;\n            var okCount = 0;\n            var skipCount = 0;\n            var failCount = 0;\n            var logs = [];\n            var total = targets.length;\n            var itemTimeoutMs = 90000;\n            window.tiBatchAIGenState = {active:true, pendingConfirm:false, generated:[], db:dbSel.value, tableName:tableName, total:total, startedAt:Date.now(), timeout_ms:itemTimeoutMs};\n            var withoutAssoc = (d.data && typeof d.data.without_associations !== 'undefined') ? parseInt(d.data.without_associations,10) : targets.filter(function(x){return !x.has_associations;}).length;\n            var withAssoc = (d.data && typeof d.data.with_associations !== 'undefined') ? parseInt(d.data.with_associations,10) : Math.max(0,total-withoutAssoc);\n            var detailMode = (d.data && d.data.detail_prompt_mode) ? String(d.data.detail_prompt_mode) : '';\n            var detailInfo = (photoDetail && detailMode === 'leaflet') ? '<br>Istruzione aggiuntiva riconosciuta: <b>foglietto illustrativo \/ materiale informativo<\/b>.' : (photoDetail ? '<br>Istruzione aggiuntiva: <b>' + esc(photoDetail) + '<\/b>.' : '');\n            replyBox.innerHTML = 'Generazione immagini AI pronta.<br>Prompt base: <b>attivita ditta + descrizione prodotto\/servizio<\/b>.' + detailInfo + '<br>Priorita: <b>' + withoutAssoc + '<\/b> prodotti\/servizi senza associazioni.<br>Successivi: <b>' + withAssoc + '<\/b> prodotti\/servizi gia con associazioni.<br><span style=\"color:#facc15;\">Ogni record ha timeout controllato: se un elemento si blocca viene saltato e il batch prosegue.<\/span>';\n\n            function refreshDb(){\n                if (window.refreshCurrentConfigDb) return Promise.resolve(window.refreshCurrentConfigDb(dbSel.value)).catch(function(){});\n                var fdDb = buildFd('ti_ai_get_db_data', {db:dbSel.value});\n                return ajaxWithTimeout(fdDb, 30000, 'Rilettura database non completata').then(function(dbRes){\n                    if (dbRes && dbRes.success && dbRes.data) { window.currentDbData = dbRes.data; if (window.renderConfig) window.renderConfig(); }\n                }).catch(function(){});\n            }\n            function finish(){\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                if (window.updateProgressPopup) window.updateProgressPopup(100, 'Generazione foto AI', 'Completamento e verifica salvataggio associazioni.');\n                Promise.resolve(refreshDb()).finally(function(){\n                    if (window.hideProgressPopup) window.hideProgressPopup(true, 'Generazione foto AI completata', {remindSave:false, successMessage:'Attivita AI completata. Immagini associate e verificate: ' + okCount + ' su ' + total + '. Errori\/saltati: ' + failCount + '.'});\n                    replyBox.innerHTML = '\u2705 Generazione immagini AI completata.<br>Immagini associate e verificate: <b>' + okCount + '<\/b> \/ ' + total + '<br>Elementi gia presenti\/saltati: <b>' + skipCount + '<\/b><br>Errori o timeout: <b>' + failCount + '<\/b><br><br>' + logs.slice(0,80).join('<br>');\n                });\n            }\n            function runNext(){\n                if (shouldCancel()) {\n                    if (window.tiBatchAIGenState && window.tiBatchAIGenState.pendingConfirm) return;\n                    replyBox.innerHTML = 'Generazione foto AI interrotta dall utente. Immagini gia generate mantenute: ' + okCount + '. Errori\/saltati: ' + failCount + '.';\n                    if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                    clearCancel();\n                    return;\n                }\n                if (index >= total) { finish(); return; }\n                var item = targets[index] || {};\n                var currentStep = index + 1;\n                var pct = Math.max(5, Math.min(98, Math.round(((index + 0.1) \/ total) * 100)));\n                var itemName = item.name || 'Elemento';\n                var priorityText = item.has_associations ? 'Elemento gia con associazioni' : 'Priorita senza associazioni';\n                if (window.updateProgressPopup) window.updateProgressPopup(pct, 'Generazione foto AI', priorityText + '. Elaborazione ' + currentStep + ' \/ ' + total + ': ' + itemName, itemName);\n                replyBox.innerHTML = 'Generazione immagini AI in corso...<br>' + priorityText + '<br>Elaborazione ' + currentStep + ' \/ ' + total + ': <b>' + esc(itemName) + '<\/b><br><span style=\"color:#94a3b8;\">Timeout massimo per questo record: ' + Math.round(itemTimeoutMs\/1000) + ' secondi.<\/span>';\n                var fd = buildFd('ti_ai_config_action', {db:dbSel.value, mode:'batch_ai_image_one', table:item.table || tableName, row_index:item.row_index, detail_prompt:photoDetail});\n                ajaxWithTimeout(fd, itemTimeoutMs, 'Timeout su record: ' + itemName).then(function(one){\n                    var data = one && one.data ? one.data : {};\n                    if (one && one.success && data.generated) {\n                        okCount++;\n                        if (window.tiBatchAIGenState && Array.isArray(window.tiBatchAIGenState.generated) && (data.added_value || data.filename || data.url)) {\n                            window.tiBatchAIGenState.generated.push({table:data.table || item.table || tableName, row_index:(typeof data.row_index !== 'undefined') ? data.row_index : item.row_index, field:data.image_field || 'Immagine', value:data.added_value || data.filename || data.url, name:data.name || itemName});\n                        }\n                        logs.push('\u2705 ' + esc(data.name || itemName) + ': ' + (data.fallback_logo ? 'foto non disponibile, usato logo ditta' : 'immagine generata e associazione salvata'));\n                    } else if (one && one.success && data.skipped) {\n                        skipCount++;\n                        logs.push('\u23ed\ufe0f ' + esc(data.name || itemName) + ': immagine gia presente');\n                    } else {\n                        failCount++;\n                        logs.push('\u274c ' + esc(data.name || itemName) + ': ' + esc(data.message || 'errore generazione'));\n                    }\n                }).catch(function(err){\n                    failCount++;\n                    logs.push('\u23f1\ufe0f ' + esc(itemName) + ': ' + esc(err && err.message ? err.message : 'timeout o errore rete') + '. Record saltato, proseguo con il successivo.');\n                }).finally(function(){\n                    index++;\n                    setTimeout(runNext, 250);\n                });\n            }\n            runNext();\n        }).catch(function(err){\n            if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n            if (window.hideProgressPopup) window.hideProgressPopup(false, 'Errore batch immagini');\n            replyBox.innerHTML = 'Errore preparazione generazione foto AI: ' + esc(err && err.message ? err.message : 'errore rete');\n        });\n    };\n})();\n<\/script>\n\n\n<script id=\"ti-help-ai-answer-copy-440\">\n(function(){\n    \"use strict\";\n    if (window.__tiHelpAiAnswerCopy440Installed) return;\n    window.__tiHelpAiAnswerCopy440Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function visibleText(node){\n        if (!node) return '';\n        try {\n            var clone = node.cloneNode(true);\n            var removeSel = 'button,.ti-btn,script,style,noscript,input,textarea,select';\n            clone.querySelectorAll(removeSel).forEach(function(el){ el.remove(); });\n            var txt = String(clone.innerText || clone.textContent || '');\n            return clean(txt);\n        } catch(e) { return clean(String(node.innerText || node.textContent || '')); }\n    }\n    function clean(txt){\n        txt = String(txt || '')\n            .replace(\/\\r\/g, '')\n            .replace(\/^\\s*AI_MANUAL_HELP\\s*:\\s*\/gim, '')\n            .replace(\/^\\s*(Istruzione\\s+(?:inviata|ricevuta)[^\\n]*|Help\\s+instruction[^\\n]*)\\s*$\/gim, '')\n            .replace(\/(?:<br\\s*\\\/?>)+\/gi, '\\n')\n            .replace(\/[ \\t]+\\n\/g, '\\n')\n            .replace(\/\\n{3,}\/g, '\\n\\n')\n            .trim();\n        return txt;\n    }\n    function sanitizeHelpBox(){\n        var box = byId('col-help-content');\n        if (!box) return;\n        try {\n            var html = String(box.innerHTML || '');\n            html = html\n                .replace(\/(?:^|<br\\s*\\\/?\\s*>)\\s*AI_MANUAL_HELP\\s*:\\s*\/gi, '')\n                .replace(\/(?:^|<br\\s*\\\/?\\s*>)\\s*(Istruzione\\s+(?:inviata|ricevuta)[^<\\n\\r]*|Help\\s+instruction[^<\\n\\r]*)\\s*(?=<br\\s*\\\/?\\s*>|$)\/gi, '')\n                .replace(\/(<br\\s*\\\/?>(\\s*)){3,}\/gi, '<br><br>');\n            if (html !== String(box.innerHTML || '')) box.innerHTML = html;\n        } catch(e) {}\n    }\n    window.tiSanitizeColumnHelpText357 = function(text){ return clean(text).replace(\/[#*]+\/g, '').trim(); };\n    window.copyColumnHelpText357 = function(){\n        sanitizeHelpBox();\n        var modal = document.querySelector('#ti-col-help-ov .ti-modal');\n        var title = byId('col-help-title');\n        var company = byId('col-help-company');\n        var box = byId('col-help-content');\n        var btn = byId('ti-col-help-copy');\n        var parts = [];\n        var t = visibleText(title); if (t) parts.push(t);\n        var c = visibleText(company); if (c) parts.push(c);\n        var b = visibleText(box); if (b) parts.push(b);\n        var text = clean(parts.join('\\n\\n'));\n        if (!text && modal) text = visibleText(modal);\n        function done(ok){\n            if (!btn) return;\n            var old = btn.getAttribute('data-old-label') || btn.innerText || 'Copia testo';\n            if (!btn.getAttribute('data-old-label')) btn.setAttribute('data-old-label', old);\n            btn.innerText = ok ? 'Testo copiato' : 'Copia manuale';\n            setTimeout(function(){ btn.innerText = old; }, 1400);\n        }\n        function copyTextarea(value){\n            try {\n                var ta = document.createElement('textarea');\n                ta.value = value;\n                ta.setAttribute('readonly','readonly');\n                ta.style.position = 'fixed';\n                ta.style.top = '0';\n                ta.style.left = '0';\n                ta.style.width = '1px';\n                ta.style.height = '1px';\n                ta.style.opacity = '0';\n                ta.style.zIndex = '-1';\n                document.body.appendChild(ta);\n                ta.focus();\n                ta.select();\n                ta.setSelectionRange(0, ta.value.length);\n                var ok = false;\n                try { ok = document.execCommand('copy'); } catch(e) { ok = false; }\n                document.body.removeChild(ta);\n                return !!ok;\n            } catch(e) { return false; }\n        }\n        if (!text) { done(false); return false; }\n        if (copyTextarea(text)) { done(true); return true; }\n        if (navigator.clipboard && navigator.clipboard.writeText) {\n            navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){\n                try { window.prompt('Copia il testo:', text); } catch(e) {}\n                done(false);\n            });\n            return true;\n        }\n        try { window.prompt('Copia il testo:', text); } catch(e) {}\n        done(false);\n        return false;\n    };\n    if (typeof MutationObserver !== 'undefined') {\n        var box = byId('col-help-content');\n        if (box) {\n            try { new MutationObserver(function(){ setTimeout(sanitizeHelpBox, 20); }).observe(box, {childList:true, subtree:true}); } catch(e) {}\n        }\n    }\n    setTimeout(sanitizeHelpBox, 250);\n})();\n<\/script>\n\n\n<!-- ti-ai-media-complete-fix-454 disattivato in 30.9.664: sostituito da ti-ai-media-safe-fix-455 per evitare blocchi UI. -->\n\n\n\n<script id=\"ti-ai-media-safe-fix-455\">\n(function(){\n    \"use strict\";\n    if (window.__tiAIMediaSafeFix455Installed) return;\n    window.__tiAIMediaSafeFix455Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function setImp(el, prop, val){ try { if (el) el.style.setProperty(prop, String(val), 'important'); } catch(e) {} }\n    function isVisible(el){\n        if (!el) return false;\n        try {\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0')) return false;\n            return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n        } catch(e) { return false; }\n    }\n    function validCtx(ctx){ return !!(ctx && typeof ctx === 'object' && ctx.tbl && ctx.key && ctx.idx !== undefined && ctx.idx !== null && ctx.idx !== ''); }\n    function cloneCtx(ctx){ return validCtx(ctx) ? {tbl:String(ctx.tbl), idx:ctx.idx, key:String(ctx.key)} : null; }\n    function saveCtx(ctx){\n        var c = cloneCtx(ctx);\n        if (!c) return null;\n        window.targetUploadContext = c;\n        window.__tiLastFileManagerCtx455 = c;\n        try { sessionStorage.setItem('tiLastFileManagerCtx455', JSON.stringify(c)); } catch(e) {}\n        return c;\n    }\n    function readCtx(){\n        var c = cloneCtx(window.targetUploadContext);\n        if (c) return saveCtx(c);\n        var fm = byId('ti-file-manager-ov');\n        if (fm) {\n            c = cloneCtx({tbl:fm.dataset ? fm.dataset.tbl : '', idx:fm.dataset ? fm.dataset.idx : '', key:fm.dataset ? fm.dataset.key : ''});\n            if (c) return saveCtx(c);\n            c = cloneCtx({tbl:fm.getAttribute('data-ti-context-table') || '', idx:fm.getAttribute('data-ti-context-row') || '', key:fm.getAttribute('data-ti-context-field') || ''});\n            if (c) return saveCtx(c);\n        }\n        c = cloneCtx(window.__tiLastFileManagerCtx455 || window.__tiLastFileManagerCtx360 || window.__tiLastFileAiCtx358 || window.__tiLastUploadContext356);\n        if (c) return saveCtx(c);\n        try {\n            var keys = ['tiLastFileManagerCtx455','tiLastFileManagerCtx360','tiLastFileAiCtx358','tiLastUploadContext356'];\n            for (var i=0;i<keys.length;i++) {\n                var raw = sessionStorage.getItem(keys[i]);\n                if (!raw) continue;\n                c = cloneCtx(JSON.parse(raw));\n                if (c) return saveCtx(c);\n            }\n        } catch(e) {}\n        return null;\n    }\n    function rowFor(ctx){\n        try {\n            if (ctx && window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[ctx.tbl]) return window.currentDbData.Tabelle[ctx.tbl][ctx.idx] || null;\n        } catch(e) {}\n        return null;\n    }\n    function recordLabel(ctx){\n        try { if (window.getRecordLabelForUpload) return window.getRecordLabelForUpload(ctx.tbl, rowFor(ctx), ctx.tbl); } catch(e) {}\n        return ctx && ctx.tbl ? ctx.tbl : '';\n    }\n    function resetAIGen(row){\n        try {\n            if (typeof window.populateAIGenPrompt === 'function') window.populateAIGenPrompt(row);\n            else {\n                var prompt = byId('ai-gen-prompt');\n                if (prompt) {\n                    var label = row ? (row.Descrizione || row.Prodotto || row.Servizio || row.Nome || row.Titolo || '') : '';\n                    prompt.value = (typeof window.buildAIGenPrompt === 'function') ? window.buildAIGenPrompt(label) : String(label || '');\n                }\n            }\n        } catch(e) {}\n        try { var detail = byId('ai-gen-detail'); if (detail) detail.value = ''; } catch(e) {}\n        try { var res = byId('ai-gen-res'); if (res) res.style.display = 'none'; } catch(e) {}\n        try { var img = byId('ai-gen-img'); if (img) img.src = ''; } catch(e) {}\n        try { var btn = byId('ai-gen-btn'); if (btn) { btn.disabled = false; btn.innerText = 'Genera Immagine'; btn.style.display = 'block'; } } catch(e) {}\n        window.currentGenFilename = null;\n    }\n    function activateAI(){\n        var ai = byId('ti-ai-gen-ov');\n        if (!ai) return false;\n        setImp(ai, 'display', 'flex');\n        setImp(ai, 'visibility', 'visible');\n        setImp(ai, 'opacity', '1');\n        setImp(ai, 'pointer-events', 'auto');\n        setImp(ai, 'z-index', '2147483647');\n        try { ai.removeAttribute('aria-hidden'); ai.setAttribute('data-ti-force-front','1'); ai.setAttribute('data-ti-config-child-modal','1'); } catch(e) {}\n        try { if (window.preparePluginModal) window.preparePluginModal(ai); } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n        return true;\n    }\n    function demoteFileManager(){\n        var fm = byId('ti-file-manager-ov');\n        if (!fm) return;\n        try { fm.setAttribute('data-ti-demoted-by-aigen455','1'); } catch(e) {}\n        setImp(fm, 'display', 'flex');\n        setImp(fm, 'visibility', 'visible');\n        setImp(fm, 'opacity', '1');\n        setImp(fm, 'pointer-events', 'none');\n        setImp(fm, 'z-index', '2147483400');\n        try { fm.removeAttribute('aria-hidden'); } catch(e) {}\n    }\n    function restoreFileManager(){\n        var fm = byId('ti-file-manager-ov');\n        if (!fm) return;\n        try {\n            if (Date.now && Date.now() < (window.tiSuppressFileManagerUntil || 0)) {\n                setImp(fm, 'display', 'none');\n                setImp(fm, 'visibility', 'hidden');\n                setImp(fm, 'opacity', '0');\n                setImp(fm, 'pointer-events', 'none');\n                try { fm.setAttribute('aria-hidden','true'); fm.removeAttribute('data-ti-demoted-by-aigen455'); } catch(e) {}\n                return;\n            }\n            if (fm.getAttribute('data-ti-demoted-by-aigen455') !== '1' && window.__tiAIGenReturnToFileManager455 !== true) {\n                return;\n            }\n        } catch(e) {}\n        try { fm.removeAttribute('data-ti-demoted-by-aigen455'); } catch(e) {}\n        setImp(fm, 'display', 'flex');\n        setImp(fm, 'visibility', 'visible');\n        setImp(fm, 'opacity', '1');\n        setImp(fm, 'pointer-events', 'auto');\n        setImp(fm, 'z-index', '2147483500');\n        try { fm.removeAttribute('aria-hidden'); if (window.preparePluginModal) window.preparePluginModal(fm); } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n    }\n    function releaseBlockedUI(){\n        try {\n            var btn = byId('ai-gen-btn');\n            if (btn) { btn.disabled = false; btn.innerText = 'Genera Immagine'; btn.style.display = 'block'; }\n        } catch(e) {}\n        try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n        try {\n            var loader = byId('ti-ai-loader-ov');\n            if (loader) { setImp(loader,'display','none'); setImp(loader,'visibility','hidden'); setImp(loader,'opacity','0'); setImp(loader,'pointer-events','none'); loader.setAttribute('aria-hidden','true'); }\n        } catch(e) {}\n        if (!isVisible(byId('ti-ai-gen-ov'))) restoreFileManager();\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n    }\n    function makeFd(action, data){\n        var form = new FormData();\n        form.append('action', action);\n        form.append('ti_action', action);\n        Object.keys(data || {}).forEach(function(k){ form.append(k, data[k] == null ? '' : data[k]); });\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(form); } catch(e) {}\n        return form;\n    }\n    function parseResponse(resp){\n        return resp.text().then(function(raw){\n            if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + String(raw || '').slice(0, 240));\n            try { return JSON.parse(raw); }\n            catch(e) { throw new Error('Risposta server non JSON: ' + String(raw || '').slice(0, 240)); }\n        });\n    }\n    function postJson(action, data, timeoutMs, preferAjax){\n        var urls = [];\n        if (preferAjax && window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl) urls.push(window.tiUrl);\n        if (window.tiAjaxUrl && urls.indexOf(window.tiAjaxUrl) === -1) urls.push(window.tiAjaxUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            var controller = null, timer = null, done = false;\n            var opts = {method:'POST', body:makeFd(action, data || {}), credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller = new AbortController(); opts.signal = controller.signal; } } catch(e) {}\n            return new Promise(function(resolve, reject){\n                timer = setTimeout(function(){\n                    if (done) return;\n                    done = true;\n                    try { if (controller) controller.abort(); } catch(e) {}\n                    reject(new Error('Timeout: il server non ha risposto nei tempi previsti. Interfaccia sbloccata.'));\n                }, timeoutMs || 120000);\n                fetch(urls[i], opts).then(parseResponse).then(function(d){ if (done) return; done = true; resolve(d); }).catch(function(err){\n                    if (done) return;\n                    done = true;\n                    if (i + 1 < urls.length) attempt(i + 1).then(resolve).catch(reject);\n                    else reject(err);\n                }).finally(function(){ clearTimeout(timer); });\n            });\n        }\n        return attempt(0);\n    }\n    function openAIGenFromManager(){\n        var ctx = readCtx();\n        var row = rowFor(ctx);\n        resetAIGen(row);\n        window.__tiAIGenReturnToFileManager455 = !!ctx;\n        try { if (typeof window.openModal === 'function') window.openModal('ti-ai-gen-ov'); } catch(e) {}\n        demoteFileManager();\n        activateAI();\n        setTimeout(function(){\n            if (!activateAI()) {\n                restoreFileManager();\n                if (window.tiAlert) window.tiAlert('Form AI non disponibile. Riapri Gestione File Associati e riprova.');\n            } else {\n                try { var p = byId('ai-gen-prompt'); if (p) p.focus({preventScroll:true}); } catch(e) {}\n            }\n        }, 120);\n        setTimeout(function(){ if (!isVisible(byId('ti-ai-gen-ov'))) restoreFileManager(); }, 1200);\n        if (!ctx && window.tiAlert) setTimeout(function(){ window.tiAlert('Generatore AI aperto, ma contesto record non rilevato. Dopo la generazione potrai associare manualmente il file.'); }, 180);\n        return false;\n    }\n    window.tiOpenAIGeneratorFromFileManager455 = openAIGenFromManager;\n    window.chooseAIManager = function(){ return openAIGenFromManager(); };\n    window.chooseAI = function(){\n        try { if (window.closeModal) window.closeModal('ti-up-choice-ov'); } catch(e) {}\n        var target = window.targetUploadContext;\n        var row = validCtx(target) ? rowFor(target) : null;\n        if (validCtx(target)) saveCtx(target);\n        resetAIGen(row);\n        window.__tiAIGenReturnToFileManager455 = false;\n        try { if (typeof window.openModal === 'function') window.openModal('ti-ai-gen-ov'); } catch(e) {}\n        activateAI();\n        setTimeout(function(){ activateAI(); }, 80);\n        return false;\n    };\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var btn = t.closest('#ti-file-manager-ov button');\n        if (!btn) return;\n        var oc = String(btn.getAttribute('onclick') || '');\n        var txt = String(btn.textContent || btn.value || '').toLowerCase();\n        if (oc.indexOf('chooseAIManager') === -1 && txt.indexOf('genera con ai') === -1) return;\n        try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        openAIGenFromManager();\n    }, true);\n    setTimeout(function(){\n        try {\n            document.querySelectorAll('#ti-file-manager-ov button').forEach(function(btn){\n                var oc = String(btn.getAttribute('onclick') || ''), txt = String(btn.textContent || '').toLowerCase();\n                if (oc.indexOf('chooseAIManager') !== -1 || txt.indexOf('genera con ai') !== -1) {\n                    btn.onclick = function(e){ if (e) { e.preventDefault(); e.stopPropagation(); } return openAIGenFromManager(); };\n                    btn.setAttribute('data-ti-ai-safe-455','1');\n                }\n            });\n        } catch(e) {}\n    }, 250);\n    function isImageDiagAdmin(){\n        var role = String(window.tiRole || '').toLowerCase();\n        var user = String(window.tiUser || '');\n        return user === 'SSGlobalAdmin' || \/amministratore|administrator|\\badmin\\b\/i.test(role);\n    }\n    window.doAIGen = function(){\n        var promptEl = byId('ai-gen-prompt'), detailEl = byId('ai-gen-detail'), btn = byId('ai-gen-btn'), res = byId('ai-gen-res'), img = byId('ai-gen-img');\n        var prompt = promptEl ? String(promptEl.value || '').trim() : '';\n        var detail = detailEl ? String(detailEl.value || '').trim() : '';\n        if (!prompt && !detail) { if (window.tiAlert) window.tiAlert('Inserisci una descrizione!'); return false; }\n        if (!prompt && detail) prompt = detail;\n        try {\n            if (!\/^Attivit[a\u00e0]\\s+ditta:\/i.test(prompt) && !\/^Verifica anzitutto\/i.test(prompt) && typeof window.buildAIGenPrompt === 'function') {\n                prompt = window.buildAIGenPrompt(prompt);\n                if (promptEl) promptEl.value = prompt;\n            }\n        } catch(e) {}\n        if (detail && prompt.indexOf(detail) === -1) prompt += ' Istruzioni di dettaglio per la generazione foto: ' + detail + '.';\n        var db = (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : 'Global';\n        var jobId = 'img_' + Date.now() + '_' + Math.random().toString(36).slice(2,8);\n        var allowDiag = isImageDiagAdmin();\n        var finished = false;\n        var watchdog = null;\n        var diagTimer = null;\n        var firstDiagSeen = false;\n        function setDiagText(txt){\n            if (!allowDiag) return;\n            try {\n                var cur = byId('ti-loader-current-item');\n                if (cur) { cur.style.display = 'block'; cur.textContent = txt; }\n            } catch(e) {}\n        }\n        function finish(){\n            finished = true;\n            if (watchdog) { try { clearTimeout(watchdog); } catch(e) {} watchdog = null; }\n            if (diagTimer) { try { clearInterval(diagTimer); } catch(e) {} diagTimer = null; }\n            if (btn) { btn.disabled = false; btn.innerText = 'Genera Immagine'; }\n        }\n        function startDiagPolling(){\n            if (!allowDiag) return;\n            setDiagText('Diagnostica generazione: avvio richiesta... ID ' + jobId);\n            diagTimer = setInterval(function(){\n                if (finished) return;\n                postJson('ti_ai_image_diag_status', {job:jobId}, 20000, true).then(function(s){\n                    if (finished || !s || !s.success || !s.data) return;\n                    if (s.data.found && s.data.diag) {\n                        firstDiagSeen = true;\n                        var dg = s.data.diag;\n                        setDiagText('Diagnostica: ' + (dg.step || '') + ' - ' + (dg.message || '') + (dg.time ? ' (' + dg.time + ')' : ''));\n                    } else if (!firstDiagSeen) {\n                        setDiagText('Diagnostica: richiesta inviata, ma il server non ha ancora scritto il primo stato. Se resta cos\u00ec, controllare Network\/AJAX, firewall o 503 hosting.');\n                    }\n                }).catch(function(){\n                    if (!finished && !firstDiagSeen) setDiagText('Diagnostica: endpoint stato non raggiungibile. Possibile blocco AJAX\/server.');\n                });\n            }, 8000);\n        }\n        if (btn) { btn.disabled = true; btn.innerText = 'Generazione in corso...'; }\n        if (res) res.style.display = 'none';\n        try { if (window.showProgressPopup) window.showProgressPopup('AI Image Generator', 'Generazione immagine in corso. In caso di attesa lunga, errore o timeout la form viene sbloccata automaticamente.', {startPercent:6, targetPercent:93, stepMs:900, cancellable:true}); } catch(e) {}\n        startDiagPolling();\n        setTimeout(function(){ if (allowDiag && !finished && !firstDiagSeen) setDiagText('Diagnostica: nessun segnale dal server generazione. Verifica in Console\/Network la richiesta ti_ai_generate_media.'); }, 18000);\n        watchdog = setTimeout(function(){\n            if (finished) return;\n            finish();\n            releaseBlockedUI();\n            try { if (window.hideProgressPopup) window.hideProgressPopup(false, 'Timeout generazione immagine'); } catch(e) {}\n            if (window.tiAlert) window.tiAlert(allowDiag ? (\"La generazione dell'immagine sta impiegando troppo tempo. Ho sbloccato la form. Ultimo stato diagnostico visibile nel popup\/log: \" + jobId) : \"La generazione dell'immagine sta impiegando troppo tempo. Ho sbloccato la form. Riprova tra poco o contatta la ditta.\");\n        }, 210000);\n        postJson('ti_ai_generate_media', {db:db, prompt:prompt, job:jobId}, 300000, true).then(function(d){\n            finish();\n            if (!d || !d.success) throw new Error(d && d.data && d.data.message ? d.data.message : 'Errore generazione immagine.');\n            window.currentGenFilename = d.data && d.data.filename ? d.data.filename : null;\n            if (img && d.data && d.data.url) img.src = d.data.url;\n            if (btn) btn.style.display = 'none';\n            if (res) res.style.display = 'block';\n            try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'Immagine generata', {remindSave:true, successMessage:'Attivita AI completata. Immagine generata correttamente.'}); } catch(e) {}\n            activateAI();\n        }).catch(function(err){\n            finish();\n            releaseBlockedUI();\n            try { if (window.hideProgressPopup) window.hideProgressPopup(false, 'Errore generazione immagine'); } catch(e) {}\n            if (window.tiAlert) window.tiAlert(allowDiag ? ('Errore generazione immagine: ' + (err && err.message ? err.message : 'errore rete')) : 'Non \u00e8 stato possibile completare la generazione immagine. Riprova tra poco o contatta la ditta.');\n        });\n        return false;\n    };\n    function uploadTemp(db, tbl, tempFile, label){ return postJson('ti_ai_media_upload', {db:db, tbl:tbl, temp_file:tempFile, record_label:label || ''}, 120000, true); }\n    window.useAIGen = function(){\n        var filename = window.currentGenFilename;\n        if (!filename) { if (window.tiAlert) window.tiAlert('Nessuna immagine generata da associare.'); return false; }\n        var db = (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : 'Global';\n        var tgt = window.targetUploadContext;\n        try { if (window.closeModal) window.closeModal('ti-ai-gen-ov'); } catch(e) {}\n        if (validCtx(tgt)) {\n            var ctx = saveCtx(tgt);\n            uploadTemp(db, ctx.tbl, filename, recordLabel(ctx)).then(function(d){\n                if (!d || !d.success) throw new Error(d && d.data && d.data.message ? d.data.message : 'errore associazione');\n                try {\n                    if (!window.currentDbData) window.currentDbData = {Tabelle:{}};\n                    if (!window.currentDbData.Tabelle) window.currentDbData.Tabelle = {};\n                    if (!window.currentDbData.Tabelle[ctx.tbl]) window.currentDbData.Tabelle[ctx.tbl] = [];\n                    if (!window.currentDbData.Tabelle[ctx.tbl][ctx.idx]) window.currentDbData.Tabelle[ctx.tbl][ctx.idx] = {};\n                    var oldVal = String(window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] || '');\n                    var arr = oldVal.split(\/[,;\\r\\n]+\/).map(function(x){ return x.trim(); }).filter(Boolean);\n                    var name = (d.data && (d.data.name || d.data.filename)) ? (d.data.name || d.data.filename) : filename;\n                    if (arr.map(function(x){ return x.toLowerCase(); }).indexOf(String(name).toLowerCase()) === -1) arr.push(name);\n                    window.currentDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] = arr.join(', ');\n                    if (!window.modifiedFields) window.modifiedFields = new Set();\n                    window.modifiedFields.add(ctx.tbl + '-' + ctx.idx + '-' + ctx.key);\n                    if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n                    if (window.renderConfig) window.renderConfig();\n                    if (window.openFileManager) window.openFileManager(ctx.tbl, ctx.idx, ctx.key);\n                } catch(e) { restoreFileManager(); }\n            }).catch(function(err){ restoreFileManager(); if (window.tiAlert) window.tiAlert('Errore salvataggio immagine AI: ' + (err && err.message ? err.message : 'errore rete')); });\n            return false;\n        }\n        if (tgt === 'chat') {\n            uploadTemp(db, 'Chat', filename, '').then(function(d){\n                if (!d || !d.success) throw new Error(d && d.data && d.data.message ? d.data.message : 'errore salvataggio chat');\n                window.lastUploadedFile = d.data.url;\n                if (window.rememberLastFileContext) window.rememberLastFileContext(window.lastUploadedFile, 'Ho generato questa immagine. Analizzala.', false);\n                var msg = byId('ti-msg'); if (msg) msg.value = 'Ho generato questa immagine. Analizzala.';\n                var send = byId('ti-send'); if (send) send.click();\n            }).catch(function(err){ if (window.tiAlert) window.tiAlert('Errore salvataggio immagine AI: ' + (err && err.message ? err.message : 'errore rete')); });\n            return false;\n        }\n        var tblName = 'Config';\n        if (typeof tgt === 'string' && tgt.indexOf('cfg-img-') === 0) { var m = tgt.match(\/cfg-img-([^-]+)-\/); if (m) tblName = m[1]; }\n        else if (tgt === 'p-foto') tblName = 'Utenti';\n        var label = (tgt === 'p-foto') ? ((byId('p-user') && byId('p-user').value) || (byId('p-utente') && byId('p-utente').value) || 'utente') : tblName;\n        uploadTemp(db, tblName, filename, label).then(function(d){\n            if (!d || !d.success) throw new Error(d && d.data && d.data.message ? d.data.message : 'errore salvataggio');\n            if (typeof tgt === 'string') {\n                var inp = byId(tgt);\n                if (inp) { inp.value = d.data.name || d.data.filename || filename; inp.dispatchEvent(new Event('change')); if (tgt.indexOf('cfg-') === 0 && window.renderConfig) window.renderConfig(); }\n            }\n            if (window.tiAlert) window.tiAlert('Immagine salvata.');\n        }).catch(function(err){ if (window.tiAlert) window.tiAlert('Errore salvataggio immagine AI: ' + (err && err.message ? err.message : 'errore rete')); });\n        return false;\n    };\n    var prevCloseModal455 = window.closeModal;\n    if (typeof prevCloseModal455 === 'function') {\n        window.closeModal = function(id){\n            var ret = prevCloseModal455.apply(this, arguments);\n            if (id === 'ti-ai-gen-ov') setTimeout(function(){ if (window.__tiAIGenReturnToFileManager455) restoreFileManager(); else releaseBlockedUI(); }, 60);\n            return ret;\n        };\n    }\n    setInterval(function(){\n        var ai = byId('ti-ai-gen-ov');\n        var fm = byId('ti-file-manager-ov');\n        if (fm && fm.getAttribute('data-ti-demoted-by-aigen455') === '1' && !isVisible(ai)) restoreFileManager();\n    }, 2500);\n})();\n<\/script>\n\n\n<script id=\"ti-ai-generator-visual-fix-464\">\n(function(){\n    \"use strict\";\n    if (window.__tiAIGeneratorVisualFix464Installed) return;\n    window.__tiAIGeneratorVisualFix464Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function setImp(el, prop, val){ try { if (el && el.style) el.style.setProperty(prop, String(val), 'important'); } catch(e) {} }\n    function visible(el){\n        if (!el) return false;\n        try {\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0')) return false;\n            return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n        } catch(e) { return false; }\n    }\n    function ensureStyle(){\n        if (byId('ti-ai-generator-visual-fix-464-style')) return;\n        var st = document.createElement('style');\n        st.id = 'ti-ai-generator-visual-fix-464-style';\n        st.textContent = ''\n        + '#ti-ai-gen-ov.ti-ai-gen-visible-464{background:rgba(0,0,0,.22)!important;backdrop-filter:none!important;-webkit-backdrop-filter:none!important;align-items:flex-start!important;justify-content:center!important;padding:16px 8px 26px 8px!important;overflow-y:auto!important;}'\n        + '#ti-ai-gen-ov.ti-ai-gen-visible-464>.ti-modal{margin:12px auto 26px auto!important;box-shadow:0 18px 46px rgba(0,0,0,.55)!important;max-height:calc(100vh - 44px)!important;}'\n        + '#ti-file-manager-ov.ti-hidden-under-aigen-464{display:none!important;visibility:hidden!important;opacity:0!important;pointer-events:none!important;}'\n        + '#ti-ai-outer.ti-mainform-blocked-464 .ti-header,#ti-ai-outer.ti-mainform-blocked-464 #ti-chat-box,#ti-ai-outer.ti-mainform-blocked-464 .ti-input-wrap,#ti-ai-outer.ti-mainform-blocked-464 #ti-order-confirm-host{user-select:none!important;-webkit-user-select:none!important;}'\n        + '#ti-ai-outer.ti-mainform-blocked-464 #ti-chat-box,#ti-ai-outer.ti-mainform-blocked-464 .ti-input-wrap,#ti-ai-outer.ti-mainform-blocked-464 .ti-header,#ti-ai-outer.ti-mainform-blocked-464 #ti-order-confirm-host{filter:grayscale(.06) opacity(.92)!important;}'\n        + '#ti-ai-loader-ov.ti-ai-center-progress-464{position:fixed!important;inset:0!important;width:100vw!important;height:100vh!important;min-height:100vh!important;background:rgba(0,0,0,.42)!important;display:flex!important;align-items:center!important;justify-content:center!important;overflow:hidden!important;pointer-events:auto!important;border-radius:0!important;z-index:2147483647!important;}'\n        + '#ti-ai-loader-ov.ti-ai-center-progress-464 .ti-loader-box{width:min(460px,calc(100vw - 28px))!important;max-width:460px!important;margin:0 auto!important;padding:18px 18px!important;border-radius:16px!important;border:1px solid #38bdf8!important;background:rgba(15,23,42,.985)!important;box-shadow:0 18px 56px rgba(0,0,0,.62)!important;pointer-events:auto!important;position:relative!important;z-index:2147483647!important;}'\n        + '#ti-ai-loader-ov.ti-ai-center-progress-464 .ti-loader-spinner{font-size:22px!important;margin-bottom:6px!important;}'\n        + '#ti-ai-loader-ov.ti-ai-center-progress-464 #ti-loader-title{font-size:15px!important;line-height:1.3!important;}'\n        + '#ti-ai-loader-ov.ti-ai-center-progress-464 #ti-loader-sub{font-size:12px!important;line-height:1.35!important;color:#cbd5e1!important;}'\n        + '#ti-ai-loader-ov.ti-ai-center-progress-464 #ti-loader-progress-wrap{display:block!important;}'\n        + '#ti-ai-loader-ov.ti-ai-center-progress-464 #ti-loader-actions{display:block!important;max-width:240px!important;margin:12px auto 0 auto!important;}'\n        + '@media (max-width:767px){#ti-ai-loader-ov.ti-ai-center-progress-464 .ti-loader-box{width:min(94vw,420px)!important;padding:16px 14px!important;}#ti-ai-gen-ov.ti-ai-gen-visible-464{padding:8px 6px 24px 6px!important;}#ti-ai-gen-ov.ti-ai-gen-visible-464>.ti-modal{width:min(96vw,820px)!important;max-height:calc(100vh - 40px)!important;margin:4px auto 12px auto!important;}}';\n        (document.head || document.documentElement).appendChild(st);\n    }\n    function setMainFormBlocked(blocked){\n        var root = byId('ti-ai-outer');\n        if (!root) return;\n        try { root.classList.remove('ti-mainform-blocked-463'); root.classList[blocked ? 'add' : 'remove']('ti-mainform-blocked-464'); } catch(e) {}\n        try { root.setAttribute('aria-busy', blocked ? 'true' : 'false'); } catch(e) {}\n    }\n    function isProgressVisible(){\n        var loader = byId('ti-ai-loader-ov');\n        return !!(loader && visible(loader));\n    }\n\n    function markAIVisible(){\n        ensureStyle();\n        var ai = byId('ti-ai-gen-ov');\n        if (!ai || !visible(ai)) return false;\n        try { ai.classList.add('ti-ai-gen-visible-464'); } catch(e) {}\n        setImp(ai, 'background', 'rgba(0,0,0,.22)');\n        setImp(ai, 'backdrop-filter', 'none');\n        setImp(ai, '-webkit-backdrop-filter', 'none');\n        setImp(ai, 'align-items', 'flex-start');\n        setImp(ai, 'justify-content', 'center');\n        setImp(ai, 'overflow-y', 'auto');\n        var fm = byId('ti-file-manager-ov');\n        if (fm && fm.getAttribute('data-ti-demoted-by-aigen455') === '1') {\n            try { fm.classList.add('ti-hidden-under-aigen-464'); } catch(e) {}\n            setImp(fm, 'display', 'none');\n            setImp(fm, 'visibility', 'hidden');\n            setImp(fm, 'opacity', '0');\n            setImp(fm, 'pointer-events', 'none');\n        }\n        return true;\n    }\n    function clearVisualState(){\n        var ai = byId('ti-ai-gen-ov');\n        if (ai && !visible(ai)) { try { ai.classList.remove('ti-ai-gen-visible-464'); } catch(e) {} }\n        var fm = byId('ti-file-manager-ov');\n        if (fm && (!visible(ai) || fm.getAttribute('data-ti-demoted-by-aigen455') !== '1')) {\n            try { fm.classList.remove('ti-hidden-under-aigen-464'); } catch(e) {}\n        }\n        var loader = byId('ti-ai-loader-ov');\n        if (loader && !visible(loader)) { try { loader.classList.remove('ti-ai-center-progress-464'); } catch(e) {} }\n        if (!isProgressVisible()) setMainFormBlocked(false);\n    }\n    function centerLoader(){\n        ensureStyle();\n        setMainFormBlocked(true);\n        var loader = byId('ti-ai-loader-ov');\n        if (!loader) return;\n        try { loader.classList.add('ti-ai-center-progress-464'); } catch(e) {}\n        setImp(loader, 'background', 'rgba(0,0,0,.42)');\n        setImp(loader, 'inset', '0');\n        setImp(loader, 'width', '100vw');\n        setImp(loader, 'height', '100vh');\n        setImp(loader, 'display', 'flex');\n        setImp(loader, 'align-items', 'center');\n        setImp(loader, 'justify-content', 'center');\n        setImp(loader, 'pointer-events', 'auto');\n        setImp(loader, 'z-index', '2147483647');\n        var box = loader.querySelector ? loader.querySelector('.ti-loader-box') : null;\n        if (box) {\n            setImp(box, 'pointer-events', 'auto');\n            setImp(box, 'margin', '0 auto');\n            setImp(box, 'max-width', '460px');\n        }\n    }\n    function isImageGenerationTitle(title, subtitle){\n        var s = String(title || '') + ' ' + String(subtitle || '');\n        return \/AI Image Generator|Generazione immagine|immagine in corso|foto AI|batch immagini\/i.test(s);\n    }\n    function installWrappers(){\n        if (typeof window.openModal === 'function' && !window.openModal.__tiVisual464) {\n            var prevOpen = window.openModal;\n            window.openModal = function(id){\n                var ret = prevOpen.apply(this, arguments);\n                if (id === 'ti-ai-gen-ov') { setTimeout(markAIVisible, 0); setTimeout(markAIVisible, 80); setTimeout(markAIVisible, 260); }\n                return ret;\n            };\n            window.openModal.__tiVisual464 = true;\n        }\n        if (typeof window.showPluginModal === 'function' && !window.showPluginModal.__tiVisual464) {\n            var prevShow = window.showPluginModal;\n            window.showPluginModal = function(id){\n                var ret = prevShow.apply(this, arguments);\n                if (id === 'ti-ai-gen-ov') { setTimeout(markAIVisible, 0); setTimeout(markAIVisible, 80); setTimeout(markAIVisible, 260); }\n                if (id === 'ti-ai-loader-ov' && window.__tiCenterAILoader464) { setTimeout(centerLoader, 0); setTimeout(centerLoader, 80); }\n                return ret;\n            };\n            window.showPluginModal.__tiVisual464 = true;\n        }\n        if (typeof window.closeModal === 'function' && !window.closeModal.__tiVisual464) {\n            var prevClose = window.closeModal;\n            window.closeModal = function(id){\n                var ret = prevClose.apply(this, arguments);\n                if (id === 'ti-ai-gen-ov' || id === 'ti-ai-loader-ov') setTimeout(clearVisualState, 80);\n                return ret;\n            };\n            window.closeModal.__tiVisual464 = true;\n        }\n        if (typeof window.showProgressPopup === 'function' && !window.showProgressPopup.__tiVisual464) {\n            var prevProgress = window.showProgressPopup;\n            window.showProgressPopup = function(title, subtitle, opts){\n                var centered = isImageGenerationTitle(title, subtitle) || (opts && opts.centered === true);\n                window.__tiCenterAILoader464 = !!centered;\n                var ret = prevProgress.apply(this, arguments);\n                if (centered) { centerLoader(); setTimeout(centerLoader, 80); setTimeout(centerLoader, 250); }\n                return ret;\n            };\n            window.showProgressPopup.__tiVisual464 = true;\n        }\n        if (typeof window.hideProgressPopup === 'function' && !window.hideProgressPopup.__tiVisual464) {\n            var prevHide = window.hideProgressPopup;\n            window.hideProgressPopup = function(){\n                var ret = prevHide.apply(this, arguments);\n                setTimeout(function(){ window.__tiCenterAILoader464 = false; clearVisualState(); setMainFormBlocked(false); }, 700);\n                return ret;\n            };\n            window.hideProgressPopup.__tiVisual464 = true;\n        }\n    }\n    function boot(){\n        ensureStyle();\n        installWrappers();\n        markAIVisible();\n        clearVisualState();\n        setMainFormBlocked(isProgressVisible());\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, {once:true}); else boot();\n    setTimeout(boot, 250);\n    setTimeout(boot, 1200);\n    setInterval(function(){ if (visible(byId('ti-ai-gen-ov'))) markAIVisible(); else clearVisualState(); }, 1200);\n})();\n<\/script>\n\n\n<script id=\"ti-ai-generator-cancel-diag-fix-469\">\n(function(){\n    \"use strict\";\n    if (window.__tiAIGeneratorCancelDiagFix469Installed) return;\n    window.__tiAIGeneratorCancelDiagFix469Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function setImp(el, prop, val){ try { if (el && el.style) el.style.setProperty(prop, String(val), 'important'); } catch(e) {} }\n    function txt(v){ return String(v == null ? '' : v); }\n    function isAdmin(){\n        var role = txt(window.tiRole || '').toLowerCase();\n        var user = txt(window.tiUser || '');\n        return user === 'SSGlobalAdmin' || \/amministratore|administrator|\\badmin\\b\/i.test(role);\n    }\n    function resetBtn(){\n        try { var btn = byId('ai-gen-btn'); if (btn) { btn.disabled = false; btn.innerText = 'Genera Immagine'; btn.style.display = 'block'; } } catch(e) {}\n    }\n    function hideLoaderHard(){\n        try { window.__tiCenterAILoader464 = false; window.__tiCenterAILoader463 = false; window.__tiCenterAILoader462 = false; } catch(e) {}\n        try {\n            var loader = byId('ti-ai-loader-ov');\n            if (loader) {\n                ['ti-ai-center-progress-464','ti-ai-center-progress-463','ti-ai-center-progress-462','ti-ai-compact-progress-458'].forEach(function(c){ try { loader.classList.remove(c); } catch(e) {} });\n                setImp(loader, 'display', 'none'); setImp(loader, 'visibility', 'hidden'); setImp(loader, 'opacity', '0'); setImp(loader, 'pointer-events', 'none');\n                loader.setAttribute('aria-hidden','true');\n            }\n        } catch(e) {}\n        try {\n            var root = byId('ti-ai-outer');\n            if (root) {\n                ['ti-mainform-blocked-469','ti-mainform-blocked-464','ti-mainform-blocked-463'].forEach(function(c){ try { root.classList.remove(c); } catch(e) {} });\n                root.setAttribute('aria-busy','false');\n            }\n        } catch(e) {}\n        try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n        resetBtn();\n    }\n    function hasGeneratedPreview(){\n        try {\n            var res = byId('ai-gen-res'), img = byId('ai-gen-img');\n            return !!(window.currentGenFilename && img && String(img.src || '').trim() !== '' && (!res || res.style.display !== 'none'));\n        } catch(e) { return false; }\n    }\n    function hideLoaderKeepPreview(){\n        try { window.__tiCenterAILoader464 = false; window.__tiCenterAILoader463 = false; window.__tiCenterAILoader462 = false; } catch(e) {}\n        try {\n            var loader = byId('ti-ai-loader-ov');\n            if (loader) {\n                ['ti-ai-center-progress-464','ti-ai-center-progress-463','ti-ai-center-progress-462','ti-ai-compact-progress-458'].forEach(function(c){ try { loader.classList.remove(c); } catch(e) {} });\n                setImp(loader, 'display', 'none'); setImp(loader, 'visibility', 'hidden'); setImp(loader, 'opacity', '0'); setImp(loader, 'pointer-events', 'none');\n                loader.setAttribute('aria-hidden','true');\n            }\n        } catch(e) {}\n        try {\n            var root = byId('ti-ai-outer');\n            if (root) {\n                ['ti-mainform-blocked-469','ti-mainform-blocked-464','ti-mainform-blocked-463'].forEach(function(c){ try { root.classList.remove(c); } catch(e) {} });\n                root.setAttribute('aria-busy','false');\n            }\n        } catch(e) {}\n        try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n        try { var btn = byId('ai-gen-btn'); if (btn && window.currentGenFilename) { btn.disabled = false; btn.style.display = 'none'; } } catch(e) {}\n        return false;\n    }\n\n    function showDiag(op, message){\n        if (!op || !op.allowDiag) return;\n        try { var cur = byId('ti-loader-current-item'); if (cur) { cur.style.display = 'block'; cur.textContent = message; } } catch(e) {}\n    }\n    function abortOp(op, silent){\n        op = op || window.__tiCurrentAIGenOp469;\n        if (op) {\n            op.cancelled = true;\n            try { (op.timers || []).forEach(function(t){ clearTimeout(t); clearInterval(t); }); } catch(e) {}\n            try { (op.controllers || []).forEach(function(c){ try { c.abort(); } catch(e) {} }); } catch(e) {}\n        }\n        try { (window.__tiAIGenControllers469 || []).forEach(function(c){ try { c.abort(); } catch(e) {} }); } catch(e) {}\n        window.__tiAIGenControllers469 = [];\n        hideLoaderHard();\n        if (!silent && window.tiAlert) window.tiAlert('Processo AI interrotto. La form e stata sbloccata. Per completare l operazione devi ripeterla.');\n        return false;\n    }\n    window.tiAbortAIGen469 = abortOp;\n    window.cancelLongAIProcess = function(){\n        try { if (hasGeneratedPreview()) return hideLoaderKeepPreview(); } catch(e) {}\n        try { if (typeof window.confirmCancelBatchAIGen === 'function' && window.confirmCancelBatchAIGen()) return false; } catch(e) {}\n        return abortOp(window.__tiCurrentAIGenOp469, false);\n    };\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (t && t.closest && t.closest('#ti-loader-close')) {\n            try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n            window.cancelLongAIProcess();\n            return false;\n        }\n    }, true);\n\n    function makeFd(action, data){\n        var fd = new FormData();\n        fd.append('action', action);\n        fd.append('ti_action', action);\n        Object.keys(data || {}).forEach(function(k){ fd.append(k, data[k] == null ? '' : data[k]); });\n        try { fd.append('upload_cleanup_token', currentUploadCleanupToken859(true)); fd.append('guided_upload_target', state.target || ''); } catch(e859fd) {}\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        return fd;\n    }\n    function parseResponse(resp){\n        return resp.text().then(function(raw){\n            if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + String(raw || '').slice(0, 220));\n            try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta server non JSON: ' + String(raw || '').slice(0, 220)); }\n        });\n    }\n    function postJson469(action, data, timeoutMs, op, preferAjax){\n        var urls = [];\n        if (preferAjax && window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (window.tiAjaxUrl && urls.indexOf(window.tiAjaxUrl) === -1) urls.push(window.tiAjaxUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            if (op && op.cancelled) return Promise.reject(new Error('Operazione interrotta dall utente.'));\n            var controller = null, timer = null, done = false;\n            var opts = {method:'POST', body:makeFd(action, data || {}), credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller = new AbortController(); opts.signal = controller.signal; } } catch(e) {}\n            if (controller) {\n                window.__tiAIGenControllers469 = window.__tiAIGenControllers469 || [];\n                window.__tiAIGenControllers469.push(controller);\n                if (op) { op.controllers = op.controllers || []; op.controllers.push(controller); }\n            }\n            return new Promise(function(resolve, reject){\n                timer = setTimeout(function(){\n                    if (done) return;\n                    done = true;\n                    try { if (controller) controller.abort(); } catch(e) {}\n                    reject(new Error('Timeout: il server non ha risposto.'));\n                }, timeoutMs || 120000);\n                fetch(urls[i], opts).then(parseResponse).then(function(d){\n                    if (done) return; done = true;\n                    if (op && op.cancelled) reject(new Error('Operazione interrotta dall utente.')); else resolve(d);\n                }).catch(function(err){\n                    if (done) return; done = true;\n                    if (op && op.cancelled) { reject(new Error('Operazione interrotta dall utente.')); return; }\n                    if (i + 1 < urls.length) attempt(i + 1).then(resolve).catch(reject); else reject(err);\n                }).finally(function(){ try { clearTimeout(timer); } catch(e) {} });\n            });\n        }\n        return attempt(0);\n    }\n    function buildPrompt(prompt, detail, promptEl){\n        try {\n            if (!\/^Attivit[a\u00e0]\\s+ditta:\/i.test(prompt) && !\/^Verifica anzitutto\/i.test(prompt) && typeof window.buildAIGenPrompt === 'function') {\n                prompt = window.buildAIGenPrompt(prompt);\n                if (promptEl) promptEl.value = prompt;\n            }\n        } catch(e) {}\n        if (detail && prompt.indexOf(detail) === -1) prompt += ' Istruzioni di dettaglio per la generazione foto: ' + detail + '.';\n        return prompt;\n    }\n    window.doAIGen = function(){\n        var promptEl = byId('ai-gen-prompt'), detailEl = byId('ai-gen-detail'), btn = byId('ai-gen-btn'), res = byId('ai-gen-res'), img = byId('ai-gen-img');\n        var prompt = promptEl ? txt(promptEl.value).trim() : '';\n        var detail = detailEl ? txt(detailEl.value).trim() : '';\n        if (!prompt && !detail) { if (window.tiAlert) window.tiAlert('Inserisci una descrizione.'); return false; }\n        if (!prompt && detail) prompt = detail;\n        prompt = buildPrompt(prompt, detail, promptEl);\n        var db = (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : 'Global';\n        var op = {id:'img_' + Date.now() + '_' + Math.random().toString(36).slice(2,8), cancelled:false, timers:[], controllers:[], allowDiag:isAdmin()};\n        window.__tiCurrentAIGenOp469 = op;\n        if (btn) { btn.disabled = true; btn.innerText = 'Generazione in corso...'; }\n        if (res) res.style.display = 'none';\n        try { if (window.showProgressPopup) window.showProgressPopup('AI Image Generator', 'Generazione immagine in corso. Premi Esci per interrompere e sbloccare subito la form.', {startPercent:6, targetPercent:93, stepMs:900, cancellable:true}); } catch(e) {}\n        op.timers.push(setTimeout(function(){\n            if (op.cancelled || window.__tiCurrentAIGenOp469 !== op) return;\n            showDiag(op, 'Diagnostica: se resta al 6%, la risposta server non e ancora arrivata. Controllo endpoint in corso...');\n        }, 8000));\n        if (op.allowDiag) {\n            showDiag(op, 'Diagnostica: invio richiesta al server. ID ' + op.id);\n            op.timers.push(setInterval(function(){\n                if (op.cancelled || window.__tiCurrentAIGenOp469 !== op) return;\n                postJson469('ti_ai_image_diag_status', {job:op.id}, 15000, op, true).then(function(s){\n                    if (op.cancelled || window.__tiCurrentAIGenOp469 !== op || !s || !s.success || !s.data) return;\n                    if (s.data.found && s.data.diag) showDiag(op, 'Diagnostica: ' + (s.data.diag.step || '') + ' - ' + (s.data.diag.message || ''));\n                    else showDiag(op, 'Diagnostica: nessun log server ancora scritto. Possibile blocco prima della funzione PHP: AJAX\/firewall\/hosting.');\n                }).catch(function(){ if (!op.cancelled) showDiag(op, 'Diagnostica: endpoint stato non raggiungibile. Controllare Network\/AJAX o errore server.'); });\n            }, 10000));\n        }\n        op.timers.push(setTimeout(function(){\n            if (op.cancelled || window.__tiCurrentAIGenOp469 !== op) return;\n            abortOp(op, true);\n            if (window.tiAlert) window.tiAlert(op.allowDiag ? ('Timeout generazione immagine. Form sbloccata. Se non e comparso nessun log server, il blocco e prima del PHP: AJAX, firewall, hosting o 503. ID: ' + op.id) : 'La generazione immagine sta impiegando troppo tempo. La form e stata sbloccata. Riprova tra poco.');\n        }, 180000));\n        postJson469('ti_ai_generate_media', {db:db, prompt:prompt, job:op.id}, 240000, op, true).then(function(d){\n            if (op.cancelled || window.__tiCurrentAIGenOp469 !== op) return;\n            op.cancelled = true;\n            try { (op.timers || []).forEach(function(t){ clearTimeout(t); clearInterval(t); }); } catch(e) {}\n            if (btn) { btn.disabled = false; btn.innerText = 'Genera Immagine'; }\n            if (!d || !d.success) throw new Error(d && d.data && d.data.message ? d.data.message : 'Errore generazione immagine.');\n            window.currentGenFilename = d.data && d.data.filename ? d.data.filename : null;\n            if (img && d.data && d.data.url) img.src = d.data.url;\n            if (btn) btn.style.display = 'none';\n            if (res) res.style.display = 'block';\n            try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'Immagine generata', {remindSave:true, notifyUser:false, successMessage:'Attivita AI completata. Immagine generata correttamente.'}); } catch(e) {}\n            setTimeout(hideLoaderKeepPreview, 120);\n            setTimeout(hideLoaderKeepPreview, 700);\n            if (op.allowDiag && window.tiAlert) setTimeout(function(){ window.tiAlert('Immagine generata correttamente. La preview resta disponibile: puoi usarla o rigenerarla.'); }, 350);\n        }).catch(function(err){\n            if (op.cancelled || window.__tiCurrentAIGenOp469 !== op) return;\n            abortOp(op, true);\n            if (window.tiAlert) window.tiAlert(op.allowDiag ? ('Errore generazione immagine: ' + (err && err.message ? err.message : 'errore rete')) : 'Non e stato possibile completare la generazione immagine. Riprova tra poco.');\n        });\n        return false;\n    };\n})();\n<\/script>\n\n\n<script id=\"ti-file-ai-save-pc-fix-471\">\n(function(){\n    \"use strict\";\n    if (window.__tiFileAiSavePcFix471Installed) return;\n    window.__tiFileAiSavePcFix471Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function val(v){ return String(v == null ? '' : v); }\n    function validCtx(ctx){ return !!(ctx && typeof ctx === 'object' && ctx.tbl && ctx.key && ctx.idx !== undefined && ctx.idx !== null && ctx.idx !== ''); }\n    function cloneCtx(ctx){ return validCtx(ctx) ? {tbl:String(ctx.tbl), idx:ctx.idx, key:String(ctx.key), id:ctx.id || ''} : null; }\n    function saveCtx(ctx){\n        var c = cloneCtx(ctx);\n        if (!c) return null;\n        window.targetUploadContext = c;\n        window.__tiLastFileManagerCtx471 = c;\n        window.__tiLastFileManagerCtx455 = c;\n        try { sessionStorage.setItem('tiLastFileManagerCtx471', JSON.stringify(c)); } catch(e) {}\n        return c;\n    }\n    function readCtx(){\n        var c = cloneCtx(window.targetUploadContext || window.__tiLastFileManagerCtx471 || window.__tiLastFileManagerCtx455 || window.__tiLastFileManagerCtx360 || window.__tiLastFileAiCtx358 || window.__tiLastUploadContext356);\n        if (c) return saveCtx(c);\n        var fm = byId('ti-file-manager-ov');\n        if (fm) {\n            c = cloneCtx({tbl:fm.dataset ? (fm.dataset.tbl || fm.dataset.table || '') : '', idx:fm.dataset ? (fm.dataset.idx || fm.dataset.row || '') : '', key:fm.dataset ? (fm.dataset.key || fm.dataset.field || '') : ''});\n            if (c) return saveCtx(c);\n            c = cloneCtx({tbl:fm.getAttribute('data-ti-context-table') || '', idx:fm.getAttribute('data-ti-context-row') || '', key:fm.getAttribute('data-ti-context-field') || ''});\n            if (c) return saveCtx(c);\n        }\n        try {\n            var keys = ['tiLastFileManagerCtx471','tiLastFileManagerCtx455','tiLastFileManagerCtx360','tiLastFileAiCtx358','tiLastUploadContext356'];\n            for (var i=0;i<keys.length;i++) {\n                var raw = sessionStorage.getItem(keys[i]);\n                if (!raw) continue;\n                c = cloneCtx(JSON.parse(raw));\n                if (c) return saveCtx(c);\n            }\n        } catch(e) {}\n        return null;\n    }\n    function rowFor(ctx){\n        try { return (ctx && window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[ctx.tbl]) ? (window.currentDbData.Tabelle[ctx.tbl][ctx.idx] || null) : null; } catch(e) { return null; }\n    }\n    function recordLabel(ctx){\n        try { if (window.getRecordLabelForUpload) return window.getRecordLabelForUpload(ctx.tbl, rowFor(ctx), ctx.tbl); } catch(e) {}\n        return ctx && ctx.tbl ? ctx.tbl : '';\n    }\n    function dbName(){ return (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : 'Global'; }\n    function makeFd(action, data, file){\n        var fd = new FormData();\n        fd.append('action', action); fd.append('ti_action', action);\n        Object.keys(data || {}).forEach(function(k){ fd.append(k, data[k] == null ? '' : data[k]); });\n        if (file) fd.append('file', file);\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        return fd;\n    }\n    function post(action, data, file, timeoutMs){\n        var urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            var controller = null, timer = null, done = false;\n            var opts = {method:'POST', body:makeFd(action, data || {}, file || null), credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller = new AbortController(); opts.signal = controller.signal; } } catch(e) {}\n            return new Promise(function(resolve, reject){\n                timer = setTimeout(function(){ if (done) return; done = true; try { if (controller) controller.abort(); } catch(e) {} reject(new Error('Timeout server.')); }, timeoutMs || 120000);\n                fetch(urls[i], opts).then(function(r){ return r.text().then(function(raw){ if (!r.ok) throw new Error('HTTP ' + r.status + ': ' + raw.slice(0,160)); try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta server non JSON: ' + raw.slice(0,160)); } }); }).then(function(d){ if (done) return; done = true; resolve(d); }).catch(function(err){ if (done) return; done = true; if (i + 1 < urls.length) attempt(i+1).then(resolve).catch(reject); else reject(err); }).finally(function(){ try { clearTimeout(timer); } catch(e) {} });\n            });\n        }\n        return attempt(0);\n    }\n    function setBusy(on, label){\n        try {\n            var b = byId('ai-gen-res') ? byId('ai-gen-res').querySelector('button[onclick*=\"useAIGen\"]') : null;\n            if (b) { b.disabled = !!on; b.innerText = on ? (label || 'Salvataggio...') : '\u2705 Usa questa Immagine'; }\n        } catch(e) {}\n    }\n    function applyLocal(ctx, name, value, dbVersion){\n        ctx = saveCtx(ctx);\n        if (!ctx || !name) return;\n        try {\n            if (!window.currentDbData) window.currentDbData = {Tabelle:{}};\n            if (!window.currentDbData.Tabelle) window.currentDbData.Tabelle = {};\n            if (!window.currentDbData.Tabelle[ctx.tbl]) window.currentDbData.Tabelle[ctx.tbl] = [];\n            if (!window.currentDbData.Tabelle[ctx.tbl][ctx.idx]) window.currentDbData.Tabelle[ctx.tbl][ctx.idx] = {};\n            var row = window.currentDbData.Tabelle[ctx.tbl][ctx.idx];\n            row[ctx.key] = value || (function(){\n                var oldVal = val(row[ctx.key] || '');\n                var arr = oldVal.split(\/[,;\\r\\n]+\/).map(function(x){ return x.trim(); }).filter(Boolean);\n                if (arr.map(function(x){ return x.toLowerCase(); }).indexOf(val(name).toLowerCase()) === -1) arr.push(name);\n                return arr.join(', ');\n            })();\n            if (dbVersion && window.currentDbData) window.currentDbData.__db_version = dbVersion;\n            if (window.configOriginalDbData) {\n                try {\n                    if (!window.configOriginalDbData.Tabelle) window.configOriginalDbData.Tabelle = {};\n                    if (!window.configOriginalDbData.Tabelle[ctx.tbl]) window.configOriginalDbData.Tabelle[ctx.tbl] = [];\n                    if (!window.configOriginalDbData.Tabelle[ctx.tbl][ctx.idx]) window.configOriginalDbData.Tabelle[ctx.tbl][ctx.idx] = {};\n                    window.configOriginalDbData.Tabelle[ctx.tbl][ctx.idx][ctx.key] = row[ctx.key];\n                    if (dbVersion) window.configOriginalDbData.__db_version = dbVersion;\n                } catch(e) {}\n            }\n            if (!window.modifiedFields) window.modifiedFields = new Set();\n            try { window.modifiedFields.delete(ctx.tbl + '-' + ctx.idx + '-' + ctx.key); } catch(e) {}\n            if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n            if (window.renderConfig) window.renderConfig();\n        } catch(e) {}\n    }\n    function attachField(db, ctx, filename){\n        ctx = saveCtx(ctx);\n        if (!ctx) return Promise.reject(new Error('Contesto riga\/campo non disponibile.'));\n        return post('ti_ai_media_attach_field', {db:db, tbl:ctx.tbl, idx:ctx.idx, id:ctx.id || ((rowFor(ctx) && rowFor(ctx).ID) || ''), key:ctx.key, file:filename}, null, 120000).then(function(d){\n            if (!d || !d.success) throw new Error(d && d.data && d.data.message ? d.data.message : 'Associazione non riuscita.');\n            var data = d.data || {};\n            applyLocal(ctx, data.name || filename, data.value || '', data.db_version || '');\n            return data;\n        });\n    }\n    function finishManagerSuccess(ctx, name, msg){\n        try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'File associato', {notifyUser:false}); } catch(e) {}\n        try { if (window.closeModal) window.closeModal('ti-ai-gen-ov'); } catch(e) {}\n        try { if (window.openFileManager) setTimeout(function(){ window.openFileManager(ctx.tbl, ctx.idx, ctx.key); }, 150); } catch(e) {}\n        if (window.tiAlert) setTimeout(function(){ window.tiAlert(msg || ('File associato e salvato nel database: ' + name)); }, 250);\n    }\n\n    var prevOpenFM = window.openFileManager;\n    if (typeof prevOpenFM === 'function' && !prevOpenFM.__tiSavePc471) {\n        window.openFileManager = function(tbl, idx, key){\n            var ctx = saveCtx({tbl:tbl, idx:idx, key:key});\n            var ret = prevOpenFM.apply(this, arguments);\n            try {\n                var fm = byId('ti-file-manager-ov');\n                if (fm && ctx) { fm.dataset.tbl = ctx.tbl; fm.dataset.idx = ctx.idx; fm.dataset.key = ctx.key; fm.setAttribute('data-ti-context-table', ctx.tbl); fm.setAttribute('data-ti-context-row', ctx.idx); fm.setAttribute('data-ti-context-field', ctx.key); }\n            } catch(e) {}\n            return ret;\n        };\n        window.openFileManager.__tiSavePc471 = true;\n    }\n\n    \/\/ 30.9.664: vecchio flusso PC 471 rimosso. La nuova gestione diretta usa tiDirectUploadFromInput616 senza ti-file-in.\n\n    var prevUseAI = window.useAIGen;\n    window.useAIGen = function(){\n        var filename = window.currentGenFilename;\n        if (!filename) { if (window.tiAlert) window.tiAlert('Nessuna immagine generata da associare.'); return false; }\n        var ctx = readCtx();\n        if (!validCtx(ctx)) {\n            if (window.tiAlert) window.tiAlert('Immagine generata, ma contesto riga\/campo non disponibile. Non chiudo la preview: riapri Gestione File Associati dalla riga corretta oppure scarica\/rigenera.');\n            return false;\n        }\n        var db = dbName();\n        setBusy(true, 'Salvataggio immagine...');\n        try { if (window.showProgressPopup) window.showProgressPopup('Salvataggio immagine', 'Associazione e salvataggio nel database in corso.', {startPercent:15, targetPercent:88, stepMs:500, cancellable:false}); } catch(e) {}\n        post('ti_ai_media_upload', {db:db, tbl:ctx.tbl, temp_file:filename, record_label:recordLabel(ctx)}, null, 180000).then(function(d){\n            if (!d || !d.success) throw new Error(d && d.data && d.data.message ? d.data.message : 'Salvataggio file AI non riuscito.');\n            var name = (d.data && (d.data.name || d.data.filename)) ? (d.data.name || d.data.filename) : filename;\n            return attachField(db, ctx, name).then(function(){ return name; });\n        }).then(function(name){\n            setBusy(false);\n            finishManagerSuccess(ctx, name, 'Immagine AI associata e salvata nel database.');\n        }).catch(function(err){\n            setBusy(false);\n            try { if (window.hideProgressPopup) window.hideProgressPopup(false, 'Errore salvataggio immagine'); } catch(e) {}\n            if (window.tiAlert) window.tiAlert('Errore salvataggio immagine AI: ' + (err && err.message ? err.message : 'errore rete') + '. La preview resta aperta: puoi riprovare senza perdere l immagine.');\n        });\n        return false;\n    };\n})();\n<\/script>\n\n\n<script id=\"ti-config-email-sms-flow-479\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigEmailSmsFlow479Installed) return;\n    window.__tiConfigEmailSmsFlow479Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function clean(v){ return String(v == null ? '' : v).replace(\/\\r\/g, '').trim(); }\n    function setReply(html){\n        var rb = byId('ti-conf-ai-reply');\n        if (rb) { rb.style.display = 'block'; rb.innerHTML = html; }\n    }\n    function clearPrompt(){\n        var p = byId('ti-conf-ai-prompt');\n        if (p) { p.value = ''; try { p.focus({preventScroll:true}); } catch(e) {} }\n    }\n    function dbName(){ return (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : 'NEW_DB'; }\n    function makeFd(action, data){\n        var fd = new FormData();\n        fd.append('action', action);\n        fd.append('ti_action', action);\n        Object.keys(data || {}).forEach(function(k){ fd.append(k, data[k] == null ? '' : data[k]); });\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        return fd;\n    }\n    function post(action, data){\n        var urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (window.ajaxurl && urls.indexOf(window.ajaxurl) === -1) urls.push(window.ajaxurl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            var controller = null, timer = null, done = false;\n            var opts = {method:'POST', body:makeFd(action, data || {}), credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller = new AbortController(); opts.signal = controller.signal; } } catch(e) {}\n            return new Promise(function(resolve, reject){\n                timer = setTimeout(function(){\n                    if (done) return;\n                    done = true;\n                    try { if (controller) controller.abort(); } catch(e) {}\n                    reject(new Error('Timeout invio: il server non ha risposto. Verifica API Key Infobip, endpoint, firewall\/hosting e, per utenti non amministratori, stato ditta non Demo.'));\n                }, 70000);\n                fetch(urls[i], opts).then(function(r){\n                    return r.text().then(function(raw){\n                        if (!r.ok) throw new Error('HTTP ' + r.status + ': ' + raw.slice(0, 240));\n                        try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta server non JSON: ' + raw.slice(0, 240)); }\n                    });\n                }).then(function(res){ done = true; resolve(res); }).catch(function(err){\n                    if (done) return;\n                    done = true;\n                    if (i + 1 < urls.length) attempt(i + 1).then(resolve).catch(reject);\n                    else reject(err);\n                }).finally(function(){ try { clearTimeout(timer); } catch(e) {} });\n            });\n        }\n        return attempt(0);\n    }\n    function validEmail(v){ return \/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(clean(v)); }\n    function extractEmail(v){ var m = clean(v).match(\/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\/i); return m ? m[0] : ''; }\n    function normalizePhone(v){\n        var s = clean(v).replace(\/[^0-9+]\/g, '');\n        if (s.indexOf('00') === 0) s = '+' + s.slice(2);\n        return s;\n    }\n    function validPhone(v){ var s = normalizePhone(v); return \/^\\+?[0-9]{8,15}$\/.test(s); }\n    function wantsConfigSend(text){\n        var t = clean(text).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'');\n        if (!t) return false;\n        var action = \/\\b(invia|manda|mandare|spedisc|inoltra|trasmetti|scrivi)\\b\/.test(t);\n        var channel = \/\\b(email|e-mail|mail|sms|messaggio|telefono|telefonico)\\b\/.test(t);\n        return action && channel;\n    }\n    function channelsFrom(text){\n        var t = clean(text).toLowerCase();\n        var ch = [];\n        if (\/email|e-mail|mail\/.test(t)) ch.push('email');\n        if (\/sms|telefono|telefonico\/.test(t)) ch.push('sms');\n        if (!ch.length && \/messaggio\/.test(t)) ch.push('sms');\n        return ch.length ? ch : ['email'];\n    }\n    function askNext(flow){\n        window.__tiConfigCommFlow479 = flow;\n        if (flow.channels.indexOf('email') !== -1 && !flow.email) {\n            flow.stage = 'ask_email';\n            setReply('<b>Invio email da Configurazione<\/b><br><br>Indica l\\'indirizzo email del destinatario.<br><small>Scrivi NO per annullare.<\/small>');\n            clearPrompt();\n            return true;\n        }\n        if (flow.channels.indexOf('sms') !== -1 && !flow.phone) {\n            flow.stage = 'ask_phone';\n            setReply('<b>Invio SMS da Configurazione<\/b><br><br>Indica il numero telefonico del destinatario.<br><small>Scrivi NO per annullare.<\/small>');\n            clearPrompt();\n            return true;\n        }\n        if (!flow.text) {\n            flow.stage = 'ask_text';\n            var dest = [];\n            if (flow.email) dest.push('email: <b>' + esc(flow.email) + '<\/b>');\n            if (flow.phone) dest.push('SMS: <b>' + esc(flow.phone) + '<\/b>');\n            setReply('<b>Testo da inviare<\/b><br><br>Destinatario: ' + dest.join(' + ') + '.<br>Scrivi ora il testo del messaggio da inviare.<br><small>Scrivi NO per annullare.<\/small>');\n            clearPrompt();\n            return true;\n        }\n        return sendFlow(flow);\n    }\n    function startFlow(text){\n        var flow = {stage:'start', channels:channelsFrom(text), email:'', phone:'', text:''};\n        window.__tiConfigCommFlow479 = flow;\n        return askNext(flow);\n    }\n    function cancelFlow(){\n        window.__tiConfigCommFlow479 = null;\n        setReply('Invio email\/SMS annullato.');\n        clearPrompt();\n        return true;\n    }\n    function handleFlowInput(text){\n        var flow = window.__tiConfigCommFlow479;\n        if (!flow) return false;\n        var raw = clean(text);\n        if (\/^(no|annulla|annullare|x|stop|esci)$\/i.test(raw)) return cancelFlow();\n        if (flow.stage === 'ask_email') {\n            var email = extractEmail(raw);\n            if (!email || !validEmail(email)) { setReply('Email non valida. Indica solo un indirizzo email valido, oppure scrivi NO per annullare.'); clearPrompt(); return true; }\n            flow.email = email;\n            return askNext(flow);\n        }\n        if (flow.stage === 'ask_phone') {\n            var phone = normalizePhone(raw);\n            if (!validPhone(phone)) { setReply('Numero telefonico non valido. Indica solo il numero per SMS, anche con prefisso internazionale, oppure scrivi NO per annullare.'); clearPrompt(); return true; }\n            flow.phone = phone;\n            return askNext(flow);\n        }\n        if (flow.stage === 'ask_text') {\n            if (raw.length < 2) { setReply('Testo troppo breve. Scrivi il testo da inviare, oppure NO per annullare.'); clearPrompt(); return true; }\n            flow.text = raw;\n            return askNext(flow);\n        }\n        return false;\n    }\n    function sendOne(flow, channel){\n        var data = {db:dbName(), subject:'Comunicazione Configurazione', history:flow.text, admin_copy:'0'};\n        if (channel === 'email') {\n            data.email = flow.email;\n            data.send_email_opt = '1';\n            data.send_sms_opt = '0';\n        } else {\n            data.email = flow.phone;\n            data.send_email_opt = '0';\n            data.send_sms_opt = '1';\n        }\n        return post('ti_ai_send_email', data).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) || (res && res.message) || 'Invio non riuscito');\n            return res.data && res.data.reply ? String(res.data.reply) : 'Invio completato.';\n        });\n    }\n    function sendFlow(flow){\n        var btn = byId('ti-conf-ai-send');\n        var old = btn ? btn.innerHTML : '';\n        if (btn) { btn.disabled = true; btn.innerHTML = 'Invio in corso...'; }\n        setReply(flow.channels.indexOf('sms') !== -1 && flow.channels.indexOf('email') === -1 ? 'Invio SMS in corso...' : 'Invio in corso...');\n        var tasks = [];\n        if (flow.channels.indexOf('email') !== -1 && flow.email) tasks.push(sendOne(flow, 'email').then(function(r){ return 'Email: ' + r; }));\n        if (flow.channels.indexOf('sms') !== -1 && flow.phone) tasks.push(sendOne(flow, 'sms').then(function(r){ return 'SMS: ' + r; }));\n        return Promise.all(tasks).then(function(results){\n            if (btn) { btn.disabled = false; btn.innerHTML = old || 'Invia'; }\n            window.__tiConfigCommFlow479 = null;\n            setReply('<b>Invio effettuato.<\/b><br><br>' + esc(results.join('\\n')).replace(\/\\n\/g,'<br>'));\n            clearPrompt();\n        }).catch(function(err){\n            if (btn) { btn.disabled = false; btn.innerHTML = old || 'Invia'; }\n            setReply('<b>Invio non riuscito.<\/b><br><br>' + esc(err && err.message ? err.message : 'Errore sconosciuto') + '<br><br>Controlla anche: API Key Infobip, sender ServiceSMS abilitato, numero con prefisso internazionale, stato ditta non Demo per utenti non amministratori, connessione server verso https:\/\/6zgm1d.api.infobip.com.<br><br>Il testo non \u00e8 stato perso: puoi correggere il destinatario o riprovare.');\n            clearPrompt();\n        });\n    }\n    function install(){\n        if (typeof window.runConfigBot !== 'function' || window.runConfigBot.__tiConfigEmailSms479) return;\n        var prev = window.runConfigBot;\n        window.runConfigBot = function(){\n            var box = byId('ti-conf-ai-prompt');\n            var text = box ? box.value : '';\n            if (window.__tiConfigCommFlow479) return handleFlowInput(text);\n            if (wantsConfigSend(text)) return startFlow(text);\n            return prev.apply(this, arguments);\n        };\n        window.runConfigBot.__tiConfigEmailSms479 = true;\n        var btn = byId('ti-conf-ai-send');\n        if (btn) btn.onclick = window.runConfigBot;\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install);\n    install();\n    setTimeout(install, 300);\n    setTimeout(install, 1200);\n})();\n<\/script>\n\n\n\n<script id=\"ti-config-ai-order-sms-final-fix-483\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigAiOrderSmsFinalFix483) return;\n    window.__tiConfigAiOrderSmsFinalFix483 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function htmlToText(html){\n        try { var d=document.createElement('div'); d.innerHTML=String(html||'').replace(\/<br\\s*\\\/?\\s*>\/gi,'\\n'); return String(d.innerText||d.textContent||'').trim(); }\n        catch(e){ return String(html||'').replace(\/<br\\s*\\\/?\\s*>\/gi,'\\n').replace(\/<[^>]+>\/g,'').trim(); }\n    }\n    function normalizeFileKey(v){\n        var s = String(v == null ? '' : v).trim();\n        try { s = decodeURIComponent(s); } catch(e) {}\n        s = s.replace(\/^https?:\\\/\\\/[^\\\/]+\/i,'');\n        s = s.replace(\/[?#].*$\/,'');\n        s = s.replace(\/\\\\\/g,'\/').replace(\/\\\/+\/g,'\/').toLowerCase().trim();\n        return s;\n    }\n    window.tiDedupeOrderFileValues483 = function(values){\n        var seen = {}, out = [];\n        (values || []).forEach(function(v){\n            var raw = String(v == null ? '' : v).trim();\n            if (!raw) return;\n            var key = normalizeFileKey(raw);\n            if (!key || seen[key]) return;\n            seen[key] = true; out.push(raw);\n        });\n        return out;\n    };\n    window.tiDedupeOrderLinkedFiles483 = function(files){\n        var seen = {}, out = [];\n        (files || []).forEach(function(f){\n            if (!f) return;\n            var key = normalizeFileKey(f.raw || f.url || f.name || '');\n            if (!key || seen[key]) return;\n            seen[key] = true; out.push(f);\n        });\n        return out;\n    };\n\n    function appendConfigChat(speaker, text){\n        var p = byId('ti-conf-ai-prompt');\n        if (!p) return false;\n        var msg = String(text || '').replace(\/\\[NO_VOICE\\]\/g,'').trim();\n        if (!msg) return false;\n        var cur = String(p.value || '').trim();\n        var label = speaker || 'AI';\n        var chunk = label + ':\\n' + msg;\n        if (cur.slice(-chunk.length) === chunk || cur.indexOf(chunk) !== -1) return true;\n        if (cur && !\/(^|\\n)(Utente|AI):\/i.test(cur)) cur = 'Utente:\\n' + cur;\n        p.value = (cur ? cur + '\\n\\n' : '') + chunk + (label === 'AI' ? '\\n\\nUtente:\\n' : '\\n\\n');\n        try { p.dispatchEvent(new Event('input', {bubbles:true})); } catch(e) {}\n        setTimeout(function(){ try { p.focus({preventScroll:true}); p.selectionStart = p.selectionEnd = p.value.length; } catch(e){} }, 30);\n        return true;\n    }\n    window.tiAppendConfigAiToUnifiedChat483 = function(text){ return appendConfigChat('AI', text); };\n\n    function forceConfigAiUi(){\n        var p = byId('ti-conf-ai-prompt');\n        if (p) {\n            p.setAttribute('placeholder','Scrivi qui la richiesta. Le risposte AI compariranno nella stessa area, come unica chat.');\n            p.style.minHeight = '230px';\n            p.style.lineHeight = '1.45';\n        }\n        var rb = byId('ti-conf-ai-reply');\n        if (rb) { rb.style.display = 'none'; rb.innerHTML = ''; rb.setAttribute('aria-hidden','true'); }\n        var buttons = Array.prototype.slice.call(document.querySelectorAll('button'));\n        buttons.forEach(function(btn){\n            var t = String(btn.textContent || '').trim().toLowerCase();\n            var near = btn.closest && btn.closest('#ti-conf-ai-chat-actions, [id*=\"conf-ai\"], div');\n            if ((t === 'istruzioni & indicazioni' || t === 'invia istruzione' || t === 'invia') && (btn.id === 'ti-conf-ai-send' || (near && (near.id === 'ti-conf-ai-chat-actions' || (near.innerHTML || '').indexOf('ti-conf-ai-prompt') !== -1)))) {\n                btn.id = 'ti-conf-ai-send';\n                btn.textContent = 'Invia';\n                btn.style.setProperty('background','#10b981','important');\n                btn.style.setProperty('color','#fff','important');\n                btn.style.setProperty('font-weight','800','important');\n                btn.onclick = function(ev){ if (ev) ev.preventDefault(); if (window.runConfigBot) return window.runConfigBot(); return false; };\n            }\n        });\n    }\n\n    var prevSet = window.setConfigAiReplyHtml;\n    window.setConfigAiReplyHtml = function(replyBox, html, plainText){\n        var text = plainText != null ? String(plainText) : htmlToText(html);\n        appendConfigChat('AI', text);\n        if (replyBox) { replyBox.style.display='none'; replyBox.innerHTML=''; replyBox.setAttribute('aria-hidden','true'); }\n        forceConfigAiUi();\n        return false;\n    };\n    if (prevSet && !prevSet.__tiReplacedBy483) window.setConfigAiReplyHtml.__prev = prevSet;\n    window.setConfigAiReplyHtml.__tiReplacedBy483 = true;\n\n    var prevRender = window.renderConfig;\n    if (typeof prevRender === 'function' && !prevRender.__tiFinalFix483) {\n        window.renderConfig = function(){\n            var ret = prevRender.apply(this, arguments);\n            setTimeout(forceConfigAiUi, 0); setTimeout(forceConfigAiUi, 120); setTimeout(forceConfigAiUi, 500);\n            return ret;\n        };\n        window.renderConfig.__tiFinalFix483 = true;\n    }\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var b = t.closest('button');\n        if (!b) return;\n        var label = String(b.textContent || '').trim().toLowerCase();\n        if (label === 'istruzioni & indicazioni' || (b.id === 'ti-conf-ai-send' && label === 'invia')) {\n            try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {}\n            if (window.runConfigBot) return window.runConfigBot();\n        }\n    }, true);\n    if (typeof MutationObserver !== 'undefined') {\n        try { new MutationObserver(function(){ setTimeout(forceConfigAiUi, 20); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n    }\n\n    function extractLatestUnifiedUserPrompt(raw){\n        raw = String(raw || '').replace(\/\\r\/g, '').trim();\n        if (!raw) return '';\n        if (!\/(^|\\n)(Utente|AI):\/i.test(raw)) return raw;\n        var last = '';\n        var re = \/Utente:\\s*\\n?([\\s\\S]*?)(?=(?:\\nAI:\\s*\\n?)|$)\/gi, m;\n        while ((m = re.exec(raw)) !== null) {\n            var chunk = String(m[1] || '').trim();\n            if (chunk) last = chunk;\n        }\n        if (last) return last;\n        return raw.replace(\/^[\\s\\S]*Utente:\\s*\\n?\/i, '').trim();\n    }\n\n    function ensureShopServiceIcon(el, size){\n        if (!el || !el.insertBefore || el.querySelector('.ti-shop-service-logo-icon')) return;\n        var src = \"data:image\\\/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAACAAElEQVR42pT9d5Bl2XkfCH6\\\/c81zaasyKyursryv7q6q9mg0PNDwoABIICmSIEWRM2KENLuamJg\\\/JmJnJ2J3R7E7sSNp5HaW4oxEkSMStABBEQJAgN1Ao72rNmW7bFZVevf8u\\\/ee8+0f55pz7r0vqxeo7s7KfPnMveecz\\\/0MaPQhYiIwSP8v+W\\\/xb2x+BULyHRR+izh7MHI\\\/in+L0x9y7jEofAnrdZOnZtY\\\/AAMg\\\/f\\\/kmdl6JAHZX5myxxXeGpV8eFgfkDn7gMb3kL1D\\\/QOYr5NdBs5dE86eqeQyWhe9\\\/PowW49KPikz67ee3KLk7UC\\\/Yc6uV\\\/rd9K9K5V6H2bpeKLmt9gXXr0HmXWGCeaGGLA\\\/rpnD+I8fvI1mr5XfQXFnZUtEvhOyaM9LFkr2UviwYcguY7EdnuwCIn5Bzt4jz9xDJcjG+6dp3ObcDWb+rkpWQfUAk95g4fjAbrzd0RcH8jv4VxNeJOVs5ADEz9AsQzCdB4an06xOXvDaMfYls+cNYV\\\/alyt9gtg8LmLsMw3dwycYCbbMDQcRDrrh5EZFbDMMeEH9tHQDZ2rEPwvSF9X2I70LyhoqHI3N2PWDfT4qPgCE3vbDhYLwSW5eY073B2T4ZdmXNBWmFDH0smVfeCDkMNo4oDD8B2To6rNvEhSOUt7l7+bUmjIuI7CS13ypQ3Dfm3SCOD5t4LxHS6IEhgTQLM4TkV3QcYS6elZzu1fRSIndQc8lOsU9l87jOFgnnNjXK7615WBR3GWDtz5KFEX9OQrrKrDPMvri5VylZdTxsqRjvMLtp+rxA8ur50z+L4cmdNC5FyQmDYixkIk5fm83XZ+tcy++U9AODjSzC2EXpjorfN3Cf7EUvBs5tgnRJljzYvLDYLjvKngLMSK5W8QKw8U1sd1wkjxK58MvpWjHPaza2e7JvCmEz9x2UvziMBA\\\/3O9CSfWcHXhByuyD\\\/blF8a8m248LNAJekidbq5PItYa9B3u5Kp0ssjcpMhXWy7cHJw4\\\/X+y3LIaGaMHRtFuOVvUo5WXPMua1c\\\/vZgfoV4qVjPyuYyh51pcnbXkdxGDL0XxXCH0lzVWLRZwOBtY5l5Bm8b6ZAPedveaohtKrb7Fij2m+f7BGPYRZa5gbj4G+DSRWMl3bDrRravVvpiYM4ibfl14HzBGMfnknvI2Y7iZCFut0XsC8LldwT5G8zWrwDZGceFD8D32ZCII40VxoygAgzLaGEHtPSvMPckl4W77OKbsShLGZiIYZYDyF4p3qhsl1YMLkttiyWcUZ4U7zcKNS2SzBelV+J+i3LYMYBh15WLtawoT31gZ+cYfouZ7ns6F7\\\/OLfjSQ61sB7BRXg9bP+YFQ66rkmyi+AplSWKSpZqbAcM+oZWWo+wGYOiNGbpHUbgPMK+MuZZRSI\\\/K3pgZCFHS9onfKYBiY6wQ38xTwLxmabbLxvVA8d3BXpVxbhz30+xqMw5ObKY9yTMi\\\/scquJnLz\\\/vk0cWlxLmohny7YUgEgRHLUZqHJQ9hpG0F3HfbsDAqeOasD1ZWoAKcT1StFZSe1bh\\\/RMj\\\/rfBW40SH7UWcxN0hLVdjtcT\\\/ZSM34azJhuxDp\\\/2RwrUZ1j9lNp93aOf3AxxYGLKPUShS7UiKYqfFKsHsFYZ8fZYvBLhYbyJXSxWPMuPXYa+1fDoRbx4uidn5pzWTgPRATLYic0lLL9dDZ7pfTmKkAYUCE4WgaWRZbBwt+sCONxynx1namtZNPyYryxvSRmMWTAxm69As9iCGnCC5O212\\\/Mz+Cm9bUfF2MZWLRz0P706nPwbbgYoLmUS+FZ4WObkXLXud7RKD\\\/GonI7MorN60TY6SDD95x1yWVeQ+nJXPc66pRcU0LjvNrKMOhTTKOg24\\\/AaiUPgVjyZQ6ZYzNl3pX1GeeJt1F9sxA1SoJ7cZYsQ5EQ9LJEoCGZc1aoyKjNODi+9\\\/GgytCcv3Sb4WTkN9cWqBrBeC8vr2fkHCKojYTErYOH7ZbFfZaV16SsXvCcb6QnGWidLPe5\\\/DFEbmuW2nwMqdkOvCouycS898mOfOkEQC+ZzLTAiHZOz57W38CuvNhlzLd1j8zr0q7PoO6fFTPADKFgKXtR3YOLqQFY1DVmlap27fsbJPLdyvf7nNDDc+tdJQxvmiumTmaYXgeESBfJVdfsSzLvCz3YeSojd+\\\/TQgDR1SsDn7KC+u08kp4krdOsbSQiHbnCjZKiW91OR7Rk2Yb90WZkYo22plqzzX+rPLHgzNOs3IBipLE\\\/Nz+XzX0r75923lF1qfQFn0AcqWLoqTK+tw2r6nh+EtJKM+S7v+WcxLcz97XIHyrKE0\\\/ber29JO2JDqgIfWEekELC55mJOdzeXTpfhOJj91OX8DYQ5ZkBQh6aQOiP+VtpWyyJvb9zEKx8oQuPz0sgfUMHtRhU2M3N+y9qLdY4YxnUdJ69YKibhv77nkTCJ7mp1NSnMftnRCSsWurj7gstInl\\\/7jA\\\/TFkJujY7vmaXadczAQ+37kU6skt2AjcWcqy+XKB5nIYkt+8Fpa0jEXG3R2VgoiFuWRFeXf42H3oLDUrJOPGUg2CLIGSrq5YL493qZUYWMwABe5\\\/Hl46QQUWjFGgz3tPCMbD+efgVn3gmEWdmVVqznGMd67AWrgYu2K8vduJbFA7qIO23M87CTlYhHK+Sab+c0MdFTat7CODqP5AAyd83D6bDxsg0Ekr8zlqyyPdEneOptHVjJeLRkCpTkOrOY9l4+400Oa80A73HeYBi7cpOTFOVtKJq7LuHTg4ekl3+\\\/OcvmRZCxLLtvRXLhV2x7iAJHIdanYSrjsA9DouOj+T4xQJLaRGMUGRTqIoTJoKJX2wc03YcU3mNcSw6fNQDFC53ee1e0HUFbwlA0RtpvCmAWBOZ8s7lUuiZNGBx5Wzz6dJmRVFgjbjMaz383NOmDl1WyUz0xgZOAxZto2RRiGzbBS\\\/HjZoGSbMuVnsmUbhoFc3g1YZW1ZSQ8My\\\/nzZ2wugUdpD4zJbjIMWRJlg\\\/RhiZzxP9e825wfnCDtiHN2gCBX3dtzhhjJaqAceJvWQtkpbsU14xhNR6zgQsMDhe3PeeR11kSBkQexkf+V4R1zaRBKU7\\\/7tba2BdHmfo2HIwrIBFnSfZLMoe3jbTpwyGNVrBiI+2BazZhhVZlcGo9hn6dDEbMAl5RVheKGt7m+GDqizl0loKQyskET5VmX+RQp+BkYkmgZRUeyCS2oKLM9ujSSx4SzUHJJQWBwbrZjZA+8\\\/RFhlERGDWlOluykyT42E5g5mwsp36ouaZ8w2z\\\/CfQpALpsXGmsU5dkt0\\\/2ab2XnNMjEl5ttfyM8lSRQyGVt1taCXRFt00KEkYqyVWrGaOqh6UURZ0PmtYHVZcpnc8ixa8zZkvHmk6CKXKIMa70CxlHMJSUNEqZHcrjGubiBQExWO5A1rWFf3eRotlKBBAGPXGJbPNqIiIR5wbisOM7NqTjJNLLcCRmVgq2sP9lFjCEN6HzbjTltC4Hvs1jNsRU4bUiVPK\\\/ZJQeVZDLYpvNhDLy374swbxPfymfytN3kbWihVbiXyCWfKB\\\/5D4mOBpLKTFOZrO9YIxMUh4Fm14aHTfZwnzY\\\/kpl3SmLJZg3gfFyzqyZrKmP1j0p7vrn2vNXOhAU\\\/hXUC5nLUktZhllYB2yOCs0gIO7YxzMZw\\\/o6nIOi0FcM5zAInJ1QuhHLpSZCvlmGcLen5wsZJw7nKuzBnNnpWGLYZMlYGrNmVfQPYBvuitNItOeTYyBZKW008ZOo\\\/7LcwvLk8FDYAsmKFHX+GnBRcfndQ3DFM9x\\\/DsUnxLPaG2SpAeNjZxSbBCMy6O2ncDR7SQuMC7zJduFROdsu\\\/vFmk2PvNRtOWNLyRrNnyM7RwE0Q+V+TyK29EVmOfmdveOIw5JSXlBjDYhtshEoiuwbQZksZuFyR52Pzd5Jwg1zyzo5gxvAS2mb\\\/aOCuDzMK56XS+q4CSM5LzDRoMqephNuh425iLYQX30LCdm3tRKc9rCIfGnu\\\/jvpU\\\/LNwQcjGK7LF\\\/liQbqG8ur91z1JMUmAk2CIt59EaxRkQRHVjoJ1lQsW2aeGWNNDZqwvy6yRXmRbSAQbAsOSTZ2EPmlK6s2IdR8PKQCMDlw2MupShbTSa2ome+8wUTrWdRxrMDk6yZG3LlOZOVaW+TNsd32xyRZVxoM00oQXrAqkSsKSQY5UlOoUfF9iXNpaf5cQiyji4+wBlo9uvL2z9cWrNaeQEKV1NHPSptlLF1ENmRMS1ak4hsoIrzobIA\\\/0YJ5JBQNqrMBfdhoBrrOUvRyEzkFnp8Zg+E2Q6fKUM5zjez3kGR0Y5CR75wWjFsrYuSTnNyRuT6MTBCPoZkVsnIBSZydAjRwMLDFeCXhY9l7CUewmE1GOs2kdBo\\\/7IFLjV6GkzDBA9QsviHBxyzm51yKouMSh46r+PS5CtrRfEwEML92uFU1tmOCfRGH6nkyprpi7GTNWgkrWPSbLYEsIUEUZtLKXibrDgntYBthrTD92EiWZAn\\\/gs78MOYHrGB\\\/bNiNGIOHXNhKlbG9E1\\\/rfieLZhuMb8p58kjxwne\\\/uNznr1vvdUirBPleHOg8InyZ5YVcWGSl4tho4h3Q5YbZXh842Gcz5NRKl+wTRcom\\\/LATgqRB7WW9cxiiATSRZ9\\\/3vsNYEqHmSg7VswDBoUjyR4kMRuDKzMlHs4dK+3eZcQjY4rKQweifJ9WS2m\\\/eejIVeSvzJB6BpYyS\\\/qEdiLMZYm8OQmCPU\\\/FNoAJNvSSigVQToIJOcBknqCmB5gAD1uupR0OtquXRB4inp2gJMG2V1I6KdlmRAG7kAZTKXe1VIUAhRdHnugD+66ymVTArg7LeQtWycqpKg4KvA1sewJsJx3CxeaQge7g3H5gsxjKpjZcwlkrOVqQv3YWy5GNdrrd5TUovyUFCBcbxMQlokWlZH1AD+utgRZgy0qZfHbr+purkIszQ+RG5\\\/m5Uf5JUrSABduzPngaqTCk1i3cy3wf34bbWP1PlFakVvsurSQBFBqzZRV4Ou60Xo+HDucoVbcyYYglQZjZ4qwMzYiSLQMmU\\\/oMbOAMyQKAWW1t4xhPRmpkiW7ZeS8ska\\\/7Iga2+YLzyQzMI2Q4TNsCKFp9cv0RuJRuwGaXgssHv1wcrzKs8ah1GnHZkJmH9LfdQkcHbGdyuayuBHcEW8iqZIaM7b9j7xxzcFogEtgpbjnQFXlcf\\\/oDtict5fgYMNl9pcJCYR7epId5ZOgSIIdD3D6NNroR9qGbpQ1civMYnhMx58mqnNarWdVtJyEFWEJaY5sILrsmLPbJUL6XP1gVUQZ8twmFGNoRS9cy8nkr2xD\\\/Qv3LhVdJpi1FDCsNabSUAWPT\\\/gTbRyQTwSUaBtOhYZl+3B4FD23CZrKFJfpHDNwnSS+b9aCQ1xVaF6J8\\\/IUMZG4MGrfJ5MFDUDRmPEyfKbeq2JzJITem2r55lp72uVcvqGzm4Ei5Riuo5NTdTmoVhTnSfYocHoqa5OJyyEanMDrDzFS6h4xVmoVfUInqiJUHcdn01VK3LG1B5W8BD515WscgZ7Pyoqgo3XcGaR4jDE4jYalkEnNp1Q1LuQ9WRGIaEpaRF7oyv5FW11zSjrZCMQ9J4oYMo4yuSZFxmKmklmM9UYBeMedp6PZ80fgIZVpbsBKNIYd8cQfBzorz2nAWbKpUu9QuCQzG8FCuDYacE6UzwPtAmxJWGw8ZTgwbsmj4GGwtXxSiENu5Yk6AV0sOD4+YuWk758ZgXDY8xzbn6Pa9ay4NdromZE77tiXPygW1gCQJRVqoF5L0QgGcL95QlADlXJJZqLXY2jM2rxSlqoXbYnSG4Fc5xu7kwBDgcjRDvgmOuNguJC9MJjK3XG6Gh1PbShKdJDnnYfvCLntyDL6hos\\\/ljdViv6v03g\\\/v1Oag0ObToFCUgGzJ8JJsLvvgnIdkmUGPPwjk3J4I5md2yE9soTVhhit9lc9J4x1jILbin7tmXYec8EG6oLKMolj7FTsrKOXdFeMAlw++yqaFpV042D3SsgFJkbNkD7vYJjzlRq5l05HsIpbPb5kS+Hy+FcuczGm4kL\\\/mWKpDsG9DThaUVgUwRl\\\/Y\\\/tQuSUJhs1hQtrFz8kvI37PyXDMXqxklUIQ8bNKqzswCO3siDCPzb5ddlxDvUJq\\\/JX0AIF9lfKCwaHWms8WTLE83uxYld4qL6VtWFJQ00RK58iInqmQpIDfILLsuwyQ0QNtOUpJVAGSSfAasILt9sGwZzFEv0xAIX+6Wo5zwXxCGN9qdBaYyU8lL3F\\\/od5vKo8DwN+tKbCPXxeUKuDwkphWXf7JVOB+fYPUmyxt29lMW60CwvduRJUdDGxucn+5ZhduwJiLbCXB69HIJ3clu7TMNfcrS4EQmn9BMvQ0YiIGKyapp5FYX2EaAGEWVSXPOYK3GjmAu7cIUi+9yKRcmaJ4rrD4\\\/EYGElfTYKAyT9GaVIjk8LOfyFQzzwmAqkqktwZSc4UT58hv+LZQMeTIjkvvNxs1xFYbNQy24H3IhGeVxE6XvlwuJy3Da5dB+8TYnXR6CwGU\\\/RwmO22j1bqtANgz6iiENGI7zRBSZeyYAqLz\\\/4+beJ5cg9sGlOXuZvw\\\/n2lwmCbVULCIfN3KDB+RRb\\\/kJYSpemyalKP1FlIhaFyLPMBeJIYAN+2PEQTU3SYuPJuQHCkNTmfyBfZ90ffgORGFzwJKIzY3UiMrcoTJiTdnuxRCsMnLaqCgZKvC2VdSQj2MwZnNZB5fOvEqRvSbZj4pqz5wPAJaTkJFHwPpEnE\\\/YhtmWcH5YHaejJU3GErol5fV5soQvY\\\/XafPMhczGUtBbNBIPKisB8Xgpbq02km5BTTWnLacCYACTWYciYGsp4SGFiUKTKcq63n6PLFvW\\\/koIIJREzz4cBlye0242Ncu1rprIU0VqUhv4OW1NZzg4SlE7Ht425KJbxtq1TQaSykF\\\/bbyD\\\/gUEFGAPT9t1cKvDQOUN\\\/2135klIWJZMfDC\\\/Z718sIKd\\\/lI0o4saM6UdVqtZKFskAsBRsY2JuTt2Yi6uhtEE8DHdSwi4x4x4IIgENCiIAgpGYGcDQaNHdkhjFzMyKmJkVWGXSWUkjqpCUprUijMVWFNy11k5Bz8E4zgwkHhMX6jQMHboUqjTOU8u5JH7C3t48LGBSmQL1B8l02VKNyilc8P3WKuj+Pe38LrBr1txSRZ6yaAdjk+nPqQ4wl6VsXKitE01dmNZ2WboALp\\\/S5XD\\\/VjqaRW7Ol71pT49KAQuF0xz59IhLW+flIz9YuhooEyPPeehAy4qBIEg4BJBwCA5BkHD1zgQECxCJ+PkUMzFYsVIgRUpBSVKSWBIzsdKGSUgcLcm0QSiZFZQOFIBh6USRXAAD5TkUJWHXSDAfHwcxttUfcnkXcijCUmQLF0ED208Ay+Z+QzyBABtZkOuMWGM5FGT+c\\\/116w5Y5p\\\/52QYPS3uzGoHtyFgOITDbcZwEKwNIkK2HWFEOQxpftgJfVse6ObhfgZmDoUAC5DYdcsQyHtYZh11Zwmhpozh7SI4iwI6ESegDSLgkHIJLwmHHI8cj4ZLjQgAQgCCRVGmsSClWTKyYFViSjEiGpCTJiFiSkmBmKJ2jgu2qL42N+W1oHW+8XSFchvYoiyDDMivjBVHUm+ESoKk5H+Ihg3K2WwDbSpVyWZjnYekYSlj75qfKtW0\\\/ED8IZguTh04q05kaclzxzAIAmdRXpj7LKK1a4wMONoVqm9qPttU7NH7XNQ6WfC8xD7ZAnuhlvqCdqGNYfl3ARRg9R2DIPCzbgZyZCYk09OmNB8djx4frk+O51drk\\\/tmpQ7smd0+MTk\\\/4o1UFUszRIOqstTrLWxt319au3+tuNFlFFIUcBYgCkiGriJQES2KlRR2RNqbNS2JqyoHLGzZUHB9zyWh\\\/O40ZS3eraEGUgcO5HCqRcuesHhmbU6H7avkYFUradiqO\\\/u9z0KCM8MvbYrip4MduKlVZwlVl4mlMOS0c2MaVRgJRMhUrEXtku0jk+3gpW20YHjK8TG\\\/E2Jny5kki+GhrriHRic4rW6MIDcvnZYYhMgrIiUIAtD3uwCnvLS75BIRDjsuOT44Pt0Jexa03po7s3XPm6M4jc\\\/XxURVE7eXN1nq33+kH\\\/ZAEudVKdbQ6MTXSmB6VYdRdby5eurXwzo3mnSUOBxwNKAxIBVARKUlKMiswEyvjXnCh517UfvwA5Trfb3Sen3OUN9YxLMu1\\\/8PmAAapAgzlO788pFZnKqZEhqEMSkYRdpBCuSghtlnHKMo05Tr5bJwzZqZNVH6PzEwlc9Xl8jKJy01t2IplXCqkmh0ORQkc8x1yStUbPZPftABt72sDID+9KGuRFclpjO1see1mpp1MJT1PCAIYDhwdAH24VfKq3ujozOlDRz9+bmL\\\/7MrN1ZuvXl+7sRQOBioIWEpWOrIxCQcCQjiOX6mO1\\\/efO3TwkUP10erN165c\\\/en5rflFHvQ47JMMoEKWIdJfTCRkmFMadtrCYev68vb9sSSucFnvjobBFEtYGFwyfygOj4ev8NxByWznJqb\\\/OBWmOHnaz\\\/Z9Gy6ZW2\\\/3G5xfZShrwA79TIUtZPOI8ttv6K7L7bHCcJ+zqVT+9DKkFHIHJNvpcNzF15uwrCl7vzQDJSVenjiQa8galCIubTygoG6W7UACCA4JQcKF47FbJa8iKrXJA3sf+vondhzc896P3p1\\\/81pvq0NRyCoiKUkGSbGnCKR\\\/HXDIcUi45HpOtTq5Z\\\/qhz56dPLTrwg9eu\\\/7cW4ONTYr6CPssB5ChLhQ5sydhpCCmTMCEy7PNkgWHIQMGDFlN1mCTP4BOWGETDpNjz2Mmh2FOSkgTPHR+vv0+HAJCYFMLnLNmI+KuZep8UKBGDUfusO12bm48U0eM8wN0tp2ghoRELnm8nR2g7J1QmWUL5zYhaBjuIN8HQynsAjbwBiizdkHOlM8C2pgNGE7rQMSTQMFCkOPGAdCveY2RU888fuyzH7r44vUbL17qbWxwMKAoiHstSpKScW4ZVwRgIQgi3syOS04FXtWp1Sf3737kq486vvvy735\\\/\\\/cpNDnoIexT1WYZJiajvWfqFga\\\/Zpl3KXDJgKEsrjcuQOGBZ9Dsu9jS4bELOBewRsoYcWxpW93fyK\\\/vR0FJ2+B41lCp46HCi6KGeL1JQVrkMl1jihPuskOUy5lY0vmkYDCb5jbVvcz6teULLUOQFZ09V3qpjInJQmaG8JMX9VGtKrFsNmQBDXsUgOlldVOacagtyFWDW+aRUT8Uh4ZDjkVshr0aVem1i\\\/KO\\\/+XOTx468\\\/Cev3HrtatDcpH6HBl0K+4gGFA0gA0Qh6XRUSVKKWIH1WCIiJaEklGQllZS9zc78u\\\/PCcc587SkpaeveRmJczLD9Y0z+e6oqAEtjpyCYC+T1YIv6MsX2b8nJiCImpRjW8u8h11QtEQQyxXnvq56P4b5mOaBS9lkSlALKGjGGOkl601NDbaMTDkAfxIBg4ZBwIFwIlxyXhEvCJTjkuAQXwmHhgASJ+LeoABgwYoQ1UAXA5bXSkLzZbsqVKktRIUibDROHKjMFgzxbMB33Q0XkPcSRmfKhTLwFZi2N8spQCMMdPJkEOi7cCrw6qvWR6emP\\\/dZXA1V58Y9f3Li9yP02BV0KehT2SQYkQ5IRKalHf8ZyYGKOx4N6TM96WhixilQQrt1a6zb7Z770mNdorF5fYqVIn6MFhzLkHBKNlVg2dIYt32SurVIweqLAg1IkGsqADcisHPMMC\\\/PlilA3NkLNMK1xlKMIkChmw3KWL0LqYepW5c9fu\\\/in1Jg+lbQSEHrU5BAcEi4cl4RHuifn+HAqcHxyfNIDKseH45Fw4LhJ\\\/SKoqJ2FHEAPZGoZ5aXPClJlBdg9AAyFpFusF9i8iDgS5jcSoVSB2z5OULI4kOHYgKHgC5QcmdnwnZHlnwRBJEgIFi65FfLrqI7Wp3d94h98tdmSr\\\/75S921Neq3EHQR9igKoEId5Yhl\\\/ErCIeGT40E4yRmokj+sv0DybyZqrXa2Vjpnv\\\/hIFPLmnVVWiogpSWmKok7WGDO\\\/GYsbJveRsa0OkjkULU9VYOydTAOyzO8aQwExVtQ1DNJQuN0lkKayQaax4beDtlojX07MW2GioEgAgiAYjm6Gk+OR65NbgVclrwavRn6N3Bo7VXL1JvTJrcL34fokfHJcEh4jfh77CDDPPRQF9Qhl79beBciBKFIdfzPZZwxlr8ZzwhjrwPYAlG1PgjKr9dL6fhhEwH4EF4eH1rKCbbEg4jTD8cmrVcbHH\\\/n5T\\\/cC8dp3XhtsblG\\\/TWGXwwAyTIYKiohIOAwB4ThetdoY9fxqFPZ7na4MB0wBqSjZfvHgPgmbxISlC7de+pb66K99oru+de+Ni8xMLIkVM6cNDAOrl8zv88x9LkCWyw62Eqt3lJEnCgxXygBNedsWq2gsXHPw8J5KjAc29FZS+SkuQxNTifIZcL8uFNncAhhD\\\/dRNOstCGYLgsOPC8Vl45LgQnnB9f7TRmJoYnR6v7hivjNZ8zxHMkZKDftDf6nTWmq2l9d5mU\\\/YDyIhlQHIAnSVBT4MVkWJStrp\\\/0XUAQxz\\\/EHc\\\/7RF3XoUkryLFBbRKSuolEA+p0GGKpRWn8Pme+TZqHSXkapRYT2dROBkJkoh3oPCrxz79aGPv7mf\\\/1x8EmxvUb9Ggg2hAMmQlwSq2KxAuOb7renP7Z7\\\/wc08\\\/ee7QeKPWGwRvX1789refv3blRjTocRQg3opJmhon7JJUtPiefOmPX3ry1555rtldv3KdVcRKklIAx7ct23rM1g6z13ieoG3fRgvewFQmJDlko5iaF0DWTjAgqRiGgyvOLwzAefG45kQ5EUzliEh7EMfMRRn\\\/kvQWhZFDDhElWOgWmqcDILtV+BV\\\/pDF1aM+JZx4d2TslgNZSa+XaUndtq93pEZGo+u5ofeLE7ImDU\\\/URT3Z6l1+6cvvli53ldTnocdSjqE9RQAihIo3bZ9KjYOS7ThaPaxs4Wt70quTCMg9pdOpzbuxsyc4vQ7iwlToX2MhkuwuAywHZnFUfeV8kLdYU+w0AujKEIOGyTkQro3sePvnYr37xJ7\\\/z7Obtu9xv06BN0QBpL1Q\\\/u+OSU\\\/XrI9\\\/4xid+\\\/uc\\\/6o\\\/WLlxeWJ5f2zE9fvr0XCSjv\\\/zzl37\\\/P3x\\\/0GlS1CMVJaMbQcIh4bLrkVulyogzMn7u558eGan87F\\\/\\\/edTeoqBN4YBUEA8P42mt\\\/ofBZeEf23Y4hg3ccyiPbQbzRR175g8ii4xiv7wwWLdmXNhu1nifHh6XA0kLR3AyiIqzIQHhsND5Z1Xnn9WJ8VOfeWTuiZO9ZnD3rRvL15a6qxtBuyuDkJSMP7sQJAQcx6tV6jtGdx7YNXtybteJ3ZsLa29\\\/+4W1KzfVoEdRj6IByTR70shhhmkokpttkE73mY2jjkvUtGAYrBonk2WsnRv5sIPKbL4AsaoNyusRolhwlPU581oveftZLkHJJKQksyGm+zGOT36jOrnzyW9+\\\/sbrt+68dZX7bRp0EA4gA46BZhzjSN2qW61\\\/\\\/Ruf\\\/C9+83NvvnPnX\\\/yr7\\\/3xHz737LPnf\\\/LT91559Uq1Xv\\\/qlx4leO+9d1tJmYBIrWQteaNidX5j\\\/9OnQdi8tUSsdFKaQZW3Mz8p9lQKTjKFqhi5yoqGN2LSe8GFZY3tdx+GYsRKt9A2L122gYc8idWOYsPwVV9nJmGUoCLpw3nkVcmrk9\\\/wRsePfPLRx37xU8KpXPzRuxf+8+tLF250l1fC1pYadCiObwOSA1IDivoc9dWg39tqbcwvz799c+HiXadWP\\\/KZh3ed3Ndc2go6AeXY2WwJGxlOmZagEUqKxmF2D7gP09P4u4PK7ryRMsxwx5ZKMYSNS0SujwQT5Zy2FJDr+5jiC8jzkpLGDDRAVDgkPPaq8Osnnnli4uDe1\\\/7kBdltY9ChsJfM4hWxnsULCI\\\/8xrETh\\\/+7\\\/\\\/Zv\\\/+yV6\\\/\\\/8X\\\/zFzet3w84WhW056K5tdM6\\\/uzA+1vjqzz325pu3V5a3iCMolRAMzTYIEUFGHIXy+KfO3nnzuhwEcSHBaggFpCRBp0LbhvMa+SAM2auUs7ZGjo1+n6xvG3X8nFy8vUWNpnapGdMH7AigxIYJyBZzVviZRaCI57euT16NvDoq9cmDex\\\/+hU9PnTr03vfPX\\\/j+G5vzi7LT4qBLod57aT88IBWSDKF0bzwkFVIUcBQOttrL7y8uXVqaOTZ37JmHo5CbS81MlNN2MODCVNKu3Ysq3hgieDvsNuRvpd6EOfc+k0MBzkzPBOVcWSysWYmXQBo587Wt6aOQWk8aPpEJPcJh4ZLrw697o+MP\\\/fynLz53afPGXdI7MBro\\\/gpYISb4CziuV63\\\/yi985tEH9v2Tf\\\/ad+Zt3VdClqE8q1Hl\\\/JMXicufLz5wZGW28\\\/PKlKOjHHRfTryh7+05nvbfn7KHqaH316l1SITT4ZjvDUxQ6yCjrUlqN8iFqK9uUU+CSBh74fqEwaaTDOv7y1qKMD7KWzPMAVNiupRWg\\\/alTgZJ0GYgkBrpV8uuiUt\\\/70OFHf\\\/nzzdXeq7\\\/\\\/3MbNe6rfpqCLqEdhHzKAPoVlRCnyXulet0b\\\/xhhgPZeKuv277853toJzP\\\/dEbcfY+q1VGaoYgGH0xGAia3NbMYs7xQmSoddicJpL5UcAaz2IXLM102mybZ45SU6GdVyQn3+QDVLY\\\/k4azjOZhJ8eTjgkPHIrs+dO9Ad8982rHPQp1LCYeP6umzF6dEtCjDS8D3\\\/oyKXbm7cX2nHfRLd2hEsEUtHC0tbPXr528tTe+kiVHI819UkTgolJKZaSZEgypChQQXT7tevHnz7l1WvkeAyXhcNmC9fQj8+x6ZFr2eeHE0bRvT0OjUtRZyWsE8A2zyi75kzbiUghdm4axmDaJgBiyHlfTIARrz3oRFSwHqabO7DSEJXGoScffOJXvnDl2ctvfusng40NHrQp6FDQ5aBP0YCjgOPtF5GKoEKSIamQdWCMQopCkgHJgMIegg4Pmqq7tfDahWf\\\/1X+aPjL3xK9\\\/sTIxQV6N3Co5XkxJTSb7mcGzPVllyzSUSxrdOR\\\/50vtpN3h0JDQYIyjMngHkfaqy2GUe5jaeHiXutgkcCeYkNC0CjamRZsdDz4W8mlMbfervfX7h4r2VS7f0UB5ZIsoEQHjwqvDrwquwcHoT08\\\/95PL87WV9VYXjCLcivKrwasKtMPP71++9+vrVhXurrCEXcYsl7pQmObSAcBhuvxvtf+LY+o3F7lqTVAQVQROdsngXQ4CQFTtlXtwlcWKbuqu87jZ2F9tDnZyep\\\/miJXZF+a9A5Rp2sQ1O6lNkyPkMrTNLR8xmTQLLW0r\\\/R+j6X8fAEVEbOfTU2bM\\\/95EX\\\/+DF+beuqH6bwi6FPcSYRN2JyWa8KbYJzNA3kRlIT3dFSiXTYxX2woWLd2cfOjT3+PGFt2+qMErnxsimPoy8TiDKsDEo3EkU7bYK8\\\/DcnNBOLDhvB4bMUaToDJF8zqJEbkmFhBzABGX2o8bH0QekcOE49cmR8V2TK5df5ZiDG7HUdHgVl\\\/KO59XGP\\\/LRh5947IjjO+xXTuwa\\\/fiTBxGP+JKNT0ISRYqZpVD0pc8+HEX8yqvvv\\\/yzt4KuFryIOMYcMikdDweDza07794+\\\/pmHV67OU+hmM9\\\/Y0TwmVMT\\\/QWmKkoNLZioEeeVMLiJ9DeKc2QrHMIUklOGz85MPtlWWDRRlcSZJ5SgBTp\\\/J1KTJwRjy740TXSYTCMqxBoIDR9f\\\/td2nDp35+kde\\\/P2fLV66xYMOwi5HA5IRpzIImRiFrbZkiHYnH1CxYkJKSSMm6q2pN771k0d+9dOn\\\/tbH3vvjH8kEHsyKIYiU4kzywk4GUGT92qzbTK+kIMyU19uPod9u\\\/gUyU7Ksj2fKkXNenxtmuC7veVveDqCCiHnpVEPHIhaChTu2e+fGcmvz3qrGoyFuK6fsJNf3\\\/V\\\/71c\\\/+2q98otOPWv0wYI6YQeQmh69KXi5ilkyC4ANgrvjOZz9\\\/7lvfmvm93\\\/lOqOvGRHuGWJGK9CuuXls49AtPe4160G9xUlMhCxHMuQCFgpLgkISch+GhUYJpNkAdTNvmi\\\/ZIkXJmS5ofzqVjkRJ4ky2Gy7nNiJz5dhHvyPH7RYolhpWQg0gwHBIeOxW4tfG56Ud+5Zk3v\\\/vW0sVbHHQo6rEMdemRnFYOIAyWGZVOCzhNTZAogOl7GvaYVW8N5\\\/\\\/wp5\\\/6R1\\\/sL61d\\\/dEr8R0nRYoZ0MJlifOoWYPz9nMf2xjaVi7NRopZlYZM\\\/De+vpweH0Z9jhwVkYfyo0vXRC4JMYa6lmkzF5FBDBBcOO7EvpmVG8tRr0dRGDMkWMWcAAhy\\\/Nl9s1\\\/9+lPff+7i737rhU5nQEqyDIgVwFqILSF+cTzYgwvhKuZqRfziL3z0S3\\\/ryR9+76X5mwNmBRkQkdah4aS4by61hO\\\/VxhvButD5c\\\/6exOW0gavOk1rLrg8KbIISbOcQqOd9mqEoSUNQLGUK0JyCS3HeTAgGZt3UJE3kHoBMnGiYtqcBeQf09oiL\\\/6o\\\/OnrmKx9eurR455WLqt+ioEsyJMWAww4ydCtbCqcx7Mue1lqZc2KTyyxJamtfp7288fq3X33o555cubGwce0WWGr5LyRg+8QPo+BYbMXIbfjcKBVczEGMXFNyvHS2nJ4Fw7sGpfpiZTR52\\\/a9zLEpBYBxPDMUjvCrO04fmj9\\\/lxTHsGzdj9HLAwKOv2NqanSk9txPL965cVuFeqNGWb6aU0GgpBUOB271hecvff0Lj+w+sPfOnUWSIUmw\\\/i1WmmPBSgXtfr8XVSZGNCBYB0PO6xzCEGW7b5ezTFHGmlVxqnlaPgCh7cmx+ECnZf6OlbnEFMRLTKn+Eqi6TaZh4xNwlhiAC8hEOD65\\\/oEPnRrbP\\\/vK\\\/\\\/u7Ub9DQZeiATFDuPCqnu\\\/5vh8TI8yQYCLsmTiFUtgsKBmFg06Ho0FcTMqQw\\\/6989enT8898PWPvfgv\\\/0xKqZMssMp06XMiGNYVs+RChsQjDNOPSl3qXDszMs+uUiOkITLsxorJ1LVzurnAsJSVhzm+A4BwK5Ude3de+ZvLWqApq6GZY6CKfpgGZktJQZuiAWe4FrbxxgZRw6mwWyFyKqCK7xOcFEbIMc6biRUpKXuD5dtrbqOWNFFhluIpNqmg8zjMIKEwDSpuDAzxmkgs2m1RCTKMQmwh96GDD84Dx0rKemvzlOSswDZTQuua53sYyDrMQsMSPXYqjcnxIx87e\\\/7bbwyaGp8UD5aE6+07sO8bX3vqkQdmhSvMfkWW3yQvo+KhLxvyViDg7krzT\\\/\\\/shTdfvRgOeqxFvcKBgrj0n17\\\/6D\\\/6wuzZ43dfeYfkgBAQZApkKxU+QAaZQWnvv1SYlE3vW8PE1M11ezJV77ynDSiP0OXymzrM\\\/I\\\/Tub\\\/p5cxljgQJPxGCSTiuI1wnbHfSvRcrC1ACp2ZmxVHcTVEsI4oGBriM2Ww7JW0AYpfgatRSxCSVsj6t1nhKnlNFYXtl06t5evVwwhKJi22jL1OOAStmiUNjJYbNcjJ7hBLrG1sFHcPbM5ahgXGco6Tey1Nu7M\\\/DwwbToG3lG8EWcCPegeTW4FePf+aR7lawcP4aB12SASmlZ79z+3b9k\\\/\\\/bz++cHH397VuDMCSBuAsKEkKkkqGat6wTGY17ZE2NARiYndv5P\\\/w\\\/fvmf\\\/k\\\/f+fEPX+ewyyokHhDQX9u8\\\/vzlB7\\\/29NK7NyLZJzkgFREJIs7A+Wnine18WzzZ2Kiw5HryMRR5GzZ2iyvFgvei+B0MZUxgm5iZo46U1DBMZQAOEARYQEaRKffCFqgcTJBErCLiJAtVkdG5TptLCU8KTCSYFThSKoqYlS4W80qPnMoEq37gVryEgGvqoCSaZyjRQ78Pkr30opXLCtrG4fcZ5g8ZNtr7uaShWk57MOy7851wHgLPsrujabmY6tVnMypNkfHJq1XGxmfPHnv7L96SYR8yYBkSKwgXTuWRR0\\\/s3T353\\\/2T7732+mUV9uNbzCrPyYyvqjJaYnpI5UKI6V3j\\\/6\\\/\\\/8e9+9atPvvzy5fZ6jzUOMRLsBHfeuLbvqeOTR\\\/asvL1FwmU4gIzn9nGoZUvJnbmk9WVtzTIv4zzLhEwF7oKVYVGvg82CeJhUtuEPPnRxsK0MmT9nU463eShzBg7i\\\/OeNJzNJnRBHyyRr1ZcvhdQaYBFmBaWS8pIsxq1Ja0msUiEEG3O5+PE2uqhMAZm3w9Uw55L\\\/fNM75xppao2WmStZiO4h4\\\/UC82bI9i2jiFOp3c2Q7V6g94ish4W0Y6lxFB4cf+rwnl4zWLw4zykIhhUTkXCnpyeDTnDr7noYhjzokUzIaCUJO1uOWtDprkeuv7aC1krr6L6dIw2\\\/vc4xkQ0RR4PB+ubd168d+fxjqxevc+gRAoaIpdkzW1wuVHq5LnSabxp+CuV9GVPjlN0Pphtk+fAg6xqA+QPirUpGnJyl9jBBH2zr5ylFYHY8P9a3p5JaJsZ9pys7\\\/ZPq3ueXpqYdKjP5Tf8pTrgghDc+Mlhfz5uApywiSyDYVv4jo6uBkvZjIeewm9qFh5T1XLNMkk1dyrJ+Kcrcp7mUPGPJfJdqn1kYEGxnu56RXjmxDIFujwmHHE+4ztGPP7h8ZUEOBokqQnrRhGLEbBeh73oyJdZ3QuWOpNxucXTlxU4FJJw4S2JiZqUACRmyDFcv3zv+zEPVyfHuoENCQCEz7ykknJSPdOXmFCizqi62ZtztkyfkPVGY0kIoTdiAXPmfKoRnfXsqUcsqv5tG00F\\\/LaWiUHq1KkEgy1rN\\\/qFililqCxZmjkvMX0uPbqMlgzywQ8BxRnaNr74TUsbf3QbYyeXSZshRa616q0zRncrlP0rTDJSqJhU2g\\\/H0dnWfqsqXUu8pr7BUIiem7HQsp\\\/RkHzXZCog5E36jPn5o5sJfX2SpQdiR0W2BZBroVaGB3QRK9LvKxh+28Yvm1jgVuL4kRIpitQT9iZVkFUGG7aX1sDUY37Ozt7TIcAyxaUNbtdTn2PIFst7GfQzsE99715paAakYnE31toVAORsIcWmsQ04pF3nqMaziBJmLreHtGddjrIJwc6lZHa+lba4UgcUZri7bg0wljl0mM9JsoRhhmTN6OqdIjpjQ6FT86ZnR99sdbWeRwUXt5EebFXAxXTER3VkWkebhJrXcxkHR9rh8A8iUoo45Nwoqs+6Gje2B7dllqsLlf0tXcQnaM\\\/6Bxo6BWBGUjTsG2y8Ua96lKD49nBBOY3rccZzOypYGgrLW4EoG+kLA95zZmbGqB4pCjsIYtEgpsz1m+hmqxtkcn4UQXqVSHx2peiRSBSMdLhQpyUqGnf6t19+fOLZ38e3LsSxD1vcuzdxhdWXMO27Rrnl7UHBSE+bs2JGLuBi6CGKwb+mMuLQ9aFpvZjVVkkMbPZ+ky0wsZRgtXrjVmBoFEQuHIECCIYy3KywonAl\\\/heFRnBz7gCgBQsOGCugJsm4bQFQaVQgEmy1dajJnhGnLgrFwx8pUagEbcW2hYPIAGhghC6WQlHjXC7I1SUy9Lsv2DJRaaVsuWVYSbFgisCl8ZFZxwiEI1vg+FbCKoDILznxqlKq227NEACwECbexc3R9sT1oD0hKyIg1KIpELAooMDs79k\\\/\\\/+6+wUnFfLlkhKiEPcKGbASLFFDExkSvgCjG2o3Hv7ga0a1AaxVlqaFRzfvXAZ86K7z4vB4ISjY8Ef8r2WKko3limh8BUUO0v2dJuacltkYQ5myrmNcdgBNGhvuDpoNw0qrVqwmLPXf887q9IuTm\\\/cvAzj4tKVQZdEi6JkJQgw59X2\\\/KilEeLYTMAmIWKMPzeMhC5EFq0a2x2MgzC3lZH85j0mMSargxJ5FEc3aAsQMOMweCc0Edi2wgzniSjMC4O8+z2qt1TT8fBKCBEYeW1mQRSgliM0bwOOVV4DXLr5HqQIYc9DttE\\\/cRYRVFZ+84uapFKKGi6TGNqcv36sgwGWigongMbKq\\\/Njd7v\\\/9n5VqutopClVEqlewgEbfnDSpl6+IDQPCUIF67nOOLnvny25juslKHjrFhju5Xsbvb8nSNOtSJbCSmIzTecmS1yPu+lcjFZ5JyebNXY5Ca4ZsBLdrQpZWR0MrkIEc5FAtq2W81lom0oI9QxKD2oJKmotbg2sWdidO\\\/U5tUOiT4jIBFBCS7pVOSw\\\/0XfYZNbFH\\\/ivIoroEWikhGWt+vI7Ma1RdkfGKjx3FgbOQSv5URoRiiy3FTJVhlM4QRsss7SJQArVCVHNec+CAouT6YvIWc2M2wGVi7B1Gu4lEihvInYT51qO2ePHTl47uDq\\\/NK1l9+T3UTDTqrYw5vzw5b0LTDyVTcc0di9Y\\\/PWRsqN0CLLiEFITMytZu+vfvDWwsISBT2SUTwMJGVhP5J0JG4Yihh0Ra4Pt+p6\\\/mOP7D9yYGeqp86s4mOJFbMMuwN2HKfia3EjS0DesHa5H0atxNBie+0Fl\\\/KofTMlNDD+hRtkA1VtawKUNuRzhvXYtl3CccNFKZJhf70VrG\\\/tOrF368YCuz5FA1IhW+ieROoYArE+lyBhVEkw25XgWEk2XsOKE9BFTCcj0vKyjkdetTYxOvvgvlf\\\/w4\\\/Srh0nEmvmteDM28vupyFtSQJW4zKnD5pX4MwCICyOCZVEORNGztlpANMvw0DsMpvgYgNeY6fx1jvRAz0PbpX90erUni\\\/\\\/48\\\/uf3Rua7Xzrf+LnH\\\/lTRY9VgFBgFXMk+AMgzzUhESfd45Tm6itXFyIUdrMpBKUL7OG8UpiFfZZkwlVGO8H7dsT3zgHgI5sTFrw0oFwWTgkA0QBixEP5MW5OVuyY8zESgUREzm+p1uABuw87yTA1kSqZLI6RF3O7j4mX7uFMrHUFOqDuLailD5eTMAKzhRlw2K9K2IeQ6QGwa0XLx546qHrz70XhT1yXJJOopIRX1QVgwaJNRSYUng\\\/yqD\\\/SCWcFURIrBEznPTQIRwWHrk+PH\\\/m9JxwsHVnJcY6JWTCFL9mlWDIua2yOfZkU3M1Y1fm8mcg04HPdSZz68FCP5FpA8R2kc\\\/2AA2GnBEMWIJ5YoqkeNTSr0jyAq\\\/mTe3aeerQ+IEpdpzxnY3dx2bnX3sbcIgEpyVnmg\\\/AaskkEtewIJgAeY4MoxzMMDPNZJJMHHdEFYTwfSccBErF40QSjnAd4fly0CcVEYdEgAuv4ktJUkmigGTgEIs4S7LdlY1IDSHSYgz2CJdNMDgXOqTZG+Y8gpfLQRiIWRSFQa8VvNhSwLH3vu29DBrK9TD9s5jz0qjWkAuZ+WyC2yQZcBjMv3xl7pPndp87dPeFNjs+OWHcFofQA5\\\/sjBIu+XWN8Ca2XTVT22idAQqXnAoJj5miKJVdA0Gw45LrkVsR1dr+J09eff5y2O3G42PmjEsXU0htRhJgS65ZAjycaV1nOWe6QNniN4usCQwUFICQx2RkmEOda5mWKMl3mG3gf+whnmDPGakLXeqCTC5BCM916g1vdrf3wMmo67R7fOPKxpmp+r3rm1devsGkKFHOZhYEPQc3MnAuymJliTIrJQchBGfYRoA4GUfFcTFx8oSYO3P46d\\\/4\\\/Pm\\\/fOny955XKiIIp1o7+rc\\\/u\\\/P0wbf\\\/1z\\\/v3LnLUpIQO44d\\\/vBvffX6y5cu\\\/PmPEywbUEY9SnT6hBKCFXMmso4URpXXrigBxsDMOlBKmjHMIVM4pZvfyfaIhe0AxfeD7peNIwtztSQ4wezWpMkcGzFE70MZUtQLW+3bP313\\\/8cfWHrzehiFpCSBSEl9g1udsDcITzy4\\\/403rgSOD72AEtgapyWNNfsSRMKt+A8+sF9Gamtli1kSU0yodzzyavBrBz500hvxb\\\/70HZYBRWGCr8+KKs6Ia7aLaGZ9nCqSIGcCFzfrs30lYl5P7IKa5NWU2FGR4cqQgBxgl0OprDglXJAESKQMR1pO8neGOU1lUx1AABCe6x7Y6+6YJHZVL5SdcPDiFal8Z2zs+SutGyMb61cWWqN1YtKiafH7Z4NTkpOpRm7kzUTMUrXX2l6jbpw7Ih5BA\\\/lUjnnm7JHpJ04cZ1z78ctB0CMIb2x0zxc\\\/XN+9c+R7L3Xu3CEATmXvU4\\\/u+NADTeGJv3hORpKsNc5sjYcEhBC+J5hkENnu9ZzXfyoOn4bJuJro0eE8TbckUbQiIyyDimFZJlDssBVMP3KTcZSxeWwEHCtSEohI9jns3P3p+T0PHtz\\\/8Qev\\\/+AN5ogCRaRR9vLO3aUf\\\/Oitr3ztyTNnDkbdgQASgGlmMqsXtpkDMQMV78iRmZ8+9969u7HiPUNoKCP8RmXnjgMfP\\\/3+j94O2u2Ux2guiDhwZ5QWqzfLWevXCmKwOkPxLISzyXU8v2btgZN6SMHR85JYFZdEOuuyRu6sOIVWJsrilCoMJCERxKmmeFxMphIdSvkTI5WpSarXpV+R8AcL63J5jZlj1l9lxDu4f\\\/2tO826krfvqrEa1aoUNAlgEoAy\\\/c1ysAzOIUqZNG63s7hemZ0m4Wnbc1JC9zXTZDabS8K58dL71ekXbv\\\/0najXJxUBCDY2r\\\/yHvxqdm968eI1kCFbE8uazr7sj7uL5a2oQkiOy+8BstVwSYbFKzaV+JPtBwZzwA1lWZYOLUuwOD\\\/tFdgn3IZwZtxcfCCi8XYTkwniMS0aLVrKilbMCivphq\\\/3+91976JufWbl4p3lLswpZq2j32s3\\\/z7\\\/8s9u3V86ePex7jtLpLFsjERBI5QZ6POj1\\\/v2\\\/e+f7f\\\/lSr9Miinn67NXgj4jG2Om\\\/9WR7oTn\\\/8hWOApIRyDAMzepCpLDUcpwgbMnN2Oc0yTYhMqVNJKWXcFi40CaK2mwIDoTruK7jOo7nOJ4HzxOu6zhxI4aZZMQcRSoKZRjJKJKhlDECUxJrp1RO42TsBB5\\\/QcQKDsRowx2tVevV2o7x7upm9+Zi1I9Y6bugI6oglqjVIxVF169HBzwSgq\\\/exr7d3FojOJkUaokON5gKuFVmUkxKtVe2Djz+gPB9GbiAw5l6iK3HAjCr9Ss3X\\\/ifr3M0YBmCFCvmQffeD18g4VDUJ46ImWTYmr\\\/91m\\\/fTfWADHeUrPJk4xZUx6qte+tRP0iyBrJnr7ah2tDteF\\\/RLqv3TJSQegGzpQoTD1jOG7bSL9s0O+fUWwpjsiIgW3CRXBWsFJEkGQF9Ft7Ku9dvPXv+3Dc\\\/9vK\\\/+t5gIyJWHPWgIlaD9lbwx\\\/\\\/Hf\\\/72H\\\/sQbpwLwUg\\\/OC4n7FOZWckoCJVeqSQgHHYr5DdQH9v7xMmdx3a\\\/\\\/G++L8NBInbIaeiwxe6RCH3YLZNMzjjF3+ig56S7DkKwcLWiFDkOCQ+OR44Lx3MrVb9Rq4816iPVkYnG6MzE9J6x6ZmR2ni1UnXJddlxGFCKJZECC8VCSkQy7EWdje7yYnPlzlZrcbPX7nbag26rHw5CFUlWilNou5LkkluvCp+cuV1yIOX7N1tLS1sXbrCWF4itGgw8NBTt3RFdukmqgvUWzU7Q6oY4ekQtTvJyD3FvxpoyG+tPwBTLZwYpbcPaXuuOTtWrY\\\/VOt81wUgBG2h9QiS+GBpoRh5QobsX5tgwTaRIQMakIUY+Vw+SQcKEPOEoZ+BwbaKY5P9yxg7s3L9\\\/mMCAlkekgmiWfKmt5GLgNHpalboOqZdcwoGcD24\\\/i2LeURAhbRMXaRsk4ikvZO2wP+XIUj7hboH9Psfb6DPtKuLeePT9xcNeDf\\\/ejb\\\/\\\/+c+EWETGHPaiQWClWgyhMmX5W8zi\\\/N9L0wcAiCEfLXaJS33ly38Nff\\\/SV332+tbSVRA9JpEAqzTsNgFsekk6ZtZBIIIgaT+CQcJCEOxIuOx45Hrs+hAfH9evV+mhjcm76yLm5uaO7KhN133MrDrGirXa0uti89v5me70bdMIwkGFEiqEYpCRxJAR5nlNtVEYmquPTjfHZnYfO7B+pO8w0YIp64cZK+9rFlfmLy51mPySWAmLvBHmeurUiV1fC87dUrx9LxOvLTsru5YEgMDlGwqX1DapPkO5mVSrq\\\/A3sneLVxTihTet9pu0TLWatJBIN1lthKEf2THdXNrSRK3MGqEi6yo7+JccV9fHJfrMZ9hQcl5jheF5j1KvVepttpi5USCA4TmNyYtALgiCedurZh1EOaQaOy47nNypz5w69+e9\\\/oEXZOFXF\\\/wAhzTAAzn2r1OQAtsAL3OwZhprplLtaso1CtrCr+fQsNzxOUEyWTEIqxyByHxP6SJYR0YBCETbFe3\\\/47Id+68uPfOPDb\\\/zxz8LN5JKajmiZISuXYThh9UjgaJ0vcivs11Fp7Hzo0IO\\\/+KHzf\\\/nu1r69PrmD184jbdUyw0I8mF1lGPADwZpEAwexLxdify\\\/hsdb2Fz65nnArwq9Xx0f3nJw9cm5u6uDOsYm6VFi\\\/u3XjvZXVxXZzrdNvdvvdftCPwkGkkSIEwfH03GHhQClK4JSZhqArfN+r1v3aaK0+Vh+brIzPju4\\\/M\\\/vgZ4+zpMgRSwu9yy8sLr93L2oyBy47DaqIpOOUyubGznAp3Jr2zfGV2wRBSkIOOAgxs5Nv3KCOS3t38a15wCFw0sFKwMYYQuYibYkVRb3e1tWFXadml995P66BY\\\/ZDPHX3QUI4RAyO9nz03Ln\\\/6hvLP337jX\\\/9R2G\\\/D4I3Nvbkf\\\/PN6aN7f\\\/bP\\\/mT5jXc4YoD2ffTJR\\\/\\\/BV2+9dPHN3\\\/5zpQNv3CURWU0eZ\\\/7+2L4Zr+53Flc5VpTmhMbBpQBMexRclO5BqeG2HX\\\/sxgxQwvSGZS7PaQvQPPw5exhiOj6bGTTSzJStVu9wKBmM+TvSfi+DJauIogFD9FbWXv2d\\\/\\\/zEb3z2oW9+\\\/MIf\\\/nSwSiwcigbIVC2UYXBdyp\\\/Vt8HR4ShR2m+I6siBD5868rkz1\\\/\\\/63YWlbu0fflTdXA3efZ86y6T96\\\/MCVmzODMi0VRTaztIjPS8GoA3ehK+\\\/L1x\\\/bHri4Jn9hx4+uPvgFLvO2u3N919fWrm5urGw1VrvRP1BLAEW05QVGwNu7YkpiAFmxfri6LpQvz8JEZHorid4ZSkB8kfq47snduybmjsxM3d08sgvHG5t7Lnz\\\/vrNt24tXFoM2j326wg6FA44GlBm9pgg0fbOEBwKiLwakaAwRC\\\/A3ARf7fONLZyeo9UGt1uAMmUV7WE9svIkXuWKOGIZ3f7ZhbN\\\/+2l\\\/pD4IuiS9JPvQlSj5qTS7cP3x8ZG9O9p7Z+DXKWIiiEq9tm\\\/G3zPl1CoaVEBAdefU6L5d1etLul9FMiJmoVvNSM9K7brn7n7wwNqVe0GznTAVlT0SYOSQopzukQRMU6aVlYtQyfOwOb9yjT2Lkkk9F1gAeTqEMWvPc4MN3DCXWFcOYQZwNpDJhhmKFIEiAnEEJrTvLb7wb7578pc+9fhvfubSX7y2euUuD7os3FiVNF06SYVtA6gSoSfhJbaSFfJq\\\/sTY0WfO7X740KX\\\/9Pq9129gcix45WK41GbHIa8G4lg\\\/KuwRh9lmtlBmIlVMgVNht8JuBW6VhEPMDAG3AscfmRydOzl77Ozc7pOzQmDp2sYr333v5qWlzcVNKVWsqqokkTJgXGz4RerEDI2ZyWNfedgbr9386ysr52+QDCnKgHXIoNqJBCDQ3xwMWu2Va0uXf3KpUnGm90\\\/OnZnbe2bvwV9\\\/vLvam39n+ebb91auLcpum4I2RX1SAamI4vAL2r2bLtwl4VC1TmHEoeLlNXKIOCKleGUTh\\\/fSO+8TyWQRl6tHpVsU+nBhSSpsza8Jl+YePXT9R1ssPOKIVMrf1EY8ioRLXuPuS9de+b9\\\/a+XifBgSOVUmGrQHr\\\/6zPxmZGV95+yoJQaiwcG4893bYGaxdu6MUkXCYpJSKM\\\/snfZZ55HiNXZMzDx9+\\\/be\\\/p6KQVARlSZsaSgt5kkRZDThM\\\/KUINctMQnPjDBtjYe+srOEILjO7hAHaKAMAWHhvKmGslsHnODbIY2JJMon9zL1V+c7vfO\\\/UF5988tc\\\/deW5S3deuthb3+QopCggOYhFmhWn6Jss2ArEAdDxyavArTr12tSh2VOff5hd5\\\/Xf+eHW7UWWirrN3r+9xWCanqFN5iiCcMivkFMhOSAZZvO32MdXo+Eccnz2qvBHUBmtjI7Xp3d0uzzo9hyoyd2Thx6YO\\\/2Rw2M762u3N1\\\/5T5evv3Gru9mR4YBN44REHQcx5IDt1A4kFDnezNm5fZ84SQBqtY2Lt6JekEhTJ4cuGydz0o\\\/laKCDdq+L+c31Oxduu98Zndg3dfojh089vvfMpw9ffWvx6vNXV67cGTRbenuQksTA4VnaOUP+OmSfKlUebPIg5FYLrkO+Q4OQlrZorEFTO3l5KW5sWN6NuSIjqUH0IEqGUbdz7cVLcx85efulq6EMSAUEqdOMrWZPVLzxiZGl5QozggHd\\\/OklZoZXQ5yeYOvW6tbNZVKAVwOIIKKQb75wgeRAeD45bq1RrY1W76w0e91AjzpICHJ8uJU9T5zobbWbt+7EKyfVaysQcM3vFznamZJzGeVweGMmP9InSrRWUKKcgIyRZ0gwG3R0ZHwSLozoc5NIDFPkywkSpXx5IqFIRcl7VlFHvfcXz9977\\\/bDv\\\/zJE584ffl7b919+0Zvo8nhgE2d\\\/FjiPqEjxnZrHrlVb2RkfN\\\/08Y+d2nVi9sL3zt944b2g3aKoR1LGCYyKKGjh4DG6c4+7TfS3SIXxIIEccnxyHFJhrMynZaRdH16dahPVyZnP\\\/OZHTj6679p7a6\\\/\\\/5PaRh3Yce2xP2AwvvXjz8ks3mivNqN8jGZEMOYVfKZmrnlOBd6NJJiDAzCqKIsUKkDIiFcTmRMm5UxDlFUQSpr4zgeGwikKlVq50fvL+3Vf+tDFzbOaBL5z++D\\\/6aGuhdfH7F+9eXu02Oxz0SBDN7OR35jGzj+\\\/dpUGPZMBRSAoUhjTSoCAgYr63RkfmaH2DIxUnxcZIIMOWGzxILXbOMqBwcPeFS3OPHjvw9KlrP3iNZZgYlPJ7F24NwvAf\\\/tYz3\\\/6zV7rdkGNNkkSjmSiBZ5juLrqQV8wSAn7Fe\\\/TxI\\\/uO7P5X\\\/+w77c0NVkzC0TtwdPfUgSeOv\\\/m7P1D9buoyRMxD9xFZOMAyKUNLg8YEByLfmdT7YPwR2Oj+UsBNJkxgz\\\/FB4EKSatEh8lP4nEpCQbISBX9w5HSBkmJauOz45Prwat7I+OwDR8995XFnvHHjrdu3nn+3u9oM2l2WUUrDZ6WIFCDIcZxKtTZe333m8NGPnayM+LdeuHzt+Qut5XUO+xT2SQaxvrBSTIrgUK2OwyfoxlXqteFVKRzoJZIMmgDhEByGQ24FlVHUxqi2Y8+Dh\\\/\\\/2\\\/\\\/VT9bonmDtrvdX5rVf\\\/6srdC3e66xuxmUkGAFAWqMW6+ZyxK5NNSI5LXq0yNXng8496MxPz33m5dXU+qQlVTC1mzoAB6dlpjr90USQE4MaAdeFAeG6ttmP\\\/zKnPnDz91IF+K\\\/zZ96\\\/dfO1GdGKXakZ8ZYHGRmn5LnW3KOzj9H6Gh06Xui2+c4fkgFjS9DhB4O4CqZA5QlLX5TOjdFgqXBIuHJ+9Oipjex4\\\/\\\/cA3nvrZ\\\/\\\/PbneV1kn1SIQGe6z75xPF\\\/\\\/I++tGvXRKin+IYxCzLpErYQMRxfAgFygUE\\\/+P0\\\/fuHb3\\\/rrTqtNAAmP\\\/IZTbTz5m58Nu\\\/3X\\\/7e\\\/UIMmhZ3YUtu6HWz0F9ho+5ENHMyhc43OvFEVmk6jyZUYfyQXgIbq1iJn6wyrRkSZH302sMhttrIK0eQWsNGYzhkYJp3GtPoixyenIry6PzY2sW\\\/3zpNzM2cOjE00Olud9uLm5nK7t9WT3QCC\\\/NFaY+dIdXq0MT3mONi8trB64c7qtYXO2iYHPY4GsRlTgjKJBx1aa7Tq0cEjPH+H+iEclxyXoh6H3Rit6nqAS47HXh3+CNV3OAeO7nv0+Fe+ecSrOL324PnfffP9V+d7W23ut5J9HiWkgUzKMdfSR4IssycugIhJHsJxAKggYFZIxvEG0zFBwBHp0ksrABqXOzGmjhP12JsaTsWt1nYe2H3yY0cPf\\\/LQ+mZw+Wbn\\\/WdXIwhqr\\\/L1qxR2qNehBw8ReVhtwg3VtWsUtElF5Ao6fgg37nJ7i2QAknZ7zERoCc16iUc1bpX8MacxcexrT47N7nzzt58NO00K2xQNSCnHcaanJ3ftnnJdx1jNyPDu2ewhlbnkNAmXMtpcX1+4txoO+syS4JJXQ3X0yKce3v+hwy\\\/9i293lxYobFPUh+7AKZUBJ80xPbMtGzHE1jfde7lbag79YUTCsr4qhpEkjP6oOSFETkLC2oFFN9k8tQUozEgyYGQmZhFnHZxaWMXtTSfdinA8cjx\\\/bHTHvunG7NTY7kl\\\/tAbfESAmKKnCdn9rYa1zd621tNFd3WTdTtRBKd5+CTYtnhQlYEiWqDdo7iCtN7m5SSy1LTM7euLnw62RVyHHJ7828tDps584ffLo6PJKp9vu33ln\\\/tpPr0aDAUcBhYPEzUvfaUUJ+tyG2ZpgNGVIV9liflkMYFs+BMlAUhAJYhl3lWJzxRxtKdFTMS+pcOHXndrIrhMzpz976vC5uXvXuq+f31p646raWCW\\\/SpurdHyG4OLmstg\\\/Kl9+hfpNkhGBaHwEu3fxpasUDcCx51EC02eDOyKy++i47FTIb1BlvD499cRvPrN2ffXiX74pW+sctCgKEklscw7HtnhPTmEw+VoI0hQnGVJ61rg+3MqOowc\\\/9I+++Ma\\\/\\\/\\\/7Cq+8YOzA5HMlosJvnSImfNuUDYwlQgSzAjTHfcguspfIQWCDsm2pFMObeZSLdQxWdLNZvYdOzrThsIHHMo17FhQFUxHLAwiPhDYLu4voqv+sBDohIKWtqk6Ir40aINpeUpDRDQqWykrpy0ZwLhqB+n+7dEQcOMJRqtUlFLCNAUHWcq6NUn0B9vD4xcuDJ\\\/cc\\\/fsRd7Tz\\\/p+9cf3cl6ra516Sgw9rKJs0\\\/lUTsT6oMPRhr0hhX3rGeKhvycHl0tCVCoXeg49dmZ8ZPHWzdWurcvMuswDLpdFmksdQ\\\/jZkBxUrGOb9SkYwW3u4uX1m89ejhRz5\\\/4pkvz7xd7199w+tuME+P4eAUrW2xWhezO5TvceDFiN9mlw4I7JzglXVWAMWUF5TNrPUVgFIkJFTEKuxu9d\\\/60\\\/NP\\\/oOPdlr9W3\\\/9hoxCckBqEBfPmrzPdkhhNpsZbLAQACdOffVg3HHJqZBXGT+w+9yvfOzyd15ceuNS3GmLIX4WLNGccVqNyqGEwRwmjAtEihxGmhxUZws7DqUIyNxDUsWlHEAyXxDaNPB8NQiUwOFSDBjKZNQNhivSa60Sq7rYwiUkGSEKWAas\\\/y0HLAccDVgGFA30H2gHSRlBx8PY7y4VCGcLCSMEEbFkarcxM03skGRdkZJXJ6\\\/ieM6eI9Of+XtPHjw9c\\\/H7l1\\\/4\\\/ZeXri3IXou1uWzYRxTEhrJKIhZH4iTepqDLeAQPt1LbMemNjUUhJe6zMp8CUdKz0Y58SaUHIUh43vj4yd\\\/6yu4vPDZyct\\\/GW7dku5OBuVMOdqaTypmMMWUuYhqTqoJg497W+6\\\/d67b6Zz9\\\/9NiTc63lXmcjEE\\\/uF1VXvb8sdvq8ukmDkFJniM0m7d+DzQ4phQzPb7rBm5E4MSoUArWGOHZ6QNW1d2+f+NoZEk7rziYrECloMqEJTE\\\/+DdaHqeIErY5EEDHryGvmmleHX588su+xv\\\/\\\/MvdeuXPv+Sypok4wbAUiVFNnQmObkImNIkZaLivfHUVu7pGwT2icyk+25buiymDJheXyT3YnhPEsTBbJ1CV4HVOJVk6Kms9Eo238SOGKyG0PL0zw2NI9IhVBRHANzVXhiV2t3lZIpvGLu9py9s\\\/AbzBUa34Pqjvrkjkf+1qNP\\\/uLjC1fWn\\\/v3r956\\\/WbY2aKgQ4MOhf3YV1jbets9GDDbfHpAuOTVZh458eg\\\/\\\/vLBT59pLXS6SxssA2IJUihggLJWW2o4D0HCq+zYufeLT6BeU4rWX74abm6RChG7i5u6G0ylMiBxOqKSzDmSQbR2c2P+rcWxqcZjXzlcmahszo5Gh3bytQ2+vUgVlzpB1oiWTJ5HO3fQZjOZc6qSszg+dWNQEYhodj++8DXya\\\/3X3ly\\\/MH\\\/sc2cmDsxsXF9WobSI8LlEjwtKNvGlcDiVKdE2stWR2SdOnvu1T80\\\/+9bVv\\\/qZHLS16z3inIgzwBonrqPD4Nd24bCt7noua7V+w0F1D+XtVQGjRIHlDJMtFc5pHKKw1QoOtChX84NtjWLRb3OWpfaPshojR0FESt6JNbbTE05HywSGQtkAwEblwSaQJSWo8GIDExIUCeeBB2himgaYPnv007\\\/xod2Hp174gzff\\\/v6Fzsq6jn6kOz2cwqCSd5VEMKMXkx5pgtyKqI6c\\\/vmnph455E423Kq\\\/+PJVDnuxzCab79byvMsiDQSEKyNE7dA\\\/OLP0w7e2zl\\\/jQBc8yjjjYVb3qXMgTIh+0joCK+aIZTho9e++t7i12D3z8UNzs6OrP7nbnW\\\/jxC6xc5zvbMQ2BHoRd3s0O02dPoWDNOBT5gbtUEzGF6neD1yfKmO0uEA3rvHWetBsrV++O3ty7tAzZ5pLzUGzn0gtam0nkRm2xjTLVNxEdwpcjSsi1ye\\\/Br\\\/ujU2c\\\/tqH93\\\/4xOU\\\/+cntn7wu+\\\/EOJBnpHRiDAljZJx2X6yaVILFwv92Yl6xAugktzQ+yNLGM0xIZ3bjMXtlsmrJpUgLThBsWSyMLacXAyHlvQyqxduIcXzT9ePr01buOEp6rUhkdyQC1wVI8SzQYUs18ZAor8ZTfr1N1grla++ITJ546+PDnjm4ud5\\\/\\\/o8v33r4VdZo0aFPYpShINGlUYvETV3hW282cSWgojOejOrLr1L7pk7tDxQvPX157+waHKRSGswMxxsfp5WhIJ2gpQUJvo1M9smvtr98I1jdIBuCQlAags3XcALAEXbPmR0b51VdPSVKRCoKNO827F9amxvyTj80MJG89NCcOT\\\/OVVWp34\\\/6WhhNFIXbtpPVNJDx9rYIRt7g5k3shUvAbNLaLmuu0fIeba6QGJIOg3Vm9chcQx7\\\/06Pj+Xe3VbtiNCC7BJeHGAt56bBvD4j1y9L99cqvkVsmrwq16I6NzTz344C9+jMPwvT98duXdqyroUNQjGUCXgrpJluYmVj0HyotmcSFvyMVLDM9EOaN2MZXUhACKpWBSahhe85nwV0bTAhXUY0rso5FLaFGyA8t4GkactuaZqWoSTIvhrK5LJ8LItlyOspkdMOm74iwGkqY4EVyCQ65PboVqY6hNNqZ2ffgzx08+Mnv5zdVX\\\/\\\/RCZ32Lu1sUTyAMZ1kignAqvj8+KoMUEp0lPEZCAIJDbsUfa+x5+FB\\\/vXfruQs3v\\\/eabG9RlIwuU+UWISBcOD6cKgsnq\\\/Qy1zYhPH\\\/H40fb790ON7f0aos1ArMcOIkhwsl2YCqFlDquZb0KPQjRAoGd+bfvuUKc+8xRty3XyIuaRI5HnS7JKBYE6AdUr5Jfo36od1qS7cd0qjSXQW2CxqZ5a5mCLjgiDqBCcAQVykGwcXN5\\\/erSzOm5\\\/R9\\\/qDG7U0YIe0qRq5vhyZ8k6LlV8qrkVeHX4NcbM1N7Hj916qtPTR2dvfmjt65892e9lSUKu7oOhIzi4sWQHciEQmxPRnwQlm958YehD9LdUUs\\\/gMnSfk2HdUPsY7PCAvc1xkTp17z9Hky7vYDd8LE68mndbDYbwGXN2qHS5LHILjMZp0qaiArN9yWvzn5DVEemD8184ptPjEz6f\\\/3b5+fnW6rZpPYm9VoU9iiKs764HS8EhDvzmUf2fPzchX\\\/+ne69hbhtoFR+iqTVJQSd+vTJRtV54X\\\/5YdDZ4oEelIVIGnexPo1wRa2x48kH6\\\/v2Lv\\\/4jf7de6x0tNSdKiYpSUWJ9FKmeYFY2SFRc0uw5klPRSJG7aSUmIRlw0xQsfm7kizDQTR4609ead5tffzXn5jyKy8FvKWqfGg\\\/3ZK0FWtp0717dGA\\\/bayRJIJIMNkwlftR30mNKbV+i1RIBGaQ0pc9IuGQiFhFWzeD1\\\/6\\\/i2P7Z488c+6R3\\\/hUe7G1dmlh6+5yd7UZdnoqGKgwIlZwHOF7TrVSHauPzU1NHJ6ZODwju\\\/35n1y498p7UbfDsh9jAGSIBGOMWEGPCxxCMpm2Bcu7gsBMDh\\\/KlkQ+5zPVeNLpkq1ia0mEGZBR5HuvbDqLcFG5EKWelAU7iu10A3LitIZYZi4n5UT9PFUxilVUgILoaPID4zOyua2THc+Jc5AWGoNLwiOvSpURMTa179TeZ37jY+1m\\\/9v\\\/\\\/Kcb97Y46MT5ZzigKDRpBwQBInJcb2aHNzvpVNy0l2S0WDjRjCEC4Lj7D03fvXgvaG1w2KOorwkcsUhm\\\/D4FCa+2b3bf3\\\/uiP9YIW917dxfIlFfQwnOaEZjI6RrLKdW8S6REHZ+EIClJhky6T0iGnwcSmnSiDhTv6kiyuvbCpeZK+9P\\\/5Yc+\\\/bcPPvc7S2s3ujR9gKprvHiTBkwh08oW7dzNK3dJ6fxWskbb6u7C2DS7dVq9ThzFp2iqtsiKlSQhSUhSoVLe1vXBm\\\/\\\/7oj86Oj63a\\\/9HT+39yFFV8QiIAtnf6pJUXr3ij\\\/quCwqjcKN779Ur1\\\/\\\/q5d7aphz0WPb1cRYTYqTkrBeqkoWQkII5Fo9Eua6EvSbLGkSmCF4phj3dzC7lmTkfSLmCU67RfeItiolurpYsmGYPAe2YPQiLAVwwO+CM68+xXU0GF+ZMNjBh\\\/iL+JRPklRBKRUa9dStUGUFt7PRHTn3ilx6\\\/9vbCT37\\\/xe5mm6MAUR9RSEpyLOWirfOysSQptXn+xvix3d2ltTSvSyRobbECAIBP5CoJFbAcUBSQipB5mKZqdYgCFXUGolLpLawli4nS7i6xVlEj1m2V7OSK1R5iQXuv4o6M7f7MY+5offGv3+ovrMSWElnanApDxcK6CaVBT2iJWS1fiv7yf2o+\\\/fc\\\/9Mz\\\/+cM\\\/\\\/Z3X7r5zj9mnuYO0cJt6A+oT7ZikRpe6TYYDzZAiRQAm93DQ57WbSIfjEJlWpWLo4Kkki4hUyHJAUX8waC9vrCy\\\/d9Wt+l6j5o2OePUKhH6fHAVR2OpGrU7Q7XM0IBVxjMQIKZnFa9h3Uh6nKvGco0qgdPJesLAuMbROTUVNSlSpYyqTa\\\/hfDeUN58HYzJZp8zDEKYqcYBRJH+UiQNsN982\\\/sp0YZEwOWBh3HRVNq9UMLps6oaRGJUwxLTDW4XY8cmvkN0R17NyXHvnQVx99+9lrr373rf7GBoc9kiGzJKl594lcKqXqfSo+0YMQEWsHt3Q4wWZSnaowKWVo4EpSEjGKgC3\\\/CaUGS+t3\\\/uynYw+fbL77fuaOqksvDVJRUkllYT6YM\\\/UvEIRDbmXP55\\\/Y89WnyXPcqbHr\\\/\\\/q7Sg30XB2miFumV6GIwCzAksDEgX6h1p3gJ\\\/\\\/muUe\\\/8fjnfutDL\\\/7h+Ut\\\/c0Gt9Wh8F006tNGkZod2ztIgIgSsGdgCNLmPO1vc3aC0WxYHQKnjbpwAEwhaaiiCcAhhrMYPJxyIsCloUWjly2y3pCTMjJ0cJWeZzBSukv6cecDHdSCTiRS1wWCcV3Rluk8ByGV7N3lKt+yXUHSDNP0q8xyJYn5YLPJyfHarUcT3k44qQhBgiyOa+5cNUlUqUl14fP6NmrYwqdygA93j9mrwR9zG+Lmf+9C5L5976S8vvvPDd8PWFgddigZQUYossws8w+RNa72DYjUnsvR2E0Z0Ou1LlcU4UWhPnEzjzy70LJSjKFxvR82OCgYp3DFpo8RjGCVjVoXlmpoBqACIyq5J4XsKVBlvQDgZCZDZXGW6RZNIV+iVqrQzC5RkGXWX5Sv\\\/8WXZDz\\\/xzXNKhVd+\\\/K7q9Kju0\\\/Ruam2QX6XJXdTcJAgSoNEd3NqgfjPrJ7FWOkxp3OYJriBBkKSEJbJeAvpPQ7ayJeeS8b1mXaTzTE75EGyLrHH5INK6yVwmf1bwqEhtJ7g8uLgFMIwNIssmeDAaqwUqbq53wlyAxaSCAiLWjmKCBbQzS0sMxc7BLm3TRQJr1DhUkZ\\\/zXVz7h7EAIWJ713gHUmXUG508+4VHH\\\/vy2ef\\\/6PyF5y7KXhtBV8Pt0y6fPfbUUnqpbq8QQrgOjB4WJ1pymbhrahEoHAAo1voJK0CBBCcTUaGUGUiNtaKI2YFhdM2x2xxnVkPMrJaee2\\\/s3KHBZm\\\/px++qKEigJwwTi1hETWSxQjELXXkO1vnVP3o16gVP\\\/\\\/KjzOr956\\\/JwYCCNtUb1GuJub1esDNa25BRxBv3KOqTEJRm2lkYVOX3kaGTWGM55d4VDAC3MnaalQ7YaqullpG87Wxv+yIM+aDH+XWck0l0C+ovtlQfGUOlXDLIZNP6KS9VasG1oYMA66lO2qzTPQxD9hem4JupQ2xHTkPHO+MYZ2R+lCbhGd43dcFlczRPif9ePOf12KuiMuqN7jj7uYef\\\/Oojf\\\/P7b1z66SXZb1PY40jPl5LaKVMW1f5OImESxysGghxwku1k4GBDzYNN4SikoECYDZL0E+uhlooxNDlWtXGoOMI4GWNZbhVPHxJ4Wn+thebmnf\\\/w4tbVOywDUiGnOZuZpwhBcPS4XOfDGfmVOVbdZhVuytf\\\/9BUBevqbj8P1r\\\/7kquwp2mpR3a195vGxUwe6L1zY+oPvaMtPIplA5BOtUiktNDGz4TjASW4oC1ZCKCigMWdgDFNNI9t+hhWAIQdczj82Wyecm46bJxWzOQM0NF5sU57UAJkQ22VnyEWrV8KmJVR50svWuxo2DEnldCpwfBYuMUOGLAOiAAzWQCpwoUNU4v1mHVqWXSxsiiKbyuG2LWeKxrPtjzOGm0NCM2tGRGPyzBcfe\\\/Ir537wO69de\\\/Wq6rco6CEapC3QpD\\\/FMQvBcUm4iDt7EWnMdHaLZCLozgZwNDngk+ZKxjfS6z1DM5orM2EMZqq+hq1NiizNCGiUSjYleB29gRVAVZcpHLAaQCYcf21+nIzyWTjkVMitkHCJFaKQKSSpMmcOJoYCK1YqUuqVP3q5348++UvnPNC7P3qXlYIkZ2REVqrRWM3GP+qVJiy7RDaUpDPyARUcbVN8qGUSCZvZZJGPkiZzMhLkzL0j0XBFjl+Q7BmUpKm5\\\/JTKWBS8bcMTbl56wiRMwCrgeOgkPbMczDcsYYzanAr5DXdyurFvb\\\/fOYrCxRmGHIk6Y8ipnHFaYYHC+lOMiYM9Q9ipLGkqZ\\\/JyypUDQ+mWOp7NQUZs487nHHvny2We\\\/dV7vQA57JLUhpmKyAwUQA693T0+dPbJ1Y6l59RaH\\\/QTLn2juZagXXaGpON6xYIM\\\/ktX7ccc8EQtLm8oZXNSE5JsohRzKlGHgjJkUSMSiwMSe3swq4nj7KUslEoDjkd9ozO3Zde7A2qV7zWt3adAmFSUlVWLFCqUzXtmhd777Rq3mfujnz3Rb\\\/esvXOEdOzo\\\/erd\\\/\\\/kp05x45IKUSAI1K3rwwFjobDUNkrjVZ5WOFnxRGnK16LhN6YcOlkYzrzZkytzX2ztPthmJCh4Db2JgZsrWjDIK8W2qnXKa8VA5HNUZsXACXpdFVa1p53o6p0\\\/+nX546d7J56cb5f\\\/p7g+WIWDIxFBuElBIRjvt1bsoaqTwEAWDaASdIoLgpGg\\\/lXThV8kcxuuvYZx997OvnXv\\\/+5Ys\\\/uSj7TT0MhIy0fm7m7aN7rwAJx6tXTv3qM1OPHG0vrL31P3+7M3+HVBQLH2YTgpTIm4wBWBguLoniZhzyshqSc4esfk1XpK6biUId0u4fG4e9OdPNtCFYxvyMGFWbNQ85ww855FZqM9MP\\\/8PPTT00tzW\\\/\\\/ur\\\/+KetG92kgaA4bYCxluJiJpJtev1PX3Mblad+9bHNTrC+Hsrzl6QvqKJo1xSFfeozsWLFoIiUJLdCYZdNQFa6Yzh3qLKBKimqTLCJac8BNZCPXGxuWhT303aGZ8UAVZCSsVZ0bmIYfwKR34GZs08+xb5PAxPlI8sknwcgqtM7xh87JcYbk2eOVvfvh1slRwtmp6YLogj7LtKdSngXsKHexr2xHBfzKO2EFwuQQOwD41bIr9PE9NRXPvrErz556eU7b3\\\/\\\/gsrIEAnjiVLWjEpsLxQp5Y3UqvunSYjGron67p0GXsCM2CkFyRS3zOxrFEFlujhp1mo2Ejhjo4jUp8T+KUyIX2YiaoLmUmF8pUcjGdEpXZ1ggByXXL+xZ2rs+CwJUds9UdsxapwpacNDxU4YKiIZcNgLms3X\\\/\\\/i1pasrj\\\/0XH66HbZIhDUJyx2lxHQePUG2EhZvo\\\/CoSDkjZAH429lJ6sexdxOb3bdp7ZkRlJ\\\/NGjZ1p7m43HKMSAdvth+Sm63OhE5\\\/7nxhS58Vng0XmZrKcGLaPyznSMTMr1VtcW37hne7qVrcb4IEH6cBJTO6h6ihrOWptipJ6A5h7q+jGS8WXI1vqo\\\/gD08QT1uBEpA6YPvl12jXbePqRT3z51Or5hde+82bU3qKgRzKI+3isEo10zUtisIKSUBE4Gqw3V9++0dpodTba3eWtGNKq+xhKWRx562IysRknU4S3ShNLJPBXGNCqxLs9045h2wMI+aKCs0WbXRNWirPhpN3XAwQ5HtyKGG90u\\\/1Os7d5ZaF9b504YpU4gnC6A1UG+OaQo353ZePlf\\\/ditd175GvnKhNjxIJCJuXz0pKYnSInkVyByzK6H4w4vYOcNVpiV4KMn2ki47UzkNGJYSMdJUMUn2D2b\\\/Ib0LZ9pG2WH7Z96yUCaJT3JyzJA5HhJADKSwrkuUkWQyONR\\\/rSqDDcWr\\\/yr\\\/7A3znlnzjsHt5X3TM9aG7Q3hO0sUzddR60EAacRolk4GUk6kUdZ9wPQ1uYEGaTjsSWLOYiuOT45Dew94B3\\\/ORTZ2Yrzd73\\\/+OrvdVVGnS0zAnSEzfLEg1WkGKSUdTvX\\\/vd781\\\/9\\\/mdnzg79tDe7vydxKOCldErT4HUhn53to2U6S5odca5AFrgeIJoofitQ6ggyG7Gw\\\/gXY4tVW+pPZwksXLiV8WN7d3\\\/y+MV\\\/++POnZVgozNYXdcsWE7FqjMZ6OwqkQo56LbmF1\\\/8N89+8b\\\/\\\/Un\\\/l4Tf+\\\/LxUTCOjtHqdUcWuab4zH1MiODJOXrbWH2y\\\/QzaXLGeQYDDnuuicFdG2n12KkhoumrYNhfA+q46HfpPzVH3SBh15EUIMM7KwFjcXAgyXokH1GcMSKkTUk8213vytrZ+9tvnSe07YHz9xiLY2OYrIrWHHfuw6iOoIHJ91Kzz2iNU8MUCIAiej1L6qmK+CrRlhloVyMhJkx+dKA6dPY3L3Q4cmjj+854f\\\/7uXmvSUKOhT2kWWhqbl7wioghlZnUFJbKUatrd783fbr7+46NUOObn0pUpHK4GyANUjl3FkWMalsVpbPezijXIAsChJMOzbr4AUVzbb1b3E+dU9Z7qk\\\/qXBrlWO\\\/9OTy31xeeulS6\\\/r8YPkeBU2SgS4mmVN6FMPS5tKInwGH7dUrd179P15\\\/+Iunjz51CEGPpneQYF5rEjyx\\\/zC5DXLrMZYV4Bi0ZBCsmAs323DmKWjZIsUdkulvGPerOOvbcAntCMXhMbYfHg4PfSVPDsOEXH84N68FBbtPirLuzLboMhigSaTYm9j4uh8rDkWhWrzT9fyGzzsePLTx1gUZBNRvEZiqdYzuJNej9QUOeqBcOWQPUHh4+IdZqaYrE0azQZAQsTeL49PoBI4d483gwCH3Q58+8eJ3Ly2\\\/f4+DXgyetuo3A9duQM\\\/ATCqiiMlRWtrcFRyXjopZzyrSRa7\\\/CGRqqMmih+b5sCHXa\\\/A8s8lFrJJGBqs1BglwRi1PJpdCQ0xSB09KrIigIZpR9nKJ+FIc3UAAXKEEmjeXORpQjCmPjPQ16e8TQKnJkUhkmiUp4rB\\\/+W\\\/e23lwx6N\\\/5+zipdvN9XUe2UntJi+3ad805vby4mp8UzhzOMy6vuaozTiz2CSqszWlMJWyQGbKYoW+wmwQ9lwhpw2Tw4cNCXtZ7C0d3yNT1WUiZDUh212DIh7u\\\/8+ozAb\\\/NjO+DiEDivoUdvjuTa6I9uL64Pb8wQcO1BpVcjxm4k5LbSxQe41qI2LnHkzPoT5GRlSMie0kLB6qGVpgTxORquLF+isMsKYmQYOzK7Rnrzh6lBY7dXI\\\/\\\/POP37i0\\\/Pb331bdFqIASqVOriTAQmjjrgRfr2B2TVLIIoHhKlaxnLMOKHq3C4eFyEw\\\/tbRZbACabEVTM1tow1CRiqCRcOOrQQJCK8roeB4\\\/BvprTYVkirnI8UdIOsDCSTF0uiTkWJtcsL4s+oV0rc4cRpLDLskBcYRkjIG4g8lxHhg7C\\\/hwPOh5qe4aEZMMZKf1+h+\\\/vLHafuzXPuy6Po3PklslErywhqlxzO5IWgOIEba5pVaGJkMW91I2YDGYcS44IcsR7qsMw1TCXLfj59BmZbETA0u7LNnq7v1JtNtF3lJ0WIqWhn2uKFJEHJFIfCqX7mJqd\\\/vmbSfozh07uHj9Vns9iBP5bjt+vcYY6qOojXAYIuhyv5vkwpzTJLdBqhRDNK3ujqGDoP0AhSCvgn37aWRc3dpwvfqH\\\/84Tbr32wp+9HLWbGSyGANerTO0YO7yne2+lc+MORz0tv2L4mybGznEkcZggmVlFcWPDqTiuS14VostwSDDFqJokPxGCnFiUQTguHIcgyHFI+UREcDLqZbaNyXEc39M6yA6xm5hewPA\\\/1C40sfFoDHTW2EuROrQJIUCuT45HMiATWa77VUCkFMtAgwpYCLAgVpZGZsJI9sYnxk8ekEHUvHhD9frx\\\/ENFHPW6K+tvfuu1T\\\/43nzn2yVOXLvVUc4M6axQE6uZd58GDamONmyFBMNLZKVtjNR4CZiQeXpOkLq4F+Jn17HbyaNEJuaQ0w7AysCxL3H66SOzS0L4rhlCEkbeNycndY4gPXYLYgEqcylcWqVrjkerm8mYwUPuO7V++rjaXVtm8au1NxeuAoPo4du5F0OdBj\\\/ttCgcEZc4pkQPK2dLgnCWBAlryWbhUb4hDB3kg1fVFVEb2PDR37OkjP\\\/69NzfvrWnyC7RQpxDOaGP\\\/3\\\/tb40f2Dda3rv7LP+jdvJ4wXVK5wMRqN9bPdZkoYCLHZccht8p+TRI4nYKwy4rMFQTtkOH55FXh6i984ohApLwEoENMqZ+2S15F+L5X8cjzKfKTcydZCsIjv6YcQcQkBLl+fsQNxPJHlYbnOcKrkFchjijm6af7yiPHIyHgeuRWkhmsYC3eoUDat0Vn146z61OP7\\\/vaJyIpb\\\/z2t9dfeJNJo88VlOLIWb5w5\\\/JfvvvE1x9c\\\/Oc\\\/Wx8dp+46AdQN1cXbzpH98mKPuxFlWJgMHlwCIGMuupxYiH6+7+ChlGNORcguhvDu7tOV4fs+CkzsAjn0P+ebH3niHpfoapcQKPJ+vUaebRBMVxbo4HFqNrubGzfPNw+ePug4WFtYSc3xErAxUbcpuy24HlUa2H2U2uvUb3PQg4ziNgnn619kXZksBsbpnHDFxAQdPapur3CnD69W2zH2xDc\\\/fOHVO++\\\/eo3DXqyPFg+vyWnUnJmdSggaH\\\/N27ujfuqVpbwSHiQHBjqPDHQmXHAeOR46QEPBrJCrwKuR6gWSA4Prxr2aoxSR9Fw78OjwvAKTnwa8KUux6iIGaMp5aCwE47DjwqvAryhHwfKHqMfhGTwuIyK2iNsJCMEsSgF8lruZF3V2fKg1URpQQ7Aj4FYBZhmkBysIj14dXFcKBX0N1jMIBOwMOA6DPMiQkCmW64HSEf2C3ch3HE9U906QkkfbTBQmH5EAF3cs\\\/vnTsQwfOfOnYz\\\/5IhusLMXFpq6s4EPv3yms3SSvdCzZQ69b0rIBhifkdZOpuF2l2bIUEbL+PkG31JJaW7PkyLhMKgtzI47ntb7jM21EOSwaAQB6kZvZjMIyDiHzcZEUK1O3Swl2qN2hrLVDOjfMX9x0\\\/MDoxdvvydRUxS8vSDaQ4lAgH3N0kv0Z+DY1x+FW1uUyDDilJRRG3NCFJzEC17YHYN4vGmLx0naVAdcRpjJ\\\/58hP1Rv2tv3o+6jZJFz8qpNhZGuHq5soPXxz\\\/8NlwoPp9JrdGHFFcLeoBoye8Sn3nxO6HDk0dnKrUfXe8Wj24a\\\/bvf1K4PoRwJmojh3ZO\\\/PrHhZLCkD1I0WcE0l55LDB1bHpk19j03lFSETSQRim2ZfDhuHAcsWPE3TU+\\\/Rsfd5RKpoCafSjI8ZTr1k9Mz37jSer2nbhToruZiuKk1RF+1a1XJ\\\/eO1796Nmr1nJgVqZRiJibhwPXgueNHpqd\\\/4an+Vq+10lp4+\\\/bW\\\/KrstSnsIhpoAiQDEB4cp7PRqq2t80Zr87WLDILUPWE9h4847HVXV5\\\/9tz\\\/95P\\\/wpT2PydtLa7x8g6M+Kak2OqLmeyePhBfeN7oSyrLj5AJ1zYRspacuWy1fNtThLWB0uWdbYWjHBSQ3bxM4iwSo+7RVgfFHh+SjKPB9MATCQjZrKWf2klPvNDGlgrV00r7jtHgbMiKG6zt7Du8Tnnvn4vthECRqvJzAQbIWS4zxq42QXyO3SqSo2+Rex+TzJT1RxELrwoXricMHSDlyfpGIyKujNj730LGv\\\/7ef\\\/+v\\\/+PbFZ99RvU09moeK4tFzTCx03PFRVWnQiZPq+m26fY3UgCHg+qjUJvfteuiLD+8\\\/d0D4Xnd5M2h1JDMT0v0WMQkBj5UgcpJLo0i7N1uSrEwcMTkg17L3iAs1lSDPHQEQDRSHiiogJ3kVxawHOkSImEPFDpEv4hfVDXpllEtCAEQREzE70FbxMGSY4AAKkExgcqr+yO6JWsW7e3np7e+dv\\\/vm+6rXibWrSYjqqPf4Y6LfkteuqEEomz3WfPZUVk\\\/XpW7NqY8\\\/\\\/M2P7P3YyR\\\/\\\/9hutF39GQZuiHnGfiGuHZwHRu3JD\\\/y4SjTmOTRdzPVLOUjdmKxHNmfBxzkCCyd68yFGVTDuX8r3E+U4pU+Elts2Ek0e6QwBiKMNGD9uBKB+JlIOmmTIDXiZSFAW0eJNGJnltiYAwUPNXbswdmdt\\\/8sidqzeD3oApNsGGKR6eKsX02tTrkC4aGxOiMcFhyEGP+u1MQwVg4UA4YmTM2b9HbvXU4iIJh1yPXN9v1B7\\\/4pn1e63rr99QYT8WiUn16oliPkRIYdAnZxODAT34AAcdWlogxxWV6sGnTj31d5+SzO\\\/+4J1br99o3luTg15m3M3K6OaxRnamkKREXAAMYdxOZYz7YKpTGdKMiaI9GziKrKeKWLqXjckhZ2eZVRlm7HkRt9S0K7CeNBjjE+F6jZ3j+x6aO\\\/HJU5\\\/+rz\\\/7xnemL3339bDVoqhPfk08+kTUbMo3XiPZpdjUjBM8DTEAxSBiGaigf+kv3z3x9NHDnz7x9qXLvNon5ZAUxHJwa6l+4oC\\\/Zya4u8CkkaWcUSMy9gQSBgWXM2zK1mrq6MdD3OFzCg3bFXbW0C5nQz+kGYN8wqqvu1vgrdPwgSCXwTh5yPsiWw8GJpA9llNJjjfqtWliN9XGqLdFUiqF+Ss3p\\\/bN7Tt17N6VG91OhxQRSQsDy6mhdtIr6ra414YQVG3Q5B70tPSgJBbkuBDkjtTcffuDO4tqc4sgSGjKfOPQ2cOHHtrzR\\\/\\\/sp\\\/3mFmmOkkpVg5WVVKiIVMSrS\\\/RWiIdOMZFo9w8+dezpv\\\/\\\/xxXfnX\\\/y9n7aXNyjqUxTGbCCydCViCThmynFzTH5qvpYYcieRy5fMA1GUD5c5RwiigkhRnDhwhhPIuLMa4K6E2+w131tYvPqziw997fGHvv6YqPjvfOtnHLnOuTNqbVW++zbJHskQZuVhOJfEev4y6q633\\\/ve5eO\\\/9NjNR45v\\\/WiFVEAsSCklVffa3dEzR6nTCTa2WHDc99FJAOdNYGDqk+QmCkMbMwYdpeA6eL9isXSXlAKwi48vjiYBYrdkEjIcIl7QlbEFUVHAiad6LigI4VMCgyDFBFq5TTv3Ub8Z+ycDq\\\/N31KB77Nyp99+52m01WSHj76UAKSsUK2KwVNTZpM4WvApVx7DrIAWhOzbiH52l9fXuuxfUIEqoVQ559crY5JOfO3P3wuri1SWOBiQjZGTWzBwmC0aKiRVtrPJ7F8WnPrZjq\\\/nYNx+\\\/8cq1l\\\/+3HwadDoV9wyRYkelWyznDIIMwV2x5FwW+SlS42LrZ8cOFHQGM7IrLUG+U58Wy7pzF38jGPOnYg4VLwhsE\\\/Tf\\\/4KeDXnj6G08113p3+2509aq6eFF\\\/\\\/NgTIpHtSZs8iXEikYo47F\\\/6wXtHPnLwzOdP\\\/ezld1VzQCrQoA4eBN13r049dGTl\\\/FXZbmbJZSaflQquGBTa1Ewcdl2HIb1\\\/tpufQEk\\\/n4tLnAsoMs4zLVLzRKsVwsNmng6qe1METVZx5VHStp4vFdqnKDywBHiNEkXFjF6lqN5AbZR77UQiFv1e0G11D5w+Ouj0giBkw1KPrUzJLoJTDJdi7g\\\/I8ca++rH6F5\\\/c+quX1dYWyZAgyPXJr6My+tDHHjz30ePf\\\/d03t5bXYslQFWbaJAbcHuagVwhi4Qb0iX\\\/4SdHu\\\/vRff6+\\\/uU6DDkVdkgPE2l4BVAQZQUVaApRUhNgGI\\\/2OFtKNSAuQxa4P6dcRYkeNxDkj+T6pCPFPZfzN2H5DUvywECqEtnNKvkMyIk7eg1ZA0t9REZRE\\\/KNQW9ak+kjxqyRi+IlWkmTG+u2NHbOTZ77yyPVn3++\\\/d4nCDmScAoASEXRKCsJMZVjjLiAjKRSd+8Kp29c2u\\\/dWwRGUBClSUoZh0O40jh+INppKqsKBATJZ6Om6hK0ljWJ6x0VVM1jPDhrK2QFgLHgUhvIZExL2vjDgP2XoAAe1PakZRNHOs6xVQ9upbJRsQgNumvj6mkCyjBc96GJkknrdeLwLIlAwCDpbzQPHD8lI9XuDmGln7ODcjTFGdg45rvD8yvG56O5K59n3opUmuS7qI6iNElzyRypjOz75Kx+av77x5t9cVmEPUZApZ2dIfBNnkaRnwiOvsnN28qNfevD1P3n97jvXKOhQ2IldDdKdE0s7y9T2EIkNhmUtFPu0Jew+ZRr3yuSPJq3LTFJRW52xbfRLklThm5bpTap9pK25k2cmw+Qo82NIuVoqJSuw4dmkyAl79MgnTtx97cbG\\\/CprVU82GCGpsRZShFDiqwMi4uZKZ8+pPdMHd9585TYnSQSzJJaqH7ig+sE9\\\/ZWtNPPUKH59+CZE\\\/HQBIS9QVOY4lCfI87AE8L7E2mFoHsunExlsNffQ1Ac3M4QpsV0qAFKR31ug4dw\\\/FLxckqI4RfhabrJJSjG5l9obQEYxD8OotdHac2S\\\/63mddp9gyK+kJkQGBwqOC8eFcN1atXHiULi6Mbg+r7baFPQp7FPYh1\\\/B5JzYfeDBTzxw5NG5v\\\/mTd5pLa4nOPJuwcNOZIsF8CopHZ\\\/7eh\\\/bPPbjv5T94qbe+RkE7sX9Joke2plNBndSSiWGrD6Wqm+A8mwkG6892vCl9TCrLm1B7iEmpmAZVZFGZbyMhJWVu2+k7JzLcEZODSYAcb9Dn3Ud2jcxO3njlGkcxpBsZ\\\/iBxehMuhEeOC+GSk8DTIKRyyfHOfvbEjdfu9TZb8T6MT2GEnQGNNKozO8K1LWQwxEwaL41NuSW1vag7CqVdWfFoaMsPA67kyLbFncN5JZyCuD1zYghjegzCxj3nQhxyeCBknhQ0xJXe+k6qqoQ8KxdERGGfag0o5igw5HdIKtpab+0+OFep19qbbc7ugZP0IVJUpAfhkuN5UztrRw\\\/2b98LlpZjERdWJAQ5HokK3LHq2M5nfvWROzfabz97nQddkhGIoIGXEECGn06eWQAOOY62+BHVxqFPPNDvBZe+\\\/wb3WxR2DXutJNTkjV9SOV0Fy4uCDZw6JT7BafDJEXCLfyzIb8JyJKstREl\\\/P7OjsTekSW5MZFHjP2YbKclQ4qmPcBU7tanxg08cvvzsxWgQIZXSjYG+DqA3nk9OhdwqHB+OD8dLGWS9Dh16dN\\\/IRP32+TvZJkwEwqOtTn1qwhmphZvt2BiDYELe7cLGWEtly5iJSrVPsM0gISdUZB3Qw0JiCXFiG4SprgkLpwfsrDoX+HNxbxjzPV9AosR60OQ66MsqQ5rZR63NWDY32eySaWt1Y3J2ujY20m12OM1tROrO45Lw4Hjk+pXdu\\\/xjB7sXbkVb7dS2EsLR2k3U2OFM7zr41PGzT+55\\\/gd3NxY2qd8B0+jhmUOffXRkz1R3taOkHg9q2TWX9PnteuT45NepOupUG0efPtFZ2rzzykWKuqlYve12GHeSoP2hzJ0WyzQVKfM5m8TcAwobJuatkv3ItDhhFJ8ha\\\/kW2fqWqzPSHoMhTZfccUEx2dcf271j\\\/6OHrzz3fjhg42GChJfguSvkVMitw6uPnjq093NP1aZ3dJeaLBUBMiJB4pHPHb\\\/80o1Bu00qivPheF2JwWbb2buLCdwfpCb1mZOUOY6HHXhQQiQqLFQwCLYBZ0Gjdlu8dq7xgQIZKmfHhDw93c3vfVhvOEPLlaIMLC3+7Qj\\\/vL0YgKlFGPS4uUaNSbRWDRsORSqKQp6\\\/dG32yP49p44sXLsnJVuyGnq1OE5t\\\/253Zrr12hUVBiQc3c8EMRyXJicxOSUO7Pendz\\\/68QP33l+\\\/d\\\/EO9zukpNuoHP6VT0wd3eMo6U2Nrbz5vpAhsgxQ51ia\\\/lUhvyY8tzpe6y2uxua7abGUma6otBlouZGZHG3eFuq7LdfUbk3zNlNhpE6TzDZ0OfFRM5immXyeHhhyUidocwA96mfoGTqriJUiqXzfHT2yFzsmadBD0KcoZBmBde8sTkfZ8VGrzv7Cx8YP78Zg0FnY3Lp0g1Sowt6t87ef\\\/MZDex+cay4sslOJndVi72TJkepfvV1\\\/9GQvjORmM+bva40s\\\/QCliBRDUCzbNwwEA0MbOvH\\\/iW1xjDEDm5YNKWyebShYTmWKh80TbAZTCQjWEHrK39n0P8NJtPnthbLKMF8Up0YQnLJJYOfWIGqtY99J6rc4HKQ\\\/0mMiFWHhxsLkoQPT586sXrkdKZccjwBSkqIAiOon9qlu2Hz9KksVFwuuh6qHHRM4cUKudrnL8srCSJf3TZ78q7+6GiwvUL8DGRG57LoBEYQYO3uwsndMgAVIxN558dAeCQ3KccTYwanNK\\\/Oc6SnFwpscb0ht9EOJLGKiPMHp7cgGTEiP5Lw6xdCqw0KOoIzrY65B2HNC05g0gedm3X4F08qeGbGPDnRNKBIlKF33RlDKaVR2ffxkrRNQpCCVUIoj1mWjiGt4Qa5Qru\\\/uHI8UK4AcSTIgYhJoLW299f1LZ77w4I2XL\\\/fWB6Qi1uZQMd+fuD\\\/ovnXVOXdavXOVO70kw9fycBFRRIqIFSAy2T5rwbExxIfJUYTFUUXWLxk2RTQuOVuTi8R\\\/ASkqICENZzEGXACja\\\/XVYVpmXBAs4+2SZ3ABZ1PWSuKcA0Vh6siKpKTVuzQySRtLhv5KnEQpprV7G6OiMf3IQ2sL3YjqEI4KAxE2Kwd2BKtbwd01ggOhMLNT7Jggx+HegJst9foljlzyR0StfuSxvb1A3bm4yL02hX1WMtqK7vzpc4MPnx5stBd\\\/8GawvpVVd6Sycw+CXZfcqtuo7\\\/oHn3LZ7CIm+Sel7G1O3nKscm9OpzLxBUO2ly1QVSkWEShOrDgvyJc3V06XQ5FqkKOnGq4JehYbw5djbXZO8adZs5RYbfVu\\\/t7zrcUlHvTTKUWS9yZcYccj15947Njkkyf7t5c6798iGRCIIqhI3nxz8dzPnZn58Nlb529we4NW7lAQpRNLAKof4v3bIw8e716al\\\/2QQKRCjgYUDUgO4oNYSdL7MH8hgFxAydmCI\\\/YRKui0oECsjb9pm0SkWvf57WnEpCKQIN7yLlvcJNhCpR9A89uwSfuADA8uNQZNJUT0Te+0aNck1cao3yJSzEwwmeZorXXlBO360LlDcxMzUyOX3t+4s9HuXLvLbuQePUSqDxUqYt7qqaUNlhFIsXDIqxNU7cDMA585\\\/taz11pL6xT2KRqQjDiktVcubI35FWAAAIAASURBVLx+hWXEMsyGB0omNzWl2LrkRwCUjBLDA1NzzYh+BjiJTZoXWyctLNOVtJ9dYskT36xSzhrD8N+4rx6zAQmwax82ypCUKJbu1RQrFiMHExFxh1mFgRx0qd\\\/R2jNkChbHMwmHhLP+7MbG829yFLLU4uuCAJaDzYWt1t2tYydnbr9+j+o7eIaov8lBn\\\/ptdLtEIOHKZhCs97yTJ9StdYJDQY97TQpaFIKigCggIlIqHjunegdxwg0LugCwJVmEvOi1tbRtlj1Zm5OphCNRSFzTM898aPy1O6Qm4aImVHlrx5CUH7r9Snn5oKERWMeQ9UWanKGgmzgQiFhFX\\\/NQ\\\/Wo3qDx6cOYLXz\\\/kOTj05Ozv\\\/cWtQc0Xg97g2m159Q631qEUwdNgzeTJJUarU0dmRqGuvXRNDXokA0QDVpG+AyoSsRKHsidsCQ+D2CGiZJ6uhElKyUSyC2MAW08hy4IoXekwbz+Mg4lzuAc2NrUx+EFOR2iIADLDhE9R5lVij65yMh6xjkbaJ0gEk1O9QKGlPeIrloIQVKKPLWKrRggWHkmHNDmLEjp01A9bW2\\\/\\\/+Orjf+fc2GRt69YqdTY47NBonfbso26HggjsMruD1YE3VZn40LmdirvrraXr87LlEkDUoYg0hZOYQUk8zLwobYEJNuTmze0Du9KGYY\\\/CdtZmYm5pWAwkMnN92KVp8rtukYBEnPMIzcVhq+rjQtGXIQbi+wQLWYVtB6MpTJcURSE5jB27eWuDVESgxM7SI9clr4Lq2MzMqCcQEchzaLweXF7le+u01YUgch2SREwQLkvJxBAuV6pidu+hqZFos7+xsMEypChgGZDehOmSjnV4Zap1H3crBWcfX7iO4wig4LaTqoNo1Fsa2DLdL1s61MgADdwfJ4JwJdAJzhaLqUdtI3SR7sscxjn1c4BNnzNSU87MwThmpiPu5BbUO9N2lAyR6eFl5D5iYq0mGlcVUXzkJbw6EBH6FPp33rr10V955PATB968c49cQZFAs0VbTXJAfpVmZsTsLG90qaU+9fOHn356v2r2f+dfvHD5VU514jiKPzEnZsOcWSwiM9QxE\\\/5UYzdnKlTkOSFXxVuGcSjqElpIVFMkJ231xN48pfIWQ4Mg833Kw9wUI+ciivwIsTSV5bTwoLVl3nec2n0iLSbvxPJBUQgeTBysrrFqNiPp0qXF3sZbt9X8EqIAxBgbpZ3j8CvKr6m1dWy1SBL5NXH6uDuoHD0+9c5rdwbtHskwAbgoxMIwqSO8k9EYk\\\/KA47FhIvTiOpo0xDB4nLHrUaY5n9qOIJdIgoy2JPLTpizxQb6YQNbEtAARzCUVvBEZjfWCHNIYRqLEWW\\\/U9CRILNHShWwsBqWL9nSmykLrrhEL4\\\/05EC5z2vZU2oWONcCIZacZbt7Y2PfI\\\/vPPXpGDLfJCjoiUJCaEkhaWeaNN9ZHRIwdPnpoebXhOwz388IGrlxZU0CEZMEkiRZJISQKgPVszw5x4XVkRzuyEFtO5NOYYXTRs16rOUSjMgpLNjlAqIKJvrjucGbgtPK0ox1Z4Bsb9jXwNrr4JaGNiRYJIStpap6lZWpknInJcEhUSrrtrhze3a9Dvvnl+5d1XNyoj0erFG\\\/zwQWclku\\\/Nq34PYYfUQPfmyBFUGaFqjfbN0c7pPWOV6tz4hX\\\/3kooGpGOgUokDkW6oOsKtOdVaFPRjCEgqQ6HFkdwKaXY8SJkDEmQpQFpdGEo4uS4mDFIRCnQxUy4VJW3R7EUK9Gqj8cX2qJZze8+oSzij+SczYgO1ZJDy0sYMGx25ZBMLAcdnxUSChDIiEAAB13MqjWgQctAm7hsqw4pIQUUqDBffW37g81OVPXu6zbU42EcBEZNTget447XKwV1wsHht\\\/ejekU6oljvByMnZEXens7W2fuFKsLYcDkCIiIWeWMRnov4Cqe9AFgwL1oGwDD1jl\\\/AMqGAZghRSoOL30yqAeWhYc4eOHcuB9pRVMUOn80R5m8GihVVSByNn7EUJYN+J3+LWOlXHqDJCTOS4zuS4c3iOt1rRyoZw6735C+w0KrtrgwvzbtAVDUEHxuTNAYcACY3GZlboDzA6SbUKv\\\/7uzM+dW19vN5c3MsC04R1JEKJSn\\\/3yJyfOHlt98Z3W9dsgmeQGDG0v5VXIq7q1qluvstn31tNEo2BnonIZolJrVBQvZqIWZ1EpkaVPtmCD5RbHqXCodVstuhyz5Vucfcn2gkhDIxeFIvSjhOc29u\\\/msQbJEFFEUqZnvRZTh3AnP3a2cWRu5cdvrvzwBY2DSncCWOlW57X3Fk5\\\/4cT49Ei\\\/c0TduU28Qip0J0bcfXuEUtRs9y5fawXet0PM3+vV9k68\\\/+p88\\\/a9geiNVMP6eH1y5yEICKjW+mbr7lI0GLC2x0qwfpw10ijOtiwpXsudjlIJiNRaNJtiDKHWF3ofzNvIIsbfdu2xiXmjSueHGAI7yC+jvE1ZGRDO1iiMcYAxZUbr\\\/+nI0+1g9gj6XX+qoUbrcn6F+j3vxP7Be1fZqZPboPE9rGR4YV6cnqV2lw7P0GqN7y1T6h+oIt63jzcCV4mZo7vuXd0IOgOKNKsgA7iAiCHcicmJD5\\\/19+ycGmvUb+\\\/TumQJvYQJpISjHCE8tzI7pt6zvGxhyZIzyvNykyJTkiAkFm9gG6JlNMATOmXWH0EGFAZZwStBluQ5p4l0CkzJvPzcn7P8hLmM9Bif8mKkOv2psyOdAVSshKBb2qnKq\\\/ArlQcOo+I3NjurP3qBSRIZets6z1dy6fbG6mL70FN7Bqd3d3\\\/syK0GqEUso6VVeXeRlSTXo8rY+mb\\\/+R9eGTs+SV5E3Wa\\\/t9Hvb1HQETJwK9XxuV31yfHR8RFIKYMw6A9aa1u9ZovhGOB4zsykTCFgWLhoHUVZxSbkaW0Pu6KwD10LKV4y4zANoQBicm16KPIM0eFUQ06TnWIIzdv0FptKKIfQQKvHOlpXgR0PboVcv\\\/H1D3tVv\\\/dnz4XnrzoNv3Jif+\\\/tKxwyuX1Cm7sjFHWo3+ULHefIHnX9Fp0+wdUK3bwBCZKSJid4Zi9dW6\\\/PzO7ZM3Hj+ZvMMhVTS31dGACrcHNr7bnXRh84uPSXL3au3tKWOVnHRQhyPHZ9t944+V99VljOgUw5xR62c27KMyC5kIcm\\\/4cBbY+PAaOVw0mCo4zUN39UcgI85xiya+E1mJTZGkoJClmAZTObsfJmRqFVsdW9\\\/R9\\\/0lpepbBPkYyh8OmyhiPqjdm\\\/86nqwd3Nl85z2COODL0vQUykQlZhFKqFa5uH\\\/u6Dm4QNGax9Zy18\\\/x73mnHLmhRFRBXmTmewtbo1rbyKcioy2upQ1CcVKaJg0F+9dksfLp4nRnZM7Do8t\\\/vQXGdto9vpdjrdQbMTdPtKSjJ8Q7MbpzmZECxEbDIpI0JEiqAoqWMp3xfNjexzFP9c8MznmOwWJAwN+8ztZBnzlUapVKIZXtnqzZTZvBAS6XtOofei3nAPzlV2NsJbK\\\/35BbcqGsf3tS\\\/Nq15AYJIhKeLOJIIWB10OpLPAtRNz7dcu4tRxsX+\\\/ujnPbg1HTgv4ND6668RuFli4vsxyQBym5puZm58KaNBc\\\/t6zKz9wVBBwRiBIVCQcl1yfFCvHkWHIylRjya4LjElvUU7Psr\\\/MKzbCorRDMCXcn6T7klqvgMCs4kmzOXA3bINji08IMsCWnEHM9eJCvLwK3jqxfgFQIAckhSExQA6RiiIZBDEn0+JGgyBU2L37e38BB7LTJtk3bSeQygXJgIPWytU7p+XpflNt3GkGCyukmIBYbE6\\\/ifoobS2rsB25U+rt92vTI+17fVaSSCVgOo0ipIESweLKxtIqgSrVaqXqjeye2v\\\/IA73V9dbiSr8fDTqdQbefxDbEHXBth+r4BIdkSGEPUY8i41ayyrddSun2Vj5hyoEjc3nP0lGr1QWUUXmpDHqdpf1l9g92aGfkPaKQR4FDgIR2qYJw4Lj1udnK3Fzz2t31\\\/+XPsGtW7Jn15ya6V+Zlp5MRXpRSgxbJHkU9IhosrKpG3dk3Hb13hUbrzo6dMgwpgnrnfWdmx5Gzkxv31jfuLVFkjCUInHALWDJYKRkinoyZEz\\\/NNJcEItfXe1NJlXThOJNBKQrBMg\\\/XT2AjK4u9HxImhxbedgFHZwcxu1yHBY2ZhKTU5zgbtSOW99XD1VjeW3AsIcOap8vQbclYM8JotRk25ZwK41BmqyKYyeR8pYwqSSpkFZDWyFIqtTwhFaion3CjYpFyPb5n4RAAZlYhhf31O2veamfwn+4Ga33s2sHtdRJuqhZFJEhGtLHAUPARdpW\\\/p+qMjURr\\\/cRwQYIlaztepRgxtb\\\/XDnttsbW2uXDxul+r1EZqtVpt5vjB2mh96eqt1lorCGQ06DNceDWujovaqF9vRJ1W1Frj3lYM8JMMM28vlZNJnAhgnbYGRgOMrBhnTkYU5tCYyqaERTxrJi9gmxUXqVxF5TXYHVSdL8UEM83ZcxuN0QdOBFv9zbcvy0BRjWlrzfulz\\\/Wfe09t9SgMYikEImKSSjJnmi7RrXv+yYM8WZWrG6h4ePws396gIESzMzNZmb+ypMJIa10mTUoLhslKZXYOWZJp4Jx1riU8x\\\/Xy4EK2sQ5W\\\/VAED9mMm0SBDvHmcdnRvJAKCZ+Em+i\\\/aFePgGQAhKwAipgUsQDYwufHIldOKk1Pwo23jgxJ9iEDJj3LlMh8i4xGWV662mbzpPFQa8apUJuBx\\\/4cyqi0STHr90kWkA1ucsSI5HyRW6vt1dube0echa0+ju6j5jovajSMICJU69zfoqhLjksra1Rx+zfv+ft3ybWVrIHL2XwIbEEbmIQUot+K+q02EVZuL1VGR9ya35io7xwZHZ2eavbl2lI3oJHHnjnzyCdOLN1e+8+\\\/8zfte9dJBrH0m1QZPd2c6VuDwUSp1AaYArnUPh74usURnhXNuLTjatR2GDbPMLug6fFMhjZ2ug9FTErSfg2eO374gKrV2tfvhFtddnxyKqDQ3TcpX7uq7q3SoB2LuLAiARIeMYMkqwggVor7HFy6jgdP0ECy71GgaGSUon697k\\\/tbJy\\\/vRUbelLioKKPZK0PTzkVpvQyxDEqU9rWqGTH0THTdA40knfAloVFqjubO7CQ6LjEr+KS48Gtktcgv8FOhVyfhQsQRSGFfQp6FHYp6oI4MbhXVp85CYPk+uTW4TfYq5FbgXCIiYMuBk0OOvEvKua4\\\/86WLxeX5NRGdZFwf7XRsoridC5Wr+Bc5cTWQeNoxwto\\\/FOCyyFC2FcL1zf2Htr52nfX2R2hU4dpbYuiHsEhkjwySmt3iCNih1bWhCvDe8ve5Jw7PhJutZCKxWhx9hRynG0IRUokwGmKIhVthrTJTTDBaUztrswerB46tPf4oU\\\/\\\/0tmxnbXdJ3e9+\\\/Kty6vLPNhC5BA5ujQE2xOALFMoNkY48zjN5ZVJ7uJSHiRnyBrnFgqXSQbYdR3nJDh0MiBsghdMcke8ppkAIWqTY6N7dstIbr53VSmlN5jjUOPAVO\\\/addkMaGon9TeJQI4Tv6gQUMyQ8TEMYiW5p\\\/jqLRw\\\/SV6d7zZJCszM1icdVJzNhY2YKZOi3YUnKlUlFctUx0EmbUND4kJ3ax2P3CoJTwshkjAMjOzWIUqQhGzYitsupYl2TeLH4pDjk1dHY6oyMeM1GlypkFch4SDoc2ezv74Rtdeox4YpJ2eDo9RZSbjkVFCbqO2YcUbHuVKHVyEmbq71l+5FmjqsJEEBzCyLIEMgjzjO+b\\\/qj+5oU3vhwvGZNdswsxxNCd0xAljoHeg5lerIoT3kuK2bS2rQiw9oxavz6\\\/sfnvVlr39P4ZE5OnWE3u6RknAc1geQDuerm6hViCRvtCfPnFh54TzLMCU0QqUsahMSrchALhEUBJHw9InQ6cnORggR1fdEoedKgJhUrUFebAfCOk9JscSchy3Z0NAsXTBEJ5N7zlmp6LIlXjPM0BcFJlM5VQIoMPENRyQ24GzGPBAE4deqY\\\/t2u\\\/X6+pXrYS\\\/gGLOiBIWN\\\/XvDxeVwtUUgahKNjFFrjagSWyZo7jmEJhMxM6CYBbV71O\\\/T3n10t0cEXlkZf\\\/zsVjfaXG6yUpmRJdHI8SN7v\\\/FM5\\\/27qz97i6WM4b9K5fVb9VtyK+RW2HEdz2EHkVTEhb6WCWQxQ6BpNgKyz8mUqyogHBY+3CpVRsXI9Jd\\\/+eMPP70\\\/cBwF4YA8VhyG3\\\/2DN1\\\/5z69wgtKEUoRse8QgHjgkPLgVpz7+i3\\\/\\\/mRNPHowcxwGIadAb\\\/Md\\\/\\\/teXX3kDKmQZEEtG0pjhHGuNC6055KyYmRV5bmX3ropwKOiTlKQkSZmm8dbIHwJCMFwId+rph3Z\\\/6SkQ3frTny3+8GWwZGaSwebCunRFdcQfrPT4vWVx8hDPr\\\/AG0fgIdbYIiKviXgfjDYLbX+s6uwJnclwudxNhRRO4S5lGE4lEdVr32CoMN6FfgKUUvXbFD6SrXnnu9qG5+nJzcPvCbeyYpME6qxAyIkgiQaSYRKzEo+8d5yQGs0EhDGZ0CkcxCCrsmmTDrJkJE6qTnyizBYgrjYqZSDay9oDIaP5x+Sd0460xPbnjyNz6+\\\/Pda7eVVLGUCxPA9UOzg6Xlwcqm1vCl5hZN76Nuk1jFNmD6LHc9wEmALxRTbKTD61s4NceXFsDR7kl3bb7ZaQUaXKgt2sEYfeT0yJljIycOjD14kFgS2Ca5ctyFcPQ2ESSEckXkCO\\\/QjvDqbXPolCsPc+rMKB3hEDiWvU5KQXjk+Ow1UB0b3bXr4Y8c2n9oIhGogAt2iT\\\/8ydNvvXwnCPsc9mNAgtET4jjB1sZvVVEdnZybOnh4Z2plFoZyZHqGKmMctkm4UIEeabBVgWR1jtlB5xSMmnxexRyO1w\\\/\\\/0ke7vTAKQ1JMUhl2jHFTQp9CTizFLch1\\\/L0zTr0Wsaof3YsfuRxFpCIK+73VTccR1dHq5mKLllZpR939+Nnor9+kqUnaWE5yGMWhJL9KJJhVb2HVO3dC\\\/ngtFoaLg5WWk3LIrZJbJWYKO9qUm1hROKBooAXBSPhwPLde3\\\/3h48227K+svnF78fmlbuXAjmBjUa0ukpLYMUtBjzYWKOhy0k0wHLZNYf48DpTYZE3kZDLgptIAjKJSVdIRi9s5bJ+Fw+S6zUMztoNn4Wn2AyDiBhMEMblVb+zoAdnuLbx1Jep2OTUL0Dvw+OFgfT1YWo2julIEQZ1NGt1JrVVipvoYEzO55FeZWrHbnu6h+R6vrdL8XX7yURyYdu+t798\\\/sfD+hookKZWpawux8dJ7RBjcW2tduh0LAXJKX4r7v5xaBepPVGm49caRX3\\\/aQ4kMpcnz42yCX0TGGA2qbGYt4ozXq1N1fN\\\/xucnZEUXkkD54iQkKeOjszOGHjlx6YQ2DLZYBdPs+ZV2lBaFwyatRdYRcL6GZapAYtJkHCZ90cojEVpGKQJxS3GT2\\\/gFEG+2r\\\/+7H7bUWD7oUasErm7jDRlyFQ44Pv77zyQdnv\\\/JkNIg2Xr5EGiGoiAj9Zi9SauTQXmwS9Tp8bR5TR0Y\\\/\\\/1j7J29wFCXaqg5HgsdHAOZwIFf6TnjQOXxM3pqnaEDRgGNhBEVKUdSjsBsPk9IBQ9w0JmIWQjTmdlf37Vm\\\/eIP9GsmoeeUuM2RviZwBBT2K+txZhevQyCRVZrG+wIMukUmbEta8Mb1ayPCpyJ3PSZbskgmnQEGT2ZjYMsyhnykSY7OZYLjkasiL45FbI69Ojs\\\/CISKQQhSM7p9yRuq9u6v9pRVSUaLuGtMHxo4fDpqtYHEl\\\/kRRQKRIONRZp137qeOQEDh0mPbvx7XLFEVEnNweQXBpZoYWF4hdunyNn\\\/mYMzo6Otm4un6XErhwKsXfv3174fZtYsl6RKm0zZAyRPuypiULQY5Lfp8GAxGEbmYNXTKNZbNjxjkmTO78Sl\\\/FYeGRW0Wl4Y+OP\\\/mxw9Wqow1BdRWhw3JtzHvosT2X3xpT3ZF4+ZLK2rz6XWlnQscjr+oIR6dQmnYEokSVxyE4SUqClDNhrKe0hCEUPRmYiEhAOJKD9a1gdZXDPmnlyBTnlR6rKQNKu9M5lcXvr2+9c4Gl6i2ssiZnkmAg7EeDldbOSe\\\/65hYHTQq7tDiy47\\\/+\\\/GBxdbC2RF1FxPA8JvBoheoeNUNiFTUj\\\/8h+eeu2thOGCo3boEyQEggsHMAlxyFmr17dcexwwM76W5eIeOLsqebl97nTJxVxl\\\/zHTgT37nEQkgw4GFBHoT5KI+NiYid129xcixtB4By\\\/YaiNGkxmG4hYe77CHB6z4QplkAAKOBCgjCBhWucKbTHJbg3VMartQHWMhGAV+VXRmKlzt7l54RoPAo7nsA4JQawAqh\\\/eF7U7\\\/TsLWf6mr6liCpnWF6gxTmHkHT\\\/kfOhMsLFBHJAQsVsKCTQa3Nykfpeq49Tt4+qdyt\\\/9RHW63mn2mKMEzhbTcJiDWPAvEVnilL6UdZxFUqYKIkXS1Yc9MvSrrYPAFlCGOaMOsXUwGoP1NHbpM6syPrVv9qGz007qW5tcb8UcER15YKq6Y7rb2aCoT2pAKgQUGx0vxGrZbjreSAwRkxaCyLpiyfeQsHHiEIjcYIILzK3UW0pFHA0o6mfikQYehTNVAOi7yXLAodO53iQSRAylGKSHFhxx505zdm7UoTDsNilsR+8Nlr8zrRyHZo9Tp01bdynoUNDhKKB6jTbaREq9fZ4e3C9GPNmN4rOYDTmDrF5OZGmFA+GMzu6qT+\\\/sLG+0VprkOI0HjrSv3wzXVjRCgImwsemO1cNuMzm4mTtN6jTJr2B8CpUaBX1uabadLSGJAqM29TlMIIf6cHONRZDodqbCRHFhrdLfyMOxUUa8SHOhxDETXo1qE1Mnjh37\\\/CO9gbr96g14aF28HCwucWToncTkVnj7dslBOJi\\\/yymsSW+bNDEKApraQRsb0bvvy1bAmx0xOUF+nQZdYgWStHMHLd5L5A99Wu\\\/X2332R9utAcf60Nq9nY1qh2NpXau6S5E+EhBMikifFExwdHgpWDoYhAkmG+ZFScfYYLWkMZDASeOevCrq40dO7p0Yr4hUbTpJJnUtMndg7OiZA++srdCgxVpwkSVSY3btIB9P6kUqg5P5oIgkH6MUE5d1BMCw51lpuQP7eKF0UoFEcoY4gg4+KYaBDBv5RHw2TghjQztdDwkSYJZKqtU7zQdP7haQpEKSgWoG3b982Xn6NOoeyzHqNbm7QTLERpsadRaCVETBYHDhinP4EFbXOAZ8MnITosSfGHDcamV8357\\\/H2P\\\/HSRZkp13oue4XxEqtc6sLK2rq7Xu6elR6JnBDDAASJAAl1wsCWLJJddoNFuzR67xj\\\/fM+N4aV3C5giRIglyAAAkSA4BDAKMwWrWWpasyq1LrzMgMfZW7n\\\/eH+1URUQ2O1bR1V1WqiOvux8\\\/5vt\\\/HS+X9u6sijADJGStLvxNsbYKU8YFpiZVtd3JEbEWkLyk6\\\/BwRQk\\\/trSG3cGyGV05Qq6E6TYh804PtmSgkySlag2+US6RPwpTawIHZgNzcy0jGvOcEEtSTY4DYJyIX42R5tEyr3Smx4ujVLz518dUrIQDa7rX\\\/8KZsRVpyksKnkQGz+PwcOba\\\/cD8WVTFDDUXMIeVqOzAwopaX4KDOyw6fHhNSAnKAEEpFEgIiBXYBeAHLZXZ2lt\\\/dhKvloOWZnZ4x1LpZ5IhIpEAGeqqMOSd+wihJfk6m6zdAjpyxNPa9q9ygPnimnC4wA+czQ0KOqJPDHHBK7uDwM0\\\/POjaT5uYBrbZyLHBc1EOfSom\\\/+OLcnbfvi\\\/YB+A4wKzbLxi9aIlqiDNUw5qbFTKJUp4r9GX1d4sQ8HTm+AbPkjsIYKE5MI0J1N4ZMOx67obuU9jCRkqktECnR2WuUyhbHuGyxXGi31Nv3+BPn5bVVijxETm4J9g6wPJDEB5DHuevgzES0sQWoA72Td42MDBA5Y1ZpbHjg5PHm6mZ7aUtf2O2CPXRmvvr2NRBhSschks0mmxlGRiRiyLKuyPTZJgXtbRC3sDSAUyepsQ+dhgGUIWbjdykVc6h4QAgx3sKUJRZyF+wicBsISEmSIcowlUFkMEGUGWebw51zYExLjdEqYKHCKiPkVpRQFPgkApKR54cCwCaQAxV4\\\/jy7EcJylfT+wmygCEjxuSkaGhN37hmBWKx4yshj4x\\\/Db8OxWVAMgAHjaPP4tOQ0Mgn7u8AdsBwcH8OLJ9TqJlejTJwJ215SrZFloe0WJ8YJ0N8\\\/pLAFyPUPm3QReqZmDLgDvEBWARhX8S26K3EuTfH6M2g7pneYqEMJOXAbeAGd0vGTY489MqL17ApAAvz4\\\/fpQmX3sySF9oDGAC+eHhibHq7VtsFyIbMAIUKX1cx5kQUmGSlwpmhobkqUKMbiTMnLkDI0ytf5myy1CBIsxsFzkjpFhJRZNosTAmjV7xPdjhqZoVKQxdqbwVe1WR3HkTiyscSvgHdKhhI0NdmxANqvk20BStQlHbNRCIg3tvrtcODUjtvdI6nhICboZa8oNsBxr8vI5ANy7uRD5gdGXcxw5MdO6dVcFXqbJDKAIBImgY89PB\\\/dXDeU6xlvFZSWCDFXzENo1LJTZ1DyFIdX2QURamp6mbWckfqBkkn5lxeEKFtlFa2h87rMv8oK7+e13woN9iloQgQHg6U0OGVgOK1SgWAFug5AUeSbDRH\\\/nUpAiajVUq55Oh7hDEm985fUmkdjubFV9qNfo6JCVh6BUwkIRIh8O96xzJ1UQyXsLsX6f5fWlia1Gb8cKtrdh6iTUQnKLUCiZH3NwBLlDioNls9lpnJ9VC1sgyZ4fQqlUGBm+DzK03NnPPD\\\/3088JRWv\\\/+bXae7dBBZgjFJnVSFnoLbPBKYJbZq4jCSJjwoDeUEgEoj6O23Q6H2t5WbZMAu6AXUS3fOHSpF22YpQ8+L66cfOgVGLPPTZgc\\\/PSTIy5jz018\\\/3VZbILENogLdDB1Dm0GGnZWD5qOr7l5uHo0O9Mz\\\/NNKdsBSJSjETI+MMg7IYQeSGHoMil3h3paOubr2sNDAxdPtVY2vOUNgmRWqUQYKACr4CB3idnAGIgQyBe3F6znHmXzc7QUUacBjbZlTYa8AMAhkkBKeUGkAOfmaH0dWOwdMSUoVsYGR4\\\/P1DZ2mntVfeMDAmSqMj1DSnqHtTSkwBz\\\/CkgGq+vs5HFkFskMyzw7KNU\\\/kVTUaUi\\\/jYUyDExAoQz1PfTbhGRuGRBPOJUAwJhgpMtRM1ayhp+8MvfXvuTalqqMrX7rbexUzWuq2WFKkJAoJUhJjSMQkU53IB1YpdXGmMUZADJO3AbmEEL7oHHvrQVxc51kyEZdNjqo1neo2QLWZCWHP3oFnrgiv\\\/06DA9Dsw1SACpABkog40aThdlBJSMvBIugiMA5lEvgVkAEMDJGOwdgFfHkLNmOurMMvIiOa1uopJSRTCTFaLsDj56FkWFgOPMLL08\\\/d45r1iVkQihVbJNIdH+IknFlWwqZc2pSLG0YdT88xHbSB\\\/+BGTV9ehtH08RywCkVR0auPD4h0cTrcoD19fbyg8NyhVdrczPjLtM1nM2ef2H69e8Mec0ScBdYEMujKbOFxcruVLqIeZ4b9gZt5ltwlFV+QA6bCgCoAMKh0om\\\/+EroRyAilDrdMUPbyQaNxZYFBQDI7MkR+8xs9cP7G\\\/\\\/k31PQNk0ckiQFB3LLZbBLiECtvRiuBbSwaD\\\/3QtgOYKMDxOxCKVKSkINTgaAJMhIrG3j2JGy7GTEd5647fOGkDbi7sOI36iZrABGArFKRD5UOby3EiZQpMNZM6HxFocBSiVrNDOuD8mQ8ikWaEvw2+D6UBrE0AANDEATk+WCXkHEkoUIfhG\\\/KUlSQ4Y4SEhDjgjGGrHxisjIz7N3dEYc1kBGowNCOdedQ6\\\/cTgaWpIpKYiYSzwuLihVCGrITkt6i5BzJS9YhNTbFT82r7gPb2yLGU56l\\\/8x9IKRwcxNFxUGMQhSgFNWsQeqQVRmn4Xjz6392BmVkKQhgoQWEQhytkM2CIp+aAO7S6DbwABQvAdYouECgzoCcAIiFqt1ftkzORUAdfe7t9cwkpSoJZKEEIphG2hptCAGA7vFi6MPAxK4ORpx5CaFY7jD0TVMoIOOLBugN2EYuDlx6ZO3duWF\\\/wGQAC3VloeS0\\\/9OTmRuvYmKsVuQpgdn5g6vjU6v428RpwB0hpj2Ea1QCApmqmnHQkIY1h18ghxjWlB18mXhlTp32y2zLGWMvb+Np7ncMWiQCiEESYCbHJwqRSeJTuGznjQ86JCW9tl0Iv\\\/TskSUkH0SkNQLGtl5+BbRPI\\\/RpfW7auXIj2d8nvMMbZQEUeHQBIsF2QgtptaLfx2HHarZKKQAl3bKAwMxm2\\\/KPlTdKXf610AUQE9+Rce31LRpFmN+czm3QHnFMnZKeOq5u3iTDJX0HoxwpO3Gd+h8IAnQKMTOFIwXKLx195vDQ7vvon32\\\/cvKGDg\\\/SObJkXS0lSUf39W1v\\\/4Vv2YFluVMslt\\\/zkFW91w68ehY0WiZBkgDIkJeJiR2UQYVomK5NJfgwCUKCkNmVZg9xQIpUCBLWzA80mv3CWJoZgwJFvvw1+B5hF+7tAEpBhaRjHpvnwhGodqnYdIo+0GgOMvAsYB1+ARKgMkuViqYSTk7S6jmfmQSEtbQAAoIlTtwtFi9DsILokiLy9b73ZvL+poshfWiEZgJLxVEZlhsuQ9o0Rte8GuG2VysrzeJe2Idd76MvryohF00gTrWewwHLQKfPS0NUnZgoFSz+xEjHw1e17RyrwFMk33q0+fnnEso0KeWDAfuSJmfU7S7JTAuGZizEofd2ih2MoifpRTzDPZeziCGOeP5z2WZkrZLiz7+1XSQQgQpAhajQ9USYfPa+2AkaI\\\/jbANUVKICZzSxM7yRjykguVQWrsZ0g\\\/CEqJxWX37Glx7hzdfaAsh08Oy8YhSAEyBLsMUIRayK6cVz6i9IsTA3bFad68p7xAh64Z2iIQIBRPzquWFx4cZeJD8hJ8\\\/feOGjA5gZZFkSTdRIwT2zPWkHg2jpYZNTllKAwRFNEdGHz58alf+gxnTA5U7v1\\\/V6TwQZrnwYpx4hEITx7ubv72fyJm2eXK8Ol5b7cqg3ZpfHjq0TNHdx94ewdCGr2CKUgwE9mT1Ubmw+VBCYo8ijy130QRkDaro4RGoNbX2EvPwf0lVETcBeQISJGHaJHXkev3FRIwBKcAg+NsfJIaNWgcUSiAOHAXnCL4wK4eLxQH6NS8IsFOz8rdBu3upbxQUsAsi9vM9Ai0FSgCJZWS7Tt3gRQIHRMrKeeRpywhgvSkXgczAepQeyKl8WNISN1gEMqP5DF7nULAxPxuIqW0baIwMDwzcfHKmALggIQkAT5caCwt7lCngYzfW6jv7PszsyXUXxXh5ZdnX\\\/\\\/O2FFzn4KWAe8CmOkfsEwLKHdTxHzKLFKaU5Gnm2J69GnoI3b1loAMfxVAhhB2dOQ4xRQJ6hOwEU849L2UYrMdKQQLuAXcAVaIbEs9ew7ad+FoBVRISr8kAACqEwavvcu\\\/8Dm5u6faPhuugF0AxkDnnzoVYoOF8eGRv\\\/6r7e\\\/d7vzwg87KCgUBKD9efhohS7xSoIFB\\\/\\\/Z9REz82VkwZ0o1lEoe1KFShnoEaBtXJ1KOAKq36Vinhwi85LDRAX72ohoalpw1251y0e2s75NSGUw4WfESkqhCiBTJABiPonbt1tHwibmA7PriprdzWB4pjDxyOvT86uKS6Hjx9TTj+knptZjRjjIDFFIRhL7a2clkjDMcKuNwRX7ta7w84Fy+FK3vqWYLwAIlySqA8AEEAQOF4HXA86HVgWIZBkaYxWF4nCIETyIvnpsuP\\\/+ZcztPHHvnT2\\\/X3l+gZgjM0iQ1BA4Wh4KjCrbSLs+kVaAPDUM5MDIaTMKxYwQQJV4QRAROuv1IBMiR8RRR0aUTzeyiGfR\\\/Jikck5hFjcS1gDvgFLFQeeaZubnpUvJpmKJr72z51QPwmoBWY7\\\/2YKk5O1tKfLhTU8WT56aPNrfQq5Hwgcl41s7MMsVuSkVGSJ79tgl7FPqEXT6QbNgkGvuTkubBJYUaK6qH9eb17O4J5akZsVogawliNjCbR5LW9nHEgVNT4Pq0sQGCkgus2q\\\/jgyX29IXo7iIbHYTCEERtEKFmdaNtlS6eGLow1\\\/rTu2GHqDgGjENrFykEAGAO8AI4tnPpdHD7AUVhDlCSUO4x3\\\/tut\\\/HMBbx+nYhieaNpyWLmmWcWYqngnDrBLYdankAVLq2yiU5Q4kv\\\/8F\\\/ZZbt1b0UFHVQyiVewjJhVAUBCUGYALIq8o\\\/ve+JmTxWNj+2vbQXW\\\/bkFlamz8zHFLydbefn2\\\/FqOs0gCGPJkkpyYlbpFQhFy\\\/0nxkgM9NRgv3odORfkTo8DOnWaMpNnbBLoIQaLkgA9LZtxrHJwS0W9hBBYQBwMQczo3Pnpn54l94dHJq8PyxgY0Pdw\\\/Hp3BoCNo19OuIQHaZCmMwMCwdS4JCEwOaaboy28hxSKLSN2NFGaNApt7XC4mRid1jjDNmJGV5nhPm2tcZInb2NmiWB2rjErPAcsEp2wNDjz06adss6SAFHbGydEhBB4I2AVPN4vvX9p95etwtaBIPFlz+5LOzN959EDWLwDugBCmKufjpL8qqN\\\/tDKLGbBoeYS+9JrsTdKCJUQKaO0NcEAiRJWt6QYoiTmoDFBSlH5hC3gcAEEpu7InJuOYC4ukf3Vqi5i2UHz55FBrR9QPWaXjPy2rL1K18E31cKYGACfAfCNojQnR2DmZn6999vr+8H796lThXCFkgPCIgXkCFwl9yK+\\\/Fn5V5V+ZmpIOS2gvyupcD30A9pZAY8D6VPwgcZxc4I5OWSPTVOjClE6nSig1ZQa6kgRLfonD8Lqu2\\\/t0ydpnFjC5\\\/MPkVA+iTU4mwldUM2HvUy6av9xeX5cyenj43srmyIUNXWN+sky4PFsbmZoenpTsur7+6FHQ8AiGQsqMBUXIoxzNKyCBywinqzxKJjzU+K+6vk+QAMSKlGnZZW3ScuDf3si\\\/7ybudHN6jThE5gGj9EBvoAAEBsbAzHJ2GwzIcrlcnhgUpRVxnuxBhOdWh3HRo1tb8NioM7BCWB5SiYLQoYA6bbSByYhQBgO5X5eeB2a2OHAkuberKuiJwaU8N\\\/uA2WHqhyxpAxZuC81EONzV0qMVOtm8tDDKHQ69ACywW3Mj49duzEgEqInIj3Vtt7ey2SAoQAAIq8uwtHGzv+6ZNlFle+Tz458c35yc3DHQpbJiYxJwmI3xXEFG2UX269VP2YcNh1EczMHMEAfxkz2dfmp0AGKEhlX8lsEcziyoIDs+yh4dLMZKfaCKuHIGPEMyAiRAyEkuB7EETUagMRjA+w6REaKigvgGYIkYL3F9wXL8uthtwmaBYwPHKmBhgo\\\/9YdFVK4ugWWA8KHqKNJUCbiLvL4UAGLVrSxA3YZuGvG4CIyrHdSJHxQgrIDVGZB0yufuRAdHIZH++jXmQV8sMwstIoFkiQjFW5uURABkR75om3b8+MIneCD2xCGoAQqSSqKecc6Qg+s+DWS5oqBSFqegoyIZKDWbi8cv3hm7uz89tJqFAoi1aq12o0Htm0Pz03NXz7v1WrNo2a7VpNCUup1jDEnWglQGQBC4DaoCEsl59yJ8O4itT2DUSEFIqAoAsTRVx9vr9X8W+vSa6LtEBICMcdihQIrFsG1sVyU41OqrdTOodptr9TEG2fHT54cbJWLW0sHdHcdo0O0XTUyDsQBi8RsUFHQ8SRnzHFNoihYwPj4s49c\\\/NXPWZa1+OXX99+7QyKIu3AGwYbZFClE4By5RdwFu2gVXct1HpYgmAQQYEbGFhcuSMAy7Rn9KtlguVgcePzp+cqwK+MVK6X64Rt7HS8wVkYAAui02otLjXMnyya2jaAyZJ+9NLV5bxC8GggfSI9DWa72w7xSpQtmnxluYgYA0MNoTtnymM214dweHnRCCSIEITEjkc\\\/HAyZdLk7cYrZ99pc\\\/PfnMuf2ba3f+76+IhkwXIYOQYejYpiUDBErSXk1uB2ApPD4Hx4+hh7Rbnx9wLn3uSn165PrNLY8Fwd0HYm0NZABE0PBxfJo0PlivIo2WddCaGgv+6OsURTHoTQAioIXcAWcA3ALqhmJMUSG7iG5l4ur5p\\\/7Wzze2Dq\\\/99rf8Rp0XbWrVxepy0GmTUnE8uH5hFGPcOTMHNguu3SQVYZJqrqSBoMb9Pysj9U87skaiiaRHZWt3FmfPnjh5+czKjbuRBABGBGEQ7i+tVVc3ysODxy6eCdr+\\\/uZOp9GWUaSn4bGA2wLLZTMzgBahhSXOH7kQ3rir9CkHhJyDW6TSAIzOhku7h7\\\/9nXCvWbh0MhofEOsrrgrcyQkYHfIXVuVRXR01KJQEW8As4BxLFTg9+4Pfexc6wvqpi+7ZUX6\\\/JO4sQ+sAkKFbhHIJBgeAKGQcbJsXisg4ISIydArjH3sMxkeI4alf\\\/tjsS+eVEqQMe8a0CwzXxXwEY4wYSkTijFmscnK8urhCmRSImBhI0Gs4iOs5ivVkZnSHZkaPTqk0PPL8s1OcM2Wsd+Q1xNq2D66D5Qo4BT0dUZZ9b9X\\\/aUFoo7ngM\\\/boE1Ovf3c4bO1D2AaMAGQm6SH+whniPebpi5SMqYi6dNf5GLuU\\\/BwnTZEiwuHShb\\\/y6bYXkpCcFIOYeZG\\\/ZMbHPwJDiUxaTunkbORYpYvHi9OTzVYLSCAiEDKLK2ZFM9M0tg0cIHRNmx0cYAhNBULAxGjlifOvXp66enoILw2O\\\/WjwP\\\/3Gm7IF4I5A2AIZQNgAEOA60O4A8nhyw50rF8TeLoUdoz5TKibuCZBEXgQeASUeIA4I6BSs42fnvvgJZ25seHyoMjXWvrckWlWIPJBBGgWHDIAD48zm7rkTikR4fUEfsETS7H9GRaQSkLmVVhppxgAZuaZ+KVEpUpuLy+Mzk+eeuHr\\\/xt0wMoFhRCCFbBwc3nnt0C2XisMjx5883dyvNnf3g0BSUmU5JWv+uNhpssEh+8xkuLBOAQAvAip0CjQ0DmGASrLaDlp2\\\/a3beP6UOjwCGeLJeXH7XnRnTYWSRKilbYAckECFoLAwO0OH+\\\/56HewCfC8ILhxzHpuVG6tkcWjWKQgg2IdmGwZGhB8BAnedGFtGIKW3vkf+mRBg+5vv7v3wGigBUmYi5ql7\\\/KCho8wCxqyCM\\\/vfvMJ6oFjY\\\/ehl\\\/F8p6Trm+Wo3PbPAKkBhYPbExNRMGZPWFeBRA4aGh93ToIIAlAJEtC1WLIVQ2DkUM5M2xZroK5eHTl2cXKhuQuCCDI1tCVN9QMYVkmjX0n0h4Sn29ytlUV5IuaaObpTUO\\\/e\\\/\\\/EbzoEZhB2VkrEyU5eEmhGdm7sPM5qWBs3\\\/t1dLZGW\\\/7MDysJ6AtVKHroBMIcXMHOgSBwNAjikBJ4Ax4mawyWgXyEJfroyVL6jZfESH0QAQQtsFvQtQBGdDOGhQroAiQA2cA6B6bAD8Sm1VjiNE3GYZJ4Ju51ur2WcnBQpGfPUPMoVp74\\\/u3KrOD7dX92of3VKcBwkfpA6kMEIaQMVYoDjxyztvaCje2SYbGZ6xl7hDnx2ayyq3Y+gQpWR2TxMoEREmk1MH2Lklx9rGLD27eDz2fdHpujM0NWp2gE7YO6+WRwdETx4ojozsrO52GT26Zl4cLE5NBnZynHmvdewC+wMoQjk5Q2AbvyB4Ca2Sa+1J0hOx05MEubW9REEEU4kCZj01DyYKNVVARgIwbfQqUtKbGZOiHy2vAXXDKsE1ytAx7R9bLl6KvvQdD09DYh8gDkqAo6kgAdF3bqCKUoAg2vvZG\\\/d46SdFcWJGdts6lMD9UL9sCGTCGlgvcIW5bBSdq+2D0yXlVWu4OlA\\\/FQswotpNiwQa7yMtDjzwxWyhberCg47bn5+y\\\/96tzUs0KMtNlC9FCBIaWg8konxBKZfvqE9OL7w8qvwGRj9rZHk\\\/au+AKsTsUCT8i4KTLqJxGxUAXRQeASeXtVTs7u2ZEEfsJ87NIzAF1kKNduvfrf1ScGfW298PDAwOhRAUqKlTsUCjZ6ECkwOtQUAcZAulrp4BAkBdYI5Fz7OTijj8+Ujg46Lz9wY4zU\\\/KrIWmVDONAFvgeDI7EZ6DNSwU+NOTfXdJCfAO51RG\\\/yWXYYlgpF06fUIyJo5pqedGdRQoV8OK+Ktb\\\/3m1ZPxCNGggfTBMh2WwZMOQFt\\\/ToeX9lNdje1Qo1TGS0qWYo63JO74Rdb1R2WKQQDZuyulcVUp26eHr9\\\/lqn2TJkpKSHoUD4Xm0nqO8dVqY7zvi0e3LIGhh64qcenXr0xOKN\\\/Wvfvk9z56yL58BrYuipo7ZStqx15PYhtTpECKSAImPPRaSGEJ0ARydh9hjsbUCnAdwBboGSbKACIyPR4hpEEiwBoKChIJwQu002UGCPn1dv30O3SCC0WzdQFil0S7Y2baCSBFLUD4\\\/ePwSN65ORviundt7sUsLYoUcSSe8FriSSZjKOfSf0iH1S6DFn5OXJcGJoYuS5ZyZyvmggsNC2GAAmjl2Ghtkg8\\\/59hXDlkYlvjo21OjUIWqRC1EoZzM67MrAAhr1xWKZxRH0so2nEmsnGyfyBUkoRkACpWyCB8fjnh6WZIDHjYyIlvFW\\\/s7qSEOkpZhyXx8v7R75IHvEsOwcIQRZPjbHhofrq7n\\\/+T8H7x0erH64erG6yASyfGuMNq7m8oSIGzAKSUCjDwCj4Hjq2c+VisLCkhAIE4Lae9ZkrXLHozs0oi5NSKgjD7V1RrVEQmSsVAjCkdi042ADlgwhQPzPZ\\\/YVxPjBQuXiyvfgg2j8AJVBvK2Z8kFt4lHFWWBmzDuZwKNkMbq1jUkgg69UjIDV\\\/Zn7jwWq7Xov90\\\/onkaRMoGRrfx\\\/qHh+eOvvZM1d\\\/6hGrYBdKxZXVtljfpO0HcmWZDquk9MxUAYmMFy52OzL9R5L21tB2cGIWRyeo44PlsqjpnDsV3FuiSAERyEgLKWhtDafm1Hv3rVefpLUSrazgwCBwm1QUBQIjMTDo6IY76fsxhJg0Qo1vSyYhu4bdBgiEwDDhNZBkYClAzhiPLS1djY5sPkwuSCmj4Qbjo+c2WC46pdNnxqcmCiy2ozFAhqCSyVkuXCmRPSZ5X8QRT85XLj5+7L39Heq4IAMAhZjWo3HjmhAyQW5d33wak9JLv39IphYSIUVKkQnojfkgaV+UEvhCEtgBxIARSQIZ6tQ0Yw9Ajb10RudHDlZqyrEAwQAZFBIwYA4fGbanx0iF7ev34fgx2Wkthe1wcZn8htprdFQ4eGJq8NLp4Kjpb+0RWuAO4fwIba45xybEQVu2BXBbo6iQMxybYY7NChYjIIBofVe1WrFHMubWadJ0eRD2lkF4QAJ0+8DEeGl5JrNHhgqnj7fv3Bf1GiqRaDlS\\\/W6a5UqZeHSyzG9l0pBTkmkuKFiB3pCB6od1BHby3Mn1+w8aR01KS39NWAIkAYIBC6TXObi32d5rlqeHGrvNaGMPl7eirQPyJDAXmDabKJCmNM83xNFwU5AgIthZg5mT9qdesZ89Q2\\\/cCN\\\/4UHnSsJn1nVgGtLHJJsZVx5M\\\/vMafPCmqO9Sq49QsFCsiCHwvLA+5SdaFJkYTqYyuWudOKMrc4vL4iXjAAISk9IgiAxfpDhxAxK5k7LyW2yAJwSny0uDVx6YLDlPGuYuIYCEggUxN7phqechMy1WcJIFExQJ\\\/7pnZD18fFM0CCJ9I5JBSqRRNb8AqzZROrLnJdkGpkBSSGMU+kcrG7yKBQIXGysh47D\\\/NLvJkZceQddN\\\/SMRi2ugAhNwqlkbmx5a\\\/vaLCAFwbnCKENjCJHO1jk3xiLFhYVe0OnjhFWzv8zATtrFN9C2QIIlSk6vfXmMVKp+cHL53yGyIKWfHkCTYyShW7\\\/fZ1sB0sMbCKaHOsVJBbamNLbjUhCglUAm4yMdugSEWADJ0iFStQ30MwYiBzh2PmnlIcH3FnJxvXb6tOh0iPnU3WbLL2kKivxNjKrMgepmieQ53gZYmgVq1GoX\\\/i0tmNheVGrUHaQWM+lSJCJAkyoqh9uLL2lf\\\/pD0cvnTjaPGrc3S7ODbtTp737S\\\/JAqSAEpUCKlGmffh8MGAAxje5Dqwh2kY6aVLLgsTmxsatuVDAQ1I6fL0NqQPLaOF1Wq3v0dtu+fEpeW1KtEAeLcmBkpxaWpoeBW8llwFSeCX3YVA4yBWZ2LUKmKUyO6ZjpEZn+wO5Hs7syTfUzkGN\\\/oO1CYWDi+NTlR8Zj7icqgHZb1PY9UipSWoFJLAMOUiYMkE9PF12bMSQAlAAnzgwOToweHu5C5KEEo3TNnkd5ll6\\\/aC\\\/q0tRgjiOG1C9FymaI3DI8Ie2XVz2HodldGSS9MaVfbcJUBM+QsUK5UB4qdA4adLgPbhGYC7xgVVznzIw4OPI\\\/vE1SQWWIjo6g3bGiQX95CbwakNEwE4CU0Lz3wBoaL129Mnpi\\\/uoXrvKC\\\/f6\\\/+XZnZAiGBwBDWHtAhzW1uaV9M\\\/FQSqXOEX08GHARUXkAazukAsoh7RgiQ8YrsxOFqfHDD+9ILYXRfPe8FjxpR6eE33gdWl2g83xuMqaN6cTFp7RjmLUbrQc3Fk5eOGltWUf7NdOhiYmDoG3BfkvRzs611u6dpcL5Y9H+Rri7zmxljVVKj57GMOrcWZJtD9KZiYqrHAkSTHIdEkQt4i4wR77\\\/oX9UU+\\\/dBr8JwxN44iTsbVO9joUSACcRqd0mu3ycHqzSTscaLDiPXmjfO6RqQ1F5c827cHHcKbu+rw89A3IlbhempwZPzx7dXpVRJiwt600y+CkdyeSCXWAFlxBENpIJenSSWW8sdXVKWYLAwkLliafmp6dLCpIMKPrR6zt\\\/9MeLFHgQBiT0JhXDgxgnboFTcIaH\\\/vIvX3zxiTEjvSMYGneuPjP7o9V1CppAEmOPbgYEj+kuQaq7CZwJw+whN2YlNfnFq5RtWVZliJd8iBwgBM3qT52WWdURAjBeLNgTg97KmvI6qET6FZEDs8rDBRcgaAVIRCChVCqcPM258O8+kLUjIAVuGewiVLeQM\\\/Q6qnoIFKVjWSPKsWS707y2dPK5y+MnRiNJxz92Zf+tO7RwBzo1Mys3qRhE2VAgw\\\/sSKSWPMbQdONxEpcxpqVUWjCOzRi+cQMet3liQgYemTE0iTJLCgjJVJSU\\\/r\\\/6\\\/lVlkGc4o5RFbmfwKQGVmYQqCTmflzvKJi2csy93f2VMqBkLqr6EECR9IgQwpKkt\\\/mI0V5eae8lXYbEbr2874gHNsHPlUtLMnqkdEytx90iDYTMyRinB0GKSU33sNiyUSANtb0Gji8CCeOe389DNwbS34xg+h1VZ7VShw8KW3UnWem+KPnJR3dqDdPFiqPv3iVHmk5B8x0ysnJORoOce+9PHpzz9bu7YUeL7SgXvZFBdM5CegU3u4YzOLuWenopWtxE+Yg7NSt3kindJhirsnbqFTsMtDjzwywZnp\\\/Sugji+vf7DV3tmkVh0iH6SAbAYbMuAWuGWvOfnOW0MvXh1Bm2l7ScFmLz838\\\/a3hrzOIckwXv\\\/6MM1cNTQ\\\/UqsUIfP8Ze+GCZ+JEuYM9iEeAihSNFQ89Zc+VW8FIopQSEaEipKZWOZcRVKIhNbYAJ+f3Pxnv9d878P4mGBGy8CtgYmhSEDHk8Q4mxjAkzPi3q3g\\\/gMKOsZVaI\\\/CwSaQAi5l4MUkZ+3HVaBhaoSgBDjWwYF\\\/sFIVnO0uH1EgQFLcUY77t6TSmQ1iouNP5Yaj0xAFyTkZk4E4s+yh86eI1NHNeyrUK1Bh0pHCLG+PsqRWzO6IYOC\\\/1K2dSyCVmLNVZ6HAusANfG\\\/p9uK5K2dtG7fWdhRxyEZeJzGdBGJtg81Nw7rUjV0KRbBWA1LWyFBpboqPD0bNjndQV5EytzUlEYCY8RBiuYQT42ppGcKQ2ocwOIauRY19ajU4oTM5EJ6YsidGxWGN6m04MQsLG8B4uLhvPX+Vd5TaaTT3G4pBZWr4cHWHGKcYLkFS7vz4Wqdab95aFX6Q2uH1Rs4yGCuM47Kdgu1Yp37pOSup3rAnuUqTVZCynVNKc8Bi6KBTmjk+Nn9qSG90epEs3a8v3Vyj+gG0DkGzC7KnllbYuBUAXFscqdfPDo0XAFEf7qfmK9PHxlaq2xS2EzeD0opYNHxMJCCl4h6moswnR+jJH8kTn7I\\\/LcUoDdH0N799o1FtUeBR4KEIgCRBPpKFFBAQcATkg0V7asx\\\/sBonDqB+cTRnfmRutOHa4WPHrbESk75Y2pRrOxB6oCeQAyMQtEF4wC1WdIXXiPdBSkJ5AZA4A86tydLejYUf\\\/fh9dIAcZ+DMZOvmvgq0u4YylXB8czM9gkSBgQAMLJt2V2LKvWGRWI49cvZYJML6whJpvVuSMgTdQwgTyYp9IlaTzHrM1BoIvaFMaU2fmeXqolmJKISFGwunL5+emZ\\\/Z3thTxNDcXA0QB5RAEajqEU6Po21R5IEKYlsXieph8\\\/CIu87g+dPu9FTnoC4O66LdSjQlpBTanJ87LVe2wGubcrG6RuPHoFAEz5O7u53\\\/7fcpVNDu4PgkyA7MT8HmEUkOoZIfLvPHT0Iz8JpeqyVGj42tvW8bnrfJNwubdxZbd+7Hp4HKSiQzViYExtBywHKBu1GxIL2A5TwBRuGftMJStnkPmdSsOGYxp\\\/jcC8eHxgoxjo8YwcL1be9gH9pH4NVABEASlaSUg8qAOwgEXqG6XV14UHtyfDqWacPQgH350vjq7SIgkgYoKInpQZgIkkWSq5NPremJg0XsDXQzzyhp4x9nkfQ297zdKgWdWKgp88ionDwAAOA2pH1UM5lAYtxyrJmz49Wiq37uKv3um+F3r4NtGY88SSwPEXLoHAFjQJzPTqmjA1ACuAWK4oQvIqcCtutUOLeVd\\\/uWkKCv+mxmqHx61rsfCE+fDSK\\\/2yjoMYhioUhBG\\\/U0AhEZB2Zx1xk5MyfbnfrSOimRUFEoxSBlMUUpV6B3TSV+M+qm3acknz4dsbjRpilpEpUQUbh0a7FQsudOz3PLAWbpcAhKmDa6img2rNkRJIlJWLxSIAUpIfzg6Obi0Y17IMLRJy+WTszaQ8PolMkqo1NwT82rzT1qtICEGSQQwf46qBArZegcyaUFOtynVkttrFKtAa2Afe4lNjQIQFQ9kNfu8MtTAri\\\/354\\\/Nc5sFywb4h4pKAkiJOFD5EHUgciDyAPRAeGB8ED4EHmk\\\/0X4FHkUdkj4JCMlpVIU2wpSqW\\\/WcUfdfl7KBcQClsqlq1cnESHuUaDwxeKNTfIa4DchbGPUgbBNUSf+3nwUAQifog75jeho9zvfvtv2BJnWGRDD55+bqVRcUARSao8fk4Ij6CgTw1SVIYjAcJBBkkGA5dcKYpZI2x0Vm6pBkQOCEhS1IWqAaIP0QWoGqWd+SR+l\\\/s8OiE486RbmPDJlAgPGK2ODM+cm1t86sA582mmAkNBpAAMQAXJGhUFoVGOfqsSxYepIMBm6BMjRKVNlGgDtAiscPx7cvAOtA\\\/Br4NfBq3WWV6P9\\\/YnLZ5xSRT\\\/8qY3SjJvS0E3U8PiZE9hpUNKtYdwqlUYfPd+p1o8erJMZL0tIQ1EhN5PKwPL7CiKSk\\\/AhUJQssy8L1jQ+TzTpmigBSUS0urg2e\\\/r4mStnl+48EBHFL4022UtSgrZ33cunBYl4X1SYouAUKKF85W1s+du79vCwOz\\\/nuKXIE7zoUKuu6prHmi2aCI62YXAcxybpaJ86DeQOSAntCO4u47njMDPCLU73V9T+gVpZxdMX92rw5PkJd2i447cNElfb7c1dLOnam1B4zGHq9d8SiEzn4zKGQIoyEUvGsUDZD8Os7lLvdxhTQkBFc+PW\\\/FgBIxkCKACb6N7N3ZV76+Q19OCb4oc1rUWJo\\\/ZbIYM6rH5g7axcOnVmnCEqhIBofr48P1u4vRKayr99KDttjARDFIQhkQgFBS2IOijDFIWspyx9\\\/PbZ\\\/m5sI8w\\\/KAx0anJkKEbZTiP1\\\/FN3PuJWVZI5p11d43Ojluts\\\/mDBizpQLGLBpaMq2A5wGybnYW\\\/T7CREiKDabVXvAOfgOFgcBMGI2RAKq1JxTp1s3b6nggCUAAjiqQPz96q1IBy+eKp+dzFo1AEpqWAN\\\/NKwK5CQgVuCKIDA1713RHQqxcHLZxsLy0H1ECg5A1V3MFr6w2I3\\\/rRnOVrdf4R9S5LkXk6GDJvSpLW6DQhQRmJraX325LELV84s3FyMROZuS4QkVKeDBwdO2Q2bUXcr0WDklEa9R0eNqCP4yGjlb\\\/xiQYjOH\\\/0gdIrKk7kgKv3It+uIFpueU7sbJAKwHACbmh5tVZUXQr3Bh8o4UFFNiUW2LtxXxivHjg8v7u0Qs4hZGaWocdkk5xmkTIccJJtYPCpEZJyhJrWm5r3MwAcpY3OKe42kQCGiAhlB5Fe3tv7ZP\\\/kOlQYFoIwCLvyDjcPO7gYEDYg6OpvS0IqSRYgKAEAYEFVrC3\\\/n\\\/\\\/jW8OwMc0sEpIIOdBo7D5Yp7IDwgHHRwT\\\/+j2++9voGFEqKUIYeefXl6\\\/chbIPwyVxmFPREfFNP8AhlEn2yMEVJClSApOLQDgSQGQlu1qAYEz10NHxCoNSkcG6NnRqrVzve1paqVXGQ4\\\/wEVbchjHD2HNT2QATx8SLR5lCvURQAt9hTz9LHPw5f\\\/gqsLznj45XPvFT\\\/0zeV34ll3xDn+jAg6R2GMuyMnj5W32DewSEYcLmJB0iDHgigOAD1KukJFmOlocrIufn9O4tBrQEquwIpAbr2DH3oITe7tAFm9d3yqOdjKLMM86epAUPrb0BGsLm0PntCnL1yevneahDoekPf+yUABI22e3IuvLGQExWbJzTVHxBjoCRHqX70\\\/uHSll0qFC+fitoNsbyqOn4a6a47RJ0GIOHoGB3sgkRwCmBZ6vYSDJRoZ0sUy9iOYHoWAmLnB2pD7uSZ0cV3UfPhdTCTIUAidFXzlEvzRgA08AVuAzJEYIxleUpJaAD2vPqY6fGTxlaoiMLOwerawUED3EEANNWvCCkKIGyDCOKzJUl6MfE8cTNQ6qH70vs34e4GOGUgBUEL\\\/DppKbMS+kF\\\/cHPhwf1tsIsABJEHYRuCDkRtkgEqkeTAIebGepiLPEDK5iwkkjQiJOJJ7gizwNI37biySDUAmKoUwGRy5fq9nDOLz56f3FmthfUaeA1qduDMLB6fp1oLuE1BmIlwJeYWMCTgHAtF8CLYrEIQ8sGBgScud4482WyYw9nwaUzqgd5jwnpYvRcNnDvNGWvvHWgzd\\\/e9zHFgdBJW7mqKwsDk6PCxid0bi2GzlXPWQ+bV6HPW4UMr0fjPrMxUOtE09cIMkihu7IfFQ3MkKkUIEmhzeWMe1dnLp5YXNzttLy62AUCJRrt4rsQKBen7SfMDuwLEkAGzLLcweGy8fv1a2AxDQGTEZ8adM8eV58ujtqq3THa0fhA6TYgCGBwCXwBn4Ldps8GfuiCLBeAOBQoOGsD2lQi2sTJ+cdpy7EhYIDkoDsickYo7MujtHlEuljDF4Zr2HWDsCylw1yHGVCaEnuJhokl\\\/RIQ+CgkwsS5KAgqUHgQAKqROzZxBmn8jQ6NlNVR5lbkJaKiNzognAoCQSAqIOsBtAAIRogxBhEl0LpAiGUJQNyFEui8qAhCeZu0YIlMK9U0jmLC3LMKuHZ2IJDLGiwNY9JAjKhUPTRPMDCBmpdvMKrgDZ4611vb8g0O9UyCzyXKH58ZGj4\\\/d\\\/L0bIAKIfJABLa3jxXNYHKT798EuQ9RMpYFTEzJkODhBIqJb1+DBAybDwcfPte8s+NtViAIEIi3fAdQMobSjgTzqePW7i7NXzjil4tH6rmliZf9XGYH6gd5th+amKlMjO9fvRp6v+R1IyevWter6eKbzlFZ4SDmazfQF6stEp353yjx+RBnAGsLa8vakYKcvnXpw877naSGYAgAVCbG+VRga6gSCEEhjtokom2YBjFv22JUzra2t8KgWQ4xRrKxLRDYyyM+fhXqLqjU4OiKlgFkIDPSOPD5O1S0II+AF7HjWsQmxH+qgeQVh9U9urrbPPHlipDg2LAKf0AIUwNnMn\\\/\\\/k5KefbL33QIRh7vwDnSCMyBAIJBERKsaY7ViOVTk\\\/U1vbybbUHlr5Z0+ChM2uhBkHyQhinL5pFKlEMyBNoZ5CwWNjLdNB2cYUgtIj8xmIDF7WbAagIpRJ7Re\\\/WYZ2JZJYYiLqm\\\/zaFcfcc+cBBRAOFCd+4cVSOwARolAcgWvNsdK6XIWAyJm5ZyssDJVGnruw8ZXX1v79Nw1IkttoF449ckIh27y9Q5pFTwAigiCA4TJwhCCKfaoAnLvnT3rX1ynwQUbAGQ\\\/rw+fmOyub\\\/u6B3sVICQCGzCJnEPwjEO0MyUIBCBni1u2VyScujRTKteU1pbO9kqStkXHY3UXbHZkddUcHdz64LUIf9fKLbzFZYWdXtUndCfJp17xrTVrdTN10xA\\\/ddCDsTsxGyn+gbmboF17B3vpWFIanr55fvH4v8INkWiLb\\\/ujJea9aIyBU6TqM9bucMRg+PR1UDzpbe\\\/qJjC+jDBDVYU29cw1KBX58Dk\\\/MycOWaobgBxA0odWgwMOJCTqoAnC1dug8dk7sHQICkMBR17+7tqzk1f\\\/mqamz0839GkQGeXB4bTnsyMYbdygKcltXon\\\/HmA2jJ+xuxSpXzo5WEIH6WXfjBIZ+dDPzWqm4AiJQEhiLoVhJFZeE6XUFbpn4W1KEuqmgFDCZFsx6DJ2Zkptc5OybqD+\\\/0j09hRSvwN6uTPf8s7dbjoQsaHoH3\\\/6gXfNAhBAFIMNkkm96roowEesBWhWntldtvHMbVGTYZ8xmdnH+ysz+yqHfbseGeoYTw7Sxjq7NZ4bkSgvsEiIHzrFUYkC0vwkCwSpwmw2fngsPqv52FbVVTUWglAZFgH8EMkJmAylKdjcEACUV7d24P37+xPil8wd3l5RSwC1gNo6OwMAg7u0Pnpzjttq9dltFIUihPxwzHe6eNHnqFxWaowvkT0eyEPrl0fftr+ccGKmQP1OyJAEgqHexo92DKIzOPH5x5eZip+0BASEL2hFUKs7AgF9vEKM0RwoR0ALGirPjilR9eV3LtTDWWJNpCUpQEhq+uN3GkXF85nkGIT5YUnsBRARK0d4ujk9DxJRyIreIw0WqtZFbcHDgjBb8VrR+e\\\/fiJ88uv78qQg9kQCJovnWt+daHwFjcmEg7CpgmN5q8ALRccFpWqRy1PUaQz2XCh3Wa09DMtIRXZlmhRGIZuJDCJM07N\\\/DN1rSZ6G3tNcmMADNzyqzwJ0MPI9MSxLxgEvq30jGbRNmdxoXIkblKRdXDYK9GUajLSFSyZziGJg4REEi1dN4BcmA2cAu5XRooTpybePubD5RSQAIQcGgYwgj2d4hz6+IJGCzKkBMiRJFlAW0cqlACs5DjyLm5qNlobVdJhtpBi8iIMVISVZCUGGTYdjYg0y5+UlJG0f7C2sCJuYlnHzu4viBZAawSTs3A5vLg2WMkZHVhUUVRolvCTOwk9UlAw36K3J5LSQZ4Z3X\\\/7d4e6UcCZBHzSqf0RVdavtM6rK3fXpg\\\/f3p3fa922NBVz9H9dWd0JGi2SLG0X4EckBcnRkrT49VrdykygslE\\\/Y+pttshtACRGk36\\\/vfQAhwdss8eZ3Yh2j6Q+1Wqd9jcSVAuVQU7PSuvrUKxYHGLFYvBTmv95vbLf\\\/Hq4MTgUadJJgBdJjGx6cjeaJcoB0dhUvu7KGJxLDv0psRRl84SUxkTYnaRqFhWozKdD8qz3qjnTTYeKTKdeiTCTAQzZlXAOZBh9pYRJ+lRbN3K32Cz8NiH7CuxBIQhOjpEVYZ6rIIyIiX7fUJm4hIyiWEm5oTzuUszkvH1vZDGRmCvDRRByaW1Va03iJY37JefpaqiB8ukQmtgiDoE9gAyGDk1RaHXXNkAKWKkAKNEvwpK9xo1dIeIkIRW\\\/JsAabSUhMbqrhBs4Pnngs2jytRkOFTBYTfa2u0sLVMUQRKb16fo6bl3ZHM8CD76f0l3lLp03h\\\/hIEvSDR6iq8mQukDpSVvrqL5+9\\\/70hbOS2c1qDQCDVlg4cQw3t0goMIcAAWPuQGno9NzBtbsqjBIZPiaZUowDc4DZBADSN2k7ACSY3I1ktWNNjrOpITZSVj5Thw04NkcB8VJJjYzzs9NciWBxT0Vqf78oXWfqwtTR2lYK54wbaAiZmF4zXNLnsE4QkhBHbZtWZbobImYxa8mEO7sK8m9XmsFJGQMwEfXCMrCr5U2JSz\\\/RGCZHLULOEkg5HnP85mnBfSwrw4c+LPkbIXbNbIwdyahqY11LP4Zy4sRSiMYUnLaymGUXC8++evFw8bC1tAcooVzAizNWvaEcLr0I3IIqDlNblqeH2geDFPlW0ZHSxsrEwHwZlHd0c1lpPwTjRpGnHaoIQCzRMkHmtQNAJEmEgDZym+xSu4FDbfbpv\\\/OluSszB9uNH\\\/3rH9S37xhot9aHxK1QTDxD2KVyR8wfhj2i20SplUhVycr3QjMlTB\\\/wQdda7Q9FyM6ZSOmkLuk1Wxt37o1duii57VVrSihu8eL0ZHvnIPlsls2HT83X7m8IX3TbargNaBO3NVE\\\/nhOydLClJMhA7OzAzjYWC+zCRWt+VB36MD02cWp6+Ox8q1jc\\\/t4dKNo4abeHKqu3D658+uzyG7eCkJPmICgTUkL6TCbSrztlPAWZ\\\/BbEmNaeycDJzhX1U571UeRU0WlKRy7oIU8Lzl4l0ni7eEWlhzBl57E9TnnKWOoxpaSlBsgupRX0Pdszmy7mJ35IRBERSZG+LywDZDN5jyxhjZNOJtXfhRkP2kPTI1Onx979rfeodQheA2QLOlWw0b1wSh4Oi04kOyJa3Bn41FR4+XT4Tscemwz3guFzg4y3Dt+9F2PdIQ0CMKwunUzIjfLTbK9opBqMmM3BsaBYgYFxHJksnZife\\\/yYO+BOFyxXz58MGS2zfrt12P3ACtizBKl3EEgxbS2lh2TmE4h9ClrMZfRgP98TplG0RlJD2mSjIGz7+7cWxq5esmze3D9q3l+vHJvu1CNSCpRCkONXTvpHR8FRI2k4oU4atV0CBiKIlx+k6HhkqbVZVwvMJsHlwpYqt\\\/nU2MTFuU99\\\/vzEaGF\\\/1\\\/va+lHj\\\/apaOZLr7fud6OrffnryzPR6s4VSmDkhKWSIrkuKSMpY35wxxaHh1QOzdVHIMpAIhFyuX9Yci1lRfbIUMRu7kivmMZe6TZnJuVZBYQaqmHnvMJ+mFH95zDEHM\\\/GUGb9ptjXalYiW87JgwixO\\\/CUIyISu\\\/ZDpfMsYmAD5kxCBceQWMltJAhJAANwC7qJlz1+Z6YTRxt018hogPRibpNXFSEZie9caGy6fnI12a0EAtQ9WrM8+U+4EoydnS4Ujjo2Nt1cIOTCVBC0hA+OEYczIfm2bOzZzCsgRSDCLsUKBDwzhUFkWi5EXRp0AIouCTmd772i3Oeaww5Xd+uoaRR6YxyNvC8xRKDB9m\\\/u1YjDzvufe7WREkdxeEPK2id6GD\\\/XmZBNkI4DTKjk26lOSLKRACRH41VuLI5fOYbHQOvCssWmrhpHnoQiGzk+HgV9fPyAdD8ot4CVCjjICKQEi3ZWOPfdoYkNIB2YS2ha4BbAYMAcHBuxjs2x6whkemRkqjBctBChVLD5UYPMzJEBJ6+DAOzzwLrx0YWthX0gZS1LY6FOXjn3xpaM7G1vffhuiMO5eJOlCQPo5swvMdmJpJXbdpikjuomPnu7hBaY5SZTuXJCTHGZL2h40dHIqUZyyRlk5WbLiTbWJveqN7LaQafHlskGxa\\\/dFyMtiTTwPWoxx17UKRbIYShH3tro2f2SOM\\\/Xp50qnZtf+4Cf+5iaA0nmMpZHhRz535e3X11oRQqEArAC7e6AIgBGwqBGKO+tW0bKmZ3Fo7Ayoz\\\/\\\/9z5U4f+OHGz\\\/+8o8kFgAD5ICOjYyIBCOBjmWNjvDJUas4iDKUnof1lmo0pJCkUASS2g21c6SEMgMgHTtZHm6PTXz7H399cMBqbGy31lch0DBlhTlMMnXfL9KgtG5\\\/JmZdNV0jjKQGzMj7Hzqmz3d++q1PyGjZkgCRVHlJBihMUgR+9d7K8JXzxdH50B0qPTLRXFgvjDhUcmpL98kaAAzBAYPWkoKQoWZUAyBjHJFx4BbjAwOFsUFrZAQcLkMhOn4QRFEkpQAACFe34MGmh+6dgbEhFZx++sS9e\\\/vt9xflQQM4xyKPhq33H9Q+8cLJmz94sHPX13FZVsGe\\\/YVPDl49W3nkzPgTJ5kMLSNQyRDkESSiQsYsNnputrm5m+2GYLbSSPdAQsyJcLKxf5gRO2F359NUkNnOCvUWnPFf62rQYYIcyWsw8pNgzDb6ssFpGVIbYo7dCNl0NcMdHStf+BuvhmHISTEiJFLKfNn47qmR\\\/UV+8oRw7PFIbfzL\\\/wRgoV3EQuXY1ZMDw+UH722pSECxYqCptgUkwS7AzHGqDEVFB0vl8szUx185e3J+kANefGLqvfWLcDhKzSZ4LXW4D74PUQBCkZRRtRnuVCEMSSoiBmFAIBKyY0z4ZIAcGUMFBBbOjYh712u+rBFR2DKGKSUghhtgxuOqdz1zWUHoriby9Sc+VDqTGdYnGo98lBzCwy+GXYdm1x\\\/n9d7xm0wKSCohjh7sFh+fLh6bvnhprnN1fr0a7L73QA0dA1aE1iEGDQbEygVnoGgPVgpFR1\\\/RQJJLpJT0QiGEjDqhX9+KvEC02jIQQGRkU4wZEoxd8Dz\\\/x7\\\/9vXf+eMD3QjgxSn6NDj1gFtjO6n61cXHozNPzu\\\/e3yXJBSaXA2zkaOh827m1sfuU1EpHGxqNSZokwrR1lxC2rUDz2i0VSRFmAZ2YFxjTr3IUwOXUI+iCUKONX6I3sjNcWdqWRpkFPfUFMOQ9EZuCSVUdQV1e1qyWTYhoNKzXb+kVQpPyGt\\\/anH3oND6UxTyWp7qlDiHFnaHj6iyWYGvaXtxGBmA12sTA0\\\/PynL+7drTY2DqHTgYIDu6sQtkB4UCjgzBRVBiCUIHz0oTBBFcdyAIjIa0Xh6n60tU2He9Q4Aq9hgEOadaRiyzKzwClpmFjmXh2bwvQeVXRgbobWVqDRNLwSFYGIQIaoMg6JfAR9XgSaL0MeLlXL9E3NJ7HS1xv7Gcq6UBfY78Le1c5OTmfzvGB2jqF9k6RUsHrw4l946YVPnvIU\\\/OEfLx\\\/4yGoHrOm40YBFIY8CIiWJUIqo0\\\/ZrjU6tLTqeVggTQjZPNx3CkQAVURInSIKUEFGnVa8CEBxu2RePK4Gy5gOD4NC798ba4y+dvv39e0drDUKgSKz++28d\\\/PhDb2svOKglDdJEcJgimS3XKlfa1cuUlZXqzniS7Z7epeNJeG6tIPYpa7KoNuy6miHmpEyYs5NkTf2YP5WhRwSFPY9FX9Ma5kWvGGvPUi27thdb3OahaC5sNg8bICIQIUiZweDHjSvGkRfqS1vWUKmzvAXANJ391OXZY2dGf\\\/N\\\/\\\/4lo1cFh0D4E0QEZQrECA6N0UIetI14olOdG+ZAtDne\\\/8xUpPnfOP2y\\\/986m0252Dg7Ia4EIYneFSrZ7AwUtDsLwMdq6iUqm9PAUlqDALcKxY7C\\\/B4cHiMmbTobSQhK7s9Cp\\\/8QuN1DvevcIMqT2nF6G+gm4H+rufOjMCPt8M9ibcRNbrEkBSUQYqTgSwbFwbMzhXiPc3ZHbG1GrDkGLooCiSO9ACLGqA7KIgH6HNLLUgIscSKFSJHxASx+S4vYyv3QaVYeaAVnuvR8vP\\\/7CyYsvnXpzbZ2QEUVRrVY7OtSa6QRtHMsJtJuWg3IAOUVB1AncwTLaDkUWMAZSe2Ew\\\/+ND0jQxG0UfqFYXFyNz2+96xTMhEXo+SDnfbWYfzqZAdck3khgyYpj6RRCzSoCEaZHOSOPlB0igg\\\/i4brQUhyoWMFJkBJ8iNLH1pmFkNgVCRlwE22GwgwQMuAOM26595aUTD5YPd5c2QQbALWjXgHEYGgdmQ73GSgVndqw0MQKeV792R\\\/LizY2ZtWqndWtNerWxk0MDU5XWUlWZW71+hbRnwDiMqH0IUqAMUognQmLIQteB+eN6BRqzQTJcTbSvqQ+Leh0MidUEHy4ZpYTvjQa1nVUWWz3D\\\/ofIB3vmgtnIy7wkADNMDMr3dSgx0apO\\\/fqPbg9PDahI3v2jd7zbS9SuglfHyKPIBxVhLMOnWNnYtcqzXQvzn2QsTuY\\\/9HYo0XD4mA1E4s4Sv3BCiZD8llfl139y\\\/9xLZ67\\\/6QetPd\\\/E+iYUYCUxq9s0j5MFyEBJRayx17z88vnC6JAXtE3ODGmfkf4GelKNspqTnNIIs3RO7MdBo65rPuTZ34iZfixmQgXpI3Q8afgBpQHQWYFqSjpK+qExiTzxTCCjkdnhxn4r9EKQkcZvo5SkFKY\\\/K6KWy+gnm9vAHGAMkZ1+7Nj8lenf\\\/413ZRCxyQk63IdimYoD2G5a5Nlzo87xGX\\\/joHbjnooEIALzrMJQZ3stPNyAqH1wbXPwxNTIhfna7QUpM7hxopwBL6YGpy84MgBE28EL52h1gw4P9V6f2LkMDY2IEospdOEgc3znLAYGsJsxh1147VQ9RQTA0Z3L9zp7y87euQV2YzAw+yUQuotbTPbDuJJBUrSzvH\\\/z9YUPvnPtYHGZmnvgHUHYRuGh0oV4nPYKCkkhEMaDp3gIq2JuHGVYShnzGkFG+xLz56SiWgOPT0AgKVT1vfapF06Xh8qbt7ZAaVmz8dchZamYGRCQZhxw1ykVrn726saNlcZWFZREJZI0sUxqpG4sMZNKi8lYJX6akSX93uSfmHyUhgBnwLgmIBEYMP3XWCz+Tj4biz3xacp3+p9M\\\/475a2T+AqPkN7H7W8L0G2OUpik6aBfBLlQmR5\\\/7y5+89u2bW7eXIdSWeQFKolJJ\\\/h6amJP4xzfkyIJTKv3Ur7x4uO+\\\/8901QokWACgol61ObfjYwPDZGW+32llYjqqHJExqHZJypobC+8vk1SHySIRB9QhlOHbmWHBUk0LGhXpme0JEu4DCjx9QrWVn6Lhw9hysrlLtUNeu8WMGKf8g08vMNsEzTcf4uO\\\/xCvZspIm4N1ONIACClZ2uY16XC32UqXlWysOaN\\\/mrPWEmE8SkZ\\\/tAJEVQa1URgJSM3TdRYvTW\\\/aikbqa8CiVRHWTmZbq9xAxhSTML47oNlIngBVLgCbq\\\/wq6cp13RqXmLP1h8\\\/meu3vvB4uHaFsgQMAIQmZ0049EFIiVRcZICIn9\\\/aXO\\\/3jn9qatb1x5IGcdrSQlKUGqlyyWD0UfW9tnuP\\\/YMPPKj4O5bet7pmc8+w774xbwy52FlPqYPUGzYNaRGckrolk6\\\/cMkeKa\\\/d2TQ6FZl0ZTIaLmO\\\/Stg0DLiFlnXuhfODZyb\\\/6J+\\\/K5HB1ARJHy1ZGEB3bDzY2Kvfvi8D33R19E7EbGY5tgi82oHOI9FforPTUp3W9KXTh8ubrXrTZI0CoRbxEJCUGGtWzc5YKOK5M7S2TvU6xC2cRE\\\/RrfAkyAKzsZc5kZ1M9GRRPez6lNzTORZm+xvqH4IT6XOf+ajCNdPcTk9DvSAlKF29aBhJiDLKohaywHB4uJI\\\/31PIlHyZFAaEHl6fAmp08MQ0kNXe65x4ZHby+OjytU0VhSZkM44fzpzjZtysfb1oO3T1HBTsS8+dDRt+dXmXEmp6st+bmi3\\\/yxwmDE0vN8nfMn8Bufl9NPWepfmWce\\\/X\\\/NKzmvQzIEfO0XxU9gvpT24Bt+JPwpKPQmZlP7P+WnGOKgf9nXAr\\\/k5s4DZwB6wCOCUsVuYeOf3yX\\\/3U\\\/Vs79xfr6ugQQtPQz55C8XGqrRIcmYW2C265PDb+6q99bPHO4d2lNoyWaXIUR1weNNXWVufuUnhUoyiAhEWkdYKMl6dHmIqCgwNjtoxRGsIPw1Zn6sy8kirseBkJJyIgWC4ogRrfDAjFIp44STu7+h6oKx3MXsfz3u54VGCeB8j3xvr4aLoa0zltf\\\/ovcWSzvhP2zhq6gymwfxsGe26nXcjm7sWhNwwJCogUMImxuibXh4QcrLKPKJ2S2GaEZBBHSbpsRqOQbm2xdUj\\\/hmLQ6dD9ZX7hbLum3v76nc\\\/+2nOzF2fX3m8Tj3SWlRk\\\/JFIGjMH1lstKA85zV2TBufeb3x31XnrhL72sovDBD24KzzMIIyVif3DPrZlyBHnqgZRm0rXzQsJsgdE1ssUU1Is5tFl+18KHzapyv530wExv06S4GaMZcActlxUKM1dOfepvvFrfqb\\\/7G9+QPmGxDJ0GQQCMp99tJuc43hFssIu8NPTE5x9zKsXbd3fKv3DVHinWvnNXfLAiFhfJO4IoQQoYnIwp5JQYPjFVW1jW4mxMnJlAAMxvtTfuLM+cPV5wsLq5K3VnzjgqELhNRIgKbJsdn6eDfaoeIGj+b6+YIm1dZlV+0LUC6b+gp4nd2OReV6YFfRn48dWm58sCfgSxJquf6VqFMco0LhwloEJiqSc9noenVmXK3aMzUI6uyXNSX8U1dCIJ65IJGZwUACDKiIBBq6XuLcOli+trRws3tp\\\/\\\/5WcP1g7auxGoiEBhtjrRxw5yYAyL5cqnnwHOm3\\\/6GqH15r\\\/7sfLDF3\\\/t8zMXTtz8+jv1rarw2iQioDiPBXLM8zztP5dXkRFkdlU0WTkbdnVbcnpUo1PL8mfR9I0pl9ubnz\\\/mPqmJcU7McowB4+bGyCzLLVTGBs+\\\/eP6JP\\\/fCzoO97\\\/+zb7R3qyAC4BYWB1AJkmGco0I6dDXOWLWAOWQV0ClPnpz+2OcvfvOb60dSVUZc6fvq7eu0tgZBG7QGNeuciaEynIPyw85ePD3SJpI4uoVIRp7cvL04c+HE1KWh3cV1KRSRQBkBQ+A2AIBl4ekTan2Vaoc6iQTj5mfXaZPSsjMdeTIZIIB95uaZN5HoI8cLlFe1IeLwM9Btisso5XvOWOqyp2bvL9ini0O5bg0m7fWkeQ7pPh5Pjql76A\\\/pgsAk0Rl6BQmQfHuYtiOTKTMY4TUwRowDd4C7YJdheJxfvTyI9s\\\/9rRdvfOPW+195W3XqJupVaSU+A9tBp8BcBwCKn3+JvKjznbcobAMpYNwuFo8\\\/deGlv\\\/zx0miltX20sbhb29hnJBljoOPblWIm7Y0kKb2Da04UAJIOw9GBoVqiGkdXKC03yb1hcXiZ6QODIiKlA0Q1\\\/06BUrprQ7GPH5ElSza+KWnFHyJDZvr6yYFDyfhZaaEStxAZAhZHB4+dmxo9NoKMvf2Vd299+4POwRFEHT2WQLsIg5MAyrIQAKNmC6IQVKTVTsBsslxwy4WxiS\\\/93VcrM8O\\\/9xsftqVi\\\/hHUq3L7EGrboCKQgYFuUDKFIUAE7hRHB0vTM4d3HpAyR2UaOq0ZTciBW8x2huZnhuZm1z+4L0MfRIiOQ8JHp4AXL9DCXaodauYqZuJys1CPRHSZ4OtjeWDXJIC61lbP8RirCnN9Gur6p9UPHJLrx+QWdjp\\\/oMx3RAj9Insw2wuA3qg+zHeRksEJ9lPKZVQn2Ns6oO49gLKyLOoSn2s3OkQADNCHVl3dX2k8\\\/fSbP157\\\/otXdxa2N68FZCiDCEDALVasTH3q2ZEXrgZBdPDOzeaP3ie\\\/ZTIiESMZLr9+Y29hY\\\/bK6WOXj42fmznx5KlCwSkMlTr7zUBKAWQX7OLIQLTXFFIqJXXAC0uvrcbNpIAE4uDkoApV1PSVksoIwLTPHLXXQ1DM3Sg4UC5Ghy0TFaRfBGVWFzBmjQ34hy0II33N5pkZSEyzRYtzPjXU2W9CJFhMbdCqX6kAGNoWtyYHg4OG3wqP1vbv\\\/ujOxq21o8195Xcg8kFf3vRuIsT0S49f+NknZShv\\\/D9\\\/Wr+3AoJMZKfloF1Ep3DxhdMzp0b+4B\\\/\\\/pH0gyKvK\\\/U0IGsA5kADRgawRKX1qGaiwND7oV\\\/dJRQbazWxtR4qdEyaPSElV29wPQyo\\\/frm9sCq9iCxEZeP8KXqwSq1WWo9QkprU5YWnLFsCU\\\/wZPbyKT2ZSub4OPtRuRHnuKPbqQrG\\\/Kzi52mHm6KW+oETKlpI98T7pBBPhYQapzM6U2hep7wUVu41Xugjq8h\\\/rF16ZIlshYASSQeTBYVXdeXB\\\/5Mm5hnzsF5463DzwDhSRAuETKUBuDw\\\/O\\\/vSLhfFxX4rqN14jr40iIJ1HiwBKkhKt3XDxsL74+i27XHEK9uCZqdnPPnn3X3+foggQhs\\\/Pnvm55z785z8U7ToEHZBRrB8infdEaGkgJ7OtF3\\\/tMwcrjYUf3FFhGyLf5PvEu6IWDREi2tbQpfmx564u\\\/+6PVNAGKdDESykAAsu2BioX\\\/+rHl7\\\/8Zmt9z8C2uyQBjKPt2iMjl\\\/\\\/WK4u\\\/\\\/UZ780CfQiYzQx+knDsD5WO\\\/9onNf\\\/eTztZh1GyB3yIZkIx0GjsoCUrHtSuGYv7Fs0OXTyhF40+ea9xbJcYRlA7SwUJ5+uKJ5770+Btfu71dC6hRhcMtiNoQ+eBHUBmDoKmBIwiZnDZkgIxZfOT4zNqP3wUVYIruTtpglqGqI0fOiaCzd8AlFc+dc47NOuODzYWVzvW71G7HeRWUhW72MekhZBGsaThF38tftvykj1S\\\/UK\\\/9N\\\/UTYs+nxT4tgawngzBTafYuYMzpPhLjE3Udj73feuoxwJxqv8u78dABZrYvlauvDQLHMBoBEJQEiFAEFHVgZyt6x3p3wP30J+ce\\\/Zmn3vny20oRkQIZAoAKQ++oVZwYszxPtTsgIxBRPBg0\\\/DLSiEvuhFEQcs5G3I7Xae\\\/vkwKw3EJHyihqH+yLRtxFJMIkx4oxk9NkuaxYQaFku9Pe31WBBh8ap5V5JfQ8kHGwHHduLPL8zv6e8pqgpBlCaoCK5VjBqIgiv17r7O\\\/p+WcqrzFf1wLbtZWUYRjUa+39PRAhKAkmDpgAECxHiEgK6TU74dERhS2IfJBam5ajBpOSFPmdgxoBUST8g4Zm52h9DDql8vjYi7\\\/49P5u+8P3tmV9H\\\/ZWQHogQlN\\\/ejUoDEL7ADJxsYk4zh2otDd3RKdj+G6ZdFddRqAKARlyV\\\/eBidsissK2PPEzL7ujg4cz46vvXEfUehroZQNlNdbUw9rRSbm5ZJEufsifoS7D3jB1zJajXTBRwt7xVR+9cELhfogJOIHBm4ZJ11USM2bTrEcm87ew58qXlgapb7ELOpX9KOzC5mBu7gcKTI5iCFEHggasr9XfHbo+7n7sk5cOHtQevH7HjCsAokZ75be+1nnpkdbieuvBOpn8vRg6qIExSoGUxCXwCC2HkXIISCmSEpmFJFEI8BsQtCDyzemReEz1cJxzAAIq24AYBeS3INT83ywdHEyX0nKAMSSiMKSgHS\\\/sDKhbSdB0MBmB0EWjzGuNkZgFJCAs21Jh6IFZ8xnyHzJQRaCyi8CkgLCDYRtESDKlbppKDQlIURQufeXHou0FTX\\\/37buEzGgGrSIrFJ\\\/46Ucqc8N\\\/\\\/NsfhJ0m7K5A1ALhmy4oEXgNGJoEuwxRK8GC6a0CmGUPVNrVGpnNKAeX17reGI+giCSijYhYKtoTIyoQTCrmBQikTPBG\\\/MhhP4Ag9bUtYLfZrNuf23VLJOjmp0HfFWvwFt1sPsLeW1ffWQh2qa96v3NKIlJyiJQu\\\/37OupMhBHSXoqlQLp389WiQkfIFKuXl6Zid72j2lkIQAYgAmE\\\/37i0rNle8+tIvP7G3tN3c8IgkyAgUeSvrq0vLQJJEBFK3BLLptrHZXQ9aEEEpTojASEbEQiUCEpGOFtImSTJjjPh7YgzIAi5ARUgKRQAyBClAivgxVYmBj7RKhgikVCICRaCUKQ61a0yXZ3q1KBWLgaJUjG6CZRRwC0iSDq4QEURhTFvU11ALmAVKcSIQAYkAhDBWibiiM2Rr\\\/fNL4e3sLfzON4FxYhYAQ8sBuwRO4eQL5898\\\/PxPvrd6tLRJ60sgPJAhmJDteHzUqsLgNBz5QCKm5SMgQ87Y9Hj7ziJlIuAJTFspxYvoUsIp8pFRPjWhRkaD5bW1f\\\/77hfmJ5od3KN6niJKsqC5MC2HXksmMCh+O8HrICuy1wVD\\\/VWL1zN\\\/6fpWHjea7bBXU07ylrKUns2wy\\\/iZEyBOJKXvaYu\\\/ph10TesoxF3IflROgGFpM3hita0IlQATAXAg6Ynnp3a9alV++\\\/NyvvPijX\\\/9+cIQGFy8iczAqAUqmga3Z+D09AtE8OClVEtAnBWj2kZKgyIy5tGVb31YRgZg5vmSklFQJYocUSAkkkumdueyapB2BiT9eLzNQoPkRSoIUKma8m1BkSsKSEDkH4rqSlHpDUQSkUBoYKSED4DpVkCmJ8ZEVQ8ApA2aPX2wlIQpIa2I4kOWSXWDFobHTUx\\\/\\\/lRduLR4tf+tt2lyFoAlREG8KyixpRJAByg6NzkB1C0DGfArk5QKfGJHvBcnENr4rck0fMnI823JGRipXzgf1tr+9Gy0sg1JiS3Vu69RaH2VEht\\\/Xl5LUXT32mVH3DN8A+yDL+yxXeug5ZRmHTF7e1I2ceWilS93phpmeTX4+0fUj54w+2bUdVyBdXVeMBzSImYsf9TZpMHMjNXUCpglnmJMLmfpfKQBEJUkDbJqivcjf+Kr1mV99+slfePrd339XNIhUBBpHq5SpnbrepKxfSNdg3MY82izl9sYemfiyB8BYnOFO5nqZvAtaVKVUBlzLzNpWAklypttjMgn91FWZPgB1SChmGIfx9s9AMeDm7BakQb3x5dYk\\\/mVmPECYxqEmOLl83Bsl2XJozFjcRrswODny03\\\/9xeaRf\\\/t3fyw318BvQqRbU7EgJo2OIWoewugc2CUQHQAEzgHRrrj+wooyb6pRuiLjpmRFjrZdmZuqHJ\\\/xdqqND25FjWYK7dHzQKWQJJnhh8qoQQgpJ3noiXtIiE7ULXboZ7CGLhZwcvfp\\\/SgTa01Wd6HZfx7fg9x6GJQNHy6Xy6\\\/qLo1GPJ\\\/OzfcwjXqPsVyZYWPSgKFuPV3mJMQ4aTb30qQlcqw5VZT4JyybartHN+x3fzD96VcvhHX\\\/wz95V0Y+cGGe5jjtFru3QY0GdsAugFMizpWe2sWMDJ60JnUXXmXKKnO4mPGJQbulTugM9N64us0tFEHpcCggiZBRkMSiGYbxga+PMA0dznbJCUCRkjpKlDKC9VhpiCYnFZkRi+t3Ju439+Q0GXGfA1YR7GJhaOBjf\\\/Fxe8j54f\\\/67fbyBghfOy1AGdthBhYQQ1\\\/bdZiYh90Hpv9EipXccOfQEPf0bwLXyxwtXjhxzB4oYatdfe9m1GyRUaKp9Naj17leh6RSCzw9xJOeYSBkXq6HlKPYNQDsrVO7MrCzvlETEtqDtuz9\\\/Ji948FD8KYI\\\/ZuXfdnqaWkYgzep64jDHtlqYheMXyhEfR3PLM6ssy65sHVlQXdPV\\\/U6NFIPAlDALdU4XHlj5cOK9dQXLzcPG4s\\\/upXMZ5BUzH9VueoBEdBCu8SHxgYvzFfOTLX36iR9IAXEzEOsL3KZpiJk\\\/PLJpIszYMgyVW4qo828bAZtpPShqp+teA6TfBRLP223Fw4NwU9X0ZnvitK5PqGpH5AxcEpoF0CEpAQCIah8OmqCouP6AAR3wB4ceubnn5y6MPHNX\\\/\\\/hwf0NitompsaIQjXkPwMm0vtO6EGnAeVRCH1gnDvMGhvvLG0DIqCNBt2rrKLtTo\\\/zSll4fuvGPQp8IH2MK+h9x5PLJOXAu13zCezW21N2++5PqegdrvftpjwEJJpNZaK+q5B6NPZ9vizif4kXOMfgwJyZI+6cYGbqh5TxkuqnABNDTRLeZHw33MCzzbYd80IpqQNjlChBwrrNE7AUKQUgARUAgbBQeGp15f1vkWPD83\\\/pmaAdrX+wYlKLdFmoRPzKZhhQjKHlHHv12WO\\\/+HEvlA\\\/+j\\\/9AUpq1wOzYyaL61S4EacK7dsLqIlMbmlVX5ZOOeoz8RUHO8ZyehAryAKIMey12JRIQMaScgC472SFVGCkVRwYnrp719lsi9FFGhAqBgWGiIxqbL0PkxCzgLjhlq1x56guPXXr5zPf+9Y83371DQQPCDqg4FY+6aAJEKWROQvsQZi9CswZKOsenoDAEvAQQGUhpueCMDhbGB\\\/3d\\\/dbNeyoMTPpSQhw21+nsAkuUMZDP\\\/KE+sukeYsVHSG372eupe8PvPYkycjer63DIfa6ka0Lw0Wu1z6mI+LAPwT6\\\/n3HlGO82ZsZ9iXqY5fwBmBgCLF2lpJuxUnFStIrjFvRxQaBUbFPUs3JMEK5maSGADCBsAzKxLt7+TsGx3S\\\/+rVe+9k\\\/56rVFUBEoZRpmSmjaZa5BizhweobZjsNV+dRc\\\/cO7BAjIMcl7oR6ZRU7+brYbTCAM+VZ3vhBJDISpxpx6L6tEvc9P5lpM8ddkuS60kYiQXXSu\\\/NWfGpqfefRXx6Mg2PjqPhmQh0LgMUhMW42spBpnbuHJn7769Ocvff933nzwxl0KmxA0tNsQlSClEICIIbNIz\\\/1ylBsCAGjWYOokto7c2WPe5iENzqLftBwxeukEMFZfXD9cWafIo0TqnYa0ZCKHu11IBPleJuZ1Hd1NToKHX71yMtE\\\/Y0zf52hL3V5Wz8ruHayZoQL1dlER6KG6nC6ffmb0gNmyNjueT3LXMWNDYaTb92hsNWAMNXZq5+E2tx1mcWSxK1opGYYqiuJ7jjTdSBVpv1+Sb9zd6dIwfBHXLMiieu3t+5F92nr1v3vhq\\\/+nt30zNEe0wK77NgADQIr8nR+9Vzw2FjY6R2\\\/dJr1iiYCIhAIZGX5pLAPtDSrAuALMtrCo9+03vUFmjllmAUaAKm6LZK3buZFSXhxCMU81NpvHSYXpoNAtFMZHCFDatlUumMEdIgCjrE2WMeAW2AVwB6zS4KOfe\\\/SJLzzyk\\\/\\\/49p0fXFNeDcIWCD\\\/Oe4sR5gBxSZtAa7i5+FlFQMuenD714qXBycr1\\\/bvW\\\/JA7W8C9ncbKZrBfpdAHGVFsQEUigpiBkiJAKF9wUOYOkghGCPHhSwuzML3eg4qw6+aH6RGa32kzG33cC02+iPXRCBmCbAsDu0b92HspTSxF+YWdWclJnkaX87fLpx\\\/nFSeiJJ6Y2RzkDlqOVS6Nn50dOzM9fHyiPFRhjiWFFJFEzizLYkHYrrWP1vb3HuwerOxKP9DJuHFXwOzHOVuD6YuYCRgyRlYBRMu7OPbaB5vBCLz6N1\\\/+\\\/m\\\/wtetLhAygHeNvMbZIIRKRjA7euta4v0oSwloLRASAwG2pZKQ0wkSfXSw2amDPC4sqMSFnFfNJrUNxA5ZZuoYGywFmAwbUcxXPVFUYd6gQ05aDVqQyru9yestLJzgKSEXN9tJ\\\/fmPqY1fae4e7r11DreTUZTASmfGAFa\\\/Ail0Zfuyzjz7\\\/84+99uV3bnzrXdnSwATfjHaUIspfUDGJqSJgNjCbuAO2y4oDLz498+m\\\/\\\/FRENDQ28Mb3FoP7S3J9hcKmTlbErNswvv\\\/3o1bH5r2cRYmyKbX40Udcn5Mu5yTErPXuITrp\\\/GmHmVv+R4CeMDca6cZkdLU5u0vLPuqZfj9tTysWk3Z4vAKNCc0BywW7iHaRu4Xx09PHnjg7eHyyUHTb1dbB4s767j2v0QlanowEt1mhUqiMDg7Ojs6cmz39zAXPC3fub6+9\\\/6C+uq0CT4\\\/mSXLtcDCILozXkp4KoASlUAo62FV\\\/8p12I3yrPFz60qUv\\\/I2X\\\/\\\/S3nKV37sTyeIYSiUQqclcCAhlu+5QqyDiQsAoWc13uuNILTGhFHugSk3IQACQRJQkZmN2LKYPG0HU4gsXRconbwDgoFvdvcoSG5DPnYxIwCWnhWvAd73oYO81JCfI6q19\\\/ff07b1EUKKFAKjPhwFiawyzkDtgFckpOZeCxV69+7M8\\\/8drvv3\\\/z2x\\\/I1hEETYh8UiKOBFbxKIO61VFmOq8QlJannzg3WijbFYDjo8UfPtgRh3sgApAi9u9LNBCG7IWYsoOHPo3KvDQMoW8k2cMFLvgRg4mH\\\/sHDxvt6rVi9yhvIWwIx\\\/X4x07aN5dXpYJC66RM5OXU+5Af7NT\\\/zTBpATnG1CZaLTomXBibOzZ976cr4qZmte1vXvvx6c2NfhBHJCKQgzdsiw9Pa1a4Wp+BUKmPnjp392Pn5\\\/\\\/bVjesrq6\\\/frq\\\/uqqBjjkRj5weTbJF0dglMPH19n96vg1P2RsLvf7X8yheOf+KvPc9stfTGXcksCD1CBImghLFbpbBbQGDEGDAsDJfnf+5j7tnjkx9\\\/YvNrP0n7L+mLEvd1TQdB+yrjDrC5epIG1WDcmuJFd\\\/D07ODUiDtc9tq17LQAs6swZ\\\/BNrt66+ckRuenJIwLnyK2kVgcgVFKjXKSPueIq3Qs4cgfsIhQGC0NDT37+kSc\\\/\\\/8hrX373va+\\\/Kzv6DAxIRkjm2pbcyjAH7MeUahPnN0ohrr+2NHZyzEW8+fqqCtooA1IRxqqXzNNEsUKf8h68Prhl7Eqk7s+DgS5KWfegLZ\\\/3mR1V4Z91N8y3UTFdhP0VNRkOXrrTpEq0rMGKsqg9QuoS13QLR\\\/MXkzgjJ7b766ugOQAdsAvoFEoT44987pnjT5xd+NGdd37vjeCoRpFP2kwUm18yibg6+J4r4fl+a\\\/OouvXezcrU+OUvPfux\\\/\\\/6Ld797c+WH16JmEyIfzBLiwKz4XVPaggxuERp7wDjYRQCAetVjxe\\\/+fvDsT5\\\/45K+9XBos3f7ebdGsmxmJZJQYapLATdRtGOUMlgvHptBxh6+c2frGa7pVixl7pFY5YxwbJlWcOaGxTvEYBs3fRH1uj189fvEvfcp2Hf9Lz9\\\/5F3+gDBwtjVhLQhApn3VHhkDFkXFinAAiAuIcuAMsAIz0CNy0gpUAkPGrylKZBOPALWQ22EVwy8Wx8U\\\/90jPnXzjx7d9+49a33lN+XcekGddfugIhE19FmSxR1FFN5rlRkoL29e9fe3BtBRlrHbVU5wgiH1SUefX6NBzxYe196IdV\\\/ujeCXatXugKi8zQRTF7V+hrGvyISfzDylHTNaCHfoeUP5i72zndX64nELjHu5F7PoBZwGywXHBKrFiZOH\\\/82b\\\/y6er97T\\\/9n\\\/+guVvVhFnSEyeSqFTahTc3YIM2I6Gbflwxq7nWfvtf7g4en7n8516ae+xn3\\\/u3329t7RGzjIuUmPawgYrAdqlQhtoWaDyJDCFEUAhRGAUTb38fOoAv\\\/5Xnx6dHfvzlt8I6Qsi1sQCVICW1ntv8qIoAVWtt9\\\/5vfXPo8unNP\\\/y2CjxMki0S9o5Zb4ZlJgAlmcDGxG4fG3NjAQNRaXLELbiEOHTxOC8VVKeRy2GLzec95q\\\/Y3KzHCcxCxiVZUKyAXSQtowXjGI4zqTCjuY2BMeaOUEKnWJoYefXXPj53cvyrv\\\/7a\\\/TdvqaCptX7JtQ0z01pKS45sEYSpyJcUyQjCpiLR9BtACmRg9L3aZxhT7BNNfspszBTg0I1jwgQZbYYk1HfI0NVMpi5hZ08oPX3EJ+g7VEz3RCRIQU99z0zMVyDYLUnB7MgPMx30nnkEpWjojNEeYt1zPGUyIkB9BtpFvQLPvvToc7\\\/48evfuHbnm+94B1UIOiA8MPrmKKb9CEy1XclsTcbpn0L\\\/k2QU1Fr7d7fsSunKn3uxXe20awEiNz+Akqh8sF1wK9A+ApIpHZAAVAQqABHQi5cOzp9q3K8\\\/9\\\/zs6LHRndVa5BtvXmbwS9nBJwnZXto4fPtGuH8AIoyFbxmkBWPIOFo22AUslE8+f6l52N5f2kUpYidHmm1kylTLUYRjl04SyQdf\\\/nHt1oM4\\\/Vsll0ZWKI6\\\/dLV2bSWqNXQvKn5PmElQ4gWrXDnzX3167LHzA9PjtcWtqN4wvg2jGoxDb5J5LLPAcsBy0S6CU8HSyPSF45\\\/9O5+kkvOtf\\\/nj9Rsr5DcwbBt7hOZHapt8HtgJGSUhdqMWU3EsqMQCEsTUmZzOIaOgSspN7HLJUdYCkNmKHtaNhI\\\/Kr4beJYi9RWzfSR51Oc7Nf3MszPakTmtelwAAV\\\/5JREFU82I6osuWxXHR2PPzYI6PEm+XeeEMYnbp6mFgip\\\/IQso4cAecArhlVqpc+uwzl155\\\/O0vv7723oLstCDyQAQo4uWnxbhmycUTgyTpmghNvLiKuegKgEQoj5arIsRHfv6ZwFONnRopQCSUAbglcCrk1YEglmhxQAZWwQxIbBeLZQh49cbe+odrV14+ee6ZU42DoFH1475C79YIWpZNoWZyGrhlTLXPCE0sB5yiPTZ65pOPBQL3bm2o0AMZJr6BjD0EASCstXbfvr35vferH96jwIs5cXHKAndYoTT+wuXajZWo1kDzp+nsB5gFdqE0N3Pmr\\\/yUWy4VJoeaKzvNxRWzZ6V7teGdGjycVYDCALgVdMq8VL78yuWf\\\/tXnd4X1gy+\\\/f\\\/DebRU0IWhC5KVbJCkEBd0rMJnvYzbfDmNQKiRlqoo302QcRLn6MMt8wVywd\\\/oYd3NhehZVjmqBea\\\/8Q\\\/srfWVm2LUssidoz0De\\\/AtHd7a\\\/rbfbzpTPKce+izD3reTNQ5hZjJgKsgEzrFu90dpgu+CUsTh44aeeOfPS1bd\\\/98fbt+6T34rfWoFKYjY8Nf78lAR3UhePk3K1OhEpqm\\\/Vgrp\\\/9rOP+fVWe79BVhFtG6wCeHUEhlYBzK8iAAMZQtiGsA0ygr0aLWxSu90KcfPG5szpsaufuaCAH+60pMxkhkK++0DxXpCJ0shEO6A26bJi+fIvfWr2lavlU1PBYbO+sALCLC3s9ZgpGTVb4VGNQl9DU+JsYwTkYLu8ODD14uXajeXwqGGMsynVgiHjyB1eKk89fdEZGujUWzvfedfb2gEl4m54wg7mwCxiNnIX3ApWxnFoamBi5JkvPfrUF67cv7b9gy9fb1cbtLdmbPIy1OWJAQp2hR8CpiMoyBKHORoson4SspRkhMzdOEYw625WJn0oQ9fM2ta6DDf4EEtQP+98XpP8UbDPPi7gfLxq\\\/7ZLqh3NOuUf4uzoVuY8XKSGvX+p5+ePg2\\\/THwwpoXpaLrql2SfOnv\\\/EY+\\\/+hzd2765Q0MLQ048RkSSD09bPbnyQ5iqKDMMjG8pIBCRQEoUKSG68fk0xOvczz3U81hg9A0rQ69+EwrDZa8MWRG2QEpT2BzHgDkQ+Uo0iD\\\/xDiqYO\\\/PFv\\\/PpPHvvsped\\\/4bGp85PvfuXa0dquYjaEHRR6miwA4l2cVPp9UJaclgoVOLdGzswS46zIhi\\\/N4Z\\\/kLig5NYxK1OSpTCQWoiEgQ2axQskpFJhjQwx1yvC0AEiBDP396q1\\\/+dWJx08fLawfXbsLSsUPMcPE18s4MQe4TVYBnQovDx1\\\/4vQrP3OODznf\\\/70P7r++KMMQZADIUYQkwsyZnILf48YFppCODI9cU8CJcWCctBwq15GCuEukzGxQKSIBJAFj2wdpR2\\\/cJU2JcakmJvMKInQjYbJGiq6XvFtl2EeClBG3dLdvHnYCxxkX+TthahTqWz1Tl30B+9nec3l4SYpmho2f3jSSg0uPiw3a2QW3XJ6bfuGvf+HWN26uv3eXgiZEHqrIuHVMTcyQW3pqz2wXbQetAloF5A5YLuMO6tkGMzxcs22ax4ESmmVrt10YGjz\\\/sy9sNgpKKFhfBL8FYRuCJsggZyBKzliSQAJAQejDQEki276+vr1UvfyxM1c+daHVFM1DXyo9wk664tinqDCLITkJGTKukLtjw5Vj40G9vfLVt5tLGzqdK+nlxPkQ2as1ZnJi4vsbt5A7488\\\/NvFTz1GpVH\\\/\\\/LoUBKJFJ8kheDxlU64c37nsbO6BJjYmuliEgJ70t2iV0SuiWS+NjT3z+0S\\\/+148eNsQ3\\\/p\\\/31j94oNp18JsQeGC72KmDDIEEKBXfBSCNtDCTEdOIwvQCovtwjr5tgu2CXdRdcbSLaLlguWi5aDmo\\\/47RTlkpbTlTasXVflJmaFksYrYB2NOr6ne09RkS9lefddnFU0UK5ArwfqdVt2IG8\\\/fKnuES9g+o7PJPUG87FvNyGMzdydPeNwdmg12wK5Wn\\\/\\\/JnVt9ZXn3jJmkYRBz+agCYjAOzGGOOU6wMDEzOjM7Ojo6ODFRKBQASgjp+cHBQX1\\\/bre7XOp4nokAJQVKkAAgTcx9S5C9\\\/+8OhycHzo4XbX\\\/8eBV5qXMgE9IJWhwEHgHgUgQAMNpZhZlbZ7ub1ta\\\/8o\\\/r5j51++ZeeeOTjZ976yo2dO+uqYxPGvgEUiIyUQCUBMVbLUDakk6SAwLv\\\/B9\\\/f\\\/MkNktTZPSBljpRYWZO4t\\\/Qi15JUNELZhPyvZ45SWgPFiPPi\\\/CRzXdVI4FqYggCUNO77rsaEDl1CBsxG7pBdAKdslSonnjn99M89Wjw29rWvLCx8eBjsVMlrg9+CqA1KgjUIrgthU3usUq18aj4EgjRUwzThkBstFHeA22hZwG3mOJbjugPFganh8mjJLlhEFLTD9n6zvVvzWy3p+0oIc\\\/NMMhT0xNjoCTXYVgExUx9kiQ55\\\/UpP\\\/kDfgTv16GLSINcul+x\\\/6SgkYcxkB9QPqVrxIzXk1KPKpowQpF8qV4YOmqSXEOPAbbScmUfPFoaHH\\\/z2j6Wnr\\\/gmI80knluu7ZYuXjrx5DPnr1w+NndsfHCoxAAiLwyCMJQSEQtFt1h0hZD1o\\\/bW5sG1m2vvvn1vaWFVhEGsNiQgBTKCsB3V4M5X33zk73xpcLJcazvA9QSfAYg0706\\\/1MTNMJNxkBwgBADY2oSJKXLczu7u9T852ru\\\/9\\\/SXnvjS331l4a3VD75+\\\/XBli0xaWEAyRImk7fPM3A\\\/TqS8hKAUilO1Ga6mNCKAUyoiUIsq38szjaxl0KjIQmhMRAUkTzASEJA5eey8Skbe2K2p1iuvDbmOlZguketTkmNWFSQEslzul6YvzT\\\/301akrs\\\/eubd763upRxNXeNngN8JsQeaYX2vB13GoiV8ipKjMtAGKcjBrRiDHAKqLtMscdOjYxffX45IUZt+RSO1CRFEopUojIbW4BoG1Jm7Wrjf27m9vXlr39KkUh8RCiADEgDA0QBBBAArA0ZML497Af2D3\\\/m0QPb+J0o+8zFSw9DATz0cNIxKGn8pQWTIvmh4VZ5OCD2PWd5FCHicAfMxK6bMiW3hQZJz0VtMvFyYlP\\\/L\\\/+qxtfv7759k3yGvEilACIlmMVKpcfOffzX3r2uRfO79X9B4s7yw92Nreqewf1Zq0V+KEUgiEWSsXh0YGp8aHj82PnzkyfOjvtuvaPf3Trq199d\\\/HeigraJEMghciIW2AVsVA+9vnnx8\\\/M3fidH4rGEWjTjVECyNgIk8YAojm3LWI2cAecIszMQcuDkLA04IxNXvn8o4+9MG+R\\\/OAbN++9ttjaO6TQM8EbMlb9G3YtpfdlRGQs7RtrH5Z2hGRHsloqbRV4ZWj40Yt2pXL4we3w4ACEBzKI3QEsDqvg5oxNGzOYMmaSkCaIhQGMITJiFjAbrQI6xZFjExc\\\/efHyx8\\\/XdtvvfWdx9d0l5TVpbgiEhI1tCAIIdax0BDICpwJhB0Q7HsGnxXes\\\/I7nHDweBdsu2gW7MjDz6MljT512hsrtg0b13nZtabe1dSC8QMUTHURAxpyBYuXY+NiZmdlLc9y169vVtbfu7t9dl74HkR+ja8zsStOBMQWNmpc1USf1eCa6F2GWKJUV41Ifnm\\\/vbZD6XgWzX4WAEIafwuysM\\\/EudTOukfr9Zxwlma5MyvZLc9AX6GqKxnIQFm+HRXQrJ15+8uLPvvy9f\\\/SHYa0KfhOEbx4dbluFyqc+9czf+ztfrLX8P\\\/z6h9\\\/97vXDw7qSoZIRaY92XDHo9iAyhpxxyx2fGH31s09+4dVHLc7\\\/t3\\\/yx2\\\/+5H0ZdrShARknqwBupTA19cLf\\\/8Ubv\\\/Pawe0H4B9B0DLcsZiul0vqTNxJjBNawGxwXDh+Eg6agBydCkzNVmZnn3x8+MIrp5r7retfv7n6\\\/nL74IiiwIRVSJFPO8mEqORGUJr9rHnvZnKntUTolMdefPLsf\\\/8LzLK2vvn22r\\\/7Jnk1iDwyvJbEfmmE6XE2BsZvWbze4sqQ0OTP6Ekgc4qDU2Onnj712BceiUJx\\\/TsL915bCtttCloQtEB4cGIaGIOVXYhCiNogI1ARoAWFEWhtmSIi3tu1\\\/i7+KnH9aRfQKVrlytSF42c\\\/8UhhpLz84zurr98NG02lP1saNpoIPTX3yQJuoe2WxobOfOLKzOOnDpZ2l757rba6pfy2mWPJgJJ1aKIBYkqiEVJSP+8v5H+z5+7X6w4n6AL+drmiMEe4BspFOCkC4FiYy00He7Fk+LB2bB4umHPkQvcizF0tE3NAqj8EboNdsCqDj\\\/2FTz74ycLh4hqEHY0GRCBEhpZz\\\/tLZf\\\/gP\\\/uLS2uHf\\\/4d\\\/+Obr19u1Qxl2KPJMOaRFFTIEGaIKSQYgQ5KRlKLV7ty4uf7G28tPP336lVcuv\\\/XmQqPeyEwLEJDLCJ2RgcG5sYNbK+YWF7fX030Lu9R\\\/8bulx8rtFszOQKsFUQQdL1Swfm1raaMzfHr8qY+dPPvC2TBifluJiAjjMyq5FJk+Icu2i3tkLvGQXcuJuDv23CNDj58nRABefesOeW0z38NML9CATCDORUqu3yYjSUvPyHLAKaFTBqfMiuXhuZlHPvv4y3\\\/1xdGzEze+t\\\/iT3\\\/rJ5vUl0ToCvw5BE8IORAEcHoFr4\\\/gQVA9BqXgqKAA5gAKKMt97krKou98OWC44RXRLlemJx\\\/7cC+c\\\/fmXhh\\\/eu\\\/cfX9m8tiVaNAj0Qzr2tKENUIcgIVUQyBBlSFITN1t6t1c33V8aOjT\\\/5518UAht7LSWy0BroilIi\\\/AhHLD2kjdkXf\\\/uQIX7iWqKHDhUzDhdEAI6FmXzHrhveCV2AsoeGh3aBHrKsl4wkLe5tQjzoiUUYLjrFifMnz3\\\/6qWu\\\/\\\/1rUNIUoggQiYhwt5\\\/NfePHqYyf\\\/P\\\/\\\/L11bXdlXYgqgDSld3ESqBJJEkKqWjxjEJGDUXEtby1cFR8AtfeHxlef\\\/uwkYm1geRMULLr3vnvvjU\\\/rWVqNUGyhGBMI7OwswFJ\\\/lN884qCX4HZqag0QLhQxTC6JB\\\/0FrdbN\\\/\\\/7j0VRc998ZErn7pYnh5u16MwYmRyyOLYs3SFMN2XTFuy8cygq6OoBA6em1Oh2Prj19sLy6CCTCcpSbdOa\\\/74q1jIbbAc4DZYDloOWS7aRSwMOoOj46dmnvnFZz\\\/2l54dOzn23tfvvPm7b62\\\/\\\/yBq6hK9DWEboo4ZAyoJjSaSwNlJqB6akbp+GIpDEDTjbyIOOdVVtL7HOkXmlsfOH3\\\/pVz\\\/TOei89Zs\\\/2Lu9JDs1iNoQdUD4KAPQeDsdGqHHwsY5IYEEKoHmei+FH+4tbu\\\/d2zn7+ceHr5yqLe8JP8qrSbNOwuQR71VMY49ZCPu3SfsNxLuWQy55AfNitQyyk9LuaFfaBfWueaI\\\/y4jU7\\\/6IfTulkHH4xo8aJ+6MXjq5v3XkH9VBhCgFkUld1s7RYrkYIQoVkvQNZJp6reP5gpkAlAROwDnabqlcKFjMcd00uhkASIIUIEJ\\\/vy6CYOj0VGdnl4xos\\\/tNoFxFoWtyCaDMw99pwd4WjI3Dzj40I1ayFbnyCKqre7Wl1YWfLJx49PiFT5699OKZ6srR7deXV96779frFAmUunmbmMRlwubIBifkbE1KtJc3bv+j32U2D\\\/erJALN0UCGQIxyvRDIDcRZKlJF5gC3LYuXxoYvfOzC2edOVqYHdtZrP\\\/r965u3t9q7e8pvgQhA6HMpw0eL9cN0cABhACfnYGUFyNIMOJACuIsqMILEbPebu+CUmFs6\\\/vylCz\\\/91J1vXl97+55oNyA0qXLJ+w6gNHojw1oyuqs49BcTXI1SUXUxeOP\\\/PLrws88\\\/9zc\\\/\\\/8G\\\/\\\/V59ZTNtapoIXpn4x7JDwjQxHFLyVk7DnQv2i\\\/uk2Ot8yPVsqLtj+lBbVTInxIfkhGZ4gdB3wgJ9a85cAyDdMmKAWgyPAYZJ8itzyxc\\\/\\\/\\\/z29fXDxRUjjkna7sxCt\\\/zMs4+89PELY5ODS\\\/e3m\\\/U2MQtYfPNhLAu\\\/MHcGbiO3kFtou3Zx8Mknzv3tX31lZmbohz++e\\\/vOSnzbpES9CcArJyYL44MH11d0s1vL4ghUToaYF5xjLk6EIAiBMxgbhUYdRIQjo2ADtNvU6QQtb3dpf+nt5epmfXBy5PIrZ0997NzI8QnHLQaRJSUDywVmmwkYj3+lAaNxMnbyi0D5gWy19XwvPv942pLRn8fS3X8XrQLY+lcJ3RJ3KoMzU+deOPfYl5545peeOfbI3N796rt\\\/9OEHX72292AnrFepUwNdcQg\\\/o4NJNIDS1OGdNsgQJqchVCBjCJVTAenFb0qS71sAp8QKleMvXj7\\\/2adv\\\/P7bG+\\\/eU55Re+uuFeoRvJF9q6RBHSc3GxoVxSQhAJUckiKIDhb33KHy2VefbGwc+o1OIldMezNZkBNlwjofOlzAHp3ZQzqsGTZCn1iWnHEhd1JZPb\\\/VNW\\\/ojmf+CKDTR8w5sqCUnsaq0THalQobKO3cWKaMZZPiaCREbjEetsNjsxP\\\/8P\\\/3X3\\\/7mx9+8N6DldXdZr0OMiTQ8JiEXQY6lAgYHx4pnzpz7PkXH\\\/mZVx\\\/dqwe1TiRjspgZ3GsapxKgor0PV0585lHGUTKeihHyfu1cmZEYuBAI42FmdR8cDpPjVG2CjMAZxLFBOjwgRGDMC6z7bzSWbuwNHZs4\\\/pmLc6dnLjx1Unpyc3FvfWH3cG2\\\/tnUYNNpAAqQifUqTyvjHVcYoEMdQm9j6ZECUqWMZR8YpzfG1i0Ol4emRydOTx85PTp+dRIdtbDWuPWhv3N88euuBahxC4IHfhKCJUYd0NIXucGg8D0DucAYCRXB0BJLY9DTtt6jTAulBoQJWCVVESUg915aL8twT5x750rNv\\\/9ZP9m8tU9iKr\\\/SGgJj+sJlbN2W84XFGfCxUVslQVwIp2cGFr7\\\/DAJ75lU+\\\/9k\\\/\\\/uLMjKKGZpMok\\\/ZkUwsNdgPSQZziLG+mbQoFEvSrRrux5yulprL6Z6R9BlSLoGUbkANvUpYbtHhV2R8tg0isvz45z1wmP6sY3nYueA1ISSR3tNP\\\/x\\\/\\\/LNV1459ZnPXP3Fn392aePo1rXlzY3dWr3TbodhKAiAM+Y41kDZHR0pnTgx\\\/ehjJycnBrYOmr\\\/3x++\\\/f2P\\\/H\\\/6Dz0nTljRBiGhUnZKUbG3sW65jDZRl6BugWzbCgrrK0pgBj4AkjUNHR3Tt7MDxkzg1Snt7QAwmChC1NCmMlAUAEnntkDWuL9\\\/8nY2hE1MXPn5xZmrwmUdm7AI\\\/2m\\\/v3NmqrVYbe63WYTNotCM\\\/kFE8qMjgEhMnVOoqMGpMMElqnFuOZRfdwmBpYLw8PDU0enJs5sL04HAx6IS19cN3vnZj+dp6q+GrUoU6dWgcAkkolMFyUNqgbFRC97dAScqkpmlossn3099Dq0lSwtRxtDjVA\\\/DqUBiEqKXVeMA1DL80dmb+sS89d\\\/0P3tm\\\/vUxhGyIPIh9kZFZ4nFWW6xJnpEKUsz4l5QmZ1x8RREcF7N63r1uDlYu\\\/9Ikbv\\\/n1sCa0zE2nL8e1C+X5K3+WKRf\\\/y6Z\\\/mMVIPOzA6la0WdkvQbmJH\\\/YVquJHTx+zWPQsXzdTXFO3Sk+3DVh5tEKRlJHMbYSJ+0xDuSRtbOz+0\\\/\\\/7xvBg8cojp1555fJLT58sf+qyU3ItzhkQRyDEiDAUMmh6fqNz6+72P\\\/9X3753d7XpiWPH5+McP8o8UikiUfqhXSoUxoaDajXfHqOsrp6yL2Xsy9VtHiRhRlpbW3DuFLQlbK\\\/j3CVyLAglaKY9+oAOTLriJx\\\/g0Mght9\\\/8vTeZF7guGxovzz1+\\\/NhTxy++fK7IkSnlNYKD7frO0lF9+8hrelEQhoGMQqkipaSM8+GJMWScc9tyHMt2mO3yQqU4PD8+e250fHrArrgSIGoHtb3m3Z\\\/c37y+1jhoBh1PRcLgkg6lMeypCOsKlM4gUlAow+AEdhomtExEIKIMNSXZIwmQKIhgZ5NNTQEfp9oRuIOEDFRgbChOsTg89PjPPbv23vLGe\\\/cp9DKuX90oZIRgBC4aspO5ZVFv\\\/y8R9yQxs0rH+3iqY9\\\/7xvvP\\\/90vzL\\\/yxPI331TaEqWtGBq1k0M5YVYR+FADU57z3FMtUr+hBnUJXrAXR2MWYR5PneFApr76zL8S9neF5GxdmROwt5tD2Idgj1bJEV6ghIQEBU2U2Ch1JRKClNITUXiw1\\\/7hd\\\/df\\\/8E7xaJbGhislIulou3anDOMJAWR7HSCZrPd6XT8gKTenDgH4YEiRA2MAMzOGEgBKRIRV7JQcuq5QzvbZDPi63S2SpmygBQhoB5shgEtrsCls7BxCF4EVy7AtUUAAhIgBc7M09o2Tk+ACOm9N0mCAiZsuxNUtkP88EcLDlGpwAcGnaGpwYmT4yefmB785ClusZBhBIyMNItUHLjL9C9CjsSAOBEJVT\\\/0DtaqD968X9+tt+pep9nxm20ZRKAi0rN1lZgtFZBOehFEEhUBSSKJfh1qW+ZuiRaUxqA0CJ0j7ByBIhJSx6QaMr8SEHhqcw2n5nD6BNWa4A5C2ADGwCowy778+SelgNvfvKYCL7326ydNUwUyDGQz7s9QlDDTP8mAC\\\/WtIlEjChQBQSussmu\\\/+YPn\\\/7vP1Jd3qjcXSWrQnkRQ3RAMpB5KIP2ZB95HVKxA\\\/VxL1N+mAXn4L+aFZ30ibjO3oj6XTchp0jJJND13Tuqm9wEAMJtTKGI7aZpQl+WQS\\\/3QG2p9GEUy8puN2iEgR8wjP\\\/R4mpQWQILtIrfMBYolrM401cXIYqREIRhnSf57EhgQB5tSShgxvOuErZP5URUQRhAiPFjDc6ep3sSL83RnUz92bH4GCiVwB2n\\\/kHa2QUUwMASujcjIa+DCgZBSILYBDgAQGeojzrXsgmuXC9x1LMfiNuOOhZzpzVIpICFlIEUQRUEo\\\/DDygtAPRRiRlKAnPYlNVndiE9MzmWCcpGzLtkMACFUEERAQeEfQsJBbwBk4FeRO7DhpQxjodQvAaH8XZ07h1Cw1OmCXgDEoDIyenpm9Mv\\\/Gb\\\/1E+G2DPyQFCKjja7iFlgtopdU1ZWez+eopvVkRAoESerhvKPcyAkAKrcbq9tpbi+c\\\/+8xbD7ak1DPGGLEHEg3KOa\\\/26n6eP2o1IuVaqN3nJvaVv1HP1RCsHBW+e4CC6SFIee1nFh7X3QzKxCCl6zGNScuNQzDzahIxzG1N+vSjxP1DVKq4o+Mj1cMmMAkqNK2LbIeLCLJua\\\/N1OCBHqzhzbMIuOcrELWR+grTQYIyxbrF8Xi2ISYOaqGdgarrhZhMHBh2ftg7g7Bk6bMBAERoSR0bpiUdg44iWNiH0sFwEXiEiONgh3wPGMoxCA3RSiBJZmAgbUutW4gaixBcDyWzDLDCZJkzoUlwpo0GnLC8QUnmX6T0mHSDKNkdARCT0l28CEVgFKI6AMwBWCZT2lzBQRDsbOHMSxichikBE3B248nPPr9\\\/eqa7uxXprAUSIDLnN3NL02fnTj58Znx3hnGHKyEmvh9TDINei7NALNu5v3X\\\/vfnNvn4QPKorLDZ8i+\\\/63Pnz5f\\\/jS3BPn19+4RiLQgnhEBcB0RM\\\/DHEf9opb6jdv6kiywyzCcP3QybZk+LorMKZUO76l3QIiGcYy9y6\\\/\\\/9DC7GvP1REatF\\\/kBc23kLPZ6YgamahBm07PDf\\\/tvvvrP\\\/sV3lpfWhS8BAKR+NZPGiTKVBtOmeNTYT8ctXrxy6m\\\/+9U8OjpSU7rt2v0IIyNCywLGjSOWTGyh7B+oVImV3qLhdI5EAlCAZwMEBFCvwsSd4oYgPtuhzz6nDFuxWcaAAZNPmOvgemvYdoVDU7XzFlFqQM7Yy6t5usvdblVl+SSiAZn9QJiRY5fqcqekhVVqm+QxE+cE3AiCIAFp7KY\\\/PLmJ5DJhDkujwEE+Nw6ufh44\\\/uLo6MjN07Q\\\/eARGiiEAJ02hlllMZfPbzL7z6Kx8PpOzUO5i+1Bl1e1qJxhkG8fbvlJznf+6ZlXvbX\\\/2\\\/\\\/nj73jJFhCQMrFEEUbO5+e7imY9f2Xz\\\/now8EH4WNA796U8P02B3mw8fut6oTy5vZgbfTTW1ehLRIC6\\\/MBPb2zUziQ\\\/x7vzPLqFAHuebr1PzOTUEpLx6Bwsus20V8Nh9n5vTcADRDs8eH\\\/sX\\\/\\\/Mv\\\/c5X3n3tJx\\\/s7hy024GSESiVAhGMu5wjd5njVkr2zMzoy594\\\/Be++Ey9I4JOlOn2QALY1WIUbjuBVJ1aE0GSQWfHL0RS\\\/GTzIzLjgvTEN7wwBUyAQlA27NWcnaPJv\\\/ozGEQHC7vB\\\/V2KOrC7TZ2m0UbqQCUNINPbG2RNmInCIQkIwG5xL6bvGWTSKbJTteygjJJVlyoBKI1AjH\\\/2ZNH1oXclYuhE1EkIUQfqATEb7DI4A2DZOD0NIpydxZX31hrrO6T5y5pPiQy5\\\/dgnHv35v\\\/Gp9390\\\/09\\\/76127QhklBRjlMB4MGtSj60QyACZ7RROPnbqs\\\/\\\/tKz\\\/3P\\\/7iv\\\/97v1nf2aGIUNfVMiIRrb52Z+6xk5Xp8fpyC5gFjIFKLYeZHgdl8EAPvRkiZtKxMi7l7kEi5SVqlMWgdTO6rT9LAfPwMxm7TlikDJLyoYNH7G0kKZ3H0t49lEpyx5GMU3KIZaaojKi+0\\\/h\\\/\\\/6NvvPjC8S\\\/93BM\\\/+\\\/NP1g7bK8v7t26vLy\\\/vNmrNMFKgiIGyHV4ZGThzevaxK8fPnZqojJbrgfi3f\\\/DuvXsH\\\/9M\\\/+BxPCRQx2jOe+DtDFemFYbVmOtqUArmSYCPM9IGxqzOcq26UqUhJgpIlbg8XS34R7Ttv+G9+AF4Vwjaq0KQCxjFo2PUmZpPh0mBLTNOMM7PctHmmW0fZ+KFMJl42IDUGj8XoMVN4Eub28+TJ79peifpgFGLhCQkiARur8K3vOKPDs3\\\/+wvXf+IDC2F2pP5RZlbHRl\\\/7ci3eurf\\\/hr3\\\/L63QobKMMYrsmxrB4hT3dQ\\\/NNMO5x9+aPm4EI\\\/uL\\\/+HOXX3n8rT\\\/4nhIhEQFJUhHK0K+1DjcPT3386rXVDWKcgHUJTnLnUGI1TGse6uI6YQaTgr0Nz3RKQV2WJepPXSQLs1\\\/8IXEvmA3RQOzfKO5iiuZdvvlhdxoDGhdOCkgF+0fg+aWZibDRABbXXZSaepRSUqmNzb1\\\/9W9u\\\/fEf\\\/eDMmWNXHz392CPHn3n6FNgWAEghkQgYA86kosAL97eqP3rz3q3bGwv3t\\\/aPvPn5WUbEkUH6TmAahMDY+MU52QmlH8bfVRIglEkSIurrMOtizZv\\\/IElKoPD8B6udhRVFFNy6BUGdRBBTYWJxlvkqWYwBdlUhmCZ950AGyaLNrMOu8oag2wWQg3RjOhSnbI8gzw3M1uPZGiWjWMi1DhQFbbh7s3zltFJn6xt7hnxhhFCIyGZPz8ycnPxP3\\\/i+1zjSED3TOMGUWgKZhInEiWCOemTAI0ly\\\/dZqY7fx6MuX3v\\\/q60HYNuAvRA0L3r27ef6zjzPHkQFPsW7pyZcloFJPS\\\/NhN788pAIePlDPLAXqct5CJp8wvXqlOnPql+P0UVbefkAZSgAjiBlfOMSRGGhiMUFJGcjmQWPmydO1hRU0h6GM73gERJJUoKQQ7dDvrK3W1la2f\\\/T99wcGiyOjQwNDlUrJKRY4AkgFHV81Wn7j6Kh+WOt4UjEHGAKzlOiIODE39gemlDdkfOLKfH1tnzTxNkn5yaqdiHpegMwAgzCjKwRSBIxAhRQ1g5WFtf\\\/1XyKSqNfT2HqSoFQ+sgtjtHn8CmGX\\\/j+9JlDP25mk6vb4brIxCFm9BgGlBOK0GM3w4buSQrD72chszV19RdLNs2BkuhLsNsKmn0xEEBQBB2RuweWI7YZHSmDkg4wAOXIbGQKz8hMLSre\\\/uPlESoIUxKLIjzqNoFR2GedxLaCITGZ4c6PKi44zUPDaHJBTL27ko+QylGvSdl8d+5rpqe+yoIe4Da2H6rAzM3Xq7rHinxGO1qUqSaYQ+spPGROi6QQY+d\\\/BvfWzn3n6nltQURsZJ4WZQSpKglCRSTxWEpQQkTrab9YOqjoaIc4iZ4QWAZA212hAk11CtJSIQkXSbKJJSWMCZ9xKcXRudPW712K\\\/X14zkNmMsfs1yhGek+a0SUqQ2s4XiYOOnumnyPAkyDpeWClpV7e+YkVO\\\/tKfqV1zTQRzL8wsqD63CcpM19K2Uz9wQ16XQnkKRO+MKvfBSClpsjI1uP3+GgmRxoPqL4mAANKU3VzvfWjhiafOn3j2\\\/9\\\/Zl\\\/Zacl3X7VX33nff0COH7iZFcWzRHERziCSSJiXZim0hUmw6spUJSAzbMAIEQRAHyE9IkA9BYiROgnxwEngI7NixTSuQZQ2WJTu0RHGmJJJqstndHHruft39pjtUrXyoaZ999qm6zwQ\\\/sJvv3aGqzjl7r7X2WvcKssbSqzkPq3a2YCayc23rtS\\\/+1eTSevXXSlRXbS5VIvJ86\\\/z6tfPXlg+sbZ\\\/NpIkZbuAusmv0nf1+FW30RQyFJtxKQ56Q0TJzY3ctl4hgO\\\/R\\\/owzhtUULarqiflbK+fG55LP1Y++uPPX4nsP7r2xdqf2n22VwbWN7Zc\\\/yDYcPXF5fFw5rUz2SMwo1sQIZNMPpkNJlJMuy0QdvvRnj0dX1K5LPpE1EGWAwlOHo4F0376xvr594n\\\/lMONdjoOEwGK0IlxoTCw1Lirp7Z+naBgpRVLAkahsIu88xLuebza9QJ5H53Xp\\\/awea\\\/HiQClukotu0U1SAlcPXNreMDQPCu6W0qkmFbDxeOXLde8+8wFoTX5lfMKuOS7KtiElIcctDd973kw8XxAyVILya0meLjGUik2tbJ\\\/\\\/qu5OLF6sgyqKCk5oPUDKfLObFzs6105dXPnjk8hsnQudCduvTyAbmcM\\\/KGjSOtdWa7Uws4uYSDhNnmuP+5oE30algXKV07ga1mKYFcSrynbkUk80z58+8\\\/NZdn\\\/zwi++eZj6SYgbWmsli\\\/vILr\\\/\\\/s333iF3\\\/50\\\/\\\/pV79w9szFIstkPqh8dfV8jYAoSm5QBmW8QTbIiruO3vTLv\\\/hj779z\\\/pUXX5N8CtaxXtmQg6XBeHz0Mx9959k3phsbUszQpi5LEJ9m07aD3Eal3FNkbRnLXhpjN\\\/uOGDGqc6NUK15o1AAwkesM4kWaYtYsbJ2wRr2GGQrT1Ux2c8eCesyrzGi2qBZGWVpdXrl+72Rro+qB2zTPOlCpkCKv7YYhzOXZ3\\\/raK3\\\/8nAzGIhlrfwqGdttSzIvp9vali2Rjss46H4C1+TSlKMqiZrK+efD2I6chAc4rqHBph3EnqOYPVRHhdI\\\/0hNUpMbbOPEQTEkrPWNR1oFJQAWJNju0Vkpoe7aYoQikKFLkUM86n7zz7+mP\\\/7Gd+8OXnN96fymBWdk0iheSzN77\\\/9n\\\/71T\\\/6lX\\\/5uV\\\/7d7\\\/wu08\\\/+\\\/\\\/+4tULF65Od3YknxT5VFrqrxSzDGWwJNnSyji78bo9P\\\/rpR3\\\/6qUc5nf3af\\\/j9C+cvsjRmLxXVwyUZjg7efnjPDfvPvvJW7VCSs5la0NPZqkoHxRA87aNYF2x1fViAMCvOJrOya94bQeCOmUxRAx2Nlt7REzIkhlqfFRi9Fc3cTKojattYSPgGCrPNxgPJkE9moTZAdaTlOVcKaEhKMd28Nt3exmCpMsiRQgWJ149ePpV8R4q5YFAKfQpSp4WzdQYppMhnm9s3HD0UnPNtiBOhcSYGY3wW+Q1IPtrGkpGrN0wQY50QyTaodKhYkZDO18atusCUCN1BYpO0idnlY99WqI03I0mUHXY+vXz8vVMvv\\\/WBTz547A+uFpURSyGSs5gX0+0\\\/\\\/\\\/oLZ89c+dnPf\\\/yf\\\/Pwnfv7nHj1x6tKbb519\\\/djp9947t355YzKd5bmMhoOVleG+A2sf+OCNd995891HD33gluu2JfviF5792p8+e+rk+7WcrfJ3k9HyaO+ee37qseNfeXHr\\\/AXOJ5LPWeRo1mFVr2nDH5oAV7sg9QwAbVIrXZanRXQa0FzZVDRokphQdOplAsSyDep6EQn9lKNrNA+RAYO9bQNKGlUfGhThbJYX87zprMEakNP4XTlQ1vTeRU7MGoCrERLXoyS55DMppiAlq4OQ85YXRVNjV0wjWRRltnctimxVp00ULsOuWcL9K+l8IcmxJhMXk8odHSI0UKFLFRIUN0upNSALuTI94hQfslamXhpSsJjLfJJvbx7\\\/0rd\\\/5Ff+3oWXj196bcJ8KlkhLPGM2Xyy8d1XX3vzrXee\\\/sPbH3zorvvvv\\\/VHHj\\\/6qc88XAyyyWSeT2eSc2k4GC8POcim0\\\/mV81dOnjz\\\/tW++9sJzb5x88\\\/h8Nm1WIJAxG8loGaOVmx+5e++R61\\\/4718uZo0LU16nyrCRtgUPLmPAgnHSJFqMu+H94QnbHTFfUGcyOZjdRHuHkomY4khMqmr5qw866O40OICBUNMPg+sXpVoV1cRjawrSWCJI7XTSQBPIhoPltdHeNWDAbCSCKsqiRKry2WxrY765wdZ5pCb2Gax8s21kwwELWhLGdgOEbe09ZAWhJsZlMsgwNxjuOEZ5VYedXH0zQ0ICqZ80bx6sQ01vwLT8jQypnK0sUMxZTGW+vfnu2Xe\\\/8fI9T33suXOXJ+dLbVqp+it9OOdbVycvPX\\\/lpRdfG42G+6\\\/bf8Oh6264Ye\\\/a3pXl8WiQZXmeT7Yn61e2z124cuHMhc2NSS7CMkqN85KaIDIOlmRpGUsrB247cu+n\\\/8Z3f\\\/8vty9frpJDqUPtibjDZlvXaaoWYR4xqDsmapNmGpLV8P6dkLMSW9HUhQmInEGVoxR4CAb16Iy+tDsHoxEi1UIawoLt2HsxnUNkOB4Cg6ppM1HwlRdxVr39YHD\\\/5z5x799+lMS8EtwLUc\\\/uFjz9+qnnf\\\/V3puvTanKy5kyKKu+mULqGMlshAzBeXSo2t0PZUMzjhZujfYSdQhQ+tsNYIeMLXiAiGEa3MLkWJSKKwnnnDggHxkgqmHdAbYhQ5MjngimnOz\\\/40rcfvuvI0c9+7I3f++Z8W2QGmRHFtGLVOC8wFexMZoNz29fOv\\\/+eoNxSA3FLRbir0c\\\/6LMtKs1CM18YH9j\\\/4+Scv\\\/uC99154jfOdxsKoKY0qQE5Xm9QYqRaJqPXAJvUAWgONdvTf4J\\\/enWe\\\/c0G8AzL6bHHlFFaPXr9O1beQDl3cqMnaM7QZem+mU0lyujXZXt8YroxLgYR678oHp6gTSyUbsCiEcu39y5ffPldQ5vNcgCzLKMjL+GIp1k+cyacz5QaTNU1pUWsq2vVdJ14s7Vu7dOq8VSOQ6ETEAqiTthNhE1dt1dvoCNw2l5tKMYM+\\\/VoAvlCH8hKMiMGI1KBq4DWM0LxyUcWV55DZ9nxz8Oqvf+nRf\\\/pTg6ce+97T38oFwpyzQjBvm3sUIvOmFmALz6lqpOmYTPTaaFnGa8O9ex\\\/5hz\\\/G6eyVP\\\/iLYrJVzdfkVRozGptK1gxeZxA56pQXzaw32hQ952kOTAefISXdYbQ6VX0YIsAYbBsSD5QFlTURo\\\/YB9BpmpEhIHurDo5kOKstDFsX2zvb7F9Y+cN36sVPNxGB1Whb5fDqdiGCUiWQyGJWoxYlnXjnxrVer64nKig4k80k5hMXZDMgqRU02wGAJo+XR0rCYl0SF2vyzAbMM49HqTdedfe77Si7byvEkFEkjNWKvk+H17qnma0ADTMcvRcv8VwHF\\\/UR\\\/WhbQOhnrHw5djRmlZwuNiUijm5FiLvmEs63tixdf+o2vHr7n5gf+zuOD1TUZrcloRQbjSoNbNqPl+VnkkueSz5HPUTsgSvmXCosDKm9FWVrFyt6l\\\/fsf\\\/fkfHw0Hz\\\/\\\/mV6dX12W2LXWcEFoYXX28tLtrs6lHwtiAY4RzAQm1ZUA0wqYjMcKrWjY3OihTMdTSDEy0g9HtS2vtT\\\/Mv3INXK1RSj4NSeAdgbLV\\\/VZdx++z6Bx++QwaVw2oZCEMRKebn3z138eK1Wx64ORsNZGlVllZlOCpYFPN5ns+LeV7MZ5xNOdvJZzvFbFrMp0WRS5bV0TGrMt4jSysHDl935NaDb796cj7ZqcZHSntbQLJs5bq9+w\\\/vn1y80qgRm7IoZDndeM+QGLZjSaquplbA0RtKZPgWtUxBxjfZthzxidlm3gQ7HxJxbo3Sv62i9ECAHr6ACfzVe8xkY+fSOxePPvnh\\\/XfedPHti\\\/lcqvwdGwUVWpS3gz+VLLvKexqMZLQsSytY2bvnphsf+6WfyOb58\\\/\\\/rzzbOnZfZVpN3XcbKNjQ9SBMLbgtIX8zXYNC21lSIMzudaOmWqKm8PIh0EXnmAYJdXXDdTGylFbg7EApiitopCKpQ0cF4sLp6+yfuO\\\/XMsXxnR4rSN7FCZabb832HDtzzqQeunt+6dGab2bIMRtUQ12BJBktVSCuGyEaVf0DpmzgcY7SM8V4s7d1\\\/5IZP\\\/qNPXv+Bg1\\\/5z3989ewZyadllB2zgQyXZLR8\\\/T23Xn\\\/PB49\\\/6dliZxP5BLUmEYFJghGIR671dgulX0SKSErfEA0ale8+RAyVB\\\/K\\\/eJBXBaLVlnEtgNbmwEiizmWo5GA4C1VI0Vjyg3NcPv7Ot379T+7\\\/Bz\\\/60C\\\/95JtffP7yW+9yOqrs98qMCn1qKXhB0JagzAZ17MFytrxy+IePPvDUY1feOffq735969Kl2u2rtD+oqKqawqL3iNPubqqUAT3lvGnyyEDeYLZMJjk5zwda3ZI6lA+R+MXWt7RvSg0K0H0oVKqRo8BRQ+\\\/NgV5KcPPZtXfPz\\\/Niz5GDl66sSzaQPBOUdjvzfLr9nf\\\/zzI23HnnqX3zmtcdPXDx1bjadTWfTKVlwIIXMZ\\\/PxUFZXhsPKx6BgXZIjG2TZcHnf2p0P37bv0J4\\\/+x9fPXPsRGkPVyWQZkMZjDAc3XjPrVfePjvf2W7wNm8KUE+vu\\\/0b01hZePvJxDaGUIDVjL\\\/sezi9f7qbbJTLi+D4rYWh8I4sRGy9qLzo1nmNVSTQkgzHGK4MVvfc99NP3Pqxe9\\\/+1psnnvne1pnznE2kqLiEmgVmq5wsg90rl85BafycLS3vvfmGW5+8\\\/\\\/BDd5786kvHv\\\/Fivn1N5jtVFdoUoqJsHcSd8OQC+KWhdBg9sD4dBytjQ8dbUHGtzbVlkq1N4az0hB2Nd4ckZFPKZhZNqnatIMoyYMAybmlpD1YO3vv5T3LC159+hpOrMt2Q+aQyp8mGGK2s3XjkI5978o7HP7R2YHXGLL9u79asuPLelT0H9l5348qgmM4vXFkqOBRBq2OqOI18ll88deGFp7\\\/95jMv55PqlZENWHf+SwcOPv7Pn\\\/re733j4qvf52QDZdRPRUHV6XfB2JfbDMcaNBcjiOVKCYRG\\\/ZxahPExGPJJNoNJorpUe2zHLsA25kmxiNUKzKgDm5oacrAkw2UsrR2845YP\\\/9zHlw9dd\\\/zPXz734rHN8+vFdMKikDxHmcxcUEfAVvby2WAwHq8cOnDzI3ff9Yn7L7x1+rU\\\/\\\/MuN02c4LZGYeb38KrdvqhlzWBrA1CdwBO+UDoVXwK1jsWXmtBZWZxqmMocLGG7hLvEwt3kA4MNDdHqT+qZX9YsIsqw+iJZktILx\\\/gM\\\/dOeDv\\\/AT3\\\/r3T++cPSeTJuenAMDBEMMVLO1Z2r9vae\\\/KnkPXP\\\/GvnjrxnVPX3j730b\\\/\\\/2Ejy5\\\/7oOye\\\/8YrMths1L1uOsijy2c7Vzfn2NotpbYIMDIYcjmVpVZbX7vzkg7c\\\/ce83\\\/\\\/VvzDevlmneyGvnNdXzV9suXESK4nuJOoPz0HF3SRgleGaGXlCb9ERi+2OcVvsrfeQhW\\\/ax6pFaab4UUkBkzsqIISfzy28df+Y\\\/njt4xwc+9KmHHvzJh69duPaDb37v0rH3J9c25zs7nM9Lf1dggGyQjYajlZXxgdXDP3zHLY\\\/eVYyXzr14\\\/Nv\\\/5Y+vnDqd72xytlOVoI2akU0hqsjXkJJtfat8yaeeDYUFEuEpIQLXK6SLECZP2Y6rHtFOjERzKt+ATlpJMKcc7xntL1JbH1az56Uuv1Qj7lx99+zOZHrko3ef\\\/JPLHI6FeU3mF8IZSSnynXwy2Rjf8pGja9ftuemOGx944q5jX3vl1S986+rp85xscb5TpwhTKfiaedRciqL6NM0WMBwPl1fueOK+k3\\\/5ar5TQd9opvUl0gDZoSXquZUItYYLXCkVEGMmEu1UdWB5SO+ZQM+QEtpOpBSiRUA3rXKNekbcyC7CmaBKJZ9LwdoCuFQAzuazyYXvbVw+\\\/u7ew4duvOumGz90yx2ffoSjbL61IzuT+WTOghgNBuPR0p6V5fHSdGPn0lun3\\\/zicxePvbt1\\\/iKnO2Wgj8ynKOYsjJoxnHBVzVvoKMPWQdaI130JCk1VAqVfTzBCqREYJR+F0U7HCSdgSs7LAKC3KV+BGk9SKF\\\/dgMLExrXVQ2WpPJdiUmxvvvn0M3d+7uNnnn9r50zRTDNXNzefli398v6VO3\\\/sgfF4MNvY+sq\\\/+cqZ774x37jMMhymVS\\\/RXlYqdWYVtjeS4TJG49sfvzefzN955nssA31rKWI140qlUKu9PXTZhwYbF0TgSGdPgpjSh\\\/gIjwwbuYaZICPTWmIlKPZ6JGqb36j+TPa1Yc1UsPkgRQ3lF7lgznyazyfrW9fW3z6Br4\\\/GB\\\/ctH9yzfGB1Zd\\\/K0toKMhR5MdnamV7b2bm8tXVhfXJlUzhnGWTbhq6Ubn9UMZ2N0UN7RCBmddQIL10Re9vFV\\\/QgbEVJQ6HXpTp7ekw7c2GsDGI\\\/rrCEhl8ceTeXCe9b5+dR1zNQJChBNNb05ZjLbOfy90\\\/sPHn\\\/HZ\\\/9yOu\\\/+XXmpfdp3vZGzFHkRz76Q\\\/lg+Of\\\/9g\\\/ffub7s\\\/WLrDIqpmAuhZ5\\\/DyrwejhQRDJkTS+6vHLD\\\/qOffvi7v\\\/P16bWrpRYKRc5mcitSz0KX9xCvA+QCWIB5EqIRw5hGrHtCtWaBCDUKc1CgCys3\\\/RvQ7DlSbaFYSY2xNqqTDMuoejZsOwYqS2wo2QBVDLp+WstSJ0eRlxb3pRYc5X9TxVHULsAa\\\/HOoWO9USkx7STKvignNXwo+SVon9EJophSNPWkSNEZajWWJa4sRoJofqpvDUh5YxaENV2R5z97bbvnhX\\\/pbb33h+bPPvV6lUJQLTEjJMBjsuf2DUgw2zpxnPpWKmp+jmEtRGH18jZDWIWM6QnxpRcZ7Rvv2PfqPP7V9deOl3\\\/pyvnmlRIPKyKeqt1SQDBswPDAXpFG6q2kF9mXxuuMyzg800Wj0FGoIkRmExRR9BJaxNI1BpcRI7R8aLwBWG40ywgxFCUuzDMGrzP8GyDKWI7yN9Shqbr1y+CtqE8667CyKdqhPpfBCHYQI0LD0bkezxdnRZob9JGNpJhrBW\\\/TirbKGEUbNnhUo3cYo9sBkK0CMPVUCo6n2zCOUsL86RWpsvCnzCilykZkgk+lg49SZU19+7r7PPbp19tLVk\\\/PqBpdDTEIW843jJ6sJ3jIjuSha6Nt++\\\/pyNzaQ9UyMjNey1bXbHr9v783Xv\\\/Q7f5bvbMl8okT5KjS7floQpjWJNyzkFAWRo6ELoNFZjcFtG2B8pHtfrNBnt9BhpERrjMAMVQU4wD0kmtVS52H9EIaIT5ksXYAEi9JxuTZNKEvNGfKZFHOUrEOZlVcVn1otIQYChRmEICXlN+BCpokuOqwTxB8WQtRv63hkBvYu7opazG6kU5SIiIOkUf8Gj2Bjy1vdJ1aBd+0tbInN1nSewMaZK4Lsw59\\\/8tzr78+2Zw2WDhFUC2+OKgm0qJNegyZHubCijkwtE+BKOHQPVtZuf+y++z\\\/9yDP\\\/9f9ee\\\/e0TDcxrxDRata+pumdGw1fQdYMqwN9V9uXGVHvVeZuDDA+Eu\\\/PZhHC2QEknL9Wt4WmxIpzDZHQCMUBFghXuTT+R9WAH1leWbRnXQ7WbkIlYlZ6uig\\\/aakPWNR0PEjnBtjjh76OpXeQOQBv0AnAOJnkVeXlJuIlw8xdzZQvyUopli0z3HGMtjcJaj0jjFGvrQYoG6fXx2vje37miYvHL0w3JsISh6Ow0KNfsa69pOBRZYZnaGMPR6gJiWxl7faPP\\\/BDf\\\/PBl377qxfeeLtM3kY+lWJe170SnKtKhht2H7SyGLa0aB2vGQHjpJWnSezJZNdCcxJGz0fQGSJZ+USEYUjjh+ahSOir4CdAtXhSwEmzIakhbcZgCQOgjfIrmqwlkULxB0Sro2W8F3aZyCWfwgXOGwRDSABkV2s43Zb1w6qdhSt288OJisnmoovdHPTBThbFpXcuDlaXj372I5sXNrYvbQQyw8bpsFnfyFgFy2YotRyDOmZ8MKpI+aVVLO8d7tl772c\\\/dutDd373f3\\\/9zCuvc3JNZluoLPdzpUPUQEtrLWtNTBi2cDBtBP1W0Ldmc7VK1e8MMD6igxPRnvhJOqr7QIN4Yhp0LDl461M6WEpY9aO2DGSzOI3vLZTMmupOGNp60RVBV2pr\\\/j7a19onFrt\\\/SyNBdMVM7HpJxgvQjERAnP6z81PanOYIZEKsamCRF5ffPgvg6Gc+OtyzZ\\\/PclXwuko2q2d8yOLnhG1BHFGd15ZkNMViS4ZKMVmS0IuMVLK\\\/uv\\\/Xmj37+4\\\/uPHHzxt79y\\\/rU3Od2U6Rbq2bTIgLyupzpGyYypDLoRUcPmpKNinJ5w6YjToQHSE0iPSMAExFUtwv3SOUDgHNiIlN8S6LKbrQiBOq7+IxpER7R8jiraWoL\\\/WMQZx1FKLrCEVDQPvPMVMVPogqsUGwmWvk2maNV8A9IHL8VhBI2LqbuaY4zU9rf6FKn3RxYsiisnzl97\\\/\\\/KHfvzBmx85OtmcTzbmZFYvtkELg2fDWns4qqK\\\/h2OMlksUFMtrK4euv\\\/2J+x\\\/+2R+5durMd\\\/7nn15973SlyJ9PUcxaMYaaC4ETKa+HS2rzf1dtFFwseDdVH5vx1haP9u59MLlzq5FUlYWQrEhDftIcC\\\/CXMeA\\\/HWod1uhPcGBS\\\/Y+WlUTQBtPqp1tQNnJcjldatzyUIBwyoJs\\\/gGfu4apnqMy2tPNnmlQQj0IMoRbobZOLLMLkW3g4oSDWyrElq2p5cHOaDcYYLstodbR3\\\/x1PPnD3Zx55\\\/8TlU197+eqJ96YbG5LPWfkjKk+6rDoSkQ0kG2Tj8coNBw\\\/df9uHPvXAbGPz1d\\\/+2qVjJ\\\/PpThW+XczKLDQJrSvrWBGHgVBzeeZ\\\/wcpk4hBCJhVqAANGI5qxcBahSqyo9opKOYB4CcWFqA7NNntwsFMqvTeikdPovSJjR7abQjwO1EoP4D+GgYpHNVfwyUAX8+CuVqCEoLEmkZQuLNb4iuut0LcIU59HDf22YRax5L\\\/JZrSdAhPoDyzYAG0H2TY4bPu6AcuTbTCWwTJGqyvXH7jx\\\/tvv\\\/omHRytLZ157\\\/73vHNu6cHWysZlPp8zr1PQsywbD4eryyv7V\\\/XfddMdjH1o9uPfiD9478c1XLh5\\\/d75xldVQ6BStHiNXPl1txquVhkoQENY+QaT3WMWbUUwbKhaF9KwxlO8F9j3o+d4nsDV\\\/HcJ53hi1HGL9cksXepixDONMlphgZDrFm8FTU383iwAzObMitV9AssJbDLLpIe6hFiGNkKKnNGakQ\\\/ITnukDP0Y1QS6KPFlFMINyxngI6Wqs4a7Kbr5y2SqrzWGl0R8sYTAa799\\\/4LYjh47ecvi+2264+eDOLL+2ubOzMynmOTPJRkuj5fHa6hKmswtvnr7wg3cuHHtn48z5fGe7UqXldfww5zUhwcYBMTCzMEuC0X\\\/4tqKJioD0qflerUVlCLLvQV+d69xzPYiExGhvWX0gdLJyhy2kNnhGl3TZbrG6LlCniXM+m0K9zkX3VQ5IhlIxeUYujGHKwiQGFhPHpIGk5FKu40CcJQaSu1n5UX8J225YBjWUQ5U2PwBYRiY3jV82ksEI2VAwGK7u2XfowPjA3tG+1cHaEgYoCuY78+nWZL6+sX1xfeviVeG8XXvFvBEDl4p\\\/0PJSNqDKXWNqodJu1glxDHSqmrewnQvH8DDa91DcViZ83uIhHQRFDpIrQHSOPQPxUYL\\\/1PcYafZbL0LYwi+BVsAoIcyvk85Is2XpDIsYV8UMS3J0LjP0IbCdYHH0w9aXNAkiAfUQMK0cAbvgT6DuvuudWE+o6amrZv4QNQTKGIwRhLgEKo\\\/uJui7SZgp8tLMEnXyaWWDH\\\/d+seGq7p1JuuPwTsREYqowkOkad16GSdjV3wx74fdAjdO\\\/WauYvmCZ+DqTSJqB9ONlZs9hQothzzS6e76E6w361RqzdapdIvbjcoggIl7sSDQPkgoBQtT3xdR8ag0HLXBae99sDdBYD+NNExLHATrL0rQnjB7rwClT96NsvfVZlEygMEeRERnKP5YGSNWrBmr0eqqmxDwLFT7DykHTLDa7AlO3ot2QLLiRaDMYNx0wHTgN6U4T+VtrR6E9NugpNqIxOuo0xFDhwBi0IMwWkuKLnVXuOf2JtY6Go02hYVTjukKbYjMx7yeR6Q78yr21oPerRaZm7ROIrFuLdO+CiTG5qHaJJKKMQXggzvLqVM+YfUY37\\\/RtcNWTmIMgqnlQtjItOMVNE7pUBcuw8SKh8kqHRN1d\\\/HxGzrENa0V0l+eBuXna\\\/IIL9PS1gJv2DbSVZCskjqJ\\\/hQuJRTyuTTwHviZ8D8aTyDDUrgF7+MoM5u669ClcgBw3CuZ4o7GJDj4MEz36hs1FNDfaW1lHhXYwHOhcaUQyWY2KqhzejqvDIN4CXu0aCoQUl60dlUqBSBkxXoXQ0zmBwRiFVodb8yQ21nVRalWUcCj6AbcVpmnt9F2BY3yf9Kf0KcR2QLV6RodpPEFdgop2a6PzLBfZljpIEG9gzz4BD4YyJ7SZNghblxas84yYkqcbEk6PcI45ekcT00w+2o1VoPdLL0TNXPZgmAqdRx9d2E2fhWSX4CPC+MK5CR\\\/i7QSsaPp188FQZ78qbyRR4y\\\/xHgajSCnliNBzgGpxRnbmpjRFdMAHwal2D9QrkIE7zG41h7RFLEUgA4wPJ\\\/CQNFoNq31poBU0PwBzN6ULrGeHrLTj78U4vyRpM8RdCjrwq\\\/B3UhfDPyKg3wzpAT86Vj6ee14KuHbyJ7xaNPayIYIhcavUhiPqjr4LPYhu0acRqi01WlO9t1O5ubZWBo3WrFls0IedbkAsCcFAMOwCnuFAM+IBlyS\\\/xQSQEavnQdtfyEDGRzw3NaebkGBAJWIlTOEJCN1K1F3mQHLYtw+ad7gMdMdlSJOikOymujFJjzti2iPG3WT8l0\\\/Ir8NkPrE+dnAuE1oVcBD2BWP209JNKUi0vTfBsAR7ZKUITw7Yde9Ia2P1vBZOg+pcCurTWpFiXIztmjH+MV0tnIlWhXjimF0qJbyqtPynXISM+Adnc1XzKdrS0JuMAUB0Quvw71lipXlLFOlTSBLwXfq06SLuGAE\\\/TF5xILW2et7FjcPTXoZ6ZrgNr48vmZqqJp00dmiJr6OFQXdRgbjoQPdXpENdIYBgJVAlajAA3hVsNMMhics6n4Ex1aSuGtOMX7RLx8467GYSErVRh65ELUK4j38UpNRssvCsKVq6iCnGgfAeSziDhS44YT4mE0OyzkdbgGFjjCPBEwwx1UJDXAoBQD\\\/LVj+GSPw00iUK4NSjaBJpKSkBN\\\/TmhRY0YXoXM\\\/mLqeXnVP9GyV2fx4FCvJEIQwTd+yN0LA8Q80juZK37RzbD9VGd7++8uk\\\/vF214Yw32n3KekJ0sbAzTBzeBkjL1QzuUi9BsVoOcvQWn84kCZTNb8Zu2ycAujqKuy9\\\/\\\/i9GRDdspYcE5DSDeDkRciUJYS6oHJdCZMWgKEf+KREG7qYsRzkl5TSx7WsOIY1Have6Lbl1Snd48YALpsJxU3AtCpLPTK8TljUBLF1rmwFEYQcRFIoYBKSndB8uCJ3HoqwJGjFQUBUdX35ymExCGT9jWTMmyXUl0YgXCQTXa5GL3s3SexBDZ1dy9uIMSTZhmV7nnK7GcTDSg+4Awl4z1DEDfrt\\\/cCPQIgMIVaFBUpk5U2Lz42ISx9i3U2eaGVEfIX\\\/Q3hw26UTTlKBAF1NMV8cNdvhUlE77tMNxT43VAbWZnUdtWii+BMbO554s8f3ShbxcyRtrmrMm3gES8eW1K67r0uAB++BBHnRIQbrjwwEkm7kXS4jpxhrATAAgdRGmzYHV+L1LQoPrONOOISbyIf21fgESgir2T8SUIfcOCxz\\\/hSmgVTilSytdCxeVvmhg0miXHe9RtK7M+5qfNcdUHvSlWNfNiH8Xk06P9NKk8ydnoV0wQQMJ+XL9NG+jZbt8tpk3phscS15aphp6pOrPXH7b2cu\\\/5BJ1VjWj5SzclANvB0Ar8geT3p0vetZGotDRg4tYk1XT6NGMdMEZjOxJdeV340SxKBB9SdCQcrEu628DpqxRbfgUVHEObPKZQH4r7MAuFw2ApdcHkXtHmCR3V5UC65GYIDKR8kPxfh4mcroJUNTkSWbiYOtYf1KrHvyw8wnQjmJzn6p2Wjz5A36mSkjM4OEQPZGeUocoSrb\\\/w9DZTv1KnQTtgu6gg5cmvi1L\\\/nTDdMvADPJipbQ6le0dO79lMrZOQA2YS6wiboNptzWZ0BXCwSOwgAwsYoJtbE4ddjG8kNIgWsc+xnUmaeqiG7gkE\\\/g6w8IFnO4BuszH0EYNRTR+kHGkUBG7fmLJgY2Sd0KnZprllbfctEYCTWAD1LunD5+yGL3sHMWB75yZNwXDmuvVdrIDpWsmqvEVg9G9oRqRLEAuMMYCenD2Z8Y7Z2tAMnV0hfJjVr2nSLNJ9JG1\\\/vT8C6YsFRYVJlxmE22aoMW7xJCsWZoEL+zC24UwIlKN+AA5eQ92pMUbSe6cVGQ+RVPsJmewnYJlDjau3WjZLe2qQWw\\\/Ii+fX7KJWjFRmnYdpi+ka0WYoeSMXW3upM1MHqDA4R9ofYXpkKfxjXMHYLm0XE9rDmAaM+vlQYBx9RATWKIsh\\\/FR7H0D\\\/dGMoisYCL9iHbsQmdsazpxybcaaxYvDRSE\\\/KHYDO8coQ3aeLnCF9uPiUF+O9C105lnCffUbRI6EnT2Kah0HOArpJnz6yL2A4UjA7o6uvsnFSRQpjRsH6i3bbJaTHUmjLQURAku9Ra5oqKmAmiUY2BalD6JX8nB3AgdOc0D1iQQkUWFQFcJAFz46SwNvA\\\/AoxAbUGllmQNDRCDx5oVBuED0uEwqJg2oOqTeVCm2d89DEYCoy9YJTIwJpEKmMjlwrrXkYMqJzeO5J4wuvZI7q7LJWCtEFaoAqdYEbOqFJIC+S0xnspyCdA89LAWPWQo5qZYkps2t7kVEcGlALuI2FlFcUnge7EX9sdho1cm9iu3JngqmFcf5rYnCjuQmnrZdsyxWHAyeNBc2fs6jOT+1T0R8T1I6XzsAsbNYrtArTAGWbWQhIO3Ck9roIAetSAiM5XxMbSsfmBa6oQdGCIAUqXrfefXadni6XCNHohtodJcm+A9JQ\\\/irhkY4ZXlgt0jSxiYWfcsAHDaqaLZlY1bXAAH0ZC2BgS7VAoCKY5XHjMJwNJUdsvaIeqCNdBWJ+gk31h2O7HVmdmPKojnilZResHgtYTIAKQ4aoFxBm4MDtkBArG8B7dQwzODzjgtuXRApMQOBxF3CPQ7dPswtC9DNWEYzxdyLiIpXdIexoQhGUNwmVNBw2kukO0FrCW+apsdKJm2YeegcqBG7Edg7WR7h4DgKMXbdGLYEeOhKzwI6V1moEG0lBbtJnjFB7vA3huxdJpKpfeKNC59PpbcCRwNnuNdvu6qrZhH0ZNvzjw7GERdbYRTAsP\\\/Uff5aRRsFhvTKT5Vfg0aNpvvPNkQ7BgJMV3+CAMPF9g9tyiKMmzrGyGbrcZTonHlzz2vkMLVtD2eezpcpL8mUJI6G2zsfYFqR9JAdbUUQRmlRrOhtzFCmTKYybxvOzClM2eutEWiz50vuPhZtCOKSbPK3ijBypAUIgurjPhPwWmBEbJt7bSKPSFkTGJb3XntFqbskYq18+uMsjhtWfnUDzSO6K2vZ49haGrPTRyHbZMWTRExl4mAm30NsVldCxCEBodInH9k5wkA1i4ewXaqh6RZ1t82WgfTexqy2qlBXR7VDJ5jNOhzR3MshpNbB6jRKvC3kNLImFqoutLQQQdBw5Tb8EuOICS9vn3yY4IEKY44zYGhG1wIjp9C1FP1qfE0\\\/DH9HprI0+Oax7GOBYm9ujvSlSzz3r6I1VgeyTeSs42aBP+wIeeiXkl80xHjzskSnHpHPJAYrSkY6wRSaLAJ2XRF\\\/cLq3D1sIFEEsNCBLo78N1bg0bjEZFuK9EF0z5sQdQS03iBWnyAnlprEtJ6Nstw72hGfJqfGGDpcHoHCrvK\\\/r4lNL23NRI8rKRvyAepBzTsIzobtrC37G1awhEu6LkYwkMDo8cynoxViKQ7oUcJNotUUpDz5DFK9UA8DeoJJ3rvI+0UYLLUly5l3i4K4+QHriKc7WXtou8DNSwkHu2P9+\\\/Aqyb5AaEeqfSgWoTFUkd4tlMIoCBTfk1h6JE5ibGLgZyQfwrHowN4Ah1HaYjbxbcfTQqa1UK7OxPFoVkX6\\\/7RqrbSK9B+biCEhWifhFoTXxJ9yk8sumyURIazO3oobu0fFALsu4WR5IKSGApjULU4eE2sq+4mjRhovpvzCi5S0tmFkwu1UoGxHGPUjPbGsk7LZOveBgKxfjXgz5CuAeqTEH7H7JWB2M2xJV68bzhVicQrmSESJM0voj\\\/Sl5aiT8iI5HcM3bTRXwikiKj2IGcaQ+2xJ40XgHvtA7fXRfrMvobU+61FXheL3gN2Xz2v72SHmXLvGRwa3tGFeXSv3NG7gI5eAP3mFyLSKGbQ1SQxhJc7cFgmNExcCPf3NiDTwSB2s9MVANiDnHh+li2ynzwkiLjodS6WVwioHRLQJ6FzUieG2dxHkTHOZ\\\/wdnAtFJveufsg3PKPgyCwTTw7hXSDv6oVZEZRu8cpCc\\\/HJTZXiTENTXOtDKzVnVKXTlgGtlDtqIyLQZxg52YKpeVl2DjWqrckZA4WPBtTCRfrNtmNjhA7kPwwYX6BXNoa9iFmVpg6looU7CtokzgbYFSfRe6dO8t1AkEzcO7ZA2+KTStJD8CgL9ggEsMEdEnt+J69b10ekLPYI7frbIZb3W\\\/9CW5K3eriKp0bX4VdqWJyjK+u8yVGTltIbhjuz8TFJB03We48PBjL9Wz4WEndR4WbPaEwzMkYnnT6LbjQPF392GSc9mCIU\\\/vAZhLt7ltjR6OlPwt7Do\\\/dFFchIlHRr7LDiClGZ2ji44I4Wrlftq02fsPK4Rzh9IiTwEmzrILQtPYK3VtcTpFPJ9sFVlJKiiAMtKgcwSJepVuwNZnJGTcy1R3\\\/oPiwlI3Ud05g8KZCQbMezuvqLJMyXET9EcC5u\\\/KnUfCaJoB\\\/0k0C9brPpR3plPbsYn+lbdr5UeAFuo+vRjzph9wN3NtPRh0SvDMTBhxhiEm5fhOCJsSNxTDymbqOCLk69llkPvYvf5mr2TfI1LFpzMoeqpMi5PqhRQjaRrYIPauwIETcKicKVImc926Ex5W7Zc6pFjlLJx5WOh2LUGNAWcf5MU+BIgvg1F0b\\\/ta5XFsRRJIkGmRCgDlwlFl1wkQ\\\/M3Xw2plyvo09O46Pl0TX0LFoDZ5kaegEdXCdVArFr92tSwONKYPFczNYP2YOD0QaOeb003GaDUfHGCJdmOA3EFkM3biXBrgnbfDcMh\\\/a0gXhRkolnJRzUQFy8MT6pGDQTHarFYINWWxwZZc0ml58E3BfSfEBnSnBTdkEnWNHRZjNB7C9URrOfrkjb1CgOBaFJLGvcjc6vhCNdYifb0IzVtS78cCEys+1TmS+axW4WGtlEwCV6+pCOBP3DpoferJE0Eqofg9WVMw06VJFXoLpoLs4ZJz\\\/STcbR9B3cHZvON3QR0WCot+N\\\/WqoPKTiRDuRMr2wLa9tgC0gYtaB7XjIemIwpO3oBmi0y0wyqcgGvVUlAh9pZKoUP0+FrIikZ2hfVP886ypTwyiaa9a92V0q\\\/xX3QKjanqGc0UCvukXVBbozPJHqIjbh9mrI8CyY74Lr90SG5gJTrvfV8g0QqZKTB65BChQXfTclA9mDr6eYgeGEusKc3PEh8QLGrSjRzsaByGgshlOD1QlYgABrYhaSkTNCclI5F0xR9p28GAZNwpj2EZAAWtipp208h\\\/M7+R6S65U3zgERpQzVXnZTa1KAo49Oj3A5kgPGhJHCHdvg3+cgYdEJH7bhgMqosuirDM4myoAN2QNcygJfsDR9EwQKIBQUgesNPunqehQ8ExBgP\\\/Y6xuw0CPLsL7ArBYThxSyc9CbsvO5EWTKOrbRJJDEz3OM2YSIOIB0NSA7sQq2EY9u6h8DZBA6HjWtb3UDCMoQnvQTgGFAw1MF1xaKhwocqFgYcGooJBnx9BVkg4CM4FmSiLZDMuZrEbJeaCyw8BfNcHHuo6nG5dF8KtZgWy90Ojs4T4a35Htd78Cirp+wbbH9C5ifAbNtZ2QOjcR5O3Bb0bS7JbSV4IzZRlfmLuQiA4XUczuhU8DdJYG7zGlYuzm4BED0JkHJy6rjOTZZZqPMUJhPO3wuhLu+IrYgFkv2mS2e2a0xJi7DSBoXM12PGCfUAl6Zi7udcluLWscAFqF23XFrdjAtUpYhmp\\\/8nUU0sxy5YJAKgLMkr8aEfLwC5Ko\\\/muQzt8ic5GzdYV0Tdv\\\/fUQhaQ2nr2wIKnrKRhNvUXofqKsJKMkLlBinSG8naxDr8m+fYpdzpzssDpF4F6+oAoA7rOANAq1wPIPenr65q6syziEJg7WFZbOkRQwJ2ahcIFggMAjrydWw\\\/kzgRTbwy4H196IqI6DBIjuVuO7WP1VBn3k+vF6xmS6d\\\/go5LVUuUiJhArggjVOVxPkDAcEsIRRNiRukAanGfe+7k1PElOO71p4WgZMAxv3oFAwuXhfSQVTREi4aeyQrjf7QCAyxHl7YxuZ6NNiFa7lw9nxHdOsRjfuGvpEGCSPIr4ZlIQTcIn74tlV18WIX9o33znrW\\\/NmcWLR1cIoCaO9U7ApNoxXd0uKIaB0AtomWjMMX5SJbiG6+pG6goZpZA+pl8zJ6BB+kCqonRbbjj9Z0hmH7sbZOd4Vi7NM\\\/RgPf8KmZgZ3NUJ+0wcLabwAF9XQAd3YrPNO0J\\\/bnKf0ryaSOy0WsCBBZJjTU+tKKVuzo0qIijSyg5hw6hp4WjP4mU2g0rLEbhTpe4Pk5FSHHWzP3Wbci0NcT1obv0O4lmnQfxlZNdcIsZmBr1EEc8W6h2jDAk\\\/vUnBQFdb3iCEpJM4IEpy\\\/0BUxomklx7wOpjsLfyFUFCK5\\\/NyFGe5BOqA3iLFMOosppB+LJOD1FCPO7gMX\\\/q3pOmSddXRYpgbVKMIK0UAdsHMo0CYtRMj8NEhNoLReAENPmY2APVksCczUgzyJiEBjsG0GFz2MTacSktBK3O0VQ+TGaQaRnTKM7rBqwpaVjsaolXdIqxlq2gQgVmgqg1Qm7wo7ZO7ufEvg+8zEI+k2gQxCmKJiOwwrhPi1cLh44PE9Hm4uydZoV\\\/9ksdOWLwkAnEYWLsykpqZUkLan3xBGdaN03GO65Z\\\/TIVDnQdEF1rpelw4oHtrvpj1kFXesjW2iN0K9u8EEfkgCtmN6I2LAQtcQOAI0MkkZwRFz0PniiQ2QnfCdxUuJhFS69EdI+Supcp3+GyA+DZC6XmQHMIzIXTqJxJghNYSqf9hNNl2HZV1DNskTyffwjMZXYIWf7JZK68Q\\\/1F5+TEfZp8ts3XIgjX+j3lzgFF+SagxMyetK7+q4lgTQFYxLovYMSqPbdDadQBDCtvAE67kNqobTJxzELR1tu+xXikwTHWyvEpgkEGBvWXdf2CnWCh7e3pMorGZEJTInrIIXKUO9aqEXeASkTWUK8nqM3ocWfkU4JdB12ZAgOKjNq6yllEkLoqYn+qFiOj8XCzK9exkGH6WegjZ+I6oaYbwb\\\/SzheHK0I3dE6f7Brp3QIPxONhMMKIRYjuPEVaXqSTb9q25yo29KpOrKfnNU+shzFPlmHrLIGiVlz9v6btGmG\\\/tYg008DDFH9252dLPNhRpg+VCaLEr9LpR9W+\\\/beOpBGAUdEm12tDchzCiFg7sEoyZI5xgaY\\\/EmX0pBCG2WVpi7HQ86qutQXhNfUENjtRPsSzrkiJLKNqC+hhojEv+7UhD5HJBEmHAUKRZDM30b3cQorsAkdzmtUag6MzsRFzpu4qg8hLseUrF3jLR3lKRbYcS09S8QptpKd4EYcv7\\\/A5V66wHRWfq9AAAAAElFTkSuQmCC\";\n        var img = document.createElement('img');\n        img.src = src;\n        img.alt = 'Shop & Service';\n        img.className = 'ti-shop-service-logo-icon';\n        img.width = size || 20;\n        img.height = size || 20;\n        img.style.width = (size || 20) + 'px';\n        img.style.height = (size || 20) + 'px';\n        img.style.borderRadius = '6px';\n        img.style.objectFit = 'cover';\n        img.style.flex = '0 0 auto';\n        var txt = String(el.textContent || '').replace(\/^[^A-Za-z0-9\u00c0-\u00ff]{1,4}\\s*\/, '').trim();\n        if (txt && !el.querySelector('span')) el.textContent = txt;\n        el.classList.add('ti-with-shop-service-icon');\n        el.insertBefore(img, el.firstChild);\n    }\n\n    function applyShopServiceIcons(){\n        var titleEls = Array.prototype.slice.call(document.querySelectorAll('h1,h2,h3,h4,.ti-card-title,.ti-section-title,.ti-popup-title,button,label'));\n        titleEls.forEach(function(el){\n            var tx = String(el.textContent || '').trim();\n            if (!tx) return;\n            if (\/Istruzioni AI configurazione\/i.test(tx)) return ensureShopServiceIcon(el, 24);\n            if (\/Shop\\s*&\\s*Service\/i.test(tx) && !\/^https?:\/i.test(tx)) return ensureShopServiceIcon(el, 20);\n            if (\/^(\ud83e\udd16|\ud83d\udee0\ufe0f|\u2699\ufe0f|\ud83d\udcf7|\ud83d\uddbc\ufe0f|\ud83d\udcac|\ud83d\udcc4|\ud83d\udcc1|\ud83d\udce6|\ud83e\uddfe|\ud83d\udce7|\ud83d\udcf1)\/.test(tx)) return ensureShopServiceIcon(el, 18);\n        });\n    }\n\n    var prevRunConfigBot484 = window.runConfigBot;\n    if (typeof prevRunConfigBot484 === 'function' && !prevRunConfigBot484.__tiUnifiedChat484) {\n        window.runConfigBot = function(){\n            forceConfigAiUi();\n            var p = byId('ti-conf-ai-prompt');\n            if (p) {\n                var raw = String(p.value || '');\n                var extracted = extractLatestUnifiedUserPrompt(raw);\n                if (!String(extracted || '').trim()) {\n                    try { if (window.tiAlert) window.tiAlert('Scrivi una richiesta per la AI di configurazione.'); else alert('Scrivi una richiesta per la AI di configurazione.'); } catch(e) {}\n                    return false;\n                }\n                p.dataset.tiUnifiedChatRaw484 = raw;\n                p.value = extracted;\n                var restore = function(){\n                    try {\n                        if (p && p.dataset && typeof p.dataset.tiUnifiedChatRaw484 !== 'undefined') {\n                            p.value = p.dataset.tiUnifiedChatRaw484;\n                            delete p.dataset.tiUnifiedChatRaw484;\n                        }\n                    } catch(e){}\n                };\n                setTimeout(restore, 60);\n                setTimeout(restore, 800);\n            }\n            return prevRunConfigBot484.apply(this, arguments);\n        };\n        window.runConfigBot.__tiUnifiedChat484 = true;\n    }\n\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ forceConfigAiUi(); applyShopServiceIcons(); });\n    setTimeout(function(){ forceConfigAiUi(); applyShopServiceIcons(); }, 0); setTimeout(function(){ forceConfigAiUi(); applyShopServiceIcons(); }, 500); setTimeout(function(){ forceConfigAiUi(); applyShopServiceIcons(); }, 1500);\n\n})();\n<\/script>\n\n\n<script id=\"ti-config-ai-sms-unified-final-485\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigAiSmsUnifiedFinal485Installed) return;\n    window.__tiConfigAiSmsUnifiedFinal485Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function clean(v){ return String(v == null ? '' : v).replace(\/\\r\/g,'').trim(); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function dbName(){ return (byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : 'NEW_DB'; }\n    function getBox(){ return byId('ti-conf-ai-prompt'); }\n    function getBtn(){ return byId('ti-conf-ai-send'); }\n    function ensureUi(){\n        var rb = byId('ti-conf-ai-reply');\n        if (rb) { rb.style.display = 'none'; rb.innerHTML = ''; rb.setAttribute('aria-hidden','true'); }\n        var btn = getBtn();\n        if (btn) {\n            btn.textContent = 'Invia';\n            btn.style.setProperty('background','#10b981','important');\n            btn.style.setProperty('color','#fff','important');\n            btn.style.setProperty('font-weight','800','important');\n        }\n    }\n    function latestUserText(raw){\n        raw = clean(raw);\n        if (!raw) return '';\n        if (!\/(^|\\n)(Utente|AI):\/i.test(raw)) return raw;\n        var last = '';\n        var re = \/Utente:\\s*\\n?([\\s\\S]*?)(?=(?:\\nAI:\\s*\\n?)|$)\/gi;\n        var m;\n        while ((m = re.exec(raw)) !== null) {\n            var chunk = clean(m[1]);\n            if (chunk) last = chunk;\n        }\n        return last || raw;\n    }\n    function appendAI(msg){\n        ensureUi();\n        var box = getBox();\n        if (!box) return;\n        var cur = clean(box.value);\n        if (cur && !\/(^|\\n)(Utente|AI):\/i.test(cur)) cur = 'Utente:\\n' + cur;\n        box.value = (cur ? cur + '\\n\\n' : '') + 'AI:\\n' + clean(msg) + '\\n\\nUtente:\\n';\n        try { box.dispatchEvent(new Event('input', {bubbles:true})); } catch(e) {}\n        setTimeout(function(){ try { box.focus({preventScroll:true}); box.selectionStart = box.selectionEnd = box.value.length; } catch(e){} }, 30);\n    }\n    function setLastUserOnly(text){\n        var box = getBox();\n        if (!box) return;\n        var cur = clean(box.value);\n        if (!\/(^|\\n)(Utente|AI):\/i.test(cur)) {\n            box.value = 'Utente:\\n' + clean(text);\n            return;\n        }\n        \/\/ If the current last Utente block is empty, fill it; otherwise keep the conversation as is.\n        if (\/Utente:\\s*$\/i.test(cur)) box.value = cur + clean(text);\n    }\n    function wantsSend(text){\n        var t = clean(text).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'');\n        var action = \/\\b(invia|manda|mandare|spedisc|inoltra|trasmetti|scrivi)\\b\/.test(t);\n        var channel = \/\\b(sms|messaggio|telefono|telefonico|email|e-mail|mail)\\b\/.test(t);\n        return action && channel;\n    }\n    function channelsFrom(text){\n        var t = clean(text).toLowerCase();\n        var ch = [];\n        if (\/email|e-mail|mail\/.test(t)) ch.push('email');\n        if (\/sms|telefono|telefonico\/.test(t)) ch.push('sms');\n        if (!ch.length && \/messaggio\/.test(t)) ch.push('sms');\n        return ch.length ? ch : ['sms'];\n    }\n    function validEmail(v){ return \/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(clean(v)); }\n    function extractEmail(v){ var m = clean(v).match(\/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\/i); return m ? m[0] : ''; }\n    function normPhone(v){\n        var s = clean(v).replace(\/[^0-9+]\/g,'');\n        if (s.indexOf('00') === 0) s = '+' + s.slice(2);\n        return s;\n    }\n    function validPhone(v){ return \/^\\+?[0-9]{8,15}$\/.test(normPhone(v)); }\n    function makeFd(action, data){\n        var fd = new FormData();\n        fd.append('action', action);\n        fd.append('ti_action', action);\n        Object.keys(data || {}).forEach(function(k){ fd.append(k, data[k] == null ? '' : data[k]); });\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        return fd;\n    }\n    function post(action, data){\n        var urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.ajaxurl && urls.indexOf(window.ajaxurl) === -1) urls.push(window.ajaxurl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            var controller = null, timer = null, done = false;\n            var opts = {method:'POST', body:makeFd(action, data || {}), credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller = new AbortController(); opts.signal = controller.signal; } } catch(e) {}\n            return new Promise(function(resolve, reject){\n                timer = setTimeout(function(){\n                    if (done) return;\n                    done = true;\n                    try { if (controller) controller.abort(); } catch(e) {}\n                    reject(new Error('Timeout invio: il server non ha risposto. Verifica admin-ajax.php, API Key Infobip, endpoint e firewall hosting.'));\n                }, 70000);\n                fetch(urls[i], opts).then(function(r){\n                    return r.text().then(function(raw){\n                        if (!r.ok) throw new Error('HTTP ' + r.status + ': ' + raw.slice(0, 260));\n                        try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta server non JSON: ' + raw.slice(0, 260)); }\n                    });\n                }).then(function(res){ done = true; resolve(res); }).catch(function(err){\n                    if (done) return;\n                    done = true;\n                    if (i + 1 < urls.length) attempt(i + 1).then(resolve).catch(reject);\n                    else reject(err);\n                }).finally(function(){ try { clearTimeout(timer); } catch(e) {} });\n            });\n        }\n        return attempt(0);\n    }\n    function askNext(flow){\n        window.__tiConfigCommFlow485 = flow;\n        ensureUi();\n        if (flow.channels.indexOf('email') !== -1 && !flow.email) {\n            flow.stage = 'ask_email';\n            appendAI('Indica l indirizzo email del destinatario. Scrivi NO per annullare.');\n            return true;\n        }\n        if (flow.channels.indexOf('sms') !== -1 && !flow.phone) {\n            flow.stage = 'ask_phone';\n            appendAI('Indica il numero telefonico per SMS, preferibilmente con prefisso internazionale. Esempio: 39333xxxxxxx. Scrivi NO per annullare.');\n            return true;\n        }\n        if (!flow.text) {\n            flow.stage = 'ask_text';\n            var dest = [];\n            if (flow.email) dest.push('email ' + flow.email);\n            if (flow.phone) dest.push('SMS ' + flow.phone);\n            appendAI('Destinatario: ' + dest.join(' + ') + '. Scrivi ora il testo da inviare. Scrivi NO per annullare.');\n            return true;\n        }\n        return sendFlow(flow);\n    }\n    function startFlow(text){\n        var flow = {stage:'start', channels:channelsFrom(text), email:'', phone:'', text:''};\n        window.__tiConfigCommFlow485 = flow;\n        appendAI('Ho capito che vuoi inviare una comunicazione. Procedo con i dati separati.');\n        return askNext(flow);\n    }\n    function cancelFlow(){\n        window.__tiConfigCommFlow485 = null;\n        appendAI('Invio annullato.');\n        return true;\n    }\n    function handleFlowInput(text){\n        var flow = window.__tiConfigCommFlow485;\n        if (!flow) return false;\n        var raw = clean(text);\n        if (\/^(no|annulla|annullare|x|stop|esci)$\/i.test(raw)) return cancelFlow();\n        if (flow.stage === 'ask_email') {\n            var email = extractEmail(raw);\n            if (!email || !validEmail(email)) { appendAI('Email non valida. Indica solo un indirizzo email valido oppure scrivi NO per annullare.'); return true; }\n            flow.email = email;\n            return askNext(flow);\n        }\n        if (flow.stage === 'ask_phone') {\n            var phone = normPhone(raw);\n            if (!validPhone(phone)) { appendAI('Numero telefonico non valido. Indica solo il numero per SMS, con prefisso internazionale se possibile, oppure scrivi NO per annullare.'); return true; }\n            flow.phone = phone;\n            return askNext(flow);\n        }\n        if (flow.stage === 'ask_text') {\n            if (raw.length < 2) { appendAI('Testo troppo breve. Scrivi il testo da inviare oppure NO per annullare.'); return true; }\n            flow.text = raw;\n            return askNext(flow);\n        }\n        return false;\n    }\n    function sendOne(flow, channel){\n        var data = {db:dbName(), subject:'Comunicazione Configurazione', history:flow.text, admin_copy:'0'};\n        if (channel === 'email') {\n            data.email = flow.email;\n            data.send_email_opt = '1';\n            data.send_sms_opt = '0';\n        } else {\n            data.email = flow.phone;\n            data.send_email_opt = '0';\n            data.send_sms_opt = '1';\n        }\n        return post('ti_ai_send_email', data).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) || (res && res.message) || 'Invio non riuscito.');\n            return res.data && res.data.reply ? String(res.data.reply) : 'Invio completato.';\n        });\n    }\n    function sendFlow(flow){\n        var btn = getBtn();\n        var old = btn ? btn.textContent : 'Invia';\n        if (btn) { btn.disabled = true; btn.textContent = 'Invio in corso...'; }\n        appendAI(flow.channels.indexOf('sms') !== -1 && flow.channels.length === 1 ? 'Invio SMS in corso...' : 'Invio comunicazione in corso...');\n        var tasks = [];\n        if (flow.channels.indexOf('email') !== -1 && flow.email) tasks.push(sendOne(flow, 'email').then(function(r){ return 'Email: ' + r; }));\n        if (flow.channels.indexOf('sms') !== -1 && flow.phone) tasks.push(sendOne(flow, 'sms').then(function(r){ return 'SMS: ' + r; }));\n        return Promise.all(tasks).then(function(results){\n            if (btn) { btn.disabled = false; btn.textContent = 'Invia'; }\n            window.__tiConfigCommFlow485 = null;\n            appendAI('Invio effettuato.\\n' + results.join('\\n'));\n        }).catch(function(err){\n            if (btn) { btn.disabled = false; btn.textContent = 'Invia'; }\n            appendAI('Invio non riuscito.\\n' + (err && err.message ? err.message : 'Errore sconosciuto') + '\\nControlla API Key Infobip, sender ServiceSMS, numero internazionale e connessione server verso Infobip.');\n        });\n    }\n    function install(){\n        ensureUi();\n        var btn = getBtn();\n        if (btn) btn.onclick = function(ev){ if (ev) ev.preventDefault(); return window.runConfigBot ? window.runConfigBot() : false; };\n        if (typeof window.runConfigBot !== 'function' || window.runConfigBot.__tiConfigSmsUnified485) return;\n        var prev = window.runConfigBot;\n        window.runConfigBot = function(){\n            ensureUi();\n            var box = getBox();\n            var text = latestUserText(box ? box.value : '');\n            if (window.__tiConfigCommFlow485) return handleFlowInput(text);\n            if (wantsSend(text)) return startFlow(text);\n            return prev.apply(this, arguments);\n        };\n        window.runConfigBot.__tiConfigSmsUnified485 = true;\n        if (btn) btn.onclick = function(ev){ if (ev) ev.preventDefault(); return window.runConfigBot(); };\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install);\n    install(); setTimeout(install, 300); setTimeout(install, 1000); setTimeout(install, 2500);\n})();\n<\/script>\n\n\n<script id=\"ti-config-open-rescue-488\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigOpenRescue488Installed) return;\n    window.__tiConfigOpenRescue488Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function selectedDb(){\n        var sel = byId('ti-ditta');\n        var v = sel && sel.value ? String(sel.value).trim() : '';\n        if (!v && window.tiCurrentDb) v = String(window.tiCurrentDb).trim();\n        return v;\n    }\n    function openOverlay(){\n        var ov = byId('ti-conf-ov');\n        try {\n            if (typeof window.closeProgressPopup === 'function') window.closeProgressPopup();\n            if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(true, 'Configurazione', {notifyUser:false});\n        } catch(e) {}\n        try {\n            if (window.showPluginModal) window.showPluginModal(ov || 'ti-conf-ov');\n            else if (window.openModal) window.openModal('ti-conf-ov');\n            else if (ov) {\n                ov.style.setProperty('display','flex','important');\n                ov.style.setProperty('position','fixed','important');\n                ov.style.setProperty('inset','0','important');\n                ov.style.setProperty('z-index','2147483000','important');\n            }\n        } catch(e) {\n            if (ov) {\n                ov.style.setProperty('display','flex','important');\n                ov.style.setProperty('position','fixed','important');\n                ov.style.setProperty('inset','0','important');\n                ov.style.setProperty('z-index','2147483000','important');\n            }\n        }\n    }\n    function setMsg(html, type){\n        var cnt = byId('ti-conf-cnt');\n        if (!cnt) return;\n        var border = type === 'error' ? '#ef4444' : (type === 'warn' ? '#f59e0b' : '#22c55e');\n        var bg = type === 'error' ? '#450a0a' : (type === 'warn' ? '#422006' : '#052e16');\n        var color = type === 'error' ? '#fecaca' : (type === 'warn' ? '#fde68a' : '#bbf7d0');\n        cnt.innerHTML = '<div id=\"ti-config-rescue-488-msg\" style=\"margin:14px;padding:14px;border-radius:12px;border:1px solid '+border+';background:'+bg+';color:'+color+';font-size:14px;line-height:1.45;\">'+html+'<\/div>';\n        var retry = byId('ti-config-rescue-488-retry');\n        if (retry) retry.onclick = function(ev){ if (ev) ev.preventDefault(); window.__tiConfigOpeningPromise459 = null; window.openConfigSafe488(ev); return false; };\n        var close = byId('ti-config-rescue-488-close');\n        if (close) close.onclick = function(ev){ if (ev) ev.preventDefault(); try { if (window.closeModal) window.closeModal('ti-conf-ov'); } catch(e) {} return false; };\n    }\n    function actionButtons(){\n        return '<br><br><button type=\"button\" id=\"ti-config-rescue-488-retry\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\">Riprova<\/button><button type=\"button\" id=\"ti-config-rescue-488-close\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>';\n    }\n    function markDb(data, db){\n        try { if (data && typeof data === 'object') data.__db_name = db; } catch(e) {}\n        try { window.__tiCurrentDbDataDb = db; window.__tiConfigLoadedDb = db; } catch(e) {}\n    }\n    function cachedData(db){\n        try {\n            var d = window.currentDbData;\n            if (!d || !d.Tabelle) return null;\n            var known = String(d.__db_name || window.__tiCurrentDbDataDb || window.__tiConfigLoadedDb || '').trim();\n            if (known && known !== db) return null;\n            if (!known) markDb(d, db);\n            return d;\n        } catch(e) { return null; }\n    }\n    function renderData(data, db, label){\n        if (!data || !data.Tabelle) throw new Error('Database senza sezione Tabelle.');\n        window.currentDbData = data;\n        markDb(data, db);\n        try { window.configOriginalDbData = JSON.parse(JSON.stringify(data)); } catch(e) { window.configOriginalDbData = data; }\n        try { if (window.modifiedFields && window.modifiedFields.clear) window.modifiedFields.clear(); } catch(e) {}\n        try { window.tiConfigDeletedRowsManifest = []; } catch(e) {}\n        if (typeof window.renderConfig !== 'function') throw new Error('Funzione renderConfig non disponibile.');\n        openOverlay();\n        try { window.renderConfig(); }\n        catch(e) { throw new Error('Errore render Configurazione: ' + (e && e.message ? e.message : e)); }\n        var cnt = byId('ti-conf-cnt');\n        var html = cnt ? String(cnt.innerHTML || '') : '';\n        if (!cnt || !html.trim() || \/Caricamento configurazione in corso\/i.test(html)) {\n            throw new Error('Render Configurazione non completato: contenuto non aggiornato.');\n        }\n        try { if (typeof window.updateConfigSaveButtonState === 'function') window.updateConfigSaveButtonState(); } catch(e) {}\n        try { console.info('Shop & Service configurazione aperta rescue 488: ' + (label || '') + ' - ' + db); } catch(e) {}\n        return true;\n    }\n    function buildFd(action, db){\n        var fd;\n        if (typeof window.buildAjaxFormData === 'function') fd = window.buildAjaxFormData(action, {db:db, ti_config_open:'1', rescue:'488'});\n        else {\n            fd = new FormData();\n            fd.append('action', action);\n            fd.append('ti_action', action);\n            fd.append('db', db);\n            fd.append('ti_config_open','1');\n            fd.append('rescue','488');\n        }\n        try {\n            if (!fd.has('action')) fd.append('action', action);\n            if (!fd.has('ti_action')) fd.append('ti_action', action);\n        } catch(e) {}\n        return fd;\n    }\n    function fetchDb(db, timeoutMs){\n        var urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.ajaxurl && urls.indexOf(window.ajaxurl) === -1) urls.push(window.ajaxurl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            var controller = null, timer = null, done = false;\n            var opts = {method:'POST', body:buildFd('ti_ai_get_db_data', db), credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller = new AbortController(); opts.signal = controller.signal; } } catch(e) {}\n            return new Promise(function(resolve, reject){\n                timer = setTimeout(function(){\n                    if (done) return;\n                    done = true;\n                    try { if (controller) controller.abort(); } catch(e) {}\n                    reject(new Error('Timeout lettura database dopo ' + Math.round((timeoutMs || 18000)\/1000) + ' secondi.'));\n                }, timeoutMs || 18000);\n                fetch(urls[i], opts).then(function(r){\n                    return r.text().then(function(raw){\n                        if (!r.ok) throw new Error('HTTP ' + r.status + ': ' + String(raw || '').slice(0,220));\n                        try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta non JSON: ' + String(raw || '').slice(0,220)); }\n                    });\n                }).then(function(j){ if (done) return; done = true; resolve(j); }).catch(function(err){\n                    if (done) return;\n                    done = true;\n                    if (i + 1 < urls.length) attempt(i + 1).then(resolve).catch(reject);\n                    else reject(err);\n                }).finally(function(){ try { clearTimeout(timer); } catch(e) {} });\n            });\n        }\n        return attempt(0);\n    }\n\n    window.openConfigSafe488 = function(ev){\n        if (ev && ev.preventDefault) ev.preventDefault();\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') {\n            if (window.tiAlert) window.tiAlert('Seleziona prima una ditta valida per aprire la configurazione.');\n            else alert('Seleziona prima una ditta valida per aprire la configurazione.');\n            return false;\n        }\n        window.__tiConfigOpeningPromise459 = null;\n        openOverlay();\n        setMsg('<b>Caricamento configurazione in corso...<\/b><br><span style=\"font-size:12px;\">Apro subito eventuali dati gia disponibili e verifico il database.<\/span>', 'ok');\n\n        var cached = cachedData(db);\n        var cacheOk = false;\n        if (cached) {\n            try {\n                renderData(cached, db, 'cache immediata');\n                cacheOk = true;\n                setTimeout(function(){\n                    var cnt = byId('ti-conf-cnt');\n                    if (cnt) {\n                        var note = document.createElement('div');\n                        note.id = 'ti-config-rescue-488-note';\n                        note.style.cssText = 'position:sticky;top:0;z-index:6;margin:8px 8px 12px 8px;padding:9px 11px;border-radius:10px;border:1px solid #f59e0b;background:#422006;color:#fde68a;font-size:12px;line-height:1.35;';\n                        note.innerHTML = '<b>Configurazione aperta.<\/b> Verifica database in background.';\n                        cnt.insertBefore(note, cnt.firstChild || null);\n                        setTimeout(function(){ if (note && note.parentNode) note.parentNode.removeChild(note); }, 3500);\n                    }\n                }, 60);\n            } catch(cacheErr) {\n                cacheOk = false;\n                setMsg('<b>Caricamento configurazione in corso...<\/b><br>Dati in memoria non utilizzabili: rileggo il database.', 'warn');\n                try { console.warn('Shop & Service config cache render failed', cacheErr); } catch(e) {}\n            }\n        }\n\n        var stillTimer = setTimeout(function(){\n            try {\n                if (!cacheOk) setMsg('<b>Caricamento configurazione ancora in corso...<\/b><br>Il server non ha ancora risposto.' + actionButtons(), 'warn');\n            } catch(e) {}\n        }, 5500);\n\n        fetchDb(db, cacheOk ? 10000 : 18000).then(function(res){\n            clearTimeout(stillTimer);\n            if (!res || !res.success || !res.data) {\n                var msg = res && res.data && res.data.message ? res.data.message : (res && res.message ? res.message : 'Risposta database non valida.');\n                throw new Error(msg);\n            }\n            renderData(res.data, db, cacheOk ? 'refresh server' : 'server');\n            return true;\n        }).catch(function(err){\n            clearTimeout(stillTimer);\n            var m = err && err.message ? err.message : String(err || 'errore sconosciuto');\n            if (cacheOk) {\n                try {\n                    var cnt = byId('ti-conf-cnt');\n                    if (cnt) {\n                        var warn = document.createElement('div');\n                        warn.style.cssText = 'position:sticky;top:0;z-index:6;margin:8px 8px 12px 8px;padding:9px 11px;border-radius:10px;border:1px solid #ef4444;background:#450a0a;color:#fecaca;font-size:12px;line-height:1.35;';\n                        warn.innerHTML = '<b>Verifica database non completata.<\/b> Stai usando i dati gia disponibili.<br>' + esc(m);\n                        cnt.insertBefore(warn, cnt.firstChild || null);\n                    }\n                } catch(e) {}\n                return true;\n            }\n            openOverlay();\n            setMsg('<b>Configurazione non caricata.<\/b><br>' + esc(m) + actionButtons() + '<br><br><span style=\"font-size:12px;\">Il messaggio resta visibile per evitare che la finestra rimanga bloccata senza indicazioni.<\/span>', 'error');\n            try { console.error('Shop & Service CONFIGURA rescue 488 error', err); } catch(e) {}\n            return false;\n        });\n        return false;\n    };\n\n    function bind(){\n        var btn = byId('ti-conf-toggle');\n        if (!btn) return;\n        btn.onclick = window.openConfigSafe488;\n        btn.setAttribute('data-ti-config-open-rescue','488');\n    }\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var btn = t.closest('#ti-conf-toggle');\n        if (!btn) return;\n        try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        return window.openConfigSafe488(ev);\n    }, true);\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bind); else bind();\n    setTimeout(bind, 300); setTimeout(bind, 1200); setInterval(bind, 2500);\n})();\n<\/script>\n\n\n\n<script id=\"ti-config-open-rescue-489\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigOpenRescue489Installed) return;\n    window.__tiConfigOpenRescue489Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function selectedDb(){\n        var sel = byId('ti-ditta');\n        return sel && sel.value ? String(sel.value) : String(window.currentDb || window.tiCurrentDb || '');\n    }\n    function openOverlay(){\n        var ov = byId('ti-conf-ov');\n        if (!ov) return null;\n        try { if (window.preparePluginModal) window.preparePluginModal(ov); } catch(e) {}\n        try { if (window.showPluginModal) window.showPluginModal(ov); else if (window.openModal) window.openModal('ti-conf-ov'); } catch(e) {}\n        try {\n            ov.style.setProperty('display','flex','important');\n            ov.style.setProperty('visibility','visible','important');\n            ov.style.setProperty('opacity','1','important');\n            ov.style.setProperty('pointer-events','auto','important');\n            ov.style.setProperty('z-index','2147483000','important');\n            ov.removeAttribute('aria-hidden');\n        } catch(e) {}\n        return ov;\n    }\n    function buttonHtml(){\n        return '<br><br><button type=\"button\" id=\"ti-config-retry-489\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\">Riprova<\/button><button type=\"button\" id=\"ti-config-close-489\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>';\n    }\n    function setMessage(html, type){\n        openOverlay();\n        var cnt = byId('ti-conf-cnt');\n        if (!cnt) return;\n        var border = type === 'error' ? '#ef4444' : (type === 'warn' ? '#f59e0b' : '#22c55e');\n        var bg = type === 'error' ? '#450a0a' : (type === 'warn' ? '#422006' : '#052e16');\n        var color = type === 'error' ? '#fecaca' : (type === 'warn' ? '#fde68a' : '#bbf7d0');\n        cnt.innerHTML = '<div style=\"margin:14px;padding:14px;border-radius:12px;border:1px solid '+border+';background:'+bg+';color:'+color+';font-size:14px;line-height:1.45;\">'+html+'<\/div>';\n        bindLocalButtons();\n    }\n    function bindLocalButtons(){\n        var r = byId('ti-config-retry-489');\n        if (r) r.onclick = function(ev){ if (ev) ev.preventDefault(); window.openConfigSafe489(ev, true); return false; };\n        var c = byId('ti-config-close-489');\n        if (c) c.onclick = function(ev){ if (ev) ev.preventDefault(); try { if (window.closeModal) window.closeModal('ti-conf-ov'); else { var ov=byId('ti-conf-ov'); if(ov) ov.style.display='none'; } } catch(e) {} return false; };\n    }\n    function buildFd(db){\n        var fd;\n        try { if (window.buildAjaxFormData) fd = window.buildAjaxFormData('ti_ai_get_db_data', {db:db, ti_config_open:'1', rescue:'489'}); } catch(e) {}\n        if (!fd) {\n            fd = new FormData();\n            fd.append('action','ti_ai_get_db_data');\n            fd.append('ti_action','ti_ai_get_db_data');\n            fd.append('db', db);\n            fd.append('ti_config_open','1');\n            fd.append('rescue','489');\n        }\n        try { if (!fd.has('action')) fd.append('action','ti_ai_get_db_data'); if (!fd.has('ti_action')) fd.append('ti_action','ti_ai_get_db_data'); } catch(e) {}\n        return fd;\n    }\n    function fetchDb(db, timeoutMs){\n        var urls=[];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.ajaxurl && urls.indexOf(window.ajaxurl)<0) urls.push(window.ajaxurl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl)<0) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            var controller=null, timer=null, done=false;\n            var opts={method:'POST', body:buildFd(db), credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller=new AbortController(); opts.signal=controller.signal; } } catch(e) {}\n            return new Promise(function(resolve,reject){\n                timer=setTimeout(function(){ if(done) return; done=true; try{ if(controller) controller.abort(); }catch(e){} reject(new Error('Timeout lettura database da '+urls[i])); }, timeoutMs||18000);\n                fetch(urls[i], opts).then(function(r){ return r.text().then(function(raw){ if(!r.ok) throw new Error('HTTP '+r.status+' da '+urls[i]+': '+String(raw||'').slice(0,240)); try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta non JSON da '+urls[i]+': '+String(raw||'').slice(0,240)); } }); }).then(function(j){ if(done) return; done=true; resolve(j); }).catch(function(err){ if(done) return; done=true; if(i+1<urls.length) attempt(i+1).then(resolve).catch(reject); else reject(err); }).finally(function(){ try{clearTimeout(timer);}catch(e){} });\n            });\n        }\n        return attempt(0);\n    }\n    function prepareData(data, db){\n        if (!data || !data.Tabelle) throw new Error('Il database letto non contiene la sezione Tabelle.');\n        window.currentDbData = data;\n        try { data.__db_name = db; window.__tiCurrentDbDataDb = db; window.__tiConfigLoadedDb = db; } catch(e) {}\n        try { window.configOriginalDbData = JSON.parse(JSON.stringify(data)); } catch(e) { window.configOriginalDbData = data; }\n        try { window.configWasSaved = false; } catch(e) {}\n        try { window.tiConfigDeletedRowsManifest = []; } catch(e) {}\n        try { if (window.modifiedFields && window.modifiedFields.clear) window.modifiedFields.clear(); } catch(e) {}\n    }\n    function renderPreparedData(source){\n        if (typeof window.renderConfig !== 'function') throw new Error('Funzione renderConfig non disponibile. Probabile errore JavaScript precedente.');\n        openOverlay();\n        try { window.renderConfig(); } catch(e) { throw new Error('Errore durante renderConfig: '+(e && e.message ? e.message : e)); }\n        try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n        var cnt = byId('ti-conf-cnt');\n        if (!cnt) throw new Error('Contenitore configurazione ti-conf-cnt non trovato dopo il render.');\n        if (!String(cnt.innerHTML || '').trim()) throw new Error('Render Configurazione vuoto.');\n        try { console.info('Shop & Service Configurazione aperta rescue 489: '+source); } catch(e) {}\n    }\n    function cacheIsUsable(db){\n        try {\n            var d = window.currentDbData;\n            if (!d || !d.Tabelle) return null;\n            var known = String(d.__db_name || window.__tiCurrentDbDataDb || window.__tiConfigLoadedDb || '').trim();\n            if (known && known !== db) return null;\n            return d;\n        } catch(e) { return null; }\n    }\n    window.openConfigSafe489 = function(ev, forceReload){\n        if (ev && ev.preventDefault) ev.preventDefault();\n        try { window.__tiConfigOpeningPromise459 = null; window.__tiConfigOpeningDb459 = ''; } catch(e) {}\n        var db = selectedDb();\n        if (!db || db === 'NEW_DB') { if (window.tiAlert) window.tiAlert('Seleziona prima una ditta valida.'); else alert('Seleziona prima una ditta valida.'); return false; }\n        openOverlay();\n        setMessage('<b>Caricamento configurazione in corso...<\/b><br><span style=\"font-size:12px;\">Lettura database e apertura form.<\/span>', 'ok');\n        var cached = !forceReload ? cacheIsUsable(db) : null;\n        if (cached) {\n            try { prepareData(cached, db); renderPreparedData('cache'); } catch(e) { try { console.warn('Shop & Service Config cache non utilizzabile', e); } catch(_) {} }\n        }\n        var slow=setTimeout(function(){ setMessage('<b>Caricamento configurazione ancora in corso...<\/b><br>Il server non ha ancora risposto. Puoi riprovare o chiudere.'+buttonHtml(), 'warn'); }, 6000);\n        fetchDb(db, 22000).then(function(res){\n            clearTimeout(slow);\n            if (!res || !res.success || !res.data) {\n                var msg = res && res.data && res.data.message ? res.data.message : (res && res.message ? res.message : 'Risposta server non valida.');\n                throw new Error(msg);\n            }\n            prepareData(res.data, db);\n            renderPreparedData('server');\n            return true;\n        }).catch(function(err){\n            clearTimeout(slow);\n            var msg = err && err.message ? err.message : String(err || 'errore sconosciuto');\n            if (cacheIsUsable(db)) {\n                try { renderPreparedData('cache dopo errore server'); } catch(e) {}\n                var cnt=byId('ti-conf-cnt');\n                if (cnt) {\n                    var note=document.createElement('div');\n                    note.style.cssText='position:sticky;top:0;z-index:10;margin:8px;padding:9px 11px;border:1px solid #ef4444;border-radius:10px;background:#450a0a;color:#fecaca;font-size:12px;line-height:1.35;';\n                    note.innerHTML='<b>Verifica server non completata.<\/b> Uso dati gia disponibili.<br>'+esc(msg);\n                    cnt.insertBefore(note,cnt.firstChild||null);\n                }\n                return false;\n            }\n            setMessage('<b>Configurazione non caricata.<\/b><br>'+esc(msg)+buttonHtml()+'<br><br><span style=\"font-size:12px;\">Dettaglio in console: Shop & Service CONFIGURA rescue 489 error.<\/span>', 'error');\n            try { console.error('Shop & Service CONFIGURA rescue 489 error', err); } catch(e) {}\n            return false;\n        });\n        return false;\n    };\n    window.openConfigSafe488 = window.openConfigSafe489;\n    window.openConfigSafe218 = window.openConfigSafe489;\n    function bind(){\n        var btn = byId('ti-conf-toggle');\n        if (btn) { btn.onclick = window.openConfigSafe489; btn.setAttribute('data-ti-config-open-rescue','489'); }\n    }\n    document.addEventListener('click', function(ev){\n        var t=ev && ev.target;\n        if (!t || !t.closest) return;\n        var btn=t.closest('#ti-conf-toggle, #ti-config-retry-489, #ti-config-rescue-488-retry');\n        if (!btn) return;\n        try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        return window.openConfigSafe489(ev, btn.id !== 'ti-conf-toggle');\n    }, true);\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bind); else bind();\n    setTimeout(bind,200); setTimeout(bind,1000); setInterval(bind,2500);\n})();\n<\/script>\n\n\n<script id=\"ti-config-open-rescue-490\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigOpenRescue490Installed) return;\n    window.__tiConfigOpenRescue490Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function selectedDb(){\n        var sel = byId('ti-ditta');\n        return sel && sel.value ? String(sel.value) : String(window.currentDb || window.tiCurrentDb || '');\n    }\n    function showOverlay(){\n        var ov = byId('ti-conf-ov');\n        if (!ov) return null;\n        try { if (window.preparePluginModal) window.preparePluginModal(ov); } catch(e) {}\n        try { if (window.showPluginModal) window.showPluginModal(ov); else if (window.openModal) window.openModal('ti-conf-ov'); } catch(e) {}\n        try {\n            ov.style.setProperty('display','flex','important');\n            ov.style.setProperty('visibility','visible','important');\n            ov.style.setProperty('opacity','1','important');\n            ov.style.setProperty('pointer-events','auto','important');\n            ov.style.setProperty('z-index','2147483000','important');\n            ov.removeAttribute('aria-hidden');\n        } catch(e) {}\n        return ov;\n    }\n    function retryButtons(){\n        return '<br><br><button type=\"button\" id=\"ti-config-retry-490\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\">Riprova<\/button><button type=\"button\" id=\"ti-config-close-490\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>';\n    }\n    function setMsg(html, type){\n        showOverlay();\n        var cnt = byId('ti-conf-cnt');\n        if (!cnt) return;\n        var border = type === 'error' ? '#ef4444' : (type === 'warn' ? '#f59e0b' : '#22c55e');\n        var bg = type === 'error' ? '#450a0a' : (type === 'warn' ? '#422006' : '#052e16');\n        var color = type === 'error' ? '#fecaca' : (type === 'warn' ? '#fde68a' : '#bbf7d0');\n        cnt.innerHTML = '<div style=\"margin:14px;padding:14px;border-radius:12px;border:1px solid '+border+';background:'+bg+';color:'+color+';font-size:14px;line-height:1.45;\">'+html+'<\/div>';\n        bindLocal();\n    }\n    function bindLocal(){\n        var r = byId('ti-config-retry-490');\n        if (r) r.onclick = function(ev){ if (ev) ev.preventDefault(); return window.openConfigSafe490(ev, true); };\n        var c = byId('ti-config-close-490');\n        if (c) c.onclick = function(ev){ if (ev) ev.preventDefault(); try { if (window.closeModal) window.closeModal('ti-conf-ov'); else { var ov=byId('ti-conf-ov'); if(ov) ov.style.display='none'; } } catch(e) {} return false; };\n    }\n    function makeFd(db){\n        var fd;\n        try { if (window.buildAjaxFormData) fd = window.buildAjaxFormData('ti_ai_get_db_data', {db:db, ti_config_open:'1', rescue:'490'}); } catch(e) {}\n        if (!fd) { fd = new FormData(); fd.append('action','ti_ai_get_db_data'); fd.append('ti_action','ti_ai_get_db_data'); fd.append('db',db); fd.append('ti_config_open','1'); fd.append('rescue','490'); }\n        try { if (!fd.has('action')) fd.append('action','ti_ai_get_db_data'); if (!fd.has('ti_action')) fd.append('ti_action','ti_ai_get_db_data'); } catch(e) {}\n        return fd;\n    }\n    function fetchDb(db, timeoutMs){\n        var urls=[];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.ajaxurl && urls.indexOf(window.ajaxurl)<0) urls.push(window.ajaxurl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl)<0) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            var controller=null, timer=null, done=false;\n            var opts={method:'POST',body:makeFd(db),credentials:'same-origin',cache:'no-store'};\n            try { if (window.AbortController) { controller=new AbortController(); opts.signal=controller.signal; } } catch(e) {}\n            return new Promise(function(resolve,reject){\n                timer=setTimeout(function(){ if(done) return; done=true; try{if(controller)controller.abort();}catch(e){} reject(new Error('Timeout lettura database da '+urls[i])); }, timeoutMs||22000);\n                fetch(urls[i],opts).then(function(r){ return r.text().then(function(raw){ if(!r.ok) throw new Error('HTTP '+r.status+' da '+urls[i]+': '+String(raw||'').slice(0,300)); try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta non JSON da '+urls[i]+': '+String(raw||'').slice(0,300)); } }); }).then(function(j){ if(done) return; done=true; resolve(j); }).catch(function(err){ if(done) return; done=true; if(i+1<urls.length) attempt(i+1).then(resolve).catch(reject); else reject(err); }).finally(function(){ try{clearTimeout(timer);}catch(e){} });\n            });\n        }\n        return attempt(0);\n    }\n    function cache(db){\n        try {\n            var d=window.currentDbData;\n            if(!d || !d.Tabelle) return null;\n            var known=String(d.__db_name || window.__tiCurrentDbDataDb || window.__tiConfigLoadedDb || '').trim();\n            if(known && known !== db) return null;\n            return d;\n        } catch(e) { return null; }\n    }\n    function prepare(data, db){\n        if(!data || !data.Tabelle) throw new Error('Il database letto non contiene la sezione Tabelle.');\n        window.currentDbData=data;\n        try { data.__db_name=db; window.__tiCurrentDbDataDb=db; window.__tiConfigLoadedDb=db; window.currentDb=db; window.tiCurrentDb=db; } catch(e) {}\n        try { window.configOriginalDbData=JSON.parse(JSON.stringify(data)); } catch(e) { window.configOriginalDbData=data; }\n        try { window.configWasSaved=false; } catch(e) {}\n        try { window.tiConfigDeletedRowsManifest=[]; } catch(e) {}\n        try { if(window.modifiedFields && window.modifiedFields.clear) window.modifiedFields.clear(); } catch(e) {}\n    }\n    function hasConfigContent(){\n        var cnt=byId('ti-conf-cnt');\n        if(!cnt) return false;\n        var html=String(cnt.innerHTML||'').trim();\n        if(!html) return false;\n        if(html.indexOf('Caricamento configurazione')>=0 || html.indexOf('Configurazione non caricata')>=0) return false;\n        return true;\n    }\n    function render(source){\n        if(typeof window.renderConfig !== 'function') throw new Error('Funzione renderConfig non disponibile. Probabile errore JavaScript precedente.');\n        showOverlay();\n        try { window.renderConfig(); } catch(e) { throw new Error('Errore durante renderConfig: '+(e && e.message ? e.message : e)); }\n        try { if(window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) { try { console.warn('Shop & Service updateConfigSaveButtonState warning', e); } catch(_) {} }\n        var cnt=byId('ti-conf-cnt');\n        if(!cnt) throw new Error('Contenitore configurazione ti-conf-cnt non trovato dopo il render.');\n        if(!String(cnt.innerHTML||'').trim()) throw new Error('Render Configurazione vuoto.');\n        try { console.info('Shop & Service Configurazione aperta rescue 490: '+source); } catch(e) {}\n        return true;\n    }\n    window.openConfigSafe490 = function(ev, forceReload){\n        if(ev && ev.preventDefault) ev.preventDefault();\n        var db=selectedDb();\n        if(!db || db==='NEW_DB') { if(window.tiAlert) window.tiAlert('Seleziona prima una ditta valida.'); else alert('Seleziona prima una ditta valida.'); return false; }\n        try { window.__tiConfigOpeningPromise459=null; window.__tiConfigOpeningDb459=''; } catch(e) {}\n        window.__tiConfigRenderedOk490=false;\n        showOverlay();\n        setMsg('<b>Caricamento configurazione in corso...<\/b><br><span style=\"font-size:12px;\">Lettura database e apertura form.<\/span>', 'ok');\n        var cached=!forceReload ? cache(db) : null;\n        if(cached) {\n            try { prepare(cached,db); window.__tiConfigRenderedOk490=render('cache'); } catch(e) { try { console.warn('Shop & Service Config cache non utilizzabile 490', e); } catch(_) {} }\n        }\n        var slow=setTimeout(function(){ if(window.__tiConfigRenderedOk490 || hasConfigContent()) return; setMsg('<b>Caricamento configurazione ancora in corso...<\/b><br>Il server non ha ancora risposto. Puoi riprovare o chiudere.'+retryButtons(), 'warn'); }, 7000);\n        fetchDb(db,26000).then(function(res){\n            clearTimeout(slow);\n            if(!res || !res.success || !res.data) {\n                var msg=res && res.data && res.data.message ? res.data.message : (res && res.message ? res.message : 'Risposta server non valida.');\n                throw new Error(msg);\n            }\n            prepare(res.data,db);\n            window.__tiConfigRenderedOk490=render('server');\n            return true;\n        }).catch(function(err){\n            clearTimeout(slow);\n            var msg=err && err.message ? err.message : String(err||'errore sconosciuto');\n            if(window.__tiConfigRenderedOk490 || hasConfigContent()) {\n                try { console.warn('Shop & Service CONFIGURA rescue 490 warning dopo render riuscito', err); } catch(e) {}\n                return false;\n            }\n            var c=cache(db);\n            if(c) {\n                try { prepare(c,db); window.__tiConfigRenderedOk490=render('cache dopo errore server'); return false; } catch(e) { msg += ' | Fallback cache non riuscito: '+(e && e.message ? e.message : e); }\n            }\n            setMsg('<b>Configurazione non caricata.<\/b><br>'+esc(msg)+retryButtons()+'<br><br><span style=\"font-size:12px;\">Apri la console per il dettaglio: Shop & Service CONFIGURA rescue 490 error.<\/span>', 'error');\n            try { console.error('Shop & Service CONFIGURA rescue 490 error', err); } catch(e) {}\n            return false;\n        });\n        return false;\n    };\n    window.openConfigSafe489=window.openConfigSafe490;\n    window.openConfigSafe488=window.openConfigSafe490;\n    window.openConfigSafe218=window.openConfigSafe490;\n    function bind(){\n        var btn=byId('ti-conf-toggle');\n        if(btn) { btn.onclick=window.openConfigSafe490; btn.setAttribute('data-ti-config-open-rescue','490'); }\n    }\n    document.addEventListener('click',function(ev){\n        var t=ev && ev.target;\n        if(!t || !t.closest) return;\n        var btn=t.closest('#ti-conf-toggle,#ti-config-retry-490,#ti-config-retry-489,#ti-config-rescue-488-retry');\n        if(!btn) return;\n        try { ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        return window.openConfigSafe490(ev, btn.id !== 'ti-conf-toggle');\n    },true);\n    if(document.readyState==='loading') document.addEventListener('DOMContentLoaded', bind); else bind();\n    setTimeout(bind,200); setTimeout(bind,1000); setInterval(bind,2500);\n})();\n<\/script>\n\n\n\n<script id=\"ti-config-open-cancel-safe-492\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigOpenCancelSafe492Installed) return;\n    window.__tiConfigOpenCancelSafe492Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(s){ return String(s == null ? '' : s).replace(\/[&<>\"']\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c];}); }\n    function selectedDb(){ var s=byId('ti-ditta'); return s && s.value ? String(s.value) : String(window.currentDb || window.tiCurrentDb || ''); }\n    function modal(){ return byId('ti-conf-ov'); }\n    function content(){ return byId('ti-conf-cnt'); }\n    function newToken(){ var t='cfg492_'+Date.now()+'_'+Math.random().toString(36).slice(2,8); window.__tiConfigActiveToken492=t; window.__tiConfigCancelled492=false; return t; }\n    function isActive(t){ return !!t && window.__tiConfigActiveToken492===t && !window.__tiConfigCancelled492; }\n    function cancelOpening(){ window.__tiConfigCancelled492=true; window.__tiConfigActiveToken492=''; try{ window.__tiConfigOpeningPromise459=null; window.__tiConfigOpeningDb459=''; }catch(e){} }\n    function show(){\n        var ov=modal(); if(!ov) return null;\n        try { if (window.preparePluginModal) window.preparePluginModal(ov); } catch(e) {}\n        try { if (window.showPluginModal) window.showPluginModal(ov); else if (window.openModal) window.openModal('ti-conf-ov'); } catch(e) {}\n        try {\n            ov.style.setProperty('display','flex','important');\n            ov.style.setProperty('visibility','visible','important');\n            ov.style.setProperty('opacity','1','important');\n            ov.style.setProperty('pointer-events','auto','important');\n            ov.style.setProperty('z-index','2147483000','important');\n            ov.removeAttribute('aria-hidden');\n        } catch(e) {}\n        return ov;\n    }\n    function hide(){ try{ if(window.closeModal) window.closeModal('ti-conf-ov'); else { var o=modal(); if(o) o.style.display='none'; } }catch(e){ var o=modal(); if(o) o.style.display='none'; } }\n    function retryHtml(){ return '<br><br><button type=\"button\" id=\"ti-config-retry-492\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\">Riprova<\/button><button type=\"button\" id=\"ti-config-close-492\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>'; }\n    function bindRetry(){\n        var r=byId('ti-config-retry-492'); if(r) r.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation&&ev.stopPropagation();} return window.openConfigSafe492(ev,true); };\n        var c=byId('ti-config-close-492'); if(c) c.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation&&ev.stopPropagation();} cancelOpening(); hide(); return false; };\n    }\n    function msg(html,type,tok){\n        if(tok && !isActive(tok)) return;\n        show(); var c=content(); if(!c) return;\n        var border=type==='error'?'#ef4444':(type==='warn'?'#f59e0b':'#22c55e');\n        var bg=type==='error'?'#450a0a':(type==='warn'?'#422006':'#052e16');\n        var color=type==='error'?'#fecaca':(type==='warn'?'#fde68a':'#bbf7d0');\n        c.innerHTML='<div style=\"margin:14px;padding:14px;border-radius:12px;border:1px solid '+border+';background:'+bg+';color:'+color+';font-size:14px;line-height:1.45;\">'+html+'<\/div>';\n        bindRetry();\n    }\n    function makeFd(db){\n        var fd=null;\n        try { if(window.buildAjaxFormData) fd=window.buildAjaxFormData('ti_ai_get_db_data',{db:db,ti_config_open:'1',rescue:'492'}); } catch(e) {}\n        if(!fd){ fd=new FormData(); fd.append('action','ti_ai_get_db_data'); fd.append('ti_action','ti_ai_get_db_data'); fd.append('db',db); fd.append('ti_config_open','1'); fd.append('rescue','492'); }\n        try { if(!fd.has('action')) fd.append('action','ti_ai_get_db_data'); if(!fd.has('ti_action')) fd.append('ti_action','ti_ai_get_db_data'); } catch(e) {}\n        return fd;\n    }\n    function fetchDb(db,timeoutMs,tok){\n        var urls=[];\n        if(window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if(window.ajaxurl && urls.indexOf(window.ajaxurl)<0) urls.push(window.ajaxurl);\n        if(window.tiUrl && urls.indexOf(window.tiUrl)<0) urls.push(window.tiUrl);\n        if(!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            if(!isActive(tok)) return Promise.reject(new Error('Apertura configurazione annullata.'));\n            var ctrl=null,timer=null,done=false;\n            var opts={method:'POST',body:makeFd(db),credentials:'same-origin',cache:'no-store'};\n            try{ if(window.AbortController){ ctrl=new AbortController(); opts.signal=ctrl.signal; } }catch(e){}\n            try{ window.__tiConfigAbort492=ctrl; }catch(e){}\n            return new Promise(function(resolve,reject){\n                timer=setTimeout(function(){ if(done) return; done=true; try{if(ctrl)ctrl.abort();}catch(e){} reject(new Error('Timeout lettura database da '+urls[i])); }, timeoutMs||26000);\n                fetch(urls[i],opts).then(function(r){return r.text().then(function(raw){ if(!r.ok) throw new Error('HTTP '+r.status+' da '+urls[i]+': '+String(raw||'').slice(0,300)); try{return JSON.parse(raw);}catch(e){throw new Error('Risposta non JSON da '+urls[i]+': '+String(raw||'').slice(0,300));} });}).then(function(j){ if(done)return; done=true; if(!isActive(tok)) reject(new Error('Apertura configurazione annullata.')); else resolve(j); }).catch(function(err){ if(done)return; done=true; if(!isActive(tok)){ reject(new Error('Apertura configurazione annullata.')); return; } if(i+1<urls.length) attempt(i+1).then(resolve).catch(reject); else reject(err); }).finally(function(){try{clearTimeout(timer);}catch(e){}});\n            });\n        }\n        return attempt(0);\n    }\n    function cache(db){\n        try{ var d=window.currentDbData; if(!d||!d.Tabelle)return null; var known=String(d.__db_name||window.__tiCurrentDbDataDb||window.__tiConfigLoadedDb||'').trim(); if(known&&known!==db)return null; return d; }catch(e){return null;}\n    }\n    function prepare(data,db){\n        if(!data||!data.Tabelle) throw new Error('Il database letto non contiene la sezione Tabelle.');\n        window.currentDbData=data;\n        try{ data.__db_name=db; window.__tiCurrentDbDataDb=db; window.__tiConfigLoadedDb=db; window.currentDb=db; window.tiCurrentDb=db; }catch(e){}\n        try{ window.configOriginalDbData=JSON.parse(JSON.stringify(data)); }catch(e){ window.configOriginalDbData=data; }\n        try{ window.configWasSaved=false; window.tiConfigDeletedRowsManifest=[]; }catch(e){}\n        try{ if(window.modifiedFields&&window.modifiedFields.clear) window.modifiedFields.clear(); }catch(e){}\n    }\n    function goodContent(){ var c=content(); if(!c)return false; var h=String(c.innerHTML||'').trim(); return !!(h && h.indexOf('Caricamento configurazione')<0 && h.indexOf('Configurazione non caricata')<0); }\n    function render(source,tok){\n        if(tok && !isActive(tok)) return false;\n        if(typeof window.renderConfig!=='function') throw new Error('Funzione renderConfig non disponibile. Probabile errore JavaScript precedente.');\n        show();\n        try{ window.renderConfig(); }catch(e){ throw new Error('Errore durante renderConfig: '+(e&&e.message?e.message:e)); }\n        if(tok && !isActive(tok)) return false;\n        var c=content(); if(!c) throw new Error('Contenitore configurazione ti-conf-cnt non trovato dopo il render.');\n        if(!String(c.innerHTML||'').trim()) throw new Error('Render Configurazione vuoto.');\n        window.__tiConfigRenderedOk492=true;\n        window.__tiConfigLastGoodHtml492=String(c.innerHTML||'');\n        window.__tiConfigLastGoodTime492=Date.now();\n        try{ if(window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); }catch(e){ try{console.warn('Shop & Service updateConfigSaveButtonState warning 492',e);}catch(_){} }\n        try{ console.info('Shop & Service Configurazione aperta rescue 492: '+source); }catch(e){}\n        return true;\n    }\n    function protectAfterRender(){\n        var c=content(); if(!c || !window.__tiConfigLastGoodHtml492) return;\n        var h=String(c.innerHTML||'');\n        if(h.indexOf('Caricamento configurazione')>=0 || h.indexOf('Configurazione non caricata')>=0){ c.innerHTML=window.__tiConfigLastGoodHtml492; try{ console.warn('Shop & Service Config 492: ripristinato contenuto dopo sovrascrittura legacy.'); }catch(e){} }\n    }\n    window.openConfigSafe492=function(ev,forceReload){\n        if(ev&&ev.preventDefault) ev.preventDefault();\n        try{ if(ev&&ev.stopPropagation) ev.stopPropagation(); if(ev&&ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }catch(e){}\n        var db=selectedDb();\n        if(!db||db==='NEW_DB'){ if(window.tiAlert) window.tiAlert('Seleziona prima una ditta valida.'); else alert('Seleziona prima una ditta valida.'); return false; }\n        try{ if(window.__tiConfigAbort492) window.__tiConfigAbort492.abort(); }catch(e){}\n        try{ window.__tiConfigOpeningPromise459=null; window.__tiConfigOpeningDb459=''; }catch(e){}\n        var tok=newToken();\n        window.__tiConfigRenderedOk492=false;\n        show();\n        var cached=!forceReload?cache(db):null;\n        if(cached){ try{ prepare(cached,db); render('cache primo click',tok); setTimeout(protectAfterRender,150); setTimeout(protectAfterRender,900); }catch(e){ try{console.warn('Shop & Service cache config non utilizzabile 492',e);}catch(_){} } }\n        if(!goodContent()) msg('<b>Caricamento configurazione in corso...<\/b><br><span style=\"font-size:12px;\">Lettura database e apertura form.<\/span>','ok',tok);\n        var slow=setTimeout(function(){ if(!isActive(tok)||window.__tiConfigRenderedOk492||goodContent()) return; msg('<b>Caricamento configurazione ancora in corso...<\/b><br>Il server non ha ancora risposto. Puoi riprovare o chiudere.'+retryHtml(),'warn',tok); },7000);\n        fetchDb(db,28000,tok).then(function(res){\n            clearTimeout(slow); if(!isActive(tok)) return false;\n            if(!res||!res.success||!res.data){ var m=res&&res.data&&res.data.message?res.data.message:(res&&res.message?res.message:'Risposta server non valida.'); throw new Error(m); }\n            prepare(res.data,db); render('server primo click',tok); setTimeout(protectAfterRender,150); setTimeout(protectAfterRender,900); setTimeout(protectAfterRender,2200); return true;\n        }).catch(function(err){\n            clearTimeout(slow); if(!isActive(tok)) { try{ console.info('Shop & Service CONFIGURA 492 richiesta ignorata dopo chiusura\/nuovo tentativo.'); }catch(e){} return false; }\n            var m=err&&err.message?err.message:String(err||'errore sconosciuto');\n            if(window.__tiConfigRenderedOk492||goodContent()){ try{console.warn('Shop & Service CONFIGURA rescue 492 warning dopo render riuscito',err);}catch(e){} protectAfterRender(); return false; }\n            var c=cache(db); if(c){ try{ prepare(c,db); render('cache dopo errore server',tok); return false; }catch(e){ m+=' | Fallback cache non riuscito: '+(e&&e.message?e.message:e); } }\n            msg('<b>Configurazione non caricata.<\/b><br>'+esc(m)+retryHtml()+'<br><br><span style=\"font-size:12px;\">Apri la console per il dettaglio: Shop & Service CONFIGURA rescue 492 error.<\/span>','error',tok);\n            try{ console.error('Shop & Service CONFIGURA rescue 492 error',err); }catch(e){}\n            return false;\n        });\n        return false;\n    };\n    window.openConfigSafe491=window.openConfigSafe492;\n    window.openConfigSafe490=window.openConfigSafe492;\n    window.openConfigSafe489=window.openConfigSafe492;\n    window.openConfigSafe488=window.openConfigSafe492;\n    window.openConfigSafe218=window.openConfigSafe492;\n    var prevClose=window.closeModal;\n    if(typeof prevClose==='function' && !prevClose.__tiConfigCancel492){ window.closeModal=function(id){ if(id==='ti-conf-ov'){ cancelOpening(); try{ if(window.__tiConfigAbort492) window.__tiConfigAbort492.abort(); }catch(e){} } return prevClose.apply(this,arguments); }; window.closeModal.__tiConfigCancel492=true; }\n    function bind(){ var b=byId('ti-conf-toggle'); if(b){ b.onclick=window.openConfigSafe492; b.setAttribute('data-ti-config-open-rescue','492'); } }\n    document.addEventListener('click',function(ev){\n        var t=ev&&ev.target; if(!t||!t.closest)return;\n        var close=t.closest('#ti-config-close-492,#ti-config-close-491,#ti-config-close-490,#ti-config-close-489'); if(close){ try{ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation)ev.stopImmediatePropagation(); }catch(e){} cancelOpening(); hide(); return false; }\n        var b=t.closest('#ti-conf-toggle,#ti-config-retry-492,#ti-config-retry-491,#ti-config-retry-490,#ti-config-retry-489,#ti-config-rescue-488-retry'); if(!b)return;\n        try{ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation)ev.stopImmediatePropagation(); }catch(e){}\n        return window.openConfigSafe492(ev,b.id!=='ti-conf-toggle');\n    },true);\n    if(document.readyState==='loading') document.addEventListener('DOMContentLoaded',bind); else bind();\n    setTimeout(bind,0); setTimeout(bind,200); setTimeout(bind,1000); setInterval(bind,2000);\n})();\n<\/script>\n\n\n\n<script id=\"ti-config-ai-focus-end-493\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigAiFocusEnd493Installed) return;\n    window.__tiConfigAiFocusEnd493Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function box(){ return byId('ti-conf-ai-prompt'); }\n    function visible(el){\n        if (!el) return false;\n        try {\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden')) return false;\n            return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n        } catch(e) { return true; }\n    }\n    function isConfigTableEditorActive(){\n        try {\n            var a = document.activeElement;\n            return !!(a && a !== document.body && a !== document.documentElement && a.matches && a.matches('#ti-conf-ov .ti-cfg-table input, #ti-conf-ov .ti-cfg-table textarea, #ti-conf-ov .ti-cfg-table select, #ti-conf-ov .ti-cfg-filter, .ti-conf-window .ti-cfg-table input, .ti-conf-window .ti-cfg-table textarea, .ti-conf-window .ti-cfg-table select, .ti-conf-window .ti-cfg-filter'));\n        } catch(e) { return false; }\n    }\n    function placeEnd(focusMode){\n        if (isConfigTableEditorActive()) return false;\n        var p = box();\n        if (!p || !visible(p)) return false;\n        try {\n            var len = String(p.value || '').length;\n            if (focusMode !== false && document.activeElement !== p && !isConfigTableEditorActive()) p.focus({preventScroll:true});\n            p.selectionStart = len;\n            p.selectionEnd = len;\n            p.scrollTop = p.scrollHeight;\n        } catch(e) {}\n        return true;\n    }\n    window.tiConfigAiFocusEnd493 = placeEnd;\n    function schedule(focusMode){\n        \/\/ Non rubare mai il focus ai campi delle tabelle Configurazione.\n        if (isConfigTableEditorActive()) return;\n        setTimeout(function(){ if (!isConfigTableEditorActive()) placeEnd(focusMode); }, 0);\n        setTimeout(function(){ if (!isConfigTableEditorActive()) placeEnd(focusMode); }, 60);\n        setTimeout(function(){ if (!isConfigTableEditorActive()) placeEnd(focusMode); }, 220);\n    }\n    function installTextareaHandlers(){\n        var p = box();\n        if (!p || p.__tiFocusEnd493) return;\n        p.__tiFocusEnd493 = true;\n        ['focus','click','mouseup','keyup','input','change'].forEach(function(evName){\n            p.addEventListener(evName, function(){ schedule(true); }, true);\n        });\n        schedule(false);\n    }\n    function wrap(name){\n        var fn = window[name];\n        if (typeof fn !== 'function' || fn.__tiFocusEnd493) return;\n        window[name] = function(){\n            var ret = fn.apply(this, arguments);\n            schedule(true);\n            return ret;\n        };\n        window[name].__tiFocusEnd493 = true;\n    }\n    function boot(){\n        installTextareaHandlers();\n        wrap('runConfigBot');\n        \/\/ 30.9.664: non avvolgere renderConfig; il focus automatico sul prompt AI rubava l'edit ai campi tabellari.\n        \/\/ wrap('renderConfig');\n        wrap('openConfigSafe493');\n        wrap('openConfigSafe492');\n        wrap('openConfigSafe491');\n        wrap('openConfigSafe490');\n        wrap('setConfigAiReplyHtml');\n        wrap('tiAppendConfigAiToUnifiedChat483');\n        wrap('tiAppendConfigAiToUnifiedChat482');\n        if (document.activeElement === box()) schedule(true);\n    }\n    document.addEventListener('focusin', function(ev){\n        if (ev && ev.target && ev.target.id === 'ti-conf-ai-prompt') schedule(true);\n    }, true);\n    document.addEventListener('click', function(ev){\n        try {\n            if (ev && ev.target && ev.target.closest && (ev.target.closest('#ti-conf-ai-send') || ev.target.closest('#ti-conf-ai-chat-actions'))) schedule(true);\n        } catch(e) {}\n    }, true);\n    if (typeof MutationObserver !== 'undefined') {\n        try { new MutationObserver(function(){ boot(); }).observe(document.documentElement, {childList:true, subtree:true}); } catch(e) {}\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);\n    boot();\n    setTimeout(boot, 300);\n    setTimeout(boot, 1000);\n    setInterval(function(){\n        var p = box();\n        if (p && document.activeElement === p) placeEnd(true);\n    }, 900);\n})();\n<\/script>\n\n\n\n<script id=\"ti-global-action-unblock-494\">\n(function(){\n    \"use strict\";\n    if (window.__tiGlobalActionUnblock494Installed) return;\n    window.__tiGlobalActionUnblock494Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function setImp(el, prop, val){ try { if (el && el.style) el.style.setProperty(prop, String(val), 'important'); } catch(e) {} }\n    function isVisible(el){\n        if (!el) return false;\n        try {\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0')) return false;\n            return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n        } catch(e) { return false; }\n    }\n    function closeLoaderHard(){\n        try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'Operazione completata', {notifyUser:false}); } catch(e) {}\n        try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n        try {\n            var ov = byId('ti-ai-loader-ov');\n            if (ov) {\n                setImp(ov, 'display', 'none');\n                setImp(ov, 'visibility', 'hidden');\n                setImp(ov, 'opacity', '0');\n                setImp(ov, 'pointer-events', 'none');\n                ov.setAttribute('aria-hidden', 'true');\n            }\n        } catch(e) {}\n        try {\n            var root = byId('ti-ai-outer');\n            if (root) {\n                ['ti-mainform-blocked-494','ti-mainform-blocked-469','ti-mainform-blocked-464','ti-mainform-blocked-463'].forEach(function(c){ try { root.classList.remove(c); } catch(e) {} });\n                root.setAttribute('aria-busy', 'false');\n            }\n        } catch(e) {}\n    }\n    function resetGlobalUi(success, text){\n        var btn = byId('global-action-btn');\n        if (btn) {\n            btn.disabled = false;\n            btn.innerText = 'Esegui Modifica su Tutti i DB';\n            setImp(btn, 'pointer-events', 'auto');\n            setImp(btn, 'opacity', '1');\n        }\n        try { if (window.finishGlobalActionProgress) window.finishGlobalActionProgress(success !== false, text || (success === false ? 'Operazione interrotta' : 'Operazione completata')); } catch(e) {}\n        closeLoaderHard();\n        try { if (window.updatePluginScrollLock) window.updatePluginScrollLock(); } catch(e) {}\n    }\n    function showResult(html, color){\n        var res = byId('global-action-res');\n        if (!res) return;\n        res.style.display = 'block';\n        if (color) res.style.color = color;\n        res.innerHTML = html;\n        try { res.scrollTop = res.scrollHeight; } catch(e) {}\n    }\n    function buildFd(prompt, previewToken, definitiveConfirm, tableScope){\n        var fd = new FormData();\n        fd.append('action', 'ti_ai_global_action');\n        fd.append('ti_action', 'ti_ai_global_action');\n        fd.append('prompt', prompt || '');\n        fd.append('global_confirm', '1');\n        if (previewToken) fd.append('global_template_preview_token', previewToken);\n        if (definitiveConfirm) fd.append('global_template_confirm', '1');\n        if (tableScope) fd.append('global_table_scope', tableScope);\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        return fd;\n    }\n    function postJson(url, fd, timeoutMs){\n        return new Promise(function(resolve, reject){\n            var done = false;\n            var controller = null;\n            var timer = null;\n            var opts = {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'};\n            try { if (window.AbortController) { controller = new AbortController(); opts.signal = controller.signal; } } catch(e) {}\n            timer = setTimeout(function(){\n                if (done) return;\n                done = true;\n                try { if (controller) controller.abort(); } catch(e) {}\n                reject(new Error('Timeout: il server non ha restituito la risposta finale. Verifica log\/risultato e riprova solo se necessario.'));\n            }, timeoutMs || 900000);\n            fetch(url || window.tiUrl || window.location.href, opts).then(function(r){\n                return r.text().then(function(raw){\n                    if (!r.ok) throw new Error('HTTP ' + r.status + ': ' + String(raw || '').slice(0, 300));\n                    try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta server non JSON: ' + String(raw || '').slice(0, 300)); }\n                });\n            }).then(function(d){ if (done) return; done = true; resolve(d); }).catch(function(err){ if (done) return; done = true; reject(err); }).finally(function(){ try { clearTimeout(timer); } catch(e) {} });\n        });\n    }\n    function openConfirm(html, onYes, onNo){\n        if (typeof window.tiConfirmYesNoAction === 'function') {\n            window.tiConfirmYesNoAction(html, onYes, function(){ resetGlobalUi(false, 'Operazione annullata'); if (onNo) onNo(); }, 'APPROVA MODIFICA DEFINITIVA', 'ANNULLA');\n        } else if (window.confirm(String(html || '').replace(\/<[^>]+>\/g, ' '))) {\n            onYes();\n        } else {\n            resetGlobalUi(false, 'Operazione annullata');\n            if (onNo) onNo();\n        }\n    }\n    function startProgress(definitiveConfirm){\n        var btn = byId('global-action-btn');\n        if (btn) {\n            btn.disabled = true;\n            btn.innerText = definitiveConfirm ? 'Esecuzione definitiva in corso...' : 'Generazione preview...';\n        }\n        showResult(definitiveConfirm ? 'Esecuzione definitiva in corso. Attendi la risposta finale: la UI verr\u00e0 sbloccata automaticamente.' : 'Genero una preview. Nessun database reale viene modificato in questa fase.', '#cbd5e1');\n        try { if (window.startGlobalActionProgress) window.startGlobalActionProgress(definitiveConfirm ? 'Avvio modifica globale definitiva...' : 'Generazione preview obbligatoria...'); } catch(e) {}\n        \/\/ La modifica globale usa la propria barra interna; non deve ereditare loader o AbortController di altri processi.\n        try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n    }\n    function processResponse(prompt, d, previewToken, definitiveConfirm, tableScope){\n        if (!d || !d.success) {\n            var msg = (d && d.data && (d.data.message || d.data.error)) ? (d.data.message || d.data.error) : 'Errore modifica globale.';\n            resetGlobalUi(false, 'Errore modifica globale');\n            showResult(\"<span style='color:#ef4444;'>Errore: \" + esc(msg) + '<\/span>', '#ef4444');\n            return;\n        }\n        var data = d.data || {};\n        if (data.requires_table_scope) {\n            resetGlobalUi(false, 'Ambito tabelle richiesto');\n            showResult(esc(data.reply || 'Indica le tabelle prima di eseguire la modifica globale.').replace(\/\\n\/g,'<br>'), '#fbbf24');\n            var answer = window.prompt(data.scope_prompt || 'Scrivi TUTTE oppure elenca le tabelle separate da virgola:', '');\n            if (answer && String(answer).trim()) {\n                setTimeout(function(){ window.doGlobalActionConfirmed(prompt, previewToken, definitiveConfirm, String(answer).trim()); }, 50);\n            } else {\n                showResult(\"<span style='color:#f59e0b;'>Operazione sospesa. Nessun database \u00e8 stato modificato.<\/span>\", '#f59e0b');\n            }\n            return;\n        }\n        if (data.requires_template_preview) {\n            resetGlobalUi(false, 'Preview generata. In attesa di approvazione');\n            var token = data.preview_token || '';\n            showResult(esc(data.reply || 'Preview generata. Nessun database modificato.').replace(\/\\n\/g,'<br>'), '#fbbf24');\n            openConfirm(data.preview_html || 'Preview generata. Confermi la modifica definitiva?', function(){\n                setTimeout(function(){ window.doGlobalActionConfirmed(prompt, token, true, tableScope); }, 80);\n            }, function(){\n                showResult(\"<span style='color:#f59e0b;'>Operazione annullata. Nessun database \u00e8 stato modificato.<\/span>\", '#f59e0b');\n            });\n            return;\n        }\n        var s = data.stats || {};\n        var finalMsg = 'Operazione globale completata';\n        if (s && (typeof s.db_modified !== 'undefined' || typeof s.tables_touched !== 'undefined' || typeof s.fields_touched !== 'undefined')) {\n            finalMsg = 'Completato: DB ' + (s.db_modified || 0) + ', tabelle ' + (s.tables_touched || 0) + ', campi ' + (s.fields_touched || 0);\n        }\n        resetGlobalUi(true, finalMsg);\n        var reply = data.reply || finalMsg;\n        showResult('<pre style=\"white-space:pre-wrap;margin:0;color:#10b981;\">' + esc(reply) + '<\/pre>', '#10b981');\n        try { console.info('Shop & Service Modifica Globale completata 494', data); } catch(e) {}\n    }\n    window.doGlobalActionConfirmed494 = function(prompt, previewToken, definitiveConfirm, tableScope){\n        prompt = String(prompt || '').trim();\n        if (!prompt) { if (window.tiAlert) window.tiAlert('Inserisci l operazione che vuoi eseguire.'); return false; }\n        startProgress(!!definitiveConfirm);\n        var fd = buildFd(prompt, previewToken, definitiveConfirm, tableScope);\n        postJson(window.tiUrl || window.location.href, fd, definitiveConfirm ? 900000 : 240000).then(function(d){\n            processResponse(prompt, d, previewToken, definitiveConfirm, tableScope);\n        }).catch(function(err){\n            resetGlobalUi(false, 'Errore o timeout modifica globale');\n            showResult(\"<span style='color:#ef4444;'>Operazione non conclusa correttamente lato interfaccia: \" + esc(err && err.message ? err.message : 'errore sconosciuto') + \"<\/span><br><br><span style='color:#fbbf24;'>Se il server ha gi\u00e0 applicato modifiche, riapri la Configurazione o controlla i database prima di ripetere il comando.<\/span>\", '#ef4444');\n            try { console.error('Shop & Service Modifica Globale 494 error', err); } catch(e) {}\n        });\n        return false;\n    };\n    if (typeof window.doGlobalActionConfirmed === 'function' && !window.doGlobalActionConfirmed.__tiUnblock494) {\n        window.doGlobalActionConfirmed = window.doGlobalActionConfirmed494;\n        window.doGlobalActionConfirmed.__tiUnblock494 = true;\n    }\n    var prevDoGlobal494 = window.doGlobalAction;\n    window.doGlobalAction = function(){\n        var promptEl = byId('global-action-prompt');\n        var prompt = promptEl ? String(promptEl.value || '').trim() : '';\n        if (!prompt) { if (window.tiAlert) window.tiAlert('Inserisci l operazione che vuoi eseguire.'); return false; }\n        var warning = '<b>Attenzione: modifica globale database<\/b><br><br>'\n            + 'Questa operazione pu\u00f2 modificare pi\u00f9 database delle ditte in una sola volta.<br>'\n            + 'Il template standard <b>shopservicemain.json<\/b> resta sempre escluso dalle modifiche.<br><br>'\n            + '<b>Operazione richiesta:<\/b><br><span style=\"color:#fbbf24;white-space:pre-wrap;display:block;margin-top:6px;\">' + esc(prompt) + '<\/span><br>'\n            + 'Confermi di voler procedere?';\n        if (typeof window.tiConfirmYesNoAction === 'function') {\n            window.tiConfirmYesNoAction(warning, function(){ window.doGlobalActionConfirmed494(prompt); }, null, 'CONFERMA MODIFICA', 'ANNULLA');\n        } else if (window.confirm(String(warning).replace(\/<[^>]+>\/g, ' '))) {\n            window.doGlobalActionConfirmed494(prompt);\n        }\n        return false;\n    };\n    window.doGlobalAction.__tiUnblock494 = true;\n    function fixGlobalButton(){\n        var b = byId('global-action-btn');\n        if (b) b.onclick = function(ev){ if (ev) ev.preventDefault(); return window.doGlobalAction(); };\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fixGlobalButton);\n    setTimeout(fixGlobalButton, 0); setTimeout(fixGlobalButton, 500); setTimeout(fixGlobalButton, 1500);\n})();\n<\/script>\n\n\n<script id=\"ti-audio-auto-presence-497\">\n(function(){\n    \"use strict\";\n    if (window.__tiAudioAutoPresence497Installed) return;\n    window.__tiAudioAutoPresence497Installed = true;\n\n    var AUTO_STATE = 3;\n    var scanTimer = null;\n    var prevFrame = null;\n    var lastPresenceTs = 0;\n    var lastGreetTs = 0;\n    var motionHits = 0;\n    var canvas = null;\n    var ctx = null;\n\n    function byId(id){ return document.getElementById(id); }\n    function now(){ return Date.now ? Date.now() : (new Date()).getTime(); }\n    function isAuto(){ return parseInt(window.muteState, 10) === AUTO_STATE; }\n    function canPersistAudio(){\n        try { return !!(window.tiAudioPersistentAllowed716 && window.tiAudioPersistentAllowed716()); }\n        catch(e) { return false; }\n    }\n    function ensureAudioKey(){\n        try { return window.tiAudioStorageKey716 ? window.tiAudioStorageKey716() : ('ti_mute_state_' + location.host + '_' + (window.tiUser || 'guest')); }\n        catch(e) { return 'ti_mute_state_' + location.host + '_' + (window.tiUser || 'guest'); }\n    }\n    function videoEl(){ return byId('ti-video-feed'); }\n    function cameraActive(){\n        var v = videoEl();\n        try {\n            return !!(v && v.srcObject && v.videoWidth > 0 && v.videoHeight > 0 && (!window.stream || window.stream.getTracks().some(function(t){ return t.readyState === 'live'; })));\n        } catch(e) { return !!(v && v.srcObject); }\n    }\n    function autoLabels(){\n        return (String(window.currLang || 'it').toLowerCase() === 'en') ? {\n            autoOff:'\ud83c\udfa7 Auto OFF', autoOn:'\ud83c\udfa7 Auto ON', offSession:'\ud83d\udd07 OFF session', off:'\ud83d\udd07 OFF', on:'\ud83d\udd0a ON'\n        } : {\n            autoOff:'\ud83c\udfa7 Auto OFF', autoOn:'\ud83c\udfa7 Auto ON', offSession:'\ud83d\udd07 OFF sessione', off:'\ud83d\udd07 OFF', on:'\ud83d\udd0a ON'\n        };\n    }\n    function setButtonAutoStyle(btn, active){\n        if (!btn) return;\n        var lbl = autoLabels();\n        btn.innerText = active ? lbl.autoOn : lbl.autoOff;\n        btn.title = active ? 'Audio Auto: presenza rilevata, audio attivo.' : 'Audio Auto: in attesa di presenza rilevata dalla videocamera.';\n        btn.style.background = active ? '#047857' : '#334155';\n        btn.style.borderColor = active ? '#10b981' : '#475569';\n        btn.style.color = '#fff';\n    }\n    function updateAudioStateFromAuto(){\n        if (!isAuto()) return;\n        var active = !!window.tiAudioAutoPresenceActive;\n        window.isMuted = !active;\n        setButtonAutoStyle(byId('ti-mute-btn'), active);\n    }\n    function userDisplayName(){\n        try {\n            var u = String(window.tiUser || '').trim();\n            if (u && u !== 'Ospite' && u !== 'Guest') return u;\n        } catch(e) {}\n        return '';\n    }\n    function welcomeText(){\n        var name = userDisplayName();\n        if (String(window.currLang || 'it').toLowerCase() === 'en') return name ? ('Welcome ' + name + '. How can I help you?') : 'Welcome to Shop & Service. How can I help you?';\n        return name ? ('Benvenuto ' + name + '. Come posso aiutarti?') : 'Benvenuto in Shop & Service. Come posso aiutarti?';\n    }\n    function greetUser(){\n        if (!isAuto()) return;\n        var t = now();\n        if (t - lastGreetTs < 45000) return;\n        lastGreetTs = t;\n        try { if (window.speechSynthesis) window.speechSynthesis.cancel(); } catch(e) {}\n        try {\n            if (typeof window.speakText === 'function') {\n                window.isMuted = false;\n                window.speakText(welcomeText());\n            }\n        } catch(e) {}\n    }\n    function markPresence(score){\n        lastPresenceTs = now();\n        if (!window.tiAudioAutoPresenceActive) {\n            window.tiAudioAutoPresenceActive = true;\n            updateAudioStateFromAuto();\n            greetUser();\n        } else {\n            updateAudioStateFromAuto();\n        }\n    }\n    function markNoPresenceIfStale(){\n        if (!isAuto()) return;\n        if (window.tiAudioAutoPresenceActive && now() - lastPresenceTs > 25000) {\n            window.tiAudioAutoPresenceActive = false;\n            window.isMuted = true;\n            updateAudioStateFromAuto();\n            try { if (window.speechSynthesis) window.speechSynthesis.cancel(); } catch(e) {}\n        } else {\n            updateAudioStateFromAuto();\n        }\n    }\n    function ensureCanvas(){\n        if (!canvas) {\n            canvas = document.createElement('canvas');\n            canvas.width = 96;\n            canvas.height = 72;\n            ctx = canvas.getContext('2d', {willReadFrequently:true});\n        }\n        return !!ctx;\n    }\n    function sampleMotion(){\n        if (!isAuto()) { stopScan(false); return; }\n        if (!cameraActive() || !ensureCanvas()) { markNoPresenceIfStale(); return; }\n        var v = videoEl();\n        try {\n            ctx.drawImage(v, 0, 0, canvas.width, canvas.height);\n            var data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;\n            if (!prevFrame) { prevFrame = new Uint8ClampedArray(data); return; }\n            var diff = 0, checked = 0;\n            for (var i = 0; i < data.length; i += 16) {\n                var d = Math.abs(data[i] - prevFrame[i]) + Math.abs(data[i+1] - prevFrame[i+1]) + Math.abs(data[i+2] - prevFrame[i+2]);\n                if (d > 45) diff++;\n                checked++;\n            }\n            prevFrame = new Uint8ClampedArray(data);\n            var score = checked ? (diff \/ checked) : 0;\n            if (score > 0.035) motionHits++; else motionHits = Math.max(0, motionHits - 1);\n            if (motionHits >= 2) markPresence(score); else markNoPresenceIfStale();\n        } catch(e) { markNoPresenceIfStale(); }\n    }\n    function startScan(){\n        if (!isAuto()) return;\n        window.tiAudioAutoPresenceActive = false;\n        window.isMuted = true;\n        updateAudioStateFromAuto();\n        if (scanTimer) return;\n        prevFrame = null;\n        motionHits = 0;\n        scanTimer = setInterval(sampleMotion, 850);\n        setTimeout(sampleMotion, 300);\n        setTimeout(sampleMotion, 1200);\n    }\n    function stopScan(clearPresence){\n        if (scanTimer) { clearInterval(scanTimer); scanTimer = null; }\n        prevFrame = null;\n        motionHits = 0;\n        if (clearPresence !== false) {\n            window.tiAudioAutoPresenceActive = false;\n            if (isAuto()) window.isMuted = true;\n            updateAudioStateFromAuto();\n        }\n    }\n    function installMuteStateOverride(){\n        var prevApply = window.applyMuteState;\n        if (typeof prevApply === 'function' && !prevApply.__tiAudioAuto497) {\n            window.applyMuteState = function(){\n                var btn = byId('ti-mute-btn');\n                var st = parseInt(window.muteState, 10);\n                var lbl = autoLabels();\n                if (st === AUTO_STATE) {\n                    window.isMuted = !window.tiAudioAutoPresenceActive;\n                    setButtonAutoStyle(btn, !!window.tiAudioAutoPresenceActive);\n                    startScan();\n                    return;\n                }\n                stopScan(true);\n                var ret = prevApply.apply(this, arguments);\n                if (btn) {\n                    if (st === 1) btn.innerText = lbl.offSession;\n                    else if (st === 2) btn.innerText = lbl.off;\n                    else if (st === 0) btn.innerText = lbl.on;\n                }\n                return ret;\n            };\n            window.applyMuteState.__tiAudioAuto497 = true;\n        }\n    }\n    function installMuteButton(){\n        var btn = byId('ti-mute-btn');\n        if (!btn || btn.__tiAudioAuto497) return;\n        btn.__tiAudioAuto497 = true;\n        btn.onclick = function(ev){\n            if (ev) { try { ev.preventDefault(); ev.stopPropagation(); } catch(e){} }\n            window.tiAudioKey = ensureAudioKey();\n            var persistent = canPersistAudio();\n            var st = parseInt(window.muteState, 10);\n            if (isNaN(st) || st < 0 || st > AUTO_STATE || (!persistent && st !== 1)) st = 0;\n            st = persistent ? ((st + 1) % 4) : (st === 1 ? 0 : 1);\n            window.muteState = st;\n            try {\n                if (persistent && (st === 2 || st === AUTO_STATE)) localStorage.setItem(window.tiAudioKey, String(st));\n                else localStorage.removeItem(window.tiAudioKey);\n            } catch(e) {}\n            if (typeof window.applyMuteState === 'function') window.applyMuteState();\n            if (st !== 0) { try { if (window.speechSynthesis) window.speechSynthesis.cancel(); } catch(e){} }\n            if (persistent && st === AUTO_STATE) startScan();\n            return false;\n        };\n    }\n    function readStoredState(){\n        try {\n            window.tiAudioKey = ensureAudioKey();\n            if (!canPersistAudio()) { localStorage.removeItem(window.tiAudioKey); if (parseInt(window.muteState, 10) !== 1) window.muteState = 0; return; }\n            var raw = localStorage.getItem(window.tiAudioKey);\n            if (raw === '3') window.muteState = AUTO_STATE;\n            else if (raw === '2') window.muteState = 2;\n        } catch(e) {}\n    }\n    function installCameraHooks(){\n        if (typeof window.openCamModal === 'function' && !window.openCamModal.__tiAudioAuto497) {\n            var prevOpen = window.openCamModal;\n            window.openCamModal = function(){\n                var ret = prevOpen.apply(this, arguments);\n                setTimeout(function(){ if (isAuto()) startScan(); }, 500);\n                setTimeout(function(){ if (isAuto()) startScan(); }, 1800);\n                return ret;\n            };\n            window.openCamModal.__tiAudioAuto497 = true;\n        }\n        if (typeof window.closeCamModal === 'function' && !window.closeCamModal.__tiAudioAuto497) {\n            var prevClose = window.closeCamModal;\n            window.closeCamModal = function(){\n                var ret = prevClose.apply(this, arguments);\n                if (isAuto()) stopScan(true);\n                return ret;\n            };\n            window.closeCamModal.__tiAudioAuto497 = true;\n        }\n    }\n    function boot(){\n        readStoredState();\n        installMuteStateOverride();\n        installMuteButton();\n        installCameraHooks();\n        if (typeof window.applyMuteState === 'function') window.applyMuteState();\n        if (isAuto()) startScan();\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);\n    setTimeout(boot, 0);\n    setTimeout(boot, 600);\n    setTimeout(boot, 1800);\n})();\n<\/script>\n\n\n\n<script id=\"ti-config-open-watchdog-501\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigOpenWatchdog501Installed) return;\n    window.__tiConfigOpenWatchdog501Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(s){ return String(s == null ? '' : s).replace(\/[&<>\"']\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c];}); }\n    function overlay(){ return byId('ti-conf-ov'); }\n    function cnt(){ return byId('ti-conf-cnt'); }\n    function selectedDb(){\n        var s = byId('ti-ditta');\n        return s && s.value ? String(s.value) : String(window.currentDb || window.tiCurrentDb || window.__tiConfigLoadedDb || '');\n    }\n    function show(){\n        var ov = overlay(); if (!ov) return;\n        try { if (window.preparePluginModal) window.preparePluginModal(ov); } catch(e) {}\n        try { if (window.showPluginModal) window.showPluginModal(ov); else if (window.openModal) window.openModal('ti-conf-ov'); } catch(e) {}\n        try {\n            ov.style.setProperty('display','flex','important');\n            ov.style.setProperty('visibility','visible','important');\n            ov.style.setProperty('opacity','1','important');\n            ov.style.setProperty('pointer-events','auto','important');\n            ov.style.setProperty('z-index','2147483000','important');\n            ov.removeAttribute('aria-hidden');\n        } catch(e) {}\n    }\n    function hide(){\n        window.__tiConfigOpenCancelled501 = true;\n        window.__tiConfigOpenToken501 = '';\n        try { if (window.__tiConfigAbort501) window.__tiConfigAbort501.abort(); } catch(e) {}\n        try { if (window.__tiConfigAbort492) window.__tiConfigAbort492.abort(); } catch(e) {}\n        try { if (window.closeModal) window.closeModal('ti-conf-ov'); else { var o=overlay(); if(o) o.style.display='none'; } } catch(e) { var o=overlay(); if(o) o.style.display='none'; }\n    }\n    function retryButtons(){\n        return '<div style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;\">'\n            + '<button type=\"button\" id=\"ti-config-retry-501\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;\">Riprova<\/button>'\n            + '<button type=\"button\" id=\"ti-config-cache-501\" class=\"ti-btn\" style=\"background:#0f766e;color:#fff;\">Apri dati in memoria<\/button>'\n            + '<button type=\"button\" id=\"ti-config-close-501\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>'\n            + '<\/div>';\n    }\n    function bindActionButtons(){\n        var r=byId('ti-config-retry-501'); if(r) r.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation&&ev.stopPropagation();} return window.openConfigSafe501(ev,true); };\n        var c=byId('ti-config-cache-501'); if(c) c.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation&&ev.stopPropagation();} return window.openConfigSafe501(ev,false,true); };\n        var x=byId('ti-config-close-501'); if(x) x.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation&&ev.stopPropagation();} hide(); return false; };\n    }\n    function message(html,type){\n        show();\n        var c = cnt(); if(!c) return;\n        var border = type==='error' ? '#ef4444' : (type==='warn' ? '#f59e0b' : '#22c55e');\n        var bg = type==='error' ? '#450a0a' : (type==='warn' ? '#422006' : '#052e16');\n        var color = type==='error' ? '#fecaca' : (type==='warn' ? '#fde68a' : '#bbf7d0');\n        c.innerHTML = '<div style=\"margin:14px;padding:14px;border-radius:12px;border:1px solid '+border+';background:'+bg+';color:'+color+';font-size:14px;line-height:1.45;\">'+html+'<\/div>';\n        bindActionButtons();\n    }\n    function isLoading(){\n        var c=cnt(); if(!c) return false;\n        var h=String(c.innerHTML||'');\n        return \/Caricamento configurazione in corso|Configurazione non caricata|ancora in corso\/i.test(h);\n    }\n    function goodContent(){\n        var c=cnt(); if(!c) return false;\n        var h=String(c.innerHTML||'').trim();\n        return !!(h && !\/Caricamento configurazione in corso|Configurazione non caricata|ancora in corso\/i.test(h));\n    }\n    function cache(db){\n        try{\n            var d = window.currentDbData;\n            if(!d || !d.Tabelle) return null;\n            var known = String(d.__db_name || window.__tiCurrentDbDataDb || window.__tiConfigLoadedDb || window.currentDb || window.tiCurrentDb || '').trim();\n            if(known && db && known !== db) return null;\n            return d;\n        } catch(e) { return null; }\n    }\n    function prepare(data,db){\n        if(!data || !data.Tabelle) throw new Error('Database non valido: manca la sezione Tabelle.');\n        window.currentDbData = data;\n        try { data.__db_name = db; window.__tiCurrentDbDataDb = db; window.__tiConfigLoadedDb = db; window.currentDb = db; window.tiCurrentDb = db; } catch(e) {}\n        try { window.configOriginalDbData = JSON.parse(JSON.stringify(data)); } catch(e) { window.configOriginalDbData = data; }\n        try { window.configWasSaved = false; window.tiConfigDeletedRowsManifest = []; } catch(e) {}\n        try { if(window.modifiedFields && window.modifiedFields.clear) window.modifiedFields.clear(); } catch(e) {}\n    }\n    function render(source){\n        show();\n        if(typeof window.renderConfig !== 'function') throw new Error('renderConfig non disponibile. Possibile errore JavaScript precedente.');\n        try { window.configForceTop = false; window.renderConfig(); } catch(e) { throw new Error('Errore renderConfig: ' + (e && e.message ? e.message : e)); }\n        var c=cnt(); if(!c) throw new Error('Contenitore ti-conf-cnt non trovato.');\n        if(!String(c.innerHTML||'').trim() || isLoading()) throw new Error('Render non completato: contenuto configurazione assente o ancora in caricamento.');\n        window.__tiConfigLastGoodHtml501 = String(c.innerHTML||'');\n        window.__tiConfigLastGoodTime501 = Date.now();\n        window.__tiConfigRenderedOk501 = true;\n        try { if(window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n        try { console.info('Shop & Service Configurazione aperta rescue 501: ' + source); } catch(e) {}\n        return true;\n    }\n    function makeFd(db){\n        var fd = null;\n        try { if(window.buildAjaxFormData) fd = window.buildAjaxFormData('ti_ai_get_db_data',{db:db,ti_config_open:'1',rescue:'501'}); } catch(e) {}\n        if(!fd){ fd = new FormData(); fd.append('action','ti_ai_get_db_data'); fd.append('ti_action','ti_ai_get_db_data'); fd.append('db',db); fd.append('ti_config_open','1'); fd.append('rescue','501'); }\n        try { if(!fd.has('action')) fd.append('action','ti_ai_get_db_data'); if(!fd.has('ti_action')) fd.append('ti_action','ti_ai_get_db_data'); } catch(e) {}\n        return fd;\n    }\n    function fetchDb(db, token){\n        var urls=[];\n        if(window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if(window.ajaxurl && urls.indexOf(window.ajaxurl)<0) urls.push(window.ajaxurl);\n        if(window.tiUrl && urls.indexOf(window.tiUrl)<0) urls.push(window.tiUrl);\n        if(!urls.length) urls.push(window.location.href.split('#')[0]);\n        function attempt(i){\n            if(window.__tiConfigOpenToken501 !== token || window.__tiConfigOpenCancelled501) return Promise.reject(new Error('Apertura annullata.'));\n            var ctrl = null, timer = null, done = false;\n            var opts = {method:'POST', body:makeFd(db), credentials:'same-origin', cache:'no-store'};\n            try { if(window.AbortController){ ctrl = new AbortController(); opts.signal = ctrl.signal; window.__tiConfigAbort501 = ctrl; } } catch(e) {}\n            return new Promise(function(resolve,reject){\n                timer = setTimeout(function(){ if(done) return; done = true; try{ if(ctrl) ctrl.abort(); }catch(e){} reject(new Error('Timeout lettura database da ' + urls[i])); }, 22000);\n                fetch(urls[i], opts).then(function(r){\n                    return r.text().then(function(raw){\n                        if(!r.ok) throw new Error('HTTP ' + r.status + ' da ' + urls[i] + ': ' + String(raw||'').slice(0,280));\n                        try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta non JSON da ' + urls[i] + ': ' + String(raw||'').slice(0,280)); }\n                    });\n                }).then(function(j){ if(done) return; done = true; resolve(j); }).catch(function(err){\n                    if(done) return; done = true;\n                    if(i+1 < urls.length) attempt(i+1).then(resolve).catch(reject); else reject(err);\n                }).finally(function(){ try{ clearTimeout(timer); }catch(e){} });\n            });\n        }\n        return attempt(0);\n    }\n    function restoreLastGood(){\n        var c=cnt();\n        if(c && window.__tiConfigLastGoodHtml501 && isLoading()) {\n            c.innerHTML = window.__tiConfigLastGoodHtml501;\n            try { console.warn('Shop & Service Config 501: ripristinato contenuto valido dopo sovrascrittura loader.'); } catch(e) {}\n            return true;\n        }\n        return false;\n    }\n    function fail(err,db){\n        if(goodContent()) { restoreLastGood(); return false; }\n        var cached = cache(db);\n        if(cached){\n            try { prepare(cached,db); render('cache dopo errore server'); return false; } catch(e) { err = new Error((err && err.message ? err.message : String(err||'')) + ' | Cache non utilizzabile: ' + (e && e.message ? e.message : e)); }\n        }\n        var m = err && err.message ? err.message : String(err || 'errore sconosciuto');\n        message('<b>Configurazione non caricata.<\/b><br>' + esc(m) + retryButtons() + '<br><span style=\"font-size:12px;color:#fecaca;\">Se il problema persiste, verificare permessi file, dimensione database, risposta admin-ajax e log PHP.<\/span>','error');\n        try { console.error('Shop & Service CONFIGURA rescue 501 error', err); } catch(e) {}\n        return false;\n    }\n    function startWatchdog(token,db){\n        setTimeout(function(){ if(window.__tiConfigOpenToken501 !== token || goodContent()) return; restoreLastGood(); if(!goodContent()) message('<b>Caricamento configurazione ancora in corso...<\/b><br>La risposta del server tarda. Puoi riprovare, aprire i dati in memoria o chiudere.' + retryButtons(),'warn'); }, 6000);\n        setTimeout(function(){ if(window.__tiConfigOpenToken501 !== token || goodContent()) return; var cached = cache(db); if(cached){ try{ prepare(cached,db); render('cache watchdog'); return; }catch(e){} } message('<b>Caricamento configurazione non concluso.<\/b><br>Il server non ha completato la risposta in tempo utile.' + retryButtons(),'warn'); }, 14000);\n    }\n    window.openConfigSafe501 = function(ev, forceReload, cacheOnly){\n        if(ev && ev.preventDefault) ev.preventDefault();\n        try { if(ev && ev.stopPropagation) ev.stopPropagation(); if(ev && ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        var db = selectedDb();\n        if(!db || db === 'NEW_DB'){ try { if(window.tiAlert) window.tiAlert('Seleziona prima una ditta valida.'); else alert('Seleziona prima una ditta valida.'); } catch(e) {} return false; }\n        try { if(window.__tiConfigAbort501) window.__tiConfigAbort501.abort(); } catch(e) {}\n        try { if(window.__tiConfigAbort492) window.__tiConfigAbort492.abort(); } catch(e) {}\n        window.__tiConfigOpenCancelled501 = false;\n        var token = 'cfg501_' + Date.now() + '_' + Math.random().toString(36).slice(2,8);\n        window.__tiConfigOpenToken501 = token;\n        window.__tiConfigRenderedOk501 = false;\n        show();\n        var cached = cache(db);\n        if(cached && !forceReload){\n            try { prepare(cached,db); render(cacheOnly ? 'cache richiesta' : 'cache immediata'); if(cacheOnly) return false; } catch(e) { try{ console.warn('Shop & Service cache config non utilizzabile 501',e); }catch(_){} }\n        }\n        if(cacheOnly){ message('<b>Dati in memoria non disponibili.<\/b><br>Premi Riprova per leggere il database dal server.' + retryButtons(), 'warn'); return false; }\n        if(!goodContent()) message('<b>Caricamento configurazione in corso...<\/b><br><span style=\"font-size:12px;\">Lettura database e apertura form.<\/span>' + retryButtons(),'ok');\n        startWatchdog(token,db);\n        fetchDb(db,token).then(function(res){\n            if(window.__tiConfigOpenToken501 !== token || window.__tiConfigOpenCancelled501) return false;\n            if(!res || !res.success || !res.data){ var m = res && res.data && res.data.message ? res.data.message : (res && res.message ? res.message : 'Risposta server non valida.'); throw new Error(m); }\n            prepare(res.data,db);\n            render('server');\n            setTimeout(restoreLastGood,120); setTimeout(restoreLastGood,700); setTimeout(restoreLastGood,1800);\n            return true;\n        }).catch(function(err){ if(window.__tiConfigOpenToken501 !== token || window.__tiConfigOpenCancelled501) return false; return fail(err,db); });\n        return false;\n    };\n    ['openConfigSafe492','openConfigSafe491','openConfigSafe490','openConfigSafe489','openConfigSafe488','openConfigSafe218'].forEach(function(n){ window[n] = window.openConfigSafe501; });\n    var prevClose = window.closeModal;\n    if(typeof prevClose === 'function' && !prevClose.__tiConfigCancel501){\n        window.closeModal = function(id){ if(id === 'ti-conf-ov'){ window.__tiConfigOpenCancelled501 = true; window.__tiConfigOpenToken501 = ''; try{ if(window.__tiConfigAbort501) window.__tiConfigAbort501.abort(); }catch(e){} } return prevClose.apply(this,arguments); };\n        window.closeModal.__tiConfigCancel501 = true;\n    }\n    function bind(){ var b=byId('ti-conf-toggle'); if(b){ b.onclick = window.openConfigSafe501; b.setAttribute('data-ti-config-open-rescue','501'); } bindActionButtons(); }\n    document.addEventListener('click',function(ev){\n        var t=ev && ev.target; if(!t || !t.closest) return;\n        var close=t.closest('#ti-config-close-501,#ti-config-close-492,#ti-config-close-491,#ti-config-close-490,#ti-config-close-489');\n        if(close){ try{ev.preventDefault();ev.stopPropagation();if(ev.stopImmediatePropagation)ev.stopImmediatePropagation();}catch(e){} hide(); return false; }\n        var b=t.closest('#ti-conf-toggle,#ti-config-retry-501,#ti-config-cache-501,#ti-config-retry-492,#ti-config-retry-491,#ti-config-retry-490,#ti-config-retry-489,#ti-config-rescue-488-retry');\n        if(!b) return;\n        try{ev.preventDefault();ev.stopPropagation();if(ev.stopImmediatePropagation)ev.stopImmediatePropagation();}catch(e){}\n        if(b.id === 'ti-config-cache-501') return window.openConfigSafe501(ev,false,true);\n        return window.openConfigSafe501(ev,b.id !== 'ti-conf-toggle');\n    },true);\n    if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded',bind); else bind();\n    setTimeout(bind,0); setTimeout(bind,250); setTimeout(bind,1000); setInterval(bind,2000);\n    setInterval(function(){ if(isLoading() && window.__tiConfigLastGoodHtml501) restoreLastGood(); },1800);\n})();\n<\/script>\n\n\n<script id=\"ti-report-attivita-fix-502\">\n(function(){\n    \"use strict\";\n    if (window.__tiReportAttivitaFix502Installed) return;\n    window.__tiReportAttivitaFix502Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function norm(v){ return String(v == null ? '' : v).trim(); }\n    function ensureState(){\n        if (!window.activityReportState) window.activityReportState = {};\n        if (!window.activityReportState.filters) window.activityReportState.filters = {};\n        if (!window.activityReportState.colFlags) window.activityReportState.colFlags = {};\n        if (!window.activityReportState.rowFlags) window.activityReportState.rowFlags = {};\n        if (!window.activityReportState.metricFlags) window.activityReportState.metricFlags = (typeof window.getDefaultActivityMetricFlags === 'function') ? window.getDefaultActivityMetricFlags() : {Prezzo:false,'Quantit\u00e0':false,Quantita:false,Totale:true};\n        if (!window.activityReportState.view) window.activityReportState.view = 'table';\n        if (!window.activityReportState.groupField) window.activityReportState.groupField = 'Username';\n    }\n    function cols(){\n        var c = Array.isArray(window.activityReportCols) ? window.activityReportCols.slice() : [];\n        if (!c.length) c = ['Username','Descrizione','Prezzo','Quantit\u00e0','Esito','Operatore','Dispositivo','Sito','Prodotto','Istruzioni','Durata','Data','Ora','Nota','Stato','Totale'];\n        return c;\n    }\n    function metrics(){\n        var c = cols();\n        var m = ['Prezzo','Quantit\u00e0','Quantita','Totale'].filter(function(x){ return c.indexOf(x) !== -1 || x === 'Totale'; });\n        return m.length ? m : ['Totale'];\n    }\n    function savePrefs(){ try { if (typeof window.saveActivityReportPrefs === 'function') window.saveActivityReportPrefs(); } catch(e){} }\n    function rerender(){ try { if (typeof window.renderActivityReport === 'function') window.renderActivityReport(); } catch(e){ try { console.warn('Shop & Service Report attivita render error 502', e); } catch(ignore){} } }\n    function getRows(){\n        try { if (typeof window.getFilteredActivityRows === 'function') return window.getFilteredActivityRows(false) || []; } catch(e){}\n        try { if (typeof window.getActivityRowsNormalized === 'function') return window.getActivityRowsNormalized() || []; } catch(e){}\n        return [];\n    }\n    function numberVal(v){\n        if (typeof window.parseReportNum === 'function') return window.parseReportNum(v, 0);\n        var s = String(v == null ? '' : v).replace(\/[^0-9,.-]\/g,'');\n        if (s.indexOf(',') >= 0 && s.indexOf('.') >= 0) s = s.replace(\/\\.\/g,'').replace(',','.');\n        else if (s.indexOf(',') >= 0) s = s.replace(',','.');\n        var n = parseFloat(s); return isNaN(n) ? 0 : n;\n    }\n    function selectedMetric(){\n        ensureState();\n        var mf = window.activityReportState.metricFlags || {};\n        var ms = metrics();\n        for (var i=0;i<ms.length;i++) if (mf[ms[i]]) return ms[i];\n        return ms.indexOf('Totale') !== -1 ? 'Totale' : ms[0];\n    }\n    function groupField(){\n        ensureState();\n        var c = cols();\n        var g = window.activityReportState.groupField || 'Username';\n        if (c.indexOf(g) === -1) g = c.indexOf('Esito') !== -1 ? 'Esito' : (c[0] || 'Username');\n        return g;\n    }\n    function aggregate(rows){\n        var g = groupField();\n        var m = selectedMetric();\n        var map = {};\n        (rows || []).forEach(function(r){\n            var key = norm(r && r[g]) || '(vuoto)';\n            if (!map[key]) map[key] = {key:key, count:0, value:0};\n            map[key].count++;\n            if (m === 'Quantit\u00e0' || m === 'Quantita') map[key].value += numberVal(r && (r['Quantit\u00e0'] != null ? r['Quantit\u00e0'] : r.Quantita));\n            else if (m === 'Prezzo') map[key].value += numberVal(r && r.Prezzo);\n            else map[key].value += numberVal(r && (r.Totale != null ? r.Totale : (numberVal(r && r.Prezzo) * (numberVal(r && (r['Quantit\u00e0'] != null ? r['Quantit\u00e0'] : r.Quantita)) || 1))));\n        });\n        return Object.keys(map).map(function(k){ return map[k]; }).sort(function(a,b){ return b.value - a.value; }).slice(0,12);\n    }\n    function drawFallback(canvasId, type){\n        var cv = byId(canvasId); if (!cv || !cv.getContext) return;\n        var ctx = cv.getContext('2d');\n        var w = cv.width || 900, h = cv.height || 360;\n        ctx.clearRect(0,0,w,h);\n        ctx.fillStyle = '#020617'; ctx.fillRect(0,0,w,h);\n        var data = aggregate(getRows());\n        ctx.fillStyle = '#e5e7eb'; ctx.font = '16px Arial';\n        ctx.fillText((type === 'trend' ? 'Andamento' : 'Grafico') + ' - ' + selectedMetric() + ' per ' + groupField(), 18, 26);\n        if (!data.length) { ctx.font='14px Arial'; ctx.fillText('Nessun dato disponibile', 18, 58); return; }\n        var max = Math.max.apply(null, data.map(function(d){ return Math.abs(d.value) || 1; }));\n        if (type === 'trend') {\n            var left=52, top=52, right=24, bottom=52, cw=w-left-right, ch=h-top-bottom;\n            ctx.strokeStyle='#334155'; ctx.beginPath(); ctx.moveTo(left,top); ctx.lineTo(left,top+ch); ctx.lineTo(left+cw,top+ch); ctx.stroke();\n            ctx.strokeStyle='#93c5fd'; ctx.lineWidth=3; ctx.beginPath();\n            data.forEach(function(d,i){ var x=left+(cw*(i\/(Math.max(1,data.length-1)))); var y=top+ch-(ch*(d.value\/max)); if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); });\n            ctx.stroke();\n            ctx.fillStyle='#bfdbfe';\n            data.forEach(function(d,i){ var x=left+(cw*(i\/(Math.max(1,data.length-1)))); var y=top+ch-(ch*(d.value\/max)); ctx.beginPath(); ctx.arc(x,y,4,0,Math.PI*2); ctx.fill(); ctx.save(); ctx.translate(x, top+ch+15); ctx.rotate(-0.45); ctx.fillStyle='#cbd5e1'; ctx.font='10px Arial'; ctx.fillText(String(d.key).slice(0,16),0,0); ctx.restore(); });\n        } else {\n            var bx=28, by=55, bw=w-56, bh=h-95, barH=Math.max(14, Math.floor(bh\/data.length)-5);\n            data.forEach(function(d,i){ var y=by+i*(barH+5); var ww=Math.max(2, Math.round((bw-190)*(d.value\/max))); ctx.fillStyle='#2563eb'; ctx.fillRect(bx+160,y,ww,barH); ctx.fillStyle='#e5e7eb'; ctx.font='12px Arial'; ctx.fillText(String(d.key).slice(0,22), bx, y+barH-3); ctx.fillText(String(Math.round(d.value*100)\/100), bx+165+ww, y+barH-3); });\n        }\n    }\n    function ensureStyle(){\n        if (byId('ti-report-attivita-fix-502-css')) return;\n        var st=document.createElement('style'); st.id='ti-report-attivita-fix-502-css';\n        st.textContent = '#ti-report-cnt .ti-report-502-panel{display:block!important;visibility:visible!important;opacity:1!important;position:relative!important;z-index:999!important;pointer-events:auto!important;background:#0f172a!important;border:1px solid #38bdf8!important;border-radius:12px!important;padding:10px!important;margin:10px 0!important;color:#e5e7eb!important;}'+\n        '#ti-report-cnt .ti-report-502-panel *{pointer-events:auto!important;}'+\n        '#ti-report-cnt .ti-report-502-row{display:flex!important;gap:8px!important;align-items:center!important;flex-wrap:wrap!important;margin:6px 0!important;}'+\n        '#ti-report-cnt .ti-report-502-btn{border:1px solid #475569!important;border-radius:8px!important;background:#1f2937!important;color:#fff!important;padding:7px 10px!important;font-weight:800!important;cursor:pointer!important;}'+\n        '#ti-report-cnt .ti-report-502-btn.is-active{background:#15803d!important;border-color:#86efac!important;}'+\n        '#ti-report-cnt .ti-report-502-chip{display:inline-flex!important;align-items:center!important;gap:5px!important;border:1px solid #334155!important;border-radius:999px!important;background:#111827!important;color:#e5e7eb!important;padding:5px 8px!important;font-size:12px!important;}'+\n        '#ti-report-cnt .ti-report-502-select{background:#111827!important;color:#fff!important;border:1px solid #475569!important;border-radius:8px!important;padding:6px 8px!important;}'+\n        '#ti-report-cnt .ti-chart-wrap{min-height:360px!important;display:block!important;visibility:visible!important;opacity:1!important;}'+\n        '#ti-report-cnt canvas{display:block!important;visibility:visible!important;opacity:1!important;max-width:100%!important;background:#020617!important;}';\n        document.head.appendChild(st);\n    }\n    function panelHtml(){\n        ensureState();\n        var c=cols(), view=window.activityReportState.view || 'table', cf=window.activityReportState.colFlags || {}, mf=window.activityReportState.metricFlags || {};\n        var html='<div id=\"ti-report-controls-502\" class=\"ti-report-502-panel\" data-ti-report-fix=\"502\">';\n        html+='<div class=\"ti-report-502-row\"><b>Vista report<\/b>';\n        [['table','Elenco dati'],['pie','Grafico torte'],['trend','Grafico andamento']].forEach(function(v){ html+='<button type=\"button\" class=\"ti-report-502-btn '+(view===v[0]?'is-active':'')+'\" data-ti-report-view=\"'+v[0]+'\">'+esc(v[1])+'<\/button>'; });\n        html+='<\/div><div class=\"ti-report-502-row\"><b>Selezione campi<\/b>';\n        c.forEach(function(col){ html+='<label class=\"ti-report-502-chip\"><input type=\"checkbox\" data-ti-report-col=\"'+esc(col)+'\" '+(!cf[col]?'checked':'')+'><span>'+esc(col)+'<\/span><\/label>'; });\n        html+='<\/div><div class=\"ti-report-502-row\"><b>Grafico<\/b><span>Raggruppa per<\/span><select class=\"ti-report-502-select\" data-ti-report-group>';\n        var visible = c.filter(function(x){ return !cf[x]; }); if (!visible.length) visible = c;\n        visible.forEach(function(col){ html+='<option value=\"'+esc(col)+'\" '+(groupField()===col?'selected':'')+'>'+esc(col)+'<\/option>'; });\n        html+='<\/select>';\n        metrics().forEach(function(m){ html+='<label class=\"ti-report-502-chip\"><input type=\"checkbox\" data-ti-report-metric=\"'+esc(m)+'\" '+(mf[m]?'checked':'')+'><span>'+esc(m)+'<\/span><\/label>'; });\n        html+='<\/div><\/div>';\n        return html;\n    }\n    function applyControls(){\n        var box=byId('ti-report-cnt'); if(!box) return;\n        ensureStyle(); ensureState();\n        var old=byId('ti-report-controls-502'); if(old) old.remove();\n        var toolbar=box.querySelector('.ti-report-toolbar');\n        if(toolbar && toolbar.parentNode) toolbar.insertAdjacentHTML('afterend', panelHtml());\n        else box.insertAdjacentHTML('afterbegin', panelHtml());\n        setTimeout(function(){ ensureDraw(); }, 40);\n    }\n    function ensureDraw(){\n        ensureState();\n        var view=window.activityReportState.view || 'table';\n        if(view === 'pie') {\n            var p=byId('ti-report-pie');\n            if(!p) { var box=byId('ti-report-cnt'); if(box) box.insertAdjacentHTML('beforeend','<div class=\"ti-chart-wrap\"><canvas id=\"ti-report-pie\" width=\"980\" height=\"380\"><\/canvas><\/div>'); }\n            try { if (typeof window.drawPie3D === 'function') window.drawPie3D('ti-report-pie', getRows()); } catch(e) { drawFallback('ti-report-pie','pie'); }\n            setTimeout(function(){ drawFallback('ti-report-pie','pie'); }, 160);\n        }\n        if(view === 'trend') {\n            var t=byId('ti-report-trend');\n            if(!t) { var box2=byId('ti-report-cnt'); if(box2) box2.insertAdjacentHTML('beforeend','<div class=\"ti-chart-wrap\"><canvas id=\"ti-report-trend\" width=\"980\" height=\"380\"><\/canvas><\/div>'); }\n            try { if (typeof window.drawTrend3D === 'function') window.drawTrend3D('ti-report-trend', getRows()); } catch(e2) { drawFallback('ti-report-trend','trend'); }\n            setTimeout(function(){ drawFallback('ti-report-trend','trend'); }, 160);\n        }\n    }\n    document.addEventListener('click', function(ev){\n        var b = ev.target && ev.target.closest ? ev.target.closest('[data-ti-report-view]') : null;\n        if(!b) return;\n        ev.preventDefault(); ev.stopPropagation();\n        ensureState(); window.activityReportState.view = b.getAttribute('data-ti-report-view') || 'table'; savePrefs(); rerender(); setTimeout(applyControls,30);\n    }, true);\n    document.addEventListener('change', function(ev){\n        var t=ev.target; if(!t) return;\n        if(t.hasAttribute('data-ti-report-col')) { ev.stopPropagation(); ensureState(); window.activityReportState.colFlags[t.getAttribute('data-ti-report-col')] = !t.checked; savePrefs(); rerender(); setTimeout(applyControls,30); return; }\n        if(t.hasAttribute('data-ti-report-metric')) { ev.stopPropagation(); ensureState(); var m=t.getAttribute('data-ti-report-metric'); window.activityReportState.metricFlags[m] = !!t.checked; var any=metrics().some(function(x){ return !!window.activityReportState.metricFlags[x]; }); if(!any) window.activityReportState.metricFlags[m] = true; savePrefs(); rerender(); setTimeout(applyControls,30); return; }\n        if(t.hasAttribute('data-ti-report-group')) { ev.stopPropagation(); ensureState(); window.activityReportState.groupField = t.value || 'Username'; savePrefs(); rerender(); setTimeout(applyControls,30); return; }\n    }, true);\n    function wrap(){\n        if(typeof window.renderActivityReport === 'function' && !window.renderActivityReport.__ti502){ var old=window.renderActivityReport; window.renderActivityReport=function(){ var r=old.apply(this,arguments); setTimeout(applyControls,0); setTimeout(applyControls,180); return r; }; window.renderActivityReport.__ti502=true; }\n        if(typeof window.openActivityReport === 'function' && !window.openActivityReport.__ti502){ var oo=window.openActivityReport; window.openActivityReport=function(){ var r=oo.apply(this,arguments); setTimeout(applyControls,120); setTimeout(applyControls,500); return r; }; window.openActivityReport.__ti502=true; }\n    }\n    wrap(); setTimeout(function(){ wrap(); applyControls(); }, 600); setTimeout(function(){ wrap(); applyControls(); }, 1800);\n})();\n<\/script>\n\n\n<script id=\"ti-config-read-wait-503\">\n(function(){\n    \"use strict\";\n    \/\/ 30.9.664: monitor 503 disattivato. In alcune installazioni intercettava fetch,\n    \/\/ apriva il popup progresso al 4% e restava attivo anche dopo l apertura 509,\n    \/\/ rallentando configurazione\/edit. Il caricamento e gestito solo da openConfigSafe509.\n    window.__tiConfigReadWait503Installed = true;\n    window.__tiConfigReadWait503Disabled549 = true;\n    window.__tiConfigReadWaitActive503 = false;\n})();\n<\/script>\n\n\n\n<script id=\"ti-company-switch-logout-config-reset-504\">\n(function(){\n    \"use strict\";\n    if (window.__tiCompanySwitchLogoutConfigReset504Installed) return;\n    window.__tiCompanySwitchLogoutConfigReset504Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function safe(fn){ try { return fn(); } catch(e) { return null; } }\n    function isConfigVisible(){\n        var ov = byId('ti-conf-ov');\n        if (!ov) return false;\n        var cs = window.getComputedStyle ? window.getComputedStyle(ov) : null;\n        return String(ov.style.display || '') !== 'none' && (!cs || cs.display !== 'none');\n    }\n    function hideElement(el){\n        if (!el) return;\n        safe(function(){ if (window.closeModal) window.closeModal(el.id || el); });\n        safe(function(){ el.style.setProperty('display','none','important'); });\n        safe(function(){ el.classList.remove('show','open','active','ti-modal-open'); });\n        safe(function(){ el.setAttribute('aria-hidden','true'); });\n    }\n    function hideProgress(reason){\n        safe(function(){ if (window.hideProgressPopup) window.hideProgressPopup(false, reason || 'Operazione interrotta', {notifyUser:false}); });\n        safe(function(){ if (window.clearLongAIProcess) window.clearLongAIProcess(); });\n        safe(function(){ if (window.cancelActiveConfigAiRequest434) window.cancelActiveConfigAiRequest434('company_switch_or_logout'); });\n        window.__tiConfigReadWaitActive503 = false;\n        window.__tiConfigReadWaitStarted503 = 0;\n    }\n    function resetConfigState(reason){\n        window.__tiCompanySwitchResetReason504 = reason || '';\n        window.__tiConfigClosedBySwitch504 = Date.now();\n        window.__tiConfigOpenToken501 = null;\n        window.__tiConfigOpenToken503 = null;\n        window.__tiConfigOpenToken492 = null;\n        window.__tiConfigOpenToken491 = null;\n        safe(function(){ window.currentDbData = null; });\n        safe(function(){ window.configOriginalDbData = null; });\n        safe(function(){ window.configWasSaved = true; });\n        safe(function(){ window.tiConfigDeletedRowsManifest = []; });\n        safe(function(){ if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); });\n        safe(function(){ window.tiConfigImportToken = null; });\n        safe(function(){ window.configBotFile = null; });\n        hideProgress(reason || 'Cambio contesto');\n    }\n    window.tiCloseConfigurationForContextChange504 = function(reason){\n        resetConfigState(reason || 'Cambio ditta');\n        hideElement(byId('ti-conf-ov'));\n        hideElement(byId('ti-file-manager-ov'));\n        hideElement(byId('ti-ai-gen-ov'));\n        hideElement(byId('ti-activity-report-ov'));\n        safe(function(){ if (window.closeDittaPopup) window.closeDittaPopup(); });\n        safe(function(){ if (window.hideOrderConfirmPanel) window.hideOrderConfirmPanel(); });\n        var cnt = byId('ti-conf-cnt');\n        if (cnt) cnt.innerHTML = '<div style=\"padding:14px;border-radius:12px;background:#111827;border:1px solid #334155;color:#e5e7eb;\">Configurazione chiusa per cambio ditta. Riapri Configura dopo il caricamento della nuova ditta.<\/div>';\n    };\n    window.tiCloseAllShopServiceUiForLogout504 = function(){\n        resetConfigState('Logoff');\n        var ids = ['ti-conf-ov','ti-login-ov','ti-profile-ov','ti-newdb-ov','ti-new-db-ov','ti-file-manager-ov','ti-ai-gen-ov','ti-activity-report-ov','ti-delete-company-ov','ti-progress-ov'];\n        ids.forEach(function(id){ hideElement(byId(id)); });\n        safe(function(){ document.querySelectorAll('.ti-modal-overlay,.ti-modal-backdrop').forEach(function(el){ hideElement(el); }); });\n        safe(function(){ if (window.closeDittaPopup) window.closeDittaPopup(); });\n        safe(function(){ if (window.hideOrderConfirmPanel) window.hideOrderConfirmPanel(); });\n        safe(function(){ var chat=byId('ti-chat-box'); if (chat) chat.innerHTML=''; });\n        safe(function(){ var msg=byId('ti-msg'); if (msg) msg.value=''; });\n        safe(function(){ var un=byId('ti-user-name'); if (un) un.textContent='Cliente\/Utente'; });\n        safe(function(){ var ur=byId('ti-user-role'); if (ur) ur.textContent=''; });\n        safe(function(){ var lb=byId('ti-login-btn'); if (lb) lb.style.display='inline-block'; });\n        safe(function(){ var ob=byId('ti-logout-btn'); if (ob) ob.style.display='none'; });\n        safe(function(){ document.cookie = 'ti_ai_ss_session=; Max-Age=0; path=\/; SameSite=Lax'; });\n    };\n    var lastDb = '';\n    function syncLastDb(){ var d=byId('ti-ditta'); if (d) lastDb = String(d.value || ''); }\n    syncLastDb();\n    document.addEventListener('DOMContentLoaded', syncLastDb, {once:true});\n    document.addEventListener('change', function(ev){\n        var t = ev && ev.target;\n        if (!t || t.id !== 'ti-ditta') return;\n        var newDb = String(t.value || '');\n        if (newDb === lastDb) return;\n        var oldDb = lastDb;\n        lastDb = newDb;\n        if (oldDb || isConfigVisible()) {\n            window.tiCloseConfigurationForContextChange504('Cambio ditta da ' + (oldDb || '-') + ' a ' + (newDb || '-'));\n        }\n        safe(function(){ var chat=byId('ti-chat-box'); if (chat) chat.innerHTML=''; });\n        safe(function(){ var msg=byId('ti-msg'); if (msg) msg.value=''; });\n        safe(function(){ window.tiCurrentSelectedDb = newDb; });\n        safe(function(){ if (newDb) localStorage.setItem('ti_saved_db', newDb); else localStorage.removeItem('ti_saved_db'); });\n    }, true);\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var logoutBtn = t.closest('#ti-logout-btn,[data-ti-logout],.ti-logout-btn');\n        if (logoutBtn) window.tiCloseAllShopServiceUiForLogout504();\n    }, true);\n    function wrapLogout(){\n        if (typeof window.doLogout === 'function' && !window.doLogout.__ti504) {\n            var oldLogout = window.doLogout;\n            window.doLogout = function(){\n                window.tiCloseAllShopServiceUiForLogout504();\n                return oldLogout.apply(this, arguments);\n            };\n            window.doLogout.__ti504 = true;\n        }\n        if (typeof window.forceSessionCloseNow === 'function' && !window.forceSessionCloseNow.__ti504) {\n            var oldForce = window.forceSessionCloseNow;\n            window.forceSessionCloseNow = function(){\n                window.tiCloseAllShopServiceUiForLogout504();\n                return oldForce.apply(this, arguments);\n            };\n            window.forceSessionCloseNow.__ti504 = true;\n        }\n    }\n    function wrapDittaRender(){\n        if (typeof window.renderDittePopup === 'function' && !window.renderDittePopup.__ti504) {\n            var oldRender = window.renderDittePopup;\n            window.renderDittePopup = function(){\n                var r = oldRender.apply(this, arguments);\n                safe(function(){\n                    document.querySelectorAll('#ti-ditta-popup .ti-ditta-row').forEach(function(row){\n                        if (row.__ti504Bound) return;\n                        row.__ti504Bound = true;\n                        row.addEventListener('click', function(){\n                            var d=byId('ti-ditta');\n                            var before=lastDb;\n                            setTimeout(function(){\n                                var after = d ? String(d.value || '') : '';\n                                if (after !== before) window.tiCloseConfigurationForContextChange504('Cambio ditta');\n                            }, 0);\n                        }, true);\n                    });\n                });\n                return r;\n            };\n            window.renderDittePopup.__ti504 = true;\n        }\n    }\n    wrapLogout(); wrapDittaRender();\n    setTimeout(function(){ syncLastDb(); wrapLogout(); wrapDittaRender(); }, 300);\n    setTimeout(function(){ wrapLogout(); wrapDittaRender(); }, 1200);\n    setInterval(function(){ wrapLogout(); wrapDittaRender(); }, 2500);\n})();\n<\/script>\n\n\n<script id=\"ti-logout-guest-guard-505\">\n(function(){\n    \"use strict\";\n    if (window.__tiLogoutGuestGuard505) return;\n    window.__tiLogoutGuestGuard505 = true;\n    function byId(id){ return document.getElementById(id); }\n    function safe(fn){ try { return fn(); } catch(e) {} }\n    function txt(v){ return String(v == null ? '' : v).trim(); }\n    function isLogged(){\n        var u = txt(window.tiUser || '');\n        if (u !== '') return true;\n        var name = byId('ti-user-name');\n        var nameTxt = name ? txt(name.textContent || '') : '';\n        if (nameTxt && !\/^(cliente\\\/utente|cliente|utente|guest|ospite)$\/i.test(nameTxt)) return true;\n        return false;\n    }\n    function logoutButtons(){\n        var sel = '#ti-logout-btn,[data-ti-logout],.ti-logout-btn';\n        return Array.prototype.slice.call(document.querySelectorAll(sel));\n    }\n    function hideGuestLogout(){\n        var logged = isLogged();\n        logoutButtons().forEach(function(btn){\n            if (!btn) return;\n            if (!logged) {\n                btn.style.setProperty('display','none','important');\n                btn.style.setProperty('visibility','hidden','important');\n                btn.style.setProperty('pointer-events','none','important');\n                btn.setAttribute('aria-hidden','true');\n                btn.dataset.tiGuestHidden505 = '1';\n            } else if (btn.dataset.tiGuestHidden505 === '1') {\n                btn.style.removeProperty('visibility');\n                btn.style.removeProperty('pointer-events');\n                btn.removeAttribute('aria-hidden');\n                btn.dataset.tiGuestHidden505 = '0';\n            }\n        });\n        var loginBtn = byId('ti-login-btn');\n        if (loginBtn && !logged) {\n            loginBtn.style.removeProperty('display');\n            loginBtn.style.setProperty('pointer-events','auto','important');\n        }\n    }\n    function isLogoutTarget(target){\n        if (!target || !target.closest) return null;\n        return target.closest('#ti-logout-btn,[data-ti-logout],.ti-logout-btn');\n    }\n    function blockGuestLogoutEvent(ev){\n        var btn = isLogoutTarget(ev && ev.target);\n        if (!btn) return;\n        if (isLogged()) return;\n        safe(function(){ ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); });\n        hideGuestLogout();\n        return false;\n    }\n    ['click','mousedown','mouseup','touchstart','touchend','pointerdown','pointerup'].forEach(function(evt){\n        try { window.addEventListener(evt, blockGuestLogoutEvent, true); } catch(e) {}\n    });\n    function wrapLogoutNoSession(){\n        if (typeof window.doLogout === 'function' && !window.doLogout.__tiGuestGuard505) {\n            var old = window.doLogout;\n            window.doLogout = function(){\n                if (!isLogged()) {\n                    hideGuestLogout();\n                    safe(function(){ if (window.closeModal) { window.closeModal('ti-conf-ov'); window.closeModal('ti-file-manager-ov'); } });\n                    safe(function(){ var fm = byId('ti-file-manager-ov'); if (fm) fm.style.display = 'none'; });\n                    return false;\n                }\n                return old.apply(this, arguments);\n            };\n            window.doLogout.__tiGuestGuard505 = true;\n        }\n    }\n    function resync(){ hideGuestLogout(); wrapLogoutNoSession(); }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', resync, {once:true});\n    resync();\n    setTimeout(resync, 100);\n    setTimeout(resync, 500);\n    setTimeout(resync, 1500);\n    setInterval(resync, 2500);\n    if (typeof MutationObserver !== 'undefined') {\n        safe(function(){ new MutationObserver(function(){ setTimeout(resync, 20); }).observe(document.documentElement, {childList:true, subtree:true, attributes:true, attributeFilter:['style','class']}); });\n    }\n})();\n<\/script>\n\n\n\n<script id=\"ti-logout-immediate-safe-506\">\n(function(){\n    \"use strict\";\n    if (window.__tiLogoutImmediateSafe506) return;\n    window.__tiLogoutImmediateSafe506 = true;\n    function byId(id){ return document.getElementById(id); }\n    function safe(fn){ try { return fn(); } catch(e) {} }\n    function text(v){ return String(v == null ? '' : v).trim(); }\n    function hasLogin(){\n        var u = text(window.tiUser || '');\n        if (u) return true;\n        var nm = byId('ti-user-name');\n        var nt = nm ? text(nm.textContent || '') : '';\n        if (nt && !\/^(cliente\\\/?utente|cliente|utente|ospite|guest)$\/i.test(nt)) return true;\n        try { return \/(?:^|;\\s*)ti_ai_ss_session=\/.test(document.cookie || ''); } catch(e) { return false; }\n    }\n    function isLogoutElement(el){\n        if (!el || !el.closest) return null;\n        var btn = el.closest('#ti-logout-btn,[data-ti-logout],.ti-logout-btn');\n        if (btn) return btn;\n        \/\/ Fallback prudente: solo pulsanti reali visibili con testo Esci\/Logout, non dentro file manager o altri popup di chiusura.\n        var b = el.closest('button,input[type=\"button\"],a');\n        if (!b) return null;\n        var label = text(b.textContent || b.value || '').toLowerCase();\n        if (!\/^(esci|logout|logoff)$\/.test(label)) return null;\n        if (b.closest('#ti-file-manager-ov,#ti-ai-gen-ov,#ti-progress-ov,#ti-conf-ov,#ti-profile-ov,#ti-activity-report-ov')) return null;\n        return b;\n    }\n    function closeAllUi(){\n        var ids = ['ti-file-manager-ov','ti-ai-gen-ov','ti-conf-ov','ti-login-ov','ti-profile-ov','ti-newdb-ov','ti-new-db-ov','ti-activity-report-ov','ti-delete-company-ov','ti-progress-ov','ti-order-confirm-ov','ti-help-ov','ti-col-help-ov','ti-global-action-ov'];\n        ids.forEach(function(id){\n            var el = byId(id);\n            if (!el) return;\n            el.style.setProperty('display','none','important');\n            el.style.setProperty('visibility','hidden','important');\n            el.style.setProperty('opacity','0','important');\n            el.style.setProperty('pointer-events','none','important');\n            el.classList.add('ti-hidden');\n        });\n        safe(function(){ document.querySelectorAll('.ti-modal-overlay,.ti-modal-backdrop,.ti-ov').forEach(function(el){\n            if (el && el.id !== 'ti-ai-outer') {\n                el.style.setProperty('display','none','important');\n                el.style.setProperty('pointer-events','none','important');\n            }\n        }); });\n        safe(function(){ if (window.cancelActiveConfigAiRequest434) window.cancelActiveConfigAiRequest434('logout_506'); });\n        safe(function(){ if (window.clearLongAIProcess) window.clearLongAIProcess(); });\n        safe(function(){ if (window.hideProgressPopup) window.hideProgressPopup(false, 'Uscita in corso', {notifyUser:false}); });\n        safe(function(){ var chat=byId('ti-chat-box'); if (chat) chat.innerHTML=''; });\n        safe(function(){ var msg=byId('ti-msg'); if (msg) msg.value=''; });\n        safe(function(){ var un=byId('ti-user-name'); if (un) un.textContent='Cliente\/Utente'; });\n        safe(function(){ var ur=byId('ti-user-role'); if (ur) ur.textContent=''; });\n        safe(function(){ var lb=byId('ti-login-btn'); if (lb) lb.style.display='inline-block'; });\n        safe(function(){ var ob=byId('ti-logout-btn'); if (ob) ob.style.display='none'; });\n    }\n    function clearCookie(){\n        safe(function(){ document.cookie = 'ti_ai_ss_session=; Max-Age=0; path=\/; SameSite=Lax'; });\n        safe(function(){ document.cookie = 'ti_ai_ss_session=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=\/'; });\n    }\n    function logoutRequest(){\n        var fd = new FormData();\n        fd.append('ti_action','ti_ai_logout');\n        fd.append('action','ti_ai_logout');\n        fd.append('_ti_logout_immediate','506');\n        var urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) === -1) urls.push(window.tiUrl);\n        if (!urls.length) urls.push(window.location.href.split('#')[0]);\n        try {\n            if (navigator.sendBeacon) {\n                var qs = new URLSearchParams();\n                qs.append('ti_action','ti_ai_logout');\n                qs.append('action','ti_ai_logout');\n                qs.append('_ti_logout_immediate','506');\n                var blob = new Blob([qs.toString()], {type:'application\/x-www-form-urlencoded'});\n                navigator.sendBeacon(urls[0], blob);\n            }\n        } catch(e) {}\n        try { return fetch(urls[0], {method:'POST', body:fd, credentials:'same-origin', keepalive:true, cache:'no-store'}); } catch(e) { return Promise.resolve(); }\n    }\n    function reloadSoon(){\n        setTimeout(function(){ try { window.location.reload(); } catch(e) { window.location.href = window.location.href.split('#')[0]; } }, 120);\n    }\n    window.tiImmediateLogout506 = function(ev){\n        if (ev) safe(function(){ ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); });\n        if (window.__tiLogoutImmediateRunning506) return false;\n        window.__tiLogoutImmediateRunning506 = true;\n        \/\/ Se il pulsante Esci e' visibile senza sessione, non deve aprire nulla: lo nasconde e basta.\n        if (!hasLogin()) {\n            closeAllUi();\n            clearCookie();\n            window.__tiLogoutImmediateRunning506 = false;\n            return false;\n        }\n        closeAllUi();\n        clearCookie();\n        var fail = setTimeout(reloadSoon, 700);\n        Promise.resolve(logoutRequest()).catch(function(){}).finally(function(){ clearTimeout(fail); reloadSoon(); });\n        return false;\n    };\n    function intercept(ev){\n        \/\/ Dopo l'inizio del logout blocca qualunque evento successivo sul layout: evita il click-through verso Gestione File Associati.\n        if (window.__tiLogoutImmediateRunning506) {\n            if (ev) safe(function(){ ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); });\n            return false;\n        }\n        var btn = isLogoutElement(ev && ev.target);\n        if (!btn) return;\n        \/\/ Intercetto SEMPRE prima degli handler nativi: Esci non deve mai aprire Gestione File Associati o altre form.\n        if (ev) safe(function(){ ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); });\n        return window.tiImmediateLogout506(ev);\n    }\n    ['pointerdown','pointerup','mousedown','mouseup','click','touchstart','touchend','keydown'].forEach(function(evt){\n        safe(function(){ window.addEventListener(evt, function(ev){\n            if (evt === 'keydown') {\n                var k = ev && (ev.key || ev.code || '');\n                if (k !== 'Enter' && k !== ' ' && k !== 'Spacebar') return;\n            }\n            intercept(ev);\n        }, true); });\n        safe(function(){ document.addEventListener(evt, function(ev){\n            if (evt === 'keydown') {\n                var k = ev && (ev.key || ev.code || '');\n                if (k !== 'Enter' && k !== ' ' && k !== 'Spacebar') return;\n            }\n            intercept(ev);\n        }, true); });\n    });\n    function bindLogoutButtons(){\n        safe(function(){ document.querySelectorAll('#ti-logout-btn,[data-ti-logout],.ti-logout-btn').forEach(function(btn){\n            if (!btn) return;\n            btn.onclick = function(ev){ return window.tiImmediateLogout506(ev); };\n            btn.onmousedown = function(ev){ return window.tiImmediateLogout506(ev); };\n            btn.ontouchend = function(ev){ return window.tiImmediateLogout506(ev); };\n            if (!hasLogin()) {\n                btn.style.setProperty('display','none','important');\n                btn.style.setProperty('visibility','hidden','important');\n                btn.style.setProperty('pointer-events','none','important');\n            } else {\n                btn.style.removeProperty('visibility');\n                btn.style.setProperty('pointer-events','auto','important');\n            }\n        }); });\n        if (typeof window.doLogout === 'function' && !window.doLogout.__tiImmediate506) {\n            var prev = window.doLogout;\n            window.doLogout = function(){\n                return window.tiImmediateLogout506(null) || false;\n            };\n            window.doLogout.__tiImmediate506 = true;\n            window.doLogout.__prev506 = prev;\n        }\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bindLogoutButtons, {once:true});\n    bindLogoutButtons();\n    setTimeout(bindLogoutButtons, 100);\n    setTimeout(bindLogoutButtons, 500);\n    setTimeout(bindLogoutButtons, 1500);\n    setInterval(bindLogoutButtons, 2000);\n    if (typeof MutationObserver !== 'undefined') safe(function(){ new MutationObserver(function(){ setTimeout(bindLogoutButtons, 10); }).observe(document.documentElement, {childList:true, subtree:true, attributes:true, attributeFilter:['style','class']}); });\n})();\n<\/script>\n\n<script id=\"ti-config-fast-render-507\">\n(function(){\n    'use strict';\n    if (window.__tiConfigFastRender507Installed) return;\n    window.__tiConfigFastRender507Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){\n        return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(ch){\n            return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[ch];\n        });\n    }\n    function norm(v){\n        try { return String(v == null ? '' : v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').replace(\/[^a-z0-9]+\/g,''); }\n        catch(e){ return String(v == null ? '' : v).toLowerCase().replace(\/[^a-z0-9]+\/g,''); }\n    }\n    function getDbName(){\n        var d = byId('ti-ditta');\n        return d ? String(d.value || '').trim() : '';\n    }\n    function getCnt(){ return byId('ti-conf-cnt'); }\n    function showOverlay(){\n        var ov = byId('ti-conf-ov');\n        try { if (window.showPluginModal) window.showPluginModal(ov || 'ti-conf-ov'); else if (window.openModal) window.openModal('ti-conf-ov'); }\n        catch(e){ if (ov) ov.style.display = 'flex'; }\n    }\n    function setTitle(txt){ var t=byId('ti-conf-title'); if(t) t.textContent = txt || 'Configurazione'; }\n    function showWait(msg){\n        showOverlay();\n        var c = getCnt();\n        if (!c) return;\n        c.innerHTML = '<div style=\"padding:18px;background:#0f172a;color:#e5e7eb;border:1px solid #334155;border-radius:12px;margin:12px;line-height:1.45;\">'\n            + '<b>Caricamento configurazione<\/b><br>'\n            + '<span style=\"font-size:13px;\">' + esc(msg || 'Lettura dati configurazione in corso...') + '<\/span><br><br>'\n            + '<button type=\"button\" id=\"ti-config-fast-cache-507\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\">Apri dati in memoria<\/button>'\n            + '<button type=\"button\" id=\"ti-config-fast-retry-507\" class=\"ti-btn\" style=\"background:#10b981;color:#fff;margin-right:8px;\">Riprova<\/button>'\n            + '<button type=\"button\" id=\"ti-config-fast-close-507\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>'\n            + '<\/div>';\n        bindWaitButtons();\n    }\n    function bindWaitButtons(){\n        var b=byId('ti-config-fast-cache-507'); if(b) b.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation();} return window.openConfigSafe507(ev,false,true); };\n        var r=byId('ti-config-fast-retry-507'); if(r) r.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation();} return window.openConfigSafe507(ev,true,false); };\n        var x=byId('ti-config-fast-close-507'); if(x) x.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation();} window.__tiConfigOpenToken507 = ''; try{ if(window.closeModal) window.closeModal('ti-conf-ov'); }catch(e){} return false; };\n    }\n    function clone(o){ try { return JSON.parse(JSON.stringify(o || {})); } catch(e){ return o || {}; } }\n    function getTables(db){ return (db && db.Tabelle && typeof db.Tabelle === 'object') ? db.Tabelle : {}; }\n    function tableRows(db, tbl){\n        var t = getTables(db)[tbl];\n        if (Array.isArray(t)) return t;\n        if (t && typeof t === 'object') return Object.values(t);\n        return [];\n    }\n    function tableKeys(rows){\n        var seen={}, keys=[];\n        (rows || []).forEach(function(row){\n            if(!row || typeof row !== 'object') return;\n            Object.keys(row).forEach(function(k){ if(!seen[k]){ seen[k]=true; keys.push(k); } });\n        });\n        keys.sort(function(a,b){\n            var pa = (a === 'ID' ? -3 : (\/^(prodotto|servizio|utente|descrizione|tipo|istruzione|username)$\/i.test(a) ? -2 : 0));\n            var pb = (b === 'ID' ? -3 : (\/^(prodotto|servizio|utente|descrizione|tipo|istruzione|username)$\/i.test(b) ? -2 : 0));\n            if(pa !== pb) return pa - pb;\n            return String(a).localeCompare(String(b),'it');\n        });\n        return keys;\n    }\n    function markChanged(tbl, idx, key, el){\n        if(!window.currentDbData || !window.currentDbData.Tabelle || !window.currentDbData.Tabelle[tbl] || !window.currentDbData.Tabelle[tbl][idx]) return;\n        window.currentDbData.Tabelle[tbl][idx][key] = el ? el.value : '';\n        if(!window.modifiedFields) window.modifiedFields = new Set();\n        try { window.modifiedFields.add(tbl + '-' + idx + '-' + key); } catch(e) {}\n        if(el){ el.style.setProperty('border-color','#fbbf24','important'); }\n        try { if(window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n    }\n    window.tiConfigFastMarkChanged507 = markChanged;\n\n    function addRow(tbl){\n        if(!window.currentDbData) window.currentDbData = {Tabelle:{}};\n        if(!window.currentDbData.Tabelle) window.currentDbData.Tabelle = {};\n        if(!Array.isArray(window.currentDbData.Tabelle[tbl])) window.currentDbData.Tabelle[tbl] = [];\n        var rows = window.currentDbData.Tabelle[tbl];\n        var keys = tableKeys(rows);\n        var row = {};\n        keys.forEach(function(k){ row[k] = (k === 'ID') ? (rows.length + 1) : ''; });\n        if(!('ID' in row)) row.ID = rows.length + 1;\n        rows.push(row);\n        renderTable(tbl);\n        try { if(window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n    }\n    window.tiConfigFastAddRow507 = addRow;\n\n    function deleteRow(tbl, idx){\n        if(!window.currentDbData || !window.currentDbData.Tabelle || !Array.isArray(window.currentDbData.Tabelle[tbl])) return;\n        if(!confirm('Eliminare la riga ' + (idx + 1) + ' della tabella ' + tbl + '?')) return;\n        window.currentDbData.Tabelle[tbl].splice(idx,1);\n        renderTable(tbl);\n        try { if(window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n    }\n    window.tiConfigFastDeleteRow507 = deleteRow;\n\n    function renderTable(tbl){\n        var holder = byId('ti-config-fast-table-body-' + norm(tbl));\n        if(!holder || !window.currentDbData) return;\n        var rows = tableRows(window.currentDbData, tbl);\n        var keys = tableKeys(rows);\n        var html = '<div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:8px 0 10px 0;\">'\n            + '<button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;\" onclick=\"window.tiConfigFastAddRow507(\\'' + esc(tbl).replace(\/&#39;\/g,'\\\\\\'') + '\\')\">+ Riga<\/button>'\n            + '<span style=\"font-size:12px;color:#9ca3af;\">' + rows.length + ' righe, ' + keys.length + ' campi. Rendering rapido anti-blocco.<\/span>'\n            + '<\/div>';\n        if(!rows.length){\n            html += '<div style=\"padding:10px;background:#111827;border:1px solid #374151;border-radius:8px;color:#cbd5e1;\">Tabella vuota. Usa + Riga per inserire il primo record.<\/div>';\n            holder.innerHTML = html;\n            return;\n        }\n        html += '<div style=\"overflow:auto;max-height:62vh;border:1px solid #334155;border-radius:8px;background:#020617;\">';\n        html += '<table class=\"ti-cfg-table ti-config-fast-table-507\" style=\"border-collapse:collapse;width:max-content;min-width:100%;font-size:12px;\">';\n        html += '<thead><tr><th style=\"position:sticky;left:0;top:0;z-index:3;background:#1f2937;color:#fff;padding:6px;border:1px solid #374151;\">Riga<\/th>';\n        keys.forEach(function(k){ html += '<th style=\"position:sticky;top:0;z-index:2;background:#1f2937;color:#fff;padding:6px;border:1px solid #374151;white-space:nowrap;\">' + esc(k) + '<\/th>'; });\n        html += '<th style=\"position:sticky;right:0;top:0;z-index:3;background:#1f2937;color:#fff;padding:6px;border:1px solid #374151;\">Azioni<\/th><\/tr><\/thead><tbody>';\n        rows.forEach(function(row, idx){\n            html += '<tr><td style=\"position:sticky;left:0;z-index:2;background:#111827;color:#e5e7eb;padding:5px;border:1px solid #374151;text-align:center;\">' + (idx+1) + '<\/td>';\n            keys.forEach(function(k){\n                var val = row && row[k] != null ? row[k] : '';\n                var inputId = 'ti-fast-cfg-' + norm(tbl) + '-' + idx + '-' + norm(k);\n                var isLong = String(val).length > 55 || \/istruzioni|descrizione|nota|note|qr|link\/i.test(k);\n                var common = 'id=\"' + inputId + '\" data-tbl=\"' + esc(tbl) + '\" data-idx=\"' + idx + '\" data-key=\"' + esc(k) + '\" onchange=\"window.tiConfigFastMarkChanged507(\\'' + esc(tbl).replace(\/&#39;\/g,'\\\\\\'') + '\\',' + idx + ',\\'' + esc(k).replace(\/&#39;\/g,'\\\\\\'') + '\\',this)\" oninput=\"this.dataset.dirty=1\"';\n                html += '<td style=\"padding:4px;border:1px solid #1f2937;vertical-align:top;background:#0b1120;\">';\n                if(isLong) html += '<textarea ' + common + ' rows=\"2\" style=\"min-width:220px;max-width:360px;background:#111827;color:#fff;border:1px solid #374151;border-radius:5px;padding:5px;\">' + esc(val) + '<\/textarea>';\n                else html += '<input ' + common + ' value=\"' + esc(val) + '\" style=\"min-width:120px;background:#111827;color:#fff;border:1px solid #374151;border-radius:5px;padding:5px;\">';\n                html += '<\/td>';\n            });\n            html += '<td style=\"position:sticky;right:0;background:#111827;padding:5px;border:1px solid #374151;\"><button type=\"button\" class=\"ti-btn\" style=\"background:#991b1b;color:#fff;font-size:11px;padding:5px 7px;\" onclick=\"window.tiConfigFastDeleteRow507(\\'' + esc(tbl).replace(\/&#39;\/g,'\\\\\\'') + '\\',' + idx + ')\">Elimina<\/button><\/td>';\n            html += '<\/tr>';\n        });\n        html += '<\/tbody><\/table><\/div>';\n        holder.innerHTML = html;\n        try { if (window.ensureAuthorizedConfigInputsEditable512) window.ensureAuthorizedConfigInputsEditable512(holder); } catch(e) {}\n    }\n    window.tiConfigFastRenderTable507 = renderTable;\n\n    function renderLite(sourceLabel){\n        var db = window.currentDbData || {Tabelle:{}};\n        var tables = Object.keys(getTables(db)).filter(function(t){ return t && t !== 'undefined'; });\n        setTitle('Impostazioni ' + (window.currentDittaName || getDbName() || 'Ditta') + ' - caricamento rapido');\n        var c = getCnt();\n        if(!c) return false;\n        var html = '<div id=\"ti-config-fast-507\" style=\"padding:12px;color:#e5e7eb;\">'\n            + '<div style=\"background:#082f49;border:1px solid #0284c7;border-radius:12px;padding:12px;margin-bottom:12px;line-height:1.45;\">'\n            + '<b>Configurazione caricata in modalit\u00e0 rapida<\/b><br>'\n            + '<span style=\"font-size:13px;\">Origine: ' + esc(sourceLabel || 'database') + '. Le tabelle vengono renderizzate solo quando vengono aperte, evitando blocchi della pagina.<\/span><br>'\n            + '<span style=\"font-size:12px;color:#bae6fd;\">Database: ' + esc((db && db.Database) || getDbName()) + ' - Tabelle: ' + tables.length + '<\/span>'\n            + '<\/div>'\n            + '<div style=\"background:#111827;border:1px solid #334155;border-radius:12px;padding:12px;margin-bottom:12px;\">'\n            + '<h4 style=\"margin:0 0 8px 0;color:#fff;\">Istruzioni AI configurazione<\/h4>'\n            + '<textarea id=\"ti-conf-ai-prompt\" class=\"ti-in\" rows=\"7\" placeholder=\"Scrivi qui la richiesta per la AI di configurazione.\" style=\"min-height:150px;line-height:1.45;width:100%;\"><\/textarea>'\n            + '<div id=\"ti-conf-ai-chat-actions\" style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:8px;\">'\n            + '<button type=\"button\" id=\"ti-conf-ai-send\" class=\"ti-btn\" style=\"background:#10b981;color:#fff;font-weight:800;\" onclick=\"window.runConfigBot&&window.runConfigBot();return false;\">Invia<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;\" onclick=\"window.verifyInstructionsArchive&&window.verifyInstructionsArchive();return false;\">Verifica istruzioni<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" style=\"background:#1d4ed8;color:#fff;\" onclick=\"window.openActivityReport&&window.openActivityReport();return false;\">Report Attivit\u00e0<\/button>'\n            + '<\/div><div id=\"ti-conf-ai-reply\" style=\"display:none;\"><\/div><\/div>';\n        if(!tables.length){\n            html += '<div style=\"padding:14px;background:#111827;border:1px solid #374151;border-radius:10px;\">Nessuna tabella trovata nel database.<\/div>';\n        } else {\n            tables.forEach(function(t){\n                var rows = tableRows(db,t);\n                html += '<details class=\"ti-cfg-details ti-config-fast-details-507\" data-tbl=\"' + esc(t) + '\" style=\"background:#111827;margin-bottom:8px;border-radius:10px;border:1px solid #334155;\">'\n                    + '<summary style=\"cursor:pointer;padding:10px 12px;color:#fff;font-weight:700;display:flex;justify-content:space-between;gap:10px;align-items:center;\">'\n                    + '<span>' + esc(t) + '<\/span><span style=\"font-size:12px;color:#93c5fd;\">' + rows.length + ' righe<\/span><\/summary>'\n                    + '<div id=\"ti-config-fast-table-body-' + norm(t) + '\" style=\"padding:8px 10px 12px 10px;\"><button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;\" onclick=\"window.tiConfigFastRenderTable507(\\'' + esc(t).replace(\/&#39;\/g,'\\\\\\'') + '\\')\">Apri tabella<\/button><\/div>'\n                    + '<\/details>';\n            });\n        }\n        html += '<\/div>';\n        c.innerHTML = html;\n        c.scrollTop = 0;\n        Array.prototype.slice.call(c.querySelectorAll('details.ti-config-fast-details-507')).forEach(function(det){\n            det.addEventListener('toggle', function(){ if(det.open){ var tbl = det.getAttribute('data-tbl') || ''; var body = byId('ti-config-fast-table-body-' + norm(tbl)); if(body && body.querySelector('button')) renderTable(tbl); } });\n        });\n        try { if(window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n        try { if(window.syncConfiguratorOperationalTipsFlagUI) window.syncConfiguratorOperationalTipsFlagUI(); } catch(e) {}\n        window.__tiConfigRenderedOk507 = true;\n        return true;\n    }\n    window.tiConfigFastRender507 = renderLite;\n\n    function fetchDb(db, token){\n        var fd;\n        try { if(window.buildAjaxFormData) fd = window.buildAjaxFormData('ti_ai_get_db_data', {db:db, ti_config_open:'1', rescue:'507'}); } catch(e) {}\n        if(!fd){ fd = new FormData(); fd.append('action','ti_ai_get_db_data'); fd.append('ti_action','ti_ai_get_db_data'); fd.append('db',db); fd.append('ti_config_open','1'); fd.append('rescue','507'); }\n        try { if(!fd.has('action')) fd.append('action','ti_ai_get_db_data'); if(!fd.has('ti_action')) fd.append('ti_action','ti_ai_get_db_data'); } catch(e) {}\n        return fetch(window.tiUrl || location.href, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'}).then(function(r){ return r.text(); }).then(function(txt){\n            var res;\n            try { res = JSON.parse(txt); } catch(e){ throw new Error('Risposta non JSON durante lettura configurazione. Inizio risposta: ' + txt.slice(0,180)); }\n            if(!res || !res.success) throw new Error((res && res.data && (res.data.message || res.data)) || 'Lettura database non riuscita.');\n            if(window.__tiConfigOpenToken507 !== token) return false;\n            window.currentDbData = res.data || {Tabelle:{}};\n            window.configOriginalDbData = clone(window.currentDbData);\n            window.configWasSaved = false;\n            if(window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear();\n            try { localStorage.setItem('ti_config_cache_507_' + db, JSON.stringify(window.currentDbData)); } catch(e) {}\n            renderLite('server');\n            return true;\n        });\n    }\n\n    function cachedDb(db){\n        if(window.currentDbData && window.currentDbData.Tabelle) return window.currentDbData;\n        try { var s = localStorage.getItem('ti_config_cache_507_' + db); if(s) return JSON.parse(s); } catch(e) {}\n        return null;\n    }\n\n    window.openConfigSafe507 = function(ev, forceReload, cacheOnly){\n        if(ev){ try { ev.preventDefault(); ev.stopPropagation(); } catch(e){} }\n        var db = getDbName();\n        if(!db){ try { if(window.tiAlert) window.tiAlert('Seleziona una ditta prima di aprire la configurazione.'); else alert('Seleziona una ditta prima di aprire la configurazione.'); } catch(e){} return false; }\n        var token = String(Date.now()) + '-' + Math.random();\n        window.__tiConfigOpenToken507 = token;\n        window.__tiConfigRenderedOk507 = false;\n        showWait('Lettura database della ditta ' + db + '.');\n        var cdb = cachedDb(db);\n        if(cacheOnly && cdb){ window.currentDbData = cdb; renderLite('dati in memoria'); return false; }\n        var slow = setTimeout(function(){\n            if(window.__tiConfigOpenToken507 !== token || window.__tiConfigRenderedOk507) return;\n            var cd = cachedDb(db);\n            if(cd){ window.currentDbData = cd; renderLite('cache mentre il server risponde'); }\n            else showWait('Il server sta impiegando piu del previsto. Puoi aprire dati in memoria, riprovare o chiudere.');\n        }, 1800);\n        fetchDb(db, token).catch(function(err){\n            if(window.__tiConfigOpenToken507 !== token) return;\n            var cd = cachedDb(db);\n            if(cd){ window.currentDbData = cd; renderLite('cache dopo errore server'); return; }\n            var c = getCnt();\n            if(c) c.innerHTML = '<div style=\"padding:18px;margin:12px;background:#450a0a;color:#fee2e2;border:1px solid #ef4444;border-radius:12px;line-height:1.45;\">'\n                + '<b>Configurazione non caricata.<\/b><br>' + esc(err && err.message ? err.message : err)\n                + '<br><br><button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\" onclick=\"window.openConfigSafe507(event,true,false)\">Riprova<\/button>'\n                + '<button type=\"button\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\" onclick=\"window.closeModal&&window.closeModal(\\'ti-conf-ov\\')\">Chiudi<\/button><\/div>';\n        }).finally(function(){ clearTimeout(slow); try { window.__tiConfigReadWaitActive503 = false; } catch(e){} });\n        return false;\n    };\n\n    var previousRender = window.renderConfig;\n    window.renderConfigFullLegacy507 = previousRender;\n    window.renderConfig = function(){\n        try {\n            if(window.__tiPreferLegacyConfigRender507 && typeof previousRender === 'function') return previousRender.apply(this, arguments);\n        } catch(e) {}\n        try { return renderLite('render rapido'); }\n        catch(e) {\n            try { console.error('Shop & Service Config fast render 507 error', e); } catch(x) {}\n            if(typeof previousRender === 'function') return previousRender.apply(this, arguments);\n        }\n    };\n\n    ['openConfigSafe506','openConfigSafe505','openConfigSafe504','openConfigSafe503','openConfigSafe502','openConfigSafe501','openConfigSafe492','openConfigSafe491','openConfigSafe490','openConfigSafe489','openConfigSafe488','openConfigSafe218'].forEach(function(n){ window[n] = window.openConfigSafe507; });\n\n    function bind(){\n        var b = byId('ti-conf-toggle');\n        if(b){ b.onclick = window.openConfigSafe507; b.setAttribute('data-ti-config-open-rescue','507'); }\n    }\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if(!t || !t.closest) return;\n        var b = t.closest('#ti-conf-toggle, #ti-config-fast-retry-507, #ti-config-fast-cache-507');\n        if(!b) return;\n        ev.preventDefault(); ev.stopPropagation();\n        if(b.id === 'ti-config-fast-cache-507') return window.openConfigSafe507(ev,false,true);\n        return window.openConfigSafe507(ev, b.id !== 'ti-conf-toggle', false);\n    }, true);\n    if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bind); else bind();\n    setTimeout(bind, 300); setTimeout(bind, 1200);\n})();\n\n\n(function(){\n    if (window.__tiBottomButtonsCompact508) return;\n    window.__tiBottomButtonsCompact508 = true;\n    function byId(id){ return document.getElementById(id); }\n    function stripIcons(txt){\n        txt = String(txt || '').replace(\/<[^>]*>\/g, ' ');\n        txt = txt.replace(\/[\\u{1F300}-\\u{1FAFF}\\u{2600}-\\u{27BF}]\/gu, ' ');\n        txt = txt.replace(\/[\u2699\ufe0f\u26a1\ud83d\uded2\ud83e\uddf9\ud83d\uddd1\ud83e\ude84\ud83d\uddc2\ud83d\udcca\ud83d\udcc1\ud83e\udd16\ud83d\udd0a\ud83d\udd07\ud83c\udfa4\ud83d\udcf7]\/g, ' ');\n        return txt.replace(\/\\s+\/g, ' ').trim();\n    }\n    var labels = {\n        'ti-new':'Nuova',\n        'ti-copy':'Copia testo',\n        'ti-file':'File',\n        'ti-products-btn':'Prodotti',\n        'ti-services-btn':'Servizi',\n        'ti-activities-btn':'Attivita',\n        'ti-kbd-btn':'Tastiera',\n        'ti-mic':'Mic',\n        'ti-cam':'Cam',\n        'ti-mute-btn':'Audio',\n        'ti-help-btn':'?',\n        'ti-conf-toggle':'Configura',\n        'ti-activation-toggle':'Attiva',\n        'ti-purchase-toggle':'Acquisizioni',\n        'ti-clean-orphans-btn':'Pulisci',\n        'ti-delete-company-toggle':'Cancella',\n        'ti-global-action-toggle':'Globale',\n        'ti-shared-setup-toggle':'Storage'\n    };\n    function setPlainButton(btn, label){\n        if (!btn) return;\n        var id = btn.id || '';\n        var finalLabel = label || labels[id] || stripIcons(btn.textContent || btn.innerHTML || '');\n        if (!finalLabel) finalLabel = stripIcons(btn.getAttribute('aria-label') || btn.getAttribute('title') || id || '');\n        if (id === 'ti-mute-btn') {\n            var t = stripIcons(btn.textContent || '');\n            if (\/auto\/i.test(t)) finalLabel = 'Audio Auto';\n            else if (\/off sessione\/i.test(t)) finalLabel = 'Audio sess.';\n            else if (\/off\/i.test(t)) finalLabel = 'Audio OFF';\n            else if (\/on\/i.test(t)) finalLabel = 'Audio ON';\n            else finalLabel = 'Audio';\n        }\n        btn.textContent = finalLabel;\n        btn.setAttribute('data-ti-compact-508', '1');\n        btn.style.setProperty('font-size', id === 'ti-help-btn' ? '11px' : '9.5px', 'important');\n        btn.style.setProperty('height', '27px', 'important');\n        btn.style.setProperty('min-height', '27px', 'important');\n        btn.style.setProperty('padding', id === 'ti-help-btn' ? '0' : '3px 4px', 'important');\n        btn.style.setProperty('min-width', '0', 'important');\n        btn.style.setProperty('max-width', id === 'ti-help-btn' ? '28px' : 'none', 'important');\n        btn.style.setProperty('width', id === 'ti-help-btn' ? 'auto' : 'auto', 'important');\n        btn.style.setProperty('white-space', 'nowrap', 'important');\n        btn.style.setProperty('overflow', 'hidden', 'important');\n        btn.style.setProperty('text-overflow', 'ellipsis', 'important');\n        btn.style.setProperty('line-height', '1', 'important');\n        btn.style.setProperty('border-radius', '6px', 'important');\n        btn.style.setProperty('gap', '0', 'important');\n        btn.style.setProperty('display', btn.style.display === 'none' ? 'none' : 'flex', 'important');\n        btn.style.setProperty('align-items', 'center', 'important');\n        btn.style.setProperty('justify-content', 'center', 'important');\n        if (id === 'ti-help-btn') btn.style.setProperty('flex', '0 1 26px', 'important');\n        else btn.style.setProperty('flex', '1 1 0', 'important');\n    }\n    function compactRow(row){\n        if (!row) return;\n        row.style.setProperty('width', '100%', 'important');\n        row.style.setProperty('box-sizing', 'border-box', 'important');\n        row.style.setProperty('display', row.style.display === 'none' ? 'none' : 'flex', 'important');\n        row.style.setProperty('flex-direction', 'row', 'important');\n        row.style.setProperty('flex-wrap', 'nowrap', 'important');\n        row.style.setProperty('gap', '4px', 'important');\n        row.style.setProperty('overflow', 'hidden', 'important');\n        row.style.setProperty('align-items', 'stretch', 'important');\n        row.style.setProperty('justify-content', 'stretch', 'important');\n    }\n    window.tiCompactMainBottomButtons508 = function(){\n        try {\n            var actionRow = document.querySelector('#ti-ai-outer .ti-actions-row');\n            var adminRow = byId('ti-admin-actions-row');\n            compactRow(actionRow);\n            compactRow(adminRow);\n            Object.keys(labels).forEach(function(id){ setPlainButton(byId(id), labels[id]); });\n            if (adminRow) Array.prototype.slice.call(adminRow.querySelectorAll('button,.ti-btn')).forEach(function(b){ setPlainButton(b); });\n            if (actionRow) Array.prototype.slice.call(actionRow.querySelectorAll('button,.ti-btn,.ti-help')).forEach(function(b){ setPlainButton(b); });\n        } catch(e) { try { console.warn('Shop & Service compact bottom buttons 508 error', e); } catch(x) {} }\n    };\n    var prevSetAdminUI = window.setAdminUI;\n    if (typeof prevSetAdminUI === 'function' && !prevSetAdminUI.__tiCompact508) {\n        window.setAdminUI = function(){\n            var r = prevSetAdminUI.apply(this, arguments);\n            setTimeout(window.tiCompactMainBottomButtons508, 0);\n            setTimeout(window.tiCompactMainBottomButtons508, 250);\n            return r;\n        };\n        window.setAdminUI.__tiCompact508 = true;\n    }\n    var prevApplyLayoutLang = window.applyLayoutLanguage;\n    if (typeof prevApplyLayoutLang === 'function' && !prevApplyLayoutLang.__tiCompact508) {\n        window.applyLayoutLanguage = function(){\n            var r = prevApplyLayoutLang.apply(this, arguments);\n            setTimeout(window.tiCompactMainBottomButtons508, 0);\n            return r;\n        };\n        window.applyLayoutLanguage.__tiCompact508 = true;\n    }\n    if (typeof MutationObserver !== 'undefined') {\n        try { new MutationObserver(function(m){\n            var needs = false;\n            (m || []).forEach(function(x){\n                if (x.target && x.target.closest && (x.target.closest('.ti-actions-row') || x.target.closest('#ti-admin-actions-row'))) needs = true;\n                (x.addedNodes || []).forEach(function(n){ if (n && n.nodeType === 1 && (n.matches && (n.matches('.ti-btn,button,#ti-admin-actions-row,.ti-actions-row') || n.querySelector && n.querySelector('#ti-admin-actions-row,.ti-actions-row,.ti-btn,button')))) needs = true; });\n            });\n            if (needs) setTimeout(window.tiCompactMainBottomButtons508, 20);\n        }).observe(document.getElementById('ti-ai-outer') || document.body, {childList:true, subtree:true}); } catch(e) {}\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', window.tiCompactMainBottomButtons508); else window.tiCompactMainBottomButtons508();\n    setTimeout(window.tiCompactMainBottomButtons508, 300);\n    setTimeout(window.tiCompactMainBottomButtons508, 1000);\n    setTimeout(window.tiCompactMainBottomButtons508, 2000);\n})();\n\n\n(function(){\n    'use strict';\n    if (window.__tiConfigFullMode509Installed) return;\n    window.__tiConfigFullMode509Installed = true;\n    window.__tiPreferLegacyConfigRender507 = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(ch){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[ch]; }); }\n    function clone(o){ try { return JSON.parse(JSON.stringify(o || {})); } catch(e){ return o || {}; } }\n    function getDbName(){ var d=byId('ti-ditta'); return d ? String(d.value || '').trim() : ''; }\n    function getCnt(){ return byId('ti-conf-cnt'); }\n    function showOverlay(){\n        var ov=byId('ti-conf-ov');\n        try { if (window.showPluginModal) window.showPluginModal(ov || 'ti-conf-ov'); else if (window.openModal) window.openModal('ti-conf-ov'); else if (ov) ov.style.display='flex'; }\n        catch(e){ if (ov) ov.style.display='flex'; }\n    }\n    function closeOverlay(){ try { if(window.closeModal) window.closeModal('ti-conf-ov'); } catch(e){} }\n    function setTitle(){ var t=byId('ti-conf-title'); if(t) t.textContent='Configurazione'; }\n    function setWait(msg){\n        showOverlay(); setTitle();\n        var c=getCnt(); if(!c) return;\n        c.innerHTML = '<div style=\"padding:18px;background:#0f172a;color:#e5e7eb;border:1px solid #334155;border-radius:12px;margin:12px;line-height:1.45;\">'\n            + '<b>Caricamento configurazione completa<\/b><br>'\n            + '<span style=\"font-size:13px;\">' + esc(msg || 'Lettura dati e apertura tabelle complete in corso...') + '<\/span><br><br>'\n            + '<button type=\"button\" id=\"ti-config-full-retry-509\" class=\"ti-btn\" style=\"background:#10b981;color:#fff;margin-right:8px;\">Riprova<\/button>'\n            + '<button type=\"button\" id=\"ti-config-full-cache-509\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\">Apri dati in memoria<\/button>'\n            + '<button type=\"button\" id=\"ti-config-full-exit-510\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;margin-right:8px;\">Esci<\/button>'\n            + '<button type=\"button\" id=\"ti-config-full-close-509\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\">Chiudi<\/button>'\n            + '<\/div>';\n        bindWaitButtons();\n    }\n    function bindWaitButtons(){\n        var r=byId('ti-config-full-retry-509'); if(r) r.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation();} return window.openConfigSafe509(ev,true,false); };\n        var b=byId('ti-config-full-cache-509'); if(b) b.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation();} return window.openConfigSafe509(ev,false,true); };\n        var e=byId('ti-config-full-exit-510'); if(e) e.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation();} if(window.tiExitConfigLoading510) return window.tiExitConfigLoading510('Caricamento configurazione interrotto. Ripeti l apertura della Configurazione.'); window.__tiConfigOpenToken509=''; closeOverlay(); return false; };\n        var x=byId('ti-config-full-close-509'); if(x) x.onclick=function(ev){ if(ev){ev.preventDefault(); ev.stopPropagation();} window.__tiConfigOpenToken509=''; closeOverlay(); return false; };\n    }\n    function legacyRenderer(){\n        var r = window.renderConfigFullLegacy507;\n        if (typeof r === 'function') return r;\n        if (window.renderConfig && window.renderConfig !== legacyRenderWrapper) return window.renderConfig;\n        return null;\n    }\n    function legacyRenderWrapper(){ return renderFull('wrapper'); }\n    function renderFull(source){\n        window.__tiPreferLegacyConfigRender507 = true;\n        showOverlay(); setTitle();\n        var r = legacyRenderer();\n        if (typeof r === 'function') {\n            try {\n                var ret = r.call(window);\n                setTimeout(function(){\n                    try { if (window.forceConfigAiUi) window.forceConfigAiUi(); } catch(e){}\n                    try { if (window.tiCompactMainBottomButtons508) window.tiCompactMainBottomButtons508(); } catch(e){}\n                    try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e){}\n                }, 60);\n                try { console.log('Shop & Service Configurazione completa aperta 509:', source || ''); } catch(e){}\n                return ret;\n            } catch(e) {\n                throw e;\n            }\n        }\n        throw new Error('Renderer configurazione completa non disponibile.');\n    }\n    function cachedDb(db){\n        \/\/ 30.9.664: usa dati in memoria solo se appartengono alla ditta selezionata.\n        try {\n            if(window.currentDbData && window.currentDbData.Tabelle){\n                var curDb = String(window.currentDbData.Database || window.currentDbData.database || window.currentDbData.__db || '').replace(\/\\.json$\/i,'');\n                var reqDb = String(db || '').replace(\/\\.json$\/i,'');\n                if(!curDb || curDb === reqDb) return window.currentDbData;\n            }\n        } catch(e){}\n        try { var s = localStorage.getItem('ti_config_cache_507_' + db) || localStorage.getItem('ti_config_cache_509_' + db); if(s) return JSON.parse(s); } catch(e){}\n        return null;\n    }\n    function prepareData(data, db){\n        window.currentDbData = data || {Tabelle:{}};\n        window.configOriginalDbData = clone(window.currentDbData);\n        window.configWasSaved = false;\n        try { if(window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); } catch(e){}\n        try { localStorage.setItem('ti_config_cache_509_' + db, JSON.stringify(window.currentDbData)); } catch(e){}\n        try { localStorage.setItem('ti_config_cache_507_' + db, JSON.stringify(window.currentDbData)); } catch(e){}\n    }\n    function buildFd(db){\n        var fd = null;\n        try { if(window.buildAjaxFormData) fd = window.buildAjaxFormData('ti_ai_get_db_data', {db:db, ti_config_open:'1', rescue:'509', full_config:'1'}); } catch(e){}\n        if(!fd){ fd = new FormData(); fd.append('action','ti_ai_get_db_data'); fd.append('ti_action','ti_ai_get_db_data'); fd.append('db',db); fd.append('ti_config_open','1'); fd.append('rescue','509'); fd.append('full_config','1'); }\n        try { if(!fd.has('action')) fd.append('action','ti_ai_get_db_data'); if(!fd.has('ti_action')) fd.append('ti_action','ti_ai_get_db_data'); } catch(e){}\n        return fd;\n    }\n    function fetchDb(db, token){\n        var url = window.tiAjaxUrl || window.tiUrl || location.href;\n        var opts = {method:'POST', body:buildFd(db), credentials:'same-origin', cache:'no-store'};\n        try { if(window.__tiConfigOpenAbort510 && window.__tiConfigOpenAbort510.signal) opts.signal = window.__tiConfigOpenAbort510.signal; } catch(e){}\n        return fetch(url, opts).then(function(r){ return r.text(); }).then(function(txt){\n            var res;\n            try { res = JSON.parse(txt); } catch(e){ throw new Error('Risposta non JSON durante lettura configurazione completa. Inizio risposta: ' + txt.slice(0,180)); }\n            if(!res || !res.success) throw new Error((res && res.data && (res.data.message || res.data)) || 'Lettura database non riuscita.');\n            if(window.__tiConfigOpenToken509 !== token) return false;\n            prepareData(res.data || {Tabelle:{}}, db);\n            renderFull('server');\n            window.__tiConfigOpenToken509 = '';\n            try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'Configurazione caricata', {notifyUser:false}); } catch(e){}\n            try { if (window.closeProgressPopup) window.closeProgressPopup(); } catch(e){}\n            return true;\n        });\n    }\n    window.openConfigSafe509 = function(ev, forceReload, cacheOnly){\n        if(ev){ try { ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e){} }\n        var db = getDbName();\n        if(!db){ try { if(window.tiAlert) window.tiAlert('Seleziona una ditta prima di aprire la configurazione.'); else alert('Seleziona una ditta prima di aprire la configurazione.'); } catch(e){} return false; }\n        var token = String(Date.now()) + '-' + Math.random();\n        window.__tiConfigOpenToken509 = token;\n        try { if(window.__tiConfigOpenAbort510 && window.__tiConfigOpenAbort510.abort) window.__tiConfigOpenAbort510.abort(); } catch(e){}\n        try { window.__tiConfigOpenAbort510 = (typeof AbortController !== 'undefined') ? new AbortController() : null; } catch(e){ window.__tiConfigOpenAbort510 = null; }\n        window.__tiConfigRenderedOk507 = true;\n        window.__tiConfigFullModeActive509 = true;\n        try { window.__tiConfigReadWaitActive503 = false; } catch(e){}\n        try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'Configurazione caricata', {notifyUser:false}); } catch(e){}\n        try { if (window.closeProgressPopup) window.closeProgressPopup(); } catch(e){}\n\n        \/\/ 30.9.664: se i dati della ditta sono gia in memoria\/cache, apri subito la configurazione\n        \/\/ senza riavviare monitor progresso o riletture server bloccanti. Il pulsante Riprova forza la rilettura.\n        var immediateData = cachedDb(db);\n        if(!forceReload && immediateData){\n            try {\n                prepareData(immediateData, db);\n                renderFull(cacheOnly ? 'dati in memoria' : 'dati in memoria rapida');\n                window.__tiConfigOpenToken509 = '';\n                return false;\n            } catch(e) { showError(e); return false; }\n        }\n        if(cacheOnly){\n            showError(new Error('Dati in memoria non disponibili per la ditta ' + db + '. Premi Riprova per rileggerli dal database.'));\n            return false;\n        }\n\n        setWait('Lettura dati configurazione della ditta ' + db + '.');\n        var slow = setTimeout(function(){\n            if(window.__tiConfigOpenToken509 !== token) return;\n            setWait('La lettura richiede piu tempo del previsto. Puoi attendere, premere Riprova oppure Esci.');\n        }, 4500);\n        fetchDb(db, token).catch(function(err){\n            if(window.__tiConfigOpenToken509 !== token) return;\n            var cd = cachedDb(db);\n            if(cd){\n                try { prepareData(cd, db); renderFull('cache dopo errore server'); window.__tiConfigOpenToken509=''; try { if(window.hideProgressPopup) window.hideProgressPopup(true, 'Configurazione caricata', {notifyUser:false}); } catch(_e){} return; }\n                catch(e){ err = e; }\n            }\n            showError(err);\n        }).finally(function(){ clearTimeout(slow); try { window.__tiConfigReadWaitActive503 = false; } catch(e){} });\n        return false;\n    };\n    function showError(err){\n        var c=getCnt(); showOverlay();\n        if(c) c.innerHTML = '<div style=\"padding:18px;margin:12px;background:#450a0a;color:#fee2e2;border:1px solid #ef4444;border-radius:12px;line-height:1.45;\">'\n            + '<b>Configurazione completa non caricata.<\/b><br>' + esc(err && err.message ? err.message : err)\n            + '<br><br><button type=\"button\" class=\"ti-btn\" style=\"background:#10b981;color:#fff;margin-right:8px;\" onclick=\"window.openConfigSafe509(event,true,false)\">Riprova<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" style=\"background:#2563eb;color:#fff;margin-right:8px;\" onclick=\"window.openConfigSafe509(event,false,true)\">Apri dati in memoria<\/button>'\n            + '<button type=\"button\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\" onclick=\"window.closeModal&&window.closeModal(\\'ti-conf-ov\\')\">Chiudi<\/button><\/div>';\n        try { console.error('Shop & Service Configurazione completa 509 error', err); } catch(e){}\n    }\n\n    \/\/ Ripristina il render completo come render principale e reindirizza tutti gli opener al flusso completo 509.\n    if (typeof window.renderConfigFullLegacy507 === 'function') {\n        window.renderConfig = function(){ return renderFull('renderConfig completo'); };\n    }\n    ['openConfigSafe508','openConfigSafe507','openConfigSafe506','openConfigSafe505','openConfigSafe504','openConfigSafe503','openConfigSafe502','openConfigSafe501','openConfigSafe492','openConfigSafe491','openConfigSafe490','openConfigSafe489','openConfigSafe488','openConfigSafe218'].forEach(function(n){ window[n] = window.openConfigSafe509; });\n    function bind(){ var b=byId('ti-conf-toggle'); if(b){ b.onclick=window.openConfigSafe509; b.setAttribute('data-ti-config-open-rescue','509-full'); } }\n    document.addEventListener('click', function(ev){\n        var t=ev && ev.target; if(!t || !t.closest) return;\n        var b=t.closest('#ti-conf-toggle,#ti-config-fast-retry-507,#ti-config-fast-cache-507,#ti-config-full-retry-509,#ti-config-full-cache-509');\n        if(!b) return;\n        ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n        if(b.id === 'ti-config-fast-cache-507' || b.id === 'ti-config-full-cache-509') return window.openConfigSafe509(ev,false,true);\n        return window.openConfigSafe509(ev, b.id !== 'ti-conf-toggle', false);\n    }, true);\n    if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bind); else bind();\n    setTimeout(bind, 200); setTimeout(bind, 900); setTimeout(bind, 1800);\n})();\n\n(function(){\n    'use strict';\n    if (window.__tiConfigExitWait510Installed) return;\n    window.__tiConfigExitWait510Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){ return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(ch){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[ch]; }); }\n    function closeConfigOverlay(){\n        try { if(window.closeModal) window.closeModal('ti-conf-ov'); } catch(e){}\n        var ov = byId('ti-conf-ov'); if(ov) { try { ov.style.display='none'; ov.classList.remove('ti-modal-open','open','show'); } catch(e){} }\n    }\n    window.tiExitConfigLoading510 = function(message){\n        window.__tiConfigOpenToken509 = '';\n        window.__tiConfigOpenToken501 = '';\n        window.__tiConfigOpenToken492 = '';\n        window.__tiConfigReadWaitActive503 = false;\n        try { if(window.__tiConfigOpenAbort510 && window.__tiConfigOpenAbort510.abort) window.__tiConfigOpenAbort510.abort(); } catch(e){}\n        try { if(window.hideProgressPopup) window.hideProgressPopup(); } catch(e){}\n        try { if(window.closeProgressPopup) window.closeProgressPopup(); } catch(e){}\n        closeConfigOverlay();\n        var msg = message || 'Caricamento configurazione interrotto. Ripeti l apertura della Configurazione.';\n        setTimeout(function(){\n            try { if(window.tiAlert) window.tiAlert(msg); else alert(msg); } catch(e){}\n        }, 80);\n        return false;\n    };\n    function addExitButtonToWait(){\n        var c = byId('ti-conf-cnt');\n        if (c && \/Caricamento configurazione|Lettura dati configurazione|Avanzamento stimato\/i.test(String(c.textContent || '')) && !byId('ti-config-full-exit-510')) {\n            var wrap = document.createElement('div');\n            wrap.style.marginTop = '10px';\n            wrap.innerHTML = '<button type=\"button\" id=\"ti-config-full-exit-510\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;margin-right:8px;\">Esci<\/button>';\n            c.appendChild(wrap);\n        }\n        var b = byId('ti-config-full-exit-510');\n        if (b && !b.__tiExit510Bound) {\n            b.__tiExit510Bound = true;\n            b.onclick = function(ev){ if(ev){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } return window.tiExitConfigLoading510(); };\n        }\n        \/\/ 30.9.664: il pulsante Esci del caricamento configurazione deve essere unico.\n        var progressButtons = Array.prototype.slice.call(document.querySelectorAll('.ti-config-progress-exit-510'));\n        for (var pi=1; pi<progressButtons.length; pi++) { try { progressButtons[pi].remove(); } catch(e){} }\n        if (!document.querySelector('.ti-config-progress-exit-510')) {\n            var candidates = Array.prototype.slice.call(document.querySelectorAll('#ti-progress-ov .ti-modal,#ti-ai-loader-ov .ti-loader-box,.ti-loader-box,.ti-progress-box,[id*=\"progress\"],[id*=\"loader\"]'));\n            var target = null;\n            for (var i=0;i<candidates.length;i++) {\n                var n = candidates[i];\n                if (!n || !n.textContent) continue;\n                var tx = String(n.textContent || '');\n                if (\/Caricamento configurazione|Lettura dati configurazione\/i.test(tx) && \/Avanzamento stimato|%\/.test(tx)) { target = n; break; }\n            }\n            if (target) {\n                var btn = document.createElement('button');\n                btn.type = 'button';\n                btn.className = 'ti-btn ti-config-progress-exit-510';\n                btn.textContent = 'Esci';\n                btn.style.cssText = 'background:#dc2626;color:#fff;margin:8px 6px 0 0;position:relative;z-index:999999;';\n                btn.onclick = function(ev){ if(ev){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } return window.tiExitConfigLoading510(); };\n                try { target.appendChild(btn); } catch(e){}\n            }\n        }\n    }\n    var oldOpen = window.openConfigSafe509;\n    if (typeof oldOpen === 'function' && !oldOpen.__tiExit510Wrapped) {\n        window.openConfigSafe509 = function(){ setTimeout(addExitButtonToWait, 0); setTimeout(addExitButtonToWait, 350); setTimeout(addExitButtonToWait, 1000); return oldOpen.apply(this, arguments); };\n        window.openConfigSafe509.__tiExit510Wrapped = true;\n        ['openConfigSafe508','openConfigSafe507','openConfigSafe506','openConfigSafe505','openConfigSafe504','openConfigSafe503','openConfigSafe502','openConfigSafe501','openConfigSafe492','openConfigSafe491','openConfigSafe490','openConfigSafe489','openConfigSafe488','openConfigSafe218'].forEach(function(n){ window[n] = window.openConfigSafe509; });\n    }\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target; if(!t || !t.closest) return;\n        var b = t.closest('#ti-config-full-exit-510,.ti-config-progress-exit-510');\n        if(!b) return;\n        ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n        return window.tiExitConfigLoading510();\n    }, true);\n    if (typeof MutationObserver !== 'undefined') {\n        try { new MutationObserver(function(){ setTimeout(addExitButtonToWait, 40); }).observe(document.body || document.documentElement, {childList:true, subtree:true}); } catch(e){}\n    }\n    \/\/ 30.9.664: nessun polling persistente. Il pulsante Esci inline e gia in setWait 509.\n    if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', addExitButtonToWait); else addExitButtonToWait();\n})();\n\n<\/script>\n\n<style id=\"ti-logout-dedupe-511-css\">\n    #ti-ai-outer .ti-header-right-controls > #ti-logout-btn { display:none; }\n    #ti-ai-outer .ti-config-progress-exit-510 + .ti-config-progress-exit-510 { display:none !important; }\n<\/style>\n<script id=\"ti-logout-dedupe-511\">\n(function(){\n    'use strict';\n    if (window.__tiLogoutDedupe511Installed) return;\n    window.__tiLogoutDedupe511Installed = true;\n    function safe(fn){ try { return fn(); } catch(e) {} }\n    function byId(id){ return document.getElementById(id); }\n    function text(el){ return String((el && (el.textContent || el.value)) || '').trim(); }\n    function hasLogin(){\n        var u = String(window.tiUser || '').trim();\n        if (u) return true;\n        var nm = byId('ti-user-name');\n        var nt = nm ? text(nm) : '';\n        if (nt && !\/^(cliente\\\/?utente|cliente|utente|ospite|guest)$\/i.test(nt)) return true;\n        try { return \/(?:^|;\\s*)ti_ai_ss_session=\/.test(document.cookie || ''); } catch(e) { return false; }\n    }\n    function canonicalLogout(){\n        var header = document.querySelector('#ti-ai-outer .ti-header-right-controls') || document.querySelector('.ti-header-right-controls');\n        var existing = Array.prototype.slice.call(document.querySelectorAll('#ti-logout-btn'));\n        var keep = existing.find(function(b){ return header && b.parentNode === header; }) || existing[0] || null;\n        if (!keep && header) {\n            keep = document.createElement('button');\n            keep.type = 'button';\n            keep.id = 'ti-logout-btn';\n            keep.className = 'ti-btn';\n            keep.textContent = 'Esci';\n            header.appendChild(keep);\n        }\n        existing.forEach(function(b){ if (b !== keep) safe(function(){ b.remove(); }); });\n        if (keep) {\n            keep.id = 'ti-logout-btn';\n            keep.type = 'button';\n            keep.classList.add('ti-btn');\n            keep.textContent = (String(window.currLang || 'it').toLowerCase() === 'en') ? 'Logout' : 'Esci';\n            keep.setAttribute('data-ti-logout','1');\n            keep.onclick = function(ev){ if(ev){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } if (window.tiImmediateLogout506) return window.tiImmediateLogout506(ev); if (window.doLogout) return window.doLogout(); return false; };\n            keep.onmousedown = keep.onclick;\n            keep.ontouchend = keep.onclick;\n            if (hasLogin()) {\n                keep.style.setProperty('display','inline-flex','important');\n                keep.style.setProperty('visibility','visible','important');\n                keep.style.setProperty('pointer-events','auto','important');\n            } else {\n                keep.style.setProperty('display','none','important');\n            }\n        }\n        Array.prototype.slice.call(document.querySelectorAll('[data-ti-logout],.ti-logout-btn')).forEach(function(b){\n            if (b === keep) return;\n            if (b.id === 'ti-logout-btn') return;\n            if (\/^(esci|logout|logoff)$\/i.test(text(b))) safe(function(){ b.remove(); });\n        });\n        var progress = Array.prototype.slice.call(document.querySelectorAll('.ti-config-progress-exit-510'));\n        progress.forEach(function(b, idx){ if (idx > 0) safe(function(){ b.remove(); }); });\n        var full = Array.prototype.slice.call(document.querySelectorAll('#ti-config-full-exit-510'));\n        full.forEach(function(b, idx){ if (idx > 0) safe(function(){ b.remove(); }); });\n    }\n    function run(){ safe(canonicalLogout); }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', run, {once:true});\n    run();\n    setTimeout(run, 100); setTimeout(run, 500); setTimeout(run, 1500);\n    setInterval(run, 2000);\n    if (typeof MutationObserver !== 'undefined') safe(function(){ new MutationObserver(function(){ setTimeout(run, 25); }).observe(document.documentElement, {childList:true, subtree:true}); });\n})();\n<\/script>\n<style id=\"ti-mobile-logout-filter-table-526\">\n#ti-ai-outer #ti-new-db-ov .ti-modal { max-height: calc(var(--ti-vh, 1vh) * 96) !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; }\n#ti-ai-outer #ti-new-db-ov .ti-new-db-info-top { color:#d1fae5 !important; }\n#ti-ai-outer #ti-new-db-ov .ti-new-db-manual-bottom { min-height:36px !important; line-height:1.15 !important; }\n#ti-ai-outer .ti-cfg-table-container,\n#ti-ai-outer .ti-import-table-container,\n#ti-ai-outer .ti-sync-hscroll-wrap,\n#ti-ai-outer .ti-table-wrap,\n#ti-ai-outer .ti-prod-table-wrap,\n#ti-ai-outer .ti-order-table-wrap,\n#ti-ai-outer .ti-purchase-table-wrap { overflow-x:auto !important; overflow-y:auto !important; max-width:100% !important; -webkit-overflow-scrolling:touch !important; touch-action: pan-x pan-y !important; overscroll-behavior:auto !important; }\n#ti-ai-outer .ti-cfg-top-scroll,\n#ti-ai-outer .ti-cfg-bottom-scroll,\n#ti-ai-outer .ti-order-top-scroll,\n#ti-ai-outer .ti-order-bottom-scroll { overflow-x:auto !important; overflow-y:hidden !important; -webkit-overflow-scrolling:touch !important; touch-action: pan-x !important; }\n#ti-ai-outer .ti-cfg-table { width:max-content !important; min-width:100% !important; table-layout:auto !important; border-collapse:collapse !important; }\n#ti-ai-outer .ti-cfg-table th,\n#ti-ai-outer .ti-cfg-table td { white-space:nowrap !important; vertical-align:top !important; }\n#ti-ai-outer .ti-cfg-table textarea { white-space:pre-wrap !important; min-width:220px !important; }\n#ti-ai-outer #ti-conf-ov .ti-cfg-table input:not([type=\"checkbox\"]):not([type=\"radio\"]),\n#ti-ai-outer #ti-conf-ov .ti-cfg-table textarea,\n#ti-ai-outer #ti-conf-ov .ti-cfg-table select,\n#ti-ai-outer #ti-conf-ov .ti-cfg-filter { pointer-events:auto !important; user-select:text !important; -webkit-user-select:text !important; opacity:1 !important; caret-color:#fff !important; color:#fff !important; -webkit-text-fill-color:#fff !important; }\n#ti-ai-outer .ti-cfg-filter-wrap-526 { display:flex !important; align-items:center !important; gap:4px !important; width:100% !important; min-width:90px !important; }\n#ti-ai-outer .ti-cfg-filter-wrap-526 .ti-cfg-filter { flex:1 1 auto !important; min-width:58px !important; }\n#ti-ai-outer .ti-cfg-filter-lens-526 { flex:0 0 24px !important; width:24px !important; min-width:24px !important; height:24px !important; min-height:24px !important; padding:0 !important; border:1px solid #64748b !important; border-radius:7px !important; background:#1d4ed8 !important; color:#fff !important; cursor:pointer !important; display:inline-flex !important; align-items:center !important; justify-content:center !important; line-height:1 !important; font-size:13px !important; }\n#ti-ai-outer .ti-cfg-filter-lens-526.is-active { background:#f59e0b !important; color:#111827 !important; border-color:#fbbf24 !important; }\n#ti-ai-outer .ti-cfg-filter.has-manual-filter-526 { border-color:#f59e0b !important; box-shadow:0 0 0 1px rgba(245,158,11,.45) !important; }\n@media (max-width: 680px) {\n  #ti-ai-outer { max-width:100vw !important; padding:6px !important; overflow-x:hidden !important; --ti-font-base:12px; --ti-font-small:11px; --ti-font-xs:10px; }\n  #ti-ai-outer .ti-modal { width:96vw !important; max-width:96vw !important; padding:12px !important; }\n  #ti-ai-outer #ti-new-db-ov .ti-modal { width:96vw !important; max-width:96vw !important; }\n  #ti-ai-outer input, #ti-ai-outer textarea, #ti-ai-outer select { font-size:16px !important; }\n  #ti-ai-outer .ti-cfg-table input, #ti-ai-outer .ti-cfg-table textarea, #ti-ai-outer .ti-cfg-table select, #ti-ai-outer .ti-cfg-filter { font-size:12px !important; }\n  #ti-ai-outer .ti-conf-body { overflow:auto !important; -webkit-overflow-scrolling:touch !important; touch-action:pan-x pan-y !important; }\n  #ti-ai-outer .ti-header { padding:8px !important; gap:6px !important; }\n  #ti-ai-outer .ti-header-bottom, #ti-ai-outer .ti-header-left-controls, #ti-ai-outer .ti-header-right-controls { gap:4px !important; }\n}\n@media (max-width: 980px) and (orientation: landscape) {\n  #ti-ai-outer { width:100vw !important; max-width:100vw !important; padding:5px !important; --ti-font-base:11px; --ti-font-small:10px; --ti-font-xs:9px; --ti-btn-h:26px; --ti-input-h:28px; }\n  #ti-ai-outer .ti-header { min-height:0 !important; padding:6px !important; margin-bottom:5px !important; gap:4px !important; }\n  #ti-ai-outer #ti-company-name { font-size:1.35em !important; }\n  #ti-ai-outer #ti-company-sub { font-size:10px !important; }\n  #ti-ai-outer .ti-actions-row, #ti-ai-outer #ti-admin-actions-row { gap:3px !important; overflow-x:auto !important; }\n  #ti-ai-outer .ti-actions-row .ti-btn, #ti-ai-outer #ti-admin-actions-row .ti-btn { font-size:8.8px !important; min-height:24px !important; height:24px !important; padding:2px 3px !important; }\n  #ti-ai-outer #ti-conf-ov .ti-conf-window, #ti-ai-outer #ti-conf-ov .ti-modal { width:98vw !important; max-width:98vw !important; height:94vh !important; max-height:94vh !important; }\n  #ti-ai-outer .ti-conf-body { top:48px !important; bottom:52px !important; overflow:auto !important; touch-action:pan-x pan-y !important; }\n  #ti-ai-outer .ti-cfg-table th, #ti-ai-outer .ti-cfg-table td { font-size:10px !important; padding:3px !important; }\n  #ti-ai-outer .ti-cfg-table input, #ti-ai-outer .ti-cfg-table textarea, #ti-ai-outer .ti-cfg-table select, #ti-ai-outer .ti-cfg-filter { font-size:10px !important; min-height:24px !important; padding:2px 4px !important; }\n}\n<\/style>\n\n<script id=\"ti-mobile-logout-filter-table-526\">\n(function(){\n    'use strict';\n    if (window.__tiMobileLogoutFilterTable526) return;\n    window.__tiMobileLogoutFilterTable526 = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function trim(v){ return txt(v).trim(); }\n    function norm(v){ try { return trim(v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').replace(\/[^a-z0-9]+\/g,''); } catch(e) { return trim(v).toLowerCase().replace(\/[^a-z0-9]+\/g,''); } }\n    function cssEsc(v){ try { return window.CSS && CSS.escape ? CSS.escape(String(v)) : String(v).replace(\/[^a-zA-Z0-9_-]\/g, function(ch){ return '\\\\' + ch; }); } catch(e){ return String(v).replace(\/[^a-zA-Z0-9_-]\/g,'_'); } }\n    function safe(fn){ try { return fn(); } catch(e) { return null; } }\n\n    function setupNewCompanyForm(){\n        var ov = byId('ti-new-db-ov'); if (!ov) return;\n        var modal = ov.querySelector('.ti-modal') || ov;\n        var h3 = modal.querySelector('h3');\n        if (h3 && !modal.querySelector('.ti-new-db-info-top')) {\n            var info = document.createElement('div');\n            info.className = 'ti-new-db-info-top';\n            info.style.cssText = 'font-size:12px;color:#d1fae5;text-align:left;line-height:1.45;background:#052e25;border:1px solid #065f46;border-radius:10px;padding:10px;margin:0 0 12px 0;';\n            info.innerHTML = '<p style=\"margin:0 0 8px 0;\">Alla fine del processo di creazione della ditta, verr\u00e0 generato l\\'archivio e farai l\\'accesso automatico a te riservato come configuratore.<\/p><p style=\"margin:0 0 8px 0;\">Il servizio sar\u00e0 <b>GRATUITO di prova per 30 giorni senza impegno<\/b>, poi potrai eventualmente attivare la ditta alle condizioni commerciali previste per il servizio Shop &amp; Service disponibili direttamente durante la configurazione ed il periodo di prova.<\/p><p style=\"margin:0;\">Trascorso il periodo di prova di 30 giorni, potrai comunque mantenere i dati in attesa del da farsi per ulteriori 30 giorni o cancellarli definitivamente.<\/p>';\n            h3.insertAdjacentElement('afterend', info);\n        }\n        var manualLinks = Array.prototype.slice.call(modal.querySelectorAll('a[href*=\"Shop-service\"],a[href*=\"Shop-Service\"],a[href*=\"Shop%20Service\"],a'))\n            .filter(function(a){ return \/manuale operativo\/i.test(trim(a.textContent || '')); });\n        var bottom = modal.querySelector('.ti-new-db-manual-bottom');\n        if (!bottom) {\n            bottom = manualLinks[0] || document.createElement('a');\n            bottom.className = ((bottom.className || '') + ' ti-new-db-manual-bottom ti-btn').trim();\n            bottom.textContent = 'Manuale operativo Shop & Service';\n            bottom.target = '_blank'; bottom.rel = 'noopener';\n            if (!bottom.getAttribute('href')) bottom.href = 'https:\/\/www.targetinformatica.it\/wp-content\/uploads\/AI-data\/AI-docs\/Shop-service\/';\n            var cancel = Array.prototype.slice.call(modal.querySelectorAll('button')).filter(function(b){ return \/annulla\/i.test(trim(b.textContent || '')); })[0];\n            if (cancel) cancel.insertAdjacentElement('beforebegin', bottom); else modal.appendChild(bottom);\n        }\n        manualLinks.forEach(function(a){ if (a !== bottom) safe(function(){ a.parentNode.removeChild(a); }); });\n    }\n\n    function hardLogout(ev){\n        if (ev) safe(function(){ ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); });\n        if (window.__tiHardLogoutRunning526) return false;\n        window.__tiHardLogoutRunning526 = true;\n        safe(function(){ if (window.clearIdleTimers) window.clearIdleTimers(); });\n        safe(function(){ window.idleForceClosing = true; });\n        ['ti-conf-ov','ti-file-manager-ov','ti-ai-gen-ov','ti-login-ov','ti-profile-ov','ti-new-db-ov','ti-newdb-ov','ti-activity-report-ov','ti-delete-company-ov','ti-progress-ov','ti-order-confirm-ov','ti-global-action-ov'].forEach(function(id){\n            var el = byId(id); if (!el) return;\n            el.style.setProperty('display','none','important');\n            el.style.setProperty('visibility','hidden','important');\n            el.style.setProperty('pointer-events','none','important');\n        });\n        safe(function(){ document.cookie = 'ti_ai_ss_session=; Max-Age=0; path=\/; SameSite=Lax'; });\n        safe(function(){ document.cookie = 'ti_ai_ss_session=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=\/'; });\n        safe(function(){ var b=byId('ti-logout-btn'); if(b) b.style.setProperty('display','none','important'); });\n        safe(function(){ var b=byId('ti-login-btn'); if(b) b.style.setProperty('display','inline-flex','important'); });\n        var fd = new FormData();\n        fd.append('action','ti_ai_logout'); fd.append('ti_action','ti_ai_logout'); fd.append('_ti_logout_526', String(Date.now()));\n        var urls = [];\n        if (window.tiAjaxUrl) urls.push(window.tiAjaxUrl);\n        if (window.tiUrl && urls.indexOf(window.tiUrl) < 0) urls.push(window.tiUrl);\n        urls.push(window.location.href.split('#')[0]);\n        function reload(){ setTimeout(function(){ try { window.location.href = window.location.href.split('#')[0]; } catch(e) { window.location.reload(); } }, 80); }\n        var done = false;\n        function finish(){ if (done) return; done = true; reload(); }\n        setTimeout(finish, 650);\n        safe(function(){ fetch(urls[0], {method:'POST', body:fd, credentials:'same-origin', keepalive:true, cache:'no-store'}).then(finish).catch(function(){ if(urls[1]) fetch(urls[1], {method:'POST', body:fd, credentials:'same-origin', keepalive:true, cache:'no-store'}).then(finish).catch(finish); else finish(); }); });\n        return false;\n    }\n    window.tiImmediateLogout526 = hardLogout;\n    window.tiImmediateLogout506 = hardLogout;\n    window.forceSessionCloseNow = hardLogout;\n    window.doLogout = hardLogout;\n    function isLogoutTarget(t){ return !!(t && t.closest && t.closest('#ti-logout-btn,[data-ti-logout],.ti-logout-btn')); }\n    ['pointerdown','click','touchend','keydown'].forEach(function(evt){\n        window.addEventListener(evt, function(ev){\n            if (evt === 'keydown') { var k = ev.key || ev.code || ''; if (k !== 'Enter' && k !== ' ' && k !== 'Spacebar') return; }\n            if (isLogoutTarget(ev.target)) return hardLogout(ev);\n        }, true);\n    });\n\n    function isProtected(el){\n        if (!el) return false;\n        var user = norm(window.tiUser || '');\n        if (user === 'ssglobaladmin') return false;\n        var tbl = norm(el.getAttribute('data-tbl') || '');\n        var key = norm(el.getAttribute('data-key') || '');\n        return tbl === 'config' && ['stato','datacreazione','dataattivazione','wcorderid','wcactivateid','activeid'].indexOf(key) >= 0;\n    }\n    function unlockInput(el){\n        if (!el || isProtected(el)) return;\n        safe(function(){ el.removeAttribute('readonly'); el.removeAttribute('disabled'); el.readOnly = false; el.disabled = false; });\n        safe(function(){ el.style.setProperty('pointer-events','auto','important'); el.style.setProperty('user-select','text','important'); el.style.setProperty('-webkit-user-select','text','important'); el.style.setProperty('opacity','1','important'); });\n    }\n    function syncField(el){\n        if (!el || !el.matches || !el.matches('#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]')) return;\n        if (isProtected(el)) return;\n        var tbl = el.getAttribute('data-tbl') || '', key = el.getAttribute('data-key') || '', idx = parseInt(el.getAttribute('data-idx') || '', 10);\n        if (!tbl || !key || isNaN(idx)) return;\n        safe(function(){\n            if (window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[tbl] && window.currentDbData.Tabelle[tbl][idx]) {\n                window.currentDbData.Tabelle[tbl][idx][key] = ('value' in el) ? el.value : el.textContent;\n                window.__configRelationSupportCache = null;\n                if (!window.modifiedFields) window.modifiedFields = new Set();\n                window.modifiedFields.add(tbl + '-' + idx + '-' + key);\n                if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n            }\n        });\n    }\n    function unlockAll(root){\n        var scope = root || byId('ti-conf-ov') || document;\n        safe(function(){ scope.querySelectorAll('#ti-conf-ov .ti-cfg-table input,#ti-conf-ov .ti-cfg-table textarea,#ti-conf-ov .ti-cfg-table select,#ti-conf-ov .ti-cfg-filter').forEach(unlockInput); });\n    }\n    document.addEventListener('focusin', function(ev){ unlockInput(ev.target); }, true);\n    document.addEventListener('input', function(ev){ unlockInput(ev.target); syncField(ev.target); }, true);\n    document.addEventListener('change', function(ev){ unlockInput(ev.target); syncField(ev.target); }, true);\n\n    var filterState = window.__tiConfigManualFilters526 = window.__tiConfigManualFilters526 || {};\n    function tableNameFrom(el){ var d = el && el.closest ? el.closest('details.ti-cfg-details') : null; return d ? trim(d.getAttribute('data-tbl') || '') : ''; }\n    function getDetails(tbl, el){\n        var t = trim(tbl) || tableNameFrom(el);\n        if (el && el.closest) { var d = el.closest('details.ti-cfg-details'); if (d) return d; }\n        try { return document.querySelector('#ti-conf-ov details.ti-cfg-details[data-tbl=\"' + cssEsc(t) + '\"]'); } catch(e) { return null; }\n    }\n    function ensureFilterUi(root){\n        var scope = root || byId('ti-conf-ov') || document;\n        safe(function(){ scope.querySelectorAll('#ti-conf-ov .ti-cfg-filter[data-key]').forEach(function(inp){\n            unlockInput(inp);\n            var tbl = trim(inp.getAttribute('data-tbl') || '') || tableNameFrom(inp);\n            if (tbl) inp.setAttribute('data-tbl', tbl);\n            var wrap = inp.parentElement;\n            if (wrap) wrap.classList.add('ti-cfg-filter-wrap-526');\n            var btn = wrap ? wrap.querySelector('.ti-cfg-filter-lens-526') : null;\n            if (!btn) {\n                btn = document.createElement('button');\n                btn.type = 'button';\n                btn.textContent = '\ud83d\udd0d';\n                btn.className = 'ti-cfg-filter-lens-526 ti-no-i18n';\n                inp.insertAdjacentElement('afterend', btn);\n            }\n            btn.setAttribute('data-tbl', tbl);\n            btn.setAttribute('data-key', inp.getAttribute('data-key') || '');\n            refreshFilterControl(inp);\n        }); });\n    }\n    function refreshFilterControl(inp){\n        if (!inp) return;\n        var tbl = trim(inp.getAttribute('data-tbl') || '') || tableNameFrom(inp), key = trim(inp.getAttribute('data-key') || '');\n        var active = !!(filterState[tbl] && trim(filterState[tbl][key] || ''));\n        inp.classList.toggle('has-manual-filter-526', active);\n        var wrap = inp.parentElement;\n        var btn = wrap ? wrap.querySelector('.ti-cfg-filter-lens-526') : null;\n        if (btn) { btn.classList.toggle('is-active', active); btn.title = active ? 'Filtro attivo: clicca per rimuoverlo' : 'Applica filtro righe'; }\n    }\n    function cellTextFor(row, cellIndex){\n        var cell = (cellIndex >= 0 && row.cells && row.cells[cellIndex]) ? row.cells[cellIndex] : row;\n        try {\n            if (window.tiConfigCellText700) return trim(window.tiConfigCellText700(cell));\n            if (cell && cell.querySelectorAll) {\n                var parts = [];\n                cell.querySelectorAll('input,textarea,select').forEach(function(el){\n                    if (el.tagName && String(el.tagName).toLowerCase() === 'select') parts.push(Array.prototype.slice.call(el.selectedOptions || []).map(function(o){ return o.textContent || o.value || ''; }).join(' '));\n                    else if ('value' in el) parts.push(el.value);\n                });\n                var visible = trim(cell.innerText || cell.textContent || '');\n                if (visible) parts.push(visible);\n                var joined = parts.map(function(v){ return trim(v); }).filter(Boolean).join(' ');\n                if (joined) return joined;\n            }\n        } catch(e) {}\n        return trim(cell && (cell.innerText || cell.textContent) || '');\n    }\n    function matchesCell(textValue, filterValue){\n        try { if (window.configFilterMatches) return window.configFilterMatches(textValue, filterValue); } catch(e) {}\n        var hay = norm(textValue), needle = norm(filterValue);\n        if (!needle) return true;\n        return hay.indexOf(needle) >= 0;\n    }\n    function applyFilter(tbl, el){\n        var det = getDetails(tbl, el); if (!det) return false;\n        var t = trim(tbl) || trim(det.getAttribute('data-tbl') || '');\n        var active = filterState[t] || {};\n        var keys = Object.keys(active).filter(function(k){ return trim(active[k]); });\n        var colIndex = {};\n        keys.forEach(function(k){\n            var inp = det.querySelector('.ti-cfg-filter[data-key=\"' + cssEsc(k) + '\"]');\n            var th = inp ? inp.closest('th') : null;\n            colIndex[k] = th && typeof th.cellIndex === 'number' ? th.cellIndex : -1;\n        });\n        var visible = 0, total = 0;\n        Array.prototype.slice.call(det.querySelectorAll('tbody tr')).forEach(function(tr){\n            if (!tr.cells || !tr.cells.length) return;\n            total++;\n            var ok = true;\n            for (var i=0;i<keys.length;i++) {\n                var k = keys[i];\n                if (!matchesCell(cellTextFor(tr, colIndex[k]), active[k])) { ok = false; break; }\n            }\n            tr.style.display = ok ? '' : 'none';\n            if (ok) visible++;\n        });\n        det.setAttribute('data-ti-filter-visible-526', String(visible));\n        det.setAttribute('data-ti-filter-total-526', String(total));\n        det.querySelectorAll('.ti-cfg-filter[data-key]').forEach(refreshFilterControl);\n        syncHScroll(det);\n        return false;\n    }\n    function toggleFilter(inp, ev){\n        if (ev) safe(function(){ ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); });\n        if (!inp) return false;\n        unlockInput(inp);\n        var tbl = trim(inp.getAttribute('data-tbl') || '') || tableNameFrom(inp), key = trim(inp.getAttribute('data-key') || '');\n        if (!tbl || !key) return false;\n        filterState[tbl] = filterState[tbl] || {};\n        if (trim(filterState[tbl][key] || '')) { filterState[tbl][key] = ''; inp.value = ''; }\n        else { filterState[tbl][key] = trim(inp.value || ''); }\n        applyFilter(tbl, inp);\n        return false;\n    }\n    window.toggleConfigTableFilterManual526 = function(tbl,key,inputEl,buttonEl,ev){ return toggleFilter(inputEl || (buttonEl && buttonEl.parentNode ? buttonEl.parentNode.querySelector('.ti-cfg-filter') : null), ev); };\n    window.toggleConfigTableFilterManual525 = window.toggleConfigTableFilterManual526;\n    window.toggleConfigTableFilterManual524 = window.toggleConfigTableFilterManual526;\n    window.toggleConfigTableFilterManual522 = window.toggleConfigTableFilterManual526;\n    window.toggleConfigTableFilterManual521 = window.toggleConfigTableFilterManual526;\n    window.setConfigTableFilterDraftManual526 = function(tbl,key,value,inputEl){ if (inputEl) unlockInput(inputEl); return false; };\n    window.setConfigTableFilterDraftManual525 = window.setConfigTableFilterDraftManual526;\n    window.setConfigTableFilterDraftManual524 = window.setConfigTableFilterDraftManual526;\n    window.setConfigTableFilterDraftManual521 = window.setConfigTableFilterDraftManual526;\n    window.scheduleConfigTableFilter = function(){ return false; };\n    window.filterConfigTable = function(){ return false; };\n    window.applyConfigTableDomFilter526 = applyFilter;\n\n    window.addEventListener('click', function(ev){\n        var btn = ev.target && ev.target.closest ? ev.target.closest('.ti-cfg-filter-lens-526,.ti-cfg-filter-lens-525,.ti-cfg-filter-lens-524,.ti-cfg-filter-lens-522,.ti-cfg-filter-lens-521') : null;\n        if (!btn) return;\n        var inp = btn.previousElementSibling && btn.previousElementSibling.classList && btn.previousElementSibling.classList.contains('ti-cfg-filter') ? btn.previousElementSibling : (btn.parentNode ? btn.parentNode.querySelector('.ti-cfg-filter') : null);\n        return toggleFilter(inp, ev);\n    }, true);\n    window.addEventListener('keydown', function(ev){\n        var el = ev.target;\n        if (!el || !el.classList || !el.classList.contains('ti-cfg-filter') || ev.key !== 'Enter') return;\n        return toggleFilter(el, ev);\n    }, true);\n    window.addEventListener('input', function(ev){\n        var el = ev.target;\n        if (!el || !el.classList || !el.classList.contains('ti-cfg-filter')) return;\n        unlockInput(el);\n        return false;\n    }, true);\n\n    function syncHScroll(root){\n        var scope = root || byId('ti-conf-ov') || document;\n        safe(function(){ scope.querySelectorAll('.ti-sync-hscroll-wrap,.ti-cfg-table-container,.ti-table-wrap').forEach(function(wrap){\n            var table = wrap.querySelector('table');\n            if (!table) return;\n            wrap.style.setProperty('overflow-x','auto','important');\n            wrap.style.setProperty('-webkit-overflow-scrolling','touch','important');\n            wrap.style.setProperty('touch-action','pan-x pan-y','important');\n            var details = wrap.closest('details,.ti-modal,.ti-conf-window') || wrap.parentElement;\n            var width = Math.max(table.scrollWidth || 0, wrap.scrollWidth || 0, wrap.clientWidth || 0);\n            if (details) Array.prototype.slice.call(details.querySelectorAll('.ti-sync-hscroll-inner,.ti-cfg-top-scroll-inner')).forEach(function(inner){ inner.style.width = width + 'px'; });\n        }); });\n    }\n    function bindScrollSync(){\n        safe(function(){ document.querySelectorAll('#ti-conf-ov details.ti-cfg-details').forEach(function(det){\n            if (det.__tiScrollSync526) return; det.__tiScrollSync526 = true;\n            var wrap = det.querySelector('.ti-sync-hscroll-wrap,.ti-cfg-table-container');\n            var bars = Array.prototype.slice.call(det.querySelectorAll('.ti-sync-hscroll-top,.ti-sync-hscroll-bottom,.ti-cfg-top-scroll,.ti-cfg-bottom-scroll'));\n            function setLeft(src){ return function(){ var left = src.scrollLeft || 0; if (wrap && wrap !== src) wrap.scrollLeft = left; bars.forEach(function(b){ if (b !== src) b.scrollLeft = left; }); }; }\n            if (wrap) wrap.addEventListener('scroll', setLeft(wrap), {passive:true});\n            bars.forEach(function(b){ b.addEventListener('scroll', setLeft(b), {passive:true}); });\n        }); });\n        syncHScroll(byId('ti-conf-ov') || document);\n    }\n\n    function refreshAll(){ setupNewCompanyForm(); unlockAll(document); ensureFilterUi(document); bindScrollSync(); }\n    var oldRender = window.renderConfig;\n    if (typeof oldRender === 'function' && !oldRender.__tiFinal526) {\n        window.renderConfig = function(){ var r = oldRender.apply(this, arguments); setTimeout(refreshAll, 0); setTimeout(refreshAll, 150); return r; };\n        window.renderConfig.__tiFinal526 = true;\n    }\n    if (typeof MutationObserver !== 'undefined') safe(function(){ new MutationObserver(function(){ setTimeout(refreshAll, 40); }).observe(byId('ti-ai-outer') || document.body, {childList:true, subtree:true}); });\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', refreshAll, {once:true}); else refreshAll();\n    setTimeout(refreshAll, 300); setTimeout(refreshAll, 1000); setTimeout(refreshAll, 2200);\n})();\n<\/script>\n\n\n<script id=\"ti-newdb-company-switch-filemanager-guard-528\">\n(function(){\n    'use strict';\n    if (window.__tiNewDbCompanySwitchFileManagerGuard528) return;\n    window.__tiNewDbCompanySwitchFileManagerGuard528 = true;\n    function byId(id){ return document.getElementById(id); }\n    function safe(fn){ try { return fn(); } catch(e) { return null; } }\n    function setImp(el, prop, val){ safe(function(){ if (el && el.style) el.style.setProperty(prop, String(val), 'important'); }); }\n    function isFileManagerId(id){ return String(id || '') === 'ti-file-manager-ov'; }\n    function suppressFileManager(reason, ms){\n        var until = Date.now() + (ms || 7000);\n        window.tiSuppressFileManagerUntil = Math.max(window.tiSuppressFileManagerUntil || 0, until);\n        window.__tiFileManagerSuppressedReason528 = reason || 'context_change';\n        window.__tiAIGenReturnToFileManager455 = false;\n        safe(function(){ window.targetUploadContext = null; });\n        safe(function(){ window.tiLastUploadContext356 = null; });\n        safe(function(){ window.tiLastUploadContext471 = null; });\n        safe(function(){ sessionStorage.removeItem('tiLastUploadContext356'); });\n        hideFileManager();\n    }\n    function hideFileManager(){\n        var fm = byId('ti-file-manager-ov');\n        if (!fm) return;\n        setImp(fm, 'display', 'none');\n        setImp(fm, 'visibility', 'hidden');\n        setImp(fm, 'opacity', '0');\n        setImp(fm, 'pointer-events', 'none');\n        safe(function(){ fm.setAttribute('aria-hidden','true'); });\n        safe(function(){ fm.removeAttribute('data-ti-demoted-by-aigen455'); });\n        safe(function(){ fm.removeAttribute('data-ti-force-front'); });\n        safe(function(){ fm.removeAttribute('data-ti-config-child-modal'); });\n        safe(function(){ fm.dataset.tbl = ''; fm.dataset.idx = ''; fm.dataset.key = ''; });\n        safe(function(){ if (window.updatePluginScrollLock) window.updatePluginScrollLock(); });\n    }\n    function isSuppressed(){ return Date.now() < (window.tiSuppressFileManagerUntil || 0); }\n    function shouldBlockFileManagerOpen(){\n        if (isSuppressed()) return true;\n        var d = byId('ti-ditta');\n        var v = d ? String(d.value || '') : '';\n        if (!v || v === 'NEW_DB') return true;\n        return false;\n    }\n\n    var oldClose = window.closeModal;\n    if (typeof oldClose === 'function' && !oldClose.__tiNewDbGuard528) {\n        window.closeModal = function(id){\n            var sid = (typeof id === 'string') ? id : (id && id.id ? id.id : '');\n            if (sid === 'ti-new-db-ov' || sid === 'ti-newdb-ov') suppressFileManager('new_company_form_closed', 9000);\n            if (sid === 'ti-file-manager-ov') {\n                window.__tiAIGenReturnToFileManager455 = false;\n                safe(function(){ window.targetUploadContext = null; });\n                safe(function(){ window.tiLastUploadContext356 = null; sessionStorage.removeItem('tiLastUploadContext356'); });\n            }\n            var ret = oldClose.apply(this, arguments);\n            if (sid === 'ti-new-db-ov' || sid === 'ti-newdb-ov') setTimeout(hideFileManager, 0);\n            return ret;\n        };\n        window.closeModal.__tiNewDbGuard528 = true;\n    }\n\n    var oldOpen = window.openModal;\n    if (typeof oldOpen === 'function' && !oldOpen.__tiNewDbGuard528) {\n        window.openModal = function(id){\n            var sid = (typeof id === 'string') ? id : (id && id.id ? id.id : '');\n            if (isFileManagerId(sid) && shouldBlockFileManagerOpen()) { hideFileManager(); return false; }\n            return oldOpen.apply(this, arguments);\n        };\n        window.openModal.__tiNewDbGuard528 = true;\n    }\n\n    var oldShow = window.showPluginModal;\n    if (typeof oldShow === 'function' && !oldShow.__tiNewDbGuard528) {\n        window.showPluginModal = function(id){\n            var sid = (typeof id === 'string') ? id : (id && id.id ? id.id : '');\n            if (isFileManagerId(sid) && shouldBlockFileManagerOpen()) { hideFileManager(); return false; }\n            return oldShow.apply(this, arguments);\n        };\n        window.showPluginModal.__tiNewDbGuard528 = true;\n    }\n\n    var oldOpenFm = window.openFileManager;\n    if (typeof oldOpenFm === 'function' && !oldOpenFm.__tiNewDbGuard528) {\n        window.openFileManager = function(tbl, idx, key){\n            if (shouldBlockFileManagerOpen()) { hideFileManager(); return false; }\n            return oldOpenFm.apply(this, arguments);\n        };\n        window.openFileManager.__tiNewDbGuard528 = true;\n    }\n\n    document.addEventListener('change', function(ev){\n        var t = ev && ev.target;\n        if (!t || t.id !== 'ti-ditta') return;\n        suppressFileManager('company_select_changed', 9000);\n    }, true);\n\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        if (t.closest('#ti-new-db-ov .btn-close,#ti-new-db-ov button[onclick*=\"closeModal\"],#ti-newdb-ov .btn-close,#ti-newdb-ov button[onclick*=\"closeModal\"]')) {\n            suppressFileManager('new_company_cancel_clicked', 9000);\n            \/* 30.9.664: non fermare qui il click: Chiudi\/Annulla devono ricevere l'evento e chiudere la form. *\/\n        }\n        if (t.closest('#ti-ditta-popup .ti-ditta-row')) {\n            suppressFileManager('company_popup_row_clicked', 9000);\n        }\n    }, true);\n\n    setInterval(function(){ if (isSuppressed()) hideFileManager(); }, 250);\n})();\n<\/script>\n\n\n<script id=\"ti-new-db-close-fix-538\">\n(function(){\n    \"use strict\";\n    if (window.__tiNewDbCloseFix538Installed) return;\n    window.__tiNewDbCloseFix538Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function safe(fn){ try { return fn(); } catch(e) { return null; } }\n    function hide(el){\n        if (!el) return false;\n        el.classList.remove('ti-modal-open','ti-open','is-open','active');\n        el.style.setProperty('display','none','important');\n        el.style.removeProperty('visibility');\n        el.style.removeProperty('opacity');\n        el.style.removeProperty('pointer-events');\n        safe(function(){ el.setAttribute('aria-hidden','true'); });\n        return true;\n    }\n    function clearCtx(){\n        window.__tiAIGenReturnToFileManager455 = false;\n        window.__tiFileManagerSuppressedReason538 = 'new_company_form_closed';\n        window.tiSuppressFileManagerUntil = Math.max(window.tiSuppressFileManagerUntil || 0, Date.now() + 9000);\n        safe(function(){ window.targetUploadContext = null; });\n        safe(function(){ window.tiLastUploadContext356 = null; });\n        safe(function(){ window.tiLastUploadContext471 = null; });\n        safe(function(){ sessionStorage.removeItem('tiLastUploadContext356'); });\n        safe(function(){ sessionStorage.removeItem('tiNewDbOpen'); });\n        safe(function(){ sessionStorage.removeItem('tiPendingNewDb'); });\n        var fm = byId('ti-file-manager-ov');\n        if (fm) {\n            fm.style.setProperty('display','none','important');\n            fm.style.setProperty('visibility','hidden','important');\n            fm.style.setProperty('opacity','0','important');\n            fm.style.setProperty('pointer-events','none','important');\n            safe(function(){ fm.setAttribute('aria-hidden','true'); });\n            safe(function(){ fm.dataset.tbl = ''; fm.dataset.idx = ''; fm.dataset.key = ''; });\n        }\n    }\n    window.closeNewDbModal538 = function(ev){\n        if (ev) { safe(function(){ ev.preventDefault(); }); safe(function(){ ev.stopPropagation(); }); safe(function(){ if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); }); }\n        clearCtx();\n        hide(byId('ti-new-db-ov') || byId('ti-newdb-ov'));\n        var sel = byId('ti-ditta');\n        if (sel && String(sel.value || '') === 'NEW_DB') { sel.value = ''; safe(function(){ sel.selectedIndex = 0; }); }\n        safe(function(){ if (document.activeElement && document.activeElement.blur) document.activeElement.blur(); });\n        safe(function(){ if (window.updatePluginScrollLock) window.updatePluginScrollLock(); });\n        safe(function(){ if (window.tiMobileUnlockScroll) window.tiMobileUnlockScroll(); });\n        return false;\n    };\n    var prevClose = window.closeModal;\n    if (typeof prevClose === 'function' && !prevClose.__tiNewDb538) {\n        window.closeModal = function(id){\n            var sid = (typeof id === 'string') ? id : (id && id.id ? id.id : '');\n            if (sid === 'ti-new-db-ov' || sid === 'ti-newdb-ov') return window.closeNewDbModal538();\n            return prevClose.apply(this, arguments);\n        };\n        window.closeModal.__tiNewDb538 = true;\n    }\n    var prevPluginClose = window.closePluginFormModal;\n    if (typeof prevPluginClose === 'function' && !prevPluginClose.__tiNewDb538) {\n        window.closePluginFormModal = function(id){\n            var sid = (typeof id === 'string') ? id : (id && id.id ? id.id : '');\n            if (sid === 'ti-new-db-ov' || sid === 'ti-newdb-ov') return window.closeNewDbModal538();\n            return prevPluginClose.apply(this, arguments);\n        };\n        window.closePluginFormModal.__tiNewDb538 = true;\n    }\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var ov = t.closest('#ti-new-db-ov,#ti-newdb-ov');\n        if (!ov) return;\n        var btn = t.closest('button,a');\n        if (!btn) return;\n        var txt = String(btn.textContent || btn.value || '').trim().toLowerCase();\n        if (btn.classList.contains('btn-close') || btn.classList.contains('ti-modal-x-red') || \/^(x|\u00d7|chiudi|close|annulla|cancel)$\/i.test(txt) || \/\\b(chiudi|annulla|cancel)\\b\/i.test(txt)) {\n            return window.closeNewDbModal538(ev);\n        }\n    }, true);\n    document.addEventListener('keydown', function(ev){\n        if (!ev || ev.key !== 'Escape') return;\n        var ov = byId('ti-new-db-ov') || byId('ti-newdb-ov');\n        if (!ov) return;\n        var cs = safe(function(){ return window.getComputedStyle(ov); });\n        if (cs && cs.display !== 'none' && cs.visibility !== 'hidden') return window.closeNewDbModal538(ev);\n    }, true);\n})();\n<\/script>\n<!-- 30.9.664: rimossi residui CSS\/commenti upload PC 558-563; operativo solo flusso backup 471. -->\n\n<script id=\"ti-global-reset-contacts-565\">\n(function(){\n    \"use strict\";\n    if (window.__tiGlobalResetContacts565Installed) return;\n    window.__tiGlobalResetContacts565Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function esc(s){\n        return String(s == null ? '' : s).replace(\/[&<>'\"]\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;',\"'\":'&#39;','\"':'&quot;'}[c]; });\n    }\n    function norm(s){\n        try { return String(s || '').toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').replace(\/[^a-z0-9]+\/g,' ').trim(); }\n        catch(e){ return String(s || '').toLowerCase().replace(\/[^a-z0-9]+\/g,' ').trim(); }\n    }\n    function isResetContactsPrompt(prompt){\n        var n = norm(prompt);\n        if (!n) return false;\n        var hasReset = \/\\b(azzera|azzerare|reset|reimposta|imposta|porta|metti|svuota|cancella)\\b\/.test(n);\n        var hasContacts = \/\\b(totale contatti|contatti totali|contacts num|contact num|total contacts|contacts number|contact number)\\b\/.test(n);\n        return hasReset && hasContacts;\n    }\n    function setResult(html, color){\n        var box = byId('global-action-res');\n        if (!box) return;\n        box.style.display = 'block';\n        if (color) box.style.color = color;\n        box.innerHTML = html;\n    }\n    function resetBtn(){\n        var btn = byId('global-action-btn');\n        if (btn) { btn.disabled = false; btn.innerText = 'Esegui Modifica su Tutti i DB'; btn.style.pointerEvents = 'auto'; btn.style.opacity = '1'; }\n    }\n    function finish(success, msg){\n        try { if (window.finishGlobalActionProgress) window.finishGlobalActionProgress(!!success, msg || (success ? 'Operazione completata' : 'Operazione interrotta')); } catch(e) {}\n        resetBtn();\n        try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n    }\n\n    function safeUnlockResetContacts(reason){\n        try { if (window.finishGlobalActionProgress) window.finishGlobalActionProgress(false, reason || 'Operazione conclusa o interrotta'); } catch(e) {}\n        try { if (window.hideProgressPopup) window.hideProgressPopup(false, reason || 'Operazione conclusa o interrotta'); } catch(e) {}\n        try { var p=byId('ti-progress-ov'); if(p){ p.style.display='none'; p.setAttribute('aria-hidden','true'); p.classList.remove('active','show','ti-ov-active'); } } catch(e) {}\n        try { if (window.clearLongAIProcess) window.clearLongAIProcess(); } catch(e) {}\n        resetBtn();\n        try { document.body.classList.remove('ti-no-scroll'); document.documentElement.classList.remove('ti-no-scroll'); document.body.style.pointerEvents=''; } catch(e) {}\n    }\n    function postDirect(prompt){\n        var btn = byId('global-action-btn');\n        if (btn) { btn.disabled = true; btn.innerText = '\u23f3 Azzero totale contatti...'; }\n        try { if (window.startGlobalActionProgress) window.startGlobalActionProgress('Azzeramento totale contatti in corso...'); } catch(e) {}\n        try { if (window.setGlobalActionProgress) window.setGlobalActionProgress(12, 'Azzeramento Contacts_num\/Contact_num nei database ditte...'); } catch(e) {}\n        setResult('Azzero il totale contatti nei database ditte. Il template resta escluso. Attendere la risposta finale.', '#cbd5e1');\n        var __tiResetDone566 = false;\n        var __tiResetWatch566 = setTimeout(function(){\n            if (__tiResetDone566) return;\n            __tiResetDone566 = true;\n            safeUnlockResetContacts('Timeout server o risposta non ricevuta. Se il server ha comunque applicato la modifica, il layout resta sbloccato. Verifica il report o ricarica solo se necessario.');\n            setResult('<span style=\"color:#f59e0b;\">Timeout server o risposta non ricevuta. Il layout e stato sbloccato. La modifica potrebbe essere stata applicata: verifica il valore totale contatti o ripeti solo se necessario.<\/span>', '#f59e0b');\n        }, 45000);\n        var fd = new FormData();\n        fd.append('action', 'ti_ai_global_reset_contacts_total');\n        fd.append('ti_action', 'ti_ai_global_reset_contacts_total');\n        fd.append('prompt', prompt || 'azzera totale contatti');\n        fd.append('global_confirm', '1');\n        fd.append('reset_contacts_total', '1');\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        var url = window.tiUrl || window.ajaxurl || window.location.href;\n        fetch(url, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'}).then(function(r){\n            return r.text().then(function(raw){\n                if (!r.ok) throw new Error('HTTP ' + r.status + ': ' + String(raw || '').slice(0,300));\n                try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta server non JSON: ' + String(raw || '').slice(0,300)); }\n            });\n        }).then(function(d){\n            if (__tiResetDone566) return;\n            __tiResetDone566 = true; clearTimeout(__tiResetWatch566);\n            if (!d || !d.success) {\n                var m = d && d.data && (d.data.message || d.data.error) ? (d.data.message || d.data.error) : 'Errore azzeramento totale contatti.';\n                throw new Error(m);\n            }\n            var data = d.data || {};\n            var reply = data.reply || 'Azzeramento totale contatti completato.';\n            finish(true, 'Azzeramento totale contatti completato');\n            setResult('<pre style=\"white-space:pre-wrap;margin:0;color:#10b981;\">' + esc(reply) + '<\/pre>', '#10b981');\n        }).catch(function(err){\n            if (__tiResetDone566) return;\n            __tiResetDone566 = true; clearTimeout(__tiResetWatch566);\n            safeUnlockResetContacts('Errore o timeout azzeramento totale contatti');\n            finish(false, 'Errore azzeramento totale contatti');\n            setResult('<span style=\"color:#ef4444;\">Errore azzeramento totale contatti: ' + esc(err && err.message ? err.message : err) + '<\/span>', '#ef4444');\n            try { console.error('Shop & Service reset contacts 565 error', err); } catch(e) {}\n        });\n    }\n    window.tiAiSsGlobalResetContacts565 = postDirect;\n    var prevConfirmed = window.doGlobalActionConfirmed;\n    window.doGlobalActionConfirmed = function(prompt, previewToken, definitiveConfirm, tableScope){\n        if (!previewToken && !definitiveConfirm && !tableScope && isResetContactsPrompt(prompt)) {\n            postDirect(prompt);\n            return false;\n        }\n        if (typeof prevConfirmed === 'function') return prevConfirmed.apply(this, arguments);\n        return false;\n    };\n    window.doGlobalActionConfirmed.__tiResetContacts565 = true;\n    var prevDo = window.doGlobalAction;\n    window.doGlobalAction = function(){\n        var el = byId('global-action-prompt');\n        var prompt = el ? String(el.value || '').trim() : '';\n        if (isResetContactsPrompt(prompt)) {\n            var warning = '<b>Attenzione: azzeramento totale contatti<\/b><br><br>' +\n                'Saranno impostati a <b>0<\/b> i campi <b>Contacts_num \/ Contact_num<\/b> nella tabella <b>Config<\/b> dei database ditte.<br>' +\n                'Il template standard resta escluso e non verr\u00e0 modificata la struttura dei database.<br><br>' +\n                '<b>Operazione richiesta:<\/b><br><span style=\"color:#fbbf24;white-space:pre-wrap;display:block;margin-top:6px;\">' + esc(prompt) + '<\/span><br>' +\n                'Confermi di voler procedere?';\n            if (typeof window.tiConfirmYesNoAction === 'function') {\n                window.tiConfirmYesNoAction(warning, function(){ postDirect(prompt); }, null, 'CONFERMA AZZERAMENTO', 'ANNULLA');\n            } else if (window.confirm(String(warning).replace(\/<[^>]+>\/g, ' '))) {\n                postDirect(prompt);\n            }\n            return false;\n        }\n        if (typeof prevDo === 'function') return prevDo.apply(this, arguments);\n        return false;\n    };\n    function bindBtn(){\n        var b = byId('global-action-btn');\n        if (!b || b.__tiResetContacts565Bound) return;\n        b.__tiResetContacts565Bound = true;\n        b.addEventListener('click', function(ev){\n            var el = byId('global-action-prompt');\n            var prompt = el ? String(el.value || '').trim() : '';\n            if (!isResetContactsPrompt(prompt)) return;\n            ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n            window.doGlobalAction();\n            return false;\n        }, true);\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bindBtn, {once:true});\n    setTimeout(bindBtn, 0); setTimeout(bindBtn, 500);\n})();\n<\/script>\n<script id=\"ti-config-code-change-and-autocomplete-569\">\n(function(){\n    if (window.__tiConfigCodeChangeAutocomplete569Installed) return;\n    window.__tiConfigCodeChangeAutocomplete569Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function val(v){ return String(v == null ? '' : v); }\n    function esc(v){ return val(v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function norm(v){ try { return val(v).toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,''); } catch(e){ return val(v).toLowerCase(); } }\n    function roleCanSignal(){\n        var role = norm(window.tiRole || window.tiUserRole || window.currentUserRole || '');\n        var user = val(window.tiUser || '').toLowerCase();\n        return user === 'ssglobaladmin' || role.indexOf('amministratore') >= 0 || role.indexOf('configuratore') >= 0 || role.indexOf('administrator') >= 0 || role.indexOf('configurator') >= 0;\n    }\n    function codeChangeNeeded(reply, prompt, data){\n        var txt = norm((reply || '') + '\\n' + (prompt || '') + '\\n' + JSON.stringify(data || {}));\n        if (!txt.trim()) return false;\n        var hasCode = \/(modifica|modificare|intervento|aggiornamento|patch|fix|svilupp|programmaz|implementaz|cambiare|variare)\/.test(txt) && \/(codice|plugin|php|javascript|funzion|sorgente|release|versione)\/.test(txt);\n        var cannotApply = \/(non posso|non e possibile|non \u00e8 possibile|non riesco|serve|necessar|richiede|richiesta|da fare|manuale|tecnico)\/.test(txt);\n        return !!(hasCode && cannotApply);\n    }\n    function ensureModal(){\n        var ov = byId('ti-code-change-email-ov-569');\n        if (ov) return ov;\n        ov = document.createElement('div');\n        ov.id = 'ti-code-change-email-ov-569';\n        ov.className = 'ti-ov ti-closable-ov';\n        ov.style.cssText = 'z-index:2147483600!important;';\n        ov.innerHTML = '<div class=\"ti-modal\" style=\"width:92%;max-width:640px;text-align:left;\">'\n            + '<h3 style=\"margin-top:0;color:#fff;border-bottom:1px solid #334155;padding-bottom:8px;\">Richiesta modifica codice plugin<\/h3>'\n            + '<div style=\"color:#dbeafe;font-size:14px;line-height:1.45;\">La richiesta AI non sembra applicabile solo modificando dati o istruzioni nel database: potrebbe richiedere una modifica al codice del plugin.<\/div>'\n            + '<div style=\"margin-top:10px;color:#cbd5e1;font-size:13px;line-height:1.45;\">Vuoi inviare una email di segnalazione a <b>SSSupport@targetinformatica.it<\/b> con copia della richiesta?<\/div>'\n            + '<div id=\"ti-code-change-email-preview-569\" style=\"margin-top:10px;max-height:180px;overflow:auto;background:#020617;border:1px solid #334155;border-radius:8px;padding:9px;color:#e5e7eb;font-size:12px;white-space:pre-wrap;\"><\/div>'\n            + '<div id=\"ti-code-change-email-status-569\" style=\"display:none;margin-top:10px;padding:8px;border-radius:8px;border:1px solid #334155;background:#020617;color:#bfdbfe;font-size:12px;\"><\/div>'\n            + '<div style=\"display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap;margin-top:14px;\">'\n            + '<button type=\"button\" class=\"ti-btn\" style=\"background:#4b5563;color:#fff;\" onclick=\"window.tiCloseCodeChangeEmailPopup569()\">Annulla<\/button>'\n            + '<button type=\"button\" id=\"ti-code-change-email-send-569\" class=\"ti-btn\" style=\"background:#dc2626;color:#fff;\" onclick=\"window.tiSendCodeChangeEmail569()\">Invia segnalazione<\/button>'\n            + '<\/div>'\n            + '<div style=\"margin-top:8px;display:flex;justify-content:stretch;\">'\n            + '<button type=\"button\" id=\"ti-code-change-email-copy-569\" class=\"ti-btn\" style=\"width:100%;background:#2563eb;color:#fff;\" onclick=\"window.tiCopyCodeChangeEmailText569(this)\">Copia testo<\/button>'\n            + '<\/div><\/div>';\n        document.body.appendChild(ov);\n        return ov;\n    }\n    window.tiBuildCodeChangeEmailCopyText569 = function(){\n        var payload = window.__tiCodeChangeEmailPayload569 || {};\n        var st = byId('ti-code-change-email-status-569');\n        var status = st && st.style.display !== 'none' ? val(st.textContent || '') : '';\n        var preview = byId('ti-code-change-email-preview-569');\n        var previewText = preview ? val(preview.textContent || preview.innerText || '') : '';\n        var txt = previewText || ('Richiesta modifica codice plugin\\n\\nRichiesta:\\n' + val(payload.prompt || '') + '\\n\\nRisposta AI \/ motivo:\\n' + val(payload.reply || ''));\n        if (status) txt += '\\n\\nStato invio:\\n' + status;\n        return txt.trim();\n    };\n    window.tiCopyCodeChangeEmailText569 = function(btn){\n        var text = window.tiBuildCodeChangeEmailCopyText569 ? window.tiBuildCodeChangeEmailCopyText569() : '';\n        function done(ok){\n            try {\n                if (btn) {\n                    var old = btn.getAttribute('data-old-label') || btn.textContent || 'Copia testo';\n                    btn.setAttribute('data-old-label', old);\n                    btn.textContent = ok ? 'Testo copiato' : 'Copia manuale';\n                    setTimeout(function(){ try { btn.textContent = old || 'Copia testo'; } catch(e){} }, 1400);\n                }\n            } catch(e) {}\n        }\n        if (!text) { done(false); return false; }\n        try {\n            if (navigator.clipboard && window.isSecureContext) {\n                navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){\n                    try {\n                        var ta = document.createElement('textarea'); ta.value = text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.focus(); ta.select(); var ok = document.execCommand('copy'); document.body.removeChild(ta); done(ok);\n                    } catch(e2){ done(false); }\n                });\n                return false;\n            }\n        } catch(e) {}\n        try {\n            var ta = document.createElement('textarea'); ta.value = text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.focus(); ta.select(); var ok = document.execCommand('copy'); document.body.removeChild(ta); done(ok); return false;\n        } catch(e) { done(false); return false; }\n    };\n    window.tiCloseCodeChangeEmailPopup569 = function(){ var ov = byId('ti-code-change-email-ov-569'); if (ov) ov.style.display = 'none'; };\n    window.tiOfferCodeChangeEmail569 = function(prompt, reply){\n        if (!roleCanSignal()) return false;\n        prompt = val(prompt).trim(); reply = val(reply).trim();\n        if (!codeChangeNeeded(reply, prompt, null)) return false;\n        var key = norm(prompt + '|' + reply).slice(0, 500);\n        if (window.__tiLastCodeChangeOffer569 === key) return false;\n        window.__tiLastCodeChangeOffer569 = key;\n        window.__tiCodeChangeEmailPayload569 = {prompt:prompt, reply:reply};\n        var ov = ensureModal();\n        var preview = byId('ti-code-change-email-preview-569');\n        if (preview) preview.innerHTML = esc('Richiesta:\\n' + prompt + '\\n\\nRisposta AI \/ motivo:\\n' + reply);\n        var st = byId('ti-code-change-email-status-569'); if (st) { st.style.display='none'; st.textContent=''; }\n        ov.style.display = 'flex';\n        return true;\n    };\n    window.tiSendCodeChangeEmail569 = function(){\n        var payload = window.__tiCodeChangeEmailPayload569 || {};\n        var st = byId('ti-code-change-email-status-569');\n        var btn = byId('ti-code-change-email-send-569');\n        if (st) { st.style.display='block'; st.style.borderColor='#2563eb'; st.style.color='#bfdbfe'; st.textContent='Invio segnalazione in corso...'; }\n        if (btn) btn.disabled = true;\n        var db = (byId('ti-ditta') && byId('ti-ditta').value) || window.tiCurrentSelectedDb || window.tiForced || window.tiSessionDb || '';\n        var fd = new FormData();\n        fd.append('ti_action','ti_ai_ss_code_change_request_email');\n        fd.append('action','ti_ai_ss_code_change_request_email');\n        fd.append('db', db);\n        fd.append('prompt', val(payload.prompt || ''));\n        fd.append('reply', val(payload.reply || ''));\n        fd.append('chat_copy', 'Copia chat richiesta da parte dell\\'utente\\n\\nRichiesta:\\n' + val(payload.prompt || '') + '\\n\\nRisposta AI \/ motivo mancata applicazione:\\n' + val(payload.reply || ''));\n        fetch(window.tiUrl || window.location.href, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'}).then(function(r){ return r.text().then(function(t){ try { return JSON.parse(t); } catch(e){ throw new Error(t.slice(0,220) || 'Risposta non valida'); } }); }).then(function(res){\n            if (!res || !res.success) throw new Error(res && res.data && res.data.message ? res.data.message : 'Invio non riuscito');\n            if (st) { st.style.borderColor='#22c55e'; st.style.color='#bbf7d0'; st.textContent=((res.data && res.data.reply) ? res.data.reply : 'Segnalazione inviata.') + ' Puoi usare Copia testo per copiare richiesta, risposta e stato invio.'; }\n        }).catch(function(e){\n            if (st) { st.style.borderColor='#ef4444'; st.style.color='#fecaca'; st.textContent='Errore invio segnalazione: ' + (e && e.message ? e.message : 'errore'); }\n        }).finally(function(){ if (btn) btn.disabled = false; });\n    };\n\n    \/\/ Sincronizzazione leggera dei campi tabellari: native autocomplete\/suggerimenti salvati, select relazionali e blur.\n    var lastValue = new WeakMap();\n    function syncField(el){\n        if (!el || !el.dataset) return;\n        var tbl = el.dataset.tbl || '', idx = el.dataset.idx, key = el.dataset.key || '';\n        if (!tbl || idx === undefined || idx === null || key === '') return;\n        var v = (el.tagName === 'SELECT') ? Array.prototype.map.call(el.selectedOptions || [], function(o){return o.value;}).join(', ') : val(el.value);\n        if (lastValue.get(el) === v) return;\n        lastValue.set(el, v);\n        try {\n            if (el.classList && el.classList.contains('ti-rel-select') && typeof window.updateMemFieldFromRelationSelect === 'function') {\n                window.updateMemFieldFromRelationSelect(tbl, parseInt(idx,10), key, el);\n            } else if (typeof window.updateMemField === 'function') {\n                window.updateMemField(tbl, parseInt(idx,10), key, el);\n            }\n        } catch(e) {}\n    }\n    ['input','change','blur','compositionend'].forEach(function(evt){\n        document.addEventListener(evt, function(e){\n            var el = e.target;\n            if (!el || !el.matches) return;\n            if (!el.matches('#ti-conf-ov .ti-cfg-table input[data-tbl][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-key]')) return;\n            \/\/ Eseguito sul singolo campo: nessuna scansione tabella e nessun re-render.\n            setTimeout(function(){ syncField(el); }, evt === 'blur' ? 0 : 20);\n        }, true);\n    });\n    if (typeof window.setConfigAiReplyHtml === 'function' && !window.setConfigAiReplyHtml.__ti569Wrapped) {\n        var prevSetReply = window.setConfigAiReplyHtml;\n        window.setConfigAiReplyHtml = function(box, html, raw){\n            var ret = prevSetReply.apply(this, arguments);\n            try {\n                var isConfigReply = box && box.id === 'ti-conf-ai-reply';\n                if (isConfigReply) {\n                    var active = window.tiActiveConfigAiRequest434 || {};\n                    var prompt = active.prompt || (byId('ti-conf-ai-prompt') ? byId('ti-conf-ai-prompt').value : '');\n                    var reply = raw || (box.textContent || box.innerText || html || '');\n                    setTimeout(function(){ window.tiOfferCodeChangeEmail569(prompt, reply); }, 80);\n                }\n            } catch(e) {}\n            return ret;\n        };\n        window.setConfigAiReplyHtml.__ti569Wrapped = true;\n    }\n})();\n<\/script>\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-step-diagnostics-576; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-visible-diagnostic-578; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n<script>\n(function(){\n    \"use strict\";\n    if (window.__tiCopyTextGlobal579Installed) return;\n    window.__tiCopyTextGlobal579Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function str(v){ return String(v == null ? '' : v); }\n    function isHidden(el){\n        try {\n            if (!el) return true;\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden')) return true;\n            var r = el.getBoundingClientRect ? el.getBoundingClientRect() : null;\n            if (r && r.width === 0 && r.height === 0) return true;\n        } catch(e) {}\n        return false;\n    }\n    function plainText(root){\n        if (!root) return '';\n        try {\n            if (root.getAttribute && root.getAttribute('data-copy-text')) return str(root.getAttribute('data-copy-text')).trim();\n            if ('value' in root && !isHidden(root)) return str(root.value).trim();\n            var clone = root.cloneNode(true);\n            try {\n                clone.querySelectorAll('button,.ti-btn,script,style,noscript,input,select,textarea').forEach(function(el){ el.remove(); });\n            } catch(e) {}\n            return str(clone.innerText || clone.textContent || '')\n                .replace(\/\\r\/g, '')\n                .replace(\/[ \\t]+\\n\/g, '\\n')\n                .replace(\/\\n{3,}\/g, '\\n\\n')\n                .trim();\n        } catch(e) {\n            try { return str(root.innerText || root.textContent || '').trim(); } catch(x) {}\n        }\n        return '';\n    }\n    function resetState(){\n        try {\n            [\n                '__tiFileManagerBusy','__tiAiFileBusy','__tiAiGenBusy','__tiMediaUploadBusy','__tiUploadBusy',\n                '__tiPreventConfigClose','__tiFilePcBusy','__tiFileAttachBusy'\n            ].forEach(function(k){ try { window[k] = false; } catch(e) {} });\n            if (document.body && document.body.classList) {\n                ['ti-busy','ti-loading','ti-modal-blocked','ti-uploading','ti-file-busy','ti-no-click'].forEach(function(c){\n                    try { document.body.classList.remove(c); } catch(e) {}\n                });\n            }\n            document.querySelectorAll('.ti-ov,.ti-modal-backdrop,.ti-loader,.ti-config-loader').forEach(function(el){\n                try {\n                    var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n                    if (cs && (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0')) el.style.pointerEvents = 'none';\n                } catch(e) {}\n            });\n            document.querySelectorAll('#ti-alert-copy,#ti-admin-comm-copy,#ti-col-help-copy,.ti-conf-ai-copy-btn,[data-copy-target],[data-ti-copy-text]').forEach(function(btn){\n                try { btn.disabled = false; btn.removeAttribute('disabled'); btn.style.pointerEvents = 'auto'; } catch(e) {}\n            });\n        } catch(e) {}\n    }\n    window.tiResetFileAndCopyBlockedState579 = resetState;\n\n    function fallbackCopy(text){\n        var ta = null;\n        try {\n            ta = document.createElement('textarea');\n            ta.value = text;\n            ta.setAttribute('readonly', 'readonly');\n            ta.style.position = 'fixed';\n            ta.style.left = '-9999px';\n            ta.style.top = '0';\n            ta.style.width = '2px';\n            ta.style.height = '2px';\n            ta.style.opacity = '0';\n            ta.style.zIndex = '2147483647';\n            document.body.appendChild(ta);\n            ta.focus();\n            ta.select();\n            ta.setSelectionRange(0, ta.value.length);\n            return !!document.execCommand('copy');\n        } catch(e) {\n            return false;\n        } finally {\n            try { if (ta && ta.parentNode) ta.parentNode.removeChild(ta); } catch(e) {}\n        }\n    }\n    function setButtonResult(btn, ok){\n        try {\n            if (!btn) return;\n            var old = btn.getAttribute('data-old-label') || btn.innerText || btn.textContent || 'Copia testo';\n            if (!btn.getAttribute('data-old-label')) btn.setAttribute('data-old-label', old);\n            btn.disabled = false;\n            btn.removeAttribute('disabled');\n            btn.innerText = ok ? 'Testo copiato' : 'Copia manuale';\n            setTimeout(function(){ try { btn.innerText = old || 'Copia testo'; } catch(e) {} }, 1300);\n        } catch(e) {}\n    }\n    function safeCopy(text, btn, sourceEl, statusCb){\n        resetState();\n        text = str(text).trim();\n        if (!text && sourceEl) text = plainText(sourceEl);\n        function done(ok){\n            setButtonResult(btn, ok);\n            try { if (typeof statusCb === 'function') statusCb(!!ok); } catch(e) {}\n            return !!ok;\n        }\n        if (!text) return done(false);\n        if (fallbackCopy(text)) return done(true);\n        try {\n            if (navigator.clipboard && navigator.clipboard.writeText) {\n                navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){\n                    try { window.prompt('Copia il testo:', text); } catch(e) {}\n                    done(false);\n                });\n                return true;\n            }\n        } catch(e) {}\n        try { window.prompt('Copia il testo:', text); } catch(e) {}\n        return done(false);\n    }\n    window.tiAiSafeCopyText579 = safeCopy;\n    window.tiCopyTextReliable = function(text, sourceEl, statusCb){ return safeCopy(text, null, sourceEl, statusCb); };\n    window.tiCopyTextToClipboard = function(text, btn){ return safeCopy(text, btn, null, null); };\n\n    function columnHelpText(){\n        try { if (window.tiSanitizeColumnHelpBox579) window.tiSanitizeColumnHelpBox579(); } catch(e) {}\n        try { if (window.tiSanitizeColumnHelpText357 && byId('col-help-content')) byId('col-help-content').innerHTML = byId('col-help-content').innerHTML; } catch(e) {}\n        var parts = [];\n        ['col-help-title','col-help-company','col-help-content'].forEach(function(id){\n            var t = plainText(byId(id));\n            if (t) parts.push(t);\n        });\n        var text = parts.join('\\n\\n').trim();\n        if (!text) text = plainText(document.querySelector('#ti-col-help-ov .ti-modal'));\n        return text;\n    }\n    function alertText(){\n        var current = plainText(byId('ti-alert-msg'));\n        if (current) return current;\n        if (window.lastOrphanCleanupReportText) return str(window.lastOrphanCleanupReportText).trim();\n        try { if (window.getAlertPlainText) return str(window.getAlertPlainText()).trim(); } catch(e) {}\n        return '';\n    }\n    function pickRoot(btn){\n        if (!btn) return null;\n        if (btn.getAttribute && btn.getAttribute('data-copy-target')) {\n            try { return document.querySelector(btn.getAttribute('data-copy-target')); } catch(e) {}\n        }\n        return btn.closest ? btn.closest('.ti-modal,.ti-ov,.ti-popup,.ti-maintenance-box,.ti-conf-ai-copy-box,.ti-admin-comm-box') : null;\n    }\n    function pickText(btn){\n        if (!btn) return '';\n        try {\n            if (btn.getAttribute && btn.getAttribute('data-ti-copy-text')) return str(btn.getAttribute('data-ti-copy-text')).trim();\n            if (btn.id === 'ti-col-help-copy') return columnHelpText();\n            if (btn.id === 'ti-alert-copy') return alertText();\n            if (btn.id === 'ti-admin-comm-copy') return plainText(byId('ti-admin-comm-msg'));\n            if (btn.id === 'ti-maint-copy') return plainText(byId('ti-maint-msg')) || plainText(btn.closest('.ti-modal'));\n            if (btn.id === 'ti-maint-runtime-copy-573') return plainText(byId('ti-maint-runtime-detail-573')) || plainText(btn.closest('.ti-modal'));\n            var root = pickRoot(btn);\n            if (!root) root = byId('ti-alert-msg') || document.body;\n            var attr = root.querySelector ? root.querySelector('[data-copy-text]') : null;\n            if (attr) {\n                var at = attr.getAttribute('data-copy-text');\n                if (at) return str(at).trim();\n            }\n            var selectors = [\n                '#ti-file-pc-diag-log-578','#ti-file-pc-diag-log-576','pre:not([aria-hidden=\"true\"])',\n                '.ti-copy-text','.ti-report-text','.ti-maintenance-detail','.ti-ai-msg','.ti-alert-text','#ti-alert-msg','#ti-admin-comm-msg'\n            ];\n            for (var i=0; i<selectors.length; i++) {\n                var el = null;\n                try { el = root.querySelector ? root.querySelector(selectors[i]) : null; } catch(e) {}\n                var t = plainText(el);\n                if (t) return t;\n            }\n            return plainText(root);\n        } catch(e) { return ''; }\n    }\n    function isCopyButton(btn){\n        if (!btn) return false;\n        var id = btn.id || '';\n        if (\/^(ti-alert-copy|ti-admin-comm-copy|ti-col-help-copy|ti-maint-copy|ti-maint-runtime-copy-573)$\/.test(id)) return true;\n        if (btn.getAttribute && (btn.getAttribute('data-copy-target') || btn.getAttribute('data-ti-copy-text'))) return true;\n        if (btn.classList && btn.classList.contains('ti-conf-ai-copy-btn')) return true;\n        var label = str(btn.innerText || btn.textContent).replace(\/\\s+\/g, ' ').trim().toLowerCase();\n        return label === 'copia testo' || label === 'copy text' || label === 'copia' || label.indexOf('copia testo') >= 0 || label.indexOf('copy text') >= 0;\n    }\n\n    window.copyColumnHelpText357 = function(){ return safeCopy(columnHelpText(), byId('ti-col-help-copy'), byId('col-help-content'), null); };\n    window.copyAlertText = function(){ return safeCopy(alertText(), byId('ti-alert-copy'), byId('ti-alert-msg'), null); };\n\n    document.addEventListener('click', function(ev){\n        var btn = ev.target && ev.target.closest ? ev.target.closest('button,a,[role=\"button\"]') : null;\n        if (!isCopyButton(btn)) return;\n        resetState();\n        var text = pickText(btn);\n        if (!text) return;\n        try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {}\n        safeCopy(text, btn, pickRoot(btn), null);\n    }, true);\n})();\n<\/script>\n\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-upload-580; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-upload-581; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-upload-582; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n<script>\n(function(){\n    \"use strict\";\n    if (window.__tiInvalidPathConfigPopup583) return;\n    window.__tiInvalidPathConfigPopup583 = true;\n    function byId(id){ return document.getElementById(id); }\n    function s(v){ return v == null ? '' : String(v); }\n    function dbName(){ var el=byId('ti-ditta'); return el ? s(el.value).trim() : ''; }\n    function canLocal(){ try { return window.canRunLocalOrphanCleanup && window.canRunLocalOrphanCleanup(); } catch(e){ return false; } }\n    function canGlobal(){ try { return window.canRunGlobalOrphanCleanup && window.canRunGlobalOrphanCleanup(); } catch(e){ return false; } }\n    function esc(x){ return s(x).replace(\/[&<>\"]\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c];}); }\n    function currentConfigOpen(){ var ov=byId('ti-conf-ov'); return !!(ov && ov.style && ov.style.display !== 'none'); }\n    function scanInvalidPaths(db){\n        var fd = new FormData();\n        fd.append('ti_action','ti_ai_scan_orphan_files');\n        fd.append('db', db);\n        fd.append('invalid_only','1');\n        fd.append('preview','1');\n        return window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true}).then(function(res){\n            if (!res || !res.success) return [];\n            return (res.data && Array.isArray(res.data.files)) ? res.data.files : [];\n        }).catch(function(){ return []; });\n    }\n    window.tiCheckInvalidFilePathsOnConfig583 = function(force){\n        if (!canLocal()) return;\n        var db = dbName();\n        if (!db || db === 'Global' || db === 'NEW_DB') return;\n        var key = 'ti-invalid-file-path-popup-583-' + db;\n        try { if (!force && sessionStorage.getItem(key) === '1') return; sessionStorage.setItem(key, '1'); } catch(e) {}\n        setTimeout(function(){\n            if (!currentConfigOpen()) return;\n            scanInvalidPaths(db).then(function(files){\n                if (!files || !files.length) return;\n                var first = files[0] || {};\n                var html = '<div style=\"text-align:left;line-height:1.4;\">'\n                    + '<b>Path file da verificare<\/b><br><br>'\n                    + 'Sono stati rilevati file o cartelle in path non valide o non associabili per la ditta corrente. '\n                    + 'Esempio rilevato:<br><code style=\"font-size:11px;word-break:break-all;\">' + esc(first.relative_path || first.path || '') + '<\/code><br><br>'\n                    + 'Usa <b>Pulisci file non associati<\/b> per la ditta. La pulizia ora include anche path non valide come <code>Informazioni\/informazioni<\/code>: se un file risulta gi\u00e0 associato, viene spostato nella path ufficiale della tabella quando possibile; altrimenti viene eliminato come non associabile.';\n                html += '<div style=\"display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;\">'\n                    + '<button type=\"button\" class=\"ti-btn\" style=\"background:#7c2d12;color:#fff;\" onclick=\"if(window.runOrphanFileCleanup)window.runOrphanFileCleanup();\">Pulisci file non associati<\/button>';\n                if (canGlobal()) html += '<button type=\"button\" class=\"ti-btn\" style=\"background:#991b1b;color:#fff;\" onclick=\"if(window.runGlobalOrphanFileCleanup)window.runGlobalOrphanFileCleanup();\">Pulisci globale<\/button>';\n                html += '<\/div><\/div>';\n                if (window.tiAlert) window.tiAlert(html); else alert(html.replace(\/<[^>]+>\/g,' '));\n            });\n        }, 1200);\n    };\n    function schedule(){ try { window.tiCheckInvalidFilePathsOnConfig583(false); } catch(e){} }\n    var oldOpenModal = window.openModal;\n    if (typeof oldOpenModal === 'function' && !oldOpenModal.__tiInvalidPath583) {\n        window.openModal = function(id){ var r = oldOpenModal.apply(this, arguments); if (s(id) === 'ti-conf-ov') setTimeout(schedule, 800); return r; };\n        window.openModal.__tiInvalidPath583 = true;\n    }\n    ['openConfigSafe509','openConfigSafe508','openConfigSafe507','openConfigSafe501','openConfigSafe492','openConfigSafe218'].forEach(function(name){\n        var fn = window[name];\n        if (typeof fn === 'function' && !fn.__tiInvalidPath583) {\n            window[name] = function(){ var r = fn.apply(this, arguments); setTimeout(schedule, 1800); return r; };\n            window[name].__tiInvalidPath583 = true;\n        }\n    });\n    var oldRender = window.renderConfig;\n    if (typeof oldRender === 'function' && !oldRender.__tiInvalidPath583) {\n        window.renderConfig = function(){ var r = oldRender.apply(this, arguments); setTimeout(schedule, 1600); return r; };\n        window.renderConfig.__tiInvalidPath583 = true;\n    }\n})();\n<\/script>\n\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-upload-590; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-upload-591; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-upload-592; ripristino flusso backup ti-file-in\/handleFileSelect. -->\n\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-backup-restore-593; ripristinato flusso storico ti-file-in\/handleFileSelect. -->\n\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-open-595; ripristinato flusso storico ti-file-in\/handleFileSelect. -->\n\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-direct-596; ripristinato flusso storico ti-file-in\/handleFileSelect. -->\n\n<script id=\"ti-config-image-throttle-596\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigImageThrottle596Installed) return;\n    window.__tiConfigImageThrottle596Installed = true;\n    var maxActive = 3, active = 0, queue = [];\n    function isTiImg(url){ return \/ti_action=ti_ai_get_image\/i.test(String(url || '')); }\n    function next(){\n        while (active < maxActive && queue.length) {\n            var img = queue.shift();\n            if (!img || img.getAttribute('data-ti-img-loaded-596') === '1') continue;\n            var src = img.getAttribute('data-ti-src-596');\n            if (!src) continue;\n            active++;\n            img.setAttribute('data-ti-img-loaded-596','1');\n            img.onload = img.onerror = function(){ active = Math.max(0, active - 1); setTimeout(next, 30); };\n            img.src = src;\n        }\n    }\n    function process(root){\n        root = root || document;\n        try {\n            root.querySelectorAll('img').forEach(function(img){\n                var src = img.getAttribute('src') || '';\n                if (!isTiImg(src) || img.getAttribute('data-ti-img-queued-596') === '1') return;\n                img.setAttribute('data-ti-src-596', src);\n                img.setAttribute('data-ti-img-queued-596','1');\n                img.removeAttribute('src');\n                img.loading = 'lazy';\n                img.decoding = 'async';\n                queue.push(img);\n            });\n            next();\n        } catch(e) {}\n    }\n    var prevRender = window.renderConfig;\n    if (typeof prevRender === 'function' && !prevRender.__tiImageThrottle596) {\n        window.renderConfig = function(){\n            var ret = prevRender.apply(this, arguments);\n            setTimeout(function(){ process(document); }, 0);\n            return ret;\n        };\n        window.renderConfig.__tiImageThrottle596 = true;\n    }\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ process(document); });\n    else setTimeout(function(){ process(document); }, 0);\n})();\n<\/script>\n\n\n<!-- 30.9.664: rimosso ti-file-manager-pc-visible-599; ripristinato flusso storico ti-file-in\/handleFileSelect. -->\n\n\n<script id=\"ti-config-login-help-600\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigLoginHelp600Installed) return;\n    window.__tiConfigLoginHelp600Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function norm(v){ return txt(v).trim().toLowerCase(); }\n    function visible(el){\n        try {\n            if (!el) return false;\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            return !(cs && (cs.display === 'none' || cs.visibility === 'hidden'));\n        } catch(e) { return !!el; }\n    }\n    function isPluginLogged(){\n        try {\n            var u = norm(window.tiUser || window.currentUser || '');\n            if (u && u !== 'cliente\/utente' && u !== 'cliente' && u !== 'utente' && u !== 'guest' && u !== 'ospite') return true;\n            var name = norm((byId('ti-user-name') || {}).textContent || '');\n            var role = norm((byId('ti-user-role') || {}).textContent || '');\n            if (name && name !== 'cliente\/utente' && name !== 'cliente' && name !== 'utente' && name !== 'ospite') return true;\n            if (role && role !== '-') return true;\n            var area = byId('ti-user-area');\n            if (visible(area) && (name || role) && name !== 'cliente\/utente') return true;\n            var logout = byId('ti-logout-btn');\n            if (visible(logout)) return true;\n        } catch(e) {}\n        return false;\n    }\n    function applyLoginButtons(){\n        var login = byId('ti-login-btn');\n        var logout = byId('ti-logout-btn');\n        if (isPluginLogged()) {\n            if (login) login.style.setProperty('display','none','important');\n            if (logout) logout.style.setProperty('display','inline-flex','important');\n            var direct = byId('ti-mobile-direct-login');\n            if (direct) direct.classList.remove('ti-direct-login-visible');\n        }\n    }\n    function ensureConfigHelp(){\n        var right = document.querySelector('#ti-conf-ov .ti-conf-header-right');\n        if (!right || byId('ti-conf-help-btn')) return;\n        var btn = document.createElement('button');\n        btn.type = 'button';\n        btn.id = 'ti-conf-help-btn';\n        btn.className = 'ti-header-help-btn ti-conf-help-btn';\n        btn.title = 'Aiuto configurazione';\n        btn.setAttribute('aria-label','Aiuto configurazione');\n        btn.textContent = '?';\n        btn.style.cssText = 'display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;min-width:24px;min-height:24px;padding:0;border-radius:999px;background:#374151;color:#fff;border:1px solid #4b5563;font-size:14px;font-weight:900;line-height:1;cursor:pointer;margin-right:6px;';\n        btn.addEventListener('click', function(ev){\n            try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {}\n            if (typeof window.openHelpModal === 'function') window.openHelpModal();\n            else {\n                var hb = byId('ti-help-btn');\n                if (hb) hb.click();\n            }\n            return false;\n        }, true);\n        var global = byId('ti-conf-global-btn');\n        if (global && global.parentNode === right) right.insertBefore(btn, global);\n        else right.insertBefore(btn, right.firstChild);\n    }\n    function apply(){ applyLoginButtons(); ensureConfigHelp(); }\n    var prevSetAdmin = window.setAdminUI;\n    if (typeof prevSetAdmin === 'function' && !prevSetAdmin.__ti600) {\n        window.setAdminUI = function(){ var r = prevSetAdmin.apply(this, arguments); setTimeout(apply, 0); return r; };\n        window.setAdminUI.__ti600 = true;\n    }\n    var prevOpenModal = window.openModal;\n    if (typeof prevOpenModal === 'function' && !prevOpenModal.__ti600) {\n        window.openModal = function(id){ var r = prevOpenModal.apply(this, arguments); if (id === 'ti-conf-ov') setTimeout(ensureConfigHelp, 0); setTimeout(applyLoginButtons, 0); return r; };\n        window.openModal.__ti600 = true;\n    }\n    ['DOMContentLoaded','ti:login','ti:config-opened'].forEach(function(evt){ document.addEventListener(evt, apply, true); });\n    setTimeout(apply, 0); setTimeout(apply, 500); setTimeout(apply, 1500);\n})();\n<\/script>\n\n\n\n\n\n\n\n\n<style id=\"ti-native-file-upload-621-css\">\n.ti-native-upload-617{background:#0f172a;border:1px solid #334155;border-radius:10px;padding:10px;box-sizing:border-box;text-align:left;}\n.ti-native-upload-row-617{display:flex;gap:8px;align-items:stretch;flex-wrap:wrap;}\n.ti-native-file-617{position:fixed!important;left:-9999px!important;top:-9999px!important;width:1px!important;height:1px!important;opacity:0!important;pointer-events:none!important;}\n.ti-native-upload-row-617 .ti-btn{flex:1;min-width:145px;}\n.ti-native-status-617{margin-top:8px;font-size:12px;line-height:1.35;color:#bfdbfe;text-align:center;white-space:normal;word-break:break-word;}\n.ti-native-status-617.ti-upload-error-617{color:#fecaca;}\n#ti-direct-upload-ov-621{position:fixed;inset:0;z-index:2147483600;display:none;align-items:center;justify-content:center;background:rgba(0,0,0,.72);padding:14px;box-sizing:border-box;}\n#ti-direct-upload-ov-621 .ti-direct-upload-box-621{width:min(92vw,430px);background:#111827;border:1px solid #374151;border-radius:14px;box-shadow:0 24px 70px rgba(0,0,0,.55);color:#e5e7eb;padding:16px;box-sizing:border-box;text-align:left;}\n#ti-direct-upload-ov-621 h3{color:#fff;margin:0 0 10px 0;font-size:17px;line-height:1.25;}\n#ti-direct-upload-ov-621 .ti-direct-upload-text-621{font-size:13px;line-height:1.45;color:#cbd5e1;margin:8px 0 12px 0;}\n#ti-direct-upload-ov-621 .ti-direct-upload-file-621{font-size:12px;color:#bfdbfe;word-break:break-word;margin:6px 0 10px 0;display:none;}\n#ti-direct-upload-ov-621 .ti-direct-progress-wrap-621{display:none;margin:12px 0 10px 0;}\n#ti-direct-upload-ov-621 .ti-direct-progress-shell-621{height:14px;background:#020617;border:1px solid #334155;border-radius:999px;overflow:hidden;}\n#ti-direct-upload-ov-621 .ti-direct-progress-bar-621{width:0%;height:100%;background:linear-gradient(90deg,#2563eb,#22c55e);transition:width .16s linear;}\n#ti-direct-upload-ov-621 .ti-direct-progress-label-621{margin-top:6px;font-size:12px;color:#dbeafe;text-align:center;}\n#ti-direct-upload-ov-621 .ti-direct-upload-log-821{display:none;margin:10px 0 8px 0;border:1px solid #475569;border-radius:10px;background:#020617;padding:8px;}\n#ti-direct-upload-ov-621 .ti-direct-upload-log-head-821{display:block;margin-bottom:6px;color:#e0f2fe;font-size:12px;font-weight:700;}\n#ti-direct-upload-ov-621 .ti-direct-upload-log-text-821{width:100%;min-height:150px;max-height:28vh;resize:vertical;box-sizing:border-box;border:1px solid #334155;border-radius:8px;background:#000;color:#d1fae5;font-family:Consolas,Monaco,monospace;font-size:11px;line-height:1.35;padding:8px;white-space:pre;overflow:auto;}\n#ti-direct-upload-ov-621 .ti-direct-upload-log-copy-821{display:block;width:100%;margin-top:8px;min-width:96px!important;padding:7px 10px!important;font-size:12px!important;background:#065f46!important;}\n#ti-direct-upload-ov-621 .ti-direct-upload-actions-621{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;}\n#ti-direct-upload-ov-621 .ti-btn{flex:1;min-width:130px;}\n#ti-direct-upload-ov-621 .ti-direct-exit-621{background:#4b5563!important;}\n#ti-direct-upload-ov-621 .ti-direct-start-621{background:#2563eb!important;}\n#ti-direct-upload-ov-621 .ti-direct-error-621{color:#fecaca!important;}\n#ti-direct-upload-ov-621 .ti-direct-source-map-629{display:none;margin:10px 0 8px 0;padding:10px;border:1px solid #334155;border-radius:10px;background:#000!important;color:#fff!important;font-size:12px;line-height:1.35;}\n#ti-direct-upload-ov-621 .ti-direct-source-map-629 label{display:flex;gap:7px;align-items:flex-start;margin-bottom:7px;background:#000!important;color:#fff!important;}\n#ti-direct-upload-ov-621 .ti-direct-source-map-629 label span,#ti-direct-upload-ov-621 #ti-direct-upload-map-label-text-629{background:#000!important;color:#fff!important;}\n#ti-direct-upload-ov-621 .ti-direct-source-map-629 input[type=checkbox]{accent-color:#111827;}\n#ti-direct-upload-ov-621 .ti-direct-source-map-629 input[type=text]{width:100%;box-sizing:border-box;border:1px solid #475569;border-radius:8px;background:#000!important;color:#fff!important;padding:8px;font-size:13px;}\n#ti-direct-upload-ov-621 .ti-direct-source-map-629 input[type=text]::placeholder{color:#e5e7eb!important;opacity:.85;}\n@media (max-width:640px){#ti-direct-upload-ov-621 .ti-direct-upload-box-621{width:96vw}.ti-native-upload-row-617{display:block}.ti-native-upload-row-617 .ti-btn{width:100%;margin-top:8px}}\n<\/style>\n<script id=\"ti-native-file-upload-651\">\n\n(function(){\n    \"use strict\";\n    if (window.__tiNativeFileUpload651Installed) return;\n    window.__tiNativeFileUpload651Installed = true;\n    window.__tiNativeFileUpload646Installed = true;\n    window.__tiNativeFileUpload645Installed = true;\n    window.__tiNativeFileUpload644Installed = true;\n    window.__tiNativeFileUpload643Installed = true;\n    window.__tiNativeFileUpload642Installed = true;\n    window.__tiNativeFileUpload641Installed = true;\n    window.__tiNativeFileUpload636Installed = true;\n    window.__tiNativeFileUpload634Installed = true;\n    window.__tiNativeFileUpload633Installed = true;\n    window.__tiNativeFileUpload632Installed = true;\n    window.__tiNativeFileUpload630Installed = true;\n    window.__tiNativeFileUpload629Installed = true;\n    window.__tiNativeFileUpload626Installed = true;\n    window.__tiNativeFileUpload625Installed = true;\n    window.__tiNativeFileUpload621Installed = true;\n    window.__tiNativeFileUpload624Installed = true;\n    window.__tiNativeFileUpload623Installed = true;\n    window.__tiNativeFileUpload622Installed = true;\n    window.__tiNativeFileUpload620Installed = true;\n    window.__tiNativeFileUpload617Installed = true;\n\n    var state = {kind:'main', target:'chat', xhr:null, busy:false, file:null, ctx:null, confirmingPath:false, confirmingUpload:false, serverAuthorizedPath659:'', serverCompanyLabel659:'', cancelRequested808:false, exitReason808:'', suppressStartClickUntil849:0, nativePickerArmedAt849:0, savedUploads859:[], uploadCleanupToken859:'', previousCleanupTokens859:[]};\n    try { window.__tiNativeFileUploadState = state; } catch(e823state) {}\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function trim(v){ return txt(v).trim(); }\n    function esc(v){ return txt(v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function dbName(){\n        var ids = ['ti-ditta','ti-db','ti-current-db','ti-selected-db'];\n        for (var i=0;i<ids.length;i++){ var el = byId(ids[i]); if (el && el.value) return el.value; }\n        try { if (window.currentDbName) return window.currentDbName; } catch(e) {}\n        try { if (window.tiCurrentDb) return window.tiCurrentDb; } catch(e2) {}\n        try { if (window.tiSelectedDb) return window.tiSelectedDb; } catch(e3) {}\n        try { if (window.currentDbData && window.currentDbData.__db_name) return window.currentDbData.__db_name; } catch(e4) {}\n        return 'Global';\n    }\n    function validCtx(ctx){ return !!(ctx && typeof ctx === 'object' && ctx.tbl && ctx.key && ctx.idx !== undefined && ctx.idx !== null && ctx.idx !== ''); }\n    function cloneCtx(ctx){ return validCtx(ctx) ? {tbl:txt(ctx.tbl), idx:ctx.idx, key:txt(ctx.key), id:ctx.id || ''} : null; }\n    function roleText(){\n        var r = trim(window.tiRole || window.tiUserRole || window.currentUserRole || '');\n        if (!r) {\n            var el = byId('ti-user-role');\n            r = trim(el ? el.textContent : '');\n        }\n        return r.toLowerCase();\n    }\n    function isPrivilegedUploadUser(){\n        var r = roleText();\n        var u = trim(window.tiUser || window.currentUser || window.tiCurrentUser || window.tiUsername || '').toLowerCase();\n        if (window.tiIsPrivilegedUploadUser === true || String(window.tiIsPrivilegedUploadUser || '').toLowerCase() === 'true') return true;\n        if (window.__tiPrivilegedUploadUser === true || window.tiCanCleanupOrphanFiles === true || window.tiConfigCanEditData === true) return true;\n        if (u === 'ssglobaladmin' || u === 'targetai') return true;\n        if (\/amministratore|administrator|configuratore|configurator|responsabile|responsible|globaladmin|global admin|globale|sysadmin\/.test(r)) return true;\n        try { if (typeof window.tiAiSsConfigCanEditData517 === 'function' && window.tiAiSsConfigCanEditData517()) return true; } catch(e) {}\n        try { if (typeof window.isAdminConfiguratorUser === 'function' && window.isAdminConfiguratorUser()) return true; } catch(e) {}\n        return false;\n    }\n    function isBasicUploadUser(){ return !isPrivilegedUploadUser(); }\n    function sanitizeNonPrivilegedMessage634(message){\n        var out = txt(message);\n        \/\/ 30.9.664: i messaggi upload non devono mai esporre elenchi di ruoli tecnici, anche se il ruolo pagina non e' ancora rilevato.\n        out = out.replace(\/Errore\\s+aggiunta\\s+file\\s+da\\s+PC\\s*:\\s*Upload\\s+file\\s+disponibile\\s+solo\\s+per[^\\r\\n.<]*\/gi, \"Errore upload file: Upload file non disponibile con il profilo corrente. Usa la procedura guidata prevista oppure richiedi l'abilitazione della sorgente file.\");\n        out = out.replace(\/Errore[^:\\r\\n<]*aggiunta[^:\\r\\n<]*file[^:\\r\\n<]*(?:PC|dispositivo)[^:\\r\\n<]*:\\s*Upload[^\\r\\n<]*(?:amministratore|administrator|configuratore|responsabile|globaladmin|global\\s*admin|SSGlobalAdmin)[^\\r\\n<]*\/gi, \"Errore upload file: Upload file non disponibile con il profilo corrente. Usa la procedura guidata prevista oppure richiedi l'abilitazione della sorgente file.\");\n        out = out.replace(new RegExp(['Upload file disponibile',' solo per'].join('') + '[^\\\\r\\\\n.]*', 'gi'), \"Upload file non disponibile con il profilo corrente. Usa la procedura guidata prevista oppure richiedi l'abilitazione della sorgente file.\");\n        out = out.replace(new RegExp(['Upload e associazione file disponibili',' solo per'].join('') + '[^\\\\r\\\\n.]*', 'gi'), \"Upload e associazione file non disponibili con il profilo corrente. Usa la procedura guidata prevista oppure richiedi l'abilitazione della sorgente file.\");\n        out = out.replace(new RegExp(['Associazione file disponibile',' solo per'].join('') + '[^\\\\r\\\\n.]*', 'gi'), \"Associazione file non disponibile con il profilo corrente.\");\n        out = out.replace(\/Funzione disponibile solo per[^\\r\\n.]*\/gi, \"Funzione non disponibile con il profilo corrente.\");\n        out = out.replace(\/Funzione globale disponibile solo per[^\\r\\n.]*\/gi, \"Funzione globale non disponibile con il profilo corrente.\");\n        out = out.replace(\/Accesso negato\\.\\s*Operazione disponibile solo per[^\\r\\n.]*\/gi, \"Accesso negato. Operazione non disponibile con il profilo corrente.\");\n        out = out.replace(\/\\bSSGlobalAdmin\\b|\\bglobal\\s*admin\\b|\\bglobaladmin\\b\/gi, 'profilo abilitato');\n        out = out.replace(\/\\bamministratore\\b|\\bamministratori\\b|\\badministrator\\b|\\bconfiguratore\\b|\\bconfiguratori\\b|\\bresponsabile\\b|\\bresponsabili\\b\/gi, 'profilo abilitato');\n        out = out.replace(\/profilo abilitato\\s*,\\s*profilo abilitato\\s*,\\s*profilo abilitato\\s*o\\s*profilo abilitato\/gi, 'profilo abilitato');\n        out = out.replace(\/profili abilitati\\s*,\\s*profili abilitati\/gi, 'profili abilitati');\n        return out;\n    }\n    function installUploadAlertSanitizer634(){\n        try {\n            var prev = window.tiAlert;\n            if (typeof prev === 'function' && !prev.__tiUploadSanitizer644) {\n                window.tiAlert = function(message){\n                    var args = Array.prototype.slice.call(arguments);\n                    args[0] = sanitizeNonPrivilegedMessage634(args[0]);\n                    return prev.apply(this, args);\n                };\n                window.tiAlert.__tiUploadSanitizer644 = true;\n                window.tiAlert.__tiUploadSanitizer634 = true;\n            }\n            var nativeAlert = window.alert;\n            if (typeof nativeAlert === 'function' && !nativeAlert.__tiUploadSanitizer644) {\n                window.alert = function(message){ return nativeAlert.call(window, sanitizeNonPrivilegedMessage634(message)); };\n                window.alert.__tiUploadSanitizer644 = true;\n            }\n        } catch(e) {}\n    }\n    function isMobileUploadDevice(){\n        try {\n            if (navigator.userAgentData && navigator.userAgentData.mobile) return true;\n        } catch(e) {}\n        var ua = trim((navigator && navigator.userAgent) || '').toLowerCase();\n        if (\/android|iphone|ipad|ipod|windows phone|mobile|tablet|kindle|silk|opera mini\/.test(ua)) return true;\n        try {\n            if (navigator.maxTouchPoints > 1 && \/macintosh|mac os\/.test(ua)) return true; \/\/ iPadOS in modalita desktop.\n        } catch(e2) {}\n        try {\n            if (window.matchMedia && window.matchMedia('(pointer: coarse)').matches && Math.min(window.innerWidth || 0, window.innerHeight || 0) <= 900) return true;\n        } catch(e3) {}\n        return false;\n    }\n    function basicDesktopUploadBlocked(){ return false; } \/\/ 30.9.664: il browser non consente il monitoraggio unit\u00e0; non blocchiamo il selettore nativo, ma non proponiamo path di sistema.\n    function removableNoticeText(kind){\n        if (isBasicUploadUser()) {\n            var p = firstAuthorizedSourcePath642();\n            if (p) return 'Il caricamento \u00e8 consentito solo dall\\'origine\/path autorizzata dalla ditta:<br><b>' + esc(p) + '<\/b><br><br>Non usare disco fisso o path di sistema generici. Inserisci eventualmente il supporto corretto, poi premi <b>Apri scelta file<\/b> e scegli il file da tale origine.<br><br>Nota tecnica: per sicurezza il browser non comunica sempre a Shop & Service il percorso locale reale del dispositivo; il plugin usa la prima origine\/path autorizzata presente nelle istruzioni della ditta.';\n            return 'Nessuna origine\/path file risulta disponibile per la ditta ' + esc(displayDittaLabel658(companyLabel642())) + '.';\n        }\n        var base2 = 'Prima di avviare la scelta del file, inserisci se necessario la chiavetta USB, il CD, il DVD o altro supporto esterno che contiene il documento.';\n        base2 += '<br><br>Per sicurezza del browser, Shop & Service non pu\u00f2 leggere o monitorare automaticamente l\\'elenco dei dischi\/supporti removibili collegati. La scelta avviene nella finestra nativa del sistema, dove potrai selezionare anche file presenti su USB, CD, DVD o altro supporto disponibile.';\n        if (window.__tiEnablePrivilegedUploadSourceMapping === true) base2 += '<br><br>Se questa \u00e8 una nuova origine da rendere disponibile agli utenti standard, puoi salvarla come istruzione: la AI user\u00e0 tale regola per consentire il caricamento guidato dalla path\/origine dichiarata.';\n        if (kind === 'manager') base2 += '<br><br>Il file selezionato verr\u00e0 caricato e associato al campo corrente.';\n        else base2 += '<br><br>Il file selezionato verr\u00e0 caricato e poi interpretato dalla AI quando possibile.';\n        return base2;\n    }\n    function isManager(kind){ return kind === 'manager'; }\n    function statusEl(kind){ return byId(isManager(kind) ? 'ti-fm-native-status-617' : 'ti-main-native-status-617'); }\n    function setInlineStatus(kind, msg, err){ var el = statusEl(kind); if (!el) return; el.className = 'ti-native-status-617' + (err ? ' ti-upload-error-617' : ''); el.innerHTML = msg ? esc(msg) : ''; }\n\n    function cleanupUrl859(){ return \"https:\/\/www.tisoft.it\/en\/?ti_action=ti_ai_ss_upload_cleanup_859\"; }\n    function makeUploadCleanupToken859(){ return 'u859_' + Date.now() + '_' + Math.random().toString(36).slice(2, 11); }\n    function currentUploadCleanupToken859(create){\n        if (!state.uploadCleanupToken859 && create !== false) state.uploadCleanupToken859 = makeUploadCleanupToken859();\n        return state.uploadCleanupToken859 || '';\n    }\n    function aiUploadTarget859(target){ target = trim(target || state.target || ''); return \/^(config-bot-file|chat|purchase-import-file)$\/.test(target); }\n    function dbForCleanup859(){ try { return dbName(); } catch(e) { return 'Global'; } }\n    function clearFileAiStorage859(removeMessages){\n        try { window.configBotFile = ''; window.lastUploadedFile = ''; window.__tiConfigBotFile798 = ''; window.__tiConfigBotFile847 = null; window.__tiLastFileAi799 = null; window.__tiLastFileAi798 = null; window.__tiLastAutoConfigFile795 = ''; window.__tiAutoFileImportRunning795 = false; } catch(e) {}\n        try { if (window.lastFileContext) { window.lastFileContext.url = ''; window.lastFileContext.active = false; window.lastFileContext.awaitingAction799 = false; } } catch(e2) {}\n        try {\n            [sessionStorage, localStorage].forEach(function(st){\n                if (!st) return;\n                var keys = [];\n                for (var i = 0; i < st.length; i++) { var k = st.key(i) || ''; if (\/^tiFileAI(798|799)::\/.test(k)) keys.push(k); }\n                keys.forEach(function(k){ try { st.removeItem(k); } catch(e) {} });\n            });\n        } catch(e3) {}\n        if (!removeMessages) return;\n        try { var fb = byId('ti-conf-ai-file'); if (fb) { fb.innerHTML = ''; fb.style.display = 'none'; } } catch(e4) {}\n        try {\n            var r = byId('ti-conf-ai-reply');\n            var rt = trim(r ? (r.textContent || '') : '');\n            if (r && \/file\\s+(caricato|disponibile|scelto)|cosa\\s+vuoi\\s+fare\\s+con\\s+questo\\s+file|anteprima\\s+import\\s+dal\\s+file|ANNULLA FILE|CARICA PRODOTTI E SERVIZI\/i.test(rt)) {\n                r.innerHTML = '';\n                r.style.display = 'none';\n            }\n        } catch(e5) {}\n        try {\n            var p = byId('ti-conf-ai-prompt');\n            if (p && \/ho\\s+caricato\\s+un\\s+file\\s+per\\s+la\\s+ai|aggiungi\\s+servizi\\s+e\\s+prodotti\\s+dal\\s+file\/i.test(p.value || '')) p.value = '';\n        } catch(e6) {}\n        try {\n            var bubbles = document.querySelectorAll('#ti-chat .ti-bubble.ai');\n            Array.prototype.forEach.call(bubbles, function(b){\n                var bt = trim(b.textContent || '');\n                if (\/file\\s+(caricato|disponibile|scelto)|cosa\\s+vuoi\\s+fare\\s+con\\s+questo\\s+file|CARICA PRODOTTI E SERVIZI|ANNULLA FILE\/i.test(bt)) {\n                    try { b.parentNode.removeChild(b); } catch(e) {}\n                }\n            });\n        } catch(e7) {}\n    }\n    function registerSavedUpload859(data, kind, target, file){\n        try {\n            if (!data) return;\n            var name = trim(data.name || data.filename || '');\n            var url = trim(data.url || '');\n            var path = trim(data.path || data.stored_path || '');\n            if (!name && url) { try { name = decodeURIComponent(url).split(\/[?#]\/)[0].split('\/').pop() || ''; } catch(e) {} }\n            if (!name && path) name = path.split(\/[\\\\\/]\/).pop() || '';\n            if (!name || !\/^\\d{14}_\\d{4}\\.[a-z0-9]{1,16}$\/i.test(name)) return;\n            var item = {name:name, filename:name, url:url, path:path, db:dbForCleanup859(), tbl:(data.tbl || (target === 'config-bot-file' || target === 'chat' ? 'Chat' : 'Config')), record:(data.record_label || ''), mode:(data.mode || 'plain'), kind:(kind || state.kind || 'main'), target:(target || state.target || ''), token:currentUploadCleanupToken859(false), original:(file && file.name ? file.name : '')};\n            state.savedUploads859 = state.savedUploads859 || [];\n            state.savedUploads859.push(item);\n            try { window.__tiGuidedUploadSaved859 = state.savedUploads859.slice(0); } catch(e2) {}\n        } catch(e3) {}\n    }\n    function cleanupRequest859(item, reason, token){\n        try {\n            var fd = new FormData();\n            fd.append('action', 'ti_ai_ss_upload_cleanup_859');\n            fd.append('ti_action', 'ti_ai_ss_upload_cleanup_859');\n            fd.append('reason', reason || 'reset_upload');\n            fd.append('cleanup_token', token || (item && item.token) || currentUploadCleanupToken859(false) || '');\n            fd.append('upload_cleanup_token', token || (item && item.token) || currentUploadCleanupToken859(false) || '');\n            fd.append('db', (item && item.db) || dbForCleanup859());\n            fd.append('tbl', (item && item.tbl) || 'Chat');\n            fd.append('record_label', (item && item.record) || '');\n            fd.append('file', (item && (item.name || item.filename)) || '');\n            fd.append('file_url', (item && item.url) || '');\n            fd.append('stored_path', (item && item.path) || '');\n            fd.append('mode', (item && item.mode) || 'plain');\n            fd.append('guided_upload_target', (item && item.target) || state.target || '');\n            return fetch(cleanupUrl859(), {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'}).then(function(r){ return r.text(); }).catch(function(){ return ''; });\n        } catch(e) { return Promise.resolve(''); }\n    }\n    function cleanupSavedUploads859(reason, removeMessages, keepToken){\n        var list = [];\n        try { if (state.savedUploads859 && state.savedUploads859.length) list = list.concat(state.savedUploads859); } catch(e) {}\n        try { if (window.__tiGuidedUploadSaved859 && window.__tiGuidedUploadSaved859.length) list = list.concat(window.__tiGuidedUploadSaved859); } catch(e2) {}\n        var token = currentUploadCleanupToken859(false);\n        var seen = {}, dedup = [];\n        list.forEach(function(it){ var k = (it && (it.path || it.url || it.name || it.filename) || '') + '|' + (it && it.token || token || ''); if (k && !seen[k]) { seen[k] = true; dedup.push(it); } });\n        state.savedUploads859 = [];\n        try { window.__tiGuidedUploadSaved859 = []; } catch(e3) {}\n        if (removeMessages) clearFileAiStorage859(true);\n        var jobs = [];\n        if (token) jobs.push(cleanupRequest859(null, reason || 'reset_upload', token));\n        dedup.forEach(function(it){ jobs.push(cleanupRequest859(it, reason || 'reset_upload', it && it.token ? it.token : token)); });\n        if (!keepToken) state.uploadCleanupToken859 = '';\n        if (!jobs.length) return Promise.resolve([]);\n        return Promise.all(jobs.map(function(p){ return p.catch(function(){ return ''; }); }));\n    }\n    function scheduleCleanup859(reason, removeMessages, token){\n        try { cleanupSavedUploads859(reason, removeMessages, true); } catch(e) {}\n        [1200, 4500, 12000].forEach(function(ms){ setTimeout(function(){ try { if (token) cleanupRequest859(null, reason || 'delayed_cleanup', token); } catch(e) {} }, ms); });\n    }\n    function hardResetUploadForReopen859(reason, removeMessages){\n        var token = currentUploadCleanupToken859(false);\n        try { if (state.xhr && state.xhr.readyState !== 4) state.xhr.abort(); } catch(e) {}\n        try { if (state.currentUploadAbort821) state.currentUploadAbort821.abort(); } catch(e2) {}\n        try { clearUploadWatchdog812(); } catch(e3) {}\n        try { removePendingNativeInputs808(); } catch(e4) {}\n        try { forceHideUploadProgress808(reason || 'reopen'); } catch(e5) {}\n        try { document.body.classList.remove('ti-upload-busy','ti-ai-upload-busy','ti-loader-active','ti-modal-open'); } catch(e6) {}\n        try { cleanupSavedUploads859(reason || 'riapertura_caricamento_file_ai', !!removeMessages, false); } catch(e7) {}\n        if (token) { [1500, 6000].forEach(function(ms){ setTimeout(function(){ cleanupRequest859(null, reason || 'riapertura_caricamento_file_ai', token); }, ms); }); }\n        state.busy = false; state.xhr = null; state.currentUploadAbort821 = null; state.file = null; state.confirmingPath = false; state.confirmingUpload = false; state.cancelRequested808 = false; state.timeoutRequested812 = false; state.uploadStartedAt812 = 0; state.uploadLastProgressAt812 = 0; state.popupClosedAfterSelect791 = false; state.autoCloseUploadModalAfterSelect791 = false; state.uploadCleanupToken859 = '';\n        try { window.__tiGuidedUploadBusy810 = false; window.__tiComplexProcessActive845 = false; window.__tiGuidedUploadSelectedFile810 = null; window.__tiGuidedUploadCancelled808 = 0; } catch(e8) {}\n    }\n\n    function saveCtx(ctx){\n        var c = cloneCtx(ctx);\n        if (!c) return null;\n        window.targetUploadContext = c;\n        window.__tiNativeUploadCtx621 = c;\n        window.__tiNativeUploadCtx620 = c;\n        window.__tiNativeUploadCtx617 = c;\n        window.__tiLastDirectUploadCtx616 = c;\n        window.__tiLastFileManagerCtx471 = c;\n        window.__tiLastFileManagerCtx455 = c;\n        try { sessionStorage.setItem('tiNativeUploadCtx621', JSON.stringify(c)); } catch(e) {}\n        try { sessionStorage.setItem('tiNativeUploadCtx620', JSON.stringify(c)); } catch(e) {}\n        try { sessionStorage.setItem('tiNativeUploadCtx617', JSON.stringify(c)); } catch(e) {}\n        return c;\n    }\n    function readCtx(){\n        var c = cloneCtx(window.__tiNativeUploadCtx621 || window.__tiNativeUploadCtx620 || window.__tiNativeUploadCtx617 || window.targetUploadContext || window.__tiLastDirectUploadCtx616 || window.__tiLastFileManagerCtx471 || window.__tiLastFileManagerCtx455 || window.__tiLastFileManagerCtx360 || window.__tiLastFileAiCtx358 || window.__tiLastUploadContext356);\n        if (c) return saveCtx(c);\n        var fm = byId('ti-file-manager-ov');\n        if (fm) {\n            c = cloneCtx({tbl:fm.dataset ? (fm.dataset.tbl || fm.dataset.table || '') : '', idx:fm.dataset ? (fm.dataset.idx || fm.dataset.row || '') : '', key:fm.dataset ? (fm.dataset.key || fm.dataset.field || '') : '', id:fm.dataset ? (fm.dataset.id || '') : ''});\n            if (c) return saveCtx(c);\n            c = cloneCtx({tbl:fm.getAttribute('data-ti-context-table') || '', idx:fm.getAttribute('data-ti-context-row') || '', key:fm.getAttribute('data-ti-context-field') || '', id:fm.getAttribute('data-ti-context-id') || ''});\n            if (c) return saveCtx(c);\n        }\n        try {\n            var raw = sessionStorage.getItem('tiNativeUploadCtx621') || sessionStorage.getItem('tiNativeUploadCtx620') || sessionStorage.getItem('tiNativeUploadCtx617') || sessionStorage.getItem('tiLastDirectUploadCtx616') || sessionStorage.getItem('tiLastFileManagerCtx471') || sessionStorage.getItem('tiLastFileManagerCtx455');\n            if (raw) { c = cloneCtx(JSON.parse(raw)); if (c) return saveCtx(c); }\n        } catch(e) {}\n        return null;\n    }\n    function rowFor(ctx){\n        try { return (ctx && window.currentDbData && window.currentDbData.Tabelle && window.currentDbData.Tabelle[ctx.tbl]) ? (window.currentDbData.Tabelle[ctx.tbl][ctx.idx] || null) : null; }\n        catch(e) { return null; }\n    }\n    function recordLabel(ctx){\n        try { if (window.getRecordLabelForUpload) return window.getRecordLabelForUpload(ctx.tbl, rowFor(ctx), ctx.tbl); } catch(e) {}\n        return ctx && ctx.tbl ? ctx.tbl : '';\n    }\n    function startButtonWantsNativePicker849(btn){\n        if (!btn || state.busy || state.confirmingPath || state.confirmingUpload || state.file) return false;\n        var label = trim((btn.textContent || btn.value || btn.getAttribute('aria-label') || ''));\n        return \/scegli|ripeti|selezione\\s+file|apri\\s+selezione|apri\\s+scelta|caricamento\/i.test(label);\n    }\n    function handleStartPointerNativePicker849(ev){\n        try {\n            var btn = ev && ev.currentTarget ? ev.currentTarget : byId('ti-direct-upload-start-621');\n            if (!startButtonWantsNativePicker849(btn)) return;\n            var now = Date.now();\n            if (state.nativePickerArmedAt849 && (now - state.nativePickerArmedAt849) < 900) {\n                if (ev.preventDefault) ev.preventDefault();\n                if (ev.stopPropagation) ev.stopPropagation();\n                if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                return false;\n            }\n            state.nativePickerArmedAt849 = now;\n            state.suppressStartClickUntil849 = Date.now() + 650;\n            setTimeout(function(){ try { if (state.suppressStartClickUntil849 && Date.now() >= state.suppressStartClickUntil849) state.suppressStartClickUntil849 = 0; } catch(e849t) {} }, 750);\n            if (ev.preventDefault) ev.preventDefault();\n            if (ev.stopPropagation) ev.stopPropagation();\n            if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n            try { if (aiUploadTarget859(state.target)) cleanupSavedUploads859('ripeti_scelta_file_ai', true, false); } catch(e859rp) {}\n            state.file = null;\n            state.confirmingUpload = false;\n            state.confirmingPath = false;\n            removePendingNativeInputs808();\n            chooseFileNow();\n            return false;\n        } catch(e) { return false; }\n    }\n    function ensureModal(){\n        var ov = byId('ti-direct-upload-ov-621');\n        if (ov) return ov;\n        ov = document.createElement('div');\n        ov.id = 'ti-direct-upload-ov-621';\n        ov.innerHTML = '<div class=\"ti-direct-upload-box-621\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"ti-direct-upload-title-621\">'\n            + '<h3 id=\"ti-direct-upload-title-621\">Caricamento guidato file<\/h3>'\n            + '<div id=\"ti-direct-upload-text-621\" class=\"ti-direct-upload-text-621\"><\/div>'\n            + '<div id=\"ti-direct-upload-file-621\" class=\"ti-direct-upload-file-621\"><\/div>'\n            + '<div id=\"ti-direct-upload-map-629\" class=\"ti-direct-source-map-629\" style=\"background:#000!important;color:#fff!important;\"><label id=\"ti-direct-upload-map-label-629\" style=\"background:#000!important;color:#fff!important;\"><input type=\"checkbox\" id=\"ti-direct-upload-map-check-629\"> <span id=\"ti-direct-upload-map-label-text-629\" style=\"background:#000!important;color:#fff!important;\">Salva questa origine\/path come istruzione di caricamento consentita<\/span><\/label><input type=\"text\" id=\"ti-direct-upload-source-label-629\" style=\"background:#000!important;color:#fff!important;\" placeholder=\"Origine\/path autorizzata prevista nelle istruzioni della ditta\"><\/div>'\n            + '<div id=\"ti-direct-progress-wrap-621\" class=\"ti-direct-progress-wrap-621\"><div class=\"ti-direct-progress-shell-621\"><div id=\"ti-direct-progress-bar-621\" class=\"ti-direct-progress-bar-621\"><\/div><\/div><div id=\"ti-direct-progress-label-621\" class=\"ti-direct-progress-label-621\">0%<\/div><\/div>'\n            + '<div id=\"ti-direct-upload-log-821\" class=\"ti-direct-upload-log-821\"><div class=\"ti-direct-upload-log-head-821\"><span>Log tecnico caricamento<\/span><\/div><textarea id=\"ti-direct-upload-log-text-821\" class=\"ti-direct-upload-log-text-821\" readonly><\/textarea><button type=\"button\" id=\"ti-direct-upload-log-copy-821\" class=\"ti-btn ti-direct-upload-log-copy-821\">Copia testo<\/button><\/div>'\n            + '<div class=\"ti-direct-upload-actions-621\"><button type=\"button\" id=\"ti-direct-upload-pick-path-655\" class=\"ti-btn ti-direct-pick-path-655\" style=\"display:none;\">Individua path<\/button><button type=\"button\" id=\"ti-direct-upload-save-source-651\" class=\"ti-btn ti-direct-save-source-651\" style=\"display:none;\">Salva origine\/path<\/button><button type=\"button\" id=\"ti-direct-upload-start-621\" class=\"ti-btn ti-direct-start-621\">Avvia caricamento<\/button><button type=\"button\" id=\"ti-direct-upload-exit-621\" class=\"ti-btn ti-direct-exit-621\">Chiudi<\/button><\/div>'\n            + '<\/div>';\n        document.body.appendChild(ov);\n        \/\/ 30.9.861: il selettore file deve aprirsi solo dal click completo del pulsante.\n        \/\/ L'avvio da pointerdown\/mousedown\/touchstart causava click soppressi e pulsante Scegli file non ripristinato dopo annullo.\n        byId('ti-direct-upload-start-621').addEventListener('click', function(ev){\n            try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {}\n            if (state.suppressStartClickUntil849 && Date.now() < state.suppressStartClickUntil849) {\n                try { if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e849) {}\n                return false;\n            }\n            state.suppressStartClickUntil849 = 0;\n            var btn = byId('ti-direct-upload-start-621');\n            var label = trim((btn && btn.textContent) || '');\n            clearStaleUploadLock810('start_button');\n            if (state.busy) {\n                setModalText('Caricamento file ancora in corso. Puoi attendere oppure premere <b>Chiudi<\/b> per annullare. Se il tempo supera il limite, il plugin sblocca automaticamente la form.', true);\n                return false;\n            }\n            if (\/ripeti|scegli\/i.test(label) && !state.confirmingPath && !state.confirmingUpload) {\n                try { if (aiUploadTarget859(state.target)) cleanupSavedUploads859('ripeti_scelta_file_ai', true, false); } catch(e859pick) {}\n                state.file = null;\n                state.confirmingUpload = false;\n                state.confirmingPath = false;\n                removePendingNativeInputs808();\n                chooseFileNow();\n                return false;\n            }\n            if (state.confirmingPath && state.file) { var f = state.file; state.confirmingPath = false; state.confirmingUpload = false; state.autoCloseUploadModalAfterSelect791 = false; setModalText('Conferma ricevuta. Avvio caricamento file...', false); startUpload(state.kind, state.target, f); return false; }\n            if (state.confirmingUpload && state.file) { var f2 = state.file; if (\/riprova\/i.test(label)) { try { cleanupSavedUploads859('riprova_stesso_file_ai', true, false); } catch(e859retry) {} } state.confirmingUpload = false; state.autoCloseUploadModalAfterSelect791 = false; setModalText('Conferma ricevuta. Avvio caricamento file...', false); startUpload(state.kind, state.target, f2); return false; }\n            if (state.file) { state.confirmingUpload = true; showStartButton(true, 'Conferma e carica file'); setModalText('File gia scelto. Premi <b>Conferma e carica file<\/b> per avviare il caricamento.', false); return false; }\n            chooseFileNow(); return false;\n        }, true);\n        var pickPath655 = byId('ti-direct-upload-pick-path-655');\n        if (pickPath655) pickPath655.addEventListener('click', function(ev){ try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {} locateAuthorizedPathOnly655(); return false; }, true);\n        var saveSrc651 = byId('ti-direct-upload-save-source-651');\n        if (saveSrc651) saveSrc651.addEventListener('click', function(ev){ try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {} saveAuthorizedSourceOnly651(); return false; }, true);\n        byId('ti-direct-upload-exit-621').addEventListener('click', function(ev){ try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {} exitUpload(); return false; }, true);\n        var copyLog821 = byId('ti-direct-upload-log-copy-821');\n        if (copyLog821) copyLog821.addEventListener('click', function(ev){\n            try { ev.preventDefault(); ev.stopPropagation(); } catch(e) {}\n            copyUploadLog821();\n            return false;\n        }, true);\n        return ov;\n    }\n    function isUploadLogAllowed821(){\n        var r = roleText();\n        var u = trim(window.tiUser || window.currentUser || window.tiCurrentUser || window.tiUsername || '').toLowerCase();\n        \/\/ 30.9.822: log tecnico visibile solo ad Amministratore e GlobalAdmin.\n        \/\/ Configuratore, Responsabile e utenti base non devono vedere pannello, textarea o pulsante copia log.\n        if (u === 'ssglobaladmin' || u === 'targetai') return true;\n        if (window.tiIsGlobalAdmin === true || window.__tiIsGlobalAdmin === true) return true;\n        if (window.tiIsAdmin === true || window.__tiIsAdmin === true) return true;\n        return !!(\/(^|[^a-z0-9])(amministratore|administrator|globaladmin|global admin|ssglobaladmin|admin)([^a-z0-9]|$)\/i.test(r));\n    }\n    function uploadLogTime821(){\n        try { var d = new Date(); return d.toLocaleTimeString('it-IT', {hour12:false}) + '.' + String(d.getMilliseconds()).padStart(3,'0'); } catch(e) { return String(Date.now()); }\n    }\n    function uploadLogContext821(){\n        var parts = [];\n        try { parts.push('db=' + (dbName() || '')); } catch(e) {}\n        try { parts.push('kind=' + (state.kind || '')); } catch(e) {}\n        try { parts.push('target=' + (state.target || '')); } catch(e) {}\n        try { parts.push('busy=' + (!!state.busy)); } catch(e) {}\n        try { parts.push('pct=' + (state.progressPercent814 || 0)); } catch(e) {}\n        try { if (state.uploadEndpoint817) parts.push('endpoint=' + state.uploadEndpoint817); } catch(e) {}\n        return parts.join(' | ');\n    }\n    function updateUploadLogPanel821(){\n        try {\n            var allowed822 = isUploadLogAllowed821();\n            var wrap = byId('ti-direct-upload-log-821');\n            var ta = byId('ti-direct-upload-log-text-821');\n            if (wrap) wrap.style.display = allowed822 ? 'block' : 'none';\n            if (!allowed822) { if (ta) ta.value = ''; return; }\n            if (ta) {\n                ta.value = (state.uploadLogLines821 || []).join('\\n');\n                ta.scrollTop = ta.scrollHeight;\n            }\n        } catch(e) {}\n    }\n    function uploadLog821(phase, detail, extra){\n        try {\n            if (!isUploadLogAllowed821()) return;\n            if (!state.uploadLogLines821) state.uploadLogLines821 = [];\n            var msg = '[' + uploadLogTime821() + '] ' + (phase || 'fase') + ' - ' + (detail || '');\n            var ctx = uploadLogContext821();\n            if (ctx) msg += ' | ' + ctx;\n            if (extra) msg += ' | ' + String(extra).replace(\/\\s+\/g, ' ').slice(0, 700);\n            state.uploadLogLines821.push(msg);\n            if (state.uploadLogLines821.length > 500) state.uploadLogLines821 = state.uploadLogLines821.slice(-500);\n            updateUploadLogPanel821();\n        } catch(e) {}\n    }\n    function initUploadLog821(reason, kind, target, file){\n        try {\n            state.uploadLogLines821 = [];\n            state.uploadEndpoint817 = '';\n            state.currentUploadAbort821 = null;\n            var wrap = byId('ti-direct-upload-log-821');\n            if (wrap) wrap.style.display = isUploadLogAllowed821() ? 'block' : 'none';\n            var ta = byId('ti-direct-upload-log-text-821');\n            if (ta) ta.value = '';\n            var f = file ? ((file.name || 'file') + ' size=' + (file.size || 0) + ' type=' + (file.type || '')) : 'file=non ancora scelto';\n            uploadLog821('init', reason || 'apertura flusso upload', 'user=' + (window.tiUser || '') + ' role=' + (window.tiRole || roleText()) + ' | ' + f + ' | ua=' + (navigator.userAgent || ''));\n        } catch(e) {}\n    }\n    function copyUploadLog821(){\n        try {\n            var btn = byId('ti-direct-upload-log-copy-821');\n            function done(ok){ if (btn) { var old = btn.getAttribute('data-old-label') || btn.textContent || 'Copia testo'; btn.setAttribute('data-old-label', old); btn.textContent = ok ? 'Testo copiato' : 'Copia manuale'; setTimeout(function(){ try { btn.textContent = old || 'Copia testo'; } catch(e) {} }, ok ? 1800 : 2600); } }\n            if (!isUploadLogAllowed821()) { done(false); return false; }\n            var ta = byId('ti-direct-upload-log-text-821');\n            var fromState = (state.uploadLogLines821 || []).join('\\n');\n            var fromTextArea = ta ? String(ta.value || '') : '';\n            var text = (fromState && fromState.length >= fromTextArea.length) ? fromState : fromTextArea;\n            if (!text) text = 'Log caricamento vuoto.';\n            function fallbackCopy(){\n                try {\n                    var h = document.createElement('textarea');\n                    h.value = text;\n                    h.setAttribute('readonly','readonly');\n                    h.style.position = 'fixed'; h.style.left = '-10000px'; h.style.top = '0'; h.style.opacity = '0';\n                    document.body.appendChild(h); h.focus(); h.select(); h.setSelectionRange(0, h.value.length);\n                    var ok = false; try { ok = document.execCommand('copy'); } catch(e) { ok = false; }\n                    document.body.removeChild(h);\n                    if (!ok && ta) { try { ta.focus(); ta.select(); ok = document.execCommand('copy'); } catch(e2) {} }\n                    done(!!ok); return !!ok;\n                } catch(e3) { done(false); return false; }\n            }\n            if (navigator.clipboard && navigator.clipboard.writeText && window.isSecureContext) {\n                navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){ fallbackCopy(); });\n            } else {\n                fallbackCopy();\n            }\n            return false;\n        } catch(e) { return false; }\n    }\n    function setProgress(p, label){\n        p = Math.max(0, Math.min(100, parseInt(p,10) || 0));\n        \/\/ 30.9.817: durante upload non far tornare indietro la barra per eventi asincroni\n        \/\/ ravvicinati (es. upload.onloadstart a 6% seguito da send a 5%). La regressione\n        \/\/ visiva dava l'impressione di blocco fisso al 5%.\n        try {\n            var cur817 = parseInt(state.progressPercent814 || 0, 10) || 0;\n            var forceReset817 = \/timeout|errore|annull|pronto per riprova|in attesa\/i.test(String(label || ''));\n            if (state.busy && p > 0 && p < 100 && cur817 > p && cur817 < 100 && !forceReset817) p = cur817;\n        } catch(e817np) {}\n        try { state.progressPercent814 = p; state.progressLabel814 = label || (p + '%'); state.progressLastAt814 = Date.now(); } catch(e814p) {}\n        try { uploadLog821('progress', label || (p + '%')); } catch(e821pl) {}\n        var bar = byId('ti-direct-progress-bar-621'), lab = byId('ti-direct-progress-label-621'), wrap = byId('ti-direct-progress-wrap-621');\n        if (wrap) wrap.style.display = 'block';\n        if (bar) bar.style.width = p + '%';\n        if (lab) {\n            \/\/ 30.9.861: sotto la barra deve restare solo la percentuale, senza commenti di fase.\n            lab.textContent = p + '%';\n            try { lab.setAttribute('title', label || (p + '%')); lab.setAttribute('aria-label', label || (p + '%')); } catch(e861lab) {}\n        }\n    }\n    function setUploadActivity814(p, label, title, subtitle, file){\n        setProgress(p, label);\n        try { state.uploadLastProgressAt812 = Date.now(); } catch(e812p) {}\n        try { updateUploadWaitPopup792(p, title || 'Caricamento file', subtitle || label || 'Avanzamento caricamento.', file && file.name ? file.name : 'file'); } catch(e814u) {}\n    }\n    function bumpUploadActivity814(minPct, maxPct, label, title, subtitle, file){\n        var cur = 0;\n        try { cur = parseInt(state.progressPercent814 || 0, 10) || 0; } catch(e814b) { cur = 0; }\n        var next = Math.max(minPct || 0, cur);\n        if (maxPct !== undefined && maxPct !== null) next = Math.min(maxPct, next);\n        setUploadActivity814(next, label, title, subtitle, file);\n    }\n    function uploadUrlListB64818(){\n        var urls = [];\n        var hardAjax818 = \"https:\/\/www.tisoft.it\/wp-admin\/admin-ajax.php\";\n        var hardDirect818 = \"https:\/\/www.tisoft.it\/en\/?ti_action=ti_ai_ss_b64_upload_818\";\n        function add(u){ u = trim(u || ''); if (!u) return; try { u = u.replace(\/#.*$\/, ''); } catch(e) {} if (urls.indexOf(u) === -1) urls.push(u); }\n        add(hardAjax818); add(hardDirect818); add(window.ajaxurl); add(window.tiAjaxUrl);\n        return urls;\n    }\n    function bytesToBase64820(bytes){\n        var binary = '';\n        var chunk = 0x8000;\n        for (var i = 0; i < bytes.length; i += chunk) {\n            var sub = bytes.subarray(i, i + chunk);\n            binary += String.fromCharCode.apply(null, sub);\n        }\n        return btoa(binary);\n    }\n    function readFileAsBase64818(file){\n        \/\/ 30.9.820: lo stallo al 15% indicava lettura locale senza onload\/onerror.\n        \/\/ Usa timeout reale, progressi visibili e fallback arrayBuffer\/chunk base64.\n        return new Promise(function(resolve, reject){\n            if (!file) { reject(new Error('Lettura locale file: file assente.')); return; }\n            var done = false;\n            var timer = null;\n            var heartbeat823 = null;\n            var hbStep823 = 0;\n            function stopHeartbeat823(){ try { if (heartbeat823) clearInterval(heartbeat823); } catch(e) {} heartbeat823 = null; }\n            function startHeartbeat823(){\n                stopHeartbeat823();\n                heartbeat823 = setInterval(function(){\n                    if (done) { stopHeartbeat823(); return; }\n                    hbStep823++;\n                    var pct823 = Math.min(24, 15 + hbStep823);\n                    try { uploadLog821('local_read_heartbeat_823', 'Lettura locale ancora in corso', 'step=' + hbStep823 + ' pct=' + pct823 + ' file=' + (file && file.name ? file.name : '')); } catch(e) {}\n                    try { setUploadActivity814(pct823, pct823 + '% - Lettura locale in corso', 'Caricamento file', 'Sto leggendo il file nel browser. Se non arriva risposta passo automaticamente al metodo alternativo.', file); } catch(e2) {}\n                }, 1200);\n            }\n            function finish(fn, val){\n                if (done) return;\n                done = true;\n                try { if (timer) clearTimeout(timer); } catch(e) {}\n                stopHeartbeat823();\n                fn(val);\n            }\n            function tryArrayBufferFallback(reason){\n                if (done) return;\n                if (!file.arrayBuffer) { finish(reject, new Error(reason || 'Lettura locale file non riuscita.')); return; }\n                try { setUploadActivity814(26, '26% - Lettura locale alternativa', 'Caricamento file', 'Il primo metodo di lettura non ha risposto. Uso lettura alternativa del browser.', file); } catch(e) {}\n                var abDone = false;\n                var abTimer = setTimeout(function(){ if (!abDone) finish(reject, new Error('Lettura locale file in timeout. Riprova oppure carica una versione TXT\/CSV\/XLSX.')); }, 12000);\n                file.arrayBuffer().then(function(buf){\n                    if (done) return;\n                    abDone = true;\n                    try { clearTimeout(abTimer); } catch(e) {}\n                    try { setUploadActivity814(31, '31% - File letto localmente', 'Caricamento file', 'File letto nel browser. Preparo invio diretto.', file); } catch(e) {}\n                    var b64 = bytesToBase64820(new Uint8Array(buf));\n                    finish(resolve, b64);\n                }).catch(function(err){\n                    if (done) return;\n                    abDone = true;\n                    try { clearTimeout(abTimer); } catch(e) {}\n                    finish(reject, new Error('Lettura locale file non riuscita: ' + (err && err.message ? err.message : (reason || err))));\n                });\n            }\n            try {\n                var fr = new FileReader();\n                startHeartbeat823();\n                timer = setTimeout(function(){\n                    try { uploadLog821('local_read_timeout_823', 'FileReader fermo: avvio fallback arrayBuffer', 'file=' + (file && file.name ? file.name : '')); } catch(e) {}\n                    try { fr.abort(); } catch(e) {}\n                    tryArrayBufferFallback('Lettura locale file ferma al 15%.');\n                }, 5000);\n                fr.onerror = function(){ tryArrayBufferFallback('Lettura locale file non riuscita.'); };\n                fr.onabort = function(){ if (!done) tryArrayBufferFallback('Lettura locale file interrotta.'); };\n                fr.onprogress = function(ev){\n                    try {\n                        if (ev && ev.lengthComputable && ev.total > 0) {\n                            var pct = Math.max(16, Math.min(24, Math.round(16 + (ev.loaded \/ ev.total) * 8)));\n                            setUploadActivity814(pct, pct + '% - Lettura locale file (' + Math.round((ev.loaded||0)\/1024) + '\/' + Math.round((ev.total||0)\/1024) + ' KB)', 'Caricamento file', 'Il browser sta leggendo il file prima dell invio diretto.', file);\n                        } else {\n                            setUploadActivity814(18, '18% - Lettura locale in corso', 'Caricamento file', 'Il browser sta leggendo il file prima dell invio diretto.', file);\n                        }\n                    } catch(e) {}\n                };\n                fr.onload = function(){\n                    if (done) return;\n                    var s = String(fr.result || '');\n                    if (!s) { tryArrayBufferFallback('Lettura locale file vuota.'); return; }\n                    var p = s.indexOf(',');\n                    try { setUploadActivity814(30, '30% - File letto localmente', 'Caricamento file', 'File letto nel browser. Preparo invio diretto.', file); } catch(e) {}\n                    finish(resolve, p >= 0 ? s.slice(p + 1) : s);\n                };\n                fr.readAsDataURL(file);\n            } catch(e) { tryArrayBufferFallback(e && e.message ? e.message : e); }\n        });\n    }\n    function uploadUrlListChunk825(){\n        var urls = [];\n        var direct825 = \"https:\/\/www.tisoft.it\/en\/?ti_action=ti_ai_ss_b64_chunk_upload_825\";\n        var ajax825 = \"https:\/\/www.tisoft.it\/wp-admin\/admin-ajax.php\";\n        function add(u){ u = trim(u || ''); if (!u) return; try { u = u.replace(\/#.*$\/, ''); } catch(e) {} if (urls.indexOf(u) === -1) urls.push(u); }\n        add(direct825); add(ajax825);\n        return urls;\n    }\n    function readSliceBase64825(blob, label, timeoutMs){\n        return new Promise(function(resolve, reject){\n            var done = false;\n            var timer = setTimeout(function(){\n                if (done) return;\n                done = true;\n                try { uploadLog821('chunk_read_timeout_829', 'Timeout lettura chunk locale', 'label=' + label + ' ms=' + (timeoutMs || 7000)); } catch(e829rt) {}\n                reject(new Error('Timeout lettura chunk locale: ' + label));\n            }, timeoutMs || 7000);\n            function finish(fn, val){\n                if (done) return;\n                done = true;\n                try { clearTimeout(timer); } catch(e) {}\n                fn(val);\n            }\n            try {\n                if (blob && blob.arrayBuffer) {\n                    blob.arrayBuffer().then(function(buf){\n                        finish(resolve, bytesToBase64820(new Uint8Array(buf)));\n                    }).catch(function(err){\n                        finish(reject, new Error('Errore lettura chunk: ' + (err && err.message ? err.message : err)));\n                    });\n                    return;\n                }\n                var fr = new FileReader();\n                fr.onerror = function(){ finish(reject, new Error('Errore FileReader chunk: ' + label)); };\n                fr.onabort = function(){ finish(reject, new Error('FileReader chunk interrotto: ' + label)); };\n                fr.onload = function(){\n                    var s = String(fr.result || '');\n                    var p = s.indexOf(',');\n                    finish(resolve, p >= 0 ? s.slice(p + 1) : s);\n                };\n                fr.readAsDataURL(blob);\n            } catch(e) {\n                finish(reject, new Error('Lettura chunk non avviata: ' + (e && e.message ? e.message : e)));\n            }\n        });\n    }\n    function postChunkPayload825(url, params, chunkIndex){\n        uploadLog821('chunk_post_prepare_825', 'Preparo invio chunk', 'chunk=' + chunkIndex + ' url=' + url);\n        var body = '';\n        try {\n            body = Object.keys(params || {}).map(function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(params[k] == null ? '' : String(params[k])); }).join('&');\n        } catch(e) {\n            uploadLog821('chunk_body_error_825', 'Errore codifica chunk', e && e.message ? e.message : e);\n            return Promise.reject(new Error('Errore codifica chunk: ' + (e && e.message ? e.message : e)));\n        }\n        var controller = null;\n        var timeoutId = null;\n        try { if (window.AbortController) { controller = new AbortController(); state.currentUploadAbort821 = controller; } } catch(e) { controller = null; }\n        var timeoutPromise = new Promise(function(_, reject){\n            timeoutId = setTimeout(function(){\n                try { uploadLog821('chunk_post_timeout_825', 'Timeout invio chunk', 'chunk=' + chunkIndex + ' url=' + url); } catch(e) {}\n                try { if (controller) controller.abort(); } catch(e) {}\n                reject(new Error('Timeout invio chunk ' + (chunkIndex + 1) + ': server non risponde.'));\n            }, 9000);\n        });\n        var fetchPromise = fetch(url, {method:'POST', credentials:'same-origin', cache:'no-store', headers:{'Content-Type':'application\/x-www-form-urlencoded; charset=UTF-8'}, body:body, signal: controller ? controller.signal : undefined}).then(function(resp){\n            return resp.text().then(function(raw){\n                uploadLog821('chunk_response_825', 'Risposta chunk', 'chunk=' + chunkIndex + ' status=' + resp.status + ' chars=' + String(raw || '').length + ' preview=' + String(raw || '').slice(0,160));\n                if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + String(raw || '').slice(0, 180));\n                var res = null; try { res = JSON.parse(raw); } catch(e) { throw new Error(raw && raw.trim() ? ('Risposta chunk non JSON: ' + raw.slice(0,180)) : 'Risposta chunk vuota.'); }\n                if (!res || !res.success) throw new Error(res && res.data && res.data.message ? res.data.message : 'Chunk non accettato dal server.');\n                return res.data || {};\n            });\n        });\n        return Promise.race([fetchPromise, timeoutPromise]).then(function(data){\n            try { if (timeoutId) clearTimeout(timeoutId); } catch(e) {}\n            try { if (state.currentUploadAbort821 === controller) state.currentUploadAbort821 = null; } catch(e) {}\n            return data;\n        }).catch(function(err){\n            try { if (timeoutId) clearTimeout(timeoutId); } catch(e) {}\n            try { if (state.currentUploadAbort821 === controller) state.currentUploadAbort821 = null; } catch(e) {}\n            var m = err && err.name === 'AbortError' ? 'Timeout invio chunk: il server non ha risposto.' : (err && err.message ? err.message : String(err));\n            uploadLog821('chunk_error_825', 'Errore chunk', 'chunk=' + chunkIndex + ' ' + m);\n            throw new Error(m);\n        });\n    }\n    function uploadChunkedDirect825(data, file, previousError){\n        if (!file || !(file instanceof File)) return Promise.reject(previousError || new Error('File non disponibile per upload a blocchi.'));\n        if (file.size && file.size > 25 * 1024 * 1024) return Promise.reject(previousError || new Error('File troppo grande per upload a blocchi.'));\n        if (state.cancelRequested808) return Promise.reject(new Error('Caricamento annullato.'));\n        var chunkSize = 32 * 1024;\n        var total = Math.max(1, Math.ceil((file.size || 0) \/ chunkSize));\n        var uploadId = 'u' + Date.now() + '_' + Math.random().toString(36).slice(2, 10);\n        var urls = uploadUrlListChunk825();\n        var endpoint = urls[0] || '';\n        uploadLog821('chunk_start_825', 'Avvio upload diretto a blocchi', 'chunkSize=' + chunkSize + ' total=' + total + ' endpoint=' + endpoint);\n        setUploadActivity814(12, '12% - Avvio upload a blocchi', 'Caricamento file', 'Invio il file a blocchi piccoli per evitare timeout e blocchi del browser.', file);\n        var baseParams = {};\n        Object.keys(data || {}).forEach(function(k){ baseParams[k] = data[k] == null ? '' : data[k]; });\n        baseParams.action = 'ti_ai_ss_b64_chunk_upload_825';\n        baseParams.ti_action = 'ti_ai_ss_b64_chunk_upload_825';\n        baseParams.upload_id = uploadId;\n        baseParams.file_name = file.name || 'upload.bin';\n        baseParams.file_type = file.type || '';\n        baseParams.file_size = String(file.size || 0);\n        baseParams.upload_cleanup_token = currentUploadCleanupToken859(true);\n        baseParams.guided_upload_target = state.target || '';\n        try {\n            if (isBasicUploadUser()) {\n                baseParams.source_policy = 'basic_authorized_path';\n                baseParams.source_label = sourceLabelForPayload(file);\n                baseParams.client_path_unavailable = '1';\n                baseParams.client_relative_path = clientRelativePath(file);\n                baseParams.authorized_path = sourceLabelForPayload(file);\n            } else {\n                baseParams.source_policy = 'privileged_upload';\n                baseParams.source_label = sourceLabelForPayload(file);\n                baseParams.client_path_unavailable = clientRelativePath(file) ? '0' : '1';\n                baseParams.client_relative_path = clientRelativePath(file);\n                baseParams.authorized_path = sourceLabelForPayload(file);\n            }\n        } catch(e) {}\n        function sendOne(i){\n            if (state.cancelRequested808) return Promise.reject(new Error('Caricamento annullato.'));\n            if (i >= total) return Promise.reject(new Error('Indice chunk oltre limite.'));\n            var start = i * chunkSize;\n            var end = Math.min(file.size || 0, start + chunkSize);\n            var blob = file.slice(start, end);\n            var pctRead = Math.max(15, Math.min(75, Math.round(15 + (i \/ total) * 60)));\n            setUploadActivity814(pctRead, pctRead + '% - Lettura chunk ' + (i + 1) + '\/' + total, 'Caricamento file', 'Lettura e invio del file a blocchi. Non chiudere la pagina.', file);\n            uploadLog821('chunk_read_start_825', 'Lettura chunk', 'chunk=' + i + '\/' + total + ' bytes=' + start + '-' + end);\n            return readSliceBase64825(blob, (i + 1) + '\/' + total, 7000).then(function(chunkB64){\n                var pctSend = Math.max(20, Math.min(82, Math.round(20 + (i \/ total) * 62)));\n                setUploadActivity814(pctSend, pctSend + '% - Invio chunk ' + (i + 1) + '\/' + total, 'Caricamento file', 'Invio chunk al server. Attendo conferma immediata.', file);\n                var params = {};\n                Object.keys(baseParams).forEach(function(k){ params[k] = baseParams[k]; });\n                params.chunk_index = String(i);\n                params.chunk_total = String(total);\n                params.chunk_b64 = chunkB64;\n                function tryEndpoint(ei){\n                    if (ei >= urls.length) return Promise.reject(new Error('Nessun endpoint upload chunk disponibile.'));\n                    return postChunkPayload825(urls[ei], params, i).catch(function(err){\n                        uploadLog821('chunk_endpoint_error_825', 'Endpoint chunk fallito', 'chunk=' + i + ' endpoint=' + ei + ' ' + (err && err.message ? err.message : err));\n                        if (ei + 1 < urls.length) return tryEndpoint(ei + 1);\n                        throw err;\n                    });\n                }\n                return tryEndpoint(0).then(function(res){\n                    uploadLog821('chunk_ok_825', 'Chunk confermato dal server', 'chunk=' + i + ' partial=' + (res && res.partial ? 1 : 0) + ' received=' + (res && res.received ? res.received : ''));\n                    if (i + 1 >= total) {\n                        setUploadActivity814(92, '92% - File ricevuto dal server', 'Caricamento file', 'File assemblato dal server. Preparo risposta AI\/opzioni.', file);\n                        return res || {};\n                    }\n                    return sendOne(i + 1);\n                });\n            });\n        }\n        return sendOne(0).catch(function(err){\n            uploadLog821('chunk_upload_failed_825', 'Upload a blocchi fallito', err && err.message ? err.message : err);\n            throw err;\n        });\n    }\n    function postB64Payload818(url, params){\n        uploadLog821('post_prepare', 'Preparazione richiesta HTTP diretta', 'url=' + url + ' keys=' + Object.keys(params || {}).join(','));\n        var body = '';\n        try {\n            var t0 = Date.now();\n            body = Object.keys(params || {}).map(function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(params[k] == null ? '' : String(params[k])); }).join('&');\n            uploadLog821('post_body_ready', 'Payload codificato', 'chars=' + body.length + ' ms=' + (Date.now() - t0));\n        } catch(e821body) {\n            uploadLog821('post_body_error', 'Errore codifica payload', e821body && e821body.message ? e821body.message : e821body);\n            return Promise.reject(new Error('Errore preparazione invio diretto: ' + (e821body && e821body.message ? e821body.message : e821body)));\n        }\n        var controller = null;\n        var timeoutId = null;\n        var hardReject = null;\n        try { if (window.AbortController) { controller = new AbortController(); state.currentUploadAbort821 = controller; } } catch(e) { controller = null; }\n        var timeoutPromise = new Promise(function(_, reject){\n            hardReject = reject;\n            timeoutId = setTimeout(function(){\n                try { uploadLog821('post_timeout', 'Timeout reale invio diretto', 'url=' + url); } catch(e) {}\n                try { if (controller) controller.abort(); } catch(e) {}\n                reject(new Error('Timeout invio diretto: nessuna risposta dal server entro 24 secondi. Endpoint: ' + url));\n            }, 24000);\n        });\n        setUploadActivity814(Math.max(20, parseInt(state.progressPercent814 || 0, 10) || 0), '20% - Invio diretto in corso', 'Caricamento file', 'Richiesta inviata al server. Attendo risposta con timeout reale.', state.file || null);\n        var fetchPromise = fetch(url, {method:'POST', credentials:'same-origin', cache:'no-store', headers:{'Content-Type':'application\/x-www-form-urlencoded; charset=UTF-8'}, body:body, signal: controller ? controller.signal : undefined}).then(function(resp){\n            uploadLog821('post_response_headers', 'Risposta HTTP ricevuta', 'status=' + resp.status + ' ok=' + resp.ok + ' url=' + url);\n            return resp.text().then(function(raw){\n                uploadLog821('post_response_text', 'Testo risposta letto', 'chars=' + String(raw || '').length + ' preview=' + String(raw || '').slice(0,220));\n                if (!resp.ok) throw new Error('HTTP ' + resp.status + ': ' + String(raw || '').slice(0, 220));\n                var res = null; try { res = JSON.parse(raw); } catch(e) { throw new Error(raw && raw.trim() ? ('Risposta server non JSON canale alternativo: ' + raw.slice(0, 220)) : 'Risposta server vuota canale alternativo.'); }\n                if (!res || !res.success) throw new Error(res && res.data && res.data.message ? res.data.message : 'Upload alternativo non riuscito.');\n                uploadLog821('post_json_ok', 'JSON upload valido', 'campi=' + Object.keys(res.data || {}).join(','));\n                return res.data || {};\n            });\n        });\n        return Promise.race([fetchPromise, timeoutPromise]).then(function(data){\n            try { if (timeoutId) clearTimeout(timeoutId); } catch(e) {}\n            try { if (state.currentUploadAbort821 === controller) state.currentUploadAbort821 = null; } catch(e) {}\n            return data;\n        }).catch(function(err){\n            try { if (timeoutId) clearTimeout(timeoutId); } catch(e) {}\n            try { if (state.currentUploadAbort821 === controller) state.currentUploadAbort821 = null; } catch(e) {}\n            var m = err && err.name === 'AbortError' ? 'Timeout invio diretto: il server non ha risposto al canale anti-blocco.' : (err && err.message ? err.message : String(err));\n            uploadLog821('post_error', 'Errore invio diretto', m);\n            throw new Error(m);\n        });\n    }\n    function b64UploadDirect818(data, file, previousError){\n        if (!file || !(file instanceof File)) return Promise.reject(previousError || new Error('File non disponibile per canale alternativo.'));\n        if (file.size && file.size > 25 * 1024 * 1024) return Promise.reject(previousError || new Error('File troppo grande per canale alternativo anti-timeout.'));\n        if (state.cancelRequested808) return Promise.reject(new Error('Caricamento annullato.'));\n        uploadLog821('b64_start', 'Avvio canale diretto anti-blocco', 'fileSize=' + (file && file.size ? file.size : 0));\n        setUploadActivity814(10, '10% - Preparo upload a blocchi', 'Caricamento file', 'Uso il canale a blocchi: ogni blocco deve avere risposta server immediata.', file);\n        return uploadChunkedDirect825(data, file, previousError).catch(function(chunkErr){\n            uploadLog821('b64_fallback_full_825', 'Fallback al canale base64 completo', chunkErr && chunkErr.message ? chunkErr.message : chunkErr);\n            setUploadActivity814(15, '15% - Fallback lettura file locale completa', 'Caricamento file', 'Il canale a blocchi non ha risposto. Provo il metodo alternativo completo.', file);\n            return readFileAsBase64818(file).then(function(b64){\n            uploadLog821('local_read_ok', 'File letto localmente', 'base64_chars=' + (b64 ? String(b64.length) : 0));\n            var params = {};\n            Object.keys(data || {}).forEach(function(k){ params[k] = data[k] == null ? '' : data[k]; });\n            params.action = 'ti_ai_ss_b64_upload_818';\n            params.ti_action = 'ti_ai_ss_b64_upload_818';\n            params.file_name = file.name || 'upload.bin';\n            params.file_type = file.type || '';\n            params.file_size = String(file.size || 0);\n            params.upload_cleanup_token = currentUploadCleanupToken859(true);\n            params.guided_upload_target = state.target || '';\n            params.file_b64 = b64;\n            try {\n                if (isBasicUploadUser()) {\n                    params.source_policy = 'basic_authorized_path';\n                    params.source_label = sourceLabelForPayload(file);\n                    params.client_path_unavailable = '1';\n                    params.client_relative_path = clientRelativePath(file);\n                    params.authorized_path = sourceLabelForPayload(file);\n                } else {\n                    params.source_policy = 'privileged_upload';\n                    params.source_label = sourceLabelForPayload(file);\n                    params.client_path_unavailable = clientRelativePath(file) ? '0' : '1';\n                    params.client_relative_path = clientRelativePath(file);\n                    params.authorized_path = sourceLabelForPayload(file);\n                }\n            } catch(e) {}\n            var urls = uploadUrlListB64818();\n            function attemptB64(i){\n                if (i >= urls.length) return Promise.reject(previousError || new Error('Canale alternativo non disponibile.'));\n                state.uploadEndpoint817 = urls[i] || '';\n                uploadLog821('endpoint_attempt', 'Tentativo endpoint upload diretto', 'index=' + i + ' url=' + (urls[i] || ''));\n                setUploadActivity814(Math.min(55, 20 + i * 8), (20 + i * 8) + '% - Invio diretto file', 'Caricamento file', 'Invio diretto al server. Attendi risposta AI.', file);\n                return postB64Payload818(urls[i], params).catch(function(err){ uploadLog821('endpoint_error', 'Endpoint fallito', (err && err.message ? err.message : err)); if (i + 1 < urls.length) return attemptB64(i + 1); throw err; });\n            }\n            return attemptB64(0).then(function(res){\n                setUploadActivity814(92, '92% - File ricevuto dal server', 'Caricamento file', 'File ricevuto. Preparo risposta AI\/opzioni utente.', file);\n                return res;\n            });\n        });\n        });\n    }\n    function setModalText(html, err){\n        var el = byId('ti-direct-upload-text-621');\n        if (!el) return;\n        el.className = 'ti-direct-upload-text-621' + (err ? ' ti-direct-error-621' : '');\n        el.innerHTML = html || '';\n    }\n    function setFileLine(file){\n        var el = byId('ti-direct-upload-file-621');\n        if (!el) return;\n        if (!file) { el.style.display = 'none'; el.innerHTML = ''; return; }\n        var size = file.size ? (' - ' + Math.round(file.size \/ 1024) + ' KB') : '';\n        el.style.display = 'block';\n        el.innerHTML = '<b>File:<\/b> ' + esc(file.name || 'upload') + esc(size);\n    }\n    function currentDb642(){ return (window.currentDbData && typeof window.currentDbData === 'object') ? window.currentDbData : {}; }\n    function configValue642(keys, def){\n        var data = currentDb642(), cfg = null;\n        try { cfg = data && data.Tabelle ? data.Tabelle.Config : null; } catch(e) { cfg = null; }\n        var list = Array.isArray(cfg) ? cfg : (cfg && typeof cfg === 'object' ? [cfg] : []);\n        keys = keys || [];\n        for (var i=0;i<list.length;i++) {\n            var row = list[i] || {};\n            for (var k=0;k<keys.length;k++) { if (row[keys[k]] !== undefined && trim(row[keys[k]]) !== '') return trim(row[keys[k]]); }\n        }\n        return def || '';\n    }\n    function friendlyCompanyFallback658(){\n        var v = trim(dbName() || 'corrente');\n        v = v.replace(\/\\.json$\/i, '').replace(\/[_\\-]+\/g, ' ').trim();\n        return v || 'corrente';\n    }\n    function companyLabel642(){ return state.serverCompanyLabel659 || configValue642(['Ditta','Ragione sociale','Ragione Sociale','Azienda','Nome ditta','Nome'], friendlyCompanyFallback658()); }\n    function instructionRows642(){\n        var out = [], seen = {};\n        function addRow(r){\n            if (!r || typeof r !== 'object') return;\n            var t = rowTxt642(r);\n            var p = extractAuthorizedPath642(r);\n            if (!p && !\/(Origine\\\/path file autorizzata|Path\\s+assoluta\\s+autorizzata|Path\\s+autorizzata|Origine\\s+autorizzata|caricamento file utenti generici|utenti generici\\\/base)\/i.test(t)) return;\n            var sig = ''; try { sig = JSON.stringify(r); } catch(e) { sig = String(out.length); }\n            if (seen[sig]) return; seen[sig] = true; out.push(r);\n        }\n        function pushRows(rows){\n            if (!rows || typeof rows !== 'object') return;\n            var wraps = ['Righe','Rows','records','Records','Dati','Valori'];\n            for (var w=0; w<wraps.length; w++) { if (rows[wraps[w]] && typeof rows[wraps[w]] === 'object') { pushRows(rows[wraps[w]]); return; } }\n            if (Array.isArray(rows)) { rows.forEach(function(r){ if (r && typeof r === 'object') addRow(r); }); return; }\n            addRow(rows); Object.keys(rows).forEach(function(k){ if (rows[k] && typeof rows[k] === 'object') addRow(rows[k]); });\n        }\n        try {\n            var data = currentDb642() || {};\n            var tabelle = data.Tabelle || {};\n            Object.keys(tabelle).forEach(function(name){\n                var norm = String(name || '').toLowerCase().replace(\/[\\s_\\-]\/g, '');\n                if (norm === 'istruzioni' || norm === 'istruzione') pushRows(tabelle[name]);\n            });\n            ['Istruzioni','istruzioni','Istruzione','istruzione'].forEach(function(k){ if (data[k]) pushRows(data[k]); });\n            (function walk(n, depth){ if (!n || typeof n !== 'object' || depth > 7) return; addRow(n); Object.keys(n).forEach(function(k){ if (n[k] && typeof n[k] === 'object') walk(n[k], depth+1); }); })(data,0);\n        } catch(e) {}\n        return out;\n    }\n    function rowTxt642(row){\n        var keys = ['Istruzione','Istruzioni','Testo','Regola','Descrizione','Nota','Path assoluta autorizzata','Path autorizzata','Origine autorizzata','Path','Origine','Sorgente'];\n        var out = '';\n        row = row || {};\n        keys.forEach(function(k){ if (row[k] !== undefined && trim(row[k]) !== '') out += ' ' + trim(row[k]); });\n        return out;\n    }\n    function extractAbsoluteUploadPath657(v){\n        v = trim(v || '').replace(\/[\\u0000-\\u001F\\u007F]+\/g, ' ').replace(\/^[\\'\\\"]+|[\\'\\\"]+$\/g, '').trim();\n        if (!v) return '';\n        var probe = v.replace(\/\\\\\\\\\/g, '\\\\').replace(\/\\\\\/g, '\\\\').replace(\/\\\/\\\/\/g, '\/');\n        var m = probe.match(\/(?:^|[^A-Za-z0-9])([A-Za-z])\\s*:\\s*[\\\\\\\/]*(?:$|[\\s\\.;,\\)\\]\\}])\/);\n        if (m && m[1]) return m[1].toUpperCase() + ':\\\\';\n        m = probe.match(\/(?:^|[^A-Za-z0-9])([A-Za-z])\\s*:\\s*[\\\\\\\/]([^\\r\\n;]*)\/);\n        if (m && m[1]) {\n            var rest = String(m[2] || '').replace(\/[\\s\\.;,\\)\\]\\}]+$\/g, '').trim();\n            return m[1].toUpperCase() + ':\\\\' + rest.replace(\/\\\/\/g, '\\\\').replace(\/^\\\\+\/, '');\n        }\n        m = v.match(\/(\\\/Volumes\\\/[^\\r\\n;<>]+)\/);\n        if (m && m[1]) return String(m[1]).replace(\/[\\s\\.;,\\)\\]\\}]+$\/g, '').trim();\n        return '';\n    }\n    function cleanUploadPath653(v){\n        v = trim(v || '');\n        v = v.replace(\/[\\u0000-\\u001F\\u007F]+\/g, ' ').replace(\/^[\\'\\\"]+|[\\'\\\"]+$\/g, '').trim();\n        var extracted = extractAbsoluteUploadPath657(v);\n        if (extracted) return extracted;\n        for (var i=0; i<4; i++) {\n            var old = v;\n            v = v.replace(\/^[\\\\\\\/]+([A-Za-z]:[\\\\\\\/]?)\/, '$1');\n            v = v.replace(\/^([A-Za-z]):[\\\\\\\/]+$\/, function(_, d){ return d.toUpperCase() + ':\\\\'; });\n            v = v.replace(\/^([A-Za-z]):$\/, function(_, d){ return d.toUpperCase() + ':\\\\'; });\n            if (v === old) break;\n        }\n        v = v.replace(\/^([A-Za-z]):[\\\\\\\/]?(.*)$\/, function(_, d, rest){ return d.toUpperCase() + ':\\\\' + String(rest || '').replace(\/\\\/\/g, '\\\\').replace(\/^\\\\+\/, ''); });\n        return v;\n    }\n    function extractAuthorizedPath642(row){\n        row = row || {};\n        var keys = ['Path assoluta autorizzata','Path Assoluta Autorizzata','Percorso assoluto autorizzato','Percorso Assoluto Autorizzato','Path autorizzata','Path Autorizzata','Origine autorizzata','Origine Autorizzata','Path','Origine','Sorgente','authorized_path','source_label','absolute_path'];\n        for (var i=0;i<keys.length;i++) { if (row[keys[i]] !== undefined && trim(row[keys[i]]) !== '') return cleanUploadPath653(trim(row[keys[i]])); }\n        var t = rowTxt642(row), m = t.match(\/(?:Path\\s+assoluta|Percorso\\s+assoluto|Path|Origine|Sorgente)\\s+autorizzat[ao]\\s*:\\s*([^.;\\r\\n]+)\/i);\n        return m && m[1] ? cleanUploadPath653(trim(m[1])).slice(0,240) : '';\n    }\n    function isSysUploadRule642(row){\n        row = row || {};\n        var t = rowTxt642(row);\n        var p = extractAuthorizedPath642(row);\n        var isUpload = !!p || \/Origini\\\/path file consentite utenti generici\\\/base|Origini file consentite utenti generici\\\/base|Origine\\\/path file autorizzata|path file consentite|caricamento file utenti generici|Path autorizzata:|Origine autorizzata:|Sorgente autorizzata:\/i.test(t);\n        return isUpload;\n    }\n    function firstAuthorizedSourcePath642(){\n        var rows = instructionRows642();\n        for (var i=0;i<rows.length;i++) { if (isSysUploadRule642(rows[i])) { var p = extractAuthorizedPath642(rows[i]); if (p && isAbsoluteUploadPath651(p)) return p; } }\n        if (state.serverAuthorizedPath659 && isAbsoluteUploadPath651(state.serverAuthorizedPath659)) return state.serverAuthorizedPath659;\n        try { var sp = sessionStorage.getItem('tiAuthorizedUploadPath659') || ''; if (sp && isAbsoluteUploadPath651(sp)) { state.serverAuthorizedPath659 = sp; return sp; } } catch(e) {}\n        return '';\n    }\n    function displayDittaLabel658(v){\n        v = trim(v || companyLabel642() || 'corrente');\n        v = v.replace(\/\\.json$\/i, '').replace(\/[_\\-]+\/g, ' ').trim();\n        return v || 'corrente';\n    }\n    function missingAuthorizedSourceMessage644(){ return 'Errore caricamento file: Nessuna origine\/path file risulta disponibile per la ditta ' + displayDittaLabel658(companyLabel642()) + '.'; }\nfunction verifiedPathUnavailableMessage651(){\n        var p = firstAuthorizedSourcePath642();\n        if (p) return 'Origine\/path autorizzata configurata per la ditta: ' + p + '. Verr\u00e0 aperta la selezione file di sistema: scegli il file solo dal supporto autorizzato indicato.';\n        return missingAuthorizedSourceMessage644();\n    }\nfunction jsNormPath651(v){\n        v = cleanUploadPath653(trim(v || '')).replace(\/\\\\\/g, '\/').replace(\/\\\/+\/g, '\/');\n        if (\/^[A-Za-z]:$\/.test(v)) v += '\/';\n        if (\/^[A-Za-z]:\\\/$\/.test(v)) return v.toLowerCase();\n        return v.replace(\/\\\/+$\/,'').toLowerCase();\n    }\n    function isAbsoluteUploadPath651(v){\n        v = trim(v || '');\n        return !!(\/^[A-Za-z]:[\\\\\\\\\/]\/.test(v) || \/^[A-Za-z]:$\/.test(v) || \/^\\\\\\\\\/.test(v) || \/^\\\/\\\/\/.test(v) || \/^\\\/\/.test(v));\n    }\n    function strictUploadPathMatch651(allowed, declared){\n        var a = jsNormPath651(allowed), d = jsNormPath651(declared);\n        if (!a || !d) return false;\n        if (a === d) return true;\n        var ap = \/\\\/$\/.test(a) ? a : (a + '\/');\n        return d.indexOf(ap) === 0;\n    }\n        function showMissingAuthorizedSource644(kind){\n        var msg = missingAuthorizedSourceMessage644();\n        setInlineStatus(isManager(kind) ? 'manager' : 'main', msg, true);\n        try { if (window.tiAlert) window.tiAlert(msg); else alert(msg); } catch(e) { try { alert(msg); } catch(ee) {} }\n        return false;\n    }\n    function setSourceMapVisible(show){\n        var box = byId('ti-direct-upload-map-629');\n        if (!box) return;\n        var basic = isBasicUploadUser();\n        \/\/ 30.9.670: per utenti privilegiati l'associazione\/caricamento diretto non deve aprire la sezione di definizione path autorizzata.\n        \/\/ La mappatura path resta disabilitata salvo esplicita riabilitazione tecnica separata.\n        if (!basic && window.__tiEnablePrivilegedUploadSourceMapping !== true) show = false;\n        box.style.display = show ? 'block' : 'none';\n        var cb = byId('ti-direct-upload-map-check-629'), inp = byId('ti-direct-upload-source-label-629'), lab = byId('ti-direct-upload-map-label-629'), labTxt = byId('ti-direct-upload-map-label-text-629'), saveBtn651 = byId('ti-direct-upload-save-source-651'), pickBtn655 = byId('ti-direct-upload-pick-path-655');\n        try { if (box) { box.style.setProperty('background','#000','important'); box.style.setProperty('color','#fff','important'); } if (lab) { lab.style.setProperty('background','#000','important'); lab.style.setProperty('color','#fff','important'); } if (labTxt) { labTxt.style.setProperty('background','#000','important'); labTxt.style.setProperty('color','#fff','important'); } if (inp) { inp.style.setProperty('background','#000','important'); inp.style.setProperty('color','#fff','important'); } } catch(e) {}\n        if (basic) {\n            if (saveBtn651) saveBtn651.style.display = 'none';\n            if (pickBtn655) pickBtn655.style.display = 'none';\n            var first = firstAuthorizedSourcePath642();\n            if (cb) { cb.checked = false; cb.style.display = 'none'; }\n            if (lab) lab.style.display = 'block';\n            if (labTxt) labTxt.textContent = first ? ('Origine\/path autorizzata dalla ditta: ' + first) : ('Nessuna origine\/path file risulta disponibile per la ditta ' + displayDittaLabel658(companyLabel642()) + '.');\n            if (inp) { inp.value = first || ''; inp.style.display = 'none'; inp.disabled = true; inp.setAttribute('aria-hidden','true'); }\n        } else {\n            if (saveBtn651) saveBtn651.style.display = show ? 'inline-flex' : 'none';\n            if (pickBtn655) pickBtn655.style.display = show ? 'inline-flex' : 'none';\n            if (cb) cb.style.display = '';\n            if (lab) lab.style.display = 'block';\n            if (labTxt) labTxt.textContent = 'Salva questa origine\/path come istruzione di caricamento consentita';\n            if (inp) { inp.style.display = 'block'; inp.disabled = false; inp.removeAttribute('aria-hidden'); inp.placeholder = 'Esempio path assoluta: F:\\\\ oppure \/Volumes\/ChiaveUSB'; }\n        }\n        if (!show) {\n            if (cb) cb.checked = false;\n            if (inp) { inp.value = ''; inp.removeAttribute('data-authorized-path'); }\n            if (saveBtn651) saveBtn651.style.display = 'none';\n            if (pickBtn655) pickBtn655.style.display = 'none';\n        }\n    }\n    function sourceMapEnabled(){\n        if (!isBasicUploadUser() && window.__tiEnablePrivilegedUploadSourceMapping !== true) return false;\n        var cb = byId('ti-direct-upload-map-check-629');\n        return !!(cb && cb.checked);\n    }\n    function sourceMapLabel(){\n        var inp = byId('ti-direct-upload-source-label-629');\n        var parts = [];\n        if (inp) {\n            parts.push(inp.value || '');\n            parts.push(inp.getAttribute('data-authorized-path') || '');\n        }\n        try { parts.push(state.authorizedPath657 || state.authorizedPath656 || window.__tiLastAuthorizedUploadPath657 || window.__tiLastAuthorizedUploadPath656 || ''); } catch(e) {}\n        try { var saveBtn = byId('ti-direct-upload-save-source-651'); if (saveBtn) parts.push(saveBtn.getAttribute('data-authorized-path') || ''); } catch(e2) {}\n        try { var txtEl = byId('ti-direct-upload-text-621'); if (txtEl) parts.push(txtEl.textContent || txtEl.innerText || ''); } catch(e3) {}\n        for (var i=0; i<parts.length; i++) { var v = cleanUploadPath653(trim(parts[i] || '')); if (v && isAbsoluteUploadPath651(v)) return v; }\n        var raw = cleanUploadPath653(trim(parts.join(' ') || ''));\n        if (raw && isAbsoluteUploadPath651(raw)) return raw;\n        if (isBasicUploadUser()) return firstAuthorizedSourcePath642();\n        return raw || '';\n    }\n    function setSourceMapLabel(v){ var inp = byId('ti-direct-upload-source-label-629'); if (inp) inp.value = cleanUploadPath653(trim(v)); }\n    function proposedAuthorizedPath641(file){\n        var typed = sourceMapLabel();\n        if (typed) return typed;\n        var rel = clientRelativePath(file);\n        if (rel && isAbsoluteUploadPath651(rel)) return rel;\n        return '';\n    }\n    function confirmPrivilegedSourcePath641(file){\n        if (isBasicUploadUser() || !sourceMapEnabled()) return false;\n        var proposed = proposedAuthorizedPath641(file);\n        setSourceMapLabel(proposed);\n        state.file = file;\n        state.confirmingPath = true;\n        setFileLine(file);\n        setModalText('Conferma la path assoluta da autorizzare per gli utenti generici\/base.<br><br><b>Path assoluta autorizzata:<\/b><br>' + esc(proposed || 'Inserisci una path assoluta nel campo sotto, ad esempio F:\\\\') + '<br><br>Indica o conferma la path assoluta, poi premi <b>Conferma path e carica<\/b>. L\\'istruzione salvata nel database conterr\u00e0 esplicitamente questa path autorizzata.', false);\n        showStartButton(true, 'Conferma path e carica');\n        return true;\n    }\n        function sourceLabelForPayload(file){\n        if (isBasicUploadUser()) return firstAuthorizedSourcePath642() || sourceMapLabel();\n        return sourceMapLabel() || 'origine caricamento autorizzata da utente abilitato';\n    }\n    function clientRelativePath(file){\n        try { return file && file.webkitRelativePath ? String(file.webkitRelativePath) : ''; } catch(e) {}\n        return '';\n    }\n    function showStartButton(show, text){\n        var btn = byId('ti-direct-upload-start-621');\n        if (!btn) return;\n        btn.style.display = show ? 'inline-flex' : 'none';\n        btn.disabled = false;\n        btn.textContent = text || 'Avvia caricamento';\n    }\n    function uploadFileName792(file){\n        try { return (file && file.name) ? String(file.name) : 'file selezionato'; } catch(e) {}\n        return 'file selezionato';\n    }\n    function showUploadWaitPopup792(kind, target, file){\n        var name = uploadFileName792(file);\n        var isChat = !isManager(kind) && (!target || target === 'chat');\n        var title = isChat ? 'Carica file AI' : 'Caricamento file';\n        var sub = ''; \/\/ 30.9.861: nessun commento sotto la barra del popup progressivo.\n        try {\n            if (typeof window.showProgressPopup === 'function') {\n                window.showProgressPopup(title, sub, {startPercent:3, targetPercent:isChat ? 91 : 96, stepMs:420, cancellable:true, currentItem:name});\n            } else if (typeof window.tiAlert === 'function') {\n                window.tiAlert('File scelto: ' + name + '. Attendi l esito del caricamento.');\n            }\n        } catch(e) {}\n        try { if (window.addMsg && isChat) window.addMsg('ai', '\ud83d\udcce File scelto: ' + name + '. Caricamento e analisi AI in corso, attendi l esito.'); } catch(e2) {}\n    }\n    function updateUploadWaitPopup792(percent, title, subtitle, item){\n        try { if (typeof window.updateProgressPopup === 'function') window.updateProgressPopup(percent, title || 'Caricamento file', '', item || ''); } catch(e) {}\n    }\n    function finishUploadWaitPopup792(success, title, message, notify){\n        try {\n            if (typeof window.hideProgressPopup === 'function') {\n                window.hideProgressPopup(success !== false, title || (success === false ? 'Upload non riuscito' : 'Upload completato'), {notifyUser: notify !== false, successMessage: message || title || 'Upload completato.', errorMessage: message || title || 'Upload non riuscito.'});\n            } else if (notify !== false && typeof window.tiAlert === 'function') {\n                window.tiAlert(message || title || (success === false ? 'Upload non riuscito.' : 'Upload completato.'));\n            }\n        } catch(e) {}\n    }\n    function addUploadAiStatus861(kind, target, message){\n        try {\n            if (isManager(kind)) return;\n            target = trim(target || state.target || 'chat');\n            message = trim(message || '');\n            if (!message) return;\n            var key = target + '|' + message;\n            var now = Date.now();\n            if (state.lastAiUploadStatus861 === key && (now - (state.lastAiUploadStatusAt861 || 0)) < 2500) return;\n            state.lastAiUploadStatus861 = key;\n            state.lastAiUploadStatusAt861 = now;\n            if (target === 'config-bot-file') {\n                var r = byId('ti-conf-ai-reply');\n                if (r) { r.style.display = 'block'; r.innerHTML = esc(message).replace(\/\\n\/g, '<br>'); return; }\n            }\n            if (typeof window.addMsg === 'function') { window.addMsg('ai', message); return; }\n            var chat = byId('ti-chat') || byId('ti-chat-box');\n            if (chat) {\n                var b = document.createElement('div');\n                b.className = 'ti-bubble ai ti-upload-ai-status-861';\n                b.textContent = message;\n                chat.appendChild(b);\n            }\n        } catch(e861msg) {}\n    }\n\n    function checkAuthorizedSourceServer659(kind, target){\n        var db = dbName();\n        if (!db || db === 'NEW_DB' || db === 'Global') { showMissingAuthorizedSource644(kind); return false; }\n        setInlineStatus(isManager(kind) ? 'manager' : 'main', 'Verifica origine\/path autorizzata...', false);\n        var fd = new FormData();\n        fd.append('action', 'ti_ai_ss_upload_source_check_664');\n        fd.append('ti_action', 'ti_ai_ss_upload_source_check_664');\n        fd.append('db', db);\n        var urls = ajaxUrlList();\n        if (!urls || !urls.length) { showMissingAuthorizedSource644(kind); return false; }\n        function attempt(i){\n            return fetch(urls[i], {method:'POST', body:fd, credentials:'same-origin'}).then(function(r){ return r.text().then(function(raw){\n                var res = null; try { res = JSON.parse(raw); } catch(e) { throw new Error(raw && raw.trim() ? ('Risposta server non JSON: ' + raw.slice(0,180)) : 'Risposta server vuota\/non JSON durante il controllo origine\/path. Verifica che il plugin attivo sia aggiornato e che admin-ajax.php sia raggiungibile.'); }\n                if (!res || !res.success) throw new Error(res && res.data && res.data.message ? res.data.message : 'Controllo origine\/path non riuscito.');\n                return res.data || {};\n            }); }).catch(function(err){ if (i + 1 < urls.length) return attempt(i+1); throw err; });\n        }\n        attempt(0).then(function(data){\n            var p = cleanUploadPath653(data.default_source || data.matched || data.authorized_path || '');\n            if (data.company_label) state.serverCompanyLabel659 = displayDittaLabel658(String(data.company_label));\n            if (p && isAbsoluteUploadPath651(p)) {\n                state.serverAuthorizedPath659 = p;\n                try { sessionStorage.setItem('tiAuthorizedUploadPath659', p); } catch(e) {}\n                showConfirm(kind, target || (isManager(kind) ? 'manager' : 'chat'));\n            } else {\n                var msg = data.message || missingAuthorizedSourceMessage644();\n                setInlineStatus(isManager(kind) ? 'manager' : 'main', sanitizeNonPrivilegedMessage634(msg), true);\n                try { if (window.tiAlert) window.tiAlert(sanitizeNonPrivilegedMessage634(msg)); else alert(sanitizeNonPrivilegedMessage634(msg)); } catch(e) {}\n            }\n        }).catch(function(err){\n            var msg = sanitizeNonPrivilegedMessage634(err && err.message ? err.message : missingAuthorizedSourceMessage644());\n            setInlineStatus(isManager(kind) ? 'manager' : 'main', msg, true);\n            try { if (window.tiAlert) window.tiAlert(msg); else alert(msg); } catch(e) {}\n        });\n        return false;\n    }\n\n    function xhrAlive810(){\n        try { return !!(state.xhr && state.xhr.readyState && state.xhr.readyState !== 4); } catch(e) {}\n        return false;\n    }\n    function resetUploadState810(reason){\n        try { clearUploadWatchdog812(); } catch(e812w) {}\n        try { if (state.xhr && state.xhr.readyState !== 4) state.xhr.abort();\n                try { if (state.currentUploadAbort821) state.currentUploadAbort821.abort(); } catch(e821a) {}\n                try { uploadLog821('watchdog_timeout', 'Watchdog 90s: processo interrotto e form sbloccata', 'progress=' + (state.progressLabel814 || '')); } catch(e821l) {}\n                state.busy = false;\n                state.xhr = null;\n                state.currentUploadAbort821 = null;\n                state.confirmingUpload = true;\n                showStartButton(true, 'Riprova stesso file');\n                setProgress(0, '0% - Timeout watchdog, pronto per riprova');\n                try { document.body.classList.remove('ti-upload-busy','ti-ai-upload-busy','ti-loader-active'); } catch(e821b) {} } catch(e) {}\n        state.busy = false;\n        state.xhr = null;\n        state.file = null;\n        state.confirmingPath = false;\n        state.confirmingUpload = false;\n        state.autoCloseUploadModalAfterSelect791 = false;\n        state.popupClosedAfterSelect791 = false;\n        state.cancelRequested808 = false;\n        state.timeoutRequested812 = false;\n        state.uploadStartedAt812 = 0;\n        state.uploadLastProgressAt812 = 0;\n        state.exitReason808 = reason || '';\n        try { window.__tiGuidedUploadCancelled808 = 0; } catch(e0) {}\n        try { document.body.classList.remove('ti-upload-busy','ti-ai-upload-busy','ti-loader-active'); } catch(e1) {}\n    }\n    function clearStaleUploadLock810(reason){\n        if (!state.busy) return;\n        if (xhrAlive810()) {\n            if (uploadAge812 && uploadAge812() > 90000) {\n                try { state.timeoutRequested812 = true; state.xhr.abort(); } catch(e812) {}\n            } else {\n                return;\n            }\n        }\n        resetUploadState810(reason || 'stale_lock');\n        try { setInlineStatus(state.kind || 'main', 'Caricamento precedente sbloccato. Puoi riprovare.', false); } catch(e) {}\n    }\n    function showConfirm(kind, target){\n        clearStaleUploadLock810('show_confirm');\n        state.kind = isManager(kind) ? 'manager' : 'main';\n        state.target = target || (state.kind === 'main' ? 'chat' : 'manager');\n        state.file = null;\n        state.confirmingPath = false;\n        state.confirmingUpload = false;\n        state.cancelRequested808 = false;\n        try { window.__tiGuidedUploadCancelled808 = 0; } catch(e810) {}\n        if (state.kind === 'manager') state.ctx = readCtx();\n        ensureModal().style.display = 'flex';\n        initUploadLog821('apertura popup caricamento guidato', state.kind, state.target, null);\n        setFileLine(null);\n        setSourceMapVisible(isBasicUploadUser());\n        setProgress(0, '0% - In attesa di avvio');\n        var wrap = byId('ti-direct-progress-wrap-621'); if (wrap) wrap.style.display = 'none';\n        if (isBasicUploadUser()) {\n            var first = firstAuthorizedSourcePath642();\n            setSourceMapLabel(first);\n            if (!first) {\n                setModalText(esc(missingAuthorizedSourceMessage644()), true);\n                showStartButton(false);\n                var ex0 = byId('ti-direct-upload-exit-621'); if (ex0) ex0.textContent = 'Chiudi';\n                return;\n            }\n            setModalText('Confermi l\\'apertura della selezione file di sistema?<br><br>Origine\/path autorizzata registrata: <b>' + esc(first) + '<\/b><br><br>Seleziona il file esclusivamente dal supporto autorizzato indicato, ad esempio <b>' + esc(first) + '<\/b>. Se la finestra di sistema non si apre direttamente su tale supporto, selezionalo manualmente dalla barra laterale o dal percorso del sistema.', false);\n            showStartButton(true, 'Apri selezione file da ' + first);\n            var ex1 = byId('ti-direct-upload-exit-621'); if (ex1) ex1.textContent = 'Chiudi';\n            return;\n        } else {\n            var label = state.kind === 'manager' ? 'Il file verr\u00e0 caricato e associato al campo selezionato.' : 'Il file verr\u00e0 caricato direttamente dal dispositivo o da un supporto esterno selezionabile.';\n            setModalText('Confermi l\\'avvio della scelta file?<br><br>' + removableNoticeText(state.kind) + '<br><br>' + esc(label), false);\n            showStartButton(true, 'Scegli file');\n        }\n        var ex = byId('ti-direct-upload-exit-621'); if (ex) ex.textContent = 'Chiudi';\n    }\n    function closeUploadModal(){\n        var ov = byId('ti-direct-upload-ov-621');\n        if (ov) ov.style.display = 'none';\n    }\n    function forceHideUploadProgress808(reason){\n        try {\n            if (window.tiLoaderState) {\n                window.tiLoaderState.active = false;\n                if (window.tiLoaderState.timer) clearInterval(window.tiLoaderState.timer);\n                window.tiLoaderState.timer = null;\n            }\n        } catch(e) {}\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e2) {}\n        try { if (typeof window.closeModal === 'function') window.closeModal('ti-ai-loader-ov'); } catch(e3) {}\n        try { var ov = byId('ti-ai-loader-ov'); if (ov) { ov.style.display = 'none'; ov.classList.remove('ti-modal-visible','ti-open','show'); } } catch(e4) {}\n        try { if (typeof window.updatePluginScrollLock === 'function') window.updatePluginScrollLock(); } catch(e5) {}\n    }\n    function removePendingNativeInputs808(){\n        try { Array.prototype.forEach.call(document.querySelectorAll('input[data-ti-guided-upload-808]'), function(el){ try { el.value = ''; el.remove(); } catch(e) {} }); } catch(e2) {}\n    }\n    function clearUploadWatchdog812(){\n        try { if (state.watchdogTimer812) clearTimeout(state.watchdogTimer812); } catch(e) {}\n        state.watchdogTimer812 = null;\n    }\n    function uploadAge812(){\n        try { return state.uploadStartedAt812 ? (Date.now() - state.uploadStartedAt812) : 0; } catch(e) {}\n        return 0;\n    }\n    function armUploadWatchdog812(kind, file){\n        clearUploadWatchdog812();\n        state.timeoutRequested812 = false;\n        state.uploadStartedAt812 = Date.now();\n        state.uploadLastProgressAt812 = Date.now();\n        state.watchdogTimer812 = setTimeout(function(){\n            try {\n                if (!state.busy) return;\n                state.timeoutRequested812 = true;\n                setModalText('Caricamento guidato file in timeout o senza conferma dal server. Operazione interrotta per evitare blocco della form.<br><br>Premi <b>Riprova stesso file<\/b> oppure <b>Chiudi<\/b>.', true);\n                setInlineStatus(kind || 'main', 'Timeout caricamento file. Ripeti il caricamento.', true);\n                updateUploadWaitPopup792(0, 'Timeout caricamento file', 'Operazione interrotta per evitare blocco. Ripeti il caricamento.', file && file.name ? file.name : 'file');\n                if (state.xhr && state.xhr.readyState !== 4) state.xhr.abort();\n            } catch(e) {}\n        }, 90000);\n    }\n    function exitUpload(){\n        uploadLog821('exit', 'Utente ha premuto Chiudi\/Annulla');\n        var cleanupToken859 = currentUploadCleanupToken859(false);\n        try { if (state.busy || cleanupToken859 || (state.savedUploads859 && state.savedUploads859.length)) scheduleCleanup859('chiudi_annulla_upload_guidato', true, cleanupToken859); } catch(e859exit) {}\n        \/\/ 30.9.808: Esci deve sempre annullare davvero upload guidato, progress popup e risposte tardive.\n        clearUploadWatchdog812();\n        state.cancelRequested808 = true;\n        state.exitReason808 = 'user_exit';\n        try { window.__tiGuidedUploadCancelled808 = Date.now(); } catch(e0) {}\n        if (state.xhr) { try { state.xhr.abort(); } catch(e) {} }\n        try { if (state.currentUploadAbort821) state.currentUploadAbort821.abort(); } catch(e821) {}\n        state.currentUploadAbort821 = null;\n        state.busy = false;\n        state.xhr = null;\n        state.file = null;\n        state.confirmingPath = false;\n        state.confirmingUpload = false;\n        state.autoCloseUploadModalAfterSelect791 = false;\n        state.popupClosedAfterSelect791 = false;\n        try { setSourceMapVisible(false); } catch(e) {}\n        try { setProgress(0, 'Upload annullato'); } catch(e2) {}\n        try { setInlineStatus(state.kind || 'main', 'Caricamento file annullato.', false); } catch(e3) {}\n        \/\/ 30.9.942: pulisce SEMPRE la riga \"File: ...\" visibile e ripristina \"Scegli file\", indipendentemente\n        \/\/ dallo stato interno del monitor upload 931 (che dopo un completamento non considera piu' l'upload\n        \/\/ \"attivo\" e quindi non esegue il proprio reset): senza questo, riaprendo il popup restava visibile\n        \/\/ il nome dell'ultimo file scelto, dando l'impressione che un file fosse gia' selezionato.\n        try { setFileLine(null); } catch(e942a) {}\n        try { showStartButton(true, 'Scegli file'); } catch(e942b) {}\n        try { if (typeof window.tiCancelConfigFileUpload931 === 'function') window.tiCancelConfigFileUpload931('exit_upload_generic'); } catch(e942c) {}\n        removePendingNativeInputs808();\n        closeUploadModal();\n        forceHideUploadProgress808('upload_exit');\n        try { document.body.classList.remove('ti-upload-busy','ti-ai-upload-busy','ti-loader-active'); } catch(e4) {}\n        return false;\n    }\n    window.tiDirectUploadExit621 = exitUpload;\n    window.tiGuidedUploadExit808 = exitUpload;\n    if (!window.__tiGuidedUploadExitCapture808) {\n        window.__tiGuidedUploadExitCapture808 = true;\n        document.addEventListener('click', function(ev){\n            try {\n                var t = ev && ev.target;\n                if (!t || !t.closest) return;\n                var btn = t.closest('button, a, [role=\"button\"]');\n                if (!btn) return;\n                var label = trim(btn.textContent || btn.getAttribute('aria-label') || btn.title || '');\n                var directExit = btn.id === 'ti-direct-upload-exit-621' || !!btn.closest('#ti-direct-upload-ov-621 .ti-direct-exit-621');\n                var loaderExit = btn.id === 'ti-loader-close' && (state.busy || \/caricamento file|carica file|upload|file caricato|analisi file\/i.test(((byId('ti-loader-title')||{}).textContent || '') + ' ' + ((byId('ti-loader-sub')||{}).textContent || '')));\n                if (!directExit && !(loaderExit && \/^\\s*(chiudi|esci|annulla|interrompi)\/i.test(label || 'Chiudi'))) return;\n                ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                exitUpload();\n                return false;\n            } catch(e) {}\n        }, true);\n        document.addEventListener('keydown', function(ev){\n            try {\n                if (!ev || ev.key !== 'Escape') return;\n                var ov = byId('ti-direct-upload-ov-621');\n                var visible = ov && getComputedStyle(ov).display !== 'none';\n                if (!visible && !state.busy) return;\n                ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                exitUpload();\n                return false;\n            } catch(e) {}\n        }, true);\n    }\n    function chooseFileNow(){\n        clearStaleUploadLock810('choose_file');\n        if (state.busy) return false;\n        state.cancelRequested808 = false;\n        try { window.__tiGuidedUploadCancelled808 = 0; } catch(e810) {}\n        var kind = state.kind;\n        if (isManager(kind)) {\n            var ctx = readCtx();\n            if (!validCtx(ctx)) { setModalText('Contesto riga\/campo non disponibile. Riapri Gestione File Associati dalla riga corretta.', true); return false; }\n            state.ctx = ctx;\n        }\n        if (isBasicUploadUser()) {\n            var firstBasicPath660 = firstAuthorizedSourcePath642();\n            if (!firstBasicPath660) {\n                setModalText(esc(missingAuthorizedSourceMessage644()), true);\n                showStartButton(false);\n                return false;\n            }\n            setSourceMapLabel(firstBasicPath660);\n        }\n        try { uploadLog821('native_picker_849', 'Apertura selettore file richiesta da click\/pointerdown utente', 'kind=' + (kind || '') + ' target=' + (state.target || '')); } catch(e849l) {}\n        var input = document.createElement('input');\n        input.type = 'file';\n        if (!isManager(kind) && isBasicUploadUser()) {\n            input.setAttribute('accept', 'image\/*,application\/pdf,text\/plain,text\/csv,.txt,.csv,.tsv,.pdf,.doc,.docx,.xls,.xlsx,.json,.xml,.html,.htm,.log,.ini,.conf,.cfg,.sql,.yaml,.yml');\n            input.setAttribute('aria-label', 'Scegli file da origine autorizzata ' + (firstAuthorizedSourcePath642() || ''));\n            input.setAttribute('title', 'Scegli file da origine autorizzata ' + (firstAuthorizedSourcePath642() || ''));\n            input.setAttribute('data-ti-authorized-path', firstAuthorizedSourcePath642() || '');\n        } else {\n            input.setAttribute('aria-label', 'Scegli file');\n        }\n        input.style.position = 'fixed'; input.style.left = '0'; input.style.top = '0'; input.style.width = '1px'; input.style.height = '1px'; input.style.opacity = '0.01'; input.style.pointerEvents = 'auto'; input.style.zIndex = '2147483647';\n        input.setAttribute('aria-hidden','true');\n        input.setAttribute('data-ti-guided-upload-808','1');\n        document.body.appendChild(input);\n        try { input.value = ''; } catch(e810) {}\n        var pickerResolved861 = false;\n        var pickerFallbackTimer861 = null;\n        function clearPickerWatch861(){\n            try { if (pickerFallbackTimer861) clearTimeout(pickerFallbackTimer861); } catch(e) {}\n            pickerFallbackTimer861 = null;\n            try { window.removeEventListener('focus', pickerFocusBack861, true); } catch(e2) {}\n            try { document.removeEventListener('visibilitychange', pickerVisibilityBack861, true); } catch(e3) {}\n        }\n        function restorePickerButton861(reason){\n            try {\n                if (pickerResolved861 || state.busy || state.confirmingUpload || state.file) return;\n                pickerResolved861 = true;\n                clearPickerWatch861();\n                try { if (input && input.parentNode) input.parentNode.removeChild(input); } catch(e0) {}\n                state.suppressStartClickUntil849 = 0;\n                state.nativePickerArmedAt849 = 0;\n                setFileLine(null);\n                setModalText('Nessun file scelto oppure la finestra del dispositivo non si \u00e8 aperta. Premi <b>Scegli file<\/b> per ripetere senza usare F5.', true);\n                showStartButton(true, 'Scegli file');\n                uploadLog821('native_picker_restore_861', 'Ripristino pulsante Scegli file', 'reason=' + (reason || 'unknown'));\n            } catch(e861r) {}\n        }\n        function pickerHasSelectedFile926(){ try { return !!(input && input.files && input.files.length); } catch(e) {} return false; }\n        function pickerFocusBack861(){ setTimeout(function(){ if (!pickerHasSelectedFile926()) restorePickerButton861('focus_back_no_change'); }, 1200); }\n        function pickerVisibilityBack861(){ try { if (!document.hidden) setTimeout(function(){ if (!pickerHasSelectedFile926()) restorePickerButton861('visibility_back_no_change'); }, 1200); } catch(e) {} }\n        window.addEventListener('focus', pickerFocusBack861, true);\n        document.addEventListener('visibilitychange', pickerVisibilityBack861, true);\n        pickerFallbackTimer861 = setTimeout(function(){ try { if (document.hasFocus && document.hasFocus() && !pickerHasSelectedFile926()) restorePickerButton861('picker_not_opened'); } catch(e) {} }, 4500);\n        input.addEventListener('change', function(){\n            pickerResolved861 = true;\n            clearPickerWatch861();\n            var file = input.files && input.files.length ? input.files[0] : null;\n            try { document.body.removeChild(input); } catch(e) {}\n            if (!file) {\n                setFileLine(null);\n                setModalText('Nessun file scelto. Premi <b>Scegli file<\/b> per ripetere il caricamento.', true);\n                showStartButton(true, 'Scegli file');\n                return;\n            }\n            if (confirmPrivilegedSourcePath641(file)) return;\n            state.file = file;\n            uploadLog821('file_selected', 'File scelto dall utente', 'name=' + (file.name || '') + ' size=' + (file.size || 0) + ' type=' + (file.type || '') + ' rel=' + (file.webkitRelativePath || ''));\n            try { window.__tiGuidedUploadSelectedFile810 = file; } catch(e810) {}\n            if (!isManager(kind) && ((state.target || '') === 'config-bot-file' || window.targetUploadContext === 'config-bot-file')) {\n                state.target = 'config-bot-file';\n                state.confirmingUpload = false;\n                state.confirmingPath = false;\n                state.autoCloseUploadModalAfterSelect791 = true;\n                state.popupClosedAfterSelect791 = false;\n                setFileLine(file);\n                setProgress(1, 'File scelto - avvio caricamento AI');\n                setModalText('File scelto correttamente.<br><br><b>File:<\/b> ' + esc(file.name || 'file selezionato') + '<br><br>\u23f3 <b>Attendi il completamento del caricamento.<\/b> Avvio caricamento e interpretazione AI dal file: nessun dato viene salvato senza anteprima e conferma successiva.<br><br>Premi <b>Chiudi<\/b> solo se vuoi annullare il caricamento in corso.', false);\n                showStartButton(false);\n                uploadLog821('auto_start_config_file_926', 'Config AI: avvio automatico upload dopo scelta file', 'name=' + (file.name || '') + ' size=' + (file.size || 0));\n                setTimeout(function(){\n                    try {\n                                if (typeof window.tiStartConfigBotFileUpload930 === 'function') {\n                                    window.tiStartConfigBotFileUpload930(file, {kind:kind, target:'config-bot-file', source:'auto_start_config_file_930'});\n                                } else {\n                                    startUpload(kind, 'config-bot-file', file);\n                                }\n                            }\n                    catch(e926) {\n                        setProgress(0, '0% - Errore avvio upload AI');\n                        setModalText('Errore avvio automatico upload AI: ' + esc(e926 && e926.message ? e926.message : 'errore sconosciuto') + '<br><br>Premi <b>Conferma e carica file<\/b> per riprovare.', true);\n                        state.file = file;\n                        state.confirmingUpload = true;\n                        showStartButton(true, 'Conferma e carica file');\n                    }\n                }, 0);\n                return;\n            }\n            state.confirmingUpload = true;\n            state.autoCloseUploadModalAfterSelect791 = false;\n            state.popupClosedAfterSelect791 = false;\n            setFileLine(file);\n            setProgress(0, 'File scelto - in attesa conferma');\n            if (!isManager(kind) && isBasicUploadUser()) {\n                var ap663 = firstAuthorizedSourcePath642() || sourceMapLabel();\n                setModalText('File scelto correttamente.<br><br><b>File:<\/b> ' + esc(file.name || 'file selezionato') + '<br><b>Origine\/path autorizzata dichiarata:<\/b> ' + esc(ap663 || '') + '<br><br>Per caricare il file premi <b>Conferma e carica file<\/b>. Prima della conferma non viene inviato alcun file.', false);\n            } else {\n                setModalText('File scelto correttamente.<br><br><b>File:<\/b> ' + esc(file.name || 'file selezionato') + '<br><br>Per caricare il file premi <b>Conferma e carica file<\/b>. Prima della conferma non viene inviato alcun file.', false);\n            }\n            showStartButton(true, 'Conferma e carica file');\n        }, {once:true});\n        if (!isBasicUploadUser()) { try { setSourceMapVisible(false); } catch(e) {} }\n        setModalText((!isManager(kind) && isBasicUploadUser()) ? (\"La selezione file di sistema si sta aprendo. Scegli il file esclusivamente dall'origine\/path autorizzata: <b>\" + esc(firstAuthorizedSourcePath642()) + \"<\/b>. Se necessario seleziona manualmente tale supporto nella finestra del sistema.\") : 'Seleziona il file dalla finestra del dispositivo. Dopo la scelta dovrai confermare il caricamento nel popup Shop & Service.', false);\n        showStartButton(false);\n        try {\n            state.nativePickerArmedAt849 = Date.now();\n            if (input.showPicker && typeof input.showPicker === 'function') input.showPicker();\n            else input.click();\n        } catch(e) {\n            try { pickerResolved861 = true; clearPickerWatch861(); } catch(e861cw) {}\n            try { document.body.removeChild(input); } catch(ee) {}\n            state.suppressStartClickUntil849 = 0;\n            state.nativePickerArmedAt849 = 0;\n            setModalText('Il browser ha bloccato l\\'apertura del selettore file. Premi <b>Scegli file<\/b> per ripetere senza usare F5.', true);\n            showStartButton(true, 'Scegli file');\n            uploadLog821('native_picker_blocked_861', 'Browser ha bloccato apertura selettore file', e && e.message ? e.message : '');\n        }\n        return false;\n    }\n    function makeFd(data, file){\n        var fd = new FormData();\n        fd.append('action', 'ti_ai_ss_direct_upload_664');\n        fd.append('ti_action', 'ti_ai_ss_direct_upload_664');\n        Object.keys(data || {}).forEach(function(k){ fd.append(k, data[k] == null ? '' : data[k]); });\n        if (file) fd.append('file', file, file.name || 'upload');\n        try {\n            if (isBasicUploadUser()) {\n                fd.append('source_policy', 'basic_authorized_path');\n                fd.append('source_label', sourceLabelForPayload(file));\n                fd.append('client_path_unavailable', '1');\n                fd.append('client_relative_path', clientRelativePath(file));\n                fd.append('authorized_path', sourceLabelForPayload(file));\n            } else {\n                fd.append('source_policy', 'privileged_upload');\n                fd.append('source_label', sourceLabelForPayload(file));\n                fd.append('client_path_unavailable', clientRelativePath(file) ? '0' : '1');\n                fd.append('client_relative_path', clientRelativePath(file));\n                fd.append('authorized_path', sourceLabelForPayload(file));\n                if (sourceMapEnabled()) {\n                    fd.append('map_upload_source', '1');\n                    fd.append('source_instruction_text', sourceMapLabel() || sourceLabelForPayload(file));\n                    fd.append('authorized_path', sourceMapLabel() || sourceLabelForPayload(file));\n                }\n            }\n        } catch(e) {}\n        try { fd.append('upload_cleanup_token', currentUploadCleanupToken859(true)); fd.append('guided_upload_target', state.target || ''); } catch(e859fd) {}\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        return fd;\n    }\n    function iframeMultipartUpload831(data, file, priorErr){\n        \/\/ 30.9.831: terzo canale anti-blocco. Se XHR resta fermo al 7%, prova submit multipart tradizionale in iframe nascosto.\n        \/\/ Evita sia XHR diretto sia lettura locale base64\/chunk. Serve soprattutto quando il browser\/ottimizzatore blocca progress\/readyState.\n        return new Promise(function(resolve, reject){\n            if (!file) { reject(new Error('Fallback iframe: file non disponibile.')); return; }\n            if (state && state.cancelRequested808) { reject(new Error('Caricamento annullato.')); return; }\n            var dt = null;\n            try { dt = new DataTransfer(); dt.items.add(file); } catch(dtErr831) { dt = null; }\n            if (!dt || !dt.files || !dt.files.length) {\n                reject(new Error('Fallback iframe non disponibile: il browser non consente di riagganciare il file selezionato.'));\n                return;\n            }\n            var id = 'ti-upload-iframe-831-' + Date.now() + '-' + Math.floor(Math.random() * 100000);\n            var endpoint = \"https:\/\/www.tisoft.it\/en\/?ti_action=ti_ai_ss_direct_upload_664\";\n            var iframe = document.createElement('iframe');\n            var form = document.createElement('form');\n            var done = false;\n            var timer = null;\n            function cleanup(){\n                try { if (timer) clearTimeout(timer); } catch(e) {}\n                timer = null;\n                try { if (form && form.parentNode) form.parentNode.removeChild(form); } catch(e) {}\n                try { if (iframe && iframe.parentNode) iframe.parentNode.removeChild(iframe); } catch(e) {}\n            }\n            function addField(k, v){\n                var inp = document.createElement('input');\n                inp.type = 'hidden';\n                inp.name = k;\n                inp.value = (v == null ? '' : String(v));\n                form.appendChild(inp);\n            }\n            function addSourceFields(){\n                try {\n                    if (isBasicUploadUser()) {\n                        addField('source_policy', 'basic_authorized_path');\n                        addField('source_label', sourceLabelForPayload(file));\n                        addField('client_path_unavailable', '1');\n                        addField('client_relative_path', clientRelativePath(file));\n                        addField('authorized_path', sourceLabelForPayload(file));\n                    } else {\n                        addField('source_policy', 'privileged_upload');\n                        addField('source_label', sourceLabelForPayload(file));\n                        addField('client_path_unavailable', clientRelativePath(file) ? '0' : '1');\n                        addField('client_relative_path', clientRelativePath(file));\n                        addField('authorized_path', sourceLabelForPayload(file));\n                        if (sourceMapEnabled()) {\n                            addField('map_upload_source', '1');\n                            addField('source_instruction_text', sourceMapLabel() || sourceLabelForPayload(file));\n                            addField('authorized_path', sourceMapLabel() || sourceLabelForPayload(file));\n                        }\n                    }\n                } catch(e) {}\n            }\n            try { uploadLog821('iframe_fallback_831', 'Avvio fallback multipart iframe', 'endpoint=' + endpoint + ' prior=' + (priorErr && priorErr.message ? priorErr.message : priorErr || '')); } catch(e831l) {}\n            try { setUploadActivity814(11, '11% - Fallback upload iframe', 'Caricamento file', 'Il canale XHR non ha risposto. Provo il canale form\/iframe tradizionale.', file); } catch(e831p) {}\n            iframe.name = id;\n            iframe.id = id;\n            iframe.style.position = 'fixed';\n            iframe.style.left = '-10000px';\n            iframe.style.top = '0';\n            iframe.style.width = '1px';\n            iframe.style.height = '1px';\n            iframe.style.opacity = '0';\n            form.method = 'post';\n            form.enctype = 'multipart\/form-data';\n            form.encoding = 'multipart\/form-data';\n            form.action = endpoint;\n            form.target = id;\n            form.style.position = 'fixed';\n            form.style.left = '-10000px';\n            form.style.top = '0';\n            addField('action', 'ti_ai_ss_direct_upload_664');\n            addField('ti_action', 'ti_ai_ss_direct_upload_664');\n            Object.keys(data || {}).forEach(function(k){ addField(k, data[k] == null ? '' : data[k]); });\n            addSourceFields();\n            try { if (window.tiAppendCurrentLanguageToFormData) { var fakeFd = { append:function(k,v){ addField(k,v); } }; window.tiAppendCurrentLanguageToFormData(fakeFd); } } catch(e) {}\n            var fileInput = document.createElement('input');\n            fileInput.type = 'file';\n            fileInput.name = 'file';\n            fileInput.style.position = 'fixed';\n            fileInput.style.left = '-10000px';\n            fileInput.style.top = '0';\n            try { fileInput.files = dt.files; } catch(assignErr831) {\n                cleanup();\n                reject(new Error('Fallback iframe: impossibile agganciare il file selezionato (' + (assignErr831 && assignErr831.message ? assignErr831.message : assignErr831) + ').'));\n                return;\n            }\n            form.appendChild(fileInput);\n            iframe.onload = function(){\n                if (done) return;\n                done = true;\n                try { setUploadActivity814(90, '90% - Risposta iframe ricevuta', 'Caricamento file', 'Risposta server ricevuta dal fallback iframe. Verifica esito.', file); } catch(e831olp) {}\n                try {\n                    var doc = iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document);\n                    var raw = doc && doc.body ? (doc.body.innerText || doc.body.textContent || '') : '';\n                    raw = String(raw || '').trim();\n                    try { uploadLog821('iframe_response_831', 'Risposta fallback iframe', 'chars=' + raw.length + ' head=' + raw.slice(0, 180)); } catch(e831rl) {}\n                    var res = JSON.parse(raw);\n                    if (!res || !res.success) {\n                        var emsg = res && res.data && res.data.message ? res.data.message : 'Upload iframe non riuscito.';\n                        cleanup();\n                        reject(new Error(sanitizeNonPrivilegedMessage634(emsg)));\n                        return;\n                    }\n                    cleanup();\n                    resolve(res.data || {});\n                } catch(parseErr831) {\n                    cleanup();\n                    reject(new Error('Fallback iframe: risposta server non JSON o non leggibile: ' + (parseErr831 && parseErr831.message ? parseErr831.message : parseErr831)));\n                }\n            };\n            timer = setTimeout(function(){\n                if (done) return;\n                done = true;\n                try { uploadLog821('iframe_timeout_831', 'Timeout fallback iframe', 'endpoint=' + endpoint); } catch(e831tl) {}\n                cleanup();\n                reject(new Error('Timeout fallback iframe: il server non ha restituito esito upload.'));\n            }, 45000);\n            try {\n                document.body.appendChild(iframe);\n                document.body.appendChild(form);\n                try { setUploadActivity814(16, '16% - Invio fallback iframe', 'Caricamento file', 'Invio file tramite form tradizionale in corso.', file); } catch(e831sp) {}\n                form.submit();\n            } catch(submitErr831) {\n                done = true;\n                cleanup();\n                reject(new Error('Fallback iframe non avviato: ' + (submitErr831 && submitErr831.message ? submitErr831.message : submitErr831)));\n            }\n        });\n    }\n    function locateAuthorizedPathOnly655(){\n        if (isBasicUploadUser()) return false;\n        var inp = byId('ti-direct-upload-source-label-629');\n        function applyPath(path, note, warn){\n            path = cleanUploadPath653(trim(path || ''));\n            if (inp && path) { inp.value = path; inp.setAttribute('data-authorized-path', path); }\n            if (path) { try { state.authorizedPath657 = path; state.authorizedPath656 = path; window.__tiLastAuthorizedUploadPath657 = path; window.__tiLastAuthorizedUploadPath656 = path; var sb = byId('ti-direct-upload-save-source-651'); if (sb) sb.setAttribute('data-authorized-path', path); } catch(e) {} }\n            var msg = note || '';\n            if (path) msg += '<br><br><b>Path\/origine individuata:<\/b> ' + esc(path);\n            if (!path || !isAbsoluteUploadPath651(path)) {\n                msg += '<br><br>Il browser non ha fornito una path assoluta completa. Inserisci o conferma manualmente una path assoluta, ad esempio <b>F:\\\\<\/b> oppure <b>\/Volumes\/ChiaveUSB<\/b>, poi premi <b>Salva origine\/path<\/b>.';\n                setModalText(msg, true);\n            } else {\n                setModalText(msg + '<br><br>Verifica la path proposta e premi <b>Salva origine\/path<\/b>.', !!warn);\n            }\n        }\n        try {\n            if (window.showDirectoryPicker) {\n                setModalText('Apertura scelta cartella\/origine. Seleziona la chiavetta, cartella o supporto da autorizzare.', false);\n                window.showDirectoryPicker({mode:'read'}).then(function(handle){\n                    var name = handle && handle.name ? String(handle.name) : '';\n                    var existing = sourceMapLabel();\n                    var proposed = existing && isAbsoluteUploadPath651(existing) ? existing : name;\n                    applyPath(proposed, 'Origine selezionata tramite scelta cartella del browser.', !isAbsoluteUploadPath651(proposed));\n                }).catch(function(err){\n                    setModalText('Scelta path annullata o non consentita dal browser. Puoi inserire manualmente la path assoluta, ad esempio <b>F:\\\\<\/b> oppure <b>\/Volumes\/ChiaveUSB<\/b>.', true);\n                });\n                return false;\n            }\n        } catch(e) {}\n        var input = document.createElement('input');\n        input.type = 'file';\n        input.setAttribute('webkitdirectory', 'webkitdirectory');\n        input.setAttribute('directory', 'directory');\n        input.setAttribute('aria-label', 'Individua cartella o supporto da autorizzare');\n        input.style.position = 'fixed'; input.style.left = '0'; input.style.top = '0'; input.style.width = '1px'; input.style.height = '1px'; input.style.opacity = '0'; input.style.pointerEvents = 'none';\n        document.body.appendChild(input);\n        input.addEventListener('change', function(){\n            var f = input.files && input.files.length ? input.files[0] : null;\n            var rel = '';\n            try { rel = f && f.webkitRelativePath ? String(f.webkitRelativePath) : ''; } catch(e) {}\n            try { document.body.removeChild(input); } catch(e2) {}\n            if (!f) { setModalText('Nessuna origine selezionata. Puoi inserire manualmente la path assoluta, ad esempio <b>F:\\\\<\/b>.', true); return; }\n            var root = rel ? rel.split('\/')[0] : '';\n            applyPath(root || sourceMapLabel(), 'Origine\/cartella individuata dalla scelta browser. Per sicurezza i browser normalmente comunicano il nome cartella, non la path assoluta reale.', true);\n        }, {once:true});\n        try { input.click(); } catch(e) {\n            try { document.body.removeChild(input); } catch(ee) {}\n            setModalText('Il browser ha bloccato la scelta cartella. Inserisci manualmente la path assoluta, ad esempio <b>F:\\\\<\/b> oppure <b>\/Volumes\/ChiaveUSB<\/b>.', true);\n        }\n        return false;\n    }\n\n    function saveAuthorizedSourceOnly651(){\n        if (isBasicUploadUser()) return false;\n        var path = sourceMapLabel();\n        if (!path || !isAbsoluteUploadPath651(path)) {\n            try {\n                var txtEl = byId('ti-direct-upload-text-621');\n                var txtPath = txtEl ? cleanUploadPath653(txtEl.textContent || txtEl.innerText || '') : '';\n                if (txtPath && isAbsoluteUploadPath651(txtPath)) path = txtPath;\n            } catch(e) {}\n        }\n        if (!path || !isAbsoluteUploadPath651(path)) {\n            setModalText('Indica una path assoluta autorizzata prima di salvare, ad esempio F:\\ oppure \/Volumes\/ChiaveUSB. Puoi usare anche il pulsante <b>Individua path<\/b> quando il browser lo consente.', true);\n            return false;\n        }\n        var db = dbName();\n        if (!db || db === 'NEW_DB' || db === 'Global') {\n            setModalText('Seleziona una ditta prima di salvare la path autorizzata.', true);\n            return false;\n        }\n        setModalText('Salvataggio origine\/path autorizzata in corso...', false);\n        setProgress(15, 'Salvataggio istruzione...');\n        var fd = new FormData();\n        fd.append('action', 'ti_ai_ss_save_upload_source_652');\n        fd.append('ti_action', 'ti_ai_ss_save_upload_source_652');\n        fd.append('db', db);\n        fd.append('authorized_path', path);\n        fd.append('source_label', path);\n        fd.append('detected_path', path);\n        fd.append('path', path);\n        fd.append('absolute_path', path);\n        fd.append('selected_path', path);\n        fd.append('folder_path', path);\n        try { var txtEl2 = byId('ti-direct-upload-text-621'); if (txtEl2) fd.append('path_text', txtEl2.textContent || txtEl2.innerText || ''); } catch(e) {}\n        fd.append('context', 'Mappatura manuale origine\/path autorizzata senza caricamento file');\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        var urls = ajaxUrlList();\n        if (!urls || !urls.length) { showMissingAuthorizedSource644(kind); return false; }\n        function attempt(i){\n            return fetch(urls[i], {method:'POST', body:fd, credentials:'same-origin'}).then(function(r){\n                return r.text().then(function(raw){\n                    var res = null;\n                    try { res = JSON.parse(raw); } catch(e) { throw new Error(raw && raw.trim() ? ('Risposta server non JSON: ' + raw.slice(0,220)) : 'Risposta server vuota\/non JSON. Verifica che admin-ajax.php sia raggiungibile e che il plugin attivo sia aggiornato.'); }\n                    if (!res || !res.success) throw new Error(res && res.data && res.data.message ? res.data.message : 'Salvataggio non riuscito.');\n                    return res.data || {};\n                });\n            }).catch(function(err){\n                if (i + 1 < urls.length) return attempt(i + 1);\n                throw err;\n            });\n        }\n        attempt(0).then(function(data){\n            setProgress(100, '100% - Istruzione salvata');\n            var msg = (data && data.message) ? data.message : 'Path autorizzata salvata.';\n            setModalText('Origine\/path autorizzata salvata.<br><br><b>Path autorizzata:<\/b> ' + esc(path) + '<br><br>' + esc(msg), false);\n            setInlineStatus('main', 'Path autorizzata salvata: ' + path, false);\n            try { state.serverAuthorizedPath659 = path; sessionStorage.setItem('tiAuthorizedUploadPath659', path); } catch(e) {}\n            try {\n                var d = currentDb642(); if (d) { if (!d.Tabelle) d.Tabelle = {}; if (!d.Tabelle.Istruzioni) d.Tabelle.Istruzioni = []; if (Array.isArray(d.Tabelle.Istruzioni)) d.Tabelle.Istruzioni.push({'Descrizione':'Origine\/path autorizzata per caricamento file utenti generici\/base','Path assoluta autorizzata':path,'Path autorizzata':path,'Origine autorizzata':path,'Istruzione':'Origine\/path file autorizzata per utenti generici\/base. Path assoluta autorizzata: '+path+'. Path autorizzata: '+path+'.'}); }\n            } catch(e) {}\n            try { if (window.tiAlert) window.tiAlert('Path autorizzata salvata: ' + path); } catch(e) {}\n        }).catch(function(err){\n            var msg = sanitizeNonPrivilegedMessage634(err && err.message ? err.message : 'errore sconosciuto');\n            setProgress(0, '0% - Errore');\n            setModalText('Errore salvataggio origine\/path: ' + esc(msg), true);\n        });\n        return false;\n    }\n    function ajaxUrlList(){\n        var urls = [];\n        var hardAjax661 = \"https:\/\/www.tisoft.it\/wp-admin\/admin-ajax.php\";\n        var hardDirect817 = \"https:\/\/www.tisoft.it\/en\/?ti_action=ti_ai_ss_direct_upload_664\";\n        function cleanUrl815(u){\n            u = trim(u || '');\n            if (!u) return '';\n            try { u = u.replace(\/#.*$\/, ''); } catch(e) {}\n            return u;\n        }\n        function looksAjax815(u){\n            u = cleanUrl815(u);\n            if (!u) return false;\n            \/\/ 30.9.815: per upload guidato usare solo endpoint AJAX reali. Le pagine frontend possono rispondere HTML o restare appese fino a timeout.\n            return \/admin-ajax\\.php(?:\\?|$)\/i.test(u) || \/[?&]ti_action=ti_ai_ss_direct_upload_\/i.test(u) || \/[?&]action=ti_ai_ss_direct_upload_\/i.test(u);\n        }\n        function add815(u){\n            u = cleanUrl815(u);\n            if (!u || !looksAjax815(u)) return;\n            if (urls.indexOf(u) === -1) urls.push(u);\n        }\n        \/\/ 30.9.829: per il caricamento guidato file AI usare prima endpoint diretto ti_action.\n        \/\/ Evita lo stallo visto con admin-ajax.php e soprattutto evita la lettura locale base64\/chunk come primo canale.\n        add815(hardDirect817);\n        add815(hardAjax661);\n        add815(window.ajaxurl);\n        add815(window.tiAjaxUrl);\n        add815(window.tiUrl);\n        return urls;\n    }\n    function xhrDirect(data, file){\n        var urls = ajaxUrlList();\n        if (!urls || !urls.length) return Promise.reject(new Error('Endpoint AJAX non disponibile per upload file. Aggiorna la pagina e riprova.'));\n        function attempt(i){\n            return new Promise(function(resolve, reject){\n                var xhr = new XMLHttpRequest();\n                state.xhr = xhr;\n                xhr.open('POST', urls[i], true);\n                xhr.withCredentials = true;\n                xhr.timeout = 35000;\n                var attemptDone817 = false;\n                var retryTimer817 = null;\n                var hardStallTimer831 = null;\n                var stallNoticeTimer831 = null;\n                var noByteTimer845 = null;\n                var longNoByteTimer845 = null;\n                var softProgressTimer861 = null;\n                function clearRetryTimer817(){ try { if (retryTimer817) clearTimeout(retryTimer817); } catch(e817ct) {} retryTimer817 = null; try { if (hardStallTimer831) clearTimeout(hardStallTimer831); } catch(e831ct) {} hardStallTimer831 = null; try { if (stallNoticeTimer831) clearTimeout(stallNoticeTimer831); } catch(e831cnt) {} stallNoticeTimer831 = null; try { if (noByteTimer845) clearTimeout(noByteTimer845); } catch(e845ct) {} noByteTimer845 = null; try { if (longNoByteTimer845) clearTimeout(longNoByteTimer845); } catch(e845lt) {} longNoByteTimer845 = null; }\n                function clearSoftProgress861(){ try { if (softProgressTimer861) clearInterval(softProgressTimer861); } catch(e861ct) {} softProgressTimer861 = null; }\n                function endpointLabel817(){ try { return String(urls[i] || '').indexOf('admin-ajax.php') !== -1 ? 'admin-ajax.php' : 'endpoint diretto ti_action'; } catch(e817el) { return 'endpoint upload'; } }\n                function startSoftProgress861(maxPct, softFile){\n                    clearSoftProgress861();\n                    var started861 = Date.now();\n                    softProgressTimer861 = setInterval(function(){\n                        try {\n                            if (attemptDone817 || state.cancelRequested808 || state.timeoutRequested812) { clearSoftProgress861(); return; }\n                            var cur861 = parseInt(state.progressPercent814 || 0, 10) || 0;\n                            var events861 = parseInt(state.uploadProgressEvents814 || 0, 10) || 0;\n                            var stage861 = String(state.uploadStage814 || '');\n                            var elapsed861 = Date.now() - started861;\n                            var ceiling861 = 64;\n                            if (\/upload_sent|response|server|headers|loading\/i.test(stage861)) ceiling861 = 91;\n                            else if (events861 > 0 || \/uploading\/i.test(stage861)) ceiling861 = 78;\n                            else if (elapsed861 > 16000) ceiling861 = 84;\n                            else if (elapsed861 > 7000) ceiling861 = 72;\n                            var max861 = Math.min(maxPct || 91, ceiling861);\n                            if (cur861 >= max861) return;\n                            var inc861 = elapsed861 < 8000 ? 2 : 1;\n                            var next861 = Math.min(max861, Math.max(cur861 + inc861, cur861 < 7 ? 7 : cur861 + inc861));\n                            setUploadActivity814(next861, next861 + '% - Caricamento file in corso', 'Caricamento file', '', softFile || file);\n                        } catch(e861sp) {}\n                    }, 1200);\n                }\n                function armHardStallWatchdog831(){\n                    clearRetryTimer817();\n                    try { uploadLog821('multipart_watchdog_831', 'Watchdog blocco iniziale armato', 'endpoint=' + (urls[i] || '') + ' stage=' + (state.uploadStage814 || '')); } catch(e831wl) {}\n                    stallNoticeTimer831 = setTimeout(function(){\n                        try {\n                            if (!attemptDone817 && (xhr.readyState || 0) <= 1 && (parseInt(state.uploadProgressEvents814 || 0, 10) || 0) < 1) {\n                                setUploadActivity814(8, '8% - Nessuna conferma upload, preparo fallback', 'Caricamento file', '', file);\n                                uploadLog821('multipart_silent_831', 'Nessun evento dopo invio browser', 'readyState=' + (xhr.readyState || 0) + ' pct=' + (state.progressPercent814 || 0));\n                            }\n                        } catch(e831nt) {}\n                    }, 2500);\n                    hardStallTimer831 = setTimeout(function(){\n                        try {\n                            if (!attemptDone817 && (xhr.readyState || 0) <= 1 && (parseInt(state.uploadProgressEvents814 || 0, 10) || 0) < 1) {\n                                uploadLog821('multipart_no_response_831', 'Blocco reale al 7%: nessun progress\/readyState server', 'endpoint=' + (urls[i] || '') + ' readyState=' + (xhr.readyState || 0) + ' stage=' + (state.uploadStage814 || ''));\n                                retryNext817('Blocco al 7%: il server\/browser non ha generato eventi di trasferimento o risposta.');\n                            }\n                        } catch(e831ht) { try { retryNext817('Blocco al 7%: watchdog upload iniziale.'); } catch(e831ri) {} }\n                    }, 10000);\n                    noByteTimer845 = setTimeout(function(){\n                        try {\n                            var pct845 = parseInt(state.progressPercent814 || 0, 10) || 0;\n                            var ev845 = parseInt(state.uploadProgressEvents814 || 0, 10) || 0;\n                            var stage845 = String(state.uploadStage814 || '');\n                            if (!attemptDone817 && ev845 < 1 && !\/uploading|upload_sent|response|server|headers|loading\/i.test(stage845)) {\n                                uploadLog821('multipart_no_byte_845', 'Nessun byte reale trasferito entro 16 secondi: passo a endpoint alternativo\/fallback', 'endpoint=' + (urls[i] || '') + ' readyState=' + (xhr.readyState || 0) + ' pct=' + pct845 + ' stage=' + (state.uploadStage814 || ''));\n                                setUploadActivity814(9, '9% - Upload lento\/bloccato, provo canale alternativo', 'Caricamento file', '', file);\n                                retryNext817('Upload lento\/bloccato: nessun byte reale trasferito entro 16 secondi.');\n                            }\n                        } catch(e845nb) { try { retryNext817('Upload lento\/bloccato: watchdog no-byte.'); } catch(e845ri) {} }\n                    }, 16000);\n                    longNoByteTimer845 = setTimeout(function(){\n                        try {\n                            var pctL845 = parseInt(state.progressPercent814 || 0, 10) || 0;\n                            var evL845 = parseInt(state.uploadProgressEvents814 || 0, 10) || 0;\n                            var stageL845 = String(state.uploadStage814 || '');\n                            if (!attemptDone817 && evL845 < 1 && !\/uploading|upload_sent|response|server|headers|loading\/i.test(stageL845)) {\n                                uploadLog821('multipart_long_stall_845', 'Stallo upload oltre 35 secondi senza progress reale: abort forzato', 'endpoint=' + (urls[i] || '') + ' readyState=' + (xhr.readyState || 0) + ' pct=' + pctL845 + ' stage=' + (state.uploadStage814 || ''));\n                                setUploadActivity814(0, '0% - Upload bloccato: canale interrotto', 'Timeout caricamento file', '', file);\n                                retryNext817('Upload bloccato oltre 35 secondi senza avanzamento reale.');\n                            }\n                        } catch(e845ls) { try { retryNext817('Upload bloccato: watchdog lungo.'); } catch(e845ri2) {} }\n                    }, 35000);\n                }\n                function retryNext817(msg){\n                    if (attemptDone817) return;\n                    attemptDone817 = true;\n                    clearRetryTimer817();\n                    clearSoftProgress861();\n                    try { state.uploadStage814 = 'retry_endpoint'; } catch(e817rs) {}\n                    try { xhr.abort(); } catch(e817ab) {}\n                    if (i + 1 < urls.length) {\n                        setUploadActivity814(10, '10% - Nessuna conferma da ' + endpointLabel817() + ', provo endpoint alternativo', 'Caricamento file', 'Il primo endpoint non ha confermato la ricezione. Provo canale diretto anti-timeout.', file);\n                        attempt(i + 1).then(resolve).catch(reject);\n                    } else {\n                        reject(new Error(msg || 'Timeout iniziale upload: il server non ha confermato ricezione file.'));\n                    }\n                }\n                xhr.timeout = 35000;\n                try { uploadLog821('multipart_attempt_829', 'Upload multipart diretto browser', 'endpoint=' + (urls[i] || '')); } catch(e829ma) {}\n                try { state.progressPercent814 = 0; state.uploadProgressEvents814 = 0; state.uploadStage814 = 'opening'; state.uploadEndpoint817 = urls[i] || ''; } catch(e814s) {}\n                setUploadActivity814(2, '2% - Apertura connessione upload (' + endpointLabel817() + ')', 'Caricamento file', '', file);\n                \/\/ 30.9.831: il timer viene armato dopo xhr.send(), quando il blocco al 7% \u00e8 realmente osservabile.\n                xhr.onloadstart = function(){\n                    try { state.uploadStage814 = 'request_started'; } catch(e814ls) {}\n                    setUploadActivity814(4, '4% - Richiesta inviata al server', 'Caricamento file', '', file);\n                };\n                if (xhr.upload) {\n                    xhr.upload.onloadstart = function(){\n                        try { state.uploadStage814 = 'upload_started'; } catch(e814uls) {}\n                        setUploadActivity814(6, '6% - Trasferimento file avviato', 'Caricamento file', '', file);\n                    };\n                    xhr.upload.onprogress = function(ev){\n                        clearRetryTimer817();\n                        try { state.uploadLastProgressAt812 = Date.now(); state.uploadProgressEvents814 = (state.uploadProgressEvents814 || 0) + 1; state.uploadStage814 = 'uploading'; } catch(e812p) {}\n                        if (ev && ev.lengthComputable && ev.total > 0) {\n                            var pct = Math.max(8, Math.min(72, Math.round(8 + (ev.loaded \/ ev.total) * 64)));\n                            var loadedKb = Math.round((ev.loaded || 0) \/ 1024);\n                            var totalKb = Math.round((ev.total || 0) \/ 1024);\n                            setUploadActivity814(pct, pct + '% - Invio file al server (' + loadedKb + '\/' + totalKb + ' KB)', 'Caricamento file', '', file);\n                        } else {\n                            var cur = parseInt(state.progressPercent814 || 0, 10) || 8;\n                            var pct2 = Math.min(64, Math.max(12, cur + 6));\n                            setUploadActivity814(pct2, pct2 + '% - Evento trasferimento ricevuto', 'Caricamento file', '', file);\n                        }\n                    };\n                    xhr.upload.onload = function(){\n                        clearRetryTimer817();\n                        try { state.uploadStage814 = 'upload_sent'; } catch(e814ul) {}\n                        setUploadActivity814(74, '74% - File inviato, attendo risposta server', 'Caricamento file', '', file);\n                        startSoftProgress861(91, file);\n                    };\n                    xhr.upload.onerror = function(){\n                        setUploadActivity814(0, '0% - Errore durante invio file', 'Errore caricamento file', 'Errore durante il trasferimento del file.', file);\n                    };\n                }\n                xhr.onreadystatechange = function(){\n                    try { state.uploadLastProgressAt812 = Date.now(); } catch(e814rs) {}\n                    if (xhr.readyState >= 2) clearRetryTimer817();\n                    if (xhr.readyState === 1) {\n                        bumpUploadActivity814(4, 8, '4% - Connessione upload aperta', 'Caricamento file', '', file);\n                    } else if (xhr.readyState === 2) {\n                        setUploadActivity814(Math.max(82, parseInt(state.progressPercent814 || 0, 10) || 0), '82% - Server raggiunto, risposta avviata', 'Caricamento file', '', file);\n                    } else if (xhr.readyState === 3) {\n                        setUploadActivity814(Math.max(88, parseInt(state.progressPercent814 || 0, 10) || 0), '88% - Risposta server in lettura', 'Caricamento file', '', file);\n                    }\n                };\n                xhr.onload = function(){\n                    if (attemptDone817) return;\n                    attemptDone817 = true;\n                    clearRetryTimer817();\n                    clearSoftProgress861();\n                    setUploadActivity814(95, '95% - Risposta server ricevuta, verifica esito', 'Caricamento file', '', file);\n                    var raw = xhr.responseText || '';\n                    if (xhr.status < 200 || xhr.status >= 300) {\n                        if (i + 1 < urls.length) { setUploadActivity814(12, '12% - Riprovo endpoint AJAX alternativo', 'Caricamento file', 'Primo endpoint non valido. Tentativo alternativo in corso.', file); attempt(i + 1).then(resolve).catch(reject); return; }\n                        reject(new Error('HTTP ' + xhr.status + ': ' + raw.slice(0, 260))); return;\n                    }\n                    var res;\n                    try { res = JSON.parse(raw); } catch(e) { reject(new Error(raw && raw.trim() ? ('Risposta server non JSON: ' + raw.slice(0, 260)) : 'Risposta server vuota\/non JSON. Verifica che admin-ajax.php sia raggiungibile e che il plugin attivo sia aggiornato.')); return; }\n                    if (!res || !res.success) { var emsg = res && res.data && res.data.message ? res.data.message : 'Upload non riuscito.'; reject(new Error(sanitizeNonPrivilegedMessage634(emsg))); return; }\n                    setUploadActivity814(98, '98% - Upload registrato, aggiorno interfaccia', 'Caricamento file', '', file);\n                    resolve(res.data || {});\n                };\n                xhr.onerror = function(){ if (attemptDone817) return; attemptDone817 = true; clearRetryTimer817(); clearSoftProgress861(); setUploadActivity814(0, '0% - Errore rete upload su ' + endpointLabel817(), 'Errore caricamento file', '', file); if (i + 1 < urls.length) attempt(i + 1).then(resolve).catch(reject); else reject(new Error('Errore rete durante upload file.')); };\n                xhr.ontimeout = function(){ if (attemptDone817) return; attemptDone817 = true; clearRetryTimer817(); clearSoftProgress861(); setUploadActivity814(0, '0% - Timeout server upload su ' + endpointLabel817(), 'Timeout caricamento file', '', file); if (i + 1 < urls.length) attempt(i + 1).then(resolve).catch(reject); else reject(new Error('Timeout server durante upload file. Stadio: ' + (state.uploadStage814 || 'sconosciuto') + '. Endpoint: ' + endpointLabel817() + '.')); };\n                xhr.onabort = function(){ if (attemptDone817 && !state.timeoutRequested812 && !state.cancelRequested808) return; clearRetryTimer817(); clearSoftProgress861(); reject(new Error(state.timeoutRequested812 ? 'Timeout caricamento guidato file: operazione interrotta per evitare blocco. Ripeti il caricamento.' : 'Caricamento annullato.')); };\n                try {\n                    setUploadActivity814(3, '3% - Creo richiesta multipart', 'Caricamento file', 'Preparazione dati file per invio.', file);\n                    xhr.send(makeFd(data || {}, file));\n                    setUploadActivity814(7, '7% - Invio consegnato al browser (' + endpointLabel817() + ')', 'Caricamento file', '', file);\n                    startSoftProgress861(72, file);\n                    armHardStallWatchdog831();\n                } catch(sendErr810) {\n                    reject(new Error('Invio file non avviato dal browser: ' + (sendErr810 && sendErr810.message ? sendErr810.message : sendErr810)));\n                }\n            });\n        }\n        return attempt(0).catch(function(err818){\n            try {\n                if (data && data.mode === 'plain' && file && !state.cancelRequested808) {\n                    return b64UploadDirect818(data, file, err818);\n                }\n            } catch(e818b) {}\n            throw err818;\n        });\n    }\n    function applyAttachedResult(ctx, data){\n        if (!ctx || !data) return;\n        try {\n            var tbl = data.tbl || ctx.tbl;\n            var idx = (data.idx !== undefined && data.idx !== null) ? data.idx : ctx.idx;\n            var key = data.key || ctx.key;\n            if (!window.currentDbData) window.currentDbData = {Tabelle:{}};\n            if (!window.currentDbData.Tabelle) window.currentDbData.Tabelle = {};\n            if (!window.currentDbData.Tabelle[tbl]) window.currentDbData.Tabelle[tbl] = [];\n            if (!window.currentDbData.Tabelle[tbl][idx]) window.currentDbData.Tabelle[tbl][idx] = {};\n            window.currentDbData.Tabelle[tbl][idx][key] = data.value || data.name || data.filename || '';\n            if (data.db_version) window.currentDbData.__db_version = data.db_version;\n            if (window.configOriginalDbData) {\n                if (!window.configOriginalDbData.Tabelle) window.configOriginalDbData.Tabelle = {};\n                if (!window.configOriginalDbData.Tabelle[tbl]) window.configOriginalDbData.Tabelle[tbl] = [];\n                if (!window.configOriginalDbData.Tabelle[tbl][idx]) window.configOriginalDbData.Tabelle[tbl][idx] = {};\n                window.configOriginalDbData.Tabelle[tbl][idx][key] = window.currentDbData.Tabelle[tbl][idx][key];\n                if (data.db_version) window.configOriginalDbData.__db_version = data.db_version;\n            }\n            if (window.modifiedFields && typeof window.modifiedFields.delete === 'function') window.modifiedFields.delete(tbl + '-' + idx + '-' + key);\n            if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState();\n        } catch(e) {}\n    }\n    function uploadAttach(file){\n        var ctx = state.ctx || readCtx();\n        if (!validCtx(ctx)) return Promise.reject(new Error('Contesto riga\/campo non disponibile. Riapri Gestione File Associati dalla riga corretta.'));\n        var db = dbName();\n        if (!db || db === 'NEW_DB' || db === 'Global') return Promise.reject(new Error('Seleziona una ditta prima di caricare il file.'));\n        var r = rowFor(ctx) || {};\n        return xhrDirect({mode:'attach', db:db, tbl:ctx.tbl, idx:ctx.idx, id:ctx.id || r.ID || '', key:ctx.key, record_label:recordLabel(ctx), upload_cleanup_token:currentUploadCleanupToken859(true), guided_upload_target:'manager'}, file).then(function(data){\n            applyAttachedResult(ctx, data);\n            try { if (window.renderConfig) window.renderConfig(); } catch(e) {}\n            try { setTimeout(function(){ if (window.openFileManager) window.openFileManager(ctx.tbl, ctx.idx, ctx.key); }, 180); } catch(e) {}\n            return data;\n        });\n    }\n    function uploadPlain(target, file){\n        var db = dbName(), tbl = 'Chat', rec = '';\n        if (target && target !== 'chat' && target !== 'config-bot-file' && target !== 'purchase-import-file') {\n            var m = txt(target).match(\/cfg-img-([^-]+)-\/);\n            tbl = m ? m[1] : (target === 'p-foto' ? 'Utenti' : 'Config');\n            rec = target === 'p-foto' ? ((byId('p-user') && byId('p-user').value) || (byId('p-utente') && byId('p-utente').value) || 'utente') : tbl;\n        }\n        var data = {mode:'plain', db:db, tbl:tbl};\n        data.upload_cleanup_token = currentUploadCleanupToken859(true);\n        data.guided_upload_target = target || 'chat';\n        if (rec) data.record_label = rec;\n        \/\/ 30.9.929: per Configurazione AI evita il fermo iniziale Edge\/Windows al 1%\/7%.\n        \/\/ Il PDF viene caricato prima con canale a blocchi\/base64, che produce avanzamento reale\n        \/\/ e non dipende dagli eventi multipart del browser. In caso di errore resta il fallback\n        \/\/ multipart storico, senza salvare dati prima dell'anteprima.\n        if (target === 'config-bot-file' && file && (file instanceof File)) {\n            try { uploadLog821('config_file_b64_first_929', 'Config AI: canale chunk\/base64 prioritario anti-fermo 1%', 'file=' + (file.name || '') + ' size=' + (file.size || 0)); } catch(e929l) {}\n            try { setUploadActivity814(5, '5% - Avvio upload AI anti-fermo', 'Caricamento file', 'Uso il canale a blocchi per evitare il fermo iniziale del browser.', file); } catch(e929p) {}\n            return b64UploadDirect818(data, file, null).catch(function(err929){\n                var msg929 = err929 && err929.message ? String(err929.message) : String(err929 || 'errore sconosciuto');\n                try { uploadLog821('config_file_b64_failed_929', 'Config AI: canale chunk\/base64 non completato, provo multipart', msg929); } catch(e929lf) {}\n                try { setUploadActivity814(10, '10% - Fallback upload browser', 'Caricamento file', 'Il canale a blocchi non ha completato. Provo il canale browser tradizionale.', file); } catch(e929fb) {}\n                return xhrDirect(data, file).catch(function(err829){\n                    var msg829 = err829 && err829.message ? String(err829.message) : String(err829 || 'errore sconosciuto');\n                    try { uploadLog821('multipart_failed_829', 'Upload multipart XHR non riuscito: provo fallback iframe', msg829); } catch(e829lf) {}\n                    try { setUploadActivity814(12, '12% - Fallback form\/iframe', 'Caricamento file', 'Il canale browser non ha completato. Provo upload form tradizionale.', file); } catch(e829fb) {}\n                    return iframeMultipartUpload831(data, file, err829).catch(function(err831){\n                        var msg831 = err831 && err831.message ? String(err831.message) : String(err831 || 'errore sconosciuto');\n                        throw new Error('Il caricamento Configurazione AI non ha completato. Chunk\/base64: ' + msg929 + '. XHR: ' + msg829 + '. Iframe: ' + msg831 + '. Riprova stesso file oppure carica TXT\/CSV\/XLSX.');\n                    });\n                });\n            });\n        }\n        \/\/ 30.9.829: il log reale mostra blocco nella lettura locale del chunk prima dell'invio.\n        \/\/ Quindi per i file AI\/non associati si usa prima il multipart diretto del browser verso endpoint ti_action.\n        \/\/ Il canale base64\/chunk resta solo fallback se il server non risponde al multipart.\n        if (file && (file instanceof File)) {\n            try { uploadLog821('multipart_first_829', 'Uso upload multipart diretto prima della lettura locale', 'file=' + (file.name || '') + ' size=' + (file.size || 0)); } catch(e829l) {}\n            try { setUploadActivity814(5, '5% - Avvio upload diretto browser', 'Caricamento file', 'Invio il file con il canale nativo del browser, senza lettura locale base64 preventiva.', file); } catch(e829p) {}\n            return xhrDirect(data, file).catch(function(err829){\n                var msg829 = err829 && err829.message ? String(err829.message) : String(err829 || 'errore sconosciuto');\n                try { uploadLog821('multipart_failed_829', 'Upload multipart XHR non riuscito: provo fallback iframe', msg829); } catch(e829lf) {}\n                try { setUploadActivity814(10, '10% - Fallback form\/iframe', 'Caricamento file', 'Il canale XHR non ha completato. Provo upload form tradizionale prima di leggere il file in base64.', file); } catch(e829fb) {}\n                return iframeMultipartUpload831(data, file, err829).catch(function(err831){\n                    var msg831 = err831 && err831.message ? String(err831.message) : String(err831 || 'errore sconosciuto');\n                    try { uploadLog821('iframe_failed_831', 'Fallback iframe non riuscito: passo a base64\/chunk', msg831); } catch(e831lf) {}\n                    try { setUploadActivity814(13, '13% - Fallback upload a blocchi', 'Caricamento file', 'Anche il canale form\/iframe non ha completato. Provo il canale a blocchi anti-blocco.', file); } catch(e831fb) {}\n                    return b64UploadDirect818(data, file, err829).catch(function(err819){\n                        var msg819 = err819 && err819.message ? String(err819.message) : String(err819 || 'errore sconosciuto');\n                        if (\/Lettura locale|FileReader|arrayBuffer|Timeout lettura chunk|timeout\/i.test(msg819)) {\n                            throw new Error('Il browser\/server non ha completato il caricamento. XHR: ' + msg829 + '. Iframe: ' + msg831 + '. Fallback: ' + msg819 + '. Riprova stesso file oppure carica una versione TXT\/CSV\/XLSX.');\n                        }\n                        throw err819;\n                    });\n                });\n            });\n        }\n        return xhrDirect(data, file);\n    }\n    function processMainTarget(target, file){\n        if (validCtx(target)) { state.ctx = target; return uploadAttach(file); }\n        target = target || 'chat';\n        if (target === 'config-bot-file') {\n            return uploadPlain(target, file).then(function(data){\n                window.configBotFile = data.url || '';\n                var fileBox = byId('ti-conf-ai-file'), promptBox = byId('ti-conf-ai-prompt'), replyBox = byId('ti-conf-ai-reply'), fileName = data.name || file.name || 'file';\n                try { if (window.tiFileAiSetAvailable799) window.tiFileAiSetAvailable799(data.url || '', fileName, 'config'); } catch(e799) {}\n                if (fileBox) { fileBox.style.display = 'block'; fileBox.innerHTML = '\ud83d\udcce <b>File caricato per AI:<\/b> ' + esc(fileName) + '<br><span style=\"font-size:13px;color:#cbd5e1;\">Adesso indica cosa deve fare la AI con il contenuto del file.<\/span>'; }\n                if (replyBox && !(window.tiFileAiSetAvailable799)) { replyBox.style.display = 'block'; replyBox.innerHTML = '\u2705 File caricato correttamente. File disponibile per la AI.'; }\n                if (promptBox && !trim(promptBox.value)) promptBox.value = 'Ho caricato un file per la AI. Analizza il contenuto del file e applica la mia prossima istruzione operativa. Aggiungi servizi e prodotti dal file, preparando prima una anteprima senza salvare.';\n                if (promptBox) { try { promptBox.focus({preventScroll:true}); } catch(e) {} }\n                try {\n                    var url926 = data && data.url ? String(data.url) : '';\n                    if (url926 && window.__tiConfigFileAutoRunUrl926 !== url926) {\n                        window.__tiConfigFileAutoRunUrl926 = url926;\n                        var currentPrompt926 = promptBox ? trim(promptBox.value || '') : '';\n                        var genericPrompt926 = !currentPrompt926 || (\/ho\\s+caricato\\s+un\\s+file\\s+per\\s+la\\s+ai\/i.test(currentPrompt926) && \/applica\\s+la\\s+mia\\s+prossima\\s+istruzione\/i.test(currentPrompt926));\n                        var autoPrompt926 = 'Aggiungi servizi e prodotti dal file caricato, preparando prima una anteprima senza salvare. Usa il file allegato come sorgente principale; non usare la preview prezzi da fonti ditta.';\n                        if (promptBox) promptBox.value = genericPrompt926 ? autoPrompt926 : currentPrompt926;\n                        if (replyBox) { replyBox.style.display = 'block'; replyBox.innerHTML = '\u2705 File caricato correttamente. Avvio interpretazione AI dal file e preparo anteprima senza salvare.'; }\n                        uploadLog821('auto_run_config_ai_926', 'Config AI: avvio interpretazione dopo upload file', 'url=' + url926 + ' prompt=' + (promptBox ? promptBox.value : ''));\n                        setTimeout(function(){\n                            try {\n                                if (typeof window.tiStartConfigFileImport929 === 'function') {\n                                    window.tiStartConfigFileImport929({url:url926, name:fileName}, (promptBox ? promptBox.value : autoPrompt926), 'auto_after_upload_929');\n                                } else if (typeof window.runConfigBot === 'function') {\n                                    window.runConfigBot();\n                                } else if (replyBox) {\n                                    replyBox.innerHTML = '\u2705 File caricato. Scrivi 1 oppure premi Invia per avviare l interpretazione AI dal file.';\n                                }\n                            } catch(eRun926) {\n                                if (replyBox) replyBox.innerHTML = '\u26a0\ufe0f File caricato, ma avvio interpretazione non riuscito: ' + esc(eRun926 && eRun926.message ? eRun926.message : 'errore sconosciuto') + '<br>Premi Invia per riprovare.';\n                            }\n                        }, 180);\n                    }\n                } catch(e926run) {}\n                return data;\n            });\n        }\n        if (target === 'purchase-import-file') {\n            return uploadPlain(target, file).then(function(data){ var fileName = data.name || file.name || 'file'; if (window.startPurchaseFileImport) window.startPurchaseFileImport(data.url || '', fileName); return data; });\n        }\n        if (target && target !== 'chat') {\n            return uploadPlain(target, file).then(function(data){\n                var inp = byId(target);\n                if (inp) { inp.value = data.name || data.filename || file.name || ''; try { inp.dispatchEvent(new Event('change', {bubbles:true})); } catch(e) {} }\n                if (txt(target).indexOf('cfg-') === 0 && window.renderConfig) window.renderConfig();\n                return data;\n            });\n        }\n        var runUpload = function(uploadFile){\n            var realFile = (uploadFile instanceof File) ? uploadFile : file;\n            return uploadPlain('chat', realFile).then(function(data){ window.lastUploadedFile = data.url || ''; try { if (window.tiFileAiSetAvailable799) window.tiFileAiSetAvailable799(window.lastUploadedFile, realFile.name || file.name || '', 'chat'); } catch(e799) {} if (window.startUserUploadedFileAI) window.startUserUploadedFileAI(window.lastUploadedFile, realFile.name || file.name || ''); return data; });\n        };\n        if (typeof window.prepareUploadFileForAI === 'function') return window.prepareUploadFileForAI(file).then(runUpload);\n        return runUpload(file);\n    }\n    function startUpload(kind, target, file){\n        clearStaleUploadLock810('start_upload');\n        if (!file) { try { file = window.__tiGuidedUploadSelectedFile810 || state.file || null; } catch(e810) { file = state.file || null; } }\n        if (!file) {\n            setModalText('Nessun file disponibile per il caricamento. Premi <b>Scegli file<\/b> e seleziona nuovamente il file.', true);\n            showStartButton(true, 'Scegli file');\n            return false;\n        }\n        state.cancelRequested808 = false;\n        try { window.__tiGuidedUploadCancelled808 = 0; } catch(e810) {}\n        if (isBasicUploadUser()) {\n            var allowedPath664 = firstAuthorizedSourcePath642() || sourceMapLabel();\n            if (!allowedPath664) {\n                setModalText(esc(missingAuthorizedSourceMessage644()), true);\n                showStartButton(false);\n                return false;\n            }\n            setSourceMapLabel(allowedPath664);\n        }\n        if (!isBasicUploadUser() && sourceMapEnabled() && (!sourceMapLabel() || !isAbsoluteUploadPath651(sourceMapLabel()))) {\n            setModalText(\"Per salvare l'origine come istruzione indica una path assoluta autorizzata, ad esempio F:\\\\ oppure \/Volumes\/ChiaveUSB.\", true);\n            showStartButton(true, 'Scegli file');\n            return;\n        }\n        kind = isManager(kind) ? 'manager' : 'main';\n        state.kind = kind; state.target = target || (kind === 'main' ? 'chat' : 'manager'); state.file = file; state.confirmingUpload = false;\n        state.uploadCleanupToken859 = makeUploadCleanupToken859();\n        state.savedUploads859 = [];\n        try { window.__tiGuidedUploadSaved859 = []; } catch(e859new) {}\n        initUploadLog821('avvio upload confermato', kind, state.target, file);\n        try { window.__tiComplexProcessActive845 = true; window.__tiGuidedUploadBusy810 = true; } catch(e845b) {}\n        setFileLine(file);\n        setModalText('Caricamento in corso. Puoi premere Chiudi per annullare l\\'operazione.', false);\n        showStartButton(false);\n        var ex = byId('ti-direct-upload-exit-621'); if (ex) ex.textContent = 'Chiudi \/ annulla';\n        setProgress(1, '1% - File confermato, preparo richiesta upload');\n        setInlineStatus(kind, 'Caricamento file in corso: ' + (file.name || 'upload'), false);\n        addUploadAiStatus861(kind, state.target, '\ud83d\udcce Caricamento file in corso. Attendi: ti avviso quando il file \u00e8 disponibile per la AI.');\n        state.cancelRequested808 = false;\n        state.timeoutRequested812 = false;\n        state.exitReason808 = '';\n        try { window.__tiGuidedUploadCancelled808 = 0; } catch(eCancelReset808) {}\n        state.busy = true;\n        armUploadWatchdog812(kind, file);\n        try { document.body.classList.add('ti-upload-busy'); } catch(eBusy808) {}\n        var autoCloseAfterSelect791 = !!state.autoCloseUploadModalAfterSelect791;\n        state.autoCloseUploadModalAfterSelect791 = false;\n        if (autoCloseAfterSelect791) {\n            state.popupClosedAfterSelect791 = true;\n            try { closeUploadModal(); } catch(eClose791) {}\n            showUploadWaitPopup792(kind, state.target, file);\n        }\n        var p = isManager(kind) ? uploadAttach(file) : processMainTarget(state.target, file);\n        p.then(function(data){\n            try { registerSavedUpload859(data, kind, state.target, file); } catch(e859reg) {}\n            if (state.cancelRequested808 || window.__tiGuidedUploadCancelled808) {\n                try { cleanupSavedUploads859('risposta_tardiva_dopo_annullo', true, false); } catch(e859late) {}\n                clearUploadWatchdog812(); state.busy = false; state.xhr = null; state.file = null; state.cancelRequested808 = false; state.timeoutRequested812 = false;\n                try { document.body.classList.remove('ti-upload-busy'); } catch(eDone808) {}\n                try { window.__tiComplexProcessActive845 = false; window.__tiGuidedUploadBusy810 = false; } catch(e845late) {}\n                forceHideUploadProgress808('late_success_ignored');\n                return data;\n            }\n            clearUploadWatchdog812(); state.busy = false; state.xhr = null; state.timeoutRequested812 = false;\n            state.popupClosedAfterSelect791 = false;\n            state.autoCloseUploadModalAfterSelect791 = false;\n            try { document.body.classList.remove('ti-upload-busy'); } catch(eDone808b) {}\n            try { window.__tiComplexProcessActive845 = false; window.__tiGuidedUploadBusy810 = false; } catch(e845done) {}\n            uploadLog821('success', 'Upload completato', 'name=' + ((data && (data.name || data.filename || data.original_name)) || (file && file.name) || 'file'));\n            setProgress(100, '100% - Caricamento completato e registrato');\n            var name = (data && (data.name || data.filename || data.original_name)) || file.name || 'file';\n            setModalText(isManager(kind) ? 'File caricato, associato e salvato nel database.<br><br><b>Chiudi questo popup di caricamento per proseguire.<\/b>' : 'File caricato correttamente.<br><br><b>Chiudi questo popup di caricamento per proseguire.<\/b>', false);\n            setFileLine({name:name, size:file.size || 0});\n            var okMsg = isManager(kind) ? ('File associato e salvato: ' + name + '. Chiudi il popup di caricamento per proseguire.') : ('File caricato: ' + name + '. Chiudi il popup di caricamento per proseguire.');\n            if (data && data.source_instruction_message) okMsg += ' - ' + data.source_instruction_message;\n            setInlineStatus(kind, okMsg, false);\n            var isChatAi792 = !isManager(kind) && (!state.target || state.target === 'chat');\n            addUploadAiStatus861(kind, state.target, '\u2705 File caricato. Avvio la fase AI e ti mostro le opzioni appena disponibili.');\n            if (isChatAi792) {\n                updateUploadWaitPopup792(93, 'File caricato', '', name);\n            } else {\n                updateUploadWaitPopup792(100, 'Caricamento completato', okMsg + ' Premi Chiudi per proseguire.', name);\n                finishUploadWaitPopup792(true, 'Caricamento completato', okMsg, true);\n            }\n            showStartButton(false);\n            try { setSourceMapVisible(false); } catch(e) {}\n            var ex2 = byId('ti-direct-upload-exit-621'); if (ex2) ex2.textContent = 'Chiudi';\n            \/\/ 30.9.810: non chiudere subito il popup; l'utente deve vedere esito ed eventuale passaggio AI\/import.\n            setTimeout(function(){\n                if (!state.busy) {\n                    try { showStartButton(false); } catch(e810) {}\n                    var ex810 = byId('ti-direct-upload-exit-621'); if (ex810) ex810.textContent = 'Chiudi';\n                }\n            }, 650);\n        }).catch(function(err){\n            var cancelled808 = !!(state.cancelRequested808 || (err && (err.name === 'AbortError' || \/abort|annull\/i.test(String(err.message || '')))));\n            if (cancelled808) {\n                clearUploadWatchdog812(); state.busy = false; state.xhr = null; state.file = null; state.confirmingUpload = false; state.confirmingPath = false; state.timeoutRequested812 = false;\n                state.popupClosedAfterSelect791 = false; state.autoCloseUploadModalAfterSelect791 = false; state.cancelRequested808 = false;\n                try { document.body.classList.remove('ti-upload-busy'); } catch(eCancel808) {}\n                try { window.__tiComplexProcessActive845 = false; window.__tiGuidedUploadBusy810 = false; } catch(e845cancel) {}\n                setInlineStatus(kind, 'Caricamento file annullato.', false);\n                closeUploadModal();\n                forceHideUploadProgress808('upload_cancelled');\n                return;\n            }\n            var reopenUploadPopup791 = !!state.popupClosedAfterSelect791;\n            var retryFile815 = state.file || file || null;\n            try { if (!retryFile815 && window.__tiGuidedUploadSelectedFile810) retryFile815 = window.__tiGuidedUploadSelectedFile810; } catch(e815rf) {}\n            var rawMsg815 = (err && err.message) ? String(err.message) : 'errore sconosciuto';\n            uploadLog821('error', 'Upload fallito', rawMsg815);\n            var isTimeout815 = !!(state.timeoutRequested812 || \/timeout|tempo massimo|timed\\s*out|scadut\/i.test(rawMsg815));\n            clearUploadWatchdog812(); state.busy = false; state.xhr = null; state.timeoutRequested812 = false;\n            state.confirmingPath = false;\n            state.popupClosedAfterSelect791 = false;\n            state.autoCloseUploadModalAfterSelect791 = false;\n            try { document.body.classList.remove('ti-upload-busy'); } catch(eErr808) {}\n            try { window.__tiComplexProcessActive845 = false; window.__tiGuidedUploadBusy810 = false; } catch(e845err) {}\n            if (reopenUploadPopup791) { try { ensureModal().style.display = 'flex'; } catch(eOpen791) {} }\n            var msg = sanitizeNonPrivilegedMessage634(rawMsg815);\n            try { cleanupSavedUploads859(isTimeout815 ? 'timeout_pronto_riprova' : 'upload_non_a_buon_fine', true, !!(isTimeout815 && retryFile815)); } catch(e859errclean) {}\n            if (isTimeout815 && retryFile815) {\n                state.file = retryFile815;\n                state.confirmingUpload = true;\n                try { window.__tiGuidedUploadSelectedFile810 = retryFile815; } catch(e815sf) {}\n                setFileLine(retryFile815);\n                setModalText('Timeout caricamento file: il server non ha risposto entro il limite neppure con il canale alternativo anti-timeout.<br><br>Il file selezionato e ancora disponibile in questa finestra: premi <b>Riprova stesso file<\/b> per ritentare senza riaprire la scelta file, oppure <b>Chiudi<\/b> per annullare. Se il documento era gia stato ricevuto dalla AI, verra mostrata una risposta\/opzione file disponibile invece del silenzio.', true);\n                setInlineStatus(kind, 'Timeout upload. Puoi riprovare lo stesso file senza riselezionarlo.', true);\n                addUploadAiStatus861(kind, state.target, '\u26a0\ufe0f Caricamento file in timeout. Puoi premere Riprova stesso file oppure Chiudi.');\n                setProgress(0, '0% - Timeout, pronto per riprova');\n                finishUploadWaitPopup792(false, 'Timeout caricamento file', 'Il caricamento e stato sbloccato. Puoi riprovare lo stesso file.', true);\n                showStartButton(true, 'Riprova stesso file');\n            } else {\n                state.file = null;\n                state.confirmingUpload = false;\n                setModalText('Errore caricamento file: ' + esc(msg), true);\n                setInlineStatus(kind, 'Errore upload: ' + msg, true);\n                addUploadAiStatus861(kind, state.target, '\u26a0\ufe0f Upload file non completato. Premi Scegli file e seleziona nuovamente il file.');\n                setProgress(0, '0% - Errore');\n                finishUploadWaitPopup792(false, 'Errore caricamento file', 'Errore caricamento file: ' + msg, true);\n                showStartButton(true, 'Scegli file');\n            }\n            try { if (!isBasicUploadUser()) setSourceMapVisible(false); } catch(e) {}\n            var ex3 = byId('ti-direct-upload-exit-621'); if (ex3) ex3.textContent = 'Chiudi';\n        });\n        return false;\n    }\n    function openDirect(kind, target){\n        clearStaleUploadLock810('open_direct');\n        if (state.busy) {\n            try { setInlineStatus(kind === 'manager' ? 'manager' : 'main', 'Caricamento file gia in corso. Premi Chiudi\/annulla se vuoi interrompere.', true); } catch(e810) {}\n            return false;\n        }\n        kind = isManager(kind) ? 'manager' : 'main';\n        if (isBasicUploadUser() && !firstAuthorizedSourcePath642()) {\n            return checkAuthorizedSourceServer659(kind, target);\n        }\n        if (kind === 'manager') {\n            var ctx = readCtx();\n            if (!validCtx(ctx)) { if (window.tiAlert) window.tiAlert('Gestione File Associati: record\/campo non disponibile. Riapri la finestra dalla riga corretta.'); return false; }\n            state.ctx = ctx;\n        }\n        showConfirm(kind, target || (kind === 'main' ? 'chat' : 'manager'));\n        return false;\n    }\n    window.tiNativeUpload617 = function(kind){ return openDirect(kind === 'manager' ? 'manager' : 'main'); };\n    window.tiNativeUpload620 = window.tiNativeUpload617;\n    window.tiNativeUpload621 = window.tiNativeUpload617;\n    window.tiNativeUpload622 = window.tiNativeUpload617;\n    window.tiNativeUpload623 = window.tiNativeUpload617;\n    window.tiNativeUpload624 = window.tiNativeUpload617;\n    window.tiNativeUpload625 = window.tiNativeUpload617;\n    window.tiNativeUpload629 = window.tiNativeUpload617;\n    window.tiNativeUpload633 = window.tiNativeUpload617;\n    window.tiNativeUpload634 = window.tiNativeUpload617;\n    window.tiNativeUpload640 = window.tiNativeUpload617;\n    window.tiNativeUpload641 = window.tiNativeUpload617;\n    window.tiNativeUpload642 = window.tiNativeUpload617;\n    window.tiNativeUpload643 = window.tiNativeUpload617;\n    window.tiNativeUpload644 = window.tiNativeUpload617;\n    window.tiNativeUpload645 = window.tiNativeUpload617;\n    window.tiNativeUpload646 = window.tiNativeUpload617;\n    window.tiNativeUpload651 = window.tiNativeUpload617;\n    window.tiNativeUpload648 = window.tiNativeUpload617;\n    window.tiNativeUpload664 = window.tiNativeUpload617;\n    function applyUploadRoleLabels(){\n        try {\n            var mainBtn = byId('ti-main-native-upload-btn-617');\n            var fmBtn = byId('ti-fm-native-upload-btn-617');\n            if (mainBtn) mainBtn.textContent = isBasicUploadUser() ? '\ud83d\udcc1 Carica da origine autorizzata' : '\ud83d\udcc1 Carica file da dispositivo \/ supporto';\n            if (fmBtn) fmBtn.textContent = '\ud83d\udcc1 Aggiungi file';\n        } catch(e) {}\n    }\n    window.showUploadChoice = function(target){ target = target || 'chat'; if (aiUploadTarget859(target)) { try { hardResetUploadForReopen859('riapertura_carica_file_ai', true); } catch(e859show) {} } applyUploadRoleLabels(); window.__tiNativeMainTarget621 = target; window.targetUploadContext = target; return openDirect('main', target); };\n    window.chooseLocal = function(){ return window.showUploadChoice(window.__tiNativeMainTarget621 || window.targetUploadContext || 'chat'); };\n    window.chooseLocalManager = function(){ return openDirect('manager', 'manager'); };\n    window.tiDirectUploadFromInput616 = function(){ return window.showUploadChoice('chat'); };\n    window.tiOpenDirectUpload616 = function(target){ return window.showUploadChoice(target || 'chat'); };\n    window.pickConfigBotFile = function(){ return window.showUploadChoice('config-bot-file'); };\n    window.pickPurchaseImportFile = function(){ return window.showUploadChoice('purchase-import-file'); };\n\n    if (!window.__tiConfigBotFileReload859Capture) {\n        window.__tiConfigBotFileReload859Capture = true;\n        document.addEventListener('click', function(ev){\n            try {\n                var t = ev && ev.target;\n                if (!t || !t.closest) return;\n                var btn = t.closest('button, a, [role=\"button\"]');\n                if (!btn) return;\n                var label = trim(btn.textContent || btn.getAttribute('aria-label') || btn.title || '');\n                var onclick = trim(btn.getAttribute('onclick') || '');\n                if (!\/pickConfigBotFile\/i.test(onclick) && !\/^\\s*(?:ri)?carica\\s+file\\s+per\\s+ai\\s*$\/i.test(label)) return;\n                ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();\n                window.pickConfigBotFile();\n                return false;\n            } catch(e859cap) {}\n        }, true);\n    }\n\n    var prevOpenFM = window.openFileManager;\n    if (typeof prevOpenFM === 'function' && !prevOpenFM.__tiNative621) {\n        window.openFileManager = function(tbl, idx, key){\n            saveCtx({tbl:tbl, idx:idx, key:key});\n            var ret = prevOpenFM.apply(this, arguments);\n            try {\n                var fm = byId('ti-file-manager-ov');\n                if (fm) {\n                    fm.dataset.tbl = tbl || '';\n                    fm.dataset.idx = String(idx);\n                    fm.dataset.key = key || '';\n                    fm.setAttribute('data-ti-context-table', tbl || '');\n                    fm.setAttribute('data-ti-context-row', String(idx));\n                    fm.setAttribute('data-ti-context-field', key || '');\n                }\n            } catch(e) {}\n            setInlineStatus('manager', '', false);\n            return ret;\n        };\n        window.openFileManager.__tiNative621 = true;\n        window.openFileManager.__tiNative620 = true;\n        window.openFileManager.__tiNative617 = true;\n    }\n    function handleNativeUploadButton849(evt){\n        var t = evt && evt.target;\n        if (!t || !t.closest) return;\n        var btn = t.closest('#ti-fm-native-upload-btn-617,#ti-main-native-upload-btn-617');\n        if (!btn) return;\n        try { if (evt.preventDefault) evt.preventDefault(); if (evt.stopImmediatePropagation) evt.stopImmediatePropagation(); } catch(e) {}\n        return openDirect(btn.id === 'ti-fm-native-upload-btn-617' ? 'manager' : 'main', btn.id === 'ti-fm-native-upload-btn-617' ? 'manager' : (window.__tiNativeMainTarget621 || window.targetUploadContext || 'chat'));\n    }\n    document.addEventListener('click', handleNativeUploadButton849, true);\n    if (document.readyState !== 'loading') { ensureModal(); applyUploadRoleLabels(); installUploadAlertSanitizer634(); }\n    else document.addEventListener('DOMContentLoaded', function(){ ensureModal(); applyUploadRoleLabels(); installUploadAlertSanitizer634(); }, false);\n    setTimeout(installUploadAlertSanitizer634, 300);\n    setTimeout(installUploadAlertSanitizer634, 1200);\n    document.addEventListener('ti:login', applyUploadRoleLabels, true);\n})();\n\n<\/script>\n\n<style id=\"ti-hide-login-logged-612-css\">\n#ti-login-btn[data-ti-logged-hidden=\"1\"],\n#ti-ai-outer.ti-logged-plugin-user #ti-login-btn {\n    display:none !important;\n    visibility:hidden !important;\n    opacity:0 !important;\n    pointer-events:none !important;\n    width:0 !important;\n    min-width:0 !important;\n    max-width:0 !important;\n    padding:0 !important;\n    margin:0 !important;\n    border:0 !important;\n    overflow:hidden !important;\n}\n<\/style>\n<script id=\"ti-login-hide-logged-612\">\n(function(){\n    \"use strict\";\n    if (window.__tiHideLoginLogged612Installed) return;\n    window.__tiHideLoginLogged612Installed = true;\n    var serverLogged = false;\n    var serverUser = \"\";\n    function byId(id){ return document.getElementById(id); }\n    function text(v){ return String(v == null ? '' : v); }\n    function norm(v){ return text(v).trim().toLowerCase(); }\n    function visible(el){\n        try {\n            if (!el) return false;\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0')) return false;\n            return true;\n        } catch(e) { return !!el; }\n    }\n    function hasRealUserName(){\n        var n = norm((byId('ti-user-name') || {}).textContent || '');\n        return !!(n && n !== 'cliente\/utente' && n !== 'cliente' && n !== 'utente' && n !== 'guest' && n !== 'ospite');\n    }\n    function isLogged(){\n        if (serverLogged || norm(serverUser)) return true;\n        var u = norm(window.tiUser || window.currentUser || window.tiCurrentUser || '');\n        if (u && u !== 'cliente\/utente' && u !== 'cliente' && u !== 'utente' && u !== 'guest' && u !== 'ospite') return true;\n        if (hasRealUserName()) return true;\n        var role = norm((byId('ti-user-role') || {}).textContent || '');\n        if (role && role !== '-') return true;\n        var logout = byId('ti-logout-btn');\n        if (visible(logout)) return true;\n        return false;\n    }\n    function hideLogin(){\n        var outer = byId('ti-ai-outer');\n        var login = byId('ti-login-btn');\n        var logout = byId('ti-logout-btn');\n        if (!isLogged()) return false;\n        if (outer) outer.classList.add('ti-logged-plugin-user');\n        if (login) {\n            login.setAttribute('data-ti-logged-hidden','1');\n            login.setAttribute('aria-hidden','true');\n            login.setAttribute('tabindex','-1');\n            login.disabled = true;\n            login.onclick = function(ev){ try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {} return false; };\n            try { login.style.setProperty('display','none','important'); } catch(e) {}\n            try { login.style.setProperty('visibility','hidden','important'); } catch(e) {}\n            try { login.style.setProperty('pointer-events','none','important'); } catch(e) {}\n        }\n        if (logout) {\n            try { logout.style.setProperty('display','inline-flex','important'); } catch(e) {}\n            logout.removeAttribute('aria-hidden');\n            logout.disabled = false;\n        }\n        var direct = byId('ti-mobile-direct-login');\n        if (direct) {\n            try { direct.style.setProperty('display','none','important'); } catch(e) {}\n            direct.classList.remove('ti-direct-login-visible');\n        }\n        return true;\n    }\n    window.tiHideLoginIfLogged612 = hideLogin;\n    var prevSetAdmin = window.setAdminUI;\n    if (typeof prevSetAdmin === 'function' && !prevSetAdmin.__ti612HideLogin) {\n        window.setAdminUI = function(){ var r = prevSetAdmin.apply(this, arguments); setTimeout(hideLogin,0); return r; };\n        window.setAdminUI.__ti612HideLogin = true;\n    }\n    var prevOpenModal = window.openModal;\n    if (typeof prevOpenModal === 'function' && !prevOpenModal.__ti612HideLogin) {\n        window.openModal = function(){ var r = prevOpenModal.apply(this, arguments); setTimeout(hideLogin,0); return r; };\n        window.openModal.__ti612HideLogin = true;\n    }\n    ['DOMContentLoaded','ti:login','ti:config-opened','click','input','change'].forEach(function(evt){\n        document.addEventListener(evt, function(){ setTimeout(hideLogin,0); }, true);\n    });\n    hideLogin();\n    setTimeout(hideLogin, 50);\n    setTimeout(hideLogin, 300);\n    setTimeout(hideLogin, 1000);\n})();\n<\/script>\n\n\n<script id=\"ti-orphan-cleanup-refresh-614\">\n(function(){\n    \"use strict\";\n    if (window.__tiOrphanCleanupRefresh614Installed) return;\n    window.__tiOrphanCleanupRefresh614Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function selectedDb(){ var s = byId('ti-ditta'); return s ? String(s.value || '').trim() : ''; }\n    function isVisible(el){\n        if (!el) return false;\n        try {\n            var cs = window.getComputedStyle ? window.getComputedStyle(el) : null;\n            if (cs && (cs.display === 'none' || cs.visibility === 'hidden')) return false;\n            return el.style.display !== 'none' && el.getAttribute('aria-hidden') !== 'true';\n        } catch(e) { return true; }\n    }\n    function fileManagerCtx(){\n        var ov = byId('ti-file-manager-ov');\n        var ctx = null;\n        try {\n            if (ov && isVisible(ov)) ctx = {tbl:ov.dataset.tbl || ov.getAttribute('data-ti-context-table') || '', idx:ov.dataset.idx || ov.getAttribute('data-ti-context-row') || '', key:ov.dataset.key || ov.getAttribute('data-ti-context-field') || ''};\n            if ((!ctx || !ctx.tbl || !ctx.key) && window.targetUploadContext) ctx = {tbl:window.targetUploadContext.tbl || '', idx:window.targetUploadContext.idx, key:window.targetUploadContext.key || ''};\n            if (ctx && ctx.tbl && ctx.key) return ctx;\n        } catch(e) {}\n        return null;\n    }\n    function reopenFileManager(ctx){\n        if (!ctx || !ctx.tbl || !ctx.key || typeof window.openFileManager !== 'function') return;\n        setTimeout(function(){\n            try { window.openFileManager(ctx.tbl, parseInt(ctx.idx, 10) || 0, ctx.key); } catch(e) {}\n        }, 150);\n    }\n    function fetchDb(dbVal){\n        if (!dbVal || dbVal === 'NEW_DB' || dbVal === 'Global') return Promise.resolve(false);\n        if (typeof window.refreshCurrentConfigDb === 'function') {\n            return Promise.resolve(window.refreshCurrentConfigDb(dbVal)).catch(function(){ return false; });\n        }\n        if (!window.fetchJsonSafe) return Promise.resolve(false);\n        var fd = new FormData();\n        fd.append('ti_action', 'ti_ai_get_db_data');\n        fd.append('db', dbVal);\n        return window.fetchJsonSafe(window.tiUrl, {method:'POST', body:fd, tiNoLongProcessSignal:true}).then(function(res){\n            if (res && res.success && res.data) {\n                window.currentDbData = res.data;\n                try { if (window.renderConfig) window.renderConfig(); } catch(e) {}\n                return true;\n            }\n            return false;\n        }).catch(function(){ return false; });\n    }\n    window.tiRefreshAfterOrphanCleanup614 = function(dbVal, ctx){\n        ctx = ctx || fileManagerCtx();\n        dbVal = dbVal || selectedDb();\n        return fetchDb(dbVal).then(function(ok){\n            if (ok) reopenFileManager(ctx);\n            return ok;\n        });\n    };\n\n    function install(){\n        if (!window.fetchJsonSafe || !window.tiUrl) return false;\n        if (typeof window.forceCleanupMissingMediaRefs610 === 'function' && !window.forceCleanupMissingMediaRefs610.__ti614RefreshWrapped) {\n            var oldForce = window.forceCleanupMissingMediaRefs610;\n            window.forceCleanupMissingMediaRefs610 = function(dbVal){\n                var ctx = fileManagerCtx();\n                return oldForce.apply(this, arguments).then(function(data){\n                    var removed = parseInt((data && data.removed) || 0, 10) || 0;\n                    if (removed > 0) {\n                        return window.tiRefreshAfterOrphanCleanup614(dbVal, ctx).then(function(){ return data; });\n                    }\n                    return data;\n                });\n            };\n            window.forceCleanupMissingMediaRefs610.__ti614RefreshWrapped = true;\n        }\n        if (typeof window.startOrphanFileCleanup === 'function' && !window.startOrphanFileCleanup.__ti614ForceFirst) {\n            var oldStart = window.startOrphanFileCleanup;\n            window.startOrphanFileCleanup = function(dbVal){\n                dbVal = dbVal || selectedDb();\n                var started = false;\n                var beginOriginal = function(){\n                    if (started) return;\n                    started = true;\n                    return oldStart.call(window, dbVal);\n                };\n                try {\n                    if (typeof window.showProgressPopup === 'function') window.showProgressPopup('Pulizia file non associati', 'Verifico prima le associazioni fantasma nei campi database.', {startPercent:2, targetPercent:14, stepMs:300, cancellable:false, currentItem:'Scansione associazioni fantasma'});\n                    if (typeof window.showOrphanCleanupEmergencyButton === 'function') window.showOrphanCleanupEmergencyButton();\n                    if (typeof window.forceCleanupMissingMediaRefs610 === 'function') {\n                        return window.forceCleanupMissingMediaRefs610(dbVal).then(function(data){\n                            var removed = parseInt((data && data.removed) || 0, 10) || 0;\n                            if (removed > 0 && typeof window.updateProgressPopup === 'function') window.updateProgressPopup(15, 'Pulizia file non associati', 'Associazioni fantasma eliminate: ' + removed + '. Avvio scansione file fisici.', 'Associazioni fantasma eliminate: ' + removed);\n                            return beginOriginal();\n                        }).catch(function(err){\n                            if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Errore pulizia associazioni fantasma');\n                            if (typeof window.tiAlert === 'function') window.tiAlert('Errore durante la verifica delle associazioni fantasma: ' + ((err && err.message) ? err.message : 'errore sconosciuto'));\n                        });\n                    }\n                } catch(e) {}\n                return beginOriginal();\n            };\n            window.startOrphanFileCleanup.__ti614ForceFirst = true;\n        }\n        if (typeof window.showOrphanCleanupFinalReport === 'function' && !window.showOrphanCleanupFinalReport.__ti614RefreshWrapped) {\n            var oldReport = window.showOrphanCleanupFinalReport;\n            window.showOrphanCleanupFinalReport = function(deletedPaths, errors, total, deleted, interrupted, processed, scopeInfo){\n                var ret = oldReport.apply(this, arguments);\n                if (!interrupted) {\n                    var dbVal = selectedDb();\n                    var ctx = fileManagerCtx();\n                    var notify = function(removed){\n                        if (removed > 0 && typeof window.tiAlert === 'function') {\n                            setTimeout(function(){ window.tiAlert('Associazioni fantasma residue eliminate: <b>' + removed + '<\/b><br>La schermata Gestione File Associati e stata aggiornata.'); }, 650);\n                        }\n                    };\n                    if (dbVal && typeof window.forceCleanupMissingMediaRefs610 === 'function') {\n                        window.forceCleanupMissingMediaRefs610(dbVal).then(function(data){\n                            var removed = parseInt((data && data.removed) || 0, 10) || 0;\n                            return window.tiRefreshAfterOrphanCleanup614(dbVal, ctx).then(function(){ notify(removed); });\n                        }).catch(function(){\n                            window.tiRefreshAfterOrphanCleanup614(dbVal, ctx);\n                        });\n                    } else {\n                        window.tiRefreshAfterOrphanCleanup614(dbVal, ctx);\n                    }\n                }\n                return ret;\n            };\n            window.showOrphanCleanupFinalReport.__ti614RefreshWrapped = true;\n        }\n        return true;\n    }\n    function retry(n){ if (install()) return; if (n > 0) setTimeout(function(){ retry(n - 1); }, 250); }\n    retry(30);\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ retry(30); });\n    else setTimeout(function(){ retry(30); }, 0);\n})();\n<\/script>\n\n\n<script id=\"ti-config-field-copy-646\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigFieldCopy646Installed) return;\n    window.__tiConfigFieldCopy646Installed = true;\n\n    function txt(v){ return String(v == null ? '' : v); }\n    function trim(v){ return txt(v).trim(); }\n    function byId(id){ return document.getElementById(id); }\n    function isCfgField(el){\n        if (!el || !el.getAttribute) return false;\n        var id = txt(el.id || '');\n        if (\/^cfg-(txt|img|rel)-\/.test(id)) return true;\n        return !!(el.dataset && el.dataset.tbl && el.dataset.key && (el.classList && (el.classList.contains('ti-in') || el.classList.contains('ti-rel-select'))));\n    }\n    function idxVal(v){\n        var n = parseInt(v, 10);\n        return isNaN(n) ? v : n;\n    }\n    function markChanged(tbl, idx, key){\n        try {\n            if (!window.modifiedFields) window.modifiedFields = new Set();\n            window.modifiedFields.add(String(tbl) + '-' + String(idx) + '-' + String(key));\n        } catch(e) {}\n        try { window.configWasSaved = false; } catch(e) {}\n        try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n    }\n    function ensureRow(tbl, idx){\n        if (!window.currentDbData) window.currentDbData = {Tabelle:{}};\n        if (!window.currentDbData.Tabelle) window.currentDbData.Tabelle = {};\n        if (!window.currentDbData.Tabelle[tbl]) window.currentDbData.Tabelle[tbl] = [];\n        if (!window.currentDbData.Tabelle[tbl][idx]) window.currentDbData.Tabelle[tbl][idx] = {};\n        return window.currentDbData.Tabelle[tbl][idx];\n    }\n    function setConfigValue(tbl, idx, key, value, el){\n        tbl = trim(tbl); key = trim(key); idx = idxVal(idx);\n        if (!tbl || !key || idx === '' || idx === null || typeof idx === 'undefined') return false;\n        try {\n            var row = ensureRow(tbl, idx);\n            row[key] = txt(value);\n            markChanged(tbl, idx, key);\n            if (el) {\n                try { el.setAttribute('data-ti-value-copied-646', '1'); } catch(e) {}\n                try { el.style.setProperty('border-color', '#fbbf24', 'important'); } catch(e) {}\n                try { if (window.forceWhiteEditStyle) window.forceWhiteEditStyle(el); } catch(e) {}\n            }\n            return true;\n        } catch(e) { return false; }\n    }\n    function selectedValue(sel){\n        if (!sel) return '';\n        if (sel.multiple) {\n            return Array.prototype.slice.call(sel.selectedOptions || []).map(function(opt){\n                return trim(opt && opt.value);\n            }).filter(Boolean).join(', ');\n        }\n        return trim(sel.value);\n    }\n    function applyRelationSelect(sel){\n        if (!sel || !sel.dataset) return false;\n        var tbl = sel.dataset.tbl || sel.getAttribute('data-tbl') || '';\n        var idx = sel.dataset.idx || sel.getAttribute('data-idx') || '';\n        var key = sel.dataset.key || sel.getAttribute('data-key') || '';\n        var joined = selectedValue(sel);\n        var ok = setConfigValue(tbl, idx, key, joined, sel);\n        try {\n            var hint = sel.parentElement ? sel.parentElement.querySelector('.ti-rel-summary') : null;\n            if (hint) hint.textContent = joined || 'Nessun record associato';\n            sel.setAttribute('data-ti-selected-value-646', joined);\n            if (joined) sel.title = 'Valore copiato nel campo: ' + joined;\n        } catch(e) {}\n        return ok;\n    }\n    function applyTextField(el){\n        if (!isCfgField(el) || !el.dataset) return false;\n        if (el.classList && el.classList.contains('ti-rel-select')) return applyRelationSelect(el);\n        if (el.disabled || el.readOnly) return false;\n        var tbl = el.dataset.tbl || el.getAttribute('data-tbl') || '';\n        var idx = el.dataset.idx || el.getAttribute('data-idx') || '';\n        var key = el.dataset.key || el.getAttribute('data-key') || '';\n        return setConfigValue(tbl, idx, key, el.value, el);\n    }\n\n    var prevRel = window.updateMemFieldFromRelationSelect;\n    window.updateMemFieldFromRelationSelect = function(tbl, idx, key, sel){\n        var ret;\n        try { if (typeof prevRel === 'function') ret = prevRel.apply(this, arguments); } catch(e) {}\n        try {\n            if (sel) {\n                if (tbl) sel.dataset.tbl = tbl;\n                if (idx !== undefined && idx !== null) sel.dataset.idx = String(idx);\n                if (key) sel.dataset.key = key;\n                applyRelationSelect(sel);\n            }\n        } catch(e) {}\n        return ret;\n    };\n    window.updateMemFieldFromMultiSelect = window.updateMemFieldFromRelationSelect;\n\n    ['input','change','blur'].forEach(function(evtName){\n        document.addEventListener(evtName, function(ev){\n            var el = ev && ev.target;\n            if (!isCfgField(el)) return;\n            if (el.classList && el.classList.contains('ti-rel-select')) applyRelationSelect(el);\n            else applyTextField(el);\n        }, true);\n    });\n    document.addEventListener('click', function(ev){\n        var el = ev && ev.target;\n        if (!el || !el.closest) return;\n        var sel = el.closest('select.ti-rel-select');\n        if (!sel) return;\n        setTimeout(function(){ applyRelationSelect(sel); }, 0);\n        setTimeout(function(){ applyRelationSelect(sel); }, 80);\n    }, true);\n\n    function readCtxFromManager(){\n        try {\n            var fm = byId('ti-file-manager-ov');\n            if (!fm) return null;\n            var c = {\n                tbl: (fm.dataset && (fm.dataset.tbl || fm.dataset.table)) || fm.getAttribute('data-ti-context-table') || '',\n                idx: (fm.dataset && (fm.dataset.idx || fm.dataset.row)) || fm.getAttribute('data-ti-context-row') || '',\n                key: (fm.dataset && (fm.dataset.key || fm.dataset.field)) || fm.getAttribute('data-ti-context-field') || ''\n            };\n            if (c.tbl && c.key && c.idx !== '') return c;\n        } catch(e) {}\n        return null;\n    }\n    function preserveManagerCtx(){\n        var c = readCtxFromManager();\n        if (!c && window.targetUploadContext && typeof window.targetUploadContext === 'object') c = window.targetUploadContext;\n        if (!c || !c.tbl || !c.key) return null;\n        try {\n            window.targetUploadContext = {tbl:c.tbl, idx:c.idx, key:c.key};\n            window.__tiNativeUploadCtx621 = window.targetUploadContext;\n            window.__tiNativeUploadCtx646 = window.targetUploadContext;\n            sessionStorage.setItem('tiNativeUploadCtx621', JSON.stringify(window.targetUploadContext));\n            sessionStorage.setItem('tiNativeUploadCtx646', JSON.stringify(window.targetUploadContext));\n        } catch(e) {}\n        return window.targetUploadContext;\n    }\n    var prevChooseManager = window.chooseLocalManager;\n    window.chooseLocalManager = function(){\n        preserveManagerCtx();\n        if (typeof window.tiNativeUpload617 === 'function') return window.tiNativeUpload617('manager');\n        if (typeof prevChooseManager === 'function') return prevChooseManager.apply(this, arguments);\n        return false;\n    };\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var btn = t.closest('#ti-fm-native-upload-btn-617');\n        if (!btn) return;\n        preserveManagerCtx();\n    }, true);\n})();\n<\/script>\n\n\n<script id=\"ti-file-manager-meta-666\">\n(function(){\n    \"use strict\";\n    if (window.__tiFileManagerMeta666Installed) return;\n    window.__tiFileManagerMeta666Installed = true;\n    function txt(v){ return String(v == null ? '' : v); }\n    function trim(v){ return txt(v).trim(); }\n    function decodeEntities(v){\n        var s = txt(v).replace(\/&amp;\/g, '&').replace(\/&#038;\/g, '&');\n        try { var ta = document.createElement('textarea'); ta.innerHTML = s; s = ta.value; } catch(e) {}\n        return s;\n    }\n    function basenameFromRef(ref){\n        var s = decodeEntities(ref).replace(\/\\\\\/g, '\/').trim();\n        try {\n            if (\/ti_action=ti_ai_get_image\/i.test(s)) {\n                var base = \/^https?:\\\/\\\/\/i.test(s) ? undefined : window.location.origin;\n                var u = new URL(s, base);\n                var f = u.searchParams.get('file') || '';\n                if (f) return decodeURIComponent(f).split(\/[\\\\\/]\/).pop();\n            }\n        } catch(e) {\n            var m = s.match(\/[?&]file=([^&#\\s\"'<>\\)]+)\/i);\n            if (m && m[1]) { try { return decodeURIComponent(m[1]).split(\/[\\\\\/]\/).pop(); } catch(x) { return m[1].split(\/[\\\\\/]\/).pop(); } }\n        }\n        s = s.split(\/[?#]\/)[0].replace(\/\\\/+$\/, '');\n        return (s.split(\/[\\\\\/]\/).pop() || '').trim();\n    }\n    window.tiTokenizeFileRefs666 = function(raw){\n        var src = decodeEntities(raw);\n        var arr = [], seen = {};\n        function add(x){ x = trim(x).replace(\/^[\"']+|[\"']+$\/g, ''); if (!x) return; var k = x.toLowerCase(); if (seen[k]) return; seen[k] = 1; arr.push(x); }\n        var re = \/(?:https?:)?\\\/\\\/[^\\s\"'<>,;]+ti_action=ti_ai_get_image[^\\s\"'<>,;]*|(?:\\\/[A-Za-z0-9_.\\\/%+-]*)?\\?ti_action=ti_ai_get_image[^\\s\"'<>,;]*|(?:^|[?&])ti_action=ti_ai_get_image[^\\s\"'<>,;]*\/ig;\n        src = src.replace(re, function(m){ add(m); return ' '; });\n        src.split(\/[,;\\r\\n]+\/).forEach(add);\n        return arr;\n    };\n    window.tiFileExt666 = function(name){ var m = txt(name).match(\/\\.([a-z0-9]{1,8})(?:$|[?#])\/i); return m ? ('.' + m[1].toLowerCase()) : ''; };\n    window.tiFormatBytes666 = function(n){\n        n = parseInt(n, 10); if (!isFinite(n) || n < 0) return '';\n        var u = ['B','KB','MB','GB']; var i = 0; var v = n;\n        while (v >= 1024 && i < u.length - 1) { v = v \/ 1024; i++; }\n        return (i === 0 ? String(v) : v.toFixed(v >= 10 ? 1 : 2)).replace('.', ',') + ' ' + u[i];\n    };\n    window.tiFileMetaFromRef666 = function(ref, url){\n        var name = basenameFromRef(ref) || basenameFromRef(url) || trim(ref) || 'file';\n        var ext = window.tiFileExt666(name) || window.tiFileExt666(ref) || window.tiFileExt666(url) || 'n\/d';\n        return { name:name, ext:ext, isImg:\/\\.(jpg|jpeg|png|webp|gif)$\/i.test(name), isPdf:\/\\.pdf$\/i.test(name) };\n    };\n    window.tiRefreshFileManagerMeta666 = function(){\n        document.querySelectorAll('#fm-grid [data-ti-file-size-url]').forEach(function(el){\n            if (el.getAttribute('data-ti-size-loading') === '1') return;\n            var url = el.getAttribute('data-ti-file-size-url') || '';\n            if (!url) { el.textContent = 'n\/d'; return; }\n            el.setAttribute('data-ti-size-loading', '1');\n            fetch(url, {method:'HEAD', credentials:'same-origin', cache:'no-store'}).then(function(r){\n                var len = r.headers ? r.headers.get('Content-Length') : '';\n                var fmt = window.tiFormatBytes666(len || 0);\n                el.textContent = fmt || 'n\/d';\n            }).catch(function(){ el.textContent = 'n\/d'; });\n        });\n    };\n    try {\n        if (!document.getElementById('ti-file-manager-meta-666-style')) {\n            var st = document.createElement('style'); st.id = 'ti-file-manager-meta-666-style';\n            st.textContent = '#ti-ai-outer .ti-file-preview-666{width:100%;height:86px;border-radius:8px;overflow:hidden;background:#0f172a;border:1px solid #334155;display:flex;align-items:center;justify-content:center;margin-bottom:4px}#ti-ai-outer .ti-file-preview-666 img{width:100%;height:100%;object-fit:cover;cursor:pointer}#ti-ai-outer .ti-file-preview-666 iframe{width:100%;height:100%;border:0;background:#fff}#ti-ai-outer .ti-file-meta-666{font-size:10px;line-height:1.25;color:#cbd5e1;margin-top:2px;word-break:break-word}';\n            document.head.appendChild(st);\n        }\n    } catch(e) {}\n})();\n<\/script>\n\n<script id=\"ti-config-autofill-saved-info-666\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigAutofillSavedInfo666Installed) return;\n    window.__tiConfigAutofillSavedInfo666Installed = true;\n    var last = new WeakMap();\n    function isCfg(el){ return !!(el && el.matches && el.matches('#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]')); }\n    function valueOf(el){\n        if (!el) return '';\n        if (el.tagName === 'SELECT' && el.multiple) return Array.from(el.selectedOptions || []).map(function(o){ return String(o.value || '').trim(); }).filter(Boolean).join(', ');\n        if (el.type === 'checkbox') return el.checked ? (el.value || '1') : '';\n        return el.value == null ? '' : el.value;\n    }\n    function apply(el){\n        if (!isCfg(el) || !window.currentDbData || !window.currentDbData.Tabelle) return false;\n        var tbl = el.getAttribute('data-tbl') || '', key = el.getAttribute('data-key') || '', idx = parseInt(el.getAttribute('data-idx') || '', 10);\n        if (!tbl || !key || isNaN(idx)) return false;\n        var realTbl = window.resolveConfigTableName ? window.resolveConfigTableName(tbl) : tbl;\n        if (!window.currentDbData.Tabelle[realTbl]) window.currentDbData.Tabelle[realTbl] = [];\n        if (!window.currentDbData.Tabelle[realTbl][idx]) window.currentDbData.Tabelle[realTbl][idx] = {};\n        var val = valueOf(el);\n        if (window.normalizeInputValueForKey) val = window.normalizeInputValueForKey(key, val);\n        var oldVal = window.currentDbData.Tabelle[realTbl][idx][key];\n        var changed = String(oldVal == null ? '' : oldVal) !== String(val == null ? '' : val);\n        if (changed) {\n            window.currentDbData.Tabelle[realTbl][idx][key] = val;\n            if (!window.modifiedFields) window.modifiedFields = new Set();\n            window.modifiedFields.add(realTbl + '-' + idx + '-' + key);\n            try { el.style.borderColor = '#60a5fa'; } catch(e) {}\n            try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n        }\n        last.set(el, String(val == null ? '' : val));\n        return changed;\n    }\n    function delayedApply(el){ setTimeout(function(){ apply(el); }, 0); setTimeout(function(){ apply(el); }, 80); setTimeout(function(){ apply(el); }, 300); }\n    ['input','change','blur','focusout','keyup','paste','cut','mouseup','pointerup','click'].forEach(function(ev){ document.addEventListener(ev, function(e){ if (isCfg(e.target)) delayedApply(e.target); }, true); });\n    document.addEventListener('focusin', function(e){\n        if (!isCfg(e.target)) return;\n        var el = e.target, n = 0;\n        last.set(el, String(valueOf(el) == null ? '' : valueOf(el)));\n        var timer = setInterval(function(){\n            n++;\n            if (!document.body.contains(el) || (document.activeElement !== el && n > 5) || n > 25) { clearInterval(timer); return; }\n            var v = String(valueOf(el) == null ? '' : valueOf(el));\n            if (last.get(el) !== v) apply(el);\n        }, 160);\n    }, true);\n    window.tiSyncConfigAutofillSavedInfo666 = function(){\n        document.querySelectorAll('#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]').forEach(apply);\n    };\n    try {\n        if (typeof window.saveConfig === 'function' && !window.saveConfig.__tiAutofill666) {\n            var prevSaveConfig666 = window.saveConfig;\n            window.saveConfig = function(){\n                try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n                try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n                return prevSaveConfig666.apply(this, arguments);\n            };\n            window.saveConfig.__tiAutofill666 = true;\n        }\n    } catch(e) {}\n})();\n<\/script>\n\n\n<script id=\"ti-config-no-saved-info-667\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigNoSavedInfo667Installed) return;\n    window.__tiConfigNoSavedInfo667Installed = true;\n    function safeId(v){ return String(v == null ? '' : v).toLowerCase().replace(\/[^a-z0-9]+\/g, '_').slice(0, 60); }\n    function isConfigEditField(el){\n        return !!(el && el.matches && el.matches('#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],.ti-conf-window .ti-cfg-table input[data-tbl][data-idx][data-key],.ti-conf-window .ti-cfg-table textarea[data-tbl][data-idx][data-key]'));\n    }\n    function hardDisableSavedInfo(el){\n        if (!isConfigEditField(el)) return;\n        try {\n            el.setAttribute('autocomplete', 'off');\n            el.setAttribute('autocorrect', 'off');\n            el.setAttribute('autocapitalize', 'off');\n            el.setAttribute('spellcheck', 'false');\n            el.setAttribute('aria-autocomplete', 'none');\n            el.setAttribute('data-ti-no-saved-info', '1');\n            el.removeAttribute('list');\n            if (el.tagName === 'INPUT') {\n                var typ = String(el.getAttribute('type') || 'text').toLowerCase();\n                if (typ === 'email' || typ === 'search' || typ === 'url' || typ === 'tel') el.setAttribute('type', 'text');\n            }\n            var tbl = el.getAttribute('data-tbl') || 'tbl';\n            var idx = el.getAttribute('data-idx') || '0';\n            var key = el.getAttribute('data-key') || 'field';\n            var wantedName = 'ti_cfg_no_saved_' + safeId(tbl) + '_' + safeId(idx) + '_' + safeId(key);\n            if (el.getAttribute('name') !== wantedName) el.setAttribute('name', wantedName);\n        } catch(e) {}\n    }\n    function apply(root){\n        var scope = root && root.querySelectorAll ? root : document;\n        try {\n            scope.querySelectorAll('#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],.ti-conf-window .ti-cfg-table input[data-tbl][data-idx][data-key],.ti-conf-window .ti-cfg-table textarea[data-tbl][data-idx][data-key]').forEach(hardDisableSavedInfo);\n        } catch(e) {}\n    }\n    window.tiDisableConfigSavedInfo667 = apply;\n    try {\n        if (typeof window.renderConfig === 'function' && !window.renderConfig.__tiNoSavedInfo667) {\n            var prevRender667 = window.renderConfig;\n            window.renderConfig = function(){\n                var ret = prevRender667.apply(this, arguments);\n                setTimeout(function(){ apply(document); }, 0);\n                setTimeout(function(){ apply(document); }, 120);\n                return ret;\n            };\n            window.renderConfig.__tiNoSavedInfo667 = true;\n        }\n    } catch(e) {}\n    ['focusin','mousedown','pointerdown','touchstart','input','change'].forEach(function(evt){\n        document.addEventListener(evt, function(ev){\n            var t = ev && ev.target;\n            if (isConfigEditField(t)) hardDisableSavedInfo(t);\n        }, true);\n    });\n    try {\n        var obs = new MutationObserver(function(muts){\n            for (var i=0; i<muts.length; i++) {\n                var m = muts[i];\n                if (m && m.addedNodes && m.addedNodes.length) { apply(document); return; }\n            }\n        });\n        obs.observe(document.body || document.documentElement, {childList:true, subtree:true});\n    } catch(e) {}\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ apply(document); }, {once:true});\n    setTimeout(function(){ apply(document); }, 0);\n    setTimeout(function(){ apply(document); }, 600);\n})();\n<\/script>\n\n\n\n<style id=\"ti-config-mobile-hscroll-674\">\n\/* 30.9.674 - Configurazione mobile: scroll orizzontale reale delle righe tabellari in verticale e orizzontale. *\/\n@media (hover:none), (pointer:coarse), (max-width:980px) {\n  #ti-conf-ov.ti-ov,\n  #ti-ai-outer #ti-conf-ov.ti-ov {\n    overflow:hidden !important;\n    align-items:stretch !important;\n    justify-content:center !important;\n    padding:0 !important;\n    touch-action:auto !important;\n  }\n  #ti-conf-ov .ti-conf-window,\n  #ti-ai-outer #ti-conf-ov .ti-conf-window {\n    position:fixed !important;\n    top:max(4px, env(safe-area-inset-top)) !important;\n    bottom:max(4px, env(safe-area-inset-bottom)) !important;\n    left:max(2px, env(safe-area-inset-left)) !important;\n    right:max(2px, env(safe-area-inset-right)) !important;\n    width:auto !important;\n    max-width:none !important;\n    height:auto !important;\n    max-height:none !important;\n    margin:0 !important;\n    border-radius:10px !important;\n    overflow:hidden !important;\n  }\n  #ti-conf-ov .ti-conf-header,\n  #ti-ai-outer #ti-conf-ov .ti-conf-header {\n    height:48px !important;\n    min-height:48px !important;\n    padding:0 42px 0 8px !important;\n  }\n  #ti-conf-ov .ti-conf-header h3,\n  #ti-ai-outer #ti-conf-ov .ti-conf-header h3 {\n    font-size:13px !important;\n    line-height:1.1 !important;\n    white-space:nowrap !important;\n    overflow:hidden !important;\n    text-overflow:ellipsis !important;\n  }\n  #ti-conf-ov .ti-conf-body,\n  #ti-ai-outer #ti-conf-ov .ti-conf-body {\n    top:48px !important;\n    bottom:50px !important;\n    left:0 !important;\n    right:0 !important;\n    padding:7px !important;\n    overflow-y:auto !important;\n    overflow-x:hidden !important;\n    -webkit-overflow-scrolling:touch !important;\n    touch-action:pan-y !important;\n    overscroll-behavior:contain !important;\n  }\n  #ti-conf-ov .ti-conf-footer,\n  #ti-ai-outer #ti-conf-ov .ti-conf-footer {\n    height:50px !important;\n    min-height:50px !important;\n    padding:7px 6px !important;\n    gap:4px !important;\n    overflow-x:auto !important;\n    overflow-y:hidden !important;\n    -webkit-overflow-scrolling:touch !important;\n    touch-action:pan-x !important;\n  }\n  #ti-conf-ov .ti-conf-footer .ti-btn,\n  #ti-ai-outer #ti-conf-ov .ti-conf-footer .ti-btn {\n    flex:0 0 auto !important;\n    min-height:32px !important;\n    height:32px !important;\n    padding:4px 7px !important;\n    font-size:10px !important;\n    line-height:1 !important;\n    white-space:nowrap !important;\n  }\n  #ti-conf-ov details.ti-cfg-details,\n  #ti-ai-outer #ti-conf-ov details.ti-cfg-details {\n    width:100% !important;\n    min-width:0 !important;\n    max-width:100% !important;\n    overflow:visible !important;\n    margin-bottom:7px !important;\n  }\n  #ti-conf-ov .ti-cfg-summary,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-summary {\n    padding:8px !important;\n    font-size:12px !important;\n    line-height:1.1 !important;\n  }\n  #ti-conf-ov .ti-cfg-top-scroll,\n  #ti-conf-ov .ti-cfg-bottom-scroll,\n  #ti-conf-ov .ti-sync-hscroll-top,\n  #ti-conf-ov .ti-sync-hscroll-bottom,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-top-scroll,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-bottom-scroll,\n  #ti-ai-outer #ti-conf-ov .ti-sync-hscroll-top,\n  #ti-ai-outer #ti-conf-ov .ti-sync-hscroll-bottom {\n    display:block !important;\n    width:100% !important;\n    max-width:100% !important;\n    height:13px !important;\n    overflow-x:auto !important;\n    overflow-y:hidden !important;\n    -webkit-overflow-scrolling:touch !important;\n    touch-action:pan-x !important;\n    background:#0f172a !important;\n    border-left:1px solid #374151 !important;\n    border-right:1px solid #374151 !important;\n  }\n  #ti-conf-ov .ti-cfg-table-container,\n  #ti-conf-ov .ti-sync-hscroll-wrap,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table-container,\n  #ti-ai-outer #ti-conf-ov .ti-sync-hscroll-wrap {\n    display:block !important;\n    width:100% !important;\n    max-width:100% !important;\n    min-width:0 !important;\n    max-height:calc((var(--ti-vh, 1vh) * 58)) !important;\n    overflow-x:scroll !important;\n    overflow-y:auto !important;\n    -webkit-overflow-scrolling:touch !important;\n    touch-action:pan-x pan-y !important;\n    overscroll-behavior:contain !important;\n    scrollbar-width:auto !important;\n    cursor:grab !important;\n  }\n  #ti-conf-ov .ti-cfg-table-container.ti-hdrag-674,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table-container.ti-hdrag-674 { cursor:grabbing !important; }\n  #ti-conf-ov .ti-cfg-table,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table {\n    width:max-content !important;\n    min-width:max(920px, 100%) !important;\n    table-layout:auto !important;\n    border-collapse:separate !important;\n    border-spacing:0 !important;\n  }\n  #ti-conf-ov .ti-cfg-table th,\n  #ti-conf-ov .ti-cfg-table td,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table th,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table td {\n    white-space:nowrap !important;\n    min-width:126px !important;\n    max-width:none !important;\n    padding:3px !important;\n    font-size:10.5px !important;\n    line-height:1.15 !important;\n    vertical-align:top !important;\n  }\n  #ti-conf-ov .ti-cfg-table th.ti-cfg-rownum,\n  #ti-conf-ov .ti-cfg-table td.ti-cfg-rownum,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table th.ti-cfg-rownum,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table td.ti-cfg-rownum {\n    left:0 !important;\n    width:42px !important;\n    min-width:42px !important;\n    max-width:42px !important;\n    padding:3px 2px !important;\n    z-index:36 !important;\n  }\n  #ti-conf-ov .ti-cfg-table th.ti-sticky-col,\n  #ti-conf-ov .ti-cfg-table td.ti-sticky-col,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table th.ti-sticky-col,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table td.ti-sticky-col {\n    left:42px !important;\n    min-width:136px !important;\n    width:136px !important;\n    max-width:136px !important;\n    z-index:35 !important;\n  }\n  #ti-conf-ov .ti-cfg-table .ti-in,\n  #ti-conf-ov .ti-cfg-table input:not([type=\"checkbox\"]):not([type=\"radio\"]),\n  #ti-conf-ov .ti-cfg-table textarea,\n  #ti-conf-ov .ti-cfg-table select,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table .ti-in,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table input:not([type=\"checkbox\"]):not([type=\"radio\"]),\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table textarea,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table select {\n    min-width:118px !important;\n    width:100% !important;\n    max-width:none !important;\n    min-height:26px !important;\n    padding:3px 4px !important;\n    font-size:12px !important;\n    line-height:1.15 !important;\n    touch-action:manipulation !important;\n  }\n  #ti-conf-ov .ti-cfg-table textarea,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table textarea {\n    min-width:190px !important;\n    min-height:46px !important;\n    white-space:pre-wrap !important;\n  }\n  #ti-conf-ov .ti-cfg-filter-wrap-526,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-filter-wrap-526 { min-width:118px !important; }\n  #ti-conf-ov .ti-cfg-filter,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-filter { min-width:78px !important; font-size:11px !important; }\n}\n@media (hover:none) and (orientation:landscape), (pointer:coarse) and (orientation:landscape), (max-width:980px) and (orientation:landscape) {\n  #ti-conf-ov .ti-conf-body,\n  #ti-ai-outer #ti-conf-ov .ti-conf-body { padding:5px !important; }\n  #ti-conf-ov .ti-cfg-table-container,\n  #ti-conf-ov .ti-sync-hscroll-wrap,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table-container,\n  #ti-ai-outer #ti-conf-ov .ti-sync-hscroll-wrap { max-height:calc((var(--ti-vh, 1vh) * 48)) !important; }\n  #ti-conf-ov .ti-cfg-table,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table { min-width:max(980px, 100%) !important; }\n  #ti-conf-ov .ti-cfg-table th,\n  #ti-conf-ov .ti-cfg-table td,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table th,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table td { font-size:9.5px !important; padding:2px !important; min-width:112px !important; }\n  #ti-conf-ov .ti-cfg-table .ti-in,\n  #ti-conf-ov .ti-cfg-table input:not([type=\"checkbox\"]):not([type=\"radio\"]),\n  #ti-conf-ov .ti-cfg-table textarea,\n  #ti-conf-ov .ti-cfg-table select,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table .ti-in,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table input:not([type=\"checkbox\"]):not([type=\"radio\"]),\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table textarea,\n  #ti-ai-outer #ti-conf-ov .ti-cfg-table select { font-size:10px !important; min-height:23px !important; padding:2px 3px !important; }\n}\n<\/style>\n\n<script id=\"ti-config-mobile-hscroll-674\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigMobileHScroll674Installed) return;\n    window.__tiConfigMobileHScroll674Installed = true;\n    function safe(fn){ try { return fn(); } catch(e) { return null; } }\n    function qsa(root, sel){ return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }\n    function isTouchLike(){ return safe(function(){ return window.matchMedia && (window.matchMedia('(hover:none)').matches || window.matchMedia('(pointer:coarse)').matches || window.matchMedia('(max-width:980px)').matches); }) !== false; }\n    function syncBars(wrap){\n        if (!wrap) return;\n        var det = wrap.closest && wrap.closest('details.ti-cfg-details');\n        if (!det) return;\n        var left = wrap.scrollLeft || 0;\n        qsa(det, '.ti-sync-hscroll-top,.ti-sync-hscroll-bottom,.ti-cfg-top-scroll,.ti-cfg-bottom-scroll').forEach(function(bar){ if (bar !== wrap) bar.scrollLeft = left; });\n    }\n    function updateWidths(root){\n        var scope = root || document;\n        qsa(scope, '#ti-conf-ov .ti-cfg-table-container, #ti-conf-ov .ti-sync-hscroll-wrap').forEach(function(wrap){\n            var table = wrap.querySelector && wrap.querySelector('table');\n            if (!table) return;\n            var cols = table.querySelectorAll ? Math.max(table.querySelectorAll('thead th').length || 0, table.rows && table.rows[0] ? table.rows[0].cells.length : 0) : 0;\n            var minByCols = Math.max(920, cols * (window.innerHeight < window.innerWidth ? 112 : 126));\n            var wanted = Math.max(minByCols, wrap.clientWidth || 0, table.scrollWidth || 0);\n            table.style.setProperty('min-width', wanted + 'px', 'important');\n            table.style.setProperty('width', 'max-content', 'important');\n            wrap.style.setProperty('overflow-x', 'scroll', 'important');\n            wrap.style.setProperty('overflow-y', 'auto', 'important');\n            wrap.style.setProperty('-webkit-overflow-scrolling', 'touch', 'important');\n            wrap.style.setProperty('touch-action', 'pan-x pan-y', 'important');\n            var det = wrap.closest && wrap.closest('details.ti-cfg-details');\n            if (det) qsa(det, '.ti-sync-hscroll-inner,.ti-cfg-top-scroll-inner,.ti-cfg-bottom-scroll-inner').forEach(function(inner){ inner.style.width = wanted + 'px'; });\n        });\n    }\n    function attachDrag(wrap){\n        if (!wrap || wrap.__tiConfigHDrag674) return;\n        wrap.__tiConfigHDrag674 = true;\n        var sx=0, sy=0, sl=0, st=0, dragging=false, decided=false;\n        wrap.addEventListener('touchstart', function(ev){\n            if (!isTouchLike() || !ev.touches || ev.touches.length !== 1) return;\n            var t = ev.touches[0]; sx = t.clientX; sy = t.clientY; sl = wrap.scrollLeft || 0; st = wrap.scrollTop || 0; dragging = false; decided = false;\n        }, {passive:true});\n        wrap.addEventListener('touchmove', function(ev){\n            if (!isTouchLike() || !ev.touches || ev.touches.length !== 1) return;\n            var t = ev.touches[0], dx = t.clientX - sx, dy = t.clientY - sy;\n            if (!decided && (Math.abs(dx) > 7 || Math.abs(dy) > 7)) { decided = true; dragging = Math.abs(dx) > Math.abs(dy) + 2; }\n            if (!dragging) return;\n            if (ev.cancelable) ev.preventDefault();\n            wrap.scrollLeft = sl - dx;\n            wrap.scrollTop = st;\n            wrap.classList.add('ti-hdrag-674');\n            syncBars(wrap);\n        }, {passive:false});\n        function end(){ dragging=false; decided=false; wrap.classList.remove('ti-hdrag-674'); }\n        wrap.addEventListener('touchend', end, {passive:true});\n        wrap.addEventListener('touchcancel', end, {passive:true});\n        wrap.addEventListener('scroll', function(){ syncBars(wrap); }, {passive:true});\n    }\n    function refresh(root){\n        var ov = document.getElementById('ti-conf-ov');\n        if (!ov) return;\n        updateWidths(root || ov);\n        qsa(root || ov, '#ti-conf-ov .ti-cfg-table-container, #ti-conf-ov .ti-sync-hscroll-wrap').forEach(attachDrag);\n        safe(function(){ if (window.initConfigTopScrollbars) window.initConfigTopScrollbars(ov); });\n    }\n    window.tiFixConfigMobileHScroll674 = refresh;\n    safe(function(){\n        if (typeof window.renderConfig === 'function' && !window.renderConfig.__tiMobileHScroll674) {\n            var prev = window.renderConfig;\n            window.renderConfig = function(){\n                var ret = prev.apply(this, arguments);\n                setTimeout(function(){ refresh(document); }, 0);\n                setTimeout(function(){ refresh(document); }, 120);\n                setTimeout(function(){ refresh(document); }, 500);\n                return ret;\n            };\n            window.renderConfig.__tiMobileHScroll674 = true;\n        }\n    });\n    document.addEventListener('toggle', function(ev){ if (ev.target && ev.target.matches && ev.target.matches('#ti-conf-ov details.ti-cfg-details')) setTimeout(function(){ refresh(ev.target); }, 30); }, true);\n    window.addEventListener('resize', function(){ setTimeout(function(){ refresh(document); }, 80); }, {passive:true});\n    window.addEventListener('orientationchange', function(){ setTimeout(function(){ refresh(document); }, 180); setTimeout(function(){ refresh(document); }, 600); }, {passive:true});\n    if (typeof MutationObserver !== 'undefined') safe(function(){\n        new MutationObserver(function(){ setTimeout(function(){ refresh(document); }, 60); }).observe(document.getElementById('ti-ai-outer') || document.body || document.documentElement, {childList:true, subtree:true});\n    });\n    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function(){ refresh(document); }, {once:true}); else refresh(document);\n    setTimeout(function(){ refresh(document); }, 300);\n    setTimeout(function(){ refresh(document); }, 1200);\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-stable-675\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveStable675Installed) return;\n    window.__tiConfigSaveStable675Installed = true;\n    function el(id){ return document.getElementById(id); }\n    function now(){ return Date.now ? Date.now() : (new Date()).getTime(); }\n    function safe(fn, fb){ try { var v = fn(); return (typeof v === 'undefined') ? fb : v; } catch(e) { return fb; } }\n    function txt(v){ return String(v == null ? '' : v); }\n    function qsa(sel, root){ return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }\n    function encPayload(obj){\n        var json = JSON.stringify(obj || {});\n        if (typeof window.utoa === 'function') return window.utoa(json);\n        return btoa(unescape(encodeURIComponent(json)));\n    }\n    function saveButton(){ return el('lbl-conf-save'); }\n    function setButtonSaving(){\n        var b = saveButton();\n        if (!b) return;\n        b.disabled = true;\n        b.setAttribute('aria-busy', 'true');\n        b.setAttribute('data-ti-saving', '1');\n        b.style.pointerEvents = 'none';\n        b.innerText = 'Salvataggio...';\n        try {\n            b.style.setProperty('background', '#d97706', 'important');\n            b.style.setProperty('background-color', '#d97706', 'important');\n            b.style.setProperty('color', '#fff', 'important');\n            b.style.setProperty('cursor', 'wait', 'important');\n        } catch(e) {}\n    }\n    function resetButton(forceClean){\n        window.__saveConfigInFlight = false;\n        window.__tiSaveConfigStartedAt585 = 0;\n        window.__tiSaveConfigStartedAt = 0;\n        try { if (window.__tiSaveConfigHardTimer585) { clearTimeout(window.__tiSaveConfigHardTimer585); window.__tiSaveConfigHardTimer585 = null; } } catch(e) {}\n        var b = saveButton();\n        if (!b) return;\n        if (forceClean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') {\n            try { window.modifiedFields.clear(); } catch(e) {}\n        }\n        var dirty = !!(!forceClean && window.modifiedFields && window.modifiedFields.size > 0);\n        b.disabled = false;\n        b.removeAttribute('disabled');\n        b.removeAttribute('aria-busy');\n        b.removeAttribute('data-ti-saving');\n        b.style.pointerEvents = '';\n        b.classList.toggle('ti-save-dirty', dirty);\n        b.classList.toggle('ti-save-clean', !dirty);\n        b.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n        try {\n            b.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important');\n            b.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important');\n            b.style.setProperty('color', '#fff', 'important');\n            b.style.setProperty('cursor', 'pointer', 'important');\n        } catch(e) {}\n    }\n    function clearBusyUi(){\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n        try {\n            qsa('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(x){ if (x && x.parentNode) x.parentNode.removeChild(x); });\n        } catch(e) {}\n    }\n    function normalizeTablesToArrays(){\n        var data = window.currentDbData;\n        if (!data || !data.Tabelle) return;\n        Object.keys(data.Tabelle).forEach(function(t){ if (!Array.isArray(data.Tabelle[t])) data.Tabelle[t] = Object.values(data.Tabelle[t] || {}); });\n    }\n    function resolveTable(t){\n        return safe(function(){ return window.resolveConfigTableName ? (window.resolveConfigTableName(t) || t) : t; }, t) || t;\n    }\n    function readFieldValue(input){\n        if (!input) return '';\n        if (input.tagName === 'SELECT' && input.multiple) return Array.prototype.slice.call(input.selectedOptions || []).map(function(o){ return txt(o.value).trim(); }).filter(Boolean).join(', ');\n        if (input.type === 'checkbox') return input.checked ? (input.value || '1') : '';\n        return input.value == null ? '' : input.value;\n    }\n    function syncVisibleFields(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        var data = window.currentDbData;\n        if (!data || !data.Tabelle) return;\n        var sel = '#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        qsa(sel).forEach(function(input){\n            if (!input || input.disabled || input.readOnly) return;\n            var tbl = input.getAttribute('data-tbl') || '';\n            var key = input.getAttribute('data-key') || '';\n            var idx = parseInt(input.getAttribute('data-idx') || '', 10);\n            if (!tbl || !key || isNaN(idx)) return;\n            var rt = resolveTable(tbl);\n            if (!data.Tabelle[rt]) data.Tabelle[rt] = [];\n            if (!data.Tabelle[rt][idx] || typeof data.Tabelle[rt][idx] !== 'object') data.Tabelle[rt][idx] = {};\n            var val = readFieldValue(input);\n            if (window.normalizeInputValueForKey) val = window.normalizeInputValueForKey(key, val);\n            var old = data.Tabelle[rt][idx][key];\n            if (txt(old) !== txt(val)) {\n                data.Tabelle[rt][idx][key] = val;\n                if (!window.modifiedFields) window.modifiedFields = new Set();\n                window.modifiedFields.add(rt + '-' + idx + '-' + key);\n            }\n        });\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n    }\n    function parseModifiedTables(){\n        var map = {};\n        var tables = Object.keys((window.currentDbData && window.currentDbData.Tabelle) ? window.currentDbData.Tabelle : {}).sort(function(a,b){ return String(b).length - String(a).length; });\n        try {\n            if (window.modifiedFields && typeof window.modifiedFields.forEach === 'function') {\n                window.modifiedFields.forEach(function(k){\n                    k = txt(k);\n                    for (var i=0; i<tables.length; i++) {\n                        var t = tables[i];\n                        if (k.indexOf(t + '-') === 0) { map[t] = true; return; }\n                    }\n                    var p = k.indexOf('-');\n                    if (p > 0) map[k.slice(0, p)] = true;\n                });\n            }\n        } catch(e) {}\n        try { (window.tiConfigDeletedRowsManifest || []).forEach(function(x){ if (x && x.table) map[resolveTable(x.table)] = true; }); } catch(e) {}\n        try { Object.keys(window.tiConfigDeletedTableSnapshots || {}).forEach(function(t){ if (t) map[resolveTable(t)] = true; }); } catch(e) {}\n        return map;\n    }\n    function addDeepChangedTables(map){\n        var cur = (window.currentDbData && window.currentDbData.Tabelle) ? window.currentDbData.Tabelle : {};\n        var old = (window.configOriginalDbData && window.configOriginalDbData.Tabelle) ? window.configOriginalDbData.Tabelle : {};\n        var all = {};\n        Object.keys(cur || {}).forEach(function(t){ all[t] = true; });\n        Object.keys(old || {}).forEach(function(t){ all[t] = true; });\n        Object.keys(all).forEach(function(t){\n            if (map[t]) return;\n            var a = JSON.stringify(cur[t] === undefined ? null : cur[t]);\n            var b = JSON.stringify(old[t] === undefined ? null : old[t]);\n            if (a !== b) map[t] = true;\n        });\n        return map;\n    }\n    function changedTables(){\n        var map = parseModifiedTables();\n        return Object.keys(addDeepChangedTables(map)).filter(function(t){ return !!t; });\n    }\n    function hasPendingDelete(){\n        try { if ((window.tiConfigDeletedRowsManifest || []).length) return true; } catch(e) {}\n        try { if (Object.keys(window.tiConfigDeletedTableSnapshots || {}).length) return true; } catch(e) {}\n        try { if (parseInt(window.tiConfigDeletedRowsCountTotal || 0, 10) > 0) return true; } catch(e) {}\n        return false;\n    }\n    function postForm(fd, timeoutMs){\n        timeoutMs = timeoutMs || 75000;\n        return new Promise(function(resolve, reject){\n            var ctrl = (typeof AbortController !== 'undefined') ? new AbortController() : null;\n            var done = false;\n            var timer = setTimeout(function(){\n                if (done) return;\n                done = true;\n                try { if (ctrl) ctrl.abort(); } catch(e) {}\n                reject(new Error('Timeout salvataggio configurazione. Verifica la connessione e riprova.'));\n            }, timeoutMs);\n            fetch(window.tiUrl, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', signal: ctrl ? ctrl.signal : undefined}).then(function(r){\n                return r.text().then(function(raw){\n                    if (!r.ok) throw new Error('Errore server ' + r.status + (raw ? ': ' + raw.slice(0, 220) : ''));\n                    try { return JSON.parse(raw); }\n                    catch(e) { throw new Error('Risposta salvataggio non JSON: ' + raw.slice(0, 220)); }\n                });\n            }).then(function(data){ if (done) return; done = true; clearTimeout(timer); resolve(data); }).catch(function(err){ if (done) return; done = true; clearTimeout(timer); reject(err); });\n        });\n    }\n    function updateAfterSuccess(res, changed){\n        var data = (res && res.data) ? res.data : {};\n        if (data.db && data.db.Tabelle) {\n            window.currentDbData = data.db;\n            window.currentDbViewData = null;\n        } else if (window.currentDbData && data.db_version) {\n            window.currentDbData.__db_version = data.db_version;\n        }\n        try { if (window.currentDbData) window.configOriginalDbData = JSON.parse(JSON.stringify(window.currentDbData)); } catch(e) {}\n        try { if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); } catch(e) {}\n        window.tiConfigDeletedRowsManifest = [];\n        window.tiConfigDeletedTableSnapshots = {};\n        window.tiConfigDeletedTableSnapshotBeforeCounts = {};\n        window.tiConfigDeletedRowsCountTotal = 0;\n        window.tiLastConfigDeleteCount = 0;\n        resetButton(true);\n        try { if (window.updateConfigSaveButtonState) window.updateConfigSaveButtonState(); } catch(e) {}\n        var warns = Array.isArray(data.value_warnings) ? data.value_warnings : [];\n        setTimeout(function(){\n            if (warns.length && window.showConfigValidationWarning) window.showConfigValidationWarning(warns, true);\n            else if (window.tiAlert) window.tiAlert('Salvataggio completato.');\n        }, 80);\n    }\n    function buildSaveForm(db, tables){\n        var fd = new FormData();\n        var data = window.currentDbData || {};\n        var payload = data;\n        if (tables && tables.length && data && data.Tabelle) {\n            payload = {Database: data.Database || db, __db_version: data.__db_version || '', Tabelle:{}};\n            tables.forEach(function(t){ if (data.Tabelle && data.Tabelle[t]) payload.Tabelle[t] = data.Tabelle[t]; });\n        }\n        fd.append('ti_action', 'ti_ai_save_full_db');\n        fd.append('db', db);\n        fd.append('payload', encPayload(payload));\n        fd.append('light_save', '1');\n        fd.append('response_light', '1');\n        fd.append('save_mode', 'light');\n        fd.append('compact_save', (tables && tables.length) ? '1' : '0');\n        fd.append('db_version', (data && data.__db_version) ? data.__db_version : '');\n        fd.append('changed_tables', encPayload(tables || []));\n        fd.append('deleted_rows', encPayload(window.tiConfigDeletedRowsManifest || []));\n        fd.append('deleted_table_snapshots', encPayload(window.tiConfigDeletedTableSnapshots || {}));\n        fd.append('deleted_rows_client_total', String(parseInt(window.tiConfigDeletedRowsCountTotal || 0, 10) || 0));\n        return fd;\n    }\n    function forceResetOnConfigOpen(){\n        var b = saveButton();\n        if (!b) return;\n        if (!window.__tiConfigSaveActive675 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving') === '1')) {\n            resetButton(false);\n        }\n    }\n    var prevOpen = window.openModal;\n    if (typeof prevOpen === 'function' && !prevOpen.__tiSaveStable675) {\n        window.openModal = function(id){\n            var ret = prevOpen.apply(this, arguments);\n            if (id === 'ti-conf-ov') setTimeout(forceResetOnConfigOpen, 60);\n            return ret;\n        };\n        window.openModal.__tiSaveStable675 = true;\n    }\n    if (typeof window.renderConfig === 'function' && !window.renderConfig.__tiSaveStable675) {\n        var prevRender = window.renderConfig;\n        window.renderConfig = function(){\n            var ret = prevRender.apply(this, arguments);\n            setTimeout(forceResetOnConfigOpen, 80);\n            return ret;\n        };\n        window.renderConfig.__tiSaveStable675 = true;\n    }\n    window.tiForceUnlockConfigSave585 = window.tiForceUnlockConfigSave587 = window.tiForceUnlockConfigSave588 = function(reason){\n        window.__tiConfigSaveActive675 = false;\n        clearBusyUi();\n        resetButton(false);\n        if (reason && window.tiAlert) window.tiAlert(reason);\n    };\n    window.saveConfig = function(){\n        var start = now();\n        if (window.__tiConfigSaveActive675 || window.__saveConfigInFlight) {\n            var age = start - (parseInt(window.__tiSaveConfigStartedAt675 || window.__tiSaveConfigStartedAt585 || window.__tiSaveConfigStartedAt || 0, 10) || 0);\n            if (age < 8000) return;\n            window.__tiConfigSaveActive675 = false;\n            window.__saveConfigInFlight = false;\n        }\n        if (!window.currentDbData || !window.currentDbData.Tabelle) {\n            resetButton(false);\n            if (window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.');\n            return;\n        }\n        window.__tiConfigSaveActive675 = true;\n        window.__saveConfigInFlight = true;\n        window.__tiSaveConfigStartedAt675 = start;\n        window.__tiSaveConfigStartedAt585 = start;\n        window.__tiSaveConfigStartedAt = start;\n        setButtonSaving();\n        try {\n            syncVisibleFields();\n            normalizeTablesToArrays();\n            if (window.validateConfigUniqueUsernames && !window.validateConfigUniqueUsernames()) throw new Error('Salvataggio bloccato: username duplicato.');\n            var localIssues = window.collectConfigValidationIssues ? window.collectConfigValidationIssues() : [];\n            if (localIssues && localIssues.length && window.showConfigValidationWarning) window.showConfigValidationWarning(localIssues, true);\n            var dbEl = el('ti-ditta');\n            var db = dbEl ? dbEl.value : '';\n            if (!db || db === 'NEW_DB' || db === 'Global') throw new Error('Seleziona ditta.');\n            var tables = changedTables();\n            if (!tables.length && !hasPendingDelete()) {\n                try { if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); } catch(e) {}\n                window.__tiConfigSaveActive675 = false;\n                clearBusyUi();\n                resetButton(true);\n                if (window.tiAlert) window.tiAlert('Nessuna modifica da salvare.');\n                return;\n            }\n            var fd = buildSaveForm(db, tables);\n            postForm(fd, 75000).then(function(res){\n                if (!res || !res.success) throw new Error((res && res.data && res.data.message) ? res.data.message : 'Salvataggio configurazione non riuscito.');\n                updateAfterSuccess(res, tables);\n            }).catch(function(err){\n                var msg = (err && err.message) ? err.message : 'Errore durante il salvataggio configurazione.';\n                resetButton(false);\n                if (window.tiAlert) window.tiAlert(msg); else alert(msg);\n            }).finally(function(){\n                window.__tiConfigSaveActive675 = false;\n                window.__saveConfigInFlight = false;\n                window.__tiSaveConfigStartedAt675 = 0;\n                clearBusyUi();\n                var b = saveButton();\n                if (b && \/salvataggio\/i.test(txt(b.innerText))) resetButton(false);\n            });\n        } catch(e) {\n            window.__tiConfigSaveActive675 = false;\n            clearBusyUi();\n            resetButton(false);\n            if (window.tiAlert) window.tiAlert((e && e.message) ? e.message : 'Errore preparazione salvataggio configurazione.');\n        }\n    };\n    setInterval(function(){\n        try {\n            var b = saveButton();\n            if (!b) return;\n            var age = now() - (parseInt(window.__tiSaveConfigStartedAt675 || window.__tiSaveConfigStartedAt585 || window.__tiSaveConfigStartedAt || 0, 10) || 0);\n            if (!window.__tiConfigSaveActive675 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving') === '1')) resetButton(false);\n            if (window.__tiConfigSaveActive675 && age > 90000) { window.__tiConfigSaveActive675 = false; clearBusyUi(); resetButton(false); }\n        } catch(e) {}\n    }, 900);\n    setTimeout(forceResetOnConfigOpen, 300);\n})();\n<\/script>\n\n\n<script id=\"ti-file-manager-meta-save-676\">\n(function(){\n    \"use strict\";\n    if (window.__tiFileManagerMetaSave676Installed) return;\n    window.__tiFileManagerMetaSave676Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function trim(v){ return txt(v).trim(); }\n    function decodeEntities(v){\n        var s = txt(v).replace(\/&amp;\/g, '&').replace(\/&#038;\/g, '&');\n        try { var ta = document.createElement('textarea'); ta.innerHTML = s; s = ta.value; } catch(e) {}\n        return s;\n    }\n    function basenameFromRef(ref){\n        var s = decodeEntities(ref).replace(\/\\\\\/g, '\/').trim();\n        try {\n            if (\/ti_action=ti_ai_get_image\/i.test(s)) {\n                var u = new URL(s, \/^https?:\\\/\\\/\/i.test(s) ? undefined : window.location.origin);\n                var f = u.searchParams.get('file') || '';\n                if (f) return decodeURIComponent(f).split(\/[\\\\\/]\/).pop();\n            }\n        } catch(e) {}\n        s = s.split(\/[?#]\/)[0].replace(\/\\\/+$\/, '');\n        return (s.split(\/[\\\\\/]\/).pop() || '').trim();\n    }\n    function normFile(v){ return basenameFromRef(v).toLowerCase(); }\n    function refsFrom(v){\n        if (window.tiTokenizeFileRefs666) return window.tiTokenizeFileRefs666(v);\n        return txt(v).split(\/[,;\\r\\n]+\/).map(function(x){ return x.trim(); }).filter(Boolean);\n    }\n    function removeRefFromValue(raw, file){\n        var targetWhole = trim(decodeEntities(file)).toLowerCase();\n        var targetBase = normFile(file);\n        return refsFrom(raw).filter(function(one){\n            var oneWhole = trim(decodeEntities(one)).toLowerCase();\n            var oneBase = normFile(one);\n            if (targetWhole && oneWhole === targetWhole) return false;\n            if (targetBase && oneBase && oneBase === targetBase) return false;\n            return true;\n        }).join(', ');\n    }\n    window.tiApplyPendingLinkedFileRemovals676 = function(){\n        var list = Array.isArray(window.pendingLinkedFileRemovals) ? window.pendingLinkedFileRemovals : [];\n        if (!list.length || !window.currentDbData || !window.currentDbData.Tabelle) return 0;\n        var changed = 0;\n        list.forEach(function(item){\n            if (!item) return;\n            var tbl = trim(item.tbl), key = trim(item.key), file = trim(item.file), idx = parseInt(item.idx, 10);\n            if (!tbl || !key || !file || isNaN(idx)) return;\n            var realTbl = (window.resolveConfigTableName ? (window.resolveConfigTableName(tbl) || tbl) : tbl);\n            if (!window.currentDbData.Tabelle[realTbl] || !window.currentDbData.Tabelle[realTbl][idx]) return;\n            var row = window.currentDbData.Tabelle[realTbl][idx];\n            var realKey = Object.prototype.hasOwnProperty.call(row, key) ? key : Object.keys(row).find(function(k){ return String(k).toLowerCase() === String(key).toLowerCase(); }) || key;\n            var oldVal = txt(row[realKey]);\n            var newVal = removeRefFromValue(oldVal, file);\n            if (oldVal !== newVal) {\n                row[realKey] = newVal;\n                changed++;\n            } else {\n                row[realKey] = newVal;\n            }\n            try { window.currentDbViewData = null; } catch(e) {}\n            if (!window.modifiedFields) window.modifiedFields = new Set();\n            try { window.modifiedFields.add(realTbl + '-' + idx + '-' + realKey); } catch(e) {}\n            try { if (window.syncConfigTableAfterRowMutation) window.syncConfigTableAfterRowMutation(realTbl); } catch(e) {}\n            try {\n                var cssEsc = window.CSS && CSS.escape ? CSS.escape : function(v){ return String(v).replace(\/([\\\\\"'\\[\\]#.:>+~*^$|=!])\/g, '\\\\$1'); };\n                document.querySelectorAll('#ti-conf-ov [data-tbl=\"' + cssEsc(realTbl) + '\"][data-idx=\"' + cssEsc(idx) + '\"][data-key=\"' + cssEsc(realKey) + '\"]').forEach(function(inp){ if (inp) inp.value = newVal; });\n            } catch(e) {}\n        });\n        if (changed && window.updateConfigSaveButtonState) { try { window.updateConfigSaveButtonState(); } catch(e) {} }\n        return changed;\n    };\n    if (typeof window.saveConfig === 'function' && !window.saveConfig.__tiPendingLinkedFileRemovals676) {\n        var prevSave676 = window.saveConfig;\n        window.saveConfig = function(){\n            try { window.tiApplyPendingLinkedFileRemovals676(); } catch(e) {}\n            return prevSave676.apply(this, arguments);\n        };\n        window.saveConfig.__tiPendingLinkedFileRemovals676 = true;\n    }\n    window.tiRefreshFileManagerMeta666 = function(){\n        var nodes = Array.prototype.slice.call(document.querySelectorAll('#fm-grid [data-ti-file-size-url]'));\n        nodes.forEach(function(el){\n            if (!el || el.getAttribute('data-ti-size-loading') === '1') return;\n            var url = el.getAttribute('data-ti-file-size-url') || '';\n            var ref = el.getAttribute('data-ti-file-ref') || url || '';\n            var db = el.getAttribute('data-ti-file-db') || ((byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : '');\n            var tbl = el.getAttribute('data-ti-file-tbl') || ((window.targetUploadContext && window.targetUploadContext.tbl) ? window.targetUploadContext.tbl : '');\n            var record = el.getAttribute('data-ti-file-record') || '';\n            if (!url && !ref) { el.textContent = 'n\/d'; return; }\n            el.setAttribute('data-ti-size-loading', '1');\n            el.textContent = 'calcolo...';\n            var fd = new FormData();\n            fd.append('ti_action', 'ti_ai_file_meta_676');\n            fd.append('action', 'ti_ai_file_meta_676');\n            fd.append('db', db || '');\n            fd.append('tbl', tbl || '');\n            fd.append('record', record || '');\n            fd.append('ref', ref || '');\n            fd.append('url', url || '');\n            var ajaxUrl = window.tiUrl || window.tiAjaxUrl || window.location.href;\n            fetch(ajaxUrl, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store'}).then(function(r){ return r.text(); }).then(function(raw){\n                var d = null;\n                try { d = JSON.parse(raw); } catch(e) { throw new Error('meta non JSON'); }\n                if (!d || !d.success) throw new Error((d && d.data && d.data.message) || 'meta non disponibile');\n                var meta = d.data || {};\n                el.textContent = meta.size_label || (meta.size ? (window.tiFormatBytes666 ? window.tiFormatBytes666(meta.size) : String(meta.size) + ' B') : 'n\/d');\n                var item = el.closest ? el.closest('.ti-file-item') : null;\n                if (item && meta.ext) {\n                    var extBox = item.querySelector('.ti-file-meta-666 b');\n                    var all = item.querySelectorAll('.ti-file-meta-666');\n                    if (all && all[0]) all[0].innerHTML = '<b>Tipo:<\/b> ' + String(meta.ext || 'n\/d');\n                }\n            }).catch(function(){\n                if (!url) { el.textContent = 'n\/d'; return; }\n                fetch(url, {method:'GET', credentials:'same-origin', cache:'no-store'}).then(function(r){\n                    var len = r.headers ? r.headers.get('Content-Length') : '';\n                    var fmt = window.tiFormatBytes666 ? window.tiFormatBytes666(len || 0) : '';\n                    el.textContent = fmt || 'n\/d';\n                }).catch(function(){ el.textContent = 'n\/d'; });\n            }).finally(function(){ el.removeAttribute('data-ti-size-loading'); });\n        });\n    };\n    var prevOpenFileManager676 = window.openFileManager;\n    if (typeof prevOpenFileManager676 === 'function' && !prevOpenFileManager676.__tiMetaRefresh676) {\n        window.openFileManager = function(){\n            var ret = prevOpenFileManager676.apply(this, arguments);\n            setTimeout(function(){ try { window.tiRefreshFileManagerMeta666(); } catch(e) {} }, 40);\n            setTimeout(function(){ try { window.tiRefreshFileManagerMeta666(); } catch(e) {} }, 500);\n            return ret;\n        };\n        window.openFileManager.__tiMetaRefresh676 = true;\n    }\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-verified-677\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveVerified677Installed) return;\n    window.__tiConfigSaveVerified677Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function now(){ return Date.now ? Date.now() : (new Date()).getTime(); }\n    function enc(obj){\n        var json = JSON.stringify(obj || {});\n        if (typeof window.utoa === 'function') return window.utoa(json);\n        return btoa(unescape(encodeURIComponent(json)));\n    }\n    function saveButton(){ return byId('lbl-conf-save'); }\n    function setBtnSaving(){\n        var b = saveButton();\n        if (!b) return;\n        b.disabled = true;\n        b.setAttribute('aria-busy', 'true');\n        b.setAttribute('data-ti-saving', '1');\n        b.style.pointerEvents = 'none';\n        b.innerText = 'Salvataggio...';\n        try {\n            b.style.setProperty('background', '#d97706', 'important');\n            b.style.setProperty('background-color', '#d97706', 'important');\n            b.style.setProperty('color', '#fff', 'important');\n            b.style.setProperty('cursor', 'wait', 'important');\n        } catch(e) {}\n    }\n    function resetBtn(clean){\n        window.__tiConfigSaveActive675 = false;\n        window.__tiConfigSaveActive677 = false;\n        window.__saveConfigInFlight = false;\n        window.__tiSaveConfigStartedAt675 = 0;\n        window.__tiSaveConfigStartedAt585 = 0;\n        window.__tiSaveConfigStartedAt = 0;\n        try { if (window.__tiSaveConfigHardTimer585) { clearTimeout(window.__tiSaveConfigHardTimer585); window.__tiSaveConfigHardTimer585 = null; } } catch(e) {}\n        var b = saveButton();\n        if (!b) return;\n        if (clean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') {\n            try { window.modifiedFields.clear(); } catch(e) {}\n        }\n        b.disabled = false;\n        try { b.removeAttribute('disabled'); b.removeAttribute('aria-busy'); b.removeAttribute('data-ti-saving'); } catch(e) {}\n        b.style.pointerEvents = '';\n        b.classList.remove('ti-save-dirty');\n        b.classList.add('ti-save-clean');\n        b.innerText = 'Salva Modifiche';\n        try {\n            b.style.setProperty('background', '#374151', 'important');\n            b.style.setProperty('background-color', '#374151', 'important');\n            b.style.setProperty('color', '#fff', 'important');\n            b.style.setProperty('cursor', 'pointer', 'important');\n        } catch(e) {}\n    }\n    function clearBusy(){\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n        try {\n            document.querySelectorAll('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(x){ if (x && x.parentNode) x.parentNode.removeChild(x); });\n        } catch(e) {}\n    }\n    function normalizeTables(){\n        var d = window.currentDbData;\n        if (!d || !d.Tabelle) return;\n        Object.keys(d.Tabelle).forEach(function(t){\n            if (!Array.isArray(d.Tabelle[t])) d.Tabelle[t] = Object.values(d.Tabelle[t] || {});\n        });\n    }\n    function readInputValue(input){\n        if (!input) return '';\n        if (input.tagName === 'SELECT' && input.multiple) return Array.prototype.slice.call(input.selectedOptions || []).map(function(o){ return txt(o.value).trim(); }).filter(Boolean).join(', ');\n        if (input.type === 'checkbox') return input.checked ? (input.value || '1') : '';\n        return input.value == null ? '' : input.value;\n    }\n    function resolveTable(t){\n        try { return window.resolveConfigTableName ? (window.resolveConfigTableName(t) || t) : t; } catch(e) { return t; }\n    }\n    function syncVisibleFields(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        var data = window.currentDbData;\n        if (!data || !data.Tabelle) return;\n        var sel = '#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        Array.prototype.slice.call(document.querySelectorAll(sel)).forEach(function(input){\n            if (!input || input.disabled || input.readOnly) return;\n            var tbl = input.getAttribute('data-tbl') || '';\n            var key = input.getAttribute('data-key') || '';\n            var idx = parseInt(input.getAttribute('data-idx') || '', 10);\n            if (!tbl || !key || isNaN(idx)) return;\n            var rt = resolveTable(tbl);\n            if (!data.Tabelle[rt]) data.Tabelle[rt] = [];\n            if (!data.Tabelle[rt][idx] || typeof data.Tabelle[rt][idx] !== 'object') data.Tabelle[rt][idx] = {};\n            var val = readInputValue(input);\n            try { if (window.normalizeInputValueForKey) val = window.normalizeInputValueForKey(key, val); } catch(e) {}\n            var old = data.Tabelle[rt][idx][key];\n            if (txt(old) !== txt(val)) {\n                data.Tabelle[rt][idx][key] = val;\n                if (!window.modifiedFields) window.modifiedFields = new Set();\n                try { window.modifiedFields.add(rt + '-' + idx + '-' + key); } catch(e) {}\n            }\n        });\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n    }\n    function changedTables(){\n        var map = {};\n        var cur = (window.currentDbData && window.currentDbData.Tabelle) ? window.currentDbData.Tabelle : {};\n        var old = (window.configOriginalDbData && window.configOriginalDbData.Tabelle) ? window.configOriginalDbData.Tabelle : {};\n        var tables = Object.keys(cur || {}).sort(function(a,b){ return String(b).length - String(a).length; });\n        try {\n            if (window.modifiedFields && typeof window.modifiedFields.forEach === 'function') {\n                window.modifiedFields.forEach(function(k){\n                    k = txt(k);\n                    for (var i=0;i<tables.length;i++) {\n                        var t=tables[i];\n                        if (k.indexOf(t + '-') === 0) { map[t]=true; return; }\n                    }\n                    var p=k.indexOf('-');\n                    if (p > 0) map[k.slice(0,p)] = true;\n                });\n            }\n        } catch(e) {}\n        try { (window.tiConfigDeletedRowsManifest || []).forEach(function(x){ if (x && x.table) map[resolveTable(x.table)] = true; }); } catch(e) {}\n        try { Object.keys(window.tiConfigDeletedTableSnapshots || {}).forEach(function(t){ if (t) map[resolveTable(t)] = true; }); } catch(e) {}\n        var all = {};\n        Object.keys(cur || {}).forEach(function(t){ all[t]=true; });\n        Object.keys(old || {}).forEach(function(t){ all[t]=true; });\n        Object.keys(all).forEach(function(t){\n            try { if (JSON.stringify(cur[t] === undefined ? null : cur[t]) !== JSON.stringify(old[t] === undefined ? null : old[t])) map[t]=true; } catch(e) { map[t]=true; }\n        });\n        return Object.keys(map).filter(Boolean);\n    }\n    function postJson(fd, timeoutMs){\n        timeoutMs = timeoutMs || 180000;\n        if (typeof window.ensureAjaxActionField === 'function') fd = window.ensureAjaxActionField(fd);\n        else { if (!fd.has('action') && fd.has('ti_action')) fd.append('action', fd.get('ti_action')); }\n        if (typeof window.tiAppendCurrentLanguageToFormData === 'function') window.tiAppendCurrentLanguageToFormData(fd);\n        var url = window.tiUrl || window.tiAjaxUrl || window.location.href;\n        if (typeof window.postFormDataJsonSafe === 'function') {\n            return Promise.race([\n                window.postFormDataJsonSafe(url, fd, {tiNoLongProcessSignal:true, cache:'no-store'}),\n                new Promise(function(_, reject){ setTimeout(function(){ reject(new Error('Timeout salvataggio configurazione. Riprova dopo aver verificato la connessione.')); }, timeoutMs); })\n            ]);\n        }\n        return new Promise(function(resolve, reject){\n            var ctrl = (typeof AbortController !== 'undefined') ? new AbortController() : null;\n            var done = false;\n            var timer = setTimeout(function(){ if (done) return; done = true; try { if (ctrl) ctrl.abort(); } catch(e) {} reject(new Error('Timeout salvataggio configurazione.')); }, timeoutMs);\n            fetch(url, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', signal: ctrl ? ctrl.signal : undefined}).then(function(r){\n                return r.text().then(function(raw){\n                    if (!r.ok) throw new Error('Errore server ' + r.status + (raw ? ': ' + raw.slice(0,220) : ''));\n                    try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta salvataggio non JSON: ' + raw.slice(0,220)); }\n                });\n            }).then(function(data){ if (done) return; done=true; clearTimeout(timer); resolve(data); }).catch(function(err){ if (done) return; done=true; clearTimeout(timer); reject(err); });\n        });\n    }\n    function buildFullSaveForm(db, tables){\n        var fd = new FormData();\n        var data = window.currentDbData || {};\n        fd.append('action', 'ti_ai_save_full_db');\n        fd.append('ti_action', 'ti_ai_save_full_db');\n        fd.append('db', db);\n        fd.append('payload', enc(data));\n        fd.append('db_version', (data && data.__db_version) ? data.__db_version : '');\n        fd.append('changed_tables', enc(tables || []));\n        fd.append('deleted_rows', enc(window.tiConfigDeletedRowsManifest || []));\n        fd.append('deleted_table_snapshots', enc(window.tiConfigDeletedTableSnapshots || {}));\n        fd.append('deleted_rows_client_total', String(parseInt(window.tiConfigDeletedRowsCountTotal || 0, 10) || 0));\n        fd.append('force_full_save', '1');\n        fd.append('verified_save_677', '1');\n        return fd;\n    }\n    function afterSuccess(res){\n        var data = (res && res.data) ? res.data : {};\n        if (data.db && data.db.Tabelle) {\n            window.currentDbData = data.db;\n            window.currentDbViewData = null;\n        } else if (window.currentDbData && data.db_version) {\n            window.currentDbData.__db_version = data.db_version;\n        }\n        try { if (window.currentDbData) window.configOriginalDbData = JSON.parse(JSON.stringify(window.currentDbData)); } catch(e) {}\n        try { if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); } catch(e) {}\n        window.pendingLinkedFileRemovals = [];\n        window.tiConfigDeletedRowsManifest = [];\n        window.tiConfigDeletedTableSnapshots = {};\n        window.tiConfigDeletedTableSnapshotBeforeCounts = {};\n        window.tiConfigDeletedRowsCountTotal = 0;\n        window.tiLastConfigDeleteCount = 0;\n        resetBtn(true);\n        setTimeout(function(){ resetBtn(true); }, 120);\n        var warns = Array.isArray(data.value_warnings) ? data.value_warnings : [];\n        setTimeout(function(){\n            if (warns.length && window.showConfigValidationWarning) window.showConfigValidationWarning(warns, true);\n            else if (window.tiAlert) window.tiAlert('\u2705 Salvataggio configurazione completato.');\n        }, 160);\n    }\n    window.tiForceUnlockConfigSave585 = window.tiForceUnlockConfigSave587 = window.tiForceUnlockConfigSave588 = window.tiForceUnlockConfigSave677 = function(reason){\n        clearBusy();\n        resetBtn(false);\n        if (reason && window.tiAlert) window.tiAlert(reason);\n    };\n    window.saveConfig = function(){\n        var start = now();\n        if (window.__tiConfigSaveActive677 || window.__saveConfigInFlight) {\n            var age = start - (parseInt(window.__tiSaveConfigStartedAt675 || window.__tiSaveConfigStartedAt585 || window.__tiSaveConfigStartedAt || 0, 10) || 0);\n            if (age < 10000) return;\n            clearBusy();\n            resetBtn(false);\n        }\n        if (!window.currentDbData || !window.currentDbData.Tabelle) {\n            resetBtn(false);\n            if (window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.');\n            return;\n        }\n        window.__tiConfigSaveActive677 = true;\n        window.__tiConfigSaveActive675 = false;\n        window.__saveConfigInFlight = true;\n        window.__tiSaveConfigStartedAt675 = start;\n        window.__tiSaveConfigStartedAt585 = start;\n        window.__tiSaveConfigStartedAt = start;\n        setBtnSaving();\n        try {\n            syncVisibleFields();\n            \/* Importante: le rimozioni file vengono riapplicate DOPO il sync dei campi visibili,\n               cos\u00ec un input ancora renderizzato non pu\u00f2 reinserire il vecchio riferimento. *\/\n            try { if (window.tiApplyPendingLinkedFileRemovals676) window.tiApplyPendingLinkedFileRemovals676(); } catch(e) {}\n            normalizeTables();\n            try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n            if (window.validateConfigUniqueUsernames && !window.validateConfigUniqueUsernames()) throw new Error('Salvataggio bloccato: username duplicato.');\n            var issues = window.collectConfigValidationIssues ? window.collectConfigValidationIssues() : [];\n            if (issues && issues.length && window.showConfigValidationWarning) window.showConfigValidationWarning(issues, true);\n            var dbEl = byId('ti-ditta');\n            var db = dbEl ? dbEl.value : '';\n            if (!db || db === 'NEW_DB' || db === 'Global') throw new Error('Seleziona ditta.');\n            var tables = changedTables();\n            var fd = buildFullSaveForm(db, tables);\n            postJson(fd, 180000).then(function(res){\n                if (!res || !res.success) throw new Error((res && res.data && res.data.message) ? res.data.message : 'Salvataggio configurazione non riuscito.');\n                afterSuccess(res);\n            }).catch(function(err){\n                var msg = (err && err.message) ? err.message : 'Errore durante il salvataggio configurazione.';\n                if (\/Accesso negato\/i.test(msg)) msg += ' Verifica di essere ancora autenticato nel login del plugin con ruolo autorizzato.';\n                if (\/Risposta salvataggio non JSON|Risposta server non valida|non JSON\/i.test(msg)) msg += ' Possibile pagina di cache\/manutenzione o sessione scaduta.';\n                resetBtn(false);\n                if (window.tiAlert) window.tiAlert(msg); else alert(msg);\n            }).finally(function(){\n                window.__tiConfigSaveActive677 = false;\n                window.__tiConfigSaveActive675 = false;\n                window.__saveConfigInFlight = false;\n                window.__tiSaveConfigStartedAt675 = 0;\n                window.__tiSaveConfigStartedAt585 = 0;\n                window.__tiSaveConfigStartedAt = 0;\n                clearBusy();\n                var b = saveButton();\n                if (b && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving') === '1')) resetBtn(false);\n            });\n        } catch(e) {\n            window.__tiConfigSaveActive677 = false;\n            window.__tiConfigSaveActive675 = false;\n            window.__saveConfigInFlight = false;\n            clearBusy();\n            resetBtn(false);\n            if (window.tiAlert) window.tiAlert((e && e.message) ? e.message : 'Errore preparazione salvataggio configurazione.');\n        }\n    };\n    setInterval(function(){\n        try {\n            var b = saveButton();\n            if (!b) return;\n            var age = now() - (parseInt(window.__tiSaveConfigStartedAt675 || window.__tiSaveConfigStartedAt585 || window.__tiSaveConfigStartedAt || 0, 10) || 0);\n            if (!window.__tiConfigSaveActive677 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving') === '1')) resetBtn(false);\n            if (window.__tiConfigSaveActive677 && age > 190000) { clearBusy(); resetBtn(false); }\n        } catch(e) {}\n    }, 1000);\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-real-678\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveReal678Installed) return;\n    window.__tiConfigSaveReal678Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function clone(o){ try { return JSON.parse(JSON.stringify(o || {})); } catch(e) { return {}; } }\n    function enc(obj){ var json = JSON.stringify(obj || {}); if (typeof window.utoa === 'function') return window.utoa(json); return btoa(unescape(encodeURIComponent(json))); }\n    function saveButton(){ return byId('lbl-conf-save'); }\n    function clearBusy(){\n        try { window.__tiConfigSaveActive675 = false; window.__tiConfigSaveActive677 = false; window.__tiConfigSaveActive678 = false; window.__saveConfigInFlight = false; } catch(e) {}\n        try { window.__tiSaveConfigStartedAt675 = 0; window.__tiSaveConfigStartedAt585 = 0; window.__tiSaveConfigStartedAt = 0; } catch(e) {}\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n    }\n    function setButtonSaving(){\n        var b = saveButton(); if (!b) return;\n        b.disabled = true; b.setAttribute('aria-busy','true'); b.setAttribute('data-ti-saving','1'); b.style.pointerEvents='none'; b.innerText='Salvataggio...';\n        try { b.style.setProperty('background','#d97706','important'); b.style.setProperty('background-color','#d97706','important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','wait','important'); } catch(e) {}\n    }\n    function resetButton(clean){\n        clearBusy();\n        if (clean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') { try { window.modifiedFields.clear(); } catch(e) {} }\n        var b = saveButton(); if (!b) return;\n        var dirty = !!(!clean && window.modifiedFields && window.modifiedFields.size > 0);\n        b.disabled = false; try { b.removeAttribute('disabled'); b.removeAttribute('aria-busy'); b.removeAttribute('data-ti-saving'); } catch(e) {}\n        b.style.pointerEvents=''; b.classList.toggle('ti-save-dirty', dirty); b.classList.toggle('ti-save-clean', !dirty); b.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n        try { b.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','pointer','important'); } catch(e) {}\n    }\n    function readVal(el){\n        if (!el) return '';\n        if (el.tagName === 'SELECT' && el.multiple) return Array.prototype.slice.call(el.selectedOptions || []).map(function(o){ return txt(o.value).trim(); }).filter(Boolean).join(', ');\n        if (el.type === 'checkbox') return el.checked ? (el.value || '1') : '';\n        return el.value == null ? '' : el.value;\n    }\n    function resolveTable(t){ try { return window.resolveConfigTableName ? (window.resolveConfigTableName(t) || t) : t; } catch(e) { return t; } }\n    function ensureArrays(){\n        var d = window.currentDbData; if (!d || !d.Tabelle) return;\n        Object.keys(d.Tabelle).forEach(function(t){ if (!Array.isArray(d.Tabelle[t])) d.Tabelle[t] = Object.values(d.Tabelle[t] || {}); });\n    }\n    function syncVisible(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        ensureArrays();\n        var d = window.currentDbData; if (!d || !d.Tabelle) return;\n        var sel = '#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        Array.prototype.slice.call(document.querySelectorAll(sel)).forEach(function(input){\n            if (!input || input.disabled || input.readOnly) return;\n            var tbl = input.getAttribute('data-tbl') || '', key = input.getAttribute('data-key') || '', idx = parseInt(input.getAttribute('data-idx') || '', 10);\n            if (!tbl || !key || isNaN(idx)) return;\n            var rt = resolveTable(tbl); if (!d.Tabelle[rt]) d.Tabelle[rt] = []; if (!d.Tabelle[rt][idx] || typeof d.Tabelle[rt][idx] !== 'object') d.Tabelle[rt][idx] = {};\n            var val = readVal(input); try { if (window.normalizeInputValueForKey) val = window.normalizeInputValueForKey(key, val); } catch(e) {}\n            if (txt(d.Tabelle[rt][idx][key]) !== txt(val)) { d.Tabelle[rt][idx][key] = val; if (!window.modifiedFields) window.modifiedFields = new Set(); try { window.modifiedFields.add(rt + '-' + idx + '-' + key); } catch(e) {} }\n        });\n        try { if (window.tiApplyPendingLinkedFileRemovals676) window.tiApplyPendingLinkedFileRemovals676(); } catch(e) {}\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n    }\n    function changedTablesFromFields(changes){ var m={}; (changes||[]).forEach(function(c){ if(c && c.table) m[c.table]=true; }); return Object.keys(m); }\n    function parseModifiedField(raw, tables){\n        raw = txt(raw); if (!raw) return null;\n        for (var i=0;i<tables.length;i++) { var t=tables[i], pfx=String(t)+'-'; if (raw.indexOf(pfx) === 0) { var rest=raw.slice(pfx.length), p=rest.indexOf('-'); if (p<=0) return null; var idx=parseInt(rest.slice(0,p),10); if (isNaN(idx)) return null; return {table:t,row:idx,key:rest.slice(p+1)}; } }\n        return null;\n    }\n    function buildFieldChanges(){\n        ensureArrays();\n        var cur = (window.currentDbData && window.currentDbData.Tabelle) ? window.currentDbData.Tabelle : {};\n        var old = (window.configOriginalDbData && window.configOriginalDbData.Tabelle) ? window.configOriginalDbData.Tabelle : {};\n        var tables = Object.keys(cur || {}).sort(function(a,b){ return String(b).length - String(a).length; });\n        var changes=[], seen={}, fullNeeded=false;\n        function add(table,row,key,value){\n            if (!table || row < 0 || !key || String(key).indexOf('__') === 0) { fullNeeded=true; return; }\n            var sig = table + '\\u0001' + row + '\\u0001' + key;\n            if (seen[sig]) return; seen[sig]=true;\n            if (value && typeof value === 'object') { fullNeeded=true; return; }\n            changes.push({table:table,row:row,key:key,value:(value == null ? '' : value)});\n        }\n        try {\n            if (window.modifiedFields && typeof window.modifiedFields.forEach === 'function') {\n                window.modifiedFields.forEach(function(raw){ var p=parseModifiedField(raw,tables); if (!p) { fullNeeded=true; return; } var r=(cur[p.table]||[])[p.row]; if (!r || typeof r !== 'object') { fullNeeded=true; return; } add(p.table,p.row,p.key,r[p.key]); });\n            }\n        } catch(e) { fullNeeded=true; }\n        try {\n            var all={}; Object.keys(cur||{}).forEach(function(t){all[t]=true;}); Object.keys(old||{}).forEach(function(t){all[t]=true;});\n            Object.keys(all).forEach(function(t){\n                var cr=Array.isArray(cur[t])?cur[t]:Object.values(cur[t]||{}), or=Array.isArray(old[t])?old[t]:Object.values(old[t]||{});\n                if (cr.length !== or.length) { fullNeeded=true; return; }\n                for (var i=0;i<cr.length;i++) {\n                    var a=cr[i]||{}, b=or[i]||{}; if (!a || typeof a !== 'object' || !b || typeof b !== 'object') { fullNeeded=true; return; }\n                    var keys={}; Object.keys(a).forEach(function(k){keys[k]=true;}); Object.keys(b).forEach(function(k){keys[k]=true;});\n                    Object.keys(keys).forEach(function(k){ if (JSON.stringify(a[k] == null ? '' : a[k]) !== JSON.stringify(b[k] == null ? '' : b[k])) add(t,i,k,a[k]); });\n                }\n            });\n        } catch(e) { fullNeeded=true; }\n        try { if ((window.tiConfigDeletedRowsManifest||[]).length || Object.keys(window.tiConfigDeletedTableSnapshots||{}).length || parseInt(window.tiConfigDeletedRowsCountTotal||0,10)>0) fullNeeded=true; } catch(e) {}\n        if (changes.length > 1000) fullNeeded=true;\n        return {changes:changes, fullNeeded:fullNeeded};\n    }\n    function postJson(fd, timeoutMs){\n        timeoutMs = timeoutMs || 120000;\n        try { if (!fd.has('action') && fd.has('ti_action')) fd.append('action', fd.get('ti_action')); } catch(e) {}\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        var url = window.tiUrl || window.location.href;\n        return new Promise(function(resolve,reject){\n            var ctrl = (typeof AbortController !== 'undefined') ? new AbortController() : null, done=false;\n            var timer = setTimeout(function(){ if(done) return; done=true; try{ if(ctrl) ctrl.abort(); }catch(e){} reject(new Error('Timeout salvataggio configurazione.')); }, timeoutMs);\n            fetch(url,{method:'POST',body:fd,credentials:'same-origin',cache:'no-store',signal:ctrl?ctrl.signal:undefined}).then(function(r){ return r.text().then(function(raw){ if(!r.ok) throw new Error('Errore server '+r.status+': '+(raw||'').slice(0,220)); try{return JSON.parse(raw);}catch(e){throw new Error('Risposta salvataggio non JSON: '+(raw||'').slice(0,220));} }); }).then(function(d){ if(done) return; done=true; clearTimeout(timer); resolve(d); }).catch(function(e){ if(done) return; done=true; clearTimeout(timer); reject(e); });\n        });\n    }\n    function postFieldSave(db, changes){ var fd=new FormData(); fd.append('ti_action','ti_ai_save_config_fields_678'); fd.append('action','ti_ai_save_config_fields_678'); fd.append('db',db); fd.append('changes',enc(changes)); fd.append('db_version',(window.currentDbData&&window.currentDbData.__db_version)||''); return postJson(fd,120000); }\n    function buildCompactPayload(tables){\n        var d=window.currentDbData||{}, payload={Database:d.Database||'', __db_version:d.__db_version||'', Tabelle:{}};\n        (tables&&tables.length?tables:Object.keys((d&&d.Tabelle)||{})).forEach(function(t){ if(d.Tabelle && d.Tabelle[t]) payload.Tabelle[t]=d.Tabelle[t]; });\n        return payload;\n    }\n    function postFullSave(db, changes){ var tables=changedTablesFromFields(changes); if(!tables.length) tables=Object.keys((window.currentDbData&&window.currentDbData.Tabelle)||{}); var fd=new FormData(); fd.append('ti_action','ti_ai_save_full_db'); fd.append('action','ti_ai_save_full_db'); fd.append('db',db); fd.append('payload',enc(buildCompactPayload(tables))); fd.append('compact_save','1'); fd.append('light_save','1'); fd.append('response_light','1'); fd.append('save_mode','light'); fd.append('changed_tables',enc(tables)); fd.append('deleted_rows',enc(window.tiConfigDeletedRowsManifest||[])); fd.append('deleted_table_snapshots',enc(window.tiConfigDeletedTableSnapshots||{})); fd.append('deleted_rows_client_total',String(parseInt(window.tiConfigDeletedRowsCountTotal||0,10)||0)); fd.append('db_version',''); fd.append('stable_save_678','1'); return postJson(fd,180000); }\n    function success(res){\n        var data=(res&&res.data)?res.data:{};\n        if (window.currentDbData && data.db_version) window.currentDbData.__db_version=data.db_version;\n        try { if (window.currentDbData) window.configOriginalDbData=clone(window.currentDbData); } catch(e) {}\n        try { if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); } catch(e) {}\n        window.pendingLinkedFileRemovals=[]; window.tiConfigDeletedRowsManifest=[]; window.tiConfigDeletedTableSnapshots={}; window.tiConfigDeletedRowsCountTotal=0;\n        resetButton(true);\n        var warns=Array.isArray(data.value_warnings)?data.value_warnings:[];\n        setTimeout(function(){ if (warns.length && window.showConfigValidationWarning) window.showConfigValidationWarning(warns,true); else if (window.tiAlert) window.tiAlert('\u2705 Salvataggio configurazione completato.'); }, 100);\n    }\n    window.saveConfig = function(){\n        var started=Date.now?Date.now():(new Date()).getTime();\n        if (window.__tiConfigSaveActive678 || window.__saveConfigInFlight) { clearBusy(); }\n        if (!window.currentDbData || !window.currentDbData.Tabelle) { resetButton(false); if (window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.'); return; }\n        var dbEl=byId('ti-ditta'), db=dbEl?dbEl.value:''; if(!db || db==='NEW_DB' || db==='Global'){ resetButton(false); if(window.tiAlert) window.tiAlert('Seleziona ditta.'); return; }\n        window.__tiConfigSaveActive678=true; window.__saveConfigInFlight=true; window.__tiSaveConfigStartedAt=started; window.__tiSaveConfigStartedAt675=started; window.__tiSaveConfigStartedAt585=started; setButtonSaving();\n        try { syncVisible(); } catch(e) { resetButton(false); if(window.tiAlert) window.tiAlert('Errore preparazione salvataggio: '+(e.message||e)); return; }\n        var built=buildFieldChanges();\n        if (!built.changes.length && !built.fullNeeded) { resetButton(true); if(window.tiAlert) window.tiAlert('Nessuna modifica da salvare.'); return; }\n        \/\/ 30.9.682: usa sempre il salvataggio full compatto. L endpoint puntuale resta disponibile, ma il full compatto e piu affidabile per associazioni file, rimozioni e campi sincronizzati.\n        var p = postFullSave(db,built.changes);\n        p.then(function(res){ if(!res || !res.success) throw new Error((res&&res.data&&res.data.message)?res.data.message:'Salvataggio configurazione non riuscito.'); success(res); })\n         .catch(function(err){ var msg=(err&&err.message)?err.message:'Errore durante il salvataggio configurazione.'; resetButton(false); if(window.tiAlert) window.tiAlert(msg); else alert(msg); })\n         .finally(function(){ window.__tiConfigSaveActive678=false; window.__saveConfigInFlight=false; window.__tiSaveConfigStartedAt=0; window.__tiSaveConfigStartedAt675=0; window.__tiSaveConfigStartedAt585=0; clearBusy(); var b=saveButton(); if(b && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); });\n    };\n    window.tiForceUnlockConfigSave678 = function(reason){ resetButton(false); if(reason && window.tiAlert) window.tiAlert(reason); };\n    setInterval(function(){ try { var b=saveButton(); if(!b) return; if(!window.__tiConfigSaveActive678 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); } catch(e){} }, 1000);\n})();\n<\/script>\n\n\n<style id=\"ti-config-single-bottom-hscroll-681\">\n\/* 30.9.681 - Configurazione: evita doppio cursore orizzontale in basso.\n   Resta visibile il cursore nativo della tabella; il cursore sintetico inferiore viene nascosto. *\/\n#ti-conf-ov .ti-cfg-bottom-scroll,\n#ti-conf-ov .ti-sync-hscroll-bottom,\n#ti-ai-outer #ti-conf-ov .ti-cfg-bottom-scroll,\n#ti-ai-outer #ti-conf-ov .ti-sync-hscroll-bottom {\n  display:none !important;\n  height:0 !important;\n  min-height:0 !important;\n  max-height:0 !important;\n  padding:0 !important;\n  margin:0 !important;\n  border:0 !important;\n  overflow:hidden !important;\n}\n#ti-conf-ov .ti-cfg-table-container,\n#ti-conf-ov .ti-sync-hscroll-wrap,\n#ti-ai-outer #ti-conf-ov .ti-cfg-table-container,\n#ti-ai-outer #ti-conf-ov .ti-sync-hscroll-wrap {\n  margin-bottom:0 !important;\n}\n<\/style>\n\n\n\n<script id=\"ti-config-save-real-682\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveReal682Installed) return;\n    window.__tiConfigSaveReal682Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function clone(o){ try { return JSON.parse(JSON.stringify(o || {})); } catch(e) { return {}; } }\n    function enc(obj){ var json = JSON.stringify(obj || {}); if (typeof window.utoa === 'function') return window.utoa(json); return btoa(unescape(encodeURIComponent(json))); }\n    function saveButton(){ return byId('lbl-conf-save'); }\n    function clearBusy(){\n        try { window.__tiConfigSaveActive675=false; window.__tiConfigSaveActive677=false; window.__tiConfigSaveActive678=false; window.__tiConfigSaveActive682=false; window.__saveConfigInFlight=false; } catch(e) {}\n        try { window.__tiSaveConfigStartedAt=0; window.__tiSaveConfigStartedAt585=0; window.__tiSaveConfigStartedAt675=0; } catch(e) {}\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n    }\n    function setSaving(){\n        var b=saveButton(); if(!b) return;\n        b.disabled=true; b.setAttribute('aria-busy','true'); b.setAttribute('data-ti-saving','1'); b.style.pointerEvents='none'; b.innerText='Salvataggio...';\n        try { b.style.setProperty('background','#d97706','important'); b.style.setProperty('background-color','#d97706','important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','wait','important'); } catch(e) {}\n    }\n    function resetButton(clean){\n        if (clean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') { try { window.modifiedFields.clear(); } catch(e) {} }\n        clearBusy();\n        var b=saveButton(); if(!b) return;\n        var dirty = !!(!clean && window.modifiedFields && window.modifiedFields.size>0);\n        b.disabled=false; try { b.removeAttribute('disabled'); b.removeAttribute('aria-busy'); b.removeAttribute('data-ti-saving'); } catch(e) {}\n        b.style.pointerEvents=''; b.classList.toggle('ti-save-dirty',dirty); b.classList.toggle('ti-save-clean',!dirty); b.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n        try { b.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','pointer','important'); } catch(e) {}\n    }\n    function readVal(el){\n        if(!el) return '';\n        if(el.tagName==='SELECT' && el.multiple) return Array.prototype.slice.call(el.selectedOptions||[]).map(function(o){return txt(o.value).trim();}).filter(Boolean).join(', ');\n        if(el.type==='checkbox') return el.checked ? (el.value || '1') : '';\n        return el.value == null ? '' : el.value;\n    }\n    function resolveTable(t){ try { return window.resolveConfigTableName ? (window.resolveConfigTableName(t)||t) : t; } catch(e) { return t; } }\n    function ensureArrays(){\n        var d=window.currentDbData; if(!d || !d.Tabelle) return;\n        Object.keys(d.Tabelle).forEach(function(t){ if(!Array.isArray(d.Tabelle[t])) d.Tabelle[t]=Object.values(d.Tabelle[t]||{}); });\n    }\n    function syncVisible(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        ensureArrays();\n        var d=window.currentDbData; if(!d || !d.Tabelle) return;\n        var sel='#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        Array.prototype.slice.call(document.querySelectorAll(sel)).forEach(function(input){\n            if(!input || input.disabled || input.readOnly) return;\n            var tbl=input.getAttribute('data-tbl')||'', key=input.getAttribute('data-key')||'', idx=parseInt(input.getAttribute('data-idx')||'',10);\n            if(!tbl || !key || isNaN(idx)) return;\n            var rt=resolveTable(tbl); if(!d.Tabelle[rt]) d.Tabelle[rt]=[]; if(!d.Tabelle[rt][idx] || typeof d.Tabelle[rt][idx] !== 'object') d.Tabelle[rt][idx]={};\n            var val=readVal(input); try { if(window.normalizeInputValueForKey) val=window.normalizeInputValueForKey(key,val); } catch(e) {}\n            if(txt(d.Tabelle[rt][idx][key]) !== txt(val)) { d.Tabelle[rt][idx][key]=val; if(!window.modifiedFields) window.modifiedFields=new Set(); try { window.modifiedFields.add(rt+'-'+idx+'-'+key); } catch(e) {} }\n        });\n        try { if (window.tiApplyPendingLinkedFileRemovals676) window.tiApplyPendingLinkedFileRemovals676(); } catch(e) {}\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n    }\n    function modifiedTables(){\n        var d=(window.currentDbData&&window.currentDbData.Tabelle)?window.currentDbData.Tabelle:{};\n        var o=(window.configOriginalDbData&&window.configOriginalDbData.Tabelle)?window.configOriginalDbData.Tabelle:{};\n        var tables={}, names={};\n        Object.keys(d||{}).forEach(function(t){names[t]=true;}); Object.keys(o||{}).forEach(function(t){names[t]=true;});\n        Object.keys(names).forEach(function(t){ try { if (JSON.stringify(d[t]||[]) !== JSON.stringify(o[t]||[])) tables[t]=true; } catch(e) { tables[t]=true; } });\n        try {\n            if (window.modifiedFields && typeof window.modifiedFields.forEach === 'function') {\n                var all=Object.keys(d||{}).sort(function(a,b){return String(b).length-String(a).length;});\n                window.modifiedFields.forEach(function(raw){ raw=txt(raw); all.forEach(function(t){ if(raw.indexOf(t+'-')===0) tables[t]=true; }); });\n            }\n        } catch(e) {}\n        try { (window.tiConfigDeletedRowsManifest||[]).forEach(function(x){ if(x && x.table) tables[resolveTable(x.table)]=true; }); } catch(e) {}\n        try { Object.keys(window.tiConfigDeletedTableSnapshots||{}).forEach(function(t){ tables[resolveTable(t)]=true; }); } catch(e) {}\n        return Object.keys(tables);\n    }\n    function buildPayload(tables){\n        var d=window.currentDbData||{}, all=(d&&d.Tabelle)?d.Tabelle:{};\n        var list=(tables&&tables.length)?tables:Object.keys(all||{});\n        var payload={Database:d.Database||'', __db_version:d.__db_version||'', Tabelle:{}};\n        list.forEach(function(t){ if(all && Object.prototype.hasOwnProperty.call(all,t)) payload.Tabelle[t]=all[t]; });\n        return payload;\n    }\n    function postFull(db,tables){\n        var fd=new FormData();\n        fd.append('ti_action','ti_ai_save_full_db'); fd.append('action','ti_ai_save_full_db'); fd.append('db',db);\n        fd.append('payload',enc(buildPayload(tables))); fd.append('compact_save','1'); fd.append('light_save','1'); fd.append('response_light','1'); fd.append('save_mode','light');\n        fd.append('changed_tables',enc(tables||[])); fd.append('deleted_rows',enc(window.tiConfigDeletedRowsManifest||[])); fd.append('deleted_table_snapshots',enc(window.tiConfigDeletedTableSnapshots||{}));\n        fd.append('deleted_rows_client_total',String(parseInt(window.tiConfigDeletedRowsCountTotal||0,10)||0)); fd.append('db_version',''); fd.append('stable_save_682','1'); fd.append('verify_after_save_682','1');\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        var url=window.tiUrl || window.location.href.split('?')[0];\n        return new Promise(function(resolve,reject){\n            var done=false, ctrl=(typeof AbortController!=='undefined')?new AbortController():null;\n            var timer=setTimeout(function(){ if(done) return; done=true; try{ if(ctrl) ctrl.abort(); }catch(e){} reject(new Error('Timeout salvataggio configurazione. Riprova o ricarica la pagina.')); }, 180000);\n            fetch(url,{method:'POST',body:fd,credentials:'same-origin',cache:'no-store',signal:ctrl?ctrl.signal:undefined}).then(function(r){ return r.text().then(function(raw){ if(!r.ok) throw new Error('Errore server '+r.status+': '+(raw||'').slice(0,260)); var d=null; try{ d=JSON.parse(raw); }catch(e){ throw new Error('Risposta salvataggio non JSON: '+(raw||'').slice(0,260)); } return d; }); }).then(function(d){ if(done) return; done=true; clearTimeout(timer); resolve(d); }).catch(function(e){ if(done) return; done=true; clearTimeout(timer); reject(e); });\n        });\n    }\n    window.saveConfig = function(){\n        if (!window.currentDbData || !window.currentDbData.Tabelle) { resetButton(false); if(window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.'); return; }\n        var dbEl=byId('ti-ditta'), db=dbEl ? (dbEl.value||'') : ''; if(!db || db==='NEW_DB' || db==='Global') { resetButton(false); if(window.tiAlert) window.tiAlert('Seleziona ditta.'); return; }\n        try { window.__tiConfigSaveActive682=true; window.__saveConfigInFlight=true; window.__tiSaveConfigStartedAt=Date.now?Date.now():(new Date()).getTime(); setSaving(); syncVisible(); } catch(e) { resetButton(false); if(window.tiAlert) window.tiAlert('Errore preparazione salvataggio: '+(e.message||e)); return; }\n        var tables=modifiedTables();\n        var hasDeleted=false; try { hasDeleted = ((window.tiConfigDeletedRowsManifest||[]).length>0 || Object.keys(window.tiConfigDeletedTableSnapshots||{}).length>0 || parseInt(window.tiConfigDeletedRowsCountTotal||0,10)>0); } catch(e) {}\n        if (!tables.length && !hasDeleted) { resetButton(true); if(window.tiAlert) window.tiAlert('Nessuna modifica da salvare.'); return; }\n        postFull(db,tables).then(function(res){\n            if(!res || !res.success) throw new Error((res&&res.data&&res.data.message)?res.data.message:'Salvataggio configurazione non riuscito.');\n            var data=res.data||{}; if(window.currentDbData && data.db_version) window.currentDbData.__db_version=data.db_version;\n            try { window.configOriginalDbData=clone(window.currentDbData); } catch(e) {}\n            window.pendingLinkedFileRemovals=[]; window.tiConfigDeletedRowsManifest=[]; window.tiConfigDeletedTableSnapshots={}; window.tiConfigDeletedRowsCountTotal=0;\n            resetButton(true);\n            var warns=Array.isArray(data.value_warnings)?data.value_warnings:[];\n            setTimeout(function(){ if(warns.length && window.showConfigValidationWarning) window.showConfigValidationWarning(warns,true); else if(window.tiAlert) window.tiAlert('\u2705 Salvataggio configurazione completato.'); },100);\n        }).catch(function(err){\n            resetButton(false);\n            if(window.tiAlert) window.tiAlert((err&&err.message)?err.message:'Errore durante il salvataggio configurazione.');\n        }).finally(function(){ clearBusy(); var b=saveButton(); if(b && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); });\n    };\n    window.tiForceUnlockConfigSave682=function(reason){ resetButton(false); if(reason&&window.tiAlert) window.tiAlert(reason); };\n    setInterval(function(){ try { var b=saveButton(); if(!b) return; if(!window.__tiConfigSaveActive682 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); } catch(e){} }, 1000);\n})();\n<\/script>\n\n<script id=\"ti-file-manager-meta-682\">\n(function(){\n    \"use strict\";\n    if (window.__tiFileManagerMeta682Installed) return;\n    window.__tiFileManagerMeta682Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function fmt(bytes){\n        bytes = parseInt(bytes || 0, 10) || 0;\n        if (bytes < 0) bytes = 0;\n        if (window.tiFormatBytes666) { try { var f = window.tiFormatBytes666(bytes); if (f) return f; } catch(e) {} }\n        if (bytes < 1024) return bytes + ' B';\n        var units=['KB','MB','GB','TB'], v=bytes\/1024, i=0;\n        while(v>=1024 && i<units.length-1){ v\/=1024; i++; }\n        var dec = v>=10 ? 1 : 2;\n        return String(v.toFixed(dec)).replace('.', ',') + ' ' + units[i];\n    }\n    function postMeta(fd, timeoutMs){\n        timeoutMs = timeoutMs || 20000;\n        try { if (!fd.has('action')) fd.append('action','ti_ai_file_meta_676'); } catch(e) {}\n        var url = window.tiUrl || window.location.href.split('?')[0];\n        return new Promise(function(resolve,reject){\n            var done=false, ctrl=(typeof AbortController!=='undefined')?new AbortController():null;\n            var timer=setTimeout(function(){ if(done) return; done=true; try{ if(ctrl) ctrl.abort(); }catch(e){} reject(new Error('timeout meta')); }, timeoutMs);\n            fetch(url,{method:'POST',body:fd,credentials:'same-origin',cache:'no-store',signal:ctrl?ctrl.signal:undefined}).then(function(r){\n                return r.text().then(function(raw){\n                    var d=null; try { d=JSON.parse(raw); } catch(e) { throw new Error('meta non JSON'); }\n                    if(!d || !d.success) throw new Error((d&&d.data&&d.data.message)||'meta non disponibile');\n                    return d.data || {};\n                });\n            }).then(function(data){ if(done) return; done=true; clearTimeout(timer); resolve(data); }).catch(function(err){ if(done) return; done=true; clearTimeout(timer); reject(err); });\n        });\n    }\n    function fetchSizeFromUrl(url, timeoutMs){\n        timeoutMs = timeoutMs || 20000;\n        if (!url) return Promise.reject(new Error('url mancante'));\n        function one(method){\n            return new Promise(function(resolve,reject){\n                var done=false, ctrl=(typeof AbortController!=='undefined')?new AbortController():null;\n                var timer=setTimeout(function(){ if(done) return; done=true; try{ if(ctrl) ctrl.abort(); }catch(e){} reject(new Error('timeout url')); }, timeoutMs);\n                fetch(url,{method:method,credentials:'same-origin',cache:'no-store',signal:ctrl?ctrl.signal:undefined}).then(function(r){\n                    if(!r.ok) throw new Error('http '+r.status);\n                    var len = r.headers && r.headers.get ? (r.headers.get('Content-Length') || r.headers.get('content-length') || '') : '';\n                    if (len && parseInt(len,10)>0) return parseInt(len,10);\n                    if (method === 'HEAD') throw new Error('no length');\n                    return r.blob().then(function(b){ return b && b.size ? b.size : 0; });\n                }).then(function(size){ if(done) return; done=true; clearTimeout(timer); resolve(size); }).catch(function(err){ if(done) return; done=true; clearTimeout(timer); reject(err); });\n            });\n        }\n        return one('HEAD').catch(function(){ return one('GET'); });\n    }\n    window.tiRefreshFileManagerMeta666 = function(){\n        var nodes = Array.prototype.slice.call(document.querySelectorAll('#fm-grid [data-ti-file-size-url],#fm-grid [data-ti-file-ref]'));\n        nodes.forEach(function(el){\n            if (!el) return;\n            var started = parseInt(el.getAttribute('data-ti-size-started') || '0', 10) || 0;\n            var now = Date.now ? Date.now() : (new Date()).getTime();\n            if (el.getAttribute('data-ti-size-loading') === '1' && started && (now - started) < 22000) return;\n            var url = el.getAttribute('data-ti-file-size-url') || '';\n            var ref = el.getAttribute('data-ti-file-ref') || url || '';\n            var db = el.getAttribute('data-ti-file-db') || ((byId('ti-ditta') && byId('ti-ditta').value) ? byId('ti-ditta').value : '');\n            var tbl = el.getAttribute('data-ti-file-tbl') || ((window.targetUploadContext && window.targetUploadContext.tbl) ? window.targetUploadContext.tbl : '');\n            var record = el.getAttribute('data-ti-file-record') || '';\n            if (!url && !ref) { el.textContent = 'n\/d'; return; }\n            el.setAttribute('data-ti-size-loading','1');\n            el.setAttribute('data-ti-size-started', String(now));\n            el.textContent = 'calcolo...';\n            var fd = new FormData();\n            fd.append('ti_action','ti_ai_file_meta_676');\n            fd.append('action','ti_ai_file_meta_676');\n            fd.append('db', db || '');\n            fd.append('tbl', tbl || '');\n            fd.append('record', record || '');\n            fd.append('ref', ref || '');\n            fd.append('url', url || '');\n            postMeta(fd, 18000).then(function(meta){\n                var size = parseInt(meta.size || 0, 10) || 0;\n                var label = meta.size_label || (size > 0 ? fmt(size) : '');\n                if (!label || label === 'n\/d' || label === '0 B') {\n                    if (url) return fetchSizeFromUrl(url, 20000).then(function(sz){ return {meta:meta, label: sz>0 ? fmt(sz) : (label || 'n\/d')}; });\n                }\n                return {meta:meta, label:label || 'n\/d'};\n            }).catch(function(){\n                if (!url) return {meta:null, label:'n\/d'};\n                return fetchSizeFromUrl(url, 20000).then(function(sz){ return {meta:null, label: sz>0 ? fmt(sz) : 'n\/d'}; }).catch(function(){ return {meta:null, label:'n\/d'}; });\n            }).then(function(res){\n                el.textContent = (res && res.label) ? res.label : 'n\/d';\n                var meta = res && res.meta ? res.meta : null;\n                var item = el.closest ? el.closest('.ti-file-item') : null;\n                if (item && meta && meta.ext) {\n                    var all = item.querySelectorAll('.ti-file-meta-666');\n                    if (all && all[0]) all[0].innerHTML = '<b>Tipo:<\/b> ' + String(meta.ext || 'n\/d');\n                }\n            }).finally(function(){\n                el.removeAttribute('data-ti-size-loading');\n                el.removeAttribute('data-ti-size-started');\n                if (\/calcolo\/i.test(txt(el.textContent))) el.textContent = 'n\/d';\n            });\n        });\n    };\n    if (typeof window.openFileManager === 'function' && !window.openFileManager.__tiMeta682) {\n        var prev = window.openFileManager;\n        window.openFileManager = function(){\n            var ret = prev.apply(this, arguments);\n            [50,600,1600].forEach(function(ms){ setTimeout(function(){ try { window.tiRefreshFileManagerMeta666(); } catch(e) {} }, ms); });\n            return ret;\n        };\n        window.openFileManager.__tiMeta682 = true;\n    }\n    setTimeout(function(){\n        try { document.querySelectorAll('#fm-grid [data-ti-file-size-url]').forEach(function(el){ if (\/calcolo|in corso\/i.test(txt(el.textContent))) el.textContent='dimensione non disponibile'; }); } catch(e) {}\n    }, 9000);\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-and-meta-683\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveAndMeta683Installed) return;\n    window.__tiConfigSaveAndMeta683Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function qsa(sel, root){ return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }\n    function enc(obj){\n        var json = JSON.stringify(obj || {});\n        if (typeof window.utoa === 'function') return window.utoa(json);\n        return btoa(unescape(encodeURIComponent(json)));\n    }\n    function clone(o){ return JSON.parse(JSON.stringify(o || {})); }\n    function saveButton(){ return byId('lbl-conf-save'); }\n    function setSaving(){\n        var b = saveButton(); if (!b) return;\n        b.disabled = true;\n        b.setAttribute('aria-busy','true');\n        b.setAttribute('data-ti-saving','1');\n        b.style.pointerEvents = 'none';\n        b.innerText = 'Salvataggio...';\n        try { b.style.setProperty('background','#d97706','important'); b.style.setProperty('background-color','#d97706','important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','wait','important'); } catch(e) {}\n    }\n    function resetSaveButton(clean){\n        window.__tiConfigSaveActive682 = false;\n        window.__tiConfigSaveActive683 = false;\n        window.__saveConfigInFlight = false;\n        window.__tiSaveConfigStartedAt = 0;\n        window.__tiSaveConfigStartedAt585 = 0;\n        window.__tiSaveConfigStartedAt675 = 0;\n        var b = saveButton(); if (!b) return;\n        if (clean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') { try { window.modifiedFields.clear(); } catch(e) {} }\n        var dirty = !!(!clean && window.modifiedFields && window.modifiedFields.size > 0);\n        b.disabled = false;\n        try { b.removeAttribute('disabled'); b.removeAttribute('aria-busy'); b.removeAttribute('data-ti-saving'); } catch(e) {}\n        b.style.pointerEvents = '';\n        b.classList.toggle('ti-save-dirty', dirty);\n        b.classList.toggle('ti-save-clean', !dirty);\n        b.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n        try { b.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','pointer','important'); } catch(e) {}\n    }\n    function clearBusy(){\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n        try { qsa('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(x){ if (x && x.parentNode) x.parentNode.removeChild(x); }); } catch(e) {}\n    }\n    function readVal(el){\n        if (!el) return '';\n        if (el.tagName === 'SELECT' && el.multiple) return Array.prototype.slice.call(el.selectedOptions || []).map(function(o){ return txt(o.value).trim(); }).filter(Boolean).join(', ');\n        if (el.type === 'checkbox') return el.checked ? (el.value || '1') : '';\n        return el.value == null ? '' : el.value;\n    }\n    function resolveTable(t){ try { return window.resolveConfigTableName ? (window.resolveConfigTableName(t) || t) : t; } catch(e) { return t; } }\n    function syncVisibleToData(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        var d = window.currentDbData;\n        if (!d || !d.Tabelle) return [];\n        Object.keys(d.Tabelle).forEach(function(t){ if (!Array.isArray(d.Tabelle[t])) d.Tabelle[t] = Object.values(d.Tabelle[t] || {}); });\n        var changed = {};\n        var sel = '#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        qsa(sel).forEach(function(input){\n            if (!input || input.disabled || input.readOnly) return;\n            var tbl = input.getAttribute('data-tbl') || '', key = input.getAttribute('data-key') || '', idx = parseInt(input.getAttribute('data-idx') || '', 10);\n            if (!tbl || !key || isNaN(idx)) return;\n            var rt = resolveTable(tbl);\n            if (!d.Tabelle[rt]) d.Tabelle[rt] = [];\n            if (!d.Tabelle[rt][idx] || typeof d.Tabelle[rt][idx] !== 'object') d.Tabelle[rt][idx] = {};\n            var val = readVal(input);\n            try { if (window.normalizeInputValueForKey) val = window.normalizeInputValueForKey(key, val); } catch(e) {}\n            if (txt(d.Tabelle[rt][idx][key]) !== txt(val)) {\n                d.Tabelle[rt][idx][key] = val;\n                changed[rt] = true;\n                if (!window.modifiedFields) window.modifiedFields = new Set();\n                try { window.modifiedFields.add(rt + '-' + idx + '-' + key); } catch(e) {}\n            }\n        });\n        try { if (window.tiApplyPendingLinkedFileRemovals676) { var n = window.tiApplyPendingLinkedFileRemovals676(); if (n && window.targetUploadContext && window.targetUploadContext.tbl) changed[resolveTable(window.targetUploadContext.tbl)] = true; } } catch(e) {}\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n        return Object.keys(changed);\n    }\n    function changedTables(localChanged){\n        var out = {}, cur = (window.currentDbData && window.currentDbData.Tabelle) ? window.currentDbData.Tabelle : {}, old = (window.configOriginalDbData && window.configOriginalDbData.Tabelle) ? window.configOriginalDbData.Tabelle : {};\n        (localChanged || []).forEach(function(t){ if (t) out[t] = true; });\n        try { if (window.modifiedFields && typeof window.modifiedFields.forEach === 'function') {\n            var names = Object.keys(cur || {}).sort(function(a,b){ return String(b).length - String(a).length; });\n            window.modifiedFields.forEach(function(raw){ raw = txt(raw); names.forEach(function(t){ if (raw.indexOf(t + '-') === 0) out[t] = true; }); });\n        }} catch(e) {}\n        var all = {}; Object.keys(cur || {}).forEach(function(t){ all[t] = true; }); Object.keys(old || {}).forEach(function(t){ all[t] = true; });\n        Object.keys(all).forEach(function(t){ try { if (JSON.stringify(cur[t] || []) !== JSON.stringify(old[t] || [])) out[t] = true; } catch(e) { out[t] = true; } });\n        try { (window.tiConfigDeletedRowsManifest || []).forEach(function(x){ if (x && x.table) out[resolveTable(x.table)] = true; }); } catch(e) {}\n        try { Object.keys(window.tiConfigDeletedTableSnapshots || {}).forEach(function(t){ if (t) out[resolveTable(t)] = true; }); } catch(e) {}\n        return Object.keys(out);\n    }\n    function postSnapshot(fd, timeoutMs){\n        timeoutMs = timeoutMs || 180000;\n        var url = window.tiUrl || window.tiAjaxUrl || window.location.href.split('?')[0];\n        return new Promise(function(resolve, reject){\n            var done = false, ctrl = (typeof AbortController !== 'undefined') ? new AbortController() : null;\n            var timer = setTimeout(function(){ if (done) return; done = true; try { if (ctrl) ctrl.abort(); } catch(e) {} reject(new Error('Timeout salvataggio configurazione. Il server non ha concluso la scrittura.')); }, timeoutMs);\n            fetch(url, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', signal:ctrl ? ctrl.signal : undefined}).then(function(r){\n                return r.text().then(function(raw){\n                    if (!r.ok) throw new Error('Errore server ' + r.status + ': ' + (raw || '').slice(0, 260));\n                    try { return JSON.parse(raw); } catch(e) { throw new Error('Risposta salvataggio non JSON: ' + (raw || '').slice(0, 260)); }\n                });\n            }).then(function(d){ if (done) return; done = true; clearTimeout(timer); resolve(d); }).catch(function(e){ if (done) return; done = true; clearTimeout(timer); reject(e); });\n        });\n    }\n    window.saveConfig = function(){\n        if (!window.currentDbData || !window.currentDbData.Tabelle) { resetSaveButton(false); if (window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.'); return; }\n        var dbEl = byId('ti-ditta'), db = dbEl ? (dbEl.value || '') : '';\n        if (!db || db === 'NEW_DB' || db === 'Global') { resetSaveButton(false); if (window.tiAlert) window.tiAlert('Seleziona ditta.'); return; }\n        setSaving();\n        window.__tiConfigSaveActive683 = true;\n        window.__saveConfigInFlight = true;\n        window.__tiSaveConfigStartedAt = Date.now ? Date.now() : (new Date()).getTime();\n        var localChanged = [];\n        try { localChanged = syncVisibleToData(); } catch(e) { resetSaveButton(false); if (window.tiAlert) window.tiAlert('Errore preparazione salvataggio: ' + (e.message || e)); return; }\n        var tables = changedTables(localChanged);\n        if (!tables.length) tables = Object.keys((window.currentDbData && window.currentDbData.Tabelle) || {});\n        var fd = new FormData();\n        fd.append('ti_action', 'ti_ai_save_config_snapshot_683');\n        fd.append('action', 'ti_ai_save_config_snapshot_683');\n        fd.append('db', db);\n        fd.append('payload', enc(window.currentDbData));\n        fd.append('changed_tables', enc(tables));\n        fd.append('deleted_rows', enc(window.tiConfigDeletedRowsManifest || []));\n        fd.append('deleted_table_snapshots', enc(window.tiConfigDeletedTableSnapshots || {}));\n        fd.append('deleted_rows_client_total', String(parseInt(window.tiConfigDeletedRowsCountTotal || 0, 10) || 0));\n        fd.append('snapshot_save_683', '1');\n        try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        postSnapshot(fd, 180000).then(function(res){\n            if (!res || !res.success) throw new Error((res && res.data && res.data.message) ? res.data.message : 'Salvataggio configurazione non riuscito.');\n            var data = res.data || {};\n            if (window.currentDbData && data.db_version) window.currentDbData.__db_version = data.db_version;\n            try { window.configOriginalDbData = clone(window.currentDbData); } catch(e) {}\n            try { if (window.modifiedFields && typeof window.modifiedFields.clear === 'function') window.modifiedFields.clear(); } catch(e) {}\n            window.pendingLinkedFileRemovals = [];\n            window.tiConfigDeletedRowsManifest = [];\n            window.tiConfigDeletedTableSnapshots = {};\n            window.tiConfigDeletedRowsCountTotal = 0;\n            resetSaveButton(true);\n            var warns = Array.isArray(data.value_warnings) ? data.value_warnings : [];\n            setTimeout(function(){\n                if (warns.length && window.showConfigValidationWarning) window.showConfigValidationWarning(warns, true);\n                else if (window.tiAlert) window.tiAlert('\u2705 Salvataggio configurazione completato.');\n            }, 80);\n        }).catch(function(err){\n            resetSaveButton(false);\n            if (window.tiAlert) window.tiAlert((err && err.message) ? err.message : 'Errore durante il salvataggio configurazione.');\n        }).finally(function(){\n            clearBusy();\n            window.__tiConfigSaveActive683 = false;\n            window.__saveConfigInFlight = false;\n            var b = saveButton();\n            if (b && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving') === '1')) resetSaveButton(false);\n        });\n    };\n    function fmt(bytes){\n        bytes = parseInt(bytes || 0, 10) || 0;\n        if (bytes <= 0) return 'dimensione non disponibile';\n        if (window.tiFormatBytes666) { try { var f = window.tiFormatBytes666(bytes); if (f) return f; } catch(e) {} }\n        if (bytes < 1024) return bytes + ' B';\n        var units = ['KB','MB','GB','TB'], v = bytes \/ 1024, i = 0;\n        while (v >= 1024 && i < units.length - 1) { v \/= 1024; i++; }\n        return String(v.toFixed(v >= 10 ? 1 : 2)).replace('.', ',') + ' ' + units[i];\n    }\n    function metaPost(el){\n        var fd = new FormData();\n        fd.append('ti_action','ti_ai_file_meta_683'); fd.append('action','ti_ai_file_meta_683');\n        fd.append('db', el.getAttribute('data-ti-file-db') || (byId('ti-ditta') ? (byId('ti-ditta').value || '') : ''));\n        fd.append('tbl', el.getAttribute('data-ti-file-tbl') || ((window.targetUploadContext && window.targetUploadContext.tbl) ? window.targetUploadContext.tbl : ''));\n        fd.append('record', el.getAttribute('data-ti-file-record') || '');\n        fd.append('ref', el.getAttribute('data-ti-file-ref') || '');\n        fd.append('url', el.getAttribute('data-ti-file-size-url') || '');\n        var url = window.tiUrl || window.location.href.split('?')[0];\n        return new Promise(function(resolve, reject){\n            var done = false, ctrl = (typeof AbortController !== 'undefined') ? new AbortController() : null;\n            var timer = setTimeout(function(){ if (done) return; done = true; try { if (ctrl) ctrl.abort(); } catch(e) {} reject(new Error('timeout meta')); }, 22000);\n            fetch(url, {method:'POST', body:fd, credentials:'same-origin', cache:'no-store', signal:ctrl ? ctrl.signal : undefined}).then(function(r){ return r.text().then(function(raw){ var d; try { d = JSON.parse(raw); } catch(e) { throw new Error('meta non JSON'); } if (!d || !d.success) throw new Error((d && d.data && d.data.message) || 'meta non disponibile'); return d.data || {}; }); }).then(function(d){ if (done) return; done = true; clearTimeout(timer); resolve(d); }).catch(function(e){ if (done) return; done = true; clearTimeout(timer); reject(e); });\n        });\n    }\n    function browserSize(url){\n        if (!url) return Promise.reject(new Error('url mancante'));\n        function head(){ return fetch(url, {method:'HEAD', credentials:'same-origin', cache:'no-store'}).then(function(r){ if (!r.ok) throw new Error('http ' + r.status); var len = r.headers && r.headers.get ? (r.headers.get('Content-Length') || r.headers.get('content-length') || '') : ''; var n = parseInt(len || '0', 10) || 0; if (n > 0) return n; throw new Error('length assente'); }); }\n        function get(){ return fetch(url, {method:'GET', credentials:'same-origin', cache:'no-store'}).then(function(r){ if (!r.ok) throw new Error('http ' + r.status); var len = r.headers && r.headers.get ? (r.headers.get('Content-Length') || r.headers.get('content-length') || '') : ''; var n = parseInt(len || '0', 10) || 0; if (n > 0) return n; return r.blob().then(function(b){ return b && b.size ? b.size : 0; }); }); }\n        return head().catch(get);\n    }\n    window.tiRefreshFileManagerMeta666 = function(){\n        qsa('#fm-grid [data-ti-file-size-url],#fm-grid [data-ti-file-ref]').forEach(function(el){\n            if (!el) return;\n            var now = Date.now ? Date.now() : (new Date()).getTime();\n            var loadingAt = parseInt(el.getAttribute('data-ti-size-loading-at') || '0', 10) || 0;\n            if (el.getAttribute('data-ti-size-loading') === '1' && loadingAt && (now - loadingAt) < 25000) return;\n            var url = el.getAttribute('data-ti-file-size-url') || '';\n            el.setAttribute('data-ti-size-loading','1');\n            el.setAttribute('data-ti-size-loading-at', String(now));\n            el.textContent = 'calcolo...';\n            metaPost(el).then(function(meta){\n                var size = parseInt(meta.size || 0, 10) || 0;\n                var label = meta.size_label || (size > 0 ? fmt(size) : '');\n                if ((!size || \/non disponibile|n\\\/d\/i.test(label)) && url) return browserSize(url).then(function(n){ return {meta:meta, label:n > 0 ? fmt(n) : (label || 'dimensione non disponibile')}; }).catch(function(){ return {meta:meta, label:label || 'dimensione non disponibile'}; });\n                return {meta:meta, label:label || 'dimensione non disponibile'};\n            }).catch(function(){\n                if (!url) return {meta:null, label:'dimensione non disponibile'};\n                return browserSize(url).then(function(n){ return {meta:null, label:n > 0 ? fmt(n) : 'dimensione non disponibile'}; }).catch(function(){ return {meta:null, label:'dimensione non disponibile'}; });\n            }).then(function(res){\n                el.textContent = (res && res.label) ? res.label : 'dimensione non disponibile';\n                var meta = res && res.meta ? res.meta : null;\n                var item = el.closest ? el.closest('.ti-file-item') : null;\n                if (item && meta && meta.ext) {\n                    var all = item.querySelectorAll('.ti-file-meta-666');\n                    if (all && all[0]) all[0].innerHTML = '<b>Tipo:<\/b> ' + txt(meta.ext || 'n\/d');\n                }\n            }).finally(function(){\n                el.removeAttribute('data-ti-size-loading');\n                el.removeAttribute('data-ti-size-loading-at');\n                if (\/calcolo\/i.test(txt(el.textContent))) el.textContent = 'dimensione non disponibile';\n            });\n        });\n    };\n    if (typeof window.openFileManager === 'function' && !window.openFileManager.__tiMeta683) {\n        var oldOpen = window.openFileManager;\n        window.openFileManager = function(){\n            var ret = oldOpen.apply(this, arguments);\n            [80, 700, 1800, 4500].forEach(function(ms){ setTimeout(function(){ try { window.tiRefreshFileManagerMeta666(); } catch(e) {} }, ms); });\n            return ret;\n        };\n        window.openFileManager.__tiMeta683 = true;\n    }\n    setTimeout(function(){\n        try { qsa('#fm-grid [data-ti-file-size-url],#fm-grid [data-ti-file-ref]').forEach(function(el){ if (\/calcolo|in corso\/i.test(txt(el.textContent))) el.textContent='dimensione non disponibile'; }); } catch(e) {}\n    }, 9000);\n    setInterval(function(){\n        try { var b = saveButton(); if (b && !window.__tiConfigSaveActive683 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving') === '1')) resetSaveButton(false); } catch(e) {}\n    }, 1000);\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-and-meta-684\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveAndMeta684Installed) return;\n    window.__tiConfigSaveAndMeta684Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function qsa(sel, root){ return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }\n    function clone(o){ try { return JSON.parse(JSON.stringify(o || {})); } catch(e) { return {}; } }\n    function enc(obj){ var json = JSON.stringify(obj || {}); if (typeof window.utoa === 'function') return window.utoa(json); return btoa(unescape(encodeURIComponent(json))); }\n    function saveButton(){ return byId('lbl-conf-save'); }\n    function clearBusy(){\n        try { window.__tiConfigSaveActive675=false; window.__tiConfigSaveActive677=false; window.__tiConfigSaveActive678=false; window.__tiConfigSaveActive682=false; window.__tiConfigSaveActive683=false; window.__tiConfigSaveActive684=false; window.__saveConfigInFlight=false; } catch(e) {}\n        try { window.__tiSaveConfigStartedAt=0; window.__tiSaveConfigStartedAt585=0; window.__tiSaveConfigStartedAt675=0; } catch(e) {}\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n        try { qsa('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(x){ if (x && x.parentNode) x.parentNode.removeChild(x); }); } catch(e) {}\n    }\n    function setSaving(){\n        var b=saveButton(); if(!b) return;\n        b.disabled=true; b.setAttribute('aria-busy','true'); b.setAttribute('data-ti-saving','1'); b.style.pointerEvents='none'; b.innerText='Salvataggio...';\n        try { b.style.setProperty('background','#d97706','important'); b.style.setProperty('background-color','#d97706','important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','wait','important'); } catch(e) {}\n    }\n    function resetButton(clean){\n        if (clean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') { try { window.modifiedFields.clear(); } catch(e) {} }\n        clearBusy();\n        var b=saveButton(); if(!b) return;\n        var dirty = !!(!clean && window.modifiedFields && window.modifiedFields.size>0);\n        b.disabled=false; try { b.removeAttribute('disabled'); b.removeAttribute('aria-busy'); b.removeAttribute('data-ti-saving'); } catch(e) {}\n        b.style.pointerEvents=''; b.classList.toggle('ti-save-dirty',dirty); b.classList.toggle('ti-save-clean',!dirty); b.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n        try { b.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','pointer','important'); } catch(e) {}\n    }\n    function readVal(el){\n        if(!el) return '';\n        if(el.tagName==='SELECT' && el.multiple) return Array.prototype.slice.call(el.selectedOptions||[]).map(function(o){return txt(o.value).trim();}).filter(Boolean).join(', ');\n        if(el.type==='checkbox') return el.checked ? (el.value || '1') : '';\n        return el.value == null ? '' : el.value;\n    }\n    function resolveTable(t){ try { return window.resolveConfigTableName ? (window.resolveConfigTableName(t)||t) : t; } catch(e) { return t; } }\n    function ensureArrays(){ var d=window.currentDbData; if(!d || !d.Tabelle) return; Object.keys(d.Tabelle).forEach(function(t){ if(!Array.isArray(d.Tabelle[t])) d.Tabelle[t]=Object.values(d.Tabelle[t]||{}); }); }\n    function syncVisible(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        ensureArrays();\n        var d=window.currentDbData; if(!d || !d.Tabelle) return;\n        var sel='#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        qsa(sel).forEach(function(input){\n            if(!input || input.disabled || input.readOnly) return;\n            var tbl=input.getAttribute('data-tbl')||'', key=input.getAttribute('data-key')||'', idx=parseInt(input.getAttribute('data-idx')||'',10);\n            if(!tbl || !key || isNaN(idx)) return;\n            var rt=resolveTable(tbl); if(!d.Tabelle[rt]) d.Tabelle[rt]=[]; if(!d.Tabelle[rt][idx] || typeof d.Tabelle[rt][idx] !== 'object') d.Tabelle[rt][idx]={};\n            var val=readVal(input); try { if (window.normalizeInputValueForKey) val=window.normalizeInputValueForKey(key,val); } catch(e) {}\n            if(txt(d.Tabelle[rt][idx][key]) !== txt(val)) { d.Tabelle[rt][idx][key]=val; if(!window.modifiedFields) window.modifiedFields=new Set(); try { window.modifiedFields.add(rt+'-'+idx+'-'+key); } catch(e) {} }\n        });\n        try { if (window.tiApplyPendingLinkedFileRemovals676) window.tiApplyPendingLinkedFileRemovals676(); } catch(e) {}\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n    }\n    function parseModifiedField(raw, tables){ raw=txt(raw); if(!raw) return null; for(var i=0;i<tables.length;i++){ var t=tables[i], pfx=String(t)+'-'; if(raw.indexOf(pfx)===0){ var rest=raw.slice(pfx.length), p=rest.indexOf('-'); if(p<=0) return null; var idx=parseInt(rest.slice(0,p),10); if(isNaN(idx)) return null; return {table:t,row:idx,key:rest.slice(p+1)}; } } return null; }\n    function buildFieldChanges(){\n        ensureArrays();\n        var cur=(window.currentDbData&&window.currentDbData.Tabelle)?window.currentDbData.Tabelle:{};\n        var old=(window.configOriginalDbData&&window.configOriginalDbData.Tabelle)?window.configOriginalDbData.Tabelle:{};\n        var tables=Object.keys(cur||{}).sort(function(a,b){return String(b).length-String(a).length;});\n        var changes=[], seen={}, fullNeeded=false;\n        function add(table,row,key,value){\n            if(!table || row<0 || !key || String(key).indexOf('__')===0){ fullNeeded=true; return; }\n            var sig=table+'\\u0001'+row+'\\u0001'+key; if(seen[sig]) return; seen[sig]=true;\n            if(value && typeof value==='object'){ fullNeeded=true; return; }\n            changes.push({table:table,row:row,key:key,value:(value==null?'':value)});\n        }\n        try { if(window.modifiedFields && typeof window.modifiedFields.forEach==='function') window.modifiedFields.forEach(function(raw){ var p=parseModifiedField(raw,tables); if(!p){ fullNeeded=true; return; } var r=(cur[p.table]||[])[p.row]; if(!r || typeof r!=='object'){ fullNeeded=true; return; } add(p.table,p.row,p.key,r[p.key]); }); } catch(e){ fullNeeded=true; }\n        try {\n            var all={}; Object.keys(cur||{}).forEach(function(t){all[t]=true;}); Object.keys(old||{}).forEach(function(t){all[t]=true;});\n            Object.keys(all).forEach(function(t){\n                var cr=Array.isArray(cur[t])?cur[t]:Object.values(cur[t]||{}), or=Array.isArray(old[t])?old[t]:Object.values(old[t]||{});\n                if(cr.length!==or.length){ fullNeeded=true; return; }\n                for(var i=0;i<cr.length;i++){\n                    var a=cr[i]||{}, b=or[i]||{}; if(!a || typeof a!=='object' || !b || typeof b!=='object'){ fullNeeded=true; return; }\n                    var keys={}; Object.keys(a).forEach(function(k){keys[k]=true;}); Object.keys(b).forEach(function(k){keys[k]=true;});\n                    Object.keys(keys).forEach(function(k){ if(JSON.stringify(a[k]==null?'':a[k]) !== JSON.stringify(b[k]==null?'':b[k])) add(t,i,k,a[k]); });\n                }\n            });\n        } catch(e){ fullNeeded=true; }\n        try { if((window.tiConfigDeletedRowsManifest||[]).length || Object.keys(window.tiConfigDeletedTableSnapshots||{}).length || parseInt(window.tiConfigDeletedRowsCountTotal||0,10)>0) fullNeeded=true; } catch(e) {}\n        if(changes.length>1000) fullNeeded=true;\n        return {changes:changes, fullNeeded:fullNeeded};\n    }\n    function postJson(fd, timeoutMs){\n        timeoutMs=timeoutMs||90000; try { if(!fd.has('action') && fd.has('ti_action')) fd.append('action', fd.get('ti_action')); } catch(e) {}\n        try { if(window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n        var url=window.tiUrl || window.tiAjaxUrl || window.location.href.split('?')[0];\n        return new Promise(function(resolve,reject){\n            var ctrl=(typeof AbortController!=='undefined')?new AbortController():null, done=false;\n            var timer=setTimeout(function(){ if(done) return; done=true; try{ if(ctrl) ctrl.abort(); }catch(e){} reject(new Error('Timeout salvataggio configurazione. Salvataggio non confermato dal server.')); }, timeoutMs);\n            fetch(url,{method:'POST',body:fd,credentials:'same-origin',cache:'no-store',signal:ctrl?ctrl.signal:undefined}).then(function(r){ return r.text().then(function(raw){ if(!r.ok) throw new Error('Errore server '+r.status+': '+(raw||'').slice(0,260)); try{return JSON.parse(raw);}catch(e){throw new Error('Risposta salvataggio non JSON: '+(raw||'').slice(0,260));} }); }).then(function(d){ if(done) return; done=true; clearTimeout(timer); resolve(d); }).catch(function(e){ if(done) return; done=true; clearTimeout(timer); reject(e); });\n        });\n    }\n    function postFieldSave(db, changes){ var fd=new FormData(); fd.append('ti_action','ti_ai_save_config_fields_690'); fd.append('action','ti_ai_save_config_fields_690'); fd.append('db',db); fd.append('changes',enc(changes)); fd.append('field_save_690','1'); return postJson(fd,30000); }\n    function changedTablesFromChanges(changes){ var m={}; (changes||[]).forEach(function(c){ if(c&&c.table)m[c.table]=true; }); return Object.keys(m); }\n    function buildCompactPayload(tables){ var d=window.currentDbData||{}, payload={Database:d.Database||'', __db_version:d.__db_version||'', Tabelle:{}}; (tables&&tables.length?tables:Object.keys((d&&d.Tabelle)||{})).forEach(function(t){ if(d.Tabelle && d.Tabelle[t]) payload.Tabelle[t]=d.Tabelle[t]; }); return payload; }\n    function postCompactFullSave(db, changes){\n        var tables=changedTablesFromChanges(changes); if(!tables.length) tables=Object.keys((window.currentDbData&&window.currentDbData.Tabelle)||{});\n        var fd=new FormData(); fd.append('ti_action','ti_ai_save_full_db'); fd.append('action','ti_ai_save_full_db'); fd.append('db',db); fd.append('payload',enc(buildCompactPayload(tables))); fd.append('compact_save','1'); fd.append('light_save','1'); fd.append('response_light','1'); fd.append('save_mode','light'); fd.append('changed_tables',enc(tables)); fd.append('deleted_rows',enc(window.tiConfigDeletedRowsManifest||[])); fd.append('deleted_table_snapshots',enc(window.tiConfigDeletedTableSnapshots||{})); fd.append('deleted_rows_client_total',String(parseInt(window.tiConfigDeletedRowsCountTotal||0,10)||0)); fd.append('db_version',''); fd.append('compact_save_684','1'); return postJson(fd,150000);\n    }\n    function onSuccess(res){\n        var data=(res&&res.data)?res.data:{}; if(window.currentDbData && data.db_version) window.currentDbData.__db_version=data.db_version;\n        try { if(window.currentDbData) window.configOriginalDbData=clone(window.currentDbData); } catch(e) {}\n        try { if(window.modifiedFields && typeof window.modifiedFields.clear==='function') window.modifiedFields.clear(); } catch(e) {}\n        window.pendingLinkedFileRemovals=[]; window.tiConfigDeletedRowsManifest=[]; window.tiConfigDeletedTableSnapshots={}; window.tiConfigDeletedRowsCountTotal=0;\n        resetButton(true);\n        var warns=Array.isArray(data.value_warnings)?data.value_warnings:[];\n        setTimeout(function(){ if(warns.length && window.showConfigValidationWarning) window.showConfigValidationWarning(warns,true); else if(window.tiAlert) window.tiAlert('\u2705 Salvataggio configurazione completato.'); },80);\n    }\n    window.saveConfig = function(){\n        if(!window.currentDbData || !window.currentDbData.Tabelle){ resetButton(false); if(window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.'); return; }\n        var dbEl=byId('ti-ditta'), db=dbEl?(dbEl.value||''):''; if(!db || db==='NEW_DB' || db==='Global'){ resetButton(false); if(window.tiAlert) window.tiAlert('Seleziona ditta.'); return; }\n        clearBusy(); setSaving(); window.__tiConfigSaveActive684=true; window.__saveConfigInFlight=true; window.__tiSaveConfigStartedAt=Date.now?Date.now():(new Date()).getTime();\n        var built;\n        try { syncVisible(); built=buildFieldChanges(); } catch(e){ resetButton(false); if(window.tiAlert) window.tiAlert('Errore preparazione salvataggio: '+(e.message||e)); return; }\n        if(!built.changes.length && !built.fullNeeded){ window.__tiConfigSaveActive692=false; window.__saveConfigInFlight=false; clearBusy(); resetButton(true); log692('no_changes_after_build_699', {modifiedFields: window.modifiedFields && window.modifiedFields.size ? window.modifiedFields.size : 0, removals:Array.isArray(window.pendingLinkedFileRemovals)?window.pendingLinkedFileRemovals.length:0}, 'info'); if(window.tiAlert) window.tiAlert('Nessuna modifica da salvare.'); return; }\n        var promise = (!built.fullNeeded && built.changes.length <= 1000) ? postFieldSave(db,built.changes) : postCompactFullSave(db,built.changes);\n        promise.then(function(res){ if(!res || !res.success) throw new Error((res&&res.data&&res.data.message)?res.data.message:'Salvataggio configurazione non riuscito.'); onSuccess(res); })\n        .catch(function(err){ resetButton(false); var msg=(err&&err.message)?err.message:'Errore durante il salvataggio configurazione.'; if(window.tiAlert) window.tiAlert(msg); else alert(msg); })\n        .finally(function(){ window.__tiConfigSaveActive684=false; window.__saveConfigInFlight=false; clearBusy(); var b=saveButton(); if(b && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); });\n    };\n    window.tiForceUnlockConfigSave684=function(reason){ resetButton(false); if(reason && window.tiAlert) window.tiAlert(reason); };\n    function fmt(bytes){ bytes=parseInt(bytes||0,10)||0; if(bytes<=0) return 'dimensione non disponibile'; if(window.tiFormatBytes666){ try{ var f=window.tiFormatBytes666(bytes); if(f) return f; }catch(e){} } if(bytes<1024) return bytes+' B'; var units=['KB','MB','GB','TB'], v=bytes\/1024, i=0; while(v>=1024 && i<units.length-1){v\/=1024;i++;} return String(v.toFixed(v>=10?1:2)).replace('.',',')+' '+units[i]; }\n    function timeoutPromise(ms,label){ return new Promise(function(_,reject){ setTimeout(function(){ reject(new Error(label||'timeout')); }, ms); }); }\n    function fetchHeadSize(url){ if(!url) return Promise.reject(new Error('url mancante')); var ctrl=(typeof AbortController!=='undefined')?new AbortController():null; var t=setTimeout(function(){ try{ if(ctrl) ctrl.abort(); }catch(e){} },7000); return fetch(url,{method:'HEAD',credentials:'same-origin',cache:'no-store',signal:ctrl?ctrl.signal:undefined}).then(function(r){ clearTimeout(t); if(!r.ok) throw new Error('http '+r.status); var len=r.headers&&r.headers.get?(r.headers.get('Content-Length')||r.headers.get('content-length')||''):''; var n=parseInt(len||'0',10)||0; if(n>0) return n; throw new Error('content length assente'); }); }\n    function postMeta(el){\n        var fd=new FormData(); fd.append('ti_action','ti_ai_file_meta_684'); fd.append('action','ti_ai_file_meta_684'); fd.append('db',el.getAttribute('data-ti-file-db')||(byId('ti-ditta')?(byId('ti-ditta').value||''):'')); fd.append('tbl',el.getAttribute('data-ti-file-tbl')||((window.targetUploadContext&&window.targetUploadContext.tbl)?window.targetUploadContext.tbl:'')); fd.append('record',el.getAttribute('data-ti-file-record')||''); fd.append('ref',el.getAttribute('data-ti-file-ref')||''); fd.append('url',el.getAttribute('data-ti-file-size-url')||'');\n        return postJson(fd,6000).then(function(d){ if(!d||!d.success) throw new Error((d&&d.data&&d.data.message)||'meta non disponibile'); return d.data||{}; });\n    }\n    window.tiRefreshFileManagerMeta666 = function(){\n        qsa('#fm-grid [data-ti-file-size-url],#fm-grid [data-ti-file-ref]').forEach(function(el){\n            if(!el) return; var current=txt(el.textContent); var now=Date.now?Date.now():(new Date()).getTime(); var started=parseInt(el.getAttribute('data-ti-size-loading-at')||'0',10)||0;\n            if(el.getAttribute('data-ti-size-loading')==='1' && started && (now-started)<18000) return;\n            var url=el.getAttribute('data-ti-file-size-url')||'';\n            el.setAttribute('data-ti-size-loading','1'); el.setAttribute('data-ti-size-loading-at',String(now));\n            if(!current || \/calcolo|n\\\/d|non disponibile\/i.test(current)) el.textContent='dimensione in corso...';\n            Promise.race([\n                postMeta(el).then(function(meta){ var n=parseInt(meta.size||0,10)||0; if(n>0) return {label:fmt(n), meta:meta}; if(meta.size_label && !\/non disponibile|n\\\/d\/i.test(meta.size_label)) return {label:meta.size_label, meta:meta}; throw new Error('meta senza dimensione'); }),\n                timeoutPromise(6500,'timeout meta')\n            ]).catch(function(){ return url ? fetchHeadSize(url).then(function(n){ return {label:n>0?fmt(n):'dimensione non disponibile', meta:null}; }) : {label:'dimensione non disponibile', meta:null}; })\n            .catch(function(){ return {label:'dimensione non disponibile', meta:null}; })\n            .then(function(res){ el.textContent=(res&&res.label)?res.label:'dimensione non disponibile'; var meta=res&&res.meta?res.meta:null, item=el.closest?el.closest('.ti-file-item'):null; if(item&&meta&&meta.ext){ var all=item.querySelectorAll('.ti-file-meta-666'); if(all&&all[0]) all[0].innerHTML='<b>Tipo:<\/b> '+txt(meta.ext||'n\/d'); } })\n            .finally(function(){ el.removeAttribute('data-ti-size-loading'); el.removeAttribute('data-ti-size-loading-at'); if(\/calcolo|in corso\/i.test(txt(el.textContent))) el.textContent='dimensione non disponibile'; });\n        });\n    };\n    if(typeof window.openFileManager==='function' && !window.openFileManager.__tiMeta684){ var prev=window.openFileManager; window.openFileManager=function(){ var ret=prev.apply(this,arguments); [120,1500].forEach(function(ms){ setTimeout(function(){ try{ window.tiRefreshFileManagerMeta666(); }catch(e){} },ms); }); return ret; }; window.openFileManager.__tiMeta684=true; }\n    setTimeout(function(){ try{ qsa('#fm-grid [data-ti-file-size-url],#fm-grid [data-ti-file-ref]').forEach(function(el){ if(\/calcolo|in corso\/i.test(txt(el.textContent))) el.textContent='dimensione non disponibile'; }); }catch(e){} },9000);\n    setInterval(function(){ try{ var b=saveButton(); if(b && !window.__tiConfigSaveActive684 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); }catch(e){} },1000);\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-diagnostic-687\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveDiagnostic687Installed) return;\n    window.__tiConfigSaveDiagnostic687Installed = true;\n    if (!window.tiConfigSaveLogAllowed) {\n        window.tiShowConfigSaveDiag687 = function(){ return false; };\n        window.tiCopyConfigSaveDiag687 = function(){ return false; };\n        return;\n    }\n    var nativeFetch = window.fetch ? window.fetch.bind(window) : null;\n    var activeTrace = null;\n    var activeStarted = 0;\n    var seq = 0;\n    var maxRows = 260;\n    var lastHeartbeat = 0;\n    var wrapTimer = null;\n    window.__tiConfigSaveDiagLog687 = window.__tiConfigSaveDiagLog687 || [];\n    function nowMs(){ return Date.now ? Date.now() : (new Date()).getTime(); }\n    function iso(){ try { return new Date().toISOString(); } catch(e) { return String(new Date()); } }\n    function txt(v){ return String(v == null ? '' : v); }\n    function byId(id){ return document.getElementById(id); }\n    function short(v,n){ v=txt(v); n=n||300; return v.length>n ? v.slice(0,n)+'...' : v; }\n    function safeJson(v,n){ try { return short(JSON.stringify(v), n || 600); } catch(e) { return short(v, n || 600); } }\n    function dbName(){ var el=byId('ti-ditta'); return el ? (el.value || '') : ''; }\n    function htmlEsc(v){ return txt(v).replace(\/[&<>\"']\/g,function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function ensurePanel(){\n        var el=byId('ti-config-save-diag-687');\n        if (el) return el;\n        el=document.createElement('div');\n        el.id='ti-config-save-diag-687';\n        el.setAttribute('role','dialog');\n        el.setAttribute('aria-live','polite');\n        el.style.cssText='position:fixed;left:50%;top:12px;transform:translateX(-50%);width:min(980px,calc(100vw - 18px));max-height:82vh;z-index:2147483647;background:#020617;color:#e5e7eb;border:2px solid #38bdf8;border-radius:14px;box-shadow:0 22px 70px rgba(0,0,0,.72);font:11px\/1.38 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:none;overflow:hidden;pointer-events:auto;';\n        el.innerHTML='<div style=\"display:flex;gap:7px;align-items:center;padding:8px 9px;background:#0f172a;border-bottom:1px solid #334155;\">'\n            + '<b style=\"color:#7dd3fc;font-size:13px;white-space:nowrap;\">Log salvataggio configurazione<\/b>'\n            + '<span style=\"color:#fde68a;font-size:10px;\">visibile, client-only, anti 503<\/span>'\n            + '<span id=\"ti-config-save-diag-status-687\" style=\"margin-left:auto;color:#bbf7d0;font-size:10px;white-space:nowrap;\"><\/span>'\n            + '<button type=\"button\" id=\"ti-config-save-diag-copy-687\" style=\"font-size:10px;padding:4px 8px;border-radius:7px;border:1px solid #475569;background:#1d4ed8;color:#fff;cursor:pointer;\">Copia testo<\/button>'\n            + '<button type=\"button\" id=\"ti-config-save-diag-clear-687\" style=\"font-size:10px;padding:4px 8px;border-radius:7px;border:1px solid #475569;background:#334155;color:#fff;cursor:pointer;\">Pulisci<\/button>'\n            + '<button type=\"button\" id=\"ti-config-save-diag-close-687\" style=\"font-size:10px;padding:4px 8px;border-radius:7px;border:1px solid #475569;background:#7f1d1d;color:#fff;cursor:pointer;\">Chiudi<\/button>'\n            + '<\/div>'\n            + '<div id=\"ti-config-save-diag-summary-687\" style=\"padding:7px 9px;border-bottom:1px solid #1e293b;background:#07111f;color:#cbd5e1;font-size:11px;\"><\/div>'\n            + '<pre id=\"ti-config-save-diag-body-687\" style=\"white-space:pre-wrap;word-break:break-word;margin:0;padding:9px;overflow:auto;max-height:calc(82vh - 86px);background:#020617;color:#e5e7eb;\"><\/pre>';\n        document.body.appendChild(el);\n        byId('ti-config-save-diag-copy-687').onclick=function(){ copyLog(); };\n        byId('ti-config-save-diag-clear-687').onclick=function(){ window.__tiConfigSaveDiagLog687=[]; renderPanel(); };\n        byId('ti-config-save-diag-close-687').onclick=function(){ el.style.display='none'; };\n        return el;\n    }\n    function startTrace(reason){\n        activeTrace='diag-'+(new Date()).toISOString().replace(\/[^0-9]\/g,'').slice(0,14)+'-'+Math.random().toString(36).slice(2,7);\n        activeStarted=nowMs(); seq=0; lastHeartbeat=0; window.__tiConfigSaveDiagLog687=[];\n        showPanel();\n        log('00_apertura_log_visibile', {reason:reason||'start', db:dbName()}, 'info');\n    }\n    function showPanel(){\n        var el=ensurePanel();\n        try { el.style.display='block'; el.style.visibility='visible'; el.style.opacity='1'; } catch(e) {}\n        renderPanel();\n    }\n    function renderPanel(){\n        var el=ensurePanel(), body=byId('ti-config-save-diag-body-687'), st=byId('ti-config-save-diag-status-687'), sm=byId('ti-config-save-diag-summary-687');\n        if (st) st.textContent = activeTrace ? ('trace '+activeTrace) : 'in attesa';\n        if (sm) {\n            var btn=byId('lbl-conf-save');\n            sm.innerHTML = 'Database: <b>'+htmlEsc(dbName()||'(non selezionato)')+'<\/b> | Pulsante: <b>'+htmlEsc(btn ? btn.innerText : '(non trovato)')+'<\/b> | In corso: <b>'+htmlEsc(window.__saveConfigInFlight?'SI':'NO')+'<\/b> | Righe log: <b>'+htmlEsc(String((window.__tiConfigSaveDiagLog687||[]).length))+'<\/b>';\n        }\n        if (body) {\n            body.textContent = (window.__tiConfigSaveDiagLog687 || []).map(function(r){\n                var d=''; try { d=JSON.stringify(r.detail || {}, null, 0); } catch(e) { d=txt(r.detail); }\n                return '[' + String(r.seq).padStart(3,'0') + '] +' + String(r.ms).padStart(6,' ') + 'ms ' + r.level.toUpperCase().padEnd(5,' ') + ' ' + r.event + (d && d !== '{}' ? ' ' + d : '');\n            }).join('\\n');\n            body.scrollTop = body.scrollHeight;\n        }\n    }\n    function log(event, detail, level){\n        level=level || 'info';\n        if (!activeTrace) { activeTrace='diag-'+(new Date()).toISOString().replace(\/[^0-9]\/g,'').slice(0,14)+'-'+Math.random().toString(36).slice(2,7); activeStarted=nowMs(); seq=0; }\n        var row={seq:++seq, ts:iso(), ms:Math.max(0, nowMs()-activeStarted), trace_id:activeTrace, event:event, level:level, detail:detail||{}};\n        window.__tiConfigSaveDiagLog687.push(row);\n        if (window.__tiConfigSaveDiagLog687.length > maxRows) window.__tiConfigSaveDiagLog687.splice(0, window.__tiConfigSaveDiagLog687.length - maxRows);\n        try { (level==='error' ? console.error : (level==='warn' ? console.warn : console.log))('[Shop-Service config save diag visible]', row); } catch(e) {}\n        showPanel();\n    }\n    function copyLog(){\n        ensurePanel();\n        var text='';\n        try { text=(byId('ti-config-save-diag-body-687')||{}).textContent || ''; } catch(e) {}\n        if (!text) text=safeJson(window.__tiConfigSaveDiagLog687 || [], 20000);\n        function done(ok){\n            var b=byId('ti-config-save-diag-copy-687'); if(b){ var old=b.textContent; b.textContent=ok?'Copiato':'Copia manuale'; setTimeout(function(){ b.textContent=old||'Copia testo'; }, 1200); }\n        }\n        if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(function(){ done(true); }).catch(function(){ fallback(); }); else fallback();\n        function fallback(){ var ta=document.createElement('textarea'); ta.value=text; document.body.appendChild(ta); ta.select(); var ok=false; try{ ok=document.execCommand('copy'); }catch(e){} document.body.removeChild(ta); done(ok); }\n    }\n    function sampleModifiedFields(){\n        var count=0, sample=[];\n        try { if (window.modifiedFields && typeof window.modifiedFields.forEach==='function') window.modifiedFields.forEach(function(x){ count++; if(sample.length<80) sample.push(String(x)); }); } catch(e) { sample.push('errore modifiedFields: '+(e.message||e)); }\n        return {count:count, sample:sample};\n    }\n    function sampleRemovals(){\n        var rem=[];\n        try { rem=(Array.isArray(window.pendingLinkedFileRemovals)?window.pendingLinkedFileRemovals:[]); } catch(e) { rem=[]; }\n        return {count:rem.length, sample:rem.slice(0,60)};\n    }\n    function changedTables(){\n        try { return (window.getConfigChangedTables ? window.getConfigChangedTables() : []); } catch(e) { return ['errore: '+(e.message||e)]; }\n    }\n    function summarizeClient(){\n        var btn=byId('lbl-conf-save') || byId('ti-conf-save');\n        var mf=sampleModifiedFields(), rm=sampleRemovals();\n        return {\n            db: dbName(),\n            plugin_version: window.tiVersion || window.tiPluginVersion || '',\n            currentDbLoaded: !!(window.currentDbData && window.currentDbData.Tabelle),\n            originalDbLoaded: !!(window.configOriginalDbData && window.configOriginalDbData.Tabelle),\n            changedTables: changedTables(),\n            modifiedFields_count: mf.count,\n            modifiedFields_sample: mf.sample,\n            pendingLinkedFileRemovals_count: rm.count,\n            pendingLinkedFileRemovals_sample: rm.sample,\n            save_button_text: btn ? btn.innerText : '',\n            save_button_disabled: btn ? !!btn.disabled : null,\n            save_button_style: btn ? short(btn.getAttribute('style') || '', 300) : '',\n            in_flight: !!window.__saveConfigInFlight\n        };\n    }\n    function actionFromBody(body){ try { if (body && typeof body.get === 'function') return body.get('ti_action') || body.get('action') || ''; } catch(e) {} return ''; }\n    function fdSummary(body){\n        var out={keys:[], approx_chars:0, payload_keys:0};\n        try {\n            if (!body || typeof body.forEach !== 'function') return out;\n            body.forEach(function(v,k){\n                var sv=(typeof v==='string') ? v : (v && v.name ? '[file '+v.name+']' : '[object]');\n                out.payload_keys++;\n                out.keys.push(k + '=' + short(sv, 100));\n                out.approx_chars += String(sv).length;\n            });\n        } catch(e) { out.error=e.message||String(e); }\n        return out;\n    }\n    function isRelevantAction(act){ return \/ti_ai_save_config|ti_ai_save_full_db|ti_ai_config_delete_rows|ti_ai_file_meta_\/i.test(act || ''); }\n    if (nativeFetch && !window.fetch.__tiConfigDiag687Wrapped) {\n        var wrappedFetch=function(input, init){\n            var body=init && init.body ? init.body : null;\n            var act=actionFromBody(body);\n            var relevant=isRelevantAction(act);\n            var started=nowMs();\n            if (relevant) { if (!activeTrace) startTrace('ajax_'+act); log('20_ajax_start', {action:act, form:fdSummary(body)}, 'info'); }\n            return nativeFetch(input, init).then(function(resp){\n                if (relevant) {\n                    log('21_ajax_headers', {action:act, ok:!!resp.ok, status:resp.status, elapsed_ms:nowMs()-started, content_type:(resp.headers&&resp.headers.get?resp.headers.get('content-type'):'')}, resp.ok?'info':'warn');\n                    try {\n                        var clone=resp.clone();\n                        clone.text().then(function(t){ log('22_ajax_body_preview', {action:act, bytes:String(t||'').length, preview:short(t,360)}, resp.ok?'info':'warn'); }).catch(function(e){ log('22_ajax_body_preview_error', {action:act, message:e&&e.message}, 'warn'); });\n                    } catch(e) { log('22_ajax_clone_error', {action:act, message:e&&e.message}, 'warn'); }\n                }\n                return resp;\n            }).catch(function(err){\n                if (relevant || activeTrace) log('23_ajax_fetch_error', {action:act, elapsed_ms:nowMs()-started, name:err&&err.name, message:err&&err.message}, 'error');\n                throw err;\n            });\n        };\n        wrappedFetch.__tiConfigDiag687Wrapped=true;\n        window.fetch=wrappedFetch;\n    }\n    function wrapSaveConfig(){\n        if (typeof window.saveConfig !== 'function') return false;\n        if (window.saveConfig.__tiDiag687) return true;\n        var prev=window.saveConfig;\n        var saveWrap=function(){\n            if (!activeTrace) startTrace('saveConfig_wrapper'); else showPanel();\n            log('10_saveConfig_inizio', summarizeClient(), 'info');\n            var ret;\n            try {\n                ret=prev.apply(this, arguments);\n                log('11_saveConfig_return', {return_type:typeof ret, is_promise:!!(ret && typeof ret.then==='function')}, 'info');\n                if (ret && typeof ret.then==='function') ret.then(function(v){ log('12_saveConfig_resolved', {value:safeJson(v,400)}, 'info'); }).catch(function(e){ log('13_saveConfig_rejected', {message:e&&e.message, stack:short(e&&e.stack||'',1000)}, 'error'); });\n            } catch(e) {\n                log('14_saveConfig_exception', {message:e.message||String(e), stack:short(e.stack||'',1000)}, 'error');\n                throw e;\n            }\n            return ret;\n        };\n        saveWrap.__tiDiag687=true;\n        window.saveConfig=saveWrap;\n        return true;\n    }\n    function installSaveWrapperLoop(){\n        var tries=0;\n        wrapTimer=setInterval(function(){ tries++; if (wrapSaveConfig() || tries>80) { clearInterval(wrapTimer); wrapTimer=null; } }, 250);\n        wrapSaveConfig();\n    }\n    function looksLikeSaveButton(t){\n        if (!t) return false;\n        try { if (t.id === 'lbl-conf-save') return true; } catch(e) {}\n        try { return \/salva modifiche|salvataggio\/i.test(String(t.innerText || t.textContent || '')) && (t.tagName === 'BUTTON' || t.closest && t.closest('button')); } catch(e) { return false; }\n    }\n    document.addEventListener('click', function(ev){\n        var t=ev.target;\n        var btn=t && t.closest ? t.closest('button') : t;\n        if (looksLikeSaveButton(btn)) {\n            startTrace('click_capture_pulsante');\n            log('01_click_pulsante_salva', summarizeClient(), 'info');\n        }\n    }, true);\n    var lastBtnText='';\n    function monitorSaveButton(){\n        var btn=byId('lbl-conf-save');\n        var text=btn ? String(btn.innerText || '') : '';\n        if (text && text !== lastBtnText) {\n            lastBtnText=text;\n            if (\/salvataggio\/i.test(text)) { if (!activeTrace) startTrace('monitor_testo_salvataggio'); log('02_pulsante_in_salvataggio', summarizeClient(), 'info'); }\n            else if (activeTrace) log('30_pulsante_cambiato', summarizeClient(), 'info');\n        }\n        var saving=!!(btn && \/salvataggio\/i.test(String(btn.innerText || '')));\n        if ((saving || window.__saveConfigInFlight) && activeTrace) {\n            var n=nowMs();\n            if (!lastHeartbeat || n-lastHeartbeat>5000) { lastHeartbeat=n; log('40_heartbeat_salvataggio', summarizeClient(), saving?'warn':'info'); }\n        }\n    }\n    setInterval(monitorSaveButton, 900);\n    try {\n        var mo=new MutationObserver(function(){ monitorSaveButton(); });\n        mo.observe(document.body, {childList:true, subtree:true, characterData:true, attributes:true, attributeFilter:['disabled','style','data-ti-saving']});\n    } catch(e) {}\n    window.addEventListener('unhandledrejection', function(ev){ if (activeTrace) { var r=ev.reason||{}; log('98_unhandledrejection', {message:r.message||String(r), stack:short(r.stack||'',1000)}, 'error'); } });\n    window.addEventListener('error', function(ev){ if (activeTrace) log('99_window_error', {message:ev.message, file:ev.filename, line:ev.lineno, col:ev.colno}, 'error'); });\n    window.tiShowConfigSaveDiag687=function(){ if (!activeTrace) startTrace('manual_open'); else showPanel(); };\n    window.tiCopyConfigSaveDiag687=function(){ showPanel(); copyLog(); };\n    window.tiShowConfigSaveDiag686=window.tiShowConfigSaveDiag687;\n    window.tiCopyConfigSaveDiag686=window.tiCopyConfigSaveDiag687;\n    window.tiShowConfigSaveDiag685=window.tiShowConfigSaveDiag687;\n    window.tiCopyConfigSaveDiag685=window.tiCopyConfigSaveDiag687;\n    installSaveWrapperLoop();\n})();\n<\/script>\n\n\n\n<script id=\"ti-config-save-diagnostic-688\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveDiagnostic688Installed) return;\n    window.__tiConfigSaveDiagnostic688Installed = true;\n    if (!window.tiConfigSaveLogAllowed) {\n        window.tiShowConfigSaveDiag688 = function(){ return false; };\n        window.tiCopyConfigSaveDiag688 = function(){ return false; };\n        window.tiShowConfigSaveDiag687 = window.tiShowConfigSaveDiag688;\n        window.tiCopyConfigSaveDiag687 = window.tiCopyConfigSaveDiag688;\n        return;\n    }\n    var rows = [];\n    var seq = 0;\n    var started = 0;\n    var traceId = '';\n    var wrapping = false;\n    function now(){ return Date.now ? Date.now() : (new Date()).getTime(); }\n    function text(v){ return String(v == null ? '' : v); }\n    function short(v,n){ v=text(v); n=n||500; return v.length>n ? v.slice(0,n)+'...' : v; }\n    function id(x){ return document.getElementById(x); }\n    function esc(v){ return text(v).replace(\/[&<>\"']\/g,function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; }); }\n    function codeVersionLabel(){ var v=text(window.tiVersion || '30.9.925'); var d=text(window.tiVersionDateTime || '2026-07-06 18:35 CEST'); return (v+(d?' - '+d:'')); }\n    function db(){ var e=id('ti-ditta'); return e ? (e.value || '') : ''; }\n    function btn(){ return id('lbl-conf-save') || id('ti-conf-save') || null; }\n    function body(){ return document.body || document.documentElement; }\n    function ensureTrace(reason){\n        if (!traceId) {\n            traceId = 'diag688-' + (new Date()).toISOString().replace(\/[^0-9]\/g,'').slice(0,14) + '-' + Math.random().toString(36).slice(2,7);\n            started = now();\n            seq = 0;\n            rows = [];\n        }\n        showPanel();\n        if (reason) add('00_log_visibile', {reason:reason, db:db(), href:short(location.href,260)}, 'info', true);\n    }\n    function ensurePanel(){\n        var el=id('ti-config-save-diag-688');\n        if (el) return el;\n        el=document.createElement('div');\n        el.id='ti-config-save-diag-688';\n        el.setAttribute('role','dialog');\n        el.setAttribute('aria-live','polite');\n        el.setAttribute('style','position:fixed !important;left:50% !important;top:8px !important;transform:translateX(-50%) !important;width:min(1040px,calc(100vw - 12px)) !important;max-height:84vh !important;z-index:2147483647 !important;background:#020617 !important;color:#e5e7eb !important;border:3px solid #facc15 !important;border-radius:14px !important;box-shadow:0 24px 80px rgba(0,0,0,.82) !important;font:11px\/1.38 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace !important;display:none;overflow:hidden !important;pointer-events:auto !important;');\n        el.innerHTML = '<div style=\"display:flex;gap:7px;align-items:center;padding:8px 9px;background:#111827;border-bottom:1px solid #475569;\">'\n            + '<b style=\"color:#fde68a;font-size:13px;white-space:nowrap;\">Log salvataggio configurazione<\/b>'\n            + '<span id=\"ti-config-save-diag-version-688\" style=\"color:#93c5fd;font-size:10px;white-space:nowrap;\">Versione codice: '+esc(codeVersionLabel())+'<\/span>'\n            + '<span style=\"color:#93c5fd;font-size:10px;\">apertura immediata inline\/click\/touch<\/span>'\n            + '<span id=\"ti-config-save-diag-status-688\" style=\"margin-left:auto;color:#bbf7d0;font-size:10px;white-space:nowrap;\"><\/span>'\n            + '<button type=\"button\" id=\"ti-config-save-diag-copy-688\" style=\"font-size:10px;padding:4px 8px;border-radius:7px;border:1px solid #475569;background:#1d4ed8;color:#fff;cursor:pointer;\">Copia testo<\/button>'\n            + '<button type=\"button\" id=\"ti-config-save-diag-clear-688\" style=\"font-size:10px;padding:4px 8px;border-radius:7px;border:1px solid #475569;background:#334155;color:#fff;cursor:pointer;\">Pulisci<\/button>'\n            + '<button type=\"button\" id=\"ti-config-save-diag-close-688\" style=\"font-size:10px;padding:4px 8px;border-radius:7px;border:1px solid #475569;background:#7f1d1d;color:#fff;cursor:pointer;\">Chiudi<\/button>'\n            + '<\/div>'\n            + '<div id=\"ti-config-save-diag-summary-688\" style=\"padding:7px 9px;border-bottom:1px solid #1e293b;background:#07111f;color:#cbd5e1;font-size:11px;\"><\/div>'\n            + '<pre id=\"ti-config-save-diag-body-688\" style=\"white-space:pre-wrap;word-break:break-word;margin:0;padding:9px;overflow:auto;max-height:calc(84vh - 88px);background:#020617;color:#e5e7eb;\"><\/pre>';\n        body().appendChild(el);\n        var c=id('ti-config-save-diag-copy-688'); if(c) c.onclick=copy;\n        var cl=id('ti-config-save-diag-clear-688'); if(cl) cl.onclick=function(){ rows=[]; seq=0; render(); };\n        var x=id('ti-config-save-diag-close-688'); if(x) x.onclick=function(){ el.style.setProperty('display','none','important'); };\n        return el;\n    }\n    function showPanel(){\n        var el=ensurePanel();\n        try { el.style.setProperty('display','block','important'); el.style.setProperty('visibility','visible','important'); el.style.setProperty('opacity','1','important'); } catch(e) {}\n        render();\n    }\n    function summary(){\n        var b=btn();\n        var rem=[]; try{ rem=Array.isArray(window.pendingLinkedFileRemovals) ? window.pendingLinkedFileRemovals : []; }catch(e){}\n        var mfCount=0; try{ if(window.modifiedFields && typeof window.modifiedFields.forEach==='function') window.modifiedFields.forEach(function(){mfCount++;}); }catch(e){}\n        return {db:db(), buttonText:b?b.innerText:'(non trovato)', disabled:b?!!b.disabled:null, savingAttr:b?b.getAttribute('data-ti-saving'):'', inFlight:!!window.__saveConfigInFlight, modifiedFields:mfCount, removals:rem.length};\n    }\n    function render(){\n        var st=id('ti-config-save-diag-status-688'), sm=id('ti-config-save-diag-summary-688'), pr=id('ti-config-save-diag-body-688');\n        if(st) st.textContent=traceId || 'in attesa';\n        var ver=id('ti-config-save-diag-version-688'); if(ver) ver.textContent='Versione codice: '+codeVersionLabel();\n        var s=summary();\n        if(sm) sm.innerHTML='Versione codice: <b>'+esc(codeVersionLabel())+'<\/b> | DB: <b>'+esc(s.db||'(non selezionato)')+'<\/b> | Pulsante: <b>'+esc(s.buttonText)+'<\/b> | disabled: <b>'+esc(String(s.disabled))+'<\/b> | inFlight: <b>'+esc(String(s.inFlight))+'<\/b> | modifiedFields: <b>'+esc(String(s.modifiedFields))+'<\/b> | rimozioni file: <b>'+esc(String(s.removals))+'<\/b>';\n        if(pr){\n            var detail = rows.map(function(r){ var d=''; try{ d=JSON.stringify(r.detail||{}); }catch(e){ d=text(r.detail); } return '['+String(r.seq).padStart(3,'0')+'] +'+String(r.ms).padStart(6,' ')+'ms '+r.level.toUpperCase().padEnd(5,' ')+' '+r.event+(d && d!=='{}' ? ' '+d : ''); }).join('\\n');\n            pr.textContent = 'Versione codice: '+codeVersionLabel()+'\\nTrace: '+(traceId || 'in attesa')+'\\n--- Dettaglio log ---\\n'+detail;\n            pr.scrollTop=pr.scrollHeight;\n        }\n    }\n    function add(event, detail, level, noRender){\n        level=level||'info';\n        if(!traceId){ traceId='diag688-'+(new Date()).toISOString().replace(\/[^0-9]\/g,'').slice(0,14)+'-'+Math.random().toString(36).slice(2,7); started=now(); }\n        rows.push({seq:++seq, ms:Math.max(0,now()-started), event:event, level:level, detail:detail||{}});\n        if(rows.length>360) rows.splice(0, rows.length-360);\n        try{ (level==='error'?console.error:(level==='warn'?console.warn:console.log))('[Shop-Service save diag 688]', rows[rows.length-1]); }catch(e){}\n        if(!noRender) showPanel();\n    }\n    function copy(){\n        showPanel();\n        var pr=id('ti-config-save-diag-body-688');\n        var t=pr ? pr.textContent : JSON.stringify(rows,null,2); if(t.indexOf('Versione codice:')!==0) t='Versione codice: '+codeVersionLabel()+'\\nTrace: '+(traceId || 'in attesa')+'\\n--- Dettaglio log ---\\n'+t;\n        function done(ok){ var b=id('ti-config-save-diag-copy-688'); if(b){ var old=b.textContent; b.textContent=ok?'Copiato':'Copia manuale'; setTimeout(function(){ b.textContent=old||'Copia testo'; },1300); } }\n        if(navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(t).then(function(){done(true);}).catch(fallback); } else fallback();\n        function fallback(){ var ta=document.createElement('textarea'); ta.value=t; body().appendChild(ta); ta.select(); var ok=false; try{ ok=document.execCommand('copy'); }catch(e){} ta.parentNode.removeChild(ta); done(ok); }\n    }\n    function looksSave(el){\n        if(!el) return false;\n        var b=el.closest ? el.closest('button') : el;\n        if(!b) return false;\n        if(b.id==='lbl-conf-save') return true;\n        var t=text(b.innerText || b.textContent || '').toLowerCase();\n        return (b.tagName==='BUTTON') && (t.indexOf('salva modifiche')>=0 || t.indexOf('salvataggio')>=0);\n    }\n    function preEvent(kind, ev){\n        var target=ev && ev.target ? ev.target : null;\n        if(looksSave(target)){\n            ensureTrace(kind);\n            add('01_evento_pulsante_'+kind, summary(), 'info');\n        }\n    }\n    ['pointerdown','mousedown','touchstart','click'].forEach(function(k){\n        try{ document.addEventListener(k, function(ev){ preEvent(k,ev); }, true); }catch(e){}\n    });\n    window.tiShowConfigSaveDiag688=function(reason){ ensureTrace(reason || 'manuale'); add('02_apertura_manuale_o_inline', summary(), 'info'); };\n    window.tiCopyConfigSaveDiag688=function(){ ensureTrace('copy_manual'); copy(); };\n    window.tiShowConfigSaveDiag687=window.tiShowConfigSaveDiag688;\n    window.tiCopyConfigSaveDiag687=window.tiCopyConfigSaveDiag688;\n    function wrapSave(){\n        if(wrapping) return;\n        try{\n            var fn=window.saveConfig;\n            if(typeof fn==='function' && !fn.__tiDiag688){\n                var w=function(){\n                    ensureTrace('saveConfig_wrapper');\n                    add('10_saveConfig_chiamata', summary(), 'info');\n                    var ret;\n                    try{ ret=fn.apply(this, arguments); add('11_saveConfig_return', {returnType:typeof ret, promise:!!(ret&&typeof ret.then==='function')}, 'info'); }\n                    catch(e){ add('12_saveConfig_exception', {message:e&&e.message, stack:short(e&&e.stack,1200)}, 'error'); throw e; }\n                    if(ret && typeof ret.then==='function') ret.then(function(v){ add('13_saveConfig_resolved', {value:short(JSON.stringify(v||{}),600)}, 'info'); }).catch(function(e){ add('14_saveConfig_rejected', {message:e&&e.message, stack:short(e&&e.stack,1200)}, 'error'); });\n                    return ret;\n                };\n                w.__tiDiag688=true;\n                wrapping=true; window.saveConfig=w; wrapping=false;\n                add('05_saveConfig_wrapped', {ok:true}, 'info');\n            }\n        } catch(e){ wrapping=false; try{ add('05_saveConfig_wrap_error', {message:e&&e.message}, 'warn'); }catch(_){} }\n    }\n    setInterval(wrapSave, 300);\n    wrapSave();\n    if(window.fetch && !window.fetch.__tiDiag688){\n        var nativeFetch=window.fetch.bind(window);\n        var wf=function(input, init){\n            var act='';\n            try{ var body=init&&init.body; if(body&&typeof body.get==='function') act=body.get('ti_action')||body.get('action')||''; }catch(e){}\n            var rel=\/ti_ai_save_config|ti_ai_save_full_db|ti_ai_config_delete_rows|ti_ai_file_meta\/i.test(act);\n            var st=now();\n            if(rel){ ensureTrace('ajax_'+act); add('20_ajax_start', {action:act}, 'info'); }\n            return nativeFetch(input, init).then(function(r){ if(rel) add('21_ajax_headers', {action:act, status:r.status, ok:r.ok, elapsed:now()-st, contentType:(r.headers&&r.headers.get?r.headers.get('content-type'):'')}, r.ok?'info':'warn'); return r; }).catch(function(e){ if(rel||traceId) add('22_ajax_error', {action:act, elapsed:now()-st, message:e&&e.message, name:e&&e.name}, 'error'); throw e; });\n        };\n        wf.__tiDiag688=true; window.fetch=wf;\n    }\n    setInterval(function(){\n        var b=btn();\n        if(!b) return;\n        var t=text(b.innerText||'');\n        if(\/salvataggio\/i.test(t) || b.disabled || b.getAttribute('data-ti-saving')==='1' || window.__saveConfigInFlight){\n            ensureTrace('monitor_salvataggio');\n            add('30_monitor_salvataggio_visibile', summary(), \/salvataggio\/i.test(t)?'warn':'info');\n        } else if(traceId) {\n            render();\n        }\n    }, 1200);\n})();\n<\/script>\n\n<script id=\"ti-config-save-diag-copy-689\">\n(function(){\n    if (window.__tiConfigSaveDiagCopy689) return;\n    window.__tiConfigSaveDiagCopy689 = true;\n    if (!window.tiConfigSaveLogAllowed) return;\n    function id(x){ return document.getElementById(x); }\n    function txt(v){ return (v==null?'':String(v)); }\n    function trim(v){ return txt(v).replace(\/^\\s+|\\s+$\/g,''); }\n    function panel(){ return id('ti-config-save-diag-panel-688') || id('ti-config-save-diag-panel-687') || null; }\n    function bodyPre(){ return id('ti-config-save-diag-body-688') || id('ti-config-save-diag-body-687') || null; }\n    function summaryEl(){ return id('ti-config-save-diag-summary-688') || id('ti-config-save-diag-summary-687') || null; }\n    function statusEl(){ return id('ti-config-save-diag-status-688') || id('ti-config-save-diag-status-687') || null; }\n    function versionLabel(){\n        var v = txt(window.tiVersion || '30.9.925');\n        var d = txt(window.tiVersionDateTime || '2026-07-06 18:35 CEST');\n        return trim(v + (d ? ' - ' + d : ''));\n    }\n    function getLogText(){\n        var pre = bodyPre();\n        var log = pre ? trim(pre.textContent || pre.innerText || '') : '';\n        var sm = summaryEl();\n        var st = statusEl();\n        var header = [];\n        header.push('Log salvataggio configurazione - Shop & Service');\n        header.push('Versione codice: ' + versionLabel());\n        header.push('Data copia: ' + (new Date()).toISOString());\n        if (st && trim(st.textContent || st.innerText || '')) header.push('Trace: ' + trim(st.textContent || st.innerText || ''));\n        if (sm && trim(sm.textContent || sm.innerText || '')) header.push('Riepilogo: ' + trim(sm.textContent || sm.innerText || '').replace(\/\\s+\/g,' '));\n        header.push('');\n        header.push('--- Dettaglio log ---');\n        if (!log) {\n            log = '[nessuna riga log visibile nel popup al momento della copia]';\n            try {\n                var b = id('lbl-conf-save');\n                log += '\\nPulsante Salva: ' + (b ? trim(b.innerText || b.textContent || '') : '(non trovato)');\n                log += '\\nPulsante disabled: ' + (b ? String(!!b.disabled) : 'n\/d');\n                log += '\\nwindow.__saveConfigInFlight: ' + String(!!window.__saveConfigInFlight);\n                var mf = 0;\n                if (window.modifiedFields && typeof window.modifiedFields.forEach === 'function') window.modifiedFields.forEach(function(){ mf++; });\n                log += '\\nmodifiedFields count: ' + mf;\n                log += '\\npendingLinkedFileRemovals count: ' + (Array.isArray(window.pendingLinkedFileRemovals) ? window.pendingLinkedFileRemovals.length : 'n\/d');\n            } catch(e) { log += '\\nErrore raccolta stato: ' + (e && e.message ? e.message : e); }\n        }\n        return header.join('\\n') + '\\n' + log + '\\n';\n    }\n    function ensureBuffer(text){\n        var host = panel() || document.body;\n        var ta = id('ti-config-save-diag-copy-buffer-689');\n        if (!ta) {\n            ta = document.createElement('textarea');\n            ta.id = 'ti-config-save-diag-copy-buffer-689';\n            ta.setAttribute('aria-label', 'Testo log salvataggio configurazione da copiare');\n            ta.style.cssText = 'display:block !important;width:calc(100% - 18px) !important;min-height:92px !important;max-height:180px !important;margin:8px 9px !important;padding:8px !important;border:1px solid #38bdf8 !important;border-radius:8px !important;background:#0f172a !important;color:#e5e7eb !important;font:11px\/1.35 monospace !important;white-space:pre !important;z-index:2147483647 !important;';\n            var sm = summaryEl();\n            if (sm && sm.parentNode) sm.parentNode.insertBefore(ta, sm.nextSibling);\n            else host.appendChild(ta);\n        }\n        ta.value = text || '';\n        try { ta.removeAttribute('readonly'); ta.focus({preventScroll:true}); ta.select(); ta.setSelectionRange(0, ta.value.length); } catch(e) {}\n        return ta;\n    }\n    function setButtonState(btn, ok, manual, n){\n        if (!btn || !btn.getAttribute) return;\n        var old = btn.getAttribute('data-ti-old-copy-label-689') || btn.textContent || 'Copia testo';\n        btn.setAttribute('data-ti-old-copy-label-689', old);\n        btn.textContent = ok ? ('Log copiato (' + String(n || 0) + ' car.)') : (manual ? 'Copia manuale: testo selezionato' : 'Copia non riuscita');\n        setTimeout(function(){ try { btn.textContent = old || 'Copia testo'; } catch(e){} }, ok ? 1800 : 2600);\n    }\n    function doCopy(btn){\n        var text = getLogText();\n        var ta = ensureBuffer(text);\n        var n = text.length;\n        var execOk = false;\n        try { execOk = document.execCommand && document.execCommand('copy'); } catch(e) { execOk = false; }\n        if (execOk) { setButtonState(btn, true, false, n); return; }\n        if (navigator.clipboard && navigator.clipboard.writeText) {\n            navigator.clipboard.writeText(text).then(function(){\n                setButtonState(btn, true, false, n);\n            }).catch(function(){\n                try { ta.focus({preventScroll:true}); ta.select(); ta.setSelectionRange(0, ta.value.length); } catch(e) {}\n                setButtonState(btn, false, true, n);\n            });\n        } else {\n            setButtonState(btn, false, true, n);\n        }\n    }\n    function isDiagCopyButton(el){\n        if (!el) return null;\n        var b = el.closest ? el.closest('button') : el;\n        if (!b || !b.id) return null;\n        return (b.id === 'ti-config-save-diag-copy-688' || b.id === 'ti-config-save-diag-copy-687') ? b : null;\n    }\n    document.addEventListener('click', function(ev){\n        var b = isDiagCopyButton(ev && ev.target);\n        if (!b) return;\n        try { ev.preventDefault(); ev.stopPropagation(); if (ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        doCopy(b);\n    }, true);\n    window.tiCopyConfigSaveDiag689 = function(){ doCopy(id('ti-config-save-diag-copy-688') || id('ti-config-save-diag-copy-687')); };\n    window.tiCopyConfigSaveDiag688 = window.tiCopyConfigSaveDiag689;\n    window.tiCopyConfigSaveDiag687 = window.tiCopyConfigSaveDiag689;\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-direct-691\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveDirect691Installed) return;\n    window.__tiConfigSaveDirect691Installed = true;\n    function id(x){ return document.getElementById(x); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function qsa(sel, root){ return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }\n    function clone(o){ try { return JSON.parse(JSON.stringify(o || {})); } catch(e) { return {}; } }\n    function enc(obj){ var json = JSON.stringify(obj || []); if (typeof window.utoa === 'function') return window.utoa(json); return btoa(unescape(encodeURIComponent(json))); }\n    function btn(){ return id('lbl-conf-save'); }\n    function log691(event, detail, level){\n        if(!window.tiConfigSaveLogAllowed) return;\n        try {\n            level = level || 'info';\n            var pre = id('ti-config-save-diag-body-688') || id('ti-config-save-diag-body-687');\n            var arr = window.__tiConfigSaveDiagLog688 || window.__tiConfigSaveDiagLog687;\n            var seq = 1, ms = 0;\n            if (Array.isArray(arr)) {\n                seq = arr.length + 1;\n                try { var first = arr[0]; ms = first && first.ts ? Math.max(0, Date.now() - Date.parse(first.ts)) : 0; } catch(e) {}\n                arr.push({seq:seq, ts:(new Date()).toISOString(), ms:ms, trace_id:'diag691', event:event, level:level, detail:detail||{}});\n            } else if (pre) {\n                var lines = txt(pre.textContent || '').split('\\n').filter(Boolean).length;\n                seq = lines + 1;\n            }\n            var d=''; try { d=JSON.stringify(detail||{}); } catch(e) { d=txt(detail); }\n            var line='['+String(seq).padStart(3,'0')+'] +'+String(ms).padStart(6,' ')+'ms '+level.toUpperCase().padEnd(5,' ')+' '+event+(d && d!=='{}' ? ' '+d : '');\n            if (pre) { pre.textContent = (txt(pre.textContent||'') ? txt(pre.textContent||'')+'\\n' : '') + line; pre.scrollTop = pre.scrollHeight; }\n            var sm = id('ti-config-save-diag-summary-688') || id('ti-config-save-diag-summary-687');\n            if (sm) { var b=btn(); sm.innerHTML = 'DB: <b>'+txt(id('ti-ditta')?id('ti-ditta').value:'')+'<\/b> | Pulsante: <b>'+txt(b?b.innerText:'')+'<\/b> | direct 691 | evento: <b>'+txt(event)+'<\/b>'; }\n            try { (level==='error'?console.error:(level==='warn'?console.warn:console.log))('[Shop-Service save direct 691]', line, detail||{}); } catch(e) {}\n        } catch(e) {}\n    }\n    function clearBusy(){\n        try { window.__tiConfigSaveActive675=false; window.__tiConfigSaveActive677=false; window.__tiConfigSaveActive678=false; window.__tiConfigSaveActive682=false; window.__tiConfigSaveActive683=false; window.__tiConfigSaveActive684=false; window.__tiConfigSaveActive691=false; window.__saveConfigInFlight=false; } catch(e) {}\n        try { window.__tiSaveConfigStartedAt=0; window.__tiSaveConfigStartedAt585=0; window.__tiSaveConfigStartedAt675=0; } catch(e) {}\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n        try { qsa('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(x){ if (x && x.parentNode) x.parentNode.removeChild(x); }); } catch(e) {}\n    }\n    function setSaving(){ var b=btn(); if(!b) return; b.disabled=true; b.setAttribute('data-ti-saving','1'); b.setAttribute('aria-busy','true'); b.style.pointerEvents='none'; b.innerText='Salvataggio...'; try{ b.style.setProperty('background','#d97706','important'); b.style.setProperty('background-color','#d97706','important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','wait','important'); }catch(e){} }\n    function resetButton(clean){\n        if (clean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') { try { window.modifiedFields.clear(); } catch(e){} }\n        clearBusy();\n        var b=btn(); if(!b) return;\n        var dirty = !!(!clean && window.modifiedFields && window.modifiedFields.size>0);\n        b.disabled=false; try{ b.removeAttribute('disabled'); b.removeAttribute('data-ti-saving'); b.removeAttribute('aria-busy'); }catch(e){}\n        b.style.pointerEvents=''; b.innerText = dirty ? 'Salva Modifiche *' : 'Salva Modifiche';\n        try{ b.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','pointer','important'); }catch(e){}\n    }\n    function readVal(el){ if(!el) return ''; if(el.tagName==='SELECT' && el.multiple) return Array.prototype.slice.call(el.selectedOptions||[]).map(function(o){return txt(o.value).trim();}).filter(Boolean).join(', '); if(el.type==='checkbox') return el.checked ? (el.value || '1') : ''; return el.value == null ? '' : el.value; }\n    function resolveTable(t){ try { return window.resolveConfigTableName ? (window.resolveConfigTableName(t)||t) : t; } catch(e) { return t; } }\n    function ensureArrays(){ var d=window.currentDbData; if(!d || !d.Tabelle) return; Object.keys(d.Tabelle).forEach(function(t){ if(!Array.isArray(d.Tabelle[t])) d.Tabelle[t]=Object.values(d.Tabelle[t]||{}); }); }\n    function syncVisible(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        ensureArrays();\n        var d=window.currentDbData; if(!d || !d.Tabelle) return;\n        var sel='#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        qsa(sel).forEach(function(input){\n            if(!input || input.disabled || input.readOnly) return;\n            var tbl=input.getAttribute('data-tbl')||'', key=input.getAttribute('data-key')||'', idx=parseInt(input.getAttribute('data-idx')||'',10);\n            if(!tbl || !key || isNaN(idx)) return;\n            var rt=resolveTable(tbl); if(!d.Tabelle[rt]) d.Tabelle[rt]=[]; if(!d.Tabelle[rt][idx] || typeof d.Tabelle[rt][idx] !== 'object') d.Tabelle[rt][idx]={};\n            var val=readVal(input); try { if (window.normalizeInputValueForKey) val=window.normalizeInputValueForKey(key,val); } catch(e) {}\n            if(txt(d.Tabelle[rt][idx][key]) !== txt(val)) { d.Tabelle[rt][idx][key]=val; if(!window.modifiedFields) window.modifiedFields=new Set(); try { window.modifiedFields.add(rt+'-'+idx+'-'+key); } catch(e) {} }\n        });\n        try { if (window.tiApplyPendingLinkedFileRemovals676) window.tiApplyPendingLinkedFileRemovals676(); } catch(e) {}\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n    }\n    function addChange(out, seen, table, row, key, value){ if(!table || row<0 || !key || String(key).indexOf('__')===0) return false; if(value && typeof value==='object') return false; var sig=table+'\\u0001'+row+'\\u0001'+key; if(seen[sig]) return true; seen[sig]=true; out.push({table:table,row:row,key:key,value:(value==null?'':String(value))}); return true; }\n    function buildChanges(){\n        ensureArrays();\n        var cur=(window.currentDbData&&window.currentDbData.Tabelle)?window.currentDbData.Tabelle:{};\n        var old=(window.configOriginalDbData&&window.configOriginalDbData.Tabelle)?window.configOriginalDbData.Tabelle:{};\n        var changes=[], seen={}, fullNeeded=false;\n        var all={}; Object.keys(cur||{}).forEach(function(t){all[t]=true;}); Object.keys(old||{}).forEach(function(t){all[t]=true;});\n        Object.keys(all).forEach(function(t){\n            var cr=Array.isArray(cur[t])?cur[t]:Object.values(cur[t]||{}), or=Array.isArray(old[t])?old[t]:Object.values(old[t]||{});\n            if(cr.length!==or.length){ fullNeeded=true; return; }\n            for(var i=0;i<cr.length;i++){\n                var a=cr[i]||{}, b=or[i]||{}; if(!a || typeof a!=='object' || !b || typeof b!=='object'){ fullNeeded=true; return; }\n                var keys={}; Object.keys(a).forEach(function(k){keys[k]=true;}); Object.keys(b).forEach(function(k){keys[k]=true;});\n                Object.keys(keys).forEach(function(k){ var av=(a[k]==null?'':a[k]), bv=(b[k]==null?'':b[k]); var same=false; try{ same=JSON.stringify(av)===JSON.stringify(bv); }catch(e){ same=txt(av)===txt(bv); } if(!same){ if(!addChange(changes,seen,t,i,k,av)) fullNeeded=true; } });\n            }\n        });\n        try { if((window.tiConfigDeletedRowsManifest||[]).length || Object.keys(window.tiConfigDeletedTableSnapshots||{}).length || parseInt(window.tiConfigDeletedRowsCountTotal||0,10)>0) fullNeeded=true; } catch(e) {}\n        if(changes.length>250) fullNeeded=true;\n        return {changes:changes, fullNeeded:fullNeeded};\n    }\n    function postFields691(db, changes){\n        return new Promise(function(resolve,reject){\n            var fd=new FormData(); fd.append('ti_action','ti_ai_save_config_fields_691'); fd.append('action','ti_ai_save_config_fields_691'); fd.append('db',db); fd.append('changes',enc(changes)); fd.append('field_save_691','1');\n            try { if(window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n            var url=window.tiUrl || window.tiAjaxUrl || window.location.href.split('?')[0];\n            var xhr=new XMLHttpRequest();\n            var done=false;\n            var finish=function(ok, payload){ if(done) return; done=true; ok ? resolve(payload) : reject(payload); };\n            xhr.open('POST', url, true);\n            xhr.timeout=25000;\n            xhr.withCredentials=true;\n            xhr.onreadystatechange=function(){\n                if(xhr.readyState!==4) return;\n                var raw=xhr.responseText || '';\n                log691('691_xhr_complete', {status:xhr.status, raw_prefix:raw.slice(0,180)}, xhr.status>=200 && xhr.status<300 ? 'info' : 'error');\n                if(xhr.status<200 || xhr.status>=300) return finish(false, new Error('Errore server '+xhr.status+': '+raw.slice(0,220)));\n                var data=null; try { data=JSON.parse(raw); } catch(e) { return finish(false, new Error('Risposta salvataggio non JSON: '+raw.slice(0,220))); }\n                finish(true, data);\n            };\n            xhr.ontimeout=function(){ log691('691_xhr_timeout', {timeout_ms:xhr.timeout}, 'error'); finish(false, new Error('Timeout salvataggio configurazione 691. Salvataggio non confermato dal server.')); };\n            xhr.onerror=function(){ log691('691_xhr_error', {}, 'error'); finish(false, new Error('Errore rete durante salvataggio configurazione 691.')); };\n            log691('691_xhr_start', {action:'ti_ai_save_config_fields_691', changes:changes.length}, 'info');\n            xhr.send(fd);\n        });\n    }\n    var previousSave = window.saveConfig;\n    window.saveConfig = function(){\n        if(!window.currentDbData || !window.currentDbData.Tabelle){ resetButton(false); if(window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.'); return; }\n        var dbEl=id('ti-ditta'), db=dbEl?(dbEl.value||''):''; if(!db || db==='NEW_DB' || db==='Global'){ resetButton(false); if(window.tiAlert) window.tiAlert('Seleziona ditta.'); return; }\n        clearBusy(); setSaving(); window.__tiConfigSaveActive691=true; window.__saveConfigInFlight=true; window.__tiSaveConfigStartedAt=Date.now?Date.now():(new Date()).getTime();\n        var built;\n        try { syncVisible(); built=buildChanges(); } catch(e){ resetButton(false); if(window.tiAlert) window.tiAlert('Errore preparazione salvataggio: '+(e.message||e)); return; }\n        log691('691_build_changes', {changes:built.changes.length, fullNeeded:built.fullNeeded}, 'info');\n        if(!built.changes.length && !built.fullNeeded){ resetButton(true); if(window.tiAlert) window.tiAlert('Nessuna modifica da salvare.'); return; }\n        if(built.fullNeeded){\n            resetButton(false);\n            if (typeof previousSave === 'function') {\n                log691('691_fallback_full_save_previous', {}, 'warn');\n                return previousSave.apply(this, arguments);\n            }\n            if(window.tiAlert) window.tiAlert('Modifica strutturale rilevata: usa il salvataggio completo dopo aver ricaricato la configurazione.');\n            return;\n        }\n        postFields691(db,built.changes).then(function(res){\n            if(!res || !res.success) throw new Error((res&&res.data&&res.data.message)?res.data.message:'Salvataggio configurazione non riuscito.');\n            var data=res.data||{};\n            if(window.currentDbData && data.db_version) window.currentDbData.__db_version=data.db_version;\n            try { if(window.currentDbData) window.configOriginalDbData=clone(window.currentDbData); } catch(e) {}\n            try { if(window.modifiedFields && typeof window.modifiedFields.clear==='function') window.modifiedFields.clear(); } catch(e) {}\n            window.pendingLinkedFileRemovals=[]; window.tiConfigDeletedRowsManifest=[]; window.tiConfigDeletedTableSnapshots={}; window.tiConfigDeletedRowsCountTotal=0;\n            resetButton(true);\n            log691('691_save_success', {changes_applied:data.changes_applied, elapsed_ms:data.elapsed_ms, mode:data.mode}, 'info');\n            if(window.tiAlert) window.tiAlert('\u2705 Salvataggio configurazione completato.');\n        }).catch(function(err){\n            resetButton(false);\n            var msg=(err&&err.message)?err.message:'Errore durante il salvataggio configurazione.';\n            log691('691_save_error', {message:msg}, 'error');\n            if(window.tiAlert) window.tiAlert(msg); else alert(msg);\n        }).finally(function(){\n            window.__tiConfigSaveActive691=false; window.__saveConfigInFlight=false; clearBusy();\n            var b=btn(); if(b && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false);\n        });\n    };\n    window.tiForceUnlockConfigSave691=function(reason){ resetButton(false); if(reason && window.tiAlert) window.tiAlert(reason); };\n    setInterval(function(){ try{ var b=btn(); if(b && !window.__tiConfigSaveActive691 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); }catch(e){} },1000);\n})();\n<\/script>\n\n\n<script id=\"ti-config-save-direct-692\">\n(function(){\n    \"use strict\";\n    if (window.__tiConfigSaveDirect692Installed) return;\n    window.__tiConfigSaveDirect692Installed = true;\n\n    function id(x){ return document.getElementById(x); }\n    function txt(v){ return String(v == null ? '' : v); }\n    function qsa(sel, root){ return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }\n    function clone(o){ try { return JSON.parse(JSON.stringify(o || {})); } catch(e) { return {}; } }\n    function enc(obj){ var json = JSON.stringify(obj || []); if (typeof window.utoa === 'function') return window.utoa(json); return btoa(unescape(encodeURIComponent(json))); }\n    function saveButton(){ return id('lbl-conf-save'); }\n    function dbName(){ var e = id('ti-ditta'); return e ? (e.value || '') : ''; }\n    function dirtySignal699(){\n        var mf=0, rem=0, del=0, star=false;\n        try { mf = window.modifiedFields && typeof window.modifiedFields.size === 'number' ? window.modifiedFields.size : 0; } catch(e) { mf = 0; }\n        try { rem = Array.isArray(window.pendingLinkedFileRemovals) ? window.pendingLinkedFileRemovals.length : 0; } catch(e) { rem = 0; }\n        try { del += (window.tiConfigDeletedRowsManifest||[]).length || 0; } catch(e) {}\n        try { del += Object.keys(window.tiConfigDeletedTableSnapshots||{}).length || 0; } catch(e) {}\n        try { del += parseInt(window.tiConfigDeletedRowsCountTotal||0,10) || 0; } catch(e) {}\n        try { var b=saveButton(); star = !!(b && \/\\*\/.test(txt(b.innerText||b.textContent||''))); } catch(e) { star=false; }\n        return {dirty: !!(mf || rem || del || star), modifiedFields:mf, removals:rem, deleted:del, star:star};\n    }\n    function isSaveButton(el){ var b = el && el.closest ? el.closest('button') : el; return !!(b && b.id === 'lbl-conf-save'); }\n    function hasDiag(){ return !!(id('ti-config-save-diag-body-688') || id('ti-config-save-diag-body-687')); }\n    function showDiag(reason){ if(!window.tiConfigSaveLogAllowed) return; try { if (window.tiShowConfigSaveDiag688) window.tiShowConfigSaveDiag688(reason || 'direct_692'); else if (window.tiShowConfigSaveDiag687) window.tiShowConfigSaveDiag687(reason || 'direct_692'); } catch(e) {} }\n    function log692(event, detail, level){\n        if(!window.tiConfigSaveLogAllowed) return;\n        level = level || 'info';\n        try { showDiag('direct_692_' + event); } catch(e) {}\n        try {\n            var pre = id('ti-config-save-diag-body-688') || id('ti-config-save-diag-body-687');\n            var lines = pre ? txt(pre.textContent || '').split('\\n').filter(Boolean).length : 0;\n            var line = '[' + String(lines + 1).padStart(3,'0') + '] +' + String(Date.now ? Date.now() : (new Date()).getTime()).slice(-6).padStart(6,' ') + 'ms ' + level.toUpperCase().padEnd(5,' ') + ' 692_' + event;\n            try { line += detail ? ' ' + JSON.stringify(detail) : ''; } catch(e) { line += ' ' + txt(detail); }\n            if (pre) { pre.textContent = (txt(pre.textContent || '') ? txt(pre.textContent || '') + '\\n' : '') + line; pre.scrollTop = pre.scrollHeight; }\n            var sm = id('ti-config-save-diag-summary-688') || id('ti-config-save-diag-summary-687');\n            if (sm) { var b = saveButton(); sm.innerHTML = 'DB: <b>' + txt(dbName()) + '<\/b> | Pulsante: <b>' + txt(b ? b.innerText : '') + '<\/b> | direct 692 | evento: <b>' + txt(event) + '<\/b>'; }\n        } catch(e) {}\n        try { (level === 'error' ? console.error : (level === 'warn' ? console.warn : console.log))('[Shop-Service save direct 692]', event, detail || {}); } catch(e) {}\n    }\n    function clearBusy(){\n        try { window.__tiConfigSaveActive675=false; window.__tiConfigSaveActive677=false; window.__tiConfigSaveActive678=false; window.__tiConfigSaveActive682=false; window.__tiConfigSaveActive683=false; window.__tiConfigSaveActive684=false; window.__tiConfigSaveActive691=false; window.__tiConfigSaveActive692=false; window.__saveConfigInFlight=false; } catch(e) {}\n        try { window.__tiSaveConfigStartedAt=0; window.__tiSaveConfigStartedAt585=0; window.__tiSaveConfigStartedAt675=0; } catch(e) {}\n        try { if (typeof window.clearLongAIProcess === 'function') window.clearLongAIProcess(); } catch(e) {}\n        try { if (typeof window.hideProgressPopup === 'function') window.hideProgressPopup(false, 'Fine salvataggio configurazione', {notifyUser:false}); } catch(e) {}\n        try { document.body.classList.remove('ti-busy','ti-loading','ti-modal-blocked'); } catch(e) {}\n        try { qsa('.ti-progress-overlay,.ti-progress-popup,#ti-progress-ov,#ti-progress-popup').forEach(function(x){ if (x && x.parentNode) x.parentNode.removeChild(x); }); } catch(e) {}\n    }\n    function formatSaveStar(){\n        var b = saveButton(); if (!b) return;\n        var t = txt(b.textContent || b.innerText || '');\n        if (\/Salva Modifiche\\s*\\*\/i.test(t) && !b.querySelector('.ti-save-star-692')) {\n            b.innerHTML = 'Salva Modifiche <span class=\"ti-save-star-692\" aria-label=\"modifiche da salvare\">*<\/span>';\n        }\n    }\n    function resetButton(clean){\n        if (clean && window.modifiedFields && typeof window.modifiedFields.clear === 'function') { try { window.modifiedFields.clear(); } catch(e) {} }\n        clearBusy();\n        var b = saveButton(); if (!b) return;\n        var dirty = !!(!clean && window.modifiedFields && window.modifiedFields.size > 0);\n        b.disabled = false;\n        try { b.removeAttribute('disabled'); b.removeAttribute('aria-busy'); b.removeAttribute('data-ti-saving'); } catch(e) {}\n        b.style.pointerEvents = '';\n        b.classList.toggle('ti-save-dirty', dirty);\n        b.classList.toggle('ti-save-clean', !dirty);\n        b.innerHTML = dirty ? 'Salva Modifiche <span class=\"ti-save-star-692\" aria-label=\"modifiche da salvare\">*<\/span>' : 'Salva Modifiche';\n        try { b.style.setProperty('background', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('background-color', dirty ? '#10b981' : '#374151', 'important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','pointer','important'); } catch(e) {}\n    }\n    function setSaving(){ var b=saveButton(); if(!b) return; b.disabled=true; b.setAttribute('data-ti-saving','1'); b.setAttribute('aria-busy','true'); b.style.pointerEvents='none'; b.innerText='Salvataggio...'; try{ b.style.setProperty('background','#d97706','important'); b.style.setProperty('background-color','#d97706','important'); b.style.setProperty('color','#fff','important'); b.style.setProperty('cursor','wait','important'); }catch(e){} }\n    function readVal(el){ if(!el) return ''; if(el.tagName==='SELECT' && el.multiple) return Array.prototype.slice.call(el.selectedOptions||[]).map(function(o){return txt(o.value).trim();}).filter(Boolean).join(', '); if(el.type==='checkbox') return el.checked ? (el.value || '1') : ''; return el.value == null ? '' : el.value; }\n    function resolveTable(t){ try { return window.resolveConfigTableName ? (window.resolveConfigTableName(t)||t) : t; } catch(e) { return t; } }\n    function ensureArrays(){ var d=window.currentDbData; if(!d || !d.Tabelle) return; Object.keys(d.Tabelle).forEach(function(t){ if(!Array.isArray(d.Tabelle[t])) d.Tabelle[t]=Object.values(d.Tabelle[t]||{}); }); }\n    function syncVisible(){\n        try { if (window.tiSyncConfigAutofillSavedInfo666) window.tiSyncConfigAutofillSavedInfo666(); } catch(e) {}\n        try { if (window.syncVisibleConfigInputsToMemory) window.syncVisibleConfigInputsToMemory(); } catch(e) {}\n        ensureArrays();\n        var d=window.currentDbData; if(!d || !d.Tabelle) return;\n        var sel='#ti-conf-ov .ti-cfg-table input[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table textarea[data-tbl][data-idx][data-key],#ti-conf-ov .ti-cfg-table select[data-tbl][data-idx][data-key]';\n        qsa(sel).forEach(function(input){\n            if(!input || input.disabled || input.readOnly) return;\n            var tbl=input.getAttribute('data-tbl')||'', key=input.getAttribute('data-key')||'', idx=parseInt(input.getAttribute('data-idx')||'',10);\n            if(!tbl || !key || isNaN(idx)) return;\n            var rt=resolveTable(tbl); if(!d.Tabelle[rt]) d.Tabelle[rt]=[]; if(!d.Tabelle[rt][idx] || typeof d.Tabelle[rt][idx] !== 'object') d.Tabelle[rt][idx]={};\n            var val=readVal(input); try { if (window.normalizeInputValueForKey) val=window.normalizeInputValueForKey(key,val); } catch(e) {}\n            if(txt(d.Tabelle[rt][idx][key]) !== txt(val)) { d.Tabelle[rt][idx][key]=val; if(!window.modifiedFields) window.modifiedFields=new Set(); try { window.modifiedFields.add(rt+'-'+idx+'-'+key); } catch(e) {} }\n        });\n        try { if (window.tiApplyPendingLinkedFileRemovals676) window.tiApplyPendingLinkedFileRemovals676(); } catch(e) {}\n        try { if (window.normalizeAllConfigStateValues) window.normalizeAllConfigStateValues(); } catch(e) {}\n    }\n    function addChange(out, seen, table, row, key, value){\n        if(!table || row<0 || !key || String(key).indexOf('__')===0) return false;\n        if(value && typeof value === 'object') return false;\n        var sig=table+'\\u0001'+row+'\\u0001'+key; if(seen[sig]) return true; seen[sig]=true;\n        out.push({table:table,row:row,key:key,value:(value==null?'':String(value))}); return true;\n    }\n    function buildChanges(){\n        ensureArrays();\n        var cur=(window.currentDbData&&window.currentDbData.Tabelle)?window.currentDbData.Tabelle:{};\n        var old=(window.configOriginalDbData&&window.configOriginalDbData.Tabelle)?window.configOriginalDbData.Tabelle:{};\n        var changes=[], seen={}, fullNeeded=false;\n        var all={}; Object.keys(cur||{}).forEach(function(t){all[t]=true;}); Object.keys(old||{}).forEach(function(t){all[t]=true;});\n        Object.keys(all).forEach(function(t){\n            var cr=Array.isArray(cur[t])?cur[t]:Object.values(cur[t]||{}), or=Array.isArray(old[t])?old[t]:Object.values(old[t]||{});\n            if(cr.length!==or.length){ fullNeeded=true; return; }\n            for(var i=0;i<cr.length;i++){\n                var a=cr[i]||{}, b=or[i]||{}; if(!a || typeof a!=='object' || !b || typeof b!=='object'){ fullNeeded=true; return; }\n                var keys={}; Object.keys(a).forEach(function(k){keys[k]=true;}); Object.keys(b).forEach(function(k){keys[k]=true;});\n                Object.keys(keys).forEach(function(k){ var av=(a[k]==null?'':a[k]), bv=(b[k]==null?'':b[k]); var same=false; try{ same=JSON.stringify(av)===JSON.stringify(bv); }catch(e){ same=txt(av)===txt(bv); } if(!same){ if(!addChange(changes,seen,t,i,k,av)) fullNeeded=true; } });\n            }\n        });\n        try { if((window.tiConfigDeletedRowsManifest||[]).length || Object.keys(window.tiConfigDeletedTableSnapshots||{}).length || parseInt(window.tiConfigDeletedRowsCountTotal||0,10)>0) fullNeeded=true; } catch(e) {}\n        if(changes.length>250) fullNeeded=true;\n        return {changes:changes, fullNeeded:fullNeeded};\n    }\n    function postFields692(db, changes){\n        return new Promise(function(resolve,reject){\n            var fd=new FormData();\n            fd.append('ti_action','ti_ai_save_config_fields_704'); fd.append('action','ti_ai_save_config_fields_704'); fd.append('db',db); fd.append('changes',enc(changes)); fd.append('field_save_704','1');\n            try { if(window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n            var url=window.tiUrl || window.tiAjaxUrl || window.location.href.split('?')[0];\n            var xhr=new XMLHttpRequest(), done=false;\n            var watchdog=setTimeout(function(){\n                if(done) return;\n                try { xhr.abort(); } catch(e) {}\n                log692('xhr_watchdog_abort_704', {timeout_ms:65000}, 'error');\n                finish(false, new Error('Timeout salvataggio configurazione. Il server non ha risposto entro 65 secondi. Salvataggio non confermato.'));\n            }, 65000);\n            function finish(ok, payload){ if(done) return; done=true; try { clearTimeout(watchdog); } catch(e) {} ok ? resolve(payload) : reject(payload); }\n            xhr.open('POST', url, true); xhr.timeout=65000; xhr.withCredentials=true;\n            xhr.onreadystatechange=function(){\n                if(xhr.readyState!==4) return;\n                var raw=xhr.responseText || '';\n                log692('xhr_complete', {status:xhr.status, raw_prefix:raw.slice(0,220)}, xhr.status>=200 && xhr.status<300 ? 'info' : 'error');\n                if(xhr.status<200 || xhr.status>=300) return finish(false, new Error('Errore server '+xhr.status+': '+raw.slice(0,220)));\n                var data=null; try { data=JSON.parse(raw); } catch(e) { return finish(false, new Error('Risposta salvataggio non JSON: '+raw.slice(0,220))); }\n                finish(true, data);\n            };\n            xhr.ontimeout=function(){ log692('xhr_timeout_704', {timeout_ms:xhr.timeout}, 'error'); finish(false, new Error('Timeout salvataggio configurazione 704. Salvataggio non confermato dal server.')); };\n            xhr.onerror=function(){ log692('xhr_error_704', {}, 'error'); finish(false, new Error('Errore rete durante salvataggio configurazione 704.')); };\n            log692('xhr_start_704', {action:'ti_ai_save_config_fields_704', changes:changes.length}, 'info');\n            xhr.send(fd);\n        });\n    }\n\n    function activeSaveLooksLive702(){\n        var b=saveButton();\n        var now=(Date.now?Date.now():(new Date()).getTime());\n        var started=parseInt(window.__tiSaveConfigStartedAt||0,10)||0;\n        var age=started ? (now-started) : 0;\n        var buttonSaving=!!(b && (\/salvataggio\/i.test(txt(b.innerText||b.textContent||'')) || b.disabled || b.getAttribute('data-ti-saving')==='1'));\n        var inFlight=!!window.__saveConfigInFlight;\n        return !!(inFlight && buttonSaving && (!started || age < 30000));\n    }\n    function clearStaleSaveLock702(reason){\n        try { window.__tiConfigSaveActive692=false; window.__saveConfigInFlight=false; } catch(e) {}\n        try { window.__tiConfigSaveActive691=false; window.__tiConfigSaveActive684=false; window.__tiConfigSaveActive683=false; window.__tiConfigSaveActive682=false; } catch(e) {}\n        try { window.__tiSaveConfigStartedAt=0; } catch(e) {}\n        try { clearBusy(); } catch(e) {}\n        log692('stale_lock_clear_702', {reason:reason||'', buttonText:txt(saveButton()?saveButton().innerText:''), modifiedFields:(window.modifiedFields&&window.modifiedFields.size)||0, removals:Array.isArray(window.pendingLinkedFileRemovals)?window.pendingLinkedFileRemovals.length:0}, 'warn');\n    }\n    function directSave692(){\n        showDiag('direct_692_start');\n        if(!window.currentDbData || !window.currentDbData.Tabelle){ resetButton(false); if(window.tiAlert) window.tiAlert('Configurazione non caricata. Riapri Configurazione e riprova.'); return; }\n        var db=dbName(); if(!db || db==='NEW_DB' || db==='Global'){ resetButton(false); if(window.tiAlert) window.tiAlert('Seleziona ditta.'); return; }\n        if(window.__tiConfigSaveActive692){\n            if(activeSaveLooksLive702()){\n                log692('gia_in_corso', {live:true}, 'warn');\n                return;\n            }\n            clearStaleSaveLock702('flag direct_692 attivo senza richiesta salvataggio viva');\n        }\n        var sig699 = dirtySignal699();\n        if(!sig699.dirty){\n            clearBusy(); resetButton(true);\n            log692('no_changes_skip_699', sig699, 'info');\n            if(window.tiAlert) window.tiAlert('Nessuna modifica da salvare.');\n            return;\n        }\n        clearBusy(); setSaving(); window.__tiConfigSaveActive692=true; window.__saveConfigInFlight=true; window.__tiSaveConfigStartedAt=Date.now?Date.now():(new Date()).getTime();\n        var built;\n        try { syncVisible(); built=buildChanges(); } catch(e){ resetButton(false); log692('build_exception', {message:e.message||String(e)}, 'error'); if(window.tiAlert) window.tiAlert('Errore preparazione salvataggio: '+(e.message||e)); return; }\n        log692('build_changes', {changes:built.changes.length, fullNeeded:built.fullNeeded, modifiedFields: window.modifiedFields && window.modifiedFields.size ? window.modifiedFields.size : 0, removals:Array.isArray(window.pendingLinkedFileRemovals)?window.pendingLinkedFileRemovals.length:0}, 'info');\n        if(!built.changes.length && !built.fullNeeded){ resetButton(true); if(window.tiAlert) window.tiAlert('Nessuna modifica da salvare.'); return; }\n        if(built.fullNeeded){\n            resetButton(false);\n            log692('full_needed_stop', {changes:built.changes.length}, 'warn');\n            if(window.tiAlert) window.tiAlert('Modifica strutturale rilevata. Per evitare timeout, salva prima le modifiche semplici oppure ricarica Configurazione e riprova.');\n            return;\n        }\n        postFields692(db,built.changes).then(function(res){\n            if(!res || !res.success) throw new Error((res&&res.data&&res.data.message)?res.data.message:'Salvataggio configurazione non riuscito.');\n            var data=res.data||{};\n            if(window.currentDbData && data.db_version) window.currentDbData.__db_version=data.db_version;\n            try { if(window.currentDbData) window.configOriginalDbData=clone(window.currentDbData); } catch(e) {}\n            try { if(window.modifiedFields && typeof window.modifiedFields.clear==='function') window.modifiedFields.clear(); } catch(e) {}\n            window.pendingLinkedFileRemovals=[]; window.tiConfigDeletedRowsManifest=[]; window.tiConfigDeletedTableSnapshots={}; window.tiConfigDeletedRowsCountTotal=0;\n            resetButton(true);\n            log692('save_success', {changes_applied:data.changes_applied, elapsed_ms:data.elapsed_ms, mode:data.mode || data.fields_saved_704 || data.fields_saved_703 || data.fields_saved_691}, 'info');\n            if(window.tiAlert) window.tiAlert('Salvataggio configurazione completato.');\n        }).catch(function(err){\n            resetButton(false);\n            var msg=(err&&err.message)?err.message:'Errore durante il salvataggio configurazione.';\n            log692('save_error', {message:msg}, 'error');\n            if(window.tiAlert) window.tiAlert(msg); else alert(msg);\n        }).finally(function(){\n            window.__tiConfigSaveActive692=false; window.__saveConfigInFlight=false; clearBusy();\n            var b=saveButton(); if(b && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false);\n            formatSaveStar();\n        });\n    }\n    directSave692.__tiDiag688 = true;\n    directSave692.__tiDirect692 = true;\n    function install(){\n        var b = saveButton();\n        window.saveConfig = directSave692;\n        window.saveConfig.__tiDiag688 = true;\n        window.saveConfig.__tiDirect692 = true;\n        if (b && !b.__tiDirect692Click) {\n            b.__tiDirect692Click = true;\n            try { b.onclick = function(ev){ if(ev){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } directSave692(); return false; }; } catch(e) {}\n        }\n        formatSaveStar();\n    }\n    document.addEventListener('click', function(ev){\n        if(!isSaveButton(ev && ev.target)) return;\n        try { ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); } catch(e) {}\n        log692('capture_click_salva', {buttonText: txt(saveButton() ? saveButton().innerText : ''), db:dbName()}, 'info');\n        directSave692();\n    }, true);\n    try {\n        var css=document.createElement('style');\n        css.id='ti-save-star-692-css';\n        css.textContent='#lbl-conf-save .ti-save-star-692{font-size:22px!important;line-height:10px!important;font-weight:900!important;vertical-align:-2px!important;margin-left:4px!important;color:#fff7ed!important;text-shadow:0 0 5px rgba(255,255,255,.65)!important;}';\n        document.head.appendChild(css);\n    } catch(e) {}\n    try { var mo=new MutationObserver(function(){ formatSaveStar(); install(); }); mo.observe(document.body,{childList:true,subtree:true,characterData:true}); } catch(e) {}\n    install();\n    setTimeout(install, 0); setTimeout(install, 350); setTimeout(install, 1200); setInterval(install, 1800);\n    setInterval(function(){ try { var b=saveButton(); if(b && !window.__tiConfigSaveActive692 && (\/salvataggio\/i.test(txt(b.innerText)) || b.disabled || b.getAttribute('data-ti-saving')==='1')) resetButton(false); } catch(e) {} }, 1000);\n})();\n<\/script>\n\n<script id=\"ti-batch-ai-products-services-693\">\n(function(){\n    \"use strict\";\n    if (window.__tiBatchAiProductsServices693Installed) return;\n    window.__tiBatchAiProductsServices693Installed = true;\n\n    function byId(id){ return document.getElementById(id); }\n    function esc(v){\n        if (window.escapeHtml) return window.escapeHtml(String(v == null ? '' : v));\n        return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; });\n    }\n    function norm(v){ return String(v == null ? '' : v).trim(); }\n    function tableScopeFromText(v){\n        var p = norm(v).toLowerCase();\n        var wantsProd = \/prodott|articol|catalogo\/.test(p);\n        var wantsServ = \/serviz|prestaz|trattament\/.test(p);\n        \/\/ 30.9.694: Prodotti e Servizi devono restare flussi separati.\n        \/\/ Se il testo li richiede entrambi, non viene creato ne avviato un batch unico.\n        if (wantsProd && wantsServ) return 'SEPARATI';\n        if (wantsServ) return 'Servizi';\n        return 'Prodotti';\n    }\n    function isPhotoPrompt(v){\n        var p = norm(v).toLowerCase();\n        if (!p) return false;\n        var photo = \/(genera|crea|aggiung|inserisc|prepara|produci).*(foto|immagin|image)|foto\\s+ai|immagin[ei]\\s+ai\/.test(p);\n        var scope = \/(prodott|articol|serviz|catalogo|tutti|tutte)\/.test(p);\n        return photo && scope;\n    }\n    function resolveDb(){\n        var candidates = [];\n        var d = byId('ti-ditta');\n        if (d && d.value) candidates.push(d.value);\n        if (window.tiCurrentSelectedDb) candidates.push(window.tiCurrentSelectedDb);\n        if (window.tiSessionDb) candidates.push(window.tiSessionDb);\n        try { var ls = localStorage.getItem('ti_saved_db'); if (ls) candidates.push(ls); } catch(e) {}\n        for (var i=0;i<candidates.length;i++) {\n            var v = norm(candidates[i]);\n            if (v && v !== 'NEW_DB' && v !== 'Global') return v;\n        }\n        return '';\n    }\n    function ajax693(action, data, ms){\n        return new Promise(function(resolve, reject){\n            var fd = new FormData();\n            fd.append('ti_action', action); fd.append('action', action);\n            Object.keys(data || {}).forEach(function(k){ fd.append(k, data[k]); });\n            try { if (window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); } catch(e) {}\n            var url = window.tiUrl || window.tiAjaxUrl || window.location.href.split('?')[0];\n            var xhr = new XMLHttpRequest(), done = false;\n            function finish(ok, payload){ if (done) return; done = true; ok ? resolve(payload) : reject(payload); }\n            xhr.open('POST', url, true);\n            xhr.withCredentials = true;\n            xhr.timeout = ms || 60000;\n            xhr.onreadystatechange = function(){\n                if (xhr.readyState !== 4) return;\n                var raw = xhr.responseText || '';\n                if (xhr.status < 200 || xhr.status >= 300) return finish(false, new Error('HTTP ' + xhr.status + ': ' + raw.slice(0,240)));\n                var json = null;\n                try { json = JSON.parse(raw); } catch(e) { return finish(false, new Error('Risposta non JSON: ' + raw.slice(0,240))); }\n                finish(true, json);\n            };\n            xhr.ontimeout = function(){ finish(false, new Error('Timeout richiesta ' + action)); };\n            xhr.onerror = function(){ finish(false, new Error('Errore rete richiesta ' + action)); };\n            xhr.send(fd);\n        });\n    }\n    function ensureReply(){\n        var rb = byId('ti-conf-ai-reply');\n        if (rb) { rb.style.display = 'block'; return rb; }\n        var area = byId('ti-conf-ai-box') || byId('ti-conf-ov') || document.body;\n        rb = document.createElement('div');\n        rb.id = 'ti-conf-ai-reply';\n        rb.style.cssText = 'display:block;margin:10px 0;padding:10px;border:1px solid #334155;border-radius:10px;background:#0f172a;color:#e5e7eb;font-size:13px;line-height:1.45;max-height:260px;overflow:auto;';\n        area.appendChild(rb);\n        return rb;\n    }\n    function logBox(rb, html){ if (rb) rb.innerHTML = html; }\n    function buildBatchButton(){\n        \/\/ 30.9.694: nessun pulsante unico Prodotti+Servizi.\n        \/\/ La funzione resta solo come cleanup difensivo per rimuovere eventuali bottoni creati dalla 30.9.693 ancora presenti nel DOM\/cache.\n        try {\n            var allBtns = Array.prototype.slice.call(document.querySelectorAll('button'));\n            allBtns.forEach(function(b){\n                var label = String(b.textContent || '').replace(\/\\s+\/g,' ').trim();\n                if (b.id === 'ti-batch-ai-all-693' || \/^Genera foto AI Prodotti e Servizi$\/i.test(label)) {\n                    if (b.parentNode) b.parentNode.removeChild(b);\n                }\n            });\n        } catch(e) {}\n    }\n    function setButtonBusy(on){\n        try {\n            document.querySelectorAll('button').forEach(function(b){\n                var t = String(b.textContent || '');\n                if (\/^\\s*Genera foto AI (Prodotti|Servizi)\\s*$\/i.test(t)) {\n                    b.disabled = !!on;\n                    b.style.cursor = on ? 'wait' : '';\n                }\n            });\n        } catch(e) {}\n    }\n    window.tiRunBatchAIGen693 = function(tableName){\n        var db = resolveDb();\n        var rb = ensureReply();\n        var scope = norm(tableName || 'Prodotti');\n        if (\/prodotti\\s*e\\s*servizi\/i.test(scope) || \/,\/.test(scope) || scope === 'SEPARATI') {\n            setButtonBusy(false);\n            logBox(rb, '<span style=\"color:#fbbf24;\">Prodotti e Servizi devono essere generati con funzioni separate.<\/span><br>Usa prima <b>Genera foto AI Prodotti<\/b> e poi <b>Genera foto AI Servizi<\/b>, oppure viceversa.');\n            if (window.tiAlert) window.tiAlert('Prodotti e Servizi devono essere generati con pulsanti separati.');\n            return false;\n        }\n        if (!\/^Prodotti$|^Servizi$\/i.test(scope)) scope = 'Prodotti';\n        scope = \/^servizi$\/i.test(scope) ? 'Servizi' : 'Prodotti';\n        if (!db) { logBox(rb, '<span style=\"color:#fca5a5;\">Seleziona prima una ditta valida.<\/span>'); if (window.tiAlert) window.tiAlert('Seleziona prima una ditta valida.'); return false; }\n        var detailEl = byId('ti-conf-ai-photo-detail');\n        var photoDetail = detailEl ? norm(detailEl.value) : '';\n        setButtonBusy(true);\n        logBox(rb, 'Avvio generazione foto AI.<br>DB: <b>' + esc(db) + '<\/b><br>Tabelle: <b>' + esc(scope) + '<\/b><br>Preparazione elenco record...');\n        try { if (window.showProgressPopup) window.showProgressPopup('Generazione foto AI', 'Preparazione elenco record per ' + scope + '.', {startPercent:3,targetPercent:15,stepMs:350,cancellable:true}); } catch(e) {}\n        ajax693('ti_ai_config_action', {db:db, mode:'batch_ai_images', tables:scope, detail_prompt:photoDetail, source:'batch693'}, 45000).then(function(res){\n            if (!res || !res.success) throw new Error(res && res.data && res.data.message ? res.data.message : 'Errore preparazione batch immagini.');\n            var data = res.data || {};\n            var targets = Array.isArray(data.targets) ? data.targets : [];\n            if (!targets.length) {\n                setButtonBusy(false);\n                try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'Nessun record da elaborare', {notifyUser:false, remindSave:false}); } catch(e) {}\n                logBox(rb, 'Nessun record trovato per <b>' + esc(scope) + '<\/b> nel database <b>' + esc(db) + '<\/b>.<br>Verifica che la ditta selezionata abbia tabelle Prodotti\/Servizi con righe dati.');\n                return;\n            }\n            var total = targets.length, index = 0, ok = 0, fail = 0, skip = 0, logs = [];\n            window.tiBatchAIGenState = {active:true, db:db, tableName:scope, total:total, generated:[], startedAt:Date.now(), source:'693'};\n            logBox(rb, 'Generazione immagini AI pronta.<br>DB: <b>' + esc(db) + '<\/b><br>Tabelle: <b>' + esc(scope) + '<\/b><br>Record totali: <b>' + total + '<\/b><br>Priorita senza associazioni: <b>' + esc(data.without_associations || 0) + '<\/b><br>Avvio generazione...');\n            function cancelled(){ try { return !!(window.isLongAIProcessCancelled && window.isLongAIProcessCancelled()); } catch(e) { return false; } }\n            function refreshDb(){\n                if (window.refreshCurrentConfigDb) return Promise.resolve(window.refreshCurrentConfigDb(db)).catch(function(){});\n                return ajax693('ti_ai_get_db_data', {db:db}, 30000).then(function(r){ if (r && r.success && r.data) { window.currentDbData = r.data; if (window.renderConfig) window.renderConfig(); } }).catch(function(){});\n            }\n            function finish(){\n                setButtonBusy(false);\n                if (window.tiBatchAIGenState) window.tiBatchAIGenState.active = false;\n                try { if (window.updateProgressPopup) window.updateProgressPopup(100, 'Generazione foto AI', 'Completamento generazione.'); } catch(e) {}\n                Promise.resolve(refreshDb()).finally(function(){\n                    try { if (window.hideProgressPopup) window.hideProgressPopup(true, 'Generazione foto AI completata', {remindSave:false, successMessage:'Immagini generate\/verificate: ' + ok + ' su ' + total + '. Errori: ' + fail + '.'}); } catch(e) {}\n                    logBox(rb, '\u2705 Generazione foto AI completata.<br>DB: <b>' + esc(db) + '<\/b><br>Tabelle: <b>' + esc(scope) + '<\/b><br>Immagini associate\/verificate: <b>' + ok + '<\/b> \/ ' + total + '<br>Gia presenti\/saltate: <b>' + skip + '<\/b><br>Errori: <b>' + fail + '<\/b><br><br>' + logs.slice(0,120).join('<br>'));\n                });\n            }\n            function next(){\n                if (cancelled()) { setButtonBusy(false); if (window.tiBatchAIGenState) window.tiBatchAIGenState.active=false; logBox(rb, 'Generazione foto AI interrotta dall utente.<br>Completate: ' + ok + '. Errori: ' + fail + '.'); return; }\n                if (index >= total) return finish();\n                var item = targets[index] || {};\n                var name = item.name || 'Elemento';\n                var pct = Math.max(5, Math.min(98, Math.round(((index + 0.1) \/ total) * 100)));\n                try { if (window.updateProgressPopup) window.updateProgressPopup(pct, 'Generazione foto AI', 'Elaborazione ' + (index+1) + ' \/ ' + total + ': ' + name, name); } catch(e) {}\n                logBox(rb, 'Generazione foto AI in corso...<br>DB: <b>' + esc(db) + '<\/b><br>Tabelle: <b>' + esc(scope) + '<\/b><br>Elaborazione <b>' + (index+1) + '<\/b> \/ ' + total + ': <b>' + esc(name) + '<\/b><br><br>' + logs.slice(-20).join('<br>'));\n                ajax693('ti_ai_config_action', {db:db, mode:'batch_ai_image_one', table:item.table || scope.split(',')[0], row_index:item.row_index, detail_prompt:photoDetail, source:'batch693'}, 180000).then(function(one){\n                    var d = one && one.data ? one.data : {};\n                    if (one && one.success && d.generated) {\n                        ok++;\n                        if (window.tiBatchAIGenState && Array.isArray(window.tiBatchAIGenState.generated) && (d.added_value || d.filename || d.url)) {\n                            window.tiBatchAIGenState.generated.push({table:d.table || item.table || '', row_index:(typeof d.row_index !== 'undefined') ? d.row_index : item.row_index, field:d.image_field || 'Immagine', value:d.added_value || d.filename || d.url, name:d.name || name});\n                        }\n                        logs.push('\u2705 ' + esc(d.name || name) + ': ' + (d.fallback_logo ? 'usato logo ditta' : 'immagine generata e associazione salvata'));\n                    } else if (one && one.success && d.skipped) {\n                        skip++; logs.push('\u23ed\ufe0f ' + esc(d.name || name) + ': gia presente');\n                    } else {\n                        fail++; logs.push('\u274c ' + esc(d.name || name) + ': ' + esc(d.message || 'errore generazione'));\n                    }\n                }).catch(function(err){ fail++; logs.push('\u274c ' + esc(name) + ': ' + esc(err && err.message ? err.message : 'errore rete\/timeout')); }).finally(function(){ index++; setTimeout(next, 250); });\n            }\n            next();\n        }).catch(function(err){\n            setButtonBusy(false);\n            try { if (window.hideProgressPopup) window.hideProgressPopup(false, 'Errore generazione foto AI'); } catch(e) {}\n            logBox(rb, '<span style=\"color:#fca5a5;\">Errore avvio generazione foto AI:<\/span><br>' + esc(err && err.message ? err.message : 'errore sconosciuto') + '<br><br>DB: <b>' + esc(db) + '<\/b><br>Tabelle: <b>' + esc(scope) + '<\/b>');\n            if (window.tiAlert) window.tiAlert('Errore avvio generazione foto AI: ' + (err && err.message ? err.message : 'errore sconosciuto'));\n        });\n        return false;\n    };\n\n    window.runBatchAIGen = function(tableName){ return window.tiRunBatchAIGen693(tableName || 'Prodotti'); };\n    window.runBatchAIGen.__tiBatch693 = true;\n\n    var prevRunConfigBot = window.runConfigBot;\n    if (typeof prevRunConfigBot === 'function' && !prevRunConfigBot.__tiPhotoPrompt693) {\n        window.runConfigBot = function(){\n            var box = byId('ti-conf-ai-prompt');\n            var prompt = box ? norm(box.value || '') : '';\n            if (isPhotoPrompt(prompt)) {\n                var scope = tableScopeFromText(prompt);\n                var rb = ensureReply();\n                rb.style.display = 'block';\n                if (scope === 'SEPARATI') {\n                    rb.innerHTML = 'Richiesta riconosciuta: generazione foto AI per <b>Prodotti<\/b> e <b>Servizi<\/b>.<br>Per scelta operativa i flussi restano separati: premi <b>Genera foto AI Prodotti<\/b> e poi <b>Genera foto AI Servizi<\/b>.';\n                    return false;\n                }\n                rb.innerHTML = 'Richiesta riconosciuta: generazione foto AI per <b>' + esc(scope) + '<\/b>.<br>Avvio batch dedicato...';\n                return window.tiRunBatchAIGen693(scope);\n            }\n            return prevRunConfigBot.apply(this, arguments);\n        };\n        window.runConfigBot.__tiPhotoPrompt693 = true;\n        var send = byId('ti-conf-ai-send');\n        if (send) send.onclick = function(ev){ if (ev) ev.preventDefault(); return window.runConfigBot(); };\n    }\n\n    document.addEventListener('click', function(ev){\n        var t = ev && ev.target;\n        if (!t || !t.closest) return;\n        var b = t.closest('button');\n        if (!b) return;\n        var label = String(b.textContent || '').trim();\n        if (\/^Genera foto AI Prodotti e Servizi$\/i.test(label)) { ev.preventDefault(); ev.stopPropagation(); buildBatchButton(); return false; }\n        if (\/^Genera foto AI Prodotti$\/i.test(label)) { ev.preventDefault(); ev.stopPropagation(); window.tiRunBatchAIGen693('Prodotti'); }\n        else if (\/^Genera foto AI Servizi$\/i.test(label)) { ev.preventDefault(); ev.stopPropagation(); window.tiRunBatchAIGen693('Servizi'); }\n    }, true);\n\n    buildBatchButton();\n    try { var mo = new MutationObserver(function(){ buildBatchButton(); }); mo.observe(document.body, {childList:true, subtree:true}); } catch(e) {}\n    setTimeout(buildBatchButton, 300); setTimeout(buildBatchButton, 1200);\n})();\n<\/script>\n\n\n\n<script id=\"ti-batch-ai-products-services-698\">\n(function(){\n    \"use strict\";\n    if (window.__tiBatchAiProductsServices698Installed) return;\n    window.__tiBatchAiProductsServices698Installed = true;\n    function byId(id){ return document.getElementById(id); }\n    function norm(v){ return String(v == null ? '' : v).trim(); }\n    function esc(v){\n        if (window.escapeHtml) return window.escapeHtml(String(v == null ? '' : v));\n        return String(v == null ? '' : v).replace(\/[&<>\"']\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]; });\n    }\n    function resolveDb(){\n        var candidates=[];\n        var d=byId('ti-ditta'); if(d && d.value) candidates.push(d.value);\n        if(window.tiCurrentSelectedDb) candidates.push(window.tiCurrentSelectedDb);\n        if(window.tiSessionDb) candidates.push(window.tiSessionDb);\n        try{ var ls=localStorage.getItem('ti_saved_db'); if(ls) candidates.push(ls); }catch(e){}\n        for(var i=0;i<candidates.length;i++){ var v=norm(candidates[i]); if(v && v!=='NEW_DB' && v!=='Global') return v; }\n        return '';\n    }\n    function ajax698(action, data, ms){\n        return new Promise(function(resolve,reject){\n            var fd=new FormData();\n            fd.append('ti_action', action); fd.append('action', action);\n            Object.keys(data || {}).forEach(function(k){ fd.append(k, data[k]); });\n            try{ if(window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); }catch(e){}\n            var url=window.tiUrl || window.tiAjaxUrl || window.location.href.split('?')[0];\n            var xhr=new XMLHttpRequest(), done=false;\n            function finish(ok,payload){ if(done) return; done=true; ok ? resolve(payload) : reject(payload); }\n            xhr.open('POST', url, true);\n            xhr.withCredentials=true;\n            xhr.timeout=ms || 30000;\n            xhr.onreadystatechange=function(){\n                if(xhr.readyState!==4) return;\n                var raw=xhr.responseText || '';\n                if(xhr.status<200 || xhr.status>=300) return finish(false, new Error('HTTP '+xhr.status+': '+raw.slice(0,240)));\n                var json=null;\n                try{ json=JSON.parse(raw); }catch(e){ return finish(false, new Error('Risposta non JSON: '+raw.slice(0,240))); }\n                finish(true,json);\n            };\n            xhr.ontimeout=function(){ finish(false, new Error('Timeout richiesta '+action)); };\n            xhr.onerror=function(){ finish(false, new Error('Errore rete richiesta '+action)); };\n            xhr.send(fd);\n        });\n    }\n    function ensureReply(){\n        var rb=byId('ti-conf-ai-reply');\n        if(rb){ rb.style.display='block'; return rb; }\n        var area=byId('ti-conf-ai-area') || byId('ti-config-form') || document.body;\n        rb=document.createElement('div');\n        rb.id='ti-conf-ai-reply';\n        rb.style.cssText='margin:10px 0;padding:10px;border:1px solid rgba(148,163,184,.35);border-radius:10px;background:rgba(15,23,42,.92);color:#e5e7eb;font-size:13px;line-height:1.45;';\n        area.appendChild(rb);\n        return rb;\n    }\n    function setButtonBusy(on){\n        try{\n            document.querySelectorAll('button').forEach(function(b){\n                var t=String(b.textContent || '');\n                if(\/^\\s*Genera foto AI (Prodotti|Servizi)\\s*$\/i.test(t)){\n                    b.disabled=!!on;\n                    b.style.cursor=on?'wait':'';\n                }\n            });\n        }catch(e){}\n    }\n    function isPhotoPrompt(v){\n        var p=norm(v).toLowerCase();\n        if(!p) return false;\n        var photo=\/(genera|crea|aggiung|inserisc|prepara|produci).*(foto|immagin|image)|foto\\s+ai|immagin[ei]\\s+ai\/.test(p);\n        var scope=\/(prodott|articol|serviz|catalogo|tutti|tutte)\/.test(p);\n        return photo && scope;\n    }\n    function tableScopeFromText(v){\n        var p=norm(v).toLowerCase();\n        var wantsProd=\/prodott|articol|catalogo\/.test(p);\n        var wantsServ=\/serviz|prestaz|trattament\/.test(p);\n        if(wantsProd && wantsServ) return 'SEPARATI';\n        if(wantsServ) return 'Servizi';\n        return 'Prodotti';\n    }\n    window.tiRunBatchAIGen698=function(tableName){\n        var rb=ensureReply();\n        var db=resolveDb();\n        var scope=norm(tableName || 'Prodotti');\n        if(\/prodotti\\s*e\\s*servizi\/i.test(scope) || \/,\/.test(scope) || scope==='SEPARATI'){\n            setButtonBusy(false);\n            rb.innerHTML='<span style=\"color:#fbbf24;\">Prodotti e Servizi devono essere generati con funzioni separate.<\/span><br>Usa il pulsante <b>Genera foto AI Prodotti<\/b> oppure <b>Genera foto AI Servizi<\/b>.';\n            if(window.tiAlert) window.tiAlert('Prodotti e Servizi devono essere generati con pulsanti separati.');\n            return false;\n        }\n        if(!\/^Prodotti$|^Servizi$\/i.test(scope)) scope='Prodotti';\n        scope=\/^servizi$\/i.test(scope) ? 'Servizi' : 'Prodotti';\n        if(!db){ rb.innerHTML='<span style=\"color:#fca5a5;\">Seleziona prima una ditta valida.<\/span>'; if(window.tiAlert) window.tiAlert('Seleziona prima una ditta valida.'); return false; }\n        if(window.tiBatchAIGenState && window.tiBatchAIGenState.active){\n            rb.innerHTML='Generazione foto AI gia in corso.<br>DB: <b>'+esc(window.tiBatchAIGenState.db || db)+'<\/b><br>Tabella: <b>'+esc(window.tiBatchAIGenState.tableName || scope)+'<\/b>.';\n            return false;\n        }\n        var detailEl=byId('ti-conf-ai-photo-detail');\n        var photoDetail=detailEl ? norm(detailEl.value) : '';\n        window.tiBatchAIGenState={active:true,preparing:true,db:db,tableName:scope,total:0,generated:[],startedAt:Date.now(),source:'698'};\n        setButtonBusy(true);\n        rb.innerHTML='Avvio generazione foto AI.<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Preparazione elenco record tramite endpoint leggero 698...';\n        try{ if(window.showProgressPopup) window.showProgressPopup('Generazione foto AI','Preparazione elenco record per '+scope+'.',{startPercent:3,targetPercent:15,stepMs:350,cancellable:true}); }catch(e){}\n        ajax698('ti_ai_batch_ai_images_prepare_698', {db:db, tables:scope, detail_prompt:photoDetail, source:'batch698'}, 20000).then(function(res){\n            if(!res || !res.success) throw new Error(res && res.data && res.data.message ? res.data.message : 'Errore preparazione elenco record.');\n            var data=res.data || {};\n            var targets=Array.isArray(data.targets) ? data.targets : [];\n            if(!targets.length){\n                setButtonBusy(false);\n                if(window.tiBatchAIGenState) window.tiBatchAIGenState.active=false;\n                try{ if(window.hideProgressPopup) window.hideProgressPopup(true,'Nessun record da elaborare',{notifyUser:false,remindSave:false}); }catch(e){}\n                rb.innerHTML='Nessun record trovato per <b>'+esc(scope)+'<\/b> nel database <b>'+esc(db)+'<\/b>.<br>Verifica che la tabella esista e contenga righe dati.';\n                return;\n            }\n            var total=targets.length,index=0,ok=0,fail=0,skip=0,logs=[];\n            window.tiBatchAIGenState={active:true,preparing:false,db:db,tableName:scope,total:total,generated:[],startedAt:Date.now(),source:'698'};\n            rb.innerHTML='Elenco record pronto.<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Record totali: <b>'+total+'<\/b><br>Senza associazioni: <b>'+esc(data.without_associations || 0)+'<\/b><br>Gia con associazioni: <b>'+esc(data.with_associations || 0)+'<\/b><br>Avvio generazione separata '+esc(scope)+'...';\n            function cancelled(){ try{ return !!(window.isLongAIProcessCancelled && window.isLongAIProcessCancelled()); }catch(e){ return false; } }\n            function refreshDb(){\n                if(window.refreshCurrentConfigDb) return Promise.resolve(window.refreshCurrentConfigDb(db)).catch(function(){});\n                return ajax698('ti_ai_get_db_data', {db:db}, 30000).then(function(r){ if(r && r.success && r.data){ window.currentDbData=r.data; if(window.renderConfig) window.renderConfig(); } }).catch(function(){});\n            }\n            function finish(){\n                setButtonBusy(false);\n                if(window.tiBatchAIGenState) window.tiBatchAIGenState.active=false;\n                try{ if(window.updateProgressPopup) window.updateProgressPopup(100,'Generazione foto AI','Completamento generazione.'); }catch(e){}\n                Promise.resolve(refreshDb()).finally(function(){\n                    try{ if(window.hideProgressPopup) window.hideProgressPopup(true,'Generazione foto AI completata',{remindSave:false,successMessage:'Immagini generate\/verificate: '+ok+' su '+total+'. Errori: '+fail+'.'}); }catch(e){}\n                    rb.innerHTML='\u2705 Generazione foto AI completata.<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Immagini associate\/verificate: <b>'+ok+'<\/b> \/ '+total+'<br>Gia presenti\/saltate: <b>'+skip+'<\/b><br>Errori: <b>'+fail+'<\/b><br><br>'+logs.slice(0,120).join('<br>');\n                });\n            }\n            function next(){\n                if(cancelled()){ setButtonBusy(false); if(window.tiBatchAIGenState) window.tiBatchAIGenState.active=false; rb.innerHTML='Generazione foto AI interrotta dall utente.<br>Completate: '+ok+'. Errori: '+fail+'.'; return; }\n                if(index>=total) return finish();\n                var item=targets[index] || {};\n                var name=item.name || 'Elemento';\n                var pct=Math.max(5,Math.min(98,Math.round(((index+0.1)\/total)*100)));\n                try{ if(window.updateProgressPopup) window.updateProgressPopup(pct,'Generazione foto AI','Elaborazione '+(index+1)+' \/ '+total+': '+name,name); }catch(e){}\n                rb.innerHTML='Generazione foto AI in corso...<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Elaborazione <b>'+(index+1)+'<\/b> \/ '+total+': <b>'+esc(name)+'<\/b><br><br>'+logs.slice(-20).join('<br>');\n                ajax698('ti_ai_batch_ai_image_one_698', {db:db, table:item.table || scope, row_index:item.row_index, detail_prompt:photoDetail, source:'batch698'}, 240000).then(function(one){\n                    var d=one && one.data ? one.data : {};\n                    if(one && one.success && d.generated){\n                        ok++;\n                        if(window.tiBatchAIGenState && Array.isArray(window.tiBatchAIGenState.generated) && (d.added_value || d.filename || d.url)){\n                            window.tiBatchAIGenState.generated.push({table:d.table || item.table || '', row_index:(typeof d.row_index!=='undefined') ? d.row_index : item.row_index, field:d.image_field || 'Immagine', value:d.added_value || d.filename || d.url, name:d.name || name});\n                        }\n                        logs.push('\u2705 '+esc(d.name || name)+': '+(d.fallback_logo ? 'usato logo ditta' : 'immagine generata e associazione salvata'));\n                    } else if(one && one.success && d.skipped){\n                        skip++; logs.push('\u23ed\ufe0f '+esc(d.name || name)+': gia presente');\n                    } else {\n                        fail++; logs.push('\u274c '+esc(d.name || name)+': '+esc(d.message || 'errore generazione'));\n                    }\n                }).catch(function(err){ fail++; logs.push('\u274c '+esc(name)+': '+esc(err && err.message ? err.message : 'errore rete\/timeout')); }).finally(function(){ index++; setTimeout(next,250); });\n            }\n            next();\n        }).catch(function(err){\n            setButtonBusy(false);\n            if(window.tiBatchAIGenState) window.tiBatchAIGenState.active=false;\n            try{ if(window.hideProgressPopup) window.hideProgressPopup(false,'Errore generazione foto AI'); }catch(e){}\n            rb.innerHTML='<span style=\"color:#fca5a5;\">Errore preparazione generazione foto AI:<\/span><br>'+esc(err && err.message ? err.message : 'errore sconosciuto')+'<br><br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b>';\n            if(window.tiAlert) window.tiAlert('Errore preparazione generazione foto AI: '+(err && err.message ? err.message : 'errore sconosciuto'));\n        });\n        return false;\n    };\n    window.tiRunBatchAIGen693=window.tiRunBatchAIGen698;\n    window.runBatchAIGen=function(tableName){ return window.tiRunBatchAIGen698(tableName || 'Prodotti'); };\n    window.runBatchAIGen.__tiBatch698=true;\n    var prevRunConfigBot=window.runConfigBot;\n    if(typeof prevRunConfigBot==='function' && !prevRunConfigBot.__tiPhotoPrompt698){\n        window.runConfigBot=function(){\n            var box=byId('ti-conf-ai-prompt');\n            var prompt=box ? norm(box.value || '') : '';\n            if(isPhotoPrompt(prompt)){\n                var scope=tableScopeFromText(prompt);\n                var rb=ensureReply(); rb.style.display='block';\n                if(scope==='SEPARATI'){\n                    rb.innerHTML='Richiesta riconosciuta: generazione foto AI per <b>Prodotti<\/b> e <b>Servizi<\/b>.<br>I flussi restano separati: usa <b>Genera foto AI Prodotti<\/b> e poi <b>Genera foto AI Servizi<\/b>.';\n                    return false;\n                }\n                rb.innerHTML='Richiesta riconosciuta: generazione foto AI per <b>'+esc(scope)+'<\/b>.<br>Avvio batch dedicato 698...';\n                return window.tiRunBatchAIGen698(scope);\n            }\n            return prevRunConfigBot.apply(this, arguments);\n        };\n        window.runConfigBot.__tiPhotoPrompt698=true;\n        var send=byId('ti-conf-ai-send'); if(send) send.onclick=function(ev){ if(ev) ev.preventDefault(); return window.runConfigBot(); };\n    }\n    document.addEventListener('click', function(ev){\n        var t=ev && ev.target;\n        if(!t || !t.closest) return;\n        var b=t.closest('button'); if(!b) return;\n        var label=String(b.textContent || '').trim();\n        if(\/^Genera foto AI Prodotti e Servizi$\/i.test(label)){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); if(window.tiAlert) window.tiAlert('Prodotti e Servizi devono essere generati con pulsanti separati.'); return false; }\n        if(\/^Genera foto AI Prodotti$\/i.test(label)){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); return window.tiRunBatchAIGen698('Prodotti'); }\n        if(\/^Genera foto AI Servizi$\/i.test(label)){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); return window.tiRunBatchAIGen698('Servizi'); }\n    }, true);\n})();\n<\/script>\n\n\n\n<script id=\"ti-batch-ai-products-services-706\">\n(function(){\n    if(window.__tiBatchAI706Applied) return; window.__tiBatchAI706Applied=true;\n    function byId(id){ return document.getElementById(id); }\n    function norm(v){ return (v==null?'':String(v)).trim(); }\n    function esc(s){ return String(s==null?'':s).replace(\/[&<>\"']\/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c];}); }\n    function normalizeKey(k){ return String(k||'').toLowerCase().normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').replace(\/[^a-z0-9]\/g,''); }\n    function resolveDb(){\n        var candidates=[];\n        var d=byId('ti-ditta'); if(d && d.value) candidates.push(d.value);\n        if(window.tiCurrentSelectedDb) candidates.push(window.tiCurrentSelectedDb);\n        if(window.tiSessionDb) candidates.push(window.tiSessionDb);\n        try{ var ls=localStorage.getItem('ti_saved_db'); if(ls) candidates.push(ls); }catch(e){}\n        for(var i=0;i<candidates.length;i++){ var v=norm(candidates[i]); if(v && v!=='NEW_DB' && v!=='Global') return v; }\n        return '';\n    }\n    function ensureReply(){\n        var rb=byId('ti-conf-ai-reply');\n        if(rb){ rb.style.display='block'; return rb; }\n        var area=byId('ti-conf-ai-area') || byId('ti-config-form') || document.body;\n        rb=document.createElement('div'); rb.id='ti-conf-ai-reply';\n        rb.style.cssText='margin:10px 0;padding:10px;border:1px solid rgba(148,163,184,.35);border-radius:10px;background:rgba(15,23,42,.92);color:#e5e7eb;font-size:13px;line-height:1.45;';\n        area.appendChild(rb); return rb;\n    }\n    function setButtonBusy(on){\n        try{ document.querySelectorAll('button').forEach(function(b){\n            var t=String(b.textContent || '').replace(\/\\s+\/g,' ').trim();\n            if(\/^Genera foto AI (Prodotti|Servizi)$\/i.test(t)){ b.disabled=!!on; b.style.cursor=on?'wait':''; }\n        }); }catch(e){}\n    }\n    function ajax706(action, data, ms){\n        return new Promise(function(resolve,reject){\n            var fd=new FormData(); fd.append('ti_action',action); fd.append('action',action);\n            Object.keys(data||{}).forEach(function(k){ fd.append(k,data[k]); });\n            try{ if(window.tiAppendCurrentLanguageToFormData) window.tiAppendCurrentLanguageToFormData(fd); }catch(e){}\n            var url=window.tiUrl || window.tiAjaxUrl || window.location.href.split('?')[0];\n            var xhr=new XMLHttpRequest(), done=false;\n            function finish(ok,payload){ if(done) return; done=true; ok?resolve(payload):reject(payload); }\n            xhr.open('POST',url,true); xhr.withCredentials=true; xhr.timeout=ms||30000;\n            xhr.onreadystatechange=function(){\n                if(xhr.readyState!==4) return;\n                var raw=xhr.responseText || '';\n                if(xhr.status<200 || xhr.status>=300) return finish(false,new Error('HTTP '+xhr.status+': '+raw.slice(0,240)));\n                var json=null; try{ json=JSON.parse(raw); }catch(e){ return finish(false,new Error('Risposta non JSON: '+raw.slice(0,240))); }\n                finish(true,json);\n            };\n            xhr.ontimeout=function(){ finish(false,new Error('Timeout richiesta '+action)); };\n            xhr.onerror=function(){ finish(false,new Error('Errore rete richiesta '+action)); };\n            xhr.send(fd);\n        });\n    }\n    function getTables(){\n        var db = (window.currentDbData && window.currentDbData.Tabelle) ? window.currentDbData : null;\n        if(!db && window.currentDbViewData && window.currentDbViewData.Tabelle) db=window.currentDbViewData;\n        return db && db.Tabelle ? db.Tabelle : null;\n    }\n    function realTableName(tables, wanted){\n        if(!tables) return wanted;\n        var nw=normalizeKey(wanted);\n        if(Object.prototype.hasOwnProperty.call(tables,wanted)) return wanted;\n        var keys=Object.keys(tables);\n        for(var i=0;i<keys.length;i++){ if(normalizeKey(keys[i])===nw) return keys[i]; }\n        if(\/^prodotti$\/i.test(wanted)){\n            for(var p=0;p<keys.length;p++){ if(\/prodott|articol\/i.test(keys[p])) return keys[p]; }\n        }\n        if(\/^servizi$\/i.test(wanted)){\n            for(var s=0;s<keys.length;s++){ if(\/serviz|prestaz|trattament\/i.test(keys[s])) return keys[s]; }\n        }\n        return wanted;\n    }\n    function pick(row, keys){\n        if(!row) return '';\n        var rowKeys=Object.keys(row), map={}; rowKeys.forEach(function(k){ map[normalizeKey(k)]=k; });\n        for(var i=0;i<keys.length;i++){ var rk=map[normalizeKey(keys[i])]; if(rk && norm(row[rk])!=='') return norm(row[rk]); }\n        return '';\n    }\n    function splitValues(v){\n        var s=norm(v); if(!s) return [];\n        return s.split(\/[\\n|;]+\/).map(function(x){ return norm(x); }).filter(Boolean);\n    }\n    function looksMedia(v){ return \/\\.(png|jpe?g|webp|gif|pdf|docx?|xlsx?|csv|txt)$\/i.test(v) || \/ti_ai_get_image|\\\/wp-content\\\/uploads\\\/AI-data\/i.test(v); }\n    function countMedia(row){\n        if(!row || typeof row!=='object') return 0;\n        var count=0;\n        Object.keys(row).forEach(function(k){\n            var nk=normalizeKey(k);\n            if(!\/(immagine|immagini|image|foto|file|doc|docs|allegato|allegati|media)\/i.test(nk)) return;\n            splitValues(row[k]).forEach(function(v){ if(looksMedia(v)) count++; });\n        });\n        return count;\n    }\n    function prepareTargetsLocal(scope){\n        var tables=getTables();\n        if(!tables) return {ok:false,message:'Dati configurazione non disponibili in memoria. Riapri Configurazione e riprova.'};\n        var real=realTableName(tables,scope);\n        var rows=tables[real];\n        if(rows && !Array.isArray(rows)) rows=Object.values(rows||{});\n        if(!Array.isArray(rows)) return {ok:false,message:'Tabella '+scope+' non trovata nella configurazione corrente.'};\n        var no=[], yes=[];\n        rows.forEach(function(row,idx){\n            if(!row || typeof row!=='object') return;\n            var ac=countMedia(row);\n            var name=pick(row,['Prodotto','Servizio','Nome','Titolo','Articolo','Descrizione','Dettaglio','Note']);\n            if(!name) name='Elemento riga '+(idx+1);\n            var target={table:real,row_index:idx,row_ref:idx+1,name:name,has_associations:ac>0,association_count:ac,priority_label:(ac>0?'gia con associazioni':'senza associazioni')};\n            if(ac>0) yes.push(target); else no.push(target);\n        });\n        return {ok:true,targets:no.concat(yes),without_associations:no.length,with_associations:yes.length,requested_tables:[real]};\n    }\n    function cancelled(){ try{ return !!(window.isLongAIProcessCancelled && window.isLongAIProcessCancelled()); }catch(e){ return false; } }\n    window.tiRunBatchAIGen706=function(tableName){\n        var rb=ensureReply(), db=resolveDb();\n        var scope=norm(tableName||'Prodotti');\n        if(\/prodotti\\s*e\\s*servizi\/i.test(scope) || \/,\/.test(scope) || scope==='SEPARATI'){\n            setButtonBusy(false);\n            rb.innerHTML='<span style=\"color:#fbbf24;\">Prodotti e Servizi devono essere generati con funzioni separate.<\/span><br>Usa il pulsante <b>Genera foto AI Prodotti<\/b> oppure <b>Genera foto AI Servizi<\/b>.';\n            if(window.tiAlert) window.tiAlert('Prodotti e Servizi devono essere generati con pulsanti separati.');\n            return false;\n        }\n        scope=\/^servizi$\/i.test(scope)?'Servizi':'Prodotti';\n        if(!db){ rb.innerHTML='<span style=\"color:#fca5a5;\">Seleziona prima una ditta valida.<\/span>'; return false; }\n        if(window.tiBatchAIGenState && window.tiBatchAIGenState.active){\n            var age=Date.now()-(window.tiBatchAIGenState.startedAt||0);\n            if(age<30000){ rb.innerHTML='Generazione foto AI gia in corso.<br>DB: <b>'+esc(window.tiBatchAIGenState.db||db)+'<\/b><br>Tabella: <b>'+esc(window.tiBatchAIGenState.tableName||scope)+'<\/b>.'; return false; }\n            window.tiBatchAIGenState.active=false;\n        }\n        var detailEl=byId('ti-conf-ai-photo-detail'); var photoDetail=detailEl?norm(detailEl.value):'';\n        window.tiBatchAIGenState={active:true,preparing:true,db:db,tableName:scope,total:0,generated:[],startedAt:Date.now(),source:'706'};\n        setButtonBusy(true);\n        rb.innerHTML='Avvio generazione foto AI.<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Preparazione elenco record locale, senza chiamata server pesante...';\n        try{ if(window.showProgressPopup) window.showProgressPopup('Generazione foto AI','Preparazione elenco record per '+scope+'.',{startPercent:3,targetPercent:12,stepMs:350,cancellable:true}); }catch(e){}\n        var prep=prepareTargetsLocal(scope);\n        if(!prep.ok){\n            setButtonBusy(false); window.tiBatchAIGenState.active=false;\n            try{ if(window.hideProgressPopup) window.hideProgressPopup(false,'Errore preparazione generazione foto AI'); }catch(e){}\n            rb.innerHTML='<span style=\"color:#fca5a5;\">'+esc(prep.message)+'<\/span>';\n            if(window.tiAlert) window.tiAlert(prep.message);\n            return false;\n        }\n        var targets=prep.targets||[];\n        if(!targets.length){\n            setButtonBusy(false); window.tiBatchAIGenState.active=false;\n            try{ if(window.hideProgressPopup) window.hideProgressPopup(true,'Nessun record da elaborare',{notifyUser:false,remindSave:false}); }catch(e){}\n            rb.innerHTML='Nessun record trovato per <b>'+esc(scope)+'<\/b> nel database <b>'+esc(db)+'<\/b>.';\n            return false;\n        }\n        var total=targets.length,index=0,ok=0,fail=0,skip=0,logs=[];\n        window.tiBatchAIGenState={active:true,preparing:false,db:db,tableName:scope,total:total,generated:[],startedAt:Date.now(),source:'706'};\n        rb.innerHTML='Elenco record pronto.<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Record totali: <b>'+total+'<\/b><br>Senza associazioni: <b>'+esc(prep.without_associations||0)+'<\/b><br>Gia con associazioni: <b>'+esc(prep.with_associations||0)+'<\/b><br>Avvio generazione separata '+esc(scope)+'...';\n        function refreshDb(){\n            if(window.refreshCurrentConfigDb) return Promise.resolve(window.refreshCurrentConfigDb(db)).catch(function(){});\n            return ajax706('ti_ai_get_db_data',{db:db},30000).then(function(r){ if(r && r.success && r.data){ window.currentDbData=r.data; if(window.renderConfig) window.renderConfig(); } }).catch(function(){});\n        }\n        function finish(){\n            setButtonBusy(false); if(window.tiBatchAIGenState) window.tiBatchAIGenState.active=false;\n            try{ if(window.updateProgressPopup) window.updateProgressPopup(100,'Generazione foto AI','Completamento generazione.'); }catch(e){}\n            Promise.resolve(refreshDb()).finally(function(){\n                try{ if(window.hideProgressPopup) window.hideProgressPopup(true,'Generazione foto AI completata',{remindSave:false,successMessage:'Immagini generate\/verificate: '+ok+' su '+total+'. Errori: '+fail+'.'}); }catch(e){}\n                rb.innerHTML='\u2705 Generazione foto AI completata.<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Immagini associate\/verificate: <b>'+ok+'<\/b> \/ '+total+'<br>Gia presenti\/saltate: <b>'+skip+'<\/b><br>Errori: <b>'+fail+'<\/b><br><br>'+logs.slice(0,120).join('<br>');\n            });\n        }\n        function next(){\n            if(cancelled()){ setButtonBusy(false); if(window.tiBatchAIGenState) window.tiBatchAIGenState.active=false; rb.innerHTML='Generazione foto AI interrotta dall utente.<br>Completate: '+ok+'. Errori: '+fail+'.'; return; }\n            if(index>=total) return finish();\n            var item=targets[index]||{}, name=item.name||'Elemento';\n            var pct=Math.max(5,Math.min(98,Math.round(((index+0.1)\/total)*100)));\n            try{ if(window.updateProgressPopup) window.updateProgressPopup(pct,'Generazione foto AI','Elaborazione '+(index+1)+' \/ '+total+': '+name,name); }catch(e){}\n            rb.innerHTML='Generazione foto AI in corso...<br>DB: <b>'+esc(db)+'<\/b><br>Tabella: <b>'+esc(scope)+'<\/b><br>Elaborazione <b>'+(index+1)+'<\/b> \/ '+total+': <b>'+esc(name)+'<\/b><br><br>'+logs.slice(-20).join('<br>');\n            ajax706('ti_ai_batch_ai_image_one_698',{db:db,table:item.table||scope,row_index:item.row_index,detail_prompt:photoDetail,source:'batch706'},240000).then(function(one){\n                var d=one && one.data ? one.data : {};\n                if(one && one.success && d.generated){ ok++; logs.push('\u2705 '+esc(d.name||name)+': '+(d.fallback_logo?'usato logo ditta':'immagine generata e associazione salvata')); }\n                else if(one && one.success && d.skipped){ skip++; logs.push('\u23ed\ufe0f '+esc(d.name||name)+': gia presente'); }\n                else { fail++; logs.push('\u274c '+esc(d.name||name)+': '+esc(d.message||'errore generazione')); }\n            }).catch(function(err){ fail++; logs.push('\u274c '+esc(name)+': '+esc(err && err.message ? err.message : 'errore rete\/timeout')); }).finally(function(){ index++; setTimeout(next,250); });\n        }\n        setTimeout(next,80);\n        return false;\n    };\n    window.tiRunBatchAIGen698=window.tiRunBatchAIGen706;\n    window.tiRunBatchAIGen693=window.tiRunBatchAIGen706;\n    window.runBatchAIGen=function(tableName){ return window.tiRunBatchAIGen706(tableName||'Prodotti'); };\n    window.runBatchAIGen.__tiBatch706=true;\n    document.addEventListener('click',function(ev){\n        var t=ev && ev.target; if(!t || !t.closest) return;\n        var b=t.closest('button'); if(!b) return;\n        var label=String(b.textContent||'').replace(\/\\s+\/g,' ').trim();\n        if(\/^Genera foto AI Prodotti e Servizi$\/i.test(label)){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); if(window.tiAlert) window.tiAlert('Prodotti e Servizi devono essere generati con pulsanti separati.'); return false; }\n        if(\/^Genera foto AI Prodotti$\/i.test(label)){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); return window.tiRunBatchAIGen706('Prodotti'); }\n        if(\/^Genera foto AI Servizi$\/i.test(label)){ ev.preventDefault(); ev.stopPropagation(); if(ev.stopImmediatePropagation) ev.stopImmediatePropagation(); return window.tiRunBatchAIGen706('Servizi'); }\n    }, true);\n})();\n<\/script>\n\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"Pagina_base_vuota.php","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_kadence_starter_templates_imported_post":false,"_kad_post_transparent":"default","_kad_post_title":"default","_kad_post_layout":"default","_kad_post_sidebar_id":"","_kad_post_content_style":"default","_kad_post_vertical_padding":"default","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-1165","page","type-page","status-publish","hentry"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO Pro 4.9.10 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"google-site-verification\" content=\"BqeY9R53B2HkBp_g3d5fh-yHxyeD5uEk2HT7-cmQ8Uw\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.tisoft.it\/en\/shop-service\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO Pro (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_GB\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Shop - Quello che facciamo meglio \u00e8 dare forma alle vostre idee\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Shop &amp; Service\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.tisoft.it\/en\/shop-service\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/www.tisoft.it\/wp-content\/uploads\/2024\/04\/cropped-LogoTarget-carico.gif\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/www.tisoft.it\/wp-content\/uploads\/2024\/04\/cropped-LogoTarget-carico.gif\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-04-14T08:48:53+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-04-17T08:14:20+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/TargetInformaticaTerni\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Shop &amp; Service\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/www.tisoft.it\/wp-content\/uploads\/2024\/04\/cropped-LogoTarget-carico.gif\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.tisoft.it\\\/en\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/#listItem\",\"name\":\"Shop &#038; Service\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/#listItem\",\"position\":2,\"name\":\"Shop &#038; Service\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en#listItem\",\"name\":\"Home\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/#organization\",\"name\":\"Target Informatica Shop\",\"description\":\"Quello che facciamo meglio \\u00e8 dare forma alle vostre idee\",\"url\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/\",\"telephone\":\"+390744288410\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/www.tisoft.it\\\/wp-content\\\/uploads\\\/2024\\\/04\\\/Target-Informatica-logo.png\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/#organizationLogo\",\"width\":3951,\"height\":1154},\"image\":{\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/#organizationLogo\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/TargetInformaticaTerni\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/targetinformatica\\\/?viewAsMember=true\"]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/#webpage\",\"url\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/\",\"name\":\"Shop & Service\",\"inLanguage\":\"en-GB\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/shop-service\\\/#breadcrumblist\"},\"datePublished\":\"2026-04-14T10:48:53+02:00\",\"dateModified\":\"2026-04-17T10:14:20+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/\",\"name\":\"Shop\",\"description\":\"Quello che facciamo meglio \\u00e8 dare forma alle vostre idee\",\"inLanguage\":\"en-GB\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.tisoft.it\\\/en\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO Pro -->\r\n\t\t<title>Shop &amp; Service<\/title>\n\n","aioseo_head_json":{"title":"Shop &amp; Service","description":"","canonical_url":"https:\/\/www.tisoft.it\/en\/shop-service\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"google-site-verification":"BqeY9R53B2HkBp_g3d5fh-yHxyeD5uEk2HT7-cmQ8Uw","miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BreadcrumbList","@id":"https:\/\/www.tisoft.it\/en\/shop-service\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.tisoft.it\/en#listItem","position":1,"name":"Home","item":"https:\/\/www.tisoft.it\/en","nextItem":{"@type":"ListItem","@id":"https:\/\/www.tisoft.it\/en\/shop-service\/#listItem","name":"Shop &#038; Service"}},{"@type":"ListItem","@id":"https:\/\/www.tisoft.it\/en\/shop-service\/#listItem","position":2,"name":"Shop &#038; Service","previousItem":{"@type":"ListItem","@id":"https:\/\/www.tisoft.it\/en#listItem","name":"Home"}}]},{"@type":"Organization","@id":"https:\/\/www.tisoft.it\/en\/#organization","name":"Target Informatica Shop","description":"Quello che facciamo meglio \u00e8 dare forma alle vostre idee","url":"https:\/\/www.tisoft.it\/en\/","telephone":"+390744288410","logo":{"@type":"ImageObject","url":"https:\/\/www.tisoft.it\/wp-content\/uploads\/2024\/04\/Target-Informatica-logo.png","@id":"https:\/\/www.tisoft.it\/en\/shop-service\/#organizationLogo","width":3951,"height":1154},"image":{"@id":"https:\/\/www.tisoft.it\/en\/shop-service\/#organizationLogo"},"sameAs":["https:\/\/www.facebook.com\/TargetInformaticaTerni","https:\/\/www.linkedin.com\/company\/targetinformatica\/?viewAsMember=true"]},{"@type":"WebPage","@id":"https:\/\/www.tisoft.it\/en\/shop-service\/#webpage","url":"https:\/\/www.tisoft.it\/en\/shop-service\/","name":"Shop & Service","inLanguage":"en-GB","isPartOf":{"@id":"https:\/\/www.tisoft.it\/en\/#website"},"breadcrumb":{"@id":"https:\/\/www.tisoft.it\/en\/shop-service\/#breadcrumblist"},"datePublished":"2026-04-14T10:48:53+02:00","dateModified":"2026-04-17T10:14:20+02:00"},{"@type":"WebSite","@id":"https:\/\/www.tisoft.it\/en\/#website","url":"https:\/\/www.tisoft.it\/en\/","name":"Shop","description":"Quello che facciamo meglio \u00e8 dare forma alle vostre idee","inLanguage":"en-GB","publisher":{"@id":"https:\/\/www.tisoft.it\/en\/#organization"}}]},"og:locale":"en_GB","og:site_name":"Shop - Quello che facciamo meglio \u00e8 dare forma alle vostre idee","og:type":"article","og:title":"Shop &amp; Service","og:url":"https:\/\/www.tisoft.it\/en\/shop-service\/","og:image":"https:\/\/www.tisoft.it\/wp-content\/uploads\/2024\/04\/cropped-LogoTarget-carico.gif","og:image:secure_url":"https:\/\/www.tisoft.it\/wp-content\/uploads\/2024\/04\/cropped-LogoTarget-carico.gif","article:published_time":"2026-04-14T08:48:53+00:00","article:modified_time":"2026-04-17T08:14:20+00:00","article:publisher":"https:\/\/www.facebook.com\/TargetInformaticaTerni","twitter:card":"summary_large_image","twitter:title":"Shop &amp; Service","twitter:image":"https:\/\/www.tisoft.it\/wp-content\/uploads\/2024\/04\/cropped-LogoTarget-carico.gif"},"aioseo_meta_data":{"post_id":"1165","title":"#post_title","description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"WebPage","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"seo_analyzer_scan_date":"2026-04-17 10:27:07","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":"0","open_ai":null,"ai":{"faqs":[],"keyPoints":[],"titles":[],"descriptions":[],"socialPosts":{"email":[],"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-04-14 08:42:06","updated":"2026-04-17 10:27:17"},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t<a href=\"https:\/\/www.tisoft.it\/en\" title=\"Home\">Home<\/a>\n<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\tShop &amp; Service\n<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.tisoft.it\/en"},{"label":"Shop &#038; Service","link":"https:\/\/www.tisoft.it\/en\/shop-service\/"}],"_links":{"self":[{"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/pages\/1165","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/comments?post=1165"}],"version-history":[{"count":4,"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/pages\/1165\/revisions"}],"predecessor-version":[{"id":1202,"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/pages\/1165\/revisions\/1202"}],"wp:attachment":[{"href":"https:\/\/www.tisoft.it\/en\/wp-json\/wp\/v2\/media?parent=1165"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}