// ====================================================================
// LOCATION DETAIL, HQ source-of-truth deep-dive for a single location.
// Reached by clicking a row in Listings → Locations. Covers the full
// listings + reviews product surface: data coverage & quality, presence
// visibility, health monitoring, citations, assets, review monitoring /
// response / alerts, sentiment, posting, and the listings → LOCALACT
// performance link. All per-location numbers derive deterministically
// from the row so each location reads consistently across visits.
// ====================================================================
(function () {
const { useState, useEffect, useMemo } = React;
const T = (...a) => (window.toast || (() => {}))(...a);

// ---- deterministic helpers ------------------------------------------
const hash = (s) => { let h = 0; for (let i = 0; i < (s||'').length; i++) h = ((h << 5) - h) + s.charCodeAt(i); return Math.abs(h); };
// Math.abs guards against negative results when callers pass signed-shifted
// seeds (e.g. `seed >> 6`) whose hash exceeds 2^31, keeps indices/ranges valid.
const pick = (seed, n) => Math.abs(seed % n);
const between = (seed, lo, hi) => lo + Math.abs(seed % (hi - lo + 1));

// ---- shared bits ----------------------------------------------------
const ldLabel = ({ children, count, style }) => (
  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '22px 0 10px', ...style }}>
    <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ink-3)' }}>{children}</div>
    {count != null && <span style={{ fontSize: 11, color: 'var(--ink-4)', fontFamily: 'var(--ff-mono)' }}>{count}</span>}
  </div>
);

const TONES = {
  ok:      { bg: 'var(--sage-soft)',     col: 'var(--sage)' },
  warn:    { bg: 'var(--marigold-soft)', col: '#917200' },
  bad:     { bg: 'var(--terra-soft)',    col: '#a12118' },
  info:    { bg: 'var(--sky-soft)',      col: 'var(--sky)' },
  neutral: { bg: 'var(--cream)',         col: 'var(--ink-3)' },
  violet:  { bg: 'rgba(77,105,137,0.12)', col: 'var(--violet)' },
};
function Pill({ tone = 'neutral', children, dot }) {
  const t = TONES[tone] || TONES.neutral;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '3px 9px', fontSize: 11, fontWeight: 700, borderRadius: 999, background: t.bg, color: t.col, whiteSpace: 'nowrap' }}>
      {dot && <span style={{ width: 6, height: 6, borderRadius: '50%', background: t.col }}/>}
      {children}
    </span>
  );
}

function MiniStat({ v, l, c = 'var(--ink)', sub }) {
  return (
    <div>
      <div style={{ fontFamily: 'var(--ff-display)', fontSize: 26, fontWeight: 500, color: c, lineHeight: 1 }}>{v}</div>
      <div style={{ fontSize: 9.5, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '.1em', marginTop: 6 }}>{l}</div>
      {sub && <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 3 }}>{sub}</div>}
    </div>
  );
}

// =====================================================================
// Build a stable per-location model from the clicked row + global data.
// =====================================================================
function buildLocationModel(loc) {
    const data = window.LOCALACT_DATA || {};
    const seed = hash(loc.unit);
    // Location type drives whether retail (product / in-store) surfaces show.
    // Falls back deterministically for any legacy row missing a type.
    const type = loc.type || (seed % 5 < 3 ? 'Service + Retail' : 'Service');
    const isRetail = /retail/i.test(type);
    const score = loc.score != null ? loc.score : between(seed, 58, 94);
    const healthy = score >= 80, weak = score < 68;
    const rating = loc.rating != null ? loc.rating : (3.6 + (seed % 13) / 10);
    const reviewCount = loc.reviewsCount != null ? loc.reviewsCount : between(seed, 70, 540);

    // ---- platform coverage (Listings Data Coverage + Visibility) ----
    const platforms = [
      { key: 'google',   name: 'Google Business Profile', kind: 'Search · Maps', controlled: true,  status: 'connected', complete: between(seed, 88, 99) },
      { key: 'apple',    name: 'Apple Business Connect',   kind: 'Maps',          controlled: true,  status: healthy ? 'connected' : (pick(seed,2)?'pending':'gap'), complete: between(seed>>2, 60, 95) },
      { key: 'bing',     name: 'Bing Places',              kind: 'Search · Maps', controlled: true,  status: weak ? 'gap' : (pick(seed>>3,3)?'connected':'pending'), complete: between(seed>>3, 40, 92) },
      { key: 'facebook', name: 'Facebook',                 kind: 'Social',        controlled: true,  status: pick(seed>>4,4)?'connected':'attention', complete: between(seed>>4, 55, 90) },
      { key: 'yelp',     name: 'Yelp',                     kind: 'Review',        controlled: false, status: pick(seed>>5,3)?'connected':'drift', complete: between(seed>>5, 50, 88) },
      { key: 'instagram',name: 'Instagram',                kind: 'Social',        controlled: false, status: pick(seed>>6,2)?'connected':'gap', complete: between(seed>>6, 45, 85) },
    ];
    const liveCount = platforms.filter(p => p.status === 'connected').length;
    const issueCount = platforms.filter(p => ['attention','drift','pending'].includes(p.status)).length;
    const gapCount = platforms.filter(p => p.status === 'gap').length;

    // ---- data quality fields (Listings Data Quality) ----
    const fields = [
      { k: 'name',        l: 'Business name',        ok: true },
      { k: 'address',     l: 'Address (NAP)',         ok: true },
      { k: 'phone',       l: 'Phone (NAP)',           ok: true },
      { k: 'hours',       l: 'Hours & special hours', ok: !weak },
      { k: 'website',     l: 'Website URL',           ok: score >= 70 },
      { k: 'categories',  l: 'Primary & secondary categories', ok: true },
      { k: 'services',    l: 'Services list',         ok: score >= 64 },
      { k: 'description', l: 'Business description',  ok: !weak },
      { k: 'attributes',  l: 'Attributes (amenities, payments)', ok: score >= 72 },
      { k: 'photos',      l: 'Photos (20+ recommended)', ok: score >= 60 },
    ];
    const filled = fields.filter(f => f.ok).length;
    const quality = Math.round((filled / fields.length) * 100);

    // ---- health monitoring (issues / missing data / errors) ----
    const issues = [];
    if (!fields.find(f=>f.k==='website').ok) issues.push({ sev: 'high', title: 'Google Business Profile URL missing', body: 'AI platforms cannot verify this location without a site URL. Affects ChatGPT, Perplexity, Google AI Overviews.', kind: 'missing' });
    const ap = platforms.find(p=>p.key==='apple');
    if (ap.status !== 'connected') issues.push({ sev: ap.status==='gap'?'high':'med', title: ap.status==='gap'?'Apple Business Connect place card unclaimed':'Apple Business Connect verification pending', body: 'Apple Maps customers may see stale or incomplete details until the place card is claimed and verified.', kind: 'apple' });
    if (platforms.find(p=>p.key==='yelp').status === 'drift') issues.push({ sev: 'med', title: 'Yelp hours drift detected', body: 'Yelp shows different Sunday hours than your source of truth. Publisher overwrite, auto-correction queued.', kind: 'sync' });
    if (!fields.find(f=>f.k==='description').ok) issues.push({ sev: 'low', title: 'Business description incomplete', body: 'A complete, keyword-rich description improves local ranking and AI citation accuracy.', kind: 'missing' });
    if (!fields.find(f=>f.k==='hours').ok) issues.push({ sev: 'med', title: 'Hours incomplete on 2 directories', body: 'Holiday and special hours are missing on Bing and Facebook.', kind: 'sync' });

    // ---- citations (Citation Management) ----
    const citTotal = 42;
    const citLive = Math.round(citTotal * (score / 100));
    const citPending = Math.min(citTotal - citLive, between(seed, 1, 4));
    const citGaps = citTotal - citLive - citPending;

    // ---- sentiment (Sentiment Visibility) ----
    const pos = between(seed, 58, 82), neg = between(seed>>2, 6, 18), neu = 100 - pos - neg;
    const sentTrend = Array.from({ length: 6 }, (_, i) => between(seed >> (i+1), Math.max(40, pos - 16), Math.min(92, pos + 8)));
    const themes = [
      { t: 'Service speed',   v: weak ? 'down' : 'up',   pos: !weak },
      { t: 'Cleaning quality', v: 'up',   pos: true },
      { t: 'Cleanliness',     v: 'flat', pos: true },
      { t: 'Wait times',      v: weak ? 'down' : 'flat', pos: !weak },
      { t: 'Staff friendliness', v: 'up', pos: true },
    ];

    return { data, seed, type, isRetail, score, healthy, weak, rating: Math.round(rating*10)/10, reviewCount,
      platforms, liveCount, issueCount, gapCount, fields, filled, quality, issues,
      citTotal, citLive, citPending, citGaps, pos, neg, neu, sentTrend, themes };
}
window.buildLocationModel = buildLocationModel;
function useLocationModel(loc) {
  return useMemo(() => buildLocationModel(loc), [loc]);
}

