// ============================================================
// DASHBOARD UI LAYER (Updated with GTM Support)
// Responsibility: HTML Generation, Charts, Modals, Toasts, DOM Manipulation
// ============================================================

import { optimizeImageForUpload } from './image-upload-optimize.js';
import { getUploadUrl, interpretUploadResponse, setUploadConfig } from './image-upload-config.js';
import { getOrderCreatedAtMs } from './order-time-channel.js';
import { readAccountingFeatures } from './fund-ledger.js';
import { orderNetBillAmount } from './finance-analytics-v5.js';

// --- 1. GLOBAL VARIABLES & CONFIG ---

// Default Colors matching shop-classic.html :root (Classic theme)
const DEFAULT_THEME_COLORS = {
    'body-bg': '#f9fafb',
    'text-primary': '#111827',
    'text-secondary': '#6b7280',
    'accent': '#006837',
    'header-bg': '#ffffff',
    'header-text': '#1f2937',
    'search-icon': '#ffffff',
    'menu-bg': '#ffffff',
    'menu-text': '#374151',
    'category-pill-text': '#000000',
    'category-pill-bg': '#ffffff',
    'category-card-label-text': '#000000',
    'category-card-label-bg': '#facc15',
    'cbs-tab-active-bg': '#facc15',
    'cbs-tab-active-text': '#111827',
    'cbs-tab-inactive-bg': '#ffffff',
    'cbs-tab-inactive-text': '#374151',
    'btn-primary-bg': '#006837',
    'btn-primary-text': '#ffffff',
    'btn-secondary-bg': '#111827',
    'btn-secondary-text': '#ffffff',
    'card-bg': '#ffffff',
    'price-text': '#1f2937',
    'footer-bg': '#111827',
    'footer-text': '#e5e7eb',
    'top-bar-bg': '#e6f4ed',
    'top-bar-text': '#374151',
    'search-btn-bg': '#ffd400',
    'search-btn-text': '#111111'
};

/** Default px when no custom typography in Firebase; aligns with shop-classic.html `var(--font-*, …)` fallbacks where defined. */
export const TYPOGRAPHY_DEFAULTS = {
    body: { pc: 16, mobile: 16 },
    heading: { pc: 20, mobile: 18 },
    button: { pc: 14, mobile: 14 },
    productTitle: { pc: 14, mobile: 14 },
    productPrice: { pc: 16, mobile: 16 },
    nav: { pc: 14, mobile: 14 },
    sidebarCategory: { pc: 14, mobile: 14 },
    sidebarCategoryIcon: { pc: 22, mobile: 22 },
    sidebarHeader: { pc: 12, mobile: 12 },
    footer: { pc: 14, mobile: 14 },
    topBar: { pc: 12, mobile: 12 },
    categoryPill: { pc: 14, mobile: 14 },
    cardDescription: { pc: 13, mobile: 13 },
    badge: { pc: 12, mobile: 12 },
    cartDrawer: { pc: 14, mobile: 14 }
};

// Website icon slots (user site) – id must match data-icon-slot in shop-classic
export const WEBSITE_ICON_SLOTS = [
    { id: 'headerSearch', label: 'Header Search', default: 'fa-solid fa-magnifying-glass' },
    { id: 'headerWishlist', label: 'Wishlist (Header)', default: 'fa-regular fa-heart' },
    { id: 'headerCompare', label: 'Compare (Header)', default: 'fa-solid fa-scale-balanced' },
    { id: 'headerCart', label: 'Cart (Header)', default: 'fa-solid fa-cart-shopping' },
    { id: 'headerMenu', label: 'Hamburger Menu', default: 'fa-solid fa-bars' },
    { id: 'headerLocation', label: 'Location', default: 'fa-solid fa-location-dot' },
    { id: 'bottomNavStore', label: 'Bottom Nav – Store', default: 'fa-solid fa-store' },
    { id: 'bottomNavSearch', label: 'Bottom Nav – Search', default: 'fa-solid fa-magnifying-glass' },
    { id: 'bottomNavWishlist', label: 'Bottom Nav – Wishlist', default: 'fa-regular fa-heart' },
    { id: 'bottomNavSupport', label: 'Bottom Nav – Support', default: 'fa-solid fa-headset' },
    { id: 'bottomNavMenu', label: 'Bottom Nav – Menu', default: 'fa-solid fa-bars' },
    { id: 'drawerClose', label: 'Drawer Close (X)', default: 'fa-solid fa-xmark' },
    { id: 'cartDrawerClose', label: 'Cart Drawer Close (X)', default: 'fa-solid fa-xmark' }
];

let serviceStripToggleSyncBound = false;
const ensureServiceStripToggleSync = () => {
    if (serviceStripToggleSyncBound) return;
    const optionalTabEl = document.getElementById('homepage-features-enabled');
    const homeDesignEl = document.getElementById('home-design-service-strip-enabled');
    if (!optionalTabEl || !homeDesignEl) return;
    serviceStripToggleSyncBound = true;
    optionalTabEl.addEventListener('change', () => {
        if (homeDesignEl.checked !== optionalTabEl.checked) homeDesignEl.checked = optionalTabEl.checked;
    });
    homeDesignEl.addEventListener('change', () => {
        if (optionalTabEl.checked !== homeDesignEl.checked) optionalTabEl.checked = homeDesignEl.checked;
    });
};

/** Font Awesome 6 free presets for Optional Content (Classic/Default service strip). */
export const HOMEPAGE_FEATURE_ICON_PRESETS = [
    'fa-solid fa-truck-fast',
    'fa-solid fa-shield-halved',
    'fa-solid fa-headset',
    'fa-solid fa-credit-card',
    'fa-solid fa-bag-shopping',
    'fa-solid fa-clock',
    'fa-solid fa-truck',
    'fa-solid fa-thumbs-up',
    'fa-solid fa-lock',
    'fa-solid fa-rotate-left',
    'fa-solid fa-gift',
    'fa-solid fa-star',
    'fa-solid fa-leaf',
    'fa-solid fa-heart',
    'fa-solid fa-medal',
    'fa-solid fa-box',
    'fa-solid fa-tags',
    'fa-solid fa-percent',
    'fa-solid fa-phone',
    'fa-solid fa-envelope',
    'fa-solid fa-location-dot',
    'fa-solid fa-circle-check',
    'fa-solid fa-handshake',
    'fa-solid fa-user-shield',
    'fa-solid fa-bolt',
    'fa-solid fa-fire',
    'fa-solid fa-cart-shopping',
    'fa-solid fa-shop',
    'fa-solid fa-money-bill-wave',
    'fa-solid fa-circle-question',
    'fa-solid fa-wrench',
    'fa-solid fa-screwdriver-wrench',
    'fa-solid fa-certificate',
    'fa-solid fa-award',
    'fa-solid fa-receipt',
    'fa-solid fa-calendar-check',
    'fa-solid fa-comments',
    'fa-solid fa-plane',
    'fa-solid fa-store',
    'fa-solid fa-recycle',
    'fa-solid fa-clipboard-check',
    'fa-solid fa-truck-ramp-box',
    'fa-solid fa-box-open',
    'fa-solid fa-shield-heart',
    'fa-solid fa-users',
    'fa-solid fa-rotate-right'
];

const PRESET_FA_ICONS = [
    'fa-solid fa-bars', 'fa-solid fa-cart-shopping', 'fa-solid fa-magnifying-glass', 'fa-regular fa-heart', 'fa-solid fa-scale-balanced',
    'fa-solid fa-store', 'fa-solid fa-headset', 'fa-solid fa-xmark', 'fa-solid fa-location-dot', 'fa-solid fa-phone',
    'fa-solid fa-list', 'fa-solid fa-chevron-down', 'fa-solid fa-user', 'fa-solid fa-envelope'
];

const CART_PRESET_FA_ICONS = [
    'fa-solid fa-cart-shopping',
    'fa-solid fa-basket-shopping',
    'fa-solid fa-bag-shopping',
    'fa-solid fa-cart-plus',
    'fa-solid fa-shopping-cart'
];

const getPresetIconsForSlot = (slotId) => (slotId === 'headerCart' ? CART_PRESET_FA_ICONS : PRESET_FA_ICONS);

export function populateWebsiteIconsPanel(iconConfig) {
    const container = document.getElementById('website-icons-list');
    if (!container) return;
    container.innerHTML = WEBSITE_ICON_SLOTS.map(slot => {
        const cfg = iconConfig[slot.id] || {};
        const type = cfg.type === 'custom' ? 'custom' : 'fontawesome';
        const presetVal = (type === 'fontawesome' && cfg.value) ? cfg.value : slot.default;
        const customVal = type === 'custom' ? (cfg.value || '') : '';
        const presetIcons = getPresetIconsForSlot(slot.id);
        return `
        <div class="flex flex-wrap items-center gap-2 p-3 hover:bg-gray-50" data-icon-slot="${slot.id}">
            <span class="w-44 font-medium text-black text-sm">${slot.label}</span>
            <label class="flex items-center gap-1 cursor-pointer"><input type="radio" name="icon-type-${slot.id}" value="fontawesome" ${type === 'fontawesome' ? 'checked' : ''}> Preset</label>
            <select class="admin-form-input w-52 text-sm" id="icon-preset-${slot.id}">${presetIcons.map(c => `<option value="${c}" ${c === presetVal ? 'selected' : ''}>${c.replace('fa-solid fa-', '').replace('fa-regular fa-', '')}</option>`).join('')}</select>
            <label class="flex items-center gap-1 cursor-pointer ml-2"><input type="radio" name="icon-type-${slot.id}" value="custom" ${type === 'custom' ? 'checked' : ''}> Custom URL</label>
            <input type="url" class="admin-form-input w-64 text-sm flex-1 min-w-0" id="icon-custom-${slot.id}" placeholder="https://..." value="${customVal}">
        </div>`;
    }).join('');
}

// Chart Instances Storage
let charts = {
    sparkSales: null,
    sparkIncome: null,
    sparkOrders: null,
    sparkVisitors: null,
    categoryDonut: null,
    revenueBar: null,
    earningsGauge: null,
    visitorsBar: null
};

let homeChartResizeObserver = null;

/**
 * Resize home dashboard Chart.js instances when their layout boxes change (xl fit viewport, flex rows).
 */
export function setupDashboardHomeChartResizeObservers() {
    if (typeof ResizeObserver === 'undefined') return;
    const home = document.getElementById('dashboard-home');
    if (!home) return;

    if (homeChartResizeObserver) {
        homeChartResizeObserver.disconnect();
        homeChartResizeObserver = null;
    }

    const resizeAll = () => {
        requestAnimationFrame(() => {
            Object.values(charts).forEach((c) => {
                if (c && typeof c.resize === 'function') {
                    try {
                        c.resize();
                    } catch (_) { /* ignore */ }
                }
            });
        });
    };

    homeChartResizeObserver = new ResizeObserver(() => resizeAll());

    const selectors = [
        '#sparkline-sales',
        '#sparkline-income',
        '#sparkline-orders',
        '#sparkline-visitors',
        '#categoryChart',
        '#revenueChart',
        '#earningsGauge',
        '#visitorsBarChart'
    ];
    selectors.forEach((sel) => {
        const canvas = home.querySelector(sel);
        const box = canvas?.parentElement;
        if (box) homeChartResizeObserver.observe(box);
    });
}

export function disconnectDashboardHomeChartResizeObservers() {
    if (homeChartResizeObserver) {
        homeChartResizeObserver.disconnect();
        homeChartResizeObserver = null;
    }
}

// Active Upload Target Storage
let activeUploadTarget = { input: null, preview: null, button: null };

/** URL inputs that use image upload — mask long URL after upload / load (not social page links). */
const DASHBOARD_MASKABLE_IMAGE_URL_IDS = [
    'logo-url',
    'footer-logo-url',
    'about-image-url',
    'small-banner-1-url',
    'small-banner-2-url',
    'small-banner-3-url',
    'promo-banner-1-url',
    'promo-banner-2-url'
];

function showDashboardUploadProgress(visible) {
    const root = document.getElementById('dashboard-upload-progress');
    if (!root) return;
    root.classList.toggle('hidden', !visible);
    if (!visible) setDashboardUploadProgressUi(0);
}

function setDashboardUploadProgressUi(percent) {
    const bar = document.getElementById('dashboard-upload-progress-bar');
    const pct = document.getElementById('dashboard-upload-progress-pct');
    const clamped = Math.max(0, Math.min(100, Math.round(percent)));
    if (bar) bar.style.width = `${clamped}%`;
    if (pct) pct.textContent = `${clamped}%`;
}

function uploadImageWithProgress(file, profile = 'product') {
    return new Promise((resolve, reject) => {
        (async () => {
            let uploadFile = file;
            try {
                setDashboardUploadProgressUi(6);
                uploadFile = await optimizeImageForUpload(file, profile);
            } catch (err) {
                reject(err);
                return;
            }
            const xhr = new XMLHttpRequest();
            xhr.timeout = 120000;
            const fd = new FormData();
            fd.append('image', uploadFile);
            xhr.open('POST', getUploadUrl());
            xhr.upload.addEventListener('progress', (e) => {
                if (e.lengthComputable) {
                    const pct = 8 + (92 * e.loaded) / e.total;
                    setDashboardUploadProgressUi(pct);
                } else {
                    setDashboardUploadProgressUi(50);
                }
            });
            xhr.onload = () => {
                const result = interpretUploadResponse(xhr.status, xhr.responseText);
                if (result.ok) resolve(result.url);
                else reject(new Error(result.message));
            };
            xhr.onerror = () => reject(new Error('আপলোড ব্যর্থ: নেটওয়ার্ক ত্রুটি — সংযোগ চেক করুন।'));
            xhr.ontimeout = () => reject(new Error('আপলোড ব্যর্থ: সময় শেষ — আবার চেষ্টা করুন।'));
            xhr.send(fd);
        })().catch(reject);
    });
}

export function removeImageUrlFieldMask(inputEl) {
    if (!inputEl) return;
    inputEl.dataset.urlMasked = '0';
    inputEl.classList.remove('sr-only');
    const flexRow = inputEl.closest('.flex.flex-wrap') || inputEl.closest('.flex.items-center.gap-2');
    flexRow?.querySelector('.upload-url-mask-ui')?.remove();
}

/**
 * Hide raw URL; show "Image uploaded" + Change (opens file picker via sibling upload button).
 */
export function applyImageUrlFieldMask(inputEl) {
    if (!inputEl || !String(inputEl.value || '').trim()) return;
    if (inputEl.dataset.urlMasked === '1') return;
    const flexRow = inputEl.closest('.flex.flex-wrap') || inputEl.closest('.flex.items-center.gap-2');
    if (!flexRow) return;
    const uploadBtn = flexRow.querySelector('button[onclick*="triggerUpload"]');
    if (flexRow.querySelector('.upload-url-mask-ui')) return;
    inputEl.dataset.urlMasked = '1';
    inputEl.classList.add('sr-only');
    const row = document.createElement('div');
    row.className = 'upload-url-mask-ui flex items-center gap-2 flex-1 min-w-0 min-h-[38px]';
    row.innerHTML = `<span class="text-sm text-emerald-700 font-medium truncate">Image uploaded</span><button type="button" class="text-xs text-indigo-600 hover:underline whitespace-nowrap shrink-0 font-semibold">Change</button>`;
    const changeBtn = row.querySelector('button');
    changeBtn.addEventListener('click', (ev) => {
        ev.preventDefault();
        if (uploadBtn && typeof window.triggerUpload === 'function') {
            window.triggerUpload(uploadBtn);
        }
    });
    if (uploadBtn) flexRow.insertBefore(row, uploadBtn);
    else flexRow.appendChild(row);
}

export function maskLoadedDashboardImageUrlFields() {
    DASHBOARD_MASKABLE_IMAGE_URL_IDS.forEach((id) => {
        const el = document.getElementById(id);
        if (el && String(el.value || '').trim()) applyImageUrlFieldMask(el);
    });
    document.querySelectorAll('.main-slide-row .main-slide-input').forEach((inp) => {
        if (String(inp.value || '').trim()) applyImageUrlFieldMask(inp);
    });
}

// --- 2. GLOBAL UI HELPERS ---

export function showToast(message, isSuccess = true) {
    const toast = document.getElementById('toast');
    if (!toast) return;
    const toastMessage = document.getElementById('toast-message');
    const icon = toast.querySelector('i');
    
    toastMessage.textContent = message;
    
    // Reset Classes
    toast.className = `fixed top-5 right-5 text-white py-2 px-6 rounded-lg shadow-xl opacity-0 translate-y-[-20px] transition-all duration-300 z-[100] flex items-center gap-2 border`;
    
    if (isSuccess) {
        toast.classList.add('bg-green-600', 'border-green-500');
        if(icon) icon.className = "fa-solid fa-check-circle";
    } else {
        toast.classList.add('bg-red-600', 'border-red-500');
        if(icon) icon.className = "fa-solid fa-triangle-exclamation";
    }

    // Animation
    requestAnimationFrame(() => {
        toast.classList.remove('opacity-0', 'translate-y-[-20px]');
    });

    setTimeout(() => {
        toast.classList.add('opacity-0', 'translate-y-[-20px]');
    }, 3000);
}

export function toggleModal(modalId, show) {
    const modal = document.getElementById(modalId);
    if (!modal) return;
    if (show) {
        modal.classList.remove('hidden');
        modal.classList.add('flex');
    } else {
        modal.classList.add('hidden');
        modal.classList.remove('flex');
    }
}

export function toggleSidebar(show) {
    const sidebar = document.getElementById('main-sidebar');
    const overlay = document.getElementById('sidebar-overlay');
    if (!sidebar || !overlay) return;

    const isDesktop = window.matchMedia('(min-width: 768px)').matches;

    if (show) {
        sidebar.classList.remove('-translate-x-full');
        if (!isDesktop) overlay.classList.remove('hidden');
    } else {
        // Desktop-এ sidebar সবসময় visible — close করা হবে না
        if (!isDesktop) {
            sidebar.classList.add('-translate-x-full');
            overlay.classList.add('hidden');
        }
    }
}

export function setLoadingState(isLoading, target) {
    if (!target || !target.button) return;
    const button = target.button;
    const icon = button.querySelector('i');
    const textSpan = button.querySelector('.button-text');
    const spinner = button.querySelector('.spinner');
    
    button.disabled = isLoading;
    if (isLoading) {
        button.classList.add('opacity-70', 'cursor-not-allowed');
        if (icon) icon.style.display = 'none';
        if (textSpan) textSpan.style.display = 'none';
        if (spinner) spinner.style.display = 'inline-block';
    } else {
        button.classList.remove('opacity-70', 'cursor-not-allowed');
        if (icon) icon.style.display = 'inline-block';
        if (textSpan) textSpan.style.display = 'inline-block';
        if (spinner) spinner.style.display = 'none';
    }
}

// --- 3. IMAGE UPLOAD HANDLING ---

window.triggerUpload = function(buttonElement) {
    activeUploadTarget = {
        input: buttonElement.dataset.targetInput,
        preview: buttonElement.dataset.previewContainer,
        button: buttonElement,
        uploadProfile: buttonElement.dataset.uploadProfile || 'product',
    };
    document.getElementById('image-upload-input').click();
};

export function updateImagePreview(inputId, previewId) {
    const inputElement = document.querySelector(inputId); 
    if (!inputElement) return;
    const row = inputElement.closest('.main-slide-row');
    if (row) {
        refreshMainSlideRowPreview(row);
        return;
    }
    const previewContainer = previewId ? document.querySelector(previewId) : null;
    if (!previewContainer) return;
    const url = inputElement.value;
    if (url) {
        previewContainer.innerHTML = `<div class="relative group w-full h-full flex items-center justify-center"><img src="${url}" class="max-w-full max-h-full object-contain rounded border border-gray-200 bg-gray-50"><div class="absolute inset-0 bg-black/50 hidden group-hover:flex items-center justify-center text-white text-xs rounded">Preview</div></div>`;
    } else {
        previewContainer.innerHTML = '';
    }
}

/** Small Hero Banners (Banners → Small): clear slot URL, optional link, and preview */
export function clearSmallBannerSlot(slotIndex) {
    const n = Number(slotIndex);
    if (!Number.isFinite(n) || n < 1 || n > 3) return;
    const urlEl = document.getElementById(`small-banner-${n}-url`);
    const linkEl = document.getElementById(`small-banner-${n}-link`);
    if (urlEl) urlEl.value = '';
    if (linkEl) linkEl.value = '';
    updateImagePreview(`#small-banner-${n}-url`, `#small-banner-${n}-preview`);
}

// --- Main slider: PC/Mobile crop preview + ratio hints (matches classic storefront object-fit: cover) ---

const BANNER_PC_CONTAINER_INNER = 1168;
const BANNER_PC_SIDEBAR = 210;
const BANNER_PC_VIEWPORT_FULL = 1920;
const BANNER_MOB_VIEWPORT = 390;
const BANNER_MOB_CONTAINER_PAD = 32;

function gcdInt(a, b) {
    a = Math.abs(Math.round(a));
    b = Math.abs(Math.round(b));
    while (b) {
        const t = b;
        b = a % b;
        a = t;
    }
    return a || 1;
}

function ratioLabel(w, h) {
    const g = gcdInt(w, h);
    return `${Math.round(w / g)}∶${Math.round(h / g)}`;
}

export function getMainBannerGeometry() {
    const sidePc = Math.min(80, Math.max(0, parseInt(document.getElementById('banner-space-desktop')?.value, 10) || 32));
    const sideMob = Math.min(40, Math.max(0, parseInt(document.getElementById('banner-space-mobile')?.value, 10) || 12));
    const Hpc = Math.min(800, Math.max(200, parseInt(document.getElementById('banner-height-desktop')?.value, 10) || 420));
    const Hmob = Math.min(400, Math.max(80, parseInt(document.getElementById('banner-height-mobile')?.value, 10) || 140));
    const presetEl = document.getElementById('banner-main-hero-preset');
    const presetFabrilife = presetEl && presetEl.value === 'fabrilife';
    const pcLayoutEl = document.getElementById('banner-desktop-main-slider-layout');
    const pcFullBleed = presetFabrilife || (pcLayoutEl && pcLayoutEl.value === 'fullBleed');
    const mobLayoutEl = document.getElementById('banner-mobile-main-slider-layout');
    const mobFullBleed = presetFabrilife || (mobLayoutEl && mobLayoutEl.value === 'fullBleed');
    const wSliderRow = Math.max(400, BANNER_PC_CONTAINER_INNER - 2 * sidePc);
    const wRefPc = pcFullBleed
        ? Math.max(320, BANNER_PC_VIEWPORT_FULL - BANNER_PC_SIDEBAR)
        : Math.max(320, wSliderRow - BANNER_PC_SIDEBAR);
    const wRefMob = mobFullBleed
        ? Math.max(240, BANNER_MOB_VIEWPORT)
        : Math.max(240, BANNER_MOB_VIEWPORT - BANNER_MOB_CONTAINER_PAD - 2 * sideMob);
    return { wRefPc, wRefMob, Hpc, Hmob, sidePc, sideMob };
}

export function updateMainBannerGlobalRatioHint() {
    const summaryEl = document.getElementById('main-banner-settings-hint-summary');
    const detailEl = document.getElementById('main-banner-settings-hint-detail');
    const legacyEl = document.getElementById('main-banner-settings-hint');
    const { wRefPc, wRefMob, Hpc, Hmob } = getMainBannerGeometry();
    const rPc = (wRefPc / Hpc).toFixed(2);
    const rMob = (wRefMob / Hmob).toFixed(2);
    const exPcW = Math.round(wRefPc * 2);
    const exPcH = Math.round(Hpc * 2);
    const exMobW = Math.round(wRefMob * 2);
    const exMobH = Math.round(Hmob * 2);
    const labPc = ratioLabel(wRefPc, Hpc);
    const labMob = ratioLabel(wRefMob, Hmob);
    const shortHtml = `PC <strong>${labPc}</strong> (~${exPcW}×${exPcH}px @2×) · Mobile <strong>${labMob}</strong> (~${exMobW}×${exMobH}px @2×)`;
    const detailHtml = `Approx. ideal aspect (no crop with <strong>cover</strong>): <strong>PC</strong> width∶height ≈ <strong>${labPc}</strong> (${rPc}∶1) — e.g. <strong>${exPcW}×${exPcH}px</strong> @2×. <strong>Mobile</strong> ≈ <strong>${labMob}</strong> (${rMob}∶1) — e.g. <strong>${exMobW}×${exMobH}px</strong>. Reference widths are approximate.`;
    if (summaryEl && detailEl) {
        summaryEl.innerHTML = shortHtml;
        detailEl.innerHTML = detailHtml;
    } else if (legacyEl) {
        legacyEl.innerHTML = `${shortHtml}<br><span class="text-[11px] text-black">${detailHtml}</span>`;
    }
}

export function refreshMainSlideRowPreview(rowEl) {
    if (!rowEl || !rowEl.classList.contains('main-slide-row')) return;
    const input = rowEl.querySelector('.main-slide-input');
    const url = (input && input.value) ? String(input.value).trim() : '';
    const pcImg = rowEl.querySelector('[data-banner-preview="pc"]');
    const mobImg = rowEl.querySelector('[data-banner-preview="mobile"]');
    const pcPh = rowEl.querySelector('[data-banner-preview-placeholder="pc"]');
    const mobPh = rowEl.querySelector('[data-banner-preview-placeholder="mobile"]');
    const pcFrame = rowEl.querySelector('[data-banner-preview-frame="pc"]');
    const mobFrame = rowEl.querySelector('[data-banner-preview-frame="mobile"]');
    const suggestEl = rowEl.querySelector('.banner-slide-suggestion');

    const { wRefPc, wRefMob, Hpc, Hmob } = getMainBannerGeometry();

    const PC_PREVIEW_W = 220;
    const MOB_PREVIEW_W = 120;
    const MAX_PREVIEW_H_PC = 92;
    const MAX_PREVIEW_H_MOB = 80;
    let hPcPrev = Math.max(44, Math.round(Hpc * (PC_PREVIEW_W / wRefPc)));
    let hMobPrev = Math.max(36, Math.round(Hmob * (MOB_PREVIEW_W / wRefMob)));
    hPcPrev = Math.min(MAX_PREVIEW_H_PC, hPcPrev);
    hMobPrev = Math.min(MAX_PREVIEW_H_MOB, hMobPrev);
    if (pcFrame) {
        pcFrame.style.width = `${PC_PREVIEW_W}px`;
        pcFrame.style.height = `${hPcPrev}px`;
    }
    if (mobFrame) {
        mobFrame.style.width = `${MOB_PREVIEW_W}px`;
        mobFrame.style.height = `${hMobPrev}px`;
    }

    const setPreviewImg = (img, ph) => {
        if (!img) return;
        if (url) {
            img.src = url;
            img.style.display = '';
            img.alt = 'Banner preview';
            if (ph) ph.classList.add('hidden');
        } else {
            img.removeAttribute('src');
            img.style.display = 'none';
            if (ph) ph.classList.remove('hidden');
        }
    };
    setPreviewImg(pcImg, pcPh);
    setPreviewImg(mobImg, mobPh);

    const thumb = rowEl.querySelector('.main-slide-thumb');
    if (thumb) {
        if (url) thumb.innerHTML = `<img src="${url.replace(/"/g, '&quot;')}" class="w-full h-full object-cover" alt="">`;
        else thumb.innerHTML = '';
    }

    if (!suggestEl) return;

    if (!url) {
        suggestEl.innerHTML = '<span class="text-black">Use ratios from <strong>Ideal image sizes</strong> above (expand for details).</span>';
        return;
    }

    const done = (iw, ih) => {
        if (!iw || !ih) {
            suggestEl.innerHTML = '<span class="text-amber-700">Could not read image dimensions (CORS or invalid URL). Compare to header tip.</span>';
            return;
        }
        const imgR = iw / ih;
        const targetPc = wRefPc / Hpc;
        const targetMob = wRefMob / Hmob;
        const diffPc = Math.abs(imgR - targetPc) / targetPc;
        const diffMob = Math.abs(imgR - targetMob) / targetMob;
        let crop = '';
        if (diffPc > 0.08 || diffMob > 0.08) {
            if (imgR > targetPc * 1.05) crop += ' Wider than PC frame — left/right may crop.';
            if (imgR < targetPc * 0.95) crop += ' Taller than PC frame — top/bottom may crop.';
            if (imgR > targetMob * 1.08) crop += ' Mobile: sides may crop more.';
            if (imgR < targetMob * 0.92) crop += ' Mobile: top/bottom may crop more.';
        } else {
            crop = ' Cropping should be minimal.';
        }
        suggestEl.innerHTML = `<span class="text-black">${iw}×${ih}px (${ratioLabel(iw, ih)})</span><span class="text-black">${crop}</span>`;
    };

    const probe = new Image();
    probe.onload = () => done(probe.naturalWidth, probe.naturalHeight);
    probe.onerror = () => done(0, 0);
    probe.src = url;
}

export function refreshAllMainSlideBannerPreviews() {
    document.querySelectorAll('.main-slide-row').forEach((row) => refreshMainSlideRowPreview(row));
    updateMainBannerGlobalRatioHint();
}

let mainBannerPreviewListenersBound = false;

function syncMainHeroPresetFromLayoutSelects() {
    const desk = document.getElementById('banner-desktop-main-slider-layout');
    const mob = document.getElementById('banner-mobile-main-slider-layout');
    const preset = document.getElementById('banner-main-hero-preset');
    if (!desk || !mob || !preset) return;
    const d = desk.value === 'fullBleed';
    const m = mob.value === 'fullBleed';
    let next = 'custom';
    if (d && m) next = 'fabrilife';
    else if (!d && !m) next = 'standard';
    preset.value = next;
}

function applyMainHeroPresetMasterSelect() {
    const preset = document.getElementById('banner-main-hero-preset');
    const desk = document.getElementById('banner-desktop-main-slider-layout');
    const mob = document.getElementById('banner-mobile-main-slider-layout');
    if (!preset || !desk || !mob) return;
    if (preset.value === 'fabrilife') {
        desk.value = 'fullBleed';
        mob.value = 'fullBleed';
    } else if (preset.value === 'standard') {
        desk.value = 'inset';
        mob.value = 'inset';
    }
}

export function initMainSliderBannerPreviewListeners() {
    if (mainBannerPreviewListenersBound) return;
    mainBannerPreviewListenersBound = true;
    const refresh = () => {
        refreshAllMainSlideBannerPreviews();
        updateBannerSideGapStatus();
    };
    ['banner-height-desktop', 'banner-height-mobile', 'banner-space-desktop', 'banner-space-mobile', 'banner-desktop-main-slider-layout', 'banner-mobile-main-slider-layout', 'banner-desktop-content-side-gap', 'banner-desktop-content-side-gap-range'].forEach((id) => {
        const el = document.getElementById(id);
        if (!el) return;
        const handler = () => {
            if (id === 'banner-desktop-main-slider-layout' || id === 'banner-mobile-main-slider-layout') {
                syncMainHeroPresetFromLayoutSelects();
            }
            refresh();
        };
        el.addEventListener('input', handler);
        el.addEventListener('change', handler);
    });
    document.getElementById('banner-main-hero-preset')?.addEventListener('change', () => {
        applyMainHeroPresetMasterSelect();
        refresh();
    });
    requestAnimationFrame(() => updateMainBannerGlobalRatioHint());
}

const BANNERS_ACTIVE_SUB_KEY = 'bannersActiveSub';
const BANNERS_SUB_IDS = new Set(['slides', 'layout', 'small', 'promos']);

let bannersSubNavInitialized = false;

