(function() {
  const { useState, useEffect, useRef } = React;

  // stringify ที่ตัด DOM element/Node ทิ้ง — กัน "Converting circular structure to JSON" ทำหน้าพัง
  // ถ้ามี DOM element หลุดเข้า state/form (เช่นเผลอ set ค่าเป็น e.target) จะถูกข้าม แทนที่จะ crash ทั้งแอป
  window.safeStringify = function (obj) {
    return JSON.stringify(obj, (k, v) => (typeof Node !== 'undefined' && v instanceof Node ? undefined : v));
  };

  // ===== Autosave ร่างลง server อัตโนมัติ — กันงานหายตอนไฟดับ + มาทำต่อจากเครื่องไหนก็ได้ =====
  // persist(form): async → คืน true(บันทึกแล้ว) / false(ข้าม เช่นยังไม่มีหัวข้อ) / throw(error)
  // เก็บเป็น "ร่าง" เสมอ ไม่เผยแพร่ — ปุ่ม "บันทึก" จริงเท่านั้นที่ publish
  function useServerAutosave({ form, enabled, persist, delay = 2500 }) {
    const [status, setStatus] = useState('');   // '' | 'saving' | 'saved' | 'error'
    const [at, setAt] = useState('');
    const lastJson = useRef(null);
    const primed = useRef(false);
    const saving = useRef(false);
    useEffect(() => {
      if (!enabled) return;
      let json;
      try { json = window.safeStringify(form); }
      catch (e) { return; } // form มีค่า serialize ไม่ได้ (เช่น DOM element หลุดเข้ามา) → ข้ามรอบนี้ ไม่ crash ทั้งหน้า
      if (!primed.current) { primed.current = true; lastJson.current = json; return; } // ข้ามค่าตอนเพิ่งโหลด/เปิด
      if (json === lastJson.current) return;
      const t = setTimeout(async () => {
        if (saving.current) return;   // กันยิงซ้อน (เช่น สร้าง draft ซ้ำ)
        saving.current = true;
        setStatus('saving');
        try {
          const ok = await persist(form);
          if (ok) { lastJson.current = json; setStatus('saved'); setAt(new Date().toLocaleTimeString('th-TH')); }
          else setStatus('');
        } catch (e) { setStatus('error'); }
        finally { saving.current = false; }
      }, delay);
      return () => clearTimeout(t);
    }, [form, enabled]);
    // ตั้ง "ค่าตั้งต้น" ใหม่หลังโหลดข้อมูลเดิมมาแก้ไข — กัน autosave ยิง update หลอกๆ ตอนเพิ่งเปิด (และไม่ให้ log การกระทำเพี้ยน)
    function markBaseline(f) { try { lastJson.current = window.safeStringify(f); } catch (e) {} primed.current = true; }
    return { status, at, markBaseline };
  }
  window.useServerAutosave = useServerAutosave;

  // ป้ายสถานะ autosave — โชว์ข้างปุ่มบันทึก
  function AutosaveStatus({ state }) {
    const s = state || {};
    let txt = '', col = '#6B7280';
    if (s.status === 'saving') txt = 'กำลังบันทึกร่างอัตโนมัติ…';
    else if (s.status === 'saved') { txt = '✓ บันทึกร่างอัตโนมัติแล้ว' + (s.at ? ' ' + s.at : ''); col = '#15976A'; }
    else if (s.status === 'error') { txt = '⚠ บันทึกร่างอัตโนมัติไม่สำเร็จ'; col = '#D8567A'; }
    if (!txt) return null;
    return <span style={{fontSize:12.5,color:col,fontWeight:600,whiteSpace:'nowrap'}}>{txt}</span>;
  }
  window.AutosaveStatus = AutosaveStatus;

  // Dropdown เลือกภาษาแบบกะทัดรัด พร้อมธงจริง
  function LangPicker({ lang, setLang }) {
    const [open, setOpen] = useState(false);
    const ref = useRef(null);
    const opts = [
      { v: 'th', flag: 'th', label: 'ไทย' },
      { v: 'en', flag: 'gb', label: 'EN' },
      { v: 'zh', flag: 'cn', label: '中文' },
    ];
    const cur = opts.find(o => o.v === lang) || opts[0];
    useEffect(() => {
      function onDoc(e) { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }
      document.addEventListener('mousedown', onDoc);
      return () => document.removeEventListener('mousedown', onDoc);
    }, []);
    const Flag = ({ c }) => (
      <img className="lang-flag" alt="" width="22" height="16"
        src={'https://flagcdn.com/w40/' + c + '.png'}
        srcSet={'https://flagcdn.com/w80/' + c + '.png 2x'} />
    );
    return (
      <div className="lang-picker" ref={ref}>
        <button type="button" className="lang-btn" onClick={() => setOpen(o => !o)} aria-label="language">
          <Flag c={cur.flag} />
          <span className="lang-label">{cur.label}</span>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
        </button>
        {open && (
          <div className="lang-menu">
            {opts.map(o => (
              <button type="button" key={o.v}
                className={'lang-item' + (o.v === lang ? ' active' : '')}
                onClick={() => { setLang(o.v); setOpen(false); }}>
                <Flag c={o.flag} /><span>{o.label}</span>
              </button>
            ))}
          </div>
        )}
      </div>
    );
  }

  // Sidebar
  // ตัวเลขแจ้งเตือนวงกลมแดงท้ายเมนู — บอกว่ามีของใหม่/ต้องรีวิวกี่รายการ
  function NavBadge({ n }) {
    if (!n || n < 1) return null;
    return <span style={{ marginLeft: 'auto', minWidth: 18, height: 18, padding: '0 5px', borderRadius: 9, background: '#E23D3D', color: '#fff', fontSize: 11, fontWeight: 700, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', lineHeight: 1, boxShadow: '0 0 0 2px rgba(226,61,61,.15)' }}>{n > 99 ? '99+' : n}</span>;
  }

  function Sidebar({ page, setPage, user, onLogout, t, lang, setLang, navOpen, counts }) {
    const role = user?.role;
    const c = { ...(counts || {}) };
    c.review_pending = (c.listings_pending || 0) + (c.submissions_pending || 0); // รอตรวจสอบ = ทรัพย์ในระบบ + ทรัพย์ที่ส่งเข้ามา
    const menus = [
      { section: 'sec_overview' },
      { id: 'dashboard', icon: 'ph-squares-four', key: 'menu_dashboard', roles: ['super_admin','admin'] },
      { id: 'executive', icon: 'ph-chart-line-up', key: 'menu_executive', roles: ['super_admin'] },

      { section: 'sec_deals' },
      { id: 'leads', icon: 'ph-tray-arrow-down', key: 'menu_leads', roles: ['super_admin','admin'], badge: 'leads_new' },
      { id: 'listings-group', icon: 'ph-buildings', key: 'menu_listings', roles: ['super_admin','admin','marketing'], group: true,
        children: [
          { id: 'listings', key: 'menu_listings_general', badge: 'review_pending' },
          { id: 'npl', key: 'menu_npl_listings' },
        ] },

      { section: 'sec_people' },
      { id: 'investors', icon: 'ph-wallet', key: 'menu_investors', roles: ['super_admin','admin'], badge: 'investors_pending' },
      { id: 'brokers', icon: 'ph-handshake', key: 'menu_brokers', roles: ['super_admin','admin'] },
      { id: 'owners', icon: 'ph-user-square', key: 'menu_owners', roles: ['super_admin','admin'] },

      { section: 'sec_content' },
      { id: 'articles', icon: 'ph-newspaper', key: 'menu_articles', roles: ['super_admin','marketing'] },

      { section: 'sec_system' },
      { id: 'users', icon: 'ph-users-three', key: 'menu_users', roles: ['super_admin'] },
      { id: 'privacy', icon: 'ph-shield-check', key: 'menu_privacy', roles: ['super_admin'], badge: 'privacy_new' },
      { id: 'audit', icon: 'ph-list-checks', key: 'menu_audit', roles: ['super_admin'] },
      { id: 'system', icon: 'ph-tree-structure', key: 'menu_system', roles: ['super_admin'] },
    ];
    // เก็บ section marker ไว้ + กรองเมนูตาม role · แล้วตัดหัวข้อ section ที่ไม่มีเมนูตามหลัง (เช่น role marketing เห็นน้อย)
    const vis = menus.filter(m => m.section || m.roles.includes(role));
    const allowed = vis.filter((m, i) => !m.section || (vis[i + 1] && !vis[i + 1].section));
    return (
      <aside className={'sidebar' + (navOpen ? ' open' : '')}>
        <div className="sidebar-logo">
          <img className="sidebar-logo-img" src="logo-mark.png" alt="PROPFINN" />
          <div>
            <div className="sidebar-brand">PROPFINN</div>
            <div className="sidebar-sub">Admin Panel</div>
          </div>
        </div>
        <nav className="sidebar-nav">
          {allowed.map((m, mi) => {
            // หัวข้อกลุ่มใหญ่ (section) — ตัวอักษรจางๆ คั่นกลุ่ม
            if (m.section) return <div key={'sec-' + m.section} className="nav-section">{t(m.section)}</div>;
            // กลุ่ม (หัวข้อไม่คลิก + ลูกๆ) เช่น "จัดการทรัพย์" → ทรัพย์ทั่วไป / ทรัพย์ NPL
            if (m.group) {
              const groupBadge = (m.children || []).reduce((s, ch) => s + (ch.badge ? (c[ch.badge] || 0) : 0), 0);
              return (
                <React.Fragment key={m.id}>
                  <div className="nav-group-label">
                    <i className={"ph " + m.icon + " nav-icon"} aria-hidden="true"></i>
                    <span>{t(m.key)}</span>
                    <NavBadge n={groupBadge} />
                  </div>
                  {(m.children || []).map(ch => (
                    <button key={ch.id} className={`nav-subitem${page === ch.id ? ' active' : ''}`} onClick={() => setPage(ch.id)}>
                      <span className="nav-subitem-dot">└</span>
                      <span>{t(ch.key)}</span>
                      <NavBadge n={ch.badge ? c[ch.badge] : 0} />
                    </button>
                  ))}
                </React.Fragment>
              );
            }
            const childActive = (m.children || []).some(c => c.id === page);
            return (
              <React.Fragment key={m.id}>
                <button className={`nav-item${(page === m.id || childActive) ? ' active' : ''}`} onClick={() => setPage(m.id)}>
                  <i className={"ph " + m.icon + " nav-icon"} aria-hidden="true"></i>
                  <span>{t(m.key)}</span>
                  <NavBadge n={m.badge ? c[m.badge] : 0} />
                </button>
                {(m.children || []).map(ch => (
                  <button key={ch.id} className={`nav-subitem${page === ch.id ? ' active' : ''}`} onClick={() => setPage(ch.id)}>
                    <span className="nav-subitem-dot">└</span>
                    <span>{t(ch.key)}</span>
                    <NavBadge n={ch.badge ? c[ch.badge] : 0} />
                  </button>
                ))}
              </React.Fragment>
            );
          })}
        </nav>
      </aside>
    );
  }

  // Top bar (ขวาบน): ภาษา (pill) + avatar วงกลม → dropdown (ชื่อ/ตำแหน่ง/ออกจากระบบ) — แบบหน้าบ้าน ประหยัดที่
  function TopBar({ user, t, lang, setLang, onLogout, setPage, onMenu }) {
    const [open, setOpen] = useState(false);
    const wrapRef = useRef(null);
    const role = user?.role;
    const initial = (user?.display_name || user?.email || 'U')[0].toUpperCase();
    useEffect(() => {
      function onDoc(e) { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); }
      document.addEventListener('mousedown', onDoc);
      return () => document.removeEventListener('mousedown', onDoc);
    }, []);
    return (
      <header className="admin-topbar">
        <button className="admin-menu-btn" onClick={onMenu} aria-label="เมนู"><i className="ph ph-list"></i></button>
        <LangPicker lang={lang} setLang={setLang} />
        <div className="admin-user-wrap" ref={wrapRef}>
          <button className="admin-avatar-btn" onClick={() => setOpen(o => !o)} title={user?.display_name || user?.email}>{initial}</button>
          {open && (
            <div className="admin-user-dropdown">
              <div className="admin-user-head">
                <b>{user?.display_name || user?.email}</b>
                <span>{user?.email}</span>
                <span className="admin-role-badge">{t('role_'+role)}</span>
              </div>
              {/* "โครงสร้างระบบ" ย้ายไปเมนูซ้ายมือแล้ว (id:'system' ใน sidebar) — เอาออกจาก dropdown นี้ */}
              <button className="admin-user-logout" onClick={() => { setOpen(false); onLogout(); }}><i className="ph ph-sign-out" aria-hidden="true"></i> {t('logout')}</button>
            </div>
          )}
        </div>
      </header>
    );
  }

  // Stats Card
  function StatCard({ icon, value, label, sub, color }) {
    return (
      <div className={`stat-card ${color || ''}`}>
        <div className="stat-top">
          <span className="stat-label">{label}</span>
          <span className="stat-ic"><i className={"ph " + icon} aria-hidden="true"></i></span>
        </div>
        <div className="stat-value">{value}</div>
        {sub && <div className="stat-sub">{sub}</div>}
      </div>
    );
  }

  // Table
  function Table({ columns, data, loading, empty = 'ไม่มีข้อมูล', emptyIcon = 'ph-tray', sort, onSort }) {
    if (loading) return <div className="loading"><span className="spinner" /> กำลังโหลด...</div>;
    if (!data?.length) return (
      <div className="empty-state">
        <div className="empty-ic"><i className={'ph ' + emptyIcon}></i></div>
        <div className="empty-msg">{empty}</div>
      </div>
    );
    const arrow = (key) => {
      const active = sort && sort.key === key;
      if (!active) return <span style={{opacity:.55,marginLeft:6,fontSize:12,color:'#2A6BE0'}}>⇅</span>;
      return <span style={{marginLeft:6,fontSize:13,color:'#2A6BE0',fontWeight:800}}>{sort.dir === 'asc' ? '▲' : '▼'}</span>;
    };
    return (
      <div className="table-wrap">
        <table className="table">
          <thead><tr>{columns.map((c,i) => {
            const sortable = c.sortKey && onSort;
            const active = sortable && sort && sort.key === c.sortKey;
            return (
              <th key={i}
                  className={sortable ? ('th-sort' + (active ? ' active' : '')) : undefined}
                  onClick={sortable ? () => onSort(c.sortKey) : undefined}>
                {c.label}{sortable ? arrow(c.sortKey) : null}
              </th>
            );
          })}</tr></thead>
          <tbody>{data.map((row, i) => (
            <tr key={row.id || i}>{columns.map((c,j) => <td key={j}>{c.render ? c.render(row, i) : row[c.key]}</td>)}</tr>
          ))}</tbody>
        </table>
      </div>
    );
  }

  // Badge — bilingual
  function Badge({ status }) {
    const lang = localStorage.getItem('admin_lang') || 'th';
    const map = {
      active:      ['badge-green',  { th: 'เผยแพร่/อนุมัติ', en: 'Active' }],
      pending:     ['badge-yellow', { th: 'รออนุมัติ', en: 'Pending' }],
      rejected:    ['badge-red',    { th: 'ปฏิเสธ', en: 'Rejected' }],
      published:   ['badge-green',  { th: 'เผยแพร่', en: 'Published' }],
      draft:       ['badge-gray',   { th: 'ร่าง', en: 'Draft' }],
      new:         ['badge-blue',   { th: 'ใหม่', en: 'New' }],
      contacted:   ['badge-yellow', { th: 'ติดต่อแล้ว', en: 'Contacted' }],
      in_progress: ['badge-purple', { th: 'กำลังดำเนิน', en: 'In Progress' }],
      closed:      ['badge-gray',   { th: 'ปิดแล้ว', en: 'Closed' }],
      user:        ['badge-gray',   { th: 'ผู้ใช้', en: 'User' }],
      agent:       ['badge-blue',   { th: 'ตัวแทน', en: 'Agent' }],
      admin:       ['badge-purple', { th: 'ผู้ดูแล', en: 'Admin' }],
      super_admin: ['badge-red',    { th: 'ผู้ดูแลสูงสุด', en: 'Super Admin' }],
      marketing:   ['badge-yellow', { th: 'การตลาด', en: 'Marketing' }],
    };
    const entry = map[status];
    if (!entry) return <span className="badge badge-gray">{status}</span>;
    return <span className={`badge ${entry[0]}`}>{entry[1][lang] || entry[1].th}</span>;
  }

  // Modal
  function Modal({ title, onClose, children }) {
    return (
      <div className="modal-overlay" onClick={onClose}>
        <div className="modal" onClick={e => e.stopPropagation()}>
          <div className="modal-header">
            <h3>{title}</h3>
            <button className="modal-close" onClick={onClose} aria-label="ปิด"><i className="ph ph-x"></i></button>
          </div>
          <div className="modal-body">{children}</div>
        </div>
      </div>
    );
  }

  // Confirm modal
  function Confirm({ message, onConfirm, onCancel, confirmLabel = 'ยืนยัน', danger }) {
    return (
      <Modal title="ยืนยันการดำเนินการ" onClose={onCancel}>
        <p style={{marginBottom: 20}}>{message}</p>
        <div className="row gap-10">
          <button className={`btn ${danger ? 'btn-danger' : 'btn-primary'}`} onClick={onConfirm}>{confirmLabel}</button>
          <button className="btn btn-ghost" onClick={onCancel}>ยกเลิก</button>
        </div>
      </Modal>
    );
  }

  // ใส่ลูกน้ำคั่นหลักพัน (เก็บค่าดิบ ไม่มีลูกน้ำ)
  function commafy(v) {
    if (v === '' || v == null) return '';
    if (typeof v === 'object') return ''; // กันค่าเพี้ยน (object หลุดเข้ามา) ไม่ให้ช่องโชว์ [object Object] ค้าง — พิมพ์ทับได้
    const s = String(v).replace(/,/g, '');
    if (s === '' || s === '-') return s;
    if (!/^-?\d*\.?\d*$/.test(s)) return ''; // ไม่ใช่ตัวเลข (เช่น "[object Object]" ที่ extension เติมมั่ว) → โชว์ว่าง พิมพ์ทับได้
    const neg = s[0] === '-' ? '-' : '';
    const [int, dec] = s.replace('-', '').split('.');
    const i = (int || '').replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    return neg + i + (dec !== undefined ? '.' + dec : '');
  }
  // input ตัวเลข — controlled ธรรมดา value=commafy(value) (แบบเดิมที่เคยเวิร์ค 945018f) + ล็อกตัวเลข + บรรทัดสรุปใต้ช่อง
  //   *สำคัญ: ไม่ใส่ autoComplete/name/data-lpignore/data-form-type — พวกนี้ (ใส่ตอนแก้ autofill) ทำให้ในช่องไม่โชว์ลูกน้ำ
  function NumInput({ value, onChange, allowDecimal, className, ...rest }) {
    const display = commafy(value);
    return (
      <>
        <input type="text" inputMode={allowDecimal ? 'decimal' : 'numeric'} className={className || 'input'}
          value={display}
          onKeyDown={e => {
            if (e.ctrlKey || e.metaKey || e.altKey || e.key.length > 1) return;
            if (!/[0-9]/.test(e.key) && !(allowDecimal && e.key === '.')) e.preventDefault(); // ล็อก: พิมพ์ได้แต่ตัวเลข
          }}
          onChange={e => {
            let raw = e.target.value.replace(/,/g, '');
            raw = allowDecimal ? raw.replace(/[^0-9.]/g, '') : raw.replace(/[^0-9]/g, '');
            onChange(raw);
          }} {...rest} />
        {display.includes(',') && <div style={{fontSize:12.5,color:'#0A7D46',marginTop:3,fontWeight:600}}>= {display}</div>}
      </>
    );
  }

  // hook ช่วยจัดลำดับตาราง — คืน { sorted, sort, onSort } เอาไปต่อกับ <Table sort onSort>
  // numKeys = รายชื่อ field ที่เป็นตัวเลข · field ที่ลงท้าย _at หรือมีคำว่า date จะเรียงแบบวันที่
  function useTableSort(data, defaultKey, defaultDir = 'desc', numKeys = []) {
    const [sort, setSort] = React.useState({ key: defaultKey, dir: defaultDir });
    const onSort = (key) => setSort(s => s.key === key ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'asc' });
    const isDate = (k) => /_at$/.test(k) || /date/i.test(k);
    const sorted = React.useMemo(() => {
      const arr = [...(data || [])];
      arr.sort((a, b) => {
        const k = sort.key;
        let av = a[k], bv = b[k];
        if (numKeys.includes(k)) { av = Number(av) || 0; bv = Number(bv) || 0; }
        else if (isDate(k)) { av = new Date(av || 0).getTime(); bv = new Date(bv || 0).getTime(); }
        else { av = (av ?? '').toString().toLowerCase(); bv = (bv ?? '').toString().toLowerCase(); }
        if (av < bv) return sort.dir === 'asc' ? -1 : 1;
        if (av > bv) return sort.dir === 'asc' ? 1 : -1;
        return 0;
      });
      return arr;
    }, [data, sort.key, sort.dir]);
    return { sorted, sort, onSort };
  }

  // ===== ปุ่มไอคอนมาตรฐาน: แก้ไข (ดินสอ) / ลบ (ถังขยะ) — ใช้เหมือนกันทุกหน้า =====
  function IconAction({ icon, color, hoverBg, title, onClick, disabled }) {
    const [hover, setHover] = useState(false);
    return (
      <button type="button" title={title} onClick={onClick} disabled={disabled}
        onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
        style={{
          width: 34, height: 34, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          border: '1px solid ' + (hover && !disabled ? color : '#E2E8F2'), borderRadius: 9,
          background: hover && !disabled ? hoverBg : '#fff', color, cursor: disabled ? 'default' : 'pointer',
          padding: 0, transition: 'all .12s', flex: '0 0 auto', opacity: disabled ? .4 : 1,
        }}>
        <i className={'ph ' + icon} style={{ fontSize: 16 }}></i>
      </button>
    );
  }
  const EditBtn = (p) => <IconAction icon="ph-pencil-simple" color="#5A6B84" hoverBg="#F0F5FF" title={p.title || 'แก้ไข'} onClick={p.onClick} disabled={p.disabled} />;
  const DeleteBtn = (p) => <IconAction icon="ph-trash" color="#E2603F" hoverBg="#FDF1EC" title={p.title || 'ลบ'} onClick={p.onClick} disabled={p.disabled} />;
  // ปุ่มมาตรฐานเพิ่ม (ใช้ IconAction เดียวกัน) — ยืนยัน/ปฏิเสธ ให้หน้าตาเหมือนกันทั้งระบบ
  const ApproveBtn = (p) => <IconAction icon="ph-check" color="#15976A" hoverBg="#E8F7F0" title={p.title || 'ยืนยัน'} onClick={p.onClick} disabled={p.disabled} />;
  const RejectBtn = (p) => <IconAction icon="ph-x" color="#D8567A" hoverBg="#FDF1F5" title={p.title || 'ปฏิเสธ'} onClick={p.onClick} disabled={p.disabled} />;
  window.EditBtn = EditBtn;
  window.DeleteBtn = DeleteBtn;
  window.ApproveBtn = ApproveBtn;
  window.RejectBtn = RejectBtn;

  // การ์ดกรองสถานะ (ใช้ซ้ำได้ทุกหน้า) — items: [{id,label,count,icon,color,bg,hot?}]
  //   active = id ที่เลือก · onSelect(id) เมื่อกด
  function FilterCards({ items, active, onSelect }) {
    return (
      <div className="filter-cards">
        {(items || []).map(it => {
          const on = active === it.id;
          return (
            <button key={it.id} type="button" className={'filter-card' + (on ? ' active' : '')}
              onClick={() => onSelect && onSelect(it.id)}
              style={{ borderColor: on ? it.color : undefined, background: on ? it.bg : undefined }}>
              <span className="fc-accent" style={{ background: it.color, opacity: on ? 1 : .3 }} />
              <div className="fc-top">
                <span className="fc-ic" style={{ background: it.bg, color: it.color }}><i className={'ph ' + it.icon}></i></span>
                {it.hot ? <span className="fc-dot" style={{ background: it.color }} title="มีรายการค้าง" /> : null}
              </div>
              <div className="fc-label">{it.label}</div>
              <div className="fc-value" style={{ color: (on || it.hot) ? it.color : undefined }}>{it.count}</div>
            </button>
          );
        })}
      </div>
    );
  }
  window.FilterCards = FilterCards;

  window.Sidebar = Sidebar;
  window.AdminTopBar = TopBar;
  window.StatCard = StatCard;
  window.Table = Table;
  window.useTableSort = useTableSort;
  window.Badge = Badge;
  window.Modal = Modal;
  window.Confirm = Confirm;
  window.NumInput = NumInput;
  window.commafy = commafy; // เปิดให้เรียกจากที่อื่น (เช่น debug readout ชั่วคราว)
  // กันพิมพ์ค่าติดลบในช่อง type=number (เครื่องหมาย - + e) — ใช้คู่กับ min="0"
  window.blockNeg = (e) => { if (['-','+','e','E'].includes(e.key)) e.preventDefault(); };
})();