// =====================================================================
// TAB · OVERVIEW, visibility, health monitoring, performance link
// =====================================================================
function OverviewTab({ loc, m }) {
  const statusMap = {
    connected: { tone: 'ok', label: 'Live' }, pending: { tone: 'warn', label: 'Pending' },
    attention: { tone: 'warn', label: 'Needs attention' }, drift: { tone: 'warn', label: 'Drift' },
    gap: { tone: 'bad', label: 'Not listed' },
  };
  return (
    <>
      <div className="grid grid-3" style={{ gap: 12 }}>
        <div className="card card-pad">
          <div style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '.08em', fontWeight: 600 }}>Listing health</div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 8 }}>
            <span style={{ fontFamily: 'var(--ff-display)', fontSize: 38, fontWeight: 500, color: m.healthy ? 'var(--sage)' : m.weak ? 'var(--terra)' : 'var(--marigold)' }}>{m.score}</span>
            <span style={{ fontSize: 13, color: 'var(--ink-4)' }}>/ 100</span>
          </div>
          <div style={{ height: 6, background: 'var(--cream-2)', borderRadius: 3, marginTop: 10, overflow: 'hidden' }}>
            <div style={{ width: m.score + '%', height: '100%', background: m.healthy ? 'var(--sage)' : m.weak ? 'var(--terra)' : 'var(--marigold)' }}/>
          </div>
        </div>
        <div className="card card-pad">
          <div style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '.08em', fontWeight: 600 }}>Data quality</div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 8 }}>
            <span style={{ fontFamily: 'var(--ff-display)', fontSize: 38, fontWeight: 500, color: 'var(--ink)' }}>{m.quality}%</span>
            <span style={{ fontSize: 13, color: 'var(--ink-4)' }}>{m.filled}/{m.fields.length} fields</span>
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 10 }}>Complete · consistent · actively maintained</div>
        </div>
        <div className="card card-pad">
          <div style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '.08em', fontWeight: 600 }}>Presence</div>
          <div style={{ display: 'flex', gap: 18, marginTop: 12 }}>
            <MiniStat v={m.liveCount} l="Live" c="var(--sage)"/>
            <MiniStat v={m.issueCount} l="Issues" c="var(--marigold)"/>
            <MiniStat v={m.gapCount} l="Gaps" c="var(--terra)"/>
          </div>
        </div>
      </div>

      {/* Listings visibility, unified presence */}
      {ldLabel({ children: 'Listings visibility, where this location appears', count: m.platforms.length + ' platforms' })}
      <div className="card" style={{ overflow: 'hidden' }}>
        {m.platforms.map((p, i) => {
          const s = statusMap[p.status];
          return (
            <div key={p.key} style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 0.9fr 1.3fr 0.7fr', gap: 12, alignItems: 'center', padding: '13px 16px', borderBottom: i === m.platforms.length-1 ? 'none' : '1px solid var(--cream-2)', fontSize: 13 }}>
              <div style={{ fontWeight: 600, color: 'var(--ink)' }}>{p.name}</div>
              <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>{p.kind}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-4)' }}>{p.controlled ? 'Direct API' : '3rd-party'}</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ flex: 1, height: 5, background: 'var(--cream-2)', borderRadius: 3, overflow: 'hidden', maxWidth: 90 }}>
                  <div style={{ width: p.complete + '%', height: '100%', background: p.complete >= 80 ? 'var(--sage)' : p.complete >= 60 ? 'var(--marigold)' : 'var(--terra)' }}/>
                </div>
                <span style={{ fontFamily: 'var(--ff-mono)', fontSize: 11, color: 'var(--ink-3)' }}>{p.complete}%</span>
              </div>
              <div style={{ textAlign: 'right' }}><Pill tone={s.tone} dot>{s.label}</Pill></div>
            </div>
          );
        })}
      </div>

      {/* Health monitoring */}
      <SourceNote>Source: Google Business Profile</SourceNote>

      {ldLabel({ children: 'Listing health monitoring', count: m.issues.length + ' to resolve' })}
      {m.issues.length === 0 ? (
        <div className="card card-pad" style={{ display: 'flex', alignItems: 'center', gap: 10, color: 'var(--sage)', fontSize: 13, fontWeight: 600 }}>
          {window.Icon.check} No sync issues, missing data, or errors detected.
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {m.issues.map((iss, i) => {
            const tone = iss.sev === 'high' ? 'bad' : iss.sev === 'med' ? 'warn' : 'info';
            return (
              <div key={i} className="card" style={{ padding: '13px 16px', display: 'flex', gap: 13, alignItems: 'center' }}>
                <span style={{ width: 8, height: 8, borderRadius: '50%', background: TONES[tone].col, flexShrink: 0 }}/>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{iss.title}</div>
                  <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2, lineHeight: 1.5 }}>{iss.body}</div>
                </div>
                <Pill tone={tone}>{iss.sev === 'high' ? 'High' : iss.sev === 'med' ? 'Sync' : 'Minor'}</Pill>
                <button className="btn btn-quiet btn-xs" onClick={() => T(iss.kind === 'missing' ? 'Opening editor' : 'Fix queued', iss.kind === 'missing' ? 'Jump to Listings data to complete this field.' : 'Re-sync queued, directories refresh within minutes.', iss.kind === 'missing' ? 'info' : 'ok')}>
                  {iss.kind === 'missing' ? 'Fix →' : 'Force sync'}
                </button>
              </div>
            );
          })}
        </div>
      )}

      {/* Listings ↔ performance link */}
      <SourceNote>Source: Google Business Profile</SourceNote>

      {ldLabel({ children: 'Listings → LOCALACT performance' })}
      <div className="card card-pad" style={{ background: 'linear-gradient(135deg, var(--ink) 0%, #2a2a2b 100%)', color: 'var(--cream)', border: 'none', display: 'grid', gridTemplateColumns: '1fr auto', gap: 24, alignItems: 'center' }}>
        <div>
          <div style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '.1em', color: 'rgba(250,246,239,.5)', fontWeight: 600 }}>Correlation · read-only feed</div>
          <div style={{ fontFamily: 'var(--ff-display)', fontSize: 19, fontWeight: 500, margin: '6px 0 8px' }}>Listings data flows into LOCALACT for visibility, not ownership.</div>
          <div style={{ fontSize: 12.5, color: 'rgba(250,246,239,.65)', lineHeight: 1.6, maxWidth: 560 }}>
            Presence, NAP accuracy, and review velocity for {loc.unit} are correlated against leads and discovery so you can see how listing health moves performance. The system reads this data; it never owns it.
          </div>
        </div>
        <div style={{ display: 'flex', gap: 22 }}>
          <MiniStatDark v={loc.leads != null ? loc.leads : between(m.seed,40,180)} l="Leads / mo"/>
          <MiniStatDark v={'+' + between(m.seed, 8, 24) + '%'} l="Discovery" c="var(--sage)"/>
          <MiniStatDark v={m.rating + '★'} l="Avg rating"/>
        </div>
      </div>
      <SourceNote>Source: Google Ads + FreshNest CRM · correlation calculated by LOCALACT</SourceNote>
    </>
  );
}
function MiniStatDark({ v, l, c = 'var(--cream)' }) {
  return (
    <div style={{ textAlign: 'left' }}>
      <div style={{ fontFamily: 'var(--ff-display)', fontSize: 24, fontWeight: 500, color: c, lineHeight: 1.1 }}>{v}</div>
      <div style={{ fontSize: 9.5, color: 'rgba(250,246,239,.45)', textTransform: 'uppercase', letterSpacing: '.1em', marginTop: 4 }}>{l}</div>
    </div>
  );
}

