/* =============================================================
   page-overview.jsx - Executive overview
   Uses the roadmap visual system while keeping the production data flow:
   management view for project progress, operational drilldowns, ISP,
   risks, and summaries.
   ============================================================= */

const EXECUTIVE_BASELINE = {
  totalLinks: 576,
  totalSites: 337,
  batchLabel: "Batch 1",
  underlay: {
    linksOrdered: 274,
    sitesOrdered: 165,
    linksDelivered: 44,
    linksActivated: 32,
    sitesOn1Net: 16,
    avgProgress: 14,
    batch2LinksPlanned: 250,
    batch2Target: "June / July 2026 onwards",
    meRows: [
      { label: "Central & Eastern Eu.", linksOrdered: 17, linksActivated: 6, sitesOrdered: 9, sitesActivated: 2 },
      { label: "Asia Pacific", linksOrdered: 19, linksActivated: 8, sitesOrdered: 16, sitesActivated: 4 },
      { label: "BrandCos", linksOrdered: 83, linksActivated: 4, sitesOrdered: 41, sitesActivated: 1 },
      { label: "India", linksOrdered: 45, linksActivated: 3, sitesOrdered: 28, sitesActivated: 1 },
      { label: "Southern Europe", linksOrdered: 48, linksActivated: 3, sitesOrdered: 17, sitesActivated: 0 },
      { label: "China", linksOrdered: 25, linksActivated: 3, sitesOrdered: 11, sitesActivated: 2 },
      { label: "Africa & ME", linksOrdered: 14, linksActivated: 3, sitesOrdered: 6, sitesActivated: 1 },
      { label: "Northern Europe", linksOrdered: 13, linksActivated: 2, sitesOrdered: 5, sitesActivated: 0 },
      { label: "Latin Americas", linksOrdered: 4, linksActivated: 0, sitesOrdered: 2, sitesActivated: 0 },
      { label: "NORAM", linksOrdered: 7, linksActivated: 0, sitesOrdered: 5, sitesActivated: 0 },
    ],
    forecast: [
      { label: "EMEA", links: 18, sites: 14 },
      { label: "APAC", links: 20, sites: 15 },
      { label: "Americas", links: 4, sites: 2 },
    ],
    risks: [
      { me: "LATAM", issue: "BAN in progress", status: "Delayed", tone: "risk" },
      { me: "India", issue: "LSA & BAN in progress", status: "Delayed", tone: "risk" },
      { me: "C. & E. Eu.", issue: "Site classification change in Armenia", status: "In review", tone: "warn" },
      { me: "China", issue: "Additional link", status: "Open", tone: "neutral" },
    ],
  },
  overlay: {
    sitesOrdered: 106,
    hwDelivered: 24,
    migrated: 9,
    securityCompliance: 9,
    notEligible: 6,
    poReleased: 101,
    quoteProvided: 122,
    meRows: [
      { label: "Africa & Middle East", totalSites: 21, sitesOrdered: 2, hwDelivered: 0, migrated: 0 },
      { label: "Asia Pacific", totalSites: 37, sitesOrdered: 17, hwDelivered: 1, migrated: 0 },
      { label: "BrandCos", totalSites: 59, sitesOrdered: 9, hwDelivered: 3, migrated: 1 },
      { label: "Central & Eastern Europe", totalSites: 32, sitesOrdered: 0, hwDelivered: 0, migrated: 0 },
      { label: "China", totalSites: 34, sitesOrdered: 0, hwDelivered: 0, migrated: 0 },
      { label: "India", totalSites: 65, sitesOrdered: 48, hwDelivered: 0, migrated: 0 },
      { label: "Latin Americas", totalSites: 16, sitesOrdered: 1, hwDelivered: 1, migrated: 0 },
      { label: "North Americas", totalSites: 20, sitesOrdered: 5, hwDelivered: 1, migrated: 1 },
      { label: "Northern Europe", totalSites: 20, sitesOrdered: 4, hwDelivered: 1, migrated: 0 },
      { label: "Southern Europe", totalSites: 53, sitesOrdered: 20, hwDelivered: 17, migrated: 7 },
    ],
    currentStatus: [
      { label: "Migration", text: "Focus is being placed on Iberia sites. Complex sites such as Malaga, Manzanares, and Madrid are targeted for completion by mid-June." },
      { label: "HW Ordering", text: "Finalize ordering for India sites and start ordering on Scotland area Gold sites." },
    ],
    risks: [
      { me: "India", issue: "Delivery lead time", status: "2.5 months instead of 1", tone: "risk" },
      { me: "India", issue: "LSA update due to price increase for invoicing", status: "Delayed", tone: "risk" },
      { me: "C. & E. Eu.", issue: "Site classification change in Armenia", status: "In review", tone: "warn" },
      { me: "China", issue: "Additional link", status: "Open", tone: "neutral" },
    ],
  },
};

const EXEC_COLORS = {
  accent: "var(--accent)",
  ok: "var(--ok)",
  info: "var(--info)",
  warn: "var(--warn)",
  risk: "var(--risk)",
  neutral: "var(--ink-300)",
};

function positiveSegments(rows) {
  return rows.filter((r) => r.value > 0);
}

function remaining(total, ...values) {
  return Math.max(0, total - values.reduce((a, b) => a + (b || 0), 0));
}

function fmt(n) {
  return Number(n || 0).toLocaleString();
}

function pct(value, total) {
  return Math.round(((value || 0) / Math.max(1, total || 0)) * 100);
}

function OvPill({ tone = "neutral", children, soft = false, mono = false }) {
  return <span className={`mc-pill tone-${tone}${soft ? " soft" : ""}${mono ? " mono" : ""}`}>{children}</span>;
}

function MetricSource({ type = "tracker", label }) {
  const text = label || {
    baseline: "Project scope",
    effective: "Project tracker",
    tracker: "Project tracker",
    calculated: "Calculated KPI",
    static: "Static context",
  }[type] || "Metric source";
  return <span className={`ov-source-chip type-${type}`}>{text}</span>;
}

