/* global React, YUNO_DATA, YUNO_COMPONENTS, YUNO_PANELS, YUNO_MARCH_2026 */
(function () {
const { useState, useMemo } = React;
const D = window.YUNO_DATA;
const M = window.YUNO_MARCH_2026;
const { C, KpiCard, KpiTile, ExpandableKpiTile, RegionTile, TrendChart, FunnelChart,
  ComparisonBar, DonutChart, computeFiltered, HeroKpiCard, HeadlineCard,
  KpiDistributionChart, EnhancedKpiPieChart, ContextualFilters, HoverInsightTooltip } = window.YUNO_COMPONENTS;

// ============================================================================
// Section: Overview — executive dashboard rebuilt against the March 2026 cascade
// ============================================================================
const PILLAR_TAGS = {
  'Scalable Growth':      'P1',
  'Operational Leverage': 'P2',
  'Customer Value':       'P3',
  'People & Culture':     'P4'
};
const Q_MONTHS = {
  Q1: ['Jan', 'Feb', 'Mar'],
  Q2: ['Apr', 'May', 'Jun'],
  Q3: ['Jul', 'Aug', 'Sep'],
  Q4: ['Oct', 'Nov', 'Dec']
};
// Bump these monthly. Today (2026-05-11) we're mid-Q2; April is the last
// completed month with full actuals, so it's the default landing view.
const CURRENT_Q = 'Q2';
const CURRENT_MONTH = 'Apr';

// All 12 monthKey strings the rest of the code uses. Order matters: it's
// chronological so "previous month" lookups (e.g. variance vs prior month)
// can walk this array directly.
const MONTH_KEYS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
                    'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
const MONTH_LABEL = {
  jan: 'January', feb: 'February', mar: 'March',     apr: 'April',
  may: 'May',     jun: 'June',     jul: 'July',      aug: 'August',
  sep: 'September', oct: 'October', nov: 'November', dec: 'December'
};
// 'Apr' (UI label) → 'apr' (key). Centralized so we don't sprinkle .toLowerCase() everywhere.
const monthLabelToKey = (label) => label ? label.toLowerCase() : null;
// 'Mar' → 'Q1', etc. Inverse of Q_MONTHS.
const Q_FOR_MONTH = Object.fromEntries(
  Object.entries(Q_MONTHS).flatMap(([q, months]) => months.map(m => [m, q]))
);
const STATUS_NUM_COLOR = { good: '#10B981', warn: '#F59E0B', bad: '#DC2626', neutral: '#1E293B' };
const STATUS_PCT_BG    = { good: '#ECFDF5', warn: '#FFFBEB', bad: '#FEF2F2', neutral: '#F1F5F9' };
const STATUS_PCT_FG    = { good: '#10B981', warn: '#F59E0B', bad: '#DC2626', neutral: '#64748B' };

// User-facing label for the current-period target line.
// Quarter-end months (Mar/Jun/Sep/Dec) get the quarter label since they're
// also the quarter-end checkpoint; mid-quarter months get the month name.
function periodTargetLabel(monthKey) {
  if (!monthKey) return 'TARGET';
  const isQuarterEnd = { mar: 'Q1', jun: 'Q2', sep: 'Q3', dec: 'Q4' };
  if (isQuarterEnd[monthKey]) return `${isQuarterEnd[monthKey]} TARGET`;
  const label = MONTH_LABEL[monthKey];
  return label ? `${label.toUpperCase()} TARGET` : 'TARGET';
}

// Synthesize an 8-point sparkline by linearly interpolating from baseline → actual.
// Real time-series will replace this once we wire monthly data.
function makeSpark(actual) {
  if (actual === null || actual === undefined || typeof actual === 'string') return [];
  const base = actual === 0 ? 0 : actual * (actual > 0 ? 0.7 : 1.3);
  const n = 8;
  const step = (actual - base) / (n - 1);
  return Array.from({ length: n }, (_, i) => base + step * i);
}
function sparkPath(values, w, h) {
  if (!values || values.length < 2) return '';
  const max = Math.max(...values), min = Math.min(...values);
  const range = max - min || 1;
  return values.map((v, i) => {
    const x = (i / (values.length - 1)) * w;
    const y = h - ((v - min) / range) * h * 0.78 - h * 0.11;
    return (i === 0 ? 'M' : 'L') + x.toFixed(1) + ' ' + y.toFixed(1);
  }).join(' ');
}

// Anchor card — pillar header (top) + hero metric tile (bottom).
// `monthKey` selects the period bucket on the KPI ('mar', 'apr', ...). The card pulls
// actual/target from that bucket. When April is active and no actual exists yet we keep
// March's actual in view as "latest" and show the April target + a tiny Mar→Apr trend strip.
// Anchor (KEY METRICS) card. Color always reflects FY-target performance (same logic in
// March and April views). Card shows: priority pill + category, big colored value, FY target
// line. No period target / variance pill on the anchor card — those live on the strategic
// cards and the deep-dive panel.
// Per-anchor narrative copy — kept only as a single-line FALLBACK shown when
// the spreadsheet's `Insights` tab has nothing for the active month. The
// primary anchor-card prose is now the curated Driving / Watch / Risk rows
// from `cascade.insights` (see selectInsightsForAnchor below).
const ANCHOR_NARRATIVES = {
  arr: "ARR ramp behind FY pace; SQL pipeline running ahead but Win Rate dragging in NA. Q2 outcome depends on +2–3 deals closing on time.",
  ebitda: "Burn discipline holding through Q1. Watch revenue-per-employee as headcount expands.",
  customer_retention: "Retention decent absolute but 8 pp gap to FY 100% — recovering that needs commercial execution.",
  enps: "−10 eNPS shows poor sentiment; 40 points to climb to FY 30 in 9 months is steep. Time to Fill drifting up.",
};

// Normalize the free-form `type` column from the Insights tab into a small set
// of canonical keys the UI styles + sorts by. The owner types phrases like
// "What's driving", "What to watch", "Risk", "Note" in the spreadsheet —
// we don't want a strict enum upstream because the prose framing matters,
// but downstream we need a stable key for icons, colors, and ordering.
function normalizeInsightType(t) {
  const s = String(t || '').toLowerCase();
  if (s.includes('driv')) return 'driving';
  if (s.includes('watch')) return 'watch';
  if (s.includes('risk')) return 'risk';
  return 'note';
}

// Build the YYYY-MM month string the Insights tab uses, from a monthKey like
// 'apr'. Year defaults to 2026 (dashboard year). When we roll over to 2027,
// either bump this constant or thread the year through from cascade metadata.
const INSIGHT_YEAR = 2026;
function insightMonthString(monthKey) {
  const idx = MONTH_KEYS.indexOf(monthKey);
  if (idx < 0) return null;
  return `${INSIGHT_YEAR}-${String(idx + 1).padStart(2, '0')}`;
}

// Pick the curated insights that should appear on an anchor card for a given
// month. Pulls from `cascade.insights.byKpi[anchor.id]` (most specific) and
// `cascade.insights.byPillar[pillar.tag]` (pillar-wide). When both contain a
// Driving/Watch/Risk insight for the active month, the KPI-level one wins —
// the anchor card is about that specific KPI. Limited to one of each type
// so a single card never shows 5 driving lines.
//
// Returns: array of up to 3 insights in display order [driving, watch, risk],
// each as { type, text, source, last_updated, _norm: 'driving'|'watch'|'risk' }.
function selectInsightsForAnchor(anchor, pillar, monthKey) {
  if (!anchor || !pillar) return [];
  const cd = window.YUNO_CASCADE_DATA || {};
  const ins = cd.insights || {};
  const byKpi = ins.byKpi || {};
  const byPillar = ins.byPillar || {};
  const monthStr = insightMonthString(monthKey);
  if (!monthStr) return [];
  // KPI insights come first so they win the de-dup-by-type pass.
  const all = [
    ...(byKpi[anchor.id] || []).map(i => ({ ...i, _source: 'kpi' })),
    ...(byPillar[pillar.tag] || []).map(i => ({ ...i, _source: 'pillar' })),
  ];
  const forMonth = all.filter(i => i.month === monthStr);
  const picked = {};
  for (const i of forMonth) {
    const k = normalizeInsightType(i.type);
    if (!picked[k]) picked[k] = { ...i, _norm: k };
  }
  const order = ['driving', 'watch', 'risk', 'note'];
  return order.filter(k => picked[k]).map(k => picked[k]);
}

// Dispatch a window event that the app's palette-action listener routes into
// a sidebar open. We use the same event channel the command palette uses so
// the wiring stays in one place (app.jsx's `yuno-palette-action` handler).
function dispatchOpenPillarInsights(pillar, anchor, monthKey) {
  window.dispatchEvent(new CustomEvent('yuno-palette-action', { detail: {
    type: 'open-pillar-insights',
    pillarTag: pillar.tag,
    pillarName: pillar.name,
    anchorId: anchor && anchor.id,
    anchorName: anchor && anchor.name,
    monthKey,
  }}));
}

// preferCascadeFy(kpi) — return the FY target to display for a KPI, with the
// cascade value preferred over the hand-curated kpi.fy.target. The spreadsheet
// is the source of truth: when an owner edits FY targets (e.g. New Logo ARR
// LATAM 6.9 → 6.2 after the "(including Banking & AI)" rename), the dashboard
// should follow.
//
// Guard: if the cascade value is 50× smaller than the hand-curated value, we
// fall back to hand-curated. This catches a known unit-scale gap where a
// percentage KPI (Overall Maya Agent Impact, Factory Developer Efficiency,
// Factory - Adoption on Platform) is stored as `format: 'number'` in
// march-data.js with a target of "34", but the spreadsheet cell is a pct
// stored as 0.34 — without the guard, the dashboard would suddenly start
// showing 0.34 instead of 34. The proper fix is to flag those KPIs as
// format: 'pct' (then cascade-merge.js' existing scalePct path picks them
// up), but that changes display from "34" to "34%". Until that decision is
// made the guard keeps the existing display correct.
function preferCascadeFy(kpi) {
  const cFy = kpi && kpi.cascade && kpi.cascade.monthlyTargets && kpi.cascade.monthlyTargets.fy;
  const handFy = kpi && kpi.fy && kpi.fy.target;
  const cValid = (cFy !== null && cFy !== undefined);
  const hValid = (handFy !== null && handFy !== undefined);
  if (!cValid) return hValid ? handFy : null;
  if (!hValid) return cFy;
  // Both present — prefer cascade unless it looks like a unit mismatch.
  if (Math.abs(cFy) > 0 && Math.abs(handFy) / Math.abs(cFy) > 50) {
    return handFy;
  }
  return cFy;
}

// Map a cascade RAG ("GREEN" / "AMBER" / "RED" / null) → status key used by
// the variance pill + value-color classes ("good" / "warn" / "bad" / "neutral").
function ragToStatus(rag) {
  if (rag === 'GREEN') return 'good';
  if (rag === 'AMBER') return 'warn';
  if (rag === 'RED')   return 'bad';
  return 'neutral';
}

// Format the variance pill text for an anchor card. Different KPIs use
// different framings:
//   "currencyMM" / "currencyK"  → "−11% vs pace"   (favourable variance vs current period target)
//   "pct"                       → "8 pp gap to FY" (percentage-point gap vs FY)
//   "score" / "number"          → "40 pts gap to FY" (absolute gap vs FY)
//   actual or target missing    → "Data pending"
function formatAnchorPill(kpi, monthKey) {
  const c = kpi.cascade || {};
  const periodActual = (c.monthlyActuals && c.monthlyActuals[monthKey])
                    ?? (kpi[monthKey] && kpi[monthKey].actual);
  const periodTarget = (c.monthlyTargets && c.monthlyTargets[monthKey])
                    ?? (kpi[monthKey] && kpi[monthKey].target);
  const fy = (c.monthlyTargets && c.monthlyTargets.fy) ?? (kpi.fy && kpi.fy.target);
  const isLower = c.polarity === 'lower_is_better' || kpi.higherIsBetter === false;
  const status = ragToStatus(c.latestRag);

  if (typeof periodActual !== 'number') {
    return { text: 'Data pending', status: 'warn', arrow: 'flat' };
  }
  // pct KPI → percentage-point gap to FY
  if (kpi.format === 'pct' && typeof fy === 'number') {
    const gap = isLower ? periodActual - fy : fy - periodActual;
    const arrow = gap > 0.5 ? 'down' : gap < -0.5 ? 'up' : 'flat';
    return { text: `${Math.round(Math.abs(gap))} pp gap to FY`, status, arrow };
  }
  // score / number with FY → absolute "pts" gap
  if ((kpi.format === 'score' || kpi.format === 'number') && typeof fy === 'number') {
    const gap = isLower ? periodActual - fy : fy - periodActual;
    const arrow = gap > 0.5 ? 'down' : gap < -0.5 ? 'up' : 'flat';
    return { text: `${Math.round(Math.abs(gap))} pts gap to FY`, status, arrow };
  }
  // Default → "X% vs pace" against current period target
  if (typeof periodTarget === 'number' && periodTarget !== 0) {
    const raw = (periodActual - periodTarget) / Math.abs(periodTarget) * 100;
    const fav = isLower ? -raw : raw;
    const sign = fav >= 0 ? '+' : '−';
    const arrow = fav < -2 ? 'down' : fav > 2 ? 'up' : 'flat';
    return { text: `${sign}${Math.round(Math.abs(fav))}% vs pace`, status, arrow };
  }
  return { text: 'Data pending', status: 'warn', arrow: 'flat' };
}

// Hero search — large centered search bar shown at the very top of the
// Executive Overview. Clicking the bar (or any of the suggestion chips below)
// opens the command palette; chips also seed the query so a single click takes
// the user from idle landing → palette open at a relevant question. The
// existing small `CommandPaletteTrigger` button still lives in the top toolbar
// for other pages and as a Cmd+K reminder, so this hero search isn't the only
// way to reach the palette.
function HeroSearch() {
  const open = (query) => {
    window.dispatchEvent(new CustomEvent('yuno-palette-toggle', {
      detail: query ? { query } : undefined,
    }));
  };
  const isMac = typeof navigator !== 'undefined' && navigator.platform && navigator.platform.toUpperCase().indexOf('MAC') >= 0;
  // Chip suggestions. Half are navigation queries (KPI / team names), half are
  // insight queries (so users learn the palette answers "why" questions too).
  const chips = [
    'why is ARR declining',
    'eNPS',
    'Eng. Core',
    'EBITDA risks',
    'contract to live time',
  ];
  return (
    <div className="hero-search-wrap">
      <div
        className="hero-search"
        role="button"
        tabIndex={0}
        onClick={() => open()}
        onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(); } }}
        aria-label="Open command palette"
      >
        <span className="hero-search-icon" aria-hidden="true">⌕</span>
        <span className="hero-search-placeholder">
          Search KPIs, teams, insights…
          <span className="hero-search-placeholder-eg"> Try “why is ARR declining” or “eng core”</span>
        </span>
        <span className="hero-search-kbd" aria-hidden="true">
          <kbd>{isMac ? '⌘' : 'Ctrl'}</kbd><kbd>K</kbd>
        </span>
      </div>
      <div className="hero-search-hints">
        {chips.map(c => (
          <button key={c} type="button" className="hero-search-chip" onClick={(e) => { e.stopPropagation(); open(c); }}>
            {c}
          </button>
        ))}
      </div>
    </div>
  );
}