// =====================================================================
// TAB · LISTINGS DATA, coverage + quality + services/attributes
// =====================================================================
function ListingsDataTab({ loc, m }) {
  const statusMap = {
    connected: { tone: 'ok', label: 'Connected' }, pending: { tone: 'warn', label: 'Pending' },
    attention: { tone: 'warn', label: 'Attention' }, drift: { tone: 'warn', label: 'Drift' }, gap: { tone: 'bad', label: 'Not listed' },
  };
  // Cleaning services apply to every location; product/in-store services only
  // to Service + Retail locations.
  const services = ['Home cleaning', 'Office cleaning', 'Online booking', 'Move-in/out cleaning',
    ...(m.isRetail ? ['Product sales', 'Curbside pickup'] : [])];
  const attributes = ['Eco-certified products', 'Wheelchair accessible', 'Accepts Apple Pay', 'Family friendly', 'Free parking', 'Appointments available'];
  return (
    <>
      {ldLabel({ children: 'Listings data coverage', count: m.liveCount + ' / ' + m.platforms.length + ' connected' })}
      <div className="grid grid-3" style={{ gap: 12 }}>
        {m.platforms.map(p => {
          const s = statusMap[p.status];
          return (
            <div key={p.key} className="card card-pad" style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--ink)' }}>{p.name}</div>
                <Pill tone={s.tone} dot>{s.label}</Pill>
              </div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{p.kind} · {p.controlled ? 'we control' : 'we influence'}</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 'auto' }}>
                <div style={{ flex: 1, height: 6, background: 'var(--cream-2)', borderRadius: 3, overflow: 'hidden' }}>
                  <div style={{ width: p.complete + '%', height: '100%', background: p.complete >= 80 ? 'var(--sage)' : p.complete >= 60 ? 'var(--marigold)' : 'var(--terra)' }}/>
                </div>
                <span style={{ fontFamily: 'var(--ff-mono)', fontSize: 11.5, color: 'var(--ink-3)' }}>{p.complete}%</span>
              </div>
            </div>
          );
        })}
      </div>

      <SourceNote>Source: Google Business Profile</SourceNote>

      {ldLabel({ children: 'Data quality, completeness & consistency', count: m.quality + '% complete' })}
      <div className="card" style={{ overflow: 'hidden' }}>
        {m.fields.map((f, i) => (
          <div key={f.k} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '11px 16px', borderBottom: i === m.fields.length-1 ? 'none' : '1px solid var(--cream-2)' }}>
            <span style={{ width: 22, height: 22, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: f.ok ? 'var(--sage-soft)' : 'var(--terra-soft)', color: f.ok ? 'var(--sage)' : 'var(--terra)', flexShrink: 0 }}>
              {f.ok ? window.Icon.check : '!'}
            </span>
            <div style={{ flex: 1, fontSize: 13, color: 'var(--ink)', fontWeight: f.ok ? 400 : 600 }}>{f.l}</div>
            {f.ok ? <span style={{ fontSize: 11.5, color: 'var(--ink-4)' }}>Consistent across all listings</span>
                  : <button className="btn btn-quiet btn-xs" onClick={() => T('Editing ' + f.l, 'Saved to source of truth and pushed to every connected directory.', 'ok')}>Complete →</button>}
          </div>
        ))}
      </div>

      <div className="grid grid-2" style={{ gap: 12, marginTop: 22 }}>
        <div className="card card-pad">
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 12 }}>Services</div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
            {services.map(s => <Pill key={s} tone="neutral">{s}</Pill>)}
            <button className="btn btn-quiet btn-xs" onClick={() => T('Add service', 'New service standardized across all listings.', 'ok')}>+ Add</button>
          </div>
        </div>
        <div className="card card-pad">
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 12 }}>Attributes</div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
            {attributes.map(s => <Pill key={s} tone="violet">{s}</Pill>)}
            <button className="btn btn-quiet btn-xs" onClick={() => T('Manage attributes', 'Amenities and payment options standardized across all listings.', 'ok')}>+ Add</button>
          </div>
        </div>
      </div>
    </>
  );
}

