/* StoreVoiceAI Onboarding wizard.
   Pure vanilla JS — renders into #svai-main and drives the setup screens. */
(function (window, document) {
    'use strict';

    var BOOT = window.storevoiceaiOnboarding || null;
    if (!BOOT) {
        return;
    }

    // ─── Tiny utility helpers ──────────────────────────────────────────────
    var $main = null;
    var $stepper = null;

    // Where to land after the training intro finishes. Resume on the exact
    // saved step (1–4) so the stepper and rendered page stay in sync after
    // a reload. The training loader itself always plays first — see boot().
    // wp_localize_script hands startStep over as a string; coerce to a number
    // or render()'s strict === checks fail and fall through to the last screen.
    var startStep  = parseInt(BOOT.startStep, 10);
    var resumeStep = (startStep >= 1 && startStep <= 4) ? startStep : 1;

    // Track in-flight setTimeouts started by renderTraining so render() can
    // cancel them and never let a stale "advance from training" call fire
    // after the user has already navigated.
    var trainingTimers = [];

    // Cleanup hook for the active screen (e.g. the "Try a call" step needs to
    // hang up any live call + stop its timer before we swap views). render()
    // runs this before clearing $main.
    var activeTeardown = null;

    function t(key, fallback) {
        return BOOT.i18n && BOOT.i18n[key] ? BOOT.i18n[key] : fallback;
    }

    // Days the schedule UI iterates over. Order matters — this is the visual
    // order of the rows. Declared before `state` because normalizeWorkingHours
    // (called from the state initializer) reads it.
    var WEEK_DAYS = [
        { key: 'mon', label: t('onboarding.day.mon', 'Mon') },
        { key: 'tue', label: t('onboarding.day.tue', 'Tue') },
        { key: 'wed', label: t('onboarding.day.wed', 'Wed') },
        { key: 'thu', label: t('onboarding.day.thu', 'Thu') },
        { key: 'fri', label: t('onboarding.day.fri', 'Fri') },
        { key: 'sat', label: t('onboarding.day.sat', 'Sat') },
        { key: 'sun', label: t('onboarding.day.sun', 'Sun') }
    ];

    function defaultWorkingHours() {
        var out = {};
        for (var i = 0; i < WEEK_DAYS.length; i++) {
            var k = WEEK_DAYS[i].key;
            out[k] = {
                off:   (k === 'sat' || k === 'sun'),
                start: '09:00',
                end:   '17:00'
            };
        }
        return out;
    }

    function normalizeWorkingHours(raw) {
        var base = defaultWorkingHours();
        if (!raw || typeof raw !== 'object') {
            return base;
        }
        for (var i = 0; i < WEEK_DAYS.length; i++) {
            var k = WEEK_DAYS[i].key;
            var v = raw[k];
            if (v && typeof v === 'object') {
                base[k] = {
                    off:   !!v.off,
                    start: typeof v.start === 'string' ? v.start : base[k].start,
                    end:   typeof v.end   === 'string' ? v.end   : base[k].end
                };
            }
        }
        return base;
    }

    var state = {
        step:        0,                     // always begin on the training intro
        agentName:   BOOT.agent.name || t('onboarding.default_agent_name', 'Grace'),
        greeting:    BOOT.agent.greeting || '',
        greetingPreset: 0,
        // Transfer config (step 2). The toggle defaults ON unless a prior save
        // explicitly chose "always". A stored email (legacy value) is ignored
        // here so it never prefills the phone field.
        businessHoursOnly: BOOT.escalation.mode !== 'always',
        escValue:    (BOOT.escalation.value && BOOT.escalation.value.indexOf('@') === -1) ? BOOT.escalation.value : '',
        workingHours: normalizeWorkingHours(BOOT.workingHours),
        // Policies step (between Customize and Activate).
        shippingPolicy:     (BOOT.policies && BOOT.policies.shipping) || '',
        returnsPolicy:      (BOOT.policies && BOOT.policies.returns) || '',
        ownerEmail:         (BOOT.policies && BOOT.policies.ownerEmail) || '',
        shippingAutofilled: !!(BOOT.policies && BOOT.policies.shippingAutofilled),
        webTested:   false
    };

    function el(tag, attrs, children) {
        var node = document.createElement(tag);
        if (attrs) {
            for (var k in attrs) {
                if (!Object.prototype.hasOwnProperty.call(attrs, k)) continue;
                var v = attrs[k];
                if (k === 'class')         node.className = v;
                else if (k === 'html')     node.innerHTML = v;
                else if (k === 'text')     node.textContent = v;
                else if (k === 'onclick')  node.addEventListener('click', v);
                else if (k === 'oninput')  node.addEventListener('input', v);
                else if (k === 'onchange') node.addEventListener('change', v);
                else if (k === 'dataset')  { for (var d in v) node.dataset[d] = v[d]; }
                else                       node.setAttribute(k, v);
            }
        }
        if (children) {
            for (var i = 0; i < children.length; i++) {
                var c = children[i];
                if (c == null) continue;
                node.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
            }
        }
        return node;
    }

    function svg(path, opts) {
        opts = opts || {};
        var ns = 'http://www.w3.org/2000/svg';
        var s = document.createElementNS(ns, 'svg');
        s.setAttribute('viewBox', opts.vb || '0 0 24 24');
        s.setAttribute('fill', opts.fill || 'none');
        s.setAttribute('stroke', opts.stroke || 'currentColor');
        s.setAttribute('stroke-width', opts.sw || '2');
        s.setAttribute('stroke-linecap', 'round');
        s.setAttribute('stroke-linejoin', 'round');
        if (opts.w) s.setAttribute('width', opts.w);
        if (opts.h) s.setAttribute('height', opts.h);
        s.innerHTML = path;
        return s;
    }

    var ICONS = {
        check:     '<polyline points="20 6 9 17 4 12"/>',
        arrowR:    '<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>',
        arrowL:    '<line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/>',
        play:      '<polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/>',
        pause:     '<rect x="6" y="4" width="4" height="16" fill="currentColor" stroke="none"/><rect x="14" y="4" width="4" height="16" fill="currentColor" stroke="none"/>',
        mic:       '<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M5 10a7 7 0 0 0 14 0"/><line x1="12" y1="19" x2="12" y2="22"/>',
        phone:     '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.69 12 19.79 19.79 0 0 1 1.61 3.4 2 2 0 0 1 3.6 1.22h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L7.91 8.85a16 16 0 0 0 6.29 6.29l.95-.95a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/>',
        globe:     '<circle cx="12" cy="12" r="9"/><line x1="3" y1="12" x2="21" y2="12"/><path d="M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/>',
        clock:     '<circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 16 14"/>',
        package:   '<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/>',
        truck:     '<rect x="1" y="3" width="15" height="13" rx="1"/><polygon points="16 8 20 8 23 11 23 16 16 16 16 8"/><circle cx="5.5" cy="18.5" r="2.5"/><circle cx="18.5" cy="18.5" r="2.5"/>',
        rotate:    '<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>',
        file:      '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="13" y2="17"/>',
        sparkle:   '<path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/>',
        info:      '<circle cx="12" cy="12" r="9"/><line x1="12" y1="8" x2="12" y2="13"/><circle cx="12" cy="16" r="0.6" fill="currentColor" stroke="none"/>',
        chart:     '<line x1="6" y1="20" x2="6" y2="14"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="18" y1="20" x2="18" y2="10"/>',
        sliders:   '<line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/>'
    };

    function icon(name, opts) {
        return svg(ICONS[name] || '', opts || {});
    }

    function clear(node) { while (node.firstChild) node.removeChild(node.firstChild); }

    // Swap a button into / out of a loading state with an inline spinner.
    // Stashes the original markup so it can be restored on error.
    function setBtnLoading(btn, loading, label) {
        if (!btn) return;
        if (loading) {
            if (btn._origHTML === undefined) btn._origHTML = btn.innerHTML;
            btn.disabled = true;
            btn.classList.add('svai-btn--loading');
            clear(btn);
            btn.appendChild(el('span', { class: 'svai-btn__spinner', 'aria-hidden': 'true' }));
            btn.appendChild(document.createTextNode(label || t('onboarding.saving', 'Saving…')));
        } else {
            btn.disabled = false;
            btn.classList.remove('svai-btn--loading');
            if (btn._origHTML !== undefined) {
                btn.innerHTML = btn._origHTML;
                delete btn._origHTML;
            }
        }
    }

    function clamp(n, lo, hi) { return Math.max(lo, Math.min(hi, n)); }

    function fmt(str) {
        var args = Array.prototype.slice.call(arguments, 1);
        var i = 0;
        return String(str).replace(/%[sd]/g, function () { return args[i++]; });
    }

    // ─── AJAX ──────────────────────────────────────────────────────────────
    function post(action, data) {
        var body = new URLSearchParams();
        body.append('action', action);
        body.append('nonce', BOOT.nonce);
        if (data) {
            for (var k in data) {
                if (Object.prototype.hasOwnProperty.call(data, k) && data[k] !== undefined) {
                    body.append(k, data[k]);
                }
            }
        }
        return fetch(BOOT.ajaxUrl, {
            method: 'POST',
            credentials: 'same-origin',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
            body: body.toString()
        }).then(function (r) { return r.json(); });
    }

    function toast(msg, isError) {
        var t = document.createElement('div');
        t.className = 'svai-toast' + (isError ? ' svai-toast--error' : '');
        t.textContent = msg;
        document.body.appendChild(t);
        requestAnimationFrame(function () { t.classList.add('svai-toast--visible'); });
        setTimeout(function () {
            t.classList.remove('svai-toast--visible');
            setTimeout(function () { t.remove(); }, 350);
        }, 2200);
    }

    // ─── Stepper ───────────────────────────────────────────────────────────
    function renderStepper(currentStep) {
        var labels = BOOT.i18n.stepLabels;
        var wrap = el('div', { class: 'svai-stepper' });
        for (var i = 0; i < labels.length; i++) {
            var n = i + 1;
            var cls = 'svai-step svai-step--clickable ' + (n < currentStep ? 'svai-step--done' : n === currentStep ? 'svai-step--active' : '');
            var node = el('div', {
                class: cls,
                role: 'button',
                tabindex: '0',
                'aria-label': labels[i],
                onclick: (function (step) { return function () { goTo(step); }; })(n)
            }, [
                el('div', { class: 'svai-step__bubble' }, [
                    n < currentStep ? icon('check', { w: 14, h: 14 }) : document.createTextNode(String(n))
                ]),
                el('span', { class: 'svai-step__label', text: labels[i] })
            ]);
            node.addEventListener('keydown', (function (step) {
                return function (e) {
                    if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); goTo(step); }
                };
            })(n));
            wrap.appendChild(node);
            if (i < labels.length - 1) {
                wrap.appendChild(el('div', {
                    class: 'svai-step__connector' + (n < currentStep ? ' svai-step__connector--done' : '')
                }));
            }
        }
        return wrap;
    }

    // ─── Screen: training intro ───────────────────────────────────────────
    function renderTraining() {
        var card = el('div', { class: 'svai-card svai-card--centered' });
        var rows = [
            { icon: 'package', text: fmt(BOOT.i18n.readingProducts, BOOT.store.productCount || 0) },
            { icon: 'truck',   text: BOOT.i18n.readingPolicies },
            { icon: 'file',    text: BOOT.i18n.readingPages },
            { icon: 'sparkle', text: fmt(BOOT.i18n.buildingPersona, BOOT.store.name) }
        ];

        var listEl = el('div', { class: 'svai-training__list' });
        var rowEls = [];
        rows.forEach(function (r, i) {
            var indicator = el('span', { class: 'svai-training__indicator' });
            var row = el('div', { class: 'svai-training__row' + (i === 0 ? ' svai-training__row--visible' : '') }, [
                indicator,
                el('span', { text: r.text })
            ]);
            row._indicator = indicator;
            rowEls.push(row);
            listEl.appendChild(row);
        });

        var progressFill = el('div', { class: 'svai-progress__fill' });
        var progress = el('div', { class: 'svai-progress' }, [progressFill]);

        var content = el('div', { class: 'svai-training' }, [
            el('div', { class: 'svai-training__orb' }, [icon('mic', { w: 32, h: 32 })]),
            el('div', { class: 'svai-training__heading' }, [
                el('h1', { text: fmt(BOOT.i18n.training, state.agentName) }),
                el('p',  { text: BOOT.i18n.trainingSub })
            ]),
            listEl,
            progress
        ]);
        card.appendChild(content);

        // Drive the animation. Each setTimeout id is tracked so render() can
        // cancel them on navigation.
        var delays = [900, 1300, 1100, 1500];
        var acc = 0;
        delays.forEach(function (d, i) {
            acc += d;
            trainingTimers.push(setTimeout(function () {
                for (var j = 0; j <= i; j++) {
                    var row = rowEls[j];
                    var ind = row._indicator;
                    if (j < i) {
                        row.className = 'svai-training__row svai-training__row--visible svai-training__row--done';
                        clear(ind);
                        ind.appendChild(icon('check', { w: 12, h: 12, stroke: '#fff' }));
                    } else {
                        row.className = 'svai-training__row svai-training__row--visible svai-training__row--active';
                    }
                }
                if (i + 1 < rowEls.length) {
                    rowEls[i + 1].classList.add('svai-training__row--visible');
                }
                progressFill.style.width = ((i + 1) / rows.length * 100) + '%';
            }, acc));
        });
        // Final completion → advance.
        trainingTimers.push(setTimeout(function () {
            var last = rowEls[rowEls.length - 1];
            last.className = 'svai-training__row svai-training__row--visible svai-training__row--done';
            clear(last._indicator);
            last._indicator.appendChild(icon('check', { w: 12, h: 12, stroke: '#fff' }));
            progressFill.style.width = '100%';
            trainingTimers.push(setTimeout(function () { goTo(resumeStep); }, 900));
        }, acc + 1200));

        return [card];
    }

    function featureRow(iconName, text) {
        return el('div', { class: 'svai-feature-list__item' }, [
            el('span', { class: 'svai-feature-list__icon' }, [icon('check', { w: 12, h: 12, stroke: '#fff' })]),
            el('span', { text: text })
        ]);
    }

    function makePlayer(caption, duration, isPrimary) {
        var bars = [];
        var waveEl = el('div', { class: 'svai-wave' });
        var heights = [8,16,24,12,28,18,10,26,20,14,30,9,22,16,12,24,18,28,14,20,10,25,15,22,8,20,27,12,18,25,10,16,22,14,20,27,12,18,10,25,15,12,20];
        heights.forEach(function (h) {
            var bar = el('div', { class: 'svai-wave__bar' });
            bar._h = h;
            waveEl.appendChild(bar);
            bars.push(bar);
        });
        var fillEl = el('div', { class: 'svai-track__fill' });
        var trackEl = el('div', { class: 'svai-track' }, [fillEl]);
        var playBtn = el('button', { class: 'svai-play-btn', 'aria-label': t('onboarding.play_demo', 'Play demo') }, [icon('play')]);

        var playing = false;
        var progress = 0;
        var timer = null;

        playBtn.addEventListener('click', function () {
            if (!playing) {
                playing = true;
                progress = 0;
                clear(playBtn); playBtn.appendChild(icon('pause'));
                timer = setInterval(function () {
                    progress = Math.min(100, progress + 1.4);
                    fillEl.style.width = progress + '%';
                    bars.forEach(function (b, i) {
                        var reached = (progress / 100) > (i / bars.length);
                        b.style.height = (reached ? Math.max(4, b._h) : 4) + 'px';
                        b.style.background = reached ? 'var(--svai-amber-500)' : 'var(--svai-border)';
                    });
                    if (progress >= 100) {
                        clearInterval(timer);
                        playing = false;
                        clear(playBtn); playBtn.appendChild(icon('play'));
                        setTimeout(function () {
                            bars.forEach(function (b) { b.style.height = '4px'; b.style.background = 'var(--svai-border)'; });
                            fillEl.style.width = '0%';
                        }, 600);
                    }
                }, 140);
            } else {
                playing = false;
                clearInterval(timer);
                clear(playBtn); playBtn.appendChild(icon('play'));
            }
        });

        return el('div', { class: 'svai-player' }, [
            el('p', { class: 'svai-player__caption', text: caption }),
            waveEl,
            el('div', { class: 'svai-player__controls' }, [
                playBtn,
                trackEl,
                el('span', { class: 'svai-player__time', text: duration })
            ])
        ]);
    }

    // ─── Screen 1: Customize ──────────────────────────────────────────────
    function renderStep2() {
        var presetTexts = function (name) {
            return [
                fmt(t('onboarding.greeting.preset_1', 'Hi, this is %s with %s. How can I help you today?'), name, BOOT.store.name),
                fmt(t('onboarding.greeting.preset_2', 'Hi, this is %s with %s on a recorded line. How can I help you today?'), name, BOOT.store.name),
                fmt(t('onboarding.greeting.preset_3', 'Hi, this is %s, your AI support rep at %s. By continuing, you consent to recording. How can I help?'), name, BOOT.store.name)
            ];
        };

        // Detect which preset matches the current greeting; default to 0.
        var matches = presetTexts(state.agentName);
        var matchIdx = matches.indexOf(state.greeting);
        if (matchIdx === -1 && state.greeting && state.greeting !== matches[0]) {
            state.greetingPreset = 3;
        } else if (matchIdx !== -1) {
            state.greetingPreset = matchIdx;
        }

        var avatarEl  = el('div', { class: 'svai-preview__avatar', text: state.agentName.charAt(0).toUpperCase() });
        var nameEl    = el('strong', { text: state.agentName });
        var bubbleEl  = el('div', { class: 'svai-preview__bubble' });
        var preview = el('div', { class: 'svai-preview' }, [
            el('p', { class: 'svai-preview__caption', text: t('onboarding.preview', 'Preview') }),
            el('div', { class: 'svai-preview__person' }, [
                avatarEl,
                el('div', {}, [nameEl, el('small', { text: BOOT.store.name })])
            ]),
            bubbleEl
        ]);

        var aside = el('aside', { class: 'svai-card__aside' }, [
            el('span', { class: 'svai-card__eyebrow', text: fmt(t('onboarding.step_count', 'Step %d of 4'), 1) }),
            el('div', {}, [
                el('h2', { class: 'svai-card__title', text: t('onboarding.title', 'Make Grace your own') }),
                el('p',  { class: 'svai-card__subtitle', text: t('onboarding.subtitle', 'Rename her and pick a greeting. You can change both anytime.') })
            ]),
            el('div', { class: 'svai-feature-list' }, [
                featureRow('package', fmt(t('onboarding.knows_products', 'Knows all %d products'), BOOT.store.productCount || 0)),
                featureRow('truck',   t('onboarding.handles_shipping', 'Handles shipping & order status')),
                featureRow('rotate',  t('onboarding.explains_returns', 'Explains your returns policy'))
            ]),
            preview
        ]);

        // Form
        var nameInput = el('input', {
            class: 'svai-input',
            type: 'text',
            value: state.agentName,
            placeholder: t('onboarding.default_agent_name', 'Grace'),
            maxlength: 30
        });
        var greetingLabel = el('label', { class: 'svai-field__label', text: fmt(t('onboarding.intro_label', 'How should %s introduce herself?'), state.agentName) });

        var radioGroup = el('div', { class: 'svai-radio-group' });
        var customTextarea = el('textarea', {
            class: 'svai-textarea',
            placeholder: t('onboarding.custom_greeting_placeholder', 'Type your custom greeting here…'),
            style: 'display:none'
        });
        customTextarea.value = state.greetingPreset === 3 ? state.greeting : '';

        function updatePreview() {
            var defaultName = t('onboarding.default_agent_name', 'Grace');
            var n = (nameInput.value || defaultName).trim();
            state.agentName = n || defaultName;
            avatarEl.textContent = state.agentName.charAt(0).toUpperCase();
            nameEl.textContent = state.agentName;
            greetingLabel.textContent = fmt(t('onboarding.intro_label', 'How should %s introduce herself?'), state.agentName);
            var texts = presetTexts(state.agentName);
            // Re-render radio labels
            var inputs = radioGroup.querySelectorAll('.svai-radio');
            inputs.forEach(function (radio, idx) {
                var span = radio.querySelector('span');
                if (idx < 3 && span) span.textContent = texts[idx];
            });
            var greeting = state.greetingPreset === 3
                ? (customTextarea.value || texts[0])
                : texts[state.greetingPreset];
            state.greeting = greeting;
            bubbleEl.textContent = '"' + greeting + '"';
        }
        nameInput.addEventListener('input', updatePreview);
        customTextarea.addEventListener('input', updatePreview);

        var presets = presetTexts(state.agentName);
        var labels = [presets[0], presets[1], presets[2], t('onboarding.custom_intro', 'Custom introduction')];
        labels.forEach(function (label, idx) {
            var rb = el('input', { type: 'radio', name: 'svai-greet' });
            if (idx === state.greetingPreset) rb.checked = true;
            var radio = el('label', {
                class: 'svai-radio' + (idx === state.greetingPreset ? ' svai-radio--selected' : '')
            }, [rb, el('span', { text: label })]);
            radio.addEventListener('click', function (e) {
                if (e.target !== rb) rb.checked = true;
                state.greetingPreset = idx;
                radioGroup.querySelectorAll('.svai-radio').forEach(function (r, i) {
                    r.classList.toggle('svai-radio--selected', i === idx);
                });
                customTextarea.style.display = idx === 3 ? 'block' : 'none';
                updatePreview();
            });
            radioGroup.appendChild(radio);
        });

        var body = el('div', { class: 'svai-card__body' }, [
            el('div', { class: 'svai-field' }, [
                el('label', { class: 'svai-field__label', text: t('onboarding.agent_name', 'Agent name') }),
                nameInput
            ]),
            el('div', { class: 'svai-field' }, [
                greetingLabel,
                radioGroup,
                customTextarea
            ]),
            el('div', { class: 'svai-actions' }, [
                el('button', {
                    class: 'svai-btn svai-btn--primary',
                    // Persist the agent details (name + greeting) before
                    // advancing. Policies live on a separate endpoint and are
                    // saved on the next step.
                    onclick: function (e) {
                        var btn = e.currentTarget;
                        setBtnLoading(btn, true, t('onboarding.saving', 'Saving…'));
                        post('storevoiceai_onboarding_save_agent', {
                            agent_name: state.agentName,
                            greeting:   state.greeting
                        }).then(function (r) {
                            if (r && r.success) { goTo(2); }
                            else { toast(BOOT.i18n.errorSaving, true); setBtnLoading(btn, false); }
                        }).catch(function () { toast(BOOT.i18n.errorSaving, true); setBtnLoading(btn, false); });
                    }
                }, [document.createTextNode(t('onboarding.looks_good', 'Looks good')), icon('arrowR')])
            ])
        ]);

        // Initial paint
        setTimeout(updatePreview, 0);

        return [el('section', { class: 'svai-card' }, [aside, body])];
    }

    // ─── Screen 2: Policies ───────────────────────────────────────────────
    // Shipping + returns policy the agent reads from on every call. Shipping is
    // seeded from the store's WooCommerce shipping zones (flagged "Auto-filled")
    // and both are editable. Persisted via ajax_save_agent → update_site.
    function renderPolicies() {
        var name = state.agentName || t('onboarding.default_agent_name', 'Grace');

        var aside = el('aside', { class: 'svai-card__aside' }, [
            el('span', { class: 'svai-card__eyebrow', text: fmt(t('onboarding.step_count', 'Step %d of 4'), 2) }),
            el('div', {}, [
                el('h2', { class: 'svai-card__title', text: fmt(t('onboarding.policies.title', 'Teach %s your policies'), name) }),
                el('p',  { class: 'svai-card__subtitle', text: fmt(t('onboarding.policies.subtitle', 'These answer the questions customers ask most. %s reads from them on every call.'), name) })
            ]),
            el('div', { class: 'svai-feature-list' }, [
                featureRow('truck',  t('onboarding.policies.feature_shipping', 'Answers “where do you ship?”')),
                featureRow('rotate', t('onboarding.policies.feature_returns', 'Handles refund & return questions')),
                featureRow('sliders', t('onboarding.policies.feature_edit', 'Edit anytime from Settings'))
            ]),
            el('p', { class: 'svai-aside-note', text: t('onboarding.settings_note', 'You can change this anytime from Settings.') })
        ]);

        var shippingTextarea = el('textarea', {
            class: 'svai-textarea',
            rows: 3,
            maxlength: 2000,
            placeholder: t('onboarding.policies.shipping_placeholder', 'Describe where you ship and any delivery details…')
        });
        shippingTextarea.value = state.shippingPolicy;
        shippingTextarea.addEventListener('input', function () {
            state.shippingPolicy = shippingTextarea.value;
            // Once the merchant edits, it's their copy — drop the badge.
            state.shippingAutofilled = false;
            if (badge) badge.style.display = 'none';
        });

        var badge = state.shippingAutofilled
            ? el('span', { class: 'svai-field__badge' }, [icon('check', { w: 11, h: 11 }), document.createTextNode(t('onboarding.policies.autofilled', 'Auto-filled'))])
            : null;

        var shippingLabelRow = el('div', { class: 'svai-field__labelrow' }, [
            el('label', { class: 'svai-field__label', text: t('onboarding.policies.shipping_label', 'Shipping policy') }),
            badge
        ]);

        var returnsTextarea = el('textarea', {
            class: 'svai-textarea',
            rows: 3,
            maxlength: 2000,
            placeholder: t('onboarding.policies.returns_placeholder', 'Describe how returns and refunds work…')
        });
        returnsTextarea.value = state.returnsPolicy;
        returnsTextarea.addEventListener('input', function () {
            state.returnsPolicy = returnsTextarea.value;
        });

        var ownerInput = el('input', {
            class: 'svai-input',
            type: 'email',
            placeholder: t('onboarding.policies.owner_placeholder', 'you@store.com')
        });
        ownerInput.value = state.ownerEmail;
        ownerInput.addEventListener('input', function () {
            state.ownerEmail = ownerInput.value;
        });

        var body = el('div', { class: 'svai-card__body' }, [
            el('div', { class: 'svai-field' }, [
                shippingLabelRow,
                shippingTextarea,
                el('p', { class: 'svai-field__hint', text: t('onboarding.policies.shipping_hint', 'Auto-filled from your WooCommerce shipping zones. Add carrier names or delivery times if needed.') })
            ]),
            el('div', { class: 'svai-field' }, [
                el('label', { class: 'svai-field__label', text: t('onboarding.policies.returns_label', 'Returns policy') }),
                returnsTextarea,
                el('p', { class: 'svai-field__hint', text: fmt(t('onboarding.policies.returns_hint', '%s will use this to answer customer questions. Edit to match your actual policy.'), name) })
            ]),
            el('div', { class: 'svai-field' }, [
                el('label', { class: 'svai-field__label', text: t('onboarding.policies.owner_label', 'Contact email for unanswered questions') }),
                ownerInput,
                el('p', { class: 'svai-field__hint', text: fmt(t('onboarding.policies.owner_hint', 'If %s can’t answer a question, they’ll direct the customer here.'), name) })
            ]),
            el('div', { class: 'svai-actions' }, [
                el('button', {
                    class: 'svai-btn svai-btn--ghost',
                    onclick: function () { goTo(1); }
                }, [icon('arrowL'), document.createTextNode(t('onboarding.back', 'Back'))]),
                el('button', {
                    class: 'svai-btn svai-btn--primary',
                    onclick: function (e) {
                        var btn = e.currentTarget;
                        setBtnLoading(btn, true, t('onboarding.saving', 'Saving…'));
                        // Persist the site-level policies + contact email. Agent
                        // details (name + greeting) were already saved when
                        // leaving the Customize step.
                        post('storevoiceai_onboarding_save_agent', {
                            shipping_policy: state.shippingPolicy,
                            returns_policy:  state.returnsPolicy,
                            owner_email:     state.ownerEmail
                        }).then(function (r) {
                            if (r && r.success) { goTo(3); }
                            else { toast(BOOT.i18n.errorSaving, true); setBtnLoading(btn, false); }
                        }).catch(function () { toast(BOOT.i18n.errorSaving, true); setBtnLoading(btn, false); });
                    }
                }, [document.createTextNode(t('onboarding.save_continue', 'Save and continue')), icon('arrowR')])
            ])
        ]);

        return [el('section', { class: 'svai-card' }, [aside, body])];
    }

    // ─── Screen 3: Try a call ─────────────────────────────────────────────
    // Replaces the old transfer-number step. Lets the merchant place a real
    // web call to their own agent, then retrieve the recording + transcript so
    // they see exactly what a customer experiences. Internal states cycle:
    //   pre → connecting → in-call → ended → reveal
    function renderTryCall() {
        var name = state.agentName || t('onboarding.default_agent_name', 'Grace');
        var initial = name.charAt(0).toUpperCase();

        var aside = el('aside', { class: 'svai-card__aside' }, [
            el('span', { class: 'svai-card__eyebrow', text: fmt(t('onboarding.step_count', 'Step %d of 4'), 3) }),
            el('div', {}, [
                el('h2', { class: 'svai-card__title', text: fmt(t('onboarding.try.title', 'Talk to %s yourself'), name) }),
                el('p',  { class: 'svai-card__subtitle', text: t('onboarding.try.subtitle', 'Your customers will experience this. You should too.') })
            ]),
            el('div', { class: 'svai-feature-list' }, [
                featureRow('package', fmt(t('onboarding.knows_products', 'Knows all %d products'), BOOT.store.productCount || 0)),
                featureRow('truck',   t('onboarding.handles_shipping', 'Handles shipping & order status')),
                featureRow('rotate',  t('onboarding.explains_returns', 'Explains your returns policy'))
            ]),
            el('p', { class: 'svai-aside-note', text: t('onboarding.settings_note', 'You can change this anytime from Settings.') })
        ]);

        // The stage swaps between the internal call states; the footer (Back +
        // Go live) and skip link persist underneath it.
        var stage = el('div', { class: 'svai-trycall' });

        // Per-render call state.
        var client = null;          // RetellWebClient instance for the live call
        var callId = null;          // captured from create_call, used to fetch notes
        var timerHandle = null;     // live-call duration ticker
        var pendingDelay = null;    // setTimeout id for the post-call processing pauses
        var ended = false;          // guards against End-button + call_ended firing twice

        function stopTimer() {
            if (timerHandle) { clearInterval(timerHandle); timerHandle = null; }
        }
        function startTimer(displayEl) {
            stopTimer();
            var secs = 0;
            displayEl.textContent = '0:00';
            timerHandle = setInterval(function () {
                secs++;
                var m = Math.floor(secs / 60), s = secs % 60;
                displayEl.textContent = m + ':' + (s < 10 ? '0' : '') + s;
            }, 1000);
        }
        function clearPendingDelay() {
            if (pendingDelay) { clearTimeout(pendingDelay); pendingDelay = null; }
        }
        function endClient() {
            if (client) {
                try { client.stopCall(); } catch (e) {}
                client = null;
            }
            stopTimer();
        }

        // Run on navigation away so a live call (and any pending timers) never
        // outlive the screen.
        activeTeardown = function () { endClient(); clearPendingDelay(); };

        // ── State painters ────────────────────────────────────────────────
        function paintPre() {
            endClient();
            clear(stage);
            stage.appendChild(el('div', { class: 'svai-trycall__state svai-trycall__center' }, [
                el('div', { class: 'svai-trycall__avatar', text: initial }),
                el('h3', { class: 'svai-trycall__name', text: fmt(t('onboarding.try.ready_title', '%s is ready'), name) }),
                el('p', { class: 'svai-trycall__sub', text: t('onboarding.try.ready_sub', 'Try asking about a product, your return policy, or shipping times.') }),
                el('div', { class: 'svai-trycall__prompt' }, [
                    el('strong', { text: t('onboarding.try.suggested', 'Suggested questions') }),
                    el('span', { text: t('onboarding.try.suggested_list', '“What’s your return policy?”   ·   “Do you ship internationally?”   ·   “Tell me about a product”') })
                ]),
                el('button', {
                    class: 'svai-btn svai-btn--primary svai-trycall__call-btn',
                    onclick: function () { beginCall(); }
                }, [icon('phone'), document.createTextNode(fmt(t('onboarding.try.start', 'Start a call with %s'), name))])
            ]));
            setGoLive(false);
        }

        function paintConnecting() {
            clear(stage);
            stage.appendChild(el('div', { class: 'svai-trycall__state svai-trycall__center' }, [
                el('div', { class: 'svai-trycall__pulse svai-trycall__pulse--live' }, [
                    el('div', { class: 'svai-trycall__avatar svai-trycall__avatar--ring', text: initial })
                ]),
                el('p', { class: 'svai-trycall__sub', text: t('onboarding.try.connecting', 'Connecting…') })
            ]));
            setGoLive(false);
        }

        // Generic spinner screen used to bridge the moments where the backend is
        // still catching up (call wrap-up, log sync + processing).
        function paintProcessing(message) {
            clear(stage);
            stage.appendChild(el('div', { class: 'svai-trycall__state svai-trycall__center' }, [
                el('div', { class: 'svai-trycall__spinner', 'aria-hidden': 'true' }),
                el('p', { class: 'svai-trycall__sub', text: message || t('onboarding.try.wrapping', 'Wrapping up your call…') })
            ]));
            setGoLive(false);
        }

        function paintInCall() {
            stopTimer();
            clear(stage);
            var timerEl = el('div', { class: 'svai-trycall__timer', text: '0:00' });
            stage.appendChild(el('div', { class: 'svai-trycall__state svai-trycall__center' }, [
                el('div', { class: 'svai-trycall__live' }, [
                    el('span', { class: 'svai-trycall__live-dot' }),
                    document.createTextNode(t('onboarding.try.live', 'Live call'))
                ]),
                el('div', { class: 'svai-trycall__pulse svai-trycall__pulse--live' }, [
                    el('div', { class: 'svai-trycall__avatar svai-trycall__avatar--ring', text: initial })
                ]),
                timerEl,
                el('p', { class: 'svai-trycall__sub', text: fmt(t('onboarding.try.in_progress_sub', 'When you’re done, hang up — we’ll show you what %s captured.'), name) }),
                el('button', {
                    class: 'svai-btn svai-btn--ghost svai-trycall__end-btn',
                    onclick: function () { handleCallEnded(); }
                }, [icon('phone'), document.createTextNode(t('onboarding.try.end', 'End call'))])
            ]));
            startTimer(timerEl);
            setGoLive(false);
        }

        // Reachable from both the End button and the SDK's call_ended event.
        // Tears the call down, then holds a short "wrapping up" pause so the
        // backend has a head start on processing the recording/transcript
        // before the merchant can ask for the notes.
        function handleCallEnded() {
            if (ended) return;
            ended = true;
            endClient();
            clearPendingDelay();
            paintProcessing(t('onboarding.try.wrapping', 'Wrapping up your call…'));
            pendingDelay = setTimeout(function () { paintEnded(); }, 4000);
        }

        function paintEnded() {
            endClient();
            clear(stage);
            stage.appendChild(el('div', { class: 'svai-trycall__state svai-trycall__center' }, [
                el('div', { class: 'svai-trycall__gift' }, [icon('sparkle', { w: 28, h: 28 })]),
                el('h3', { class: 'svai-trycall__name', text: fmt(t('onboarding.try.ended_title', '%s took notes.'), name) }),
                el('p', { class: 'svai-trycall__sub', text: t('onboarding.try.ended_sub', 'See the summary, transcript, and recording from your call.') }),
                el('button', {
                    class: 'svai-btn svai-btn--primary',
                    onclick: function () { retrieveNotes(); }
                }, [icon('file'), document.createTextNode(fmt(t('onboarding.try.retrieve', 'Retrieve %s’s notes'), name)), icon('arrowR')])
            ]));
            setGoLive(false);
        }

        function paintReveal(details) {
            clear(stage);
            var summary      = (details.summary || '').trim();
            var recordingUrl = details.recordingUrl || '';
            var transcript   = (details.transcript || '').trim();

            // Nothing ready yet — the provider can still be processing the call.
            if (!summary && !recordingUrl && !transcript) {
                stage.appendChild(el('div', { class: 'svai-trycall__state svai-trycall__center' }, [
                    el('div', { class: 'svai-trycall__gift' }, [icon('clock', { w: 26, h: 26 })]),
                    el('h3', { class: 'svai-trycall__name', text: t('onboarding.try.processing_title', 'Notes are still being prepared') }),
                    el('p', { class: 'svai-trycall__sub', text: t('onboarding.try.processing_sub', 'Recording and transcript can take a few moments after the call ends. Refresh to check again.') }),
                    el('button', {
                        class: 'svai-btn svai-btn--ghost',
                        onclick: function () { retrieveNotes(); }
                    }, [icon('rotate'), document.createTextNode(t('onboarding.try.refresh', 'Refresh'))])
                ]));
                setGoLive(true);
                return;
            }

            var reveal = el('div', { class: 'svai-trycall__reveal' });

            reveal.appendChild(el('div', { class: 'svai-trycall__card' }, [
                el('span', { class: 'svai-trycall__card-label', text: fmt(t('onboarding.try.summary_label', 'What %s understood'), name) }),
                el('p', { class: 'svai-trycall__summary', text: summary || t('onboarding.try.no_summary', 'No summary was captured for this call.') })
            ]));

            if (recordingUrl) {
                var audio = el('audio', { class: 'svai-trycall__audio', controls: 'controls', preload: 'metadata' });
                var src = document.createElement('source');
                src.src = recordingUrl;
                audio.appendChild(src);
                reveal.appendChild(el('div', { class: 'svai-trycall__card' }, [
                    el('span', { class: 'svai-trycall__card-label', text: t('onboarding.try.recording_label', 'Recording') }),
                    audio
                ]));
            }

            if (transcript) {
                var lines = el('div', { class: 'svai-trycall__transcript' });
                renderTranscriptLines(transcript).forEach(function (n) { lines.appendChild(n); });
                lines.style.display = 'none';
                var chev = icon('arrowR', { w: 14, h: 14 });
                var toggle = el('button', { class: 'svai-trycall__transcript-toggle', type: 'button' }, [
                    el('span', { text: t('onboarding.try.transcript_label', 'Full transcript') }),
                    chev
                ]);
                toggle.addEventListener('click', function () {
                    var open = lines.style.display !== 'none';
                    lines.style.display = open ? 'none' : 'block';
                    chev.style.transform = open ? '' : 'rotate(90deg)';
                });
                reveal.appendChild(el('div', { class: 'svai-trycall__card' }, [toggle, lines]));
            }

            stage.appendChild(reveal);
            setGoLive(true);
        }

        // ── Call lifecycle ────────────────────────────────────────────────
        function beginCall() {
            if (!window.retellClientJsSdk || !window.retellClientJsSdk.RetellWebClient) {
                toast(t('onboarding.try.sdk_error', 'Voice calling could not load. Please refresh the page and try again.'), true);
                return;
            }
            ended = false;
            clearPendingDelay();
            paintConnecting();
            post('storevoiceai_onboarding_create_call', {}).then(function (r) {
                if (!r || !r.success) {
                    var code = r && r.data && r.data.code;
                    if (code === 'minutes_exhausted') {
                        toast(t('onboarding.try.minutes_exhausted', 'You’ve used up your trial minutes — but you can still finish setup.'), true);
                    } else {
                        toast((r && r.data && r.data.message) || BOOT.i18n.errorSaving, true);
                    }
                    paintPre();
                    return;
                }
                callId = (r.data && r.data.call_id) || null;
                client = new window.retellClientJsSdk.RetellWebClient();
                client.on('call_started', function () { paintInCall(); });
                client.on('call_ended', function () { handleCallEnded(); });
                client.on('error', function () {
                    endClient();
                    toast(t('onboarding.try.call_error', 'We couldn’t start the call. Please allow microphone access and try again.'), true);
                    paintPre();
                });
                client.startCall({ accessToken: r.data.access_token }).catch(function () {
                    endClient();
                    toast(t('onboarding.try.call_error', 'We couldn’t start the call. Please allow microphone access and try again.'), true);
                    paintPre();
                });
            }).catch(function () {
                toast(BOOT.i18n.errorSaving, true);
                paintPre();
            });
        }

        // Retrieve the agent's notes: show a processing screen, fire the log
        // sync immediately, then pause before fetching + revealing so the
        // backend has time to finish processing the call we just placed.
        function retrieveNotes() {
            clearPendingDelay();
            paintProcessing(fmt(t('onboarding.try.retrieving_msg', 'Retrieving %s’s notes…'), name));

            function fetchAndReveal() {
                post('storevoiceai_onboarding_retrieve_call', { call_id: callId || '' }).then(function (r) {
                    if (r && r.success) {
                        paintReveal(r.data || {});
                    } else {
                        toast((r && r.data && r.data.message) || BOOT.i18n.errorSaving, true);
                        paintReveal({}); // surfaces the "still processing" + Refresh state
                    }
                }).catch(function () {
                    toast(BOOT.i18n.errorSaving, true);
                    paintReveal({});
                });
            }

            // Kick the sync now; once it's queued, wait a beat before reading
            // the logs back. A failed sync still falls through to a fetch.
            post('storevoiceai_onboarding_sync_calls', {}).then(function () {
                pendingDelay = setTimeout(fetchAndReveal, 3000);
            }).catch(function () {
                pendingDelay = setTimeout(fetchAndReveal, 1500);
            });
        }

        // ── Persistent footer ─────────────────────────────────────────────
        var goLiveBtn = el('button', {
            class: 'svai-btn svai-btn--primary',
            onclick: function () { goTo(4); }
        }, [document.createTextNode(t('onboarding.try.golive', 'Go live')), icon('arrowR')]);
        goLiveBtn.style.display = 'none';
        function setGoLive(show) { goLiveBtn.style.display = show ? '' : 'none'; }

        var body = el('div', { class: 'svai-card__body' }, [
            stage,
            el('div', { class: 'svai-actions' }, [
                el('button', {
                    class: 'svai-btn svai-btn--ghost',
                    onclick: function () { goTo(2); }
                }, [icon('arrowL'), document.createTextNode(t('onboarding.back', 'Back'))]),
                goLiveBtn
            ]),
            el('button', {
                class: 'svai-text-btn',
                style: 'align-self:center',
                onclick: function () { goTo(4); }
            }, [document.createTextNode(t('onboarding.try.skip', 'Skip for now — I’ll try it later'))])
        ]);

        paintPre();

        return [el('section', { class: 'svai-card' }, [aside, body])];
    }

    // Split a plain-text transcript into speaker-labelled lines. Lines shaped
    // like "Agent: …" / "User: …" get their speaker bolded; anything else is
    // rendered verbatim.
    function renderTranscriptLines(transcript) {
        var lines = String(transcript).split(/\r?\n/).filter(function (l) { return l.trim().length; });
        return lines.map(function (line) {
            var m = line.match(/^\s*([A-Za-z][\w .'-]{0,30}?):\s*(.*)$/);
            if (m) {
                return el('p', { class: 'svai-trycall__line' }, [
                    el('strong', { text: m[1].trim() + ': ' }),
                    document.createTextNode(m[2])
                ]);
            }
            return el('p', { class: 'svai-trycall__line', text: line });
        });
    }

    // ─── Screen 3: Go live ────────────────────────────────────────────────
    function renderStep4() {
        var name = state.agentName || t('onboarding.default_agent_name', 'Grace');
        var minutesEl = el('div', { class: 'svai-minutes__number', text: '0' });
        var aside = el('aside', { class: 'svai-card__aside svai-card__aside--dark svai-card__aside--wide' }, [
            el('span', { class: 'svai-card__eyebrow', text: t('onboarding.all_done', 'All done') }),
            el('div', {}, [
                el('h2', { class: 'svai-card__title', text: fmt(t('onboarding.live_title', '%s is live for %s'), name, BOOT.store.name) }),
                el('p',  { class: 'svai-card__subtitle', text: t('onboarding.live_subtitle', 'Your store now has 24/7 AI voice support. Your free minutes never expire.') })
            ]),
            el('div', { class: 'svai-minutes' }, [
                minutesEl,
                el('p', { class: 'svai-minutes__label', text: t('onboarding.free_minutes', 'free minutes') }),
                el('p', { class: 'svai-minutes__hint',  text: t('onboarding.never_expire', 'Never expire') })
            ]),
            el('div', { class: 'svai-checklist' }, [
                checkRow(fmt(t('onboarding.trained_products', '%s trained on %d products'), name, BOOT.store.productCount || 0)),
                checkRow(t('onboarding.configured', 'Greeting configured')),
                checkRow(t('onboarding.widget_ready', 'Web widget ready to embed'))
            ])
        ]);

        function nextStep(iconName, title, desc, dest, primary) {
            return el('button', {
                class: 'svai-nextstep' + (primary ? ' svai-nextstep--primary' : ''),
                onclick: function () { finishAndGo(dest); }
            }, [
                el('span', { class: 'svai-nextstep__icon' }, [icon(iconName)]),
                el('span', { class: 'svai-nextstep__text' }, [
                    el('span', { class: 'svai-nextstep__title', text: title }),
                    el('span', { class: 'svai-nextstep__desc',  text: desc })
                ]),
                el('span', { class: 'svai-nextstep__arrow' }, [icon('arrowR')])
            ]);
        }

        var body = el('div', { class: 'svai-card__body svai-card__body--golive' }, [
            el('div', {}, [
                el('h3', { text: t('onboarding.next_heading', 'You’re all set. What’s next?') }),
                el('p',  { class: 'svai-field__hint', text: t('onboarding.next_hint', 'Jump straight into the place that fits — you can move between them anytime from the menu.') })
            ]),
            el('div', { class: 'svai-nextsteps' }, [
                nextStep('chart',   t('onboarding.open_dashboard', 'Open your dashboard'),  t('onboarding.open_dashboard_desc', 'See calls, transcripts and live analytics.'), BOOT.dashboard, true),
                nextStep('sliders', fmt(t('onboarding.fine_tune', 'Fine-tune %s'), name),    t('onboarding.fine_tune_desc', 'Voice, greeting, knowledge base and more.'),   BOOT.settings),
                nextStep('phone',   t('onboarding.add_phone', 'Add a phone number'),   fmt(t('onboarding.add_phone_desc', 'Let customers reach %s by phone.'), name),  BOOT.settings)
            ]),
            el('div', { class: 'svai-upsell' }, [
                el('div', { class: 'svai-upsell__text' }, [
                    el('strong', { text: t('onboarding.ready_more', 'Ready for more?') }),
                    el('p', { text: t('onboarding.upsell', 'Unlimited minutes, a dedicated number, and full call transcripts.') })
                ]),
                el('button', {
                    class: 'svai-btn svai-btn--accent svai-upsell__cta',
                    onclick: function () { finishAndGo(BOOT.pricing || BOOT.billing || BOOT.settings); }
                }, [document.createTextNode(t('onboarding.view_plans', 'View plans')), icon('arrowR')])
            ]),
            el('div', { class: 'svai-golive-footer' }, [
                el('button', {
                    class: 'svai-btn svai-btn--ghost',
                    onclick: function () { goTo(3); }
                }, [icon('arrowL'), document.createTextNode(t('onboarding.back', 'Back'))])
            ])
        ]);

        // Count up free minutes
        var n = 0;
        var tick = setInterval(function () {
            n += 1;
            minutesEl.textContent = String(n);
            if (n >= 10) clearInterval(tick);
        }, 90);

        return [el('section', { class: 'svai-card' }, [aside, body])];
    }

    function checkRow(label) {
        return el('div', { class: 'svai-checklist__item' }, [
            icon('check'), el('span', { text: label })
        ]);
    }

    // Mark onboarding complete, then navigate to the explicitly requested
    // destination. The requested url always wins — the backend returns the
    // dashboard as a fallback only, so we don't let it override an explicit
    // "Settings" or "Plans" target.
    function finishAndGo(url) {
        var dest = url || BOOT.dashboard;
        post('storevoiceai_onboarding_finish', {}).then(function () {
            window.location.href = dest;
        }).catch(function () {
            window.location.href = dest;
        });
    }

    // ─── Router ────────────────────────────────────────────────────────────
    function goTo(step) {
        state.step = step;
        post('storevoiceai_onboarding_save_step', { step: step });
        render();
    }

    function render() {
        // Cancel any pending training-screen timers before swapping the view
        // so a late "advance from training" can never overwrite the user's
        // current step.
        trainingTimers.forEach(clearTimeout);
        trainingTimers = [];

        // Let the outgoing screen clean up (hang up a live call, clear timers).
        if (activeTeardown) {
            try { activeTeardown(); } catch (e) {}
            activeTeardown = null;
        }

        clear($main);

        if ($stepper) {
            clear($stepper);
            if (state.step >= 1 && state.step <= 4) {
                $stepper.appendChild(renderStepper(state.step));
            }
        }

        var screen;
        if (state.step === 0)      screen = renderTraining();
        else if (state.step === 1) screen = renderStep2();
        else if (state.step === 2) screen = renderPolicies();
        else if (state.step === 3) screen = renderTryCall();
        else                       screen = renderStep4();

        screen.forEach(function (n) { $main.appendChild(n); });
    }

    // ─── Confirm modal ────────────────────────────────────────────────────
    // Lightweight in-wizard confirm dialog used in place of window.confirm
    // so the UX matches the rest of the takeover.
    function confirmDialog(opts) {
        opts = opts || {};
        var overlay = el('div', { class: 'svai-modal-overlay' });
        var dialog  = el('div', {
            class: 'svai-modal',
            role: 'dialog',
            'aria-modal': 'true',
            'aria-labelledby': 'svai-modal-title'
        });

        dialog.appendChild(el('div', { class: 'svai-modal__icon' }, [
            icon(opts.icon || 'info', { w: 22, h: 22 })
        ]));
        dialog.appendChild(el('h3', {
            class: 'svai-modal__title',
            id: 'svai-modal-title',
            text: opts.title || t('onboarding.confirm_default_title', 'Are you sure?')
        }));
        dialog.appendChild(el('p', {
            class: 'svai-modal__message',
            text: opts.message || ''
        }));

        var cancelBtn  = el('button', {
            type: 'button',
            class: 'svai-btn svai-btn--ghost',
            text: opts.cancelLabel || t('onboarding.confirm_cancel', 'Cancel')
        });
        var confirmBtn = el('button', {
            type: 'button',
            class: 'svai-btn svai-btn--primary',
            text: opts.confirmLabel || t('onboarding.confirm_confirm', 'Confirm')
        });

        dialog.appendChild(el('div', { class: 'svai-modal__actions' }, [cancelBtn, confirmBtn]));
        overlay.appendChild(dialog);
        document.body.appendChild(overlay);

        function close() {
            overlay.classList.remove('svai-modal-overlay--visible');
            document.removeEventListener('keydown', onKey);
            setTimeout(function () { if (overlay.parentNode) overlay.parentNode.removeChild(overlay); }, 220);
        }
        function onKey(e) {
            if (e.key === 'Escape') { e.preventDefault(); close(); }
            else if (e.key === 'Enter') {
                var active = document.activeElement;
                var inActions = active && typeof active.closest === 'function' && active.closest('.svai-modal__actions');
                if (!active || (active.tagName !== 'BUTTON' && !inActions)) {
                    e.preventDefault();
                    confirmBtn.click();
                }
            }
        }
        cancelBtn.addEventListener('click', close);
        confirmBtn.addEventListener('click', function () {
            close();
            if (typeof opts.onConfirm === 'function') opts.onConfirm();
        });
        overlay.addEventListener('click', function (e) { if (e.target === overlay) close(); });
        document.addEventListener('keydown', onKey);

        requestAnimationFrame(function () {
            overlay.classList.add('svai-modal-overlay--visible');
            cancelBtn.focus();
        });
    }

    // ─── Skip ─────────────────────────────────────────────────────────────
    function wireGlobal() {
        document.querySelectorAll('[data-action="skip"]').forEach(function (node) {
            node.addEventListener('click', function () {
                confirmDialog({
                    title:        t('onboarding.skip_title', 'Skip the rest of setup?'),
                    message:      t('onboarding.skip_message', 'Anything you’ve already filled in will be saved. You can run this setup again anytime from the dashboard.'),
                    confirmLabel: t('onboarding.skip_confirm', 'Skip for now'),
                    cancelLabel:  t('onboarding.skip_cancel', 'Stay and finish'),
                    onConfirm:    function () { finishAndGo(BOOT.dashboard); }
                });
            });
        });
    }

    // ─── Boot ──────────────────────────────────────────────────────────────
    function boot() {
        $main = document.getElementById('svai-main');
        if (!$main) return;
        $stepper = document.getElementById('svai-stepper-mount');
        wireGlobal();
        render();
    }

    window.StoreVoiceAIOnboarding = { start: boot };

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', boot);
    } else {
        boot();
    }

})(window, document);
