// Drill-down detail overlay — CEO-grade view of a single location

// ── Helper: build flag context (the "why" story) ───────────────────────
const buildFlagStory = (loc) => {
  if (!loc.flags || !loc.flags.length) return null;
  const story = { flags: [], exposure: [], trajectory: null, franchisee: null };

  // Scarborough is the canonical worst case — wire CASL incident data into it
  const isWhyte = loc.id === 'HB010';
  const isRichmondHill = loc.id === 'HB009';

  if (loc.flags.includes('CASL')) {
    story.flags.push({
      code: 'CASL',
      severity: 'critical',
      headline: isWhyte
        ? '12 SMS sent to customers who never gave consent'
        : '8 SMS sends to opted-out segment',
      detail: isWhyte
        ? 'Campaign "Back to School Kids Cut" reached 12 recipients flagged consent=false in the CRM. List source: CSV uploaded by franchisee Aug 12 — bypassed the consent gate.'
        : 'Campaign "Promo Burst" reached 8 recipients who had previously unsubscribed. Resolved with warning.',
      proof: isWhyte
        ? [
            { l: 'Recipients without consent', v: '12 / 142' },
            { l: 'Customer complaint filed', v: 'Jordan M. · Aug 18' },
            { l: 'Citation', v: 'CASL §6 — no consent' },
            { l: 'Compliance score', v: loc.casl + '%' },
          ]
        : [
            { l: 'Recipients opted-out', v: '8' },
            { l: 'Resolution', v: 'Warning · Jun 2' },
            { l: 'Compliance score', v: loc.casl + '%' },
          ],
      priors: isWhyte ? 1 : 0,
    });
  }

  if (loc.flags.includes('PIPEDA')) {
    story.flags.push({
      code: 'PIPEDA',
      severity: 'high',
      headline: `${100 - loc.pipeda}% of customer records missing required consent stamps`,
      detail: 'Customer files lack documented purpose-of-collection stamps. Affects how this location can use the data for any subsequent marketing or analytics.',
      proof: [
        { l: 'Records flagged', v: Math.round((100 - loc.pipeda) * 2.4) + ' files' },
        { l: 'Compliance score', v: loc.pipeda + '%' },
        { l: 'Audit threshold', v: '95% min' },
      ],
      priors: 0,
    });
  }

  // CEO exposure
  story.exposure = [
      { tag: 'LEGAL', label: 'Maximum CASL penalty', value: '$10M', sub: 'per non-consenting recipient', tone: 'red' },
    { tag: 'BRAND', label: 'Brand-damage risk', value: 'Network-wide', sub: 'one shop\'s flag attaches to the parent brand', tone: 'red' },
    { tag: 'FINANCIAL', label: 'Royalty exposure', value: '~$' + (loc.royalties * 78).toLocaleString(), sub: 'projected QTD if performance holds', tone: 'amber' },
    { tag: 'CONTRACT', label: 'Violations vs. cap', value: ((isWhyte ? 1 : 0) + 1) + ' of 3', sub: '§12.3 escalation triggers at 3 in 90d', tone: 'red' },
  ];

  // Trajectory — last 10 weeks from sparkline; show is-it-getting-worse
  const peak = Math.max(...loc.trend);
  const now = loc.trend[loc.trend.length - 1];
  story.trajectory = {
    peak,
    now,
    pctFromPeak: Math.round((now - peak) / peak * 100),
    sssYoY: loc.sssYoY,
  };

  // Franchisee record
  story.franchisee = {
    name: isWhyte ? 'K. Manderson' : isRichmondHill ? 'G. Latham' : 'Franchisee',
    tenure: 'Operating since ' + loc.opened,
    priors: isWhyte ? 1 : 0,
    royaltyHistory: 'Current · on-time 11 of last 12',
    clause: '§12.3 — escalation after 3 violations in 90 days',
  };

  return story;
};