// =====================================================================
// TAB · CITATIONS, coverage & sync across directories
// =====================================================================
function CitationsTab({ loc, m }) {
  const data = m.data;
  const all = (data.listings || []).slice(0, 30);
  const [filter, setFilter] = useState('all');
  const rows = all.filter(l => filter === 'all' || (filter === 'live' ? l.status === 'live' : l.status !== 'live'));
  return (
    <>
      <div className="grid grid-3" style={{ gap: 12 }}>
        <div className="card card-pad"><MiniStat v={m.citLive} l="Citations live" c="var(--sage)" sub={'of ' + m.citTotal + ' directories'}/></div>
        <div className="card card-pad"><MiniStat v={m.citPending} l="Syncing" c="var(--marigold)" sub="awaiting publisher refresh"/></div>
        <div className="card card-pad"><MiniStat v={m.citGaps} l="Gaps" c="var(--terra)" sub="no citation found"/></div>
      </div>

      {ldLabel({ children: 'Citation coverage & sync status', count: rows.length + ' directories' })}
      <div style={{ display: 'flex', gap: 6, marginBottom: 10 }}>
        {[['all','All'],['live','Live'],['issue','Needs sync']].map(([k,l]) => (
          <button key={k} onClick={() => setFilter(k)} style={{ padding: '6px 12px', borderRadius: 999, fontSize: 11.5, fontWeight: 600, cursor: 'pointer', border: filter===k?'1px solid var(--ink-3)':'1px solid var(--cream-3)', background: filter===k?'var(--ink)':'var(--paper)', color: filter===k?'var(--paper)':'var(--ink-3)' }}>{l}</button>
        ))}
        <div style={{ flex: 1 }}/>
        <button className="btn btn-primary btn-sm" onClick={() => T('Sync queued', 'All citations for ' + loc.unit + ' will refresh within 15 minutes.', 'info')}>↺ Sync all citations</button>
      </div>
      <div className="card" style={{ overflow: 'hidden' }}>
        <table className="tbl">
          <thead><tr><th>Directory</th><th>Type</th><th>Status</th><th>Last sync</th><th>Connection</th></tr></thead>
          <tbody>
            {rows.map((l, i) => (
              <tr key={i}>
                <td className="strong">{l.name}</td>
                <td><Pill tone="neutral">{l.type}</Pill></td>
                <td><Pill tone={l.status === 'live' ? 'ok' : 'warn'} dot>{l.status === 'live' ? 'Live' : (l.controlled ? 'Issue' : 'Drift')}</Pill></td>
                <td className="mono muted">{l.lastSync}</td>
                <td style={{ fontSize: 12, color: 'var(--ink-3)' }}>{l.controlled ? 'Direct API' : '3rd-party cadence'}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </>
  );
}

// =====================================================================
// TAB · REVIEWS, monitoring, AI response + approval, alerts
// =====================================================================
function ReviewsTab({ loc, m }) {
  const base = (m.data.reviews || []);
  const [reviews, setReviews] = useState(() => base.map((r, i) => ({ ...r, _id: i, flag: r.stars <= 2 ? 'negative' : (i === 0 ? 'new' : null) })));
  const [platform, setPlatform] = useState('all');
  const [drafting, setDrafting] = useState(null);
  const [reply, setReply] = useState('');
  const [approval, setApproval] = useState(true);

  const draft = (r) => {
    setDrafting(r);
    setReply(r.stars >= 4
      ? `Hi ${r.author.split(' ')[0]}, thank you for the kind words! The ${loc.unit} team works hard to make every visit count and we can't wait to welcome you back.`
      : `Hi ${r.author.split(' ')[0]}, thank you for the honest feedback. This isn't the experience we want at ${loc.unit}. I'd like to make it right, please reach out so we can follow up directly.`);
  };
  const post = () => {
    setReviews(rs => rs.map(x => x._id === drafting._id ? { ...x, replied: true, pending: approval } : x));
    setDrafting(null);
    T(approval ? 'Sent for approval' : 'Reply posted', approval ? 'Routed to the brand approver before it publishes.' : 'Live on ' + drafting.platform + ' within 2 minutes.', 'ok');
  };

  const filtered = platform === 'all' ? reviews : reviews.filter(r => r.platform.toLowerCase() === platform);
  const needs = reviews.filter(r => !r.replied);
  const negatives = reviews.filter(r => r.stars <= 2).length;

  const alerts = [
    { k: 'new', l: 'New reviews', d: 'Notify the location owner the moment a review lands.', on: true },
    { k: 'neg', l: 'Negative reviews (≤ 2★)', d: 'Escalate to the owner and brand within 15 minutes.', on: true },
    { k: 'vol', l: 'High-volume changes', d: 'Alert when review volume spikes or drops vs. the location baseline.', on: true },
    { k: 'kw',  l: 'Keyword watch', d: 'Flag reviews mentioning safety, refund, or manager.', on: false },
  ];

  return (
    <>
      <div className="grid grid-4" style={{ gap: 12 }}>
        <div className="card card-pad"><MiniStat v={m.rating + '★'} l="Avg rating" c="var(--marigold)"/></div>
        <div className="card card-pad"><MiniStat v={m.reviewCount} l="Total reviews" sub={'+' + between(m.seed,4,18) + ' this month'}/></div>
        <div className="card card-pad"><MiniStat v={needs.length} l="Needs reply" c={needs.length?'var(--marigold)':'var(--sage)'}/></div>
        <div className="card card-pad"><MiniStat v={negatives} l="Negative open" c={negatives?'var(--terra)':'var(--sage)'}/></div>
      </div>

      {/* Monitoring */}
      {ldLabel({ children: 'Review monitoring, all platforms', count: filtered.length + ' shown' })}
      <div style={{ display: 'flex', gap: 6, marginBottom: 10 }}>
        <window.DropMenu value={platform} onPick={setPlatform} options={[{v:'all',l:'All platforms'},{v:'google',l:'Google'},{v:'yelp',l:'Yelp'},{v:'facebook',l:'Facebook'}]}/>
        <div style={{ flex: 1 }}/>
        <button className="btn btn-primary btn-sm" onClick={() => { if (!needs.length) { T('All caught up','No pending replies.','ok'); return; } draft(needs[0]); }}>
          Respond with Lenny {needs.length > 0 && <span style={{ marginLeft: 4, background: 'rgba(255,255,255,.22)', padding: '1px 6px', borderRadius: 999, fontSize: 10 }}>{needs.length}</span>}
        </button>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {filtered.map(r => (
          <div key={r._id} className="card card-pad">
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
              <div style={{ width: 36, height: 36, borderRadius: '50%', background: 'var(--cream-2)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, fontWeight: 700 }}>{r.author[0]}</div>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600 }}>{r.author}</div>
                <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{r.platform} · {r.time} ago</div>
              </div>
              <div style={{ flex: 1 }}/>
              {r.flag === 'new' && !r.replied && <Pill tone="info">New</Pill>}
              {r.stars <= 2 && <Pill tone="bad">Negative</Pill>}
              <div style={{ color: 'var(--marigold)', fontSize: 14 }}>{'★'.repeat(r.stars)}<span style={{ color: 'var(--cream-3)' }}>{'★'.repeat(5 - r.stars)}</span></div>
              {r.replied ? <Pill tone={r.pending ? 'warn' : 'ok'}>{r.pending ? 'Awaiting approval' : 'Replied'}</Pill> : <Pill tone="warn">Needs reply</Pill>}
            </div>
            <div style={{ fontSize: 13, color: 'var(--ink-2)', lineHeight: 1.55, paddingLeft: 46 }}>"{r.text}"</div>
            {!r.replied && (
              <div style={{ marginTop: 10, paddingLeft: 46, display: 'flex', gap: 6 }}>
                <button className="btn btn-terra btn-sm" onClick={() => draft(r)}>Draft reply with Lenny</button>
                <button className="btn btn-ghost btn-sm" onClick={() => setReviews(rs => rs.map(x => x._id === r._id ? { ...x, replied: true } : x))}>Skip</button>
              </div>
            )}
          </div>
        ))}
      </div>

      {/* Alerts */}
      <SourceNote>Source: Google Business Profile</SourceNote>

      {ldLabel({ children: 'Review alerts' })}
      <div className="card" style={{ overflow: 'hidden' }}>
        {alerts.map((a, i) => <AlertRow key={a.k} a={a} last={i === alerts.length-1}/>)}
      </div>

      {/* Response composer */}
      <window.Modal open={!!drafting} onClose={() => setDrafting(null)} width={580}
        title={`Reply to ${drafting?.author}`} sub={drafting ? `${drafting.platform} · ${drafting.stars}★ · ${loc.unit}` : ''}
        footer={<>
          <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--ink-3)', marginRight: 'auto' }}>
            <input type="checkbox" checked={approval} onChange={e => setApproval(e.target.checked)}/> Route through approval
          </label>
          <button className="btn btn-ghost btn-sm" onClick={() => setDrafting(null)}>Cancel</button>
          <button className="btn btn-ghost btn-sm" onClick={() => setReply(t => t + '\n\nP.S. your next visit is on us, just mention this review.')}> Re-draft</button>
          <button className="btn btn-primary btn-sm" onClick={post}>{approval ? 'Send for approval' : 'Post reply'}</button>
        </>}>
        {drafting && (
          <div>
            <div style={{ padding: '12px 14px', background: 'var(--cream)', borderRadius: 10, fontSize: 12.5, color: 'var(--ink-2)', marginBottom: 14, lineHeight: 1.6 }}>
              <strong>Their review:</strong> "{drafting.text}"
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
              <window.LennyLlama size={24} mood="thinking" bg="circle"/>
              <span style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 600 }}>Lenny's draft, tone-matched to brand voice</span>
            </div>
            <textarea value={reply} onChange={e => setReply(e.target.value)} rows={6}
              style={{ width: '100%', padding: '10px 12px', fontSize: 13, fontFamily: 'var(--ff-ui)', border: '1px solid var(--cream-3)', borderRadius: 8, resize: 'vertical', lineHeight: 1.5, boxSizing: 'border-box' }}/>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 6 }}>{approval ? 'Will route to the brand approver before publishing.' : 'Publishes directly to ' + drafting.platform + '.'} · {reply.length} chars</div>
          </div>
        )}
      </window.Modal>
    </>
  );
}
function AlertRow({ a, last }) {
  const [on, setOn] = useState(a.on);
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '14px 16px', borderBottom: last ? 'none' : '1px solid var(--cream-2)' }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{a.l}</div>
        <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>{a.d}</div>
      </div>
      <button onClick={() => setOn(o => !o)} style={{ width: 42, height: 24, borderRadius: 999, border: 'none', cursor: 'pointer', background: on ? 'var(--sage)' : 'var(--cream-3)', position: 'relative', transition: 'background .2s' }}>
        <span style={{ position: 'absolute', top: 2, left: on ? 20 : 2, width: 20, height: 20, borderRadius: '50%', background: '#fff', transition: 'left .2s', boxShadow: '0 1px 3px rgba(0,0,0,.2)' }}/>
      </button>
    </div>
  );
}

