// ─────────────────────────────────────────────
// DEV_Design_Inspector.jsx  (V7)
// Inspector panel for selected objects, cols, pages
// Depends on: SDS styles (DEV_Design.jsx)
//             DOConfirm, ValueListPicker (V6 DEV_Design.jsx)
//
// V7 change: no Apply button — every change auto-
// commits to parent state via onUpdate immediately.
// Only Save button writes to DB.
// ─────────────────────────────────────────────

function Inspector({ selectedObj, fields, tables, app, onBrowseIcon, onUpdate, onDelete }) {
  const [local,         setLocal]         = React.useState(null);
  const [confirm,       setConfirm]       = React.useState(null);
  const [labelMenuOpen, setLabelMenuOpen] = React.useState(false);

  React.useEffect(() => {
    setLocal(selectedObj ? JSON.parse(JSON.stringify(selectedObj)) : null);
    setLabelMenuOpen(false);
  }, [selectedObj?.id, selectedObj?._isCol, selectedObj?._isRow, selectedObj?._isPage]);

  // Close label menu on outside click — must be before early return
  React.useEffect(() => {
    if (!labelMenuOpen) return;
    const handler = () => setLabelMenuOpen(false);
    setTimeout(() => document.addEventListener('click', handler), 0);
    return () => document.removeEventListener('click', handler);
  }, [labelMenuOpen]);

  if (!local) return (
    <div style={{ padding:14 }}>
      <div style={SDS.inspHeader}>Inspector</div>
      <div style={{ fontSize:12, color:'#94a3b8', textAlign:'center', marginTop:40 }}>
        Click an object<br/>to edit
      </div>
    </div>
  );

  // Auto-commit every change to parent — no Apply needed
  function set(key, val) {
    setLocal(prev => {
      const updated = { ...prev, [key]: val };
      onUpdate && onUpdate(updated);
      return updated;
    });
  }

  // Multi-key atomic update — for field selector (sets field_code + field_id + label at once)
  function setMany(pairs) {
    setLocal(prev => {
      const updated = { ...prev, ...pairs };
      onUpdate && onUpdate(updated);
      return updated;
    });
  }

  // ── Helpers ──
  const lbl = (txt) => (
    <div style={SDS.inspLabel}>{txt}</div>
  );

  const inp = (key, props={}) => (
    <input {...props} value={local[key] ?? ''} onChange={e => set(key, e.target.value)}
      style={SDS.inspInput} />
  );

  const sel = (key, options) => (
    <select value={local[key] ?? ''} onChange={e => set(key, e.target.value)}
      style={{ ...SDS.inspInput, background:'#fff' }}>
      {options.map(o => <option key={o.v} value={o.v}>{o.l}</option>)}
    </select>
  );

  const toggle = (key, opts) => (
    <div style={{ display:'flex', border:'1.5px solid #e2e8f0', borderRadius:6,
                  overflow:'hidden', marginBottom:8 }}>
      {opts.map(o => (
        <button key={String(o.v)} type="button" onClick={() => set(key, o.v)}
          style={{ flex:1, padding:'5px 4px', border:'none', fontSize:11, fontWeight:500,
                   cursor:'pointer', fontFamily:'DM Sans, sans-serif',
                   background: local[key]===o.v ? '#0f1923' : '#fff',
                   color:      local[key]===o.v ? '#fff'    : '#64748b' }}>
          {o.l}
        </button>
      ))}
    </div>
  );

  const deleteBtn = (msg) => (
    <button onClick={() => setConfirm({ msg, onOk: () => onDelete && onDelete(local) })}
      style={{ ...SDS.inspDelete, marginTop:16 }}>Delete</button>
  );

  const activeFields = (fields || []).filter(f => f.status !== 0);

  // V7 field selector — atomic update of field_code + field_id + label
  const fieldSelector = (autoLabel=true) => (
    <select
      value={local.field_code || ''}
      onChange={e => {
        const f = activeFields.find(f => f.code === e.target.value);
        setMany({
          field_code: e.target.value,
          field_id:   f?.id || '',
          ...(autoLabel && f && !local.label ? { label: f.name } : {}),
        });
      }}
      style={{ ...SDS.inspInput, background:'#fff' }}>
      <option value="">— unassigned —</option>
      {activeFields.map(f => (
        <option key={f.id} value={f.code}>{f.name} ({f.code})</option>
      ))}
    </select>
  );

  // Label position options
  const LABEL_POS = [
    { v:'topleft',     l:'Top Left',      group:1 },
    { v:'topcenter',   l:'Top Center',    group:1 },
    { v:'topright',    l:'Top Right',     group:1 },
    { v:'leftinline',  l:'Left (inline)', group:2 },
    { v:'rightinline', l:'Right (inline)',group:2 },
    { v:'bottomleft',  l:'Bottom Left',   group:3 },
    { v:'bottomcenter',l:'Bottom Center', group:3 },
    { v:'bottomright', l:'Bottom Right',  group:3 },
  ];

  const labelPosLabel = (v) => LABEL_POS.find(p => p.v === v)?.l || 'Top Left';

  const labelSection = () => {
    const currentPos = local.labelpos || 'topleft';
    let prevGroup = null;
    return (
      <div style={{ marginBottom:4 }}>
        {/* Header row: "Label — Top Left" + menu icon */}
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between',
                      marginBottom:4, marginTop:10 }}>
          <div style={{ fontSize:10, fontWeight:700, color:'#94a3b8',
                        textTransform:'uppercase', letterSpacing:'0.06em' }}>
            Label
            <span style={{ fontWeight:400, color:'#cbd5e1', marginLeft:4 }}>
              – {labelPosLabel(currentPos)}
            </span>
          </div>
          {/* Position menu trigger */}
          <div style={{ position:'relative' }}>
            <button onClick={() => setLabelMenuOpen(o => !o)}
              title="Label Position"
              style={{ background:'none', border:'1.5px solid #e2e8f0', borderRadius:5,
                       cursor:'pointer', padding:'2px 6px', fontSize:14, color:'#64748b',
                       lineHeight:1, fontFamily:'inherit' }}>
              ⋮
            </button>
            {labelMenuOpen && (
              <div style={{ position:'absolute', right:0, top:'100%', marginTop:2,
                            background:'#fff', border:'1.5px solid #e2e8f0',
                            borderRadius:8, boxShadow:'0 4px 16px rgba(0,0,0,0.12)',
                            zIndex:300, minWidth:150, overflow:'hidden' }}>
                {LABEL_POS.map(opt => {
                  const showDivider = prevGroup !== null && opt.group !== prevGroup;
                  prevGroup = opt.group;
                  return (
                    <React.Fragment key={opt.v}>
                      {showDivider && (
                        <div style={{ height:1, background:'#f1f5f9', margin:'2px 0' }} />
                      )}
                      <button
                        onClick={() => {
                          set('labelpos', opt.v);
                          setLabelMenuOpen(false);
                        }}
                        style={{ display:'block', width:'100%', textAlign:'left',
                                 padding:'7px 14px', border:'none', background:
                                 currentPos === opt.v ? '#eff6ff' : '#fff',
                                 color: currentPos === opt.v ? '#2563eb' : '#374151',
                                 fontSize:12, cursor:'pointer',
                                 fontWeight: currentPos === opt.v ? 600 : 400,
                                 fontFamily:'DM Sans, sans-serif' }}>
                        {currentPos === opt.v && '✓ '}{opt.l}
                      </button>
                    </React.Fragment>
                  );
                })}
                <div style={{ height:1, background:'#f1f5f9', margin:'2px 0' }} />
                <button
                  onClick={() => { set('labelpos', 'none'); setLabelMenuOpen(false); }}
                  style={{ display:'block', width:'100%', textAlign:'left',
                           padding:'7px 14px', border:'none',
                           background: currentPos==='none' ? '#fef2f2' : '#fff',
                           color: currentPos==='none' ? '#dc2626' : '#94a3b8',
                           fontSize:12, cursor:'pointer',
                           fontFamily:'DM Sans, sans-serif' }}>
                  {currentPos==='none' && '✓ '}Hidden
                </button>
              </div>
            )}
          </div>
        </div>

        {/* Label text input */}
        {inp('label', { placeholder:'Display label' })}
      </div>
    );
  };

  // ── Page inspector ──
  if (local._isPage) {
    return (
      <div style={SDS.inspPanel}>
        {confirm && <DOConfirm {...confirm} onClose={() => setConfirm(null)} />}
        <div style={SDS.inspHeaderRow}>
          <div style={{ display:'flex', alignItems:'center', gap:4 }}>
            <span style={SDS.inspHeader}>Inspector</span>
            {typeof KBIcon !== 'undefined' && <KBIcon kbKey="design.page" />}
          </div>
          <span style={SDS.inspBadge('#7c3aed')}>Page</span>
        </div>
        {lbl('Page Name')}
        {inp('name', { placeholder:'Info, Photos…' })}
        {deleteBtn('Delete this page and all its rows and objects?')}
      </div>
    );
  }

  // ── Col inspector ──
  if (local._isCol) {
    const sibTotal = Number(local._siblingTotal || 0);
    const maxW     = 12 - sibTotal;
    return (
      <div style={SDS.inspPanel}>
        {confirm && <DOConfirm {...confirm} onClose={() => setConfirm(null)} />}
        <div style={SDS.inspHeaderRow}>
          <div style={{ display:'flex', alignItems:'center', gap:4 }}>
            <span style={SDS.inspHeader}>Inspector</span>
            {typeof KBIcon !== 'undefined' && <KBIcon kbKey="design.column" />}
          </div>
          <span style={SDS.inspBadge('#64748b')}>Column</span>
        </div>
        {lbl('Width (1–12)')}
        <div style={{ display:'flex', gap:4, flexWrap:'wrap', marginBottom:6 }}>
          {[1,2,3,4,5,6,7,8,9,10,11,12].map(n => {
            const active   = Number(local.width) === n;
            const disabled = n > maxW;
            return (
              <button key={n} onClick={() => !disabled && set('width', n)}
                style={{ width:26, height:26,
                         border:      active   ? '2px solid #2563eb'  : '1.5px solid #e2e8f0',
                         borderRadius: 5,
                         background:  active   ? '#eff6ff'            :
                                      disabled ? '#f8fafc'            : '#fff',
                         fontSize:12, fontWeight:600,
                         color:       active   ? '#2563eb'            :
                                      disabled ? '#cbd5e1'            : '#374151',
                         cursor:      disabled ? 'not-allowed'        : 'pointer',
                         fontFamily: 'DM Sans, sans-serif' }}>
                {n}
              </button>
            );
          })}
        </div>
        <div style={{ fontSize:11, color:'#94a3b8', marginBottom:14 }}>
          {local.width}/12 · max available: {maxW}/12
        </div>
        {deleteBtn('Delete this column and all its objects?')}
      </div>
    );
  }

  // ── Object inspector ──
  const oi = objIcon(local.type);

  return (
    <div style={SDS.inspPanel}>
      {confirm && <DOConfirm {...confirm} onClose={() => setConfirm(null)} />}
      <div style={SDS.inspHeaderRow}>
        <div style={{ display:'flex', alignItems:'center', gap:4 }}>
          <span style={SDS.inspHeader}>Inspector</span>
          {typeof KBIcon !== 'undefined' &&
            <KBIcon kbKey={`design.object.${local.type}`} />}
        </div>
        <span style={{ ...SDS.inspBadge(oi.color) }}>{local.type}</span>
      </div>

      {local.type === 'input' && <>
        {lbl('Field')}{fieldSelector()}
        {labelSection()}
        {lbl('Height (rows)')}
        {inp('height', { type:'number', placeholder:'1', min:'1', max:'20' })}
      </>}

      {local.type === 'number' && (() => {
        const fmt = local.format || '';
        return (<>
          {lbl('Field')}{fieldSelector()}
          {labelSection()}

          {lbl('Format')}
          <select value={fmt} onChange={e => set('format', e.target.value)}
            style={{ ...SDS.inspInput, background:'#fff' }}>
            <option value="">Plain</option>
            <option value="currency">Currency</option>
            <option value="percent">Percent (%)</option>
            <option value="decimal">Decimal</option>
          </select>

          {/* Currency symbol */}
          {fmt === 'currency' && <>
            {lbl('Currency Symbol')}
            <select value={local.currencySymbol || '$'}
              onChange={e => set('currencySymbol', e.target.value)}
              style={{ ...SDS.inspInput, background:'#fff' }}>
              <option value="$">$ Dollar</option>
              <option value="€">€ Euro</option>
              <option value="£">£ Pound</option>
              <option value="¥">¥ Yen</option>
              <option value="₿">₿ Bitcoin</option>
            </select>
          </>}

          {/* Decimal places — currency and decimal */}
          {(fmt === 'currency' || fmt === 'decimal') && <>
            {lbl('Decimal Places')}
            <select value={local.decimals ?? 0}
              onChange={e => set('decimals', Number(e.target.value))}
              style={{ ...SDS.inspInput, background:'#fff' }}>
              <option value={0}>0 — Integer</option>
              <option value={1}>1 — .0</option>
              <option value={2}>2 — .00</option>
              <option value={3}>3 — .000</option>
              <option value={4}>4 — .0000</option>
              <option value={5}>5 — .00000</option>
              <option value={6}>6 — .000000</option>
            </select>
          </>}

          {/* Min / Max — all formats */}
          <div style={{ display:'flex', gap:8, marginBottom:8 }}>
            <div style={{ flex:1 }}>
              {lbl('Min')}
              <input type="number" value={local.min ?? ''}
                onChange={e => set('min', e.target.value)}
                placeholder="e.g. 0"
                style={{ ...SDS.inspInput, marginBottom:0 }} />
            </div>
            <div style={{ flex:1 }}>
              {lbl('Max')}
              <input type="number" value={local.max ?? ''}
                onChange={e => set('max', e.target.value)}
                placeholder="e.g. 150"
                style={{ ...SDS.inspInput, marginBottom:0 }} />
            </div>
          </div>

          {lbl('Align')}
          {toggle('align', [
            { v:'left',  l:'← Left'  },
            { v:'right', l:'→ Right' },
          ])}
        </>);
      })()}

      {local.type === 'date' && <>
        {lbl('Field')}{fieldSelector()}
        {labelSection()}

        {lbl('Display Format')}
        <select value={local.dateformat || 'MM/DD/YYYY'}
          onChange={e => set('dateformat', e.target.value)}
          style={{ ...SDS.inspInput, background:'#fff' }}>
          <option value="MM/DD/YYYY">MM/DD/YYYY — 01/15/2025</option>
          <option value="DD/MM/YYYY">DD/MM/YYYY — 15/01/2025</option>
          <option value="YYYY-MM-DD">YYYY-MM-DD — 2025-01-15</option>
          <option value="MMM D YYYY">MMM D YYYY — Jan 15 2025</option>
          <option value="MMMM D YYYY">MMMM D YYYY — January 15 2025</option>
          <option value="D MMM YYYY">D MMM YYYY — 15 Jan 2025</option>
        </select>
        <div style={{ fontSize:10, color:'#94a3b8', marginBottom:8, lineHeight:1.4 }}>
          Format applies to display. Date picker stores YYYY-MM-DD.
        </div>

        {lbl('Align')}
        {toggle('align', [
          { v:'left',  l:'← Left'  },
          { v:'right', l:'→ Right' },
        ])}
      </>}

      {local.type === 'time' && <>
        {lbl('Field')}{fieldSelector()}
        {labelSection()}

        {lbl('Display Format')}
        <select value={local.timeformat || '12'}
          onChange={e => set('timeformat', e.target.value)}
          style={{ ...SDS.inspInput, background:'#fff' }}>
          <option value="12">12 hour — 2:30 PM</option>
          <option value="24">24 hour — 14:30</option>
        </select>
        <div style={{ fontSize:10, color:'#94a3b8', marginBottom:8, lineHeight:1.4 }}>
          Always stored as 24hr (14:30). Format controls display only.
        </div>

        {lbl('Align')}
        {toggle('align', [
          { v:'left',  l:'← Left'  },
          { v:'right', l:'→ Right' },
        ])}
      </>}

      {local.type === 'select' && <>
        {lbl('Field')}{fieldSelector()}
        {labelSection()}

        {lbl('Options Source')}
        <select value={local.options_source || 'valuelist'}
          onChange={e => set('options_source', e.target.value)}
          style={{ ...SDS.inspInput, background:'#fff' }}>
          <option value="valuelist">Value List</option>
          <option value="inline">Inline (type options below)</option>
          <option value="table" disabled>Table (coming soon)</option>
          <option value="query" disabled>Query (coming soon)</option>
        </select>

        {/* Value List source */}
        {(local.options_source || 'valuelist') === 'valuelist' && <>
          {lbl('Value List')}
          {typeof ValueListPicker !== 'undefined'
            ? <ValueListPicker appId={app?.app_id} value={local.valuelist_name || ''}
                onChange={v => set('valuelist_name', v)} />
            : inp('valuelist_name', { placeholder:'Value list name' })
          }
        </>}

        {/* Inline source */}
        {local.options_source === 'inline' && <>
          {lbl('Options — one per line')}
          <textarea
            value={local.inline_options || ''}
            onChange={e => setLocal(prev => ({ ...prev, inline_options: e.target.value }))}
            onBlur={e => set('inline_options', e.target.value)}
            placeholder={'Male | M\nFemale | F\nOther'}
            style={{ ...SDS.inspInput, height:100, resize:'vertical',
                     fontFamily:'DM Mono, monospace', fontSize:11, marginBottom:2 }} />
          <div style={{ fontSize:10, color:'#94a3b8', marginBottom:8, lineHeight:1.4 }}>
            Plain text or <strong>Label | Value</strong> pairs
          </div>
        </>}
        {toggle('multi', [
          { v:false, l:'Single Select' },
          { v:true,  l:'Multi Select'  },
        ])}
      </>}

      {local.type === 'checkbox' && <>
        {lbl('Field')}{fieldSelector()}
        {labelSection()}

        {lbl('Allow Multiple')}
        {toggle('multi', [
          { v:false, l:'Single Select' },
          { v:true,  l:'Multi Select'  },
        ])}

        {lbl('Display')}
        {toggle('display', [
          { v:'horizontal', l:'Horizontal' },
          { v:'vertical',   l:'Vertical'   },
        ])}

        {lbl('Options Source')}
        <select value={local.options_source || 'valuelist'}
          onChange={e => set('options_source', e.target.value)}
          style={{ ...SDS.inspInput, background:'#fff' }}>
          <option value="valuelist">Value List</option>
          <option value="inline">Inline (type options below)</option>
          <option value="table" disabled>Table (coming soon)</option>
          <option value="query" disabled>Query (coming soon)</option>
        </select>

        {(local.options_source || 'valuelist') === 'valuelist' && <>
          {lbl('Value List')}
          {typeof ValueListPicker !== 'undefined'
            ? <ValueListPicker appId={app?.app_id} value={local.valuelist_name || ''}
                onChange={v => set('valuelist_name', v)} />
            : inp('valuelist_name', { placeholder:'Value list name' })
          }
        </>}

        {local.options_source === 'inline' && <>
          {lbl('Options — one per line')}
          <textarea
            value={local.inline_options || ''}
            onChange={e => setLocal(prev => ({ ...prev, inline_options: e.target.value }))}
            onBlur={e => set('inline_options', e.target.value)}
            placeholder={'Option 1\nOption 2\nLabel | Value'}
            style={{ ...SDS.inspInput, height:100, resize:'vertical',
                     fontFamily:'DM Mono, monospace', fontSize:11, marginBottom:2 }} />
          <div style={{ fontSize:10, color:'#94a3b8', marginBottom:8, lineHeight:1.4 }}>
            Plain text or <strong>Label | Value</strong> pairs
          </div>
        </>}
      </>}

      {local.type === 'file' && (() => {
        const display = local.display || 'gallery';
        return (<>
          {lbl('Field')}{fieldSelector()}
          {labelSection()}

          {lbl('Display As')}
          <select value={display} onChange={e => set('display', e.target.value)}
            style={{ ...SDS.inspInput, background:'#fff' }}>
            <option value="image">Single Image</option>
            <option value="gallery">Gallery</option>
            <option value="file">File List</option>
          </select>

          {/* ── Single Image options ── */}
          {display === 'image' && <>
            {lbl('Width')}
            <select value={local.width || '100%'} onChange={e => set('width', e.target.value)}
              style={{ ...SDS.inspInput, background:'#fff' }}>
              <option value="100%">100% of column (default)</option>
              <option value="75%">75%</option>
              <option value="50%">50%</option>
              <option value="25%">25%</option>
            </select>
            {lbl('Max Height (px)')}
            <select value={local.maxHeight || '300'} onChange={e => set('maxHeight', e.target.value)}
              style={{ ...SDS.inspInput, background:'#fff' }}>
              {[100,150,200,300,400,500,600,800].map(n => (
                <option key={n} value={String(n)}>{n}px</option>
              ))}
            </select>
            {lbl('Align')}
            {toggle('align', [
              { v:'left',   l:'←' },
              { v:'center', l:'↔' },
              { v:'right',  l:'→' },
            ])}
          </>}

          {/* ── Gallery options ── */}
          {display === 'gallery' && <>
            {lbl('Thumb Size (px)')}
            <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:8 }}>
              <input type="range" min="40" max="300" step="10"
                value={local.thumbSize || 100}
                onChange={e => set('thumbSize', Number(e.target.value))}
                style={{ flex:1 }} />
              <span style={{ fontSize:12, color:'#374151', minWidth:40, textAlign:'right',
                             fontFamily:'DM Mono, monospace' }}>
                {local.thumbSize || 100}px
              </span>
            </div>
            {lbl('Display Rows')}
            <select value={local.displayRows || 'all'} onChange={e => set('displayRows', e.target.value)}
              style={{ ...SDS.inspInput, background:'#fff' }}>
              <option value="all">Show all</option>
              {[1,2,3,4,5].map(n => (
                <option key={n} value={n}>{n} row{n>1?'s':''}</option>
              ))}
            </select>
          </>}

          {/* ── File List options ── */}
          {display === 'file' && <>
            {lbl('Display Rows')}
            <select value={local.displayRows || 'all'} onChange={e => set('displayRows', e.target.value)}
              style={{ ...SDS.inspInput, background:'#fff' }}>
              <option value="all">Show all</option>
              {[5,10,15,20,25,50].map(n => (
                <option key={n} value={n}>{n} rows</option>
              ))}
            </select>
            {lbl('Show Columns')}
            {['File Size','Date Uploaded','Uploaded By'].map((col, i) => {
              const key = ['showSize','showDate','showUser'][i];
              return (
                <label key={col} style={{ display:'flex', alignItems:'center', gap:8,
                                          fontSize:12, color:'#374151', cursor:'pointer',
                                          marginBottom:6, userSelect:'none' }}>
                  <input type="checkbox" checked={local[key] !== false}
                    onChange={e => set(key, e.target.checked)}
                    style={{ width:14, height:14, cursor:'pointer' }} />
                  {col}
                </label>
              );
            })}
          </>}

          {/* ── Permissions (all three modes) ── */}
          <div style={{ borderTop:'1px solid #f1f5f9', marginTop:8, paddingTop:10 }}>
            {lbl('Permissions')}
            {[
              ['allowCamera',   'Allow Camera'  ],
              ['allowUpload',   'Allow Upload'  ],
              ['allowDownload', 'Allow Download'],
              ['allowDelete',   'Allow Delete'  ],
            ].map(([key, label]) => (
              <label key={key} style={{ display:'flex', alignItems:'center', gap:8,
                                        fontSize:12, color:'#374151', cursor:'pointer',
                                        marginBottom:6, userSelect:'none' }}>
                <input type="checkbox" checked={local[key] !== false}
                  onChange={e => set(key, e.target.checked)}
                  style={{ width:14, height:14, cursor:'pointer' }} />
                {label}
              </label>
            ))}
          </div>
        </>);
      })()}

      {local.type === 'text' && <>
        {lbl('Content')}
        <textarea value={local.content || ''} onChange={e => set('content', e.target.value)}
          placeholder="Text, HTML, or {f1} field values"
          style={{ ...SDS.inspInput, height:72, resize:'vertical', marginBottom:2 }} />
        <div style={{ fontSize:10, color:'#94a3b8', marginBottom:8, lineHeight:1.4 }}>
          Use {'{f1}'}, {'{f2}'} etc. to insert field values
        </div>

        {lbl('Text Alignment')}
        <div style={{ display:'flex', border:'1.5px solid #e2e8f0', borderRadius:6,
                      overflow:'hidden', marginBottom:8 }}>
          {[['left','⬤ L'],['center','⬤ C'],['right','⬤ R']].map(([v,l]) => (
            <button key={v} type="button" onClick={() => set('textalign', v)}
              style={{ flex:1, padding:'5px 2px', border:'none', fontSize:11,
                       fontWeight:500, cursor:'pointer', fontFamily:'DM Sans, sans-serif',
                       background: (local.textalign||'left')===v ? '#0f1923' : '#fff',
                       color:      (local.textalign||'left')===v ? '#fff'    : '#64748b' }}>
              {v === 'left' ? '⫷' : v === 'center' ? '≡' : '⫸'}
            </button>
          ))}
        </div>

        {lbl('Font Size')}
        <select value={local.fontsize || ''}
          onChange={e => set('fontsize', e.target.value ? Number(e.target.value) : '')}
          style={{ ...SDS.inspInput, background:'#fff' }}>
          <option value="">— default —</option>
          {[10,11,12,13,14,16,18,20,24,28,32,36,48].map(n => (
            <option key={n} value={n}>{n}px</option>
          ))}
        </select>

        {lbl('Font Weight')}
        <div style={{ display:'flex', border:'1.5px solid #e2e8f0', borderRadius:6,
                      overflow:'hidden', marginBottom:8 }}>
          {[['normal','Normal'],[false,'Normal'],[true,'Bold']].slice(1).map(([v,l]) => (
            <button key={String(v)} type="button" onClick={() => set('bold', v)}
              style={{ flex:1, padding:'5px 4px', border:'none',
                       fontSize: v ? 13 : 12,
                       fontWeight: v ? 700 : 400,
                       cursor:'pointer', fontFamily:'DM Sans, sans-serif',
                       background: Boolean(local.bold)===Boolean(v) ? '#0f1923' : '#fff',
                       color:      Boolean(local.bold)===Boolean(v) ? '#fff'    : '#64748b' }}>
              {l}
            </button>
          ))}
        </div>

        {typeof ColorPicker !== 'undefined'
          ? <ColorPicker label="Font Color" value={local.color || ''}
              onChange={v => set('color', v)} />
          : <>{lbl('Font Color')}{inp('color', { placeholder:'#374151' })}</>
        }

        {typeof ColorPicker !== 'undefined'
          ? <ColorPicker label="Background" value={local.bg_color || ''}
              onChange={v => set('bg_color', v)} />
          : <>{lbl('Background')}{inp('bg_color', { placeholder:'transparent' })}</>
        }
        {local.bg_color && (
          <button onClick={() => { const u={...local}; delete u.bg_color; setLocal(u); onUpdate&&onUpdate(u); }}
            style={SDS.inspClearBtn}>✕ clear background</button>
        )}
      </>}

      {local.type === 'rating' && (() => {
        const scale    = parseInt(local.maxstars) || 5;
        const emoji    = local.emoji || '⭐';
        const preview  = parseInt(local.preview_val) || Math.ceil(scale / 2);

        return (<>
          {lbl('Field')}{fieldSelector()}
          {labelSection()}

          {lbl('Emoji')}
          <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:8 }}>
            <div style={{ width:44, height:44, border:'1.5px solid #e2e8f0', borderRadius:7,
                          display:'flex', alignItems:'center', justifyContent:'center',
                          fontSize:24, background:'#f8fafc' }}>
              {emoji}
            </div>
            <button type="button"
              onClick={() => onBrowseIcon && onBrowseIcon(v => set('emoji', v))}
              style={{ padding:'6px 10px', background:'#fff', border:'1.5px solid #e2e8f0',
                       borderRadius:6, fontSize:11, color:'#2563eb', cursor:'pointer',
                       fontFamily:'DM Sans, sans-serif', fontWeight:600 }}>
              🔍 Pick Emoji
            </button>
            {local.emoji && local.emoji !== '⭐' && (
              <button type="button" onClick={() => set('emoji', '⭐')}
                style={{ background:'none', border:'none', color:'#94a3b8',
                         cursor:'pointer', fontSize:12 }}>reset</button>
            )}
          </div>

          {lbl('Scale (Max Steps)')}
          <div style={{ display:'flex', flexWrap:'wrap', gap:5, marginBottom:8 }}>
            {[1,2,3,4,5,6,7,8,9,10].map(n => (
              <button key={n} type="button" onClick={() => set('maxstars', n)}
                style={{ width:32, height:32, borderRadius:6,
                         border: scale===n ? '2px solid #0f1923' : '1.5px solid #e2e8f0',
                         background: scale===n ? '#0f1923' : '#fff',
                         color:      scale===n ? '#fff'    : '#374151',
                         fontSize:13, fontWeight: scale===n ? 700 : 400,
                         cursor:'pointer', fontFamily:'DM Sans, sans-serif' }}>
                {n}
              </button>
            ))}
          </div>

          {lbl('Preview')}
          <div style={{ background:'#f8fafc', border:'1px solid #e2e8f0',
                        borderRadius:7, padding:'10px 12px', marginBottom:8 }}>
            <div style={{ display:'flex', gap:3, flexWrap:'wrap' }}>
              {Array.from({ length: scale }, (_, i) => (
                <span key={i} style={{
                  fontSize: scale > 7 ? 18 : 22,
                  opacity:  i < preview ? 1 : 0.2,
                  filter:   i < preview ? 'none' : 'grayscale(1)',
                  cursor:   'pointer',
                  transition:'opacity 0.1s',
                }} onClick={() => setLocal(p => ({ ...p, preview_val: i + 1 }))}>
                  {emoji}
                </span>
              ))}
            </div>
            <div style={{ fontSize:10, color:'#94a3b8', marginTop:4 }}>
              Click to preview · {preview}/{scale}
            </div>
          </div>
        </>);
      })()}

      {local.type === 'divider' && <>
        {lbl('Height (px)')}
        <input type="number" value={local.height ?? 1}
          onChange={e => set('height', Math.min(500, Math.max(0, Number(e.target.value))))}
          min="0" max="500" step="1"
          style={{ ...SDS.inspInput, width:80 }} />
        <div style={{ fontSize:10, color:'#94a3b8', marginBottom:8 }}>
          0–500px · 0 = space only, no line
        </div>

        {lbl('Color')}
        <div style={{ display:'flex', alignItems:'center', gap:6, marginBottom:4 }}>
          {typeof ColorPicker !== 'undefined'
            ? <ColorPicker label="" value={local.color || ''}
                onChange={v => set('color', v)} />
            : <input value={local.color || ''} onChange={e => set('color', e.target.value)}
                placeholder="" style={{ ...SDS.inspInput, marginBottom:0 }} />
          }
        </div>
        {/* Permanent clear button — always visible */}
        <button onClick={() => set('color', '')}
          style={{ ...SDS.inspClearBtn,
                   color: local.color ? '#dc2626' : '#94a3b8' }}>
          {local.color ? '✕ clear — space only' : '✕ no color (space only)'}
        </button>
      </>}

      {local.type === 'map' && <>
        {lbl('Address Mode')}
        {toggle('mapmode', [
          { v:'full',    l:'📍 Full'    },
          { v:'partial', l:'✂️ Partial' },
        ])}

        {/* Full mode — single address field */}
        {(local.mapmode || 'full') === 'full' && <>
          {lbl('Full Address Field')}
          {fieldSelector(false)}
        </>}

        {/* Partial mode — individual fields */}
        {local.mapmode === 'partial' && <>
          {lbl('Address Field')}
          <select value={local.field_address || ''}
            onChange={e => setMany({ field_address: e.target.value })}
            style={{ ...SDS.inspInput, background:'#fff' }}>
            <option value="">— none —</option>
            {activeFields.map(f => (
              <option key={f.id} value={f.code}>{f.name} ({f.code})</option>
            ))}
          </select>
          {lbl('City Field')}
          <select value={local.field_city || ''}
            onChange={e => set('field_city', e.target.value)}
            style={{ ...SDS.inspInput, background:'#fff' }}>
            <option value="">— none —</option>
            {activeFields.map(f => (
              <option key={f.id} value={f.code}>{f.name} ({f.code})</option>
            ))}
          </select>
          {lbl('State Field')}
          <select value={local.field_state || ''}
            onChange={e => set('field_state', e.target.value)}
            style={{ ...SDS.inspInput, background:'#fff' }}>
            <option value="">— none —</option>
            {activeFields.map(f => (
              <option key={f.id} value={f.code}>{f.name} ({f.code})</option>
            ))}
          </select>
          {lbl('Zip Field')}
          <select value={local.field_zip || ''}
            onChange={e => set('field_zip', e.target.value)}
            style={{ ...SDS.inspInput, background:'#fff' }}>
            <option value="">— none —</option>
            {activeFields.map(f => (
              <option key={f.id} value={f.code}>{f.name} ({f.code})</option>
            ))}
          </select>
          {lbl('Country Field')}
          <select value={local.field_country || ''}
            onChange={e => set('field_country', e.target.value)}
            style={{ ...SDS.inspInput, background:'#fff' }}>
            <option value="">— none —</option>
            {activeFields.map(f => (
              <option key={f.id} value={f.code}>{f.name} ({f.code})</option>
            ))}
          </select>
        </>}

        {lbl('Map Type')}
        {toggle('maptype', [
          { v:'roadmap',   l:'🗺 Roadmap'   },
          { v:'satellite', l:'🛰 Satellite' },
        ])}

        {lbl('Zoom Level')}
        <select value={local.zoom ?? 15}
          onChange={e => set('zoom', Number(e.target.value))}
          style={{ ...SDS.inspInput, background:'#fff' }}>
          {[
            [0,'0 — Entire World'],[1,'1'],[2,'2'],[3,'3'],
            [4,'4 — Continents'],[5,'5'],[6,'6'],[7,'7'],
            [8,'8 — Countries'],[9,'9'],[10,'10'],[11,'11'],
            [12,'12'],[13,'13'],[14,'14'],
            [15,'15 — Neighborhoods'],[16,'16'],[17,'17'],
            [18,'18 — Streets'],[19,'19'],[20,'20'],
            [21,'21'],[22,'22 — Buildings'],
          ].map(([v,l]) => (
            <option key={v} value={v}>{l}</option>
          ))}
        </select>

        {lbl('Map Height (px)')}
        <select value={local.mapheight ?? 400}
          onChange={e => set('mapheight', Number(e.target.value))}
          style={{ ...SDS.inspInput, background:'#fff' }}>
          {[100,150,200,250,300,350,400,450,500,600,700,800].map(n => (
            <option key={n} value={n}>{n}px{n===400 ? ' (default)' : ''}</option>
          ))}
        </select>

        <label style={{ display:'flex', alignItems:'center', gap:8, fontSize:12,
                        color:'#374151', cursor:'pointer', marginBottom:10,
                        userSelect:'none' }}>
          <input type="checkbox"
            checked={local.showcaption !== false}
            onChange={e => set('showcaption', e.target.checked)}
            style={{ width:14, height:14, cursor:'pointer' }} />
          Show address caption below map
        </label>

        {labelSection()}
      </>}

      {local.type === 'portal' && (
        typeof PortalInspector !== 'undefined'
          ? <PortalInspector
              local={{ ...local, _appId: app?.app_id }}
              set={set}
              tables={typeof tables !== 'undefined' ? tables : []}
              fields={activeFields}
              lbl={lbl}
              sel={sel}
              inp={inp}
            />
          : <>
              {lbl('Label')}
              {inp('label', { placeholder:'Related records' })}
              {lbl('Related Table')}
              {inp('portal_table', { placeholder:'tN' })}
            </>
      )}

      {local.type === 'weather' && <>
        {lbl('Label')}
        {inp('label', { placeholder:'Current Weather' })}
        {lbl('City Field')}{fieldSelector(false)}
      </>}

      {deleteBtn(`Delete this ${local.type} object?`)}
    </div>
  );
}
