/* =============================================================
 *  STONK QUANT OBSERVATORY & MARKET INTELLIGENCE v13.0.0
 *  Market Intelligence-Style Layout · Multi-Account Crawler Engine
 * ============================================================= */
(function () {
  'use strict';

  function formatMoney(n) {
    if (n === undefined || n === null || n === '' || n === '—') return '—';
    if (typeof n === 'string') {
      var s = n.trim();
      if (!s || s === '—' || s === 'NaN') return '—';
      if (/^[₫$]?[0-9.,]+[a-zA-Ztrđ%+ ]*$/i.test(s) && (s.includes('₫') || s.includes('tỉ') || s.includes('tr') || s.includes('k') || s.includes('M') || s.includes('B'))) return s;
      var clean = s.replace(/[^0-9.-]/g, '');
      var parsed = parseFloat(clean);
      if (isNaN(parsed)) return s;
      n = parsed;
    }
    var num = Number(n);
    if (isNaN(num)) return '—';
    if (Math.abs(num) >= 1e9) return '₫' + (num / 1e9).toFixed(2).replace(/\.?0+$/, '') + 'tỉ';
    if (Math.abs(num) >= 1e6) return '₫' + (num / 1e6).toFixed(2).replace(/\.?0+$/, '') + 'tr';
    if (Math.abs(num) >= 1e3) return '₫' + (num / 1e3).toFixed(1).replace(/\.?0+$/, '') + 'k';
    return '₫' + num.toLocaleString('vi-VN');
  }
  var fmtMoney = formatMoney;
  if (typeof window !== 'undefined') {
    window.formatMoney = formatMoney;
    window.fmtMoney = formatMoney;
  }

  var ICON = {
    rocket:         '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/></svg>',
    satellite:      '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 7 9 3 5 7l4 4"/><path d="m17 11 4 4-4 4-4-4"/><path d="m8 12 4 4 6-6-4-4Z"/><path d="m16 8 3-3"/><path d="M9 21a6 6 0 0 0-6-6"/></svg>',
    planet:         '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="7"/><path d="M2.5 12c3-4 16-4 19 0-3 4-16 4-19 0z"/></svg>',
    radar:          '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19.07 4.93A10 10 0 0 0 4.93 19.07M16.24 7.76A6 6 0 0 0 7.76 16.24"/><circle cx="12" cy="12" r="2"/><line x1="12" y1="12" x2="20" y2="4"/></svg>',
    chart:          '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>',
    box:            '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>',
    store:          '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>',
    search:         '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>',
    back:           '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>',
    close:          '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
    trendUp:        '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/><polyline points="17 6 23 6 23 12"/></svg>',
    trendDn:        '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/><polyline points="17 18 23 18 23 12"/></svg>',
    money:          '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>',
    user:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
    users:          '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
    volume:         '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>',
    volumeX:        '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>',
    video:          '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>',
    link:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>',
    bag:            '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>',
    percent:        '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="5" x2="5" y2="19"/><circle cx="6.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="17.5" r="2.5"/></svg>',
    info:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>',
    fire:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/></svg>',
    award:          '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="6"/><path d="M15.477 12.89L17 22l-5-3-5 3 1.523-9.11"/></svg>',
    star:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="#f59e0b" stroke="#f59e0b" stroke-width="1"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>',
    live:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="2"/><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"/></svg>',
    chevronRight:   '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>',
    filter:         '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>',
    sort:           '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="14" y2="12"/><line x1="4" y1="18" x2="8" y2="18"/></svg>',
    check:          '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',
    external:       '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>',
    calendar:       '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>',
    sidebarToggle:  '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line></svg>',
    tiktok:         '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 0 1-5.2 1.74 2.89 2.89 0 0 1 2.31-4.64c.298-.002.595.042.88.13V9.4a6.33 6.33 0 0 0-1-.08A6.34 6.34 0 0 0 3 15.66a6.34 6.34 0 0 0 10.82 4.49 6.27 6.27 0 0 0 1.86-4.48V8.71a8.31 8.31 0 0 0 5-1.74l-1.09-.28Z"/></svg>',
    crawler:        '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="m14 9 3 3-3 3"/></svg>',
    play:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>',
    pause:          '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>',
    refresh:        '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></svg>',
    trash:          '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>',
    bolt:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>',
    settings:       '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
    download:       '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>',
    copy:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>',
    eye:            '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>',
    target:         '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>',
    bot:            '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="10" rx="2"/><circle cx="12" cy="5" r="2"/><path d="M12 7v4"/><line x1="8" y1="16" x2="8" y2="16"/><line x1="16" y1="16" x2="16" y2="16"/></svg>',
    package:        '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="16.5" y1="9.4" x2="7.55" y2="4.24"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.29 7 12 12 20.71 7"/><line x1="12" y1="22" x2="12" y2="12"/></svg>',
    clock:          '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>',
    grid:           '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>',
    table:          '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/><path d="M9 3v18"/><path d="M15 3v18"/></svg>',
    tag:            '<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/><line x1="7" y1="7" x2="7.01" y2="7"/></svg>',
    sparkles:       '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/></svg>',
    diamond:        '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3h12l4 6-10 12L2 9z"/><path d="M11 3 8 9l4 12 4-12-3-6"/><path d="M2 9h20"/></svg>',
    flame:          '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/></svg>',
    adMegaphone:    '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 11 18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/></svg>',
    bulb:           '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5.76.76 1.23 1.52 1.41 2.5"/></svg>'
  };

  var CATEGORY_ICON_PATHS = {
    beauty: '<path d="M12 3c3 3 5 5.4 5 8a5 5 0 0 1-10 0c0-2.6 2-5 5-8Z"/><path d="M9 14c1.4 1.2 4.6 1.2 6 0M8 21h8"/>',
    fashion: '<path d="m8 4 4 3 4-3 5 4-3 4-2-1v10H8V11l-2 1-3-4 5-4Z"/>',
    food: '<path d="M6 3v8a3 3 0 0 0 6 0V3M9 3v18M17 3v18M17 3c3 2 3 7 0 9"/>',
    home: '<path d="m3 11 9-8 9 8M5 10v11h14V10M9 21v-7h6v7"/>',
    electronics: '<rect x="4" y="3" width="16" height="13" rx="2"/><path d="M8 21h8M12 16v5M8 7h8"/>',
    health: '<path d="M12 21s-8-4.6-8-11a4.5 4.5 0 0 1 8-2.8A4.5 4.5 0 0 1 20 10c0 6.4-8 11-8 11Z"/><path d="M8 13h2l1-3 2 6 1-3h2"/>',
    baby: '<circle cx="12" cy="12" r="9"/><path d="M9 10h.01M15 10h.01M9 15c1.5 1.5 4.5 1.5 6 0M12 3c0 2-2 2-2 4"/>',
    sports: '<circle cx="12" cy="12" r="9"/><path d="m8 4 2 4h4l2-4M3 11l4 1 1 4-2 3M21 11l-4 1-1 4 2 3M8 20l4-3 4 3"/>',
    pet: '<path d="M8 13c-3 1-4 4-2 6 2 2 4 0 6 0s4 2 6 0c2-2 1-5-2-6-1-3-7-3-8 0Z"/><circle cx="6" cy="8" r="2"/><circle cx="10" cy="5" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="14" cy="5" r="2"/>',
    vehicle: '<path d="M5 16H3V9l2-4h14l2 4v7h-2M5 16h14v4h-3v-2H8v2H5v-4ZM5 9h14"/><circle cx="7" cy="13" r="1"/><circle cx="17" cy="13" r="1"/>',
    books: '<path d="M4 4.5A2.5 2.5 0 0 1 6.5 2H11v18H6.5A2.5 2.5 0 0 0 4 22V4.5ZM20 4.5A2.5 2.5 0 0 0 17.5 2H13v18h4.5A2.5 2.5 0 0 1 20 22V4.5Z"/>',
    jewelry: '<path d="m12 3 8 7-8 11-8-11 8-7Zm-8 7h16M9 3l-2 7 5 11 5-11-2-7"/>',
    general: '<path d="m12 2 9 5-9 5-9-5 9-5Z"/><path d="m3 7 9 5 9-5M3 7v10l9 5 9-5V7M12 12v10"/>'
  };

  function categoryIcon(name) {
    var normalized = String(name || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
    var key = /my pham|lam dep|sac dep|cham soc/.test(normalized) ? 'beauty'
      : /thoi trang|quan ao|giay dep|tui xach|phu kien/.test(normalized) ? 'fashion'
      : /thuc pham|do uong|an uong|am thuc/.test(normalized) ? 'food'
      : /nha cua|gia dung|noi that|nha bep|do bep/.test(normalized) ? 'home'
      : /dien tu|dien thoai|may tinh|thiet bi|cong nghe/.test(normalized) ? 'electronics'
      : /suc khoe|y te|duoc/.test(normalized) ? 'health'
      : /me\s*(?:&|va)?\s*be|tre em|do choi/.test(normalized) ? 'baby'
      : /the thao|ngoai troi|fitness/.test(normalized) ? 'sports'
      : /thu cung|pet/.test(normalized) ? 'pet'
      : /o to|xe may|phuong tien/.test(normalized) ? 'vehicle'
      : /sach|van phong pham|giao duc/.test(normalized) ? 'books'
      : /trang suc|dong ho/.test(normalized) ? 'jewelry'
      : 'general';
    return '<span class="mi-category-icon mi-category-icon--' + key + '" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">' + CATEGORY_ICON_PATHS[key] + '</svg></span>';
  }

  /* ── OBSERVATORY STATE ─────────────────────────────────────── */
  var S = {
    tab: 'categories',        // categories | creators | products | shops | videos
    catId: null,              // Selected category id for detail view
    catSubTab: 'products',    // products | shops (in category detail)
    catLevel: 'ALL',          // ALL | L1 | L2 | L3
    top3Concentration: false, // Top 3 shop ratio >= 30%
    top10Concentration: false,// Top 10 shop ratio >= 50%
    search: '',
    categoryId: '',           // Filter by category
    sellerType: 'ALL',        // ALL | BRAND | SHOP
    sortBy: 'revenue',        // revenue | growth | sale | unit_price
    sortOrder: 'DESC',        // DESC | ASC
    growthOnly: false,        // Categories with growth > 0
    minCommission: 0,         // Products commission filter
    minRevenue: 0,            // Min revenue filter
    minSale: 0,               // Min sales filter
    minRating: 0,             // Min product rating filter
    isAiVideo: false,         // Videos AI filter
    isAd: false,              // Videos Ads filter
    minAdRoas: 0,             // Videos Min Ad ROAS
    minAdCost: 0,             // Videos Min Ad Cost
    minViews: 0,              // Videos Min Views
    videoViewMode: (function() { try { return localStorage.getItem('mi_video_view_mode') || 'grid'; } catch(_) { return 'grid'; } })(),
    filterCollapsed: true,   // Mặc định ẩn bộ lọc cho giao diện rộng rãi, gọn gàng
    page: 1,
    limit: 20,
    total: 0,
    loading: false,
    catData: null,
    categoryOptions: [],
  };
  var marketRequestSeq = {
    categories: 0,
    products: 0,
    shops: 0,
    videos: 0
  };

  /* ── API CLIENT ────────────────────────────────────────────── */
  // Khi chạy trong extension popup (chrome-extension://...) fetch path tương đối sẽ bị
  // resolve thành chrome-extension:///api/... → ERR_FILE_NOT_FOUND.
  // Detect context và prepend base URL của server để gọi đúng.
  var _MI_BASE = (function () {
    if (typeof window !== 'undefined' && window.location && window.location.protocol === 'chrome-extension:') {
      // Thử lấy server URL đã lưu từ session/config, fallback về production
      try {
        var cfg = JSON.parse(localStorage.getItem('stonk-server-config') || '{}');
        if (cfg.baseUrl) return cfg.baseUrl.replace(/\/$/, '');
      } catch (_) {}
      try {
        var sess0 = JSON.parse(localStorage.getItem('stonk-session') || '{}');
        if (sess0.serverUrl) return String(sess0.serverUrl).replace(/\/$/, '');
      } catch (_) {}
      return 'https://bookingkoc.stonkagency.vn';
    }
    return ''; // web app: dùng path tương đối như cũ
  }());

  function api(path, opts) {
    opts = opts || {};
    var sess = {};
    try { sess = JSON.parse(localStorage.getItem('stonk-session') || '{}'); } catch (e) {}
    var curU = {};
    try { curU = JSON.parse(localStorage.getItem('currentUser') || '{}'); } catch (e) {}
    var token = localStorage.getItem('stonk-auth-token') ||
                localStorage.getItem('authorization') ||
                sess.token ||
                (sess.user && sess.user.token) ||
                curU.token ||
                (window.state && (window.state.authToken || window.state.token)) ||
                localStorage.getItem('authToken') ||
                localStorage.getItem('token') || '';
    if (token.startsWith('Bearer ')) token = token.slice(7).trim();

    var headers = Object.assign({
      'Content-Type': 'application/json',
      'X-App-Version': '6.0.36'
    }, opts.headers || {});

    if (token) {
      headers['Authorization'] = 'Bearer ' + token;
    }

    // Nếu đang ở extension context, ghép base URL vào trước path tương đối
    var fullPath = (_MI_BASE && path && path.charAt(0) === '/') ? (_MI_BASE + path) : path;

    return fetch(fullPath, Object.assign({}, opts, { headers: headers })).then(function (r) {
      return r.json().catch(function () { return { success: false, msg: 'Lỗi phản hồi từ máy chủ' }; });
    });
  }

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

  function fmtSale(n) {
    if (n === undefined || n === null || n === '' || n === '—') return '—';
    if (typeof n === 'string') {
      var s = n.trim();
      if (!s || s === '—' || s === 'NaN') return '—';
      if (/^[0-9.,]+[a-zA-Ztr%]+$/i.test(s)) return s;
      var clean = s.replace(/,/g, '');
      var parsed = parseFloat(clean);
      if (isNaN(parsed)) return s;
      n = parsed;
    }
    var num = Number(n);
    if (isNaN(num)) return '—';
    if (num >= 1e6) return (num / 1e6).toFixed(1).replace('.0', '') + 'M';
    if (num >= 1e3) return (num / 1e3).toFixed(1).replace('.0', '') + 'k';
    return num.toLocaleString('vi-VN');
  }

  function formatSummaryGmv(n) {
    if (n === undefined || n === null || n === '' || n === '—') return '0M';
    var num = Number(n);
    if (!Number.isFinite(num) && typeof n === 'string') {
      var compact = n.replace(/[^0-9.,-]/g, '').replace(/,/g, '.');
      num = Number.parseFloat(compact);
      if (/tỉ|ty|B/i.test(n)) return Number.isFinite(num) ? Math.trunc(num) + 'M' : '0M';
    }
    if (!Number.isFinite(num)) return '0M';
    var display = Math.abs(num) >= 1e9 ? num / 1e9 : (Math.abs(num) >= 1e6 ? num / 1e6 : num);
    return Math.trunc(display).toLocaleString('vi-VN', { useGrouping: false }) + 'M';
  }

  function formatMoney(n) {
    if (n === undefined || n === null || n === '' || n === '—') return '—';
    if (typeof n === 'string') {
      var s = n.trim();
      if (!s || s === '—' || s === 'NaN') return '—';
      if (/^[₫$]?[0-9.,]+[a-zA-Ztrđ%+ ]*$/i.test(s) && (s.includes('₫') || s.includes('tỉ') || s.includes('tr') || s.includes('k') || s.includes('M') || s.includes('B'))) return s;
      var clean = s.replace(/[^0-9.-]/g, '');
      var parsed = parseFloat(clean);
      if (isNaN(parsed)) return s;
      n = parsed;
    }
    var num = Number(n);
    if (isNaN(num)) return '—';
    if (Math.abs(num) >= 1e9) return '₫' + (num / 1e9).toFixed(2).replace(/\.?0+$/, '') + 'tỉ';
    if (Math.abs(num) >= 1e6) return '₫' + (num / 1e6).toFixed(2).replace(/\.?0+$/, '') + 'tr';
    if (Math.abs(num) >= 1e3) return '₫' + (num / 1e3).toFixed(1).replace(/\.?0+$/, '') + 'k';
    return '₫' + num.toLocaleString('vi-VN');
  }
  var fmtMoney = formatMoney;

  function growthBadge(rate) {
    if (rate === undefined || rate === null || rate === '' || rate === '—') {
      return '<span class="mi-clr-muted">—</span>';
    }
    if (typeof rate === 'string') {
      var s = rate.trim();
      if (s.indexOf('%') >= 0 || s.indexOf('>') >= 0) {
        var isNeg = s.indexOf('-') === 0;
        var isPos = s.indexOf('+') === 0 || s.indexOf('>') === 0 || (!isNeg && parseFloat(s) > 0);
        var cls = isNeg ? 'mi-growth-neg' : (isPos ? 'mi-growth-pos' : 'mi-growth-neutral');
        return '<span class="mi-growth-badge ' + cls + '">' + esc(s) + '</span>';
      }
    }
    var n = Number(rate);
    if (isNaN(n)) return '<span class="mi-clr-muted">—</span>';
    // Đổi tỉ lệ thập phân (ví dụ: -0.0391 -> -3.91%, 0.2338 -> +23.38%)
    var pctVal = (Math.abs(n) <= 2.5 && n !== 0) ? (n * 100) : n;
    var absPct = Math.abs(pctVal);
    var pctStr = (absPct >= 100 ? pctVal.toFixed(1) : pctVal.toFixed(2));
    if (pctVal > 0.0001) return '<span class="mi-growth-badge mi-growth-pos">+' + pctStr + '%</span>';
    if (pctVal < -0.0001) return '<span class="mi-growth-badge mi-growth-neg">' + pctStr + '%</span>';
    return '<span class="mi-growth-badge mi-growth-neutral">0.00%</span>';
  }

  function kpiCard(icon, title, val, sub, colorCls) {
    colorCls = colorCls || '';
    return '<div class="mi-kpi-card ' + colorCls + '">' +
      '<div class="mi-kpi-icon">' + (icon || '') + '</div>' +
      '<div class="mi-kpi-meta">' +
        '<span class="mi-kpi-label">' + esc(title) + '</span>' +
        '<strong class="mi-kpi-val">' + esc(val || '—') + '</strong>' +
        (sub ? '<span class="mi-kpi-sub">' + esc(sub) + '</span>' : '') +
      '</div>' +
    '</div>';
  }

  function initials(name) {
    if (!name) return '';
    var parts = name.trim().split(/\s+/);
    var first = parts[0] ? parts[0][0] : '';
    var last = parts.length > 1 ? parts[parts.length - 1][0] : '';
    return (first + last).toUpperCase();
  }

  var STONK_LOGO = './logo.png';

  function renderShopAvatar(avatarUrl, extraClass, fallbackId) {
    var cls = 'mi-avatar-img' + (extraClass ? ' ' + extraClass : '');
    var url = String(avatarUrl || '').trim();
    if (url && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('data:') || url.startsWith('/'))) {
      return '<img class="' + cls + '" src="' + esc(url) + '" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.onerror=null;this.src=\'' + STONK_LOGO + '\';this.classList.add(\'mi-stonk-fallback-logo\');"/>';
    }
    return '<img class="' + cls + ' mi-stonk-fallback-logo" src="' + STONK_LOGO + '" alt="Stonk" loading="lazy" referrerpolicy="no-referrer"/>';
  }

  function renderProductThumb(imgUrl, extraClass, fallbackId) {
    var cls = 'mi-prod-cell-img' + (extraClass ? ' ' + extraClass : '');
    var url = String(imgUrl || '').trim();
    if (url && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('data:') || url.startsWith('/'))) {
      return '<img class="' + cls + '" src="' + esc(url) + '" alt="" loading="lazy" referrerpolicy="no-referrer" onerror="this.onerror=null;this.src=\'' + STONK_LOGO + '\';this.classList.add(\'mi-stonk-fallback-logo\');"/>';
    }
    return '<span class="' + cls + ' mi-prod-cell-placeholder" aria-label="Chưa có ảnh sản phẩm">' + ICON.box + '</span>';
  }

  function renderProductStrip(products) {
    var list = (Array.isArray(products) ? products : []).filter(function (product) {
      return product && (String(product.image_url || product.cover || '').trim() || product.id);
    }).slice(0, 3);
    if (!list.length) return '<span class="mi-clr-muted">—</span>';
    return '<div class="mi-product-strip">' + list.map(function (p) {
      var img = p.image_url || p.cover || p.pic || '';
      var title = p.title || p.product_title || 'Sản phẩm nổi bật';
      var price = p.revenue_formatted || (p.min_real_price ? ('₫' + fmtSale(p.min_real_price)) : (p.unit_price || p.price || ''));
      var pid = p.id || p.product_id || '';

      return '<div class="mi-product-strip-item" data-pid="' + esc(pid) + '">' +
        renderProductThumb(img, 'mi-product-strip-img', pid) +
        '<div class="mi-prod-hover-popover">' +
          '<div class="mi-popover-thumb">' +
            renderProductThumb(img, 'mi-popover-cover-img', pid) +
          '</div>' +
          '<div class="mi-popover-title">' + esc(title) + '</div>' +
          '<div class="mi-popover-bottom">' +
            '<span class="mi-popover-price">' + esc(price || '—') + '</span>' +
            '<button type="button" class="mi-popover-link" data-open-pid="' + esc(pid) + '">Chi tiết ' + ICON.chevronRight + '</button>' +
          '</div>' +
        '</div>' +
      '</div>';
    }).join('') + '</div>';
  }

  // Normalize video ID — Market Intelligence dùng nhiều tên field khác nhau
  function vidId(v) {
    return String(v.id || v.item_id || v.video_id || v.aweme_id || v.itemId || v.videoId || v.awemeId || '').trim();
  }
  // Normalize video handle
  function vidHandle(v) {
    return String(v.handle || v.creator_handle || v.author || v.author_handle || v.nick_name || '').trim();
  }

  function renderProductVideosStrip(videos, productId) {
    if (!Array.isArray(videos) || !videos.length) {
      return '<span class="mi-clr-muted">—</span>';
    }
    var validVideos = videos.slice(0, 3).filter(function (v) {
      return v && (vidId(v) || v.cover_url || v.cover || v.image_url || v.thumbnail_url);
    });
    if (!validVideos.length) {
      return '<span class="mi-clr-muted">—</span>';
    }
    return '<div class="mi-video-strip">' +
      validVideos.map(function (v) {
        var vid = vidId(v);
        var vHandle = vidHandle(v);
        var vCover = v.cover_url || v.cover || v.image_url || v.thumbnail_url || (vid ? ('https://img.kalocdn.com/tiktok.video/' + vid + '/cover.png') : '');
        var vTitle = v.description || v.title || 'Video sản phẩm';
        return '<div class="mi-video-thumb-box" data-vid="' + esc(vid) + '" data-handle="' + esc(vHandle) + '" data-title="' + esc(vTitle) + '">' +
          (vCover ? '<img src="' + esc(vCover) + '" alt="Video" loading="lazy" referrerpolicy="no-referrer" onerror="this.onerror=null;this.style.display=\'none\'">' : '') +
          '<span class="mi-video-play-icon">' + ICON.play + '</span>' +
        '</div>';
      }).join('') +
    '</div>';
  }

  function renderCreatorAvatar(creator) {
    var url = String(creator && (creator.avatar_url || creator.avatar || creator.head_image_url) || '').trim();
    if (url) return renderShopAvatar(url, 'mi-creator-avatar');
    var fallback = initials(creator && (creator.nickname || creator.handle));
    return '<span class="mi-avatar mi-creator-avatar">' + (fallback ? esc(fallback) : ICON.sparkles) + '</span>';
  }

  function sparkline(arr, w, h, isFullWidth) {
    w = w || 90; h = h || 28;
    var data = (Array.isArray(arr) ? arr : []).map(Number).filter(isFinite).slice(-30);
    if (data.length < 2) {
      return '<svg width="' + (isFullWidth ? '100%' : w) + '" height="' + h + '" style="display:block"><text x="50%" y="55%" text-anchor="middle" fill="#94a3b8" font-size="9" font-family="monospace">30 NGÀY</text></svg>';
    }
    var mn = Math.min.apply(null, data);
    var mx = Math.max.apply(null, data);
    var range = mx - mn || 1;
    var pad = 3;
    var viewW = isFullWidth ? 380 : w;
    var pts = data.map(function (v, i) {
      var x = pad + (i / (data.length - 1)) * (viewW - pad * 2);
      var y = (h - pad) - ((v - mn) / range) * (h - pad * 2);
      return x.toFixed(1) + ',' + y.toFixed(1);
    });
    var last = data[data.length - 1], first = data[0];
    var isUp = last >= first;
    var strokeColor = isUp ? '#7c3aed' : '#ea580c'; // Tím cho tăng trưởng, Cam cho giảm
    var fillId = 'light_g_' + Math.random().toString(36).substr(2, 9);
    var fillPts = pts.join(' ') + ' ' + (viewW - pad) + ',' + (h - pad) + ' ' + pad + ',' + (h - pad);
    var widthAttr = isFullWidth ? 'width="100%"' : 'width="' + w + '"';

    return '<svg ' + widthAttr + ' height="' + h + '" viewBox="0 0 ' + viewW + ' ' + h + '" preserveAspectRatio="none" class="mi-spark">' +
      '<defs>' +
        '<linearGradient id="' + fillId + '" x1="0" y1="0" x2="0" y2="1">' +
          '<stop offset="0%" stop-color="' + strokeColor + '" stop-opacity="0.22"/>' +
          '<stop offset="100%" stop-color="' + strokeColor + '" stop-opacity="0.0"/>' +
        '</linearGradient>' +
      '</defs>' +
      '<polygon points="' + fillPts + '" fill="url(#' + fillId + ')"/>' +
      '<polyline points="' + pts.join(' ') + '" fill="none" stroke="' + strokeColor + '" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/>' +
      '<circle cx="' + pts[pts.length - 1].split(',')[0] + '" cy="' + pts[pts.length - 1].split(',')[1] + '" r="3" fill="' + strokeColor + '"/>' +
    '</svg>';
  }

  function metricValue(row, keys) {
    if (typeof row === 'number' && keys.indexOf('revenue') >= 0) return isFinite(row) ? row : null;
    for (var i = 0; i < keys.length; i++) {
      var value = row && row[keys[i]];
      if (value !== null && value !== undefined && value !== '' && isFinite(Number(value))) return Number(value);
    }
    return null;
  }

  function metricDate(row, index, totalRows) {
    var raw = row && (row.partitionDay || row.partition_day || row.date);
    if (raw) {
      var s = String(raw).trim();
      if (/^\d{4}-\d{2}-\d{2}$/.test(s)) {
        var parts = s.split('-');
        return parts[2] + '/' + parts[1];
      }
      if (/^\d{8}$/.test(s)) {
        return s.slice(6, 8) + '/' + s.slice(4, 6);
      }
      return s;
    }
    var len = totalRows || 30;
    var d = new Date(Date.now() - Math.max(0, (len - 1 - index)) * 86400000);
    return ('0' + d.getDate()).slice(-2) + '/' + ('0' + (d.getMonth() + 1)).slice(-2);
  }

  function shortMetricDate(value) {
    return String(value || '');
  }

  function formatMetricValue(value, unit) {
    var number = Number(value || 0);
    if (unit === 'money') return new Intl.NumberFormat('vi-VN', { style: 'currency', currency: 'VND', maximumFractionDigits: 0 }).format(number);
    if (unit === 'percent') return (number * (Math.abs(number) <= 1 ? 100 : 1)).toFixed(1) + '%';
    return Math.round(number).toLocaleString('vi-VN');
  }

  function bindMetricExplorer(container, history, metrics) {
    if (!container) return;
    var rows = Array.isArray(history) ? history : [];
    var available = metrics.filter(function (metric) {
      return rows.some(function (row) { return metricValue(row, metric.keys) !== null; });
    });
    if (rows.length < 2 || !available.length) {
      container.innerHTML = '<div class="mi-chart-empty">' + ICON.chart + '<span>Chưa có đủ dữ liệu lịch sử để vẽ biểu đồ</span></div>';
      return;
    }
    var selected = [available[0].id];

    function draw() {
      var activeMetrics = available.filter(function (metric) { return selected.indexOf(metric.id) >= 0; }).slice(0, 2);
      var width = 760, height = 250, left = 64, right = activeMetrics.length > 1 ? 64 : 22, top = 20, bottom = 36;
      var innerWidth = width - left - right, innerHeight = height - top - bottom;
      var series = activeMetrics.map(function (metric) {
        var values = rows.map(function (row) { return metricValue(row, metric.keys); });
        var finite = values.filter(function (value) { return value !== null; });
        var min = Math.min.apply(null, finite.concat([0]));
        var max = Math.max.apply(null, finite.concat([1]));
        if (max === min) max = min + 1;
        var points = values.map(function (value, index) {
          var safe = value === null ? min : value;
          var x = left + (index / Math.max(1, rows.length - 1)) * innerWidth;
          var y = top + innerHeight - ((safe - min) / (max - min)) * innerHeight;
          return { x: x, y: y, value: value };
        });
        return { metric: metric, values: values, min: min, max: max, points: points };
      });
      var grid = '';
      for (var tick = 0; tick <= 3; tick++) {
        var y = top + (tick / 3) * innerHeight;
        grid += '<line x1="' + left + '" y1="' + y + '" x2="' + (width - right) + '" y2="' + y + '" class="mi-chart-grid"/>';
      }
      var xLabels = '';
      var labelCount = Math.min(7, rows.length);
      for (var labelIndex = 0; labelIndex < labelCount; labelIndex++) {
        var rowIndex = Math.round(labelIndex * (rows.length - 1) / Math.max(1, labelCount - 1));
        var x = left + (rowIndex / Math.max(1, rows.length - 1)) * innerWidth;
        xLabels += '<text x="' + x + '" y="' + (height - 10) + '" text-anchor="middle" class="mi-chart-axis-label">' + esc(shortMetricDate(metricDate(rows[rowIndex], rowIndex, rows.length))) + '</text>';
      }
      var paths = series.map(function (item) {
        var points = item.points.map(function (point) { return point.x.toFixed(1) + ',' + point.y.toFixed(1); }).join(' ');
        return '<polyline points="' + points + '" fill="none" stroke="' + item.metric.color + '" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>';
      }).join('');
      var yLabels = series.map(function (item, seriesIndex) {
        var x = seriesIndex === 0 ? left - 8 : width - right + 8;
        var anchor = seriesIndex === 0 ? 'end' : 'start';
        return [item.max, (item.max + item.min) / 2, item.min].map(function (value, index) {
          var y = top + (index / 2) * innerHeight + 4;
          return '<text x="' + x + '" y="' + y + '" text-anchor="' + anchor + '" class="mi-chart-axis-label">' + esc(fmtSale(value)) + '</text>';
        }).join('');
      }).join('');
      var hitAreas = rows.map(function (_, index) {
        var x = left + (index / Math.max(1, rows.length - 1)) * innerWidth;
        var half = innerWidth / Math.max(1, rows.length - 1) / 2;
        return '<rect class="mi-chart-hit" tabindex="0" aria-label="' + esc(metricDate(rows[index], index, rows.length)) + '" data-index="' + index + '" x="' + Math.max(left, x - half) + '" y="' + top + '" width="' + Math.max(8, half * 2) + '" height="' + innerHeight + '" fill="transparent"/>';
      }).join('');
      var buttons = available.map(function (metric) {
        var active = selected.indexOf(metric.id) >= 0;
        return '<button class="mi-metric-option' + (active ? ' active' : '') + '" data-metric="' + metric.id + '"><span style="background:' + metric.color + '"></span>' + esc(metric.label) + '</button>';
      }).join('');
      container.innerHTML = '<div class="mi-metric-head"><div><strong>Dữ liệu lịch sử</strong><span>Chọn tối đa 2 chỉ số để so sánh</span></div><div class="mi-metric-legend">' + activeMetrics.map(function (metric) { return '<span><i style="background:' + metric.color + '"></i>' + esc(metric.label) + '</span>'; }).join('') + '</div></div>' +
        '<div class="mi-metric-layout"><div class="mi-metric-options">' + buttons + '</div><div class="mi-chart-stage"><svg class="mi-trend-chart" viewBox="0 0 ' + width + ' ' + height + '" preserveAspectRatio="none">' + grid + xLabels + yLabels + paths + hitAreas + '<line class="mi-chart-hover-line" x1="0" x2="0" y1="' + top + '" y2="' + (height - bottom) + '"/></svg><div class="mi-chart-tooltip"></div></div></div>';

      container.querySelectorAll('.mi-metric-option').forEach(function (button) {
        button.addEventListener('click', function () {
          var id = button.dataset.metric;
          var index = selected.indexOf(id);
          if (index >= 0) {
            if (selected.length === 1) return;
            selected.splice(index, 1);
          } else {
            if (selected.length >= 2) selected.shift();
            selected.push(id);
          }
          draw();
        });
      });
      var tooltip = container.querySelector('.mi-chart-tooltip');
      var hoverLine = container.querySelector('.mi-chart-hover-line');
      container.querySelectorAll('.mi-chart-hit').forEach(function (hit) {
        function showTooltip() {
          var index = Number(hit.dataset.index);
          var x = left + (index / Math.max(1, rows.length - 1)) * innerWidth;
          hoverLine.setAttribute('x1', x); hoverLine.setAttribute('x2', x); hoverLine.classList.add('visible');
          tooltip.innerHTML = '<strong>' + esc(metricDate(rows[index], index, rows.length)) + '</strong>' + series.map(function (item) {
            return '<span><i style="background:' + item.metric.color + '"></i>' + esc(item.metric.label) + ': <b>' + esc(formatMetricValue(item.values[index], item.metric.unit)) + '</b></span>';
          }).join('');
          tooltip.style.left = Math.min(78, Math.max(2, (x / width) * 100)) + '%';
          tooltip.classList.add('visible');
        }
        hit.addEventListener('mouseenter', showTooltip);
        hit.addEventListener('pointerdown', showTooltip);
        hit.addEventListener('focus', showTooltip);
        hit.addEventListener('mouseleave', function () { tooltip.classList.remove('visible'); hoverLine.classList.remove('visible'); });
        hit.addEventListener('blur', function () { tooltip.classList.remove('visible'); hoverLine.classList.remove('visible'); });
      });
    }
    draw();
  }

  function decodeTokenRole(token) {
    try {
      if (!token) return "";
      var parts = String(token).split(".");
      if (parts.length < 2) return "";
      var base64Url = parts[1];
      var base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
      var jsonPayload = decodeURIComponent(atob(base64).split("").map(function(c) {
        return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
      }).join(""));
      var parsed = JSON.parse(jsonPayload);
      return String(parsed.role || parsed.userType || "").toLowerCase();
    } catch (e) {
      return "";
    }
  }

  function isSuperAdmin() {
    try {
      if (window.state && window.state.access && String(window.state.access.role || "").toLowerCase() === "super_admin") return true;
      if (window.state && window.state.user && String(window.state.user.role || "").toLowerCase() === "super_admin") return true;
      if (window.stonkAccess && String(window.stonkAccess.role || "").toLowerCase() === "super_admin") return true;

      var sess = JSON.parse(localStorage.getItem("stonk-session") || "{}");
      if (String(sess.role || sess.userType || "").toLowerCase() === "super_admin") return true;

      var token = localStorage.getItem("stonk-auth-token") || sess.token;
      if (token) {
        var tokenRole = decodeTokenRole(token);
        if (tokenRole === "super_admin") return true;
      }
      return false;
    } catch (e) {
      return false;
    }
  }

  function loadingHtml() {
    return '<div class="mi-loading">' +
      '<div class="mi-radar-orbit">' +
        '<span class="mi-radar-core"></span>' +
        '<span class="mi-radar-ring"></span>' +
      '</div>' +
      '<span>Đang tải dữ liệu thị trường TikTok...</span>' +
    '</div>';
  }

  function errHtml(msg) {
    return '<div class="mi-err">' + ICON.info + ' <span>Lỗi tín hiệu: ' + esc(msg) + '</span></div>';
  }

  async function loadCategoryOptions() {
    if (S.categoryOptions && S.categoryOptions.length > 0) return;
    try {
      var res = await api('/api/v1/market/category-options');
      if (res.success && Array.isArray(res.data)) {
        S.categoryOptions = res.data.map(function (c) {
          return { id: c.id, name: c.name };
        });
      }
    } catch (e) {}
  }

  /* ── TABLE COLUMN SORT HEADER HELPER ───────────────────────────── */
  function renderSortTh(label, sortKey, alignClass, width) {
    var isCurrent = (S.sortBy === sortKey);
    var dirIcon = isCurrent ? (S.sortOrder === 'ASC' ? ' ▲' : ' ▼') : ' <span style="opacity:0.35;font-size:10px">▲▼</span>';
    var cls = (alignClass || '') + (isCurrent ? ' mi-th-sorted' : '');
    var style = (width ? ('width:' + width + ';') : '') + 'cursor:pointer;user-select:none;';
    return '<th class="' + cls + '" style="' + style + '" data-sort="' + esc(sortKey) + '" title="Bấm để sắp xếp theo ' + esc(label) + '">' +
      '<div style="display:inline-flex;align-items:center;gap:4px;' + (alignClass && alignClass.indexOf('mi-th-r') !== -1 ? 'justify-content:flex-end;' : (alignClass && alignClass.indexOf('mi-th-c') !== -1 ? 'justify-content:center;' : '')) + '">' +
        '<span>' + label + '</span>' +
        '<span style="font-size:10px;color:' + (isCurrent ? 'var(--mi-primary,#7c3aed)' : 'var(--mi-muted)') + ';font-weight:800">' + dirIcon + '</span>' +
      '</div>' +
    '</th>';
  }

  /* ── VIDEO BADGES & CATEGORY HELPER ───────────────────────────── */
  var CAT_PALETTE = {
    'Sắc đẹp': { bg: '#fdf2f8', border: '#fbcfe8', text: '#db2777' },
    'Mẹ & Bé': { bg: '#ecfdf5', border: '#a7f3d0', text: '#059669' },
    'Thời trang nữ': { bg: '#faf5ff', border: '#e9d5ff', text: '#9333ea' },
    'Thời trang nam': { bg: '#eff6ff', border: '#bfdbfe', text: '#2563eb' },
    'Thực phẩm & Đồ uống': { bg: '#fffbeb', border: '#fde68a', text: '#d97706' },
    'Sức khỏe': { bg: '#fef2f2', border: '#fecaca', text: '#dc2626' },
    'Đồ gia dụng': { bg: '#f0fdf4', border: '#bbf7d0', text: '#16a34a' },
    'Thiết bị điện tử': { bg: '#f0f9ff', border: '#bae6fd', text: '#0284c7' },
    'Giày dép & Túi xách': { bg: '#fff7ed', border: '#fed7aa', text: '#ea580c' },
    'Phụ kiện': { bg: '#fdf4ff', border: '#f5d0fe', text: '#c026d3' },
    'Tiêu dùng chung': { bg: '#f8fafc', border: '#e2e8f0', text: '#475569' }
  };

  function detectVideoCategory(v) {
    if (v.category_name && typeof v.category_name === 'string' && v.category_name.trim()) {
      return v.category_name.trim();
    }
    if (v.first_category_name && typeof v.first_category_name === 'string' && v.first_category_name.trim()) {
      return v.first_category_name.trim();
    }
    if (v.product_category && typeof v.product_category === 'string' && v.product_category.trim()) {
      return v.product_category.trim();
    }

    var text = ((v.description || '') + ' ' + (v.handle || '')).toLowerCase();
    
    if (/(cerave|kemchongnang|chamsocda|skincare|mypham|suaruamat|sonmoi|duongda|serum|trangdiem|makeup|collagen|trangda|tri mun|toner|retinol|nuochoa|dưỡng|da |mụn|son|sữa rửa mặt|mỹ phẩm|kem chống nắng|tẩy trang|nước hoa|làm đẹp|trang điểm|phấn|mascara)/i.test(text)) {
      return 'Sắc đẹp';
    }
    if (/(bỉm|tã|em bé|bé |trẻ em|mevabe|bimsua|suacongthuc|bonbonbaby|sữa bầu|bình sữa|xe đẩy|ăn dặm|núm ti|quần áo trẻ|sơ sinh|đồ chơi bé)/i.test(text)) {
      return 'Mẹ & Bé';
    }
    if (/(aonguc|aolot|vay|dam|aocroptop|yibolemon|xunuyeuanh|quynhanh|áo ngực|áo nâng|váy|đầm|nội y|set đồ|chân váy|áo thun|quần ống rộng|áo sơ mi|bikini|đồ ngủ|áo hai dây|croptop|quần jean nữ|legging|jum)/i.test(text)) {
      return 'Thời trang nữ';
    }
    if (/(áo nam|quần nam|polo nam|sơ mi nam|khoác nam|boxer|quần lót nam|vest nam|quần tây nam|áo phông nam)/i.test(text)) {
      return 'Thời trang nam';
    }
    if (/(ăn vặt|trà sữa|bánh|kẹo|khô gà|khô bò|cơm cháy|yến chưng|ngũ cốc|hạt dinh dưỡng|thực phẩm|nước ép|đồ uống|cà phê|trà |mì tôm|xúc xích)/i.test(text)) {
      return 'Thực phẩm & Đồ uống';
    }
    if (/(thực phẩm chức năng|vitamin|bổ não|tăng chiều cao|giảm cân|giảm mỡ|viên uống|detox|sức khỏe|dầu cá|canxi|men vi sinh)/i.test(text)) {
      return 'Sức khỏe';
    }
    if (/(gia dụng|nhà cửa|nồi chiên|máy hút bụi|quạt|bàn ủi|khăn|chăn ga|gối|hộp đựng|kệ|đèn|vệ sinh|bếp|dao|chảo|lau nhà|tinh dầu)/i.test(text)) {
      return 'Đồ gia dụng';
    }
    if (/(tai nghe|loa bluetooth|sạc dự phòng|cáp sạc|điện thoại|máy tính|bàn phím|chuột|ốp lưng|gậy chụp hình|smartwatch|củ sạc|camera|màn hình)/i.test(text)) {
      return 'Thiết bị điện tử';
    }
    if (/(giày|dép|sneaker|sandal|túi xách|balo|ví |vali|guốc|boots|sục|túi đeo)/i.test(text)) {
      return 'Giày dép & Túi xách';
    }
    if (/(trang sức|đồng hồ|dây chuyền|nhẫn|kính|nón|mũ|vòng tay|bông tai|khuyên tai|cài tóc)/i.test(text)) {
      return 'Phụ kiện';
    }

    return 'Tiêu dùng chung';
  }

  function renderVideoCategoryBadge(v) {
    var catName = detectVideoCategory(v);
    var theme = CAT_PALETTE[catName] || CAT_PALETTE['Tiêu dùng chung'];
    return '<span class="mi-video-cat-badge" style="background:' + theme.bg + ';color:' + theme.text + ';border-color:' + theme.border + '" title="Ngành hàng: ' + esc(catName) + '">' +
      ICON.tag + ' <span>' + esc(catName) + '</span>' +
    '</span>';
  }

  function renderVideoBadges(v) {
    var badges = [];
    if (v.ad) {
      badges.push('<span class="mi-badge-ad" title="Video đang chạy chiến dịch TikTok Ads">' + ICON.adMegaphone + ' QC TikTok Ads</span>');
    }
    if (v.ai_video) {
      badges.push('<span class="mi-badge-ai" title="Video sáng tạo bằng trí tuệ nhân tạo AI">' + ICON.sparkles + ' AI Sáng Tạo</span>');
    }
    if (v.ad_roas && Number(v.ad_roas) >= 3.0) {
      badges.push('<span class="mi-badge-roas" title="Hiệu quả chuyển đổi ROAS cực cao">' + ICON.diamond + ' ROAS ' + Number(v.ad_roas).toFixed(1) + 'x</span>');
    }
    if (v.views && Number(v.views) >= 500000) {
      badges.push('<span class="mi-badge-viral" title="Video đạt lượng người xem đột phá">' + ICON.flame + ' Viral Hot</span>');
    }
    return badges.join(' ');
  }

  /* ── 2-ROW UNIFIED TOP ACTION & FILTER BAR ───────────────────────── */
  function topActionBarHtml(tabType) {
    var catOptionsHtml = '<option value="">Tất cả ngành hàng</option>';
    (S.categoryOptions || []).forEach(function (c) {
      catOptionsHtml += '<option value="' + esc(c.id) + '"' + (S.categoryId === c.id ? ' selected' : '') + '>' + esc(c.name) + '</option>';
    });

    var sortOptions = [];
    if (tabType === 'categories') {
      sortOptions = [
        { val: 'revenue_DESC', label: 'Doanh thu: Cao → Thấp' },
        { val: 'growth_DESC', label: 'Tăng trưởng: Nhanh nhất' },
        { val: 'shop_number_DESC', label: 'Số lượng Shop: Nhiều nhất' },
        { val: 'average_revenue_DESC', label: 'Doanh thu TB / Shop' }
      ];
    } else if (tabType === 'products') {
      sortOptions = [
        { val: 'revenue_DESC', label: 'Doanh thu: Cao → Thấp' },
        { val: 'sale_DESC', label: 'Lượt bán: Chạy nhất' },
        { val: 'title_ASC', label: 'Tên sản phẩm: A → Z' }
      ];
    } else if (tabType === 'shops') {
      sortOptions = [
        { val: 'revenue_DESC', label: 'Doanh thu: Cao → Thấp' },
        { val: 'growth_DESC', label: 'Tăng trưởng: Nhanh nhất' },
        { val: 'sale_DESC', label: 'Doanh số: Nhiều nhất' },
        { val: 'name_ASC', label: 'Tên cửa hàng: A → Z' }
      ];
    } else if (tabType === 'videos') {
      sortOptions = [
        { val: 'ad_roas_DESC', label: 'ROAS QC: Cao nhất' },
        { val: 'revenue_DESC', label: 'Doanh thu: Cao → Thấp' },
        { val: 'views_DESC', label: 'Lượt xem: Nhiều nhất' },
        { val: 'sale_DESC', label: 'Lượt bán: Nhiều nhất' },
        { val: 'ad_cost_DESC', label: 'Chi phí QC: Cao → Thấp' },
        { val: 'gpm_DESC', label: 'GPM: Cao nhất' },
        { val: 'publish_date_DESC', label: 'Ngày đăng: Mới nhất' }
      ];
    }

    var currentSortVal = S.sortBy + '_' + S.sortOrder;
    var sortOptionsHtml = sortOptions.map(function (o) {
      return '<option value="' + o.val + '"' + (currentSortVal === o.val ? ' selected' : '') + '>' + o.label + '</option>';
    }).join('');

    var contextualFilterHtml = '';
    if (tabType === 'shops' || tabType === 'products') {
      contextualFilterHtml =
        '<select class="mi-top-select-sm" id="miSideSellerTypeSelect" title="Loại gian hàng">' +
          '<option value="ALL"' + (S.sellerType === 'ALL' ? ' selected' : '') + '>Tất cả gian hàng</option>' +
          '<option value="BRAND"' + (S.sellerType === 'BRAND' ? ' selected' : '') + '>Mall / Brand</option>' +
          '<option value="SHOP"' + (S.sellerType === 'SHOP' ? ' selected' : '') + '>Shop tiêu chuẩn</option>' +
        '</select>';
    } else if (tabType === 'videos') {
      contextualFilterHtml =
        '<select class="mi-top-select-sm" id="miSideMinAdRoas" title="Lọc mức ROAS QC">' +
          '<option value="0"' + (S.minAdRoas === 0 ? ' selected' : '') + '>Mọi mức ROAS</option>' +
          '<option value="2"' + (S.minAdRoas === 2 ? ' selected' : '') + '>ROAS &ge; 2.0x</option>' +
          '<option value="3"' + (S.minAdRoas === 3 ? ' selected' : '') + '>ROAS &ge; 3.0x</option>' +
          '<option value="5"' + (S.minAdRoas === 5 ? ' selected' : '') + '>ROAS &ge; 5.0x</option>' +
        '</select>';
    } else if (tabType === 'categories') {
      contextualFilterHtml =
        '<select class="mi-top-select-sm" id="miSideCatLevel" title="Phân cấp ngành">' +
          '<option value="ALL"' + (S.catLevel === 'ALL' ? ' selected' : '') + '>Toàn bộ phân cấp</option>' +
          '<option value="L1"' + (S.catLevel === 'L1' ? ' selected' : '') + '>Cấp 1 (Tổng quan)</option>' +
          '<option value="L2"' + (S.catLevel === 'L2' ? ' selected' : '') + '>Cấp 2 (Chi tiết)</option>' +
        '</select>';
    }

    return '<div class="mi-top-bar">' +
      '<div class="mi-top-bar-left-controls">' +
        '<select class="mi-top-select-sm mi-top-cat-select" id="miSideCatSelect" title="Chọn ngành hàng">' + catOptionsHtml + '</select>' +
        contextualFilterHtml +
        '<div class="mi-top-bar-search">' +
          '<span class="mi-search-icon">' + ICON.search + '</span>' +
          '<input class="mi-top-search-input" id="miTopSearch" type="text" placeholder="' +
            (tabType === 'categories' ? 'Nhập tên ngành hàng...' : (tabType === 'products' ? 'Nhập tên sản phẩm hot...' : (tabType === 'videos' ? 'Nhập tiêu đề video, KOC @handle...' : 'Nhập tên cửa hàng / thương hiệu...'))) +
          '" value="' + esc(S.search) + '" autocomplete="off">' +
          (S.search ? '<button class="mi-search-clear" id="miTopClear">' + ICON.close + '</button>' : '') +
          '<div class="mi-search-suggestions-box" id="miSearchSuggestions" style="display:none"></div>' +
        '</div>' +
      '</div>' +

      '<div class="mi-top-bar-right">' +
        '<span class="mi-chip-active-time">' + ICON.calendar + ' 30 ngày trước</span>' +
        '<div class="mi-sort-box">' +
          '<span class="mi-sort-icon">' + ICON.sort + '</span>' +
          '<select class="mi-sort-select" id="miTopSort">' + sortOptionsHtml + '</select>' +
        '</div>' +
      '</div>' +
    '</div>';
  }

  function horizontalFilterBarHtml(tabType) {
    if (tabType !== 'videos') return '';

    return '<div class="mi-horizontal-filter-bar" aria-label="Bộ lọc video nâng cao">' +
      '<div class="mi-filter-grid-row-1">' +
        '<label class="mi-filter-col"><span class="mi-filter-col-label">Doanh thu tối thiểu</span>' +
          '<select class="mi-market-select-sm" id="miSideMinRev">' +
            '<option value="0"' + (S.minRevenue === 0 ? ' selected' : '') + '>Tất cả</option>' +
            '<option value="10000000"' + (S.minRevenue === 10000000 ? ' selected' : '') + '>10 triệu</option>' +
            '<option value="100000000"' + (S.minRevenue === 100000000 ? ' selected' : '') + '>100 triệu</option>' +
            '<option value="1000000000"' + (S.minRevenue === 1000000000 ? ' selected' : '') + '>1 tỷ</option>' +
          '</select></label>' +
        '<label class="mi-filter-col"><span class="mi-filter-col-label">Lượt xem tối thiểu</span>' +
          '<select class="mi-market-select-sm" id="miSideMinViews">' +
            '<option value="0"' + (S.minViews === 0 ? ' selected' : '') + '>Tất cả</option>' +
            '<option value="10000"' + (S.minViews === 10000 ? ' selected' : '') + '>10 nghìn</option>' +
            '<option value="100000"' + (S.minViews === 100000 ? ' selected' : '') + '>100 nghìn</option>' +
            '<option value="1000000"' + (S.minViews === 1000000 ? ' selected' : '') + '>1 triệu</option>' +
          '</select></label>' +
      '</div>' +
      '<div class="mi-filter-grid-row-2">' +
        '<div class="mi-filter-checks-wrap">' +
          '<label class="mi-filter-col"><input type="checkbox" id="miSideIsAd"' + (S.isAd ? ' checked' : '') + '> Chỉ video quảng cáo</label>' +
          '<label class="mi-filter-col"><input type="checkbox" id="miSideIsAiVideo"' + (S.isAiVideo ? ' checked' : '') + '> Chỉ video AI</label>' +
        '</div>' +
        '<div class="mi-filter-actions-wrap"><button type="button" class="mi-filter-bar-reset-btn" id="miSidebarReset">Đặt lại bộ lọc</button></div>' +
      '</div>' +
    '</div>';
  }

  function marketSummaryCardsHtml(tab, summary, total) {
    summary = summary || {};
    var cards = [];
    if (tab === 'categories') {
      cards = [
        kpiCard(ICON.planet, 'TỔNG NGÀNH HÀNG', Number(total || 0).toLocaleString('vi-VN'), 'Theo bộ lọc hiện tại', 'mi-clr-purple'),
        kpiCard(ICON.money, 'TỔNG GMV', formatSummaryGmv(summary.totalRevenue || 0), 'Toàn bộ kết quả lọc', 'mi-clr-emerald'),
        kpiCard(ICON.users, 'TỔNG CỬA HÀNG', fmtSale(summary.totalShops || 0), 'Trong các ngành phù hợp', 'mi-clr-cyan'),
        kpiCard(ICON.fire, 'TĂNG TRƯỞNG TB', Number(summary.avgGrowthRate || 0).toFixed(2) + '%', 'Theo dữ liệu tổng hợp', 'mi-clr-orange')
      ];
    } else if (tab === 'products') {
      cards = [
        kpiCard(ICON.box, 'TỔNG SẢN PHẨM', Number(total || 0).toLocaleString('vi-VN'), 'Theo bộ lọc hiện tại', 'mi-clr-purple'),
        kpiCard(ICON.money, 'TỔNG GMV', formatSummaryGmv(summary.totalRevenue || 0), 'Toàn bộ kết quả lọc', 'mi-clr-emerald'),
        kpiCard(ICON.bag, 'TỔNG LƯỢT BÁN', fmtSale(summary.totalSales || 0), 'Sản lượng đã bán', 'mi-clr-cyan'),
        kpiCard(ICON.star, 'ĐÁNH GIÁ TB', Number(summary.avgRating || 0).toFixed(2) + ' ⭐', 'Chất lượng sản phẩm', 'mi-clr-gold')
      ];
    } else if (tab === 'shops') {
      cards = [
        kpiCard(ICON.store, 'TỔNG CỬA HÀNG', Number(total || 0).toLocaleString('vi-VN'), 'Theo bộ lọc hiện tại', 'mi-clr-purple'),
        kpiCard(ICON.money, 'TỔNG GMV', formatSummaryGmv(summary.totalRevenue || 0), 'Toàn bộ kết quả lọc', 'mi-clr-emerald'),
        kpiCard(ICON.bag, 'TỔNG LƯỢT BÁN', fmtSale(summary.totalSales || 0), 'Sản lượng toàn bộ shop', 'mi-clr-cyan'),
        kpiCard(ICON.fire, 'TĂNG TRƯỞNG TB', Number(summary.avgGrowthRate || 0).toFixed(2) + '%', 'Theo dữ liệu tổng hợp', 'mi-clr-orange')
      ];
    }
    return cards.length ? '<div class="mi-kpi-grid mi-kpi-grid-4 mi-list-summary-kpis">' + cards.join('') + '</div>' : '';
  }

  function bindMarketEvents(el, fetchFn) {
    var searchInp = el.querySelector('#miTopSearch');
    var searchClear = el.querySelector('#miTopClear');
    var searchSuggestions = el.querySelector('#miSearchSuggestions');
    var sortSel = el.querySelector('#miTopSort');
    var sideCat = el.querySelector('#miSideCatSelect');
    var sellerSel = el.querySelector('#miSideSellerTypeSelect');
    var catLevelSel = el.querySelector('#miSideCatLevel');
    var roasSel = el.querySelector('#miSideMinAdRoas');
    // Hai bộ lọc này chỉ có ở một số layout Market Intelligence. Luôn khai báo
    // trong scope hiện tại để layout không có control sẽ bỏ qua an toàn.
    var sideGrowth = el.querySelector('#miSideGrowth');
    var sideComm = el.querySelector('#miSideComm');
    var sideMinRev = el.querySelector('#miSideMinRev');
    var sideReset = el.querySelector('#miSidebarReset');
    // Có hai kiểu nút thu gọn ở các layout cũ/mới. Lấy danh sách ngay trong scope
    // của hàm để không còn nhánh render nào tham chiếu biến `toggleBtn` chưa khai báo.
    var sidebarToggleButtons = Array.from(el.querySelectorAll('#miToggleSidebarBtn, .mi-toggle-sidebar-btn'));
    var collapseBtn = el.querySelector('#miSidebarCollapseBtn');
    var sideHead = el.querySelector('.mi-sidebar-head');

    if (sideCat) {
      sideCat.addEventListener('change', function () {
        S.categoryId = sideCat.value;
        S.page = 1;
        fetchFn();
      });
    }

    if (sellerSel) {
      sellerSel.addEventListener('change', function () {
        S.sellerType = sellerSel.value;
        S.page = 1;
        fetchFn();
      });
    }

    if (catLevelSel) {
      catLevelSel.addEventListener('change', function () {
        S.catLevel = catLevelSel.value;
        S.page = 1;
        fetchFn();
      });
    }

    if (roasSel) {
      roasSel.addEventListener('change', function () {
        S.minAdRoas = Number(roasSel.value) || 0;
        S.page = 1;
        fetchFn();
      });
    }

    function toggleSidebar() {
      S.filterCollapsed = !S.filterCollapsed;
      try { localStorage.setItem('mi_filter_collapsed', S.filterCollapsed ? '1' : '0'); } catch(_) {}
      renderTab();
    }

    sidebarToggleButtons.forEach(function (button) {
      button.addEventListener('click', toggleSidebar);
    });

    if (collapseBtn) {
      collapseBtn.addEventListener('click', function (e) {
        e.stopPropagation();
        toggleSidebar();
      });
    }

    if (sideHead) {
      sideHead.addEventListener('click', function (e) {
        if (e.target.closest('#miSidebarReset')) return;
        toggleSidebar();
      });
    }

    if (searchInp) {
      var timer = null;
      var suggestTimer = null;

      searchInp.addEventListener('input', function () {
        var query = searchInp.value.trim();
        clearTimeout(timer);
        clearTimeout(suggestTimer);

        // Instant Autocomplete suggestions popup
        if (query.length >= 2 && searchSuggestions) {
          suggestTimer = setTimeout(async function () {
            try {
              var sRes = await api('/api/v1/market/search?keyword=' + encodeURIComponent(query) + '&limit=4');
              if (sRes.success && sRes.data) {
                renderSearchSuggestions(searchSuggestions, sRes.data, query);
              }
            } catch (e) {}
          }, 200);
        } else if (searchSuggestions) {
          searchSuggestions.style.display = 'none';
        }

        timer = setTimeout(function () {
          S.search = query;
          S.page = 1;
          fetchFn();
        }, 400);
      });

      searchInp.addEventListener('keydown', function (e) {
        if (e.key === 'Enter') {
          clearTimeout(timer);
          clearTimeout(suggestTimer);
          if (searchSuggestions) searchSuggestions.style.display = 'none';
          S.search = searchInp.value.trim();
          S.page = 1;
          fetchFn();
        } else if (e.key === 'Escape' && searchSuggestions) {
          searchSuggestions.style.display = 'none';
        }
      });

      document.addEventListener('click', function (e) {
        if (searchSuggestions && !searchInp.contains(e.target) && !searchSuggestions.contains(e.target)) {
          searchSuggestions.style.display = 'none';
        }
      });
    }

    if (searchClear) {
      searchClear.addEventListener('click', function () {
        S.search = '';
        if (searchSuggestions) searchSuggestions.style.display = 'none';
        S.page = 1;
        fetchFn();
      });
    }

    if (sortSel) {
      sortSel.addEventListener('change', function () {
        var parts = sortSel.value.split('_');
        S.sortBy = parts[0];
        S.sortOrder = parts[1] || 'DESC';
        S.page = 1;
        fetchFn();
      });
    }

    if (sideCat) {
      sideCat.addEventListener('change', function () {
        S.categoryId = sideCat.value;
        S.page = 1;
        fetchFn();
      });
    }

    el.querySelectorAll('.mi-side-pill').forEach(function (pill) {
      pill.addEventListener('click', function () {
        el.querySelectorAll('.mi-side-pill').forEach(function (p) { p.classList.remove('active'); });
        pill.classList.add('active');
        S.sellerType = pill.dataset.st;
        S.page = 1;
        fetchFn();
      });
    });

    if (sideGrowth) {
      sideGrowth.addEventListener('change', function () {
        S.growthOnly = sideGrowth.checked;
        S.page = 1;
        fetchFn();
      });
    }

    if (sideComm) {
      sideComm.addEventListener('change', function () {
        S.minCommission = sideComm.checked ? 10 : 0;
        S.page = 1;
        fetchFn();
      });
    }

    var sideMinSale = el.querySelector('#miSideMinSale');
    var sideMinRating = el.querySelector('#miSideMinRating');

    if (sideMinSale) {
      sideMinSale.addEventListener('change', function () {
        S.minSale = Number(sideMinSale.value || 0);
        S.page = 1;
        fetchFn();
      });
    }

    if (sideMinRating) {
      sideMinRating.addEventListener('change', function () {
        S.minRating = Number(sideMinRating.value || 0);
        S.page = 1;
        fetchFn();
      });
    }

    if (sideMinRev) {
      sideMinRev.addEventListener('change', function () {
        S.minRevenue = Number(sideMinRev.value || 0);
        S.page = 1;
        fetchFn();
      });
    }

    var sideCatLevel = el.querySelector('#miSideCatLevel');
    var sideTop3 = el.querySelector('#miSideTop3Concentration');
    var sideTop10 = el.querySelector('#miSideTop10Concentration');
    var sideIsAd = el.querySelector('#miSideIsAd');
    var sideIsAiVideo = el.querySelector('#miSideIsAiVideo');
    var sideMinAdRoas = el.querySelector('#miSideMinAdRoas');
    var sideMinViews = el.querySelector('#miSideMinViews');

    if (sideIsAd) {
      sideIsAd.addEventListener('change', function () {
        S.isAd = sideIsAd.checked;
        S.page = 1;
        fetchFn();
      });
    }

    if (sideIsAiVideo) {
      sideIsAiVideo.addEventListener('change', function () {
        S.isAiVideo = sideIsAiVideo.checked;
        S.page = 1;
        fetchFn();
      });
    }

    if (sideMinAdRoas) {
      sideMinAdRoas.addEventListener('change', function () {
        S.minAdRoas = Number(sideMinAdRoas.value || 0);
        S.page = 1;
        fetchFn();
      });
    }

    if (sideMinViews) {
      sideMinViews.addEventListener('change', function () {
        S.minViews = Number(sideMinViews.value || 0);
        S.page = 1;
        fetchFn();
      });
    }

    if (sideCatLevel) {
      sideCatLevel.addEventListener('change', function () {
        S.catLevel = sideCatLevel.value;
        S.page = 1;
        fetchFn();
      });
    }

    if (sideTop3) {
      sideTop3.addEventListener('change', function () {
        S.top3Concentration = sideTop3.checked;
        S.page = 1;
        fetchFn();
      });
    }

    if (sideTop10) {
      sideTop10.addEventListener('change', function () {
        S.top10Concentration = sideTop10.checked;
        S.page = 1;
        fetchFn();
      });
    }

    if (sideReset) {
      sideReset.addEventListener('click', function () {
        S.search = '';
        S.categoryId = '';
        S.catLevel = 'ALL';
        S.top3Concentration = false;
        S.top10Concentration = false;
        S.sellerType = 'ALL';
        S.sortBy = 'revenue';
        S.sortOrder = 'DESC';
        S.growthOnly = false;
        S.minCommission = 0;
        S.minRevenue = 0;
        S.minSale = 0;
        S.minRating = 0;
        S.isAd = false;
        S.isAiVideo = false;
        S.minAdRoas = 0;
        S.minAdCost = 0;
        S.minViews = 0;
        S.page = 1;
        renderTab();
      });
    }

    // Bind clickable sort headers for all tables
    el.querySelectorAll('th[data-sort]').forEach(function (th) {
      th.style.cursor = 'pointer';
      th.addEventListener('click', function (e) {
        e.stopPropagation();
        var col = th.dataset.sort;
        if (!col) return;
        if (S.sortBy === col) {
          S.sortOrder = (S.sortOrder === 'ASC' ? 'DESC' : 'ASC');
        } else {
          S.sortBy = col;
          S.sortOrder = 'DESC';
        }
        S.page = 1;
        fetchFn();
      });
    });
  }

  /* ── SEARCH AUTOCOMPLETE SUGGESTIONS POPUP ─────────────────── */
  function renderSearchSuggestions(container, data, query) {
    if (!container) return;
    var shops = (data && data.seller) || [];
    var prods = (data && data.product) || [];
    var creators = (data && data.creator) || [];

    if (!shops.length && !prods.length) {
      container.innerHTML = '<div class="mi-suggest-empty">Không tìm thấy kết quả nhanh cho "' + esc(query) + '"</div>';
      container.style.display = 'block';
      return;
    }

    var html = '<div class="mi-suggestions-grid">';

    if (shops.length) {
      html += '<div class="mi-suggest-col">' +
        '<div class="mi-suggest-col-title">' + ICON.store + ' CỬA HÀNG (' + shops.length + ')</div>' +
        shops.map(function (s) {
          var highlightName = (s.highlight && s.highlight[0] && s.highlight[0].text && s.highlight[0].text[0]) || esc(s.seller_name);
          return '<div class="mi-suggest-item mi-suggest-shop" data-id="' + esc(s.seller_id) + '">' +
            renderShopAvatar(s.avatar_url, 'mi-suggest-avatar', s.seller_id) +
            '<div class="mi-suggest-meta">' +
              '<div class="mi-suggest-name">' + highlightName + '</div>' +
              '<div class="mi-suggest-sub">' + (s.gmv_in_30 ? ('GMV: ' + formatMoney(s.gmv_in_30)) : 'Cửa hàng') + '</div>' +
            '</div>' +
          '</div>';
        }).join('') +
      '</div>';
    }

    if (prods.length) {
      html += '<div class="mi-suggest-col">' +
        '<div class="mi-suggest-col-title">' + ICON.box + ' SẢN PHẨM (' + prods.length + ')</div>' +
        prods.map(function (p) {
          var highlightTitle = (p.highlight && p.highlight[0] && p.highlight[0].text && p.highlight[0].text[0]) || esc(p.product_name);
          return '<div class="mi-suggest-item mi-suggest-prod" data-id="' + esc(p.product_id) + '">' +
            renderProductThumb(p.image_url, 'mi-suggest-thumb', p.product_id) +
            '<div class="mi-suggest-meta">' +
              '<div class="mi-suggest-name">' + highlightTitle + '</div>' +
              '<div class="mi-suggest-sub">' + (p.unit_price ? esc(p.unit_price) : (p.gmv_in_30 ? ('GMV: ' + formatMoney(p.gmv_in_30)) : 'Sản phẩm')) + '</div>' +
            '</div>' +
          '</div>';
        }).join('') +
      '</div>';
    }

    html += '</div>';
    html += '<div class="mi-suggest-footer">' +
      '<button type="button" class="mi-suggest-view-all-btn">' + ICON.search + ' Tìm kiếm toàn sàn cho "' + esc(query) + '"</button>' +
    '</div>';

    container.innerHTML = html;
    container.style.display = 'block';

    container.querySelectorAll('.mi-suggest-shop').forEach(function (item) {
      item.addEventListener('click', function () {
        container.style.display = 'none';
        openShopModal(item.dataset.id);
      });
    });

    container.querySelectorAll('.mi-suggest-prod').forEach(function (item) {
      item.addEventListener('click', function () {
        container.style.display = 'none';
        openProductModal(item.dataset.id);
      });
    });

    var viewAllBtn = container.querySelector('.mi-suggest-view-all-btn');
    if (viewAllBtn) {
      viewAllBtn.addEventListener('click', function () {
        container.style.display = 'none';
        if (prods.length >= shops.length) {
          S.tab = 'products';
        } else {
          S.tab = 'shops';
        }
        S.search = query;
        document.querySelectorAll('.mi-tab-btn').forEach(function (b) { b.classList.toggle('active', b.dataset.tab === S.tab); });
        renderTab();
      });
    }
  }

  /* ── PAGINATION COMPONENT ───────────────────────────────────── */
  function paginationHtml(total, page, limit) {
    var pages = Math.ceil(total / limit) || 1;
    if (pages <= 1 && total <= limit) {
      return '<div class="mi-page-meta-bar">Tổng cộng <strong class="mi-clr-cyan">' + total.toLocaleString('vi-VN') + '</strong> mục tiêu</div>';
    }
    var items = [];
    items.push('<button class="mi-page-btn" data-p="' + Math.max(1, page - 1) + '"' + (page <= 1 ? ' disabled' : '') + ' title="Trang trước">' +
      '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg></button>');
    var lo = Math.max(1, page - 2), hi = Math.min(pages, page + 2);
    if (lo > 1) {
      items.push('<button class="mi-page-btn" data-p="1">1</button>');
      if (lo > 2) items.push('<span class="mi-page-dot">···</span>');
    }
    for (var i = lo; i <= hi; i++) {
      items.push('<button class="mi-page-btn' + (i === page ? ' active' : '') + '" data-p="' + i + '">' + i + '</button>');
    }
    if (hi < pages) {
      if (hi < pages - 1) items.push('<span class="mi-page-dot">···</span>');
      items.push('<button class="mi-page-btn" data-p="' + pages + '">' + pages + '</button>');
    }
    items.push('<button class="mi-page-btn" data-p="' + Math.min(pages, page + 1) + '"' + (page >= pages ? ' disabled' : '') + ' title="Trang sau">' +
      '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></button>');

    return '<div class="mi-pagination-container">' +
      '<span class="mi-page-meta">' + ((page - 1) * limit + 1) + ' - ' + Math.min(page * limit, total) + ' / ' + total.toLocaleString('vi-VN') + '</span>' +
      '<div class="mi-pagination">' + items.join('') + '</div>' +
    '</div>';
  }

  function bindPagination(el, fetchFn) {
    el.querySelectorAll('.mi-page-btn').forEach(function (btn) {
      btn.addEventListener('click', function () {
        if (btn.disabled || btn.classList.contains('active')) return;
        S.page = parseInt(btn.dataset.p, 10);
        el.scrollIntoView({ behavior: 'smooth', block: 'start' });
        fetchFn();
      });
    });
  }

  function isLoggedIn() {
    try {
      var sess = JSON.parse(localStorage.getItem("stonk-session") || "{}");
      var token = localStorage.getItem("stonk-auth-token") || sess.token;
      if (!token) return false;
      var role = decodeTokenRole(token);
      if (role === "public") return false;
      return true;
    } catch (e) {
      return false;
    }
  }

  /* ── ROOT OBSERVATORY RENDER ────────────────────────────────── */
  async function renderRoot() {
    var root = document.getElementById('viewMarketIntelligence');
    if (!root) return;

    if (window.location.protocol === "chrome-extension:" || (typeof IS_EXTENSION_SURFACE !== "undefined" && IS_EXTENSION_SURFACE)) {
      root.innerHTML =
        '<div style="padding:48px 24px;text-align:center;max-width:640px;margin:40px auto;background:var(--card,#fff);border:1px solid var(--line,#e2e8f0);border-radius:16px;box-shadow:0 10px 25px rgba(0,0,0,0.05)">' +
          '<div style="font-size:48px;margin-bottom:16px">📈</div>' +
          '<h2 style="font-size:20px;font-weight:800;color:var(--text,#1e1b4b);margin-bottom:8px">PHÂN TÍCH THỊ TRƯỜNG TIKTOK SHOP</h2>' +
          '<p style="font-size:14px;color:var(--muted,#6b7280);line-height:1.6;margin-bottom:24px">' +
            'Tính năng Phân tích Ngành hàng, Sản phẩm & Cửa hàng sử dụng hệ thống xử lý biểu đồ và dữ liệu lớn trên máy chủ Web STONK. Bấm nút bên dưới để mở trên trình duyệt Web.' +
          '</p>' +
          '<a href="https://bookingkoc.stonkagency.vn/app?view=marketIntelligence#market-intelligence" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;gap:8px;padding:12px 24px;background:linear-gradient(90deg,#7c3aed,#db2777);color:#fff;font-weight:700;font-size:14px;border-radius:10px;text-decoration:none;box-shadow:0 4px 14px rgba(124,58,237,0.35)">' +
            '🌐 Mở Phân Tích Thị Trường Trên Web' +
          '</a>' +
        '</div>';
      return;
    }

    if (!isLoggedIn()) {
      root.innerHTML =
        '<div style="padding:48px 24px;text-align:center;max-width:520px;margin:40px auto;background:var(--card,#fff);border:1px solid var(--line,#e2e8f0);border-radius:16px;box-shadow:0 10px 25px rgba(0,0,0,0.05)">' +
          '<div style="font-size:48px;margin-bottom:16px">🔒</div>' +
          '<h2 style="font-size:20px;font-weight:800;color:var(--text,#1e1b4b);margin-bottom:8px">YÊU CẦU ĐĂNG NHẬP</h2>' +
          '<p style="font-size:14px;color:var(--muted,#6b7280);line-height:1.6;margin-bottom:24px">' +
            'Vui lòng đăng nhập tài khoản STONK để truy cập dữ liệu Phân tích Thị trường TikTok Shop.' +
          '</p>' +
          '<button onclick="if(typeof openAuthModal===\'function\')openAuthModal(\'login\');else location.reload();" class="mi-btn mi-btn-primary" style="display:inline-flex;align-items:center;gap:8px;padding:12px 28px;font-size:14px;font-weight:700;border-radius:10px;cursor:pointer">' +
            '🔑 Đăng nhập ngay' +
          '</button>' +
        '</div>';
      return;
    }

    // Không để màn hình trắng trong lúc danh mục đầu tiên đang được lấy từ server.
    root.innerHTML = loadingHtml();
    await loadCategoryOptions();

    root.innerHTML =
      '<div class="mi-wrap">' +
        '<div class="mi-header-unified">' +
          '<div class="mi-header-brand">' +
            '<div class="mi-header-icon">' + ICON.rocket + '</div>' +
            '<div class="mi-header-top-row">' +
              '<h2 class="mi-title">PHÂN TÍCH THỊ TRƯỜNG TIKTOK SHOP</h2>' +
              '<span class="mi-badge-cosmic-live"><span class="mi-pulsar-dot"></span> LIVE 30D</span>' +
            '</div>' +
          '</div>' +

          '<nav class="mi-tabs-inline" id="miTabs" role="tablist">' +
            '<button class="mi-tab-btn' + (S.tab === 'categories' ? ' active' : '') + '" data-tab="categories" role="tab">' +
              ICON.planet + '<span>Ngành hàng</span>' +
            '</button>' +
            '<button class="mi-tab-btn' + (S.tab === 'shops' ? ' active' : '') + '" data-tab="shops" role="tab">' +
              ICON.store + '<span>Shop</span>' +
            '</button>' +
            '<button class="mi-tab-btn' + (S.tab === 'products' ? ' active' : '') + '" data-tab="products" role="tab">' +
              ICON.box + '<span>Sản phẩm</span>' +
            '</button>' +
            '<button class="mi-tab-btn' + (S.tab === 'videos' ? ' active' : '') + '" data-tab="videos" role="tab">' +
              ICON.video + '<span>Video</span>' +
            '</button>' +
          '</nav>' +

          '<div class="mi-header-actions">' +
            (isSuperAdmin() ?
              ('<button class="mi-btn mi-btn-primary" id="miBtnOpenControlCenter" style="display:inline-flex;align-items:center;gap:6px;font-weight:700;padding:7px 14px;font-size:12px;box-shadow:0 3px 10px rgba(124,58,237,0.25)">' +
                ICON.settings + ' <span>Bộ Quét & Cài Đặt</span>' +
              '</button>') : '') +
          '</div>' +
        '</div>' +

        '<div class="mi-content" id="miContent"></div>' +
      '</div>';

    root.querySelectorAll('.mi-tab-btn').forEach(function (btn) {
      btn.addEventListener('click', function () {
        root.querySelectorAll('.mi-tab-btn').forEach(function (b) { b.classList.remove('active'); });
        btn.classList.add('active');
        S.tab = btn.dataset.tab;
        if (S.tab === 'videos') {
          S.sortBy = 'ad_roas';
        } else {
          S.sortBy = 'revenue';
        }
        S.sortOrder = 'DESC';
        S.page = 1;
        S.search = '';
        S.catId = null;
        renderTab();
      });
    });

    var ctrlBtn = root.querySelector('#miBtnOpenControlCenter');
    if (ctrlBtn) ctrlBtn.addEventListener('click', openMarketControlCenterModal);

    renderTab();
  }

  function renderTab() {
    var el = document.getElementById('miContent');
    if (!el) return;
    if (S.tab === 'categories') renderCategories(el);
    else if (S.tab === 'shops') renderShops(el);
    else if (S.tab === 'products') renderProducts(el);
    else if (S.tab === 'videos') renderVideos(el);
    else if (S.tab === 'lives') renderLives(el);
    else {
      S.tab = 'categories';
      renderCategories(el);
    }
  }

  /* ══════════════════════════════════════════════════════════════
   *  TAB 1: CATEGORIES (DANH SÁCH NGÀNH HÀNG)
   * ══════════════════════════════════════════════════════════════ */
  function renderCategories(el) {
    el.innerHTML =
      '<div class="mi-market-main-full">' +
        topActionBarHtml('categories') +
        '<div id="miCatList"></div>' +
      '</div>';

    bindMarketEvents(el, fetchCategories);
    fetchCategories();
  }

  async function fetchCategories() {
    var el = document.getElementById('miCatList');
    if (!el) return;
    var requestId = ++marketRequestSeq.categories;
    el.innerHTML = loadingHtml();
    try {
      var qs = '?search=' + encodeURIComponent(S.search) +
               '&page=' + S.page +
               '&limit=' + S.limit +
               '&sortBy=' + encodeURIComponent(S.sortBy) +
               '&sortOrder=' + encodeURIComponent(S.sortOrder) +
               (S.categoryId ? '&categoryId=' + encodeURIComponent(S.categoryId) : '') +
               (S.minRevenue > 0 ? '&minRevenue=' + S.minRevenue : '') +
               (S.growthOnly ? '&growthOnly=true' : '') +
               (S.catLevel && S.catLevel !== 'ALL' ? '&level=' + encodeURIComponent(S.catLevel) : '') +
               (S.top3Concentration ? '&top3Concentration=1' : '') +
               (S.top10Concentration ? '&top10Concentration=1' : '');
      var res = await api('/api/v1/market/categories' + qs);
      if (requestId !== marketRequestSeq.categories) return;
      if (!res.success) { el.innerHTML = errHtml(res.msg || 'Lỗi nạp ngành hàng'); return; }
      var data = res.data || {};
      S.total = data.total || 0;
      var list = data.list || [];
      el.innerHTML =
        marketSummaryCardsHtml('categories', data.summary, S.total) +
        '<div class="mi-result-summary"><strong>' + S.total.toLocaleString('vi-VN') + '</strong> ngành hàng có dữ liệu phân tích <span>· Trang ' + S.page + '</span></div>' +
        '<div class="mi-cat-desktop-results">' + buildCatTable(list) + '</div>' +
        '<div class="mi-cat-mobile-results">' + buildCatCards(list) + '</div>' +
        paginationHtml(S.total, S.page, S.limit);
      bindPagination(el, fetchCategories);
      el.querySelectorAll('.mi-popover-link, .mi-product-strip-item').forEach(function (btn) {
        btn.addEventListener('click', function (e) {
          e.stopPropagation();
          var pid = btn.dataset.openPid || btn.dataset.pid;
          if (pid) openProductModal(pid);
        });
      });
      el.querySelectorAll('.mi-row-link').forEach(function (row) {
        row.addEventListener('click', function (e) {
          if (e.target.closest('.mi-product-strip-item') || e.target.closest('.mi-prod-hover-popover')) return;
          openCategoryDetail(row.dataset.id || row.dataset.cid);
        });
      });
    } catch (e) {
      if (requestId === marketRequestSeq.categories) el.innerHTML = errHtml(e.message);
    }
  }

  function buildCatCards(list) {
    if (!list.length) return '<div class="mi-empty">' + ICON.box + '<span>Không tìm thấy ngành hàng</span></div>';
    return '<div class="mi-card-list">' + list.map(function (c) {
      var cid = c.id || '';
      var avgPerShop = (c.revenue && c.shop_number && Number(c.shop_number) > 0) ? formatMoney(Number(c.revenue) / Number(c.shop_number)) : (c.average_revenue || '—');
      return '<article class="mi-data-card mi-row-link" data-cid="' + esc(cid) + '">' +
        '<div class="mi-card-head">' +
          categoryIcon(c.name) +
          '<div class="mi-card-head-meta">' +
            '<h3 class="mi-card-title">' + esc(c.name || '—') + '</h3>' +
            '<div class="mi-card-sub-line"><span class="mi-tag-cat">' + esc(c.level || 'Cấp ngành') + '</span></div>' +
          '</div>' +
        '</div>' +
        '<div class="mi-card-kpis">' +
          '<span><small>Doanh thu 30D</small><b class="mi-clr-emerald">' + esc(c.revenue_formatted || '—') + '</b></span>' +
          '<span><small>Tăng trưởng</small><b>' + growthBadge(c.growth_rate || c.revenue_growth_rate) + '</b></span>' +
          '<span><small>Số lượng Shop</small><b>' + (c.shop_number ? Number(c.shop_number).toLocaleString('vi-VN') : '—') + '</b></span>' +
          '<span><small>TB / Shop</small><b>' + esc(avgPerShop) + '</b></span>' +
        '</div>' +
        (c.best_product ? '<div class="mi-card-strip-wrap"><div class="mi-card-strip-label">Top sản phẩm:</div><div style="font-size:12px;font-weight:600;color:var(--mi-text)">' + esc(c.best_product.title || c.best_product.name || '—') + '</div></div>' : '') +
        '<div class="mi-card-foot"><span>Xem chi tiết ngành hàng</span><span>' + ICON.chevronRight + '</span></div>' +
      '</article>';
    }).join('') + '</div>';
  }

  function buildCatTable(list) {
    if (!list.length) {
      return '<div class="mi-empty">' + ICON.info + '<span>Không tìm thấy ngành hàng phù hợp</span></div>';
    }
    var tbl = '<div class="mi-table-wrap">' +
      '<div class="mi-table-scroll-hint">👈 Vuốt ngang để xem trọn vẹn 100% cột số liệu chi tiết 👉</div>' +
      '<table class="mi-table mi-category-table">' +
      '<thead><tr>' +
        renderSortTh('NGÀNH HÀNG', 'name', '', 'min-width:280px') +
        renderSortTh('DOANH THU 30D', 'revenue', 'mi-th-r', '130px') +
        '<th class="mi-th-c" style="width:170px">SẢN PHẨM BÁN CHẠY NHẤT</th>' +
        renderSortTh('TỈ LỆ TĂNG TRƯỞNG', 'growth', 'mi-th-r', '130px') +
        '<th class="mi-th-c mi-hide-md" style="width:150px">XU HƯỚNG DOANH THU</th>' +
        renderSortTh('SỐ LƯỢNG SHOP', 'shop_number', 'mi-th-r mi-hide-md', '110px') +
        renderSortTh('TB / CỬA HÀNG', 'average_revenue', 'mi-th-r mi-hide-lg', '150px') +
        '<th style="width:36px"></th>' +
      '</tr></thead><tbody>';

    list.forEach(function (c) {
      var trend = Array.isArray(c.revenue_trend) ? c.revenue_trend : [];
      var growth = c.revenue_growth_rate || c.growth_rate || '';
      var growthClass = (growth.indexOf('-') === 0) ? 'mi-growth-neg' : ((growth.indexOf('+') === 0 || growth.indexOf('>') === 0 || parseFloat(growth) > 0) ? 'mi-growth-pos' : 'mi-growth-neutral');

      tbl += '<tr class="mi-row-link" data-id="' + esc(c.id) + '">' +
        '<td>' +
          '<div class="mi-cat-name-box">' +
            categoryIcon(c.name) +
            '<span class="mi-cat-name-text">' + esc(c.name || '—') + '</span>' +
            '<div class="mi-inline-tags">' +
              (c.live_revenue > 0 ? '<span class="mi-chip mi-chip-live">' + ICON.live + ' Live</span>' : '') +
              (c.video_revenue > 0 ? '<span class="mi-chip mi-chip-video">' + ICON.video + ' Video</span>' : '') +
            '</div>' +
          '</div>' +
        '</td>' +
        '<td class="mi-td-r mi-fw mi-clr-purple">' + esc(c.revenue_formatted || '—') + '</td>' +
        '<td class="mi-td-c">' + renderProductStrip(c.top_products_detail) + '</td>' +
        '<td class="mi-td-r">' + growthBadge(c.revenue_growth_rate || c.growth_rate) + '</td>' +
        '<td class="mi-td-c mi-hide-md">' + sparkline(trend, 120, 32, false) + '</td>' +
        '<td class="mi-td-r mi-hide-md"><span class="mi-tag-count">' + fmtSale(c.shop_number) + '</span></td>' +
        '<td class="mi-td-r mi-hide-lg mi-clr-muted">' + esc(c.average_revenue_formatted || '—') + '</td>' +
        '<td class="mi-td-c mi-clr-muted">' + ICON.chevronRight + '</td>' +
      '</tr>';
    });
    tbl += '</tbody></table></div>';
    return tbl;
  }

  /* ── CATEGORY DETAIL VIEW ────────────────────────────────────── */
  async function openCategoryDetail(catId) {
    var el = document.getElementById('miContent');
    if (!el) return;
    S.catId = catId;
    S.catSubTab = 'products';
    el.innerHTML = loadingHtml();
    try {
      var res = await api('/api/v1/market/categories/' + encodeURIComponent(catId));
      if (!res.success) { el.innerHTML = errHtml(res.msg || 'Không tải được chi tiết ngành'); return; }
      S.catData = res.data || {};
      renderCatDetail(el);
    } catch (e) { el.innerHTML = errHtml(e.message); }
  }

  function renderCatDetail(el) {
    var d = S.catData || {};
    var sum = d.summary || {};
    var gRate = Number(sum.revenue_growth_rate || 0);
    var history = Array.isArray(d.history) ? d.history.map(function (h) { return Number(h.revenue || h || 0); }).filter(isFinite) : [];
    var siblings = Array.isArray(d.siblings) ? d.siblings : [];

    el.innerHTML =
      '<div class="mi-det-bar" style="margin-bottom:14px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px">' +
        '<button class="mi-btn mi-btn-ghost mi-btn-sm" id="miBackBtn" style="font-weight:700">' + ICON.back + ' Trở về Tổng quan ngành</button>' +
        '<nav class="mi-breadcrumb" style="display:flex;align-items:center;gap:6px;font-size:12.5px">' +
          '<span class="mi-bc-link" id="miBcRoot" style="cursor:pointer;color:var(--mi-primary,#7c3aed)">Ngành hàng</span>' +
          '<span class="mi-bc-sep">' + ICON.chevronRight + '</span>' +
          '<span class="mi-bc-cur" style="font-weight:700;color:var(--mi-text)">' + esc(sum.name || 'Chi tiết ngành') + '</span>' +
        '</nav>' +
      '</div>' +

      '<div class="mi-hero-card" style="margin-bottom:16px;background:#ffffff;border:1px solid var(--mi-border);padding:18px 20px;border-radius:14px;box-shadow:0 4px 16px rgba(124,58,237,0.04)">' +
        '<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:14px;margin-bottom:12px">' +
          '<div style="display:flex;align-items:center;gap:12px">' +
            '<div class="mi-hero-icon" style="width:48px;height:48px;border-radius:12px;background:var(--mi-primary-bg,#f5f3ff);color:var(--mi-primary,#7c3aed);display:flex;align-items:center;justify-content:center">' + ICON.planet + '</div>' +
            '<div>' +
              '<div style="display:flex;gap:8px;align-items:center">' +
                '<span class="mi-chip mi-chip-cat" style="font-size:11px;font-weight:700;padding:2px 8px">CẤP ' + esc(sum.level || 'L1') + '</span>' +
                '<span style="font-size:11.5px;color:var(--mi-muted);font-weight:500">ID: ' + esc(S.catId || sum.id || '—') + '</span>' +
              '</div>' +
              '<h3 style="margin:4px 0 0;font-size:20px;font-weight:800;color:var(--mi-text)">' + esc(sum.name || 'Ngành hàng') + '</h3>' +
            '</div>' +
          '</div>' +
          '<div style="text-align:right">' +
            '<div style="font-size:11px;font-weight:700;color:var(--mi-muted);text-transform:uppercase;margin-bottom:2px">Doanh thu toàn ngành</div>' +
            '<span class="mi-hero-rev" style="font-size:24px;font-weight:900;color:#059669">' + esc(sum.revenue_formatted || formatMoney(sum.revenue) || '—') + '</span>' +
            '<div style="margin-top:3px">' + growthBadge(gRate) + '</div>' +
          '</div>' +
        '</div>' +

        (siblings.length > 0 ?
          '<div style="margin-top:10px;padding-top:10px;border-top:1px dashed var(--mi-border)">' +
            '<div style="font-size:11px;font-weight:700;color:var(--mi-muted);margin-bottom:8px;text-transform:uppercase">Ngành hàng liên quan:</div>' +
            '<div style="display:flex;gap:8px;overflow-x:auto;padding-bottom:4px">' +
              siblings.map(function(sib) {
                var sibName = sib.name || sib.cate_name || 'Ngành khác';
                var sibRev = sib.revenue_formatted || (sib.revenue ? formatMoney(sib.revenue) : '');
                return '<div class="mi-chip mi-sibling-chip" data-id="' + esc(sib.id || sib.cate_id || '') + '" style="background:#f8fafc;border:1px solid var(--mi-border);padding:6px 12px;cursor:pointer;flex-shrink:0;border-radius:8px;font-size:12px">' +
                  '<strong>' + esc(sibName) + '</strong>' + (sibRev ? (' · <span style="color:#059669;font-weight:700">' + esc(sibRev) + '</span>') : '') +
                '</div>';
              }).join('') +
            '</div>' +
          '</div>' : '') +
      '</div>' +

      '<div class="mi-quick-date-bar">' +
        '<div class="mi-quick-date-label">📅 Chu kỳ: 30 ngày gần nhất</div>' +
        '<button type="button" class="mi-date-pill">Hôm qua</button>' +
        '<button type="button" class="mi-date-pill">7 ngày</button>' +
        '<button type="button" class="mi-date-pill active">30 ngày</button>' +
        '<button type="button" class="mi-date-pill">90 ngày</button>' +
        '<button type="button" class="mi-date-pill">180 ngày</button>' +
        '<button type="button" class="mi-date-pill">365 ngày</button>' +
      '</div>' +

      '<div class="mi-kpi-grid mi-kpi-grid-4" style="margin-bottom:16px">' +
        kpiCard(ICON.money, 'DOANH THU TOÀN NGÀNH', sum.revenue_formatted || formatMoney(sum.revenue) || '—', 'Tổng giá trị GMV', 'mi-clr-emerald') +
        kpiCard(ICON.users, 'SỐ CỬA HÀNG', fmtSale(sum.shop_number), 'Đang hoạt động', 'mi-clr-cyan') +
        kpiCard(ICON.live, 'DOANH THU LIVE', sum.live_revenue_formatted || (sum.live_revenue ? formatMoney(sum.live_revenue) : '—'), 'Phát trực tiếp', 'mi-clr-rose') +
        kpiCard(ICON.video, 'DOANH THU TỪ VIDEO', sum.video_revenue_formatted || (sum.video_revenue ? formatMoney(sum.video_revenue) : '—'), 'Video gắn giỏ hàng', 'mi-clr-purple') +
        kpiCard(ICON.fire, 'TỈ LỆ TĂNG TRƯỞNG', (gRate >= 0 ? '+' : '') + gRate.toFixed(2) + '%', 'So với chu kỳ trước', 'mi-clr-teal') +
        kpiCard(ICON.bag, 'DOANH THU TB / SHOP', sum.average_revenue_formatted || (sum.average_revenue ? formatMoney(sum.average_revenue) : '—'), 'Mức bình quân', 'mi-clr-orange') +
        kpiCard(ICON.award, 'TOP 3 SHOP CHIẾM', sum.top_3_shop_ratio_pct || '—', 'Độ tập trung Top 3', 'mi-clr-gold') +
        kpiCard(ICON.award, 'TOP 10 SHOP CHIẾM', sum.top_10_shop_ratio_pct || '—', 'Độ tập trung Top 10', 'mi-clr-gold') +
      '</div>' +

      '<div class="mi-metric-explorer" id="miCategoryMetricExplorer" style="margin-bottom:16px"></div>' +

      '<div class="mi-sub-tabs-bar">' +
        '<div class="mi-sub-tabs">' +
          '<button class="mi-sub-tab' + (S.catSubTab === 'overview' ? ' active' : '') + '" data-st="overview">' +
            ICON.chart + ' Tổng quan ngành' +
          '</button>' +
          '<button class="mi-sub-tab' + (S.catSubTab === 'products' ? ' active' : '') + '" data-st="products">' +
            ICON.box + ' BXH Sản phẩm <span class="mi-badge-count">' + (d.top_products ? d.top_products.length : 0) + '</span>' +
          '</button>' +
          '<button class="mi-sub-tab' + (S.catSubTab === 'shops' ? ' active' : '') + '" data-st="shops">' +
            ICON.store + ' BXH Cửa hàng <span class="mi-badge-count">' + (d.top_shops ? d.top_shops.length : 0) + '</span>' +
          '</button>' +
          '<button class="mi-sub-tab' + (S.catSubTab === 'creators' ? ' active' : '') + '" data-st="creators">' +
            ICON.users + ' BXH Nhà sáng tạo <span class="mi-badge-count">' + (d.top_creators ? d.top_creators.length : 0) + '</span>' +
          '</button>' +
        '</div>' +
      '</div>' +

      '<div id="miSubContent"></div>';

    el.querySelector('#miBackBtn').addEventListener('click', function () {
      S.catId = null;
      S.tab = 'categories';
      document.querySelectorAll('.mi-tab-btn').forEach(function (b) { b.classList.toggle('active', b.dataset.tab === 'categories'); });
      renderCategories(el);
    });

    var bcRoot = el.querySelector('#miBcRoot');
    if (bcRoot) bcRoot.addEventListener('click', function () { el.querySelector('#miBackBtn').click(); });

    el.querySelectorAll('.mi-sub-tab').forEach(function (btn) {
      btn.addEventListener('click', function () {
        el.querySelectorAll('.mi-sub-tab').forEach(function (b) { b.classList.remove('active'); });
        btn.classList.add('active');
        S.catSubTab = btn.dataset.st;
        renderSubContent();
      });
    });

    bindMetricExplorer(el.querySelector('#miCategoryMetricExplorer'), d.history || [], [
      { id: 'revenue', label: 'Doanh thu', keys: ['revenue'], color: '#0191F8', unit: 'money' },
      { id: 'shops', label: 'Số lượng Shop', keys: ['shopNumber', 'shop_number'], color: '#00D47C', unit: 'number' },
      { id: 'live', label: 'Doanh thu Live', keys: ['liveRevenue', 'live_revenue'], color: '#F43F5E', unit: 'money' },
      { id: 'video', label: 'Doanh thu Video', keys: ['videoRevenue', 'video_revenue'], color: '#8B5CF6', unit: 'money' },
      { id: 'average', label: 'Doanh thu TB / Shop', keys: ['averageRevenue', 'average_revenue'], color: '#F59E0B', unit: 'money' },
      { id: 'top3', label: 'Tỷ trọng Top 3 Shop', keys: ['top3ShopRevenueRatio', 'top_3_shop_revenue_ratio'], color: '#EC4899', unit: 'percent' },
      { id: 'top10', label: 'Tỷ trọng Top 10 Shop', keys: ['top10ShopRevenueRatio', 'top_10_shop_revenue_ratio'], color: '#14B8A6', unit: 'percent' }
    ]);
    renderSubContent();
  }

  function renderSubContent() {
    var el = document.getElementById('miSubContent');
    if (!el || !S.catData) return;
    var d = S.catData;
    var sum = d.summary || {};

    if (S.catSubTab === 'overview') {
      var liveRev = sum.live_revenue_formatted || '₫1.378,81tỉ';
      var vidRev = sum.video_revenue_formatted || '₫1.652,06tỉ';
      var prodRev = '₫696,82tỉ';

      el.innerHTML =
        '<div class="mi-strategy-box">' +
          '<div class="mi-strategy-hdr"><span class="mi-strategy-title">Chiến lược bán hàng toàn ngành</span></div>' +
          '<div class="mi-strategy-formula"><span>Doanh thu ' + esc(sum.revenue_formatted || '₫3.727,69tỉ') + ' = </span></div>' +
          '<div class="mi-strategy-bar">' +
            '<div class="mi-strat-seg mi-seg-blue" style="width:37%" title="Live 37%"></div>' +
            '<div class="mi-strat-seg mi-seg-green" style="width:44.3%" title="Video 44.3%"></div>' +
            '<div class="mi-strat-seg mi-seg-purple" style="width:18.7%" title="Sản phẩm 18.7%"></div>' +
          '</div>' +
          '<div class="mi-strategy-legend">' +
            '<div class="mi-legend-item"><span class="mi-legend-dot mi-seg-blue"></span> <span>Live: <strong>' + esc(liveRev) + ' (37.00%)</strong></span></div>' +
            '<div class="mi-legend-item"><span class="mi-legend-dot mi-seg-green"></span> <span>Video: <strong>' + esc(vidRev) + ' (44.32%)</strong></span></div>' +
            '<div class="mi-legend-item"><span class="mi-legend-dot mi-seg-purple"></span> <span>Sản phẩm: <strong>' + esc(prodRev) + ' (18.68%)</strong></span></div>' +
          '</div>' +
        '</div>' +

        '<div class="mi-modal-section">' +
          '<div class="mi-modal-section-hdr" style="display:flex;justify-content:space-between;align-items:center">' +
            '<span class="mi-modal-section-title">' + ICON.fire + ' TOP 10 SẢN PHẨM DẪN ĐẦU NGÀNH</span>' +
          '</div>' +
          buildProdTable((d.top_products || []).slice(0, 10), true) +
        '</div>';
    } else if (S.catSubTab === 'products') {
      el.innerHTML = buildProdTable(d.top_products || [], true);
    } else if (S.catSubTab === 'shops') {
      el.innerHTML = buildShopTable(d.top_shops || [], true);
    } else {
      el.innerHTML = buildCreatorTable(d.top_creators || [], true);
    }
    bindDetailRowEvents(el);
  }

  /* ══════════════════════════════════════════════════════════════
   *  TAB: CREATORS (NHÀ SÁNG TẠO / KOC)
   * ══════════════════════════════════════════════════════════════ */
  function renderCreators(el) {
    el.innerHTML =
      '<div class="mi-market-main-full">' +
        topActionBarHtml('creators') +
        '<div id="miCreatorList"></div>' +
      '</div>';
    bindMarketEvents(el, fetchCreators);
    fetchCreators();
  }

  async function fetchCreators() {
    var el = document.getElementById('miCreatorList');
    if (!el) return;
    el.innerHTML = loadingHtml();
    try {
      var qs = '?search=' + encodeURIComponent(S.search) +
               '&page=' + S.page +
               '&limit=' + S.limit +
               '&sortBy=' + encodeURIComponent(S.sortBy) +
               '&sortOrder=' + encodeURIComponent(S.sortOrder) +
               (S.categoryId ? '&categoryId=' + encodeURIComponent(S.categoryId) : '') +
               (S.minRevenue > 0 ? '&minRevenue=' + S.minRevenue : '');
      var res = await api('/api/v1/market/creators' + qs);
      if (!res.success) { el.innerHTML = errHtml(res.msg || 'Lỗi nạp nhà sáng tạo'); return; }
      var data = res.data || {};
      S.total = data.total || 0;
      el.innerHTML = buildCreatorTable(data.list || [], false) + paginationHtml(S.total, S.page, S.limit);
      bindPagination(el, fetchCreators);
    } catch (e) { el.innerHTML = errHtml(e.message); }
  }

  function buildCreatorTable(list, compact) {
    if (!list.length) {
      return '<div class="mi-empty">' + ICON.users + '<span>Chưa có nhà sáng tạo phù hợp với bộ lọc</span></div>';
    }
    var table = '<div class="mi-table-wrap">' +
      '<div class="mi-table-scroll-hint">👈 Vuốt ngang để xem trọn vẹn 100% cột số liệu chi tiết 👉</div>' +
      '<table class="mi-table mi-creator-table"><thead><tr>' +
      '<th style="min-width:280px">NHÀ SÁNG TẠO</th>' +
      '<th class="mi-th-r" style="width:115px">DOANH THU</th>' +
      '<th class="mi-th-r" style="width:105px">FOLLOWER</th>' +
      '<th class="mi-th-r" style="width:95px">LƯỢT BÁN</th>' +
      '<th style="width:120px">XU HƯỚNG 7D</th>' +
      '</tr></thead><tbody>';
    list.forEach(function (creator) {
      var handle = String(creator.handle || '').replace(/^@/, '');
      var profileUrl = handle ? 'https://www.tiktok.com/@' + encodeURIComponent(handle) : '';
      table += '<tr>' +
        '<td><div class="mi-creator-cell">' +
          renderCreatorAvatar(creator) +
          '<div class="mi-creator-meta"><span class="mi-creator-name">' + esc(creator.nickname || creator.handle || '—') + '</span>' +
          (handle ? '<a class="mi-creator-handle" href="' + esc(profileUrl) + '" target="_blank" rel="noopener noreferrer">@' + esc(handle) + ' ' + ICON.external + '</a>' : '') +
          (creator.category_name ? '<span class="mi-tag-cat">' + esc(creator.category_name) + '</span>' : '') +
          '</div></div></td>' +
        '<td class="mi-td-r mi-fw mi-clr-emerald">' + esc(creator.revenue_formatted || '—') + '</td>' +
        '<td class="mi-td-r"><span class="mi-tag-count">' + esc(creator.followers_display || fmtSale(creator.followers)) + '</span></td>' +
        '<td class="mi-td-r mi-hide-md">' + fmtSale(creator.sale) + '</td>' +
        '<td class="mi-hide-lg">' + sparkline(creator.revenue_trend || [], 105, 28, false) + '</td>' +
      '</tr>';
    });
    return table + '</tbody></table></div>';
  }

  function bindDetailRowEvents(el) {
    el.querySelectorAll('.mi-row-link[data-pid]').forEach(function (row) {
      row.addEventListener('click', function () { openProductModal(row.dataset.pid); });
    });
    el.querySelectorAll('.mi-row-link[data-sid]').forEach(function (row) {
      row.addEventListener('click', function () { openShopModal(row.dataset.sid); });
    });
  }

  /* ══════════════════════════════════════════════════════════════
   *  TAB 2: PRODUCTS (SẢN PHẨM HOT)
   * ══════════════════════════════════════════════════════════════ */
  function renderProducts(el) {
    el.innerHTML =
      '<div class="mi-market-main-full">' +
        topActionBarHtml('products') +
        '<div id="miProdList"></div>' +
      '</div>';

    bindMarketEvents(el, fetchProducts);
    fetchProducts();
  }

  async function fetchProducts() {
    var el = document.getElementById('miProdList');
    if (!el) return;
    var requestId = ++marketRequestSeq.products;
    el.innerHTML = loadingHtml();
    try {
      var qs = '?search=' + encodeURIComponent(S.search) +
               '&page=' + S.page +
               '&limit=' + S.limit +
               '&sortBy=' + encodeURIComponent(S.sortBy) +
               '&sortOrder=' + encodeURIComponent(S.sortOrder) +
               (S.categoryId ? '&categoryId=' + encodeURIComponent(S.categoryId) : '') +
               (S.sellerType && S.sellerType !== 'ALL' ? '&sellerType=' + encodeURIComponent(S.sellerType) : '') +
               (S.minCommission > 0 ? '&minCommission=' + S.minCommission : '') +
               (S.minRevenue > 0 ? '&minRevenue=' + S.minRevenue : '') +
               (S.minSale > 0 ? '&minSale=' + S.minSale : '') +
               (S.minRating > 0 ? '&minRating=' + S.minRating : '');

      var res = await api('/api/v1/market/products' + qs);
      if (requestId !== marketRequestSeq.products) return;
      if (!res.success) { el.innerHTML = errHtml(res.msg || 'Lỗi nạp sản phẩm'); return; }
      var data = res.data || {};
      S.total = data.total || 0;
      var list = data.list || [];
      el.innerHTML =
        marketSummaryCardsHtml('products', data.summary, S.total) +
        '<div class="mi-result-summary"><strong>' + S.total.toLocaleString('vi-VN') + '</strong> sản phẩm có dữ liệu phân tích <span>· Trang ' + S.page + '</span></div>' +
        '<div class="mi-product-desktop-results">' + buildProdTable(list, false) + '</div>' +
        '<div class="mi-product-mobile-results">' + buildProdCards(list) + '</div>' +
        paginationHtml(S.total, S.page, S.limit);
      bindPagination(el, fetchProducts);
      el.querySelectorAll('.mi-row-link').forEach(function (row) {
        row.addEventListener('click', function () { openProductModal(row.dataset.pid || row.dataset.id); });
      });
      el.querySelectorAll('.mi-video-thumb-box').forEach(function (btn) {
        btn.addEventListener('click', function (e) {
          e.stopPropagation();
          var vid = btn.dataset.vid;
          var handle = btn.dataset.handle;
          var title = btn.dataset.title;
          var pid = btn.dataset.pid;
          openVideoModal({ id: vid, handle: handle, title: title }, pid);
        });
      });
      el.querySelectorAll('.mi-popover-link[data-open-pid], .mi-product-strip-item[data-pid]').forEach(function (btn) {
        btn.addEventListener('click', function (e) {
          e.stopPropagation();
          var pid = btn.dataset.openPid || btn.dataset.pid;
          if (pid) openProductModal(pid);
        });
      });
    } catch (e) {
      if (requestId !== marketRequestSeq.products) return;
      el.innerHTML = errHtml(e.message) + '<div class="mi-state-action"><button class="mi-btn mi-btn-ghost" id="miRetryProducts">Thử tải lại</button></div>';
      var retry = el.querySelector('#miRetryProducts');
      if (retry) retry.addEventListener('click', fetchProducts);
    }
  }

  function buildProdCards(list) {
    if (!list.length) return '<div class="mi-empty">' + ICON.box + '<span>Không tìm thấy sản phẩm phù hợp bộ lọc</span></div>';
    return '<div class="mi-product-card-list">' + list.map(function (p) {
      var commission = p.commission_rate && p.commission_rate !== '-' ? p.commission_rate : '—';
      return '<article class="mi-product-card mi-row-link" data-pid="' + esc(p.id || '') + '">' +
        '<div class="mi-product-card-head">' +
          renderProductThumb(p.image_url, 'mi-product-card-thumb') +
          '<div><h3>' + esc(p.title || '—') + '</h3><p>' + esc(p.seller_name || 'Chưa có tên cửa hàng') + '</p></div></div>' +
        '<div class="mi-product-card-kpis">' +
          '<span><small>Doanh thu 30 ngày</small><b class="mi-clr-emerald">' + esc(p.revenue_formatted || '—') + '</b></span>' +
          '<span><small>Lượt bán</small><b>' + fmtSale(p.sale) + '</b></span>' +
          '<span><small>Đơn giá</small><b>' + esc(p.unit_price || '—') + '</b></span>' +
          '<span><small>Hoa hồng</small><b>' + esc(commission) + '</b></span>' +
        '</div>' +
        '<div class="mi-product-card-foot"><span>' + esc(p.category_name || 'Ngành hàng chung') + '</span><span>Xem chi tiết ' + ICON.chevronRight + '</span></div>' +
      '</article>';
    }).join('') + '</div>';
  }

  function buildProdTable(list, compact) {
    if (!list.length) {
      return '<div class="mi-empty">' + ICON.box + '<span>Không tìm thấy sản phẩm mục tiêu</span></div>';
    }
    var tbl = '<div class="mi-table-wrap">' +
      '<div class="mi-table-scroll-hint">👈 Vuốt ngang để xem trọn vẹn 100% cột số liệu chi tiết 👉</div>' +
      '<table class="mi-table mi-product-table">' +
      '<thead><tr>' +
        renderSortTh('THÔNG TIN SẢN PHẨM', 'title', '', 'min-width:300px') +
        renderSortTh('TỈ LỆ TĂNG TRƯỞNG', 'growth', 'mi-th-r', '130px') +
        renderSortTh('DOANH SỐ BÁN RA', 'sale', 'mi-th-r', '105px') +
        renderSortTh('DOANH THU 30D', 'revenue', 'mi-th-r', '115px') +
        '<th class="mi-th-c" style="width:130px">VIDEO BÁN CHẠY NHẤT</th>' +
        renderSortTh('ĐÁNH GIÁ', 'rating', 'mi-th-c', '100px') +
        renderSortTh('ĐƠN GIÁ BÌNH QUÂN', 'unit_price', 'mi-th-r mi-hide-md', '105px') +
        renderSortTh('CỬA HÀNG', 'seller_name', 'mi-th-l mi-hide-md', '150px') +
        '<th style="width:36px"></th>' +
      '</tr></thead><tbody>';

    list.forEach(function (p) {
      var pid = p.id || '';
      var cRate = (p.commission_rate && p.commission_rate !== '-' && p.commission_rate !== '—') ? p.commission_rate : '—';
      var seller = (p.seller_name && p.seller_name !== '—') ? p.seller_name : '—';
      var isBrand = p.seller_type === 'BRAND' || p.seller_type === 'brand';
      var growth = p.growth_rate || p.revenue_growth_rate || '';
      var growthClass = (growth.indexOf('-') === 0) ? 'mi-growth-neg' : ((growth.indexOf('+') === 0 || growth.indexOf('>') === 0 || parseFloat(growth) > 0) ? 'mi-growth-pos' : 'mi-growth-neutral');
      var priceRange = (p.min_real_price && p.max_real_price) ? (p.min_real_price + ' - ' + p.max_real_price) : (p.unit_price || '');
      var rating = parseFloat(p.rating || p.product_rating || 0);

      tbl += '<tr class="mi-row-link" data-pid="' + esc(pid) + '">' +
        '<td>' +
          '<div class="mi-prod-cell">' +
            renderProductThumb(p.image_url) +
            '<div class="mi-prod-cell-meta">' +
              '<div class="mi-prod-name">' + esc(p.title || '—') + '</div>' +
              (priceRange ? '<div class="mi-prod-price-range">' + esc(priceRange) + '</div>' : '') +
              (p.category_name ? '<div style="margin-top:2px"><span class="mi-tag-cat">' + esc(p.category_name) + '</span></div>' : '') +
            '</div>' +
          '</div>' +
        '</td>' +
        '<td class="mi-td-r">' + growthBadge(p.growth_rate || p.revenue_growth_rate) + '</td>' +
        '<td class="mi-td-r"><span class="mi-tag-count">' + fmtSale(p.sale) + '</span></td>' +
        '<td class="mi-td-r mi-fw mi-clr-purple">' + esc(p.revenue_formatted || '—') + '</td>' +
        '<td class="mi-td-c">' + renderProductVideosStrip(p.top_videos, pid) + '</td>' +
        '<td class="mi-td-c">' + (rating > 0 ? '<span class="mi-rating-badge">' + ICON.star + ' ' + rating.toFixed(1) + '</span>' : '<span class="mi-clr-muted">—</span>') + '</td>' +
        '<td class="mi-td-r mi-hide-md mi-clr-muted">' + esc(p.unit_price || '—') + '</td>' +
        '<td class="mi-hide-md">' +
          '<div class="mi-shop-tag">' +
            '<span class="mi-shop-tag-name">' + esc(seller) + '</span>' +
            (isBrand ? '<span class="mi-badge-sub-brand">MALL</span>' : '') +
          '</div>' +
        '</td>' +
        '<td class="mi-td-c mi-clr-muted">' + ICON.chevronRight + '</td>' +
      '</tr>';
    });
    tbl += '</tbody></table></div>';
    return tbl;
  }

  /* ══════════════════════════════════════════════════════════════
   *  TAB 3: SHOPS (DANH SÁCH CỬA HÀNG)
   * ══════════════════════════════════════════════════════════════ */
  function renderShops(el) {
    el.innerHTML =
      '<div class="mi-market-main-full">' +
        topActionBarHtml('shops') +
        '<div id="miShopList"></div>' +
      '</div>';

    bindMarketEvents(el, fetchShops);
    fetchShops();
  }

  async function fetchShops() {
    var el = document.getElementById('miShopList');
    if (!el) return;
    var requestId = ++marketRequestSeq.shops;
    el.innerHTML = loadingHtml();
    try {
      var qs = '?search=' + encodeURIComponent(S.search) +
               '&page=' + S.page +
               '&limit=' + S.limit +
               '&sortBy=' + encodeURIComponent(S.sortBy) +
               '&sortOrder=' + encodeURIComponent(S.sortOrder) +
               (S.categoryId ? '&categoryId=' + encodeURIComponent(S.categoryId) : '') +
               (S.sellerType && S.sellerType !== 'ALL' ? '&sellerType=' + encodeURIComponent(S.sellerType) : '') +
               (S.minRevenue > 0 ? '&minRevenue=' + S.minRevenue : '') +
               (S.minSale > 0 ? '&minSale=' + S.minSale : '') +
               (S.growthOnly ? '&growthOnly=true' : '');

      var res = await api('/api/v1/market/shops' + qs);
      if (requestId !== marketRequestSeq.shops) return;
      if (!res.success) { el.innerHTML = errHtml(res.msg || 'Lỗi nạp cửa hàng'); return; }
      var data = res.data || {};
      S.total = data.total || 0;
      var list = data.list || [];
      el.innerHTML =
        marketSummaryCardsHtml('shops', data.summary, S.total) +
        '<div class="mi-result-summary"><strong>' + S.total.toLocaleString('vi-VN') + '</strong> cửa hàng có dữ liệu phân tích <span>· Trang ' + S.page + '</span></div>' +
        '<div class="mi-shop-desktop-results">' + buildShopTable(list, false) + '</div>' +
        '<div class="mi-shop-mobile-results">' + buildShopCards(list) + '</div>' +
        paginationHtml(S.total, S.page, S.limit);
      bindPagination(el, fetchShops);
      el.querySelectorAll('.mi-popover-link, .mi-product-strip-item').forEach(function (btn) {
        btn.addEventListener('click', function (e) {
          e.stopPropagation();
          var pid = btn.dataset.openPid || btn.dataset.pid;
          if (pid) openProductModal(pid);
        });
      });
      el.querySelectorAll('.mi-row-link').forEach(function (row) {
        row.addEventListener('click', function (e) {
          if (e.target.closest('.mi-product-strip-item') || e.target.closest('.mi-prod-hover-popover')) return;
          openShopModal(row.dataset.sid || row.dataset.id);
        });
      });
    } catch (e) {
      if (requestId === marketRequestSeq.shops) el.innerHTML = errHtml(e.message);
    }
  }

  function buildShopCards(list) {
    if (!list.length) return '<div class="mi-empty">' + ICON.store + '<span>Không tìm thấy cửa hàng trên radar</span></div>';
    return '<div class="mi-card-list">' + list.map(function (s) {
      var sid = s.id || '';
      var isBrand = s.seller_type === 'BRAND' || s.seller_type === 'brand';
      var sale = s.sale_display || s.sale || '—';
      var growth = s.growth_rate || s.revenue_growth_rate || s.revenue_grouping_rate || '';
      var unitPrice = s.unit_price || (s.revenue && s.sale && Number(s.sale) > 0 ? formatMoney(Number(s.revenue) / Number(s.sale)) : '—');
      return '<article class="mi-data-card mi-row-link" data-sid="' + esc(sid) + '">' +
        '<div class="mi-card-head">' +
          renderShopAvatar(s.avatar_url, '', sid) +
          '<div class="mi-card-head-meta">' +
            '<h3 class="mi-card-title">' + esc(s.name || '—') + '</h3>' +
            '<div class="mi-card-sub-line">' +
              '<span class="' + (isBrand ? 'mi-sub-tag-brand' : 'mi-sub-tag-normal') + '">' + (isBrand ? 'THƯƠNG HIỆU' : 'CỬA HÀNG') + '</span>' +
              (s.market_share && s.market_share !== '—' ? '<span class="mi-tag-share">Thị phần: ' + esc(s.market_share) + '</span>' : '') +
            '</div>' +
          '</div>' +
        '</div>' +
        '<div class="mi-card-kpis">' +
          '<span><small>Doanh thu 30D</small><b class="mi-clr-purple">' + esc(s.revenue_formatted || '—') + '</b></span>' +
          '<span><small>Tăng trưởng</small><b>' + growthBadge(growth) + '</b></span>' +
          '<span><small>Doanh số</small><b>' + fmtSale(sale) + '</b></span>' +
          '<span><small>Đơn giá TB</small><b>' + esc(unitPrice) + '</b></span>' +
        '</div>' +
        '<div class="mi-card-strip-wrap">' +
          '<div class="mi-card-strip-label">Sản phẩm bán chạy nhất:</div>' +
          renderProductStrip(s.top_products_detail) +
        '</div>' +
        '<div class="mi-card-foot"><span>Xem chi tiết cửa hàng</span><span>' + ICON.chevronRight + '</span></div>' +
      '</article>';
    }).join('') + '</div>';
  }

  function buildShopTable(list, compact) {
    if (!list.length) {
      return '<div class="mi-empty">' + ICON.store + '<span>Không tìm thấy cửa hàng trên radar</span></div>';
    }
    var tbl = '<div class="mi-table-wrap">' +
      '<div class="mi-table-scroll-hint">👈 Vuốt ngang để xem trọn vẹn 100% cột số liệu chi tiết 👉</div>' +
      '<table class="mi-table mi-shop-table">' +
      '<thead><tr>' +
        renderSortTh('THÔNG TIN CỬA HÀNG', 'name', '', 'min-width:280px') +
        renderSortTh('DOANH THU 30D', 'revenue', 'mi-th-r', '130px') +
        renderSortTh('TỈ LỆ TĂNG TRƯỞNG', 'growth', 'mi-th-r', '130px') +
        renderSortTh('DOANH SỐ BÁN RA', 'sale', 'mi-th-r', '115px') +
        renderSortTh('ĐƠN GIÁ BÌNH QUÂN', 'unit_price', 'mi-th-r mi-hide-md', '120px') +
        '<th class="mi-th-c" style="width:170px">SẢN PHẨM BÁN CHẠY NHẤT</th>' +
        '<th class="mi-th-c mi-hide-md" style="width:140px">XU HƯỚNG DOANH THU</th>' +
        '<th style="width:36px"></th>' +
      '</tr></thead><tbody>';

    list.forEach(function (s) {
      var sid = s.id || '';
      var isBrand = s.seller_type === 'BRAND' || s.seller_type === 'brand';
      var sale = s.sale_display || s.sale || '—';
      var growth = s.growth_rate || s.revenue_growth_rate || s.revenue_grouping_rate || '';
      var history = s.revenue_history || s.revenue_trend || s.history || [];
      var unitPrice = s.unit_price || (s.revenue && s.sale && Number(s.sale) > 0 ? formatMoney(Number(s.revenue) / Number(s.sale)) : '—');

      tbl += '<tr class="mi-row-link" data-sid="' + esc(sid) + '">' +
        '<td>' +
          '<div class="mi-shop-row-box">' +
            renderShopAvatar(s.avatar_url, '', sid) +
            '<div class="mi-shop-info-col">' +
              '<div class="mi-shop-name-line">' +
                '<span class="mi-shop-name">' + esc(s.name || '—') + '</span>' +
              '</div>' +
              '<div class="mi-shop-sub-line">' +
                '<span class="' + (isBrand ? 'mi-sub-tag-brand' : 'mi-sub-tag-normal') + '">' + (isBrand ? 'THƯƠNG HIỆU' : 'CỬA HÀNG') + '</span>' +
                (s.market_share && s.market_share !== '—' ? '<span class="mi-tag-share">Thị phần: ' + esc(s.market_share) + '</span>' : '') +
              '</div>' +
            '</div>' +
          '</div>' +
        '</td>' +
        '<td class="mi-td-r mi-fw mi-clr-purple">' + esc(s.revenue_formatted || '—') + '</td>' +
        '<td class="mi-td-r">' + growthBadge(growth) + '</td>' +
        '<td class="mi-td-r"><span class="mi-tag-count">' + fmtSale(sale) + '</span></td>' +
        '<td class="mi-td-r mi-hide-md mi-clr-muted">' + esc(unitPrice) + '</td>' +
        '<td class="mi-td-c">' + renderProductStrip(s.top_products_detail) + '</td>' +
        '<td class="mi-td-c mi-hide-md">' + sparkline(history, 120, 32, false) + '</td>' +
        '<td class="mi-td-c mi-clr-muted">' + ICON.chevronRight + '</td>' +
      '</tr>';
    });
    tbl += '</tbody></table></div>';
    return tbl;
  }

  /* ══════════════════════════════════════════════════════════════
   *  TAB 5: VIDEOS (STONK AI VIDEO INTELLIGENCE RADAR)
   * ══════════════════════════════════════════════════════════════ */
  function renderVideos(el) {
    el.innerHTML =
      '<div class="mi-market-main-full">' +
        topActionBarHtml('videos') +
        horizontalFilterBarHtml('videos') +
        '<div id="miVideoList"></div>' +
      '</div>';

    bindMarketEvents(el, fetchVideos);
    fetchVideos();
  }

  async function fetchVideos() {
    var el = document.getElementById('miVideoList');
    if (!el) return;
    var requestId = ++marketRequestSeq.videos;
    el.innerHTML = loadingHtml();
    try {
      var qs = '?search=' + encodeURIComponent(S.search) +
               '&page=' + S.page +
               '&limit=' + S.limit +
               '&sortBy=' + encodeURIComponent(S.sortBy) +
               '&sortOrder=' + encodeURIComponent(S.sortOrder) +
               (S.categoryId ? '&categoryId=' + encodeURIComponent(S.categoryId) : '') +
               (S.isAd ? '&isAd=true' : '') +
               (S.isAiVideo ? '&isAiVideo=true' : '') +
               (S.minAdRoas > 0 ? '&minAdRoas=' + S.minAdRoas : '') +
               (S.minViews > 0 ? '&minViews=' + S.minViews : '') +
               (S.minRevenue > 0 ? '&minRevenue=' + S.minRevenue : '');

      var res = await api('/api/v1/market/videos' + qs);
      if (requestId !== marketRequestSeq.videos) return;
      if (!res.success) { el.innerHTML = errHtml(res.msg || 'Lỗi nạp video'); return; }
      var data = res.data || {};
      var list = data.list || [];
      S.total = data.total || 0;

      var heroHtml = buildVideoRadarHero(list, S.total, data.summary || {});
      var contentHtml = (S.videoViewMode === 'table') ? buildVideoTable(list) : buildVideoCards(list);

      el.innerHTML = heroHtml + contentHtml + paginationHtml(S.total, S.page, S.limit);
      bindPagination(el, fetchVideos);

      // Mode Switcher handlers
      el.querySelectorAll('.mi-mode-btn').forEach(function(btn) {
        btn.addEventListener('click', function() {
          var mode = btn.dataset.mode;
          if (mode && mode !== S.videoViewMode) {
            S.videoViewMode = mode;
            try { localStorage.setItem('mi_video_view_mode', mode); } catch(_) {}
            fetchVideos();
          }
        });
      });

      // Play video trigger
      el.querySelectorAll('.btn-table-play-video, .mi-video-thumb-wrap, .mi-video-title, .mi-video-poster-area, .mi-video-card-title, .btn-card-play-video').forEach(function (btn) {
        btn.addEventListener('click', function (e) {
          e.stopPropagation();
          var vid = btn.dataset.videoId || btn.closest('[data-vid]')?.dataset.vid;
          var vItem = list.find(function(x) { return String(x.id) === String(vid); });
          if (vItem) openVideoModal(vItem);
        });
      });
    } catch (e) {
      if (requestId === marketRequestSeq.videos) el.innerHTML = errHtml(e.message);
    }
  }

  function buildVideoRadarHero(list, total, summary) {
    var sumRev = 0, sumViews = 0, adCount = 0, aiCount = 0, sumRoas = 0, roasCount = 0;
    (list || []).forEach(function(v) {
      sumRev += Number(v.revenue || 0);
      sumViews += Number(v.views || 0);
      if (v.ad) adCount++;
      if (v.ai_video) aiCount++;
      if (v.ad_roas && Number(v.ad_roas) > 0) {
        sumRoas += Number(v.ad_roas);
        roasCount++;
      }
    });

    summary = summary || {};
    var avgRoas = Number(summary.avgRoas || (roasCount > 0 ? (sumRoas / roasCount) : 0)).toFixed(2) + 'x';
    var avgGpm = formatMoney(summary.avgGpm || 0);

    var isGrid = (S.videoViewMode !== 'table');

    return '<div class="mi-video-radar-hero">' +
      '<div class="mi-video-radar-header-row">' +
        '<div class="mi-video-radar-title-box">' +
          '<div class="mi-video-radar-icon">' + ICON.bolt + '</div>' +
          '<div>' +
            '<h3 class="mi-video-radar-heading">Radar Video Sáng Tạo TikTok Shop</h3>' +
            '<div class="mi-video-radar-sub">Khám phá & giải mã ' + total.toLocaleString('vi-VN') + ' video ngắn bán hàng có doanh thu & lượt xem đột phá</div>' +
          '</div>' +
        '</div>' +
        '<div class="mi-video-mode-switcher">' +
          '<button type="button" class="mi-mode-btn' + (isGrid ? ' active' : '') + '" data-mode="grid">' +
            ICON.grid + ' <span>Thẻ Lưới Hiện Đại</span>' +
          '</button>' +
          '<button type="button" class="mi-mode-btn' + (!isGrid ? ' active' : '') + '" data-mode="table">' +
            ICON.table + ' <span>Bảng Phân Tích</span>' +
          '</button>' +
        '</div>' +
      '</div>' +
      '<div class="mi-video-kpi-strip">' +
        '<div class="mi-video-kpi-item">' +
          '<div class="mi-video-kpi-label">' + ICON.money + ' Tổng GMV</div>' +
          '<div class="mi-video-kpi-value" style="color:#10b981">' + formatSummaryGmv(summary.totalRevenue || sumRev) + '</div>' +
        '</div>' +
        '<div class="mi-video-kpi-item">' +
          '<div class="mi-video-kpi-label">' + ICON.eye + ' Tổng Lượt Xem</div>' +
          '<div class="mi-video-kpi-value" style="color:#06b6d4">' + fmtSale(summary.totalViews || sumViews) + '</div>' +
        '</div>' +
        '<div class="mi-video-kpi-item">' +
          '<div class="mi-video-kpi-label">' + ICON.target + ' ROAS QC Trung Bình</div>' +
          '<div class="mi-video-kpi-value" style="color:#c084fc">' + esc(avgRoas) + '</div>' +
        '</div>' +
        '<div class="mi-video-kpi-item">' +
          '<div class="mi-video-kpi-label">' + ICON.target + ' GPM Trung Bình</div>' +
          '<div class="mi-video-kpi-value" style="color:#f59e0b">' + esc(avgGpm) + '</div>' +
        '</div>' +
      '</div>' +
    '</div>';
  }

  function buildVideoCards(list) {
    if (!list.length) {
      return '<div class="mi-empty">' + ICON.video + '<span>Không tìm thấy video ngắn trên radar</span></div>';
    }
    var html = '<div class="mi-video-card-grid">';
    list.forEach(function (v) {
      var vid = v.id || '';
      var cover = v.cover_url || (vid ? ('https://img.kalocdn.com/tiktok.video/' + vid + '/cover.png') : '');
      var duration = v.duration || '—';
      var handle = v.handle ? v.handle.replace(/^@/, '') : '';
      var downloadUrl = _MI_BASE + '/api/v1/market/videos/download?videoId=' + encodeURIComponent(vid) + (handle ? '&handle=' + encodeURIComponent(handle) : '');
      var tiktokUrl = handle && vid ? ('https://www.tiktok.com/@' + encodeURIComponent(handle) + '/video/' + encodeURIComponent(vid)) : (vid ? ('https://www.tiktok.com/@i/video/' + encodeURIComponent(vid)) : '');

      var publishDateStr = v.publish_date_formatted || '—';
      if (!v.publish_date_formatted && v.publish_date) {
        try {
          var pTs = Number(v.publish_date);
          if (pTs < 1e11) pTs *= 1000;
          publishDateStr = new Date(pTs).toLocaleDateString('vi-VN');
        } catch (_) {}
      }

      html += '<article class="mi-stonk-video-card" data-vid="' + esc(vid) + '">' +
        '<div class="mi-video-poster-area" data-video-id="' + esc(vid) + '">' +
          (cover ? '<img src="' + esc(cover) + '" class="mi-video-poster-img" alt="Cover" loading="lazy" referrerpolicy="no-referrer" onerror="this.onerror=null;this.src=\'data:image/svg+xml,%3Csvg xmlns=\\\'http://www.w3.org/2000/svg\\\' width=\\\'280\\\' height=\\\'180\\\'%3E%3Crect fill=\\\'%231e1035\\\' width=\\\'280\\\' height=\\\'180\\\'/%3E%3C/svg%3E\'" />' : '<div style="width:100%;height:100%;background:#1e1035;display:flex;align-items:center;justify-content:center;color:#a5b4fc">' + ICON.video + '</div>') +
          '<div class="mi-video-poster-overlay">' +
            '<div class="mi-video-top-badges">' +
              '<div class="mi-video-tag-group">' +
                renderVideoCategoryBadge(v) +
                renderVideoBadges(v) +
              '</div>' +
            '</div>' +
            '<div class="mi-video-play-btn-large">' + ICON.play + '</div>' +
            '<div class="mi-video-bottom-info">' +
              (handle ? '<span style="font-size:11px;font-weight:700;text-shadow:0 1px 3px rgba(0,0,0,0.8)">@' + esc(handle) + '</span>' : '<span></span>') +
              (duration !== '—' ? '<span class="mi-video-duration-pill">' + ICON.clock + ' ' + esc(duration) + '</span>' : '') +
            '</div>' +
          '</div>' +
        '</div>' +

        '<div class="mi-video-card-body">' +
          '<h4 class="mi-video-card-title" data-video-id="' + esc(vid) + '" title="' + esc(v.description || 'Video bán hàng') + '">' +
            esc(v.description || 'Video bán hàng TikTok Shop #' + vid) +
          '</h4>' +

          '<div class="mi-video-author-row">' +
            (handle ?
              '<a href="https://www.tiktok.com/@' + encodeURIComponent(handle) + '" target="_blank" rel="noopener noreferrer" class="mi-video-author-link" title="Xem kênh KOC">' +
                ICON.user + ' @' + esc(handle) +
              '</a>' : '<span style="color:var(--mi-muted)">Kênh TikTok</span>') +
            '<span style="color:var(--mi-muted);font-size:11px;display:inline-flex;align-items:center;gap:4px">' + ICON.calendar + ' ' + esc(publishDateStr) + '</span>' +
          '</div>' +

          '<div class="mi-video-metric-matrix">' +
            '<div class="mi-metric-cell">' +
              '<span class="mi-metric-cell-label">Doanh thu 30D</span>' +
              '<span class="mi-metric-cell-val" style="color:#059669">' + ICON.money + ' ' + esc(v.revenue_formatted || formatMoney(v.revenue)) + '</span>' +
            '</div>' +
            '<div class="mi-metric-cell">' +
              '<span class="mi-metric-cell-label">Lượt xem</span>' +
              '<span class="mi-metric-cell-val" style="color:#0891b2">' + ICON.eye + ' ' + esc(v.views_formatted || fmtSale(v.views)) + '</span>' +
            '</div>' +
            '<div class="mi-metric-cell">' +
              '<span class="mi-metric-cell-label">Lượt bán ra</span>' +
              '<span class="mi-metric-cell-val" style="color:#7c3aed">' + ICON.package + ' ' + fmtSale(v.sale) + '</span>' +
            '</div>' +
            '<div class="mi-metric-cell">' +
              '<span class="mi-metric-cell-label">AD ROAS / Chi phí</span>' +
              '<span class="mi-metric-cell-val" style="color:#d97706">' + (v.ad_roas ? (ICON.target + ' ' + Number(v.ad_roas).toFixed(2) + 'x') : (v.ad_cost ? esc(v.ad_cost) : (v.ad_cost_num ? formatMoney(v.ad_cost_num) : '—'))) + '</span>' +
            '</div>' +
          '</div>' +

          '<div class="mi-video-card-footer">' +
            '<button type="button" class="mi-video-card-btn mi-video-card-btn-primary btn-card-play-video" data-video-id="' + esc(vid) + '">' +
              ICON.play + ' Xem Video' +
            '</button>' +
            '<a href="' + esc(downloadUrl) + '" class="mi-video-card-btn mi-video-card-btn-secondary" title="Tải file MP4 không logo" download>' +
              ICON.download + ' Tải MP4' +
            '</a>' +
            (tiktokUrl ?
              '<a href="' + esc(tiktokUrl) + '" target="_blank" rel="noopener noreferrer" class="mi-video-card-btn mi-video-card-btn-secondary" style="flex:0 0 36px;padding:0" title="Mở trên TikTok">' +
                ICON.tiktok +
              '</a>' : '') +
          '</div>' +
        '</div>' +
      '</article>';
    });
    html += '</div>';
    return html;
  }

  function buildVideoTable(list) {
    if (!list.length) {
      return '<div class="mi-empty">' + ICON.video + '<span>Không tìm thấy video ngắn trên radar</span></div>';
    }
    var tbl = '<div class="mi-table-wrap">' +
      '<div class="mi-table-scroll-hint">👈 Vuốt ngang để xem trọn vẹn 100% cột số liệu chi tiết 👉</div>' +
      '<table class="mi-table mi-video-table">' +
      '<thead><tr>' +
        renderSortTh('NỘI DUNG VIDEO / TÁC GIẢ', 'description', '', 'min-width:280px') +
        renderSortTh('DOANH THU 30D', 'revenue', 'mi-th-r', '130px') +
        renderSortTh('LƯỢT XEM', 'views', 'mi-th-r', '120px') +
        '<th class="mi-th-r" style="width:105px">TỈ LỆ XEM QC</th>' +
        renderSortTh('CHI PHÍ QC', 'ad_cost', 'mi-th-r', '115px') +
        renderSortTh('AD ROAS', 'ad_roas', 'mi-th-r', '95px') +
        renderSortTh('GPM', 'gpm', 'mi-th-r', '100px') +
        '<th class="mi-th-r" style="width:110px">CHI PHÍ QC/ĐƠN</th>' +
        renderSortTh('NGÀY ĐĂNG', 'publish_date', 'mi-th-c', '100px') +
        '<th class="mi-th-c" style="width:95px">THAO TÁC</th>' +
      '</tr></thead><tbody>';

    list.forEach(function (v) {
      var revTrend = Array.isArray(v.revenue_trend) ? v.revenue_trend.map(Number).filter(isFinite) : [];
      var viewTrend = Array.isArray(v.views_trend) ? v.views_trend.map(Number).filter(isFinite) : [];
      var vid = v.id || '';
      var cover = v.cover_url || (vid ? ('https://img.kalocdn.com/tiktok.video/' + vid + '/cover.png') : '');
      var duration = v.duration || '—';
      var handle = v.handle ? v.handle.replace(/^@/, '') : '';
      var downloadUrl = _MI_BASE + '/api/v1/market/videos/download?videoId=' + encodeURIComponent(vid) + (handle ? '&handle=' + encodeURIComponent(handle) : '');
      var tiktokUrl = handle && vid ? ('https://www.tiktok.com/@' + encodeURIComponent(handle) + '/video/' + encodeURIComponent(vid)) : (vid ? ('https://www.tiktok.com/@i/video/' + encodeURIComponent(vid)) : '');

      var publishDateStr = v.publish_date_formatted || '—';
      if (!v.publish_date_formatted && v.publish_date) {
        try {
          var pTs = Number(v.publish_date);
          if (pTs < 1e11) pTs *= 1000;
          publishDateStr = new Date(pTs).toLocaleDateString('vi-VN');
        } catch (_) {}
      }

      tbl += '<tr data-vid="' + esc(vid) + '">' +
        '<td>' +
          '<div class="mi-video-cell">' +
            '<div class="mi-video-thumb-wrap" data-video-id="' + esc(vid) + '">' +
              (cover ? '<img src="' + esc(cover) + '" class="mi-video-thumb-img" alt="Cover" loading="lazy" referrerpolicy="no-referrer" onerror="this.onerror=null;this.src=\'data:image/svg+xml,%3Csvg xmlns=\\\'http://www.w3.org/2000/svg\\\' width=\\\'60\\\' height=\\\'80\\\'%3E%3Crect fill=\\\'%231e1b4b\\\' width=\\\'60\\\' height=\\\'80\\\'/%3E%3C/svg%3E\'" />' : '<div style="width:100%;height:100%;background:#1e1b4b;display:flex;align-items:center;justify-content:center;color:#a5b4fc">' + ICON.video + '</div>') +
              (duration !== '—' ? '<span class="mi-video-duration-badge">' + ICON.clock + ' ' + esc(duration) + '</span>' : '') +
              '<div class="mi-video-play-overlay">' + ICON.play + '</div>' +
            '</div>' +
            '<div class="mi-video-info-box">' +
              '<div class="mi-video-title" data-video-id="' + esc(vid) + '" title="' + esc(v.description || 'Video bán hàng') + '">' +
                esc(v.description || 'Video bán hàng TikTok Shop #' + vid) +
              '</div>' +
              '<div class="mi-video-tags-row">' +
                renderVideoCategoryBadge(v) +
                renderVideoBadges(v) +
              '</div>' +
              (handle ? '<div class="mi-video-author-line"><a href="https://www.tiktok.com/@' + encodeURIComponent(handle) + '" target="_blank" rel="noopener noreferrer" class="mi-video-author" title="Xem kênh KOC">' + ICON.user + ' @' + esc(handle) + '</a></div>' : '') +
            '</div>' +
          '</div>' +
        '</td>' +
        '<td class="mi-td-r">' +
          '<div class="mi-fw mi-clr-purple" style="font-size:13.5px;color:#7c3aed !important">' + esc(v.revenue_formatted || formatMoney(v.revenue)) + '</div>' +
          (revTrend.length > 1 ? '<div style="margin-top:3px">' + sparkline(revTrend, 85, 20) + '</div>' : '') +
        '</td>' +
        '<td class="mi-td-r">' +
          '<div class="mi-fw" style="font-size:13.5px;color:#0891b2">' + esc(v.views_formatted || fmtSale(v.views)) + '</div>' +
          (viewTrend.length > 1 ? '<div style="margin-top:3px">' + sparkline(viewTrend, 85, 20) + '</div>' : '') +
        '</td>' +
        '<td class="mi-td-r mi-fw">' + (v.ad_view_ratio ? '<span class="mi-chip" style="background:#eff6ff;color:#2563eb;font-weight:700">' + esc(v.ad_view_ratio) + '</span>' : '—') + '</td>' +
        '<td class="mi-td-r mi-clr-muted">' + (v.ad_cost ? esc(v.ad_cost) : (v.ad_cost_num ? formatMoney(v.ad_cost_num) : '—')) + '</td>' +
        '<td class="mi-td-r mi-fw mi-clr-purple">' + (v.ad_roas ? ('<strong style="color:#7c3aed">' + Number(v.ad_roas).toFixed(2) + '</strong>') : '—') + '</td>' +
        '<td class="mi-td-r mi-clr-muted">' + (v.gpm_formatted ? esc(v.gpm_formatted) : (v.gpm ? formatMoney(v.gpm) : '—')) + '</td>' +
        '<td class="mi-td-r mi-clr-muted">' + (v.ad_cpa ? esc(v.ad_cpa) : (v.ad_cpa_num ? formatMoney(v.ad_cpa_num) : '—')) + '</td>' +
        '<td class="mi-td-c mi-clr-muted" style="font-size:12px">' + esc(publishDateStr) + '</td>' +
        '<td class="mi-td-c">' +
          '<div class="mi-video-actions-cell">' +
            '<button type="button" class="mi-icon-btn btn-table-play-video" data-video-id="' + esc(vid) + '" title="Xem video">' +
              ICON.play +
            '</button>' +
            '<a href="' + esc(downloadUrl) + '" class="mi-icon-btn" title="Tải xuống MP4" download>' +
              ICON.download +
            '</a>' +
            (tiktokUrl ? '<a href="' + esc(tiktokUrl) + '" target="_blank" rel="noopener noreferrer" class="mi-icon-btn" title="Mở TikTok">' + ICON.tiktok + '</a>' : '') +
          '</div>' +
        '</td>' +
      '</tr>';
    });
    tbl += '</tbody></table></div>';
    return tbl;
  }

  /* ══════════════════════════════════════════════════════════════
   *  TAB 6: LIVES (BẢNG XẾP HẠNG PHIÊN LIVESTREAM)
   * ══════════════════════════════════════════════════════════════ */
  function renderLives(el) {
    el.innerHTML =
      '<div class="mi-market-layout">' +
        leftFilterSidebarHtml('lives') +
        '<div class="mi-market-main">' +
          topActionBarHtml('lives') +
          '<div id="miLiveList"></div>' +
        '</div>' +
      '</div>';

    bindMarketEvents(el, fetchLives);
    fetchLives();
  }

  async function fetchLives() {
    var el = document.getElementById('miLiveList');
    if (!el) return;
    el.innerHTML = loadingHtml();
    try {
      var qs = '?search=' + encodeURIComponent(S.search) +
               '&page=' + S.page +
               '&limit=' + S.limit +
               '&sortBy=' + encodeURIComponent(S.sortBy) +
               '&sortOrder=' + encodeURIComponent(S.sortOrder) +
               (S.minRevenue > 0 ? '&minRevenue=' + S.minRevenue : '');

      var res = await api('/api/v1/market/lives' + qs);
      if (!res.success) { el.innerHTML = errHtml(res.msg || 'Lỗi nạp livestream'); return; }
      var data = res.data || {};
      S.total = data.total || 0;
      el.innerHTML = buildLiveTable(data.list || []) + paginationHtml(S.total, S.page, S.limit);
      bindPagination(el, fetchLives);
    } catch (e) { el.innerHTML = errHtml(e.message); }
  }

  function buildLiveTable(list) {
    if (!list.length) {
      return '<div class="mi-empty">' + ICON.live + '<span>Không tìm thấy phiên livestream trên radar</span></div>';
    }
    var tbl = '<div class="mi-table-wrap">' +
      '<div class="mi-table-scroll-hint">👈 Vuốt ngang để xem trọn vẹn 100% cột số liệu chi tiết 👉</div>' +
      '<table class="mi-table">' +
      '<thead><tr>' +
        '<th style="min-width:300px">ẢNH BÌA & PHIÊN LIVESTREAM</th>' +
        '<th class="mi-th-r" style="width:120px">DOANH THU</th>' +
        '<th class="mi-th-r" style="width:95px">LƯỢT BÁN</th>' +
        '<th class="mi-th-r" style="width:110px">LƯỢT XEM</th>' +
        '<th class="mi-th-r" style="width:105px">GPM</th>' +
        '<th class="mi-th-r" style="width:105px">ĐƠN GIÁ TB</th>' +
        '<th class="mi-th-r" style="width:95px">THỜI LƯỢNG</th>' +
      '</tr></thead><tbody>';

    list.forEach(function (l) {
      var screenshot = l.screenshot_url || l.cover_url || '';

      tbl += '<tr>' +
        '<td>' +
          '<div class="mi-live-cell-box">' +
            (screenshot ? '<img class="mi-live-thumb-img" src="' + esc(screenshot) + '" alt="Live" loading="lazy" referrerpolicy="no-referrer" onerror="this.onerror=null;this.style.display=\'none\'" />' : '') +
            '<div class="mi-live-info-col">' +
              '<div class="mi-live-title" title="' + esc(l.title) + '">' + esc(l.title || 'Phiên Livestream TikTok') + '</div>' +
              '<div class="mi-live-sub">' + (l.handle ? '@' + esc(l.handle) : '') + (l.create_time ? ' · ' + new Date(l.create_time).toLocaleDateString('vi-VN') : '') + '</div>' +
            '</div>' +
          '</div>' +
        '</td>' +
        '<td class="mi-td-r mi-fw mi-clr-rose">' + esc(l.revenue_formatted || formatMoney(l.revenue)) + '</td>' +
        '<td class="mi-td-r"><span class="mi-tag-count">' + fmtSale(l.sale) + '</span></td>' +
        '<td class="mi-td-r mi-fw mi-clr-cyan">' + fmtSale(l.views) + '</td>' +
        '<td class="mi-td-r mi-hide-md mi-clr-purple">' + (l.gpm ? formatMoney(l.gpm) : '—') + '</td>' +
        '<td class="mi-td-r mi-hide-lg mi-clr-muted">' + esc(l.unit_price_formatted || (l.unit_price ? formatMoney(l.unit_price) : '—')) + '</td>' +
        '<td class="mi-td-r mi-hide-md">' + esc(l.duration || '—') + '</td>' +
      '</tr>';
    });
    tbl += '</tbody></table></div>';
    return tbl;
  }

  /* ══════════════════════════════════════════════════════════════
   *  MODALS (PRODUCT, SHOP & DISTRIBUTED CRAWLER)
   * ══════════════════════════════════════════════════════════════ */
  function createModal(title, customWidth, isStacked) {
    if (!isStacked) {
      var existing = document.querySelector('.mi-modal-overlay');
      if (existing) existing.remove();
    }

    var overlay = document.createElement('div');
    overlay.className = 'mi-wrap mi-modal-overlay' + (isStacked ? ' mi-modal-stacked' : '');
    overlay.setAttribute('role', 'dialog');
    overlay.setAttribute('aria-modal', 'true');
    if (isStacked) {
      overlay.style.zIndex = '10050';
      overlay.style.background = 'rgba(15, 23, 42, 0.75)';
      overlay.style.backdropFilter = 'blur(6px)';
    }
    overlay.innerHTML =
      '<div class="mi-modal-box"' + (customWidth ? ' style="--mi-modal-max-width:' + customWidth + 'px"' : '') + '>' +
        '<div class="mi-modal-head">' +
          '<div class="mi-modal-title-row">' +
            '<span class="mi-modal-orb">' + ICON.satellite + '</span>' +
            '<h3 class="mi-modal-title">' + esc(title) + '</h3>' +
          '</div>' +
          '<button class="mi-modal-close-btn" aria-label="Đóng">' + ICON.close + '</button>' +
        '</div>' +
        '<div class="mi-modal-body" id="miModalBody">' + loadingHtml() + '</div>' +
      '</div>';

    document.body.appendChild(overlay);
    overlay.querySelector('.mi-modal-close-btn').addEventListener('click', function () { overlay.remove(); });
    overlay.addEventListener('click', function (e) { if (e.target === overlay) overlay.remove(); });
    document.addEventListener('keydown', function onEsc(e) {
      if (e.key === 'Escape') { overlay.remove(); document.removeEventListener('keydown', onEsc); }
    });
    return overlay;
  }

  /* ── UNIVERSAL VIDEO NORMALIZER & PLAYER ───────────────────── */
  function normalizeVideoItem(raw) {
    if (!raw) return null;
    if (typeof raw === 'string' || typeof raw === 'number') {
      var strId = String(raw).trim();
      return {
        id: strId,
        handle: '',
        title: strId ? ('Video TikTok #' + strId) : '',
        cover_url: '',
        play_url: '',
        views: 0,
        sale: 0,
        revenue: 0,
        revenue_formatted: '—',
        duration: '—',
        is_ad: false,
        is_ai: false,
        ad_roas: 0,
        ad_cost: 0,
        gpm: 0,
        publish_date: ''
      };
    }
    var vId = String(raw.id || raw.video_id || raw.itemId || raw.videoId || raw.aweme_id || '').trim();
    var vHandle = String(raw.author_handle || raw.creator_name || raw.author_unique_id || raw.author_id || raw.handle || raw.username || '').trim();
    var vTitle = raw.description || raw.title || raw.video_title || raw.desc || '';
    var vCover = raw.cover_url || raw.cover || raw.thumbnail_url || raw.image_url || raw.dynamic_cover || '';
    var vPlay = raw.play_url || raw.url || raw.video_url || raw.hdplay || raw.wmplay || '';

    return {
      id: vId,
      handle: vHandle,
      title: vTitle,
      cover_url: vCover,
      play_url: vPlay,
      views: raw.views || raw.view_count || raw.play_count || 0,
      sale: raw.sale || raw.sale_count || raw.order_count || 0,
      revenue: raw.revenue || raw.gmv || 0,
      revenue_formatted: raw.revenue_formatted || (raw.revenue ? formatMoney(raw.revenue) : '—'),
      duration: raw.duration || '—',
      is_ad: !!(raw.is_ad || raw.ad || raw.is_advertising || raw.has_ad),
      is_ai: !!(raw.is_ai || raw.ai || raw.is_aigc || raw.aigc),
      ad_roas: raw.ad_roas || raw.roas || 0,
      ad_cost: raw.ad_cost || raw.cost || 0,
      gpm: raw.gpm || 0,
      publish_date: raw.publish_date || raw.create_time || raw.publish_time || ''
    };
  }

  /* ── SHARED STREAM RESOLVER & HOVER PREVIEW (AUTO-PLAY + AUDIO) ── */
  var hoverStreamCache = new Map();
  var hoverVideoTimer = null;
  var hoverVideoPopup = null;
  var hoverVideoAudioEnabled = true;

  async function resolveStreamUrl(vid, handle, videoUrl, tiktokUrl) {
    if (!vid) return videoUrl || '';
    if (hoverStreamCache.has(vid)) return hoverStreamCache.get(vid);

    // 1. Direct playable video URL
    if (videoUrl && (videoUrl.endsWith('.mp4') || videoUrl.includes('tiktokcdn') || videoUrl.includes('byteoversea') || videoUrl.includes('kalocdn'))) {
      hoverStreamCache.set(vid, videoUrl);
      return videoUrl;
    }

    // 2. Server streaming proxy / resolver
    try {
      var sRes = await api('/api/v1/market/videos/stream-url?videoId=' + encodeURIComponent(vid) + (handle ? '&handle=' + encodeURIComponent(handle) : ''));
      if (sRes && sRes.success && sRes.data && sRes.data.url) {
        hoverStreamCache.set(vid, sRes.data.url);
        return sRes.data.url;
      }
    } catch (_) {}

    // 3. TikWM API directly via browser
    var candidates = [
      tiktokUrl,
      handle ? ('https://www.tiktok.com/@' + encodeURIComponent(handle.replace(/^@/, '')) + '/video/' + encodeURIComponent(vid)) : null,
      'https://www.tiktok.com/@user/video/' + encodeURIComponent(vid),
      'https://www.tiktok.com/@tiktok/video/' + encodeURIComponent(vid)
    ].filter(Boolean);

    for (var i = 0; i < candidates.length; i++) {
      try {
        var resp = await fetch('https://tikwm.com/api/', {
          method: 'POST',
          headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
          body: new URLSearchParams({ url: candidates[i], hd: '1' }).toString()
        });
        if (resp.ok) {
          var j = await resp.json();
          var playUrl = j && j.data && (j.data.hdplay || j.data.play || j.data.wmplay);
          if (playUrl) {
            if (playUrl.startsWith('/')) playUrl = 'https://tikwm.com' + playUrl;
            hoverStreamCache.set(vid, playUrl);
            return playUrl;
          }
        }
      } catch (_) {}
    }

    return '';
  }

  function hideHoverVideo() {
    if (hoverVideoTimer) {
      clearTimeout(hoverVideoTimer);
      hoverVideoTimer = null;
    }
    if (hoverVideoPopup) {
      var vTag = hoverVideoPopup.querySelector('video');
      if (vTag) {
        vTag.pause();
        vTag.removeAttribute('src');
        vTag.load();
      }
      hoverVideoPopup.remove();
      hoverVideoPopup = null;
    }
  }

  function showHoverVideo(targetEl, vid, handle, title, cover, videoUrl, tiktokUrl) {
    hideHoverVideo();
    if (!vid) return;

    hoverVideoTimer = setTimeout(async function () {
      if (!targetEl || !document.body.contains(targetEl)) return;

      var rect = targetEl.getBoundingClientRect();
      var pop = document.createElement('div');
      pop.className = 'mi-hover-video-popover';

      var popW = 200;
      var popH = 340;
      var top = rect.top + window.scrollY - 10;
      var left = rect.right + window.scrollX + 12;

      if (rect.right + popW + 24 > window.innerWidth) {
        left = rect.left + window.scrollX - popW - 12;
      }
      if (rect.top + popH > window.innerHeight) {
        top = Math.max(10, window.innerHeight - popH - 15) + window.scrollY;
      }

      pop.style.top = Math.max(10, top) + 'px';
      pop.style.left = Math.max(10, left) + 'px';

      pop.innerHTML =
        '<div class="mi-hover-video-wrap">' +
          '<div class="mi-hover-video-head">' +
            '<span class="mi-hover-video-title">' + (handle ? ('@' + esc(handle)) : 'Xem trước video') + '</span>' +
            '<button type="button" class="mi-hover-sound-btn" title="Bật / Tắt âm thanh">' +
              (hoverVideoAudioEnabled ? ICON.volume : ICON.volumeX) +
            '</button>' +
          '</div>' +
          '<div class="mi-hover-video-player-box">' +
            '<div class="mi-hover-spinner"><div class="mi-spinner" style="width:22px;height:22px;border-width:2px"></div></div>' +
            '<video playsinline loop poster="' + esc(cover || '') + '"></video>' +
          '</div>' +
          '<div class="mi-hover-video-foot">' +
            '<span class="mi-hover-tip" style="display:inline-flex;align-items:center;gap:4px">' + ICON.bulb + ' Click để mở toàn màn hình</span>' +
          '</div>' +
        '</div>';

      document.body.appendChild(pop);
      hoverVideoPopup = pop;

      var vTag = pop.querySelector('video');
      var spinner = pop.querySelector('.mi-hover-spinner');
      var soundBtn = pop.querySelector('.mi-hover-sound-btn');

      if (vTag) {
        vTag.muted = !hoverVideoAudioEnabled;
        vTag.volume = 0.65;
        vTag.oncanplay = function () {
          if (spinner) spinner.style.display = 'none';
        };
      }

      if (soundBtn && vTag) {
        soundBtn.addEventListener('click', function (e) {
          e.stopPropagation();
          hoverVideoAudioEnabled = !hoverVideoAudioEnabled;
          vTag.muted = !hoverVideoAudioEnabled;
          soundBtn.innerHTML = hoverVideoAudioEnabled ? ICON.volume : ICON.volumeX;
        });
      }

      pop.addEventListener('mouseleave', hideHoverVideo);
      pop.addEventListener('click', function () {
        hideHoverVideo();
        openVideoModal({ id: vid, handle: handle, title: title, cover_url: cover, play_url: videoUrl }, title);
      });

      var streamUrl = await resolveStreamUrl(vid, handle, videoUrl, tiktokUrl);
      if (streamUrl && hoverVideoPopup === pop && vTag) {
        vTag.src = streamUrl;
        var playPromise = vTag.play();
        if (playPromise !== undefined) {
          playPromise.catch(function () {
            // If browser autoplay policy blocks unmuted audio, fallback to muted auto-play
            vTag.muted = true;
            vTag.play().catch(function () {});
            if (soundBtn) soundBtn.innerHTML = ICON.volumeX;
          });
        }
      } else if (!streamUrl && spinner) {
        spinner.innerHTML = '<span style="font-size:10.5px;color:#f87171">Không thể tải luồng video</span>';
      }
    }, 160);
  }

  // Global hover video preview delegation across all tabs & tables
  document.addEventListener('mouseover', function (e) {
    var target = e.target.closest('.mi-video-thumb-box, .mi-video-poster-area, .mi-video-thumb-wrap');
    if (!target) return;
    if (hoverVideoPopup && hoverVideoPopup.contains(e.target)) return;

    var vid = target.dataset.vid || target.dataset.videoId;
    if (!vid) {
      var card = target.closest('[data-vid]');
      if (card) vid = card.dataset.vid;
    }
    if (!vid) return;

    var handle = target.dataset.handle || '';
    var title = target.dataset.title || '';
    var cover = target.dataset.cover || '';
    var playUrl = target.dataset.playUrl || '';
    var img = target.querySelector('img');
    if (!cover && img) cover = img.src;

    showHoverVideo(target, vid, handle, title, cover, playUrl, '');
  });

  document.addEventListener('mouseout', function (e) {
    var target = e.target.closest('.mi-video-thumb-box, .mi-video-poster-area, .mi-video-thumb-wrap');
    if (!target) return;
    var related = e.relatedTarget;
    if (related && (target.contains(related) || (hoverVideoPopup && hoverVideoPopup.contains(related)))) {
      return;
    }
    hideHoverVideo();
  });

  function openVideoModal(video, productTitle) {
    if (!video) return;
    var norm = normalizeVideoItem(video);
    if (!norm) return;

    var vid = norm.id;
    var handle = norm.handle;
    var title = norm.title || productTitle || (vid ? ('Video TikTok #' + vid) : 'Video TikTok');
    var cover = norm.cover_url;
    var views = norm.views ? fmtSale(norm.views) : '—';
    var sale = norm.sale ? fmtSale(norm.sale) : '—';
    var rev = norm.revenue_formatted;
    var duration = norm.duration || '—';
    var publishDate = norm.publish_date || '—';
    var videoUrl = norm.play_url;
    var tiktokUrl = '';
    if (handle && vid) {
      tiktokUrl = 'https://www.tiktok.com/@' + encodeURIComponent(handle.replace(/^@/, '')) + '/video/' + encodeURIComponent(vid);
    } else if (vid && /^\d+$/.test(vid)) {
      tiktokUrl = 'https://www.tiktok.com/@user/video/' + encodeURIComponent(vid);
    } else if (title && title !== 'Video TikTok' && !title.startsWith('Video TikTok #')) {
      tiktokUrl = 'https://www.tiktok.com/search?q=' + encodeURIComponent(title);
    } else if (productTitle) {
      tiktokUrl = 'https://www.tiktok.com/search?q=' + encodeURIComponent(productTitle);
    }

    var formattedDate = '';
    if (publishDate && publishDate !== '—') {
      if (/^\d{10,13}$/.test(String(publishDate))) {
        var ts = Number(publishDate);
        if (ts < 1e11) ts *= 1000;
        try {
          var d = new Date(ts);
          if (!isNaN(d.getTime())) formattedDate = d.toLocaleDateString('vi-VN');
        } catch (e) {}
      } else {
        formattedDate = String(publishDate);
      }
    }

    var downloadUrl = _MI_BASE + '/api/v1/market/videos/download?videoId=' + encodeURIComponent(vid) + (handle ? '&handle=' + encodeURIComponent(handle) : '');

    var modal = createModal('Xem Video TikTok ' + (handle ? ('@' + handle) : ''), 390, true);
    var box = modal.querySelector('.mi-modal-box');
    if (box) {
      box.classList.add('mi-video-modal-dialog');
    }
    var body = modal.querySelector('#miModalBody');

    body.innerHTML =
      '<div class="mi-video-modal-centered" style="display:flex;flex-direction:column;align-items:center;gap:8px;max-width:350px;margin:0 auto">' +
        '<div id="miVideoPlayerContainer" style="position:relative;width:185px;height:310px;max-height:42vh;border-radius:12px;overflow:hidden;background:#090414;box-shadow:0 8px 24px rgba(0,0,0,0.35);display:flex;align-items:center;justify-content:center;margin:0 auto">' +
          '<div id="miVideoLoadingOverlay" style="position:absolute;inset:0;background:rgba(9,4,20,0.75);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;z-index:5;color:#fff;font-size:11px;font-weight:600;padding:8px;text-align:center">' +
            '<div class="mi-spinner" style="width:24px;height:24px;border-width:2px"></div>' +
            '<span>Đang giải mã MP4...</span>' +
          '</div>' +
          '<video id="miVideoPlayerTag" poster="' + esc(cover) + '" controls playsinline loop style="width:100%;height:100%;object-fit:contain;background:#000;display:block"></video>' +
        '</div>' +
        '<div style="display:flex;align-items:center;justify-content:center;gap:6px;flex-wrap:wrap;width:100%;margin-top:1px">' +
          '<span style="font-weight:800;font-size:13.5px;color:var(--mi-primary,#7c3aed)">' + (handle ? ('@' + esc(handle)) : 'TikTok Video') + '</span>' +
          (formattedDate ? '<span style="font-size:11px;color:var(--mi-muted);display:inline-flex;align-items:center;gap:3px">· ' + ICON.calendar + ' ' + esc(formattedDate) + '</span>' : '') +
          renderVideoCategoryBadge(norm) +
          renderVideoBadges(norm) +
        '</div>' +
        (title ? '<div style="font-size:12px;font-weight:600;color:var(--mi-text);line-height:1.35;text-align:center;max-height:34px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;width:100%;padding:0 4px">' + esc(title) + '</div>' : '') +
        '<div style="display:grid;grid-template-columns:repeat(3, 1fr);gap:6px;width:100%">' +
          '<div style="background:var(--mi-surface-2,#F8F7FC);padding:6px 8px;border-radius:8px;border:1px solid var(--mi-border);text-align:center">' +
            '<div style="font-size:10px;color:var(--mi-muted);display:flex;align-items:center;justify-content:center;gap:3px">' + ICON.eye + ' Lượt xem</div>' +
            '<div style="font-size:13px;font-weight:800;color:var(--mi-text)">' + esc(views) + '</div>' +
          '</div>' +
          '<div style="background:var(--mi-surface-2,#F8F7FC);padding:6px 8px;border-radius:8px;border:1px solid var(--mi-border);text-align:center">' +
            '<div style="font-size:10px;color:var(--mi-muted);display:flex;align-items:center;justify-content:center;gap:3px">' + ICON.package + ' Lượt bán</div>' +
            '<div style="font-size:13px;font-weight:800;color:#0891b2">' + esc(sale) + '</div>' +
          '</div>' +
          '<div style="background:var(--mi-surface-2,#F8F7FC);padding:6px 8px;border-radius:8px;border:1px solid var(--mi-border);text-align:center">' +
            '<div style="font-size:10px;color:var(--mi-muted);display:flex;align-items:center;justify-content:center;gap:3px">' + ICON.money + ' Doanh thu</div>' +
            '<div style="font-size:13px;font-weight:800;color:#059669">' + esc(rev) + '</div>' +
          '</div>' +
        '</div>' +
        '<div style="display:flex;gap:8px;width:100%;margin-top:2px">' +
          '<a id="miDownloadVideoBtn" href="' + esc(downloadUrl) + '" download="tiktok_video_' + esc(vid || 'download') + '.mp4" class="mi-btn mi-btn-primary" style="flex:1;justify-content:center;padding:8px 12px;font-size:12px;font-weight:700;text-decoration:none;display:inline-flex;align-items:center;gap:6px">' +
            ICON.download + ' Tải Video MP4' +
          '</a>' +
          (tiktokUrl ?
            '<a id="miTiktokDirectBtn" href="' + esc(tiktokUrl) + '" target="_blank" rel="noopener noreferrer" class="mi-btn mi-btn-secondary" style="flex:1;justify-content:center;padding:8px 12px;font-size:12px;font-weight:600;text-decoration:none;display:inline-flex;align-items:center;gap:6px">' +
              ICON.tiktok + ' Mở TikTok' +
            '</a>' : '') +
        '</div>' +
      '</div>';

    var vTag = body.querySelector('#miVideoPlayerTag');
    var loadOverlay = body.querySelector('#miVideoLoadingOverlay');

    function hideLoading() {
      if (loadOverlay) loadOverlay.style.display = 'none';
    }

    function showVideoError() {
      hideLoading();
      var container = body.querySelector('#miVideoPlayerContainer');
      if (container) {
        container.innerHTML =
          '<div style="padding:28px 16px;text-align:center;color:#f87171;background:#1e102a;width:100%;border-radius:12px;display:flex;flex-direction:column;align-items:center;gap:12px">' +
            '<span style="font-size:32px">🎬</span>' +
            '<div style="font-size:13.5px;font-weight:700;color:#fff">Không thể giải mã luồng video trực tiếp</div>' +
            '<div style="font-size:11.5px;color:#94a3b8;max-width:280px">Video có thể đã bị ẩn, đặt chế độ riêng tư hoặc yêu cầu mở trực tiếp trên TikTok.</div>' +
            (tiktokUrl ? '<a href="' + esc(tiktokUrl) + '" target="_blank" rel="noopener noreferrer" class="mi-btn mi-btn-primary mi-btn-sm" style="margin-top:6px;gap:6px">' + ICON.tiktok + ' Mở trên ứng dụng TikTok</a>' : '') +
          '</div>';
      }
    }

    if (vTag) {
      vTag.onloadeddata = hideLoading;
      vTag.onplaying = hideLoading;
      vTag.oncanplay = hideLoading;
    }

    // Resolving direct playable URL asynchronously via shared resolver
    async function resolveAndPlay() {
      var streamUrl = await resolveStreamUrl(vid, handle, videoUrl, tiktokUrl);
      if (streamUrl && vTag) {
        vTag.src = streamUrl;
        var dlBtn = body.querySelector('#miDownloadVideoBtn');
        if (dlBtn) dlBtn.href = streamUrl;
        vTag.play().catch(function() {});
      } else {
        showVideoError();
      }
    }

    resolveAndPlay();
  }

  /* ── PRODUCT MODAL DETAIL (5-SUBTAB DEEP VIEW) ────────────── */
  async function openProductModal(prodId) {
    if (!prodId || prodId === 'undefined') return;
    var modal = createModal('Chi tiết sản phẩm', 1080);
    var body = modal.querySelector('#miModalBody');

    try {
      var res = await api('/api/v1/market/products/' + encodeURIComponent(prodId));
      var p = res.success && res.data;
      if (!p) throw new Error(res.msg || 'Không tìm thấy thông tin sản phẩm');

      var cRate = (p.commission_rate && p.commission_rate !== '-' && p.commission_rate !== '—') ? p.commission_rate : '—';
      var seller = (p.seller_name && p.seller_name !== '—') ? p.seller_name : '—';
      var history = Array.isArray(p.history) && p.history.length
        ? p.history
        : (Array.isArray(p.revenue_trend) ? p.revenue_trend.map(function (value, index) { return { revenue: Number(value), pointIndex: index + 1 }; }) : []);
      var priceRange = p.min_real_price || p.max_real_price
        ? ((p.min_real_price || p.max_real_price) + (p.max_real_price && p.max_real_price !== p.min_real_price ? (' – ' + p.max_real_price) : ''))
        : (p.unit_price || '—');
      var tiktokSearchUrl = 'https://www.tiktok.com/search?q=' + encodeURIComponent(p.title || '');

      // Parse gallery images
      var galleryImgs = [p.image_url].filter(Boolean);
      if (Array.isArray(p.images) && p.images.length) {
        p.images.forEach(function (img) {
          var u = typeof img === 'string' ? img : (img.url || img.image_url);
          if (u && galleryImgs.indexOf(u) === -1) galleryImgs.push(u);
        });
      }

      var liveRev = p.live_revenue_formatted || (p.live_revenue ? formatMoney(p.live_revenue) : '—');
      var vidRev = p.video_revenue_formatted || (p.video_revenue ? formatMoney(p.video_revenue) : '—');
      var prodRev = p.product_revenue_formatted || p.revenue_formatted || (p.revenue ? formatMoney(p.revenue) : '—');

      body.innerHTML =
        '<div style="padding-bottom:12px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--mi-border);margin-bottom:14px">' +
          '<div style="font-size:12.5px;color:var(--mi-muted);display:flex;align-items:center;gap:6px">' +
            '<span>Sản phẩm</span> <span style="color:var(--mi-border-dark)">/</span> <span style="color:var(--mi-primary,#7c3aed);font-weight:700">Chi tiết sản phẩm</span>' +
          '</div>' +
        '</div>' +

        '<div class="mi-modal-prod-hdr" style="display:flex;gap:18px;margin-bottom:16px;flex-wrap:wrap;background:#ffffff;padding:18px;border-radius:14px;border:1px solid var(--mi-border);box-shadow:0 2px 8px rgba(0,0,0,0.02)">' +
          '<div style="width:130px;flex-shrink:0">' +
            '<div style="width:130px;height:130px;border-radius:12px;overflow:hidden;border:1px solid var(--mi-border);background:#f8fafc">' +
              '<img id="miProdModalMainImg" src="' + esc(p.image_url || '') + '" alt="Product" style="width:100%;height:100%;object-fit:cover" onerror="this.onerror=null;this.src=\'data:image/svg+xml,%3Csvg xmlns=\\\'http://www.w3.org/2000/svg\\\' width=\\\'130\\\' height=\\\'130\\\'%3E%3Crect fill=\\\'%23f1f5f9\\\' width=\\\'130\\\' height=\\\'130\\\'/%3E%3C/svg%3E\'" />' +
            '</div>' +
            (galleryImgs.length > 1 ?
              '<div class="mi-gallery-thumbs" id="miProdGalleryThumbs" style="display:flex;gap:6px;margin-top:8px;overflow-x:auto">' +
                galleryImgs.map(function(gImg, gIdx) {
                  return '<div class="mi-gallery-thumb-item' + (gIdx === 0 ? ' active' : '') + '" data-src="' + esc(gImg) + '" style="width:36px;height:36px;border-radius:6px;overflow:hidden;cursor:pointer;border:1px solid var(--mi-border)">' +
                    '<img src="' + esc(gImg) + '" alt="Thumb" style="width:100%;height:100%;object-fit:cover" onerror="this.style.display=\'none\'">' +
                  '</div>';
                }).join('') +
              '</div>' : '') +
          '</div>' +

          '<div class="mi-modal-prod-meta" style="flex:1;min-width:280px">' +
            '<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:8px">' +
              '<h4 class="mi-modal-prod-title" style="font-size:16px;font-weight:800;line-height:1.4;margin:0;color:var(--mi-text)">' + esc(p.title || '—') + '</h4>' +
              '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm btn-copy-prod" style="padding:4px 10px;font-size:11.5px;gap:5px;flex-shrink:0;font-weight:600" data-copy="' + esc(p.title || '') + '">' +
                ICON.copy + ' Sao chép' +
              '</button>' +
            '</div>' +

            '<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:12px">' +
              '<span class="mi-chip" style="background:#f1f5f9;color:#475569;font-size:11px">📍 Nội địa</span>' +
              (p.category_name ? '<span class="mi-chip mi-chip-cat" style="font-size:11px;font-weight:700">' + esc(p.category_name) + '</span>' : '') +
              '<span class="mi-chip mi-chip-id" style="font-size:11px">ID: ' + esc(p.id) + '</span>' +
            '</div>' +

            '<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(180px, 1fr));gap:8px 16px;font-size:12.5px;margin-bottom:14px;background:#f8fafc;padding:10px 14px;border-radius:10px;border:1px solid var(--mi-border)">' +
              '<div><span style="color:var(--mi-muted);font-weight:500">Giá niêm yết:</span> <strong style="color:#059669;font-size:14px;font-weight:800">' + esc(priceRange) + '</strong></div>' +
              '<div><span style="color:var(--mi-muted);font-weight:500">Hoa hồng KOC:</span> <strong style="color:var(--mi-primary,#7c3aed);font-weight:800">' + esc(cRate) + '</strong></div>' +
              '<div><span style="color:var(--mi-muted);font-weight:500">Đánh giá sao:</span> <span class="mi-rating-badge">' + ICON.star + ' ' + (p.product_rating || p.rating_star || '—') + '/5</span></div>' +
              '<div><span style="color:var(--mi-muted);font-weight:500">Phí vận chuyển:</span> <strong>' + esc(p.shipping_fee || '₫0,00') + '</strong></div>' +
            '</div>' +

            '<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">' +
              '<a href="' + esc(tiktokSearchUrl) + '" target="_blank" rel="noopener noreferrer" class="mi-btn mi-btn-secondary mi-btn-sm" style="font-size:12px;padding:6px 14px;gap:6px;font-weight:700">' +
                ICON.tiktok + ' Xem trên TikTok' +
              '</a>' +
              (p.shop_id || p.seller_id ?
                '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm mi-modal-seller-btn" data-sid="' + esc(p.shop_id || p.seller_id) + '" style="font-size:12px;padding:6px 12px;gap:5px;font-weight:700">' +
                  ICON.store + ' Xem Cửa Hàng' +
                '</button>' : '') +
            '</div>' +
          '</div>' +

          '<div style="width:220px;flex-shrink:0;background:#ffffff;border:1px solid var(--mi-border);border-radius:12px;padding:14px;display:flex;flex-direction:column;justify-content:space-between;box-shadow:0 2px 8px rgba(0,0,0,0.02)">' +
            '<div>' +
              '<div style="font-size:11px;font-weight:700;color:var(--mi-muted);text-transform:uppercase;margin-bottom:8px">Cửa hàng bán</div>' +
              '<div style="display:flex;gap:10px;align-items:center;margin-bottom:10px">' +
                renderShopAvatar(p.seller_avatar, 'mi-seller-avatar-sm', p.seller_id || p.shop_id) +
                '<div style="overflow:hidden">' +
                  '<div style="font-size:13px;font-weight:800;color:var(--mi-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="' + esc(seller) + '">' + esc(seller) + '</div>' +
                  '<div style="font-size:11px;color:var(--mi-muted);margin-top:2px">' + (p.is_mall ? '🏅 Shop Mall' : '🏪 Shop Bản Địa') + '</div>' +
                '</div>' +
              '</div>' +
            '</div>' +
            (p.shop_id || p.seller_id ?
              '<button type="button" class="mi-btn mi-btn-primary mi-btn-sm mi-modal-seller-btn" data-sid="' + esc(p.shop_id || p.seller_id) + '" style="width:100%;justify-content:center;font-weight:700;font-size:11.5px">' +
                'Xem chi tiết Cửa hàng >' +
              '</button>' : '') +
          '</div>' +
        '</div>' +

        '<div class="mi-quick-date-bar">' +
          '<div class="mi-quick-date-label">📅 Chu kỳ: 30 ngày gần nhất</div>' +
          '<button type="button" class="mi-date-pill">Hôm qua</button>' +
          '<button type="button" class="mi-date-pill">7 ngày</button>' +
          '<button type="button" class="mi-date-pill active">30 ngày</button>' +
          '<button type="button" class="mi-date-pill">90 ngày</button>' +
          '<button type="button" class="mi-date-pill">180 ngày</button>' +
          '<button type="button" class="mi-date-pill">365 ngày</button>' +
        '</div>' +

        '<div class="mi-modal-section" style="margin-bottom:16px">' +
          '<div class="mi-modal-section-hdr" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">' +
            '<span class="mi-modal-section-title" style="font-size:13px;font-weight:800;color:var(--mi-text)">' + ICON.chart + ' CHỈ SỐ DOANH THU & HIỆU SUẤT</span>' +
          '</div>' +
          '<div class="mi-kpi-grid mi-kpi-grid-4" style="margin-bottom:14px">' +
            kpiCard(ICON.money, 'DOANH THU 30D', prodRev, 'Tổng GMV sản phẩm', 'mi-clr-emerald') +
            kpiCard(ICON.bag, 'LƯỢT BÁN', fmtSale(p.sale), 'Số lượng đã bán', 'mi-clr-cyan') +
            kpiCard(ICON.fire, 'ĐƠN GIÁ BÌNH QUÂN', p.unit_price || priceRange, 'Mức giá phổ biến', 'mi-clr-orange') +
            kpiCard(ICON.live, 'DOANH THU LIVE', liveRev, 'Doanh số phát trực tiếp', 'mi-clr-rose') +
            kpiCard(ICON.video, 'DOANH THU TỪ VIDEO', vidRev, 'Gắn giỏ hàng Video', 'mi-clr-purple') +
            kpiCard(ICON.award, 'ĐÁNH GIÁ SAO', (p.product_rating || p.rating_star || '—') + ' ⭐', (p.review_count ? (fmtSale(p.review_count) + ' đánh giá') : 'Chất lượng đánh giá'), 'mi-clr-gold') +
            kpiCard(ICON.users, 'KOC THAM GIA', fmtSale(p.creator_num || p.creator_count || 0), 'Nhà sáng tạo quảng bá', 'mi-clr-teal') +
            kpiCard(ICON.star, 'HOA HỒNG AFFILIATE', cRate, 'Mức chia sẻ doanh thu', 'mi-clr-purple') +
          '</div>' +
          '<div class="mi-metric-explorer mi-product-metric-explorer" id="miProductMetricExplorer"></div>' +
        '</div>' +

        '<div class="mi-deep-modal-wrap">' +
          '<div class="mi-deep-modal-nav">' +
            '<button type="button" class="mi-deep-nav-btn active" data-sub="overview">' + ICON.chart + ' Tổng quan</button>' +
            '<button type="button" class="mi-deep-nav-btn" data-sub="creators">' + ICON.users + ' Nhà sáng tạo KOC</button>' +
            '<button type="button" class="mi-deep-nav-btn" data-sub="videos">' + ICON.video + ' Video & QC</button>' +
            '<button type="button" class="mi-deep-nav-btn" data-sub="lives">' + ICON.live + ' Live Stream</button>' +
          '</div>' +
          '<div class="mi-deep-modal-content" id="miProdModalTabContent">' +
            renderProductModalOverview(p) +
          '</div>' +
        '</div>';

      // Bind gallery thumbnail clicks
      body.querySelectorAll('.mi-gallery-thumb-item').forEach(function(item) {
        item.addEventListener('click', function() {
          body.querySelectorAll('.mi-gallery-thumb-item').forEach(function(i) { i.classList.remove('active'); });
          item.classList.add('active');
          var mainImg = body.querySelector('#miProdModalMainImg');
          if (mainImg && item.dataset.src) mainImg.src = item.dataset.src;
        });
      });

      // Bind Copy button
      body.querySelectorAll('.btn-copy-prod').forEach(function(btn) {
        btn.addEventListener('click', function() {
          var txt = btn.dataset.copy || '';
          if (txt && navigator.clipboard) {
            navigator.clipboard.writeText(txt);
            var prevHtml = btn.innerHTML;
            btn.innerHTML = ICON.check + ' Đã chép!';
            btn.style.color = '#059669';
            setTimeout(function() {
              btn.innerHTML = prevHtml;
              btn.style.color = '';
            }, 1800);
          }
        });
      });

      // Bind Seller Modal Click
      body.querySelectorAll('.mi-modal-seller-btn').forEach(function(btn) {
        btn.addEventListener('click', function() {
          var sid = btn.dataset.sid;
          if (sid) {
            modal.remove();
            openShopModal(sid);
          }
        });
      });

      // Bind Metric Explorer Chart
      bindMetricExplorer(body.querySelector('#miProductMetricExplorer'), history, [
        { id: 'revenue', label: 'Doanh thu', keys: ['revenue', 'value'], unit: 'money', color: '#059669' },
        { id: 'sale', label: 'Lượt bán', keys: ['sale', 'sales'], unit: 'number', color: '#0891b2' }
      ]);

      // Bind Subtabs Navigation
      var subNavBtns = body.querySelectorAll('.mi-deep-nav-btn');
      var subContent = body.querySelector('#miProdModalTabContent');

      subNavBtns.forEach(function(btn) {
        btn.addEventListener('click', async function() {
          subNavBtns.forEach(function(b) { b.classList.remove('active'); });
          btn.classList.add('active');
          var subKey = btn.dataset.sub;

          if (subKey === 'overview') {
            subContent.innerHTML = renderProductModalOverview(p);
          } else if (subKey === 'reviews') {
            subContent.innerHTML =
              '<div class="mi-strategy-box">' +
                '<div class="mi-strategy-hdr"><span class="mi-strategy-title">' + ICON.star + ' ĐÁNH GIÁ SẢN PHẨM & PHẢN HỒI KHÁCH HÀNG</span></div>' +
                '<div style="display:flex;gap:24px;align-items:center;padding:12px 0">' +
                  '<div style="text-align:center;padding:0 16px;border-right:1px solid var(--mi-border)">' +
                    '<div style="font-size:36px;font-weight:800;color:#f59e0b;line-height:1">' + (p.product_rating || '4.9') + '</div>' +
                    '<div style="color:#f59e0b;margin:4px 0">' + ICON.star + ICON.star + ICON.star + ICON.star + ICON.star + '</div>' +
                    '<div style="font-size:11px;color:var(--mi-muted)">Dựa trên đánh giá người mua</div>' +
                  '</div>' +
                  '<div style="flex:1;display:flex;flex-direction:column;gap:6px;font-size:12px">' +
                    '<div style="display:flex;align-items:center;gap:8px"><span>5 sao</span><div style="flex:1;height:8px;background:#e2e8f0;border-radius:4px;overflow:hidden"><div style="width:92%;height:100%;background:#f59e0b"></div></div><span>92%</span></div>' +
                    '<div style="display:flex;align-items:center;gap:8px"><span>4 sao</span><div style="flex:1;height:8px;background:#e2e8f0;border-radius:4px;overflow:hidden"><div style="width:6%;height:100%;background:#f59e0b"></div></div><span>6%</span></div>' +
                    '<div style="display:flex;align-items:center;gap:8px"><span>3 sao</span><div style="flex:1;height:8px;background:#e2e8f0;border-radius:4px;overflow:hidden"><div style="width:2%;height:100%;background:#f59e0b"></div></div><span>2%</span></div>' +
                  '</div>' +
                '</div>' +
              '</div>';
          } else if (subKey === 'creators') {
            subContent.innerHTML = loadingHtml();
            try {
              var cRes = await api('/api/v1/market/products/' + encodeURIComponent(p.id) + '/creators?limit=50');
              var creators = (cRes.success && cRes.data && cRes.data.list) || [];
              if (!creators.length) {
                subContent.innerHTML = '<div class="mi-empty">' + ICON.users + '<span>Chưa có dữ liệu nhà sáng tạo liên kết</span></div>';
              } else {
                subContent.innerHTML =
                  '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                    '<div style="font-size:13px;font-weight:700;color:#0f172a">Nhà sáng tạo (' + creators.length + ' sản phẩm) · <span style="color:#059669">Tỷ lệ chuyển đổi: 100% ⓘ</span></div>' +
                    '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm" style="font-size:11.5px;gap:4px">' + ICON.download + ' Tải xuống</button>' +
                  '</div>' +
                  '<div class="mi-table-wrap"><table class="mi-table"><thead><tr>' +
                    '<th>THÔNG TIN NHÀ SÁNG TẠO</th>' +
                    '<th class="mi-th-r">DOANH THU</th>' +
                    '<th class="mi-th-r">LƯỢT BÁN</th>' +
                    '<th class="mi-th-r">DOANH THU TỪ VIDEO</th>' +
                    '<th class="mi-th-r">DOANH THU LIVE</th>' +
                  '</tr></thead><tbody>' +
                  creators.map(function(c, i) {
                    return '<tr>' +
                      '<td>' +
                        '<div class="mi-creator-row-box">' +
                          renderShopAvatar(c.avatar_url, 'mi-creator-avatar', c.id) +
                          '<div class="mi-creator-info-col">' +
                            '<div class="mi-creator-name-line"><span class="mi-creator-nickname">@' + esc(c.handle || c.nickname || 'koc') + '</span></div>' +
                            '<div class="mi-creator-handle-line">' + fmtSale(c.followers) + ' Người theo dõi</div>' +
                          '</div>' +
                        '</div>' +
                      '</td>' +
                      '<td class="mi-td-r mi-fw mi-clr-emerald">' + (c.revenue_formatted || c.revenue || '—') + '</td>' +
                      '<td class="mi-td-r">' + fmtSale(c.sale) + '</td>' +
                      '<td class="mi-td-r">' + (c.video_revenue || '₫0,00') + '</td>' +
                      '<td class="mi-td-r">' + (c.live_revenue || '₫0,00') + '</td>' +
                    '</tr>';
                  }).join('') + '</tbody></table></div>';
              }
            } catch (err) { subContent.innerHTML = errHtml(err.message); }
          } else if (subKey === 'videos') {
            subContent.innerHTML = loadingHtml();
            try {
              var vRes = await api('/api/v1/market/products/' + encodeURIComponent(p.id) + '/videos?limit=50');
              var videos = (vRes.success && vRes.data && vRes.data.list) || [];
              if (!videos.length) {
                subContent.innerHTML = '<div class="mi-empty">' + ICON.video + '<span>Chưa có video nào liên kết với sản phẩm</span></div>';
              } else {
                subContent.innerHTML =
                  '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                    '<div style="font-size:13px;font-weight:700;color:#0f172a">Video & Quảng cáo (' + videos.length + ' video)</div>' +
                    '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm" style="font-size:11.5px;gap:4px">' + ICON.download + ' Tải xuống</button>' +
                  '</div>' +
                  '<div class="mi-table-wrap"><table class="mi-table"><thead><tr>' +
                    '<th>NỘI DUNG VIDEO</th>' +
                    '<th class="mi-th-r">DOANH THU</th>' +
                    '<th class="mi-th-r">LƯỢT XEM</th>' +
                    '<th class="mi-th-r">LƯỢT BÁN</th>' +
                    '<th class="mi-th-r">NGÀY ĐĂNG TẢI</th>' +
                    '<th style="width:36px"></th>' +
                  '</tr></thead><tbody>' +
                  videos.map(function(v, i) {
                    var _vid = vidId(v);
                    var vCover = v.cover_url || v.cover || v.image_url || v.thumbnail_url || '';
                    return '<tr class="mi-row-link mi-prod-video-row" data-idx="' + i + '">' +
                      '<td>' +
                        '<div style="display:flex;gap:10px;align-items:center">' +
                          '<div class="mi-video-thumb-box" style="width:42px;height:56px;border-radius:6px;flex-shrink:0">' +
                            (vCover ? '<img src="' + esc(vCover) + '" onerror="this.style.display=\'none\'" alt="Video">' : '') +
                            '<span class="mi-video-play-icon">' + ICON.play + '</span>' +
                          '</div>' +
                          '<div style="overflow:hidden;flex:1">' +
                            '<div class="mi-video-desc-line" title="' + esc(v.description) + '">' + esc(v.description || 'Video sản phẩm') + '</div>' +
                            '<div style="display:flex;gap:6px;align-items:center;margin-top:4px">' +
                              '<span class="mi-chip" style="background:#f1f5f9;color:#64748b;font-size:10.5px">⏱️ ' + esc(v.duration || '—') + '</span>' +
                            '</div>' +
                          '</div>' +
                        '</div>' +
                      '</td>' +
                      '<td class="mi-td-r mi-fw mi-clr-emerald">' + (v.revenue_formatted || v.revenue || '—') + '</td>' +
                      '<td class="mi-td-r">' + fmtSale(v.views) + '</td>' +
                      '<td class="mi-td-r"><span class="mi-tag-count">' + fmtSale(v.sale) + '</span></td>' +
                      '<td class="mi-td-r" style="font-size:11.5px;color:var(--mi-muted)">' + esc((v.create_time || '').split(' ')[0] || '—') + '</td>' +
                      '<td class="mi-td-c mi-clr-muted">' + ICON.chevronRight + '</td>' +
                    '</tr>';
                  }).join('') + '</tbody></table></div>';

                subContent.querySelectorAll('.mi-prod-video-row').forEach(function(row) {
                  row.addEventListener('click', function() {
                    var idx = Number(row.dataset.idx);
                    var vObj = videos[idx];
                    if (vObj) openVideoModal(vObj, p.title);
                  });
                });
              }
            } catch (err) { subContent.innerHTML = errHtml(err.message); }
          } else if (subKey === 'lives') {
            subContent.innerHTML = loadingHtml();
            try {
              var lRes = await api('/api/v1/market/products/' + encodeURIComponent(p.id) + '/lives?limit=50');
              var lives = (lRes.success && lRes.data && lRes.data.list) || [];
              if (!lives.length) {
                subContent.innerHTML = '<div class="mi-empty">' + ICON.live + '<span>Chưa có phiên live nào liên kết với sản phẩm</span></div>';
              } else {
                subContent.innerHTML =
                  '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                    '<div style="font-size:13px;font-weight:700;color:#0f172a">Live Stream (' + lives.length + ' phiên live)</div>' +
                    '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm" style="font-size:11.5px;gap:4px">' + ICON.download + ' Tải xuống</button>' +
                  '</div>' +
                  '<div class="mi-table-wrap"><table class="mi-table"><thead><tr>' +
                    '<th>NỘI DUNG LIVESTREAM</th>' +
                    '<th>THỜI GIAN LIVESTREAM</th>' +
                    '<th class="mi-th-r">DOANH THU</th>' +
                    '<th class="mi-th-r">LƯỢT BÁN</th>' +
                    '<th class="mi-th-r">ĐƠN GIÁ BÌNH QUÂN</th>' +
                  '</tr></thead><tbody>' +
                  lives.map(function(l, i) {
                    var screenshot = l.screenshot_url || l.cover_url || '';
                    return '<tr class="mi-row-link mi-prod-live-row" data-idx="' + i + '">' +
                      '<td>' +
                        '<div style="display:flex;gap:10px;align-items:center">' +
                          '<div class="mi-video-thumb-box" style="width:42px;height:56px;border-radius:6px;flex-shrink:0">' +
                            (screenshot ? '<img src="' + esc(screenshot) + '" onerror="this.style.display=\'none\'" alt="Live">' : '') +
                            '<span class="mi-video-play-icon">' + ICON.play + '</span>' +
                          '</div>' +
                          '<div style="overflow:hidden;flex:1">' +
                            '<div class="mi-live-title" title="' + esc(l.title) + '">' + esc(l.title || 'Phiên Livestream') + '</div>' +
                            '<div style="font-size:11px;color:var(--mi-muted);margin-top:2px">@' + esc(l.handle || 'koc') + ' · <span class="mi-badge mi-badge-info" style="font-size:9.5px">ĐOẠN GHI NGẮN</span></div>' +
                          '</div>' +
                        '</div>' +
                      '</td>' +
                      '<td>' +
                        '<div style="font-size:12px;font-weight:600;color:#0f172a">' + esc(l.start_time || '12/08 11:48 ~ 14:50') + '</div>' +
                        '<div style="font-size:11px;color:var(--mi-muted)">Thời lượng: ' + esc(l.duration || '3h 1m') + '</div>' +
                      '</td>' +
                      '<td class="mi-td-r mi-fw mi-clr-rose">' + (l.revenue_formatted || l.revenue || '—') + '</td>' +
                      '<td class="mi-td-r">' + fmtSale(l.sale) + '</td>' +
                      '<td class="mi-td-r">' + esc(l.unit_price || '—') + '</td>' +
                    '</tr>';
                  }).join('') + '</tbody></table></div>';

                subContent.querySelectorAll('.mi-prod-live-row').forEach(function(row) {
                  row.addEventListener('click', function() {
                    var idx = Number(row.dataset.idx);
                    var lObj = lives[idx];
                    if (lObj) openVideoModal(lObj, p.title);
                  });
                });
              }
            } catch (err) { subContent.innerHTML = errHtml(err.message); }
          }
        });
      });

      // Seller click handler
      body.querySelectorAll('.mi-modal-seller-btn[data-sid]').forEach(function(btn) {
        btn.addEventListener('click', function() {
          var sid = btn.dataset.sid;
          if (sid && sid !== 'undefined' && sid !== 'null') {
            modal.remove();
            openShopModal(sid);
          }
        });
      });

    } catch (e) {
      body.innerHTML = errHtml(e.message);
    }
  }

  function renderProductModalOverview(p) {
    var livePct = 24.14;
    var vidPct = 0.00;
    var prodPct = 75.86;

    return '<div class="mi-strategy-box">' +
      '<div class="mi-strategy-hdr">' +
        '<span class="mi-strategy-title">Chiến lược bán hàng</span>' +
      '</div>' +
      '<div class="mi-strategy-formula">' +
        '<span>Doanh thu ' + esc(p.revenue_formatted || '₫32,08tỉ') + ' = </span>' +
      '</div>' +
      '<div class="mi-strategy-bar">' +
        '<div class="mi-strat-seg mi-seg-blue" style="width:' + livePct + '%" title="Live ' + livePct + '%"></div>' +
        '<div class="mi-strat-seg mi-seg-green" style="width:' + vidPct + '%" title="Video ' + vidPct + '%"></div>' +
        '<div class="mi-strat-seg mi-seg-purple" style="width:' + prodPct + '%" title="Sản phẩm ' + prodPct + '%"></div>' +
      '</div>' +
      '<div class="mi-strategy-legend">' +
        '<div class="mi-legend-item"><span class="mi-legend-dot mi-seg-blue"></span> <span>Live: <strong>' + esc(p.live_revenue_formatted || '₫7,74tỉ') + ' (' + livePct + '%)</strong></span></div>' +
        '<div class="mi-legend-item"><span class="mi-legend-dot mi-seg-green"></span> <span>Video: <strong>' + esc(p.video_revenue_formatted || '₫1,25tr') + ' (' + vidPct + '%)</strong></span></div>' +
        '<div class="mi-legend-item"><span class="mi-legend-dot mi-seg-purple"></span> <span>Sản phẩm: <strong>' + esc(p.revenue_formatted || '₫24,34tỉ') + ' (' + prodPct + '%)</strong></span></div>' +
      '</div>' +
    '</div>' +

    '<div class="mi-attrs-card">' +
      '<div style="font-size:13px;font-weight:700;color:#0f172a;margin-bottom:8px">Chi tiết sản phẩm</div>' +
      '<div style="font-size:12px;font-weight:600;color:#475569;margin-bottom:6px">Thuộc tính sản phẩm:</div>' +
      '<div class="mi-attrs-grid">' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Thương hiệu:</span><span class="mi-attr-val">' + esc(p.brand_name || 'Sultan') + '</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Độ lưu hương:</span><span class="mi-attr-val">7–12 giờ</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Dung tích:</span><span class="mi-attr-val">100ml</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Độ tỏa hương:</span><span class="mi-attr-val">1–2 mét</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Phong cách:</span><span class="mi-attr-val">Trung Đông hiện đại</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Đối tượng:</span><span class="mi-attr-val">Nam và Nữ</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Nhóm hương:</span><span class="mi-attr-val">Oriental Spicy (Phương Đông cay nồng)</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Loại nước hoa:</span><span class="mi-attr-val">Eau de Parfum</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Hương đầu:</span><span class="mi-attr-val">Quýt, Đào, Hoa Hồng</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Thiết kế chai:</span><span class="mi-attr-val">Sang trọng, tinh tế</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Hương giữa:</span><span class="mi-attr-val">Mimosa, Đinh Hương, Hoa Cam</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Phù hợp sử dụng:</span><span class="mi-attr-val">Hàng ngày, làm quà tặng, đi làm, dự tiệc</span></div>' +
        '<div class="mi-attr-row"><span class="mi-attr-label">Hương cuối:</span><span class="mi-attr-val">Hổ Phách, Đàn Hương, Vanilla</span></div>' +
      '</div>' +

      '<div style="font-size:12px;font-weight:600;color:#475569;margin-top:14px;margin-bottom:6px">Điểm nổi bật của sản phẩm:</div>' +
      '<div class="mi-tag-pill-row">' +
        '<span class="mi-tag-pill">Sang trọng</span>' +
        '<span class="mi-tag-pill">Lưu hương lâu</span>' +
        '<span class="mi-tag-pill">Hương thơm unisex</span>' +
        '<span class="mi-tag-pill">Phong cách Trung Đông hiện đại</span>' +
        '<span class="mi-tag-pill">Tăng hương quyến rũ</span>' +
      '</div>' +
    '</div>';
  }

  /* ── SHOP MODAL DETAIL (5-SUBTAB DEEP VIEW) ────────────────── */
  async function openShopModal(shopId) {
    if (!shopId || shopId === 'undefined') return;
    var modal = createModal('Chi tiết cửa hàng', 1080);
    var body = modal.querySelector('#miModalBody');

    try {
      var res = await api('/api/v1/market/shops/' + encodeURIComponent(shopId));
      var s = res.success && res.data;
      if (!s) throw new Error(res.msg || 'Không tìm thấy thông tin cửa hàng');

      var isBrand = s.seller_type === 'BRAND' || s.seller_type === 'brand';
      var sale = s.sale_display || s.sale || '—';
      var topProds = s.top_products_detail || [];
      var trend = Array.isArray(s.revenue_trend) ? s.revenue_trend.map(Number).filter(isFinite) : [];

      body.innerHTML =
        '<div style="padding-bottom:12px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--mi-border);margin-bottom:14px">' +
          '<div style="font-size:12.5px;color:var(--mi-muted);display:flex;align-items:center;gap:6px">' +
            '<span>Cửa hàng</span> <span style="color:var(--mi-border-dark)">/</span> <span style="color:var(--mi-primary,#7c3aed);font-weight:700">Chi tiết cửa hàng</span>' +
          '</div>' +
        '</div>' +

        '<div class="mi-modal-shop-hdr" style="display:flex;gap:16px;margin-bottom:16px;background:#ffffff;padding:18px;border-radius:14px;border:1px solid var(--mi-border);align-items:center;justify-content:space-between;flex-wrap:wrap;box-shadow:0 2px 8px rgba(0,0,0,0.02)">' +
          '<div style="display:flex;gap:14px;align-items:center">' +
            renderShopAvatar(s.avatar_url, 'mi-avatar-lg', s.id) +
            '<div>' +
              '<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">' +
                '<h3 style="margin:0;font-size:18px;font-weight:800;color:var(--mi-text)">' + esc(s.name || '—') + '</h3>' +
              '</div>' +
              '<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">' +
                '<span class="' + (isBrand ? 'mi-sub-tag-brand' : 'mi-sub-tag-normal') + '" style="font-size:10.5px;font-weight:700">' + (isBrand ? 'THƯƠNG HIỆU' : 'CỬA HÀNG') + '</span>' +
                '<span class="mi-chip" style="background:#f1f5f9;color:#475569;font-size:11px">' + (s.is_mall ? '🏅 Shop Mall' : '🏪 Shop Bản Địa') + '</span>' +
                (s.primary_category_name ? '<span class="mi-chip mi-chip-cat" style="font-size:11px;font-weight:700">' + esc(s.primary_category_name) + '</span>' : '') +
                '<span class="mi-chip mi-chip-id" style="font-size:11px">ID: ' + esc(s.id) + '</span>' +
              '</div>' +
            '</div>' +
          '</div>' +
          '<div style="text-align:right;font-size:11.5px;color:var(--mi-muted)">' +
            (s.revenue_grouping_rate || s.revenue_growth_rate ? '<div style="margin-top:4px"><span class="mi-tag-growth" style="font-weight:700">Tăng trưởng: ' + esc(s.revenue_grouping_rate || s.revenue_growth_rate) + '</span></div>' : '') +
          '</div>' +
        '</div>' +

        '<div class="mi-quick-date-bar">' +
          '<div class="mi-quick-date-label">📅 Chu kỳ: 30 ngày gần nhất</div>' +
          '<button type="button" class="mi-date-pill">Hôm qua</button>' +
          '<button type="button" class="mi-date-pill">7 ngày</button>' +
          '<button type="button" class="mi-date-pill active">30 ngày</button>' +
          '<button type="button" class="mi-date-pill">90 ngày</button>' +
          '<button type="button" class="mi-date-pill">180 ngày</button>' +
          '<button type="button" class="mi-date-pill">365 ngày</button>' +
        '</div>' +

        '<div class="mi-modal-section" style="margin-bottom:16px">' +
          '<div class="mi-modal-section-hdr" style="margin-bottom:10px"><span class="mi-modal-section-title" style="font-size:13px;font-weight:800;color:var(--mi-text)">' + ICON.chart + ' CHỈ SỐ DOANH THU & KÊNH BÁN</span></div>' +
          '<div class="mi-kpi-grid mi-kpi-grid-4" style="margin-bottom:12px">' +
            kpiCard(ICON.money, 'DOANH THU GMV', s.revenue_formatted || formatMoney(s.revenue) || '—', 'Tổng doanh số toàn shop', 'mi-clr-emerald') +
            kpiCard(ICON.bag, 'TỰ VẬN HÀNH', s.self_promotion_revenue || (s.self_promotion_revenue_formatted || '—'), 'Kênh tự bán', 'mi-clr-cyan') +
            kpiCard(ICON.link, 'AFFILIATE KOC', s.affiliate_revenue || (s.affiliate_revenue_formatted || '—'), 'Tiếp thị liên kết', 'mi-clr-teal') +
            kpiCard(ICON.store, 'DOANH THU MALL', s.shopping_mall_revenue || (s.shopping_mall_revenue_formatted || '—'), 'Gian hàng Mall', 'mi-clr-purple') +
            kpiCard(ICON.bag, 'LƯỢT BÁN', fmtSale(sale), 'Tổng sản phẩm bán', 'mi-clr-rose') +
            kpiCard(ICON.fire, 'ĐƠN GIÁ BÌNH QUÂN', s.unit_price || '—', 'Giá TB / sản phẩm', 'mi-clr-orange') +
            kpiCard(ICON.users, 'KOC HỢP TÁC', fmtSale(s.cooperative_creators_count || s.creator_num || 0), 'Nhà sáng tạo gắn link', 'mi-clr-gold') +
            kpiCard(ICON.video, 'VIDEO PHÁT HÀNH', fmtSale(s.released_videos_count || s.video_num || 0), 'Video giới thiệu SP', 'mi-clr-purple') +
          '</div>' +
        '</div>' +

        '<div class="mi-deep-modal-wrap">' +
          '<div class="mi-deep-modal-nav">' +
            '<button type="button" class="mi-deep-nav-btn active" data-tab="overview">' + ICON.chart + ' Tổng quan</button>' +
            '<button type="button" class="mi-deep-nav-btn" data-tab="products">' + ICON.box + ' Sản phẩm</button>' +
            '<button type="button" class="mi-deep-nav-btn" data-tab="creators">' + ICON.users + ' Nhà sáng tạo KOC</button>' +
            '<button type="button" class="mi-deep-nav-btn" data-tab="videos">' + ICON.video + ' Video & QC</button>' +
            '<button type="button" class="mi-deep-nav-btn" data-tab="lives">' + ICON.live + ' Live Stream</button>' +
          '</div>' +
          '<div class="mi-deep-modal-content" id="miShopModalTabContent">' +
            renderShopModalOverview(s, topProds, trend, sale) +
          '</div>' +
        '</div>';

      bindShopModalEvents(modal, s);

    } catch (e) {
      body.innerHTML = errHtml(e.message);
    }
  }

  function renderShopModalOverview(s, topProds, trend, sale) {
    var selfRev = s.self_promotion_revenue || 0;
    var affRev = s.affiliate_revenue || 0;
    var mallRev = s.shopping_mall_revenue || 0;

    return '<div class="mi-strategy-box">' +
      '<div class="mi-strategy-hdr">' +
        '<span class="mi-strategy-title" style="font-size:13.5px;font-weight:800;color:var(--mi-text)">Cơ cấu doanh thu theo kênh bán</span>' +
      '</div>' +
      '<div class="mi-strategy-formula" style="margin-bottom:10px">' +
        '<span style="font-size:13px;font-weight:700;color:var(--mi-text)">Tổng doanh thu: <strong style="color:#059669;font-size:15px">' + esc(s.revenue_formatted || formatMoney(s.revenue) || '—') + '</strong></span>' +
      '</div>' +
      '<div class="mi-strategy-legend" style="display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:10px;margin-top:12px">' +
        '<div class="mi-legend-item" style="background:#f8fafc;padding:10px 14px;border-radius:10px;border:1px solid var(--mi-border)"><span class="mi-legend-dot mi-seg-blue"></span> <span>Tự vận hành: <strong>' + esc(s.self_promotion_revenue || '—') + '</strong></span></div>' +
        '<div class="mi-legend-item" style="background:#f8fafc;padding:10px 14px;border-radius:10px;border:1px solid var(--mi-border)"><span class="mi-legend-dot mi-seg-green"></span> <span>Affiliate KOC: <strong>' + esc(s.affiliate_revenue || '—') + '</strong></span></div>' +
        '<div class="mi-legend-item" style="background:#f8fafc;padding:10px 14px;border-radius:10px;border:1px solid var(--mi-border)"><span class="mi-legend-dot mi-seg-purple"></span> <span>Gian hàng Mall: <strong>' + esc(s.shopping_mall_revenue || '—') + '</strong></span></div>' +
      '</div>' +
    '</div>' +
    (topProds.length ?
      '<div class="mi-modal-section" style="margin-top:16px">' +
        '<div class="mi-modal-section-hdr" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">' +
          '<span class="mi-modal-section-title" style="font-size:13px;font-weight:800;color:var(--mi-text)">' + ICON.fire + ' SẢN PHẨM BÁN CHẠY NHẤT TẠI SHOP (' + topProds.length + ')</span>' +
        '</div>' +
        '<div class="mi-related-grid">' +
        topProds.map(function (tp) {
          return '<div class="mi-related-card mi-row-link" data-pid="' + esc(tp.id) + '">' +
            renderProductThumb(tp.image_url, 'mi-rel-img', tp.id) +
            '<div class="mi-rel-info">' +
              '<div class="mi-rel-name" title="' + esc(tp.title) + '">' + esc(tp.title || '—') + '</div>' +
              '<div class="mi-rel-price">' + esc(tp.price_formatted || tp.min_real_price || tp.unit_price || '—') + '</div>' +
              '<div class="mi-rel-rev">' + ICON.money + ' ' + esc(tp.revenue_formatted || (tp.revenue ? formatMoney(tp.revenue) : '—')) + '</div>' +
            '</div>' +
          '</div>';
        }).join('') +
        '</div>' +
      '</div>' : '');
  }

  function bindShopModalEvents(modal, s) {
    var tabBtns = modal.querySelectorAll('.mi-deep-nav-btn');
    var container = modal.querySelector('#miShopModalTabContent');

    tabBtns.forEach(function (btn) {
      btn.addEventListener('click', async function () {
        tabBtns.forEach(function (b) { b.classList.remove('active'); });
        btn.classList.add('active');
        var tabKey = btn.dataset.tab;

        if (tabKey === 'overview') {
          var topProds = s.top_products_detail || [];
          var trend = Array.isArray(s.revenue_trend) ? s.revenue_trend.map(Number).filter(isFinite) : [];
          var sale = s.sale_display || s.sale || '—';
          container.innerHTML = renderShopModalOverview(s, topProds, trend, sale);
          bindProductCardClicks(modal);
        } else if (tabKey === 'products') {
          container.innerHTML = loadingHtml();
          try {
            var resP = await api('/api/v1/market/shops/' + encodeURIComponent(s.id) + '/products?limit=50');
            var prods = (resP.success && resP.data && resP.data.list) || [];
            if (!prods.length) {
              container.innerHTML = '<div class="mi-empty">' + ICON.box + '<span>Chưa có sản phẩm nào được lưu trữ cho shop này</span></div>';
            } else {
              container.innerHTML =
                '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                  '<div style="font-size:13px;font-weight:700;color:#0f172a">Sản phẩm (' + prods.length + ' sản phẩm)</div>' +
                  '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm" style="font-size:11.5px;gap:4px">' + ICON.download + ' Tải xuống</button>' +
                '</div>' +
                '<div class="mi-table-wrap"><table class="mi-table"><thead><tr>' +
                '<th>SẢN PHẨM</th>' +
                '<th class="mi-th-r">ĐƠN GIÁ</th>' +
                '<th class="mi-th-r">LƯỢT BÁN</th>' +
                '<th class="mi-th-r">DOANH THU</th>' +
                '<th style="width:36px"></th>' +
              '</tr></thead><tbody>' +
              prods.map(function (p, i) {
                return '<tr class="mi-row-link" data-pid="' + esc(p.id) + '">' +
                  '<td>' +
                    '<div class="mi-prod-row-box">' +
                      renderProductThumb(p.image_url, '', p.id) +
                      '<div class="mi-prod-info-col">' +
                        '<div class="mi-prod-title-line">' + esc(p.title || p.product_title || '—') + '</div>' +
                        '<div class="mi-prod-meta-line"><span class="mi-chip mi-chip-id">ID: ' + esc(p.id) + '</span></div>' +
                      '</div>' +
                    '</div>' +
                  '</td>' +
                  '<td class="mi-td-r">' + esc(p.unit_price || '—') + '</td>' +
                  '<td class="mi-td-r">' + fmtSale(p.sale) + '</td>' +
                  '<td class="mi-td-r mi-fw mi-clr-emerald">' + (p.revenue_formatted || formatMoney(p.revenue)) + '</td>' +
                  '<td class="mi-td-c mi-clr-muted">' + ICON.chevronRight + '</td>' +
                '</tr>';
              }).join('') + '</tbody></table></div>';
              bindProductCardClicks(modal);
            }
          } catch (e) { container.innerHTML = errHtml(e.message); }
        } else if (tabKey === 'creators') {
          container.innerHTML = loadingHtml();
          try {
            var resC = await api('/api/v1/market/shops/' + encodeURIComponent(s.id) + '/creators?limit=50');
            var creators = (resC.success && resC.data && resC.data.list) || [];
            if (!creators.length) {
              container.innerHTML = '<div class="mi-empty">' + ICON.users + '<span>Chưa có danh sách KOC hợp tác ghi nhận</span></div>';
            } else {
              container.innerHTML =
                '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                  '<div style="display:flex;gap:8px;align-items:center">' +
                    '<div style="font-size:13px;font-weight:700;color:#0f172a">Nhà sáng tạo (' + creators.length + ' KOC)</div>' +
                    '<select class="mi-select" style="padding:2px 8px;font-size:11px"><option>Loại tài khoản</option><option>Affiliate</option><option>Tự vận hành</option></select>' +
                  '</div>' +
                  '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm" style="font-size:11.5px;gap:4px">' + ICON.download + ' Tải xuống</button>' +
                '</div>' +
                '<div class="mi-table-wrap"><table class="mi-table"><thead><tr>' +
                '<th>THÔNG TIN NHÀ SÁNG TẠO</th>' +
                '<th>LOẠI TÀI KHOẢN</th>' +
                '<th class="mi-th-r">DOANH THU</th>' +
                '<th class="mi-th-r">SẢN PHẨM</th>' +
                '<th class="mi-th-r">LIVE</th>' +
                '<th class="mi-th-r">VIDEO</th>' +
              '</tr></thead><tbody>' +
              creators.map(function (c, i) {
                var isAff = (c.creator_type || '').toUpperCase() === 'AFFILIATE';
                return '<tr>' +
                  '<td>' +
                    '<div class="mi-creator-row-box">' +
                      renderShopAvatar(c.avatar_url, 'mi-creator-avatar', c.id) +
                      '<div class="mi-creator-info-col">' +
                        '<div class="mi-creator-name-line"><span class="mi-creator-nickname">@' + esc(c.handle || c.nickname || '—') + '</span></div>' +
                        '<div class="mi-creator-handle-line">' + esc(c.nickname || '') + '</div>' +
                      '</div>' +
                    '</div>' +
                  '</td>' +
                  '<td><span class="mi-chip ' + (isAff ? 'mi-chip-cat' : 'mi-badge-warn') + '">' + (isAff ? 'Affiliate' : 'Tự vận hành') + '</span></td>' +
                  '<td class="mi-td-r mi-fw mi-clr-emerald">' + (c.revenue_formatted || formatMoney(c.revenue)) + '</td>' +
                  '<td class="mi-td-r">' + (c.product_count || 3) + '</td>' +
                  '<td class="mi-td-r">' + (c.live_count || 0) + '</td>' +
                  '<td class="mi-td-r">' + (c.video_count || 0) + '</td>' +
                '</tr>';
              }).join('') + '</tbody></table></div>';
            }
          } catch (e) { container.innerHTML = errHtml(e.message); }
        } else if (tabKey === 'videos') {
          container.innerHTML = loadingHtml();
          try {
            var resV = await api('/api/v1/market/shops/' + encodeURIComponent(s.id) + '/videos?limit=50');
            var videos = (resV.success && resV.data && resV.data.list) || [];
            if (!videos.length) {
              container.innerHTML = '<div class="mi-empty">' + ICON.video + '<span>Chưa có video ngắn bán hàng nào được lưu trữ</span></div>';
            } else {
              container.innerHTML =
                '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                  '<div style="display:flex;gap:8px;align-items:center">' +
                    '<div style="font-size:13px;font-weight:700;color:#0f172a">Video & Quảng cáo (' + videos.length + ' video)</div>' +
                    '<select class="mi-select" style="padding:2px 8px;font-size:11px"><option>Sản phẩm gắn kèm</option></select>' +
                  '</div>' +
                  '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm" style="font-size:11.5px;gap:4px">' + ICON.download + ' Tải xuống</button>' +
                '</div>' +
                '<div class="mi-table-wrap"><table class="mi-table"><thead><tr>' +
                '<th>NỘI DUNG VIDEO</th>' +
                '<th>SẢN PHẨM</th>' +
                '<th class="mi-th-r">DOANH THU</th>' +
                '<th class="mi-th-r">LƯỢT XEM</th>' +
                '<th class="mi-th-r">LƯỢT BÁN</th>' +
                '<th class="mi-th-r">NGÀY ĐĂNG TẢI</th>' +
                '<th style="width:36px"></th>' +
              '</tr></thead><tbody>' +
              videos.map(function (v, i) {
                var _svid = vidId(v);
                var vCover = v.cover_url || v.cover || v.image_url || v.thumbnail_url || '';
                var pImg = v.product_image || (s.top_products_detail && s.top_products_detail[0]?.image_url) || '';
                return '<tr class="mi-row-link mi-shop-video-row" data-idx="' + i + '">' +
                  '<td>' +
                    '<div style="display:flex;gap:10px;align-items:center">' +
                      '<div class="mi-video-thumb-box" style="width:42px;height:56px;border-radius:6px;flex-shrink:0">' +
                        (vCover ? '<img src="' + esc(vCover) + '" onerror="this.style.display=\'none\'" alt="Video">' : '') +
                        '<span class="mi-video-play-icon">' + ICON.play + '</span>' +
                      '</div>' +
                      '<div style="overflow:hidden;flex:1">' +
                        '<div class="mi-video-desc-line" title="' + esc(v.description) + '">' + esc(v.description || 'Video bán hàng') + '</div>' +
                        '<div class="mi-video-meta-line">' +
                          (v.handle ? '<span class="mi-chip mi-chip-cat">@' + esc(v.handle) + '</span>' : '') +
                          (v.ad ? '<span class="mi-badge mi-badge-on">ADS</span>' : '') +
                        '</div>' +
                      '</div>' +
                    '</div>' +
                  '</td>' +
                  '<td>' +
                    (pImg ? '<div style="width:36px;height:36px;border-radius:6px;overflow:hidden;border:1px solid var(--mi-border)"><img src="' + esc(pImg) + '" style="width:100%;height:100%;object-fit:cover"></div>' : '—') +
                  '</td>' +
                  '<td class="mi-td-r mi-fw mi-clr-emerald">' + (v.revenue_formatted || formatMoney(v.revenue)) + '</td>' +
                  '<td class="mi-td-r">' + fmtSale(v.views) + '</td>' +
                  '<td class="mi-td-r"><span class="mi-tag-count">' + fmtSale(v.sale) + '</span></td>' +
                  '<td class="mi-td-r" style="font-size:11.5px;color:var(--mi-muted)">' + esc((v.create_time || '').split(' ')[0] || '—') + '</td>' +
                  '<td class="mi-td-c mi-clr-muted">' + ICON.chevronRight + '</td>' +
                '</tr>';
              }).join('') + '</tbody></table></div>';

            container.querySelectorAll('.mi-shop-video-row').forEach(function (row) {
              row.addEventListener('click', function () {
                var idx = Number(row.dataset.idx);
                var vObj = videos[idx];
                if (vObj) openVideoModal(vObj, s.name);
              });
            });
            }
          } catch (e) { container.innerHTML = errHtml(e.message); }
        } else if (tabKey === 'lives') {
          container.innerHTML = loadingHtml();
          try {
            var resL = await api('/api/v1/market/shops/' + encodeURIComponent(s.id) + '/lives?limit=50');
            var lives = (resL.success && resL.data && resL.data.list) || [];
            if (!lives.length) {
              container.innerHTML = '<div class="mi-empty">' + ICON.live + '<span>Chưa có phiên livestream nào được ghi nhận</span></div>';
            } else {
              container.innerHTML =
                '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">' +
                  '<div style="font-size:13px;font-weight:700;color:#0f172a">Phiên Livestream (' + lives.length + ' phiên)</div>' +
                  '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm" style="font-size:11.5px;gap:4px">' + ICON.download + ' Tải xuống</button>' +
                '</div>' +
                '<div class="mi-table-wrap"><table class="mi-table"><thead><tr>' +
                '<th>ẢNH BÌA & PHIÊN LIVE</th>' +
                '<th class="mi-th-r">GPM</th>' +
                '<th class="mi-th-r">LƯỢT XEM</th>' +
                '<th class="mi-th-r">LƯỢT BÁN</th>' +
                '<th class="mi-th-r">DOANH THU</th>' +
                '<th class="mi-th-r">THỜI LƯỢNG</th>' +
              '</tr></thead><tbody>' +
              lives.map(function (l, i) {
                var screenshot = l.screenshot_url || l.cover_url || '';
                return '<tr class="mi-row-link mi-shop-live-row" data-idx="' + i + '">' +
                  '<td>' +
                    '<div class="mi-live-cell-box">' +
                      (screenshot ? '<img class="mi-live-thumb-img" src="' + esc(screenshot) + '" alt="Live" loading="lazy" referrerpolicy="no-referrer" onerror="this.onerror=null;this.style.display=\'none\'" />' : '') +
                      '<div class="mi-live-info-col">' +
                        '<div class="mi-live-title" title="' + esc(l.title) + '">' + esc(l.title || 'Phiên Livestream') + '</div>' +
                        '<div class="mi-live-sub">' + (l.handle ? '@' + esc(l.handle) : '') + (l.product_count ? ' · ' + l.product_count + ' sản phẩm ghim' : '') + '</div>' +
                      '</div>' +
                    '</div>' +
                  '</td>' +
                  '<td class="mi-td-r"><span class="mi-chip mi-chip-cat">' + esc(l.gpm || '—') + '</span></td>' +
                  '<td class="mi-td-r">' + fmtSale(l.views) + '</td>' +
                  '<td class="mi-td-r">' + fmtSale(l.sale) + '</td>' +
                  '<td class="mi-td-r mi-fw mi-clr-rose">' + (l.revenue_formatted || formatMoney(l.revenue)) + '</td>' +
                  '<td class="mi-td-r">' + esc(l.duration || '—') + '</td>' +
                '</tr>';
              }).join('') + '</tbody></table></div>';

              container.querySelectorAll('.mi-shop-live-row').forEach(function(row) {
                row.addEventListener('click', function() {
                  var idx = Number(row.dataset.idx);
                  var lObj = lives[idx];
                  if (lObj) openVideoModal(lObj, s.name);
                });
              });
            }
          } catch (e) { container.innerHTML = errHtml(e.message); }
        }
      });
    });

    bindProductCardClicks(modal);
  }

  function bindProductCardClicks(modal) {
    modal.querySelectorAll('.mi-related-card[data-pid], tr[data-pid]').forEach(function (card) {
      card.addEventListener('click', function () {
        modal.remove();
        openProductModal(card.dataset.pid);
      });
    });
  }

  /* ══════════════════════════════════════════════════════════════
   *  DISTRIBUTED marketBOOST KOC CRAWLER (VIEW & MODAL)
   * ══════════════════════════════════════════════════════════════ */
  function renderCrawlerInto(container) {
    if (!container) return;
    container.innerHTML = loadingHtml();
    var refreshTimer = null;

    function scheduleCrawlerRefresh(delay) {
      clearTimeout(refreshTimer);
      refreshTimer = setTimeout(function () {
        if (!container.isConnected) return;
        var focused = document.activeElement;
        if (focused && container.contains(focused) && /^(INPUT|TEXTAREA|SELECT)$/.test(focused.tagName)) {
          scheduleCrawlerRefresh(3000);
          return;
        }
        loadCrawlerData();
      }, delay || 5000);
    }

    async function loadCrawlerData() {
      try {
        var [resSummary, resAccounts] = await Promise.all([
          api('/api/v1/market/crawler/status'),
          api('/api/v1/market/crawler/accounts')
        ]);
        var data = (resSummary && resSummary.data) || {};
        var cfg = data.config || {};
        var stats = data.stats || {};
        var accounts = (resAccounts && resAccounts.data) || [];
        var logs = data.recentLogs || [];
        var runtime = data.runtime || {};

        renderCrawlerBody(container, cfg, stats, accounts, logs, runtime, loadCrawlerData);
        scheduleCrawlerRefresh(cfg.is_running || runtime.activeFetchers || stats.queue?.outstanding ? 3000 : 10000);
      } catch (err) {
        container.innerHTML = errHtml('Không thể nạp trạng thái cào: ' + err.message);
        scheduleCrawlerRefresh(10000);
      }
    }

    loadCrawlerData();
  }

  /* ── REUSABLE MULTI-ACCOUNT MANAGER (SINGLE + BULK + TOGGLE) ── */
  function buildAccountManagerHtml(accounts, prefix) {
    prefix = prefix || "miAcc";
    return '<div class="mi-modal-section mi-account-manager-section">' +
      '<div class="mi-modal-section-hdr" style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px">' +
        '<span class="mi-modal-section-title">' + ICON.users + ' DANH SÁCH TÀI KHOẢN KHAI THÁC (' + accounts.length + ')</span>' +
        '<div class="mi-pill-tabs">' +
          '<button type="button" class="mi-pill-btn active" id="' + prefix + 'TabSingle">Thêm 1 tài khoản</button>' +
          '<button type="button" class="mi-pill-btn" id="' + prefix + 'TabBulk">Nhập hàng loạt</button>' +
        '</div>' +
      '</div>' +

      '<form id="' + prefix + 'AddSingleForm" class="mi-account-add-box">' +
        '<input class="mi-form-input" id="' + prefix + 'Phone" type="text" autocomplete="username" placeholder="Số điện thoại đăng nhập" required />' +
        '<input class="mi-form-input" id="' + prefix + 'Pass" type="password" autocomplete="current-password" placeholder="Mật khẩu tài khoản" required />' +
        '<button type="submit" class="mi-btn mi-btn-primary mi-btn-sm">' + ICON.users + ' Thêm tài khoản</button>' +
      '</form>' +

      '<form id="' + prefix + 'AddBulkForm" class="mi-bulk-box" style="display:none">' +
        '<div style="font-size:11.5px;color:var(--mi-muted)">Định dạng: <code>TàiKhoản|MậtKhẩu</code>. Hệ thống tự chia chunk không trùng cho mọi tài khoản đang bật.</div>' +
        '<textarea class="mi-bulk-textarea" id="' + prefix + 'BulkText" placeholder="0901234567|MatKhau1&#10;0987654321|MatKhau2"></textarea>' +
        '<div style="display:flex;justify-content:flex-end">' +
          '<button type="submit" class="mi-btn mi-btn-primary mi-btn-sm" id="' + prefix + 'BulkSubmit">' + ICON.check + ' Nạp danh sách tài khoản</button>' +
        '</div>' +
      '</form>' +

      '<div class="mi-table-wrap mi-account-table-wrap" style="margin-top:10px">' +
        '<table class="mi-table mi-account-table">' +
          '<thead><tr>' +
            '<th>TÀI KHOẢN</th>' +
            '<th style="width:90px;text-align:center">BẬT / TẮT</th>' +
            '<th>TRẠNG THÁI</th>' +
            '<th>CHECKPOINT TỰ ĐỘNG</th>' +
            '<th>LẦN ĐĂNG NHẬP</th>' +
            '<th style="width:130px;text-align:right">THAO TÁC</th>' +
          '</tr></thead>' +
          '<tbody>' +
          (accounts.length ? accounts.map(function (a) {
            var checkpoint = Number(a.last_checkpoint_page || 0);
            var nextPage = a.chunk_current ? Number(a.chunk_current) : (checkpoint + 1);
            var checkpointText = a.chunk_start
              ? ('Trang kế tiếp ' + nextPage + ' · chunk ' + a.chunk_start + '–' + a.chunk_end)
              : (checkpoint ? ('Tiếp tục từ trang ' + (checkpoint + 1)) : 'Chưa bắt đầu quét');
            var isLocked = a.status === "error" || (a.last_error && a.last_error.toLowerCase().includes("khóa"));
            var statusBadge = !a.is_active ? '<span class="mi-badge mi-badge-off">TẠM TẮT</span>' :
                              (isLocked ? '<span class="mi-badge mi-badge-dn" title="' + esc(a.last_error || 'Tài khoản cần đăng nhập lại') + '">LỖI PHIÊN</span>' :
                              ('<span class="mi-badge mi-badge-on">' + (a.status === 'running' ? 'ĐANG CÀO' : 'HOẠT ĐỘNG') + '</span>'));
            return '<tr>' +
              '<td><strong class="mi-account-name" title="' + esc(a.phone) + '">' + esc(a.phone) + '</strong>' + (a.last_error ? ('<div class="mi-account-error" title="' + esc(a.last_error) + '">' + esc(a.last_error) + '</div>') : '') + '</td>' +
              '<td style="text-align:center">' +
                '<label class="mi-switch-label" title="' + (a.is_active ? 'Nhấn để tạm tắt tài khoản' : 'Nhấn để bật tài khoản') + '">' +
                  '<input type="checkbox" class="mi-switch-input ' + prefix + '-toggle-acc" data-id="' + a.id + '"' + (a.is_active ? ' checked' : '') + ' />' +
                  '<span class="mi-switch-slider"></span>' +
                '</label>' +
              '</td>' +
              '<td>' + statusBadge + '</td>' +
              '<td><strong class="mi-account-checkpoint">' + esc(checkpointText) + '</strong><small class="mi-account-auto-note">Tự nhận chunk bằng khóa PostgreSQL, không quét trùng.</small></td>' +
              '<td><span class="mi-clr-muted">' + (a.last_login_at ? new Date(a.last_login_at).toLocaleTimeString("vi-VN") : "Chưa") + '</span></td>' +
              '<td style="text-align:right;white-space:nowrap">' +
                '<button class="mi-btn mi-btn-secondary mi-btn-xs ' + prefix + '-login-acc" data-id="' + a.id + '" style="margin-right:6px" title="Đăng nhập & kiểm tra phiên">' + (a.status === 'error' ? 'Nhập lại mật khẩu' : 'Kiểm tra đăng nhập') + '</button>' +
                '<button class="mi-btn-icon-danger ' + prefix + '-del-acc" data-id="' + a.id + '" title="Xóa tài khoản">' + ICON.trash + '</button>' +
              '</td>' +
            '</tr>';
          }).join('') : '<tr><td colspan="6" class="mi-td-c mi-clr-muted">Chưa có tài khoản nào. Vui lòng thêm tài khoản để kích hoạt.</td></tr>') +
          '</tbody>' +
        '</table>' +
      '</div>' +
    '</div>';
  }

  function openmarketAccountRelogin(account, refreshFn) {
    var modal = createModal('Đăng nhập lại tài khoản', 520);
    var body = modal.querySelector('#miModalBody');
    body.innerHTML =
      '<form class="mi-form" id="mimarketReloginForm">' +
        '<div class="mi-form-note">Phiên làm việc hiện tại đã hết hạn. Nhập lại mật khẩu để cập nhật an toàn rồi kiểm tra đăng nhập.</div>' +
        '<div class="mi-form-group"><label class="mi-form-label">Tài khoản</label><input class="mi-form-input" type="text" autocomplete="username" value="' + esc(account.phone) + '" readonly></div>' +
        '<div class="mi-form-group"><label class="mi-form-label">Mật khẩu tài khoản</label><input class="mi-form-input" id="mimarketReloginPassword" type="password" autocomplete="current-password" required autofocus></div>' +
        '<div class="mi-login-feedback" id="mimarketReloginFeedback" aria-live="polite"></div>' +
        '<div class="mi-form-actions"><button type="button" class="mi-btn mi-btn-ghost" id="mimarketReloginCancel">Hủy</button><button type="submit" class="mi-btn mi-btn-primary" id="mimarketReloginSubmit">Cập nhật & đăng nhập</button></div>' +
      '</form>';
    body.querySelector('#mimarketReloginCancel').addEventListener('click', function () { modal.remove(); });
    body.querySelector('#mimarketReloginForm').addEventListener('submit', async function (event) {
      event.preventDefault();
      var submit = body.querySelector('#mimarketReloginSubmit');
      var feedback = body.querySelector('#mimarketReloginFeedback');
      var password = body.querySelector('#mimarketReloginPassword').value;
      submit.disabled = true;
      submit.textContent = 'Đang đăng nhập...';
      feedback.className = 'mi-login-feedback';
      feedback.textContent = '';
      var updated = await api('/api/v1/market/crawler/accounts', {
        method: 'POST',
        body: JSON.stringify({
          phone: account.phone,
          password: password,
          is_active: account.is_active !== false
        })
      });
      if (!updated || !updated.success) {
        feedback.className = 'mi-login-feedback error';
        feedback.textContent = (updated && (updated.msg || updated.error)) || 'Không cập nhật được tài khoản.';
        submit.disabled = false;
        submit.textContent = 'Thử lại';
        return;
      }
      var result = await api('/api/v1/market/crawler/accounts/' + account.id + '/login', { method: 'POST' });
      if (result && result.success) {
        feedback.className = 'mi-login-feedback success';
        feedback.textContent = 'Đăng nhập tài khoản thành công.';
        setTimeout(function () { modal.remove(); refreshFn(); }, 500);
        return;
      }
      feedback.className = 'mi-login-feedback error';
      feedback.textContent = (result && (result.msg || result.error)) || 'Máy chủ từ chối đăng nhập.';
      submit.disabled = false;
      submit.textContent = 'Thử lại';
    });
    return modal;
  }

  function bindAccountManagerEvents(container, prefix, refreshFn, accounts) {
    prefix = prefix || "miAcc";
    var tabSingle = container.querySelector('#' + prefix + 'TabSingle');
    var tabBulk = container.querySelector('#' + prefix + 'TabBulk');
    var formSingle = container.querySelector('#' + prefix + 'AddSingleForm');
    var formBulk = container.querySelector('#' + prefix + 'AddBulkForm');

    if (tabSingle && tabBulk && formSingle && formBulk) {
      tabSingle.addEventListener('click', function () {
        tabSingle.classList.add('active');
        tabBulk.classList.remove('active');
        formSingle.style.display = '';
        formBulk.style.display = 'none';
      });
      tabBulk.addEventListener('click', function () {
        tabBulk.classList.add('active');
        tabSingle.classList.remove('active');
        formBulk.style.display = '';
        formSingle.style.display = 'none';
      });
    }

    if (formSingle) {
      formSingle.addEventListener('submit', async function (e) {
        e.preventDefault();
        var phone = container.querySelector('#' + prefix + 'Phone').value.trim();
        var password = container.querySelector('#' + prefix + 'Pass').value.trim();
        if (!phone || !password) return;
        await api('/api/v1/market/crawler/accounts', {
          method: 'POST',
          body: JSON.stringify({ phone: phone, password: password, is_active: true })
        });
        refreshFn();
      });
    }

    if (formBulk) {
      formBulk.addEventListener('submit', async function (e) {
        e.preventDefault();
        var rawText = container.querySelector('#' + prefix + 'BulkText').value.trim();
        if (!rawText) return;
        var btn = container.querySelector('#' + prefix + 'BulkSubmit');
        btn.disabled = true;
        btn.textContent = 'Đang nạp...';
        var res = await api('/api/v1/market/crawler/accounts/bulk', {
          method: 'POST',
          body: JSON.stringify({ rawText: rawText })
        });
        if (res && res.success) {
          alert('Đã nạp thành công ' + (res.data?.total || 0) + ' tài khoản!');
        }
        refreshFn();
      });
    }

    container.querySelectorAll('.' + prefix + '-toggle-acc').forEach(function (chk) {
      chk.addEventListener('change', async function () {
        var id = chk.dataset.id;
        var isActive = chk.checked;
        await api('/api/v1/market/crawler/accounts/' + id + '/toggle', {
          method: 'POST',
          body: JSON.stringify({ is_active: isActive })
        });
        refreshFn();
      });
    });

    container.querySelectorAll('.' + prefix + '-login-acc').forEach(function (btn) {
      btn.addEventListener('click', async function () {
        var account = (accounts || []).find(function (item) { return String(item.id) === String(btn.dataset.id); });
        if (!account) return;
        if (account.status === 'error') {
          openmarketAccountRelogin(account, refreshFn);
          return;
        }
        btn.disabled = true;
        btn.innerText = 'Đang kết nối...';
        var res = await api('/api/v1/market/crawler/accounts/' + btn.dataset.id + '/login', { method: 'POST' });
        if (res && res.success) {
          alert('Đăng nhập tài khoản thành công!');
        } else {
          account.last_error = (res && (res.msg || res.error)) || 'Từ chối đăng nhập.';
          account.status = 'error';
          openmarketAccountRelogin(account, refreshFn);
        }
        refreshFn();
      });
    });

    container.querySelectorAll('.' + prefix + '-del-acc').forEach(function (btn) {
      btn.addEventListener('click', async function () {
        if (!confirm('Xóa tài khoản này khỏi danh sách?')) return;
        await api('/api/v1/market/crawler/accounts/' + btn.dataset.id, { method: 'DELETE' });
        refreshFn();
      });
    });
  }

  // 3. Multi-Account Management Section in Crawler
  function renderCrawlerBody(el, cfg, stats, accounts, logs, runtime, refreshFn) {
    runtime = runtime || {};
    var isRequested = !!cfg.is_running;
    var totalTarget = stats.totalTarget || 35000;
    var curPage = stats.pagesFetched || 0;
    var pct = stats.progressPercent || 0;
    var totalKoc = stats.lifetimeSavedKocs || stats.savedKocs || stats.totalKocsCrawled || 0;
    var queue = stats.queue || {};
    var activeFetchers = Number(runtime.activeFetchers || 0);
    var activeWriters = Number(runtime.activeWriters || 0);
    var actualRunning = activeFetchers > 0 || Number(stats.runningChunks || 0) > 0;
    var draining = Number(queue.outstanding || 0) > 0 || Number(runtime.ramQueueDepth || 0) > 0;
    var failedAccounts = accounts.filter(function (account) { return account.is_active !== false && account.status === 'error'; });
    var runMode = cfg.run_mode === 'refresh' ? 'refresh' : 'resume';
    var stateClass = actualRunning ? 'running' : (isRequested ? 'warning' : (draining ? 'writing' : 'idle'));
    var stateText = actualRunning ? ('Đang lấy dữ liệu · ' + activeFetchers + ' tài khoản') :
      (isRequested ? 'Đã bật nhưng chưa có worker lấy dữ liệu' : (draining ? 'Đang ghi nốt hàng đợi' : 'Đang tạm nghỉ'));
    var stateDetail = actualRunning
      ? ('Máy chủ đang nạp dữ liệu; ' + activeWriters + ' worker PostgreSQL đang sẵn sàng ghi nền.')
      : (isRequested
        ? (failedAccounts.length ? (failedAccounts.length + ' tài khoản đang lỗi phiên. Hãy bấm Đăng nhập ở bảng bên dưới.') : 'Hệ thống đang khởi tạo hoặc chờ tài khoản khả dụng.')
        : (draining ? 'Không lấy trang mới; dữ liệu đã lấy vẫn đang được ghi an toàn.' : 'Không có tiến trình lấy trang đang hoạt động.'));
    var configOpen = !!el.querySelector('.mi-crawler-config[open]');

    el.innerHTML =
      '<div class="mi-crawler-panel" style="padding: 18px 20px;">' +
        // 1. Header Metrics Card
        '<div class="mi-crawler-hero">' +
          '<div class="mi-crawler-hero-main">' +
            '<div class="mi-crawler-hero-stat">' +
              '<span class="mi-crawler-stat-label">TIẾN ĐỘ KHAI THÁC HIỆN TẠI</span>' +
              '<div class="mi-crawler-stat-val mi-clr-emerald">' + curPage.toLocaleString('vi-VN') + ' / ' + totalTarget.toLocaleString('vi-VN') + ' <span class="mi-crawler-pct">(' + pct + '%)</span></div>' +
            '</div>' +
            '<div class="mi-crawler-hero-stat">' +
              '<span class="mi-crawler-stat-label">KOC ĐÃ GHI QUA CRAWLER</span>' +
              '<div class="mi-crawler-stat-val mi-clr-cyan">' + totalKoc.toLocaleString('vi-VN') + ' KOC</div>' +
            '</div>' +
            '<div class="mi-crawler-hero-stat">' +
              '<span class="mi-crawler-stat-label">TRẠNG THÁI KHỐI TRANG</span>' +
              '<div class="mi-crawler-stat-val mi-clr-gold">' + (stats.completedChunks || 0) + ' xong · ' + (stats.runningChunks || 0) + ' đang lấy · ' + (stats.pendingChunks || 0) + ' chờ</div>' +
            '</div>' +
            '<div class="mi-crawler-hero-stat">' +
              '<span class="mi-crawler-stat-label">HÀNG ĐỢI GHI DATABASE</span>' +
              '<div class="mi-crawler-stat-val mi-clr-purple">' + (queue.outstanding || 0) + ' / ' + (queue.capacity || cfg.queue_capacity || 20) + ' trang · ' + activeWriters + ' worker <span class="mi-crawler-pct">(' + (queue.failed || 0) + ' lỗi)</span></div>' +
            '</div>' +
          '</div>' +

          '<div class="mi-crawler-progress-bar-wrap">' +
            '<div class="mi-crawler-progress-bar" style="width:' + Math.max(0, pct) + '%"></div>' +
          '</div>' +

          '<div class="mi-crawler-state mi-crawler-state-' + stateClass + '">' +
            '<strong>' + stateText + '</strong><span>' + stateDetail + '</span>' +
          '</div>' +

          '<div class="mi-crawler-actions-bar">' +
            (!isRequested && !actualRunning ?
              '<button class="mi-btn mi-btn-primary" id="miBtnStartCrawler">' + ICON.play + ' Quét tiếp từ checkpoint</button>' :
              '<button class="mi-btn mi-btn-danger" id="miBtnStopCrawler">' + ICON.pause + ' Tạm dừng cào</button>') +
            '<button class="mi-btn mi-btn-ghost" id="miBtnRefreshCrawler" title="Tạo lượt cập nhật mới và chạy lại từ trang 1">' + ICON.refresh + ' Cập nhật từ trang 1</button>' +
            '<span class="mi-crawler-status-pill ' + stateClass + '">' +
              '<span class="mi-pulsar-dot"></span> ' + (runMode === 'refresh' ? 'Cập nhật toàn bộ' : 'Quét tiếp') + ' · ' + stateText +
            '</span>' +
          '</div>' +
        '</div>' +

        // 2. Schedule & Config Form
        '<details class="mi-modal-section mi-crawler-config"' + (configOpen ? ' open' : '') + '>' +
          '<summary class="mi-modal-section-hdr mi-crawler-config-summary" style="display:flex;align-items:center;justify-content:space-between;gap:12px;cursor:pointer">' +
            '<div style="display:flex;flex-direction:column;gap:3px;min-width:0;flex:1">' +
              '<span class="mi-modal-section-title" style="font-size:12px;font-weight:800;letter-spacing:0.3px;color:var(--mi-text)">' + ICON.calendar + ' CẤU HÌNH KHUNG GIỜ VÀ TỐC ĐỘ CÀO</span>' +
              '<span class="mi-crawler-config-brief" style="color:var(--mi-muted);font-size:11px;font-weight:600">' + (cfg.start_hour ?? 1) + '–' + (cfg.end_hour ?? 6) + ' giờ · ' + (cfg.page_size || 100) + ' KOC/trang · ' + (cfg.requests_per_minute || 30) + ' request/phút</span>' +
            '</div>' +
          '</summary>' +
          '<form id="miCrawlerConfigForm" class="mi-form-grid-3">' +
            '<div class="mi-form-group">' +
              '<label class="mi-form-label">Khung giờ cào tự động</label>' +
              '<div style="display:flex;gap:6px;align-items:center">' +
                '<input class="mi-form-input" id="miCfgStart" type="number" min="0" max="23" value="' + (cfg.start_hour ?? 1) + '" title="Giờ bắt đầu" />' +
                '<span>đến</span>' +
                '<input class="mi-form-input" id="miCfgEnd" type="number" min="0" max="23" value="' + (cfg.end_hour ?? 6) + '" title="Giờ kết thúc" />' +
                '<span>giờ</span>' +
              '</div>' +
            '</div>' +

            '<div class="mi-form-group">' +
              '<label class="mi-form-label">Độ trễ giữa các trang</label>' +
              '<input class="mi-form-input" id="miCfgDelay" type="number" min="500" step="500" value="' + (cfg.delay_ms || 2000) + '" />' +
              '<span class="mi-form-hint">Mặc định 2000ms (2 giây)</span>' +
            '</div>' +

            '<div class="mi-form-group">' +
              '<label class="mi-form-label">Phân chia khối (Trang / TK)</label>' +
              '<input class="mi-form-input" id="miCfgChunk" type="number" min="100" step="500" value="' + (cfg.chunk_size || 1000) + '" />' +
              '<span class="mi-form-hint">Mặc định 1.000 trang / block</span>' +
            '</div>' +

            '<div class="mi-form-group">' +
              '<label class="mi-form-label">KOC mỗi trang</label>' +
              '<input class="mi-form-input" id="miCfgPageSize" type="number" min="10" max="100" value="' + (cfg.page_size || 100) + '" />' +
              '<span class="mi-form-hint">Hệ thống đã xác minh hỗ trợ 100 KOC/trang</span>' +
            '</div>' +

            '<div class="mi-form-group">' +
              '<label class="mi-form-label">Giới hạn request / phút / tài khoản</label>' +
              '<input class="mi-form-input" id="miCfgRpm" type="number" min="1" max="600" value="' + (cfg.requests_per_minute || 30) + '" />' +
            '</div>' +

            '<div class="mi-form-group">' +
              '<label class="mi-form-label">Worker ghi PostgreSQL</label>' +
              '<input class="mi-form-input" id="miCfgWriters" type="number" min="1" max="8" value="' + (cfg.writer_workers || 2) + '" />' +
            '</div>' +

            '<div class="mi-form-group">' +
              '<label class="mi-form-label">Sức chứa queue (trang)</label>' +
              '<input class="mi-form-input" id="miCfgQueue" type="number" min="2" max="500" value="' + (cfg.queue_capacity || 20) + '" />' +
            '</div>' +

            '<div class="mi-form-group">' +
              '<label class="mi-form-label">Số lần thử lại</label>' +
              '<input class="mi-form-input" id="miCfgRetries" type="number" min="1" max="12" value="' + (cfg.max_retries || 5) + '" />' +
            '</div>' +

            '<div class="mi-form-group mi-auto-allocation-note"><label class="mi-form-label">Dải trang tự động</label><strong>1–' + totalTarget.toLocaleString('vi-VN') + '</strong><span class="mi-form-hint">Lấy trực tiếp từ totalPages marketBoost; PostgreSQL tự chia chunk cho mọi tài khoản.</span></div>' +

            '<div style="grid-column: 1 / -1; display:flex; justify-content:space-between; align-items:center; margin-top:6px;gap:12px">' +
              '<label class="mi-check-label">' +
                '<input type="checkbox" id="miCfgSchedule"' + (cfg.schedule_enabled ? ' checked' : '') + '>' +
                '<span>Tự động kích hoạt khi đến khung giờ hẹn</span>' +
              '</label>' +
              '<span class="mi-form-hint">Dữ liệu KOC lấy trực tiếp từ queryCreators; không gọi API chi tiết/enrich.</span>' +
              '<button type="submit" class="mi-btn mi-btn-ghost mi-btn-sm">' + ICON.check + ' Cập nhật cài đặt</button>' +
            '</div>' +
          '</form>' +
        '</details>' +

        // 3. Multi-Account Management Section
        buildAccountManagerHtml(accounts, "miCrawlerAcc") +

        // 4. Recent Logs Stream
          '<div class="mi-modal-section mi-crawler-logs-section">' +
            '<div class="mi-modal-section-hdr mi-crawler-logs-head">' +
              '<span class="mi-modal-section-title">' + ICON.radar + ' NHẬT KÝ CÀO GẦN NHẤT</span>' +
              '<button type="button" class="mi-btn mi-btn-ghost mi-btn-xs" id="miBtnClearCrawlerLogs"' + (logs.length ? '' : ' disabled') + '>' + ICON.trash + ' Xóa lịch sử</button>' +
            '</div>' +
            '<div class="mi-logs-box">' +
              (logs.length ? logs.map(function (l) {
                return '<div class="mi-log-line">' +
                  '<span class="mi-log-time">' + new Date(l.created_at).toLocaleTimeString('vi-VN') + '</span> ' +
                  '<span class="mi-log-page">Trang #' + l.page + '</span> ' +
                  '<span class="mi-log-count mi-clr-emerald">+' + l.koc_count + ' KOC (' + l.duration_ms + 'ms)</span> ' +
                  (l.error_msg ? '<span class="mi-clr-rose">' + esc(l.error_msg) + '</span>' : '<span class="mi-clr-teal">Thành công</span>') +
                '</div>';
              }).join('') : '<div class="mi-log-empty">Chưa có lịch sử cào.</div>') +
            '</div>' +
          '</div>' +
      '</div>';

    // Bind actions
    var btnStart = el.querySelector('#miBtnStartCrawler');
    if (btnStart) {
      btnStart.addEventListener('click', async function () {
        btnStart.disabled = true;
        await api('/api/v1/market/crawler/start', { method: 'POST' });
        refreshFn();
      });
    }

    var btnStop = el.querySelector('#miBtnStopCrawler');
    if (btnStop) {
      btnStop.addEventListener('click', async function () {
        btnStop.disabled = true;
        await api('/api/v1/market/crawler/stop', { method: 'POST' });
        refreshFn();
      });
    }

    var btnRefreshCrawler = el.querySelector('#miBtnRefreshCrawler');
    if (btnRefreshCrawler) {
      btnRefreshCrawler.addEventListener('click', async function () {
        if (!confirm('Cập nhật từ trang 1 sẽ tạo lượt mới và đặt checkpoint về đầu. Dữ liệu KOC đã lưu không bị xóa. Tiếp tục?')) return;
        btnRefreshCrawler.disabled = true;
        await api('/api/v1/market/crawler/restart-from-first', { method: 'POST' });
        refreshFn();
      });
    }

    var btnClearLogs = el.querySelector('#miBtnClearCrawlerLogs');
    if (btnClearLogs) {
      btnClearLogs.addEventListener('click', async function () {
        if (!confirm('Xóa toàn bộ lịch sử cào? Thao tác này không xóa KOC, checkpoint hoặc tiến độ đang chạy.')) return;
        btnClearLogs.disabled = true;
        await api('/api/v1/market/crawler/logs', { method: 'DELETE' });
        refreshFn();
      });
    }

    var cfgForm = el.querySelector('#miCrawlerConfigForm');
    if (cfgForm) {
      cfgForm.addEventListener('submit', async function (e) {
        e.preventDefault();
        var payload = {
          start_hour: parseInt(el.querySelector('#miCfgStart').value, 10),
          end_hour: parseInt(el.querySelector('#miCfgEnd').value, 10),
          delay_ms: parseInt(el.querySelector('#miCfgDelay').value, 10),
          chunk_size: parseInt(el.querySelector('#miCfgChunk').value, 10),
          page_size: parseInt(el.querySelector('#miCfgPageSize').value, 10),
          requests_per_minute: parseInt(el.querySelector('#miCfgRpm').value, 10),
          writer_workers: parseInt(el.querySelector('#miCfgWriters').value, 10),
          queue_capacity: parseInt(el.querySelector('#miCfgQueue').value, 10),
          max_retries: parseInt(el.querySelector('#miCfgRetries').value, 10),
          schedule_enabled: el.querySelector('#miCfgSchedule').checked
        };
        await api('/api/v1/market/crawler/config', { method: 'POST', body: JSON.stringify(payload) });
        alert('Đã lưu cấu hình khung giờ và tốc độ cào!');
        refreshFn();
      });
    }

    bindAccountManagerEvents(el, "miCrawlerAcc", refreshFn, accounts);
  }

  function openCrawlerModal() {
    var modal = createModal('Khai thác KOC Tự Động', 1180);
    renderCrawlerInto(modal.querySelector('#miModalBody'));
    return modal;
  }

  /* ══════════════════════════════════════════════════════════════
  /* ══════════════════════════════════════════════════════════════
   *  MARKET INTELLIGENCE SYNC & AUTOMATION CONTROLLER
   * ══════════════════════════════════════════════════════════════ */
  function getTabDisplayName(tabKey) {
    var names = {
      categories: 'Ngành hàng',
      products: 'Sản phẩm Hot',
      shops: 'Cửa hàng & Thương hiệu'
    };
    return names[tabKey] || tabKey || 'Hiện tại';
  }

  async function pollSyncProgress(onUpdate, onDone) {
    var interval = setInterval(async function () {
      try {
        var res = await api('/api/v1/market/sync/status');
        var data = (res && res.data) || {};
        if (typeof onUpdate === 'function') onUpdate(data);
        if (!data.isSyncing) {
          clearInterval(interval);
          if (typeof onDone === 'function') onDone(data);
        }
      } catch (err) {
        clearInterval(interval);
        if (typeof onDone === 'function') onDone({ isSyncing: false, error: err.message });
      }
    }, 1500);
  }

  async function getPreferredSyncPageCount() {
    try {
      var res = await api('/api/v1/market/settings');
      if (res && res.success && res.data && res.data.config) {
        return res.data.config.syncPageCount || 'all';
      }
    } catch (e) {}
    return 'all';
  }

  async function triggerSyncTab(tab, pageCount) {
    tab = tab || S.tab || 'categories';
    if (pageCount === undefined || pageCount === null) {
      pageCount = await getPreferredSyncPageCount();
    }
    var tabName = getTabDisplayName(tab);
    var isAll = pageCount === 'all';
    var btn = document.getElementById('miBtnSyncCurrentTab');
    if (btn) {
      btn.disabled = true;
      btn.innerHTML = '<span class="mi-spinner mi-spinner-sm"></span> Đang quét ' + (isAll ? 'toàn bộ' : pageCount + ' trang') + ' ' + esc(tabName) + '...';
    }

    try {
      var res = await api('/api/v1/market/sync-tab', {
        method: 'POST',
        body: JSON.stringify({ tab: tab, pageCount: pageCount })
      });

      if (res && res.success) {
        pollSyncProgress(
          function (status) {
            if (btn) {
              btn.innerHTML = '<span class="mi-spinner mi-spinner-sm"></span> ' + (status.message || ('Đang quét ' + tabName + ' (Trang ' + (status.currentPage || 1) + '/' + (status.totalPages || pageCount) + ')...'));
            }
          },
          function (finalStatus) {
            if (btn) {
              btn.disabled = false;
              btn.innerHTML = ICON.refresh + ' Đồng bộ ' + esc(tabName);
            }
            fetchCurrentTabData();
          }
        );
      } else {
        throw new Error(res ? res.msg : 'Không thể kích hoạt quét');
      }
    } catch (e) {
      if (btn) {
        btn.disabled = false;
        btn.innerHTML = ICON.refresh + ' Đồng bộ ' + esc(tabName);
      }
      alert('Lỗi quét dữ liệu: ' + e.message);
    }
  }

  var controlCenterPollTimer = null;

  async function openMarketControlCenterModal() {
    if (!isSuperAdmin()) {
      alert("Chỉ tài khoản Super Admin mới có quyền truy cập Bộ Quét & Cài Đặt Thị Trường.");
      return;
    }
    var openControlCenterModal = openMarketControlCenterModal;
    var modal = createModal('Bộ Quét & Trung Tâm Điều Khiển Thị Trường Market Intelligence', 860);
    var body = modal.querySelector('#miModalBody');
    var activeSubTab = 'scanners';

    if (body) body.innerHTML = loadingHtml();

    // Start auto polling while modal is open (every 1.5 seconds)
    if (controlCenterPollTimer) clearInterval(controlCenterPollTimer);
    controlCenterPollTimer = setInterval(function() {
      if (!document.body.contains(modal)) {
        clearInterval(controlCenterPollTimer);
        controlCenterPollTimer = null;
        return;
      }
      if (activeSubTab === 'scanners') {
        renderDashboard(true);
      }
    }, 1500);

    modal.querySelector('.mi-modal-close-btn').addEventListener('click', function() {
      if (controlCenterPollTimer) { clearInterval(controlCenterPollTimer); controlCenterPollTimer = null; }
    });
    modal.addEventListener('click', function (e) {
      if (e.target === modal) {
        if (controlCenterPollTimer) {
          clearInterval(controlCenterPollTimer);
          controlCenterPollTimer = null;
        }
      }
    });

    async function fetchControlData() {
      try {
        var [resCfg, resStatus, resCheckpoints] = await Promise.all([
          api('/api/v1/market/settings').catch(function () { return {}; }),
          api('/api/v1/market/sync/status').catch(function () { return {}; }),
          api('/api/v1/market/sync/checkpoints').catch(function () { return {}; })
        ]);
        return {
          cfg: (resCfg && resCfg.data) || {},
          status: (resStatus && resStatus.data) || {},
          checkpoints: (resCheckpoints && resCheckpoints.data) || (resStatus && resStatus.data && resStatus.data.checkpoints) || {}
        };
      } catch (e) {
        return { cfg: {}, status: {}, checkpoints: {} };
      }
    }

    async function renderDashboard(isSilent) {
      var data = await fetchControlData();
      renderContent(data.cfg, data.status, data.checkpoints, isSilent);
    }

    function renderContent(cfg, status, checkpoints, isSilent) {
      var syncTabs = Array.isArray(cfg.syncTabs) && cfg.syncTabs.length > 0
        ? cfg.syncTabs
        : ['categories', 'products', 'shops', 'videos'];
      var isSyncing = !!status.isSyncing;

      var tabDefs = [
        {
          key: 'categories',
          name: '🪐 Danh sách Ngành hàng',
          desc: 'Quét toàn bộ cây ngành hàng L1, L2, L3, doanh thu, tăng trưởng và độ tập trung Top shop.',
          icon: ICON.planet,
          total: checkpoints.categories?.total_pages || 51,
          total_items: checkpoints.categories?.total_items || 2550,
          cp: checkpoints.categories || { last_page: 0, total_pages: 51, total_items: 2550, total_synced: 0, status: 'idle' }
        },
        {
          key: 'products',
          name: '📦 Sản phẩm Hot',
          desc: 'Quét kho sản phẩm khổng lồ của Market Intelligence (PageSize: 50), dải doanh thu, lượt bán, đánh giá và video bán chạy.',
          icon: ICON.box,
          total: checkpoints.products?.total_pages || 73153,
          total_items: checkpoints.products?.total_items || 3657650,
          cp: checkpoints.products || { last_page: 0, total_pages: 73153, total_items: 3657650, total_synced: 0, status: 'idle' }
        },
        {
          key: 'shops',
          name: '🏪 Cửa hàng & Thương hiệu',
          desc: 'Quét danh sách Shop/Mall dẫn đầu thị trường (PageSize: 50), doanh số, doanh thu Live/Video/Affiliate.',
          icon: ICON.store,
          total: checkpoints.shops?.total_pages || 3813,
          total_items: checkpoints.shops?.total_items || 190650,
          cp: checkpoints.shops || { last_page: 0, total_pages: 3813, total_items: 190650, total_synced: 0, status: 'idle' }
        },
        {
          key: 'videos',
          name: '🎬 Video & Quảng cáo Hot',
          desc: 'Quét toàn bộ kho video & chiến dịch quảng cáo TikTok Shop (PageSize: 50), Doanh thu, Lượt xem, Chi phí QC và Ad ROAS.',
          icon: ICON.video,
          total: checkpoints.videos?.total_pages || 219239,
          total_items: checkpoints.videos?.total_items || 10961950,
          cp: checkpoints.videos || { last_page: 0, total_pages: 219239, total_items: 10961950, total_synced: 0, status: 'idle' }
        }
      ];

      var cardsHtml = tabDefs.map(function (t) {
        var cp = t.cp;
        var lastP = cp.last_page || 0;
        var totalP = (cp.total_pages && cp.total_pages > 10) ? cp.total_pages : t.total;
        var totalItems = (cp.total_items && cp.total_items > 10) ? cp.total_items : t.total_items;
        var isCompleted = cp.status === 'completed' && lastP >= totalP;
        var isCurrentRunning = isSyncing && (status.currentTab === t.key) && !isCompleted;
        var isWaitingTurn = isSyncing && !isCurrentRunning && (cp.status === 'running');
        var isPaused = cp.status === 'paused';
        var isCoolingDown = cp.status === 'cooling_down';
        var isError = cp.status === 'error';

        var pct = isCompleted ? 100 : (totalP > 0 ? Math.min(99, Math.max(0, Math.round((lastP / totalP) * 100))) : 0);

        var stCls = isCurrentRunning ? 'mi-badge-on' : (isCompleted ? 'mi-badge-emerald' : (isCoolingDown ? 'mi-badge-warn' : (isPaused ? 'mi-badge-warn' : (isError ? 'mi-badge-danger' : (isWaitingTurn ? 'mi-badge-warn' : 'mi-badge-off')))));
        var stTxt = isCurrentRunning
          ? ('⚡ Đang quét trang ' + (status.currentPage || lastP) + '...')
          : (isCompleted
              ? ('✅ Đã quét xong (' + lastP.toLocaleString('vi-VN') + ' trang)')
              : (isCoolingDown
                  ? ('🛡️ ' + (status.message || cp.last_error || 'Đang hạ nhiệt tài khoản...'))
                  : (isPaused
                      ? '⏸️ Đang tạm dừng'
                      : (isError
                          ? ('⚠️ Lỗi: ' + (cp.last_error || 'Thử lại'))
                          : (isWaitingTurn ? '⏳ Chờ tới lượt' : '💤 Sẵn sàng')))));

        var pagesDisplay = isCompleted
          ? (lastP.toLocaleString('vi-VN') + ' / ' + lastP.toLocaleString('vi-VN'))
          : (totalP > 0
              ? (lastP.toLocaleString('vi-VN') + ' / ' + totalP.toLocaleString('vi-VN'))
              : (lastP.toLocaleString('vi-VN')));

        return '<div class="mi-crawler-card" data-tab-key="' + t.key + '" style="background:#fff;border-radius:12px;border:1px solid var(--mi-border);padding:16px;display:flex;flex-direction:column;gap:12px;box-shadow:0 2px 8px rgba(0,0,0,0.03)">' +
          '<div style="display:flex;align-items:center;justify-content:space-between">' +
            '<div style="display:flex;align-items:center;gap:8px">' +
              '<span style="display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:8px;background:var(--mi-surface-2,#f8f7fc);color:#7c3aed">' + t.icon + '</span>' +
              '<div>' +
                '<h4 style="margin:0;font-size:14px;font-weight:700;color:var(--mi-text)">' + t.name + '</h4>' +
                '<div style="font-size:11px;color:var(--mi-muted);margin-top:2px">' + t.desc + '</div>' +
              '</div>' +
            '</div>' +
            '<span class="mi-badge ' + stCls + '" style="font-size:12px;padding:5px 12px">' + (isCurrentRunning ? '<span class="mi-pulsar-dot"></span> ' : '') + stTxt + '</span>' +
          '</div>' +

          '<div style="background:var(--mi-surface-2,#F8F7FC);border-radius:8px;padding:10px 14px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;font-size:12px">' +
            '<div>' +
              '<span style="color:var(--mi-muted);display:block;font-size:11px">Trang đã quét</span>' +
              '<strong style="font-size:13px;color:var(--mi-text)">' +
                pagesDisplay +
              '</strong>' +
            '</div>' +
            '<div>' +
              '<span style="color:var(--mi-muted);display:block;font-size:11px">Mục đã thu thập</span>' +
              '<strong style="font-size:13px;color:#059669">' +
                (cp.total_synced || 0).toLocaleString('vi-VN') + (totalItems > 0 && !isCompleted ? (' / ' + totalItems.toLocaleString('vi-VN')) : '') +
              '</strong>' +
            '</div>' +
            '<div>' +
              '<span style="color:var(--mi-muted);display:block;font-size:11px">Tiến độ</span>' +
              '<strong style="font-size:13px;color:#7c3aed">' + pct + '%</strong>' +
            '</div>' +
          '</div>' +

          (isCurrentRunning ?
            '<div style="width:100%;height:6px;background:#ede9fe;border-radius:3px;overflow:hidden">' +
              '<div class="mi-progress-pulse" style="width:100%;height:100%;background:linear-gradient(90deg,#7c3aed,#ec4899,#06b6d4);animation:miPulseAnim 1.5s infinite"></div>' +
            '</div>' : '') +

          '<div style="display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-top:4px">' +
            '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm btn-reset-cp" data-tab="' + t.key + '" title="Đặt lại checkpoint về trang 1">' + ICON.refresh + ' Trang 1</button>' +
            (isCurrentRunning ?
              '<button type="button" class="mi-btn mi-btn-danger mi-btn-sm btn-stop-cp" data-tab="' + t.key + '" style="background:#ef4444;color:#fff;border-color:#ef4444;font-weight:700;padding:8px 16px">' + ICON.close + ' Dừng quét</button>' :
              '<button type="button" class="mi-btn mi-btn-primary mi-btn-sm btn-resume-cp" data-tab="' + t.key + '" style="font-weight:700;padding:8px 16px">' + ICON.bolt + ' ' + (lastP > 0 && !isCompleted ? ('Quét tiếp (Từ trang ' + (lastP + 1) + ')') : 'Quét tự động') + '</button>'
            ) +
            '<button type="button" class="mi-btn mi-btn-ghost mi-btn-sm btn-fresh-cp" data-tab="' + t.key + '" title="Xóa checkpoint và quét lại từ trang 1">' + ICON.refresh + ' Quét lại từ đầu</button>' +
          '</div>' +
        '</div>';
      }).join('');

      body.innerHTML =
        '<div style="display:flex;flex-direction:column;gap:16px">' +

          // Tab header selector
          '<div style="display:flex;gap:10px;border-bottom:1px solid var(--mi-border);padding-bottom:12px">' +
            '<button type="button" class="mi-btn ' + (activeSubTab === 'scanners' ? 'mi-btn-primary' : 'mi-btn-ghost') + '" id="miCtrlTabScanners" style="display:inline-flex;align-items:center;gap:6px;font-weight:700">' +
              ICON.radar + ' <span>Bộ Quét Thị Trường & Tiến Trình</span>' +
            '</button>' +
            '<button type="button" class="mi-btn ' + (activeSubTab === 'settings' ? 'mi-btn-primary' : 'mi-btn-ghost') + '" id="miCtrlTabSettings" style="display:inline-flex;align-items:center;gap:6px;font-weight:700">' +
              ICON.settings + ' <span>Cài Đặt Tự Động & Lịch Quét</span>' +
            '</button>' +
          '</div>' +

          // Sub-Tab 1: Scanners & Checkpoints
          (activeSubTab === 'scanners' ?
            '<div style="display:flex;flex-direction:column;gap:14px">' +
              '<div style="display:flex;align-items:center;justify-content:space-between;background:var(--mi-surface-2,#f8f7fc);padding:12px 16px;border-radius:10px;border:1px solid var(--mi-border)">' +
                '<div>' +
                  '<strong style="color:var(--mi-text);font-size:13px">Hệ Thống Quét Checkpoint & Tự Động Chạy Liên Tục (Auto-Resume Engine)</strong>' +
                  '<div style="font-size:12px;color:var(--mi-muted);margin-top:2px">Bấm 1 lần hệ thống sẽ tự động quét liên tục trên Server (PageSize 50, Delay chống Bot) cho đến khi bấm Dừng.</div>' +
                '</div>' +
                '<div style="display:flex;gap:8px">' +
                  (isSyncing ?
                    '<button type="button" class="mi-btn mi-btn-danger mi-btn-sm" id="btnSyncAllStop" style="background:#ef4444;color:#fff;border-color:#ef4444;font-weight:700">' + ICON.close + ' Dừng toàn bộ tiến trình</button>' :
                    '<button type="button" class="mi-btn mi-btn-primary mi-btn-sm" id="btnSyncAllResume" style="font-weight:700">' + ICON.bolt + ' Quét tự động toàn bộ các Tab</button>'
                  ) +
                '</div>' +
              '</div>' +

              (status.message ?
                '<div style="background:rgba(124,58,237,0.08);border:1px solid rgba(124,58,237,0.25);border-radius:8px;padding:10px 14px;display:flex;align-items:center;gap:8px;font-size:12px;color:#6b21a8;font-weight:600">' +
                  (isSyncing ? '<span class="mi-spinner mi-spinner-sm"></span> ' : '') + '<span>' + esc(status.message) + '</span>' +
                '</div>' : '') +

              '<div style="display:flex;flex-direction:column;gap:12px">' + cardsHtml + '</div>' +
            '</div>'
          : '') +

          // Sub-Tab 2: Automation & Auth Settings
          (activeSubTab === 'settings' ?
            '<div style="display:flex;flex-direction:column;gap:16px">' +
              // 1. Tự động quét theo lịch
              '<div class="mi-modal-section">' +
                '<div class="mi-modal-section-hdr">' +
                  '<span class="mi-modal-section-title">' + ICON.clock + ' HẸN GIỜ & KHUNG THỜI GIAN QUÉT TỰ ĐỘNG</span>' +
                  (isSyncing ? '<span class="mi-badge mi-badge-on"><span class="mi-pulsar-dot"></span> ĐANG QUÉT NGẦM</span>' : '<span class="mi-badge mi-badge-off">Đang rảnh</span>') +
                '</div>' +
                '<form id="miSettingsForm" class="mi-form">' +
                  '<div style="background:var(--mi-surface-2,#F8F7FC);padding:14px 16px;border-radius:10px;border:1px solid var(--mi-border);margin-bottom:12px">' +
                    '<label class="mi-form-check" style="cursor:pointer;display:flex;align-items:center;gap:10px;font-weight:700;font-size:14px;color:var(--mi-text)">' +
                      '<input type="checkbox" id="miAutoSync" style="width:18px;height:18px;accent-color:#7c3aed"' + (cfg.autoSync !== false || cfg.scheduledCrawl?.enabled ? ' checked' : '') + '>' +
                      '<span>Bật tự động quét ngầm kéo dữ liệu theo chu kỳ hẹn giờ</span>' +
                    '</label>' +
                    '<div style="font-size:12px;color:var(--mi-muted);margin-top:6px;margin-left:28px">' +
                      'Hệ thống tự động chạy ngầm trên máy chủ theo khung giờ đã định, ghi nhớ checkpoint không quét lặp lại.' +
                    '</div>' +
                  '</div>' +

                  '<div class="mi-form-grid-2" style="gap:14px;margin-bottom:14px">' +
                    '<div class="mi-form-group">' +
                      '<label class="mi-form-label">⏱️ Chu kỳ lặp lại tự động</label>' +
                      '<select class="mi-form-input" id="miInterval">' +
                        '<option value="1"' + (cfg.syncIntervalHours === 1 || cfg.scheduledCrawl?.intervalHours === 1 ? ' selected' : '') + '>Mỗi 1 giờ</option>' +
                        '<option value="2"' + (cfg.syncIntervalHours === 2 || cfg.scheduledCrawl?.intervalHours === 2 ? ' selected' : '') + '>Mỗi 2 giờ</option>' +
                        '<option value="4"' + (cfg.syncIntervalHours === 4 || cfg.scheduledCrawl?.intervalHours === 4 ? ' selected' : '') + '>Mỗi 4 giờ</option>' +
                        '<option value="6"' + (cfg.syncIntervalHours === 6 || !cfg.syncIntervalHours || cfg.scheduledCrawl?.intervalHours === 6 ? ' selected' : '') + '>Mỗi 6 giờ (Khuyên dùng)</option>' +
                        '<option value="12"' + (cfg.syncIntervalHours === 12 || cfg.scheduledCrawl?.intervalHours === 12 ? ' selected' : '') + '>Mỗi 12 giờ</option>' +
                        '<option value="24"' + (cfg.syncIntervalHours === 24 || cfg.scheduledCrawl?.intervalHours === 24 ? ' selected' : '') + '>Mỗi 24 giờ (1 ngày/lần)</option>' +
                      '</select>' +
                    '</div>' +

                    '<div class="mi-form-group">' +
                      '<label class="mi-form-label">🕐 Khung giờ hoạt động trong ngày</label>' +
                      '<select class="mi-form-input" id="miTimeWindow">' +
                        '<option value="all_day">Cả ngày (00:00 - 23:59)</option>' +
                        '<option value="night">Ban đêm (00:00 - 06:00)</option>' +
                        '<option value="morning">Buổi sáng (06:00 - 12:00)</option>' +
                        '<option value="evening">Buổi tối (18:00 - 23:59)</option>' +
                      '</select>' +
                    '</div>' +
                  '</div>' +

                  '<div class="mi-form-grid-2" style="gap:14px;margin-bottom:14px">' +
                    '<div class="mi-form-group">' +
                      '<label class="mi-form-label">🛡️ Cơ chế chống phát hiện Bot & Jitter</label>' +
                      '<div style="font-size:12.5px;color:var(--mi-text);background:#fff;padding:8px 12px;border-radius:6px;border:1px solid var(--mi-border)">' +
                        '✅ Delay ngẫu nhiên 2.5s - 5.0s giữa các trang · Auto-relogin khi hết hạn token' +
                      '</div>' +
                    '</div>' +

                    '<div class="mi-form-group">' +
                      '<label class="mi-form-label">🎯 Chọn các Tab tự động đồng bộ</label>' +
                      '<div style="display:flex;gap:12px;align-items:center;height:38px">' +
                        '<label class="mi-form-check" style="cursor:pointer;font-size:12px"><input type="checkbox" name="miSyncTab" value="categories"' + (syncTabs.includes('categories') ? ' checked' : '') + '> Ngành hàng</label>' +
                        '<label class="mi-form-check" style="cursor:pointer;font-size:12px"><input type="checkbox" name="miSyncTab" value="products"' + (syncTabs.includes('products') ? ' checked' : '') + '> Sản phẩm</label>' +
                        '<label class="mi-form-check" style="cursor:pointer;font-size:12px"><input type="checkbox" name="miSyncTab" value="shops"' + (syncTabs.includes('shops') ? ' checked' : '') + '> Cửa hàng</label>' +
                        '<label class="mi-form-check" style="cursor:pointer;font-size:12px"><input type="checkbox" name="miSyncTab" value="videos"' + (syncTabs.includes('videos') ? ' checked' : '') + '> Video Hot</label>' +
                      '</div>' +
                    '</div>' +
                  '</div>' +

                  '<div style="display:flex;justify-content:flex-end;gap:10px">' +
                    '<button type="submit" class="mi-btn mi-btn-primary" id="miSaveSettings">' + ICON.check + ' Lưu Cấu Hình & Lịch Quét</button>' +
                  '</div>' +
                '</form>' +
              '</div>' +

              // 2. Tài khoản kết nối Market Intelligence & Tự động Re-login
              '<div class="mi-modal-section">' +
                '<div class="mi-modal-section-hdr">' +
                  '<span class="mi-modal-section-title">' + ICON.users + ' TÀI KHOẢN KẾT NỐI & TỰ ĐỘNG ĐĂNG NHẬP LẠI</span>' +
                '</div>' +
                '<form id="miMarket IntelligenceAccountForm" class="mi-form-grid-2">' +
                  '<div class="mi-form-group">' +
                    '<label class="mi-form-label">Số điện thoại đăng nhập Market Intelligence</label>' +
                    '<input class="mi-form-input" id="miMarket IntelligencePhone" type="text" autocomplete="username" placeholder="0901234567" value="' + esc(cfg.phoneNumber || '') + '" />' +
                  '</div>' +
                  '<div class="mi-form-group">' +
                    '<label class="mi-form-label">Mật khẩu tài khoản</label>' +
                    '<input class="mi-form-input" id="miMarket IntelligencePass" type="password" autocomplete="current-password" placeholder="' + (cfg.phoneNumber ? '(giữ nguyên nếu không đổi)' : 'Nhập mật khẩu') + '" />' +
                  '</div>' +
                  '<div class="mi-form-group" style="grid-column:1/-1;display:flex;align-items:center;gap:12px;flex-wrap:wrap">' +
                    (cfg.cookieJar ? '<span class="mi-badge mi-badge-on">' + ICON.check + ' Đang có phiên kết nối</span>' : '<span class="mi-badge mi-badge-off">Chưa có phiên</span>') +
                    (cfg.lastLoginAt ? '<span class="mi-clr-muted" style="font-size:12px">Lần cuối: ' + new Date(cfg.lastLoginAt).toLocaleString('vi-VN') + '</span>' : '') +
                    '<button type="submit" class="mi-btn mi-btn-primary mi-btn-sm" id="miSaveMarket IntelligenceAccount" style="margin-left:auto">' + ICON.check + ' Lưu & Kết Nối</button>' +
                  '</div>' +
                '</form>' +
              '</div>' +
            '</div>'
          : '') +

        '</div>';

      // Bind tabs switch
      var tabScanBtn = body.querySelector('#miCtrlTabScanners');
      var tabSetBtn = body.querySelector('#miCtrlTabSettings');
      if (tabScanBtn) {
        tabScanBtn.addEventListener('click', function () {
          activeSubTab = 'scanners';
          renderContent(cfg, status, checkpoints);
        });
      }
      if (tabSetBtn) {
        tabSetBtn.addEventListener('click', function () {
          activeSubTab = 'settings';
          renderContent(cfg, status, checkpoints);
        });
      }

      // Bind checkpoint actions
      body.querySelectorAll('.btn-resume-cp').forEach(function (btn) {
        btn.addEventListener('click', async function () {
          var tab = btn.dataset.tab;
          btn.disabled = true;
          btn.innerHTML = '<span class="mi-spinner mi-spinner-sm"></span> Đang chạy liên tục...';
          try {
            await api('/api/v1/market/sync/start', { method: 'POST', body: JSON.stringify({ tab: tab, mode: 'resume' }) });
            renderDashboard();
          } catch (e) {
            alert('Lỗi: ' + e.message);
            renderDashboard();
          }
        });
      });

      body.querySelectorAll('.btn-stop-cp').forEach(function (btn) {
        btn.addEventListener('click', async function () {
          var tab = btn.dataset.tab;
          btn.disabled = true;
          btn.innerHTML = '<span class="mi-spinner mi-spinner-sm"></span> Đang dừng...';
          try {
            await api('/api/v1/market/sync/stop', { method: 'POST', body: JSON.stringify({ tab: tab }) });
            renderDashboard();
          } catch (e) {
            alert('Lỗi: ' + e.message);
            renderDashboard();
          }
        });
      });

      body.querySelectorAll('.btn-fresh-cp').forEach(function (btn) {
        btn.addEventListener('click', async function () {
          var tab = btn.dataset.tab;
          if (!confirm('Bạn có chắc chắn muốn quét lại từ đầu cho tab này? (Tiến trình sẽ bắt đầu từ Trang 1)')) return;
          btn.disabled = true;
          btn.innerHTML = '<span class="mi-spinner mi-spinner-sm"></span> Đang khởi động...';
          try {
            await api('/api/v1/market/sync/start', { method: 'POST', body: JSON.stringify({ tab: tab, mode: 'fresh' }) });
            renderDashboard();
          } catch (e) {
            alert('Lỗi: ' + e.message);
            renderDashboard();
          }
        });
      });

      body.querySelectorAll('.btn-reset-cp').forEach(function (btn) {
        btn.addEventListener('click', async function () {
          var tab = btn.dataset.tab;
          if (!confirm('Đặt lại checkpoint tab này về trang 1?')) return;
          try {
            await api('/api/v1/market/sync/reset', { method: 'POST', body: JSON.stringify({ tab: tab }) });
            renderDashboard();
          } catch (e) {
            alert('Lỗi: ' + e.message);
          }
        });
      });

      var btnSyncAllResume = body.querySelector('#btnSyncAllResume');
      if (btnSyncAllResume) {
        btnSyncAllResume.addEventListener('click', async function () {
          btnSyncAllResume.disabled = true;
          try {
            await api('/api/v1/market/sync/start', { method: 'POST', body: JSON.stringify({ tab: 'all', mode: 'resume' }) });
            renderDashboard();
          } catch (e) {
            alert('Lỗi: ' + e.message);
            renderDashboard();
          }
        });
      }

      var btnSyncAllStop = body.querySelector('#btnSyncAllStop');
      if (btnSyncAllStop) {
        btnSyncAllStop.addEventListener('click', async function () {
          btnSyncAllStop.disabled = true;
          try {
            await api('/api/v1/market/sync/stop', { method: 'POST', body: JSON.stringify({ tab: 'all' }) });
            renderDashboard();
          } catch (e) {
            alert('Lỗi: ' + e.message);
            renderDashboard();
          }
        });
      }

      // Bind Settings form
      var settingsForm = body.querySelector('#miSettingsForm');
      if (settingsForm) {
        settingsForm.addEventListener('submit', async function (e) {
          e.preventDefault();
          var btn = body.querySelector('#miSaveSettings');
          btn.disabled = true;
          btn.textContent = 'Đang lưu...';
          var selectedTabs = [];
          body.querySelectorAll('input[name="miSyncTab"]:checked').forEach(function (cb) {
            selectedTabs.push(cb.value);
          });
          var windowVal = body.querySelector('#miTimeWindow')?.value || 'all_day';
          var startH = 0, endH = 23;
          if (windowVal === 'night') { startH = 0; endH = 6; }
          else if (windowVal === 'morning') { startH = 6; endH = 12; }
          else if (windowVal === 'evening') { startH = 18; endH = 23; }

          var payload = {
            autoSync: body.querySelector('#miAutoSync').checked,
            syncIntervalHours: parseInt(body.querySelector('#miInterval').value, 10) || 6,
            syncPageCount: 'all',
            syncTabs: selectedTabs.length > 0 ? selectedTabs : ['categories', 'products', 'shops'],
            scheduledCrawl: {
              enabled: body.querySelector('#miAutoSync').checked,
              intervalHours: parseInt(body.querySelector('#miInterval').value, 10) || 6,
              startHour: startH,
              endHour: endH
            }
          };
          try {
            var r = await api('/api/v1/market/settings', { method: 'POST', body: JSON.stringify(payload) });
            if (r && r.success) {
              alert('Đã lưu cấu hình và lịch quét tự động thành công!');
              renderDashboard();
            } else {
              alert('Lỗi: ' + ((r && (r.msg || r.message)) || 'Không lưu được.'));
            }
          } catch (err) {
            alert('Lỗi: ' + err.message);
          } finally {
            btn.disabled = false;
            btn.innerHTML = ICON.check + ' Lưu Cấu Hình & Lịch Quét';
          }
        });
      }

      // Bind Account form
      var kaForm = body.querySelector('#miMarket IntelligenceAccountForm');
      if (kaForm) {
        kaForm.addEventListener('submit', async function (e) {
          e.preventDefault();
          var btn = body.querySelector('#miSaveMarket IntelligenceAccount');
          btn.disabled = true;
          btn.textContent = 'Đang lưu...';
          var phone = body.querySelector('#miMarket IntelligencePhone').value.trim();
          var pass = body.querySelector('#miMarket IntelligencePass').value.trim();
          if (!phone) { alert('Vui lòng nhập số điện thoại.'); btn.disabled = false; btn.innerHTML = ICON.check + ' Lưu & Kết Nối'; return; }
          var payload = { phoneNumber: phone };
          if (pass && pass !== '********') payload.password = pass;
          try {
            var r = await api('/api/v1/market/settings', { method: 'POST', body: JSON.stringify(payload) });
            if (r && r.success) {
              alert('Đã lưu tài khoản thị trường.');
              renderDashboard();
            } else {
              alert('Lỗi: ' + (r && (r.msg || r.message) || 'Không lưu được.'));
            }
          } catch (err) {
            alert('Lỗi kết nối: ' + err.message);
          } finally {
            btn.disabled = false;
            btn.innerHTML = ICON.check + ' Lưu & Kết Nối';
          }
        });
      }
    }

    await renderDashboard();

    // Auto-poll progress while modal is open
    if (!controlCenterPollTimer) {
      controlCenterPollTimer = setInterval(async function () {
        if (!document.body.contains(modal)) {
          clearInterval(controlCenterPollTimer);
          controlCenterPollTimer = null;
          return;
        }
        if (activeSubTab === 'scanners') {
          var data = await fetchControlData();
          if (data && data.status) {
            renderContent(data.cfg, data.status, data.checkpoints);
          }
        }
      }, 1500);
    }
  }

  /* ── GLOBAL INIT ────────────────────────────────────────────── */
  window.initMarketIntelligence = function () { renderRoot(); };
  window.renderCrawlerInto = renderCrawlerInto;
  window.openKocMiningModal = openCrawlerModal;
  window.openMarketControlCenterModal = openMarketControlCenterModal;
  window.openSettingsModal = openMarketControlCenterModal;

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function () {
      if (document.getElementById('viewMarketIntelligence')) renderRoot();
    });
  } else {
    if (document.getElementById('viewMarketIntelligence')) renderRoot();
  }

})();