// =====================================================================
// TAB · SENTIMENT, analysis + trend visibility
// =====================================================================
function SentimentTab({ loc, m }) {
  const segs = [
    { l: 'Positive', v: m.pos, c: 'var(--sage)' },
    { l: 'Neutral',  v: m.neu, c: 'var(--marigold)' },
    { l: 'Negative', v: m.neg, c: 'var(--terra)' },
  ];
  const arrow = (d) => d === 'up' ? '↑' : d === 'down' ? '↓' : '→';
  return (
    <>
      {ldLabel({ children: 'Sentiment breakdown', count: 'last 90 days' })}
      <div className="card card-pad">
        <div style={{ display: 'flex', height: 14, borderRadius: 999, overflow: 'hidden', marginBottom: 16 }}>
          {segs.map(s => <div key={s.l} style={{ width: s.v + '%', background: s.c }} title={`${s.l} ${s.v}%`}/>)}
        </div>
        <div style={{ display: 'flex', gap: 28 }}>
          {segs.map(s => (
            <div key={s.l} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <span style={{ width: 10, height: 10, borderRadius: 3, background: s.c }}/>
              <span style={{ fontFamily: 'var(--ff-display)', fontSize: 22, fontWeight: 500, color: 'var(--ink)' }}>{s.v}%</span>
              <span style={{ fontSize: 12, color: 'var(--ink-3)' }}>{s.l}</span>
            </div>
          ))}
        </div>
      </div>

      <div className="grid grid-2" style={{ gap: 12, marginTop: 22 }}>
        <div className="card card-pad">
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 14 }}>Positive sentiment trend</div>
          <div style={{ display: 'flex', alignItems: 'flex-end', gap: 10, height: 90 }}>
            {m.sentTrend.map((v, i) => (
              <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
                <div style={{ width: '100%', height: `${v}%`, background: 'linear-gradient(to top, var(--sage), var(--sage-soft))', borderRadius: '6px 6px 0 0' }}/>
                <span style={{ fontSize: 10, color: 'var(--ink-4)', fontFamily: 'var(--ff-mono)' }}>{['M1','M2','M3','M4','M5','Now'][i]}</span>
              </div>
            ))}
          </div>
        </div>
        <div className="card card-pad">
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 14 }}>Themes customers mention</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
            {m.themes.map(t => (
              <div key={t.t} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ fontSize: 13, color: 'var(--ink)', flex: 1 }}>{t.t}</span>
                <span style={{ fontSize: 13, fontWeight: 700, color: t.v === 'down' ? 'var(--terra)' : t.v === 'up' ? 'var(--sage)' : 'var(--ink-4)' }}>{arrow(t.v)}</span>
                <Pill tone={t.pos ? 'ok' : 'bad'}>{t.pos ? 'Positive' : 'Watch'}</Pill>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="card card-pad" style={{ marginTop: 22, display: 'flex', gap: 12, alignItems: 'flex-start', background: 'var(--sage-soft)', border: 'none' }}>
        <div style={{ width: 32, height: 32, borderRadius: '50%', overflow: 'hidden', flexShrink: 0, border: '1.5px solid var(--sage)', background: 'var(--paper)' }}>
          <window.LennyLlama size={32} mood="happy" bg="circle"/>
        </div>
        <div style={{ fontSize: 12.5, color: 'var(--ink-2)', lineHeight: 1.6 }}>
          {m.weak
            ? `Service speed and wait times are dragging sentiment at ${loc.unit}. Three of the last ten reviews mention slow counter service, worth a staffing review.`
            : `Sentiment at ${loc.unit} is trending up, led by cleaning quality and staff friendliness. Keep responding within 48h to protect the streak.`}
        </div>
      </div>
    </>
  );
}