// ── HQ Actions — contextual to compliance level ───────────────────────
const buildActions = (loc, onOpenCasl) => {
  if (loc.flags && loc.flags.length) {
    return [
      {
        tag: 'CONTAIN',  tagColor: '#B91C1C', tagBg: '#FEE2E2',
        icon: 'shield',
        title: 'Halt outbound marketing at this location',
        why: 'Every additional non-compliant send is a new $10M-per-recipient exposure. Stops bleeding while you investigate.',
        impact: 'Affects 1 shop · pauses Email, SMS, in-app',
        cta: 'Halt now', primary: true,
      },
      {
        tag: 'ESCALATE', tagColor: '#92400E', tagBg: '#FEF3C7',
        icon: 'flag',
        title: 'Issue formal Notice of Default',
        why: 'Per franchise agreement §12.3. Starts the 30-day cure period. Required on the record if termination ever becomes necessary.',
        impact: 'Letter routed to franchisee + HQ legal',
        cta: 'Draft notice',
      },
      {
        tag: 'INVESTIGATE', tagColor: '#3730A3', tagBg: '#E0E7FF',
        icon: 'pin',
        title: 'Dispatch Field Ops to site (48h)',
        why: 'Senior Brand Health rep on the ground assesses whether the shop is recoverable or systemic. Boots-on-ground beats a phone call.',
        impact: 'GTA East regional · ~1 day on-site',
        cta: 'Assign Field Ops',
      },
      {
        tag: 'FINANCIAL', tagColor: '#B45309', tagBg: '#FEF3C7',
        icon: 'dollar',
        title: 'Hold royalty pending compliance review',
        why: 'Avoid accepting royalty payments on operations under regulatory review. Funds escrowed, not lost.',
        impact: 'Holds ~$' + Math.round(loc.royalties * 78 / 1000) + 'k/quarter projected receivable',
        cta: 'Place on hold',
      },
      {
        tag: 'DIALOGUE', tagColor: '#0369A1', tagBg: '#E0F2FE',
        icon: 'message',
        title: 'Schedule franchisee review call',
        why: 'Documented conversation with the operator and HQ legal. Sets explicit remediation expectations on the record.',
        impact: '60 min · franchisee + HQ legal + you',
        cta: 'Schedule',
      },
      {
        tag: 'TERMINAL', tagColor: '#7F1D1D', tagBg: '#FECACA',
        icon: 'warn',
        title: 'Open termination review',
        why: 'Last-resort contract review. Protects the parent brand from a systemic offender. Available now — 2nd violation pending.',
        impact: 'Legal + compliance · 14-day timeline',
        cta: 'Open review', terminal: true,
      },
    ];
  }
  // Healthy shop — different toolkit entirely
  return [
    {
      tag: 'RECOGNIZE', tagColor: '#166534', tagBg: '#DCFCE7',
      icon: 'star',
      title: 'Send franchisee recognition',
      why: 'Top-quartile performer. Public recognition reinforces brand standards for the rest of the network.',
      impact: 'Featured in next network briefing',
      cta: 'Send recognition',
    },
    {
      tag: 'RECORD', tagColor: '#3730A3', tagBg: '#E0E7FF',
      icon: 'building',
      title: 'View franchisee record',
      why: 'Full file: agreement, royalty history, prior incidents, growth requests.',
      impact: '',
      cta: 'Open record',
    },
    {
      tag: 'GROWTH', tagColor: '#0369A1', tagBg: '#E0F2FE',
      icon: 'trend',
      title: 'Approve growth request',
      why: 'Operator has requested second-chair expansion. Capital under franchise agreement.',
      impact: 'Pending request · est. ROI 18mo',
      cta: 'Review request',
    },
    {
      tag: 'REVIEW', tagColor: '#475569', tagBg: '#F1F5F9',
      icon: 'message',
      title: 'Schedule operations review',
      why: 'Routine check-in. Recommended quarterly for shops above quota.',
      impact: 'Optional · 30 min',
      cta: 'Schedule',
    },
  ];
};