/** Compact sub-tabs inside Customization → Banners; panels stay mounted (IDs unchanged for save/load). */
export function initBannersSubNav() {
    if (bannersSubNavInitialized) return;
    const root = document.querySelector('.customization-tab-panel[data-tab="banners"]');
    if (!root) return;
    const buttons = Array.from(root.querySelectorAll('button[data-banners-sub]'));
    const panels = root.querySelectorAll('[data-banners-sub-panel]');
    if (buttons.length === 0 || panels.length === 0) return;
    bannersSubNavInitialized = true;

    const baseBtn = 'banners-sub-btn px-3 py-2 rounded-lg text-xs whitespace-nowrap shrink-0 transition border';
    const setBtnActive = (btn, active) => {
        if (active) {
            btn.className = `${baseBtn} font-semibold bg-indigo-50 text-indigo-700 shadow-sm ring-1 ring-indigo-100 border-indigo-100`;
        } else {
            btn.className = `${baseBtn} font-medium text-black hover:bg-white hover:text-black border-transparent bg-transparent`;
        }
    };

    const applySub = (key) => {
        const k = BANNERS_SUB_IDS.has(key) ? key : 'slides';
        try {
            localStorage.setItem(BANNERS_ACTIVE_SUB_KEY, k);
        } catch (_) {
            /* ignore quota / private mode */
        }
        buttons.forEach((btn) => setBtnActive(btn, btn.getAttribute('data-banners-sub') === k));
        panels.forEach((panel) => {
            const match = panel.getAttribute('data-banners-sub-panel') === k;
            panel.classList.toggle('hidden', !match);
        });
        refreshAllMainSlideBannerPreviews();
    };

    buttons.forEach((btn) => {
        btn.addEventListener('click', () => {
            const id = btn.getAttribute('data-banners-sub');
            if (id) applySub(id);
        });
    });

    let initial = 'slides';
    try {
        const raw = localStorage.getItem(BANNERS_ACTIVE_SUB_KEY);
        if (raw && BANNERS_SUB_IDS.has(raw)) initial = raw;
    } catch (_) {
        /* ignore */
    }
    applySub(initial);
}

export async function handleImageUpload(file) {
    if (!file || !activeUploadTarget.button) return;
    const targetSnapshot = { ...activeUploadTarget };
    setLoadingState(true, targetSnapshot);
    showDashboardUploadProgress(true);
    setDashboardUploadProgressUi(0);
    try {
        const uploadedUrl = await uploadImageWithProgress(file, targetSnapshot.uploadProfile || 'product');
        const inputElement = document.querySelector(targetSnapshot.input);
        if (inputElement) {
            inputElement.value = uploadedUrl;
            if (targetSnapshot.preview) {
                updateImagePreview(targetSnapshot.input, targetSnapshot.preview);
            } else {
                const rowUp = inputElement.closest('.main-slide-row');
                if (rowUp) refreshMainSlideRowPreview(rowUp);
            }
            applyImageUrlFieldMask(inputElement);
            showToast('Image uploaded successfully!');
        } else {
            throw new Error('আপলোড ব্যর্থ: ইমেজ URL বসানোর ঘর পাওয়া যায়নি।');
        }
    } catch (error) {
        console.error('Image upload error:', error);
        showToast(error.message || 'আপলোড ব্যর্থ।', false);
    } finally {
        setDashboardUploadProgressUi(100);
        setTimeout(() => showDashboardUploadProgress(false), 450);
        setLoadingState(false, targetSnapshot);
        const fin = document.getElementById('image-upload-input');
        if (fin) fin.value = '';
        activeUploadTarget = {};
    }
}

// --- 4. DATA PROCESSING FOR CHARTS ---

function getLastNDays(n) {
    const days = [];
    for (let i = n - 1; i >= 0; i--) {
        const d = new Date();
        d.setDate(d.getDate() - i);
        days.push({
            dateStr: d.toLocaleDateString('en-GB'), 
            label: d.toLocaleDateString('en-US', { day: 'numeric', month: 'short' })
        });
    }
    return days;
}

function getLast12Months() {
    const months = [];
    const today = new Date();
    for (let i = 11; i >= 0; i--) {
        const d = new Date(today.getFullYear(), today.getMonth() - i, 1);
        months.push({
            key: `${d.getMonth()}-${d.getFullYear()}`,
            label: d.toLocaleDateString('en-US', { month: 'short' })
        });
    }
    return months;
}

/** Oldest first: each bucket is a 7-day window ending `today - wi*7`. */
function getLastNWeeks(n) {
    const weeks = [];
    for (let wi = n - 1; wi >= 0; wi--) {
        const end = new Date();
        end.setDate(end.getDate() - wi * 7);
        end.setHours(23, 59, 59, 999);
        const start = new Date(end);
        start.setDate(start.getDate() - 6);
        start.setHours(0, 0, 0, 0);
        weeks.push({
            start,
            end,
            label: `${start.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`
        });
    }
    return weeks;
}

function formatDashDateRangeEn(d1, d2) {
    const a = d1.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
    const b = d2.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
    return `${a} – ${b}`;
}

function isUnknownLocationPart(val) {
    const t = String(val || '').trim();
    return !t || t.toLowerCase() === 'unknown';
}

/** Derive a display label from visitor meta (IP geo: city, region, country, or stored location string). */
function visitorLocationLabelFromMeta(meta) {
    if (!meta || typeof meta !== 'object') return '';
    const city = !isUnknownLocationPart(meta.city) ? String(meta.city).trim() : '';
    const region = !isUnknownLocationPart(meta.region) ? String(meta.region).trim() : '';
    const country = !isUnknownLocationPart(meta.country) ? String(meta.country).trim() : '';
    const locationStr =
        meta.location != null && String(meta.location).trim() && !isUnknownLocationPart(meta.location)
            ? String(meta.location).trim()
            : '';

    if (city && region && city !== region) return `${city}, ${region}`;
    if (city && country && city !== country) return `${city}, ${country}`;
    if (city) return city;
    if (region && country && region !== country) return `${region}, ${country}`;
    if (region) return region;
    if (country) return country;
    if (locationStr) return locationStr;
    return '';
}

export function processDashboardData(orders, products, visitors, rawSiteSettings) {
    const useNetSales = readAccountingFeatures(rawSiteSettings).netSalesUnified;
    const last7Days = getLastNDays(7);
    const last14Days = getLastNDays(14);
    const last30Days = getLastNDays(30);
    const last12Months = getLast12Months();
    const last12Weeks = getLastNWeeks(12);

    const daily14Sales = new Array(14).fill(0);
    const daily14Income = new Array(14).fill(0);
    const daily14Orders = new Array(14).fill(0);
    const daily30Revenue = new Array(30).fill(0);
    const daily30Orders = new Array(30).fill(0);
    const weekly12Revenue = new Array(12).fill(0);
    const weekly12Orders = new Array(12).fill(0);
    const daily14Visitors = new Array(14).fill(0);

    let stats = {
        totalSales: 0,
        totalIncome: 0, 
        paidOrders: 0,
        totalTax: 0,
        totalVisitors: Object.keys(visitors || {}).length,
        dailySales: new Array(7).fill(0),
        dailyIncome: new Array(7).fill(0),
        dailyOrders: new Array(7).fill(0),
        dailyVisitors: new Array(7).fill(0),
        categorySales: {},
        monthlyRevenue: new Array(12).fill(0),
        monthlyOrders: new Array(12).fill(0),
        productSalesCount: {},
        topLocations: []
    };

    try {
        Object.values(orders || {}).forEach(order => {
            if (order.status === 'Canceled' || order.status === 'Refunded') return;

            const tMs = getOrderCreatedAtMs(order);
            if (tMs == null) return;
            const orderDate = new Date(tMs);
            const dateStr = orderDate.toLocaleDateString('en-GB');
            const monthKey = `${orderDate.getMonth()}-${orderDate.getFullYear()}`;
            
            const total = useNetSales ? orderNetBillAmount(order) : parseFloat(order.totalBill || 0);
            const subtotal = parseFloat(order.subtotal || total);
            const taxAmt = parseFloat(order.amount?.tax ?? 0) || 0;
            
            stats.totalSales += total;
            stats.totalIncome += subtotal;
            stats.totalTax += taxAmt;
            stats.paidOrders++;

            const dayIndex = last7Days.findIndex(d => d.dateStr === dateStr);
            if (dayIndex !== -1) {
                stats.dailySales[dayIndex] += total;
                stats.dailyIncome[dayIndex] += subtotal;
                stats.dailyOrders[dayIndex] += 1;
            }

            const day14 = last14Days.findIndex(d => d.dateStr === dateStr);
            if (day14 !== -1) {
                daily14Sales[day14] += total;
                daily14Income[day14] += subtotal;
                daily14Orders[day14] += 1;
            }

            const day30 = last30Days.findIndex(d => d.dateStr === dateStr);
            if (day30 !== -1) {
                daily30Revenue[day30] += total;
                daily30Orders[day30] += 1;
            }

            const t = orderDate.getTime();
            for (let wi = 0; wi < last12Weeks.length; wi++) {
                const w = last12Weeks[wi];
                if (t >= w.start.getTime() && t <= w.end.getTime()) {
                    weekly12Revenue[wi] += total;
                    weekly12Orders[wi] += 1;
                    break;
                }
            }

            const monthIndex = last12Months.findIndex(m => m.key === monthKey);
            if (monthIndex !== -1) {
                stats.monthlyRevenue[monthIndex] += total;
                stats.monthlyOrders[monthIndex] += 1;
            }

            if (order.items) {
                const items = Array.isArray(order.items) ? order.items : Object.values(order.items);
                items.forEach(item => {
                    const qty = parseInt(item.quantity || 1);
                    const price = parseFloat(item.price || 0);
                    const totalItemPrice = qty * price;

                    let catName = 'Others';
                    if (item.category) {
                        catName = item.category;
                    } else if (products && products[item.id]) {
                        catName = products[item.id].category || 'Others';
                    }
                    
                    if (!stats.categorySales[catName]) stats.categorySales[catName] = 0;
                    stats.categorySales[catName] += totalItemPrice;

                    if (!stats.productSalesCount[item.id]) {
                        stats.productSalesCount[item.id] = {
                            name: item.name || 'Unknown',
                            image: item.image || item.imageUrl,
                            count: 0,
                            totalEarned: 0
                        };
                    }
                    stats.productSalesCount[item.id].count += qty;
                    stats.productSalesCount[item.id].totalEarned += totalItemPrice;
                });
            }
        });

        const locationCounts = {};
        Object.values(visitors || {}).forEach(visitor => {
            if(visitor.lastActive) {
                const visitDate = new Date(visitor.lastActive).toLocaleDateString('en-GB');
                const dayIndex = last7Days.findIndex(d => d.dateStr === visitDate);
                if (dayIndex !== -1) {
                    stats.dailyVisitors[dayIndex] += 1;
                }
                const day14v = last14Days.findIndex(d => d.dateStr === visitDate);
                if (day14v !== -1) daily14Visitors[day14v] += 1;
            }
            const locLabel = visitorLocationLabelFromMeta(visitor.meta);
            if (locLabel) {
                locationCounts[locLabel] = (locationCounts[locLabel] || 0) + 1;
            }
        });
        stats.topLocations = Object.entries(locationCounts)
            .map(([label, count]) => ({ label, count }))
            .sort((a, b) => b.count - a.count)
            .slice(0, 5);
    } catch (err) {
        console.error("Data Processing Error:", err);
    }

    const sumArr = (a) => (a || []).reduce((x, y) => x + (Number(y) || 0), 0);
    const pctTrend = (cur, prev) => {
        if (prev > 0) return ((cur - prev) / prev) * 100;
        if (cur > 0) return 100;
        return 0;
    };

    const curSales7 = sumArr(stats.dailySales);
    const prevSales7 = sumArr(daily14Sales.slice(0, 7));
    const curIncome7 = sumArr(stats.dailyIncome);
    const prevIncome7 = sumArr(daily14Income.slice(0, 7));
    const curOrders7 = sumArr(stats.dailyOrders);
    const prevOrders7 = sumArr(daily14Orders.slice(0, 7));
    const curVis7 = sumArr(stats.dailyVisitors);
    const prevVis7 = sumArr(daily14Visitors.slice(0, 7));

    const now = new Date();
    const currentPeriodEnd = new Date(now);
    currentPeriodEnd.setHours(23, 59, 59, 999);
    const currentPeriodStart = new Date(now);
    currentPeriodStart.setDate(currentPeriodStart.getDate() - 6);
    currentPeriodStart.setHours(0, 0, 0, 0);
    const prevPeriodEnd = new Date(currentPeriodStart);
    prevPeriodEnd.setDate(prevPeriodEnd.getDate() - 1);
    prevPeriodEnd.setHours(23, 59, 59, 999);
    const prevPeriodStart = new Date(prevPeriodEnd);
    prevPeriodStart.setDate(prevPeriodStart.getDate() - 6);
    prevPeriodStart.setHours(0, 0, 0, 0);

    return {
        ...stats,
        labels7Days: last7Days.map(d => d.label),
        labels12Months: last12Months.map(m => m.label),
        labels30Days: last30Days.map(d => d.label),
        daily30Revenue,
        daily30Orders,
        labels12Weeks: last12Weeks.map((w) => w.label),
        weekly12Revenue,
        weekly12Orders,
        currentPeriodRangeLabel: formatDashDateRangeEn(currentPeriodStart, currentPeriodEnd),
        comparisonRangeLabel: formatDashDateRangeEn(prevPeriodStart, prevPeriodEnd),
        kpiTrendSalesPct: pctTrend(curSales7, prevSales7),
        kpiTrendIncomePct: pctTrend(curIncome7, prevIncome7),
        kpiTrendOrdersPct: pctTrend(curOrders7, prevOrders7),
        kpiTrendVisitorsPct: pctTrend(curVis7, prevVis7)
    };
}

/** @param {unknown} order */
function orderTimestampMs(order) {
    return getOrderCreatedAtMs(order);
}

/** Same phone + orders within this window (ms) are flagged as duplicate / double order */
const DUPLICATE_ORDER_WINDOW_MS = 2 * 24 * 60 * 60 * 1000;

function normalizePhoneForDuplicateDetection(phone) {
    const d = String(phone || '').replace(/\D/g, '');
    return d.length >= 10 ? d : '';
}

/**
 * Returns Set of order ids that have at least one other order with the same phone
 * and createdAt within DUPLICATE_ORDER_WINDOW_MS.
 * @param {Record<string, object>} ordersData
 * @returns {Set<string>}
 */
function computeDuplicateOrderIdsSet(ordersData) {
    const dupIds = new Set();
    if (!ordersData || typeof ordersData !== 'object') return dupIds;
    const list = Object.entries(ordersData).map(([id, val]) => ({ id, ...val }));
    for (let i = 0; i < list.length; i++) {
        const a = list[i];
        const pa = normalizePhoneForDuplicateDetection(a.customerInfo?.phone || a.phone);
        if (!pa) continue;
        const ta = orderTimestampMs(a);
        if (ta == null) continue;
        for (let j = i + 1; j < list.length; j++) {
            const b = list[j];
            const pb = normalizePhoneForDuplicateDetection(b.customerInfo?.phone || b.phone);
            if (pa !== pb) continue;
            const tb = orderTimestampMs(b);
            if (tb == null) continue;
            if (Math.abs(ta - tb) <= DUPLICATE_ORDER_WINDOW_MS) {
                dupIds.add(a.id);
                dupIds.add(b.id);
            }
        }
    }
    return dupIds;
}

/**
 * Other orders (same phone) within duplicate window vs current order time.
 * @returns {{ id: string, createdAtMs: number, order: object }[]}
 */
function getRelatedDuplicateOrdersForDetail(currentId, order, ordersData) {
    const out = [];
    if (!order || !ordersData || typeof ordersData !== 'object') return out;
    const phone = normalizePhoneForDuplicateDetection(order.customerInfo?.phone || order.phone);
    if (!phone) return out;
    const t0 = orderTimestampMs(order);
    if (t0 == null) return out;
    for (const [oid, val] of Object.entries(ordersData)) {
        if (oid === currentId) continue;
        const p = normalizePhoneForDuplicateDetection(val?.customerInfo?.phone || val?.phone);
        if (p !== phone) continue;
        const t = orderTimestampMs(val);
        if (t == null) continue;
        if (Math.abs(t - t0) <= DUPLICATE_ORDER_WINDOW_MS) {
            out.push({ id: oid, createdAtMs: t, order: val });
        }
    }
    out.sort((a, b) => b.createdAtMs - a.createdAtMs);
    return out;
}

const BD_INTEL_CITIES = [
    'Dhaka',
    'Chattogram',
    'Chittagong',
    'Sylhet',
    'Rajshahi',
    'Khulna',
    'Barishal',
    'Barisal',
    'Rangpur',
    'Mymensingh',
    "Cox's Bazar",
    'Coxs Bazar',
    'Narayanganj',
    'Gazipur',
    'Comilla',
    'Cumilla',
    'Jessore',
    'Jashore',
    'Bogura',
    'Bogra',
    'Tangail',
    'Kushtia',
    'Dinajpur',
    'Faridpur',
    'Noakhali',
];

function intelMatchDeliveryZone(address) {
    if (address == null || typeof address !== 'string') return '';
    const trimmed = address.trim();
    if (!trimmed) return '';
    const lower = trimmed.toLowerCase();
    for (const city of BD_INTEL_CITIES) {
        if (lower.includes(city.toLowerCase())) {
            return city === 'Chittagong' ? 'Chattogram' : city;
        }
    }
    const parts = trimmed.split(/[,，]/).map((s) => s.trim()).filter(Boolean);
    if (parts.length > 0) {
        const last = parts[parts.length - 1];
        return last.length > 40 ? `${last.slice(0, 37)}…` : last;
    }
    return trimmed.length > 40 ? `${trimmed.slice(0, 37)}…` : trimmed;
}

function intelFormatHourRange(startHour, endHour) {
    const fmt = (h) => {
        const d = new Date();
        d.setHours(h, 0, 0, 0);
        return d.toLocaleTimeString('en-GB', { hour: 'numeric', minute: '2-digit', hour12: true });
    };
    return `${fmt(startHour)} – ${fmt(endHour)}`;
}

/** Safe text for innerHTML (addresses, zone names). */
function intelEscapeHtml(str) {
    if (str == null) return '';
    return String(str)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;');
}

function intelBarInner(widthPct, barColorClass) {
    const w = Math.min(100, Math.max(0, widthPct));
    return `<div class="h-2 w-full rounded-full bg-gray-700 overflow-hidden"><div class="h-full rounded-full transition-all duration-500 ${barColorClass}" style="width:${w}%"></div></div>`;
}

function setIntelHtml(containerId, html) {
    const el = document.getElementById(containerId);
    if (el) el.innerHTML = html;
}

function intelEmptyCard(iconClass, title, body) {
    return `<div class="flex flex-col items-center justify-center flex-grow text-center py-2 gap-2">
        <div class="h-12 w-12 rounded-full bg-gray-800 flex items-center justify-center text-gray-500"><i class="${iconClass} text-xl"></i></div>
        <p class="text-xs font-semibold text-gray-400 uppercase tracking-wide">${intelEscapeHtml(title)}</p>
        <p class="text-sm text-gray-400 leading-relaxed max-w-[220px]">${intelEscapeHtml(body)}</p>
    </div>`;
}

const OPENAI_INTEL_PROXY_ENDPOINT = 'api/openai-intel.php';
const intelAiState = {
    inFlight: false,
    lastSignature: '',
    lastResult: null,
    lastRequestMs: 0,
    minIntervalMs: 45000
};

function renderIntelLoadingState() {
    const loadingHtml = `<div class="flex items-center gap-2 py-2 text-sm text-gray-400"><i class="fa-solid fa-circle-notch fa-spin text-indigo-400"></i><span>Loading AI insights…</span></div>`;
    setIntelHtml('intel-revenue-content', loadingHtml);
    setIntelHtml('intel-timing-content', loadingHtml);
    setIntelHtml('intel-geo-content', loadingHtml);
    setIntelHtml('intel-retention-content', loadingHtml);
}

function toPct(current, previous) {
    if (!previous) return null;
    return Math.round(((current - previous) / previous) * 100);
}

function renderIntelFromAi(ai, metrics) {
    const revenuePct = toPct(metrics.thisWeekRevenue, metrics.prevWeekRevenue);
    const revenueBadge = revenuePct == null
        ? 'No baseline'
        : `${revenuePct > 0 ? '+' : ''}${revenuePct}% vs last week`;
    const revenueHtml = `<div class="flex flex-col gap-2">
        <div class="flex items-end gap-2 flex-wrap">
            <p class="text-2xl font-bold text-gray-100 tabular-nums">${metrics.thisWeekRevenue.toLocaleString()} ৳</p>
            <span class="text-xs font-medium text-gray-400">${revenueBadge}</span>
        </div>
        <p class="text-xs text-gray-400 leading-relaxed">${intelEscapeHtml(ai.revenueInsight || 'No revenue insight generated.')}</p>
    </div>`;

    const timingHtml = `<div class="flex flex-col gap-2">
        <div class="flex items-baseline gap-2 flex-wrap">
            <p class="text-xl font-bold text-gray-100">${intelEscapeHtml(metrics.peakRangeLabel || 'N/A')}</p>
            <span class="text-[10px] font-bold uppercase tracking-wider text-amber-300 bg-amber-950/50 px-1.5 py-0.5 rounded border border-amber-800/60">Peak window</span>
        </div>
        <p class="text-xs text-gray-400 leading-relaxed">${intelEscapeHtml(ai.timingInsight || 'No timing insight generated.')}</p>
    </div>`;

    const geoHtml = `<div class="flex flex-col gap-2">
        <p class="text-xs text-gray-400 uppercase tracking-wider font-semibold">Top delivery zone: <span class="text-sky-300 font-semibold normal-case">${intelEscapeHtml(metrics.topZone || 'N/A')}</span></p>
        <p class="text-xs text-gray-400 leading-relaxed">${intelEscapeHtml(ai.geoInsight || 'No geographic insight generated.')}</p>
    </div>`;

    const retentionHtml = `<div class="flex flex-col gap-2">
        <div class="flex items-end gap-2 flex-wrap">
            <p class="text-2xl font-bold text-gray-100 tabular-nums">${metrics.repeatRate}%</p>
            <span class="text-xs font-medium text-gray-400">repeat rate</span>
        </div>
        <p class="text-xs text-gray-400 leading-relaxed">${intelEscapeHtml(ai.retentionInsight || 'No retention insight generated.')}</p>
    </div>`;

    setIntelHtml('intel-revenue-content', revenueHtml);
    setIntelHtml('intel-timing-content', timingHtml);
    setIntelHtml('intel-geo-content', geoHtml);
    setIntelHtml('intel-retention-content', retentionHtml);
}

async function requestOpenAiInsights(metrics) {
    const res = await fetch(OPENAI_INTEL_PROXY_ENDPOINT, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ metrics })
    });

    if (!res.ok) {
        const text = await res.text().catch(() => '');
        throw new Error(`AI proxy request failed (${res.status}): ${text.slice(0, 180)}`);
    }

    const data = await res.json();
    if (data?.status !== 'success' || !data?.insights) throw new Error('AI proxy returned invalid payload.');
    return data.insights;
}

/**
 * Populate homepage AI Business Intelligence cards from completed orders.
 * @param {Record<string, any>|null|undefined} ordersData
 */
export function generateBusinessIntelligence(ordersData) {
    const orders = Object.values(ordersData || {}).filter(
        (o) => o && o.status !== 'Canceled' && o.status !== 'Refunded'
    );

    if (orders.length === 0) {
        const msg = 'Complete a few sales to unlock forecasts, ad windows, delivery hotspots, and loyalty signals.';
        setIntelHtml('intel-revenue-content', intelEmptyCard('fa-solid fa-chart-line', 'Awaiting data', msg));
        setIntelHtml('intel-timing-content', intelEmptyCard('fa-solid fa-clock', 'Awaiting data', msg));
        setIntelHtml('intel-geo-content', intelEmptyCard('fa-solid fa-map-location-dot', 'Awaiting data', msg));
        setIntelHtml('intel-retention-content', intelEmptyCard('fa-solid fa-crown', 'Awaiting data', msg));
        intelAiState.lastResult = null;
        intelAiState.lastSignature = '';
        return;
    }

    const now = Date.now();
    const MS_DAY = 86400000;
    const week1Start = now - 7 * MS_DAY;
    const week2Start = now - 14 * MS_DAY;
    const week2End = week1Start;

    let sumThisWeek = 0;
    let sumPrevWeek = 0;
    let countThisWeek = 0;
    let countPrevWeek = 0;
    const windowCounts = new Array(8).fill(0);
    const zoneCounts = {};
    const phoneOrderCounts = {};

    for (const order of orders) {
        const t = orderTimestampMs(order);
        const total = parseFloat(order.totalBill || 0) || 0;

        if (t != null) {
            if (t >= week1Start && t <= now) {
                sumThisWeek += total;
                countThisWeek++;
            } else if (t >= week2Start && t < week2End) {
                sumPrevWeek += total;
                countPrevWeek++;
            }
            const hour = new Date(t).getHours();
            const slot = Math.min(7, Math.floor(hour / 3));
            windowCounts[slot]++;
        }

        const zone = intelMatchDeliveryZone(order.customerInfo?.address);
        if (zone) zoneCounts[zone] = (zoneCounts[zone] || 0) + 1;

        const digits = String(order.customerInfo?.phone || '').replace(/\D/g, '');
        if (digits.length >= 7) phoneOrderCounts[digits] = (phoneOrderCounts[digits] || 0) + 1;
    }

    let bestIdx = 0;
    for (let i = 1; i < windowCounts.length; i++) if (windowCounts[i] > windowCounts[bestIdx]) bestIdx = i;
    const startH = bestIdx * 3;
    const endH = startH + 3;
    const zoneEntries = Object.entries(zoneCounts).sort((a, b) => b[1] - a[1]);
    const topZone = zoneEntries[0]?.[0] || '';
    const repeatPhones = Object.values(phoneOrderCounts).filter((c) => c > 1).length;
    const uniquePhones = Object.keys(phoneOrderCounts).length;
    const repeatRate = uniquePhones ? Math.round((repeatPhones / uniquePhones) * 100) : 0;

    const metrics = {
        totalOrders: orders.length,
        thisWeekRevenue: Math.round(sumThisWeek),
        prevWeekRevenue: Math.round(sumPrevWeek),
        thisWeekOrders: countThisWeek,
        prevWeekOrders: countPrevWeek,
        peakRangeLabel: intelFormatHourRange(startH, endH),
        peakRangeOrders: windowCounts[bestIdx],
        topZone,
        topZoneOrders: zoneEntries[0]?.[1] || 0,
        repeatBuyers: repeatPhones,
        uniqueBuyers: uniquePhones,
        repeatRate
    };

    const signature = JSON.stringify(metrics);
    if (signature === intelAiState.lastSignature && intelAiState.lastResult) {
        renderIntelFromAi(intelAiState.lastResult, metrics);
        return;
    }

    const nowMs = Date.now();
    if (
        intelAiState.lastResult &&
        nowMs - intelAiState.lastRequestMs < intelAiState.minIntervalMs
    ) {
        renderIntelFromAi(intelAiState.lastResult, metrics);
        return;
    }

    if (intelAiState.inFlight) return;
    intelAiState.inFlight = true;
    intelAiState.lastSignature = signature;
    intelAiState.lastRequestMs = nowMs;
    renderIntelLoadingState();

    requestOpenAiInsights(metrics)
        .then((ai) => {
            intelAiState.lastResult = {
                revenueInsight: String(ai?.revenueInsight || ''),
                timingInsight: String(ai?.timingInsight || ''),
                geoInsight: String(ai?.geoInsight || ''),
                retentionInsight: String(ai?.retentionInsight || '')
            };
            renderIntelFromAi(intelAiState.lastResult, metrics);
        })
        .catch((error) => {
            console.error('AI insight generation failed:', error);
            const fallback = {
                revenueInsight: 'Could not fetch AI response right now. Check API key, quota, or network and retry.',
                timingInsight: `Current peak window appears to be ${metrics.peakRangeLabel}. Schedule budget concentration around this slot.`,
                geoInsight: metrics.topZone
                    ? `Most orders currently cluster around ${metrics.topZone}. Localize creatives and delivery promise for that area.`
                    : 'No reliable geographic concentration found yet. Improve address quality in checkout.',
                retentionInsight: `Repeat rate is ${metrics.repeatRate}%. Launch a 24-hour post-delivery follow-up with a small repeat-order incentive.`
            };
            intelAiState.lastResult = fallback;
            renderIntelFromAi(fallback, metrics);
        })
        .finally(() => {
            intelAiState.inFlight = false;
        });
}

// --- 5. CHART RENDERING ---

export function setupChartTheme() {
    try {
        if (typeof Chart !== 'undefined') {
            Chart.defaults.color = '#9CA3AF';
            Chart.defaults.borderColor = '#374151';
            Chart.defaults.font.family = "'Inter', sans-serif";
            Chart.defaults.font.size = 11;
            Chart.defaults.plugins.tooltip.backgroundColor = '#111827';
            Chart.defaults.plugins.tooltip.titleColor = '#F9FAFB';
            Chart.defaults.plugins.tooltip.bodyColor = '#D1D5DB';
            Chart.defaults.plugins.tooltip.borderColor = '#4B5563';
            Chart.defaults.plugins.tooltip.borderWidth = 1;
        }
    } catch (e) { console.warn("Chart.js not loaded yet."); }
}