// =====================================================================
// TAB · POSTS, Google Posts create / schedule / approve
// =====================================================================
function PostsTab({ loc, m }) {
  const [posts, setPosts] = useState([
    { id: 1, title: 'Weekend deep-clean special', when: 'Scheduled · Sat 9:00 AM', status: 'approved', type: 'Offer' },
    { id: 2, title: 'New office-cleaning plans now live', when: 'Pending approval', status: 'pending', type: 'Update' },
    { id: 3, title: 'Father\'s Day pre-orders open', when: 'Draft', status: 'draft', type: 'Event' },
  ]);
  const [composing, setComposing] = useState(false);
  const [title, setTitle] = useState('');
  const tone = { approved: 'ok', pending: 'warn', draft: 'neutral' };
  const label = { approved: 'Scheduled', pending: 'Awaiting approval', draft: 'Draft' };
  return (
    <>
      {ldLabel({ children: 'Google Posts', count: posts.length + ' posts' })}
      <div style={{ display: 'flex', marginBottom: 10 }}>
        <div style={{ flex: 1 }}/>
        <button className="btn btn-primary btn-sm" onClick={() => setComposing(true)}>+ Create post</button>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {posts.map(p => (
          <div key={p.id} className="card card-pad" style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
            <div style={{ width: 44, height: 44, borderRadius: 10, background: 'var(--cream)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--ink-3)', flexShrink: 0 }}>{window.Icon.campaigns}</div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--ink)' }}>{p.title}</div>
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>{p.type} · {p.when}</div>
            </div>
            <Pill tone={tone[p.status]} dot>{label[p.status]}</Pill>
            {p.status === 'pending'
              ? <button className="btn btn-quiet btn-xs" onClick={() => { setPosts(ps => ps.map(x => x.id===p.id?{...x,status:'approved',when:'Scheduled · tomorrow 9:00 AM'}:x)); T('Approved','Post scheduled and queued to Google Business Profile.','ok'); }}>Approve →</button>
              : p.status === 'draft'
              ? <button className="btn btn-quiet btn-xs" onClick={() => { setPosts(ps => ps.map(x => x.id===p.id?{...x,status:'pending',when:'Pending approval'}:x)); T('Submitted','Sent to the brand approver.','ok'); }}>Submit →</button>
              : <button className="btn btn-quiet btn-xs" onClick={() => T('Post','Scheduled, edit anytime before it publishes.','info')}>View</button>}
          </div>
        ))}
      </div>

      <window.Modal open={composing} onClose={() => setComposing(false)} width={560}
        title="Create Google Post" sub={loc.unit + ' · publishes to Google Business Profile'}
        footer={<>
          <button className="btn btn-ghost btn-sm" onClick={() => setComposing(false)}>Cancel</button>
          <button className="btn btn-primary btn-sm" onClick={() => { if (title.trim()) { setPosts(ps => [{ id: Date.now(), title: title.trim(), when: 'Pending approval', status: 'pending', type: 'Update' }, ...ps]); } setComposing(false); setTitle(''); T('Submitted for approval','Routed to the brand approver before it publishes.','ok'); }}>Schedule & submit</button>
        </>}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div>
            <label style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '.06em', fontWeight: 600, display: 'block', marginBottom: 6 }}>Post title</label>
            <input value={title} onChange={e => setTitle(e.target.value)} placeholder="e.g. Weekend deep-clean special"
              style={{ width: '100%', padding: '9px 12px', border: '1px solid var(--cream-3)', borderRadius: 8, fontSize: 13, fontFamily: 'var(--ff-sans)', boxSizing: 'border-box' }}/>
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="btn btn-ghost btn-sm" onClick={() => setTitle(' ' + (m.weak ? 'We miss you, 20% off your next deep clean' : 'This weekend only: deep-clean bundle savings'))}> Draft with Lenny</button>
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', lineHeight: 1.6, padding: '10px 12px', background: 'var(--cream)', borderRadius: 8 }}>
            Posts route through brand approval before publishing, then schedule to Google Business Profile automatically.
          </div>
        </div>
      </window.Modal>
    </>
  );
}