// ── Sub-components ─────────────────────────────────────────────────────

const ExposureBanner = ({ story, loc, onOpenCasl }) => (
  <section className="exposure-banner">
    <div className="exposure-banner-head">
      <div className="exposure-banner-tag">
        <Icon name="warn" size={14} stroke={2.4}/> WHY THIS LOCATION IS FLAGGED
      </div>
      <div className="exposure-banner-title">
        {loc.flags.length > 1
          ? `${loc.name.replace('Hawke & Blade — ','')} is one violation away from contract escalation`
          : `${loc.name.replace('Hawke & Blade — ','')} has a live regulatory flag`}
      </div>
      <div className="exposure-banner-sub">
        {loc.flags.length} active flag{loc.flags.length > 1 ? 's' : ''} · 1 prior violation in last 90 days · franchise agreement §12.3 escalates at 3
      </div>
    </div>

    <div className="exposure-evidence">
      {story.flags.map((f, i) => (
        <div key={f.code} className={'evidence-card evidence-' + f.severity}>
          <div className="evidence-card-head">
            <span className="evidence-flag">{f.code}</span>
            <span className="evidence-sev">{f.severity === 'critical' ? 'CRITICAL' : 'HIGH'}</span>
          </div>
          <div className="evidence-headline">{f.headline}</div>
          <p className="evidence-detail">{f.detail}</p>
          <div className="evidence-proof">
            {f.proof.map(p => (
              <div key={p.l} className="evidence-proof-row">
                <span>{p.l}</span><b>{p.v}</b>
              </div>
            ))}
          </div>
          {f.code === 'CASL' && onOpenCasl && (
            <button className="evidence-cta" data-live="1" onClick={onOpenCasl}>
              Open full incident file <Icon name="arrow-right" size={12} stroke={2.4}/>
            </button>
          )}
        </div>
      ))}
    </div>

    <div className="exposure-strip">
      <div className="exposure-strip-l">HQ EXPOSURE</div>
      {story.exposure.map(e => (
        <div key={e.tag} className={'exposure-stat exposure-' + e.tone}>
          <div className="exposure-stat-tag">{e.tag}</div>
          <div className="exposure-stat-v">{e.value}</div>
          <div className="exposure-stat-l">{e.label}</div>
          <div className="exposure-stat-sub">{e.sub}</div>
        </div>
      ))}
    </div>
  </section>
);

const TrajectoryCard = ({ loc, story }) => {
  const max = Math.max(...loc.trend);
  const min = Math.min(...loc.trend);
  const flagged = !!(loc.flags && loc.flags.length);
  return (
    <section className="detail-block">
      <h3>
        Performance trajectory
        <span className={'traj-pill ' + (story && story.trajectory.pctFromPeak < -15 ? 'is-bad' : story && story.trajectory.pctFromPeak < 0 ? 'is-mid' : 'is-good')}>
          {story ? (story.trajectory.pctFromPeak >= 0 ? '+' : '') + story.trajectory.pctFromPeak + '% from peak' : 'Stable'}
        </span>
      </h3>
      <div className="traj-meta">
        Weekly revenue index · last 10 weeks · {flagged ? 'declining alongside flag history' : 'tracking with network'}
      </div>
      <div className="traj-chart">
        {loc.trend.map((v, i) => {
          const h = ((v - min * 0.85) / (max - min * 0.85)) * 100;
          const isLast2 = i >= loc.trend.length - 2;
          return (
            <div key={i} className="traj-col">
              <div className="traj-bar-wrap">
                <div
                  className={'traj-bar' + (flagged && isLast2 ? ' is-bad' : '') + (flagged && !isLast2 && i >= loc.trend.length - 4 ? ' is-warn' : '')}
                  style={{ height: Math.max(8, h) + '%' }}
                />
              </div>
              <div className="traj-week">W-{loc.trend.length - i - 1}</div>
            </div>
          );
        })}
      </div>
      <div className="traj-callout">
        {flagged ? (
          <><Icon name="warn" size={13} stroke={2.2}/> Revenue, NPS and utilization have all declined for 4 consecutive weeks. Pattern matches franchisee-distress signature.</>
        ) : (
          <><Icon name="check" size={13} stroke={2.2}/> Performance steady or improving across all tracked indicators.</>
        )}
      </div>
    </section>
  );
};