export function renderSparkline(canvasId, dataPoints, labels, color) {
    const ctx = document.getElementById(canvasId);
    if (!ctx || typeof Chart === 'undefined') return;
    const existing = charts[canvasId];
    if (existing) {
        existing.data.labels = labels;
        existing.data.datasets[0].data = dataPoints;
        existing.data.datasets[0].borderColor = color;
        existing.data.datasets[0].pointBackgroundColor = color;
        existing.update('none');
        return;
    }

    try {
        charts[canvasId] = new Chart(ctx, {
            type: 'line',
            data: {
                labels: labels,
                datasets: [{
                    data: dataPoints,
                    borderColor: color,
                    borderWidth: 2,
                    backgroundColor: (context) => {
                        const chart = context.chart;
                        const { ctx: c2, chartArea } = chart;
                        if (!chartArea || chartArea.bottom <= chartArea.top) {
                            return color + '28';
                        }
                        const gradient = c2.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
                        gradient.addColorStop(0, color + '40');
                        gradient.addColorStop(1, color + '00');
                        return gradient;
                    },
                    fill: true,
                    pointRadius: (ctx) => {
                        const len = ctx.dataset.data?.length ?? 0;
                        return len > 0 && ctx.dataIndex === len - 1 ? 3.5 : 0;
                    },
                    pointHoverRadius: 5,
                    pointBackgroundColor: color,
                    pointBorderColor: '#111111',
                    pointBorderWidth: 2,
                    tension: 0.35
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: { legend: { display: false }, tooltip: { intersect: false } },
                scales: { x: { display: false }, y: { display: false } }
            }
        });
    } catch(e) { console.error("Chart Error:", canvasId, e); }
}

export function renderCategoryChart(catData) {
    const ctx = document.getElementById('categoryChart');
    if (!ctx || typeof Chart === 'undefined') return;

    const sortedCats = Object.entries(catData || {}).sort((a, b) => b[1] - a[1]);
    const labels = sortedCats.map((c) => c[0]).slice(0, 4);
    const data = sortedCats.map((c) => c[1]).slice(0, 4);
    const palette = ['#4F46E5', '#0D9488', '#D97706', '#7C3AED'];

    const totalSum = data.reduce((s, v) => s + (Number(v) || 0), 0);
    const hasRealData = labels.length > 0 && totalSum > 0;

    const topNameEl = document.getElementById('top-cat-value');
    const topAmtEl = document.getElementById('top-cat-amount');
    const topPctEl = document.getElementById('top-cat-percent');
    if (hasRealData) {
        const topVal = Number(sortedCats[0][1]) || 0;
        const topPct = totalSum > 0 ? (topVal / totalSum) * 100 : 0;
        if (topNameEl) topNameEl.textContent = String(sortedCats[0][0] || '—');
        if (topAmtEl) topAmtEl.textContent = topVal.toLocaleString('en-US', { maximumFractionDigits: 0 }) + ' ৳';
        if (topPctEl) topPctEl.textContent = topPct.toFixed(1) + '%';
    } else {
        if (topNameEl) topNameEl.textContent = '—';
        if (topAmtEl) topAmtEl.textContent = '—';
        if (topPctEl) topPctEl.textContent = '—';
    }

    const displayLabels = hasRealData ? labels : [''];
    const displayData = hasRealData ? data : [1];
    const displayColors = hasRealData ? palette.slice(0, labels.length) : ['#4B5563'];

    try {
        if (!charts.categoryDonut) {
            charts.categoryDonut = new Chart(ctx, {
                type: 'doughnut',
                data: {
                    labels: displayLabels,
                    datasets: [{
                        data: displayData,
                        backgroundColor: displayColors,
                        borderColor: '#111111',
                        borderWidth: hasRealData ? 2 : 0,
                        hoverOffset: hasRealData ? 5 : 0
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    cutout: '68%',
                    animation: { duration: 0 },
                    plugins: {
                        legend: { display: false },
                        tooltip: {
                            enabled: hasRealData,
                            callbacks: {
                                label: (item) => {
                                    const v = Number(item.raw) || 0;
                                    const pct = totalSum > 0 ? ((v / totalSum) * 100).toFixed(1) : '0.0';
                                    return ` ${item.label}: ${v.toLocaleString('en-US', { maximumFractionDigits: 0 })} ৳ (${pct}%)`;
                                }
                            }
                        }
                    }
                }
            });
        } else {
            const ds = charts.categoryDonut.data.datasets[0];
            charts.categoryDonut.data.labels = displayLabels;
            ds.data = displayData;
            ds.backgroundColor = displayColors;
            ds.borderWidth = hasRealData ? 2 : 0;
            ds.borderColor = '#111111';
            ds.hoverOffset = hasRealData ? 5 : 0;
            if (charts.categoryDonut.options.plugins?.tooltip) {
                charts.categoryDonut.options.plugins.tooltip.enabled = hasRealData;
            }
            charts.categoryDonut.update('none');
        }
    } catch (e) {
        console.error('Category Chart Error', e);
    }

    const legendContainer = document.getElementById('category-legend');
    if (legendContainer) {
        if (!hasRealData) {
            legendContainer.innerHTML =
                '<p class="text-sm text-gray-400 text-center py-6 px-2 leading-relaxed">No category revenue yet. When orders include product lines, the split appears here.</p>';
        } else {
            legendContainer.innerHTML = labels
                .map((label, i) => {
                    const v = Number(data[i]) || 0;
                    const pct = totalSum > 0 ? ((v / totalSum) * 100).toFixed(1) : '0.0';
                    const col = palette[i];
                    return `
            <div class="flex items-center justify-between gap-2 rounded-lg border border-gray-800 bg-gray-900/80 px-3 py-2.5" role="listitem">
                <div class="flex items-center gap-2 min-w-0">
                    <span class="h-2.5 w-2.5 rounded-full shrink-0 ring-2 ring-gray-800 shadow-sm" style="background-color: ${col}"></span>
                    <span class="text-xs font-medium text-gray-100 truncate">${String(label)}</span>
                </div>
                <div class="flex items-center gap-2 shrink-0 text-xs tabular-nums">
                    <span class="text-gray-400">${v.toLocaleString('en-US', { maximumFractionDigits: 0 })} ৳</span>
                    <span class="font-semibold text-gray-100">${pct}%</span>
                </div>
            </div>`;
                })
                .join('');
        }
    }
}

function compactAxisNumber(value) {
    const n = Number(value);
    if (!Number.isFinite(n)) return '';
    const a = Math.abs(n);
    if (a >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
    if (a >= 1000) return (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
    return String(Math.round(n));
}

export function renderRevenueChart(revenueData, orderData, labels) {
    const ctx = document.getElementById('revenueChart');
    if (!ctx || typeof Chart === 'undefined') return;

    const rev = revenueData || [];
    const ord = orderData || [];
    const lbl = labels || [];

    try {
        if (charts.revenueBar) {
            charts.revenueBar.destroy();
            charts.revenueBar = null;
        }
        charts.revenueBar = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: lbl,
                datasets: [
                    {
                        type: 'bar',
                        label: 'Revenue',
                        data: rev,
                        backgroundColor: (c) => {
                            const chart = c.chart;
                            const { ctx: c2, chartArea } = chart;
                            if (!chartArea) return 'rgba(79, 70, 229, 0.9)';
                            const g = c2.createLinearGradient(chartArea.left, 0, chartArea.right, 0);
                            g.addColorStop(0, 'rgba(79, 70, 229, 0.92)');
                            g.addColorStop(1, 'rgba(99, 102, 241, 0.75)');
                            return g;
                        },
                        borderRadius: 8,
                        borderSkipped: false,
                        barPercentage: 0.62,
                        categoryPercentage: 0.84,
                        yAxisID: 'y'
                    },
                    {
                        type: 'line',
                        label: 'Orders',
                        data: ord,
                        borderColor: '#059669',
                        backgroundColor: 'rgba(5, 150, 105, 0.08)',
                        borderWidth: 2.25,
                        tension: 0.35,
                        pointRadius: 0,
                        pointHoverRadius: 5,
                        pointBackgroundColor: '#059669',
                        pointBorderColor: '#111111',
                        pointBorderWidth: 2,
                        fill: false,
                        yAxisID: 'y1'
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                animation: { duration: 0 },
                interaction: { mode: 'index', intersect: false },
                plugins: {
                    legend: { display: false },
                    tooltip: {
                        mode: 'index',
                        intersect: false,
                        callbacks: {
                            label: (item) => {
                                if (item.datasetIndex === 0) {
                                    const v = item.parsed.y;
                                    return ` Revenue: ${Number(v).toLocaleString('en-US', { maximumFractionDigits: 0 })} ৳`;
                                }
                                return ` Orders: ${item.parsed.y}`;
                            }
                        }
                    }
                },
                scales: {
                    y: {
                        position: 'left',
                        grid: { color: '#374151', borderDash: [2, 4] },
                        border: { display: false },
                        ticks: {
                            color: '#9CA3AF',
                            font: { size: 11 },
                            maxTicksLimit: 6,
                            callback: (v) => compactAxisNumber(v)
                        }
                    },
                    y1: {
                        position: 'right',
                        grid: { drawOnChartArea: false },
                        border: { display: false },
                        ticks: {
                            color: '#059669',
                            font: { size: 11 },
                            precision: 0,
                            maxTicksLimit: 6,
                            callback: (v) => (Number.isInteger(v) ? v : Math.round(Number(v)))
                        }
                    },
                    x: {
                        grid: { display: false },
                        border: { display: false },
                        ticks: {
                            color: '#9CA3AF',
                            font: { size: 11 },
                            maxRotation: 45,
                            minRotation: 0,
                            autoSkip: true,
                            maxTicksLimit: 14
                        }
                    }
                }
            }
        });
    } catch (e) {
        console.error('Revenue Chart Error', e);
    }
}

export function renderEarningsGauge(income, sales) {
    const ctx = document.getElementById('earningsGauge');
    if (!ctx || typeof Chart === 'undefined') return;
    
    const profit = Math.round(sales * 0.3); // Dummy calculation
    const expense = sales - profit;

    const safeSetText = (id, val) => { const el = document.getElementById(id); if(el) el.textContent = val; };
    safeSetText('gauge-total-earnings', profit.toLocaleString() + ' ৳');
    safeSetText('gauge-revenue', sales.toLocaleString() + ' ৳');
    safeSetText('gauge-profit', profit.toLocaleString() + ' ৳');

    try {
        if (!charts.earningsGauge) {
            charts.earningsGauge = new Chart(ctx, {
            type: 'doughnut',
            data: {
                labels: ['Profit', 'Expense'],
                datasets: [{
                    data: [profit, expense],
                    backgroundColor: ['#10B981', '#27272a'],
                    borderColor: '#111111',
                    borderWidth: 2,
                    circumference: 180,
                    rotation: -90,
                    cutout: '76%'
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                animation: false,
                plugins: { legend: { display: false }, tooltip: { enabled: false } }
            }
            });
        } else {
            const ds = charts.earningsGauge.data.datasets[0];
            ds.data = [profit, expense];
            ds.borderColor = '#111111';
            ds.borderWidth = 2;
            charts.earningsGauge.update('none');
        }
    } catch(e) { console.error("Gauge Error", e); }
}

export function renderVisitorBarChart(dataPoints, labels) {
    const ctx = document.getElementById('visitorsBarChart');
    if (!ctx || typeof Chart === 'undefined') return;

    try {
        if (!charts.visitorsBar) {
            charts.visitorsBar = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: labels,
                    datasets: [{
                        label: 'Visitors',
                        data: dataPoints,
                        backgroundColor: (c) => {
                            const chart = c.chart;
                            const { ctx: c2, chartArea } = chart;
                            if (!chartArea) return 'rgba(79, 70, 229, 0.85)';
                            const g = c2.createLinearGradient(0, chartArea.bottom, 0, chartArea.top);
                            g.addColorStop(0, 'rgba(129, 140, 248, 0.35)');
                            g.addColorStop(1, 'rgba(67, 56, 202, 0.92)');
                            return g;
                        },
                        borderRadius: 8,
                        borderSkipped: false,
                        barPercentage: 0.68,
                        categoryPercentage: 0.86
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    animation: { duration: 0 },
                    plugins: {
                        legend: { display: false },
                        tooltip: {
                            intersect: false,
                            mode: 'index',
                            callbacks: {
                                title: (items) => (items[0] ? String(items[0].label) : ''),
                                label: (item) => ` ${item.dataset.label || 'Visitors'}: ${item.parsed.y}`
                            }
                        }
                    },
                    scales: {
                        x: {
                            display: true,
                            grid: { display: false },
                            border: { display: false },
                            ticks: {
                                color: '#9CA3AF',
                                font: { size: 11 },
                                maxRotation: 0,
                                autoSkip: true,
                                maxTicksLimit: 8
                            }
                        },
                        y: {
                            display: true,
                            beginAtZero: true,
                            suggestedMax: undefined,
                            grid: { color: '#374151', borderDash: [2, 4] },
                            border: { display: false },
                            ticks: {
                                color: '#9CA3AF',
                                font: { size: 11 },
                                precision: 0,
                                maxTicksLimit: 5
                            }
                        }
                    }
                }
            });
        } else {
            charts.visitorsBar.data.labels = labels;
            charts.visitorsBar.data.datasets[0].data = dataPoints;
            charts.visitorsBar.update('none');
        }
    } catch (e) {
        console.error('Visitor Chart Error', e);
    }
}

/**
 * Download a Chart.js chart registered on a canvas element as PNG (dashboard home).
 * @param {string} canvasId
 * @param {string} [filenameBase]
 * @returns {boolean}
 */
export function exportDashboardChartPngByCanvasId(canvasId, filenameBase) {
    const canvas = document.getElementById(canvasId);
    if (!canvas || typeof Chart === 'undefined' || typeof Chart.getChart !== 'function') return false;
    const chart = Chart.getChart(canvas);
    if (!chart) return false;
    let dataUrl;
    try {
        dataUrl = chart.toBase64Image('image/png', 1);
    } catch (err) {
        console.warn('exportDashboardChartPngByCanvasId', err);
        return false;
    }
    const a = document.createElement('a');
    a.href = dataUrl;
    const safe = String(filenameBase || 'chart').replace(/[^\w\-]+/g, '-').replace(/^-|-$/g, '') || 'chart';
    a.download = `${safe}.png`;
    a.click();
    return true;
}

// --- 6. HTML GENERATORS & RENDERERS ---

/** Status chips for dashboard home (dark cards). */
const getStatusBadgeHome = (status) => {
    const st = String(status || 'Pending');
    const safe = st.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
    const map = {
        Pending: 'bg-amber-500/15 text-amber-300 border border-amber-600/40',
        Confirmed: 'bg-sky-500/15 text-sky-300 border border-sky-600/40',
        Processing: 'bg-violet-500/15 text-violet-300 border border-violet-600/40',
        Shipped: 'bg-emerald-500/15 text-emerald-300 border border-emerald-600/40',
        Delivered: 'bg-gray-800 text-gray-200 border border-gray-600',
        Canceled: 'bg-rose-500/15 text-rose-300 border border-rose-600/40',
        Refunded: 'bg-orange-500/15 text-orange-300 border border-orange-600/40'
    };
    const cls = map[st] || 'bg-gray-800 text-gray-300 border border-gray-600';
    return `<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wider ${cls}">${safe}</span>`;
};

const getStatusBadge = (status) => {
    const st = String(status || 'Pending');
    const safe = st.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
    const map = {
        Pending: 'bg-amber-50 text-amber-800 border border-amber-100',
        Confirmed: 'bg-sky-50 text-sky-800 border border-sky-100',
        Processing: 'bg-violet-50 text-violet-800 border border-violet-100',
        Shipped: 'bg-emerald-50 text-emerald-800 border border-emerald-100',
        Delivered: 'bg-slate-100 text-gray-800 border border-slate-200',
        Canceled: 'bg-rose-50 text-rose-800 border border-rose-100',
        Refunded: 'bg-orange-50 text-orange-800 border border-orange-100'
    };
    const cls = map[st] || 'bg-slate-50 text-gray-800 border border-slate-200';
    return `<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wider ${cls}">${safe}</span>`;
};

export function renderMasterDetailView({ containerId, detailContainerId, data, title, itemRenderer, detailRenderer, state }) {
    const container = document.getElementById(containerId);
    const paginationContainer = document.getElementById(containerId + '-pagination');
    if (!container) return;

    if (!data || Object.keys(data).length === 0) {
        container.innerHTML = `<div class="p-3 text-center text-black italic">No ${title.toLowerCase()} found.</div>`;
        if (paginationContainer) paginationContainer.innerHTML = '';
        return;
    }

    let items = Object.entries(data).map(([id, val]) => ({ id, ...val }));
    items.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));

    if (state.searchTerm) {
        const term = state.searchTerm.toLowerCase();
        items = items.filter(item => 
            (item.customerInfo?.name || item.name || '').toLowerCase().includes(term) ||
            (item.customerInfo?.phone || item.phone || '').includes(term) ||
            item.id.toLowerCase().includes(term)
        );
    }
    
    if (state.statusFilter && state.statusFilter !== 'All') {
        items = items.filter(item => item.status === state.statusFilter);
    }

    if (items.length === 0) {
        container.innerHTML = `<div class="p-3 text-center text-black italic">No matches found.</div>`;
        if (paginationContainer) paginationContainer.innerHTML = '';
        return;
    }

    const totalItems = items.length;
    const totalPages = Math.ceil(totalItems / state.itemsPerPage);
    const start = (state.currentPage - 1) * state.itemsPerPage;
    const paginatedItems = items.slice(start, start + state.itemsPerPage);

    container.innerHTML = paginatedItems.map(item => itemRenderer(item.id, item)).join('');

    if (paginationContainer) {
        paginationContainer.innerHTML = `
            <span class="text-xs text-black">Page ${state.currentPage} of ${totalPages || 1} (${totalItems} items)</span>
            <div class="flex gap-2">
                <button class="pagination-btn p-1.5 rounded hover:bg-gray-100 disabled:opacity-50" data-page="${state.currentPage - 1}" ${state.currentPage === 1 ? 'disabled' : ''}><i class="fa-solid fa-chevron-left"></i></button>
                <button class="pagination-btn p-1.5 rounded hover:bg-gray-100 disabled:opacity-50" data-page="${state.currentPage + 1}" ${state.currentPage >= totalPages ? 'disabled' : ''}><i class="fa-solid fa-chevron-right"></i></button>
            </div>
        `;
    }

    const detailContainer = document.getElementById(detailContainerId);
    if (detailContainer && window.innerWidth >= 1024 && paginatedItems.length > 0 && !detailContainer.innerHTML.trim()) {
        const firstId = paginatedItems[0].id;
        detailContainer.innerHTML = detailRenderer(firstId, paginatedItems[0]);
        const firstEl = container.querySelector(`[data-id="${firstId}"]`);
        if(firstEl) firstEl.classList.add('bg-indigo-50', 'border-indigo-400');
    }
}

function escapeHtmlAttr(s) {
    if (s == null) return '';
    return String(s)
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
}

function escapeHtmlText(s) {
    if (s == null) return '';
    const d = document.createElement('div');
    d.textContent = String(s);
    return d.innerHTML;
}

export function getStatusBadgeLight(status) {
    const st = status || 'Pending';
    const map = {
        Pending: 'bg-amber-50 text-amber-800 border-amber-100',
        Confirmed: 'bg-sky-50 text-sky-800 border-sky-100',
        Processing: 'bg-violet-50 text-violet-800 border-violet-100',
        Shipped: 'bg-emerald-50 text-emerald-800 border-emerald-100',
        Delivered: 'bg-slate-100 text-gray-800 border-slate-200',
        Canceled: 'bg-rose-50 text-rose-800 border-rose-100',
        Refunded: 'bg-orange-50 text-orange-800 border-orange-100'
    };
    const cls = map[st] || 'bg-slate-50 text-gray-800 border-slate-200';
    return `<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold border ${cls}">${escapeHtmlText(st)}</span>`;
}

function orderCreatedLocalYmd(ts) {
    const d = new Date(ts || 0);
    if (Number.isNaN(d.getTime())) return '';
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, '0');
    const day = String(d.getDate()).padStart(2, '0');
    return `${y}-${m}-${day}`;
}

export function renderCompletedOrdersTableView({
    tbodyId,
    paginationId,
    data,
    state
}) {
    const tbody = document.getElementById(tbodyId);
    const paginationContainer = paginationId ? document.getElementById(paginationId) : null;
    if (!tbody) return;

    if (!data || Object.keys(data).length === 0) {
        tbody.innerHTML = `<tr><td colspan="6" class="px-4 py-12 text-center text-black italic">No orders yet.</td></tr>`;
        if (paginationContainer) paginationContainer.innerHTML = '';
        return;
    }

    let items = Object.entries(data).map(([id, val]) => ({ id, ...val }));
    items.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));

    if (state.searchTerm) {
        const term = state.searchTerm.toLowerCase();
        items = items.filter(item =>
            (item.customerInfo?.name || item.name || '').toLowerCase().includes(term) ||
            (item.customerInfo?.phone || item.phone || '').includes(term) ||
            item.id.toLowerCase().includes(term)
        );
    }

    if (state.statusFilter && state.statusFilter !== 'All') {
        items = items.filter(item => item.status === state.statusFilter);
    }

    if (state.dateYmd && String(state.dateYmd).trim()) {
        const want = String(state.dateYmd).trim();
        items = items.filter(item => orderCreatedLocalYmd(item.createdAt) === want);
    }

    if (items.length === 0) {
        tbody.innerHTML = `<tr><td colspan="6" class="px-4 py-12 text-center text-black italic">No matching orders.</td></tr>`;
        if (paginationContainer) paginationContainer.innerHTML = '';
        return;
    }

    const duplicateOrderIds = computeDuplicateOrderIdsSet(data);

    const totalItems = items.length;
    const perPage = Math.max(1, parseInt(state.itemsPerPage, 10) || 20);
    const totalPages = Math.ceil(totalItems / perPage);
    const page = Math.min(Math.max(1, state.currentPage || 1), totalPages);
    state.currentPage = page;
    const start = (page - 1) * perPage;
    const paginatedItems = items.slice(start, start + perPage);

    const rows = paginatedItems.map((item) => {
        const id = item.id;
        const name = item.customerInfo?.name || 'Guest';
        const phone = item.customerInfo?.phone || '—';
        const doubleOrderBadge = duplicateOrderIds.has(id)
            ? `<span class="inline-flex items-center bg-red-100 text-red-800 text-[10px] font-bold px-2 py-0.5 rounded ml-2 border border-red-200" title="একই নম্বর থেকে ২ দিনের মধ্যে আরও অর্ডার">Double Order</span>`
            : '';
        const status = item.status || 'Pending';
        const total = item.totalBill ?? 0;
        const dt = new Date(item.createdAt || 0);
        const dateStr = Number.isNaN(dt.getTime()) ? '—' : dt.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' });
        let rawItems = [];
        if (item.items) {
            rawItems = Array.isArray(item.items) ? item.items : Object.values(item.items);
        }
        const productText = rawItems.length
            ? rawItems.slice(0, 2).map((i) => i.name || 'Item').join(', ') + (rawItems.length > 2 ? ` +${rawItems.length - 2}` : '')
            : '—';
        const rawMetaIp = item.meta && item.meta.ip;
        const metaIp =
            rawMetaIp && String(rawMetaIp).trim() && String(rawMetaIp) !== 'Unknown' ? String(rawMetaIp).trim() : '';
        const blockControl = metaIp
            ? `<button type="button" data-action="blockCustomerIp" data-id="${escapeHtmlAttr(id)}" data-ip="${escapeHtmlAttr(metaIp)}" class="inline-flex items-center justify-center w-9 h-9 rounded-lg border border-red-200 bg-white text-red-600 hover:bg-red-50 shrink-0" title="Block this IP" aria-label="Block this IP"><i class="fa-solid fa-ban"></i></button>`
            : `<span class="inline-flex items-center gap-1 shrink-0 cursor-default" title="IP not captured"><span class="inline-flex items-center justify-center w-9 h-9 text-black" aria-hidden="true"><i class="fa-solid fa-ban"></i></span><span class="text-xs text-black whitespace-nowrap">No IP</span></span>`;
        return `
<tr class="order-master-row border-b border-gray-100 hover:bg-blue-50/50 cursor-pointer transition" data-id="${escapeHtmlAttr(id)}" data-action="viewOrderDetail">
  <td class="px-4 py-2 whitespace-nowrap text-black">${escapeHtmlText(dateStr)}</td>
  <td class="px-4 py-2">
    <p class="font-semibold text-black flex flex-wrap items-center gap-x-1">${escapeHtmlText(name)}${doubleOrderBadge}</p>
    <p class="text-xs text-black">${escapeHtmlText(phone)}</p>
  </td>
  <td class="px-4 py-2 text-black max-w-xs truncate" title="${escapeHtmlAttr(productText)}">${escapeHtmlText(productText)}</td>
  <td class="px-4 py-2 text-right font-semibold text-black whitespace-nowrap">${escapeHtmlText(String(total))} ৳</td>
  <td class="px-4 py-2">${getStatusBadgeLight(status)}</td>
  <td class="px-4 py-2 text-right">
    <div class="inline-flex items-center justify-end gap-1.5">
      <span class="inline-flex items-center justify-center w-9 h-9 rounded-lg border border-gray-200 bg-white text-blue-600 pointer-events-none" title="Open order"><i class="fa-solid fa-eye"></i></span>
      ${blockControl}
    </div>
  </td>
</tr>`;
    });

    tbody.innerHTML = '';
    const chunkSize = 12;
    let rowIndex = 0;
    const renderChunk = () => {
        const next = rows.slice(rowIndex, rowIndex + chunkSize).join('');
        tbody.insertAdjacentHTML('beforeend', next);
        rowIndex += chunkSize;
        if (rowIndex < rows.length) requestAnimationFrame(renderChunk);
    };
    renderChunk();

    if (paginationContainer) {
        paginationContainer.innerHTML = `
      <div class="flex items-center gap-2 text-sm text-black">
        <span>Rows per page:</span>
        <strong>${perPage}</strong>
        <span class="text-black">·</span>
        <span>Page ${page} of ${totalPages} (${totalItems} orders)</span>
      </div>
      <div class="flex gap-2">
        <button type="button" class="pagination-btn p-2 rounded-lg border border-gray-200 bg-white hover:bg-gray-50 disabled:opacity-40" data-page="${page - 1}" ${page <= 1 ? 'disabled' : ''}><i class="fa-solid fa-chevron-left"></i></button>
        <button type="button" class="pagination-btn p-2 rounded-lg border border-gray-200 bg-white hover:bg-gray-50 disabled:opacity-40" data-page="${page + 1}" ${page >= totalPages ? 'disabled' : ''}><i class="fa-solid fa-chevron-right"></i></button>
      </div>`;
    }
}

function parseBrowserNameFromUserAgent(ua) {
    if (!ua || typeof ua !== 'string') return '';
    const s = ua.slice(0, 600);
    if (/Edg\//.test(s)) return 'Microsoft Edge';
    if (/OPR\/|Opera\//i.test(s)) return 'Opera';
    if (/Chrome\//.test(s)) return 'Google Chrome';
    if (/Firefox\//i.test(s)) return 'Mozilla Firefox';
    if (/SamsungBrowser\//i.test(s)) return 'Samsung Internet';
    if (/Safari\//.test(s) && !/Chrome|Chromium|Edg/i.test(s)) return 'Safari';
    return 'Web browser';
}

function formatMetaLocation(meta) {
    const loc = meta.location;
    if (loc && String(loc).trim()) return String(loc).trim();
    const parts = [meta.city, meta.region, meta.country].filter(
        (p) => p && String(p).trim() && String(p) !== 'Unknown'
    );
    if (parts.length) return parts.join(', ');
    const ip = meta.ip;
    if (ip && ip !== 'Unknown') {
        const isp = meta.isp && meta.isp !== 'Unknown' ? String(meta.isp) : '';
        return isp ? `${ip} (${isp})` : `IP: ${ip}`;
    }
    return 'Location not available';
}

function friendlyOsFromMeta(meta) {
    const ua = meta.userAgent || '';
    const os = meta.os;
    if (/Windows NT 10\.0|Windows NT 11\.0/i.test(ua)) return 'Windows 10/11';
    if (/Windows NT/i.test(ua)) return 'Windows';
    if (os && /^Win32|Win64$/i.test(String(os))) return 'Windows';
    if (os && /^MacIntel$/i.test(String(os))) return 'macOS';
    if (/iPhone|iPod/i.test(ua)) return 'iOS';
    if (/iPad/i.test(ua)) return 'iPadOS';
    if (/Android/i.test(ua)) return 'Android';
    if (os && String(os).trim() && String(os) !== 'Unknown') return String(os);
    return 'Unknown OS';
}

function friendlyDeviceFromMeta(meta) {
    const m = meta.deviceModel;
    if (m && m !== 'Generic Device' && m !== 'Unknown' && String(m).trim()) return String(m);
    const ua = meta.userAgent || '';
    if (/iPad/i.test(ua)) return 'iPad';
    if (/iPhone/i.test(ua)) return 'iPhone';
    if (/Android/i.test(ua)) return 'Android device';
    if (meta.deviceType === 'Mobile' || /Mobile|webOS|BlackBerry|IEMobile/i.test(ua)) return 'Mobile device';
    return 'Desktop';
}

function metaOsRowIconClass(meta) {
    const ua = (meta.userAgent || '').toLowerCase();
    const os = String(meta.os || '').toLowerCase();
    if (/iphone|ipad|ipod|macintosh|mac os x|macintel/.test(ua) || os.includes('mac')) return 'fa-brands fa-apple';
    if (/android/.test(ua)) return 'fa-brands fa-android';
    if (/windows|win32|win64/.test(ua) || os.startsWith('win')) return 'fa-brands fa-windows';
    if (/linux|x11/.test(ua)) return 'fa-brands fa-linux';
    return 'fa-solid fa-desktop';
}

function metaDeviceRowIconClass(meta) {
    const ua = meta.userAgent || '';
    const mobile = meta.deviceType === 'Mobile' || /Android|iPhone|iPad|iPod|Mobile|webOS|BlackBerry|IEMobile/i.test(ua);
    return mobile ? 'fa-solid fa-mobile-screen' : 'fa-solid fa-desktop';
}

function metaBrowserRowIconClass(label) {
    const b = (label || '').toLowerCase();
    if (b.includes('edge')) return 'fa-brands fa-edge';
    if (b.includes('firefox')) return 'fa-brands fa-firefox-browser';
    if (b.includes('safari') && !b.includes('chrome')) return 'fa-brands fa-safari';
    if (b.includes('opera')) return 'fa-brands fa-opera';
    if (b.includes('chrome')) return 'fa-brands fa-chrome';
    if (b.includes('samsung')) return 'fa-brands fa-chrome';
    return 'fa-solid fa-globe';
}

export function renderUserInfoSection(meta) {
    try {
        if (!meta || typeof meta !== 'object') {
            return `<div class="mt-4 p-2 bg-gray-50 rounded-lg border border-gray-200 text-xs text-black italic text-center">No technical info available.</div>`;
        }
        const device = friendlyDeviceFromMeta(meta);
        const os = friendlyOsFromMeta(meta);
        const location = formatMetaLocation(meta);
        const ipRaw = meta.ip;
        const ip = ipRaw && ipRaw !== 'Unknown' ? String(ipRaw) : 'Not captured';
        let browser = meta.browser && String(meta.browser).trim() ? String(meta.browser).trim() : '';
        if (!browser) browser = parseBrowserNameFromUserAgent(meta.userAgent || '');
        if (!browser) browser = 'Unknown browser';

        const deviceIcon = metaDeviceRowIconClass(meta);
        const osIcon = metaOsRowIconClass(meta);
        const browserIcon = metaBrowserRowIconClass(browser);

        return `
            <div class="mt-4 p-2 bg-slate-50 rounded-lg border border-slate-200">
                <h4 class="font-bold text-xs text-black uppercase mb-2 border-b border-slate-200 pb-1 flex items-center gap-2">
                    <i class="fa-solid fa-fingerprint"></i> Tech & Location Info
                </h4>
                <div class="grid grid-cols-1 sm:grid-cols-2 gap-y-2 gap-x-4 text-xs text-black">
                    <div class="flex items-center gap-2"><i class="${deviceIcon} text-black w-4"></i><span class="font-medium">${escapeHtmlText(device)}</span></div>
                    <div class="flex items-center gap-2"><i class="${osIcon} text-black w-4"></i><span>${escapeHtmlText(os)}</span></div>
                     <div class="flex items-center gap-2"><i class="fa-solid fa-location-dot text-black w-4"></i><span>${escapeHtmlText(location)}</span></div>
                    <div class="flex items-center gap-2"><i class="fa-solid fa-wifi text-black w-4"></i><span>${escapeHtmlText(ip)}</span></div>
                    <div class="flex items-center gap-2 sm:col-span-2"><i class="${browserIcon} text-black w-4"></i><span class="truncate">${escapeHtmlText(browser)}</span></div>
                </div>
            </div>
        `;
    } catch (e) {
        return `<div class="mt-4 text-xs text-red-400">Error info</div>`;
    }
}

function formatOrderPaymentSummary(order) {
    const p = order.payment;
    if (!p) return 'N/A';
    if (Array.isArray(p.breakdown) && p.breakdown.length > 0) {
        return p.breakdown.map((b) => `${escapeHtmlText(String(b.method || ''))}: ${Number(b.amount) || 0}৳`).join(' · ');
    }
    return escapeHtmlText(String(p.method != null ? p.method : 'N/A'));
}

function buildDuplicateOrderDetailAlertHTML(id, order, allOrdersData) {
    const related = getRelatedDuplicateOrdersForDetail(id, order, allOrdersData);
    if (!related.length) return '';
    const phoneDisplay = escapeHtmlText(String(order.customerInfo?.phone || ''));
    const lines = related.map((r) => {
        const dt = new Date(r.createdAtMs);
        const ds = Number.isNaN(dt.getTime())
            ? '—'
            : dt.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' });
        const enc = encodeURIComponent(r.id);
        const tail = escapeHtmlText(String(r.id).slice(-6));
        return `<li class="flex flex-wrap items-center justify-between gap-2 py-1.5 border-b border-red-100 last:border-0">
      <a href="#order-detail/${enc}" class="text-indigo-600 font-semibold hover:underline text-sm">Order …${tail}</a>
      <span class="text-xs text-black">${escapeHtmlText(ds)}</span>
    </li>`;
    }).join('');
    return `
    <div class="mb-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-900" role="alert">
      <p class="font-bold mb-1"><i class="fa-solid fa-triangle-exclamation mr-2"></i>সতর্কতা: সম্ভাব্য ডাবল অর্ডার</p>
      <p class="mb-2">এই কাস্টমার (${phoneDisplay}) এর একই নম্বর থেকে ২ দিনের মধ্যে আরও <strong>${related.length}</strong> টি সম্পর্কিত অর্ডার পাওয়া গেছে। ডাবল অর্ডার কিনা যাচাই করুন।</p>
      <ul class="list-none m-0 p-0">${lines}</ul>
    </div>`;
}

export function generateCompletedOrderDetailHTML(id, order, clientId, allOrdersData = null) {
    if (!order) return '<div class="p-3 text-red-500">Order data not found.</div>';
    const statuses = ['Pending', 'Confirmed', 'Processing', 'Shipped', 'Delivered', 'Refunded', 'Canceled'];
    const statusOptions = statuses.map(s => `<option value="${s}" ${s === (order.status || 'Pending') ? 'selected' : ''}>${s}</option>`).join('');
    
    const fbBadge = order.fb_event_sent 
        ? `<span class="text-xs text-blue-600 font-bold flex items-center gap-1"><i class="fa-brands fa-facebook"></i> Sent</span>` 
        : `<span class="text-xs text-black flex items-center gap-1"><i class="fa-brands fa-facebook"></i> Not Sent</span>`;

    const orderLockedForCourier = (order.status === 'Shipped' || order.status === 'Delivered' || order.status === 'Canceled' || order.status === 'Refunded');
    const courierInfo = order.courierInfo || (order.steadfastInfo ? {
        provider: 'steadfast',
        consignmentId: order.steadfastInfo.consignment_id || '',
        trackingCode: order.steadfastInfo.tracking_code || '',
        status: order.steadfastInfo.status || ''
    } : null);
    const courierLabel = courierInfo?.provider ? courierInfo.provider.toUpperCase() : 'N/A';
    const sentAtText = courierInfo?.createdAt ? new Date(courierInfo.createdAt).toLocaleString() : 'N/A';
    const courierActionBlock = orderLockedForCourier
        ? `<div class="rounded-lg border border-gray-200 bg-gray-50 p-2 text-sm">
                <p><strong>Courier:</strong> ${escapeHtmlText(courierLabel)}</p>
                <p><strong>Tracking:</strong> ${escapeHtmlText(courierInfo?.trackingCode || 'N/A')}</p>
                <p><strong>Consignment:</strong> ${escapeHtmlText(courierInfo?.consignmentId || 'N/A')}</p>
           </div>`
        : `<div class="rounded-lg border border-red-100 bg-red-50/30 p-2.5" data-courier-send-card data-id="${id}">
                <p class="text-[11px] font-bold uppercase tracking-wide text-red-700 mb-2">Courier Entry</p>
                <div class="flex flex-col sm:flex-row gap-2">
                    <select data-courier-select data-id="${id}" class="admin-form-input text-sm py-2">
                        <option value="steadfast">Steadfast</option>
                        <option value="pathao">Pathao</option>
                        <option value="redx">RedX</option>
                    </select>
                    <button type="button" data-action="sendToCourier" data-id="${id}" class="bg-red-600 text-white font-semibold py-2 px-4 rounded-lg text-sm hover:bg-red-700 sm:w-auto w-full">
                        <i class="fa-solid fa-truck-fast mr-2"></i>Send to Courier
                    </button>
                </div>
                <div data-courier-status class="mt-2 text-[11px] text-black bg-white border border-slate-200 rounded-md px-2 py-1.5">
                    <span class="font-semibold">Last sent:</span>
                    ${escapeHtmlText(courierLabel)} | Tracking: ${escapeHtmlText(courierInfo?.trackingCode || 'N/A')} | Consignment: ${escapeHtmlText(courierInfo?.consignmentId || 'N/A')} | Time: ${escapeHtmlText(sentAtText)}
                </div>
           </div>`;
    const discountAmt = Number(order.amount?.discount) || 0;
    const subDisplay = order.subtotal != null ? order.subtotal : (order.amount?.subtotal != null ? order.amount.subtotal : 0);
    const paymentSummary = formatOrderPaymentSummary(order);
    const userInfoHTML = renderUserInfoSection(order.meta);
    const metaIpForBlock = order.meta && order.meta.ip && String(order.meta.ip).trim() && String(order.meta.ip) !== 'Unknown'
        ? String(order.meta.ip).trim()
        : '';
    const blockIpBtnHTML = metaIpForBlock
        ? `<div class="mt-2 flex flex-wrap justify-end gap-2"><button type="button" data-action="blockCustomerIp" data-id="${escapeHtmlText(id)}" data-ip="${escapeHtmlText(metaIpForBlock)}" class="text-xs font-bold bg-red-600 text-white py-2 px-3 rounded-lg hover:bg-red-700">Block this IP</button></div>`
        : '';
    const itemsHTML = (order.items ? (Array.isArray(order.items) ? order.items : Object.values(order.items)) : []).map(item => {
        const rq = Number(item.refundedQty) || 0;
        const refundNote = rq > 0 ? ` <span class="text-amber-700 text-xs font-medium">(ফেরত ${rq})</span>` : '';
        return `<div class="flex items-center gap-2 text-black"><img src="${item.image || item.imageUrl || 'https://i.ibb.co/vz1A5p1/placeholder-image.png'}" class="w-12 h-12 object-cover rounded-md flex-shrink-0"><div class="flex-grow"><p class="font-semibold">${item.name} ${item.variantName ? `(${item.variantName})` : ''}${refundNote}</p><p class="text-xs text-black">${item.quantity} x ${item.price}৳</p></div><span class="font-semibold">${item.price * item.quantity}৳</span></div>`;
    }).join('');
    const refundAuditHtml = (order.refundMeta || order.refundAudit) ? `<div class="mb-3 p-2 bg-amber-50 border border-amber-100 rounded text-xs text-amber-900"><strong>Refund:</strong> ${escapeHtmlText(order.status === 'Refunded' ? 'Full refund recorded.' : 'Partial line refunds may apply.')}</div>` : '';
    const duplicateOrderAlertHtml = allOrdersData && typeof allOrdersData === 'object'
        ? buildDuplicateOrderDetailAlertHTML(id, order, allOrdersData)
        : '';
    const invHref = clientId ? `invoice.html?orderId=${encodeURIComponent(id)}&clientId=${encodeURIComponent(clientId)}` : `invoice.html?orderId=${encodeURIComponent(id)}`;

    return `
        <div class="p-3 bg-white border-t border-indigo-100 mt-2 rounded-b-lg">
            <div class="flex flex-col sm:flex-row justify-between items-start mb-4 gap-2">
                <div class="flex items-center gap-2">
                    <h3 class="text-lg font-bold text-black">Order #${id.slice(-6)}</h3>
                    ${fbBadge}
                </div>
                <div class="flex flex-wrap gap-2"><a href="${invHref}" target="_blank" class="bg-amber-100 text-amber-800 font-semibold py-1.5 px-3 rounded-lg text-xs hover:bg-amber-200 inline-flex items-center gap-1"><i class="fa-solid fa-file-invoice"></i> Invoice</a><button type="button" data-action="copyOrderDetails" data-id="${id}" class="bg-indigo-100 text-indigo-700 font-semibold py-1.5 px-3 rounded-lg text-xs hover:bg-indigo-200"><i class="fa-solid fa-copy mr-2"></i>Copy for Courier</button></div>
            </div>
            
            ${refundAuditHtml}
            ${duplicateOrderAlertHtml}
            <div class="grid grid-cols-1 gap-2 text-sm mb-4 bg-gray-50 p-2 rounded-lg border border-gray-100">
                <div><strong>Name:</strong> <span>${order.customerInfo.name}</span></div>
                <div><strong>Phone:</strong> <span>${order.customerInfo.phone}</span></div>
                <div><strong>Address:</strong> <span>${order.customerInfo.address}</span></div>
            </div>

            <div class="mb-4 border border-indigo-100 rounded-xl p-2 bg-indigo-50/40" data-fraud-card data-id="${id}">
                <div class="flex items-center justify-between gap-2 mb-2">
                    <p class="text-xs font-bold uppercase tracking-wide text-indigo-700">Fraud Check</p>
                    <button type="button" data-action="runOrderFraudCheck" data-id="${id}" class="bg-indigo-600 text-white font-semibold py-1.5 px-3 rounded-lg text-xs hover:bg-indigo-700 inline-flex items-center gap-1.5">
                        <i class="fa-solid fa-shield-halved"></i> Check Fraud
                    </button>
                </div>
                <div class="hidden rounded-lg border border-indigo-100 bg-white px-3 py-2 text-xs text-indigo-700 mb-2" data-fraud-loading>
                    <span class="inline-flex items-center gap-2"><i class="fa-solid fa-spinner fa-spin"></i><span data-fraud-loading-text>Validating number...</span></span>
                </div>
                <div class="hidden rounded-lg border px-3 py-2 text-xs" data-fraud-result>
                    <div class="flex items-center justify-between gap-2 mb-1">
                        <span class="font-semibold text-black">Risk score: <span data-fraud-score class="font-bold">--</span></span>
                        <span data-fraud-badge class="inline-flex px-2 py-0.5 rounded-full border text-[11px] font-semibold">Pending</span>
                    </div>
                    <p data-fraud-verdict class="text-sm font-bold text-black">—</p>
                    <p data-fraud-action class="text-xs text-black mt-0.5">—</p>
                    <div class="mt-2 grid grid-cols-3 gap-2">
                        <div class="rounded-md border border-slate-200 bg-white px-2 py-1.5">
                            <p class="text-[10px] text-black">Total</p>
                            <p data-fraud-total class="text-xs font-bold text-black">0</p>
                        </div>
                        <div class="rounded-md border border-emerald-200 bg-emerald-50 px-2 py-1.5">
                            <p class="text-[10px] text-emerald-700">Delivered</p>
                            <p data-fraud-delivered class="text-xs font-bold text-emerald-800">0</p>
                        </div>
                        <div class="rounded-md border border-red-200 bg-red-50 px-2 py-1.5">
                            <p class="text-[10px] text-red-700">Cancel/Return</p>
                            <p data-fraud-return class="text-xs font-bold text-red-800">0</p>
                        </div>
                    </div>
                    <div class="mt-2 overflow-x-auto">
                        <table class="min-w-full text-[11px] border border-slate-200 rounded-md overflow-hidden">
                            <thead class="bg-slate-50 text-black">
                                <tr>
                                    <th class="px-2 py-1 text-left">Courier</th>
                                    <th class="px-2 py-1 text-right">Total</th>
                                    <th class="px-2 py-1 text-right">Delivered</th>
                                    <th class="px-2 py-1 text-right">Return</th>
                                </tr>
                            </thead>
                            <tbody data-fraud-courier-body class="divide-y divide-slate-100"></tbody>
                        </table>
                    </div>
                </div>
            </div>
            
            <div class="flex flex-wrap gap-2 mb-4">
                <button type="button" data-action="editOrder" data-id="${id}" class="bg-blue-100 text-blue-700 font-semibold py-1.5 px-3 rounded text-xs hover:bg-blue-200"><i class="fa-solid fa-pen-to-square mr-1"></i> Edit Info</button>
                <button type="button" data-action="deleteOrder" data-id="${id}" class="bg-red-100 text-red-700 font-semibold py-1.5 px-3 rounded text-xs hover:bg-red-200"><i class="fa-solid fa-trash mr-1"></i> Delete Order</button>
            </div>

            ${userInfoHTML}
            ${blockIpBtnHTML}
            
            <div class="flex items-center gap-2 mb-4 mt-4 bg-gray-100 p-2 rounded-lg" data-order-status-row>
                <span class="text-xs font-bold text-black">Change Status:</span>
                <select class="admin-form-input text-sm py-1 flex-grow">${statusOptions}</select>
                <button type="button" data-action="updateOrderStatus" data-id="${id}" class="bg-indigo-600 text-white font-semibold py-1 px-4 rounded-lg text-sm hover:bg-indigo-700">Update</button>
            </div>
            
            <hr class="my-3">
            <h4 class="font-semibold text-sm mb-2">Items</h4>
            <div class="space-y-3 text-sm max-h-48 overflow-y-auto pr-2 mb-3">${itemsHTML}</div>
            <hr class="my-3">
            
            <div class="flex justify-end">
                <div class="w-full max-w-xs space-y-1 text-sm">
                    <p class="flex justify-between">Subtotal: <span class="font-medium">${subDisplay}৳</span></p>
                    ${discountAmt > 0 ? `<p class="flex justify-between text-green-700">Discount: <span class="font-medium">-${discountAmt}৳</span></p>` : ''}
                    <p class="flex justify-between">Shipping: <span class="font-medium">${order.shipping?.cost ?? 0}৳</span></p>
                    <p class="flex justify-between gap-2 items-start"><span class="shrink-0">Payment:</span> <span class="font-medium text-right text-xs break-words">${paymentSummary}</span></p>
                    ${order.soldByUid ? `<p class="flex justify-between text-xs text-black gap-2"><span>Cashier:</span> <span class="font-mono truncate max-w-[200px]" title="${escapeHtmlAttr(order.soldByUid)}">${escapeHtmlText(order.soldByUid)}</span></p>` : ''}
                    <p class="flex justify-between text-base font-bold">Total: <span>${order.totalBill}৳</span></p>
                </div>
            </div>
            <div class="mt-4">${courierActionBlock}</div>
        </div>`;
}

export function generateIncompleteOrderDetailHTML(id, order) {
    if (!order) return '<div class="p-3 text-red-500">Order data not found.</div>';
    const userInfoHTML = renderUserInfoSection(order.meta);
    const metaIpForBlock = order.meta && order.meta.ip && String(order.meta.ip).trim() && String(order.meta.ip) !== 'Unknown'
        ? String(order.meta.ip).trim()
        : '';
    const blockIpBtnHTML = metaIpForBlock
        ? `<div class="mt-2 flex flex-wrap justify-end gap-2"><button type="button" data-action="blockCustomerIp" data-id="${escapeHtmlText(id)}" data-ip="${escapeHtmlText(metaIpForBlock)}" class="text-xs font-bold bg-red-600 text-white py-2 px-3 rounded-lg hover:bg-red-700">Block this IP</button></div>`
        : '';
    return `<div class="p-3 bg-white border-t border-indigo-100 mt-2 rounded-b-lg"><h3 class="text-lg font-bold text-black mb-3">Incomplete Order</h3><div class="space-y-2 text-sm"><p><strong>Name:</strong> <span>${order.name || 'N/A'}</span></p><p><strong>Phone:</strong> <span>${order.phone}</span></p><p><strong>Address:</strong> <span>${order.address || 'N/A'}</span></p><p class="text-xs text-black mt-2">Captured at: ${new Date(order.capturedAt).toLocaleString()}</p></div>${userInfoHTML}${blockIpBtnHTML}</div>`;
}

export function renderTopProductsList(productsMap) {
    const container = document.getElementById('top-products-list');
    if (!container) return;

    const sortedProducts = Object.values(productsMap).sort((a, b) => b.count - a.count).slice(0, 4);
    if (sortedProducts.length === 0) {
        container.innerHTML = `
            <div class="flex flex-col items-center justify-center rounded-xl border border-dashed border-gray-700 bg-gray-900/50 px-4 py-10 text-center h-full min-h-[180px]">
                <i class="fa-solid fa-box-open text-3xl text-gray-500 mb-3" aria-hidden="true"></i>
                <p class="text-sm font-semibold text-gray-100">No product sales yet</p>
                <p class="text-xs text-gray-400 mt-1 max-w-[220px]">Completed orders with line items will rank products here.</p>
                <div class="mt-5 flex flex-wrap items-center justify-center gap-2">
                    <a href="inventory.html" class="inline-flex items-center gap-2 rounded-xl border border-indigo-600 bg-indigo-600 px-4 py-2 text-xs font-semibold text-white shadow-sm hover:bg-indigo-500 no-underline transition">Add product</a>
                    <button type="button" data-panel="completed-orders" class="dashboard-nav-btn inline-flex items-center gap-2 rounded-xl border border-gray-600 bg-gray-900 px-4 py-2 text-xs font-semibold text-gray-200 hover:bg-gray-800 transition">View orders</button>
                </div>
            </div>`;
        return;
    }

    const maxEarned = Math.max(...sortedProducts.map((p) => Number(p.totalEarned) || 0), 1);

    container.innerHTML = sortedProducts
        .map((p, index) => {
            const nameSafe = escapeHtmlText(String(p.name || 'Unknown'));
            const imgSrc = escapeHtmlAttr(String(p.image || 'https://placehold.co/56?text=+'));
            const pct = Math.round(((Number(p.totalEarned) || 0) / maxEarned) * 100);
            return `
        <div class="flex items-center gap-2 p-2.5 rounded-xl border border-gray-800 bg-gray-900/40 hover:border-indigo-700 hover:bg-gray-900/80 transition">
            <span class="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gray-900 border border-gray-700 text-xs font-extrabold text-indigo-400 shadow-sm">${index + 1}</span>
            <img src="${imgSrc}" alt="" class="h-11 w-11 rounded-xl object-cover bg-gray-800 ring-2 ring-gray-800 shadow-sm shrink-0" width="44" height="44" loading="lazy" decoding="async">
            <div class="min-w-0 flex-1">
                <h4 class="text-sm font-semibold text-gray-100 truncate">${nameSafe}</h4>
                <p class="text-xs text-gray-400 mt-0.5">${p.count} sold</p>
                <div class="mt-1.5 h-1.5 w-full max-w-[140px] rounded-full bg-gray-700 overflow-hidden">
                    <div class="h-full rounded-full bg-indigo-500 transition-all" style="width:${pct}%"></div>
                </div>
            </div>
            <div class="text-right shrink-0">
                <p class="text-sm font-bold text-gray-100 tabular-nums">${Number(p.totalEarned).toLocaleString()} ৳</p>
            </div>
        </div>`;
        })
        .join('');
}

/**
 * @param {Array<{ label: string, count: number }>} locations - from processDashboardData.topLocations
 */
export function renderTopLocationsList(locations) {
    const container = document.getElementById('top-locations-list');
    if (!container) return;

    const list = Array.isArray(locations) ? locations : [];
    if (list.length === 0) {
        container.innerHTML = '<p class="text-gray-400 text-sm text-center py-4">No location data yet</p>';
        return;
    }

    container.innerHTML = list.map((row, index) => {
        const labelSafe = escapeHtmlText(String(row.label || ''));
        const titleAttr = escapeHtmlAttr(String(row.label || ''));
        const n = Number(row.count) || 0;
        return `
        <div class="flex items-center justify-between p-2 rounded-lg hover:bg-gray-900/60 transition gap-2 border border-transparent hover:border-gray-700">
            <div class="flex items-center gap-2 min-w-0 flex-1">
                <div class="h-10 w-10 rounded-lg bg-teal-950/50 text-teal-400 flex items-center justify-center shrink-0 border border-teal-800/40">
                    <i class="fa-solid fa-location-dot"></i>
                </div>
                <div class="min-w-0">
                    <h4 class="text-sm font-medium text-gray-100 truncate" title="${titleAttr}">${labelSafe}</h4>
                    <p class="text-xs text-gray-400">${n} visitor${n === 1 ? '' : 's'}</p>
                </div>
            </div>
            <span class="text-[10px] text-teal-400 font-bold shrink-0">#${index + 1}</span>
        </div>`;
    }).join('');
}

export function renderRecentOrdersTable(orders) {
    const tbody = document.getElementById('recent-orders-table-body');
    if (!tbody) return;

    const recentOrders = Object.entries(orders || {})
        .sort((a, b) => (b[1].createdAt || 0) - (a[1].createdAt || 0))
        .slice(0, 4);

    if (recentOrders.length === 0) {
        tbody.innerHTML = `<tr><td colspan="6" class="py-14 text-center">
            <div class="inline-flex flex-col items-center max-w-sm mx-auto">
                <span class="flex h-12 w-12 items-center justify-center rounded-2xl bg-gray-800 text-gray-500 mb-3"><i class="fa-solid fa-inbox text-xl" aria-hidden="true"></i></span>
                <p class="text-sm font-semibold text-gray-100">No orders to show</p>
                <p class="text-xs text-gray-400 mt-1">New orders will appear here as soon as they are placed.</p>
                <div class="mt-5 flex flex-wrap items-center justify-center gap-2">
                    <button type="button" data-panel="completed-orders" class="dashboard-nav-btn inline-flex items-center gap-2 rounded-xl border border-indigo-600 bg-indigo-600 px-4 py-2 text-xs font-semibold text-white hover:bg-indigo-500 transition">Open orders</button>
                    <a href="inventory.html" class="inline-flex items-center gap-2 rounded-xl border border-gray-600 bg-gray-900 px-4 py-2 text-xs font-semibold text-gray-200 hover:bg-gray-800 no-underline transition">Add product</a>
                </div>
            </div>
        </td></tr>`;
        return;
    }

    tbody.innerHTML = recentOrders
        .map(([id, order]) => {
            const items = order.items ? (Array.isArray(order.items) ? order.items : Object.values(order.items)) : [];
            const productText =
                items
                    .slice(0, 2)
                    .map((i) => escapeHtmlText(String(i.name || '')))
                    .filter(Boolean)
                    .join(', ') + (items.length > 2 ? ` +${items.length - 2}` : '');
            const customer = escapeHtmlText(String(order.customerInfo?.name || 'Guest'));
            const idShort = escapeHtmlText(id.slice(-6));

            return `
            <tr class="hover:bg-gray-900/50 transition-colors border-b border-gray-800 last:border-0">
                <td class="py-1.5 px-2 xl:py-1 xl:px-1.5 font-mono text-[11px] xl:text-[10px] font-semibold text-indigo-400">#${idShort}</td>
                <td class="py-1.5 px-2 xl:py-1 xl:px-1.5 font-medium text-gray-100 text-xs xl:text-[11px]">${customer}</td>
                <td class="py-1.5 px-2 xl:py-1 xl:px-1.5 text-[11px] xl:text-[10px] text-gray-400 truncate max-w-[140px] xl:max-w-[100px]">${productText || '—'}</td>
                <td class="py-1.5 px-2 xl:py-1 xl:px-1.5 font-bold text-gray-100 tabular-nums text-xs">${order.totalBill} ৳</td>
                <td class="py-1.5 px-2 xl:py-1 xl:px-1.5">${getStatusBadgeHome(order.status || 'Pending')}</td>
                <td class="py-1.5 px-2 xl:py-1 xl:px-1.5 text-right">
                    <button type="button" data-panel="completed-orders" class="dashboard-nav-btn inline-flex h-8 w-8 xl:h-7 xl:w-7 items-center justify-center rounded-md border border-gray-600 bg-gray-900 text-gray-400 hover:bg-gray-800 hover:text-indigo-300 hover:border-indigo-600 transition text-xs" title="Open orders">
                        <i class="fa-solid fa-eye text-sm" aria-hidden="true"></i><span class="sr-only">View order</span>
                    </button>
                </td>
            </tr>`;
        })
        .join('');
}

export function renderProducts(productsData) {
    const container = document.getElementById('product-list-container');
    if (!container) return;
    if (!productsData || Object.keys(productsData).length === 0) { 
        container.innerHTML = '<p class="text-center text-black p-3">No products found.</p>'; 
        return; 
    }
    
    container.innerHTML = `
        <table class="min-w-full divide-y divide-gray-200">
            <thead class="bg-gray-50">
                <tr>
                    <th class="px-6 py-2 text-left text-xs font-semibold text-black uppercase">Product</th>
                    <th class="px-6 py-2 text-left text-xs font-semibold text-black uppercase">Price</th>
                    <th class="px-6 py-2 text-left text-xs font-semibold text-black uppercase">Stock</th>
                    <th class="px-6 py-2"><span class="sr-only">Actions</span></th>
                </tr>
            </thead>
            <tbody class="bg-white divide-y divide-gray-200">
                ${Object.entries(productsData).sort(([, a], [, b]) => (b.createdAt || 0) - (a.createdAt || 0)).map(([id, p]) => {
                    let priceHtml = `${p.salePrice}৳`;
                    if (p.hasVariants) {
                        const prices = Object.values(p.variants || {}).map(v => v.salePrice);
                        priceHtml = prices.length > 0 ? `${Math.min(...prices)}৳+` : 'N/A';
                    }
                    let stock = p.stock;
                    if (p.hasVariants && p.variants) {
                        stock = Object.values(p.variants).reduce((total, variant) => total + (Number(variant.stock) || 0), 0);
                    }
                    const stockText = (stock === null || stock === undefined) ? 'Unlimited' : `${stock} units`;
                    
                    return `<tr>
                        <td class="px-6 py-4"><div class="flex items-center"><img class="h-10 w-10 rounded-lg object-cover" src="${p.imageUrl}" alt="${p.name}"><div class="ml-4"><div class="text-sm font-medium text-black">${p.name}</div><div class="text-xs text-black">${(p.categories && p.categories.length) ? p.categories.join(', ') : (p.category || '')}</div></div></div></td>
                        <td class="px-6 py-4 text-sm text-black">${priceHtml}</td>
                        <td class="px-6 py-4 text-sm">${stockText}</td>
                        <td class="px-6 py-4 text-right text-sm font-medium"><button type="button" data-action="printProductLabels" data-id="${id}" class="text-emerald-600 hover:text-emerald-900 mr-3">Labels</button><button type="button" data-action="editProduct" data-id="${id}" class="text-indigo-600 hover:text-indigo-900">Edit</button><button type="button" data-action="deleteProduct" data-id="${id}" class="text-red-600 hover:text-red-900 ml-4">Delete</button></td>
                    </tr>`;
                }).join('')}
            </tbody>
        </table>`;
}

export function renderCategories(categoriesData, categoryOrder = []) {
    const container = document.getElementById('category-list-container');
    if (!container) return;
    if (!categoriesData || Object.keys(categoriesData).length === 0) {
        container.innerHTML = '<p class="text-center text-black p-3">No categories found.</p>';
        container.removeAttribute('data-order');
        return;
    }
    const ids = Object.keys(categoriesData).sort((a, b) => b.localeCompare(a));
    const orderedIds = Array.isArray(categoryOrder) && categoryOrder.length > 0
        ? [...categoryOrder.filter(id => categoriesData[id]), ...ids.filter(id => !categoryOrder.includes(id))]
        : ids;
    container.dataset.order = JSON.stringify(orderedIds);

    const total = orderedIds.length;
    container.innerHTML = `
        <table class="min-w-full divide-y divide-gray-200">
            <thead class="bg-gray-50">
                <tr><th class="px-2 py-2 w-20 text-center text-xs font-semibold text-black uppercase">Order</th><th class="px-3 py-2 w-16 text-center text-xs font-semibold text-black uppercase">No.</th><th class="px-6 py-2 text-left text-xs font-semibold text-black uppercase">Category</th><th class="px-6 py-2"><span class="sr-only">Actions</span></th></tr>
            </thead>
            <tbody class="bg-white divide-y divide-gray-200">
                ${orderedIds.map((id, index) => {
                    const cat = categoriesData[id];
                    if (!cat) return '';
                    const num = index + 1;
                    const canUp = index > 0;
                    const canDown = index < total - 1;
                    const subcats = cat && cat.subcategories && typeof cat.subcategories === 'object' ? Object.values(cat.subcategories).filter(Boolean) : [];
                    return `<tr data-category-id="${id}">
                        <td class="px-2 py-4 text-center border-r border-gray-100 align-middle">
                            <div class="flex flex-col items-center gap-0.5">
                                <button type="button" class="category-move-up w-8 h-8 flex items-center justify-center rounded border border-gray-200 bg-gray-50 hover:bg-indigo-50 hover:border-indigo-300 text-black hover:text-indigo-600 disabled:opacity-30 disabled:cursor-not-allowed transition" data-category-id="${id}" title="Move up" ${!canUp ? 'disabled' : ''}><i class="fa-solid fa-chevron-up text-xs"></i></button>
                                <button type="button" class="category-move-down w-8 h-8 flex items-center justify-center rounded border border-gray-200 bg-gray-50 hover:bg-indigo-50 hover:border-indigo-300 text-black hover:text-indigo-600 disabled:opacity-30 disabled:cursor-not-allowed transition" data-category-id="${id}" title="Move down" ${!canDown ? 'disabled' : ''}><i class="fa-solid fa-chevron-down text-xs"></i></button>
                            </div>
                        </td>
                        <td class="px-3 py-4 text-center border-r border-gray-100 font-semibold text-black">${num}</td>
                        <td class="px-6 py-4"><div class="flex items-center"><img class="h-10 w-10 rounded-lg object-cover" src="${cat.imageUrl}" alt="${cat.name}"><div class="ml-4"><div class="text-sm font-medium text-black">${cat.name}</div><div class="text-xs text-black">${subcats.length} subcategories</div></div></div></td>
                        <td class="px-6 py-4 text-right text-sm font-medium"><button data-action="editCategory" data-id="${id}" class="text-indigo-600 hover:text-indigo-900">Edit</button><button data-action="deleteCategory" data-id="${id}" class="text-red-600 hover:text-red-900 ml-4">Delete</button></td>
                    </tr>`;
                }).join('')}
            </tbody>
        </table>`;
}

// --- 7. DOM GENERATORS & COLOR ENGINE ---

export function addMainSlideRow(slide = {}) {
    const container = document.getElementById('main-slider-container');
    if (!container) return;
    const url = typeof slide === 'string' ? slide : (slide.imageUrl || '');
    const linkUrl = typeof slide === 'string' ? '' : String(slide.linkUrl || '').trim();
    const escAttr = (s) => String(s || '').replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
    const div = document.createElement('div');
    div.className = 'main-slide-row border border-gray-200 rounded-lg p-2.5 bg-gray-50/90 space-y-2';
    const uniqueId = 'main-slide-' + Date.now() + Math.random().toString(36).substring(2);
    div.innerHTML = `
        <div class="flex flex-wrap items-center gap-1.5">
            <div class="flex-grow min-w-[180px] relative">
                <input type="url" value="${escAttr(url)}" id="${uniqueId}" class="admin-form-input main-slide-input text-xs !py-2 w-full" placeholder="Banner image URL">
            </div>
            <button type="button" onclick="triggerUpload(this)" class="flex-shrink-0 bg-gray-500 text-white p-1.5 rounded hover:bg-gray-600 text-sm" data-target-input="#${uniqueId}" data-upload-profile="banner" title="Upload">
                <i class="fa-solid fa-upload"></i>
            </button>
            <div id="preview-${uniqueId}" class="main-slide-thumb w-11 h-9 bg-gray-50 rounded border flex items-center justify-center overflow-hidden flex-shrink-0">
                ${url ? `<img src="${escAttr(url)}" class="w-full h-full object-cover" alt="">` : ''}
            </div>
            <button type="button" class="remove-main-slide-btn text-red-500 hover:text-red-700 p-1.5 rounded hover:bg-red-100 flex-shrink-0" title="Remove slide">
                <i class="fa-solid fa-trash-can text-sm"></i>
            </button>
        </div>
        <div class="flex flex-wrap items-center gap-2">
            <label for="${uniqueId}-link" class="text-[11px] font-medium text-black shrink-0">Link</label>
            <input type="text" id="${uniqueId}-link" value="${escAttr(linkUrl)}" class="admin-form-input main-slide-link text-xs !py-2 flex-1 min-w-[12rem] max-w-xl" placeholder="#/shop or https://… (optional)">
        </div>
        <div class="banner-slide-wysiwyg border-t border-gray-100 pt-2">
            <p class="text-[10px] font-medium text-black mb-1.5">Crop preview <span class="text-black font-normal">(cover, center · approx. frames)</span></p>
            <div class="flex flex-col sm:flex-row gap-2 flex-wrap">
                <div>
                    <p class="text-[10px] font-medium text-black mb-0.5">Desktop</p>
                    <div data-banner-preview-frame="pc" class="relative mx-auto border border-gray-400 rounded overflow-hidden bg-gray-300 shadow-inner">
                        <img data-banner-preview="pc" alt="" class="absolute inset-0 w-full h-full object-cover object-center z-[1]" />
                        <div data-banner-preview-placeholder="pc" class="absolute inset-0 z-0 flex items-center justify-center text-[9px] text-black px-1 text-center bg-gray-300 leading-tight">URL / upload</div>
                    </div>
                </div>
                <div>
                    <p class="text-[10px] font-medium text-black mb-0.5">Mobile</p>
                    <div data-banner-preview-frame="mobile" class="relative mx-auto border border-gray-400 rounded overflow-hidden bg-gray-300 shadow-inner">
                        <img data-banner-preview="mobile" alt="" class="absolute inset-0 w-full h-full object-cover object-center z-[1]" />
                        <div data-banner-preview-placeholder="mobile" class="absolute inset-0 z-0 flex items-center justify-center text-[9px] text-black px-1 text-center bg-gray-300 leading-tight">URL / upload</div>
                    </div>
                </div>
            </div>
            <p class="banner-slide-suggestion text-[11px] text-black mt-1.5 leading-snug"></p>
        </div>
    `;
    container.appendChild(div);
    div.querySelector('.remove-main-slide-btn').addEventListener('click', () => div.remove());
    const inp = div.querySelector('.main-slide-input');
    if (inp) {
        inp.addEventListener('input', () => refreshMainSlideRowPreview(div));
        inp.addEventListener('change', () => refreshMainSlideRowPreview(div));
    }
    refreshMainSlideRowPreview(div);
    if (inp && String(url || '').trim()) applyImageUrlFieldMask(inp);
}

export function addShippingOptionRow(option = { label: '', cost: '' }) {
    const list = document.getElementById('shipping-options-admin-list'); 
    if (!list) return;
    const div = document.createElement('div'); 
    div.className = 'flex items-center gap-2 p-2 bg-gray-50 rounded-lg border';
    div.innerHTML = `<input type="text" placeholder="Zone Name" value="${option.label}" class="admin-form-input shipping-label-input !py-2"><input type="number" placeholder="Cost" value="${option.cost}" class="admin-form-input shipping-cost-input !py-2 w-32"><button type="button" class="remove-shipping-option-btn text-red-500 hover:text-red-700 p-2 rounded-full hover:bg-red-100"><i class="fa-solid fa-trash-can"></i></button>`;
    list.appendChild(div); 
    div.querySelector('.remove-shipping-option-btn').addEventListener('click', () => div.remove());
}

export function addVariantRow(variant = { name: '', regularPrice: '', salePrice: '', stock: '' }) {
    const list = document.getElementById('product-variants-list'); 
    if (!list) return;
    const div = document.createElement('div'); 
    div.className = 'p-2 bg-gray-50 rounded-lg border grid grid-cols-12 gap-2 items-center variant-row';
    div.innerHTML = `<div class="col-span-5"><input type="text" placeholder="e.g., Red, L" class="admin-form-input variant-name !py-2" value="${variant.name || ''}" required></div><div class="col-span-2"><input type="number" placeholder="Reg. ৳" class="admin-form-input variant-regular-price !py-2 text-center" value="${variant.regularPrice || ''}"></div><div class="col-span-2"><input type="number" placeholder="Sale ৳" class="admin-form-input variant-sale-price !py-2 text-center" value="${variant.salePrice || ''}" required></div><div class="col-span-2"><input type="number" placeholder="Stock" class="admin-form-input variant-stock !py-2 text-center" value="${variant.stock || ''}"></div><button type="button" class="remove-variant-btn text-red-500 hover:text-red-700 p-2 rounded-full hover:bg-red-100 col-span-1 text-center"><i class="fa-solid fa-trash-can"></i></button>`;
    list.appendChild(div); 
    div.querySelector('.remove-variant-btn').addEventListener('click', () => div.remove());
}

export function addGalleryImageRow(url = '') {
    const container = document.getElementById('gallery-images-container'); 
    if (!container) return;
    const div = document.createElement('div'); 
    div.className = 'flex items-center gap-2 gallery-image-row'; 
    const uniqueId = 'gallery-image-' + Date.now() + Math.random().toString(36).substring(2);
    div.innerHTML = `<input type="url" value="${url}" id="${uniqueId}" class="admin-form-input gallery-image-input" placeholder="https://example.com/image.jpg"><button type="button" onclick="triggerUpload(this)" class="flex-shrink-0 bg-gray-500 text-white font-semibold py-2 px-3 rounded-lg hover:bg-gray-600 text-xs" data-target-input="#${uniqueId}" data-upload-profile="product"><i class="fa-solid fa-upload"></i></button><button type="button" class="remove-gallery-image-btn text-red-500 hover:text-red-700 p-2 rounded-full hover:bg-red-100"><i class="fa-solid fa-trash-can"></i></button>`;
    container.appendChild(div); 
    div.querySelector('.remove-gallery-image-btn').addEventListener('click', () => div.remove());
}

// --- NEW: COLOR MANAGEMENT LOGIC ---

/** CBS = Category + banner home section tabs; merged into theme presets so pickers always get sensible defaults. */
const CBS_TAB_PRESET_DEFAULTS = {
    'cbs-tab-active-bg': '#facc15',
    'cbs-tab-active-text': '#111827',
    'cbs-tab-inactive-bg': '#ffffff',
    'cbs-tab-inactive-text': '#374151'
};

const THEME_PRESETS = {
    red: {
        'body-bg': '#f9fafb', 'text-primary': '#111827', 'text-secondary': '#6b7280', 'accent': '#f20c33',
        'header-bg': '#ffffff', 'header-text': '#1f2937', 'menu-bg': '#f20c33', 'menu-text': '#ffffff',
        'top-bar-bg': '#fef2f2', 'top-bar-text': '#991b1b',
        'btn-primary-bg': '#f20c33', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#111827', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#1f2937', 'footer-bg': '#111827', 'footer-text': '#e5e7eb'
    },
    blue: {
        'body-bg': '#f0f9ff', 'text-primary': '#0f172a', 'text-secondary': '#475569', 'accent': '#2563eb',
        'header-bg': '#ffffff', 'header-text': '#1e293b', 'menu-bg': '#2563eb', 'menu-text': '#ffffff',
        'top-bar-bg': '#eff6ff', 'top-bar-text': '#1e40af',
        'btn-primary-bg': '#2563eb', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#0f172a', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#1e40af', 'footer-bg': '#0f172a', 'footer-text': '#e2e8f0'
    },
    green: {
        'body-bg': '#f0fdf4', 'text-primary': '#064e3b', 'text-secondary': '#065f46', 'accent': '#16a34a',
        'header-bg': '#ffffff', 'header-text': '#064e3b', 'menu-bg': '#16a34a', 'menu-text': '#ffffff',
        'top-bar-bg': '#ecfdf5', 'top-bar-text': '#065f46',
        'btn-primary-bg': '#16a34a', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#064e3b', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#15803d', 'footer-bg': '#064e3b', 'footer-text': '#ecfdf5'
    },
    dark: {
        'body-bg': '#111827', 'text-primary': '#f9fafb', 'text-secondary': '#9ca3af', 'accent': '#6366f1',
        'header-bg': '#1f2937', 'header-text': '#f3f4f6', 'menu-bg': '#374151', 'menu-text': '#ffffff',
        'top-bar-bg': '#1e1b4b', 'top-bar-text': '#c7d2fe',
        'btn-primary-bg': '#6366f1', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#4b5563', 'btn-secondary-text': '#ffffff',
        'card-bg': '#1f2937', 'price-text': '#e0e7ff', 'footer-bg': '#000000', 'footer-text': '#9ca3af'
    },
    purple: {
        'body-bg': '#faf5ff', 'text-primary': '#3b0764', 'text-secondary': '#5b21b6', 'accent': '#7c3aed',
        'header-bg': '#ffffff', 'header-text': '#4c1d95', 'menu-bg': '#7c3aed', 'menu-text': '#ffffff',
        'top-bar-bg': '#f3e8ff', 'top-bar-text': '#5b21b6',
        'btn-primary-bg': '#7c3aed', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#4c1d95', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#6d28d9', 'footer-bg': '#3b0764', 'footer-text': '#e9d5ff'
    },
    indigo: {
        'body-bg': '#eef2ff', 'text-primary': '#312e81', 'text-secondary': '#4338ca', 'accent': '#4f46e5',
        'header-bg': '#ffffff', 'header-text': '#3730a3', 'menu-bg': '#4f46e5', 'menu-text': '#ffffff',
        'top-bar-bg': '#e0e7ff', 'top-bar-text': '#4338ca',
        'btn-primary-bg': '#4f46e5', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#312e81', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#4338ca', 'footer-bg': '#312e81', 'footer-text': '#c7d2fe'
    },
    orange: {
        'body-bg': '#fff7ed', 'text-primary': '#9a3412', 'text-secondary': '#c2410c', 'accent': '#ea580c',
        'header-bg': '#ffffff', 'header-text': '#9a3412', 'menu-bg': '#ea580c', 'menu-text': '#ffffff',
        'top-bar-bg': '#ffedd5', 'top-bar-text': '#c2410c',
        'btn-primary-bg': '#ea580c', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#9a3412', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#c2410c', 'footer-bg': '#9a3412', 'footer-text': '#ffedd5'
    },
    amber: {
        'body-bg': '#fffbeb', 'text-primary': '#78350f', 'text-secondary': '#b45309', 'accent': '#d97706',
        'header-bg': '#ffffff', 'header-text': '#92400e', 'menu-bg': '#d97706', 'menu-text': '#ffffff',
        'top-bar-bg': '#fef3c7', 'top-bar-text': '#b45309',
        'btn-primary-bg': '#d97706', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#78350f', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#b45309', 'footer-bg': '#78350f', 'footer-text': '#fef3c7'
    },
    teal: {
        'body-bg': '#f0fdfa', 'text-primary': '#134e4a', 'text-secondary': '#0f766e', 'accent': '#0d9488',
        'header-bg': '#ffffff', 'header-text': '#134e4a', 'menu-bg': '#0d9488', 'menu-text': '#ffffff',
        'top-bar-bg': '#ccfbf1', 'top-bar-text': '#0f766e',
        'btn-primary-bg': '#0d9488', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#134e4a', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#0f766e', 'footer-bg': '#134e4a', 'footer-text': '#ccfbf1'
    },
    cyan: {
        'body-bg': '#ecfeff', 'text-primary': '#164e63', 'text-secondary': '#0e7490', 'accent': '#0891b2',
        'header-bg': '#ffffff', 'header-text': '#155e75', 'menu-bg': '#0891b2', 'menu-text': '#ffffff',
        'top-bar-bg': '#cffafe', 'top-bar-text': '#0e7490',
        'btn-primary-bg': '#0891b2', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#164e63', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#0e7490', 'footer-bg': '#164e63', 'footer-text': '#cffafe'
    },
    pink: {
        'body-bg': '#fdf2f8', 'text-primary': '#831843', 'text-secondary': '#9d174d', 'accent': '#db2777',
        'header-bg': '#ffffff', 'header-text': '#831843', 'menu-bg': '#db2777', 'menu-text': '#ffffff',
        'top-bar-bg': '#fce7f3', 'top-bar-text': '#9d174d',
        'btn-primary-bg': '#db2777', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#831843', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#be185d', 'footer-bg': '#831843', 'footer-text': '#fce7f3'
    },
    rose: {
        'body-bg': '#fff1f2', 'text-primary': '#881337', 'text-secondary': '#be123c', 'accent': '#e11d48',
        'header-bg': '#ffffff', 'header-text': '#9f1239', 'menu-bg': '#e11d48', 'menu-text': '#ffffff',
        'top-bar-bg': '#ffe4e6', 'top-bar-text': '#be123c',
        'btn-primary-bg': '#e11d48', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#881337', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#be123c', 'footer-bg': '#881337', 'footer-text': '#ffe4e6'
    },
    violet: {
        'body-bg': '#f5f3ff', 'text-primary': '#4c1d95', 'text-secondary': '#5b21b6', 'accent': '#8b5cf6',
        'header-bg': '#ffffff', 'header-text': '#5b21b6', 'menu-bg': '#8b5cf6', 'menu-text': '#ffffff',
        'top-bar-bg': '#ede9fe', 'top-bar-text': '#5b21b6',
        'btn-primary-bg': '#8b5cf6', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#4c1d95', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#6d28d9', 'footer-bg': '#4c1d95', 'footer-text': '#ede9fe'
    },
    lime: {
        'body-bg': '#f7fee7', 'text-primary': '#365314', 'text-secondary': '#4d7c0f', 'accent': '#65a30d',
        'header-bg': '#ffffff', 'header-text': '#365314', 'menu-bg': '#65a30d', 'menu-text': '#ffffff',
        'top-bar-bg': '#ecfccb', 'top-bar-text': '#4d7c0f',
        'btn-primary-bg': '#65a30d', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#365314', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#4d7c0f', 'footer-bg': '#365314', 'footer-text': '#ecfccb'
    },
    slate: {
        'body-bg': '#f8fafc', 'text-primary': '#0f172a', 'text-secondary': '#475569', 'accent': '#475569',
        'header-bg': '#ffffff', 'header-text': '#1e293b', 'menu-bg': '#475569', 'menu-text': '#ffffff',
        'top-bar-bg': '#f1f5f9', 'top-bar-text': '#334155',
        'btn-primary-bg': '#475569', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#0f172a', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#334155', 'footer-bg': '#0f172a', 'footer-text': '#e2e8f0'
    },
    emerald: {
        'body-bg': '#ecfdf5', 'text-primary': '#064e3b', 'text-secondary': '#047857', 'accent': '#059669',
        'header-bg': '#ffffff', 'header-text': '#065f46', 'menu-bg': '#059669', 'menu-text': '#ffffff',
        'top-bar-bg': '#d1fae5', 'top-bar-text': '#047857',
        'btn-primary-bg': '#059669', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#064e3b', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#047857', 'footer-bg': '#064e3b', 'footer-text': '#d1fae5'
    },
    sky: {
        'body-bg': '#f0f9ff', 'text-primary': '#0c4a6e', 'text-secondary': '#0369a1', 'accent': '#0284c7',
        'header-bg': '#ffffff', 'header-text': '#0c4a6e', 'menu-bg': '#0284c7', 'menu-text': '#ffffff',
        'top-bar-bg': '#e0f2fe', 'top-bar-text': '#0369a1',
        'btn-primary-bg': '#0284c7', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#0c4a6e', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#0369a1', 'footer-bg': '#0c4a6e', 'footer-text': '#e0f2fe'
    },
    chaldal: {
        'body-bg': '#fffbeb', 'text-primary': '#422006', 'text-secondary': '#78350f', 'accent': '#eab308',
        'header-bg': '#ffffff', 'header-text': '#422006', 'menu-bg': '#eab308', 'menu-text': '#422006',
        'top-bar-bg': '#fef9c3', 'top-bar-text': '#713f12',
        'btn-primary-bg': '#eab308', 'btn-primary-text': '#422006', 'btn-secondary-bg': '#ca8a04', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#a16207', 'footer-bg': '#ca8a04', 'footer-text': '#fef9c3'
    },
    golapi: {
        'body-bg': '#fdf2f8', 'text-primary': '#831843', 'text-secondary': '#9d174d', 'accent': '#ec4899',
        'header-bg': '#ffffff', 'header-text': '#831843', 'menu-bg': '#f472b6', 'menu-text': '#831843',
        'top-bar-bg': '#fce7f3', 'top-bar-text': '#9d174d',
        'btn-primary-bg': '#ec4899', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#be185d', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#be185d', 'footer-bg': '#9d174d', 'footer-text': '#fce7f3'
    },
    coral: {
        'body-bg': '#fff7ed', 'text-primary': '#7c2d12', 'text-secondary': '#c2410c', 'accent': '#f97316',
        'header-bg': '#ffffff', 'header-text': '#7c2d12', 'menu-bg': '#fb923c', 'menu-text': '#ffffff',
        'top-bar-bg': '#ffedd5', 'top-bar-text': '#c2410c',
        'btn-primary-bg': '#f97316', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#ea580c', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#ea580c', 'footer-bg': '#c2410c', 'footer-text': '#ffedd5'
    },
    navy: {
        'body-bg': '#f8fafc', 'text-primary': '#0f172a', 'text-secondary': '#334155', 'accent': '#1e3a8a',
        'header-bg': '#1e3a8a', 'header-text': '#ffffff', 'menu-bg': '#1e40af', 'menu-text': '#ffffff',
        'top-bar-bg': '#dbeafe', 'top-bar-text': '#1e40af',
        'btn-primary-bg': '#1e40af', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#0f172a', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#1e40af', 'footer-bg': '#0f172a', 'footer-text': '#e2e8f0'
    },
    gold: {
        'body-bg': '#fffbeb', 'text-primary': '#422006', 'text-secondary': '#78350f', 'accent': '#d97706',
        'header-bg': '#ffffff', 'header-text': '#422006', 'menu-bg': '#d97706', 'menu-text': '#ffffff',
        'top-bar-bg': '#fef3c7', 'top-bar-text': '#92400e',
        'btn-primary-bg': '#d97706', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#b45309', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#b45309', 'footer-bg': '#78350f', 'footer-text': '#fef3c7'
    },
    mint: {
        'body-bg': '#f0fdf4', 'text-primary': '#14532d', 'text-secondary': '#166534', 'accent': '#22c55e',
        'header-bg': '#ffffff', 'header-text': '#14532d', 'menu-bg': '#22c55e', 'menu-text': '#ffffff',
        'top-bar-bg': '#dcfce7', 'top-bar-text': '#166534',
        'btn-primary-bg': '#22c55e', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#15803d', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#15803d', 'footer-bg': '#14532d', 'footer-text': '#dcfce7'
    },
    fuchsia: {
        'body-bg': '#fdf4ff', 'text-primary': '#701a75', 'text-secondary': '#86198f', 'accent': '#c026d3',
        'header-bg': '#ffffff', 'header-text': '#701a75', 'menu-bg': '#c026d3', 'menu-text': '#ffffff',
        'top-bar-bg': '#fae8ff', 'top-bar-text': '#86198f',
        'btn-primary-bg': '#c026d3', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#86198f', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#a21caf', 'footer-bg': '#701a75', 'footer-text': '#fae8ff'
    },
    brown: {
        'body-bg': '#faf5f0', 'text-primary': '#292524', 'text-secondary': '#57534e', 'accent': '#a16207',
        'header-bg': '#ffffff', 'header-text': '#292524', 'menu-bg': '#78716c', 'menu-text': '#ffffff',
        'top-bar-bg': '#fef3c7', 'top-bar-text': '#713f12',
        'btn-primary-bg': '#a16207', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#57534e', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#92400e', 'footer-bg': '#44403c', 'footer-text': '#e7e5e4'
    },
    peach: {
        'body-bg': '#fff7ed', 'text-primary': '#7c2d12', 'text-secondary': '#9a3412', 'accent': '#ea580c',
        'header-bg': '#ffffff', 'header-text': '#7c2d12', 'menu-bg': '#fdba74', 'menu-text': '#7c2d12',
        'top-bar-bg': '#ffedd5', 'top-bar-text': '#9a3412',
        'btn-primary-bg': '#ea580c', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#c2410c', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#c2410c', 'footer-bg': '#9a3412', 'footer-text': '#ffedd5'
    },
    lavender: {
        'body-bg': '#f5f3ff', 'text-primary': '#4c1d95', 'text-secondary': '#5b21b6', 'accent': '#8b5cf6',
        'header-bg': '#ffffff', 'header-text': '#4c1d95', 'menu-bg': '#a78bfa', 'menu-text': '#ffffff',
        'top-bar-bg': '#ede9fe', 'top-bar-text': '#5b21b6',
        'btn-primary-bg': '#8b5cf6', 'btn-primary-text': '#ffffff', 'btn-secondary-bg': '#6d28d9', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#6d28d9', 'footer-bg': '#4c1d95', 'footer-text': '#ede9fe'
    },
    daraz: {
        'body-bg': '#fefce8', 'text-primary': '#1c1917', 'text-secondary': '#44403c', 'accent': '#eab308',
        'header-bg': '#ffffff', 'header-text': '#1c1917', 'menu-bg': '#facc15', 'menu-text': '#1c1917',
        'top-bar-bg': '#fef9c3', 'top-bar-text': '#713f12',
        'btn-primary-bg': '#eab308', 'btn-primary-text': '#1c1917', 'btn-secondary-bg': '#ca8a04', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#a16207', 'footer-bg': '#422006', 'footer-text': '#fef9c3'
    },
    /** Swapno storefront look — also used by `buildSwapnoThemePresetUpdate` (snake_case in builder). */
    swapno: {
        'body-bg': '#f3f4f6', 'text-primary': '#111827', 'text-secondary': '#4b5563', 'accent': '#e01826',
        'header-bg': '#e01826', 'header-text': '#ffffff', 'search-icon': '#111827', 'menu-bg': '#e01826', 'menu-text': '#ffffff',
        'category-pill-text': '#111827', 'category-pill-bg': '#fef3c7',
        'category-card-label-text': '#111827', 'category-card-label-bg': '#ffd400',
        'top-bar-bg': '#ffd400', 'top-bar-text': '#111827',
        'btn-primary-bg': '#ffd400', 'btn-primary-text': '#111827', 'btn-secondary-bg': '#111827', 'btn-secondary-text': '#ffffff',
        'card-bg': '#ffffff', 'price-text': '#e01826', 'footer-bg': '#111827', 'footer-text': '#e5e7eb'
    }
};

/**
 * Merge payload for Firebase `siteSettings`: Classic storefront + Shwapno/Swapno layout, colors, floating grocery cart.
 * (Legacy `activeDesign: 'swapno'` + shop-swapno.html is retired — preset uses `classic` + layout flags.)
 * @param {object} siteSettingsLike — current `siteSettings` (or partial with `layout` / `colors`).
 */
export function buildSwapnoThemePresetUpdate(siteSettingsLike) {
    const s = siteSettingsLike && typeof siteSettingsLike === 'object' ? siteSettingsLike : {};
    const curL = s.layout && typeof s.layout === 'object' ? { ...s.layout } : {};
    const curC = s.colors && typeof s.colors === 'object' ? { ...s.colors } : {};
    const prevCc = (curL.categoryConfig && typeof curL.categoryConfig === 'object') ? { ...curL.categoryConfig } : {};
    const categoryConfig = { ...prevCc, cardStyle: 'shwapno' };
    const layout = {
        ...curL,
        activeDesign: 'classic',
        headerDesign: 'shwapno',
        productDisplayStyle: 'shwapno',
        bottomNavEnabled: true,
        bottomNavDesign: 'default',
        cartDrawerDesign: 'default',
        hamburgerMenuStyle: 'shwapnoPcView',
        pcLayoutStyle: 'shwapnoLeftMenu',
        cartIconStyle: 'groceryCart',
        groceryCartTopColor: '#e01826',
        groceryCartBottomColor: '#1f2937',
        categoryConfig
    };
    const colors = {
        ...curC,
        primary_color: '#111827',
        accent_color: '#e01826',
        header_icon_color: '#ffd400',
        section_title_color: '#111827',
        body_bg: '#f3f4f6',
        text_primary: '#111827',
        text_secondary: '#4b5563',
        header_bg: '#e01826',
        header_text: '#ffffff',
        search_icon_color: '#111827',
        menu_bg: '#e01826',
        menu_text: '#ffffff',
        category_pill_text: '#111827',
        category_pill_bg: '#fef3c7',
        category_card_label_text: '#111827',
        category_card_label_bg: '#ffd400',
        cbs_tab_active_bg: '#ffd400',
        cbs_tab_active_text: '#111827',
        cbs_tab_inactive_bg: '#ffffff',
        cbs_tab_inactive_text: '#374151',
        top_bar_bg: '#ffd400',
        top_bar_text: '#111827',
        btn_primary_bg: '#ffd400',
        btn_primary_text: '#111827',
        btn_secondary_bg: '#111827',
        btn_secondary_text: '#ffffff',
        card_bg: '#ffffff',
        card_border: curC.card_border || '#e5e7eb',
        price_text: '#e01826',
        footer_bg: '#111827',
        footer_text: '#e5e7eb',
        footer_accent: '#ffd400'
    };
    return { layout, colors };
}

/** Normalize hex (#rgb / #rrggbb) or transparent for Optional Content style overrides. */
export function normalizeOptionalContentCssColor(raw) {
    const s = String(raw == null ? '' : raw).trim();
    if (!s) return null;
    const lower = s.toLowerCase();
    if (lower === 'transparent') return 'transparent';
    let hex = s.startsWith('#') ? s.slice(1) : s;
    if (/^[0-9a-f]{3}$/i.test(hex)) {
        hex = hex.split('').map((c) => c + c).join('');
    }
    if (/^[0-9a-f]{6}$/i.test(hex)) return `#${hex.toLowerCase()}`;
    return null;
}

export function normalizeOptionalContentShadow(raw) {
    const s = String(raw == null ? '' : raw).trim();
    if (!s || s.length > 200) return null;
    const lower = s.toLowerCase();
    if (lower.includes('url(') || lower.includes('<') || lower.includes('expression') || lower.includes('@import')) return null;
    return s;
}

function normalizeIconClassForOptionalPicker(s) {
    return String(s || '').trim().replace(/\s+/g, ' ').toLowerCase();
}

function expandHexForColorInput(t) {
    const s = String(t || '').trim();
    if (/^#[0-9A-Fa-f]{6}$/i.test(s)) return s.toLowerCase();
    if (/^#[0-9A-Fa-f]{3}$/i.test(s)) {
        const h = s.slice(1);
        return `#${h.split('').map((c) => c + c).join('')}`.toLowerCase();
    }
    return null;
}

export function syncOptionalContentIconPickerHighlight() {
    for (let i = 1; i <= 4; i += 1) {
        const input = document.getElementById(`homepage-feature-${i}-icon`);
        const wrap = document.getElementById(`homepage-feature-${i}-icon-presets`);
        if (!input || !wrap) continue;
        const cur = normalizeIconClassForOptionalPicker(input.value);
        wrap.querySelectorAll('button[data-icon-class]').forEach((btn) => {
            const preset = normalizeIconClassForOptionalPicker(btn.getAttribute('data-icon-class') || '');
            const on = preset === cur;
            btn.classList.toggle('ring-2', on);
            btn.classList.toggle('ring-indigo-600', on);
            btn.classList.toggle('ring-offset-1', on);
            btn.setAttribute('aria-pressed', on ? 'true' : 'false');
        });
    }
}

export function setupOptionalContentTabControls() {
    for (let i = 1; i <= 4; i += 1) {
        const wrap = document.getElementById(`homepage-feature-${i}-icon-presets`);
        if (!wrap || wrap.dataset.populated === '1') continue;
        wrap.dataset.populated = '1';
        wrap.className = 'max-h-32 overflow-y-auto grid grid-cols-6 gap-1 p-1 border border-gray-200 rounded-lg bg-white';
        wrap.setAttribute('role', 'group');
        wrap.setAttribute('aria-label', `Icon presets for feature ${i}`);
        HOMEPAGE_FEATURE_ICON_PRESETS.forEach((cls) => {
            const btn = document.createElement('button');
            btn.type = 'button';
            btn.setAttribute('data-icon-class', cls);
            btn.className = 'flex items-center justify-center h-8 rounded border border-gray-100 bg-gray-50 hover:bg-indigo-50 text-black text-sm';
            btn.title = cls;
            btn.innerHTML = `<i class="${cls}" aria-hidden="true"></i>`;
            btn.addEventListener('click', () => {
                const inp = document.getElementById(`homepage-feature-${i}-icon`);
                if (inp) inp.value = cls;
                syncOptionalContentIconPickerHighlight();
            });
            wrap.appendChild(btn);
        });
    }

    const optionalColorPairs = [
        ['optional-content-card-bg', 'optional-content-card-bg-hex'],
        ['optional-content-card-border', 'optional-content-card-border-hex'],
        ['optional-content-icon-bg', 'optional-content-icon-bg-hex'],
        ['optional-content-icon-border', 'optional-content-icon-border-hex'],
        ['optional-content-icon-fg', 'optional-content-icon-fg-hex'],
        ['optional-content-title-color', 'optional-content-title-color-hex'],
        ['optional-content-subtitle-color', 'optional-content-subtitle-color-hex']
    ];
    optionalColorPairs.forEach(([pickerId, hexId]) => {
        const picker = document.getElementById(pickerId);
        const hex = document.getElementById(hexId);
        if (!picker || !hex || picker.dataset.optionalContentSync === '1') return;
        picker.dataset.optionalContentSync = '1';
        picker.addEventListener('input', () => { hex.value = picker.value; });
        hex.addEventListener('input', () => {
            const expanded = expandHexForColorInput(hex.value.trim());
            if (expanded) picker.value = expanded;
        });
    });

    for (let i = 1; i <= 4; i += 1) {
        const inp = document.getElementById(`homepage-feature-${i}-icon`);
        if (inp && inp.dataset.optionalIconSync !== '1') {
            inp.dataset.optionalIconSync = '1';
            inp.addEventListener('input', () => syncOptionalContentIconPickerHighlight());
        }
    }

    const resetBtn = document.getElementById('optional-content-colors-reset');
    if (resetBtn && resetBtn.dataset.bound !== '1') {
        resetBtn.dataset.bound = '1';
        resetBtn.addEventListener('click', () => {
            optionalColorPairs.forEach(([pickerId, hexId]) => {
                const picker = document.getElementById(pickerId);
                const hexEl = document.getElementById(hexId);
                if (hexEl) hexEl.value = '';
                if (picker) picker.value = '#ffffff';
            });
            const shadow = document.getElementById('optional-content-card-shadow');
            if (shadow) shadow.value = '';
            const gap = document.getElementById('optional-content-grid-gap');
            if (gap) gap.value = '';
            syncOptionalContentIconPickerHighlight();
        });
    }
}

export function setupColorPickerSync() {
    const colorIds = [
        'body-bg', 'text-primary', 'text-secondary', 'accent',
        'header-bg', 'header-text', 'search-icon', 'menu-bg', 'menu-text',
        'category-pill-text', 'category-pill-bg', 'category-card-label-text', 'category-card-label-bg',
        'cbs-tab-active-bg', 'cbs-tab-active-text', 'cbs-tab-inactive-bg', 'cbs-tab-inactive-text',
        'top-bar-bg', 'top-bar-text',
        'btn-primary-bg', 'btn-primary-text', 'btn-secondary-bg', 'btn-secondary-text',
        'card-bg', 'price-text', 'footer-bg', 'footer-text'
    ];

    [['order-call-order-now-bg', 'order-call-order-now-bg-hex'], ['order-call-order-now-text', 'order-call-order-now-text-hex']].forEach(([pickerId, hexId]) => {
        const picker = document.getElementById(pickerId);
        const hex = document.getElementById(hexId);
        if (picker && hex) {
            picker.addEventListener('input', () => { hex.value = picker.value; });
            hex.addEventListener('input', () => {
                const t = hex.value.trim();
                if (/^#[0-9A-Fa-f]{6}$/i.test(t)) picker.value = t;
            });
        }
    });

    // Sync Logic
    colorIds.forEach(id => {
        const picker = document.getElementById(`color-${id}`);
        const hex = document.getElementById(`color-${id}-hex`);
        if (picker && hex) {
            picker.addEventListener('input', () => hex.value = picker.value);
            hex.addEventListener('input', () => picker.value = hex.value);
        }
    });

    // Preset Button Logic
    document.querySelectorAll('.preset-btn').forEach(btn => {
        btn.addEventListener('click', (e) => {
            const presetName = e.currentTarget.dataset.preset;
            const base = THEME_PRESETS[presetName];
            const colors = base ? { ...CBS_TAB_PRESET_DEFAULTS, ...base } : null;
            if (colors) {
                Object.entries(colors).forEach(([key, val]) => {
                    if (key.startsWith('_')) return;
                    const picker = document.getElementById(`color-${key}`);
                    const hex = document.getElementById(`color-${key}-hex`);
                    if (picker && hex) {
                        picker.value = val;
                        hex.value = val;
                    }
                });
                showToast(`Applied ${presetName} theme!`);
            }
        });
    });
}

/** Toggle ImgBB vs Daiman key fields in dashboard upload API section */
export function syncImageUploadProviderFields() {
    const sel = document.getElementById('image-upload-provider');
    if (!sel) return;
    const imgbbWrap = document.getElementById('imgbb-api-key-wrap');
    const daimanWrap = document.getElementById('daiman-api-key-wrap');
    const isDaiman = sel.value === 'daiman';
    if (imgbbWrap) imgbbWrap.classList.toggle('hidden', isDaiman);
    if (daimanWrap) daimanWrap.classList.toggle('hidden', !isDaiman);
}

export function syncCategoryProductSpacingInputs() {
    const enabledEl = document.getElementById('cat-product-spacing-enabled');
    const sidePaddingEl = document.getElementById('cat-product-side-padding');
    const cardGapEl = document.getElementById('cat-product-card-gap');
    const wrapEl = document.getElementById('cat-product-spacing-inputs');
    if (!enabledEl) return;
    const enabled = !!enabledEl.checked;
    if (sidePaddingEl) sidePaddingEl.disabled = !enabled;
    if (cardGapEl) cardGapEl.disabled = !enabled;
    if (wrapEl) {
        wrapEl.classList.toggle('opacity-60', !enabled);
        wrapEl.classList.toggle('pointer-events-none', !enabled);
    }
}

/** Theme-typical desktop / mobile header row heights (px) when layout has no saved custom values */
export function getDefaultLayoutHeaderHeights(headerDesign) {
    const hd = headerDesign || 'default';
    if (hd === 'minimal') return { desktop: 80, mobile: 72 };
    if (hd === 'shwapno') return { desktop: 105, mobile: 48 };
    if (hd === 'centerTrade') return { desktop: 110, mobile: 52 };
    return { desktop: 96, mobile: 80 };
}

/** Typical desktop search bar max-width (px) when layout has no saved searchBarWidth — aligns with shop-classic.css */
export function getDefaultSearchBarWidthPx(headerDesign) {
    const hd = headerDesign || 'default';
    if (hd === 'minimal') return 576;
    if (hd === 'shwapno') return 600;
    if (hd === 'centerTrade') return 352;
    return 576;
}

/** Show/hide custom header nav category list textarea (dashboard Header design tab) */
export function syncHeaderNavCustomCategoriesVisibility() {
    const wrap = document.getElementById('header-nav-custom-categories-wrap');
    const customRadio = document.querySelector('input[name="header-nav-type"][value="customCategories"]');
    if (!wrap) return;
    wrap.classList.toggle('hidden', !(customRadio && customRadio.checked));
}

/** All inventory category names in order (for header custom-link target dropdown). */
export function getOrderedCategoryNamesForHeaderDropdown(categoriesMap, categoryOrderArr) {
    const map = categoriesMap && typeof categoriesMap === 'object' ? categoriesMap : {};
    const ids = Object.keys(map);
    const orderedIds = Array.isArray(categoryOrderArr) && categoryOrderArr.length > 0
        ? [...categoryOrderArr.filter((id) => map[id]), ...ids.filter((id) => !categoryOrderArr.includes(id))]
        : ids.sort((a, b) => b.localeCompare(a));
    return orderedIds
        .map((id) => {
            const cat = map[id];
            return cat && typeof cat.name === 'string' ? cat.name.trim() : '';
        })
        .filter(Boolean);
}

function parseSavedCustomCategoriesToRows(raw) {
    if (raw == null) return [];
    const arr = Array.isArray(raw) ? raw : (typeof raw === 'object' ? Object.values(raw) : [String(raw)]);
    const rows = [];
    arr.forEach((item) => {
        if (typeof item === 'string') {
            const t = String(item).trim();
            if (t) rows.push({ label: t, target: t });
            return;
        }
        if (item && typeof item === 'object') {
            const target = String(item.target ?? item.category ?? item.hrefName ?? '').trim();
            const labelRaw = item.label != null ? String(item.label).trim() : '';
            if (!target && !labelRaw) return;
            rows.push({ label: labelRaw || target, target: target || labelRaw });
        }
    });
    return rows;
}

function escAttr(s) {
    return String(s ?? '')
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/</g, '&lt;');
}

function buildTargetSelectOptionsHtml(categoryNames, selectedTarget) {
    const names = Array.isArray(categoryNames) ? categoryNames : [];
    let html = '<option value="">— Select category —</option>';
    names.forEach((name) => {
        const sel = name === selectedTarget ? ' selected' : '';
        html += `<option value="${escAttr(name)}"${sel}>${escAttr(name)}</option>`;
    });
    return html;
}

let _headerNavCustomListDelegated = false;

function ensureHeaderNavCustomCategoriesListDelegated() {
    if (_headerNavCustomListDelegated) return;
    const wrap = document.getElementById('header-nav-custom-categories-wrap');
    if (!wrap) return;
    _headerNavCustomListDelegated = true;
    wrap.addEventListener('click', (e) => {
        if (e.target.closest('#header-nav-custom-categories-add-btn')) {
            e.preventDefault();
            const ctx = typeof window.__getDashboardCategoriesForHeaderUi === 'function'
                ? window.__getDashboardCategoriesForHeaderUi()
                : { map: {}, order: [] };
            appendHeaderNavCustomCategoryRow({}, ctx.map, ctx.order);
            try {
                if (typeof window.__refreshHeaderCategoryLinksUi === 'function') window.__refreshHeaderCategoryLinksUi();
            } catch (_) { /* noop */ }
            return;
        }
        const rm = e.target.closest('.header-nav-custom-row-remove');
        if (rm) {
            e.preventDefault();
            rm.closest('.header-nav-custom-row')?.remove();
            try {
                if (typeof window.__refreshHeaderCategoryLinksUi === 'function') window.__refreshHeaderCategoryLinksUi();
            } catch (_) { /* noop */ }
        }
    });
    wrap.addEventListener('input', (e) => {
        if (e.target.closest('.header-nav-custom-row') && typeof window.__refreshHeaderCategoryLinksUi === 'function') {
            window.__refreshHeaderCategoryLinksUi();
        }
    });
    wrap.addEventListener('change', (e) => {
        if (e.target.closest('.header-nav-custom-row') && typeof window.__refreshHeaderCategoryLinksUi === 'function') {
            window.__refreshHeaderCategoryLinksUi();
        }
    });
}

function appendHeaderNavCustomCategoryRow(row, categoriesMap, categoryOrderArr) {
    const list = document.getElementById('header-nav-custom-categories-list');
    if (!list) return;
    const names = getOrderedCategoryNamesForHeaderDropdown(categoriesMap, categoryOrderArr);
    const label = row.label != null ? String(row.label).trim() : '';
    const target = row.target != null ? String(row.target).trim() : '';
    const div = document.createElement('div');
    div.className = 'header-nav-custom-row flex flex-wrap items-end gap-2 p-2 rounded-xl border border-gray-200 bg-white';
    div.innerHTML = `
        <div class="flex-1 min-w-[160px]">
            <label class="block text-xs font-medium text-black mb-1">Strip label</label>
            <input type="text" class="admin-form-input w-full py-2 text-sm header-nav-custom-label" placeholder="e.g. SUMMER FEST" value="${escAttr(label)}">
        </div>
        <div class="flex-1 min-w-[200px]">
            <label class="block text-xs font-medium text-black mb-1">Opens category</label>
            <select class="admin-form-input w-full py-2 text-sm header-nav-custom-target">${buildTargetSelectOptionsHtml(names, target)}</select>
        </div>
        <button type="button" class="header-nav-custom-row-remove shrink-0 p-2 rounded-lg text-red-600 hover:bg-red-50 border border-transparent hover:border-red-200 transition" title="Remove row" aria-label="Remove link"><i class="fa-solid fa-trash-can" aria-hidden="true"></i></button>
    `;
    list.appendChild(div);
}

/**
 * Render custom header strip rows from saved `layout.menuConfig.customCategories`
 * (strings or `{ label, target }`). Requires `categoriesMap` + `categoryOrder` for dropdown options.
 */
export function renderHeaderNavCustomCategoriesList(savedRaw, categoriesMap, categoryOrderArr) {
    const list = document.getElementById('header-nav-custom-categories-list');
    if (!list) return;
    ensureHeaderNavCustomCategoriesListDelegated();
    const rows = parseSavedCustomCategoriesToRows(savedRaw);
    list.innerHTML = '';
    if (rows.length === 0) {
        appendHeaderNavCustomCategoryRow({}, categoriesMap, categoryOrderArr);
    } else {
        rows.forEach((r) => appendHeaderNavCustomCategoryRow(r, categoriesMap, categoryOrderArr));
    }
}

/** Each row’s label + target (may be incomplete). */
export function getHeaderNavCustomCategoryRowsFromDOM() {
    const list = document.getElementById('header-nav-custom-categories-list');
    if (!list) return [];
    const out = [];
    list.querySelectorAll('.header-nav-custom-row').forEach((row) => {
        const label = row.querySelector('.header-nav-custom-label')?.value?.trim() ?? '';
        const target = row.querySelector('.header-nav-custom-target')?.value?.trim() ?? '';
        out.push({ label, target });
    });
    return out;
}

/** Payload for Firebase: `{ label, target }[]` (skips rows with no target). */
export function getHeaderNavCustomCategoriesFromDOM() {
    return getHeaderNavCustomCategoryRowsFromDOM()
        .filter((r) => r.target)
        .map((r) => ({
            label: (r.label && r.label.trim()) ? r.label.trim() : r.target.trim(),
            target: r.target.trim()
        }));
}

/** After inventory categories load, repopulate target `<select>` options without clearing labels. */
export function refreshHeaderNavCustomCategoryTargetDropdowns(categoriesMap, categoryOrderArr) {
    const names = getOrderedCategoryNamesForHeaderDropdown(categoriesMap, categoryOrderArr);
    document.querySelectorAll('.header-nav-custom-row .header-nav-custom-target').forEach((sel) => {
        const cur = String(sel.value || '').trim();
        sel.innerHTML = buildTargetSelectOptionsHtml(names, names.includes(cur) ? cur : '');
        if (names.includes(cur)) sel.value = cur;
    });
}

/** First 8 inventory category names in menu order — mirrors storefront strip when mode is "categories". */
export function getOrderedInventoryCategoryNamesForHeaderStrip(categoriesMap, categoryOrderArr) {
    const map = categoriesMap && typeof categoriesMap === 'object' ? categoriesMap : {};
    const ids = Object.keys(map);
    const orderedIds = Array.isArray(categoryOrderArr) && categoryOrderArr.length > 0
        ? [...categoryOrderArr.filter((id) => map[id]), ...ids.filter((id) => !categoryOrderArr.includes(id))]
        : ids.sort((a, b) => b.localeCompare(a));
    const names = orderedIds
        .map((id) => {
            const cat = map[id];
            return cat && typeof cat.name === 'string' ? cat.name.trim() : '';
        })
        .filter(Boolean);
    return names.slice(0, 8);
}

function escapeHeaderStripPreviewText(s) {
    return String(s ?? '')
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
}

/**
 * Live preview chips + validation for Header category links (Customization → Header).
 * `categoriesMap` / `categoryOrder` come from the main dashboard Firebase cache (window.__refreshHeaderCategoryLinksUi).
 */
export function refreshHeaderCategoryLinksUi(categoriesMap, categoryOrderArr) {
    const previewEl = document.getElementById('header-nav-strip-preview');
    const validationEl = document.getElementById('header-nav-custom-categories-validation');
    if (!previewEl) return;

    const navType = (document.querySelector('input[name="header-nav-type"]:checked') || {}).value || 'categories';
    const sectionsLabels = ['Home', 'Products', 'Featured', 'Categories'];
    let chips = [];
    if (navType === 'sections') {
        chips = [...sectionsLabels];
    } else if (navType === 'customCategories') {
        const rows = getHeaderNavCustomCategoryRowsFromDOM();
        chips = rows
            .filter((r) => r.target)
            .map((r) => ((r.label && r.label.trim()) ? r.label.trim() : r.target.trim()));
    } else {
        chips = getOrderedInventoryCategoryNamesForHeaderStrip(categoriesMap, categoryOrderArr);
    }

    const chipClass = 'inline-flex items-center rounded-full border border-slate-300/90 bg-white px-3 py-1 text-xs font-semibold text-black shadow-sm';
    previewEl.innerHTML = chips.length
        ? chips.map((label) => `<span class="${chipClass}">${escapeHeaderStripPreviewText(label)}</span>`).join('')
        : '<span class="text-xs text-black italic">Nothing to show yet — add categories in Inventory or choose another mode.</span>';

    if (!validationEl) return;
    if (navType !== 'customCategories') {
        validationEl.className = 'mt-3 hidden text-sm rounded-xl border px-3 py-2.5';
        validationEl.textContent = '';
        validationEl.innerHTML = '';
        return;
    }
    const rows = getHeaderNavCustomCategoryRowsFromDOM();
    const nameSet = new Set();
    Object.values(categoriesMap || {}).forEach((cat) => {
        if (cat && typeof cat.name === 'string' && cat.name.trim()) nameSet.add(cat.name.trim());
    });
    const missingTargetLabels = rows.filter((r) => r.label && !r.target).map((r) => r.label.trim());
    const badTargets = rows
        .filter((r) => r.target && !nameSet.has(r.target.trim()))
        .map((r) => r.target.trim());
    const uniqBad = [...new Set(badTargets)];
    if (missingTargetLabels.length === 0 && uniqBad.length === 0) {
        validationEl.className = 'mt-3 hidden text-sm rounded-xl border px-3 py-2.5';
        validationEl.textContent = '';
        validationEl.innerHTML = '';
    } else {
        validationEl.classList.remove('hidden');
        validationEl.className =
            'mt-3 text-sm rounded-xl border border-amber-200 bg-amber-50 text-amber-950 px-3 py-2.5';
        const parts = [];
        if (missingTargetLabels.length) {
            parts.push(
                `<strong>${missingTargetLabels.length} row(s)</strong> need a category: ${missingTargetLabels
                    .slice(0, 5)
                    .map(escapeHeaderStripPreviewText)
                    .join(', ')}${missingTargetLabels.length > 5 ? '…' : ''}.`
            );
        }
        if (uniqBad.length) {
            parts.push(
                `<strong>${uniqBad.length} target(s)</strong> not in inventory: ${uniqBad
                    .slice(0, 6)
                    .map(escapeHeaderStripPreviewText)
                    .join(', ')}${uniqBad.length > 6 ? '…' : ''}.`
            );
        }
        validationEl.innerHTML =
            parts.join(' ') + ' Save is still allowed; fix targets so storefront links work.';
    }
}

/** For save feedback: rows missing target or target not in inventory. */
export function countUnmatchedHeaderCustomCategoryNames(categoriesMap) {
    const navType = (document.querySelector('input[name="header-nav-type"]:checked') || {}).value || 'categories';
    if (navType !== 'customCategories') return 0;
    const nameSet = new Set();
    Object.values(categoriesMap || {}).forEach((cat) => {
        if (cat && typeof cat.name === 'string' && cat.name.trim()) nameSet.add(cat.name.trim());
    });
    let n = 0;
    getHeaderNavCustomCategoryRowsFromDOM().forEach((r) => {
        if (r.label && !r.target) n += 1;
        else if (r.target && !nameSet.has(r.target.trim())) n += 1;
    });
    return n;
}

/** Fills Desktop search bar max-width input from saved layout or theme default */
export function refreshSearchWidthInputFromLayout(layout, headerDesignOverride) {
    const l = layout || {};
    const hdrSel = document.getElementById('section-design-header');
    const rawHd = headerDesignOverride != null ? headerDesignOverride : ((hdrSel && hdrSel.value) ? hdrSel.value : (l.headerDesign || 'default'));
    const hd = ['default', 'minimal', 'shwapno', 'centerTrade'].includes(rawHd) ? rawHd : 'default';
    const defW = getDefaultSearchBarWidthPx(hd);
    const el = document.getElementById('section-design-desktop-search-max-width');
    const sw = l.searchBarWidth;
    if (el) el.value = (sw != null && sw !== '') ? String(Number(sw)) : String(defW);
}

/** Fills Section designs header height inputs from saved layout or theme defaults */
export function refreshHeaderHeightInputsFromLayout(layout, headerDesignOverride) {
    const l = layout || {};
    const hdrSel = document.getElementById('section-design-header');
    const rawHd = headerDesignOverride != null ? headerDesignOverride : ((hdrSel && hdrSel.value) ? hdrSel.value : (l.headerDesign || 'default'));
    const hd = ['default', 'minimal', 'shwapno', 'centerTrade'].includes(rawHd) ? rawHd : 'default';
    const def = getDefaultLayoutHeaderHeights(hd);
    const desktopEl = document.getElementById('section-design-desktop-header-height');
    const mobileEl = document.getElementById('section-design-mobile-header-height');
    const dh = l.headerHeight;
    const dm = l.mobileHeaderHeight;
    if (desktopEl) desktopEl.value = (dh != null && dh !== '') ? String(Number(dh)) : String(def.desktop);
    if (mobileEl) mobileEl.value = (dm != null && dm !== '') ? String(Number(dm)) : String(def.mobile);
}

/** Header spacing + Shwapno gap fields: range + compact number + reset (ids unchanged for save logic). */
export const HEADER_SPACING_SLIDER_CONFIG = [
    { id: 'section-design-header-main-gap', min: 4, max: 64, defaultPx: 24 },
    { id: 'section-design-header-nav-gap', min: 4, max: 64, defaultPx: 20 },
    { id: 'section-design-header-icon-gap', min: 4, max: 64, defaultPx: 24 },
    { id: 'section-design-header-icon-size-pc', min: 10, max: 48, defaultPx: 20 },
    { id: 'section-design-header-icon-size-mobile', min: 10, max: 48, defaultPx: 24 },
    { id: 'section-design-shwapno-gap-main', min: 4, max: 64, defaultPx: 10 },
    { id: 'section-design-shwapno-gap-app', min: 4, max: 64, defaultPx: 16 },
    { id: 'section-design-shwapno-gap-lang', min: 4, max: 64, defaultPx: 12 },
    { id: 'section-design-shwapno-gap-auth', min: 0, max: 48, defaultPx: 6 },
    { id: 'section-design-shwapno-gap-logo', min: 0, max: 64, defaultPx: 10 },
    { id: 'section-design-shwapno-gap-right', min: 0, max: 48, defaultPx: 8 },
    { id: 'section-design-shwapno-auth-font-size', min: 10, max: 24, defaultPx: 11 }
];

let _headerSpacingSliderBindingsDone = false;
let _bannerSideGapBindingsDone = false;
let _shellMaxWidthBindingsDone = false;
let _bannerGapSavedLayoutSnapshot = null;

function parseBoundedInt(raw, min, max, fallback) {
    const s = String(raw ?? '').trim();
    if (!s) return fallback;
    const n = parseInt(s, 10);
    if (!Number.isFinite(n)) return fallback;
    return Math.min(max, Math.max(min, n));
}

function getDesktopGapStatusModel() {
    const layout = _bannerGapSavedLayoutSnapshot || {};
    const bannerConfig = layout.bannerConfig || {};
    const globalInputEl = document.getElementById('banner-desktop-content-side-gap');
    const sideSpaceInputEl = document.getElementById('banner-space-desktop');
    const presetEl = document.getElementById('banner-main-hero-preset');
    const desktopLayoutEl = document.getElementById('banner-desktop-main-slider-layout');
    const homeModeEl = document.getElementById('classic-home-layout-mode');

    const savedGlobalRaw = layout.desktopContentSideGapPx;
    const savedGlobal = (savedGlobalRaw != null && savedGlobalRaw !== '' && Number.isFinite(Number(savedGlobalRaw)))
        ? Math.min(120, Math.max(0, Number(savedGlobalRaw)))
        : null;
    const savedGlobalEffective = savedGlobal != null ? savedGlobal : 0;
    const draftGlobal = parseBoundedInt(globalInputEl && globalInputEl.value, 0, 120, savedGlobal);
    const draftGlobalEffective = draftGlobal != null ? draftGlobal : 0;

    const savedSideRaw = bannerConfig.sideSpacingDesktop;
    const savedBannerInset = (savedSideRaw != null && savedSideRaw !== '' && Number.isFinite(Number(savedSideRaw)))
        ? Math.min(80, Math.max(0, Number(savedSideRaw)))
        : 32;
    const draftBannerInset = parseBoundedInt(sideSpaceInputEl && sideSpaceInputEl.value, 0, 80, savedBannerInset);

    const homeLayoutMode = (homeModeEl && homeModeEl.value) || layout.homeLayoutMode || 'full';
    const preset = (presetEl && presetEl.value) || bannerConfig.mainHeroPreset || 'standard';
    const desktopMainSliderLayout = (desktopLayoutEl && desktopLayoutEl.value) || bannerConfig.desktopMainSliderLayout || 'inset';
    const hasDraftGlobal = draftGlobal != null && Number.isFinite(Number(draftGlobal));

    let isFullBleed = false;
    let fullBleedReason = '';
    if (preset === 'fabrilife') {
        isFullBleed = true;
        fullBleedReason = 'Hero preset: Fabrilife';
    } else if (desktopMainSliderLayout === 'fullBleed') {
        isFullBleed = true;
        fullBleedReason = 'Desktop slider layout: Full width';
    } else if (draftGlobalEffective === 0 && homeLayoutMode !== 'boxed') {
        isFullBleed = true;
        fullBleedReason = 'Global gap is 0 (full mode)';
    }

    const effectiveBannerInset = isFullBleed
        ? 0
        : (hasDraftGlobal ? draftGlobalEffective : draftBannerInset);

    return {
        savedGlobal,
        savedGlobalEffective,
        draftGlobalEffective,
        draftBannerInset: effectiveBannerInset,
        isFullBleed,
        fullBleedReason,
        deltaGlobal: draftGlobalEffective - savedGlobalEffective
    };
}

function updateBannerSideGapStatus() {
    const savedEl = document.getElementById('banner-desktop-gutter-saved-value');
    const effectiveEl = document.getElementById('banner-desktop-gutter-effective-value');
    const insetEl = document.getElementById('banner-desktop-banner-inset-effective-value');
    const deltaEl = document.getElementById('banner-desktop-gutter-delta-value');
    if (!savedEl && !effectiveEl && !insetEl && !deltaEl) return;
    const status = getDesktopGapStatusModel();
    if (savedEl) {
        savedEl.textContent = status.savedGlobal != null ? `${status.savedGlobal}px` : 'Not set';
    }
    if (effectiveEl) {
        effectiveEl.textContent = `${status.draftGlobalEffective}px`;
    }
    if (insetEl) {
        insetEl.textContent = status.isFullBleed
            ? `0px (ignored: ${status.fullBleedReason})`
            : `${status.draftBannerInset}px`;
    }
    if (deltaEl) {
        const d = status.deltaGlobal;
        const sign = d > 0 ? '+' : '';
        deltaEl.textContent = `${sign}${d}px`;
    }
}

export function ensureBannerSideGapBindings() {
    if (_bannerSideGapBindingsDone) return;
    const num = document.getElementById('banner-desktop-content-side-gap');
    const range = document.getElementById('banner-desktop-content-side-gap-range');
    const reset = document.getElementById('banner-desktop-content-side-gap-reset');
    if (!num || !range || !reset) return;
    const clamp = (v) => Math.min(120, Math.max(0, v));
    const refreshPreview = () => {
        try {
            refreshAllMainSlideBannerPreviews();
            updateMainBannerGlobalRatioHint();
        } catch (_) {}
        updateBannerSideGapStatus();
    };
    const syncRangeFromNum = () => {
        const raw = String(num.value || '').trim();
        if (raw === '') {
            range.value = '0';
            refreshPreview();
            return;
        }
        const n = clamp(parseInt(raw, 10) || 0);
        num.value = String(n);
        range.value = String(n);
        refreshPreview();
    };
    range.addEventListener('input', () => {
        num.value = range.value;
        refreshPreview();
    });
    num.addEventListener('input', syncRangeFromNum);
    num.addEventListener('change', syncRangeFromNum);
    reset.addEventListener('click', (e) => {
        e.preventDefault();
        num.value = '';
        range.value = '0';
        refreshPreview();
    });
    ['banner-space-desktop', 'banner-main-hero-preset', 'banner-desktop-main-slider-layout', 'classic-home-layout-mode'].forEach((id) => {
        const el = document.getElementById(id);
        if (!el) return;
        el.addEventListener('input', updateBannerSideGapStatus);
        el.addEventListener('change', updateBannerSideGapStatus);
    });
    _bannerSideGapBindingsDone = true;
    updateBannerSideGapStatus();
}

const SHELL_MAX_WIDTH_PAIR_IDS = [
    { num: 'banner-desktop-shell-max-width', range: 'banner-desktop-shell-max-width-range', reset: 'banner-desktop-shell-max-width-reset' },
    { num: 'section-design-desktop-shell-max-width', range: 'section-design-desktop-shell-max-width-range', reset: 'section-design-desktop-shell-max-width-reset' }
];

export function hydrateDesktopShellMaxWidthFields(layout) {
    const l = layout || {};
    const raw = l.desktopShellMaxWidthPx;
    const has = raw != null && String(raw).trim() !== '' && Number.isFinite(Number(raw));
    const n = has ? Math.min(1800, Math.max(1100, Number(raw))) : null;
    const dashboardNum = document.getElementById('banner-desktop-shell-max-width');
    const dashboardRange = document.getElementById('banner-desktop-shell-max-width-range');
    const adminNum = document.getElementById('section-design-desktop-shell-max-width');
    const adminRange = document.getElementById('section-design-desktop-shell-max-width-range');
    if (dashboardNum) dashboardNum.value = n != null ? String(n) : '';
    if (dashboardRange) dashboardRange.value = n != null ? String(n) : '1320';
    if (adminNum) adminNum.value = n != null ? String(n) : '';
    if (adminRange) adminRange.value = n != null ? String(n) : '1320';
}

export function ensureDesktopShellMaxWidthBindings() {
    if (_shellMaxWidthBindingsDone) return;
    const minW = 1100;
    const maxW = 1800;
    const defaultVisual = 1320;
    const clamp = (v) => Math.min(maxW, Math.max(minW, v));
    let boundAny = false;
    SHELL_MAX_WIDTH_PAIR_IDS.forEach(({ num: numId, range: rangeId, reset: resetId }) => {
        const num = document.getElementById(numId);
        const range = document.getElementById(rangeId);
        const reset = document.getElementById(resetId);
        if (!num || !range || !reset) return;
        boundAny = true;
        const syncRangeFromNum = () => {
            const raw = String(num.value || '').trim();
            if (raw === '') {
                range.value = String(defaultVisual);
                return;
            }
            const n = clamp(parseInt(raw, 10) || defaultVisual);
            num.value = String(n);
            range.value = String(n);
        };
        range.addEventListener('input', () => {
            num.value = range.value;
        });
        num.addEventListener('input', syncRangeFromNum);
        num.addEventListener('change', syncRangeFromNum);
        reset.addEventListener('click', (e) => {
            e.preventDefault();
            num.value = '';
            range.value = String(defaultVisual);
        });
    });
    if (boundAny) _shellMaxWidthBindingsDone = true;
}

export function ensureHeaderSpacingSliderBindings() {
    if (_headerSpacingSliderBindingsDone) return;
    let bound = 0;
    HEADER_SPACING_SLIDER_CONFIG.forEach(({ id, min, max, defaultPx }) => {
        const num = document.getElementById(id);
        const range = document.getElementById(`${id}-range`);
        const reset = document.getElementById(`${id}-reset`);
        if (!num || !range || !reset) return;
        if (num.dataset.spacingSliderBound === '1') {
            bound += 1;
            return;
        }
        num.dataset.spacingSliderBound = '1';
        const clamp = (v) => Math.min(max, Math.max(min, v));
        const defaultVisual = clamp(defaultPx);
        const syncRangeFromNum = () => {
            const raw = String(num.value || '').trim();
            if (raw === '') {
                range.value = String(defaultVisual);
            } else {
                const n = clamp(parseInt(raw, 10) || min);
                num.value = String(n);
                range.value = String(n);
            }
        };
        range.addEventListener('input', () => {
            num.value = range.value;
        });
        num.addEventListener('input', () => {
            const raw = String(num.value || '').trim();
            if (raw === '') {
                range.value = String(defaultVisual);
                return;
            }
            const n = clamp(parseInt(raw, 10) || min);
            num.value = String(n);
            range.value = String(n);
        });
        num.addEventListener('change', syncRangeFromNum);
        reset.addEventListener('click', (e) => {
            e.preventDefault();
            num.value = '';
            range.value = String(defaultVisual);
        });
        bound += 1;
    });
    if (bound === HEADER_SPACING_SLIDER_CONFIG.length) {
        _headerSpacingSliderBindingsDone = true;
        syncHeaderSpacingSlidersFromInputs();
    }
}

export function syncHeaderSpacingSlidersFromInputs() {
    HEADER_SPACING_SLIDER_CONFIG.forEach(({ id, min, max, defaultPx }) => {
        const num = document.getElementById(id);
        const range = document.getElementById(`${id}-range`);
        if (!num || !range) return;
        const clamp = (v) => Math.min(max, Math.max(min, v));
        const defaultVisual = clamp(defaultPx);
        const raw = String(num.value || '').trim();
        if (raw === '') range.value = String(defaultVisual);
        else {
            const n = clamp(parseInt(raw, 10) || min);
            range.value = String(n);
        }
    });
}

/** Fills dashboard form fields from RTDB `siteSettings`. Safe to call again after lazy-loaded `dashboard-settings.html` is injected (pixel/TikTok/GTM, etc.). */
export function populateSiteSettings(s) {
    ensureHeaderSpacingSliderBindings();
    ensureBannerSideGapBindings();
    ensureDesktopShellMaxWidthBindings();
    if (!s) return;

    const getProp = (obj, path) => path.split('.').reduce((o, key) => (o && o[key] !== 'undefined') ? o[key] : undefined, obj);
    
    // Expanded Mapping for Advanced Colors
    const mapping = { 
        'site-name': 'siteName', 
        'topbar-text': 'textContent.topBarText', 
        'logo-url': 'logoUrl',
        'footer-logo-url': 'footerLogoUrl',
        'about-title': 'textContent.aboutTitle', 
        'about-image-url': 'textContent.aboutImageUrl', 
        'about-description': 'textContent.aboutDescription', 
        'copyright-text': 'textContent.copyright', 
        'developer-credit': 'textContent.developerCredit',
        'imgbb-api-key': 'imgbbApiKey',
        'daiman-api-key': 'daimanApiKey',
        'openai-api-key-dash': 'openaiApiKey',
        'openai-model-dash': 'openaiModel',
        
        // Color Mapping (Picker)
        'color-body-bg': 'colors.body_bg',
        'color-text-primary': 'colors.text_primary',
        'color-text-secondary': 'colors.text_secondary',
        'color-accent': 'colors.accent_color',
        'color-header-bg': 'colors.header_bg',
        'color-header-text': 'colors.header_text',
        'color-search-btn-bg': 'colors.search_btn_bg',
        'color-search-btn-text': 'colors.search_btn_text',
        'color-search-icon': 'colors.search_icon_color',
        'color-menu-bg': 'colors.menu_bg',
        'color-menu-text': 'colors.menu_text',
        'color-category-pill-text': 'colors.category_pill_text',
        'color-category-pill-bg': 'colors.category_pill_bg',
        'color-category-card-label-text': 'colors.category_card_label_text',
        'color-category-card-label-bg': 'colors.category_card_label_bg',
        'color-cbs-tab-active-bg': 'colors.cbs_tab_active_bg',
        'color-cbs-tab-active-text': 'colors.cbs_tab_active_text',
        'color-cbs-tab-inactive-bg': 'colors.cbs_tab_inactive_bg',
        'color-cbs-tab-inactive-text': 'colors.cbs_tab_inactive_text',
        'color-btn-primary-bg': 'colors.btn_primary_bg',
        'color-btn-primary-text': 'colors.btn_primary_text',
        'color-btn-secondary-bg': 'colors.btn_secondary_bg',
        'color-btn-secondary-text': 'colors.btn_secondary_text',
        'color-card-bg': 'colors.card_bg',
        'color-price-text': 'colors.price_text',
        'color-footer-bg': 'colors.footer_bg',
        'color-footer-text': 'colors.footer_text',
        'color-top-bar-bg': 'colors.top_bar_bg',
        'color-top-bar-text': 'colors.top_bar_text',

        // Other Settings
        'contact-phone': 'contactInfo.phoneNumber', 
        'contact-hotline': 'contactInfo.hotlineNumber', 
        'contact-whatsapp': 'contactInfo.whatsappNumber', 
        'contact-messenger': 'contactInfo.messengerPageId',
        'contact-address': 'contactInfo.address', 
        'feature1-title': 'productPageText.feature1Title', 
        'feature1-subtitle': 'productPageText.feature1Subtitle', 
        'feature2-title': 'productPageText.feature2Title', 
        'feature2-subtitle': 'productPageText.feature2Subtitle', 
        'feature3-title': 'productPageText.feature3Title', 
        'feature3-subtitle': 'productPageText.feature3Subtitle', 
        'related-products-title': 'productPageText.relatedProductsTitle',
        'delivery-time-text': 'deliveryTimeText',
        'pixel-id': 'pixelId', 
        'push-enabled': 'pushConfig.enabled',
        'push-vapid-public-key': 'pushConfig.vapidPublicKey',
        'push-default-icon': 'pushConfig.defaultIconUrl',
        'tiktok-pixel-id': 'tiktokConfig.pixelId',
        'tiktok-access-token': 'tiktokConfig.accessToken',
        'tiktok-test-event-code': 'tiktokConfig.testEventCode',
        'tiktok-enabled': 'tiktokConfig.enabled',
        'steadfast-api-key': 'steadfastConfig.apiKey', 
        'steadfast-secret-key': 'steadfastConfig.secretKey',
        'steadfast-api-base-url': 'steadfastConfig.baseUrl',
        'prevent-duplicate-orders': 'preventDuplicateOrders24h',
        'duplicate-order-hours': 'duplicateOrderHours',
        'fb-access-token': 'facebookConfig.accessToken',
        'fb-ad-account-id': 'facebookConfig.adAccountId',
        
        // GTM Mapping (NEW)
        'gtm-id': 'gtmId'
    };

    const capiEn = document.getElementById('fb-capi-enabled');
    if (capiEn) capiEn.checked = getProp(s, 'facebookConfig.capiEnabled') !== false;

    const blockedMsg = document.getElementById('blocked-visitor-message');
    if (blockedMsg) {
        const v = getProp(s, 'security.blockedVisitorMessage');
        blockedMsg.value = v != null ? String(v) : '';
    }

    for (const [id, path] of Object.entries(mapping)) { 
        const el = document.getElementById(id); 
        if (el) { 
            let value = getProp(s, path);
            
            // --- SMART DEFAULT LOGIC (New) ---
            // If checking a color field and it's empty, use the DEFAULT value.
            if (id.startsWith('color-')) {
                const colorKey = id.replace('color-', ''); // e.g., 'header-bg'
                if (!value && DEFAULT_THEME_COLORS[colorKey]) {
                    value = DEFAULT_THEME_COLORS[colorKey];
                }
            }
            if (id === 'duplicate-order-hours') {
                let n = typeof value === 'number' ? value : parseInt(String(value), 10);
                if (!Number.isFinite(n) || n < 1) n = 24;
                value = Math.min(8760, n);
            }

            if (el.type === 'checkbox') {
                el.checked = Boolean(value);
            } else {
                el.value = value || '';
            }
            
            // Sync Hex Input for Colors
            if (id.startsWith('color-')) {
                const hexEl = document.getElementById(`${id}-hex`);
                if (hexEl) hexEl.value = value || '#000000';
            }
        } 
    }

    const defaultHomepageFeatures = [
        { icon: 'fa-solid fa-truck-fast', title: '60 Mins Delivery', subtitle: 'Free shipping over 1500tk' },
        { icon: 'fa-solid fa-shield-halved', title: 'Authorized Products', subtitle: 'Within 30 days for an exchange' },
        { icon: 'fa-solid fa-headset', title: 'Customer Service Support', subtitle: '8am to 10pm' },
        { icon: 'fa-solid fa-credit-card', title: 'Flexible Payments', subtitle: 'Pay with multiple credit cards' }
    ];
    const homepageFeatures = (s.layout && s.layout.homepageFeatures) ? s.layout.homepageFeatures : {};
    const serviceStripOn = homepageFeatures.enabled === true;
    const homepageFeatureEnabledEl = document.getElementById('homepage-features-enabled');
    if (homepageFeatureEnabledEl) homepageFeatureEnabledEl.checked = serviceStripOn;
    const homeDesignServiceStripEl = document.getElementById('home-design-service-strip-enabled');
    if (homeDesignServiceStripEl) homeDesignServiceStripEl.checked = serviceStripOn;
    ensureServiceStripToggleSync();
    const homepageFeatureItems = Array.isArray(homepageFeatures.items) ? homepageFeatures.items : [];
    for (let i = 0; i < 4; i += 1) {
        const defaults = defaultHomepageFeatures[i];
        const row = homepageFeatureItems[i] && typeof homepageFeatureItems[i] === 'object' ? homepageFeatureItems[i] : {};
        const iconEl = document.getElementById(`homepage-feature-${i + 1}-icon`);
        const titleEl = document.getElementById(`homepage-feature-${i + 1}-title`);
        const subtitleEl = document.getElementById(`homepage-feature-${i + 1}-subtitle`);
        if (iconEl) iconEl.value = String(row.icon || defaults.icon || '').trim();
        if (titleEl) titleEl.value = String(row.title || defaults.title || '').trim();
        if (subtitleEl) subtitleEl.value = String(row.subtitle || defaults.subtitle || '').trim();
    }

    const optStyles = homepageFeatures.styles && typeof homepageFeatures.styles === 'object' ? homepageFeatures.styles : {};
    const setOptionalContentColorPair = (pickerId, hexId, rawVal) => {
        const picker = document.getElementById(pickerId);
        const hexEl = document.getElementById(hexId);
        if (!picker || !hexEl) return;
        const v = normalizeOptionalContentCssColor(rawVal);
        if (!v) {
            hexEl.value = '';
            picker.value = '#ffffff';
            return;
        }
        hexEl.value = v;
        picker.value = v === 'transparent' ? '#ffffff' : v;
    };
    setOptionalContentColorPair('optional-content-card-bg', 'optional-content-card-bg-hex', optStyles.cardBg);
    setOptionalContentColorPair('optional-content-card-border', 'optional-content-card-border-hex', optStyles.cardBorder);
    setOptionalContentColorPair('optional-content-icon-bg', 'optional-content-icon-bg-hex', optStyles.iconBg);
    setOptionalContentColorPair('optional-content-icon-border', 'optional-content-icon-border-hex', optStyles.iconBorder);
    setOptionalContentColorPair('optional-content-icon-fg', 'optional-content-icon-fg-hex', optStyles.iconColor);
    setOptionalContentColorPair('optional-content-title-color', 'optional-content-title-color-hex', optStyles.titleColor);
    setOptionalContentColorPair('optional-content-subtitle-color', 'optional-content-subtitle-color-hex', optStyles.subtitleColor);
    const shadowInp = document.getElementById('optional-content-card-shadow');
    if (shadowInp) shadowInp.value = optStyles.cardShadow != null ? String(optStyles.cardShadow) : '';
    const gapInp = document.getElementById('optional-content-grid-gap');
    if (gapInp) {
        const g = optStyles.gridGapPx;
        gapInp.value = g != null && g !== '' ? String(g) : '';
    }
    syncOptionalContentIconPickerHighlight();

    // Recommended For You (Optional Content tab) — list depends on `allProducts`; dashboard.js refreshes after populate.
    try {
        if (typeof window.refreshRecommendedForYouDashboardControls === 'function') {
            window.refreshRecommendedForYouDashboardControls();
        }
    } catch (_) { /* noop */ }
    try {
        if (typeof window.refreshHomeCategoryRowsSectionDashboardControls === 'function') {
            window.refreshHomeCategoryRowsSectionDashboardControls();
        }
    } catch (_) { /* noop */ }
    try {
        if (typeof window.refreshCategoryBannerSectionDashboardControls === 'function') {
            window.refreshCategoryBannerSectionDashboardControls();
        }
    } catch (_) { /* noop */ }

    setUploadConfig(
        getProp(s, 'imageUploadProvider'),
        getProp(s, 'imgbbApiKey') || '',
        getProp(s, 'daimanApiKey') || ''
    );

    const iupEl = document.getElementById('image-upload-provider');
    if (iupEl) {
        const v = getProp(s, 'imageUploadProvider');
        iupEl.value = v === 'daiman' ? 'daiman' : 'imgbb';
    }
    syncImageUploadProviderFields();

    const fsTh = s.freeShippingThreshold;
    document.querySelectorAll('.free-shipping-threshold-input').forEach((el) => {
        el.value = (fsTh != null && fsTh !== '') ? fsTh : '';
    });

    // --- Mobile bottom CTA bar (Classic product page) ---
    const updateMobileBottomBarSecondaryVisibility = () => {
        const cnt = document.getElementById('mobile-bottom-bar-btn-count');
        const wrap = document.getElementById('mobile-bottom-secondary-wrap');
        if (!wrap) return;
        const one = cnt && String(cnt.value) === '1';
        wrap.classList.toggle('hidden', !!one);
    };
    const mb = s.mobileBottomBar || {};
    const mbCap = document.getElementById('mobile-bottom-bar-caption');
    if (mbCap) mbCap.value = mb.captionAbove != null ? String(mb.captionAbove) : '';
    const mbCnt = document.getElementById('mobile-bottom-bar-btn-count');
    if (mbCnt) mbCnt.value = Number(mb.buttonCount) === 1 ? '1' : '2';
    const mbPl = document.getElementById('mobile-bottom-primary-label');
    if (mbPl) mbPl.value = mb.primaryLabel != null && String(mb.primaryLabel).trim() !== '' ? String(mb.primaryLabel) : 'Add to Cart';
    const mbPa = document.getElementById('mobile-bottom-primary-action');
    if (mbPa) mbPa.value = mb.primaryAction === 'buyNow' ? 'buyNow' : 'addToCart';
    const mbSl = document.getElementById('mobile-bottom-secondary-label');
    if (mbSl) mbSl.value = mb.secondaryLabel != null && String(mb.secondaryLabel).trim() !== '' ? String(mb.secondaryLabel) : 'Buy Now';
    const mbSa = document.getElementById('mobile-bottom-secondary-action');
    if (mbSa) mbSa.value = mb.secondaryAction === 'addToCart' ? 'addToCart' : 'buyNow';
    updateMobileBottomBarSecondaryVisibility();
    if (mbCnt && !mbCnt.dataset.mobileBarBound) {
        mbCnt.dataset.mobileBarBound = '1';
        mbCnt.addEventListener('change', updateMobileBottomBarSecondaryVisibility);
    }

    // --- NEW: POPULATE BANNER VISIBILITY TOGGLES ---
    const visibility = s.bannerVisibility || {
        mainSlider: true,
        smallBanners: true,
        promoBanner1: true,
        promoBanner2: true
    };

    const setToggle = (id, val) => {
        const el = document.getElementById(id);
        // If val is undefined (old data), default to true (visible)
        if(el) el.checked = (val !== false); 
    };

    setToggle('visibility-main-slider', visibility.mainSlider);
    setToggle('visibility-small-banners', visibility.smallBanners);
    setToggle('visibility-promo-1', visibility.promoBanner1);
    setToggle('visibility-promo-2', visibility.promoBanner2);

    const fpp = s.facebookPagePost || {};
    const defaultFbTemplate = 'New Product!\n\n{name}\n{price}\n{link}\n\n{description}';
    const fpId = document.getElementById('fb-page-id');
    if (fpId) fpId.value = fpp.pageId || '';
    const fpTok = document.getElementById('fb-page-access-token');
    if (fpTok) fpTok.value = fpp.pageAccessToken || '';
    const fpName = document.getElementById('fb-page-name');
    if (fpName) fpName.value = fpp.pageName || '';
    const fpAuto = document.getElementById('fb-page-auto-post');
    if (fpAuto) fpAuto.checked = !!fpp.autoPost;
    const fpTpl = document.getElementById('fb-page-post-template');
    if (fpTpl) fpTpl.value = (fpp.postTemplate != null && String(fpp.postTemplate).trim() !== '') ? fpp.postTemplate : defaultFbTemplate;

    const layout = s.layout || {};
    _bannerGapSavedLayoutSnapshot = layout;
    const setSelect = (id, val, allowed) => {
        const el = document.getElementById(id);
        if (el && allowed && allowed.includes(val)) el.value = val;
    };
    setSelect('section-design-header', layout.headerDesign || 'default', ['default', 'minimal', 'shwapno', 'centerTrade']);
    refreshHeaderHeightInputsFromLayout(layout);
    refreshSearchWidthInputFromLayout(layout);
    const setHeaderSpacingInput = (id, val) => {
        const el = document.getElementById(id);
        if (!el) return;
        el.value = (val != null && val !== '' && Number.isFinite(Number(val))) ? String(Number(val)) : '';
    };
    const bannerSideGapEl = document.getElementById('banner-desktop-content-side-gap');
    if (bannerSideGapEl) {
        bannerSideGapEl.value = (layout.desktopContentSideGapPx != null && layout.desktopContentSideGapPx !== '' && Number.isFinite(Number(layout.desktopContentSideGapPx)))
            ? String(Number(layout.desktopContentSideGapPx))
            : '';
    }
    const bannerSideGapRangeEl = document.getElementById('banner-desktop-content-side-gap-range');
    if (bannerSideGapRangeEl) {
        const rawGap = String(layout.desktopContentSideGapPx ?? '').trim();
        bannerSideGapRangeEl.value = rawGap && Number.isFinite(Number(rawGap))
            ? String(Math.min(120, Math.max(0, parseInt(rawGap, 10) || 0)))
            : '0';
    }
    hydrateDesktopShellMaxWidthFields(layout);
    updateBannerSideGapStatus();
    setHeaderSpacingInput('section-design-header-main-gap', layout.headerMainGapPx);
    setHeaderSpacingInput('section-design-header-nav-gap', layout.headerNavGapPx);
    setHeaderSpacingInput('section-design-header-icon-gap', layout.headerIconGapPx);
    setHeaderSpacingInput('section-design-header-icon-size-pc', layout.headerIconSizePcPx);
    setHeaderSpacingInput('section-design-header-icon-size-mobile', layout.headerIconSizeMobilePx);
    setHeaderSpacingInput('section-design-shwapno-gap-main', layout.shwapnoGapMainPx);
    setHeaderSpacingInput('section-design-shwapno-gap-app', layout.shwapnoGapAppPx);
    setHeaderSpacingInput('section-design-shwapno-gap-lang', layout.shwapnoGapLangPx);
    setHeaderSpacingInput('section-design-shwapno-gap-auth', layout.shwapnoGapAuthPx);
    setHeaderSpacingInput('section-design-shwapno-gap-logo', layout.shwapnoGapLogoLocPx);
    setHeaderSpacingInput('section-design-shwapno-gap-right', layout.shwapnoGapRightPx);
    setHeaderSpacingInput('section-design-shwapno-auth-font-size', layout.shwapnoAuthFontSizePx);
    syncHeaderSpacingSlidersFromInputs();
    const shwapnoAppImgIn = document.getElementById('shwapno-app-img-url');
    const shwapnoAppLinkIn = document.getElementById('shwapno-app-link-url');
    if (shwapnoAppImgIn) shwapnoAppImgIn.value = (layout.shwapnoAppImgUrl != null && String(layout.shwapnoAppImgUrl).trim() !== '') ? String(layout.shwapnoAppImgUrl).trim() : '';
    if (shwapnoAppLinkIn) shwapnoAppLinkIn.value = (layout.shwapnoAppLinkUrl != null && String(layout.shwapnoAppLinkUrl).trim() !== '') ? String(layout.shwapnoAppLinkUrl).trim() : '';
    updateImagePreview('#shwapno-app-img-url', '#shwapno-app-img-preview');
    setSelect('classic-home-layout-mode', layout.homeLayoutMode === 'boxed' ? 'boxed' : 'full', ['full', 'boxed']);
    setSelect('section-design-banner', layout.bannerDesign || 'full', ['full', 'split']);
    setSelect('section-design-footer', layout.footerDesign || 'columns', ['columns', 'centered']);
    setSelect('section-design-product-card', layout.productDisplayStyle || 'classic', ['classic', 'app', 'minimal', 'imageFocus', 'chaldal', 'daraz', 'shwapno', 'orderCall']);
    const detailLayoutVal =
        layout.productDetailLayout === 'mobileCommerceV1' || layout.productDetailLayout === 'mobileCommerceV2'
            ? layout.productDetailLayout
            : 'standard';
    setSelect(
        'section-design-product-detail-layout',
        detailLayoutVal,
        ['standard', 'mobileCommerceV1', 'mobileCommerceV2']
    );
    const classicCardSizeEl = document.getElementById('section-design-classic-card-size');
    const classicCardWidthPxEl = document.getElementById('section-design-classic-card-width-px');
    const classicCardHeightPxEl = document.getElementById('section-design-classic-card-height-px');
    const appCardHeightEl = document.getElementById('section-design-app-card-height');
    const appCardWidthEl = document.getElementById('section-design-app-card-width');
    if (classicCardSizeEl) classicCardSizeEl.value = (layout.classicCardSize && ['small','medium','large'].includes(layout.classicCardSize)) ? layout.classicCardSize : 'medium';
    if (classicCardWidthPxEl) classicCardWidthPxEl.value = (layout.classicCardWidthPx != null && layout.classicCardWidthPx !== '') ? Number(layout.classicCardWidthPx) : '';
    if (classicCardHeightPxEl) classicCardHeightPxEl.value = (layout.classicCardHeightPx != null && layout.classicCardHeightPx !== '') ? Number(layout.classicCardHeightPx) : '';
    if (appCardHeightEl) appCardHeightEl.value = (layout.appStyleCardHeight && ['small','medium','large'].includes(layout.appStyleCardHeight)) ? layout.appStyleCardHeight : 'medium';
    if (appCardWidthEl) appCardWidthEl.value = (layout.appStyleCardWidth && ['small','medium','large'].includes(layout.appStyleCardWidth)) ? layout.appStyleCardWidth : 'medium';
    const classicWrap = document.getElementById('section-design-classic-card-size-wrap');
    const appWrap = document.getElementById('section-design-app-card-size-wrap');
    const productCardStyle = layout.productDisplayStyle || 'classic';
    if (classicWrap) classicWrap.classList.toggle('hidden', productCardStyle === 'app' || productCardStyle === 'orderCall');
    if (appWrap) appWrap.classList.toggle('hidden', !['app'].includes(productCardStyle));
    const ocWrap = document.getElementById('order-call-order-now-colors-wrap');
    if (ocWrap) ocWrap.classList.toggle('hidden', productCardStyle !== 'orderCall');
    const setOrderCallHexPair = (pickerId, hexId, stored) => {
        const picker = document.getElementById(pickerId);
        const hexEl = document.getElementById(hexId);
        if (!picker || !hexEl) return;
        const raw = stored != null && String(stored).trim() ? String(stored).trim() : '';
        const v = raw && !raw.startsWith('#') ? `#${raw}` : raw;
        if (/^#[0-9A-Fa-f]{6}$/i.test(v)) {
            hexEl.value = v;
            picker.value = v;
        } else {
            hexEl.value = '';
            picker.value = pickerId === 'order-call-order-now-text' ? '#ffffff' : '#006837';
        }
    };
    setOrderCallHexPair('order-call-order-now-bg', 'order-call-order-now-bg-hex', layout.orderCallOrderNowBg);
    setOrderCallHexPair('order-call-order-now-text', 'order-call-order-now-text-hex', layout.orderCallOrderNowText);
    setSelect('section-design-section-header', layout.sectionHeaderDesign || 'default', ['default', 'centered', 'underline']);
    setSelect('section-design-small-banners', layout.smallBannersLayout || 'equal', ['equal', 'bigSmall']);
    setSelect('section-design-promo-banner', layout.promoBannerDesign || 'default', ['default', 'fullBleed', 'card']);
    setSelect('section-design-featured', layout.featuredSectionDesign || 'default', ['default', 'compact', 'grid']);
    setSelect('section-design-category-rows', layout.categoryRowsDesign || 'default', ['default', 'card', 'minimal']);
    setSelect('section-design-bottom-nav', layout.bottomNavDesign || 'default', ['default', 'centerProminent', 'iconsOnly', 'hidden']);
    setSelect('section-design-cart-drawer', layout.cartDrawerDesign || 'default', ['default', 'sheet', 'minimal']);
    setSelect('section-design-cart-bag', layout.cartIconStyle || 'default', ['default', 'groceryCart']);
    const groceryWrap = document.getElementById('grocery-cart-colors-wrap');
    if (groceryWrap) groceryWrap.classList.toggle('hidden', (layout.cartIconStyle || 'default') !== 'groceryCart');
    const groceryTop = document.getElementById('grocery-cart-top-color');
    const groceryBottom = document.getElementById('grocery-cart-bottom-color');
    if (groceryTop) groceryTop.value = (layout.groceryCartTopColor != null && layout.groceryCartTopColor !== '') ? String(layout.groceryCartTopColor) : '#dc2626';
    if (groceryBottom) groceryBottom.value = (layout.groceryCartBottomColor != null && layout.groceryCartBottomColor !== '') ? String(layout.groceryCartBottomColor) : '#1f2937';
    setSelect('section-design-hamburger-menu', layout.hamburgerMenuStyle || 'default', ['default', 'shwapnoPcView']);
    setSelect('section-design-pc-layout-style', layout.pcLayoutStyle || 'default', ['default', 'shwapnoLeftMenu']);
    const typo = layout.typography || {};
    const typoGroups = ['body', 'heading', 'button', 'productTitle', 'productPrice', 'nav', 'sidebarCategory', 'sidebarCategoryIcon', 'sidebarHeader', 'footer', 'topBar', 'categoryPill', 'cardDescription', 'badge', 'cartDrawer'];
    typoGroups.forEach((g) => {
        const pcEl = document.getElementById(`typo-${g}-pc`);
        const mobEl = document.getElementById(`typo-${g}-mobile`);
        const v = typo[g] || {};
        const pcVal = v.pc;
        const mobVal = v.mobile;
        const def = TYPOGRAPHY_DEFAULTS[g] || {};
        const phPc = def.pc != null ? String(def.pc) : '—';
        const phMob = def.mobile != null ? String(def.mobile) : '—';
        if (pcEl) {
            pcEl.value = pcVal != null && pcVal !== '' ? String(pcVal) : '';
            pcEl.placeholder = phPc;
        }
        if (mobEl) {
            mobEl.value = mobVal != null && mobVal !== '' ? String(mobVal) : '';
            mobEl.placeholder = phMob;
        }
    });
    const hl = layout.hamburgerLinks || {};
    const setHmLink = (elId, key) => {
        const el = document.getElementById(elId);
        if (el) el.checked = hl[key] !== false;
    };
    setHmLink('hm-link-shop', 'shop');
    setHmLink('hm-link-featured', 'featured');
    setHmLink('hm-link-wishlist', 'wishlist');
    setHmLink('hm-link-account', 'account');
    setHmLink('hm-link-support', 'support');
    const bottomNavEnabledCb = document.getElementById('section-design-bottom-nav-enabled');
    if (bottomNavEnabledCb) bottomNavEnabledCb.checked = layout.bottomNavEnabled !== false;
    const upsellEnabledCb = document.getElementById('section-design-upsell-enabled');
    if (upsellEnabledCb) upsellEnabledCb.checked = layout.upsellEnabled !== false;
    setSelect('checkout-address-form-mode', layout.checkoutAddressForm === 'structured' ? 'structured' : 'simple', ['simple', 'structured']);

    const ctWrap = document.getElementById('center-trade-header-options-wrap');
    const hdrSel = document.getElementById('section-design-header');
    if (ctWrap && hdrSel) ctWrap.classList.toggle('hidden', hdrSel.value !== 'centerTrade');
    const slantedCb = document.getElementById('center-trade-slanted-tabs-enabled');
    if (slantedCb) slantedCb.checked = !!layout.slantedTabNavEnabled;
    const slantedLbl = document.getElementById('center-trade-slanted-tab-first-label');
    if (slantedLbl) slantedLbl.value = (layout.slantedTabFirstLabel != null && String(layout.slantedTabFirstLabel).trim() !== '') ? String(layout.slantedTabFirstLabel).trim() : '';

    // Populate Main Slider (bannerSlides = { imageUrl, title?, subtitle? } or fallback bannerUrls)
    const mainSliderContainer = document.getElementById('main-slider-container');
    if (mainSliderContainer) {
        mainSliderContainer.innerHTML = '';
        if (s.bannerSlides && Array.isArray(s.bannerSlides) && s.bannerSlides.length > 0) {
            s.bannerSlides.forEach(slide => addMainSlideRow(slide));
        } else if (s.bannerUrls && Array.isArray(s.bannerUrls)) {
            s.bannerUrls.forEach(url => addMainSlideRow(url));
        } else if (s.bannerUrl) {
            addMainSlideRow(s.bannerUrl);
        } else {
            addMainSlideRow();
        }
    }
    // Banner size & spacing (layout.bannerConfig)
    const bc = s.layout && s.layout.bannerConfig ? s.layout.bannerConfig : {};
    const setNum = (id, val, def) => { const el = document.getElementById(id); if (el) el.value = (val != null && val !== '') ? val : def; };
    setNum('banner-height-desktop', bc.desktopHeight, 420);
    setNum('banner-height-mobile', bc.mobileHeight, 140);
    setNum('banner-space-desktop', bc.sideSpacingDesktop, 32);
    setNum('banner-space-mobile', bc.sideSpacingMobile, 12);
    const deskMainLayoutEl = document.getElementById('banner-desktop-main-slider-layout');
    if (deskMainLayoutEl) deskMainLayoutEl.value = bc.desktopMainSliderLayout === 'fullBleed' ? 'fullBleed' : 'inset';
    const mobMainLayoutEl = document.getElementById('banner-mobile-main-slider-layout');
    if (mobMainLayoutEl) mobMainLayoutEl.value = bc.mobileMainSliderLayout === 'fullBleed' ? 'fullBleed' : 'inset';
    const presetEl = document.getElementById('banner-main-hero-preset');
    if (presetEl) {
        const pr = bc.mainHeroPreset;
        if (pr === 'fabrilife') {
            if (deskMainLayoutEl) deskMainLayoutEl.value = 'fullBleed';
            if (mobMainLayoutEl) mobMainLayoutEl.value = 'fullBleed';
            presetEl.value = 'fabrilife';
        } else if (pr === 'standard') {
            if (deskMainLayoutEl) deskMainLayoutEl.value = 'inset';
            if (mobMainLayoutEl) mobMainLayoutEl.value = 'inset';
            presetEl.value = 'standard';
        } else if (pr === 'custom') {
            presetEl.value = 'custom';
        } else {
            const d = deskMainLayoutEl && deskMainLayoutEl.value === 'fullBleed';
            const m = mobMainLayoutEl && mobMainLayoutEl.value === 'fullBleed';
            if (d && m) presetEl.value = 'fabrilife';
            else if (!d && !m) presetEl.value = 'standard';
            else presetEl.value = 'custom';
        }
    }
    setNum('banner-top-space-desktop', bc.topSpacingDesktop, 0);
    setNum('banner-bottom-space-desktop', bc.bottomSpacingDesktop, 0);
    const sbc = s.layout && s.layout.smallBannerConfig ? s.layout.smallBannerConfig : {};
    setNum('small-banner-height-desktop', sbc.desktopHeight, 150);
    setNum('small-banner-height-mobile', sbc.mobileHeight, 80);
    const smallBannerCountEl = document.getElementById('small-banner-count');
    if (smallBannerCountEl && (sbc.count === 1 || sbc.count === 2)) smallBannerCountEl.value = String(sbc.count);
    const pbc = s.layout && s.layout.promoBannerConfig ? s.layout.promoBannerConfig : {};
    setNum('promo-banner-height-desktop', pbc.desktopHeight, 150);
    setNum('promo-banner-height-mobile', pbc.mobileHeight, 120);

    // Footer logo (Classic storefront)
    const flc = s.footerLogoConfig || {};
    const flHeight = Math.min(120, Math.max(16, parseInt(flc.height, 10) || 40));
    const flHInput = document.getElementById('footer-logo-height');
    const flHSlider = document.getElementById('slider-footer-logo-h');
    const flHDisp = document.getElementById('disp-footer-logo-h');
    if (flHInput) flHInput.value = flHeight;
    if (flHSlider) flHSlider.value = flHeight;
    if (flHDisp) flHDisp.textContent = String(flHeight);
    const showFl = document.getElementById('show-footer-logo');
    if (showFl) showFl.checked = flc.show !== false;
    const invFl = document.getElementById('invert-footer-logo');
    if (invFl) invFl.checked = flc.invert !== false;

    // Category display (layout.categoryConfig)
    const cc = s.layout && s.layout.categoryConfig ? s.layout.categoryConfig : {};
    setNum('category-listing-banner-max-height-mobile', cc.listingBannerMaxHeightMobile, 200);
    setNum('category-listing-banner-max-height-desktop', cc.listingBannerMaxHeightDesktop, 240);
    setNum('category-size-desktop', cc.sizeDesktop, 170);
    setNum('category-size-mobile', cc.sizeMobile, 96);
    setNum('category-space-between', cc.spaceBetween, 10);
    setNum('category-rows-desktop', cc.rowsDesktop, 1);
    setNum('category-rows-mobile', cc.rowsMobile, 1);
    const shapeRound = document.querySelector('input[name="category-shape"][value="round"]');
    const shapeSquare = document.querySelector('input[name="category-shape"][value="square"]');
    if (cc.shape === 'square' && shapeSquare) shapeSquare.checked = true;
    else if (shapeRound) shapeRound.checked = true;
    setNum('cat-display-size-desktop', cc.sizeDesktop, 170);
    setNum('cat-display-size-mobile', cc.sizeMobile, 96);
    setNum('cat-display-space-between', cc.spaceBetween, 10);
    setNum('cat-display-rows-desktop', cc.rowsDesktop, 1);
    setNum('cat-display-rows-mobile', cc.rowsMobile, 1);
    setNum('cat-display-per-row-desktop', cc.categoriesPerRowDesktop, 4);
    setNum('cat-display-per-row-mobile', cc.categoriesPerRowMobile, 3);
    const cdShapeRound = document.querySelector('input[name="cat-display-shape"][value="round"]');
    const cdShapeSquare = document.querySelector('input[name="cat-display-shape"][value="square"]');
    if (cc.shape === 'square' && cdShapeSquare) cdShapeSquare.checked = true;
    else if (cdShapeRound) cdShapeRound.checked = true;
    const cdLayoutSwiper = document.querySelector('input[name="cat-display-layout"][value="swiper"]');
    const cdLayoutGrid = document.querySelector('input[name="cat-display-layout"][value="grid"]');
    if (cc.layoutType === 'grid' && cdLayoutGrid) cdLayoutGrid.checked = true;
    else if (cdLayoutSwiper) cdLayoutSwiper.checked = true;
    const cdCardStyleDefault = document.querySelector('input[name="cat-display-card-style"][value="default"]');
    const cdCardStyleChaldal = document.querySelector('input[name="cat-display-card-style"][value="chaldal"]');
    const cdCardStyleShwapno = document.querySelector('input[name="cat-display-card-style"][value="shwapno"]');
    if (cc.cardStyle === 'shwapno' && cdCardStyleShwapno) cdCardStyleShwapno.checked = true;
    else if (cc.cardStyle === 'chaldal' && cdCardStyleChaldal) cdCardStyleChaldal.checked = true;
    else if (cdCardStyleDefault) cdCardStyleDefault.checked = true;
    const catPageBannerEn = document.getElementById('cat-category-page-banner-enabled');
    if (catPageBannerEn) catPageBannerEn.checked = cc.categoryPageBannerEnabled !== false;
    const catShowDesc = document.getElementById('cat-show-category-description');
    if (catShowDesc) catShowDesc.checked = cc.showCategoryDescription !== false;
    const catEqEn = document.getElementById('cat-equal-tabs-enabled');
    if (catEqEn) catEqEn.checked = !!cc.mobileHeroEqualTabsEnabled;
    const catEqLbl = document.getElementById('cat-equal-tabs-first-label');
    if (catEqLbl) catEqLbl.value = (cc.mobileHeroEqualTabsFirstLabel != null && String(cc.mobileHeroEqualTabsFirstLabel).trim() !== '') ? String(cc.mobileHeroEqualTabsFirstLabel).trim() : '';
    const catEqCnt = document.getElementById('cat-equal-tabs-category-count');
    if (catEqCnt) catEqCnt.value = String(Math.min(8, Math.max(1, Number(cc.mobileHeroEqualTabsCategoryCount) || 4)));
    const productSpacingEnabled = document.getElementById('cat-product-spacing-enabled');
    if (productSpacingEnabled) productSpacingEnabled.checked = cc.productSpacingEnabled === true;
    setNum('cat-product-side-padding', cc.productSidePaddingPx, 0);
    setNum('cat-product-card-gap', cc.productCardGapPx, 10);
    syncCategoryProductSpacingInputs();
    const menuCfg = s.layout && s.layout.menuConfig ? s.layout.menuConfig : {};
    const menuYes = document.querySelector('input[name="menu-show-categories"][value="yes"]');
    const menuNo = document.querySelector('input[name="menu-show-categories"][value="no"]');
    if (menuCfg.showCategoriesInMenu && menuYes) menuYes.checked = true;
    else if (menuNo) menuNo.checked = true;
    const headerNavSections = document.querySelector('input[name="header-nav-type"][value="sections"]');
    const headerNavCategories = document.querySelector('input[name="header-nav-type"][value="categories"]');
    const headerNavCustom = document.querySelector('input[name="header-nav-type"][value="customCategories"]');
    const navT = menuCfg.headerNavType;
    if (navT === 'sections' && headerNavSections) headerNavSections.checked = true;
    else if (navT === 'customCategories' && headerNavCustom) headerNavCustom.checked = true;
    else if (headerNavCategories) headerNavCategories.checked = true;
    const rawCustom = menuCfg.customCategories;
    const ctx = typeof window.__getDashboardCategoriesForHeaderUi === 'function'
        ? window.__getDashboardCategoriesForHeaderUi()
        : { map: {}, order: [] };
    renderHeaderNavCustomCategoriesList(rawCustom, ctx.map, ctx.order);
    syncHeaderNavCustomCategoriesVisibility();

    const pg = s.layout && s.layout.productGridConfig ? s.layout.productGridConfig : {};
    setNum('product-grid-cols-desktop', pg.columnsDesktop, 5);
    setNum('product-grid-cols-mobile', pg.columnsMobile, 3);

    populateWebsiteIconsPanel(s.layout && s.layout.iconConfig ? s.layout.iconConfig : {});

    // Small Banners
    if (s.smallBanners && Array.isArray(s.smallBanners)) {
        const ids = ['small-banner-1-url', 'small-banner-2-url', 'small-banner-3-url'];
        ids.forEach((id, index) => {
            const el = document.getElementById(id);
            if (el) {
                el.value = s.smallBanners[index] || '';
                updateImagePreview(`#${id}`, `#${id.replace('url', 'preview')}`);
            }
        });
    }
    const sbLinkIds = ['small-banner-1-link', 'small-banner-2-link', 'small-banner-3-link'];
    const sbLinks = (s.smallBannerLinks && Array.isArray(s.smallBannerLinks)) ? s.smallBannerLinks : [];
    sbLinkIds.forEach((id, index) => {
        const lel = document.getElementById(id);
        if (lel) lel.value = String(sbLinks[index] || '').trim();
    });

    // Promo Banners
    const promo1El = document.getElementById('promo-banner-1-url');
    const promo2El = document.getElementById('promo-banner-2-url');
    if (promo1El) {
        promo1El.value = s.promoBanner1Url || '';
        updateImagePreview('#promo-banner-1-url', '#promo-banner-1-preview');
    }
    if (promo2El) {
        promo2El.value = s.promoBanner2Url || '';
        updateImagePreview('#promo-banner-2-url', '#promo-banner-2-preview');
    }
    const p1l = document.getElementById('promo-banner-1-link');
    const p2l = document.getElementById('promo-banner-2-link');
    if (p1l) p1l.value = String(s.promoBanner1Link || '').trim();
    if (p2l) p2l.value = String(s.promoBanner2Link || '').trim();

    const ann = s.homeAnnouncementStrip && typeof s.homeAnnouncementStrip === 'object' ? s.homeAnnouncementStrip : {};
    const annEn = document.getElementById('home-announcement-strip-enabled');
    if (annEn) annEn.checked = !!ann.enabled;
    const annTitle = document.getElementById('home-announcement-strip-title');
    const annBody = document.getElementById('home-announcement-strip-body');
    const annCtaT = document.getElementById('home-announcement-strip-cta-text');
    const annCtaU = document.getElementById('home-announcement-strip-cta-url');
    if (annTitle) annTitle.value = String(ann.title || '').trim();
    if (annBody) annBody.value = String(ann.body || '').trim();
    if (annCtaT) annCtaT.value = String(ann.ctaText || '').trim();
    if (annCtaU) annCtaU.value = String(ann.ctaUrl || '').trim();

    const nar = s.homeNewArrivalBand && typeof s.homeNewArrivalBand === 'object' ? s.homeNewArrivalBand : {};
    const narEn = document.getElementById('home-new-arrival-band-enabled');
    const narLbl = document.getElementById('home-new-arrival-band-label');
    if (narEn) narEn.checked = !!nar.enabled;
    if (narLbl) narLbl.value = String(nar.label || '').trim();
    const narFabGrid = document.getElementById('home-nar-fabrilife-cat-grid-enabled');
    const narFabLimit = document.getElementById('home-nar-fabrilife-cat-grid-limit');
    if (narFabGrid) narFabGrid.checked = !!nar.fabrilifeCategoryGridEnabled;
    if (narFabLimit) {
        const lim = Number(nar.fabrilifeCategoryGridLimit);
        const v = Number.isFinite(lim) ? Math.min(24, Math.max(4, lim)) : 8;
        narFabLimit.value = String(v);
    }
    const narFabPostEn = document.getElementById('home-nar-fab-post-banners-enabled');
    if (narFabPostEn) narFabPostEn.checked = !!nar.fabrilifePostGridBannersEnabled;
    const narBanners = Array.isArray(nar.fabrilifePostGridBanners) ? nar.fabrilifePostGridBanners : [];
    const b1 = narBanners[0] && typeof narBanners[0] === 'object' ? narBanners[0] : {};
    const b2 = narBanners[1] && typeof narBanners[1] === 'object' ? narBanners[1] : {};
    const setVal = (id, v) => { const el = document.getElementById(id); if (el) el.value = String(v || '').trim(); };
    setVal('home-nar-fab-post-b1-url', b1.imageUrl);
    setVal('home-nar-fab-post-b1-link', b1.linkUrl);
    setVal('home-nar-fab-post-b1-caption', b1.caption);
    setVal('home-nar-fab-post-b2-url', b2.imageUrl);
    setVal('home-nar-fab-post-b2-link', b2.linkUrl);
    setVal('home-nar-fab-post-b2-caption', b2.caption);

    // Shipping Options
    const shippingList = document.getElementById('shipping-options-admin-list'); 
    if (shippingList) { 
        shippingList.innerHTML = ''; 
        if (s.shippingOptions && s.shippingOptions.length > 0) { 
            s.shippingOptions.forEach(opt => addShippingOptionRow(opt)); 
        } else { 
            addShippingOptionRow({ label: 'Inside Dhaka', cost: 70 }); 
            addShippingOptionRow({ label: 'Outside Dhaka', cost: 130 }); 
        } 
    }

    // Previews
    ['#logo-url:#logo-url-preview', '#footer-logo-url:#footer-logo-url-preview', '#about-image-url:#about-image-url-preview', '#home-nar-fab-post-b1-url:#home-nar-fab-post-b1-preview', '#home-nar-fab-post-b2-url:#home-nar-fab-post-b2-preview'].forEach(p => { 
        const [inputId, previewId] = p.split(':'); 
        updateImagePreview(inputId, previewId); 
    });

    requestAnimationFrame(() => {
        refreshAllMainSlideBannerPreviews();
        maskLoadedDashboardImageUrlFields();
        try {
            if (typeof window.__refreshHeaderCategoryLinksUi === 'function') {
                window.__refreshHeaderCategoryLinksUi();
            }
        } catch (_) { /* noop */ }
    });
}

export function populateCategoryDropdown(categoriesData) {
    const container = document.getElementById('product-categories-container');
    const categorySelect = document.getElementById('product-category');
    const noCategoryAlert = document.getElementById('no-category-alert');
    const hasCategories = categoriesData && Object.keys(categoriesData).length > 0;
    if (noCategoryAlert) noCategoryAlert.classList.toggle('hidden', hasCategories);

    if (container) {
        container.innerHTML = '';
        if (hasCategories) {
            Object.values(categoriesData).forEach(cat => {
                const label = document.createElement('label');
                label.className = 'flex items-center gap-2 cursor-pointer text-sm text-black';
                const cb = document.createElement('input');
                cb.type = 'checkbox';
                cb.name = 'product-categories';
                cb.value = cat.name;
                label.appendChild(cb);
                label.appendChild(document.createTextNode(cat.name));
                container.appendChild(label);
            });
            requestAnimationFrame(() => refreshProductSubcategoryDropdown(categoriesData));
        } else {
            container.innerHTML = '<p class="text-sm text-black">Please add a category first.</p>';
            populateProductSubcategoryDropdown([]);
        }
        return;
    }
    if (!categorySelect) return;
    const currentValue = categorySelect.value;
    categorySelect.innerHTML = '';
    if (hasCategories) {
        categorySelect.innerHTML = '<option value="" disabled selected>Select a category...</option>';
        Object.values(categoriesData).forEach(cat => {
            const option = document.createElement('option');
            option.value = cat.name;
            option.textContent = cat.name;
            categorySelect.appendChild(option);
        });
        if (currentValue && Object.values(categoriesData).some(c => c.name === currentValue)) {
            categorySelect.value = currentValue;
        }
    } else {
        categorySelect.innerHTML = '<option value="">Please add a category first</option>';
    }
}

export function populateHeaderCategories(categoriesData, selectedCategories = [], categoryOrder = [], categoryIcons = {}) {
    const container = document.getElementById('header-categories-container');
    if (!container) return;

    if (categoriesData == null) {
        container.innerHTML = '<p class="text-xs text-black">Loading categories...</p>';
        return;
    }

    const categoriesMap = (categoriesData && typeof categoriesData === 'object') ? categoriesData : {};
    const ids = Object.keys(categoriesMap);
    const orderedIds = Array.isArray(categoryOrder) && categoryOrder.length > 0
        ? [...categoryOrder.filter((id) => categoriesMap[id]), ...ids.filter((id) => !categoryOrder.includes(id))]
        : ids.sort((a, b) => b.localeCompare(a));
    const categories = orderedIds
        .map((id) => categoriesMap[id])
        .filter((cat) => cat && String(cat.name || '').trim());
    const selectedSet = new Set(
        Array.isArray(selectedCategories)
            ? selectedCategories.map((name) => String(name || '').trim()).filter(Boolean)
            : []
    );
    const availableNames = new Set(categories.map((cat) => String(cat.name || '').trim()));
    const matchedSelected = new Set([...selectedSet].filter((name) => availableNames.has(name)));
    const useExplicitSelection = matchedSelected.size > 0;

    if (categories.length === 0) {
        container.innerHTML = '<p class="text-xs text-black">Please add categories first.</p>';
        return;
    }

    container.innerHTML = '';
    const iconMap = (categoryIcons && typeof categoryIcons === 'object') ? categoryIcons : {};
    categories.forEach((cat, idx) => {
        const name = String(cat.name || '').trim();
        if (!name) return;

        const rowWrap = document.createElement('div');
        rowWrap.className = 'flex items-center justify-between gap-2 py-1.5';

        const row = document.createElement('label');
        row.className = 'flex items-center gap-2 py-1.5 text-sm text-black cursor-pointer';

        const cb = document.createElement('input');
        cb.type = 'checkbox';
        cb.name = 'header-categories';
        cb.value = name;
        cb.className = 'h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500';
        cb.checked = useExplicitSelection ? matchedSelected.has(name) : false;

        const txt = document.createElement('span');
        txt.textContent = name;

        row.appendChild(cb);
        row.appendChild(txt);

        const iconControls = document.createElement('div');
        iconControls.className = 'flex items-center gap-2';

        const hiddenInput = document.createElement('input');
        hiddenInput.type = 'hidden';
        hiddenInput.name = 'header-category-icons';
        hiddenInput.dataset.categoryName = name;
        hiddenInput.value = String(iconMap[name] || '').trim();

        const preview = document.createElement('div');
        preview.id = `header-cat-icon-preview-${idx}-${name.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}`;
        preview.className = 'w-8 h-8 rounded border border-gray-200 bg-white overflow-hidden flex items-center justify-center shrink-0';

        const syncPreview = () => {
            const url = String(hiddenInput.value || '').trim();
            preview.innerHTML = url
                ? `<img src="${url.replace(/"/g, '&quot;')}" class="w-full h-full object-contain" alt="">`
                : '<i class="fa-regular fa-image text-black text-xs" aria-hidden="true"></i>';
        };
        syncPreview();

        const uploadBtn = document.createElement('button');
        uploadBtn.type = 'button';
        uploadBtn.className = 'px-2 py-1 text-[11px] rounded border border-indigo-200 text-indigo-700 hover:bg-indigo-50';
        uploadBtn.textContent = 'Upload';
        uploadBtn.setAttribute('onclick', 'triggerUpload(this)');
        uploadBtn.dataset.targetInput = `#${preview.id}-input`;
        uploadBtn.dataset.previewContainer = `#${preview.id}`;
        uploadBtn.dataset.uploadProfile = 'category';

        hiddenInput.id = `${preview.id}-input`;
        hiddenInput.addEventListener('input', syncPreview);
        hiddenInput.addEventListener('change', syncPreview);

        const removeBtn = document.createElement('button');
        removeBtn.type = 'button';
        removeBtn.className = 'px-2 py-1 text-[11px] rounded border border-gray-200 text-black hover:bg-gray-100';
        removeBtn.textContent = 'Remove';
        removeBtn.addEventListener('click', () => {
            hiddenInput.value = '';
            hiddenInput.dispatchEvent(new Event('input', { bubbles: true }));
            syncPreview();
        });

        iconControls.appendChild(hiddenInput);
        iconControls.appendChild(preview);
        iconControls.appendChild(uploadBtn);
        iconControls.appendChild(removeBtn);

        rowWrap.appendChild(row);
        rowWrap.appendChild(iconControls);
        container.appendChild(rowWrap);
    });
}

/** Classic shop: categories shown in the swiper/grid strip directly below the home banner (subset of inventory). */
export function populateHomeStripCategories(categoriesData, selectedNames = [], categoryOrder = []) {
    const container = document.getElementById('home-thumbnail-categories-container');
    if (!container) return;

    if (categoriesData == null) {
        container.innerHTML = '<p class="text-xs text-black">Loading categories...</p>';
        return;
    }

    const categoriesMap = (categoriesData && typeof categoriesData === 'object') ? categoriesData : {};
    const ids = Object.keys(categoriesMap);
    const orderedIds = Array.isArray(categoryOrder) && categoryOrder.length > 0
        ? [...categoryOrder.filter((id) => categoriesMap[id]), ...ids.filter((id) => !categoryOrder.includes(id))]
        : ids.sort((a, b) => b.localeCompare(a));
    const categories = orderedIds
        .map((id) => categoriesMap[id])
        .filter((cat) => cat && String(cat.name || '').trim());
    const selectedSet = new Set(
        Array.isArray(selectedNames)
            ? selectedNames.map((name) => String(name || '').trim()).filter(Boolean)
            : []
    );
    const availableNames = new Set(categories.map((cat) => String(cat.name || '').trim()));
    const matchedSelected = new Set([...selectedSet].filter((name) => availableNames.has(name)));
    const useExplicitSelection = matchedSelected.size > 0;

    if (categories.length === 0) {
        container.innerHTML = '<p class="text-xs text-black">Please add categories first.</p>';
        return;
    }

    container.innerHTML = '';
    categories.forEach((cat) => {
        const name = String(cat.name || '').trim();
        if (!name) return;
        const label = document.createElement('label');
        label.className = 'flex items-center gap-2 py-1 text-xs text-black cursor-pointer';
        const cb = document.createElement('input');
        cb.type = 'checkbox';
        cb.name = 'home-thumbnail-categories';
        cb.value = name;
        cb.className = 'h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500';
        cb.checked = useExplicitSelection ? matchedSelected.has(name) : false;
        const txt = document.createElement('span');
        txt.textContent = name;
        label.appendChild(cb);
        label.appendChild(txt);
        container.appendChild(label);
    });
}

export function resetCategoryForm() {
    const form = document.getElementById('category-form'); 
    if (!form) return;
    form.reset(); 
    document.getElementById('category-id').value = ''; 
    const desc = document.getElementById('category-description');
    if (desc) desc.value = '';
    document.getElementById('category-form-title').textContent = 'Add New Category';
    document.querySelector('#save-category-btn .button-text').textContent = 'Save Category'; 
    document.getElementById('category-cancel-btn').classList.add('hidden'); 
    document.getElementById('category-image-preview').innerHTML = '';
    const lbPrev = document.getElementById('category-listing-banner-preview');
    if (lbPrev) lbPrev.innerHTML = '';
    const lbUrl = document.getElementById('category-listing-banner-url');
    if (lbUrl) lbUrl.value = '';
    const lbEn = document.getElementById('category-listing-banner-enabled');
    if (lbEn) lbEn.checked = true;
    renderSubcategoryList([]);
}

function normalizeSubcategoryHex(v, fallback) {
    const t = String(v || '').trim();
    if (/^#[0-9A-Fa-f]{6}$/i.test(t)) return t;
    if (/^#[0-9A-Fa-f]{3}$/i.test(t)) return t;
    return fallback;
}

export function getSubcategoriesFromDOM() {
    const rows = Array.from(document.querySelectorAll('#subcategory-list .subcategory-row'));
    const data = {};
    rows.forEach((row, index) => {
        const name = row.querySelector('.subcategory-name-input')?.value.trim();
        if (!name) return;
        const bgEl = row.querySelector('.subcategory-bg-color');
        const txEl = row.querySelector('.subcategory-text-color');
        const bgColor = normalizeSubcategoryHex(bgEl && bgEl.value, '#ffffff');
        const textColor = normalizeSubcategoryHex(txEl && txEl.value, '#000000');
        data[`sub_${Date.now()}_${index}`] = { name, bgColor, textColor };
    });
    return data;
}

function subcategoryRowHtml(name, bgColor = '#ffffff', textColor = '#000000') {
    const esc = (s) => String(s).replace(/"/g, '&quot;').replace(/</g, '&lt;');
    const bg = normalizeSubcategoryHex(bgColor, '#ffffff');
    const tx = normalizeSubcategoryHex(textColor, '#000000');
    return `
        <div class="subcategory-row flex flex-wrap items-center gap-2">
            <input type="text" class="admin-form-input subcategory-name-input flex-1 min-w-[120px]" value="${esc(name)}">
            <input type="color" class="subcategory-bg-color w-9 h-9 p-0 border border-gray-200 rounded cursor-pointer shrink-0" value="${bg}" title="Background">
            <input type="color" class="subcategory-text-color w-9 h-9 p-0 border border-gray-200 rounded cursor-pointer shrink-0" value="${tx}" title="Text">
            <button type="button" class="remove-subcategory-btn text-red-600 hover:text-red-800 px-2 py-1 text-sm border border-red-200 rounded shrink-0">Remove</button>
        </div>
    `;
}

export function renderSubcategoryList(subcategories) {
    const container = document.getElementById('subcategory-list');
    if (!container) return;
    const entries = Array.isArray(subcategories)
        ? subcategories
            .map((item) => {
                if (typeof item === 'string') {
                    const n = item.trim();
                    return n ? { name: n, bgColor: '#ffffff', textColor: '#000000' } : null;
                }
                if (item && typeof item === 'object' && item.name) {
                    return {
                        name: String(item.name).trim(),
                        bgColor: item.bgColor || '#ffffff',
                        textColor: item.textColor || '#000000'
                    };
                }
                return null;
            })
            .filter(Boolean)
        : Object.values(subcategories || {})
            .map((s) => {
                if (!s || !s.name) return null;
                return {
                    name: String(s.name).trim(),
                    bgColor: s.bgColor || '#ffffff',
                    textColor: s.textColor || '#000000'
                };
            })
            .filter((e) => e && e.name);
    if (!entries.length) {
        container.innerHTML = '<p class="text-xs text-black">No subcategory added yet.</p>';
        return;
    }
    container.innerHTML = entries.map((e) => subcategoryRowHtml(e.name, e.bgColor, e.textColor)).join('');
    container.querySelectorAll('.remove-subcategory-btn').forEach((btn) => {
        btn.addEventListener('click', () => {
            const row = btn.closest('.subcategory-row');
            if (row) row.remove();
            if (!container.querySelector('.subcategory-row')) {
                container.innerHTML = '<p class="text-xs text-black">No subcategory added yet.</p>';
            }
        });
    });
}

export function addSubcategoryFromInput() {
    const input = document.getElementById('subcategory-name-input');
    const container = document.getElementById('subcategory-list');
    if (!input || !container) return;
    const name = input.value.trim();
    if (!name) return;
    const existing = Array.from(container.querySelectorAll('.subcategory-name-input')).map((el) => el.value.trim().toLowerCase());
    if (existing.includes(name.toLowerCase())) {
        input.value = '';
        return;
    }
    const empty = container.querySelector('p');
    if (empty) container.innerHTML = '';
    const row = document.createElement('div');
    row.className = 'subcategory-row flex flex-wrap items-center gap-2';
    row.innerHTML = `
        <input type="text" class="admin-form-input subcategory-name-input flex-1 min-w-[120px]" value="${String(name).replace(/"/g, '&quot;').replace(/</g, '&lt;')}">
        <input type="color" class="subcategory-bg-color w-9 h-9 p-0 border border-gray-200 rounded cursor-pointer shrink-0" value="#ffffff" title="Background">
        <input type="color" class="subcategory-text-color w-9 h-9 p-0 border border-gray-200 rounded cursor-pointer shrink-0" value="#000000" title="Text">
        <button type="button" class="remove-subcategory-btn text-red-600 hover:text-red-800 px-2 py-1 text-sm border border-red-200 rounded shrink-0">Remove</button>
    `;
    container.appendChild(row);
    row.querySelector('.remove-subcategory-btn')?.addEventListener('click', () => {
        row.remove();
        if (!container.querySelector('.subcategory-row')) {
            container.innerHTML = '<p class="text-xs text-black">No subcategory added yet.</p>';
        }
    });
    input.value = '';
}

export function refreshProductSubcategoryDropdown(categoriesData) {
    const checked = Array.from(document.querySelectorAll('#product-categories-container input[name="product-categories"]:checked')).map((el) => el.value);
    const selectedCats = Object.values(categoriesData || {}).filter((cat) => checked.includes(cat.name));
    const entries = [];
    selectedCats.forEach((cat) => {
        const parentName = cat.name || '';
        Object.values(cat.subcategories || {}).forEach((s) => {
            const subName = s?.name ? String(s.name).trim() : '';
            if (!subName) return;
            entries.push({
                key: `${parentName}|||${subName}`,
                parent: parentName,
                name: subName
            });
        });
    });
    populateProductSubcategoryDropdown(entries);
}

export function populateProductSubcategoryDropdown(entries, currentValue = '') {
    const select = document.getElementById('product-subcategory');
    if (!select) return;
    select.innerHTML = '<option value="">Select a sub category...</option>';
    const uniqueMap = new Map();
    (entries || []).forEach((entry) => {
        if (!entry || !entry.key || !entry.name) return;
        if (!uniqueMap.has(entry.key)) uniqueMap.set(entry.key, entry);
    });
    uniqueMap.forEach((entry) => {
        const opt = document.createElement('option');
        opt.value = entry.key;
        opt.textContent = `${entry.parent} > ${entry.name}`;
        opt.dataset.subName = entry.name;
        opt.dataset.parentName = entry.parent;
        select.appendChild(opt);
    });
    if (!currentValue) return;
    const direct = Array.from(select.options).find((opt) => opt.value === currentValue);
    if (direct) {
        select.value = direct.value;
        return;
    }
    // Backward compatibility with old product data where only subcategory name was saved.
    const legacy = Array.from(select.options).find((opt) => opt.dataset.subName === currentValue);
    if (legacy) select.value = legacy.value;
}

// --- 8. CRM / CUSTOMER INTELLIGENCE UI ---

export function renderCustomerList(customers) {
    const container = document.getElementById('crm-list-container');
    const totalCountEl = document.getElementById('crm-total-count');
    if (!container) return;

    if (!customers || customers.length === 0) {
        container.innerHTML = `<div class="flex flex-col items-center justify-center h-48 text-black"><i class="fa-solid fa-users-slash text-3xl mb-3 text-indigo-200"></i><span class="text-xs font-medium">No Customers Found</span></div>`;
        if(totalCountEl) totalCountEl.textContent = '0 Customers';
        return;
    }

    if(totalCountEl) totalCountEl.textContent = `${customers.length} Customers`;

    container.innerHTML = customers.map(c => `
        <div class="crm-customer-item p-2 rounded-lg border border-transparent hover:bg-indigo-50 hover:border-indigo-100 cursor-pointer transition-all group" data-phone="${c.phone}" data-action="viewCustomer">
            <div class="flex justify-between items-start">
                <h4 class="font-bold text-black text-sm group-hover:text-indigo-700">${c.name}</h4>
                ${c.isVIP ? '<span class="text-[10px] font-bold bg-yellow-100 text-yellow-700 px-1.5 py-0.5 rounded border border-yellow-200">VIP</span>' : ''}
            </div>
            <div class="flex justify-between items-center mt-1">
                <span class="text-xs text-black font-mono">${c.phone}</span>
                <span class="text-xs font-bold text-green-600 bg-green-50 px-1.5 rounded">${c.totalSpent} ৳</span>
            </div>
        </div>
    `).join('');
}

export function renderCustomerDetails(profile) {
    const view = document.getElementById('crm-detail-view');
    const placeholder = document.getElementById('crm-detail-placeholder');
    
    if (!profile) {
        view.classList.add('hidden');
        placeholder.classList.remove('hidden');
        return;
    }

    view.classList.remove('hidden');
    placeholder.classList.add('hidden');

    // Header Info
    document.getElementById('crm-profile-name').textContent = profile.name;
    document.getElementById('crm-profile-phone').textContent = profile.phone;
    document.getElementById('crm-profile-loc').textContent = profile.address || 'Unknown Location';
    
    // Badges
    const vipBadge = document.getElementById('crm-badge-vip');
    const returnBadge = document.getElementById('crm-badge-return');
    if(vipBadge) vipBadge.classList.toggle('hidden', !profile.isVIP);
    if(returnBadge) returnBadge.classList.toggle('hidden', profile.orderCount <= 1);

    // Tech Info
    const meta = profile.lastMeta || {};
    document.getElementById('crm-device-model').textContent = meta.deviceModel || 'N/A';
    document.getElementById('crm-device-os').textContent = meta.os || 'N/A';
    document.getElementById('crm-ip-addr').textContent = meta.ip || 'N/A';
    document.getElementById('crm-isp-info').textContent = meta.isp || 'N/A';
    document.getElementById('crm-battery-stat').textContent = meta.batteryLevel ? `${meta.batteryLevel} ${meta.isCharging === 'Yes' ? '⚡' : ''}` : 'N/A';

    // Marketing Info
    document.getElementById('crm-fbclid').textContent = profile.fbclid || 'None';
    document.getElementById('crm-source').textContent = profile.source || 'Direct';
    document.getElementById('crm-first-seen').textContent = new Date(profile.firstSeen).toLocaleDateString();

    // Stats
    document.getElementById('crm-total-spent').textContent = profile.totalSpent.toLocaleString() + ' ৳';
    document.getElementById('crm-order-count').textContent = profile.orderCount;
    document.getElementById('crm-aov').textContent = Math.round(profile.totalSpent / (profile.orderCount || 1)).toLocaleString() + ' ৳';

    // Timeline Generation
    const timelineContainer = document.getElementById('crm-timeline');
    timelineContainer.innerHTML = '';

    if (profile.history && profile.history.length > 0) {
        profile.history.forEach(event => {
            let icon = 'fa-circle';
            let colorClass = 'bg-gray-200 border-gray-300';
            
            if (event.type === 'order') {
                icon = 'fa-bag-shopping';
                colorClass = 'bg-green-100 border-green-300 text-green-600';
            } else if (event.type === 'visit') {
                icon = 'fa-eye';
                colorClass = 'bg-blue-100 border-blue-300 text-blue-600';
            }

            const html = `
                <div class="relative group">
                    <div class="absolute -left-[29px] top-0 h-8 w-8 rounded-full border-2 ${colorClass} flex items-center justify-center bg-white z-10">
                        <i class="fa-solid ${icon} text-xs"></i>
                    </div>
                    <div class="pb-1">
                        <div class="flex justify-between items-start">
                            <span class="font-bold text-sm text-black">${event.title}</span>
                            <span class="text-xs text-black font-mono">${new Date(event.timestamp).toLocaleDateString()}</span>
                        </div>
                        <p class="text-xs text-black mt-0.5">${event.details}</p>
                    </div>
                </div>
            `;
            timelineContainer.insertAdjacentHTML('beforeend', html);
        });
    } else {
        timelineContainer.innerHTML = '<p class="text-sm text-black italic pl-2">No activity history available.</p>';
    }
}