// =====================================================================
// TAB · ASSETS, image management, GBP + Apple, location + corporate
// =====================================================================
function AssetsTab({ loc, m }) {
  const lib = m.data.imageLibrary || {};
  const palette = ['linear-gradient(135deg,#fcdec5,#f6a663)','linear-gradient(135deg,#c5e6d3,#62ba88)','linear-gradient(135deg,#e5eefd,#669bf1)','linear-gradient(135deg,#e0e5eb,#4d6989)','linear-gradient(135deg,#faebbf,#d6b84d)','linear-gradient(135deg,#fae6e4,#d5711c)'];
  // Retail locations show storefront/product imagery; Service-only locations
  // show crew/service imagery in those slots instead.
  const tileLabels = m.isRetail
    ? ['Deep clean results','Storefront','Product shelf','Team','Service van','Refill station','Office clean','Interior']
    : ['Deep clean results','Crew at work','Before / after','Team','Service van','Equipment','Office clean','Home exterior'];
  const tiles = Array.from({ length: 8 }, (_, i) => ({
    grad: palette[i % palette.length],
    label: tileLabels[i],
    scope: i < 2 ? 'corporate' : 'location',
    surfaces: { gbp: (m.seed >>> i) % 2 === 0, apple: (m.seed >>> (i+1)) % 3 !== 0 },
  }));
  return (
    <>
      <div className="grid grid-3" style={{ gap: 12 }}>
        <div className="card card-pad"><MiniStat v={tiles.length} l="Photos managed" sub="GBP + Apple only"/></div>
        <div className="card card-pad"><MiniStat v={tiles.filter(t=>t.scope==='corporate').length} l="Corporate assets" c="var(--violet)"/></div>
        <div className="card card-pad"><MiniStat v={tiles.filter(t=>t.scope==='location').length} l="Location uploads" c="var(--sage)"/></div>
      </div>

      {ldLabel({ children: 'Listings image library', count: tiles.length + ' images' })}
      <div className="grid grid-4" style={{ gap: 12 }}>
        {tiles.map((t, i) => (
          <div key={i} className="card" style={{ overflow: 'hidden' }}>
            <div style={{ height: 110, background: t.grad, display: 'flex', alignItems: 'flex-start', justifyContent: 'flex-end', padding: 8 }}>
              <Pill tone={t.scope === 'corporate' ? 'violet' : 'ok'}>{t.scope === 'corporate' ? 'Corporate' : 'Location'}</Pill>
            </div>
            <div style={{ padding: '10px 12px' }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink)' }}>{t.label}</div>
              <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
                <span style={{ fontSize: 10.5, fontWeight: 600, padding: '2px 7px', borderRadius: 999, background: t.surfaces.gbp ? 'var(--sage-soft)' : 'var(--cream)', color: t.surfaces.gbp ? 'var(--sage)' : 'var(--ink-4)' }}>GBP{t.surfaces.gbp ? ' ✓' : ''}</span>
                <span style={{ fontSize: 10.5, fontWeight: 600, padding: '2px 7px', borderRadius: 999, background: t.surfaces.apple ? 'var(--sage-soft)' : 'var(--cream)', color: t.surfaces.apple ? 'var(--sage)' : 'var(--ink-4)' }}>Apple{t.surfaces.apple ? ' ✓' : ''}</span>
              </div>
            </div>
          </div>
        ))}
        <button className="card" onClick={() => T('Upload','Drop images to publish to GBP and Apple for ' + loc.unit + '.','info')}
          style={{ minHeight: 168, border: '1.5px dashed var(--cream-3)', background: 'var(--cream)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 8, cursor: 'pointer', color: 'var(--ink-3)' }}>
          <span style={{ fontSize: 24 }}>{window.Icon.plus}</span>
          <span style={{ fontSize: 12, fontWeight: 600 }}>Upload image</span>
        </button>
      </div>
    </>
  );
}

// =====================================================================
// MAIN, page shell with header + tabs
// =====================================================================
const TABS = [
  ['overview', 'Overview'],
  ['data', 'Listings data'],
  ['citations', 'Citations'],
  ['reviews', 'Reviews'],
  ['sentiment', 'Sentiment'],
  ['posts', 'Posts'],
  ['assets', 'Assets'],
];

function LocationDetailPage({ loc, onBack, onEdit }) {
  const [tab, setTab] = useState('overview');
  const m = useLocationModel(loc);
  useEffect(() => { setTab('overview'); }, [loc]);

  const statusTone = { 'Active': 'ok', 'Onboarding': 'warn', 'At-risk': 'bad' };
  const fmtUpdated = (d) => d == null ? '–' : d === 1 ? 'yesterday' : d <= 30 ? `${d} days ago` : `${Math.floor(d/30)} mo ago`;

  return (
    <div className="page active">
      {/* Breadcrumb / back */}
      <button onClick={onBack} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'none', border: 'none', cursor: 'pointer', color: 'var(--ink-3)', fontSize: 12.5, fontWeight: 600, padding: 0, marginBottom: 14, fontFamily: 'var(--ff-ui)' }}>
        <span style={{ transform: 'rotate(180deg)', display: 'inline-flex' }}>{window.Icon.arrow}</span> All locations
      </button>

      {/* Header */}
      <div className="page-header" style={{ alignItems: 'flex-start' }}>
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <div className="page-title" style={{ marginBottom: 0 }}>{loc.unit}</div>
            <Pill tone={statusTone[loc.status] || 'neutral'} dot>{loc.status || 'Active'}</Pill>
            <Pill tone={m.isRetail ? 'violet' : 'neutral'}>{m.type}</Pill>
            {(() => {
              const rd = loc.readiness || (window.marketingReadinessFor ? window.marketingReadinessFor(loc.unit, m.score) : 'ready');
              const map = { ready: ['ok', 'Ready'], attention: ['warn', 'Needs attention'], blocked: ['bad', 'Blocked'] };
              const [tone, label] = map[rd] || map.ready;
              return <Pill tone={tone} dot>{label}</Pill>;
            })()}
          </div>
          <div className="page-sub" style={{ marginTop: 6 }}>{loc.address ? loc.address + ' · ' : ''}{loc.city} · {loc.region} region</div>
          <div style={{ display: 'flex', gap: 20, marginTop: 12, flexWrap: 'wrap', fontSize: 12.5, color: 'var(--ink-3)' }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>{window.Icon.user} {loc.owner || '–'}</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontFamily: 'var(--ff-mono)' }}>{window.Icon.phone} {loc.phone || '–'}</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>{window.Icon.bell} Updated {fmtUpdated(loc.daysAgo)}</span>
          </div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost btn-sm" onClick={() => onEdit ? onEdit(loc) : T('Edit ' + loc.unit, 'Editing the source of truth, changes push to every connected directory on save.', 'info')}>Edit details</button>
          <button className="btn btn-primary btn-sm" onClick={() => T('Pushing updates', loc.unit + ' details pushing to Google, Yelp, Apple Maps, Facebook · ETA ~6 min.', 'ok')}>Push updates →</button>
        </div>
      </div>

      {/* Tabs */}
      <div className="tabs" style={{ marginBottom: 18 }}>
        {TABS.map(([k, l]) => (
          <button key={k} className={'tab ' + (tab === k ? 'active' : '')} onClick={() => setTab(k)}>{l}</button>
        ))}
      </div>

      {tab === 'overview'  && <OverviewTab loc={loc} m={m}/>}
      {tab === 'data'      && <ListingsDataTab loc={loc} m={m}/>}
      {tab === 'citations' && <CitationsTab loc={loc} m={m}/>}
      {tab === 'reviews'   && <ReviewsTab loc={loc} m={m}/>}
      {tab === 'sentiment' && <SentimentTab loc={loc} m={m}/>}
      {tab === 'posts'     && <PostsTab loc={loc} m={m}/>}
      {tab === 'assets'    && <AssetsTab loc={loc} m={m}/>}
    </div>
  );
}
window.LocationDetailPage = LocationDetailPage;

})();