const FranchiseeCard = ({ loc, story }) => (
  <section className="detail-block">
    <h3>Franchisee accountability</h3>
    <div className="franchisee-row">
      <div className="franchisee-avatar">{(story.franchisee.name.match(/\b\w/g) || []).join('').slice(0, 2)}</div>
      <div className="franchisee-body">
        <div className="franchisee-name">{story.franchisee.name}</div>
        <div className="franchisee-meta">{story.franchisee.tenure} · {loc.type === 'franchised' ? 'Franchisee' : 'Corporate GM'}</div>
      </div>
      <button className="ghost-btn small">Open record <Icon name="arrow-right" size={11} stroke={2.2}/></button>
    </div>
    <div className="franchisee-grid">
      <div className="franchisee-stat">
        <div className="franchisee-stat-l">Prior violations (90d)</div>
        <div className={'franchisee-stat-v ' + (story.franchisee.priors > 0 ? 'is-bad' : '')}>{story.franchisee.priors}</div>
      </div>
      <div className="franchisee-stat">
        <div className="franchisee-stat-l">Royalty payments</div>
        <div className="franchisee-stat-v">{story.franchisee.royaltyHistory}</div>
      </div>
      <div className="franchisee-stat franchisee-stat-wide">
        <div className="franchisee-stat-l">Active agreement clause</div>
        <div className="franchisee-stat-v">{story.franchisee.clause}</div>
      </div>
    </div>
  </section>
);

const HqActions = ({ actions }) => (
  <section className="detail-block detail-block-wide ceo-actions-block">
    <h3>
      HQ actions
      <span className="ceo-actions-note">levers available to HQ on this location</span>
    </h3>
    <div className="ceo-actions">
      {actions.map(a => (
        <article key={a.title} className={'ceo-action' + (a.primary ? ' is-primary' : '') + (a.terminal ? ' is-terminal' : '')}>
          <div className="ceo-action-tag-row">
            <span className="ceo-action-tag" style={{ background: a.tagBg, color: a.tagColor }}>
              <Icon name={a.icon} size={11} stroke={2.4}/> {a.tag}
            </span>
          </div>
          <div className="ceo-action-title">{a.title}</div>
          <p className="ceo-action-why">{a.why}</p>
          {a.impact && <div className="ceo-action-impact">{a.impact}</div>}
          <button className={'ceo-action-cta' + (a.primary ? ' is-primary' : '') + (a.terminal ? ' is-terminal' : '')}>
            {a.cta} <Icon name="arrow-right" size={12} stroke={2.4}/>
          </button>
        </article>
      ))}
    </div>
  </section>
);

// ── Location Health Score (gap #2) ─────────────────────────────────────
// Explainable score: weighted sum of revenue, utilization, NPS, compliance,
// retention, supply, and marketing participation. So franchisees can see
// exactly why their shop is flagged.
const HEALTH_WEIGHTS = [
  { key: 'revenue',    label: 'Revenue trend',         weight: 20, info: 'YoY same-shop sales for shops open >12 months. Below network avg subtracts.' },
  { key: 'utilization',label: 'Utilization',           weight: 20, info: 'Booked chair-hours ÷ available. Target 75%.' },
  { key: 'nps',        label: 'NPS / reputation',      weight: 15, info: 'Net Promoter Score from post-visit surveys + Google rating delta.' },
  { key: 'compliance', label: 'Compliance',            weight: 15, info: 'Brand standards audit + CASL/PIPEDA. Each open flag subtracts.' },
  { key: 'retention',  label: 'Customer retention',    weight: 15, info: '% of clients revisiting within 90 days.' },
  { key: 'supply',     label: 'Supply / inventory',    weight: 10, info: 'Stockout shops, vendor SLA breaches.' },
  { key: 'marketing',  label: 'Marketing participation', weight: 5, info: '% of HQ brand campaigns the shop has activated.' },
];

