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

  // รายการโซนผังเมืองมาตรฐาน (ตรงกับ backend zoning.service) — ใช้ตอนแอดมินเลือกเอง
  const ZONE_LIST = [
    { code:'1110', label:'ที่อยู่อาศัยหนาแน่นน้อย', color:'#FFFF00' },
    { code:'1120', label:'ที่อยู่อาศัยหนาแน่นปานกลาง', color:'#FF7F00' },
    { code:'1130', label:'ที่อยู่อาศัยหนาแน่นมาก', color:'#A87000' },
    { code:'1999', label:'อนุรักษ์เพื่อการอยู่อาศัย', color:'#F57D96' },
    { code:'2000', label:'พาณิชยกรรมและที่อยู่อาศัยหนาแน่นมาก', color:'#FF0000' },
    { code:'3100', label:'อุตสาหกรรม', color:'#8400A8' },
    { code:'3200', label:'อุตสาหกรรมและคลังสินค้า', color:'#BF00FF' },
    { code:'3300', label:'คลังสินค้า', color:'#FF00FF' },
    { code:'3400', label:'อุตสาหกรรมเฉพาะกิจ', color:'#BF7FFF' },
    { code:'5500', label:'ที่โล่งเพื่อนันทนาการและรักษาคุณภาพสิ่งแวดล้อม', color:'#7FFFFF' },
    { code:'6100', label:'สถาบันการศึกษา', color:'#557300' },
    { code:'6200', label:'สถาบันศาสนา', color:'#ADADAD' },
    { code:'6300', label:'สถาบันราชการ สาธารณูปโภคและสาธารณูปการ', color:'#007FFF' },
    { code:'7110', label:'ชนบทและเกษตรกรรม', color:'#38A800' },
    { code:'7180', label:'อนุรักษ์ชนบทและเกษตรกรรม', color:'#4CE600' },
    { code:'7200', label:'ปฏิรูปที่ดินเพื่อเกษตรกรรม', color:'#E69800' },
    { code:'8600', label:'อนุรักษ์ป่าไม้', color:'#267300' },
    { code:'8900', label:'อนุรักษ์ทรัพยากรธรรมชาติและสิ่งแวดล้อม', color:'#C29ED7' },
  ];

  // ช่องผังเมือง: มีพิกัด→กดดึงจาก DPT อัตโนมัติ · ไม่มี/DPT ไม่มีข้อมูล→เลือกจาก dropdown เอง · เก็บลง form.zoning
  function ZoningField({ form, set }) {
    const [loading, setLoading] = useState(false);
    const [importing, setImporting] = useState(false);
    const [noData, setNoData] = useState(false);
    const [msg, setMsg] = useState('');
    const z = form.zoning;
    const hasLoc = form.lat != null && form.lng != null && String(form.lat).trim() !== '' && String(form.lng).trim() !== '';
    function apply(zone) { set('zoning', zone); set('city_plan', zone ? zone.label : ''); } // sync city_plan เดิมไว้ด้วย (recap/หน้าเว็บ)
    async function autoFetch() {
      setLoading(true); setMsg('');
      try {
        const r = await window.api.zoning(form.lat, form.lng);
        if (r && r.found) { apply({ code:r.code, label:r.label, color:r.color, plan:r.plan||'', source:'DPT', fetched_at:r.fetched_at }); setNoData(false); }
        else { setMsg('พื้นที่นี้ยังไม่มีข้อมูลผังเมืองในระบบ — กดปุ่มนำเข้าด้านล่าง หรือเลือกเอง'); setNoData(true); }
      } catch (e) { setMsg('ดึงไม่สำเร็จ: ' + (e.message || e)); }
      setLoading(false);
    }
    async function doImport() {
      const code = window.api.provinceCodeOf(form.province);
      if (!code) { setMsg('ไม่รู้จักจังหวัด "' + (form.province || '') + '" — เลือกเองด้านล่างได้'); return; }
      setImporting(true); setMsg('กำลังนำเข้าผังเมืองจังหวัด ' + form.province + '... (เบราว์เซอร์กำลังดึงจากกรมฯ)');
      try {
        const r = await window.api.importProvinceBrowser(code, (n) => setMsg('นำเข้าแล้ว ' + n + ' โซน...'));
        if (r && r.inserted > 0) { setMsg('นำเข้า ' + r.inserted + ' โซนแล้ว — กำลังดึงให้...'); setNoData(false); await autoFetch(); }
        else setMsg('จังหวัด ' + form.province + ' ยังไม่มีข้อมูลผังเมือง — เลือกเองด้านล่างได้');
      } catch (e) { setMsg('นำเข้าไม่สำเร็จ: ' + (e.message || e)); }
      setImporting(false);
    }
    return (
      <div className="form-group" style={{gridColumn:'span 2'}}>
        <label>ผังเมือง (การใช้ประโยชน์ที่ดิน)</label>
        {z && z.label &&
          <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:8,background:'#F7FAFF',border:'1px solid #E2E8F2',borderRadius:8,padding:'8px 10px'}}>
            <span style={{width:18,height:18,borderRadius:4,background:z.color||'#ccc',border:'1px solid rgba(0,0,0,.2)',flexShrink:0}} />
            <b style={{fontSize:13.5}}>{z.label}</b>
            <span style={{fontSize:11,color: z.source==='DPT' ? '#0A7D46':'#6B7280'}}>· {z.source==='DPT' ? 'อัตโนมัติจากกรมโยธาฯ' : 'เลือกเอง'}</span>
            <button type="button" onClick={()=>apply(null)} style={{marginLeft:'auto',border:'none',background:'transparent',color:'#D8567A',cursor:'pointer',fontSize:12,fontFamily:'inherit'}}>ล้าง</button>
          </div>}
        <div style={{display:'flex',gap:8,flexWrap:'wrap',alignItems:'center'}}>
          <button type="button" disabled={!hasLoc||loading} onClick={autoFetch}
            style={{padding:'9px 12px',borderRadius:8,border:'1px solid '+(hasLoc?'#2A6BE0':'#D8DEE9'),background: hasLoc?'#EEF4FF':'#F3F4F6',color:hasLoc?'#2A6BE0':'#9CA3AF',fontWeight:700,fontSize:12.5,cursor:(hasLoc&&!loading)?'pointer':'default',fontFamily:'inherit',whiteSpace:'nowrap'}}>
            <i className="ph ph-crosshair-simple" style={{marginRight:5}}></i>{loading?'กำลังดึง...':'ดึงจากพิกัดอัตโนมัติ'}</button>
          <select className="input" style={{flex:1,minWidth:180}} value={z?(z.code||''):''} onChange={e=>{
            const code=e.target.value; if(!code){ apply(null); return; }
            const o=ZONE_LIST.find(x=>x.code===code);
            if(o) apply({ code:o.code, label:o.label, color:o.color, source:'manual', fetched_at:new Date().toISOString() });
          }}>
            <option value="">— เลือกเอง —</option>
            {ZONE_LIST.map(o=><option key={o.code} value={o.code}>{o.label}</option>)}
          </select>
        </div>
        {!hasLoc && <div style={{fontSize:11.5,color:'#9A7400',marginTop:5}}><i className="ph ph-info" style={{marginRight:4}}></i>ยังไม่มีพิกัด — ปักหมุดบนแผนที่ก่อนถึงจะดึงอัตโนมัติได้ หรือเลือกเอง</div>}
        {msg && <div style={{fontSize:11.5,color:'#9A7400',marginTop:5}}>{msg}</div>}
        {noData && form.province &&
          <button type="button" disabled={importing} onClick={doImport}
            style={{marginTop:6,padding:'8px 12px',borderRadius:8,border:'1px solid #B5860B',background:'#FFF9EC',color:'#8A6D1B',fontWeight:700,fontSize:12.5,cursor:importing?'default':'pointer',fontFamily:'inherit'}}>
            <i className="ph ph-download-simple" style={{marginRight:5}}></i>{importing ? 'กำลังนำเข้า...' : 'นำเข้าข้อมูลผังเมืองจังหวัด ' + form.province}</button>}
      </div>
    );
  }

  // ราคาประเมินราชการ (กรมธนารักษ์): กรอก ระวาง(UTM 4 ส่วน)+เลขที่ดิน จากโฉนด → เบราว์เซอร์ยิง JSONP ดึง EVAPRICE → เติมมูลค่าประเมิน
  function LandPriceField({ form, set }) {
    const [m1, setM1] = useState(''); const [m2, setM2] = useState('');
    const [m3, setM3] = useState(''); const [m4, setM4] = useState('');
    const [scale, setScale] = useState(''); const [landno, setLandno] = useState('');
    const [loading, setLoading] = useState(false);
    const [res, setRes] = useState(null); // {price_per_wa, scale, exact} | {error}
    const dig = (setter) => (e) => setter((e.target.value || '').replace(/[^0-9]/g, ''));
    const wa1 = (Number(form.land_rai) || 0) * 400 + (Number(form.land_ngan) || 0) * 100 + (Number(form.land_wa) || 0);
    const totalWa = wa1 > 0 ? wa1 : (Number(form.land_sqw) || 0);
    const total = res && res.price_per_wa != null && totalWa > 0 ? Math.round(res.price_per_wa * totalWa) : null;
    const ready = form.province && m1 && m2 && m3 && m4 && landno;
    const fetchPrice = async () => {
      if (!ready) return;
      setLoading(true); setRes(null);
      try {
        const r = await window.api.landPrice(form.province, m1, m2, m3, m4, scale, landno);
        setRes(r.found ? r : { error: r.reason || 'ไม่พบข้อมูล' });
      } catch (e) { setRes({ error: 'ผิดพลาด: ' + (e.message || e) }); }
      setLoading(false);
    };
    const box = { padding: '5px 8px', border: '1px solid #d6dae1', borderRadius: 7, fontSize: 13, fontFamily: 'inherit', width: '100%', boxSizing: 'border-box' };
    return (
      <div style={{ marginTop: 12, padding: '12px 14px', background: '#F3F8FF', border: '1px solid #CFE0FA', borderRadius: 10 }}>
        <div style={{ fontWeight: 700, fontSize: 13.5, color: '#102E54', marginBottom: 3, display: 'flex', alignItems: 'center', gap: 6 }}>
          <i className="ph ph-bank"></i>ดึงราคาประเมินราชการ (กรมธนารักษ์)
        </div>
        <div style={{ fontSize: 11.5, color: '#5A6B82', marginBottom: 9 }}>
          อ่านจากโฉนด: <b>ระวาง UTM</b> 4 ช่อง (เช่น 5136 / 3 / 6416 / 11) + <b>เลขที่ดิน</b> · จังหวัดใช้ค่า "{form.province || '— ยังไม่เลือกจังหวัด —'}"
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill,minmax(92px,1fr))', gap: 8, alignItems: 'end' }}>
          <div><label style={{ fontSize: 11, color: '#5A6B82' }}>ระวาง 1</label><input inputMode="numeric" style={box} value={m1} onChange={dig(setM1)} placeholder="5136" /></div>
          <div><label style={{ fontSize: 11, color: '#5A6B82' }}>ระวาง 2</label><input inputMode="numeric" style={box} value={m2} onChange={dig(setM2)} placeholder="3" /></div>
          <div><label style={{ fontSize: 11, color: '#5A6B82' }}>ระวาง 3</label><input inputMode="numeric" style={box} value={m3} onChange={dig(setM3)} placeholder="6416" /></div>
          <div><label style={{ fontSize: 11, color: '#5A6B82' }}>ระวาง 4 (แผ่น)</label><input inputMode="numeric" style={box} value={m4} onChange={dig(setM4)} placeholder="11" /></div>
          <div><label style={{ fontSize: 11, color: '#5A6B82' }}>เลขที่ดิน</label><input inputMode="numeric" style={box} value={landno} onChange={dig(setLandno)} placeholder="203" /></div>
          <div><label style={{ fontSize: 11, color: '#5A6B82' }}>มาตราส่วน <span style={{ color: '#9AA6B5' }}>(ไม่บังคับ)</span></label><input inputMode="numeric" style={box} value={scale} onChange={dig(setScale)} placeholder="1000" /></div>
        </div>
        <button type="button" disabled={!ready || loading} onClick={fetchPrice}
          style={{ marginTop: 10, padding: '8px 16px', borderRadius: 8, border: 'none', background: ready && !loading ? '#1F58C4' : '#AEBACB', color: '#fff', fontWeight: 700, fontSize: 13, cursor: ready && !loading ? 'pointer' : 'default', fontFamily: 'inherit' }}>
          <i className="ph ph-magnifying-glass" style={{ marginRight: 6 }}></i>{loading ? 'กำลังดึง...' : 'ดึงราคาประเมิน'}</button>
        {res && res.error && <div style={{ marginTop: 8, fontSize: 12.5, color: '#B42318' }}><i className="ph ph-warning-circle" style={{ marginRight: 4 }}></i>{res.error}</div>}
        {res && res.price_per_wa != null && (
          <div style={{ marginTop: 9, padding: '9px 12px', background: '#EAF4EA', border: '1px solid #BEE3BE', borderRadius: 8, fontSize: 13 }}>
            <div style={{ fontWeight: 700, color: '#1B6B2E' }}>ราคาประเมิน {res.price_per_wa.toLocaleString('th-TH')} บาท/ตร.ว. <span style={{ fontWeight: 500, fontSize: 11.5, color: '#5A6B82' }}>(มาตราส่วน 1:{res.scale}{!res.exact && ' · แผ่นใกล้เคียง'})</span></div>
            {totalWa > 0
              ? <div style={{ marginTop: 4, color: '#25422C' }}>× เนื้อที่ {totalWa.toLocaleString('th-TH')} ตร.ว. = <b>{total.toLocaleString('th-TH')} บาท</b>
                  <button type="button" onClick={() => set('appraised_value', String(total))} style={{ marginLeft: 10, padding: '4px 12px', borderRadius: 7, border: '1px solid #1B6B2E', background: '#fff', color: '#1B6B2E', fontWeight: 700, fontSize: 12, cursor: 'pointer', fontFamily: 'inherit' }}>ใส่ในมูลค่าประเมิน</button></div>
              : <div style={{ marginTop: 4, color: '#8A6D1B' }}>กรอกเนื้อที่ดิน (ไร่-งาน-วา) ด้านบนก่อน ระบบจะคูณให้เป็นมูลค่ารวมอัตโนมัติ
                  <button type="button" onClick={() => set('appraised_value', String(res.price_per_wa))} style={{ marginLeft: 10, padding: '4px 10px', borderRadius: 7, border: '1px solid #8A6D1B', background: '#fff', color: '#8A6D1B', fontWeight: 700, fontSize: 12, cursor: 'pointer', fontFamily: 'inherit' }}>ใส่ราคาต่อ ตร.ว.</button></div>}
          </div>
        )}
      </div>
    );
  }

  // ===== ชุดข้อมูลดีลขายฝาก/จำนอง =====
  // ===== ชุดประเภททรัพย์มาตรฐาน (ใช้ทั้งระบบ) =====
  const PTYPES = [
    { v: 'land_bare',         l: 'ที่ดินเปล่า',            icon: 'ph-tree' },
    { v: 'land_building',     l: 'ที่ดิน + สิ่งปลูกสร้าง',  icon: 'ph-house-line' },
    { v: 'house_townhome',    l: 'บ้าน / ทาวน์โฮม',        icon: 'ph-house' },
    { v: 'condo',             l: 'คอนโด',                 icon: 'ph-buildings' },
    { v: 'factory_warehouse', l: 'โรงงาน + โกดัง',         icon: 'ph-factory' },
    { v: 'hotel_apartment',   l: 'โรงแรม / หอพัก',         icon: 'ph-building' },
    { v: 'commercial',        l: 'อาคารพาณิชย์',           icon: 'ph-storefront' },
  ];
  const PTYPE_LABEL = Object.fromEntries(PTYPES.map(t => [t.v, t.l]));
  const CONSIGN_PTYPES = PTYPES; // backward alias
  const WORK_STATUSES = ['ดำเนินการ','เคสชน','ปรับเงื่อนไข','รอผลพิจารณา','อนุมัติ','ไม่อนุมัติ','อนุมัติแบบมีเงื่อนไข','ส่งพิจารณาใหม่','อื่นๆ/ยกเลิก','รอตัดสินใจ','รอส่งงาน'];
  let INITIAL_HUB_TAB = 'basic'; // แท็บเริ่มต้นเมื่อเปิด hub จัดการทรัพย์ (คลิก "สถานะดีล" ในตาราง → 'deal')
  // สีตามความหมาย: รอ=เหลือง/เทา · กำลังทำ=เขียวอ่อน/ฟ้า · ผ่าน=เขียว · ไม่ผ่าน/ยกเลิก=แดง · มีเงื่อนไข=น้ำตาล/ม่วง
  const WORK_STATUS_STYLE = {
    'ดำเนินการ':          { bg:'#DBF1E7', fg:'#0F7A4D' },
    'เคสชน':              { bg:'#EDE4FB', fg:'#6D3BBF' },
    'ปรับเงื่อนไข':        { bg:'#DDE9FC', fg:'#1F58C4' },
    'รอผลพิจารณา':        { bg:'#FBF0D0', fg:'#9A7400' },
    'อนุมัติ':             { bg:'#15976A', fg:'#FFFFFF' },
    'ไม่อนุมัติ':           { bg:'#FBE4EC', fg:'#C0344F' },
    'อนุมัติแบบมีเงื่อนไข': { bg:'#9A6A1B', fg:'#FFFFFF' },
    'ส่งพิจารณาใหม่':      { bg:'#7C3AED', fg:'#FFFFFF' },
    'อื่นๆ/ยกเลิก':        { bg:'#C0202A', fg:'#FFFFFF' },
    'รอตัดสินใจ':          { bg:'#2563C9', fg:'#FFFFFF' },
    'รอส่งงาน':            { bg:'#E5E7EB', fg:'#4B5563' },
  };
  function WorkStatusBadge({ status }) {
    if (!status) return null;
    const s = WORK_STATUS_STYLE[status] || { bg:'#E5E7EB', fg:'#4B5563' };
    return <span style={{display:'inline-flex',alignItems:'center',padding:'3px 11px',borderRadius:99,fontSize:12,fontWeight:600,background:s.bg,color:s.fg,whiteSpace:'nowrap'}}>{status}</span>;
  }
  // หมวดทรัพย์: ขายขาด (เขียว) · จำนอง/ขายฝาก (เหลือง)
  const DEAL_TYPE_STYLE = {
    sale:        { bg:'#DBF1E7', fg:'#0F7A4D', label:'ทรัพย์ฝากขาย' },
    mortgage:    { bg:'#FBF0D0', fg:'#9A7400', label:'จำนอง' },
    consignment: { bg:'#FBF0D0', fg:'#9A7400', label:'ขายฝาก' },
    npl:         { bg:'#FDECEC', fg:'#C0392B', label:'ทรัพย์ NPL' },
  };
  function DealTypeBadge({ type }) {
    const s = DEAL_TYPE_STYLE[type] || DEAL_TYPE_STYLE.sale;
    return <span style={{display:'inline-flex',alignItems:'center',padding:'3px 11px',borderRadius:99,fontSize:12,fontWeight:600,background:s.bg,color:s.fg,whiteSpace:'nowrap'}}>{s.label}</span>;
  }
  // คอลัมน์หมวด — ทรัพย์รับสองแบบ(จำนอง+ขายฝาก) โชว์สองป้าย (จำนองบน ขายฝากล่าง)
  function DealTypeCell({ row }) {
    const it = Array.isArray(row.invest_types) ? row.invest_types : [];
    if (it.length > 1) {
      const types = ['mortgage','consignment'].filter(x => it.includes(x));
      return <span style={{display:'inline-flex',flexDirection:'row',gap:6,alignItems:'center',flexWrap:'wrap'}}>
        {types.map(tp => <DealTypeBadge key={tp} type={tp} />)}
      </span>;
    }
    return <DealTypeBadge type={row.listing_type} />;
  }

  // สวิตช์ เปิด/ปิด การแสดงหน้าเว็บ (active=แสดง · inactive/draft=ซ่อน) ในตารางหน้ารวม
  //   สถานะ pending/rejected ไม่ใช่สิ่งที่ toggle ได้ — โชว์ badge แทน (ใช้ปุ่มอนุมัติ/ปฏิเสธ)
  function WebToggle({ row, readOnly }) {
    const [status, setStatus] = useState(row.status);
    useEffect(() => { setStatus(row.status); }, [row.id, row.status]); // sync กับข้อมูลที่โหลดใหม่ (กันปุ่มค้างค่าเก่าหลัง bulk/รีโหลด)
    const [busy, setBusy] = useState(false);
    if (status === 'pending' || status === 'rejected') return <Badge status={status} />;
    // การตลาด (ดูอย่างเดียว) — โชว์สถานะเฉยๆ ไม่ให้กดสลับ
    if (readOnly) { const on = status === 'active'; return <span className={'badge ' + (on ? 'badge-green' : 'badge-gray')}>{on ? 'แสดง' : 'ซ่อน'}</span>; }
    // ทรัพย์ AMC ที่กรรมสิทธิ์ยังไม่ใช่ AMC → ล็อก เปิดแสดงไม่ได้
    if (row.npl_locked) return (
      <span title='ทรัพย์ AMC: สถานะกรรมสิทธิ์ยังไม่ใช่ "กรรมสิทธิ์ AMC" — เปิดแสดงบนเว็บไม่ได้'
        style={{display:'inline-flex',alignItems:'center',gap:6,color:'#94A3B8',fontSize:12.5,fontWeight:600,cursor:'not-allowed'}}>
        <i className="ph ph-lock-simple" style={{fontSize:15}}></i>ล็อก (ไม่ใช่ AMC)
      </span>
    );
    const on = status === 'active';
    async function toggle() {
      setBusy(true);
      try { await window.api.setListingVisibility(row.id, !on); setStatus(on ? 'inactive' : 'active'); } catch (e) { alert(e.message || 'ทำรายการไม่สำเร็จ'); }
      setBusy(false);
    }
    return (
      <button onClick={toggle} disabled={busy} title={on ? 'กำลังแสดงบนเว็บ — คลิกเพื่อซ่อน' : 'ซ่อนอยู่ — คลิกเพื่อแสดง'}
        style={{display:'inline-flex',alignItems:'center',gap:8,border:'none',background:'none',cursor:busy?'wait':'pointer',padding:0,fontFamily:'inherit',opacity:busy?.6:1}}>
        <span style={{width:38,height:22,borderRadius:99,background:on?'#15976A':'#CBD5E1',position:'relative',transition:'background .15s',flexShrink:0}}>
          <span style={{position:'absolute',top:2,left:on?18:2,width:18,height:18,borderRadius:'50%',background:'#fff',transition:'left .15s',boxShadow:'0 1px 3px rgba(0,0,0,.25)'}} />
        </span>
        <span style={{fontSize:12.5,fontWeight:600,color:on?'#15976A':'#64748B'}}>{on ? 'แสดง' : 'ซ่อน'}</span>
      </button>
    );
  }

  // ปุ่ม ? เล็กๆ ข้างหัวข้อ — กดแล้วโชว์คำอธิบายเป็น popover (ประหยัดพื้นที่กว่ากล่อง info)
  function Hint({ children, color = '#1F58C4' }) {
    const [open, setOpen] = useState(false);
    const ref = useRef(null);
    useEffect(() => {
      if (!open) return;
      const close = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
      document.addEventListener('mousedown', close);
      return () => document.removeEventListener('mousedown', close);
    }, [open]);
    return (
      <span ref={ref} style={{position:'relative',display:'inline-flex',verticalAlign:'middle'}}>
        <button type="button" onClick={() => setOpen(o => !o)}
          style={{width:18,height:18,borderRadius:'50%',border:'1.5px solid '+color,background: open?color:'transparent',color: open?'#fff':color,fontSize:12,fontWeight:700,lineHeight:1,cursor:'pointer',display:'inline-flex',alignItems:'center',justifyContent:'center',padding:0,fontFamily:'inherit'}}
          title="ดูคำอธิบาย">?</button>
        {open && (
          <span style={{position:'absolute',top:24,left:0,zIndex:50,width:340,maxWidth:'80vw',background:'#fff',border:'1px solid #E2E8F2',borderRadius:10,boxShadow:'0 8px 24px rgba(0,0,0,.14)',padding:'12px 14px',fontSize:13,fontWeight:400,color:'#334',lineHeight:1.6,whiteSpace:'normal',textAlign:'left'}}>
            {children}
          </span>
        )}
      </span>
    );
  }
  const FIS = [
    { key: 'icbc', name: 'ICBC (Asia)', logo: 'https://icons.duckduckgo.com/ip3/icbc.com.cn.ico' },
    { key: 'kbank', name: 'ธนาคารกสิกรไทย', logo: 'https://icons.duckduckgo.com/ip3/kasikornbank.com.ico' },
    { key: 'scb', name: 'SCB ไทยพาณิชย์', logo: 'https://icons.duckduckgo.com/ip3/scb.co.th.ico' },
    { key: 'krungsri', name: 'krungsri กรุงศรี', logo: 'https://icons.duckduckgo.com/ip3/krungsri.com.ico' },
    { key: 'sawad', name: 'ศรีสวัสดิ์', logo: 'https://icons.duckduckgo.com/ip3/sawad.co.th.ico' },
    { key: 'carfinn', name: 'CarFinn', logo: 'https://icons.duckduckgo.com/ip3/carfinn.com.ico' },
    { key: 'tidlor', name: 'เงินติดล้อ', logo: 'https://icons.duckduckgo.com/ip3/tidlor.com.ico' },
    { key: 'snn', name: 'SNN', logo: '' }, { key: 'jsmoney', name: 'JS Money', logo: '' },
    { key: 'ngernchaiyo', name: 'เงินไชโย', logo: '' },
  ];

  // dropdown สถาบันการเงิน พร้อมโลโก้หน้าชื่อ (native select ใส่รูปไม่ได้)
  function FiSelect({ value, onChange }) {
    const [open, setOpen] = useState(false);
    const ref = useRef(null);
    useEffect(() => {
      const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
      document.addEventListener('mousedown', h); return () => document.removeEventListener('mousedown', h);
    }, []);
    const cur = FIS.find(x => x.key === value);
    const Row = ({ o }) => (
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '8px 12px', cursor: 'pointer' }}
        onMouseEnter={e => e.currentTarget.style.background = '#F4F8FD'} onMouseLeave={e => e.currentTarget.style.background = '#fff'}
        onClick={() => { onChange(o ? o.key : ''); setOpen(false); }}>
        {o && o.logo ? <img src={o.logo} style={{ width: 20, height: 20, objectFit: 'contain' }} alt="" /> : <span style={{ width: 20 }} />}
        <span>{o ? o.name : '— ไม่ระบุ —'}</span>
      </div>
    );
    return (
      <div ref={ref} style={{ position: 'relative' }}>
        <button type="button" className="input" style={{ display: 'flex', alignItems: 'center', gap: 9, textAlign: 'left', cursor: 'pointer' }} onClick={() => setOpen(o => !o)}>
          {cur ? <>{cur.logo && <img src={cur.logo} style={{ width: 20, height: 20, objectFit: 'contain' }} alt="" />}<span>{cur.name}</span></> : <span style={{ color: '#9AA0AA' }}>เลือกสถาบันการเงิน</span>}
        </button>
        {open && <div style={{ position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 50, background: '#fff', border: '1px solid #E2E8F2', borderRadius: 8, maxHeight: 260, overflowY: 'auto', boxShadow: '0 8px 24px rgba(0,0,0,.12)', marginTop: 4 }}>
          <Row o={null} />
          {FIS.map(o => <Row key={o.key} o={o} />)}
        </div>}
      </div>
    );
  }

  // แผงจัดการดีล (CRM) — เปิดจากหน้าจัดการทรัพย์: ไทม์ไลน์ + เปลี่ยนสถานะเร็ว + โน้ต
  // แถวผู้สนใจ 1 คน — อัปเดตสถานะ + โน้ต + ไทม์ไลน์ (sync กับ "รายการที่ต้องติดตาม")
  const LEAD_ST = { new: ['badge-yellow', 'ใหม่'], contacted: ['badge-blue', 'ติดต่อแล้ว'], closed: ['badge-gray', 'ปิดดีล'] };
  // สถานะยืนยันตัวตนของผู้สนใจฝั่งลงทุน — บอกทีมว่าต้องตามต่อแค่ไหน
  const KYC_ST = {
    no_account: ['badge-red', 'ยังไม่สมัคร'],
    unverified: ['badge-yellow', 'ยังไม่ทำ KYC'],
    pending: ['badge-blue', 'รอตรวจ KYC'],
    rejected: ['badge-red', 'KYC ไม่ผ่าน'],
    verified: ['badge-green', 'KYC ผ่านแล้ว'],
  };
  function InterestedRow({ listingId, p, onChanged, onOpenCustomer }) {
    const [open, setOpen] = useState(false);
    const [status, setStatus] = useState(p.status || 'new');
    const [note, setNote] = useState('');
    const [tl, setTl] = useState(null);
    const [busy, setBusy] = useState(false);
    const base = '/admin/listings/' + listingId + '/interested/' + p.kind + '/' + p.id;
    function loadTl() { window.api.get(base + '/timeline').then(d => setTl(d.activities || [])).catch(() => setTl([])); }
    function toggle() { setOpen(o => { if (!o && tl === null) loadTl(); return !o; }); }
    async function save() {
      if (status === p.status && !note.trim()) return;
      setBusy(true);
      try {
        await window.api.post(base, { status, note: note.trim() || undefined });
        p.status = status; setNote(''); loadTl(); onChanged && onChanged();
      } catch (e) { alert('บันทึกไม่สำเร็จ'); }
      setBusy(false);
    }
    const sm = LEAD_ST[p.status] || ['badge-gray', p.status];
    const km = p.kyc ? (KYC_ST[p.kyc] || null) : null;
    return (
      <div style={{ border: '0.5px solid var(--border)', borderRadius: 10, padding: '10px 12px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13.5, fontWeight: 600 }}>
              {p.phone && onOpenCustomer
                ? <span className="cust-clickable" onClick={() => onOpenCustomer({ phone: p.phone, name: p.name })} title="ดูโปรไฟล์ลูกค้า (ทรัพย์อื่นที่สนใจ + ประวัติติดตาม)">{p.name || '-'}</span>
                : (p.name || '-')}
              {' '}{p.is_investor ? <span style={{ fontSize: 11, color: '#1F58C4' }}>· นักลงทุน</span> : <span style={{ fontSize: 11, color: 'var(--text-2)' }}>· ผู้สนใจซื้อ</span>}</div>
            <div style={{ fontSize: 12, color: 'var(--text-2)' }}>{p.phone ? '📞 ' + p.phone : (p.email ? '✉️ ' + p.email : '—')}{p.note ? ' · โน้ต: ' + p.note : ''}</div>
          </div>
          {Number(p.interest_count) > 1 && (
            <span className="badge badge-red" title="ลูกค้ากดสนใจซ้ำ — ควรรีบติดต่อกลับ">🔔 ตามมา {p.interest_count} ครั้ง</span>
          )}
          {km && <span className={'badge ' + km[0]}>{km[1]}</span>}
          <span className={'badge ' + sm[0]}>{sm[1]}</span>
          <button className="btn btn-sm btn-ghost" onClick={toggle}>{open ? 'ปิด' : 'อัปเดต'}</button>
        </div>
        {open && (
          <div style={{ marginTop: 8, borderTop: '0.5px solid var(--border)', paddingTop: 8 }}>
            <select className="input input-sm" value={status} onChange={e => setStatus(e.target.value)}>
              <option value="new">ใหม่ / ยังไม่ติดต่อ</option>
              <option value="contacted">ติดต่อแล้ว</option>
              <option value="closed">ปิดดีล</option>
            </select>
            <input className="input input-sm" style={{ marginTop: 6 }} placeholder="โน้ต เช่น โทรแล้ว นัดดูทรัพย์ศุกร์นี้" value={note} onChange={e => setNote(e.target.value)} />
            <button className="btn btn-primary btn-sm" style={{ marginTop: 6 }} disabled={busy} onClick={save}>{busy ? 'กำลังบันทึก...' : 'บันทึก'}</button>
            {tl && tl.length > 0 && (
              <div style={{ marginTop: 8, fontSize: 12, color: 'var(--text-2)', borderLeft: '2px solid var(--border)', paddingLeft: 10, display: 'flex', flexDirection: 'column', gap: 5 }}>
                {tl.map(a => (<div key={a.id}>
                  {a.to_status && <span>→ <b>{LEAD_ST[a.to_status] ? LEAD_ST[a.to_status][1] : a.to_status}</b> </span>}
                  {a.note && <span>“{a.note}” </span>}
                  <span style={{ color: '#9AA6B8' }}>· {a.user_name || 'ระบบ'} · {new Date(a.created_at).toLocaleString('th-TH', { dateStyle: 'short', timeStyle: 'short' })}</span>
                </div>))}
              </div>
            )}
          </div>
        )}
      </div>
    );
  }
  function InterestedList({ listingId }) {
    const [people, setPeople] = useState(null);
    const [err, setErr] = useState('');       // เดิม catch เงียบแล้วโชว์ "ยังไม่มีคนสนใจ" → แยกไม่ออกว่าไม่มีหรือโหลดพัง
    const [loading, setLoading] = useState(false);
    const [cust, setCust] = useState(null);   // คลิกชื่อ → เปิดโปรไฟล์ลูกค้า (ทรัพย์อื่น + ประวัติ)
    function load() {
      setLoading(true); setErr('');
      window.api.get('/admin/listings/' + listingId + '/interested')
        .then(d => { setPeople(d.people || []); setLoading(false); })
        .catch(e => { setErr((e && e.message) || 'โหลดรายชื่อผู้สนใจไม่สำเร็จ'); setLoading(false); });
    }
    useEffect(() => { load(); }, [listingId]);
    // โหลดเอง+รีโหลดหลังอัปเดตสถานะอยู่แล้ว จึงไม่ต้องมีปุ่มรีเฟรช (เอาออกลดรก)
    const bar = (
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
        <span style={{ fontSize: 13, fontWeight: 600 }}>
          ผู้สนใจ {people ? people.length : '—'} คน{loading && people ? ' · กำลังอัปเดต…' : ''}
        </span>
      </div>
    );
    if (err) return (
      <div>
        <div style={{ fontSize: 13, color: '#D8567A', background: '#FFF3F6', border: '1px solid #F6C9D6', borderRadius: 10, padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
          <span>โหลดไม่สำเร็จ: {err}</span>
          <button className="btn btn-sm btn-ghost" onClick={load} disabled={loading}>
            <i className="ph ph-arrows-clockwise" style={{ marginRight: 4 }}></i>{loading ? 'กำลังลอง...' : 'ลองใหม่'}
          </button>
        </div>
      </div>
    );
    if (people === null) return <div style={{ fontSize: 13, color: 'var(--text-2)' }}>กำลังโหลด...</div>;
    if (people.length === 0) return <div>{bar}<div style={{ fontSize: 13, color: 'var(--text-2)' }}>ยังไม่มีคนสนใจทรัพย์นี้</div></div>;
    return <div>{bar}<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>{people.map(p => <InterestedRow key={p.kind + p.id} listingId={listingId} p={p} onChanged={load} onOpenCustomer={setCust} />)}</div>
      {cust && <window.CustomerDrawer phone={cust.phone} name={cust.name} onClose={() => setCust(null)} />}
    </div>;
  }

  // แท็บ "สถานะดีล + ไทม์ไลน์" ในหน้าจัดการทรัพย์ (เนื้อหาเดียวกับ DealPanel แต่เป็นแท็บเต็ม)
  function DealTab({ id, initialStatus, onChanged }) {
    const [acts, setActs] = useState(null);
    const [note, setNote] = useState('');
    const [funder, setFunder] = useState('');
    const [status, setStatus] = useState(initialStatus || '');
    const [curStatus, setCurStatus] = useState(initialStatus || '');
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState('');
    function loadActs() {
      window.api.get('/admin/listings/' + id + '/activities').then(d => {
        const a = d.activities || []; setActs(a);
        const last = a.find(x => x.type === 'status');
        if (last && last.to_status) { setCurStatus(last.to_status); setStatus(s => s || last.to_status); }
      }).catch(() => setActs([]));
    }
    useEffect(() => { loadActs(); }, [id]);
    async function save() {
      const changed = status && status !== curStatus;
      if (!note.trim() && !funder.trim() && !changed) { setErr('ใส่โน้ต แหล่งทุน หรือเปลี่ยนสถานะก่อน'); return; }
      setBusy(true); setErr('');
      try {
        const body = {}; if (note.trim()) body.note = note.trim(); if (funder.trim()) body.funder = funder.trim(); if (changed) body.work_status = status;
        const d = await window.api.post('/admin/listings/' + id + '/activities', body);
        if (d && d.error) { setErr(d.error); setBusy(false); return; }
        setNote(''); setFunder(''); if (changed) setCurStatus(status);
        loadActs(); onChanged && onChanged();
      } catch (e) { setErr('บันทึกไม่สำเร็จ: ' + e.message); }
      setBusy(false);
    }
    return (
      <div style={{ maxWidth: 680 }}>
        <div className="card" style={{ padding: 18, marginBottom: 18 }}>
          <label style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--navy)' }}>อัปเดตสถานะดีล (ทีม) + โน้ต
            {curStatus && <span style={{ marginLeft: 8 }}><WorkStatusBadge status={curStatus} /></span>}</label>
          <div style={{ fontSize: 12, color: 'var(--text-2)', margin: '4px 0 10px' }}>ความคืบหน้าการทำดีลภายในทีม (ส่งแหล่งทุน / ติดตามผล) — คนละเรื่องกับการแสดงบนเว็บ</div>
          <select className="input" value={status} onChange={e => setStatus(e.target.value)}>
            <option value="">— เลือกสถานะ —</option>
            {WORK_STATUSES.map(s => <option key={s} value={s}>{s}</option>)}
          </select>
          <input className="input" style={{ marginTop: 8 }} placeholder="ส่งไปแหล่งทุน (ธนาคาร/กองทุน/นักลงทุน)" value={funder} onChange={e => setFunder(e.target.value)} />
          <textarea className="input" style={{ marginTop: 8 }} rows={2} placeholder="โน้ต เช่น ธนาคารขอสลิปเงินเดือนเพิ่ม" value={note} onChange={e => setNote(e.target.value)} />
          {err && <div style={{ color: '#D8567A', fontSize: 12, marginTop: 6 }}>{err}</div>}
          <button className="btn btn-primary btn-sm" style={{ marginTop: 10 }} disabled={busy} onClick={save}>{busy ? 'กำลังบันทึก...' : 'บันทึก'}</button>
        </div>
        <div style={{ fontSize: 11.5, textTransform: 'uppercase', letterSpacing: '.04em', color: 'var(--text-2)', marginBottom: 12 }}>ไทม์ไลน์ดีล (ทีม)</div>
        {acts === null ? <div style={{ color: 'var(--text-2)', fontSize: 13 }}>กำลังโหลด...</div>
          : acts.length === 0 ? <div style={{ color: 'var(--text-2)', fontSize: 13 }}>ยังไม่มีกิจกรรม — เริ่มบันทึกได้เลย</div>
          : <div style={{ borderLeft: '2px solid var(--border)', paddingLeft: 14, display: 'flex', flexDirection: 'column', gap: 14 }}>
              {acts.map(a => (
                <div key={a.id} style={{ position: 'relative' }}>
                  <span style={{ position: 'absolute', left: -21, top: 3, width: 10, height: 10, borderRadius: '50%', background: a.type === 'status' ? '#2A6BE0' : '#9AA6B8' }} />
                  {a.type === 'status'
                    ? <div style={{ fontSize: 13 }}>เปลี่ยนสถานะ <span style={{ color: 'var(--text-2)' }}>{a.from_status || '—'} →</span> <b>{a.to_status}</b></div>
                    : <div style={{ fontSize: 13 }}>บันทึกโน้ต</div>}
                  {a.funder && <div style={{ fontSize: 12.5, color: '#1F58C4', marginTop: 2, display: 'inline-flex', alignItems: 'center', gap: 4 }}><i className="ph ph-bank" style={{ fontSize: 13 }}></i> ส่งไป: <b>{a.funder}</b></div>}
                  {a.note && <div style={{ fontSize: 12.5, color: 'var(--text-2)', marginTop: 1 }}>“{a.note}”</div>}
                  <div style={{ fontSize: 11.5, color: '#9AA6B8', marginTop: 2 }}>{a.user_name || 'ระบบ'} · {new Date(a.created_at).toLocaleString('th-TH')}</div>
                </div>
              ))}
            </div>}
      </div>
    );
  }

  // หมวดเอกสารแนบใบขอสินเชื่อ (เก็บ private ใน GCS — เปิดดูผ่าน signed URL เฉพาะทีมงาน)
  const LOAN_DOC_CATS = [['deed', 'เอกสารโฉนด'], ['appraisal', 'เล่มประเมิน'], ['statement', 'สเตทเมนท์'], ['other', 'เอกสารอื่นๆ']];

  // แท็บ "ใบขอสินเชื่อ": กรอกข้อมูลสินเชื่อ (เก็บใน form.loan_form) + เอกสารแนบ + พิมพ์ใบขอพิจารณาอนุมัติวงเงิน (พร้อมรูปทรัพย์)
  function LoanFormTab({ form, set, images, id }) {
    const lf = form.loan_form || {};
    const setLF = (k, v) => set('loan_form', { ...lf, [k]: v });
    const ltMap = { mortgage: 'จำนอง', consignment: 'ขายฝาก' };
    const baht = v => { const n = Number(String(v == null ? '' : v).replace(/,/g, '')); return isFinite(n) && n ? n.toLocaleString('en-US') : ''; };
    // ค่า default ดึงจากข้อมูลทรัพย์ (ถ้าไม่กรอกในฟอร์มนี้)
    const dft = {
      customer_name: form.owner_name || '',
      locations: [form.province, form.district].filter(Boolean).join(' / '),
      loan_type: ltMap[form.listing_type] || form.listing_type || '',
      loan_amount: baht(form.max_loan),
      collateral: form.description || '',
      appraisal: form.appraised_value ? baht(form.appraised_value) + ' บาท' : '',
      occupation: '', income: '', purpose: '',
    };
    const val = k => (lf[k] != null && String(lf[k]).trim()) ? lf[k] : dft[k];
    // ประเภทลูกค้า: บุคคลธรรมดา (ชื่อ-สกุล) / นิติบุคคล (ชื่อบริษัท)
    const ctype = lf.customer_type === 'juristic' ? 'juristic' : 'individual';
    const isJuristic = ctype === 'juristic';
    const custTypeTh = isJuristic ? 'นิติบุคคล' : 'บุคคลธรรมดา';
    const custName = isJuristic ? (String(lf.company_name || '').trim() || val('customer_name')) : val('customer_name');

    // ===== เอกสารแนบ =====
    const [docs, setDocs] = useState([]);
    const [docBusy, setDocBusy] = useState('');
    function loadDocs() {
      if (!id) return;
      window.api.get('/admin/listings/' + id).then(d => setDocs(d.documents || [])).catch(() => {});
    }
    useEffect(() => { loadDocs(); }, [id]);

    // อัปโหลดตรงเข้า GCS (ข้ามเซิร์ฟเวอร์ → รับไฟล์ใหญ่ได้ ไม่ติดเพดาน Cloud Run 32MB)
    //   1) ขอ signed URL  2) PUT ไฟล์ตรงเข้า GCS  3) ยืนยันกลับ backend บันทึกลงระบบ
    async function uploadDoc(cat, e) {
      const files = Array.from(e.target.files || []);
      if (!files.length || !id) return;
      const MAX = 200 * 1024 * 1024;   // กันพลาดอัปไฟล์มหึมา (200MB) — ไม่ใช่ลิมิตจริงของ GCS
      const base = '/admin/listings/' + id + '/documents';
      setDocBusy(cat);
      for (const f of files) {
        try {
          if (f.size > MAX) { alert('ไฟล์ใหญ่เกินไป: ' + f.name + ' (' + (f.size/1048576).toFixed(0) + ' MB)'); continue; }
          const ctype = f.type || 'application/octet-stream';
          // 1) ขอ signed URL
          const s = await window.api.post(base + '/signed-upload', { filename: f.name, contentType: ctype, category: cat });
          if (!s || !s.uploadUrl) throw new Error('ขอลิงก์อัปโหลดไม่สำเร็จ');
          // 2) PUT ตรงเข้า GCS (ต้องใช้ contentType ตัวเดียวกับที่เซ็น)
          const put = await fetch(s.uploadUrl, { method: 'PUT', headers: { 'Content-Type': s.contentType }, body: f });
          if (!put.ok) throw new Error('อัปโหลดไปที่จัดเก็บไม่สำเร็จ (HTTP ' + put.status + ')');
          // 3) ยืนยัน → บันทึกลงระบบ
          await window.api.post(base + '/confirm', { key: s.key, filename: f.name, category: cat });
        } catch (err) { alert('อัปโหลดไม่สำเร็จ: ' + ((err && err.message) || '') + '\n(' + f.name + ')'); }
      }
      if (e.target) e.target.value = '';
      setDocBusy('');
      loadDocs();
    }
    async function viewDoc(docId) {
      // เปิดแท็บว่างทันทีตอนคลิก (เป็น user gesture) แล้วค่อยชี้ไป signed URL
      //   ถ้าเรียก window.open หลัง await เบราว์เซอร์จะบล็อกป๊อปอัป = "เปิดไม่ได้บางครั้ง"
      const win = window.open('', '_blank');
      try {
        const d = await window.api.get('/admin/listings/' + id + '/documents/' + docId + '/url');
        if (d && d.url) { if (win) win.location.href = d.url; else window.location.href = d.url; }
        else { if (win) win.close(); alert('เปิดไฟล์ไม่สำเร็จ'); }
      } catch (e) { if (win) win.close(); alert('เปิดไฟล์ไม่สำเร็จ: ' + ((e && e.message) || '')); }
    }
    async function delDoc(docId) {
      if (!window.confirm('ลบเอกสารนี้?')) return;
      try { await window.api.delete('/admin/listings/' + id + '/documents/' + docId); loadDocs(); }
      catch { alert('ลบไม่สำเร็จ'); }
    }
    const fmtSize = n => { const b = Number(n) || 0; return b >= 1048576 ? (b / 1048576).toFixed(1) + ' MB' : Math.max(1, Math.round(b / 1024)) + ' KB'; };

    function buildDocHTML(showBar) {
      const esc = s => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
      const ml = s => esc(s).replace(/\n/g, '<br>');
      const imgs = (images || []).slice(0, 4).map(i => i.url).filter(Boolean);
      const cols = Math.min(imgs.length, 4) || 1;
      const photoHTML = imgs.length ? '<div class="pcap">รูปประกอบทรัพย์</div><div class="photos" style="grid-template-columns:repeat(' + cols + ',1fr)">' + imgs.map(u => '<div class="ph"><img src="' + esc(u) + '"></div>').join('') + '</div>' : '';
      // รายการเอกสารแนบ (แสดงชื่อไฟล์เป็นเช็กลิสต์ — ตัวไฟล์เก็บ private ส่งแยก ไม่ฝังลิงก์ในเอกสารพิมพ์)
      const attachLines = LOAN_DOC_CATS.map(([k, label]) => {
        const ds = (docs || []).filter(d => (d.category || 'other') === k);
        return ds.length ? '<div>☑ ' + esc(label) + ' (' + ds.length + ') — ' + esc(ds.map(d => d.filename).join(', ')) + '</div>' : '';
      }).filter(Boolean).join('');
      const attachHTML = attachLines || '<span style="color:#8a95a5">— ยังไม่มีเอกสารแนบ —</span>';
      const logo = location.origin + '/logo-mark.png';
      const html = '<!doctype html><html lang="th"><head><meta charset="utf-8">'
        + '<title>ใบขอพิจารณาอนุมัติวงเงิน ' + esc(form.listing_code || '') + '</title>'
        + '<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>'
        + '<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@400;600;700&display=swap" rel="stylesheet">'
        + '<style>@page{size:A4 landscape;margin:11mm}*{box-sizing:border-box}body{font-family:"Sarabun","Tahoma",sans-serif;color:#16233a;margin:0;font-size:13px}'
        + '.hd{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.hd .mid{text-align:center}.hd .mid img{height:50px;display:block;margin:0 auto 2px}'
        + '.hd .mid .nm{font-weight:700;letter-spacing:3px;color:#2E5AA8;font-size:15px}.hd .rt{text-align:right;font-size:12.5px;line-height:1.5}.hd .rt .co{font-weight:700}'
        + '.title{font-size:20px;font-weight:700;margin:14px 0 6px}.meta{font-size:13.5px;margin:2px 0}'
        + 'table{width:100%;border-collapse:collapse;margin-top:10px}td{border:1px solid #6f8093;padding:8px 11px;vertical-align:top;line-height:1.5}'
        + '.lbl{background:#eef2f7;font-weight:700;width:180px;white-space:nowrap}'
        + '.pcap{font-weight:600;font-size:13px;margin:16px 0 6px;color:#16233a}.photos{display:grid;gap:10px}.photos .ph{aspect-ratio:3/2;border:1px solid #cfd6e0;border-radius:6px;overflow:hidden;background:#eef1f5}.photos img{width:100%;height:100%;object-fit:cover;display:block}'
        + '.bar{text-align:center;margin:18px 0}.bar button{font-family:inherit;font-size:14px;font-weight:600;background:#1F58C4;color:#fff;border:none;border-radius:8px;padding:10px 22px;cursor:pointer}'
        + '.sheet{max-width:1040px;margin:0 auto;padding:10px 30px}@media print{.bar{display:none};.sheet{max-width:none;margin:0;padding:0}}</style></head><body><div class="sheet">'
        + '<div class="hd"><div class="lt" style="font-size:13px">รหัสทรัพย์ ' + esc(form.listing_code || '-') + '</div>'
        + '<div class="mid"><img src="' + logo + '" alt="PROPFINN" onerror="this.style.display=\'none\'"><div class="nm">PROPFINN</div></div>'
        // ข้อมูลบริษัทต้องตรงกับ window.PF_COMPANY ในเว็บ (frontend/content-pages.jsx) — แหล่งอ้างอิงเดียว
        + '<div class="rt"><div class="co">บริษัท พร็อพฟินน์ อินเตอร์ กรุ๊ป จำกัด</div><div>อาคาร 66 ทาวเวอร์ เลขที่ 2566 ชั้น 7 ห้อง 701</div><div>ถนนสุขุมวิท แขวงบางนาเหนือ เขตบางนา กรุงเทพมหานคร 10260</div></div></div>'
        + '<div class="title">ใบขอพิจารณาอนุมัติวงเงิน</div>'
        + '<div class="meta">ประเภทลูกค้า : ' + esc(custTypeTh) + '</div>'
        + '<div class="meta">' + (isJuristic ? 'ชื่อนิติบุคคล' : 'ชื่อ-นามสกุลลูกค้า') + ' : ' + esc(custName) + '</div>'
        + '<div class="meta">สถานที่ตั้งทรัพย์ : ' + esc(val('locations')) + '</div>'
        + '<table><tbody>'
        + '<tr><td class="lbl">ประเภทการขอสินเชื่อ</td><td>' + ml(val('loan_type')) + '</td><td class="lbl">วงเงินกู้</td><td>' + (baht(val('loan_amount')) || esc(val('loan_amount'))) + ' บาท</td></tr>'
        + '<tr><td class="lbl">หลักประกัน</td><td>' + ml(val('collateral')) + '</td><td class="lbl">ราคาประเมิน</td><td>' + ml(val('appraisal')) + '</td></tr>'
        + '<tr><td class="lbl">อาชีพ</td><td colspan="3">' + ml(val('occupation')) + '</td></tr>'
        + '<tr><td class="lbl">รายได้</td><td colspan="3">' + ml(val('income')) + '</td></tr>'
        + '<tr><td class="lbl">วัตถุประสงค์การใช้วงเงิน</td><td colspan="3">' + ml(val('purpose')) + '</td></tr>'
        + '<tr><td class="lbl">เอกสารแนบ</td><td colspan="3">' + attachHTML + '</td></tr>'
        + '</tbody></table>' + photoHTML
        + (showBar ? '<div class="bar"><button onclick="window.print()">🖨️ พิมพ์ / บันทึกเป็น PDF</button></div>' : '')
        + '</div></body></html>';
      return html;
    }
    function openDoc() {
      const w = window.open('', '_blank');
      if (!w) { alert('เบราว์เซอร์บล็อกป๊อปอัป — อนุญาต popup ของเว็บนี้แล้วลองใหม่'); return; }
      w.document.write(buildDocHTML(true)); w.document.close();
    }

    const [docHtml, setDocHtml] = useState(() => buildDocHTML(false));
    useEffect(() => { const t = setTimeout(() => setDocHtml(buildDocHTML(false)), 250); return () => clearTimeout(t); }, [form, images, docs]);

    return (
      <div style={{ maxWidth: 1060 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, marginBottom: 12, flexWrap: 'wrap' }}>
          <div style={{ fontSize: 12.5, color: 'var(--text-2)' }}><i className="ph ph-file-text" style={{ marginRight: 5, color: '#4A6079' }}></i>ตัวอย่างเอกสาร — ดึงจากข้อมูลทรัพย์อัตโนมัติ (เห็นเป๊ะเหมือนตอนพิมพ์)</div>
          <button className="btn btn-primary" onClick={openDoc}><i className="ph ph-printer" style={{ marginRight: 5 }}></i>เปิด/พิมพ์ใบขอสินเชื่อ</button>
        </div>
        {/* ข้อมูลลูกค้า — บุคคลธรรมดา (ชื่อ-สกุล) หรือ นิติบุคคล (ชื่อบริษัท) */}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 12, marginBottom: 14 }}>
          <div className="form-group">
            <label>ประเภทลูกค้า</label>
            <select className="input" value={ctype} onChange={e => setLF('customer_type', e.target.value)}>
              <option value="individual">บุคคลธรรมดา</option>
              <option value="juristic">นิติบุคคล</option>
            </select>
          </div>
          {isJuristic ? (
            <div className="form-group" style={{ gridColumn: 'span 2' }}>
              <label>ชื่อนิติบุคคล / บริษัท</label>
              <input className="input" value={lf.company_name || ''} onChange={e => setLF('company_name', e.target.value)} placeholder="เช่น บริษัท ตัวอย่าง จำกัด" />
            </div>
          ) : (
            <div className="form-group" style={{ gridColumn: 'span 2' }}>
              <label>ชื่อ-นามสกุล ลูกค้า</label>
              <input className="input" value={lf.customer_name || ''} onChange={e => setLF('customer_name', e.target.value)} placeholder={dft.customer_name || 'ชื่อ-นามสกุล'} />
            </div>
          )}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 12, marginBottom: 14 }}>
          {[['occupation', 'อาชีพ'], ['income', 'รายได้'], ['purpose', 'วัตถุประสงค์การใช้วงเงิน']].map(([k, label]) => (
            <div key={k} className="form-group">
              <label>{label} <span style={{ fontSize: 11, fontWeight: 400, color: '#9AA6B5' }}>(กรอกเพื่อขึ้นในเอกสาร)</span></label>
              <textarea className="input" rows={2} value={lf[k] || ''} onChange={e => setLF(k, e.target.value)} />
            </div>
          ))}
        </div>

        {/* เอกสารแนบ — เก็บ private ใน GCS เปิดดูผ่านลิงก์ชั่วคราว */}
        <h3 style={{ margin: '4px 0 8px', fontSize: 15, color: '#102E54' }}>
          <i className="ph ph-paperclip" style={{ marginRight: 6, color: '#4A6079' }}></i>เอกสารแนบ
          <span style={{ fontSize: 12, fontWeight: 500, color: '#8A6D1B' }}> · เก็บแบบส่วนตัว เปิดดูได้เฉพาะทีมงาน (PDF/JPG/PNG · รองรับไฟล์ใหญ่)</span>
        </h3>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2,1fr)', gap: 12, marginBottom: 16 }}>
          {LOAN_DOC_CATS.map(([k, label]) => {
            const ds = docs.filter(d => (d.category || 'other') === k);
            return (
              <div key={k} style={{ border: '1px solid var(--border)', borderRadius: 10, padding: '10px 12px' }}>
                <div style={{ fontWeight: 600, fontSize: 13.5, marginBottom: 6 }}>
                  {label}{ds.length > 0 && <span style={{ color: '#15976A' }}> ({ds.length})</span>}
                </div>
                {ds.map(d => (
                  <div key={d.id} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, padding: '3px 0' }}>
                    <i className="ph ph-file" style={{ color: '#4A6079' }}></i>
                    <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={d.filename}>{d.filename}</span>
                    <span style={{ color: '#9AA6B5', whiteSpace: 'nowrap' }}>{fmtSize(d.size_bytes)}</span>
                    <a onClick={() => viewDoc(d.id)} style={{ cursor: 'pointer', color: '#2A6BE0', fontWeight: 600 }}>เปิด</a>
                    <a onClick={() => delDoc(d.id)} style={{ cursor: 'pointer', color: '#D8567A', fontWeight: 600 }}>ลบ</a>
                  </div>
                ))}
                <input type="file" accept="application/pdf,image/jpeg,image/png" multiple disabled={docBusy === k}
                  onChange={e => uploadDoc(k, e)} style={{ fontSize: 12.5, marginTop: 6, width: '100%' }} />
                {docBusy === k && <div style={{ fontSize: 12, color: '#2A6BE0', marginTop: 4 }}>กำลังอัปโหลด...</div>}
              </div>
            );
          })}
        </div>
        <iframe title="ตัวอย่างใบขอสินเชื่อ" srcDoc={docHtml}
          style={{ width: '100%', height: 720, border: '1px solid var(--border)', borderRadius: 10, background: '#fff', display: 'block' }}
          onLoad={e => { try { const h = e.target.contentWindow.document.body.scrollHeight; if (h > 120) e.target.style.height = (h + 26) + 'px'; } catch (_) {} }} />
        <div style={{ fontSize: 12, color: 'var(--text-2)', marginTop: 12, lineHeight: 1.6 }}>
          <i className="ph ph-info" style={{ marginRight: 5 }}></i>ชื่อลูกค้าเว้นว่าง = ใช้ชื่อจากข้อมูลผู้ขายอัตโนมัติ · <b>เอกสารแนบ</b> เก็บแบบส่วนตัว (ไม่ขึ้นหน้าเว็บ) ในเอกสารพิมพ์จะแสดงเป็นเช็กลิสต์รายชื่อไฟล์ ตัวไฟล์ส่งแนบแยกให้ไฟแนนซ์
        </div>
      </div>
    );
  }

  function ListingsPage({ navigate, route, t, embedded, kind, user }) {
    const isNpl = kind === 'npl';
    // การตลาด (marketing) = ดูอย่างเดียว (ตามตารางสิทธิ์: ดูทรัพย์ทั้งหมด ✓ · เพิ่ม/แก้ไข/อนุมัติ/ลบ ✗)
    //   backend บล็อกการเขียนด้วย requireAdmin อยู่แล้ว — ซ่อนปุ่มไม่ให้เห็นเพื่อ UX ที่ถูก
    const canWrite = user?.role === 'super_admin' || user?.role === 'admin';
    const basePage = route?.page || (isNpl ? 'npl' : 'listings'); // เปิด/ปิด editor ให้อยู่หน้าเดิม (ปกติ/NPL)
    const [tab, setTab] = useState('all');
    const [reportBusy, setReportBusy] = useState(false);
    const [listings, setListings] = useState([]);
    const [total, setTotal] = useState(0); // จำนวนทรัพย์ทั้งหมด (นับจาก server) สำหรับแบ่งหน้า
    const [counts, setCounts] = useState({}); // จำนวนต่อสถานะ (การ์ดกรอง)
    const [loading, setLoading] = useState(true);
    const [rejectModal, setRejectModal] = useState(null);
    const [reason, setReason] = useState('');
    const [sort, setSort] = useState({ key: 'created_at', dir: 'desc' }); // คลิกหัวคอลัมน์เพื่อจัดลำดับ
    const [pageSize, setPageSize] = useState(20); // จำนวนต่อหน้า (10/20/30/40/50)
    const [pageNum, setPageNum] = useState(1);
    const [selectedIds, setSelectedIds] = useState([]); // เลือกหลายรายการเพื่อทำพร้อมกัน (ลบ/แสดง/ซ่อน)
    function onSort(key) {
      setSort(s => s.key === key ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'asc' });
    }

    // ดู route.action เพื่อเปิด editor
    const editor = route?.action === 'new' ? 'new' : (route?.action === 'edit' && route?.id ? route.id : null);

    function openEditor(id) { navigate(basePage, id === 'new' ? 'new' : 'edit', id === 'new' ? null : id); }
    function closeEditor() { navigate(basePage); }

    // โหลดทีละหน้า (server-side) — จัดลำดับ+นับ+แบ่งหน้าที่ server · seq กันผลลัพธ์เก่ามาทับหน้าใหม่
    const loadSeq = useRef(0);
    function load() {
      const seq = ++loadSeq.current;
      setLoading(true);
      window.api.listings(tab === 'all' ? '' : tab, isNpl ? 'npl' : 'normal',
          { page: pageNum, limit: pageSize, sortKey: sort.key, sortDir: sort.dir })
        .then(d => { if (seq !== loadSeq.current) return; setListings(d.listings || []); setTotal(d.total || 0); if (d.counts) setCounts(d.counts); setLoading(false); })
        .catch(() => { if (seq !== loadSeq.current) return; setLoading(false); });
    }
    useEffect(() => { if (!editor) load(); }, [tab, editor, pageNum, pageSize, sort.key, sort.dir]);
    useEffect(() => { setPageNum(1); }, [tab, pageSize, sort.key, sort.dir]); // สลับแท็บ/จำนวนต่อหน้า/จัดลำดับ → กลับหน้า 1
    useEffect(() => { setSelectedIds([]); }, [tab]); // สลับแท็บ → ล้างที่เลือกไว้
    useEffect(() => { const tp = Math.max(1, Math.ceil(total / pageSize)); if (pageNum > tp) setPageNum(tp); }, [total]); // ลบจนหน้าเกิน → เด้งกลับหน้าสุดท้าย

    async function approve(id) { await window.api.approveListing(id); load(); }
    async function reject(id) { await window.api.rejectListing(id, reason); setRejectModal(null); setReason(''); load(); }

    const tabs = [
      { id: 'all',      label: t('listings_tab_all'),      color: '#2A6BE0', bg: '#EAF1FD', icon: 'ph-stack' },
      { id: 'active',   label: t('listings_tab_active'),   color: '#15976A', bg: '#E8F7F0', icon: 'ph-globe-simple' },
      { id: 'draft',    label: t('listings_tab_draft'),    color: '#64748B', bg: '#F1F5F9', icon: 'ph-note-pencil' },
      { id: 'pending',  label: t('listings_tab_pending'),  color: '#B5860B', bg: '#FFF7E6', icon: 'ph-clock-countdown' },
      { id: 'rejected', label: t('listings_tab_rejected'), color: '#D8567A', bg: '#FDF1F5', icon: 'ph-arrow-u-left-back' },
    ];

    if (editor) return <ListingEditorFull id={editor} onBack={closeEditor} isNpl={isNpl} t={t} />;

    async function del(id) { if (window.confirm('ลบทรัพย์นี้?')) { await window.api.delete('/admin/listings/' + id); load(); } }


    // ข้อมูลมาจาก server แล้ว (จัดลำดับ+แบ่งหน้าที่ server) — ใช้ตรงๆ ไม่ประมวลผลซ้ำฝั่ง browser
    const paged = listings;
    const totalRows = total;
    const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));
    const curPage = Math.min(pageNum, totalPages);
    const fromRow = totalRows === 0 ? 0 : (curPage - 1) * pageSize + 1;
    const toRow = (curPage - 1) * pageSize + paged.length;

    // เลือกหลายรายการ — ทำพร้อมกัน (ลบ/แสดง/ซ่อน)
    const pageIds = paged.map(r => r.id);
    const allPageSelected = pageIds.length > 0 && pageIds.every(id => selectedIds.includes(id));
    function toggleRow(id) { setSelectedIds(s => s.includes(id) ? s.filter(x => x !== id) : [...s, id]); }
    function toggleAllPage() { setSelectedIds(s => allPageSelected ? s.filter(id => !pageIds.includes(id)) : [...new Set([...s, ...pageIds])]); }
    async function bulkDelete() {
      if (!selectedIds.length) return;
      if (!window.confirm('ลบทรัพย์ที่เลือก ' + selectedIds.length + ' รายการ?\nการกระทำนี้ย้อนกลับไม่ได้')) return;
      let fail = 0;
      for (const id of selectedIds) { try { await window.api.delete('/admin/listings/' + id); } catch (e) { fail++; } }
      setSelectedIds([]); load();
      if (fail) alert('ลบไม่สำเร็จ ' + fail + ' รายการ');
    }
    async function bulkVisibility(show) {
      if (!selectedIds.length) return;
      for (const id of selectedIds) { try { await window.api.setListingVisibility(id, show); } catch (e) {} }
      setSelectedIds([]); load();
    }

    const cols = [
      ...(canWrite ? [{ label: <input type="checkbox" checked={allPageSelected} onChange={toggleAllPage} style={{cursor:'pointer',width:16,height:16}} title="เลือกทั้งหน้า" />,
        render: r => <input type="checkbox" checked={selectedIds.includes(r.id)} onChange={() => toggleRow(r.id)} onClick={e => e.stopPropagation()} style={{cursor:'pointer',width:16,height:16}} /> }] : []),
      { label: 'รหัส', sortKey: 'listing_code', render: r => <span style={{fontSize:12,color:'var(--text-2)',fontFamily:'monospace'}}>{r.listing_code || r.id.slice(0,8)}</span> },
      { label: t('listings_name'), sortKey: 'title', render: r => canWrite
        ? <span className="text-bold" style={{cursor:'pointer',color:'var(--blue)'}} onClick={() => openEditor(r.id)}>{r.title}</span>
        : <span className="text-bold">{r.title}</span> },
      // { label: t('listings_agent'), sortKey: 'agent_name', render: r => r.agent_name }, // ซ่อนชั่วคราว — เอากลับได้

      { label: t('listings_price'), sortKey: 'price', render: r => Number(r.price).toLocaleString() + ' ฿' },
      { label: 'หมวด', sortKey: 'listing_type', render: r => <DealTypeCell row={r} /> },
      { label: 'เจ้าของ / นายหน้า', sortKey: 'broker_name', render: r => {
        const isBroker = r.source_channel === 'broker' || (!r.source_channel && r.broker_id);
        const name = isBroker ? r.broker_name : (r.owner_display || r.owner_name);
        const isOwnerEntity = !isBroker && !!r.owner_display;
        if (!name) return <span style={{color:'var(--text-2)'}}>—</span>;
        return <span style={{display:'inline-flex',alignItems:'center',gap:5,fontSize:12.5,whiteSpace:'nowrap'}} title={isBroker ? 'นายหน้า' : (isOwnerEntity ? 'เจ้าของทรัพย์' : 'ลูกค้า/เจ้าของ')}>
          <i className={'ph ' + (isBroker ? 'ph-handshake' : (isOwnerEntity ? 'ph-user-square' : 'ph-user'))} style={{color:'var(--text-2)',fontSize:14}}></i>{name}
        </span>;
      }},
      { label: 'แสดงหน้าเว็บ', sortKey: 'status', render: r => <WebToggle row={r} readOnly={!canWrite} /> },
      // จำนวนผู้สนใจต่อทรัพย์ (Phase 2) — คลิกเปิดแท็บ "ผู้สนใจทรัพย์นี้"
      { label: 'ผู้สนใจ', sortKey: 'interest_count', render: r => {
        const n = Number(r.interest_count) || 0;
        return <span style={{cursor:'pointer',display:'inline-flex',alignItems:'center',gap:5}} title="จำนวนผู้สนใจทรัพย์นี้ — คลิกเพื่อดูรายชื่อ" onClick={() => { INITIAL_HUB_TAB = 'interested'; openEditor(r.id); }}>
          {n > 0
            ? <span className="badge" style={{background:'#EAF1FD',color:'#1F58C4',fontWeight:700}}><i className="ph ph-users-three" style={{marginRight:4}}></i>{n}</span>
            : <span style={{color:'var(--text-2)',fontSize:12.5}}><i className="ph ph-users-three" style={{marginRight:4}}></i>0</span>}
        </span>;
      }},
      { label: 'สถานะดีล (ทีม)', sortKey: 'work_status', render: r => canWrite
        ? <span style={{cursor:'pointer',display:'inline-flex',alignItems:'center',gap:5}} title="ความคืบหน้าการทำดีลภายในทีม (ส่งแหล่งทุน/ติดตามผล) — คนละเรื่องกับการแสดงบนเว็บ · คลิกเพื่อเปิดหน้าจัดการทรัพย์ แท็บสถานะดีล" onClick={() => { INITIAL_HUB_TAB = 'deal'; openEditor(r.id); }}>{r.work_status ? <WorkStatusBadge status={r.work_status} /> : <span style={{color:'var(--blue)',fontSize:12.5,borderBottom:'1px dashed var(--blue)'}}>ตั้งสถานะ</span>}<i className="ph ph-clock-counter-clockwise" style={{fontSize:13,color:'var(--text-2)'}}></i></span>
        : (r.work_status ? <WorkStatusBadge status={r.work_status} /> : <span style={{color:'var(--text-2)'}}>—</span>) },
      ...(canWrite ? [{ label: t('actions'), render: r => (
        <div className="row gap-6">
          <window.EditBtn onClick={() => openEditor(r.id)} />
          {r.status === 'pending' && <>
            <window.ApproveBtn title="อนุมัติ" onClick={() => approve(r.id)} />
            <window.RejectBtn title="ปฏิเสธ" onClick={() => setRejectModal(r.id)} />
          </>}
          <window.DeleteBtn onClick={() => del(r.id)} />
        </div>
      )}] : []),
    ];

    // ส่งออกรายงานดีล (หมวดลงทุน) เป็น CSV เปิดใน Excel ได้ — คอลัมน์ Update รวมไทม์ไลน์ให้อัตโนมัติ
    async function exportDealReport() {
      setReportBusy(true);
      try {
        const d = await window.api.dealsReport();
        const rows = (d && d.rows) || [];
        const fmtDate = v => { if (!v) return ''; const t = new Date(v); const y = String((t.getFullYear() + 543) % 100).padStart(2, '0'); return String(t.getDate()).padStart(2, '0') + '/' + String(t.getMonth() + 1).padStart(2, '0') + '/' + y; };
        const num = v => { const n = Number(v); return isFinite(n) && n ? n.toLocaleString('en-US') : ''; };
        const ptLabel = v => { const p = PTYPES.find(x => x.v === v); return p ? p.l : (v || ''); };
        const areaOf = r => {
          const wa = (Number(r.land_rai) || 0) * 400 + (Number(r.land_ngan) || 0) * 100 + (Number(r.land_wa) || 0);
          if (wa > 0) { const rai = Math.floor(wa / 400), ng = Math.floor((wa % 400) / 100), w = +(wa % 100).toFixed(1); return [rai && rai + ' ไร่', ng && ng + ' งาน', w && w + ' ตร.ว.'].filter(Boolean).join(' '); }
          return r.land_sqw ? r.land_sqw + ' ตร.ว.' : '';
        };
        const chan = r => r.source_channel === 'broker' ? (r.broker_name || 'นายหน้า') : (r.source_channel === 'lead' ? 'ลีด (โดยตรง)' : '');
        const updates = r => (r.activities || []).map(a => {
          const parts = []; if (a.type === 'status' && a.to_status) parts.push('→ ' + a.to_status); if (a.funder) parts.push('ส่ง: ' + a.funder); if (a.note) parts.push(a.note);
          return fmtDate(a.created_at) + ' ' + parts.join(' · ');
        }).join('\n');
        const head = ['วันที่ส่งงาน', 'รหัสทรัพย์', 'ชื่อลูกค้า', 'รายละเอียดทรัพย์', 'เนื้อที่', 'วงเงินกู้', 'จังหวัด', 'ประเภททรัพย์', 'ช่องทาง', 'สถาบันการเงิน', 'สถานะ', 'Update'];
        const table = rows.map(r => [fmtDate(r.created_at), r.listing_code || '', r.owner_name || '', r.description || '', areaOf(r), num(r.max_loan), r.province || '', ptLabel(r.property_type), chan(r), r.financial_institution || '', r.work_status || '', updates(r)]);
        const esc = v => { const s = String(v == null ? '' : v); return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s; };
        const csv = '﻿' + [head, ...table].map(row => row.map(esc).join(',')).join('\r\n');
        const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
        const url = URL.createObjectURL(blob); const a = document.createElement('a');
        a.href = url; a.download = 'รายงานดีล_' + new Date().toISOString().slice(0, 10) + '.csv'; a.click(); URL.revokeObjectURL(url);
      } catch (e) { alert('สร้างรายงานไม่สำเร็จ: ' + (e.message || e)); }
      setReportBusy(false);
    }

    const body = (
      <>
        <div className="page-header" style={{flexWrap:'nowrap'}}>
          {!embedded && <h1 style={{whiteSpace:'nowrap'}}><i className="ph ph-buildings" style={{marginRight:8,color:'#4A6079'}}></i>{isNpl ? t('menu_npl_listings') : t('menu_listings_general')}</h1>}
          <div style={{marginLeft:'auto',display:'flex',alignItems:'center',gap:8}}>
            {canWrite && <button className="btn btn-ghost" style={{whiteSpace:'nowrap'}} disabled={reportBusy} onClick={exportDealReport} title="ส่งออกรายงานดีล (หมวดลงทุน) เป็น Excel/CSV พร้อมไทม์ไลน์">
              <i className="ph ph-download-simple" style={{marginRight:5}}></i>{reportBusy ? 'กำลังสร้าง...' : 'รายงานดีล'}</button>}
            {canWrite && <button className="btn btn-primary" style={{whiteSpace:'nowrap'}} onClick={() => openEditor('new')}>{t('listings_create')}</button>}
          </div>
        </div>
        {/* การ์ดกรองตามสถานะการแสดงบนเว็บ (คนละเรื่องกับ “สถานะดีล”) — โชว์จำนวน + กดกรอง */}
        <window.FilterCards
          items={tabs.map(tb => ({ ...tb, count: counts[tb.id] || 0, hot: tb.id === 'pending' && (counts.pending || 0) > 0 }))}
          active={tab} onSelect={setTab} />
        {/* แท็บ "รอตรวจสอบ" = คิวรีวิวรวม: ทรัพย์ที่คนส่งเข้ามา (ฟอร์มสาธารณะ) + ทรัพย์ในระบบที่รออนุมัติ */}
        {tab === 'pending' && !isNpl && (
          <div style={{marginTop:16}}>
            <div style={{fontSize:14,fontWeight:700,color:'var(--navy)',marginBottom:10,display:'flex',alignItems:'center',gap:6}}><i className="ph ph-tray-arrow-down"></i> ทรัพย์ที่คนส่งเข้ามา (จากฟอร์มสาธารณะ)</div>
            <div className="card"><window.SubmissionsPage embedded /></div>
            <div style={{fontSize:14,fontWeight:700,color:'var(--navy)',margin:'24px 0 10px',display:'flex',alignItems:'center',gap:6}}><i className="ph ph-buildings"></i> ทรัพย์ในระบบที่รออนุมัติ{totalRows>0?` (${totalRows})`:''}</div>
          </div>
        )}
        {/* แถบจัดการหลายรายการ — เฉพาะคนที่แก้ไขได้ + มีรายการให้เลือก (ไม่มีของ = ไม่โชว์ ลดรก) */}
        {canWrite && paged.length > 0 && <div style={{display:'flex',alignItems:'center',gap:10,flexWrap:'wrap',marginTop:16,minHeight:42,padding:'6px 14px',borderRadius:10,transition:'background .12s',
          background: selectedIds.length ? '#EEF4FF' : '#F6F8FC', border:'1px solid '+(selectedIds.length ? '#CADBF7' : 'var(--border)')}}>
          {selectedIds.length > 0 ? (
            <>
              <span style={{fontSize:13.5,fontWeight:700,color:'#1F58C4'}}>เลือกไว้ {selectedIds.length} รายการ</span>
              <button className="btn btn-sm btn-danger" onClick={bulkDelete}><i className="ph ph-trash"></i> ลบที่เลือก</button>
              <button className="btn btn-sm btn-soft" onClick={() => bulkVisibility(true)}><i className="ph ph-eye"></i> แสดงบนเว็บ</button>
              <button className="btn btn-sm btn-soft" onClick={() => bulkVisibility(false)}><i className="ph ph-eye-slash"></i> ซ่อน</button>
              <button className="btn btn-sm btn-ghost" style={{marginLeft:'auto'}} onClick={() => setSelectedIds([])}>ยกเลิกการเลือก</button>
            </>
          ) : (
            <span style={{fontSize:12.5,color:'var(--text-2)',display:'inline-flex',alignItems:'center',gap:6}}><i className="ph ph-check-square" style={{fontSize:15}}></i> ติ๊กช่องหน้ารายการเพื่อจัดการหลายรายการพร้อมกัน (ลบ / แสดง / ซ่อน)</span>
          )}
        </div>}
        <div className="card" style={{marginTop:12}}>
          <Table columns={cols} data={paged} loading={loading}
            empty={tab === 'pending' ? 'ไม่มีทรัพย์ในระบบที่รออนุมัติ — เคลียร์คิวหมดแล้ว 🎉' : (tab === 'rejected' ? 'ไม่มีทรัพย์ที่ถูกตีกลับ' : (tab === 'draft' ? 'ยังไม่มีทรัพย์ที่เป็นฉบับร่าง' : 'ยังไม่มีทรัพย์ในรายการนี้'))}
            emptyIcon={tab === 'pending' ? 'ph-check-circle' : 'ph-buildings'} sort={sort} onSort={onSort} />
          {!loading && totalRows > 0 && (
            <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',gap:12,flexWrap:'wrap',padding:'12px 4px 2px',borderTop:'1px solid var(--border)',marginTop:4}}>
              <div style={{display:'flex',alignItems:'center',gap:8,fontSize:13,color:'var(--text-2)'}}>
                <span>แสดงต่อหน้า</span>
                <select className="input input-sm" style={{width:72}} value={pageSize} onChange={e => setPageSize(Number(e.target.value))}>
                  {[10,20,30,40,50].map(n => <option key={n} value={n}>{n}</option>)}
                </select>
                <span>· {fromRow}–{toRow} จาก {totalRows} รายการ</span>
              </div>
              <div style={{display:'flex',alignItems:'center',gap:6}}>
                <button className="btn btn-sm btn-ghost" disabled={curPage<=1} onClick={() => setPageNum(1)} title="หน้าแรก">«</button>
                <button className="btn btn-sm btn-ghost" disabled={curPage<=1} onClick={() => setPageNum(curPage-1)}>‹ ก่อนหน้า</button>
                <span style={{fontSize:13,fontWeight:600,padding:'0 6px',whiteSpace:'nowrap'}}>หน้า {curPage} / {totalPages}</span>
                <button className="btn btn-sm btn-ghost" disabled={curPage>=totalPages} onClick={() => setPageNum(curPage+1)}>ถัดไป ›</button>
                <button className="btn btn-sm btn-ghost" disabled={curPage>=totalPages} onClick={() => setPageNum(totalPages)} title="หน้าสุดท้าย">»</button>
              </div>
            </div>
          )}
        </div>
        {rejectModal && <Modal title="เหตุผลที่ปฏิเสธ" onClose={() => setRejectModal(null)}>
          <textarea className="input" placeholder="ระบุเหตุผล..." value={reason} onChange={e => setReason(e.target.value)} rows={3} />
          <div className="row gap-10" style={{marginTop:16}}>
            <button className="btn btn-danger" onClick={() => reject(rejectModal)}>ปฏิเสธ</button>
            <button className="btn btn-ghost" onClick={() => setRejectModal(null)}>ยกเลิก</button>
          </div>
        </Modal>}
      </>
    );
    return embedded ? body : <div className="page">{body}</div>;
  }

  // ======= Combined Editor + Images =======
  // สร้าง invest_types + invest_terms จากฟอร์ม — โหมดรับสองแบบ (ขายฝาก+จำนอง เงื่อนไขคนละชุด)
  function investPayload(f) {
    const inv = f.listing_type === 'consignment' || f.listing_type === 'mortgage';
    if (!inv) return { invest_types: null, invest_terms: null };
    if (!f.dual) return { invest_types: [f.listing_type], invest_terms: null };  // แบบเดียว — backend ใช้ช่องเดี่ยว
    const other = f.listing_type === 'consignment' ? 'mortgage' : 'consignment';
    return {
      invest_types: [f.listing_type, other],
      invest_terms: {
        [f.listing_type]: { est_return: f.est_return, term_years: f.term_years, max_loan: f.max_loan },
        [other]: { est_return: f.sec_est_return, term_years: f.sec_term_years, max_loan: f.sec_max_loan },
      },
    };
  }
  const investTypeTh = (t) => t === 'mortgage' ? 'จำนอง' : 'ขายฝาก';

  function ListingEditorFull({ id, onBack, isNpl, t }) {
    const isNew = id === 'new';
    const [step, setStep] = useState(isNew ? 0 : 1);
    const [catChosen, setCatChosen] = useState(!isNew || isNpl); // NPL: ล็อกหมวดเป็น NPL เลย ข้ามหน้าเลือกหมวด → ไปเลือกประเภททรัพย์
    const [catSel, setCatSel] = useState(isNew ? (isNpl ? 'npl' : '') : 'sale'); // หมวดที่กำลังเลือกในหน้า picker · 'both' = ขายฝาก+จำนอง
    const [form, setForm] = useState({
      title: '', description: '', property_type: '', listing_type: isNpl ? 'npl' : 'sale',  // NPL ล็อก npl · อื่นๆ เลือกเองในหน้า picker
      price: '', price_per_sqm: '', area_sqm: '', bedrooms: '', bathrooms: '', floors: '',
      province: '', district: '', address: '', est_return: '', max_loan: '', term_years: '',
      deed_type: '', cover_image_url: '', status: 'active',  // ทรัพย์ใหม่แสดงบนเว็บเลย — คุมเปิด/ปิดที่ปุ่มในตารางหน้ารวม
      project_name: '', full_address: '', listing_code: '', is_negotiable: false,
      nearby_transit: '', amenities: [], facilities: [],
      floor_number: '', direction: '', unit_view: '', parking_spots: '',
      land_sqw: '', land_rai: '', land_ngan: '', land_wa: '',
      price_per_sqw: '', road_frontage: '', city_plan: '', road_access: '',
      rental_income: '', subdistrict: '', zipcode: '',
      appraised_value: '', deal_status: 'open', document_checklist: [], location_risk_override: '',
      lat: '', lng: '', loan_form: {}, owner_id: '',
      dual: false, sec_est_return: '', sec_term_years: '', sec_max_loan: '',  // โหมดรับสองแบบ: เงื่อนไขของแบบที่สอง
    });
    const [riskCfg, setRiskCfg] = useState(null);   // config เอกสาร/ทำเล จาก backend
    const [riskPreview, setRiskPreview] = useState(null); // ผลคำนวณสด {ltv, grade, score, factors}
    const [images, setImages] = useState([]);
    const [lightbox, setLightbox] = useState(null); // index รูปที่กำลังดูใหญ่ (null = ปิด)
    const [loading, setLoading] = useState(!isNew);
    const [saving, setSaving] = useState(false);
    const [saveMsg, setSaveMsg] = useState('');
    const [uploading, setUploading] = useState(false);
    const [uploadProgress, setUploadProgress] = useState('');
    const [amenityInput, setAmenityInput] = useState('');
    const [facilityInput, setFacilityInput] = useState('');
    const [activeTab, setActiveTab] = useState(isNew ? 'basic' : INITIAL_HUB_TAB);
    useEffect(() => { INITIAL_HUB_TAB = 'basic'; }, []); // reset หลังใช้ครั้งเดียว
    const [wstep, setWstep] = useState(1); // 1=ข้อมูลหลัก 2=ราคา&สเปค 3=ที่ตั้ง&ดีล 4=รูปภาพ
    // คีย์ลัด lightbox: Esc ปิด, ←/→ เลื่อนรูป
    useEffect(() => {
      if (lightbox === null) return;
      const onKey = (e) => {
        if (e.key === 'Escape') setLightbox(null);
        else if (e.key === 'ArrowLeft') setLightbox(v => (v - 1 + images.length) % images.length);
        else if (e.key === 'ArrowRight') setLightbox(v => (v + 1) % images.length);
      };
      window.addEventListener('keydown', onKey);
      return () => window.removeEventListener('keydown', onKey);
    }, [lightbox, images.length]);
    const [errFields, setErrFields] = useState({}); // ฟิลด์บังคับที่ยังไม่กรอก
    const fileRef = useRef(null);
    const [savedId, setSavedId] = useState(isNew ? null : id);
    const savedRef = useRef(isNew ? null : id);  // id แบบ sync (กัน autosave สร้าง draft ซ้ำตอนรอ setSavedId)
    const lsCreating = useRef(false);

    // autosave ร่างลง server — ทรัพย์ใหม่สร้างเป็น draft (ไม่ขึ้นเว็บ) จนกว่าจะกด "บันทึก" จริง
    const autosave = window.useServerAutosave({
      form,
      enabled: !!String(form.title || '').trim(),
      persist: async (f) => {
        if (!String(f.title || '').trim()) return false;
        const token = await window.firebaseAuth.currentUser?.getIdToken();
        if (!savedRef.current) {
          if (lsCreating.current) return false;   // กำลังสร้างอยู่ → ไม่สร้างซ้ำ
          lsCreating.current = true;
          try {
            const res = await fetch(window.ADMIN_CONFIG.apiUrl + '/admin/listings', {
              method: 'POST', headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' },
              body: window.safeStringify({ ...f, ...investPayload(f), status: 'draft', autosave: true }),
            });
            const data = await res.json();
            if (data.listing && data.listing.id) {
              savedRef.current = data.listing.id;
              setSavedId(data.listing.id);
              if (data.listing.listing_code) setForm(cur => ({ ...cur, listing_code: cur.listing_code || data.listing.listing_code }));
              return true;
            }
            return false;
          } finally { lsCreating.current = false; }
        }
        const res = await fetch(window.ADMIN_CONFIG.apiUrl + '/admin/listings/' + savedRef.current, {
          method: 'PUT', headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' },
          body: window.safeStringify({ ...f, ...investPayload(f), status: '', autosave: true }),  // status '' = ไม่เปลี่ยนสถานะ (ไม่เผยแพร่)
        });
        return res.ok;
      },
    });

    function set(k,v) {
      // กันค่าเพี้ยนทุกช่อง: ถ้าเผลอส่ง DOM event เข้ามา (เช่น NumInput ตัวเก่าที่ cache ค้างส่ง event แทน string)
      // → ดึงเฉพาะ .target.value ไม่ให้ field กลายเป็น [object Object] · array/object ปกติ (amenities/checklist ไม่มี .target) ไม่โดนแตะ
      if (v && typeof v === 'object' && v.target && 'value' in v.target) v = v.target.value;
      setForm(f => ({...f, [k]: v}));
    }

    function loadListing(lid) {
      window.api.get('/admin/listings/' + lid)
        .then(d => {
          if(d.listing) {
            const l = d.listing;
            // ทรัพย์รับสองแบบ: เปิดโหมด dual + ดึงเงื่อนไขแบบที่สองมาใส่ช่อง sec_*
            const it = Array.isArray(l.invest_types) ? l.invest_types : [];
            const dual = it.length > 1;
            let sec = { dual: false };
            if (dual) {
              const other = l.listing_type === 'consignment' ? 'mortgage' : 'consignment';
              const ot = (l.invest_terms && l.invest_terms[other]) || {};
              sec = { dual: true, sec_est_return: ot.est_return ?? '', sec_term_years: ot.term_years ?? '', sec_max_loan: ot.max_loan ?? '' };
            }
            setForm(f => ({...f, ...l, amenities: l.amenities||[], facilities: l.facilities||[],
              document_checklist: Array.isArray(l.document_checklist) ? l.document_checklist : [],
              deal_status: l.deal_status || 'open',
              appraised_value: l.appraised_value || '',
              loan_form: (l.loan_form && typeof l.loan_form === 'object') ? l.loan_form : {},
              location_risk_override: (l.risk_factors && l.risk_factors.location_override) || '', ...sec}));
          }
          setImages(d.images || []);
          setLoading(false);
        }).catch(() => setLoading(false));
    }
    useEffect(() => { if (!isNew) loadListing(id); }, [id]);
    useEffect(() => { if (form.province && errFields.province) setErrFields(p => ({...p, province: undefined})); }, [form.province]);

    // โหลด config (รายการเอกสาร + ระดับทำเล) ครั้งเดียว
    useEffect(() => { window.api.riskConfig().then(setRiskCfg).catch(() => {}); }, []);
    const [brokers, setBrokers] = useState([]);
    useEffect(() => { window.api.brokers().then(d => setBrokers(d.brokers || [])).catch(() => {}); }, []);
    const [owners, setOwners] = useState([]);
    useEffect(() => { window.api.owners().then(d => setOwners(d.owners || [])).catch(() => {}); }, []);

    // ประกาศใหม่: ดึงรหัสอ้างอิงถัดไปมาโชว์ล่วงหน้า (preview — generate จริงตอนบันทึก)
    useEffect(() => {
      if (isNew && !savedId) window.api.nextListingCode(form.listing_type === 'npl' ? 'npl' : '').then(d => set('listing_code', d.next_code)).catch(() => {});
    }, [savedId, form.listing_type]);

    // คำนวณ risk grade สด เมื่อ field ที่เกี่ยวข้องเปลี่ยน (debounce)
    useEffect(() => {
      const tm = setTimeout(() => {
        window.api.riskPreview({
          max_loan: form.max_loan, price: form.price, appraised_value: form.appraised_value,
          province: form.province, location_risk_override: form.location_risk_override,
          document_checklist: form.document_checklist, term_years: form.term_years,
        }).then(setRiskPreview).catch(() => {});
      }, 400);
      return () => clearTimeout(tm);
    }, [form.max_loan, form.price, form.appraised_value, form.province, form.location_risk_override, form.document_checklist, form.term_years]);

    // toggle เอกสารในเช็กลิสต์
    function toggleDoc(key) {
      setForm(f => {
        const list = Array.isArray(f.document_checklist) ? [...f.document_checklist] : [];
        const i = list.findIndex(d => d.key === key);
        if (i >= 0) list[i] = { ...list[i], provided: !list[i].provided };
        else list.push({ key, provided: true });
        return { ...f, document_checklist: list };
      });
    }
    const docProvided = (key) => (form.document_checklist || []).some(d => d.key === key && d.provided);

    async function save() {
      setSaving(true); setSaveMsg('');
      const token = await window.firebaseAuth.currentUser?.getIdToken();
      const sid = savedRef.current;  // ใช้ id แบบ sync (เผื่อ autosave เพิ่งสร้าง draft ไว้) กันสร้างซ้ำ
      const url = sid ? '/admin/listings/' + sid : '/admin/listings';
      const method = sid ? 'PUT' : 'POST';
      try {
        const res = await fetch(window.ADMIN_CONFIG.apiUrl + url, {
          method, headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' },
          body: window.safeStringify({ ...form, ...investPayload(form) }),
        });
        const data = await res.json();
        if (data.listing?.id) {
          savedRef.current = data.listing.id;
          setSavedId(data.listing.id);
          setForm(f => ({...f, ...data.listing, amenities: data.listing.amenities||[], facilities: data.listing.facilities||[]}));
          setSaveMsg('บันทึกเรียบร้อย ✅');
        } else { setSaveMsg('เกิดข้อผิดพลาด ❌'); }
      } catch { setSaveMsg('เกิดข้อผิดพลาด ❌'); }
      setSaving(false);
      setTimeout(() => setSaveMsg(''), 3000);
    }

    // ===== ตรวจสอบฟิลด์บังคับก่อนไปขั้นถัดไป / บันทึก =====
    function validateStep(s) {
      const e = {};
      if (s === 1) {
        if (!String(form.title||'').trim()) e.title = 'กรุณากรอกชื่อทรัพย์';
        if (!form.price || Number(form.price) <= 0) e.price = 'กรุณากรอกราคา';
      }
      if (s === 2 && !String(form.province||'').trim()) e.province = 'กรุณาเลือกจังหวัด/ที่ตั้ง';
      return e;
    }
    function firstInvalidBefore(target) {
      for (let s = 1; s < target; s++) { const e = validateStep(s); if (Object.keys(e).length) return { s, e }; }
      return null;
    }
    function flagError(e) {
      setErrFields(e);
      setSaveMsg('⚠️ ' + Object.values(e)[0]);
      setTimeout(() => setSaveMsg(''), 3000);
    }
    function goNext() {
      const e = validateStep(wstep);
      if (Object.keys(e).length) { flagError(e); return; }
      setErrFields({});
      setWstep(s => Math.min(2, s + 1));
    }
    function goToStep(n) {
      if (n > wstep) { const bad = firstInvalidBefore(n); if (bad) { setWstep(bad.s); flagError(bad.e); return; } }
      setErrFields({});
      setWstep(n);
    }
    function saveGuarded() {
      const bad = firstInvalidBefore(3); // ตรวจ step 1-2
      if (bad) { setWstep(bad.s); flagError(bad.e); return; }
      save();
    }

    async function uploadFiles(e) {
      const files = Array.from(e.target.files || []);
      if (!files.length || !savedId) return;
      setUploading(true);
      const token = await window.firebaseAuth.currentUser?.getIdToken();
      let success = 0;
      for (let i = 0; i < files.length; i++) {
        setUploadProgress('อัปโหลด ' + (i+1) + '/' + files.length + '...');
        try {
          const fd = new FormData(); fd.append('file', files[i]);
          const res = await fetch(window.ADMIN_CONFIG.apiUrl + '/admin/listings/' + savedId + '/upload', {
            method: 'POST', headers: { Authorization: 'Bearer ' + token }, body: fd,
          });
          if ((await res.json()).url) success++;
        } catch {}
      }
      setUploadProgress(success + '/' + files.length + ' สำเร็จ ✅');
      setUploading(false);
      if (fileRef.current) fileRef.current.value = '';
      loadListing(savedId);
    }

    async function deleteImage(imageId) {
      if (!window.confirm('ลบรูปนี้?')) return;
      const token = await window.firebaseAuth.currentUser?.getIdToken();
      await fetch(window.ADMIN_CONFIG.apiUrl + '/admin/listings/' + savedId + '/images/' + imageId, {
        method: 'DELETE', headers: { Authorization: 'Bearer ' + token },
      });
      loadListing(savedId);
    }

    async function setCover(url) {
      const token = await window.firebaseAuth.currentUser?.getIdToken();
      await fetch(window.ADMIN_CONFIG.apiUrl + '/admin/listings/' + savedId, {
        method: 'PUT', headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' },
        body: window.safeStringify({ ...form, cover_image_url: url }),
      });
      setForm(f => ({...f, cover_image_url: url}));
      loadListing(savedId);
    }

    if (loading) return <div className="loading">กำลังโหลด...</div>;

    const pt = form.property_type;
    const isLandBare = pt === 'land_bare';
    const isLandType = pt === 'land_bare' || pt === 'land_building';        // มีรายละเอียดที่ดิน (ไร่/งาน/วา, ผังเมือง, หน้ากว้าง)
    const isCondo = pt === 'condo';
    const isCommercial = pt === 'factory_warehouse' || pt === 'hotel_apartment'; // มีรายได้ค่าเช่า
    const hasRooms = ['house_townhome','condo','hotel_apartment','land_building','commercial'].includes(pt);
    const hasFloors = ['house_townhome','factory_warehouse','hotel_apartment','land_building','commercial'].includes(pt);
    const hasLandSqw = ['house_townhome','factory_warehouse','hotel_apartment','commercial'].includes(pt); // เนื้อที่ดิน ตร.ว. (อาคารบนที่ดิน)
    const hasArea = !isLandBare;                                           // พื้นที่ใช้สอย
    const hasAmenities = !isLandBare;
    const isLand = isLandType; // backward alias
    // หมวดดีล: invest = จำนอง/ขายฝาก (มี risk+ผลตอบแทน) · sale = ขายขาด (ไม่มี)
    const isInvest = form.listing_type === 'mortgage' || form.listing_type === 'consignment';
    const otherInvestType = form.listing_type === 'consignment' ? 'mortgage' : 'consignment';  // แบบที่สอง (โหมด dual)

    // Step 0a: เลือกหมวดดีล (ขายขาด / ขายฝาก-จำนอง) ก่อน
    if (step === 0 && !catChosen) return (
      <div className="page">
        <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:16,fontSize:14,flexWrap:'wrap'}}>
          <button onClick={onBack} style={{background:'none',border:'none',cursor:'pointer',color:'#1F58C4',fontWeight:600,fontSize:14,fontFamily:'inherit',display:'inline-flex',alignItems:'center',gap:5,padding:0}}>← {isNpl ? t('menu_npl_listings') : t('menu_listings_general')}</button>
          <span style={{color:'#C5CCD6'}}>/</span>
          <span style={{fontWeight:700,color:'#102E54'}}>เพิ่มทรัพย์ใหม่</span>
        </div>
        <div className="card" style={{padding:32}}>
          <div style={{textAlign:'center',marginBottom:26}}>
            <h2 style={{fontSize:20,fontWeight:800,color:'#102E54',margin:0}}>เลือกหมวดทรัพย์</h2>
            <p style={{fontSize:13.5,color:'#6B7280',marginTop:6}}>เลือกประเภทดีลที่จะลงประกาศ แล้วกด “ถัดไป”</p>
          </div>
          <div style={{display:'flex',flexWrap:'wrap',justifyContent:'center',gap:14,maxWidth:1020,margin:'0 auto'}}>
            {[
              {v:'sale',        icon:'ph-tag',       label:'ทรัพย์ฝากขาย', desc:'ซื้อ-ขายทรัพย์ · โอนกรรมสิทธิ์',      bd:'#BBF0D4', bg:'#F1FCF6', c:'#0F7A4D', dc:'#15976A'},
              {v:'consignment', icon:'ph-coins',     label:'ขายฝาก', desc:'ทรัพย์ลงทุน · มีผลตอบแทน & ความเสี่ยง', bd:'#F3E2A8', bg:'#FFFBEF', c:'#9A7400', dc:'#B5860B'},
              {v:'mortgage',    icon:'ph-scroll',    label:'จำนอง',  desc:'ทรัพย์ลงทุน · มีผลตอบแทน & ความเสี่ยง', bd:'#F3E2A8', bg:'#FFFBEF', c:'#9A7400', dc:'#B5860B'},
              {v:'both',        icon:'ph-coins',     label:'ขายฝากและจำนอง', desc:'ทรัพย์ลงทุน · รับ 2 แบบ เงื่อนไขคนละชุด', bd:'#F3E2A8', bg:'#FFFBEF', c:'#9A7400', dc:'#B5860B'},
              // NPL ไม่อยู่ในหน้าทั่วไป — สร้างจากเมนู "ทรัพย์ NPL" เท่านั้น
            ].map(o => {
              const sel = catSel === o.v;
              return (
                <button key={o.v} onClick={() => setCatSel(o.v)}
                  onMouseEnter={e => { if(!sel){ e.currentTarget.style.transform='translateY(-3px)'; e.currentTarget.style.boxShadow='0 10px 24px rgba(0,0,0,.10)'; } }}
                  onMouseLeave={e => { if(!sel){ e.currentTarget.style.transform=''; e.currentTarget.style.boxShadow='0 1px 3px rgba(0,0,0,.04)'; } }}
                  style={{flex:'1 1 190px',maxWidth:230,minHeight:198,position:'relative',padding:'24px 18px',borderRadius:18,border:'2px solid '+(sel?o.c:o.bd),background:o.bg,cursor:'pointer',textAlign:'center',boxShadow:sel?'0 8px 22px '+o.c+'2E':'0 1px 3px rgba(0,0,0,.04)',transition:'all .15s',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center'}}>
                  {sel && <span style={{position:'absolute',top:12,right:12,width:24,height:24,borderRadius:'50%',background:o.c,color:'#fff',fontSize:14,display:'inline-flex',alignItems:'center',justifyContent:'center',fontWeight:700}}>✓</span>}
                  <div style={{width:66,height:66,borderRadius:'50%',background:'#fff',border:'1.5px solid '+o.bd,display:'flex',alignItems:'center',justifyContent:'center',gap:4,fontSize:30,color:o.c,marginBottom:14,boxShadow:'0 2px 8px rgba(0,0,0,.05)'}}>
                    {o.v === 'both'
                      ? <><i className="ph ph-coins" style={{fontSize:24}}></i><i className="ph ph-scroll" style={{fontSize:24}}></i></>
                      : <i className={"ph " + o.icon}></i>}
                  </div>
                  <div style={{fontWeight:800,fontSize:16.5,color:o.c}}>{o.label}</div>
                  <div style={{color:o.dc,fontSize:12.5,marginTop:5,lineHeight:1.5}}>{o.desc}</div>
                </button>
              );
            })}
          </div>
          <div style={{display:'flex',justifyContent:'space-between',marginTop:26}}>
            <button className="btn btn-ghost" onClick={onBack}>← ย้อนกลับ</button>
            <button className="btn btn-primary" disabled={!catSel} style={{opacity:catSel?1:.5,minWidth:170,justifyContent:'center'}}
              onClick={() => { if(catSel){ const dual = catSel==='both'; set('listing_type', dual ? 'consignment' : catSel); set('dual', dual); setCatChosen(true); } }}>ถัดไป →</button>
          </div>
        </div>
      </div>
    );

    // Step 0b: เลือกประเภททรัพย์
    if (step === 0) {
      const catBadge = form.dual
        ? {bg:'#FFFBEF',bd:'#F3E2A8',c:'#9A7400',t:'ขายฝากและจำนอง'}
        : ({
            sale:        {bg:'#F1FCF6',bd:'#BBF0D4',c:'#0F7A4D',t:'ทรัพย์ฝากขาย'},
            consignment: {bg:'#FFFBEF',bd:'#F3E2A8',c:'#9A7400',t:'ขายฝาก'},
            mortgage:    {bg:'#FFFBEF',bd:'#F3E2A8',c:'#9A7400',t:'จำนอง'},
            npl:         {bg:'#FDECEC',bd:'#F3C5C5',c:'#C0392B',t:'ทรัพย์ NPL'},
          }[form.listing_type] || {bg:'#F1FCF6',bd:'#BBF0D4',c:'#0F7A4D',t:'ทรัพย์ฝากขาย'});
      return (
      <div className="page">
        <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:16,fontSize:14,flexWrap:'wrap'}}>
          <button onClick={onBack} style={{background:'none',border:'none',cursor:'pointer',color:'#1F58C4',fontWeight:600,fontSize:14,fontFamily:'inherit',display:'inline-flex',alignItems:'center',gap:5,padding:0}}>← {isNpl ? t('menu_npl_listings') : t('menu_listings_general')}</button>
          <span style={{color:'#C5CCD6'}}>/</span>
          <span style={{fontWeight:700,color:'#102E54'}}>เพิ่มทรัพย์ใหม่</span>
        </div>
        <div className="card" style={{padding:32}}>
          <div style={{display:'flex',alignItems:'center',justifyContent:'center',gap:10,marginBottom:24}}>
            <span style={{padding:'5px 14px',borderRadius:99,fontSize:13,fontWeight:700,background:catBadge.bg,border:'1.5px solid '+catBadge.bd,color:catBadge.c}}>{catBadge.t}</span>
          </div>
          <div style={{textAlign:'center',marginBottom:20}}>
            <h2 style={{fontSize:19,fontWeight:800,color:'#102E54',margin:0}}>เลือกประเภททรัพย์</h2>
          </div>
          <div style={{display:'flex',flexWrap:'wrap',justifyContent:'center',gap:14,maxWidth:920,margin:'0 auto'}}>
            {PTYPES.map(t => {
              const sel = form.property_type===t.v;
              return (
              <button key={t.v} onClick={() => set('property_type',t.v)}
                onMouseEnter={e => { if(!sel){ e.currentTarget.style.transform='translateY(-3px)'; e.currentTarget.style.boxShadow='0 8px 20px rgba(0,0,0,.09)'; e.currentTarget.style.borderColor='#B9CDEC'; } }}
                onMouseLeave={e => { if(!sel){ e.currentTarget.style.transform=''; e.currentTarget.style.boxShadow='0 1px 3px rgba(0,0,0,.04)'; e.currentTarget.style.borderColor='#E5E7EB'; } }}
                style={{flex:'1 1 195px',maxWidth:265,minHeight:138,position:'relative',padding:'22px 16px',borderRadius:16,border:'2px solid '+(sel?'#2A6BE0':'#E5E7EB'),background:sel?'#F0F6FF':'#fff',cursor:'pointer',textAlign:'center',boxShadow:sel?'0 6px 18px rgba(42,107,224,.18)':'0 1px 3px rgba(0,0,0,.04)',transition:'all .15s',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center'}}>
                {sel && <span style={{position:'absolute',top:10,right:12,width:24,height:24,borderRadius:'50%',background:'#2A6BE0',color:'#fff',fontSize:14,display:'inline-flex',alignItems:'center',justifyContent:'center',fontWeight:700}}>✓</span>}
                <div style={{width:58,height:58,borderRadius:'50%',background:sel?'#DDE9FC':'#F3F6FB',display:'flex',alignItems:'center',justifyContent:'center',fontSize:28,color:sel?'#2A6BE0':'#4A6079',marginBottom:12,transition:'all .15s'}}>
                  <i className={"ph " + t.icon}></i>
                </div>
                <div style={{fontWeight:700,fontSize:15,color:sel?'#1F58C4':'#102E54'}}>{t.l}</div>
              </button>
              );
            })}
          </div>
          <div style={{display:'flex',justifyContent:'space-between',marginTop:24}}>
            <button className="btn btn-ghost" onClick={() => { if (isNpl) { onBack(); } else { setCatSel(form.dual ? 'both' : form.listing_type); setCatChosen(false); } }}>← ย้อนกลับ</button>
            <button className="btn btn-primary" disabled={!form.property_type} style={{minWidth:170,justifyContent:'center',opacity:form.property_type?1:.5}} onClick={() => setStep(1)}>ถัดไป →</button>
          </div>
        </div>
      </div>
      );
    }

    const ptObj = PTYPES.find(t => t.v === pt);
    const ptLabel = ptObj ? ptObj.l : (pt || '—');  // แค่ชื่อ ไม่เอาชื่อคลาสไอคอน (ph-*) มาโชว์เป็นตัวอักษร
    const typeBadge = {
      sale:        {bg:'#F1FCF6',bd:'#BBF0D4',c:'#0F7A4D',t:'ทรัพย์ฝากขาย'},
      consignment: {bg:'#FFFBEF',bd:'#F3E2A8',c:'#9A7400',t:'ขายฝาก'},
      mortgage:    {bg:'#FFFBEF',bd:'#F3E2A8',c:'#9A7400',t:'จำนอง'},
      npl:         {bg:'#FDECEC',bd:'#F3C5C5',c:'#C0392B',t:'ทรัพย์ NPL'},
    }[form.listing_type] || {bg:'#F1FCF6',bd:'#BBF0D4',c:'#0F7A4D',t:'ทรัพย์ฝากขาย'};

    // การ์ดสรุป "ข้อมูลที่กรอกแล้ว" — ไล่แสดงทุกช่องที่กรอกจริง (ไม่ใช่เฉพาะช่องบังคับ)
    function renderRecap() {
      const has = v => v !== null && v !== undefined && String(v).trim() !== '';
      const baht = v => { const n = Number(String(v).replace(/,/g,'')); return (has(v) && isFinite(n)) ? n.toLocaleString('th-TH') + ' ฿' : ''; }; // กันโชว์ "NaN ฿" จากค่าขยะ
      const chip = (label, value) => has(value) ? (
        <span key={label} style={{display:'inline-flex',alignItems:'center',gap:6,background:'#fff',border:'1px solid #DDE9FC',borderRadius:8,padding:'5px 11px',fontSize:13}}>
          <span style={{color:'#6B7280'}}>{label}</span><b style={{color:'#102E54'}}>{value}</b>
        </span>
      ) : null;
      // เนื้อที่ดิน — ปัดหน่วยให้ถูก (1 ไร่=4 งาน, 1 งาน=100 ตร.ว.) ไม่โชว์หน่วยที่เป็น 0
      const totalWa = (Number(form.land_rai)||0)*400 + (Number(form.land_ngan)||0)*100 + (Number(form.land_wa)||0);
      const nRai = Math.floor(totalWa/400), remWa = totalWa%400, nNgan = Math.floor(remWa/100), nWa = Math.round((remWa%100)*100)/100;
      const land = totalWa > 0 ? [nRai && nRai+' ไร่', nNgan && nNgan+' งาน', nWa && nWa+' ตร.ว.'].filter(Boolean).join(' ') : '';
      const fi = (FIS.find(f => f.key === form.financial_institution) || {}).name;
      const chan = { lead:'ลีด', broker:'นายหน้า' }[form.source_channel];
      const items = [
        // ข้อมูลหลัก
        ['ชื่อทรัพย์', form.title],
        ['ชื่อโครงการ', form.project_name],
        ['ราคา', baht(form.price)],
        ['ต่อรอง', form.is_negotiable ? 'ต่อรองได้' : ''],
        ['รายละเอียด', has(form.description) ? (form.description.length>24 ? form.description.slice(0,24)+'…' : form.description) : ''],
        // ลงทุน
        ['ผลตอบแทน', has(form.est_return) ? form.est_return+'%/ปี' : ''],
        ['ระยะเวลา', has(form.term_years) ? form.term_years+' ปี' : ''],
        ['วงเงิน', baht(form.max_loan)],
        ['มูลค่าประเมิน', baht(form.appraised_value)],
        ['รายได้ค่าเช่า', has(form.rental_income) ? baht(form.rental_income)+'/ด.' : ''],
        // สเปค
        [form.property_type==='hotel_apartment' ? 'จำนวนห้องพัก' : 'ห้องนอน', form.bedrooms],
        [form.property_type==='hotel_apartment' ? '' : 'ห้องน้ำ', form.property_type==='hotel_apartment' ? '' : form.bathrooms],
        ['พื้นที่ใช้สอย', has(form.area_sqm) ? form.area_sqm+' ตร.ม.' : ''],
        ['จำนวนชั้น', form.floors],
        ['เนื้อที่ดิน', land || (has(form.land_sqw) ? form.land_sqw+' ตร.ว.' : '')],
        ['ชั้นที่', form.floor_number],
        ['ทิศ', form.direction],
        ['วิว', form.unit_view],
        ['ที่จอดรถ', has(form.parking_spots) ? form.parking_spots+' คัน' : ''],
        ['หน้ากว้าง', has(form.road_frontage) ? form.road_frontage+' ม.' : ''],
        ['ผังเมือง', form.city_plan],
        ['ติดถนน', form.road_access],
        // ที่ตั้ง
        ['ที่อยู่', form.full_address],
        ['แขวง/ตำบล', form.subdistrict],
        ['เขต/อำเภอ', form.district],
        ['จังหวัด', form.province],
        ['ไปรษณีย์', form.zipcode],
        ['ใกล้สถานี', form.nearby_transit],
        // ผู้ขาย/ดีล (ภายใน)
        ['ลูกค้า', form.owner_name],
        ['เลขโฉนด', form.deed_no],
        ['ภาระเดิม', baht(form.existing_debt)],
        ['สถาบันการเงิน', fi],
        ['สถานะดีล (ทีม)', form.work_status],
        ['ช่องทาง', chan],
        ['สิ่งอำนวยความสะดวก', (form.amenities && form.amenities.length) ? form.amenities.length+' รายการ' : ''],
        ['สิ่งอำนวยรอบๆ', (form.facilities && form.facilities.length) ? form.facilities.length+' รายการ' : ''],
      ];
      const chips = items.map(([l, v]) => chip(l, v)).filter(Boolean);
      const pill = (txt, bg, bd, c) => <span style={{padding:'4px 12px',borderRadius:99,fontSize:12.5,fontWeight:700,background:bg,border:'1.5px solid '+bd,color:c,whiteSpace:'nowrap'}}>{txt}</span>;
      return (
        <div style={{background:'#F4F8FD',border:'1px solid #DDE9FC',borderRadius:10,padding:'10px 14px',marginBottom:16}}>
          <div style={{fontSize:12,fontWeight:700,color:'#1F58C4',marginBottom:8}}><i className="ph ph-clipboard-text" style={{marginRight:5}}></i>ข้อมูลที่กรอกแล้ว</div>
          <div style={{display:'flex',flexWrap:'wrap',gap:8,alignItems:'center'}}>
            {pill(typeBadge.t, typeBadge.bg, typeBadge.bd, typeBadge.c)}
            {pill(ptLabel, '#EAF1FC', '#C9DDF8', '#1F58C4')}
            {chips}
          </div>
        </div>
      );
    }

    // สเปคทรัพย์ — ใช้ซ้ำได้ (หมวดฝากขาย: คอลัมน์ขวาใต้ผู้ขาย · หมวดลงทุน: เต็มแถว)
    function renderSpec() {
      return (<>
            <h3 style={{margin:'4px 0 8px',color:'#102E54',display:'flex',alignItems:'center',justifyContent:'space-between',gap:12,flexWrap:'wrap'}}>
              <span><i className="ph ph-ruler" style={{marginRight:6,color:'#4A6079'}}></i>สเปคทรัพย์</span>
              {(() => {
                const useWah = isLand || hasLandSqw;
                if (!isCondo && !useWah) return null;
                const priceN = Number(String(form.price||'').replace(/,/g,'')) || 0;
                const areaN = Number(form.area_sqm) || 0;
                const wah = (Number(form.land_rai)||0)*400 + (Number(form.land_ngan)||0)*100 + (Number(form.land_wa)||0);
                const val = isCondo ? (priceN>0 && areaN>0 ? Math.round(priceN/areaN) : null)
                                    : (priceN>0 && wah>0 ? Math.round(priceN/wah) : null);
                const unit = isCondo ? 'ตร.ม.' : 'ตร.ว.';
                return <span style={{fontSize:12,fontWeight:600,color:'#0A7D46',background:'#E9F7F0',border:'1px solid #BFE6D2',borderRadius:99,padding:'4px 11px',whiteSpace:'nowrap'}} title="คำนวณอัตโนมัติจาก ราคา ÷ เนื้อที่"><i className="ph ph-calculator" style={{marginRight:4}}></i>ราคา/{unit}: {val != null ? val.toLocaleString('th-TH') + ' ฿' : '— กรอกราคา+เนื้อที่'}</span>;
              })()}
            </h3>
            <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(98px,1fr))',gap:10}}>
              {hasRooms && <div className="form-group"><label>{form.property_type==='hotel_apartment' ? 'จำนวนห้องพัก (ห้อง)' : 'ห้องนอน'}</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} value={form.bedrooms||''} onChange={e => set('bedrooms',e.target.value)} /></div>}
              {hasRooms && form.property_type!=='hotel_apartment' && <div className="form-group"><label>ห้องน้ำ</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} value={form.bathrooms||''} onChange={e => set('bathrooms',e.target.value)} /></div>}
              {hasArea && <div className="form-group"><label>พื้นที่ใช้สอย (ตร.ม.)</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} value={form.area_sqm||''} onChange={e => set('area_sqm',e.target.value)} /></div>}
              {hasFloors && <div className="form-group"><label>จำนวนชั้น</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} value={form.floors||''} onChange={e => set('floors',e.target.value)} /></div>}
              {hasLandSqw && <div className="form-group" style={{gridColumn:'span 2'}}><label>เนื้อที่ดิน (ไร่-งาน-วา)</label>
                <div style={{display:'flex',gap:6}}>
                  <input className="input" type="number" min="0" onKeyDown={window.blockNeg} placeholder="ไร่" value={form.land_rai||''} onChange={e => set('land_rai',e.target.value)} />
                  <input className="input" type="number" min="0" onKeyDown={window.blockNeg} placeholder="งาน" value={form.land_ngan||''} onChange={e => set('land_ngan',e.target.value)} />
                  <input className="input" type="number" min="0" onKeyDown={window.blockNeg} placeholder="ตร.ว." value={form.land_wa||''} onChange={e => set('land_wa',e.target.value)} />
                </div>
              </div>}
              {isCondo && <div className="form-group"><label>ชั้นที่</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} value={form.floor_number||''} onChange={e => set('floor_number',e.target.value)} /></div>}
              {isCondo && <div className="form-group" style={{gridColumn:'span 2'}}><label>ทิศ</label><select className="input" value={form.direction||''} onChange={e => set('direction',e.target.value)}>
                <option value="">เลือก</option><option>เหนือ</option><option>ใต้</option><option>ตะวันออก</option><option>ตะวันตก</option><option>ตะวันออกเฉียงเหนือ</option><option>ตะวันออกเฉียงใต้</option><option>ตะวันตกเฉียงเหนือ</option><option>ตะวันตกเฉียงใต้</option>
              </select></div>}
              {isCondo && <div className="form-group"><label>วิว</label><input className="input" value={form.unit_view||''} onChange={e => set('unit_view',e.target.value)} placeholder="เช่น วิวแม่น้ำ" /></div>}
              {hasRooms && <div className="form-group"><label>ที่จอดรถ (คัน)</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} value={form.parking_spots||''} onChange={e => set('parking_spots',e.target.value)} /></div>}
              {isLand && <div style={{gridColumn:'1/-1',display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(190px,1fr))',gap:14,alignItems:'start'}}>
                <div className="form-group"><label>เนื้อที่ดิน (ไร่-งาน-วา)</label>
                  <div style={{display:'flex',gap:6}}>
                    <input className="input" type="number" min="0" onKeyDown={window.blockNeg} placeholder="ไร่" value={form.land_rai||''} onChange={e => set('land_rai',e.target.value)} />
                    <input className="input" type="number" min="0" onKeyDown={window.blockNeg} placeholder="งาน" value={form.land_ngan||''} onChange={e => set('land_ngan',e.target.value)} />
                    <input className="input" type="number" min="0" onKeyDown={window.blockNeg} placeholder="ตร.ว." value={form.land_wa||''} onChange={e => set('land_wa',e.target.value)} />
                  </div>
                </div>
                <div className="form-group"><label>หน้ากว้าง (เมตร)</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} value={form.road_frontage||''} onChange={e => set('road_frontage',e.target.value)} /></div>
                {(() => {
                  const ra = form.road_access || '';
                  const abuts = ra ? (ra.includes('ไม่ติด') ? 'no' : 'yes') : '';
                  const lanes = ra.includes('มากกว่า') ? '>8' : ((ra.match(/(\d+)\s*เลน/) || [])[1] || '');
                  const laneTxt = (v) => v === '>8' ? ' มากกว่า 8 เลน' : (v ? ' ' + v + ' เลน' : '');
                  const setAbuts = (v) => set('road_access', v === 'no' ? 'ไม่ติดถนน' : v === 'yes' ? ('ติดถนน' + laneTxt(lanes)) : '');
                  const setLanes = (v) => set('road_access', 'ติดถนน' + laneTxt(v));
                  return (
                    <div className="form-group"><label>ติดถนน</label>
                      <div style={{display:'flex',gap:6}}>
                        <select className="input" style={{flex:1}} value={abuts} onChange={e => setAbuts(e.target.value)}>
                          <option value="">เลือก</option>
                          <option value="no">ไม่ติดถนน</option>
                          <option value="yes">ติดถนน</option>
                        </select>
                        {abuts === 'yes' && <select className="input" style={{flex:1}} value={lanes} onChange={e => setLanes(e.target.value)}>
                          <option value="">จำนวนเลน</option>
                          <option value="2">2 เลน</option>
                          <option value="4">4 เลน</option>
                          <option value="6">6 เลน</option>
                          <option value="8">8 เลน</option>
                          <option value=">8">มากกว่า 8 เลน</option>
                        </select>}
                      </div>
                    </div>
                  );
                })()}
              </div>}
            </div>
      </>);
    }

    // ประเมิน & ดีล (ภายใน): ดึงราคาประเมิน + Risk Grade + ข้อมูลดีล — โชว์ในแท็บ "ประเมิน & ดีล" (ทรัพย์ที่บันทึกแล้ว) หรือใน wizard ตอนสร้างใหม่
    function renderInternalSections() {
      if (!isInvest) return null;
      return (<>
        <LandPriceField form={form} set={set} />
        <h3 style={{margin:'18px 0 16px',color:'#102E54',display:'flex',alignItems:'center',gap:8}}>
          <i className="ph ph-gauge" style={{marginRight:6,color:'#4A6079'}}></i>ระดับความเสี่ยง (Risk Grade)
          <Hint><i className="ph ph-lightbulb" style={{marginRight:4}}></i>แอดมินกรอกแค่ <b>เอกสาร</b> และปรับ <b>ทำเล</b> (ถ้าจำเป็น) — ระบบดึง LTV + ระยะเวลามาเอง แล้วคำนวณเกรดให้อัตโนมัติ</Hint>
        </h3>
        <div style={{display:'grid',gridTemplateColumns:'1.3fr 1fr',gap:20,alignItems:'start'}}>
          <div className="form-group">
            <label>เช็กลิสต์เอกสาร (ติ๊กที่มี)</label>
            <div style={{display:'flex',flexDirection:'column',gap:8,marginTop:4}}>
              {(() => {
                const DOC_LABELS = { deed:'โฉนด / เอกสารสิทธิ์', appraisal:'หนังสือประเมินราคา', owner_id:'สำเนาบัตรเจ้าของ', encumbrance:'เอกสารภาระผูกพัน', photos:'รูปถ่ายทรัพย์' };
                const required = riskCfg?.risk_grade_config?.document?.required || ['deed','appraisal','owner_id','encumbrance','photos'];
                const critical = riskCfg?.risk_grade_config?.document?.critical || ['deed'];
                return required.map(k => (
                  <label key={k} style={{display:'flex',alignItems:'center',gap:10,cursor:'pointer',fontSize:14}}>
                    <input type="checkbox" checked={docProvided(k)} onChange={() => toggleDoc(k)} style={{width:18,height:18}} />
                    <span>{DOC_LABELS[k] || k} {critical.includes(k) && <span style={{color:'#D8567A',fontSize:12}}>(สำคัญ)</span>}</span>
                  </label>
                ));
              })()}
            </div>
          </div>
          <div style={{display:'flex',flexDirection:'column',gap:14}}>
            <div className="form-group">
              <label>ระดับความเสี่ยงทำเล</label>
              <select className="input" value={form.location_risk_override||''} onChange={e => set('location_risk_override',e.target.value)}>
                <option value="">อัตโนมัติจากจังหวัด{riskPreview?.factors ? ' (' + ({low:'ต่ำ',medium:'กลาง',high:'สูง'}[riskPreview.factors.location_level]||'-') + ')' : ''}</option>
                <option value="low">ปรับเป็น: ต่ำ</option>
                <option value="medium">ปรับเป็น: กลาง</option>
                <option value="high">ปรับเป็น: สูง</option>
              </select>
            </div>
            <div className="form-group"><label>LTV (คำนวณอัตโนมัติจากยอดสินเชื่อ ÷ มูลค่า)</label>
              <div className="input" style={{display:'flex',alignItems:'center',background:'#F3F6FB',fontWeight:700,color:'#1F58C4'}}>
                {riskPreview && riskPreview.ltv != null ? riskPreview.ltv + '%' : '—'}
              </div>
            </div>
            {riskPreview && (() => {
              const g = riskPreview.grade;
              const tone = {
                A:['#15976A','#DBF1E7','A · ต่ำมาก'], B:['#15976A','#DBF1E7','B · ต่ำ'],
                C:['#B5860B','#FBF0D0','C · ปานกลาง'], D:['#D9822B','#FBE9D6','D · สูง'],
                E:['#D8567A','#FBE4EC','E · สูงมาก'],
              }[g] || ['#555','#eee',g];
              const f = riskPreview.factors || {};
              const lvTxt = { 1:'ต่ำ',2:'กลาง',3:'สูง' };
              return (
                <div style={{background:tone[1],borderRadius:12,padding:'14px 16px'}}>
                  <div style={{display:'flex',alignItems:'center',justifyContent:'space-between'}}>
                    <span style={{fontSize:13,color:'#444'}}>ระดับความเสี่ยง</span>
                    <span style={{fontWeight:800,fontSize:20,color:tone[0]}}>{tone[2]} ({riskPreview.score})</span>
                  </div>
                  <div style={{marginTop:10,fontSize:12.5,color:'#555',lineHeight:1.7}}>
                    LTV {riskPreview.ltv!=null?riskPreview.ltv+'%':'—'} = {lvTxt[f.ltv]} · ทำเล = {lvTxt[f.location]} · เอกสาร{f.missing_docs?.length?(' ขาด '+f.missing_docs.length+' ใบ'):' ครบ'} = {lvTxt[f.document]} · ระยะเวลา = {lvTxt[f.term]}
                    <br/><span style={{color:'#777'}}>ถ่วงน้ำหนัก (LTV40/ทำเล30/เอกสาร20/เวลา10) = {f.weighted}</span>
                  </div>
                </div>
              );
            })()}
          </div>
        </div>
        <h3 style={{margin:'18px 0 8px',color:'#102E54'}}><i className="ph ph-file-text" style={{marginRight:6,color:'#4A6079'}}></i>ข้อมูลดีล (ขายฝาก/จำนอง) <span style={{fontSize:12,fontWeight:500,color:'#8A6D1B'}}>· ข้อมูลภายใน ไม่โชว์หน้าเว็บ</span></h3>
        <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:14}}>
          <div className="form-group"><label>ชื่อ-สกุล ลูกค้า</label><input className="input" value={form.owner_name||''} onChange={e => set('owner_name',e.target.value)} /></div>
          <div className="form-group"><label>เลขที่โฉนด</label><input className="input" value={form.deed_no||''} onChange={e => set('deed_no',e.target.value)} /></div>
          <div className="form-group"><label>ภาระเดิม (บาท)</label><window.NumInput className="input" value={form.existing_debt||''} onChange={v => set('existing_debt',v)} placeholder="หนี้/จำนองเดิม" /></div>
          <div className="form-group"><label>สถาบันการเงิน</label><FiSelect value={form.financial_institution||''} onChange={v => set('financial_institution',v)} /></div>
          <div className="form-group"><label>สถานะดีล (ทีม) {form.work_status && <WorkStatusBadge status={form.work_status} />}</label>
            <select className="input" value={form.work_status||''} onChange={e => set('work_status',e.target.value)}>
              <option value="">เลือก</option>
              {WORK_STATUSES.map(s => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
          <div className="form-group"><label>ช่องทาง</label>
            <select className="input" value={form.source_channel||''} onChange={e => { const v=e.target.value; set('source_channel',v); if(v!=='broker') set('broker_id',null); if(v!=='lead') set('owner_id',''); }}>
              <option value="">เลือก</option>
              <option value="lead">ลีด (โดยตรง)</option>
              <option value="broker">นายหน้า</option>
            </select>
          </div>
          {form.source_channel==='broker' && <div className="form-group"><label>เลือกนายหน้า</label>
            <select className="input" value={form.broker_id||''} onChange={e => set('broker_id',e.target.value||null)}>
              <option value="">— เลือกนายหน้า —</option>
              {brokers.map(b => <option key={b.id} value={b.id}>{b.name}{b.company?' ('+b.company+')':''}</option>)}
            </select>
            {brokers.length===0 && <span style={{fontSize:12,color:'#8A6D1B'}}>ยังไม่มีนายหน้า — เพิ่มที่เมนู "นายหน้า"</span>}
          </div>}
          {form.source_channel==='lead' && <div className="form-group"><label>เจ้าของทรัพย์</label>
            <select className="input" value={form.owner_id||''} onChange={e => set('owner_id',e.target.value||null)}>
              <option value="">— เลือกเจ้าของทรัพย์ —</option>
              {owners.map(o => <option key={o.id} value={o.id}>{o.name}</option>)}
            </select>
            {owners.length===0 && <span style={{fontSize:12,color:'#8A6D1B'}}>ยังไม่มีเจ้าของทรัพย์ — เพิ่มที่เมนู "เจ้าของทรัพย์"</span>}
          </div>}
        </div>
      </>);
    }

    // ทรัพย์ใหม่ = wizard (wstep) · ทรัพย์ที่บันทึกแล้ว = แท็บบน (basic/location) ให้เข้าชุดกับแท็บอื่น
    const showStep1 = (isNew && wstep === 1) || (!isNew && activeTab === 'basic');
    const showStep2 = (isNew && wstep === 2) || (!isNew && activeTab === 'location');
    const showInfoArea = isNew || activeTab === 'basic' || activeTab === 'location';

    return (
      <div className="page" style={{maxWidth:1080, margin:'0 auto'}}>
        {/* breadcrumb (ซ้าย) + บันทึก (ขวา) — stepper ย้ายลงไปในแท็บ "ข้อมูลทรัพย์" แล้ว */}
        <div className="page-header" style={{gap:16, marginBottom:20}}>
          <div style={{display:'inline-flex',alignItems:'center',gap:7,fontSize:14,whiteSpace:'nowrap'}}>
            <button onClick={onBack} style={{background:'none',border:'none',cursor:'pointer',color:'#1F58C4',fontWeight:600,fontSize:14,fontFamily:'inherit',display:'inline-flex',alignItems:'center',gap:5,padding:0}}>← {isNpl ? t('menu_npl_listings') : t('menu_listings_general')}</button>
            <span style={{color:'#C5CCD6'}}>/</span>
            <span style={{fontWeight:700,color:'#102E54'}}>{isNew && !savedId ? 'เพิ่มทรัพย์' : 'แก้ไขทรัพย์'}</span>
          </div>
          <div style={{flex:1}} />
          {showInfoArea && (
          <div className="row gap-10" style={{alignItems:'center'}}>
            <window.AutosaveStatus state={autosave} />
            {saveMsg && <span style={{fontWeight:600,color:saveMsg.includes('✅')?'#15976A':'#D8567A'}}>{saveMsg}</span>}
            <button className="btn btn-primary" onClick={saveGuarded} disabled={saving}>{saving ? 'กำลังบันทึก...' : <><i className="ph ph-floppy-disk" style={{marginRight:5}}></i>บันทึก</>}</button>
          </div>
          )}
        </div>

        {/* แท็บจัดการทรัพย์แบบ hub — ข้อมูล / ดีล / ผู้สนใจ (เฉพาะทรัพย์ที่บันทึกแล้ว) */}
        {!isNew && (
          <div style={{display:'flex',gap:4,marginBottom:20,borderBottom:'1px solid var(--border)',flexWrap:'wrap'}}>
            {[['basic','ph-clipboard-text','ข้อมูลหลัก & สเปค'],['location','ph-map-pin','ที่ตั้ง & รูปภาพ'],...(isInvest?[['assess','ph-gauge','ประเมิน & ดีล']]:[]),['loanform','ph-file-text','ใบขอสินเชื่อ'],['deal','ph-handshake','สถานะดีล + ไทม์ไลน์'],['interested','ph-users-three','ผู้สนใจทรัพย์นี้']].map(([k,ic,l]) => (
              <button key={k} onClick={() => setActiveTab(k)} style={{border:'none',background:'none',cursor:'pointer',fontFamily:'inherit',fontSize:14,fontWeight:activeTab===k?700:600,color:activeTab===k?'#1F58C4':'#6B7280',padding:'9px 16px',borderBottom:'2.5px solid '+(activeTab===k?'#1F58C4':'transparent'),marginBottom:-1,display:'inline-flex',alignItems:'center',gap:6}}><i className={'ph '+ic}></i>{l}</button>
            ))}
          </div>
        )}
        {!isNew && activeTab === 'deal' && <DealTab id={id} initialStatus={form.work_status} onChanged={() => loadListing(id)} />}
        {!isNew && activeTab === 'interested' && (
          <div style={{maxWidth:760}}>
            <div style={{fontSize:12.5,color:'var(--text-2)',marginBottom:14}}>คนที่กด “สนใจ” ทรัพย์นี้ (นักลงทุน + ผู้สนใจซื้อ) — โทร/อีเมลติดต่อ แล้วอัปเดตสถานะ+โน้ต (sync กับ “รายการที่ต้องติดตาม”)</div>
            <InterestedList listingId={id} />
          </div>
        )}
        {!isNew && activeTab === 'assess' && (
          <div style={{maxWidth:1040}}>
            <div className="row gap-10" style={{alignItems:'center',marginBottom:16}}>
              <div style={{fontSize:12.5,color:'var(--text-2)',flex:1}}><i className="ph ph-lock-key" style={{marginRight:5,color:'#8A6D1B'}}></i>ข้อมูลภายในสำหรับทีมงาน (ประเมินราคา · เกรดความเสี่ยง · ข้อมูลดีล) — ไม่แสดงบนเว็บ</div>
              <window.AutosaveStatus state={autosave} />
              {saveMsg && <span style={{fontWeight:600,color:saveMsg.includes('✅')?'#15976A':'#D8567A'}}>{saveMsg}</span>}
              <button className="btn btn-primary" onClick={save} disabled={saving}>{saving ? 'กำลังบันทึก...' : <><i className="ph ph-floppy-disk" style={{marginRight:5}}></i>บันทึก</>}</button>
            </div>
            {renderInternalSections()}
          </div>
        )}
        {!isNew && activeTab === 'loanform' && (
          <div style={{maxWidth:1040}}>
            <div className="row gap-10" style={{alignItems:'center',marginBottom:6,justifyContent:'flex-end'}}>
              <window.AutosaveStatus state={autosave} />
              {saveMsg && <span style={{fontWeight:600,color:saveMsg.includes('✅')?'#15976A':'#D8567A'}}>{saveMsg}</span>}
            </div>
            <LoanFormTab form={form} set={set} images={images} id={id} />
          </div>
        )}
        {showInfoArea && (<>
        {/* stepper โชว์เฉพาะตอนสร้างทรัพย์ใหม่ (guided) · ทรัพย์ที่บันทึกแล้วใช้แท็บบนแทน */}
        {isNew && (
        <div style={{display:'flex', alignItems:'center', justifyContent:'center', gap:0, flexWrap:'wrap', marginBottom:18}}>
          {[{n:1,l:'ข้อมูลหลัก & สเปค'},{n:2,l:'ที่ตั้ง & รูปภาพ'}].map((s,i) => (
            <React.Fragment key={s.n}>
              {i>0 && <div style={{flex:'0 0 24px',height:2,background: wstep>=s.n ? '#1F58C4':'#E2E8F2'}} />}
              <button onClick={() => goToStep(s.n)}
                style={{display:'inline-flex',alignItems:'center',gap:7,border:'none',background:'none',cursor:'pointer',padding:'4px 8px',fontFamily:'inherit'}}>
                <span style={{width:26,height:26,borderRadius:'50%',display:'inline-flex',alignItems:'center',justifyContent:'center',fontWeight:700,fontSize:13,
                  background: wstep===s.n ? '#1F58C4' : wstep>s.n ? '#15976A' : '#E2E8F2',
                  color: wstep>=s.n ? '#fff' : '#6B7280'}}>{wstep>s.n ? '✓' : s.n}</span>
                <span style={{fontSize:13.5,fontWeight: wstep===s.n?700:500, color: wstep===s.n ? '#102E54' : '#6B7280'}}>{s.l}</span>
              </button>
            </React.Fragment>
          ))}
        </div>
        )}
        {/* กล่องข้อมูล NPL (ภายใน) — เฉพาะทรัพย์ NPL ที่นำเข้าจาก SWP · ไม่โชว์ลูกค้า */}
        {form.listing_type === 'npl' && form.npl_meta && typeof form.npl_meta === 'object' && (
          <div className="card" style={{padding:16, background:'#FFF9F0', border:'1px solid #F0D9B5', maxWidth:1040, margin:'0 auto 16px'}}>
            <div style={{fontWeight:700,color:'#9A6A00',marginBottom:10,display:'flex',alignItems:'center',gap:6}}><i className="ph ph-lock-key"></i> ข้อมูล NPL (ภายใน — ไม่แสดงให้ลูกค้าเห็น)</div>
            <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(190px,1fr))',gap:'8px 18px',fontSize:13}}>
              {[['แหล่ง/บริษัท','source'],['รหัสลูกหนี้','debtor_code'],['สถานะกรรมสิทธิ์','ownership'],['ผู้อยู่อาศัย','occupancy'],['ประเภทย่อย','subtype'],['เลขหลักประกัน','collateral_no'],['วางมัดจำ','deposit']].map(([lb,k]) => form.npl_meta[k] ? (
                <div key={k}><span style={{color:'var(--text-2)'}}>{lb}: </span><b style={{color:'#102E54'}}>{form.npl_meta[k]}</b></div>
              ) : null)}
            </div>
            {form.npl_meta.source_url && <div style={{marginTop:8,fontSize:12.5}}><a href={form.npl_meta.source_url} target="_blank" rel="noopener" style={{color:'#1F58C4'}}>↗ เปิดหน้าทรัพย์ต้นทาง (swpamc)</a> <span style={{color:'var(--text-2)'}}>— สำหรับทีมงานเท่านั้น</span></div>}
          </div>
        )}
        {/* ============ STEP 1-2: ฟอร์มข้อมูล ============ */}
        {wstep <= 2 && (
          <div className="card" style={{padding:24, maxWidth:1040, margin:'0 auto'}}>
            {renderRecap()}
            {showStep1 && <>
            {/* รหัสทรัพย์ (สร้างอัตโนมัติ แก้ไม่ได้) ย้ายมาอยู่ข้างหัวข้อ — ประหยัดพื้นที่ */}
            <div className="row between" style={{marginBottom:10}}>
              <h3 style={{margin:0,color:'#102E54'}}><i className="ph ph-clipboard-text" style={{marginRight:6,color:'#4A6079'}}></i>ข้อมูลหลัก</h3>
              <span style={{display:'inline-flex',alignItems:'center',gap:8,background:'#F3F6FB',border:'1px solid #E2E8F2',borderRadius:99,padding:'5px 14px',fontSize:13}}>
                <span style={{color:'#6B7280'}}>รหัสทรัพย์</span>
                <b style={{color: savedId?'#102E54':'#9CA3AF',letterSpacing:.3}}>{form.listing_code || 'กำลังสร้าง...'}</b>
              </span>
            </div>
            <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:14}}>
              {/* ชื่อทรัพย์ + ชื่อโครงการ บรรทัดเดียวกัน */}
              <div className="form-group" style={{gridColumn:'span 2'}}><label>ชื่อทรัพย์ *</label><input className="input" style={errFields.title?{borderColor:'#D8567A'}:{}} value={form.title} onChange={e => { set('title',e.target.value); if(errFields.title) setErrFields(p => ({...p,title:undefined})); }} placeholder="เช่น คอนโด High Rise วิวแม่น้ำ" />{errFields.title && <span style={{color:'#D8567A',fontSize:12,marginTop:4,display:'block'}}>{errFields.title}</span>}</div>
              <div className="form-group" style={{gridColumn:'span 2'}}><label>ชื่อโครงการ</label><input className="input" value={form.project_name||''} onChange={e => set('project_name',e.target.value)} /></div>
              {/* ราคาขาย ย้ายขึ้นมาบรรทัดเดียวกับ ต่อรองได้ (สถานะทรัพย์เอาออก → คุมที่ปุ่มเปิด/ปิดในตารางหน้ารวม) */}
              <div className="form-group" style={{gridColumn:'span 2'}}>
                <label style={{display:'flex',alignItems:'center',gap:6}}>ราคาขาย (บาท) *
                  <Hint color={isInvest?'#9A7400':'#0A7D46'}>
                    {isInvest
                      ? <span><b style={{color:'#8A6D1B'}}><i className="ph ph-chart-line-up" style={{marginRight:4}}></i>หมวดลงทุน (จำนอง/ขายฝาก)</b><br/>กรอกรายละเอียดการลงทุนเพิ่มในส่วน "ข้อมูลการลงทุน" ด้านล่าง</span>
                      : <span><b style={{color:'#0A7D46'}}><i className="ph ph-tag" style={{marginRight:4}}></i>หมวดทรัพย์ฝากขาย</b><br/>ลงแค่ราคาขาย ไม่มีผลตอบแทน/วงเงิน/ความเสี่ยง</span>}
                  </Hint>
                </label>
                <window.NumInput className="input" style={errFields.price?{borderColor:'#D8567A'}:{}} value={form.price} onChange={v => {
                  set('price', v); // set() กัน event/object หลุดเข้ามาให้แล้วแบบรวมศูนย์
                  if(errFields.price) setErrFields(p => ({...p,price:undefined}));
                }} placeholder="เช่น 5,000,000" />
                {errFields.price && <span style={{color:'#D8567A',fontSize:12,marginTop:4,display:'block'}}>{errFields.price}</span>}
              </div>
              <div className="form-group" style={{gridColumn:'span 2'}}><label>ต่อรองได้</label><select className="input" value={form.is_negotiable?'yes':'no'} onChange={e => set('is_negotiable',e.target.value==='yes')}>
                <option value="no">ไม่ต่อรอง</option><option value="yes">ต่อรองได้</option>
              </select></div>
              {/* หมวดลงทุน: รายละเอียดเต็มแถวที่นี่ · หมวดฝากขาย: ย้ายไปคู่กับข้อมูลผู้ขายด้านล่าง */}
              {isInvest && <div className="form-group" style={{gridColumn:'span 4'}}><label>รายละเอียด</label><textarea className="input" rows={3} value={form.description||''} onChange={e => set('description',e.target.value)} /></div>}
            </div>

            {/* หมวดลงทุน: ข้อมูลการลงทุน · หมวดฝากขาย: ข้อมูลผู้ขาย (ราคาขายย้ายไปอยู่ข้อมูลหลักแล้ว) */}
            {isInvest ? (<>
            <h3 style={{margin:'14px 0 8px',color:'#102E54',display:'flex',alignItems:'center',gap:8}}>
              <i className="ph ph-chart-line-up" style={{marginRight:4}}></i>ข้อมูลการลงทุน
              <Hint color="#9A7400"><span><b style={{color:'#8A6D1B'}}><i className="ph ph-chart-line-up" style={{marginRight:4}}></i>หมวดลงทุน (จำนอง/ขายฝาก)</b><br/>ผลตอบแทน · ยอดสินเชื่อ · มูลค่าประเมิน · LTV · ระยะเวลา · เรตติ้งความเสี่ยง</span></Hint>
            </h3>
            {form.dual && <div style={{fontSize:13.5,fontWeight:700,color:'#9A7400',background:'#FFFBEF',border:'1px solid #F3E2A8',borderRadius:8,padding:'6px 12px',marginBottom:10,display:'inline-block'}}>เงื่อนไขแบบ: {investTypeTh(form.listing_type)}</div>}
            <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(160px,1fr))',gap:12}}>
              <div className="form-group"><label>ผลตอบแทน (% ต่อปี)</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} step="0.1" value={form.est_return||''} onChange={e => set('est_return',e.target.value)} placeholder="เช่น 7.5" /></div>
              <div className="form-group"><label>ระยะเวลา (ปี)</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} step="0.5" value={form.term_years||''} onChange={e => set('term_years',e.target.value)} placeholder="เช่น 3" /></div>
              <div className="form-group"><label>ยอดขอสินเชื่อ (บาท)</label><window.NumInput className="input" value={form.max_loan||''} onChange={v => set('max_loan',v)} placeholder="เช่น 12,000,000" /></div>
              <div className="form-group"><label>มูลค่าประเมิน (บาท){form.dual && <span style={{fontSize:11,color:'#8A6D1B'}}> · ใช้ร่วมทั้ง 2 แบบ</span>}</label><window.NumInput className="input" value={form.appraised_value||''} onChange={v => set('appraised_value',v)} placeholder="ไม่กรอก = ใช้ราคา" /></div>
              {isCommercial && <div className="form-group"><label>รายได้ค่าเช่า (บาท/เดือน)</label><window.NumInput className="input" value={form.rental_income||''} onChange={v => set('rental_income',v)} /></div>}
            </div>
            {form.dual && (<>
              <div style={{fontSize:13.5,fontWeight:700,color:'#9A7400',background:'#FFFBEF',border:'1px solid #F3E2A8',borderRadius:8,padding:'6px 12px',margin:'16px 0 10px',display:'inline-block'}}>เงื่อนไขแบบ: {investTypeTh(otherInvestType)} <span style={{fontSize:11,fontWeight:500,color:'#8A6D1B'}}>· มูลค่าประเมินใช้ร่วมกับด้านบน</span></div>
              <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(160px,1fr))',gap:12}}>
                <div className="form-group"><label>ผลตอบแทน (% ต่อปี)</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} step="0.1" value={form.sec_est_return||''} onChange={e => set('sec_est_return',e.target.value)} placeholder="เช่น 6.5" /></div>
                <div className="form-group"><label>ระยะเวลา (ปี)</label><input className="input" type="number" min="0" onKeyDown={window.blockNeg} step="0.5" value={form.sec_term_years||''} onChange={e => set('sec_term_years',e.target.value)} placeholder="เช่น 3" /></div>
                <div className="form-group"><label>ยอดขอสินเชื่อ (บาท)</label><window.NumInput className="input" value={form.sec_max_loan||''} onChange={v => set('sec_max_loan',v)} placeholder="เช่น 9,000,000" /></div>
              </div>
            </>)}
            </>) : (
            <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:24,alignItems:'stretch',marginTop:14}}>
              {/* ซ้าย: รายละเอียด (ยืดเต็มความสูงให้เท่าฝั่งขวา) */}
              <div style={{display:'flex',flexDirection:'column'}}>
                <h3 style={{margin:'0 0 8px',color:'#102E54'}}><i className="ph ph-note-pencil" style={{marginRight:6,color:'#4A6079'}}></i>รายละเอียดทรัพย์</h3>
                <textarea className="input" style={{resize:'vertical',minHeight:230,flex:1}} value={form.description||''} onChange={e => set('description',e.target.value)} placeholder="รายละเอียดเพิ่มเติมของทรัพย์..." />
              </div>
              {/* ขวา: ข้อมูลผู้ขาย / ช่องทาง (ภายใน) */}
              <div>
                <h3 style={{margin:'0 0 8px',color:'#102E54'}}><i className="ph ph-file-text" style={{marginRight:6,color:'#4A6079'}}></i>ข้อมูลผู้ขาย / ช่องทาง <span style={{fontSize:12,fontWeight:500,color:'#8A6D1B'}}>· ภายใน</span></h3>
                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14}}>
                  {/* เลือกช่องทางก่อน → แล้วค่อยโชว์ช่องที่เกี่ยวข้อง (ลีด=ชื่อลูกค้า · นายหน้า=เลือกนายหน้า) */}
                  <div className="form-group" style={{gridColumn:'span 2'}}><label>ช่องทาง</label>
                    <select className="input" value={form.source_channel||''} onChange={e => { const v=e.target.value; set('source_channel',v); if(v==='broker') set('owner_name',''); else set('broker_id',null); if(v!=='lead') set('owner_id',''); }}>
                      <option value="">— เลือกช่องทางก่อน —</option>
                      <option value="lead">ลีด (โดยตรง)</option>
                      <option value="broker">นายหน้า</option>
                    </select>
                  </div>
                  {form.source_channel==='lead' && <div className="form-group" style={{gridColumn:'span 2'}}><label>เจ้าของทรัพย์</label>
                    <select className="input" value={form.owner_id||''} onChange={e => set('owner_id',e.target.value||null)}>
                      <option value="">— เลือกเจ้าของทรัพย์ —</option>
                      {owners.map(o => <option key={o.id} value={o.id}>{o.name}</option>)}
                    </select>
                    {owners.length===0 && <span style={{fontSize:12,color:'#8A6D1B'}}>ยังไม่มีเจ้าของทรัพย์ — เพิ่มที่เมนู “เจ้าของทรัพย์”</span>}
                  </div>}
                  {form.source_channel==='lead' && <div className="form-group" style={{gridColumn:'span 2'}}><label>ชื่อ-สกุล ลูกค้า <span style={{fontSize:11,fontWeight:400,color:'#9AA6B5'}}>(ถ้ามี)</span></label><input className="input" value={form.owner_name||''} onChange={e => set('owner_name',e.target.value)} /></div>}
                  {form.source_channel==='broker' && <div className="form-group" style={{gridColumn:'span 2'}}><label>เลือกนายหน้า</label>
                    <select className="input" value={form.broker_id||''} onChange={e => set('broker_id',e.target.value||null)}>
                      <option value="">— เลือกนายหน้า —</option>
                      {brokers.map(b => <option key={b.id} value={b.id}>{b.name}{b.company?' ('+b.company+')':''}</option>)}
                    </select>
                    {brokers.length===0 && <span style={{fontSize:12,color:'#8A6D1B'}}>ยังไม่มีนายหน้า — เพิ่มที่เมนู “นายหน้า”</span>}
                  </div>}
                  {isCommercial && <div className="form-group" style={{gridColumn:'span 2'}}><label>รายได้ค่าเช่า (บาท/เดือน)</label><window.NumInput className="input" value={form.rental_income||''} onChange={v => set('rental_income',v)} /></div>}
                  <div className="form-group" style={{gridColumn:'span 2'}}><label>เลขที่โฉนด</label><input className="input" value={form.deed_no||''} onChange={e => set('deed_no',e.target.value)} placeholder="เลขที่โฉนดที่ดิน" /></div>
                </div>
                <div style={{marginTop:16}}>{renderSpec()}</div>
              </div>
            </div>
            )}

            {isInvest && renderSpec()}

            </>}

            {showStep2 && <>
            {/* Risk Grade + ข้อมูลดีล + ดึงราคาประเมิน ย้ายไปแท็บ "ประเมิน & ดีล" แล้ว · ตอนสร้างใหม่ยังกรอกในนี้ได้ */}
            {isNew && renderInternalSections()}

            <h3 style={{margin:'14px 0 8px',color:'#102E54',display:'flex',alignItems:'center',gap:8}}>
              <i className="ph ph-map-pin" style={{marginRight:6,color:'#4A6079'}}></i>ที่ตั้ง <span style={{color:'#D8567A'}}>*</span>
              <Hint><i className="ph ph-lightbulb" style={{marginRight:4}}></i>พิมพ์ชื่อจังหวัด เขต หรือตำบล ระบบจะแสดงตัวเลือกให้เลือก — เลือกแล้วจะกรอกให้อัตโนมัติ</Hint>
            </h3>
            {errFields.province && <div style={{background:'#FDECF1',border:'1px solid #F3C5D4',borderRadius:10,padding:'8px 14px',marginBottom:12,fontSize:13,color:'#B23A5E'}}><i className="ph ph-warning" style={{marginRight:5}}></i>{errFields.province}</div>}
            {/* ซ้าย: ที่อยู่ทั้งหมด · ขวา: แผนที่ปักหมุด (เล็ก) */}
            <div style={{display:'grid',gridTemplateColumns:'1.15fr 1fr',gap:20,alignItems:'start'}}>
              <ThaiAddressForm form={form} set={set} />
              <div>
                <div style={{fontSize:13,fontWeight:600,color:'#102E54',marginBottom:6,display:'flex',alignItems:'center',gap:6}}>
                  <i className="ph ph-map-trifold" style={{marginRight:6,color:'#4A6079'}}></i>ตำแหน่งบนแผนที่
                  <Hint><i className="ph ph-map-pin" style={{marginRight:4}}></i>ค้นหาที่อยู่ หรือคลิกบนแผนที่เพื่อปักหมุด — เก็บพิกัดไปแสดงแผนที่หน้าเว็บ + ปุ่มนำทาง</Hint>
                </div>
                <window.MapPicker lat={form.lat} lng={form.lng} height={230}
                  address={[form.address, form.subdistrict, form.district, form.province].filter(Boolean).join(' ')}
                  onChange={(la, ln) => { set('lat', la); set('lng', ln); }} />
              </div>
            </div>
            <ZoningField form={form} set={set} />

            {hasAmenities && (<>
              <h3 style={{margin:'14px 0 8px',color:'#102E54'}}><i className="ph ph-list-checks" style={{marginRight:6,color:'#4A6079'}}></i>สิ่งอำนวยความสะดวก</h3>
              <div style={{display:'flex',flexWrap:'wrap',gap:8,marginBottom:12}}>
                {(form.amenities||[]).map((a,i) => (
                  <span key={i} style={{background:'#DDE9FC',color:'#1F58C4',padding:'5px 12px',borderRadius:99,fontSize:13,fontWeight:500,display:'flex',alignItems:'center',gap:6}}>
                    {a} <button style={{background:'none',border:'none',cursor:'pointer',color:'#1F58C4',fontWeight:700}} onClick={() => set('amenities',(form.amenities||[]).filter((_,j)=>j!==i))}>✕</button>
                  </span>
                ))}
              </div>
              <div className="row gap-8">
                <input className="input" style={{flex:1}} value={amenityInput} onChange={e => setAmenityInput(e.target.value)} placeholder="พิมพ์แล้วกด Enter" onKeyDown={e => { if(e.key==='Enter'&&amenityInput.trim()){set('amenities',[...(form.amenities||[]),amenityInput.trim()]);setAmenityInput('');}}} />
                <button className="btn btn-sm btn-primary" onClick={() => {if(amenityInput.trim()){set('amenities',[...(form.amenities||[]),amenityInput.trim()]);setAmenityInput('');}}}>+ เพิ่ม</button>
              </div>
              <div style={{display:'flex',flexWrap:'wrap',gap:6,marginTop:8}}>
                {['การตกแต่ง','เครื่องปรับอากาศ','เครื่องทำน้ำอุ่น','เครื่องดูดควันจากเตา','ที่จอดรถ','ระเบียง','เฟอร์นิเจอร์ Built-in','ห้องครัวแยก'].filter(s => !(form.amenities||[]).includes(s)).map(s => (
                  <button key={s} className="btn btn-sm btn-ghost" style={{fontSize:12}} onClick={() => {if(!(form.amenities||[]).includes(s))set('amenities',[...(form.amenities||[]),s]);}}>{s}</button>
                ))}
              </div>

              <h3 style={{margin:'14px 0 8px',color:'#102E54'}}><i className="ph ph-buildings" style={{marginRight:6,color:'#4A6079'}}></i>สิ่งอำนวยความสะดวกโดยรอบ</h3>
              <div style={{display:'flex',flexWrap:'wrap',gap:8,marginBottom:12}}>
                {(form.facilities||[]).map((f,i) => (
                  <span key={i} style={{background:'#DBF1E7',color:'#15976A',padding:'5px 12px',borderRadius:99,fontSize:13,fontWeight:500,display:'flex',alignItems:'center',gap:6}}>
                    {f} <button style={{background:'none',border:'none',cursor:'pointer',color:'#15976A',fontWeight:700}} onClick={() => set('facilities',(form.facilities||[]).filter((_,j)=>j!==i))}>✕</button>
                  </span>
                ))}
              </div>
              <div className="row gap-8">
                <input className="input" style={{flex:1}} value={facilityInput} onChange={e => setFacilityInput(e.target.value)} placeholder="พิมพ์แล้วกด Enter" onKeyDown={e => {if(e.key==='Enter'&&facilityInput.trim()){set('facilities',[...(form.facilities||[]),facilityInput.trim()]);setFacilityInput('');}}} />
                <button className="btn btn-sm btn-primary" onClick={() => {if(facilityInput.trim()){set('facilities',[...(form.facilities||[]),facilityInput.trim()]);setFacilityInput('');}}}>+ เพิ่ม</button>
              </div>
              <div style={{display:'flex',flexWrap:'wrap',gap:6,marginTop:8}}>
                {['สระว่ายน้ำ','ฟิตเนส','กล้องวงจรปิด','รปภ. 24 ชม.','ลานจอดรถใต้ดิน','สวนส่วนกลาง','ร้านสะดวกซื้อ','ร้านอาหาร','ซาวน่า','ใกล้ BTS','ใกล้ MRT','ใกล้ห้างสรรพสินค้า','ใกล้สนามบิน','ใกล้ทางด่วน','ใกล้โรงพยาบาล','ใกล้โรงเรียน/มหาวิทยาลัย'].filter(s => !(form.facilities||[]).includes(s)).map(s => (
                  <button key={s} className="btn btn-sm btn-ghost" style={{fontSize:12}} onClick={() => {if(!(form.facilities||[]).includes(s))set('facilities',[...(form.facilities||[]),s]);}}>{s}</button>
                ))}
              </div>
            </>)}

            </>}

            {/* ปุ่มเดินขั้น + บันทึก — เฉพาะ wizard ตอนสร้างทรัพย์ใหม่ (ทรัพย์ที่บันทึกแล้วใช้ปุ่มบันทึกด้านบน + สลับแท็บ) */}
            {isNew && (
            <div className="row between" style={{marginTop:24,alignItems:'center'}}>
              <button className="btn btn-ghost" onClick={() => { if(wstep<=1) setStep(0); else setWstep(s => s-1); }}>← ย้อนกลับ</button>
              {wstep===1
                ? <button className="btn btn-primary" onClick={goNext}>ถัดไป →</button>
                : <button className="btn btn-primary" onClick={saveGuarded} disabled={saving}>{saving ? 'กำลังบันทึก...' : <><i className="ph ph-floppy-disk" style={{marginRight:5}}></i>บันทึก</>}</button>}
            </div>
            )}
          </div>
        )}

        {/* ============ TAB: รูปภาพ — ยังไม่บันทึก ============ */}
        {showStep2 && !savedId && (
          <div className="card" style={{padding:'40px 24px',textAlign:'center'}}>
            <div style={{fontSize:48,marginBottom:12}}><i className="ph ph-floppy-disk" style={{fontSize:44,color:'#C5CCD6'}}></i></div>
            <h3 style={{marginBottom:8,color:'#102E54'}}>บันทึกข้อมูลทรัพย์ก่อน</h3>
            <p style={{color:'#6B7280',marginBottom:20,lineHeight:1.6}}>รูปภาพต้องผูกกับทรัพย์ที่บันทึกแล้ว — กดบันทึกข้อมูลทรัพย์ก่อน แล้วแท็บรูปภาพจะใช้งานได้ทันที</p>
            <button className="btn btn-primary" onClick={saveGuarded} disabled={saving}>{saving ? 'กำลังบันทึก...' : <><i className="ph ph-floppy-disk" style={{marginRight:5}}></i>บันทึกข้อมูลทรัพย์</>}</button>
          </div>
        )}

        {/* ============ TAB: รูปภาพ ============ */}
        {showStep2 && savedId && (
          <div style={{maxWidth:1040, margin:'0 auto'}}>
            {/* รูปหน้าปก (ซ้าย) + คำแนะนำ/อัปโหลด (ขวา) อยู่ในการ์ดเดียว ประหยัดพื้นที่ */}
            <div className="card" style={{padding:20,marginBottom:20}}>
              <div style={{display:'grid',gridTemplateColumns:'minmax(0,1.15fr) 1fr',gap:20,alignItems:'start'}}>
                <div>
                  <h3 style={{marginBottom:12}}><i className="ph ph-image" style={{marginRight:6,color:'#4A6079'}}></i>รูปหน้าปก</h3>
                  {form.cover_image_url
                    ? <img src={form.cover_image_url} style={{width:'100%',maxHeight:300,borderRadius:12,border:'2px solid #2A6BE0',objectFit:'cover'}} alt="" />
                    : <div style={{padding:40,textAlign:'center',color:'#9CA3AF',background:'#F3F4F6',borderRadius:12}}>ยังไม่มีรูปหน้าปก — อัปโหลดด้านขวา</div>}
                </div>
                <div>
                  <h3 style={{marginBottom:8}}><i className="ph ph-upload-simple" style={{marginRight:6,color:'#4A6079'}}></i>อัปโหลดรูปภาพ</h3>
                  <div style={{background:'#F0F7FF',border:'1px solid #DDE9FC',borderRadius:10,padding:14,marginBottom:14}}>
                    <b style={{color:'#1F58C4',fontSize:13.5}}><i className="ph ph-ruler" style={{marginRight:5}}></i>คำแนะนำขนาดรูป</b>
                    <div style={{display:'flex',flexDirection:'column',gap:5,marginTop:8,fontSize:12.5,color:'#3B5998'}}>
                      <div>• <b>รูปหน้าปก:</b> 1200 x 800 px (3:2)</div>
                      <div>• <b>รูปทั่วไป:</b> 1200 x 900 px (4:3)</div>
                      <div>• <b>ขนาดไฟล์:</b> ไม่เกิน 5 MB</div>
                      <div>• <b>รูปแบบ:</b> JPG, PNG, WebP</div>
                    </div>
                    <p style={{fontSize:11.5,color:'#6B7280',marginTop:8}}><i className="ph ph-lightbulb" style={{marginRight:4}}></i>ใช้รูปแนวนอน — รูปแรกเป็นหน้าปกอัตโนมัติ</p>
                  </div>
                  <input ref={fileRef} type="file" accept="image/jpeg,image/png,image/webp" multiple onChange={uploadFiles} disabled={uploading} style={{fontSize:13.5,width:'100%'}} />
                  {uploadProgress && <div style={{marginTop:8,fontWeight:600,color:uploading?'#2A6BE0':(uploadProgress.includes('✅')?'#15976A':'#D8567A')}}>{uploadProgress}</div>}
                </div>
              </div>
            </div>

            {/* Gallery */}
            <div className="card" style={{padding:20}}>
              <h3 style={{marginBottom:16}}><i className="ph ph-images" style={{marginRight:6,color:'#4A6079'}}></i>รูปภาพทั้งหมด ({images.length} รูป)</h3>
              {images.length === 0
                ? <div style={{padding:40,textAlign:'center',color:'#9CA3AF'}}><div style={{fontSize:48,marginBottom:12}}><i className="ph ph-image"></i></div><p>ยังไม่มีรูปภาพ</p></div>
                : <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:14}}>
                    {images.map((img,idx) => (
                      <div key={img.id} style={{position:'relative',borderRadius:12,overflow:'hidden',border:'3px solid '+(form.cover_image_url===img.url?'#2A6BE0':'#E5E7EB')}}>
                        <img src={img.url} onClick={() => setLightbox(idx)} title="คลิกเพื่อดูรูปใหญ่" style={{width:'100%',height:140,objectFit:'cover',display:'block',cursor:'zoom-in'}} alt="" />
                        {form.cover_image_url===img.url && <span style={{position:'absolute',top:8,left:8,background:'#2A6BE0',color:'#fff',fontSize:11,fontWeight:700,padding:'3px 10px',borderRadius:99}}><i className="ph ph-star" style={{marginRight:3}}></i>หน้าปก</span>}
                        <span style={{position:'absolute',top:8,right:8,background:'rgba(0,0,0,.5)',color:'#fff',fontSize:11,padding:'2px 8px',borderRadius:99}}>#{idx+1}</span>
                        <div style={{display:'flex',gap:4,padding:8}}>
                          {form.cover_image_url!==img.url && <button className="btn btn-sm btn-ghost" style={{flex:1,fontSize:11}} onClick={() => setCover(img.url)}>ตั้งเป็นหน้าปก</button>}
                          <window.DeleteBtn onClick={() => deleteImage(img.id)} />
                        </div>
                      </div>
                    ))}
                  </div>}
            </div>
          </div>
        )}
        </>)}

        {/* Lightbox: กรอบดำเล็กลง (กล่องกลางจอ) + ปุ่มปิดชัด + เลื่อนซ้าย/ขวา (วนลูป) + Esc ปิด */}
        {lightbox !== null && images[lightbox] && (
          <div onClick={() => setLightbox(null)} style={{position:'fixed',inset:0,background:'rgba(0,0,0,.6)',zIndex:99999,display:'flex',alignItems:'center',justifyContent:'center',padding:'3vh 3vw'}}>
            <div onClick={e => e.stopPropagation()} style={{position:'relative',background:'#141414',borderRadius:16,padding:14,maxWidth:'min(900px,94vw)',maxHeight:'92vh',display:'flex',flexDirection:'column',boxShadow:'0 24px 70px rgba(0,0,0,.55)'}}>
              <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',color:'#fff',marginBottom:10}}>
                <span style={{fontSize:14,fontWeight:600}}><i className="ph ph-image" style={{marginRight:6}}></i>{lightbox+1} / {images.length}</span>
                <button onClick={() => setLightbox(null)} title="ปิด (Esc)" style={{background:'rgba(255,255,255,.16)',border:'none',color:'#fff',width:34,height:34,borderRadius:'50%',cursor:'pointer',fontSize:16,display:'inline-flex',alignItems:'center',justifyContent:'center'}}><i className="ph ph-x"></i></button>
              </div>
              <div style={{position:'relative',display:'flex',alignItems:'center',justifyContent:'center',minHeight:0,flex:1}}>
                {images.length > 1 && <button onClick={() => setLightbox((lightbox - 1 + images.length) % images.length)} title="ก่อนหน้า" style={{position:'absolute',left:6,width:44,height:44,borderRadius:'50%',background:'rgba(0,0,0,.55)',border:'1px solid rgba(255,255,255,.3)',color:'#fff',cursor:'pointer',fontSize:26,lineHeight:1}}>‹</button>}
                <img src={images[lightbox].url} style={{maxWidth:'100%',maxHeight:'78vh',objectFit:'contain',borderRadius:8,display:'block'}} alt="" />
                {images.length > 1 && <button onClick={() => setLightbox((lightbox + 1) % images.length)} title="ถัดไป" style={{position:'absolute',right:6,width:44,height:44,borderRadius:'50%',background:'rgba(0,0,0,.55)',border:'1px solid rgba(255,255,255,.3)',color:'#fff',cursor:'pointer',fontSize:26,lineHeight:1}}>›</button>}
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }

  window.ListingsPage = ListingsPage;

  // ===== Hub: ยุบ "จัดการทรัพย์" + "ทรัพย์ที่ส่งเข้ามา" เป็นเมนูเดียว มีแท็บบนสุด =====
})();