// Compute the pillar-status block for an anchor card: total KPI count from
// the full cascade, split into measured/awaiting and (for measured ones)
// grouped by RAG color. Falls back to STRATEGIC + HEADLINES when cascade
// global isn't loaded.
function computePillarStatus(pillar) {
  const cd = window.YUNO_CASCADE_DATA;
  if (cd && Array.isArray(cd.kpis)) {
    // Cascade pillar field is "P1: Scalable Growth" — match by tag prefix.
    const pillarTag = pillar.tag; // 'P1' | 'P2' | 'P3' | 'P4'
    const matching = cd.kpis.filter(k => typeof k.pillar === 'string' && k.pillar.startsWith(pillarTag + ':'));
    // "Measured" = any KPI that has reported an actual value for any month so
    // far this year. Previously we counted only KPIs with k.latest.month set,
    // which the ETL populates ONLY when BOTH actual and monthly target exist
    // — KPIs with quarterly-only targets (most of Op Leverage / Customer
    // Value) reported actuals but were wrongly classified "awaiting". For
    // P2 the dashboard showed "1 measured" while 53 of 55 had a March actual.
    const hasAnyActual = (k) => k.actuals && Object.values(k.actuals).some(v => typeof v === 'number');
    const measured = matching.filter(hasAnyActual);
    // RAG-rated subset: needs both actual and target → only these populate
    // the green/amber/red tiles. The remainder are "measured but unrated"
    // (we have the value but no monthly target to compare against).
    const rated = measured.filter(k => k.latest && k.latest.rag_color);
    return {
      total: matching.length,
      measured: measured.length,
      awaiting: matching.length - measured.length,
      rated: rated.length,
      unrated: measured.length - rated.length,
      good: rated.filter(k => k.latest.rag_color === 'GREEN').length,
      warn: rated.filter(k => k.latest.rag_color === 'AMBER').length,
      bad:  rated.filter(k => k.latest.rag_color === 'RED').length,
    };
  }
  // Fallback: dashboard's STRATEGIC + HEADLINES
  const strategic = (M.STRATEGIC || []).filter(k => k.pillar === pillar.name);
  const headlines = M.HEADLINES ? Object.values(M.HEADLINES).flat().filter(k => k.pillar === pillar.name) : [];
  const all = [...strategic, ...headlines];
  return { total: all.length, measured: 0, awaiting: all.length, rated: 0, unrated: 0, good: 0, warn: 0, bad: 0 };
}

// Rich anchor card — replaces the previous slim AnchorCard + AnchorDrillIn drawer.
//
//   - Pillar accent stripe at top (muted shade, 4px)
//   - Pillar tag + name header
//   - KPI name + big value (RAG color from cascade.latestRag)
//   - Trend arrow + variance pill (format depends on KPI: pp gap, pts gap, or % vs pace)
//   - FY target footer
//   - PILLAR STATUS — total / measured / awaiting + green/amber/red count tiles
//   - "What's driving · what to watch" narrative (per-anchor, editable)
//
// Click → smooth-scrolls to the matching pillar group on the same page.
function AnchorCard({ pillar, anchor, hasData, monthKey = 'mar' }) {
  if (!anchor) {
    return (
      <div className="exec-overview-anchor-rich is-empty">
        <div className={`anchor-rich-stripe stripe-${pillar.tag.toLowerCase()}`} />
        <div className="anchor-rich-summary">
          <div className="anchor-rich-pillar-tag">{pillar.tag}</div>
          <div className="anchor-rich-pillar-name">{pillar.name}</div>
          <div className="anchor-rich-divider" />
          <div className="anchor-rich-kpi-name">No anchor mapped</div>
        </div>
      </div>
    );
  }
  const c = anchor.cascade || {};
  // Resolve anchor value: prefer cascade actual for active month → hand-curated
  // anchor[monthKey].actual → walk back chronologically until we find a non-null
  // value. Mirrors MarchCard's carry-over so anchors don't go blank mid-quarter
  // (e.g. May view kept anchors showing "—" because cascade.monthlyActuals.may
  // is null for most anchors; meanwhile the strategic cards below carried over
  // April values, creating a confusing UX mismatch).
  const valueWithCarryOver = (() => {
    const direct = (c.monthlyActuals && c.monthlyActuals[monthKey])
                ?? (anchor[monthKey] && anchor[monthKey].actual);
    if (direct !== null && direct !== undefined) return { value: direct, carriedFrom: null };
    const idx = MONTH_KEYS.indexOf(monthKey);
    for (let i = idx - 1; i >= 0; i--) {
      const mk = MONTH_KEYS[i];
      const v = (c.monthlyActuals && c.monthlyActuals[mk])
             ?? (anchor[mk] && anchor[mk].actual);
      if (v !== null && v !== undefined) return { value: v, carriedFrom: mk };
    }
    return { value: null, carriedFrom: null };
  })();
  const value = valueWithCarryOver.value;
  const carriedFromMonth = valueWithCarryOver.carriedFrom;
  const fyTarget = (c.monthlyTargets && c.monthlyTargets.fy) ?? (anchor.fy && anchor.fy.target);
  const fyPending = anchor.fy && anchor.fy.pending;
  const status = ragToStatus(c.latestRag);
  const valueColor = status === 'neutral' ? '#94A3B8'
                  : status === 'good' ? '#059669'
                  : status === 'warn' ? '#D97706'
                  : '#DC2626';
  const pill = formatAnchorPill(anchor, monthKey);
  const pillarStatus = computePillarStatus(pillar);
  // Curated insights from the spreadsheet's Insights tab for the active
  // month. Up to 3 (driving / watch / risk). Empty array → render a single
  // tile-derived line as a fallback so the card never looks bare.
  const curatedInsights = selectInsightsForAnchor(anchor, pillar, monthKey);
  // Tile-derived single-line fallback. Used only when no curated insights
  // exist for the active month — gives the card a tiny prose line so the
  // bottom half isn't empty for newly-tracked pillars.
  const fallbackNarrative = (() => {
    const { good, warn, bad } = pillarStatus;
    const rated = good + warn + bad;
    if (rated === 0) return '';
    const ratio = bad / rated;
    if (ratio >= 0.5)             return 'Pillar needs attention — multiple KPIs at risk.';
    if (ratio >= 0.25)            return 'Watch the at-risk KPIs.';
    if (warn >= rated / 2)        return 'Several KPIs trending; nothing in red yet.';
    return 'Pillar broadly on track.';
  })();

  // The card has two click zones (see CSS .anchor-rich-summary / .anchor-rich-insights):
  //  - top half (header → status tiles) → smooth-scroll to the pillar's
  //    KPI group below on the same page (existing behavior).
  //  - bottom half (insights block) → open the right-side sidebar with all
  //    curated insights for the pillar + anchor KPI across all months.
  const handleScrollToPillar = (e) => {
    e.stopPropagation();
    const el = document.querySelector(`[data-pillar-group="${pillar.name}"]`);
    if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
  };
  const handleOpenInsights = (e) => {
    e.stopPropagation();
    dispatchOpenPillarInsights(pillar, anchor, monthKey);
  };

  const fyLine = (fyPending || fyTarget == null)
    ? 'FY plan refresh landing'
    : `FY target ${M.formatValue(fyTarget, anchor.format)}`;
  const monthLabelShort = (MONTH_LABEL[monthKey] || '').slice(0, 3).toUpperCase();
  const insightCount = curatedInsights.length;

  return (
    <div className="exec-overview-anchor-rich">
      <div className={`anchor-rich-stripe stripe-${pillar.tag.toLowerCase()}`} />
      {/* TOP ZONE — click → scroll to pillar group below */}
      <div className="anchor-rich-summary" onClick={handleScrollToPillar}>
        <div className="anchor-rich-pillar-tag">{pillar.tag}</div>
        <div className="anchor-rich-pillar-name">{pillar.name}</div>
        <div className="anchor-rich-divider" />

        <div className="anchor-rich-kpi-name">{anchor.name}</div>
        <div className="anchor-rich-value" style={{ color: valueColor }}>
          {hasData && typeof value === 'number' ? M.formatValue(value, anchor.format) : '—'}
        </div>
        {hasData && carriedFromMonth && carriedFromMonth !== monthKey && (
          <div style={{ fontSize: 11, color: '#94A3B8', marginTop: -2, marginBottom: 2, letterSpacing: '0.02em' }}>
            latest: {MONTH_LABEL[carriedFromMonth] || carriedFromMonth}
          </div>
        )}

        <div className="anchor-rich-trend">
          <span className={`anchor-rich-arrow arrow-${pill.arrow}`} aria-hidden="true">
            {pill.arrow === 'down' ? '↘' : pill.arrow === 'up' ? '↗' : '→'}
          </span>
          <span className={`anchor-rich-pill pill-${pill.status}`}>{pill.text}</span>
        </div>
        <div className="anchor-rich-fy">{fyLine}</div>

        <div className="anchor-rich-status-label">PILLAR STATUS</div>
        <div className="anchor-rich-status-counts">
          {pillarStatus.total} KPIs · {pillarStatus.measured} measured · {pillarStatus.awaiting} awaiting
        </div>
        <div className="anchor-rich-status-tiles">
          <div className="anchor-rich-status-tile tile-good">{pillarStatus.good}</div>
          <div className="anchor-rich-status-tile tile-warn">{pillarStatus.warn}</div>
          <div className="anchor-rich-status-tile tile-bad">{pillarStatus.bad}</div>
        </div>
      </div>

      {/* BOTTOM ZONE — click → open right-side sidebar with all pillar insights */}
      <div
        className="anchor-rich-insights"
        onClick={handleOpenInsights}
        role="button"
        tabIndex={0}
        title="View all pillar insights"
      >
        <div className="anchor-rich-insights-head">
          <span className="anchor-rich-insights-label">{monthLabelShort} HIGHLIGHTS</span>
          <span className="anchor-rich-insights-count">
            {insightCount > 0
              ? `${insightCount} insight${insightCount === 1 ? '' : 's'}`
              : 'no curated text yet'}
          </span>
        </div>
        {insightCount > 0 ? (
          <>
            {curatedInsights.map((ins, i) => {
              const labelByType = { driving: 'Driving', watch: 'Watch', risk: 'Risk', note: 'Note' };
              const iconByType  = { driving: '↗',       watch: '⚠',     risk: '⚡',  note: '·' };
              return (
                <div className={`anchor-rich-insight-row row-${ins._norm}`} key={i}>
                  <span className={`anchor-rich-insight-icon icon-${ins._norm}`} aria-hidden="true">{iconByType[ins._norm]}</span>
                  <span className="anchor-rich-insight-text">
                    <span className="anchor-rich-insight-label">{labelByType[ins._norm]} · </span>
                    {ins.text}
                  </span>
                </div>
              );
            })}
            <div className="anchor-rich-insights-more">View all pillar insights →</div>
          </>
        ) : (
          <div className="anchor-rich-insights-empty">{fallbackNarrative || 'No insights yet for this month.'}</div>
        )}
      </div>
    </div>
  );
}