const scoreFor = (loc) => {
  // Each axis scores 0–100. Combined score is the weighted sum.
  const sssBaseline = 8; // network YoY norm
  const axis = {
    revenue:     Math.max(0, Math.min(100, 50 + (loc.sssYoY - sssBaseline) * 6)),
    utilization: Math.max(0, Math.min(100, (loc.utilization / 75) * 100)),
    nps:         Math.max(0, Math.min(100, ((loc.nps + 20) / 90) * 100)),
    compliance:  Math.max(0, Math.min(100, ((loc.casl + loc.pipeda) / 2) - (loc.flags ? loc.flags.length * 18 : 0))),
    retention:   Math.max(0, Math.min(100, loc.retention * 1.4)),
    supply:      loc.id === 'HB009' ? 64 : loc.id === 'HB010' ? 48 : 86,
    marketing:   Math.max(0, Math.min(100, loc.engagement)),
  };
  const total = HEALTH_WEIGHTS.reduce((acc, w) => acc + axis[w.key] * (w.weight / 100), 0);
  return { axis, total: Math.round(total) };
};

const HealthScoreCard = ({ loc }) => {
  const { axis, total } = scoreFor(loc);
  const band = total >= 75 ? 'good' : total >= 60 ? 'mid' : 'bad';
  const bandLabel = band === 'good' ? 'HEALTHY' : band === 'mid' ? 'WATCH' : 'AT RISK';
  return (
    <section className="detail-block detail-block-wide">
      <h3>
        Location Health Score
        <span className={'traj-pill is-' + band}>{total} · {bandLabel}</span>
        <InfoDot tip="Weighted score across revenue, utilization, NPS, compliance, retention, supply, and marketing. Franchisees see the same formula so the rating is defensible."/>
      </h3>
      <div className="health-grid">
        {HEALTH_WEIGHTS.map(w => {
          const v = axis[w.key];
          const cls = v >= 75 ? 'good' : v >= 60 ? 'mid' : 'bad';
          const contrib = v * (w.weight / 100);
          return (
            <div key={w.key} className="health-row">
              <div className="health-row-l">
                <div className="health-name">{w.label}<InfoDot tip={w.info}/></div>
                <div className="health-weight">weight {w.weight}%</div>
              </div>
              <div className="health-bar"><div className={'health-fill health-fill-' + cls} style={{ width: v + '%' }}/></div>
              <div className="health-score">{Math.round(v)}</div>
              <div className="health-contrib">contributes {contrib.toFixed(1)}</div>
            </div>
          );
        })}
      </div>
      <div className="health-foot">
        <span>{total} = sum of weighted contributions</span>
        <button className="ghost-btn small">View formula history</button>
      </div>
    </section>
  );
};