function ScopeRing({ size = 220, total, breakdown, totalLabel }) {
  const stroke = 14;
  const gap = 4;
  const radii = breakdown.map((_, i) => size / 2 - 8 - i * (stroke + gap));
  const cx = size / 2;
  const cy = size / 2;
  const arc = (r, frac) => {
    const angle = frac * Math.PI * 2 - Math.PI / 2;
    const x = cx + r * Math.cos(angle);
    const y = cy + r * Math.sin(angle);
    const large = frac > 0.5 ? 1 : 0;
    if (frac >= 1) return `M ${cx} ${cy - r} A ${r} ${r} 0 1 1 ${cx - 0.01} ${cy - r} A ${r} ${r} 0 1 1 ${cx} ${cy - r}`;
    return `M ${cx} ${cy - r} A ${r} ${r} 0 ${large} 1 ${x} ${y}`;
  };

  return (
    <div className="ov-ring-wrap">
      <svg width={size} height={size} className="ov-ring-svg">
        {radii.map((r, i) => (
          <circle key={`t-${i}`} cx={cx} cy={cy} r={r} fill="none" stroke="var(--bg-sunken)" strokeWidth={stroke}/>
        ))}
        {breakdown.map((b, i) => {
          const frac = Math.max(0.001, Math.min(1, (b.value || 0) / Math.max(1, total)));
          return (
            <path key={`a-${i}`} d={arc(radii[i], frac)} fill="none" stroke={b.color} strokeWidth={stroke} strokeLinecap="round"/>
          );
        })}
        <text x={cx} y={cy - 4} textAnchor="middle" className="ov-ring-num">{fmt(total)}</text>
        <text x={cx} y={cy + 16} textAnchor="middle" className="ov-ring-lbl">{totalLabel}</text>
      </svg>
      <div className="ov-ring-legend">
        {breakdown.map((b) => (
          <div key={b.key} className="ov-ring-row">
            <span className="ov-ring-sw" style={{ background: b.color }}/>
            <span className="ov-ring-row-l">{b.label}</span>
            <span className="ov-ring-row-n mono">{fmt(b.value)}</span>
            <span className="ov-ring-row-p mono">{pct(b.value, total)}%</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function ScopeHeader({ base, projectHighlights, linkBreakdown, siteBreakdown }) {
  return (
    <div className="ov-scope">
      <div className="ov-scope-h">
        <div className="mc-eyebrow">Project scope - {base.batchLabel}</div>
      </div>
      <div className="ov-scope-grid">
        <div className="ov-scope-rings">
          <ScopeRing total={base.totalLinks} breakdown={linkBreakdown} totalLabel="total links"/>
          <ScopeRing total={base.totalSites} breakdown={siteBreakdown} totalLabel="total sites"/>
        </div>
        <div className="ov-scope-audit">
          <div className="ov-scope-audit-h">Project advancement</div>
          <div className="ov-source-row">
            <MetricSource type="baseline" label={`${fmt(base.totalLinks)} links / ${fmt(base.totalSites)} sites in scope`}/>
            <MetricSource type="tracker" label={`${base.batchLabel} migration view`}/>
          </div>
          <div className="ov-scope-audit-grid">
            {projectHighlights.map((x) => (
              <div key={x.label} className="ov-audit-tile">
                <div className="ov-audit-value mono">{x.value}</div>
                <div className="ov-audit-label">{x.label}</div>
                <div className="ov-audit-note">{x.note}</div>
              </div>
            ))}
          </div>
          <div className="ov-scope-note">
            This view is for management: link ordering, delivery, activation, hardware readiness, migration progress, and visible risks.
          </div>
        </div>
      </div>
    </div>
  );
}

function KpiSegmented({ label, value, hint, segments, primary, popTitle, popHint, side = "bottom", source = "effective", sourceLabel }) {
  const [activeSegment, setActiveSegment] = React.useState(null);
  const cardRef = React.useRef(null);
  const total = (segments || []).reduce((a, s) => a + (s.value || 0), 0) || 1;
  const safeSegments = segments || [];
  const sourceText = sourceLabel || (
    source === "baseline" ? "Program baseline"
    : source === "calculated" ? "Calculated"
    : source === "workbook" ? "Project tracker"
    : null);
  const adjustPopover = React.useCallback(() => {
    const card = cardRef.current;
    if (!card) return;
    const pop = card.querySelector(".kpi-pop");
    if (!pop) return;
    card.style.setProperty("--kpi-pop-shift", "0px");
    const rect = pop.getBoundingClientRect();
    const vw = window.innerWidth || document.documentElement.clientWidth || 0;
    const margin = 12;
    let shift = 0;
    if (rect.right > vw - margin) shift = (vw - margin) - rect.right;
    else if (rect.left < margin) shift = margin - rect.left;
    if (shift) card.style.setProperty("--kpi-pop-shift", shift + "px");
  }, []);
  return (
    <div ref={cardRef} className={"ov-kpi kpi kpi-hover" + (primary ? " primary accent" : "")} data-source={source} onMouseEnter={adjustPopover} onFocus={adjustPopover}>
      <div className="ov-kpi-topline">
        <div className="ov-kpi-label">{label}</div>
        {sourceText && <span className={`ov-source-chip type-${source}`}>{sourceText}</span>}
      </div>
      <div className="ov-kpi-value">{value}</div>
      <div className="ov-kpi-hint">{hint}</div>
      <div className="ov-kpi-bar">
        {safeSegments.map((s, i) => (
          <div key={i} className="ov-kpi-seg" style={{ flex: Math.max(0.001, (s.value || 0) / total), background: s.color }} title={`${s.label}: ${fmt(s.value)}`}/>
        ))}
      </div>
      <div className="ov-kpi-legend">
        {safeSegments.map((s, i) => (
          <div key={i} className="ov-kpi-leg">
            <span className="ov-kpi-leg-sw" style={{ background: s.color }}/>
            <span className="ov-kpi-leg-l">{s.label}</span>
            <span className="mono ov-kpi-leg-n">{fmt(s.value)}</span>
          </div>
        ))}
      </div>
      <div className="kpi-hint-row"><span className="kpi-hint-dot"/> Hover for breakdown</div>
      {safeSegments.length > 0 && (
        <div className={"kpi-pop kpi-pop-" + side}>
          <div className="kpi-pop-h">
            <div className="kpi-pop-title">{popTitle || label}</div>
            {popHint && <div className="kpi-pop-hint">{popHint}</div>}
          </div>
          <div className="kpi-pop-body">
            <PieChart segments={safeSegments} size={132} activeIndex={activeSegment} onActiveIndex={setActiveSegment}/>
            <ul className="kpi-pop-legend">
              {safeSegments.map((s, i) => {
                const p = pct(s.value, total);
                return (
                  <li key={i} className={activeSegment === i ? "active" : ""} onMouseEnter={() => setActiveSegment(i)} onMouseLeave={() => setActiveSegment(null)}>
                    <span className="dot" style={{ background: s.color }}/>
                    <span className="lbl">{s.label}</span>
                    <span className="num"><span className="mono">{fmt(s.value)}</span> <span className="pct">{p}%</span></span>
                  </li>
                );
              })}
            </ul>
          </div>
        </div>
      )}
    </div>
  );
}

function MeConstellation({ rows, scoreFn, xLabel = "Order rate", yLabel = "Activation rate" }) {
  const points = React.useMemo(() => rows
    .map((r) => ({ ...r, ...scoreFn(r) }))
    .sort((a, b) => (b.y - a.y) || (b.scope - a.scope) || String(a.label).localeCompare(String(b.label))), [rows, scoreFn]);
  const maxR = Math.max(...points.map((p) => p.scope), 1);
  const tierFor = (value) => {
    if (value >= 0.3) return { tone: "ok", label: "30%+ activated", color: "var(--ok)" };
    if (value >= 0.1) return { tone: "info", label: "10-30%", color: "var(--info)" };
    return { tone: "warn", label: "Below 10%", color: "var(--warn)" };
  };

  return (
    <div className="ov-constellation ov-me-performance">
      <div className="ov-me-performance-head">
        <span>Management Entity</span>
        <span>Scope</span>
        <span>{xLabel}</span>
        <span>{yLabel}</span>
      </div>
      <div className="ov-me-performance-rows">
        {points.map((p) => {
          const tier = tierFor(p.y || 0);
          const orderPct = Math.max(0, Math.min(100, Math.round((p.x || 0) * 100)));
          const activationPct = Math.max(0, Math.min(100, Math.round((p.y || 0) * 100)));
          const bubble = Math.round(14 + Math.sqrt((p.scope || 0) / maxR) * 24);
          return (
            <div
              key={p.label}
              className={`ov-me-performance-row tone-${tier.tone}`}
              title={`${p.label} - ${p.scope} links ordered, ${p.linksActivated || 0} activated (${activationPct}%)`}
            >
              <div className="ov-me-name">
                <span className="ov-me-name-main">{p.label}</span>
                <span className={`ov-me-tier tone-${tier.tone}`}>{tier.label}</span>
              </div>
              <div className="ov-me-scope">
                <span className="ov-me-scope-bubble" style={{ width: bubble, height: bubble, background: tier.color }}/>
                <span className="mono ov-me-scope-num">{fmt(p.scope)}</span>
              </div>
              <div className="ov-me-meter" aria-label={`${xLabel} ${orderPct}%`}>
                <div className="ov-me-meter-top">
                  <span>{xLabel}</span>
                  <strong className="mono">{orderPct}%</strong>
                </div>
                <div className="ov-me-track">
                  <span className="ov-me-fill order" style={{ width: `${orderPct}%` }}/>
                </div>
              </div>
              <div className="ov-me-meter" aria-label={`${yLabel} ${activationPct}%`}>
                <div className="ov-me-meter-top">
                  <span>{yLabel}</span>
                  <strong className="mono">{activationPct}%</strong>
                </div>
                <div className="ov-me-track">
                  <span className="ov-me-fill" style={{ width: `${activationPct}%`, background: tier.color }}/>
                </div>
              </div>
            </div>
          );
        })}
      </div>
      <div className="ov-const-legend">
        <span className="mc-cell-meta">Scope circle = ordered links - bars use the same order and activation calculations</span>
        <span className="mc-flex"/>
        <span className="ov-const-leg"><span className="mc-dot tone-ok"/>30%+ activated</span>
        <span className="ov-const-leg"><span className="mc-dot tone-info"/>10-30%</span>
        <span className="ov-const-leg"><span className="mc-dot tone-warn"/>Below 10%</span>
      </div>
    </div>
  );
}

function MeStackedBars({ rows, series, totalKey }) {
  const max = Math.max(...rows.map((r) => Math.max(...series.map((s) => r[s.key] || 0))), 1);
  const sorted = [...rows].sort((a, b) => (b[totalKey || series[0].key] || 0) - (a[totalKey || series[0].key] || 0));
  return (
    <div className="ov-mebars">
      <div className="ov-mebars-legend">
        {series.map((s) => (
          <span key={s.key} className="ov-mebars-leg"><span className="ov-mebars-leg-sw" style={{ background: s.color }}/>{s.label}</span>
        ))}
      </div>
      <div className="ov-mebars-rows">
        {sorted.map((r) => (
          <div key={r.label} className="ov-mebars-row">
            <div className="ov-mebars-label" title={r.label}>{r.label}</div>
            <div className="ov-mebars-track">
              {series.map((s) => {
                const value = r[s.key] || 0;
                return (
                  <div key={s.key} className="ov-mebars-bar-wrap">
                    <div className="ov-mebars-bar" style={{ width: `${(value / max) * 100}%`, background: s.color }}/>
                    <span className="ov-mebars-bar-n mono">{value}</span>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function FlowFunnel({ steps, tone = "accent" }) {
  const max = Math.max(...steps.map((s) => s.value), 1);
  const colors = tone === "overlay"
    ? ["var(--accent)", "var(--info)", "var(--warn)", "var(--ok)"]
    : ["var(--accent)", "var(--info)", "var(--ok)", "var(--ink-400)"];
  return (
    <div className="ov-funnel">
      {steps.map((s, i) => {
        const width = (s.value / max) * 100;
        const prev = i > 0 ? steps[i - 1].value : null;
        const conversion = prev ? pct(s.value, prev) : null;
        return (
          <div key={s.label} className="ov-funnel-step">
            <div className="ov-funnel-row">
              <div className="ov-funnel-label"><span className="mono ov-funnel-idx">{String(i + 1).padStart(2, "0")}</span><span>{s.label}</span></div>
              <span className="ov-funnel-val">{fmt(s.value)}</span>
            </div>
            <div className="ov-funnel-bar-wrap">
              <div className="ov-funnel-bar-track"><div className="ov-funnel-bar" style={{ width: `${width}%`, background: colors[i % colors.length], height: "100%" }}/></div>
              <span className="mono ov-funnel-conv">{conversion !== null ? <><strong>{conversion}%</strong> conv.</> : <>&nbsp;</>}</span>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function IspDonut({ data }) {
  const size = 180;
  const stroke = 24;
  const cx = size / 2;
  const cy = size / 2;
  const r = (size - stroke) / 2;
  const total = data.reduce((a, d) => a + d.value, 0) || 1;
  const circ = 2 * Math.PI * r;
  let offset = 0;
  const colors = { terminated: "var(--ok)", inProgress: "var(--info)", onHold: "var(--risk)", pending: "var(--ink-300)" };

  return (
    <div className="ov-donut-wrap">
      <svg width={size} height={size} className="ov-donut-svg">
        <g transform={`rotate(-90 ${cx} ${cy})`}>
          {data.map((d) => {
            const len = (d.value / total) * circ;
            const dashoffset = -offset;
            offset += len;
            return <circle key={d.key} cx={cx} cy={cy} r={r} fill="none" stroke={colors[d.key]} strokeWidth={stroke} strokeDasharray={`${len} ${circ}`} strokeDashoffset={dashoffset}/>;
          })}
        </g>
        <text x={cx} y={cy - 2} textAnchor="middle" className="ov-donut-num">{fmt(total)}</text>
        <text x={cx} y={cy + 16} textAnchor="middle" className="ov-donut-lbl">circuits</text>
      </svg>
      <div className="ov-donut-legend">
        {data.map((d) => (
          <div key={d.key} className="ov-donut-row">
            <span className="ov-donut-sw" style={{ background: colors[d.key] }}/>
            <span className="ov-donut-l">{d.label}</span>
            <span className="mono ov-donut-n">{fmt(d.value)}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function RiskList({ rows, dense = false }) {
  return (
    <div className={"ov-risks" + (dense ? " dense" : "")}>
      {rows.map((r, i) => (
        <div key={i} className="ov-risk-row">
          <div className="ov-risk-me mono">{r.me}</div>
          <div className="ov-risk-issue">{r.issue}</div>
          <OvPill tone={r.tone || "neutral"} soft>{r.status || "Open"}</OvPill>
        </div>
      ))}
    </div>
  );
}

function ForecastStrip({ items }) {
  const max = Math.max(...items.flatMap((i) => [i.links, i.sites]), 1);
  return (
    <div className="ov-forecast">
      {items.map((it) => (
        <div key={it.label} className="ov-forecast-card">
          <div className="ov-forecast-region">{it.label}</div>
          <div className="ov-forecast-grid">
            <div className="ov-forecast-metric">
              <div className="ov-forecast-num mono">+{it.links}</div>
              <div className="ov-forecast-l">links</div>
              <div className="ov-forecast-bar"><div style={{ width: `${(it.links / max) * 100}%`, background: "var(--accent)" }}/></div>
            </div>
            <div className="ov-forecast-metric">
              <div className="ov-forecast-num mono">+{it.sites}</div>
              <div className="ov-forecast-l">sites</div>
              <div className="ov-forecast-bar"><div style={{ width: `${(it.sites / max) * 100}%`, background: "var(--info)" }}/></div>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

function TopCountriesBar({ rows }) {
  const max = Math.max(...rows.map((r) => r.links), 1);
  const sorted = [...rows].sort((a, b) => b.links - a.links);
  return (
    <div className="ov-countries">
      {sorted.map((r) => (
        <div key={r.country} className="ov-country-row">
          <div className="ov-country-name">{r.country}</div>
          <div className="ov-country-bar-wrap">
            <div className="ov-country-bar-track">
              <div className="ov-country-bar-fill" style={{ width: `${(r.links / max) * 100}%` }}/>
              {r.atRisk > 0 && <div className="ov-country-bar-risk" style={{ width: `${(r.atRisk / max) * 100}%` }}/>}
            </div>
            <span className="mono ov-country-n">{fmt(r.links)}</span>
            {r.atRisk > 0 && <span className="mono ov-country-risk-n">- {r.atRisk} at risk</span>}
          </div>
        </div>
      ))}
    </div>
  );
}

function LatestRisks({ rows, onNav }) {
  return (
    <div className="ov-latest-risks">
      {rows.length === 0 ? (
        <Empty title="No active risks recorded."/>
      ) : rows.map((r, i) => {
        const tone = r.riskLevel === "High" ? "risk" : r.riskLevel === "Medium" ? "warn" : "info";
        return (
          <div key={i} className="ov-latest-risk-row">
            <div className="ov-latest-risk-head">
              <span className="ov-latest-risk-name">{r.siteName || r.linkRef}</span>
              <OvPill tone={tone} soft>{r.riskLevel || "Risk"}</OvPill>
            </div>
            <div className="ov-latest-risk-meta">
              <span className="mono">{r.linkRef || "-"}</span>
              <span className="mc-cell-meta">-</span>
              <span className="mc-cell-meta">{r.country || "-"}</span>
            </div>
            <div className="ov-latest-risk-text">{r.blocker || r.nextSteps || "No detail recorded"}</div>
          </div>
        );
      })}
      {rows.length > 0 && (
        <button className="btn ghost ov-view-all" onClick={() => onNav && onNav("risk")}>View all</button>
      )}
    </div>
  );
}

function OvSection({ kind, title, subtitle, children, tone = "accent", metricSource = null }) {
  return (
    <section className={`ov-section tone-${tone}`}>
      <div className="ov-section-h">
        <div>
          <div className="mc-eyebrow">{kind}</div>
          <h2 className="ov-section-title">{title}</h2>
          <p className="ov-section-sub">{subtitle}</p>
        </div>
        {metricSource}
      </div>
      {children}
    </section>
  );
}

// T021: One-glance executive headline. Synthesizes the four dimensions an exec
// needs without opening engineering modules: advancement (complete + migrated),
// migration readiness (ready for next migration), and blockers.
function ExecutiveHeadline({ band }) {
  const totalSites = band.totalSites || 0;
  const complete = band.complete || 0;
  const overlayMigrated = band.overlayMigrated || 0;
  const blocked = band.blocked || 0;
  const ready = band.sitesReadyForMigration || 0;
  const readyDenom = band.trackedReadinessSites || 0;
  const narrative = totalSites > 0
    ? `${complete} site${complete === 1 ? "" : "s"} fully complete (${band.pctComplete || 0}%). ${overlayMigrated} migrated to overlay, ${ready}${readyDenom ? ` of ${readyDenom}` : ""} ready for the next migration window, ${blocked} blocked${blocked > 0 ? " and needing intervention" : ""}.`
    : "Awaiting tracker data for this batch.";
  const tiles = [
    {
      tone: "ok",
      label: "Sites complete",
      value: fmt(complete),
      note: totalSites ? `${band.pctComplete || 0}% of ${fmt(totalSites)} tracked sites` : "no tracked sites yet",
      sub: "End-to-end finished",
    },
    {
      tone: "accent",
      label: "Sites migrated",
      value: fmt(overlayMigrated),
      note: totalSites ? `${band.pctOverlayMigrated || 0}% migrated to overlay` : "no tracked sites yet",
      sub: "Overlay live",
    },
    {
      tone: "info",
      label: "Sites ready next migration",
      value: readyDenom ? `${fmt(ready)} / ${fmt(readyDenom)}` : fmt(ready),
      note: readyDenom ? `${band.pctSitesReadyForMigration || 0}% with change gates ready` : "readiness data pending",
      sub: "Pre-change gates done",
    },
    {
      tone: blocked > 0 ? "risk" : "neutral",
      label: "Sites blocked",
      value: fmt(blocked),
      note: blocked > 0 ? "Action needed" : "Path clear",
      sub: "Lifecycle phase = blocked",
    },
  ];
  return (
    <div className="ov-headline" role="region" aria-label="Executive headline">
      <div className="ov-headline-narrative">
        <span className="ov-headline-eyebrow">At a glance</span>
        <span className="ov-headline-text">{narrative}</span>
      </div>
      <div className="ov-headline-grid">
        {tiles.map((t) => (
          <div key={t.label} className={`ov-headline-tile tone-${t.tone}`}>
            <div className="ov-headline-label">{t.label}</div>
            <div className="ov-headline-value mono">{t.value}</div>
            <div className="ov-headline-note">{t.note}</div>
            <div className="ov-headline-sub">{t.sub}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function PageOverview({ model, onNav, onOpenInPM }) {
  const { underlay, overlay, summary, kpi } = model;
  const base = EXECUTIVE_BASELINE;
  // PROP-10: ME rows derived from the project tracker model. Fall back to baseline if helper missing or empty
  // (e.g. brand-new instance with no data yet).
  const derivedMeRows = React.useMemo(() => (PRData.meAggregateFromModel ? PRData.meAggregateFromModel(model) : []), [model]);
  const u = React.useMemo(() => ({
    ...base.underlay,
    meRows: derivedMeRows.length ? derivedMeRows.map((r) => ({
      label: r.label,
      linksOrdered: r.linksOrdered,
      linksActivated: r.linksActivated,
      sitesOrdered: r.sitesOrdered,
      sitesActivated: r.sitesActivated,
    })) : base.underlay.meRows,
  }), [derivedMeRows]);
  const o = React.useMemo(() => ({
    ...base.overlay,
    meRows: derivedMeRows.length ? derivedMeRows.map((r) => ({
      label: r.label,
      totalSites: r.totalSites,
      sitesOrdered: r.hwOrdered,
      hwDelivered: r.hwDelivered,
      migrated: r.migrated,
    })) : base.overlay.meRows,
  }), [derivedMeRows]);
  const totalSites = base.totalSites;
  const totalLinks = base.totalLinks;

  const projectHighlights = [
    { label: "Links ordered", value: `${fmt(u.linksOrdered)} / ${fmt(totalLinks)}`, note: "underlay scope launched" },
    { label: "Links delivered", value: fmt(u.linksDelivered), note: "ready for activation path" },
    { label: "Activated on 1NET", value: fmt(u.linksActivated), note: "new underlay live" },
    { label: "Sites migrated", value: fmt(o.migrated), note: "overlay migration completed" },
  ];

  const linkBreakdown = [
    { key: "ordered", value: u.linksOrdered, color: EXEC_COLORS.accent, label: "Links ordered" },
    { key: "delivered", value: u.linksDelivered, color: EXEC_COLORS.info, label: "Delivered" },
    { key: "activated", value: u.linksActivated, color: EXEC_COLORS.ok, label: "Activated on 1NET" },
  ];
  const siteBreakdown = [
    { key: "ulOrdered", value: u.sitesOrdered, color: EXEC_COLORS.accent, label: "Sites ordered (UL)" },
    { key: "hw", value: o.hwDelivered, color: EXEC_COLORS.info, label: "HW delivered" },
    { key: "migrated", value: o.migrated, color: EXEC_COLORS.ok, label: "Migrated" },
  ];

  const underlaySegments = {
    ordered: positiveSegments([
      { label: "Ordered", value: u.linksOrdered, color: EXEC_COLORS.accent },
      { label: "Cancelled", value: kpi.cancelledLinks || 0, color: EXEC_COLORS.risk },
      { label: "Remaining", value: remaining(totalLinks, u.linksOrdered, kpi.cancelledLinks || 0), color: "var(--ink-200)" },
    ]),
    delivered: positiveSegments([
      { label: "Activated", value: u.linksActivated, color: EXEC_COLORS.ok },
      { label: "Delivered pending activation", value: Math.max(0, u.linksDelivered - u.linksActivated), color: EXEC_COLORS.info },
      { label: "Ordered pending delivery", value: Math.max(0, u.linksOrdered - u.linksDelivered), color: EXEC_COLORS.warn },
      { label: "Not yet ordered", value: remaining(totalLinks, u.linksOrdered), color: "var(--ink-200)" },
    ]),
    sites: positiveSegments([
      { label: "On 1NET", value: u.sitesOn1Net, color: EXEC_COLORS.ok },
      { label: "Ordered pending E2E", value: Math.max(0, u.sitesOrdered - u.sitesOn1Net), color: EXEC_COLORS.info },
      { label: "Remaining", value: remaining(totalSites, u.sitesOrdered), color: "var(--ink-200)" },
    ]),
    progress: kpi.breakdownOrderProgress && kpi.breakdownOrderProgress.length
      ? kpi.breakdownOrderProgress
      : positiveSegments([
        { label: "Progress", value: u.avgProgress, color: EXEC_COLORS.accent },
        { label: "Remaining", value: remaining(100, u.avgProgress), color: "var(--ink-200)" },
      ]),
  };

  const overlaySegments = {
    ordered: positiveSegments([
      { label: "Ordered", value: o.sitesOrdered, color: EXEC_COLORS.accent },
      { label: "Remaining", value: remaining(totalSites, o.sitesOrdered), color: "var(--ink-200)" },
    ]),
    hardware: positiveSegments([
      { label: "Delivered", value: o.hwDelivered, color: EXEC_COLORS.ok },
      { label: "Ordered pending HW", value: Math.max(0, o.sitesOrdered - o.hwDelivered), color: EXEC_COLORS.info },
      { label: "Remaining", value: remaining(totalSites, o.sitesOrdered), color: "var(--ink-200)" },
    ]),
    migrated: positiveSegments([
      { label: "Migrated", value: o.migrated, color: EXEC_COLORS.ok },
      { label: "Scheduled", value: kpi.sitesMigrationScheduled || 0, color: EXEC_COLORS.info },
      { label: "In planning", value: kpi.sitesMigrationInPlanning || 0, color: EXEC_COLORS.warn },
      { label: "Failed", value: kpi.sitesMigrationFailed || 0, color: EXEC_COLORS.risk },
      { label: "Not started", value: remaining(totalSites, o.migrated, kpi.sitesMigrationScheduled || 0, kpi.sitesMigrationInPlanning || 0, kpi.sitesMigrationFailed || 0), color: "var(--ink-200)" },
    ]),
    compliance: positiveSegments([
      { label: "Compliant", value: o.securityCompliance, color: EXEC_COLORS.ok },
      { label: "Remaining", value: remaining(totalSites, o.securityCompliance), color: "var(--ink-200)" },
    ]),
    eligible: positiveSegments([
      { label: "Not eligible", value: o.notEligible, color: EXEC_COLORS.risk },
      { label: "Eligible / to qualify", value: remaining(totalSites, o.notEligible), color: EXEC_COLORS.ok },
    ]),
  };

  const underlayScore = React.useCallback((row) => ({
    x: Math.min(1, (row.linksOrdered || 0) / 90),
    y: row.linksOrdered > 0 ? Math.min(1, (row.linksActivated || 0) / row.linksOrdered) : 0,
    scope: row.linksOrdered || 0,
  }), []);

  // G4: lifecycle KPI band aggregated from the shared project lifecycle model.
  const lifecycleBand = React.useMemo(() => {
    const sites = model.sites || [];
    let complete = 0, overlayMigrated = 0, overlayScheduled = 0, underlayActivated = 0, blocked = 0, withData = 0;
    let percentSum = 0, percentN = 0;
    let trackedReadinessSites = 0, sitesReadyForMigration = 0, readinessRows = 0;
    let linksBlockedBeforeChange = 0, linksInMigration = 0, postMigrationCompleteLinks = 0;
    let readinessTotal = 0, readinessReady = 0;
    for (const s of sites) {
      const lc = s && s.lifecycle;
      if (!lc) continue;
      withData++;
      if (lc.lifecyclePhase === "complete") complete++;
      if (lc.overlayMigrated) overlayMigrated++;
      if (lc.lifecyclePhase === "overlay-scheduled") overlayScheduled++;
      if (lc.underlayActivatedCount > 0 && lc.underlayActivatedCount === lc.underlayLinkCount) underlayActivated++;
      if (lc.lifecyclePhase === "blocked") blocked++;
      if (typeof lc.lifecyclePercent === "number") { percentSum += lc.lifecyclePercent; percentN++; }
      if (lc.newUnderlayRowCount > 0) {
        trackedReadinessSites++;
        readinessRows += lc.newUnderlayRowCount || 0;
        readinessTotal += lc.newUnderlayTotalReadiness || 0;
        readinessReady += lc.newUnderlayReadyReadiness || 0;
        if (lc.newUnderlayReadyForMigration) sitesReadyForMigration++;
        linksBlockedBeforeChange += lc.linksBlockedBeforeChange || 0;
        linksInMigration += lc.linksInMigration || 0;
        postMigrationCompleteLinks += lc.newUnderlayPostMigrationCompleteLinks || 0;
      }
    }
    const denom = Math.max(1, withData);
    const readinessDenom = Math.max(1, trackedReadinessSites);
    const readinessLinkDenom = Math.max(1, readinessRows);
    return {
      totalSites: withData,
      complete, overlayMigrated, overlayScheduled, underlayActivated, blocked,
      averagePercent: percentN ? Math.round(percentSum / percentN) : 0,
      pctComplete: Math.round((complete / denom) * 100),
      pctOverlayMigrated: Math.round((overlayMigrated / denom) * 100),
      pctUnderlayActivated: Math.round((underlayActivated / denom) * 100),
      trackedReadinessSites,
      sitesReadyForMigration,
      readinessRows,
      linksBlockedBeforeChange,
      linksInMigration,
      postMigrationCompleteLinks,
      pctSitesReadyForMigration: Math.round((sitesReadyForMigration / readinessDenom) * 100),
      pctPostMigrationComplete: Math.round((postMigrationCompleteLinks / readinessLinkDenom) * 100),
      readinessPercent: readinessTotal ? Math.round((readinessReady / readinessTotal) * 100) : 0,
    };
  }, [model.sites]);
  const securityReady = overlay.filter((row) => PRData.statusBucket(row.tasks?.goldenImage, "task") === "ok").length;
  const underlayFunnel = [
    { label: "Links ordered", value: kpi.orderedLinks || 0 },
    { label: "Delivered", value: kpi.deliveredLinks || 0 },
    { label: "Activated on 1NET", value: kpi.activatedLinks || 0 },
    { label: "Sites complete E2E", value: kpi.sitesCompleteE2E || 0 },
  ];
  const overlayFunnel = [
    { label: "Sites ordered", value: kpi.sitesOrdered || 0 },
    { label: "HW delivered", value: kpi.sitesHwDelivered || 0 },
    { label: "Golden Image ready", value: securityReady },
    { label: "Migrated", value: kpi.sitesMigrated || 0 },
  ];
  const ispDonut = [
    { key: "terminated", label: "Terminated", value: kpi.ispTerminated || 0 },
    { key: "inProgress", label: "In progress", value: kpi.ispInProgress || 0 },
    { key: "onHold", label: "On hold", value: kpi.ispOnHold || 0 },
    { key: "pending", label: "Pending", value: kpi.ispActive || 0 },
  ];
  const topCountries = summary.byCountry
    .filter((c) => c.key)
    .sort((a, b) => b.links - a.links)
    .slice(0, 7)
    .map((c) => ({ country: window.COUNTRY_ISO.canonical(c.key), links: c.links, atRisk: c.atRisk || 0 }));
  const recentRisks = PRData.riskRows(underlay).slice(0, 5);

  return (
    <div className="page exec-overview ov-overview">
      <div className="mc-page-h">
        <div className="mc-page-h-left">
          <div className="mc-eyebrow">Module - 01 / Overview</div>
          <h1 className="mc-page-title">Executive overview</h1>
          <p className="mc-page-sub">Management view of the 1NET migration: underlay ordering, link delivery, activation, overlay readiness, migrated sites, risks, and upcoming scope.</p>
        </div>
      </div>

      {/* T021 — Executive headline: one-glance management synthesis */}
      <ExecutiveHeadline band={lifecycleBand}/>

      {/* "What's happening" — live PM Planner monitor */}
      <WhatsHappeningPanel model={model} onOpenInPM={onOpenInPM} onNav={onNav}/>

      <ScopeHeader base={base} projectHighlights={projectHighlights} linkBreakdown={linkBreakdown} siteBreakdown={siteBreakdown}/>

      {/* G4 — Lifecycle KPI band */}
      <div className="ov-section">
        <div className="ov-section-h">
          <div>
            <div className="ov-section-kind">Lifecycle</div>
            <div className="ov-section-title">Site lifecycle progress</div>
            <div className="ov-section-sub">Per-site phase from underlay activation, New Underlay readiness, overlay migration, and ISP termination. Updates on every PM/tracker write.</div>
          </div>
          <MetricSource type="calculated" label="Project lifecycle tracker"/>
        </div>
        <div className="ov-kpi-grid">
          <KpiSegmented source="calculated" primary label="Sites complete" value={`${fmt(lifecycleBand.complete)} / ${fmt(lifecycleBand.totalSites)}`} hint={`${lifecycleBand.pctComplete}% end-to-end`} segments={positiveSegments([
            { label: "Complete", value: lifecycleBand.complete, color: EXEC_COLORS.ok },
            { label: "Other", value: Math.max(0, lifecycleBand.totalSites - lifecycleBand.complete), color: "var(--ink-200)" },
          ])} popTitle="Lifecycle: complete" popHint="Sites with underlay activated, overlay migrated, and ISP terminated."/>
          <KpiSegmented source="calculated" label="Overlay migrated" value={fmt(lifecycleBand.overlayMigrated)} hint={`${lifecycleBand.pctOverlayMigrated}% of sites`} segments={positiveSegments([
            { label: "Migrated", value: lifecycleBand.overlayMigrated, color: EXEC_COLORS.ok },
            { label: "Scheduled", value: lifecycleBand.overlayScheduled, color: EXEC_COLORS.info },
            { label: "Other", value: Math.max(0, lifecycleBand.totalSites - lifecycleBand.overlayMigrated - lifecycleBand.overlayScheduled), color: "var(--ink-200)" },
          ])} popTitle="Overlay migration" popHint="Sites whose overlay is Migrated or Scheduled."/>
          <KpiSegmented source="calculated" label="Underlay activated" value={fmt(lifecycleBand.underlayActivated)} hint={`${lifecycleBand.pctUnderlayActivated}% of sites`} segments={positiveSegments([
            { label: "Activated", value: lifecycleBand.underlayActivated, color: EXEC_COLORS.accent },
            { label: "Other", value: Math.max(0, lifecycleBand.totalSites - lifecycleBand.underlayActivated), color: "var(--ink-200)" },
          ])} popTitle="Underlay activation" popHint="Sites where every underlay link is Activated."/>
          <KpiSegmented source="calculated" label="Blocked" value={fmt(lifecycleBand.blocked)} hint={lifecycleBand.blocked > 0 ? "needs intervention" : "no blocked sites"} segments={positiveSegments([
            { label: "Blocked", value: lifecycleBand.blocked, color: EXEC_COLORS.risk },
            { label: "Healthy", value: Math.max(0, lifecycleBand.totalSites - lifecycleBand.blocked), color: "var(--ink-200)" },
          ])} popTitle="Blocked sites" popHint="Sites whose lifecycle phase is blocked."/>
          <KpiSegmented source="calculated" label="Sites ready for migration" value={`${fmt(lifecycleBand.sitesReadyForMigration)} / ${fmt(lifecycleBand.trackedReadinessSites)}`} hint={`${lifecycleBand.pctSitesReadyForMigration}% with change gates ready`} segments={positiveSegments([
            { label: "Ready", value: lifecycleBand.sitesReadyForMigration, color: EXEC_COLORS.ok },
            { label: "Remaining", value: Math.max(0, lifecycleBand.trackedReadinessSites - lifecycleBand.sitesReadyForMigration), color: "var(--ink-200)" },
          ])} popTitle="New Underlay readiness" popHint="Sites whose pre-change readiness and change-management gates are complete."/>
          <KpiSegmented source="calculated" label="Links blocked before change" value={fmt(lifecycleBand.linksBlockedBeforeChange)} hint={lifecycleBand.linksBlockedBeforeChange ? "pre-change or CAB gate blocked" : "no pre-change blockers"} segments={positiveSegments([
            { label: "Blocked", value: lifecycleBand.linksBlockedBeforeChange, color: EXEC_COLORS.risk },
            { label: "Not blocked", value: Math.max(0, lifecycleBand.readinessRows - lifecycleBand.linksBlockedBeforeChange), color: "var(--ink-200)" },
          ])} popTitle="Pre-change blockers" popHint="Blocked New Underlay readiness items before the migration window."/>
          <KpiSegmented source="calculated" label="Links in migration" value={fmt(lifecycleBand.linksInMigration)} hint="connectivity and user-test phase active" segments={positiveSegments([
            { label: "In migration", value: lifecycleBand.linksInMigration, color: EXEC_COLORS.info },
            { label: "Other", value: Math.max(0, lifecycleBand.readinessRows - lifecycleBand.linksInMigration), color: "var(--ink-200)" },
          ])} popTitle="Link migration phase" popHint="Links with Gateway, Internet, User Test, or Migration Successful work in progress."/>
          <KpiSegmented source="calculated" label="Post-migration closure" value={`${fmt(lifecycleBand.postMigrationCompleteLinks)} / ${fmt(lifecycleBand.readinessRows)}`} hint={`${lifecycleBand.pctPostMigrationComplete}% closed`} segments={positiveSegments([
            { label: "Closed", value: lifecycleBand.postMigrationCompleteLinks, color: EXEC_COLORS.ok },
            { label: "Open", value: Math.max(0, lifecycleBand.readinessRows - lifecycleBand.postMigrationCompleteLinks), color: "var(--ink-200)" },
          ])} popTitle="Post-migration closure" popHint="Rack photo, ServiceNow, Confluence, CMDB, and legacy termination steps complete."/>
        </div>
      </div>

      <OvSection
        kind="Underlay"
        title="Circuit ordering, delivery & activation"
        subtitle="Program baseline view of the Batch 1 underlay path — order, delivery, and activation gates. Live project tracker counts appear in the operations panel below."
        metricSource={<MetricSource type="baseline" label="Program baseline · Batch 1"/>}
      >
        <div className="ov-kpi-grid">
          <KpiSegmented source="baseline" primary label="Links ordered" value={`${fmt(u.linksOrdered)} / ${fmt(totalLinks)}`} hint="Approved scope baseline" segments={underlaySegments.ordered} popTitle="Underlay link order breakdown" popHint="Ordered, cancelled, and remaining link scope (program baseline)."/>
          <KpiSegmented source="baseline" label="Links delivered / activated" value={`${fmt(u.linksDelivered)} / ${fmt(u.linksActivated)}`} hint="baseline delivered vs activated" segments={underlaySegments.delivered} popTitle="Circuit delivery and activation" popHint="Delivery progress toward activation (program baseline)."/>
          <KpiSegmented source="baseline" label="Sites on 1NET" value={fmt(u.sitesOn1Net)} hint="baseline activated end-to-end" segments={underlaySegments.sites} popTitle="Underlay site activation" popHint="Sites activated against the program baseline scope."/>
          <KpiSegmented source="baseline" label="Avg deployment maturity" value={`${u.avgProgress}%`} hint="baseline progress across ordered links" segments={underlaySegments.progress} popTitle="Order progress distribution" popHint="Progress grouped by deployment maturity (program baseline)."/>
        </div>

        <div className="ov-row split">
          <div className="ov-panel">
            <div className="ov-panel-h">
              <div>
                <div className="ov-panel-title">By Management Entity</div>
              <div className="ov-panel-sub">Program baseline order rate x activation rate, sized by ordered link scope.</div>
              </div>
            </div>
            <MeConstellation rows={u.meRows} scoreFn={underlayScore}/>
          </div>
          <div className="ov-rail">
            <div className="ov-panel">
              <div className="ov-panel-h">
                <div className="ov-panel-title">Program risks & delays</div>
                <span className="mono mc-cell-meta">{u.risks.length} baseline notes</span>
              </div>
              <RiskList rows={u.risks}/>
            </div>
            <div className="ov-batch">
              <div className="ov-batch-tag mono">Program plan - incoming</div>
              <div className="ov-batch-val mono">+{fmt(u.batch2LinksPlanned)}</div>
              <div className="ov-batch-sub">links planned</div>
              <div className="ov-batch-target">Target start - <strong>{u.batch2Target}</strong></div>
            </div>
          </div>
        </div>

        <div className="ov-panel">
          <div className="ov-panel-h">
            <div>
              <div className="ov-panel-title">Program forecast</div>
              <div className="ov-panel-sub">Planning view for the next project wave.</div>
            </div>
            <OvPill tone="accent" soft mono>30D forecast</OvPill>
          </div>
          <ForecastStrip items={u.forecast}/>
        </div>
      </OvSection>

      <OvSection
        kind="Overlay"
        title="Hardware, security & migration"
        subtitle={`Program baseline view across the approved ${fmt(totalSites)}-site scope. Live project tracker counts appear in the operations panel below.`}
        tone="info"
        metricSource={<MetricSource type="baseline" label="Program baseline · Batch 1"/>}
      >
        <div className="ov-kpi-grid five">
          <KpiSegmented source="baseline" primary label="Sites ordered" value={fmt(o.sitesOrdered)} hint={`of ${fmt(totalSites)} baseline sites`} segments={overlaySegments.ordered} popTitle="Overlay site ordering" popHint="Ordered and remaining site scope (program baseline)."/>
          <KpiSegmented source="baseline" label="HW delivered" value={fmt(o.hwDelivered)} hint={`of ${fmt(totalSites)} baseline sites`} segments={overlaySegments.hardware} popTitle="Hardware delivery breakdown" popHint="Delivered hardware and remaining scope (program baseline)."/>
          <KpiSegmented source="baseline" label="Sites migrated" value={fmt(o.migrated)} hint={`of ${fmt(totalSites)} baseline sites`} segments={overlaySegments.migrated} popTitle="Overlay migration breakdown" popHint="Migrated, scheduled, in planning, failed, and not started (program baseline)."/>
          <KpiSegmented source="baseline" label="Security compliance" value={fmt(o.securityCompliance)} hint="baseline readiness gate" segments={overlaySegments.compliance} popTitle="Security readiness" popHint="Security readiness against the program baseline scope."/>
          <KpiSegmented source="baseline" label="Not eligible" value={fmt(o.notEligible)} hint="baseline prerequisites blocking" segments={overlaySegments.eligible} popTitle="Migration eligibility" popHint="Eligibility status against the program baseline scope."/>
        </div>

        <div className="ov-row split">
          <div className="ov-panel">
            <div className="ov-panel-h">
              <div>
                <div className="ov-panel-title">By Management Entity</div>
                <div className="ov-panel-sub">Program baseline total sites, ordered, HW delivered, and migrated.</div>
              </div>
            </div>
            <MeStackedBars rows={o.meRows} totalKey="totalSites" series={[
              { key: "totalSites", label: "Total sites", color: "var(--ink-300)" },
              { key: "sitesOrdered", label: "Sites ordered", color: "var(--accent)" },
              { key: "hwDelivered", label: "HW delivered", color: "var(--info)" },
              { key: "migrated", label: "Migrated", color: "var(--ok)" },
            ]}/>
          </div>
          <div className="ov-rail">
            <div className="ov-panel">
              <div className="ov-panel-h">
                <div className="ov-panel-title">Program status notes</div>
                <span className="mono mc-cell-meta">operating notes</span>
              </div>
              <div className="ov-status">
                {o.currentStatus.map((row) => (
                  <div key={row.label} className="ov-status-row">
                    <div className="ov-status-label">{row.label}</div>
                    <div className="ov-status-text">{row.text}</div>
                  </div>
                ))}
              </div>
            </div>
            <div className="ov-panel">
              <div className="ov-panel-h">
                <div className="ov-panel-title">Program risks & delays</div>
                <span className="mono mc-cell-meta">{o.risks.length} baseline notes</span>
              </div>
              <RiskList rows={o.risks} dense/>
            </div>
          </div>
        </div>
      </OvSection>

      <OvSection
        kind="Operations"
        title="Operational detail"
        subtitle="Live project tracker view — underlay, overlay, ISP, risks, and blocker follow-up. Reflects current tracker rows and ongoing edits."
        tone="neutral"
        metricSource={<MetricSource type="effective" label="Project tracker"/>}
      >
        <div className="ov-row tri">
          <div className="ov-panel">
            <div className="ov-panel-h">
              <div>
                <div className="ov-panel-title">Underlay delivery funnel</div>
                <div className="ov-panel-sub">Live underlay progress and calculated conversion.</div>
              </div>
              <MetricSource type="effective" label="Project tracker"/>
            </div>
            <FlowFunnel steps={underlayFunnel} tone="accent"/>
          </div>
          <div className="ov-panel">
            <div className="ov-panel-h">
              <div>
                <div className="ov-panel-title">Overlay readiness funnel</div>
                <div className="ov-panel-sub">Live overlay readiness and migration conversion.</div>
              </div>
              <MetricSource type="effective" label="Project tracker"/>
            </div>
            <FlowFunnel steps={overlayFunnel} tone="overlay"/>
          </div>
          <div className="ov-panel">
            <div className="ov-panel-h">
              <div>
                <div className="ov-panel-title">ISP terminations</div>
                <div className="ov-panel-sub">{fmt(kpi.ispTotal || 0)} legacy circuit records tracked.</div>
              </div>
              <MetricSource type="effective" label="Project tracker"/>
            </div>
            <IspDonut data={ispDonut}/>
          </div>
        </div>

        <div className="ov-row split">
          <div className="ov-panel">
            <div className="ov-panel-h">
              <div>
                <div className="ov-panel-title">Top countries - underlay links</div>
                <div className="ov-panel-sub">Bar shows link volume; red overlay shows at-risk subset.</div>
              </div>
            </div>
            <TopCountriesBar rows={topCountries}/>
          </div>
          <div className="ov-panel">
            <div className="ov-panel-h">
              <div>
                <div className="ov-panel-title">Latest risks & blockers</div>
                <div className="ov-panel-sub">Top active risks requiring project attention.</div>
              </div>
            </div>
            <LatestRisks rows={recentRisks} onNav={onNav}/>
          </div>
        </div>
      </OvSection>
    </div>
  );
}

/* =============================================================
   WhatsHappeningPanel — live "what's next" monitor on top of Overview.
   Driven by PMClient migration plans. Per-site cards aggregate the
   plan + the site's lifecycle + underlay link state + overlay status
   + ISP rows so management gets a single-glance read of what's
   actually moving this week / next week / next month.
   Live updates: subscribes to PMClient.subscribe.
   Deep link: each card has "Open in PM" which routes through onOpenInPM.
   ============================================================= */
function WhatsHappeningPanel({ model, onOpenInPM, onNav }) {
  const [period, setPeriod] = React.useState("this-week"); // this-week | next-week | this-month | next-month | next-4w | past-week
  const [plans, setPlans] = React.useState([]);
  const [loaded, setLoaded] = React.useState(false);

  // -- Load plans + subscribe to PM client events for live updates
  React.useEffect(() => {
    let alive = true;
    const reload = async () => {
      try {
        const list = await (window.PMClient && window.PMClient.listPlans ? window.PMClient.listPlans({}) : Promise.resolve([]));
        if (!alive) return;
        const arr = Array.isArray(list) ? list : (list && Array.isArray(list.items) ? list.items : []);
        setPlans(arr);
        setLoaded(true);
      } catch (e) {
        if (alive) { setPlans([]); setLoaded(true); }
      }
    };
    reload();
    const unsub = (window.PMClient && window.PMClient.subscribe) ? window.PMClient.subscribe(() => reload()) : null;
    return () => { alive = false; if (typeof unsub === "function") unsub(); };
  }, []);

  // -- Period boundaries (date-only, inclusive)
  function startOfDay(d) { const x = new Date(d); x.setHours(0,0,0,0); return x; }
  function addDays(d, n) { const x = new Date(d); x.setDate(x.getDate() + n); return x; }
  function startOfWeekMon(d) { const x = startOfDay(d); const dow = (x.getDay() + 6) % 7; return addDays(x, -dow); }
  function startOfMonth(d) { const x = startOfDay(d); x.setDate(1); return x; }
  function endOfMonth(d) { const x = startOfMonth(d); x.setMonth(x.getMonth() + 1); return addDays(x, -1); }
  function fmtIso(d) { return d.toISOString().slice(0,10); }
  const now = startOfDay(new Date());
  const periodWindow = React.useMemo(() => {
    if (period === "this-week")  { const a = startOfWeekMon(now); return { from: a, to: addDays(a, 6), label: "This week" }; }
    if (period === "next-week")  { const a = addDays(startOfWeekMon(now), 7); return { from: a, to: addDays(a, 6), label: "Next week" }; }
    if (period === "past-week")  { const a = addDays(startOfWeekMon(now), -7); return { from: a, to: addDays(a, 6), label: "Past week" }; }
    if (period === "this-month") return { from: startOfMonth(now), to: endOfMonth(now), label: "This month" };
    if (period === "next-month") { const a = startOfMonth(addDays(endOfMonth(now), 1)); return { from: a, to: endOfMonth(a), label: "Next month" }; }
    if (period === "next-4w")    return { from: now, to: addDays(now, 27), label: "Next 4 weeks" };
    return { from: startOfWeekMon(now), to: addDays(startOfWeekMon(now), 6), label: "This week" };
  }, [period, now.getTime()]);

  // -- Filter plans whose targetDate sits in the window
  const planInWindow = React.useCallback((p) => {
    const d = p.targetDate ? new Date(p.targetDate) : null;
    if (!d || isNaN(d)) return false;
    const ts = startOfDay(d).getTime();
    return ts >= periodWindow.from.getTime() && ts <= periodWindow.to.getTime();
  }, [periodWindow]);
  const visiblePlans = React.useMemo(() => {
    return plans
      .filter((p) => p.status !== "cancelled")
      .filter(planInWindow)
      .sort((a, b) => String(a.targetDate || "").localeCompare(String(b.targetDate || "")));
  }, [plans, planInWindow]);

  // -- Counters by plan type
  const counters = React.useMemo(() => {
    const c = { total: visiblePlans.length, underlay: 0, overlay: 0, combined: 0, isp: 0, other: 0, done: 0, blocked: 0, inProgress: 0, scheduled: 0, planned: 0 };
    for (const p of visiblePlans) {
      if (p.type === "underlay-link") c.underlay++;
      else if (p.type === "overlay-site") c.overlay++;
      else if (p.type === "combined-underlay-overlay") c.combined++;
      else if (p.type === "isp-termination") c.isp++;
      else c.other++;
      if (p.status === "done") c.done++;
      if (p.status === "blocked") c.blocked++;
      if (p.status === "in_progress") c.inProgress++;
      if (p.status === "scheduled") c.scheduled++;
      if (p.status === "planned") c.planned++;
    }
    return c;
  }, [visiblePlans]);

  // -- Per-card: aggregate the site, lifecycle, underlay links, overlay, ISP rows
  function buildCard(plan) {
    const siteCode = plan.siteCode || "";
    const siteName = plan.siteName || "";
    const lc = (() => {
      const site = (model.sites || []).find((s) =>
        (siteCode && s.siteCode === siteCode) ||
        (siteName && s.siteName && s.siteName.toLowerCase() === siteName.toLowerCase()));
      return site || null;
    })();
    const links = (() => {
      if (plan.linkRefs && plan.linkRefs.length) {
        return (model.underlay || []).filter((u) => plan.linkRefs.includes(u.linkRef));
      }
      return (model.underlay || []).filter((u) => u.siteName && siteName && u.siteName.toLowerCase() === siteName.toLowerCase());
    })();
    const overlay = (model.overlay || []).find((o) =>
      (siteCode && o.siteCode === siteCode) ||
      (siteName && o.siteName && o.siteName.toLowerCase() === siteName.toLowerCase()));
    const isps = (model.isp || []).filter((i) =>
      (siteCode && i.siteCode === siteCode) ||
      (siteName && i.siteName && i.siteName.toLowerCase() === siteName.toLowerCase()));
    return { plan, site: lc, links, overlay, isps };
  }
  const cards = React.useMemo(() => visiblePlans.map(buildCard), [visiblePlans, model]);

  const TYPE_LABEL = { "underlay-link": "Underlay", "overlay-site": "Overlay", "combined-underlay-overlay": "Underlay + Overlay", "isp-termination": "ISP termination", "other": "Other" };
  const TYPE_COLOR = { "underlay-link": "var(--info)", "overlay-site": "var(--accent)", "combined-underlay-overlay": "var(--info)", "isp-termination": "var(--warn)", "other": "var(--ink-400)" };
  const STATUS_LABEL = { planned: "Planned", scheduled: "Scheduled", in_progress: "In progress", blocked: "Blocked", done: "Done", cancelled: "Cancelled" };

  function entityForOpen(plan) {
    if (plan.type === "underlay-link") return "underlay";
    if (plan.type === "isp-termination") return "isp";
    return "overlay";
  }
  function idForOpen(plan) {
    if (plan.type === "underlay-link" || plan.type === "isp-termination") {
      return (plan.linkRefs && plan.linkRefs[0]) || plan.siteCode || plan.siteName || "";
    }
    return plan.siteCode || plan.siteName || "";
  }

  const PERIOD_OPTIONS = [
    { id: "past-week",  label: "Past week" },
    { id: "this-week",  label: "This week" },
    { id: "next-week",  label: "Next week" },
    { id: "this-month", label: "This month" },
    { id: "next-month", label: "Next month" },
    { id: "next-4w",    label: "Next 4 weeks" },
  ];

  return (
    <div className="ov-section ov-whats-happening" style={{ marginBottom: 18 }}>
      <div className="ov-section-h">
        <div>
          <div className="ov-section-kind">Live</div>
          <div className="ov-section-title">What's happening</div>
          <div className="ov-section-sub">
            Migration plans from Project Management → "What's next". Updates instantly when plans are edited, rescheduled, or closed in PM.
          </div>
        </div>
        <MetricSource type="effective" label="Project Management plans + tracker data"/>
      </div>

      <div className="flex gap-sm" style={{ alignItems: "center", flexWrap: "wrap", marginBottom: 12 }}>
        <div className="pm-window-toggle" role="tablist" style={{ display: "inline-flex", border: "1px solid var(--line)", borderRadius: 999, padding: 2, background: "var(--surface)" }}>
          {PERIOD_OPTIONS.map((p) => (
            <button key={p.id}
                    className={period === p.id ? "active" : ""}
                    onClick={() => setPeriod(p.id)}
                    style={{
                      padding: "5px 12px", borderRadius: 999, border: 0, background: period === p.id ? "var(--accent)" : "transparent",
                      color: period === p.id ? "white" : "var(--ink-700)", fontSize: 12, cursor: "pointer", fontWeight: 500,
                    }}>
              {p.label}
            </button>
          ))}
        </div>
        <span style={{ flex: 1 }}/>
        <span className="mono" style={{ fontSize: 11.5, color: "var(--ink-500)" }}>
          {fmtIso(periodWindow.from)} → {fmtIso(periodWindow.to)}
        </span>
        {onNav && (
          <button className="btn" onClick={() => onNav("pm")} style={{ fontSize: 12 }}>
            <Glyph name="ext" size={12}/> Open Planner
          </button>
        )}
      </div>

      <div className="grid g-4" style={{ gap: 10, marginBottom: 14 }}>
        <KpiSegmented primary label="Plans in window" value={fmt(counters.total)} hint={periodWindow.label} segments={[]} popTitle="Plans in this period" popHint="Distinct PM plans whose target date falls inside the chosen window."/>
        <KpiSegmented label="Underlay" value={fmt(counters.underlay + counters.combined)} hint={`${counters.combined} combined`} segments={positiveSegments([
          { label: "Underlay-only",  value: counters.underlay, color: EXEC_COLORS.info },
          { label: "Combined",       value: counters.combined, color: EXEC_COLORS.accent },
        ])} popTitle="Underlay activity" popHint="Plans activating underlay link(s), including combined plans that also activate overlay."/>
        <KpiSegmented label="Overlay" value={fmt(counters.overlay + counters.combined)} hint={`${counters.combined} combined`} segments={positiveSegments([
          { label: "Overlay-only", value: counters.overlay, color: EXEC_COLORS.accent },
          { label: "Combined",     value: counters.combined, color: EXEC_COLORS.info },
        ])} popTitle="Overlay activity" popHint="Plans migrating overlay sites, including combined plans."/>
        <KpiSegmented label="Status mix" value={fmt(counters.inProgress + counters.scheduled)} hint={counters.blocked ? `${counters.blocked} blocked` : `${counters.done} done`}
          segments={positiveSegments([
            { label: "In progress", value: counters.inProgress, color: EXEC_COLORS.accent },
            { label: "Scheduled",   value: counters.scheduled,  color: EXEC_COLORS.info },
            { label: "Planned",     value: counters.planned,    color: "var(--ink-300)" },
            { label: "Done",        value: counters.done,       color: EXEC_COLORS.ok },
            { label: "Blocked",     value: counters.blocked,    color: EXEC_COLORS.risk },
          ])}
          popTitle="Plan status mix" popHint="Status breakdown of plans inside the chosen window."/>
      </div>

      {loaded && cards.length === 0 && (
        <div className="card" style={{ padding: 20, textAlign: "center", color: "var(--ink-500)" }}>
          <Glyph name="info" size={16}/>
          <div style={{ marginTop: 6, fontSize: 13 }}>No PM plans land in <strong>{periodWindow.label}</strong>. Schedule one from <button className="btn ghost" style={{ padding: 0, color: "var(--accent)", textDecoration: "underline", border: 0, background: "transparent", cursor: "pointer", fontSize: 13 }} onClick={() => onNav && onNav("pm")}>Project Management → What's next</button>.</div>
        </div>
      )}

      {cards.length > 0 && (
        <div className="grid" style={{ gap: 10, gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))" }}>
          {cards.map(({ plan, site, links, overlay: ov, isps }) => {
            const activatedLinks = links.filter((u) => (u.circuitStatus || "").toLowerCase() === "activated").length;
            const blockerLinks = links.filter((u) => !!String(u.blocker || "").trim()).length;
            const ispsTerminated = isps.filter((i) => i.terminated === "Yes").length;
            const lcPhase = site && site.lifecycle && site.lifecycle.lifecyclePhase;
            const lcPercent = site && site.lifecycle && site.lifecycle.lifecyclePercent;
            const eligibility = site && site.lifecycle && site.lifecycle.overlayEligibilityBlockers;
            return (
              <div key={plan.id || (plan.siteCode + plan.targetDate)} className="card" style={{ padding: 12, borderLeft: `3px solid ${TYPE_COLOR[plan.type] || "var(--ink-400)"}`, display: "flex", flexDirection: "column", gap: 8 }}>
                <div style={{ display: "flex", alignItems: "flex-start", gap: 8 }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }}>
                      <span className="pm-plan-card-type" style={{ background: TYPE_COLOR[plan.type] || "var(--ink-400)", color: "white", padding: "1px 8px", borderRadius: 999, fontSize: 10.5, fontWeight: 600 }}>{TYPE_LABEL[plan.type] || plan.type}</span>
                      <span className={"pm-plan-card-status pm-status-" + plan.status} style={{ fontSize: 11 }}>{STATUS_LABEL[plan.status] || plan.status}</span>
                      <span className="mono" style={{ fontSize: 11.5, color: "var(--ink-500)" }}>{plan.targetDate}</span>
                    </div>
                    <div style={{ marginTop: 4, fontWeight: 600, fontSize: 14, color: "var(--ink-900)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                      {plan.siteCode || plan.siteName || "—"}
                    </div>
                    {plan.siteName && plan.siteCode && plan.siteName !== plan.siteCode && (
                      <div className="muted" style={{ fontSize: 11.5 }}>{plan.siteName}</div>
                    )}
                    <div className="muted" style={{ fontSize: 11, marginTop: 2 }}>
                      {plan.country || "—"}{plan.region ? " · " + plan.region : ""}
                      {plan.ownerEmail && <> · owner <span className="mono">{plan.ownerEmail.split("@")[0]}</span></>}
                    </div>
                  </div>
                  {lcPhase && window.LifecyclePhasePill && (
                    <LifecyclePhasePill phase={lcPhase} percent={lcPercent} compact/>
                  )}
                </div>

                {plan.migrationWindow && (
                  <div style={{ fontSize: 11.5, color: "var(--ink-700)" }}>
                    <Glyph name="info" size={11}/> <span style={{ marginLeft: 4 }}>{plan.migrationWindow}</span>
                  </div>
                )}

                {/* Cross-tracker snapshot for this site */}
                <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6, fontSize: 11 }}>
                  <div style={{ background: "var(--bg-elev)", padding: "5px 8px", borderRadius: 6 }}>
                    <div style={{ color: "var(--ink-500)", fontSize: 10 }}>Underlay</div>
                    <div className="mono"><strong>{activatedLinks}</strong>/{links.length}</div>
                  </div>
                  <div style={{ background: "var(--bg-elev)", padding: "5px 8px", borderRadius: 6 }}>
                    <div style={{ color: "var(--ink-500)", fontSize: 10 }}>Overlay</div>
                    <div className="mono" style={{ fontSize: 11 }}>{ov ? (ov.migrationStatus || ov.migrationSchedule || "—") : "—"}</div>
                  </div>
                  <div style={{ background: "var(--bg-elev)", padding: "5px 8px", borderRadius: 6 }}>
                    <div style={{ color: "var(--ink-500)", fontSize: 10 }}>ISP term.</div>
                    <div className="mono"><strong>{ispsTerminated}</strong>/{isps.length}</div>
                  </div>
                  <div style={{ background: "var(--bg-elev)", padding: "5px 8px", borderRadius: 6 }}>
                    <div style={{ color: "var(--ink-500)", fontSize: 10 }}>Blockers</div>
                    <div className="mono" style={{ color: blockerLinks > 0 ? "var(--risk)" : "var(--ink-700)" }}><strong>{blockerLinks}</strong></div>
                  </div>
                </div>

                {/* Link list (compact) */}
                {links.length > 0 && (
                  <div style={{ fontSize: 11, color: "var(--ink-700)" }}>
                    <span style={{ color: "var(--ink-500)", marginRight: 4 }}>Links:</span>
                    {links.slice(0, 4).map((u, i) => (
                      <span key={u.linkRef || i} className="mono" style={{ marginRight: 6 }}>
                        {u.linkRef}<span style={{ color: (u.circuitStatus || "").toLowerCase() === "activated" ? "var(--ok)" : "var(--ink-500)", marginLeft: 2 }}>·{(u.circuitStatus || "—").slice(0, 4)}</span>
                      </span>
                    ))}
                    {links.length > 4 && <span className="muted">+{links.length - 4} more</span>}
                  </div>
                )}

                {/* Eligibility blockers from lifecycle */}
                {eligibility && eligibility.length > 0 && (plan.type === "overlay-site" || plan.type === "combined-underlay-overlay") && (
                  <div className="pm-readonly-pill" style={{ fontSize: 11, padding: "4px 8px", background: "var(--warn-soft)", color: "var(--warn)" }}>
                    Overlay eligibility: {eligibility.join(", ")}
                  </div>
                )}

                {/* Plan blockers */}
                {plan.blockers && plan.blockers.length > 0 && (
                  <div className="pm-readonly-pill" style={{ fontSize: 11, padding: "4px 8px", background: "var(--risk-soft)", color: "var(--risk)" }}>
                    {plan.blockers.length} plan blocker{plan.blockers.length === 1 ? "" : "s"}: {(plan.blockers[0] && plan.blockers[0].description) || ""}
                  </div>
                )}

                <div className="flex gap-sm" style={{ marginTop: "auto", paddingTop: 4 }}>
                  <button className="btn ghost" style={{ padding: "2px 8px", fontSize: 11 }} onClick={() => onNav && onNav("pm")}>
                    Planner
                  </button>
                  {onOpenInPM && (
                    <button className="btn" style={{ padding: "2px 8px", fontSize: 11 }} onClick={() => onOpenInPM(entityForOpen(plan), idForOpen(plan))}>
                      <Glyph name="ext" size={10}/> Open in PM
                    </button>
                  )}
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

window.PageOverview = PageOverview;
window.WhatsHappeningPanel = WhatsHappeningPanel;