function TrendAreaChart({ kpi, status, height = 150, monthKey }) {
  // Slots are positioned by month-of-year (0=Jan-1, 12=end-Dec) so x-spacing
  // honors real time. When the cascade has a Jan-1 baseline we prepend it as a
  // hollow point so the chart shows the full year-to-date arc, not just March-onward.
  // All 12 months are slotted — quarter-end months (Mar/Jun/Sep/Dec) get the
  // quarter label and pull target from kpi.q*, mid-quarter months get the
  // 3-letter month label and pull target from kpi.cascade.monthlyTargets[mk].
  const ca = (kpi.cascade && kpi.cascade.monthlyActuals) || {};
  const ct = (kpi.cascade && kpi.cascade.monthlyTargets) || {};
  const hasBaseline = kpi.cascade && typeof kpi.cascade.jan1 === 'number';
  const slots = [];
  if (hasBaseline) slots.push({ label: 'Jan-1', actual: kpi.cascade.jan1, monthIdx: 0, isBaseline: true });
  const QUARTER_LABEL = { mar: 'Q1', jun: 'Q2', sep: 'Q3', dec: 'Q4' };
  const QUARTER_TARGET_KEY = { mar: 'q1', jun: 'q2', sep: 'q3', dec: 'q4' };
  MONTH_KEYS.forEach((mk, i) => {
    const monthIdx = i + 1;
    const cascadeActual = ca[mk];
    const handActual = kpi[mk] && kpi[mk].actual;
    const actual = (cascadeActual !== undefined && cascadeActual !== null) ? cascadeActual : (handActual ?? null);
    const qKey = QUARTER_TARGET_KEY[mk];
    const handTarget = (qKey && kpi[qKey] && kpi[qKey].target) ?? (kpi[mk] && kpi[mk].target);
    const target = handTarget != null ? handTarget : (ct[mk] ?? null);
    // Drop the "JAN" label when the Jan-1 baseline label is also rendered —
    // they sit visually adjacent and overlap. The Jan-1 baseline communicates
    // the start point; the JAN end-of-month tick stays in place (just unlabelled).
    const label = (mk === 'jan' && hasBaseline) ? '' : (QUARTER_LABEL[mk] || mk.toUpperCase());
    slots.push({ key: mk, label, actual, target, monthIdx });
  });
  // Active slot — the one tied to the dashboard's current period filter.
  // When the user toggles March → April, this should move from the MAR slot
  // to the APR slot even if the Apr actual hasn't landed yet (so the chart
  // visibly tracks the active period). Falls back to the latest slot with an
  // actual if no monthKey is passed.
  const activeSlotIdx = (() => {
    if (!monthKey) return -1;
    const i = slots.findIndex(s => s.key === monthKey);
    return i;
  })();
  // Last position with a real actual.
  let lastActualIdx = -1;
  slots.forEach((s, i) => { if (s.actual !== null && s.actual !== undefined) lastActualIdx = i; });
  if (lastActualIdx < 0) return null;
  const lastActual = slots[lastActualIdx].actual;
  // Per-month slope: walk back to the previous slot with a real actual and
  // compute (lastActual − prevActual) / months between. Time-honest with the
  // non-uniform Jan-1/Mar/Apr/Q2/Q3/Q4 spacing.
  let slopePerMonth = 0;
  for (let i = lastActualIdx - 1; i >= 0; i--) {
    if (slots[i].actual !== null && slots[i].actual !== undefined) {
      const dt = slots[lastActualIdx].monthIdx - slots[i].monthIdx;
      if (dt > 0) slopePerMonth = (lastActual - slots[i].actual) / dt;
      break;
    }
  }
  const projectedValues = slots.map((s, i) =>
    i <= lastActualIdx ? s.actual : lastActual + slopePerMonth * (s.monthIdx - slots[lastActualIdx].monthIdx)
  );
  // Target pace: prefer cascade-truth (Jan-1 → FY linear pace) when present.
  // Cascade trend line: 13 points 0=Jan-1, 1=end-Jan, …, 12=end-Dec — look up
  // each slot's value via slot.monthIdx so it works whether or not Jan-1 is present.
  const fyTarget = (kpi.fy && kpi.fy.target !== undefined && kpi.fy.target !== null) ? kpi.fy.target : null;
  const startVal = slots[0].actual !== null && slots[0].actual !== undefined ? slots[0].actual : lastActual;
  const tl = kpi.cascade && kpi.cascade.trendLine;
  const tlValid = Array.isArray(tl) && tl.length === 13 && tl.every(v => typeof v === 'number');
  const idealValues = tlValid
    ? slots.map(s => tl[s.monthIdx])
    : (fyTarget !== null
        ? slots.map((_, i) => startVal + (fyTarget - startVal) * (i / (slots.length - 1)))
        : null);

  // Geometry — time-proportional x: month 0 → padX, month 12 → w − padX.
  const w = 320, h = height, padX = 14, padTop = 22, padBot = 14;
  const xs = slots.map(s => padX + (s.monthIdx / 12) * (w - 2 * padX));
  const allValues = [...projectedValues, ...(idealValues || [])].filter(v => v !== null && v !== undefined);
  const min = Math.min(...allValues);
  const max = Math.max(...allValues);
  const range = (max - min) || Math.abs(max) || 1;
  const yTop = padTop, yBot = h - padBot;
  const yFor = v => yBot - ((v - min) / range) * (yBot - yTop);

  // Path strings
  const actualPath = slots.slice(0, lastActualIdx + 1)
    .map((s, i) => `${i === 0 ? 'M' : 'L'}${xs[i].toFixed(1)},${yFor(s.actual).toFixed(1)}`).join(' ');
  const projPath = lastActualIdx < slots.length - 1
    ? slots.slice(lastActualIdx).map((_, i) =>
        `${i === 0 ? 'M' : 'L'}${xs[lastActualIdx + i].toFixed(1)},${yFor(projectedValues[lastActualIdx + i]).toFixed(1)}`
      ).join(' ')
    : '';
  // Filled area under actual + projected combined line
  const combinedY = projectedValues.map(yFor);
  const areaPath = xs.map((x, i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${combinedY[i].toFixed(1)}`).join(' ')
    + ` L${xs[xs.length - 1].toFixed(1)},${yBot.toFixed(1)} L${xs[0].toFixed(1)},${yBot.toFixed(1)} Z`;
  const idealPath = idealValues
    ? xs.map((x, i) => `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${yFor(idealValues[i]).toFixed(1)}`).join(' ')
    : '';

  const stroke = STATUS_NUM_COLOR[status] || '#3E4FE0';
  const fill = STATUS_PCT_BG[status] || '#EEF2FF';
  // Compact baseline value formatter for the inline annotation.
  const fmtBaseline = (v) => {
    if (v === null || v === undefined) return '';
    if (kpi.format === 'currencyMM') return `$${Number(v).toFixed(1)}M`;
    if (kpi.format === 'currencyK')  return `$${Math.round(v)}K`;
    if (kpi.format === 'pct')        return `${Number(v).toFixed(0)}%`;
    if (kpi.format === 'months' || kpi.format === 'days') return `${Math.round(v)}`;
    return Number.isInteger(v) ? String(v) : Number(v).toFixed(1);
  };
  return (
    <div className="kpi-trend-area">
      <svg viewBox={`0 0 ${w} ${h + 14}`} preserveAspectRatio="none" width="100%" height={h + 14} aria-hidden="true">
        {/* Filled area under combined actual+projected line — communicates "where we're heading" */}
        <path d={areaPath} fill={fill} opacity="0.5" />
        {/* Target pace: very thin dotted gray, linear to FY target */}
        {idealPath && <path d={idealPath} fill="none" stroke="#94A3B8" strokeWidth="1" strokeDasharray="2 3" strokeLinecap="round" opacity="0.7" />}
        {/* Projected: thinner dashed status color, forward from last actual */}
        {projPath && <path d={projPath} fill="none" stroke={stroke} strokeWidth="1.75" strokeDasharray="6 4" strokeLinecap="round" opacity="0.9" />}
        {/* Actual: thick solid status color */}
        {actualPath && <path d={actualPath} fill="none" stroke={stroke} strokeWidth="3" strokeLinejoin="round" strokeLinecap="round" />}
        {/* Markers on actual data points only — Jan-1 baseline is hollow.
            The active slot (per period filter) is rendered larger and emphasized
            so the chart visibly tracks March vs April vs Q2 etc. When the active
            slot has no actual yet (e.g. April pre-data-landing), a dashed empty
            circle sits at the active x-position pinned to the Target trajectory. */}
        {slots.slice(0, lastActualIdx + 1).map((s, i) => {
          const isActive = i === activeSlotIdx;
          if (s.isBaseline) {
            return (
              <g key={i}>
                <circle cx={xs[i]} cy={yFor(s.actual)} r="4" fill="#FFFFFF" stroke={stroke} strokeWidth="1.75" />
                <text x={xs[i]} y={yFor(s.actual) - 9} fontSize="9" fontWeight="700" fill="#475569" textAnchor="middle">{fmtBaseline(s.actual)}</text>
              </g>
            );
          }
          return (
            <circle key={i} cx={xs[i]} cy={yFor(s.actual)} r={isActive ? 5.5 : 4} fill={stroke} stroke="#FFFFFF" strokeWidth={isActive ? 2 : 1.5} />
          );
        })}
        {/* "Now" placeholder for the active slot when its actual hasn't landed.
            Renders as a small dashed empty circle pinned to the Target trajectory
            value at that x — signals "we're now in this month, data pending". */}
        {activeSlotIdx > -1 && activeSlotIdx > lastActualIdx && idealValues && idealValues[activeSlotIdx] != null && (
          <g>
            <circle cx={xs[activeSlotIdx]} cy={yFor(idealValues[activeSlotIdx])} r="5" fill="#FFFFFF" stroke="#9CA3AF" strokeWidth="1.5" strokeDasharray="2 2" />
          </g>
        )}
        {/* X-axis labels — only at year-anchors (Jan-1 / Jan if no baseline),
            quarter-end checkpoints (Q1/Q2/Q3/Q4), AND the active month if it
            isn't already one of those. Hides the per-month labels that
            previously crammed 12-13 strings into a 320px viewBox and read
            "all over the place". */}
        {slots.map((s, i) => {
          const isQuarterEnd = ['Q1', 'Q2', 'Q3', 'Q4'].includes(s.label);
          const isYearStart = s.isBaseline || (i === 0 && !hasBaseline && s.label);
          const isActive = i === activeSlotIdx;
          const showLabel = isQuarterEnd || isYearStart || isActive;
          if (!showLabel) return null;
          const isPast = i <= lastActualIdx;
          // Active month label uses its 3-letter abbrev (e.g. "APR") when not
          // a quarter-end; for quarter-ends we keep the "Q1/Q2/Q3/Q4" label
          // which is already informative.
          const label = isYearStart ? (s.label || 'Jan') : s.label;
          return (
            <text key={i}
              x={xs[i]}
              y={h + 11}
              fontSize="9"
              fontWeight={isActive || isPast ? '700' : '600'}
              letterSpacing="0.04em"
              fill={isActive ? '#0F172A' : (isPast ? '#0F172A' : '#94A3B8')}
              textAnchor={i === 0 ? 'start' : (i === slots.length - 1 ? 'end' : 'middle')}>
              {label}
            </text>
          );
        })}
      </svg>
      <div className="kpi-trend-legend">
        <span className="kpi-trend-legend-item">
          <span className="kpi-trend-swatch solid-thick" style={{ background: stroke }} />Actual
        </span>
        <span className="kpi-trend-legend-item">
          <span className="kpi-trend-swatch dashed-color" style={{ '--c': stroke }} />Projected
        </span>
        {idealValues && (
          <span className="kpi-trend-legend-item">
            <span className="kpi-trend-swatch dotted-gray" />Target pace
          </span>
        )}
      </div>
    </div>
  );
}

// "TREND" + "DRIVERS" rows shown below every deep-dive trend chart.
// Trend caption: projected end-of-year value vs FY target → "expected to miss / hit FY by X%".
function TrendCaption({ kpi, status }) {
  // Polarity-aware: for lower-is-better KPIs (CAC Payback, Sales Cycle, Bugs/Month,
  // Latency, Voluntary Turnover, Time to Fill, Burn Multiple, …) the verb flips.
  // E.g. CAC Payback Period projected EOY 32mo vs FY target 7mo → "miss" not "beat".
  // Prefers cascade.projectedLine when present so the caption matches the chart's
  // dashed projected line (and any downstream consumers like Slack digests).
  const c = kpi.cascade;
  const cascadeProj = c && c.projectedLine && Array.isArray(c.projectedLine.values) && c.projectedLine.values.length
    ? c.projectedLine.values[c.projectedLine.values.length - 1]
    : null;
  let projectedEoy = cascadeProj;
  if (projectedEoy === null) {
    // Fallback: project EOY using the slope of recent actuals (flat if only one).
    const marActual = kpi.mar && kpi.mar.actual;
    const aprActual = kpi.apr && kpi.apr.actual;
    if (marActual !== null && marActual !== undefined) {
      if (aprActual !== null && aprActual !== undefined) {
        const slope = aprActual - marActual;
        // 8 months from Apr to Dec
        projectedEoy = aprActual + slope * 8;
      } else {
        // No measurable growth yet — project flat
        projectedEoy = marActual;
      }
    }
  }
  const fyTarget = (c && c.monthlyTargets && c.monthlyTargets.fy) || (kpi.fy && kpi.fy.target);
  const isLowerBetter = (c && c.polarity === 'lower_is_better') || kpi.higherIsBetter === false;
  let trendStr = '—';
  // Compute a TREND-SPECIFIC status (not the latest-month RAG passed in via
  // props). A KPI can be GREEN this month (above interpolated month target)
  // while its FY projection misses badly — e.g. Payments Concierge Adoption
  // at 15.4% in March is ahead of the 13% March waypoint but on track to
  // land at 61% vs FY 80%. The trend caption colour must reflect the
  // PROJECTION, not the month. Otherwise we render "Projected to miss by
  // 23%" in green, sending mixed signals.
  let trendStatus = 'neutral';
  if (projectedEoy !== null && fyTarget !== null && fyTarget !== undefined && fyTarget !== 0) {
    const rawPct = ((projectedEoy - fyTarget) / Math.abs(fyTarget)) * 100;
    const fav = isLowerBetter ? -rawPct : rawPct;
    const absFav = Math.abs(fav);
    // Within ±1% of FY → "reach target" (not "beat by 0%" / "miss by 0%").
    if (absFav < 1) {
      trendStr = 'Projected to reach FY target';
      trendStatus = 'good';
    } else {
      const verb = fav >= 0 ? 'beat' : 'miss';
      // Wording is conditional ("would" not "will") and explicit about the
      // model ("on current pace"). The projection is a linear extrapolation
      // from Jan-1 baseline through the latest actual — it assumes the
      // current rate continues, which seasonality / Q4 push / deal-mix
      // shifts routinely violate. "~" signals imprecision. Sub-line gives
      // the model in plain English so executives don't read the integer
      // percentage as a forecast.
      trendStr = `On current pace, would ${verb} FY by ~${Math.round(absFav)}%`;
      if (fav >= 0)        trendStatus = 'good';
      else if (fav >= -30) trendStatus = 'warn';
      else                 trendStatus = 'bad';
    }
  }
  // Confidence qualifier — surfaces the model's data-thinness honestly.
  // Counts actuals available for this KPI.
  const actualMonths = c && c.monthlyActuals
    ? Object.values(c.monthlyActuals).filter(v => typeof v === 'number').length
    : 0;
  const lowData = actualMonths > 0 && actualMonths < 3;
  const modelNote = projectedEoy !== null && fyTarget !== null && fyTarget !== undefined && fyTarget !== 0
    ? `Linear extrapolation from Jan-1 baseline · ${actualMonths || 'no'} actual month${actualMonths === 1 ? '' : 's'}${lowData ? ' · limited data' : ''}`
    : null;
  return (
    <div className="kpi-trend-meta">
      <div className="kpi-trend-meta-row">
        <span className="kpi-trend-meta-label">TREND</span>
        <span className={`kpi-trend-meta-value status-${trendStatus}`}
              title="Projection is a linear extrapolation — assumes current rate continues. Not a forecast.">
          {trendStr}
        </span>
      </div>
      {modelNote && (
        <div className="kpi-trend-meta-row" style={{ marginTop: 2 }}>
          <span className="kpi-trend-meta-label" style={{ visibility: 'hidden' }}>TREND</span>
          <span style={{ fontSize: 11, color: '#94A3B8', letterSpacing: '0.02em', fontStyle: 'italic' }}>
            {modelNote}
          </span>
        </div>
      )}
      <div className="kpi-trend-meta-row">
        <span className="kpi-trend-meta-label">DRIVERS</span>
        <span className="kpi-trend-meta-value muted">— No driver notes</span>
      </div>
    </div>
  );
}

// Clock-style circular progress: arc shows "actual / target" as % of the way toward target.
// Direction-aware via higherIsBetter: % higher is closer to target either way.
// Caps the ring at 100% (over-performance shows the ring full + numeric % > 100).
function ProgressGauge({ actual, target, higherIsBetter, status, label, size = 44 }) {
  const valid = actual !== null && actual !== undefined && target !== null && target !== undefined && target !== 0;
  let pct = 0;
  if (valid) {
    const ratio = higherIsBetter ? (actual / target) : (target / actual);
    pct = Math.max(0, Math.round(ratio * 100));
  }
  const clamped = Math.min(100, pct);
  const r = (size / 2) - 4;
  const c = 2 * Math.PI * r;
  const dash = (clamped / 100) * c;
  const stroke = STATUS_NUM_COLOR[status] || '#94A3B8';
  return (
    <div className="kpi-gauge" title={label || `${pct}% of target`}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} aria-hidden="true">
        <circle cx={size/2} cy={size/2} r={r}
          fill="none" stroke="#E2E8F0" strokeWidth="3.5" />
        <circle cx={size/2} cy={size/2} r={r}
          fill="none" stroke={stroke} strokeWidth="3.5"
          strokeDasharray={`${dash} ${c - dash}`}
          strokeDashoffset={c / 4}
          strokeLinecap="round"
          transform={`scale(1, -1) translate(0, ${-size})`} />
      </svg>
      <span className="kpi-gauge-label" style={{ color: stroke }}>{valid ? `${pct}%` : '—'}</span>
    </div>
  );
}

// Strategic KPI card. No arrow icons, semantic colors, "TBA" badge for Tier 1 - TBA,
// optional info icon (kpi.tooltip), FY target footnote.
// `monthKey` selects which period bucket on the KPI to show (mar/apr/may/...).
// Data resolution: prefer `kpi[monthKey]` (hand-curated, in march-data.js for Mar/Apr),
// fall back to `kpi.cascade.monthly{Actuals,Targets}[monthKey]` (auto-generated from
// the spreadsheet, every month). Once Phase 3's full march-data.js retirement lands,
// the cascade path becomes the only source.
// Resolve a KPI's (actual, target) for a given month.
//
// Precedence: cascade > hand-curated.
//
// Why: cascade-data.js is regenerated monthly from the canonical spreadsheet
// (`KPI Cascade.xlsx`). march-data.js's hand-curated `kpi.mar` / `kpi.apr`
// fields drift the moment owners edit the spreadsheet. As of 2026-05-11 a
// drift audit found 188 stale values across ~50 KPIs (e.g. # Total SQLs Mar
// Actual showed 79 on the dashboard while the spreadsheet says 42). Flipping
// precedence makes the dashboard self-correct on every monthly refresh —
// no more parallel manual update of march-data.js.
//
// march-data.js still carries the curated structural metadata (PILLARS,
// strategic-KPI list, anchor mappings, FY targets, quarter targets, tier
// flags, format, tooltip overrides). Only the monthly bucket fields are
// superseded.
function periodBucket(kpi, monthKey) {
  if (!monthKey) return null;
  const c = kpi.cascade || {};
  const cActuals = c.monthlyActuals || {};
  const cTargets = c.monthlyTargets || {};
  const cA = cActuals[monthKey];
  const cT = cTargets[monthKey];
  if (cA != null || cT != null) {
    return { actual: cA ?? null, target: cT ?? null };
  }
  // Fall back to the hand-curated bucket when cascade has nothing
  // (KPI not in the cascade, or month not yet populated).
  return kpi[monthKey] || null;
}
const QUARTER_END_KEYS = new Set(['mar', 'jun', 'sep', 'dec']);
const Q_FOR_MONTH_KEY = {
  jan: 'q1', feb: 'q1', mar: 'q1', apr: 'q2', may: 'q2', jun: 'q2',
  jul: 'q3', aug: 'q3', sep: 'q3', oct: 'q4', nov: 'q4', dec: 'q4'
};

// Resolves a KPI's period stats for the active month. Used by drill-in cascade
// panels (L1 strategic, L1 headlines list, L2 headline detail) to avoid repeating
// the same Mar/Apr-vs-other-month branching everywhere. Quarter-end months
// (Mar/Jun/Sep/Dec) score health vs FY target; mid-quarter months score vs
// month target and surface a variance pill + "of target" line. Previous-month
// fallback walks chronologically so Apr→Mar, May→Apr, Jul→Jun, etc.
function resolveKpiForMonth(kpi, mk) {
  if (!mk || !kpi) {
    return { actual: null, target: null, localStatus: 'neutral', status: 'neutral',
             pct: null, showOfTarget: false, isQuarterEnd: false,
             isCarriedOver: false, carriedFromMonth: null };
  }
  const cur = periodBucket(kpi, mk) || {};
  // currentPeriodActual = ONLY the active month's actual (null if not reported).
  // actual = currentPeriodActual ?? carry-over (walk back chronologically).
  // The split matters for variance: comparing a carried-over Mar value against
  // an Apr target produces a misleading -8% that contradicts the chart.
  const currentPeriodActual = (cur.actual !== null && cur.actual !== undefined) ? cur.actual : null;
  let carriedFromMonth = null;
  let carriedValue = null;
  if (currentPeriodActual === null) {
    const idx = MONTH_KEYS.indexOf(mk);
    for (let i = idx - 1; i >= 0; i--) {
      const mk2 = MONTH_KEYS[i];
      const b = periodBucket(kpi, mk2);
      if (b && b.actual !== null && b.actual !== undefined) {
        carriedFromMonth = mk2;
        carriedValue = b.actual;
        break;
      }
    }
  }
  const actual = currentPeriodActual !== null ? currentPeriodActual : carriedValue;
  const isCarriedOver = currentPeriodActual === null && carriedValue !== null;
  const target = (cur.target !== null && cur.target !== undefined) ? cur.target : null;
  const isQuarterEnd = QUARTER_END_KEYS.has(mk);
  const fyTarget = kpi.fy && kpi.fy.target;
  // Status fallback ladder. Cards + drill-ins must agree on color so we
  // share this logic across MarchCard / resolveKpiForMonth / KpiDrillDown.
  //   1) Quarter-end month  → vs FY target (the quarter-end IS the FY checkpoint).
  //   2) Mid-quarter w/ monthly target → vs monthly target (immediate pace).
  //   3) Mid-quarter w/o monthly target → vs FY target (don't return 'neutral'
  //      when we have a real signal — KPIs with quarterly-only targets like
  //      Qualified Pipeline Stage Demo were rendering neutral on cards but
  //      red on drill-ins, confusing the user).
  const localStatus = isQuarterEnd
    ? M.healthVsFY(actual, fyTarget, kpi.higherIsBetter)
    : (target !== null && target !== undefined
        ? M.health(actual, target, kpi.higherIsBetter)
        : M.healthVsFY(actual, fyTarget, kpi.higherIsBetter));
  const status = window.YUNO_KPI_STATUS ? window.YUNO_KPI_STATUS(kpi, localStatus) : localStatus;
  // Don't compute variance when the displayed value was carried from a prior
  // month — would compare Mar actual vs Apr target = misleading -8%.
  const pct = (!isQuarterEnd && !isCarriedOver)
    ? M.variancePct(currentPeriodActual, target, kpi.higherIsBetter)
    : null;
  return { actual, target, localStatus, status, pct,
           showOfTarget: !isQuarterEnd && !isCarriedOver, isQuarterEnd,
           isCarriedOver, carriedFromMonth };
}

// Quarter-context row used by L1/L2 drill panels for mid-quarter months.
// "Apr → Q2 target", "May → Q2 target", "Jul → Q3 target", etc.
function resolveQuarterContext(kpi, mk, actual) {
  if (!mk || !kpi || QUARTER_END_KEYS.has(mk)) return null;
  const qKey = Q_FOR_MONTH_KEY[mk];
  const qLabel = qKey ? qKey.toUpperCase() : '';
  const qBucket = qKey && kpi[qKey];
  const qt = qBucket && qBucket.target;
  const qPending = qBucket && qBucket.pending;
  const hasQ = qt !== null && qt !== undefined;
  const qStatus = hasQ ? M.health(actual, qt, kpi.higherIsBetter) : 'neutral';
  const qPct = hasQ ? M.variancePct(actual, qt, kpi.higherIsBetter) : null;
  return { qKey, qLabel, qt, qPending, hasQ, qStatus, qPct };
}

function MarchCard({ kpi, headlines, isActive, onClick, hasData, monthKey = 'mar' }) {
  const cur = hasData ? periodBucket(kpi, monthKey) : null;
  // Previous month for variance carry-over: chronological neighbour, regardless
  // of quarter boundary (e.g. Apr → Mar, May → Apr, Jul → Jun).
  const prevIdx = monthKey ? MONTH_KEYS.indexOf(monthKey) - 1 : -1;
  const prevKey = prevIdx >= 0 ? MONTH_KEYS[prevIdx] : null;
  const prev = prevKey ? periodBucket(kpi, prevKey) : null;
  // Display value: use the current period's actual when available, otherwise carry
  // over the previous month so the card still shows a number while new-month data
  // is pending.
  const actual = (cur && cur.actual !== null && cur.actual !== undefined)
    ? cur.actual
    : (prev ? prev.actual : null);
  // Variance value: ONLY the current period's actual. Don't carry over the
  // previous month's number for an apples-to-oranges comparison against this
  // month's target. When the current month's actual is null, the pill shows a
  // grey em-dash with a "<period> pending" tooltip so the user can see the
  // chart is tracking the active period even before data lands.
  const currentPeriodActual = (cur && cur.actual !== null && cur.actual !== undefined) ? cur.actual : null;
  const target = cur ? cur.target : null;
  // Quarter-end months (Mar/Jun/Sep/Dec) score health vs FY target since they
  // also serve as quarter checkpoints. Mid-quarter months score vs month target
  // (the clock ring conveys progress through the quarter).
  // Prefer the cascade FY target — when the spreadsheet changes (e.g. New
  // Logo ARR LATAM went from 6.9 to 6.2 after the "(including Banking & AI)"
  // rename), the hand-curated value goes stale and the card would score
  // health against the wrong target. preferCascadeFy() guards against a known
  // unit-scale gap where cascade returns 0.34 for KPIs hand-curated as 34
  // (pct-formatted spreadsheet cells but format: 'number' in march-data).
  const fyTargetForHealth = preferCascadeFy(kpi);
  // Same status-fallback ladder as resolveKpiForMonth — keeps card + drill-in
  // in agreement. See its comment for details.
  const localStatus = !hasData
    ? 'neutral'
    : (QUARTER_END_KEYS.has(monthKey)
        ? M.healthVsFY(actual, fyTargetForHealth, kpi.higherIsBetter)
        : (target !== null && target !== undefined
            ? M.health(actual, target, kpi.higherIsBetter)
            : M.healthVsFY(actual, fyTargetForHealth, kpi.higherIsBetter)));
  // Prefer cascade RAG (authoritative — computed from latest-month actual vs target
  // with polarity-aware logic) over the dashboard's own local computation.
  const status = window.YUNO_KPI_STATUS ? window.YUNO_KPI_STATUS(kpi, localStatus) : localStatus;
  const pct = (hasData && !QUARTER_END_KEYS.has(monthKey) && currentPeriodActual !== null && target !== null && target !== undefined)
    ? M.variancePct(currentPeriodActual, target, kpi.higherIsBetter)
    : null;
  // "Pending" treatment for any active month that has no actual yet
  // (mid-month / future / TBD KPIs). Existing Apr-specific behavior generalized.
  const periodPending = hasData && currentPeriodActual === null && !QUARTER_END_KEYS.has(monthKey);
  const periodLabel = monthKey ? (MONTH_LABEL[monthKey] || monthKey) : '';
  const valueColor = STATUS_NUM_COLOR[status];
  const isTba = kpi.tier === 'tba';
  return (
    <div className={`march-card${isActive ? ' active' : ''}${isTba ? ' tba' : ''}`} onClick={() => onClick && onClick(kpi)}>
      <span className="march-card-tag">{PILLAR_TAGS[kpi.pillar]}</span>
      <span className="march-card-team">{kpi.team}</span>
      {isTba && <span className="march-card-tba">To be activated</span>}
      <div className="march-card-name">
        {kpi.name}
        {kpi.cascade && kpi.cascade.warning && (
          <span className="cascade-warning-badge" title={`Cascade data note: ${kpi.cascade.warning}`}>!</span>
        )}
      </div>
      <div className="march-card-value" style={{ color: valueColor }}>
        {M.formatValue(actual, kpi.format)}
      </div>
      {/* Show "<MONTH> TARGET" row for mid-quarter months. Quarter-end months
          (Mar/Jun/Sep/Dec) skip this — their FY-target footer line says it all. */}
      {!QUARTER_END_KEYS.has(monthKey) && (
        <div className="march-card-target">
          <span>{periodTargetLabel(monthKey)}: {M.formatValue(target, kpi.format)}</span>
          {pct !== null ? (
            <span className="march-card-pct" style={{ background: STATUS_PCT_BG[status], color: STATUS_PCT_FG[status] }}>
              {pct >= 0 ? '+' : ''}{pct}%
            </span>
          ) : (
            <span className="march-card-pct"
                  style={{ background: '#F1F5F9', color: '#94A3B8' }}
                  title={periodPending ? `${periodLabel} actual pending` : undefined}>—</span>
          )}
        </div>
      )}
      {(() => {
        // FY target footer — shown in both March and April views. Q2 progress + trajectory
        // live in the deep-dive panel, not on the card.
        // Same preferCascadeFy() helper as above to keep cards in sync with
        // spreadsheet updates while avoiding the pct unit-scale gap.
        const fyTarget = preferCascadeFy(kpi);
        const fyPending = kpi.fy?.pending;
        const hasFyTarget = fyTarget !== null && fyTarget !== undefined;
        if (hasFyTarget) {
          return <div className="march-card-fy">FY target: {M.formatValue(fyTarget, kpi.format)}</div>;
        }
        if (fyPending) {
          return <div className="march-card-fy is-pending">FY target TBC</div>;
        }
        return <div className="march-card-fy">FY target: —</div>;
      })()}
      {headlines && headlines.length > 0 && (
        <div className="march-card-headlines-hint">▼ {headlines.length} headlines</div>
      )}
    </div>
  );
}

function OverviewSection({ view, onKpiClick, onOpenSidebar, activeQ: extQ, setActiveQ: extSetQ, activeMonth: extMonth, setActiveMonth: extSetMonth }) {
  // Cascading panels: L1 = strategic KPI drill, L2 = headline KPI drill
  const [activeStrategicId, setActiveStrategicId] = useState(null);
  const [activeHeadlineId, setActiveHeadlineId] = useState(null);
  // Period state — uses parent state if provided, else falls back to local state.
  const [localQ, setLocalQ] = useState(CURRENT_Q);
  const [localMonth, setLocalMonth] = useState(CURRENT_MONTH);
  const activeQ = extQ !== undefined ? extQ : localQ;
  const setActiveQ = extSetQ || setLocalQ;
  const activeMonth = extMonth !== undefined ? extMonth : localMonth;
  const setActiveMonth = extSetMonth || setLocalMonth;

  // Strategic + headline lookup over the new March dataset
  const strategicById = M.STRATEGIC_BY_ID;
  const headlinesByParent = M.HEADLINES;
  const allKnownById = useMemo(() => {
    const out = { ...strategicById };
    for (const parent of Object.keys(headlinesByParent)) {
      for (const h of headlinesByParent[parent]) out[h.id] = h;
    }
    return out;
  }, []);

  // Click a strategic card: toggle L1 panel. Same card again closes everything.
  const handleStrategicClick = (kpi) => {
    setActiveStrategicId(prev => {
      if (prev === kpi.id) { setActiveHeadlineId(null); return null; }
      setActiveHeadlineId(null);
      return kpi.id;
    });
  };
  const handleHeadlineClick = (hkpi) => {
    setActiveHeadlineId(prev => prev === hkpi.id ? null : hkpi.id);
  };
  // Closing the L1 panel: scroll back to whatever pillar the user was viewing
  // so they can keep navigating between cards. Without this, React re-renders
  // remove the panel + main content reflows + the page snaps to the top.
  // pillarToFocus overrides the "stay on current pillar" default — used by
  // the pillar-header click handler to jump to a different pillar.
  const closeL1 = (pillarToFocus) => {
    const targetPillar = pillarToFocus || activePillarName;
    setActiveStrategicId(null);
    setActiveHeadlineId(null);
    if (targetPillar) {
      // Run after React commits the state change so the layout is settled
      // before we scroll.
      requestAnimationFrame(() => {
        const el = document.querySelector(`[data-pillar-group="${targetPillar}"]`);
        if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
      });
    }
  };
  const closeL2 = () => { setActiveHeadlineId(null); };

  // Resolve current selection
  const activeStrategic = activeStrategicId ? allKnownById[activeStrategicId] : null;
  const activeHeadline  = activeHeadlineId  ? allKnownById[activeHeadlineId]  : null;
  const activeHeadlines = activeStrategicId ? (headlinesByParent[activeStrategicId] || []) : [];
  const activePillarName = activeStrategic ? activeStrategic.pillar : null;

  const cascadeClass = activeStrategicId
    ? (activeHeadlineId ? 'cascade-l2' : 'cascade-l1')
    : '';

  // Default month for a quarter — current month if active Q is current, else last for past, first for future
  const defaultMonthFor = (q) => {
    if (q === CURRENT_Q) return CURRENT_MONTH;
    const order = ['Q1', 'Q2', 'Q3', 'Q4'];
    return order.indexOf(q) < order.indexOf(CURRENT_Q) ? Q_MONTHS[q][2] : Q_MONTHS[q][0];
  };
  const handleQClick = (q) => {
    setActiveQ(q);
    setActiveMonth(defaultMonthFor(q));
  };

  // Resolve UI label → canonical lowercase monthKey. The active month is
  // valid iff it belongs to the active quarter (defensive — should always
  // hold given how the picker is wired, but keeps null-safety in one place).
  const monthKey = (Q_MONTHS[activeQ] && Q_MONTHS[activeQ].includes(activeMonth))
    ? monthLabelToKey(activeMonth)
    : null;
  const hasData = monthKey !== null;

  const scrollToPillar = (pillar) => {
    const el = document.querySelector(`[data-pillar-group="${pillar.name}"]`);
    if (!el) return;
    el.scrollIntoView({ behavior: 'smooth', block: 'start' });
    el.classList.remove('pulse');
    void el.offsetWidth;
    el.classList.add('pulse');
    setTimeout(() => el.classList.remove('pulse'), 1500);
  };

  return (
    <div className={`exec-overview ${cascadeClass}`}>
      {/* Hero search — primary entry point. Sits at the top of the overview
          so it's the first thing the eye lands on when the dashboard opens. */}
      <HeroSearch />

      {/* Period selector — quarter pill on top, active quarter's months below */}
      <div className="exec-overview-topbar stacked">
        <div className="exec-overview-period-row">
          {['Q1', 'Q2', 'Q3', 'Q4'].map(q => (
            <button
              key={q}
              className={`exec-overview-period-btn quarter${activeQ === q ? ' active' : ''}`}
              onClick={() => handleQClick(q)}>
              {q}
            </button>
          ))}
        </div>
        <div className="exec-overview-period-row month">
          {Q_MONTHS[activeQ].map(m => (
            <button
              key={m}
              className={`exec-overview-period-btn month${activeMonth === m ? ' active' : ''}`}
              onClick={() => setActiveMonth(m)}>
              {m}
            </button>
          ))}
        </div>
      </div>

      {/* KEY METRICS strip — four anchor cards. Clicking an anchor toggles the
          Pattern C drill-in drawer below (3 milestones + Jan→Dec chart). */}
      <div className="exec-overview-key-metrics">
        <div className="exec-overview-anchors">
          {M.PILLARS.map(p => (
            <AnchorCard
              key={p.id}
              pillar={p}
              anchor={strategicById[p.anchorId]}
              hasData={hasData}
              monthKey={monthKey}
            />
          ))}
        </div>
      </div>

      {/* Pillar groups: strategic KPI cards via MarchCard (no arrows, no FY-target row) */}
      {M.PILLARS.map(pillar => {
        const kpis = M.STRATEGIC_BY_PILLAR[pillar.name].filter(k => k.tier !== 'anchor');
        if (kpis.length === 0) return null;
        const isActivePillar = activePillarName === pillar.name;
        const isFocusMode = !!activePillarName;
        const groupClass = `exec-overview-pillar-group${isFocusMode ? (isActivePillar ? ' is-active' : ' is-dimmed') : ''}`;
        return (
          <div key={pillar.name} className={groupClass} data-pillar-group={pillar.name}>
            <div className="pgh" onClick={isFocusMode && !isActivePillar ? (() => { closeL1(pillar.name); }) : undefined}>
              <span className="pgh-tag">{PILLAR_TAGS[pillar.name]}</span>
              <span className="pgh-name">{pillar.name}</span>
              {isFocusMode && !isActivePillar && (
                <span className="pgh-hint">click to expand</span>
              )}
            </div>
            <div className="strategic-kpis-grid">
              {kpis.map(kpi => (
                <MarchCard
                  key={kpi.id}
                  kpi={kpi}
                  headlines={headlinesByParent[kpi.id]}
                  isActive={activeStrategicId === kpi.id}
                  onClick={handleStrategicClick}
                  hasData={hasData}
                  monthKey={monthKey}
                />
              ))}
            </div>
          </div>
        );
      })}

      {/* Cascading drill-down panels — L1 for strategic KPI, L2 for headline KPI */}
      {activeStrategicId && (() => {
        const k = activeStrategic;
        if (!k) return null;
        const mk = monthKey || 'mar';
        // Quarter-end months (Mar/Jun/Sep/Dec) → "vs FY target" framing.
        // Mid-quarter months → "vs <Month> target" + variance pct + quarter-progress row.
        // Cross-view consistency: prefer cascade.latestRag (same source the overview
        // cards use via YUNO_KPI_STATUS) so the deep-dive panel renders the same
        // color as the card the user clicked.
        const r = resolveKpiForMonth(k, mk);
        const kActual = r.actual;
        const kTarget = r.target;
        const status = r.status;
        const pct = r.pct;
        const showOfTarget = r.showOfTarget;

        return (
          <div className="exec-overview-cascade">
            {/* L1 panel — strategic KPI overview */}
            <aside className="exec-overview-cascade-panel l1 open">
              <div className="panel-header">
                <div className="panel-eyebrow">
                  {PILLAR_TAGS[k.pillar]} · {k.pillar}{k.team ? ` · ${k.team.toUpperCase()}` : ''}
                </div>
                <div className="panel-title-row">
                  <div className="panel-title">{k.name}</div>
                  <button className="close-btn" onClick={closeL1}>Close ✕</button>
                </div>
                <div className="panel-stats">
                  <div className={`big ${status}`}>{M.formatValue(kActual, k.format)}</div>
                  {showOfTarget && kTarget !== null && kTarget !== undefined && (
                    <div className="of">of {M.formatValue(kTarget, k.format)}</div>
                  )}
                  {pct !== null && (
                    <div className={`pct ${status}`}>{pct >= 0 ? '+' : ''}{pct}%</div>
                  )}
                  {r.isCarriedOver && (
                    <div style={{ fontSize: 12, color: '#94A3B8', letterSpacing: '0.02em', marginLeft: 8 }}>
                      latest: {MONTH_LABEL[r.carriedFromMonth] || r.carriedFromMonth}
                      &nbsp;·&nbsp;{MONTH_LABEL[mk] || mk} actual pending
                    </div>
                  )}
                </div>
                {/* Mid-quarter view → quarter-end target row (Q-of-mk: Q2 for Apr/May,
                    Q3 for Jul/Aug, etc.) + trajectory chart + FY target line.
                    Quarter-end view → FY target line only. */}
                {(() => {
                  // Pass null when value is carried over — otherwise we'd compute
                  // a Q-target variance against a stale prior-month value.
                  const qctx = resolveQuarterContext(k, mk, r.isCarriedOver ? null : kActual);
                  if (!qctx) return null;
                  const { qLabel, qt, qPending, hasQ, qStatus, qPct } = qctx;
                  return (
                    <div className={`panel-q2-row${qPending ? ' is-pending' : ''}`}>
                      <span className="panel-q2-label">{qLabel} target</span>
                      <span className="panel-q2-value">
                        {hasQ ? M.formatValue(qt, k.format) : (qPending ? 'pending' : '—')}
                      </span>
                      {qPct !== null && (
                        <span className="panel-q2-pct" style={{ background: STATUS_PCT_BG[qStatus], color: STATUS_PCT_FG[qStatus] }}>
                          {qPct >= 0 ? '+' : ''}{qPct}% vs {qLabel}
                        </span>
                      )}
                    </div>
                  );
                })()}
                {/* Two-line trajectory chart — shown in BOTH March and April views, not only April. */}
                <TrendAreaChart kpi={k} status={status} monthKey={mk} />
                <TrendCaption kpi={k} status={status} />
                {(k.fy && (k.fy.target !== undefined && k.fy.target !== null || k.fy.pending)) && (
                  <div className={`panel-fy${k.fy.pending ? ' is-pending' : ''}`}>
                    {k.fy.target !== undefined && k.fy.target !== null
                      ? `FY target: ${M.formatValue(k.fy.target, k.format)}`
                      : 'FY target TBC'}
                  </div>
                )}
              </div>
              {activeHeadlines.length > 0 ? (
                <>
                  <div className="panel-section-title">
                    <span>Headlines · {activeHeadlines.length}</span>
                    <span className="hint">click to drill</span>
                  </div>
                  <div className="drill-list">
                    {activeHeadlines.map(hkpi => {
                      const hr = resolveKpiForMonth(hkpi, mk);
                      const hAct = hr.actual;
                      const hStatus = hr.status;
                      const isSelected = activeHeadlineId === hkpi.id;
                      return (
                        <div
                          key={hkpi.id}
                          className={`drill-row${isSelected ? ' selected' : ''}`}
                          onClick={() => handleHeadlineClick(hkpi)}>
                          <span className="h-name">{hkpi.name}</span>
                          <span className="h-value">{M.formatValue(hAct, hkpi.format)}</span>
                          <span className={`h-status ${hStatus}`}></span>
                        </div>
                      );
                    })}
                  </div>
                </>
              ) : (
                <div className="empty-headlines">No further breakdown.</div>
              )}
            </aside>

            {/* L2 panel — headline KPI detail (same visual treatment as L1) */}
            {activeHeadline && (() => {
              const h = activeHeadline;
              const hr = resolveKpiForMonth(h, mk);
              const hActual = hr.actual;
              const hTarget = hr.target;
              const hStatus = hr.status;
              const hPct = hr.pct;
              const hShowOf = hr.showOfTarget;
              return (
                <aside className="exec-overview-cascade-panel l2 open">
                  <div className="panel-header">
                    <div className="panel-eyebrow">
                      Headline · {k.name}{h.team ? ` · ${h.team.toUpperCase()}` : ''}
                    </div>
                    <div className="panel-title-row">
                      <div className="panel-title">{h.name}</div>
                      <button className="close-btn" onClick={closeL2}>Close ✕</button>
                    </div>
                    <div className="panel-stats">
                      <div className={`big ${hStatus}`}>{M.formatValue(hActual, h.format)}</div>
                      {hShowOf && hTarget !== null && hTarget !== undefined && (
                        <div className="of">of {M.formatValue(hTarget, h.format)}</div>
                      )}
                      {hPct !== null && (
                        <div className={`pct ${hStatus}`}>{hPct >= 0 ? '+' : ''}{hPct}%</div>
                      )}
                      {hr.isCarriedOver && (
                        <div style={{ fontSize: 12, color: '#94A3B8', letterSpacing: '0.02em', marginLeft: 8 }}>
                          latest: {MONTH_LABEL[hr.carriedFromMonth] || hr.carriedFromMonth}
                          &nbsp;·&nbsp;{MONTH_LABEL[mk] || mk} actual pending
                        </div>
                      )}
                    </div>
                    {/* Quarter-end target row — only meaningful in mid-quarter views */}
                    {(() => {
                      const qctx = resolveQuarterContext(h, mk, hr.isCarriedOver ? null : hActual);
                      if (!qctx) return null;
                      const { qLabel, qt, qPending, hasQ, qStatus, qPct } = qctx;
                      return (
                        <div className={`panel-q2-row${qPending ? ' is-pending' : ''}`}>
                          <span className="panel-q2-label">{qLabel} target</span>
                          <span className="panel-q2-value">
                            {hasQ ? M.formatValue(qt, h.format) : (qPending ? 'pending' : '—')}
                          </span>
                          {qPct !== null && (
                            <span className="panel-q2-pct" style={{ background: STATUS_PCT_BG[qStatus], color: STATUS_PCT_FG[qStatus] }}>
                              {qPct >= 0 ? '+' : ''}{qPct}% vs {qLabel}
                            </span>
                          )}
                        </div>
                      );
                    })()}
                    {/* Two-line trajectory chart — present in BOTH March and April views. */}
                    <TrendAreaChart kpi={h} status={hStatus} monthKey={mk} />
                    <TrendCaption kpi={h} status={hStatus} />
                    {(h.fy && (h.fy.target !== undefined && h.fy.target !== null || h.fy.pending)) && (
                      <div className={`panel-fy${h.fy.pending ? ' is-pending' : ''}`}>
                        {h.fy.target !== undefined && h.fy.target !== null
                          ? `FY target: ${M.formatValue(h.fy.target, h.format)}`
                          : 'FY target TBC'}
                      </div>
                    )}
                  </div>
                  <div className="detail-section">
                    <div className="driver-list">
                      <div className="label">Drivers</div>
                      <ul><li>No driver notes</li></ul>
                    </div>
                  </div>
                </aside>
              );
            })()}
          </div>
        );
      })()}
    </div>
  );
}

// ============================================================================
// Section: All BD KPIs (grid ⇄ table toggle)
// ============================================================================
function AllKpisSection({ filters, setFilters, view, setView, onKpiClick }) {
  const [mode, setMode] = useState('grid');
  const [query, setQuery] = useState('');
  const [sortKey, setSortKey] = useState('pctToTarget');
  const [sortDir, setSortDir] = useState('asc');

  const allKpis = [...D.STRATEGIC, ...D.CUSTOMER_VALUE, ...D.MAYA];

  const rows = useMemo(() => {
    const out = allKpis
      .filter(k => query ? k.name.toLowerCase().includes(query.toLowerCase()) || (k.owner || '').toLowerCase().includes(query.toLowerCase()) : true)
      .map(k => {
        const actual = computeFiltered(k, view, filters, 'actual');
        const target = computeFiltered(k, view, filters, 'target');
        const pace = view === 'qtd' && (k.format === 'currencyMM' || k.format === 'currencyK')
          ? target * D.QUARTER_PROGRESS : target;
        const pct = D.pctToTarget(actual, pace, k.higherIsBetter);
        return { kpi: k, actual, target, pct, status: D.health(actual, pace, k.higherIsBetter) };
      });
    out.sort((a, b) => {
      const av = sortKey === 'name' ? a.kpi.name : sortKey === 'owner' ? (a.kpi.owner || '') : a[sortKey];
      const bv = sortKey === 'name' ? b.kpi.name : sortKey === 'owner' ? (b.kpi.owner || '') : b[sortKey];
      if (typeof av === 'string') return sortDir === 'asc' ? av.localeCompare(bv) : bv.localeCompare(av);
      return sortDir === 'asc' ? av - bv : bv - av;
    });
    return out;
  }, [allKpis, query, view, filters, sortKey, sortDir]);

  function clickSort(k) {
    if (sortKey === k) setSortDir(sortDir === 'asc' ? 'desc' : 'asc');
    else { setSortKey(k); setSortDir('asc'); }
  }

  function exportCsv() {
    const header = ['KPI', 'Owner', 'Current', 'Target', '% to target', 'Status'];
    const lines = [header.join(',')];
    rows.forEach(r => {
      lines.push([
        `"${r.kpi.name}"`, `"${r.kpi.owner || ''}"`,
        r.actual, r.target, r.pct.toFixed(1) + '%', r.status,
      ].join(','));
    });
    const blob = new Blob([lines.join('\n')], { type: 'text/csv' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = `yuno-bd-kpis-${view}.csv`;
    a.click();
  }

  return (
    <>
      {/* Contextual Filters for All KPIs */}
      <ContextualFilters
        availableFilters={['time', 'region', 'source']}
        filters={filters}
        setFilters={setFilters}
        view={view}
        setView={setView}
      />

      <div className="allkpi-toolbar">
        <div className="allkpi-toolbar-left">
          <input className="search-input" placeholder={`Search ${allKpis.length} KPIs…`}
            value={query} onChange={e => setQuery(e.target.value)}/>
          <div className="view-toggle">
            <button className={mode === 'grid' ? 'active' : ''} onClick={() => setMode('grid')}>Grid</button>
            <button className={mode === 'table' ? 'active' : ''} onClick={() => setMode('table')}>Table</button>
          </div>
        </div>
        <div className="allkpi-toolbar-right">
          <span className="text-xs text-muted">{rows.length} of {allKpis.length} KPIs</span>
          <button className="btn btn-secondary" onClick={exportCsv}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" style={{marginRight: '4px'}}>
              <path d="M7 14L12 9L17 14" stroke="currentColor" strokeWidth="2" transform="rotate(180)" transformOrigin="12 12"/>
            </svg>
            CSV
          </button>
        </div>
      </div>

      {mode === 'grid' ? (
        <div className="kpi-tile-grid all-grid">
          {rows.map(r => (
            <KpiTile key={r.kpi.id} kpi={r.kpi} view={view} filters={filters} onClick={onKpiClick}/>
          ))}
          {rows.length === 0 && (
            <div className="empty-state" style={{ gridColumn: '1/-1' }}>
              <div className="icon">🔎</div>
              <h3>No KPIs match</h3>
              <p>Try a different search term or clear filters.</p>
            </div>
          )}
        </div>
      ) : (
        <div className="panel" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="table-wrap" style={{ maxHeight: 700 }}>
            <table className="tbl">
              <thead>
                <tr>
                  <th onClick={() => clickSort('name')}>KPI
                    <svg className="sort-ind" width="12" height="12" viewBox="0 0 24 24" style={{marginLeft: '4px', transform: sortKey==='name' ? (sortDir==='asc' ? 'rotate(0deg)' : 'rotate(180deg)') : 'rotate(90deg)'}}>
                      <path d="M7 14L12 9L17 14" stroke="currentColor" strokeWidth="2" fill="none"/>
                    </svg>
                  </th>
                  <th className="num" onClick={() => clickSort('actual')}>Current
                    <svg className="sort-ind" width="12" height="12" viewBox="0 0 24 24" style={{marginLeft: '4px', transform: sortKey==='actual' ? (sortDir==='asc' ? 'rotate(0deg)' : 'rotate(180deg)') : 'rotate(90deg)'}}>
                      <path d="M7 14L12 9L17 14" stroke="currentColor" strokeWidth="2" fill="none"/>
                    </svg>
                  </th>
                  <th className="num" onClick={() => clickSort('target')}>Target
                    <svg className="sort-ind" width="12" height="12" viewBox="0 0 24 24" style={{marginLeft: '4px', transform: sortKey==='target' ? (sortDir==='asc' ? 'rotate(0deg)' : 'rotate(180deg)') : 'rotate(90deg)'}}>
                      <path d="M7 14L12 9L17 14" stroke="currentColor" strokeWidth="2" fill="none"/>
                    </svg>
                  </th>
                  <th onClick={() => clickSort('pctToTarget')}>Progress
                    <svg className="sort-ind" width="12" height="12" viewBox="0 0 24 24" style={{marginLeft: '4px', transform: sortKey==='pctToTarget' ? (sortDir==='asc' ? 'rotate(0deg)' : 'rotate(180deg)') : 'rotate(90deg)'}}>
                      <path d="M7 14L12 9L17 14" stroke="currentColor" strokeWidth="2" fill="none"/>
                    </svg>
                  </th>
                  <th>Status</th>
                  <th onClick={() => clickSort('owner')}>Owner
                    <svg className="sort-ind" width="12" height="12" viewBox="0 0 24 24" style={{marginLeft: '4px', transform: sortKey==='owner' ? (sortDir==='asc' ? 'rotate(0deg)' : 'rotate(180deg)') : 'rotate(90deg)'}}>
                      <path d="M7 14L12 9L17 14" stroke="currentColor" strokeWidth="2" fill="none"/>
                    </svg>
                  </th>
                </tr>
              </thead>
              <tbody>
                {rows.map(r => (
                  <tr key={r.kpi.id} onClick={() => onKpiClick(r.kpi)} style={{ cursor: 'pointer' }}>
                    <td>
                      <strong>{r.kpi.name}</strong>
                      <div className="text-xs text-muted">{r.kpi.unit}</div>
                    </td>
                    <td className="num tabular">{D.fmt(r.actual, r.kpi.format)}</td>
                    <td className="num tabular text-muted">{D.fmt(r.target, r.kpi.format)}</td>
                    <td>
                      <span className="bar-track">
                        <span className={`bar-fill ${r.status}`} style={{ width: `${Math.min(100, Math.max(0, 100 + r.pct))}%` }}/>
                      </span>
                      <span className="tabular text-xs" style={{ marginLeft: 8, fontWeight: 600 }}>
                        {r.pct >= 0 ? '+' : ''}{r.pct.toFixed(1)}%
                      </span>
                    </td>
                    <td><span className={`badge badge-${r.status === 'good' ? 'good' : r.status === 'warn' ? 'warn' : r.status === 'bad' ? 'bad' : 'neutral'}`}>{r.status}</span></td>
                    <td className="text-sm">{r.kpi.owner}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </>
  );
}

// ============================================================================
// Section: Pipeline & Funnel
// ============================================================================
function PipelineSection({ filters, setFilters, view, setView, onKpiClick }) {
  const pipelineKpi = D.STRATEGIC.find(k => k.id === 'qualified_pipeline');
  const stageData = Object.entries(pipelineKpi.byStage)
    .filter(([k]) => !filters.stage?.length || filters.stage.includes(k))
    .map(([k, v]) => ({ name: k, actual: v.current, target: v.target }));
  const funnelData = D.buildFunnel(filters.source);
  const sqlRegions = filters.region?.length ? filters.region : D.REGIONS;

  return (
    <>
      {/* Contextual Filters for Pipeline Analysis */}
      <ContextualFilters
        availableFilters={['time', 'stage', 'source']}
        filters={filters}
        setFilters={setFilters}
        view={view}
        setView={setView}
      />
      <div className="hero-grid" style={{ gridTemplateColumns: 'repeat(3, 1fr)' }}>
        <KpiCard kpi={D.STRATEGIC.find(k=>k.id==='qualified_pipeline')} view={view} filters={filters} onClick={onKpiClick}/>
        <KpiCard kpi={D.STRATEGIC.find(k=>k.id==='win_rate')} view={view} filters={filters} onClick={onKpiClick}/>
        <KpiCard kpi={D.STRATEGIC.find(k=>k.id==='sales_cycle')} view={view} filters={filters} onClick={onKpiClick}/>
      </div>

      <div className="section-title">Funnel <div className="line"/></div>
      <div className="panel">
        <div className="panel-head">
          <div>
            <div className="panel-title">Lead → Won funnel</div>
            <div className="panel-subtitle">{filters.source?.length ? `Sources: ${filters.source.join(', ')}` : 'All acquisition sources'}</div>
          </div>
        </div>
        <FunnelChart data={funnelData}/>
      </div>

      <div className="section-title">Pipeline by Stage <div className="line"/></div>
      <div className="row-1-1">
        <div className="panel">
          <div className="panel-head"><div className="panel-title">Pipeline value by stage ($M)</div></div>
          <ComparisonBar data={stageData} dataKey="actual" targetKey="target" format="currencyMM" height={260}/>
        </div>
        <div className="panel">
          <div className="panel-head"><div className="panel-title">SQLs by region</div></div>
          <ComparisonBar
            data={sqlRegions.map(r => ({ name: r, actual: D.SQLS_BY_REGION[r]?.q2Progress || 0, target: D.SQLS_BY_REGION[r]?.q2Target || 0 }))}
            dataKey="actual" targetKey="target" format="" height={260} color={C.blue}/>
        </div>
      </div>
    </>
  );
}

// ============================================================================
// Section: Regions
// ============================================================================
function RegionsSection({ filters, setFilters, view, setView, onKpiClick }) {
  const [primaryKpiId, setPrimaryKpiId] = useState('new_logo_arr');
  const primary = D.STRATEGIC.find(k => k.id === primaryKpiId);
  const regions = filters.region?.length ? filters.region : D.REGIONS;
  const matrixKpis = ['new_logo_arr', 'qualified_pipeline', 'win_rate', 'acv', 'sales_cycle', 'cac_payback'];

  return (
    <>
      {/* Contextual Filters for Regional Analysis */}
      <ContextualFilters
        availableFilters={['time', 'source']}
        filters={filters}
        setFilters={setFilters}
        view={view}
        setView={setView}
      />

      <div className="panel-head" style={{ marginBottom: 8 }}>
        <div>
          <div className="text-xs text-muted" style={{ textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>Compare by</div>
        </div>
        <div className="panel-actions">
          {matrixKpis.map(id => {
            const k = D.STRATEGIC.find(x => x.id === id);
            return (
              <button key={id}
                className={`panel-tab ${primaryKpiId === id ? 'active' : ''}`}
                onClick={() => setPrimaryKpiId(id)}>{k.name}</button>
            );
          })}
        </div>
      </div>

      <div className="region-grid">
        {regions.map(r => (
          <RegionTile key={r} region={r} kpi={primary} view={view}
            selected={filters.region?.includes(r)} onClick={() => onKpiClick(primary)}/>
        ))}
      </div>

      <div className="section-title">Regional comparison matrix <div className="line"/></div>
      <div className="panel" style={{ padding: 0, overflow: 'hidden' }}>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>KPI</th>
                {regions.map(r => <th key={r} className="num">{r}</th>)}
                <th className="num">Total / Avg</th>
              </tr>
            </thead>
            <tbody>
              {matrixKpis.map(id => {
                const k = D.STRATEGIC.find(x => x.id === id);
                if (!k.byRegion) return null;
                const totalActual = computeFiltered(k, view, filters, 'actual');
                const totalTarget = computeFiltered(k, view, filters, 'target');
                return (
                  <tr key={id}>
                    <td><strong>{k.name}</strong></td>
                    {regions.map(r => {
                      const row = k.byRegion[r];
                      if (!row) return <td key={r} className="num text-muted">—</td>;
                      const actual = view === 'qtd' ? (row.q2Progress ?? row.q1Actual) : row.q1Actual;
                      const target = view === 'qtd' ? row.q2Target : row.q4Target;
                      const status = D.health(actual, target, k.higherIsBetter);
                      return (
                        <td key={r} className="num">
                          <span style={{ fontWeight: 600 }}>{D.fmt(actual, k.format)}</span>
                          <div style={{ fontSize: 10, marginTop: 2 }}>
                            <span className={`text-${status === 'good' ? 'good' : status === 'warn' ? 'warn' : 'bad'}`}>● </span>
                            <span className="text-muted">vs {D.fmt(target, k.format)}</span>
                          </div>
                        </td>
                      );
                    })}
                    <td className="num">
                      <strong>{D.fmt(totalActual, k.format)}</strong>
                      <div className="text-xs text-muted" style={{ marginTop: 2 }}>vs {D.fmt(totalTarget, k.format)}</div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
}

// ============================================================================
// Section: Sources
// ============================================================================
function SourcesSection({ filters, view }) {
  const winRate = D.STRATEGIC.find(k => k.id === 'win_rate');
  const acv = D.STRATEGIC.find(k => k.id === 'acv');
  const sources = filters.source?.length ? filters.source : D.SOURCES;
  const sourceWinData = sources.map(s => {
    const v = winRate.bySource[s] || {};
    return { name: s, actual: v.current || 0, target: v.target || 0 };
  });
  const sourceAcvData = sources.map(s => {
    const v = acv.bySource[s] || {};
    return { name: s, actual: v.current || 0, target: v.target || 0 };
  });
  const mktDonut = Object.entries(D.MARKETING_SQLS).map(([name, v]) => ({ name, value: v.q2Progress, target: v.q2Target }));
  return (
    <>
      <div className="section-title">SQL Source Mix <div className="line"/></div>
      <div className="row-3">
        <div className="panel">
          <div className="panel-head"><div className="panel-title">Marketing SQLs by channel · Q2 progress</div></div>
          <DonutChart data={mktDonut} height={240}/>
        </div>
        <div className="panel">
          <div className="panel-head"><div className="panel-title">Win rate by source</div></div>
          <ComparisonBar data={sourceWinData} dataKey="actual" targetKey="target" format="pct" height={240}/>
        </div>
        <div className="panel">
          <div className="panel-head"><div className="panel-title">ACV by source ($K)</div></div>
          <ComparisonBar data={sourceAcvData} dataKey="actual" targetKey="target" format="currencyK" height={240}/>
        </div>
      </div>

      <div className="section-title">Channel performance <div className="line"/></div>
      <div className="panel" style={{ padding: 0, overflow: 'hidden' }}>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>Source</th>
                <th className="num">Win rate</th>
                <th className="num">ACV</th>
                <th className="num">Sales cycle</th>
                <th>Health</th>
              </tr>
            </thead>
            <tbody>
              {sources.map(s => {
                const wr = winRate.bySource[s];
                const av = acv.bySource[s];
                const sc = D.STRATEGIC.find(k=>k.id==='sales_cycle').bySource[s];
                const status = wr ? D.health(wr.current, wr.target, true) : 'neutral';
                return (
                  <tr key={s}>
                    <td><strong>{s}</strong></td>
                    <td className="num">{wr ? `${wr.current}%` : '—'}</td>
                    <td className="num">{av ? `$${av.current}K` : '—'}</td>
                    <td className="num">{sc ? `${sc.current} mo` : '—'}</td>
                    <td><span className={`badge badge-${status === 'good' ? 'good' : status === 'warn' ? 'warn' : status === 'bad' ? 'bad' : 'neutral'}`}>{status}</span></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
}

// ============================================================================
// Section: Efficiency
// ============================================================================
function EfficiencySection({ filters, view, onKpiClick }) {
  const cycle = D.STRATEGIC.find(k => k.id === 'sales_cycle');
  const cac = D.STRATEGIC.find(k => k.id === 'cac_payback');
  const regions = filters.region?.length ? filters.region : D.REGIONS;
  const cycleByRegion = regions.map(r => ({
    name: r, actual: cycle.byRegion[r].q1Actual,
    target: view === 'qtd' ? cycle.byRegion[r].q2Target : cycle.byRegion[r].q4Target,
  }));
  const cacByRegion = regions.map(r => ({
    name: r, actual: cac.byRegion[r].q1Actual,
    target: view === 'qtd' ? cac.byRegion[r].q2Target : cac.byRegion[r].q4Target,
  }));
  const sources = filters.source?.length ? filters.source : D.SOURCES;
  const cycleBySource = sources.map(s => {
    const v = cycle.bySource[s] || {};
    return { name: s, actual: v.current || 0, target: v.target || 0 };
  });

  return (
    <>
      <div className="hero-grid" style={{ gridTemplateColumns: 'repeat(3, 1fr)' }}>
        <KpiCard kpi={cycle} view={view} filters={filters} onClick={onKpiClick}/>
        <KpiCard kpi={cac} view={view} filters={filters} onClick={onKpiClick}/>
        <KpiCard kpi={D.STRATEGIC.find(k=>k.id==='contract_to_live')} view={view} filters={filters} onClick={onKpiClick}/>
      </div>

      <div className="section-title">Sales Cycle <div className="line"/></div>
      <div className="row-1-1">
        <div className="panel">
          <div className="panel-head"><div className="panel-title">Sales cycle by region (months)</div></div>
          <ComparisonBar data={cycleByRegion} dataKey="actual" targetKey="target" format="months" height={240}/>
        </div>
        <div className="panel">
          <div className="panel-head"><div className="panel-title">Sales cycle by source (months)</div></div>
          <ComparisonBar data={cycleBySource} dataKey="actual" targetKey="target" format="months" height={240}/>
        </div>
      </div>

      <div className="section-title">CAC Payback <div className="line"/></div>
      <div className="panel">
        <div className="panel-head"><div className="panel-title">CAC payback by region (months — lower is better)</div></div>
        <ComparisonBar data={cacByRegion} dataKey="actual" targetKey="target" format="months" height={240}/>
      </div>
    </>
  );
}

// ============================================================================
// Section: Customer Value (+ Maya lives here as a sub-section)
// ============================================================================
function CustomerSection({ filters, view, onKpiClick }) {
  const regions = filters.region?.length ? filters.region : D.REGIONS;
  return (
    <>
      <div className="section-title">Post-sale customer health <div className="line"/></div>
      <div className="row-3">
        {D.CUSTOMER_VALUE.map(k => (
          <KpiCard key={k.id} kpi={k} view={view} filters={filters} onClick={onKpiClick}/>
        ))}
      </div>

      <div className="section-title">By region <div className="line"/></div>
      <div className="row-3">
        {D.CUSTOMER_VALUE.map(k => (
          <div key={k.id} className="panel">
            <div className="panel-head"><div className="panel-title">{k.name}</div></div>
            <ComparisonBar
              data={regions.map(r => ({ name: r, actual: k.byRegion[r].q2Progress ?? k.byRegion[r].q1Actual, target: k.byRegion[r].q2Target }))}
              dataKey="actual" targetKey="target" format={k.format} height={200}/>
          </div>
        ))}
      </div>

      <div className="section-title">Maya Agent <div className="line"/></div>
      <div className="row-1-1">
        {D.MAYA.map(k => (
          <div key={k.id} className="panel">
            <div className="panel-head">
              <div>
                <div className="panel-title">{k.name}</div>
                <div className="panel-subtitle">Owner: {k.owner}</div>
              </div>
              <button className="btn btn-ghost" onClick={() => onKpiClick(k)}>Detail →</button>
            </div>
            <ComparisonBar
              data={regions.map(r => ({ name: r, actual: k.byRegion[r].q2Progress ?? k.byRegion[r].q1Actual, target: k.byRegion[r].q2Target }))}
              dataKey="actual" targetKey="target" format={k.format} height={200} color={C.greenDark}/>
          </div>
        ))}
      </div>
    </>
  );
}

// ============================================================================
// Section: Lead Funnel deep-dive (one-page solution for Leads KPI)
// ============================================================================
function LeadFunnelSection({ filters, setFilters, view, setView, onKpiClick }) {
  const lf = D.LEAD_FUNNEL;
  const [expandedStage, setExpandedStage] = useState(null);

  // Apply region/source filters to stage totals
  const filteredStages = useMemo(() => {
    const base = {};
    lf.stages.forEach(s => { base[s.id] = 0; });
    let useRegion = filters.region?.length;
    let useSource = filters.source?.length;
    if (!useRegion && !useSource) {
      lf.stages.forEach(s => { base[s.id] = s.q2Progress; });
    } else if (useSource && !useRegion) {
      filters.source.forEach(src => {
        const row = lf.bySource[src];
        if (!row) return;
        Object.keys(row).forEach(k => { base[k] = (base[k] || 0) + row[k]; });
      });
    } else {
      // region (and optionally source) — region wins on totals
      filters.region.forEach(r => {
        const row = lf.byRegion[r];
        if (!row) return;
        ['leads','mqls','sqls','sao','opps','won'].forEach(k => { base[k] = (base[k] || 0) + (row[k]||0); });
      });
    }
    return lf.stages.map(s => ({
      ...s,
      actual: base[s.id] || 0,
    }));
  }, [filters.region, filters.source]);

  const maxVal = Math.max(...filteredStages.map(s => s.actual), 1);

  // Stage transition rates
  const transitions = useMemo(() => {
    const out = [];
    for (let i = 1; i < filteredStages.length; i++) {
      const prev = filteredStages[i - 1];
      const curr = filteredStages[i];
      const rate = prev.actual > 0 ? (curr.actual / prev.actual) * 100 : 0;
      const benchKey = `${prev.name.split(' ')[0]}→${curr.name.split(' ')[0]}`;
      const bench = lf.benchmarks[benchKey] || {};
      out.push({ from: prev.name, to: curr.name, rate, target: bench.target || 0, status: rate >= (bench.target || 0) ? 'good' : rate >= (bench.target || 0) * 0.85 ? 'warn' : 'bad' });
    }
    return out;
  }, [filteredStages]);

  const sourceRows = Object.entries(lf.bySource).map(([s, r]) => ({
    source: s,
    leads: r.leads, mqls: r.mqls, sqls: r.sqls, won: r.won,
    leadToSql: ((r.sqls / r.leads) * 100).toFixed(1),
    sqlToWon: ((r.won / r.sqls) * 100).toFixed(1),
  })).sort((a, b) => b.sqls - a.sqls);

  const regionRows = Object.entries(lf.byRegion).map(([r, v]) => ({
    region: r, owner: v.ownerName,
    leads: v.leads, mqls: v.mqls, sqls: v.sqls, won: v.won,
    leadToSql: ((v.sqls / v.leads) * 100).toFixed(1),
  })).sort((a, b) => b.sqls - a.sqls);

  const totalLeads = filteredStages[0]?.actual || 0;
  const totalSqls = filteredStages[2]?.actual || 0;
  const totalWon = filteredStages[5]?.actual || 0;
  const overallConv = totalLeads > 0 ? ((totalWon / totalLeads) * 100).toFixed(2) : '—';

  return (
    <>
      {/* Contextual Filters for Lead Funnel Analysis */}
      <ContextualFilters
        availableFilters={['time', 'region', 'source']}
        filters={filters}
        setFilters={setFilters}
        view={view}
        setView={setView}
      />

      {/* Headline strip — the big number */}
      <div className="lead-headline">
        <div className="lh-block">
          <div className="lh-label">Leads QTD</div>
          <div className="lh-big">{totalLeads.toLocaleString()}</div>
          <div className="lh-meta text-muted">vs {(lf.stages[0].q2Target).toLocaleString()} Q2 target</div>
        </div>
        <div className="lh-arrow">
          <svg width="24" height="16" viewBox="0 0 24 16" fill="none">
            <path d="M1 8H23M16 1L23 8L16 15" stroke="#6B7280" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </div>
        <div className="lh-block">
          <div className="lh-label">SQLs QTD</div>
          <div className="lh-big">{totalSqls.toLocaleString()}</div>
          <div className="lh-meta text-muted">vs {(lf.stages[2].q2Target).toLocaleString()} Q2 target</div>
        </div>
        <div className="lh-arrow">
          <svg width="24" height="16" viewBox="0 0 24 16" fill="none">
            <path d="M1 8H23M16 1L23 8L16 15" stroke="#6B7280" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </div>
        <div className="lh-block">
          <div className="lh-label">Won QTD</div>
          <div className="lh-big text-good">{totalWon.toLocaleString()}</div>
          <div className="lh-meta text-muted">{overallConv}% lead → won</div>
        </div>
        <div className="lh-block lh-status">
          <div className="lh-label">Pacing</div>
          <div className={`lh-pace ${totalSqls / lf.stages[2].q2Target / D.QUARTER_PROGRESS >= 1 ? 'text-good' : 'text-warn'}`}>
            {((totalSqls / lf.stages[2].q2Target) / D.QUARTER_PROGRESS * 100).toFixed(0)}%
          </div>
          <div className="lh-meta text-muted">of pace</div>
        </div>
      </div>

      <div className="section-title">Funnel — click any stage to expand <div className="line"/></div>
      <div className="panel">
        <div className="lead-funnel">
          {filteredStages.map((s, i) => {
            const w = (s.actual / maxVal) * 100;
            const tgt = (s.q2Target / maxVal) * 100;
            const pace = s.q2Target * D.QUARTER_PROGRESS;
            const status = s.actual >= pace ? 'good' : s.actual >= pace * 0.85 ? 'warn' : 'bad';
            const conv = i > 0 && filteredStages[i-1].actual > 0
              ? ((s.actual / filteredStages[i-1].actual) * 100).toFixed(1)
              : null;
            const isExp = expandedStage === s.id;
            return (
              <div key={s.id} className={`lead-funnel-row ${isExp ? 'expanded' : ''}`}>
                <button className="lfr-head" onClick={() => setExpandedStage(isExp ? null : s.id)}>
                  <div className="lfr-name">
                    <span className={`dot status-${status}`}/>
                    <strong>{s.name}</strong>
                    <span className="text-xs text-muted">· {s.owner}</span>
                  </div>
                  <div className="lfr-bar-wrap">
                    <div className="lfr-target-marker" style={{ left: `${tgt}%` }} title="Q2 target"/>
                    <div className={`lfr-bar status-${status}`} style={{ width: `${Math.max(w, 4)}%` }}>
                      <span className="lfr-bar-val">{s.actual.toLocaleString()}</span>
                    </div>
                  </div>
                  <div className="lfr-num">
                    <div className="lfr-num-val">{s.actual.toLocaleString()}</div>
                    <div className="lfr-num-tgt">/ {s.q2Target.toLocaleString()}</div>
                  </div>
                  <div className={`lfr-conv text-${status === 'good' ? 'good' : status === 'warn' ? 'warn' : 'bad'}`}>
                    {conv ? `${conv}%` : '—'}
                  </div>
                  <span className="lfr-chevron">{isExp ? '▾' : '▸'}</span>
                </button>
                {isExp && (
                  <div className="lfr-branch">
                    <div className="lfr-branch-grid">
                      <div>
                        <div className="branch-label">By region</div>
                        <table className="lf-mini-tbl">
                          <tbody>
                            {regionRows.map(r => (
                              <tr key={r.region}>
                                <td><strong>{r.region}</strong> <span className="text-xs text-muted">{r.owner}</span></td>
                                <td className="num">{(r[s.id] ?? r.leads).toLocaleString()}</td>
                              </tr>
                            ))}
                          </tbody>
                        </table>
                      </div>
                      <div>
                        <div className="branch-label">By source</div>
                        <table className="lf-mini-tbl">
                          <tbody>
                            {sourceRows.map(r => (
                              <tr key={r.source}>
                                <td><strong>{r.source}</strong></td>
                                <td className="num">{(r[s.id] ?? r.leads).toLocaleString()}</td>
                              </tr>
                            ))}
                          </tbody>
                        </table>
                      </div>
                      <div>
                        <div className="branch-label">Weekly trend (Q2)</div>
                        <div style={{ height: 120 }}>
                          <Recharts.ResponsiveContainer>
                            <Recharts.LineChart data={lf.weekly} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>
                              <Recharts.CartesianGrid strokeDasharray="3 3" stroke={C.n100} vertical={false}/>
                              <Recharts.XAxis dataKey="week" tick={{ fontSize: 10, fill: C.n400 }} axisLine={false} tickLine={false}/>
                              <Recharts.YAxis tick={{ fontSize: 10, fill: C.n400 }} axisLine={false} tickLine={false} width={30}/>
                              <Recharts.Tooltip/>
                              <Recharts.Line type="monotone" dataKey={s.id === 'leads' ? 'leads' : s.id === 'mqls' ? 'mqls' : 'sqls'} stroke={C.blue} strokeWidth={2} dot={{ r: 3 }}/>
                            </Recharts.LineChart>
                          </Recharts.ResponsiveContainer>
                        </div>
                      </div>
                    </div>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>

      <div className="section-title">Conversion rates between stages <div className="line"/></div>
      <div className="row-3">
        {transitions.map((t, i) => (
          <div key={i} className={`conv-card conv-${t.status}`}>
            <div className="conv-from">{t.from}</div>
            <div className="conv-arrow">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none">
                <path
                  d="M7 14L12 9L17 14"
                  stroke="#6B7280"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  transform="rotate(180)"
                  transformOrigin="12 12"
                />
              </svg>
            </div>
            <div className="conv-to">{t.to}</div>
            <div className={`conv-rate text-${t.status === 'good' ? 'good' : t.status === 'warn' ? 'warn' : 'bad'}`}>
              {t.rate.toFixed(1)}%
            </div>
            <div className="conv-target text-muted text-xs">target {t.target.toFixed(1)}%</div>
          </div>
        ))}
      </div>

      <div className="section-title">Source attribution <div className="line"/></div>
      <div className="panel" style={{ padding: 0, overflow: 'hidden' }}>
        <div className="table-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>Source</th>
                <th className="num">Leads</th>
                <th className="num">MQLs</th>
                <th className="num">SQLs</th>
                <th className="num">Won</th>
                <th className="num">Lead→SQL</th>
                <th className="num">SQL→Won</th>
              </tr>
            </thead>
            <tbody>
              {sourceRows.map(r => (
                <tr key={r.source}>
                  <td><strong>{r.source}</strong></td>
                  <td className="num">{r.leads.toLocaleString()}</td>
                  <td className="num">{r.mqls.toLocaleString()}</td>
                  <td className="num">{r.sqls}</td>
                  <td className="num"><strong>{r.won}</strong></td>
                  <td className="num">{r.leadToSql}%</td>
                  <td className="num">{r.sqlToWon}%</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      <div className="section-title">Lead-gen ownership <div className="line"/></div>
      <div className="row-3">
        {lf.leadOwners.map(o => {
          const pct = (o.actual / o.target) * 100;
          const pace = pct / (D.QUARTER_PROGRESS * 100) * 100;
          const status = pace >= 100 ? 'good' : pace >= 80 ? 'warn' : 'bad';
          return (
            <div key={o.name} className={`lead-owner-card lo-${status}`}>
              <div className="lo-head">
                <div className={`avatar avatar-lg owner-avatar-${status}`}>{o.name.slice(0,2).toUpperCase()}</div>
                <div>
                  <div className="lo-name">{o.name}</div>
                  <div className="lo-role text-xs text-muted">{o.role}</div>
                </div>
              </div>
              <div className="lo-scope text-xs text-muted">{o.scope}</div>
              <div className="lo-stat">
                <div className="lo-metric text-xs text-muted">{o.metric}</div>
                <div className="lo-val">{o.actual.toLocaleString()} <span className="lo-tgt text-muted">/ {o.target.toLocaleString()}</span></div>
                <div className={`lo-pace text-${status === 'good' ? 'good' : status === 'warn' ? 'warn' : 'bad'}`}>
                  {pace.toFixed(0)}% of pace
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </>
  );
}

// ============================================================================
// Section: Owners — grouped by department for exec consumption
// ============================================================================
function OwnersSection({ filters, view, onKpiClick }) {
  const [selectedDepartment, setSelectedDepartment] = useState('all');
  const [expandedDepartments, setExpandedDepartments] = useState(new Set(['sales', 'marketing', 'customer_success', 'operations']));

  const departments = D.DEPARTMENTS;

  function memberScore(member) {
    // Safety check for member object
    if (!member || typeof member !== 'object') {
      return { items: [], avgPct: 0, status: 'bad' };
    }

    const memberRegions = member.regions || [];
    const regionSet = filters.region?.length
      ? memberRegions.filter(r => filters.region.includes(r))
      : memberRegions;
    const localFilters = { ...filters, region: regionSet.length ? regionSet : memberRegions };
    const items = (member.kpiIds || []).map(id => {
      const k = D.STRATEGIC.find(x => x.id === id) || D.CUSTOMER_VALUE.find(x => x.id === id) || D.MAYA.find(x => x.id === id);
      if (!k) return null;
      const actual = computeFiltered(k, view, localFilters, 'actual');
      const target = computeFiltered(k, view, localFilters, 'target');
      return { k, actual, target, pct: D.pctToTarget(actual, target, k.higherIsBetter), status: D.health(actual, target, k.higherIsBetter) };
    }).filter(Boolean);
    const avgPct = items.length ? items.reduce((s, i) => s + i.pct, 0) / items.length : 0;
    return { items, avgPct, status: avgPct >= 0 ? 'good' : avgPct >= -15 ? 'warn' : 'bad' };
  }

  // Active owner filter membership check
  function visibleMembers(members) {
    if (!filters.owner?.length) return members;
    return members.filter(m => {
      const memberName = typeof m === 'string' ? m : m.name;
      return filters.owner.includes(memberName);
    });
  }

  // Filter departments based on dropdown selection
  const visibleDepartments = selectedDepartment === 'all'
    ? departments
    : departments.filter(dept => dept.id === selectedDepartment);

  const toggleDepartment = (deptId) => {
    const newExpanded = new Set(expandedDepartments);
    if (newExpanded.has(deptId)) {
      newExpanded.delete(deptId);
    } else {
      newExpanded.add(deptId);
    }
    setExpandedDepartments(newExpanded);
  };

  return (
    <>
      {/* Department Filter Dropdown */}
      <div className="section-title">
        Team Performance by Department
        <div className="department-filter-container">
          <select
            className="department-filter"
            value={selectedDepartment}
            onChange={(e) => setSelectedDepartment(e.target.value)}
          >
            <option value="all">All Departments</option>
            {departments.map(dept => (
              <option key={dept.id} value={dept.id}>{dept.label}</option>
            ))}
          </select>
        </div>
      </div>

      <div className="dept-overview">
        <div className="dept-overview-label">Executive Summary · Q2 2026 Performance by Department</div>
        <div className="dept-overview-grid">
          {visibleDepartments.map(dep => {
            const members = visibleMembers(dep.members || []).filter(m => m && typeof m === 'object');
            if (!members.length) return null;
            const avgPct = members.reduce((sum, m) => sum + memberScore(m).avgPct, 0) / members.length;
            const status = avgPct >= 0 ? 'good' : avgPct >= -15 ? 'warn' : 'bad';
            return (
              <div key={dep.id} className={`dept-stat dept-${status}`}>
                <div className="dept-stat-icon" style={{ background: dep.color }}>{dep.icon}</div>
                <div className="dept-stat-body">
                  <div className="dept-stat-label">{dep.label}</div>
                  <div className="dept-stat-sub text-xs text-muted">{dep.sub}</div>
                  <div className={`dept-stat-pct text-${status === 'good' ? 'good' : status === 'warn' ? 'warn' : 'bad'}`}>
                    {avgPct >= 0 ? '+' : ''}{avgPct.toFixed(0)}%
                  </div>
                  <div className="text-xs text-muted">{members.length} {members.length === 1 ? 'lead' : 'leads'}</div>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {visibleDepartments.map(dep => {
        const members = visibleMembers(dep.members);
        if (!members.length) return null;
        const isExpanded = expandedDepartments.has(dep.id);
        const avgPct = members.reduce((sum, m) => sum + memberScore(m).avgPct, 0) / members.length;
        const status = avgPct >= 0 ? 'good' : avgPct >= -15 ? 'warn' : 'bad';

        return (
          <div key={dep.id} className="dept-block">
            <button
              className="dept-header dept-header-clickable"
              onClick={() => toggleDepartment(dep.id)}
            >
              <div className="dept-header-left">
                <div className="dept-icon" style={{ background: dep.color }}>{dep.icon}</div>
                <div>
                  <div className="dept-title">{dep.label}</div>
                  <div className="dept-sub text-xs text-muted">{dep.sub}</div>
                </div>
              </div>
              <div className="dept-header-right">
                <div className="dept-performance">
                  <span className={`text-${status === 'good' ? 'good' : status === 'warn' ? 'warn' : 'bad'}`}>
                    {avgPct >= 0 ? '+' : ''}{avgPct.toFixed(0)}%
                  </span> avg
                </div>
                <span className="dept-count">{members.length} {members.length === 1 ? 'member' : 'members'}</span>
                <span className="dept-chevron">{isExpanded ? '▼' : '▶'}</span>
              </div>
            </button>

            {isExpanded && (
              <div className="owner-grid">
                {members.map(o => {
                  // Handle both string and object member formats
                  const memberName = typeof o === 'string' ? o : (o.name || 'Unknown');
                  const memberTitle = typeof o === 'object' ? (o.title || 'Team Member') : 'Team Member';
                  const memberRegions = typeof o === 'object' ? (o.regions || ['Global']) : ['Global'];

                  const memberObj = typeof o === 'string' ? { name: o, title: 'Team Member', regions: ['Global'], kpiIds: [] } : o;
                  const s = memberScore(memberObj);
                  const initials = memberName.split(' ').map(n => n[0]).join('').toUpperCase().slice(0,2);
                  return (
                    <div key={memberName} className={`owner-card owner-${s.status}`}>
                      <div className="owner-head">
                        <div className={`owner-avatar owner-avatar-${s.status}`}>{initials}</div>
                        <div className="owner-info">
                          <div className="owner-name">{memberName}</div>
                          <div className="owner-title">{memberTitle}</div>
                          <div className="owner-scope text-xs text-muted">
                            {memberRegions.length === D.REGIONS.length ? 'Global' : memberRegions.join(', ')}
                          </div>
                        </div>
                        <div className={`owner-score text-${s.status === 'good' ? 'good' : s.status === 'warn' ? 'warn' : 'bad'}`}>
                          {s.avgPct >= 0 ? '+' : ''}{s.avgPct.toFixed(0)}%
                        </div>
                      </div>
                      <div className="owner-kpi-list">
                        <div className="kpi-list-header">
                          <span>Assigned KPIs ({s.items.length})</span>
                        </div>
                        {s.items.map(({ k, actual, target, pct, status }) => (
                          <button key={k.id} className="owner-kpi-row" onClick={() => onKpiClick(k)}>
                            <span className={`dot status-${status}`}/>
                            <span className="kpi-name">{k.name}</span>
                            <span className="kpi-values">
                              <span className="val">{D.fmt(actual, k.format)}</span>
                              <span className="tgt">/ {D.fmt(target, k.format)}</span>
                            </span>
                            <span className={`delta text-${status === 'good' ? 'good' : status === 'warn' ? 'warn' : 'bad'}`}>
                              {pct >= 0 ? '+' : ''}{pct.toFixed(0)}%
                            </span>
                          </button>
                        ))}
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        );
      })}
    </>
  );
}

// ============================================================================
// Teams Section — picker (tile grid) + leaf-team page (anchor row + MarchCards)
//
// Two render modes:
//   1. Picker (selectedTeam = 'picker', 'all', null, or any cluster id) —
//      shows TeamPicker: a tile grid of every leaf team grouped under cluster
//      headers. Cluster headers (CRO, Product & Tech) are not clickable to
//      their own page; only leaf teams navigate.
//   2. Leaf team (any leaf id like 'finance', 'cro:gm-latam', 'hr', …) —
//      shows TeamPage: anchor row at top (any tier='anchor' KPIs the team owns)
//      + strategic KPI cards grouped by pillar, using the same MarchCard
//      component as Executive Overview.
//
// Sub-team click on a card → onKpiClick handler (existing right-side drawer).
// ============================================================================

// Picker tile — single team or sub-team.
function TeamPickerTile({ teamLabel, kpiCount, onClick, pillar, anchorName, hasData }) {
  return (
    <div
      className={`team-picker-tile${hasData ? '' : ' no-data'}`}
      onClick={hasData ? onClick : undefined}>
      {pillar && <div className="team-picker-tile-pillar">{pillar}</div>}
      <div className="team-picker-tile-name">
        {teamLabel}{hasData && <span className="team-picker-tile-arrow"> →</span>}
      </div>
      <div className="team-picker-tile-meta">
        {hasData
          ? (anchorName ? `Anchor: ${anchorName} · ` : '') + `${kpiCount} KPI${kpiCount === 1 ? '' : 's'}`
          : 'No KPIs mapped yet'}
      </div>
    </div>
  );
}

// Picker grid — clusters with sub-teams render as section headers + sub-team
// tiles; standalone clusters fall under "Other teams".
function TeamPicker({ clusters, kpiTeamMap, findKpi, onSelectTeam }) {
  const clustersWithChildren = clusters.filter(c => c.id !== 'all' && c.children && c.children.length);
  const standalone = clusters.filter(c => c.id !== 'all' && (!c.children || !c.children.length));

  // Resolve metadata for a leaf node (kpiCount, owned anchor name, pillar tag).
  const resolveLeaf = (node) => {
    const ids = (node.kpiKey && kpiTeamMap[node.kpiKey]) || [];
    const kpis = ids.map(findKpi).filter(Boolean);
    const anchor = kpis.find(k => k.tier === 'anchor');
    // Pick the most-represented pillar tag for the tile chip.
    const pillarCount = {};
    kpis.forEach(k => { if (k.pillar) pillarCount[k.pillar] = (pillarCount[k.pillar] || 0) + 1; });
    const topPillar = Object.entries(pillarCount).sort((a, b) => b[1] - a[1])[0];
    return {
      kpiCount: kpis.length,
      anchorName: anchor ? anchor.name : null,
      pillar: topPillar ? `${PILLAR_TAGS[topPillar[0]] || ''} · ${topPillar[0]}`.trim() : null,
    };
  };

  return (
    <div className="team-picker">
      <p className="team-picker-intro">
        Pick a team. Cluster headers (CRO, Product &amp; Tech) are just labels — only individual teams are clickable.
      </p>
      {clustersWithChildren.map(cluster => (
        <div key={cluster.id}>
          <div className="team-picker-cluster-head">{cluster.label}</div>
          <div className="team-picker-tiles">
            {cluster.children.map(sub => {
              const meta = resolveLeaf(sub);
              return (
                <TeamPickerTile
                  key={sub.id}
                  teamLabel={sub.label}
                  kpiCount={meta.kpiCount}
                  pillar={meta.pillar}
                  anchorName={meta.anchorName}
                  hasData={meta.kpiCount > 0}
                  onClick={() => onSelectTeam && onSelectTeam(sub.id)}
                />
              );
            })}
          </div>
        </div>
      ))}
      {standalone.length > 0 && (
        <div>
          <div className="team-picker-cluster-head">Other teams</div>
          <div className="team-picker-tiles">
            {standalone.map(cluster => {
              const meta = resolveLeaf(cluster);
              return (
                <TeamPickerTile
                  key={cluster.id}
                  teamLabel={cluster.label}
                  kpiCount={meta.kpiCount}
                  pillar={meta.pillar}
                  anchorName={meta.anchorName}
                  hasData={meta.kpiCount > 0}
                  onClick={() => onSelectTeam && onSelectTeam(cluster.id)}
                />
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

// Leaf team page — same layout as Executive Overview, filtered to this team.
function TeamPage({ node, parentCluster, kpiTeamMap, findKpi, monthKey, hasData, activeQ, activeMonth, handleQClick, setActiveMonth, onKpiClick }) {
  const ids = (node.kpiKey && kpiTeamMap[node.kpiKey]) || [];
  const kpis = ids.map(findKpi).filter(Boolean);
  // Anchors first; then the rest grouped by pillar.
  const anchors = kpis.filter(k => k.tier === 'anchor');
  const nonAnchors = kpis.filter(k => k.tier !== 'anchor');
  const byPillar = {};
  nonAnchors.forEach(k => {
    const p = k.pillar || 'Other';
    (byPillar[p] = byPillar[p] || []).push(k);
  });
  const orderedPillars = (M.PILLARS || []).map(p => p.name).filter(p => byPillar[p]);
  Object.keys(byPillar).forEach(p => { if (!orderedPillars.includes(p)) orderedPillars.push(p); });

  const headerSubtitle = parentCluster
    ? `Sub-team of ${parentCluster.label} · ${kpis.length} KPI${kpis.length === 1 ? '' : 's'}`
    : `${kpis.length} KPI${kpis.length === 1 ? '' : 's'}`;

  return (
    <div className="teams-overview content" style={{ backgroundColor: '#FAFBFC', minHeight: '100vh', padding: '24px 28px' }}>
      <div className="page-header" style={{ marginBottom: 16 }}>
        {parentCluster && (
          <div style={{ fontSize: 12, color: '#94A3B8', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 4 }}>
            {parentCluster.label}
          </div>
        )}
        <h1 style={{ fontSize: 28, fontWeight: 700, color: '#0F172A', margin: '0 0 6px 0', letterSpacing: '-0.01em' }}>
          {node.label}
        </h1>
        <p style={{ fontSize: 13, color: '#64748B', margin: 0 }}>{headerSubtitle}</p>
      </div>

      {/* Period selector — same Q1-Q4 + months as Executive Overview, shared state. */}
      <div className="exec-overview-topbar stacked" style={{ marginBottom: 18 }}>
        <div className="exec-overview-period-row">
          {['Q1', 'Q2', 'Q3', 'Q4'].map(q => (
            <button
              key={q}
              className={`exec-overview-period-btn quarter${activeQ === q ? ' active' : ''}`}
              onClick={() => handleQClick(q)}>
              {q}
            </button>
          ))}
        </div>
        <div className="exec-overview-period-row month">
          {Q_MONTHS[activeQ].map(m => (
            <button
              key={m}
              className={`exec-overview-period-btn month${activeMonth === m ? ' active' : ''}`}
              onClick={() => setActiveMonth(m)}>
              {m}
            </button>
          ))}
        </div>
      </div>

      {kpis.length === 0 && (
        <div style={{ padding: '20px 24px', background: '#fff', border: '1px dashed #CBD5E1', borderRadius: 12, color: '#64748B', fontSize: 14 }}>
          No KPIs mapped to <strong style={{ color: '#1E293B' }}>{node.label}</strong> yet.
        </div>
      )}

      {/* Anchor row — only for teams that own anchor KPIs. */}
      {anchors.length > 0 && (
        <div className="exec-overview-anchors" style={{ marginBottom: 24 }}>
          {anchors.map(a => {
            const pillar = M.PILLARS.find(p => p.name === a.pillar) || { tag: '', name: a.pillar };
            return <AnchorCard key={a.id} pillar={pillar} anchor={a} hasData={hasData} monthKey={monthKey} />;
          })}
        </div>
      )}

      {/* Strategic KPI cards grouped by pillar — same MarchCard layout as Executive Overview. */}
      {orderedPillars.map(pillarName => {
        const groupKpis = byPillar[pillarName];
        return (
          <div key={pillarName} className="exec-overview-pillar-group">
            <div className="pgh">
              <span className="pgh-tag">{PILLAR_TAGS[pillarName] || ''}</span>
              <span className="pgh-name">{pillarName}</span>
            </div>
            <div className="exec-overview-march-grid">
              {groupKpis.map(k => (
                <MarchCard
                  key={k.id}
                  kpi={k}
                  isActive={false}
                  onClick={() => onKpiClick && onKpiClick(k)}
                  hasData={hasData}
                  monthKey={monthKey}
                />
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

function TeamsSection({ filters, setFilters, view, setView, onKpiClick, selectedTeam = 'picker', onSelectTeam, activeQ: extQ, setActiveQ: extSetQ, activeMonth: extMonth, setActiveMonth: extSetMonth }) {
  // Shared period state — Teams syncs with Executive Overview.
  const [localQ, setLocalQ] = useState(extQ || CURRENT_Q);
  const [localMonth, setLocalMonth] = useState(extMonth || CURRENT_MONTH);
  const activeQ = extQ !== undefined ? extQ : localQ;
  const setActiveQ = extSetQ || setLocalQ;
  const activeMonth = extMonth !== undefined ? extMonth : localMonth;
  const setActiveMonth = extSetMonth || setLocalMonth;
  // Same generalized resolver as OverviewSection — any (Q, month) pair that
  // matches Q_MONTHS produces a canonical lowercase monthKey.
  const monthKey = (Q_MONTHS[activeQ] && Q_MONTHS[activeQ].includes(activeMonth))
    ? monthLabelToKey(activeMonth)
    : null;
  const hasData = monthKey !== null;
  const defaultMonthFor = (q) => {
    if (q === CURRENT_Q) return CURRENT_MONTH;
    const order = ['Q1', 'Q2', 'Q3', 'Q4'];
    return order.indexOf(q) < order.indexOf(CURRENT_Q) ? Q_MONTHS[q][2] : Q_MONTHS[q][0];
  };
  const handleQClick = (q) => { setActiveQ(q); setActiveMonth(defaultMonthFor(q)); };

  const clusters = D.TEAM_CLUSTERS || [];
  const kpiTeamMap = D.KPI_TEAM_MAP || {};
  // Resolve a KPI id against the cascade-aware March 2026 dataset (preferred) or
  // the headline drilldowns. Falls back to legacy D.STRATEGIC if neither has it.
  const findKpi = useMemo(() => {
    const headlines = M.HEADLINES ? Object.values(M.HEADLINES).flat() : [];
    const headlineById = Object.fromEntries(headlines.map(h => [h.id, h]));
    return (id) => M.STRATEGIC_BY_ID[id] || headlineById[id] || null;
  }, []);

  // Resolve the selected node + parent cluster (for breadcrumb).
  const { node, parentCluster, isPicker } = useMemo(() => {
    if (!selectedTeam || selectedTeam === 'picker' || selectedTeam === 'all') {
      return { node: null, parentCluster: null, isPicker: true };
    }
    for (const c of clusters) {
      if (c.id === selectedTeam) {
        // Cluster WITH children → picker (clusters don't have their own page).
        if (c.children && c.children.length) return { node: c, parentCluster: null, isPicker: true };
        // Standalone cluster (Finance, AI, HR, …) → leaf-team page.
        return { node: c, parentCluster: null, isPicker: false };
      }
      if (c.children) {
        const sub = c.children.find(s => s.id === selectedTeam);
        if (sub) return { node: sub, parentCluster: c, isPicker: false };
      }
    }
    return { node: null, parentCluster: null, isPicker: true };
  }, [selectedTeam, clusters]);

  if (isPicker) {
    return (
      <div className="teams-overview content" style={{ backgroundColor: '#FAFBFC', minHeight: '100vh', padding: '24px 28px' }}>
        <div className="page-header" style={{ marginBottom: 16 }}>
          <h1 style={{ fontSize: 28, fontWeight: 700, color: '#0F172A', margin: '0 0 6px 0', letterSpacing: '-0.01em' }}>Teams</h1>
          <p style={{ fontSize: 13, color: '#64748B', margin: 0 }}>Pick a team to see its KPIs</p>
        </div>
        <TeamPicker clusters={clusters} kpiTeamMap={kpiTeamMap} findKpi={findKpi} onSelectTeam={onSelectTeam} />
      </div>
    );
  }

  return (
    <TeamPage
      node={node}
      parentCluster={parentCluster}
      kpiTeamMap={kpiTeamMap}
      findKpi={findKpi}
      monthKey={monthKey}
      hasData={hasData}
      activeQ={activeQ}
      activeMonth={activeMonth}
      handleQClick={handleQClick}
      setActiveMonth={setActiveMonth}
      onKpiClick={onKpiClick}
    />
  );
}

window.YUNO_SECTIONS = { OverviewSection, AllKpisSection, PipelineSection, RegionsSection, SourcesSection, EfficiencySection, CustomerSection, OwnersSection, LeadFunnelSection, TeamsSection };
})();