// ── HQ audit trail (gap #4) ────────────────────────────────────────────
// Every consequential HQ action on this shop is logged with actor + timestamp.
const buildAuditTrail = (loc) => {
  const isWhyte = loc.id === 'HB010';
  const isRichmondHill = loc.id === 'HB009';
  if (isWhyte) {
    return [
      { ts: '2026-09-13 09:42', actor: 'M. Diaz · Compliance Officer',  action: 'Opened CASL incident INC-2026-031 for HQ review' },
      { ts: '2026-09-13 09:38', actor: 'System · AI',                    action: 'AI insight surfaced: pattern matches prior high-risk franchisee cases — REQUIRES HUMAN REVIEW' },
      { ts: '2026-09-12 16:11', actor: 'Y. Templeton · Managing Director',     action: 'Held royalty pending compliance review (~$2,128 escrowed)' },
      { ts: '2026-09-12 14:02', actor: 'M. Diaz · Compliance Officer',  action: 'Issued Notice of Default · §12.3 (cure period: 30d)' },
      { ts: '2026-09-12 11:30', actor: 'Y. Templeton · Managing Director',     action: 'Halted outbound marketing at this location' },
      { ts: '2026-09-11 17:08', actor: 'A. Fontaine · Field Ops',          action: 'Dispatched to site · arrival Sep 13 14:00 EDT' },
      { ts: '2026-09-08 10:14', actor: 'System · Brand Standards',     action: 'Audit score dropped to 62% (threshold 70%) — auto-flagged' },
      { ts: '2026-09-06 14:39', actor: 'System · CASL Monitor',         action: 'Flagged as CASL violation · severity raised to CRITICAL' },
    ];
  }
  if (isRichmondHill) {
    return [
      { ts: '2026-09-12 13:24', actor: 'M. Diaz · Compliance Officer',  action: 'CASL audit reviewed — 8 opted-out recipients · WARNING issued' },
      { ts: '2026-09-11 09:00', actor: 'System · AI',                    action: 'AI insight surfaced: NPS coaching plan recommended for A. Russell' },
      { ts: '2026-09-10 15:42', actor: 'G. Latham · Operator',           action: 'Acknowledged Q3 brand standards broadcast' },
      { ts: '2026-08-28 11:08', actor: 'Y. Templeton · Managing Director',     action: 'Approved vendor switch request — Maple Grooming Imports' },
    ];
  }
  return [
    { ts: '2026-09-13 08:11', actor: 'Y. Templeton · Managing Director', action: 'Reviewed monthly performance · no actions taken' },
    { ts: '2026-09-08 10:14', actor: 'System · Brand Standards', action: 'Audit complete · 92% — passed' },
    { ts: '2026-08-30 16:42', actor: 'M. Diaz · Compliance Officer', action: 'Sent quarterly franchisee recognition' },
  ];
};

const AuditTrailCard = ({ loc }) => {
  const trail = buildAuditTrail(loc);
  return (
    <section className="detail-block detail-block-wide">
      <h3>
        HQ audit trail
        <InfoDot tip="Every consequential HQ action on this shop, with actor and timestamp. Same audit format used everywhere — campaigns, POs, brand price changes, RBAC, AI dismissals."/>
        <span className="card-head-count">{trail.length} entries · last 30d</span>
      </h3>
      <ul className="casl-audit">
        {trail.map((e, i) => (
          <li key={i}>
            <span className="casl-audit-ts">{e.ts}</span>
            <span className="casl-audit-actor">{e.actor}</span>
            <span className="casl-audit-action">{e.action}</span>
          </li>
        ))}
      </ul>
      <div className="health-foot">
        <span>Retained 7 years per Franchise Settings</span>
        <button className="ghost-btn small">Export PDF</button>
      </div>
    </section>
  );
};

// ── Main ───────────────────────────────────────────────────────────────

const LocationDetail = ({ location, onClose, onOpenCasl }) => {
  if (!location) return null;
  const detail = window.FRANCHISE_DATA.buildLocationDetail(location);
  const story = buildFlagStory(location);
  const actions = buildActions(location);
  const isFlagged = !!(location.flags && location.flags.length);

  return (
    <div className="overlay" onClick={onClose}>
      <aside className={'detail' + (isFlagged ? ' is-flagged' : '')} onClick={e => e.stopPropagation()}>
        <header className="detail-head">
          <div className="detail-head-left">
            <button className="detail-back" onClick={onClose}>
              <Icon name="arrow-right" size={14} stroke={2.2} style={{ transform:'rotate(180deg)' }}/> Back to network
            </button>
            <div className="detail-title-row">
              <div className={'detail-shop-mark' + (isFlagged ? ' is-flagged' : '')}>{location.id.slice(-2)}</div>
              <div>
                <h2>{location.name} <TypeBadge type={location.type}/></h2>
                <div className="detail-shop-meta">
                  <span><Icon name="pin" size={12} stroke={2}/> {location.city}</span>
                  <span><Icon name="calendar" size={12} stroke={2}/> Opened {location.opened}</span>
                  <Compliance level={location.compliance} flags={location.flags}/>
                </div>
              </div>
            </div>
          </div>
          <div className="detail-head-right">
            <button className="ghost-btn">Operator view <Icon name="arrow-right" size={13} stroke={2.2}/></button>
            <button className="icon-btn" onClick={onClose}><Icon name="close" size={16}/></button>
          </div>
        </header>

        <div className="detail-body">
          {isFlagged && story && <ExposureBanner story={story} loc={location} onOpenCasl={onOpenCasl}/>}

          <div className="detail-kpis">
            <div className="dk"><div className="dk-l">Revenue (T)</div><div className="dk-v">{fmtMoneyFull(location.revenue)}</div><div className={'dk-sub ' + (location.sssYoY < 0 ? 'is-bad' : 'is-good')}>{location.sssYoY >= 0 ? '+' : ''}{location.sssYoY}% YoY</div></div>
            <div className="dk"><div className="dk-l">Utilization</div><div className="dk-v">{location.utilization.toFixed(1)}%</div>
              <div className="util-bar" style={{ marginTop: 6 }}><div className="util-fill" style={{ width: location.utilization + '%', background: location.utilization >= 75 ? '#16A34A' : location.utilization >= 60 ? '#F59E0B' : '#EF4444' }}/></div>
            </div>
            <div className="dk"><div className="dk-l">Rev / Hour</div><div className="dk-v">${location.revPerHour.toFixed(2)}</div></div>
            <div className="dk"><div className="dk-l">NPS</div><div className="dk-v">{location.nps}</div></div>
            <div className="dk"><div className="dk-l">Royalty (proj.)</div><div className="dk-v">{location.type === 'corporate' ? '—' : '~' + fmtMoneyFull(location.royalties)}</div><div className="dk-sub">{location.type === 'corporate' ? 'Corporate' : 'Net sales × rate'}</div></div>
            <div className="dk"><div className="dk-l">Loyalty %</div><div className="dk-v">{location.loyaltyPenetration}%</div></div>
          </div>

          <div className="detail-grid">
            <HealthScoreCard loc={location}/>
            <TrajectoryCard loc={location} story={story}/>
            {isFlagged && story
              ? <FranchiseeCard loc={location} story={story}/>
              : (
                <section className="detail-block">
                  <h3>Compliance snapshot</h3>
                  <div className="comp-rows">
                    <div className="comp-row"><div className="comp-row-l">CASL · email consent</div><div className="comp-row-v"><span className={'nps-chip ' + (location.casl >= 95 ? 'good' : location.casl >= 85 ? 'mid' : 'low')}>{location.casl}%</span></div></div>
                    <div className="comp-row"><div className="comp-row-l">PIPEDA · privacy</div><div className="comp-row-v"><span className={'nps-chip ' + (location.pipeda >= 95 ? 'good' : location.pipeda >= 85 ? 'mid' : 'low')}>{location.pipeda}%</span></div></div>
                    <div className="comp-row"><div className="comp-row-l">Retention (90d)</div><div className="comp-row-v">{location.retention}%</div></div>
                    <div className="comp-row"><div className="comp-row-l">Referrals</div><div className="comp-row-v">{location.referrals}</div></div>
                    <div className="comp-row"><div className="comp-row-l">Loyalty redemptions</div><div className="comp-row-v">{location.redemptions}</div></div>
                    <div className="comp-row"><div className="comp-row-l">Engagement</div><div className="comp-row-v">{location.engagement}%</div></div>
                  </div>
                </section>
              )
            }

            <HqActions actions={actions}/>
            <AuditTrailCard loc={location}/>
          </div>
        </div>
      </aside>
    </div>
  );
};

Object.assign(window, { LocationDetail });
