/* global React */
/* Demand Profiling — Customer Health cards + drill-down drawer */

/* ============================================================
   Derive per-customer demand-profile metrics from existing data.
   - Containers / month, monthly revenue, total potential demand
   - 12m historical series for: total demand, enquired, won
   - 3-month forecast for total demand
   - Per-lane: customer demand, enquiries received/converted, gap
   - Lane-specific agentic actions
   ============================================================ */
function useCustomerMetrics(c) {
  return React.useMemo(() => deriveCustomerMetrics(c), [c.name, c.volume]);
}

function deriveCustomerMetrics(c) {
  /* Assumed average revenue per TEU for sea-heavy customers */
  const AVG_REV_PER_TEU = 4500;
  /* Won TEU/month = revenue / avg rev per TEU / 12 */
  const wonPerMonth = Math.max(2, Math.round(c.volume / AVG_REV_PER_TEU / 12));
  const monthlyRevenue = Math.round(c.volume / 12);
  /* Win rate (%) — fall back if missing */
  const winRate = c.convCurrent || c.conversion || 30;
  /* Enquired per month = won / winRate */
  const enquiredPerMonth = Math.max(wonPerMonth, Math.round(wonPerMonth / Math.max(winRate / 100, 0.1)));
  /* Total potential demand — function of tier (share of wallet we have) */
  const tierMult = c.tier === 'Enterprise' ? 1.8 : c.tier === 'Growth' ? 2.4 : 3.2;
  const totalPotential = Math.round(enquiredPerMonth * tierMult);

  /* Seeded deterministic noise per customer */
  const seed = (n) => {
    const x = Math.sin((c.name.length + n) * 47.13) * 10000;
    return x - Math.floor(x);
  };

  const months = ['Jun','Jul','Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar','Apr','May'];
  /* Slight upward drift; demand surging customers grow more */
  const slope = (c.demandSlope || 0.4) / 10;

  const totalSeries = months.map((_, i) => Math.max(8, Math.round(
    totalPotential * (0.88 + i * slope * 0.18 + (seed(i + 1) - 0.5) * 0.08)
  )));
  const enquiredSeries = months.map((_, i) => Math.max(2, Math.round(
    enquiredPerMonth * (0.78 + i * slope * 0.22 + (seed(i + 20) - 0.5) * 0.14)
  )));
  /* Won fluctuates more with win-rate trend */
  const convDeltaTrend = (c.convDelta || 0) / 100;
  const wonSeries = months.map((_, i) => Math.max(1, Math.round(
    wonPerMonth * (0.7 + i * (slope + convDeltaTrend) * 0.18 + (seed(i + 40) - 0.5) * 0.18)
  )));

  /* Forecast: next 3 months — total demand only */
  const forecastMonths = ['Jun+1','Jun+2','Jun+3'];
  const lastTotal = totalSeries[totalSeries.length - 1];
  const forecastSeries = forecastMonths.map((_, i) => Math.round(
    lastTotal * (1.04 + i * 0.05 + slope * 1.4)
  ));
  const forecastBand = forecastSeries.map((v) => ({
    expected: v,
    low: Math.round(v * 0.86),
    high: Math.round(v * 1.16),
  }));

  /* Per-lane breakdown (using existing lanes share). */
  const lanesPerformance = (c.lanes || []).map((l, i) => {
    const share = l.sharePct / 100;
    /* Customer total on this lane scales with overall potential + share + jitter */
    const laneDemand = Math.max(4, Math.round(totalPotential * share * (0.9 + seed(70 + i) * 0.4)));
    /* Enquiries received: a portion of demand reaches us */
    const reachFactor = 0.55 + seed(80 + i) * 0.35;
    const received = Math.max(2, Math.round(laneDemand * reachFactor));
    /* Converted (won) = a portion of received via win-rate ± lane noise */
    const laneWinFactor = Math.max(0.12, Math.min(0.95, (winRate / 100) + (seed(90 + i) - 0.5) * 0.4));
    const converted = Math.max(1, Math.round(received * laneWinFactor));
    /* Demand not received = customer demand that did NOT reach us as an enquiry */
    const gap = Math.max(0, laneDemand - received);
    /* Per-lane average markup: vary around customer baseline */
    const avgMarkup = +Math.max(3.5, Math.min(12.5,
      (c.markup || 7.5) + (seed(100 + i) - 0.5) * 2.4
    )).toFixed(1);
    const conversionPct = received > 0 ? Math.round((converted / received) * 100) : 0;
    return {
      origin: l.origin, dest: l.dest, mode: l.mode,
      laneDemand, received, converted, gap,
      lostInEnquiry: Math.max(0, received - converted),
      conversionPct,
      avgMarkup,
    };
  }).sort((a, b) => b.gap - a.gap);

  /* Lane-specific agentic actions — follow the deep-dive spec:
       1) Upsell ABC lane (draft upsell email)
       2) Offer better rates on XYZ lane — x% price reduction → y% win-rate lift, reduce markup from a → b
       3) Schedule QBR to focus on specific upsides
  */
  const laneActions = [];
  const totalGap = lanesPerformance.reduce((s, l) => s + l.gap, 0);

  if (lanesPerformance.length > 0) {
    const topGap = lanesPerformance[0];
    if (topGap.gap > 0) {
      laneActions.push({
        icon: 'mail-out', tone: 'agent',
        title: `Upsell ${topGap.origin} → ${topGap.dest} — ${topGap.gap} TEU/mo untapped`,
        reason: `Customer ships ~${topGap.laneDemand} TEU/mo on this lane but only enquires us for ${topGap.received}. Propose a volume commitment at ${(topGap.avgMarkup - 0.8).toFixed(1)}% markup in exchange for first-look on the remaining ${topGap.gap} TEU.`,
        cta: 'Draft upsell email',
      });
    }
    /* Worst conversion on lanes we already serve — concrete elasticity numbers */
    const worstConv = lanesPerformance
      .filter(l => l.received >= 3)
      .sort((a, b) => a.conversionPct - b.conversionPct)[0];
    if (worstConv && worstConv.conversionPct < 55) {
      const newMarkup = Math.max(3.5, worstConv.avgMarkup - 1.5);
      const priceReductionPct = +(((worstConv.avgMarkup - newMarkup) / (100 + worstConv.avgMarkup)) * 100).toFixed(1);
      /* simple elasticity: each 1pp markup cut ≈ +8pp win-rate */
      const winLiftPp = Math.round((worstConv.avgMarkup - newMarkup) * 8);
      const newConv = Math.min(95, worstConv.conversionPct + winLiftPp);
      laneActions.push({
        icon: 'trend-down', tone: 'brand',
        title: `Better rates on ${worstConv.origin} → ${worstConv.dest} — ${priceReductionPct}% off lifts win rate +${winLiftPp}pp`,
        reason: `Only ${worstConv.conversionPct}% win on ${worstConv.received} enquiries. Reduce markup ${worstConv.avgMarkup}% → ${newMarkup.toFixed(1)}% (≈${priceReductionPct}% price drop). Modeled win-rate ${worstConv.conversionPct}% → ${newConv}%, recovering ~${Math.round(worstConv.received * (newConv - worstConv.conversionPct) / 100)} TEU/mo we're already being asked for.`,
        cta: 'Apply new rate',
      });
    }
  }
  /* QBR action — always last, focused on top upside */
  if (totalGap >= 8 || lanesPerformance.length >= 3) {
    const topLanes = lanesPerformance.slice(0, 2).map(l => `${l.origin}→${l.dest}`).join(', ');
    laneActions.push({
      icon: 'users', tone: 'brand',
      title: `Schedule QBR — focus on ${topLanes}`,
      reason: `Across all lanes, ${totalGap} TEU/mo of customer demand isn't reaching us. Frame the QBR around the top 2 gaps and tie it to a renewal commitment.`,
      cta: 'Plan QBR',
    });
  }

  return {
    months, forecastMonths,
    wonPerMonth, monthlyRevenue, totalPotential, enquiredPerMonth, winRate,
    totalSeries, enquiredSeries, wonSeries, forecastSeries, forecastBand,
    lanesPerformance, laneActions,
    totalGap,
  };
}

/* Demand-trend agent actions, derived from forecast direction + recent history.
   Spec:
   - Confirm upcoming demand with customer (send email)
   - Investigate demand drop (schedule call with customer)
   - Understand tailwinds and headwinds in forecast
*/
function deriveDemandActions(c, m) {
  const last3 = m.totalSeries.slice(-3).reduce((a, b) => a + b, 0) / 3;
  const prev3 = m.totalSeries.slice(-6, -3).reduce((a, b) => a + b, 0) / 3;
  const recentDeltaPct = Math.round(((last3 - prev3) / prev3) * 100);
  const fcEnd = m.forecastSeries[m.forecastSeries.length - 1];
  const fcStart = m.totalSeries[m.totalSeries.length - 1];
  const fcDeltaPct = Math.round(((fcEnd - fcStart) / fcStart) * 100);
  const actions = [];

  /* Confirm upcoming demand — always relevant; tone shifts on direction */
  actions.push({
    icon: 'mail-out', tone: 'agent',
    title: fcDeltaPct >= 5
      ? `Confirm upcoming demand spike — +${fcDeltaPct}% over 3 months`
      : `Confirm next-quarter demand with customer`,
    reason: fcDeltaPct >= 5
      ? `Forecast shows ~${fcEnd} TEU/mo by month +3 (band ${m.forecastBand[2].low}–${m.forecastBand[2].high}). Lock pre-allocated capacity before peak by confirming the surge with AM ${c.am}.`
      : `Forecast holds at ~${fcEnd} TEU/mo (band ${m.forecastBand[2].low}–${m.forecastBand[2].high}). Send a short confirmation email to validate volume before committing rates.`,
    cta: 'Send email',
  });

  /* Investigate demand drop — only if there is one in recent or forecast window */
  if (recentDeltaPct <= -10 || c.demandDeltaPct <= -15) {
    actions.push({
      icon: 'phone', tone: 'warning',
      title: `Investigate demand drop — ${Math.abs(recentDeltaPct)}% softening last quarter`,
      reason: `Demand has fallen ${Math.abs(recentDeltaPct)}% over the last 3 months vs prior 3. Could be seasonal, internal restructure, or share loss to a competitor. Schedule a 20-min call with the buyer to diagnose.`,
      cta: 'Schedule call',
    });
  } else if (fcDeltaPct < 0) {
    actions.push({
      icon: 'phone', tone: 'warning',
      title: `Investigate softening forecast — ${fcDeltaPct}% over next 3 months`,
      reason: `Model expects ~${Math.abs(fcDeltaPct)}% reduction in customer demand. Get ahead of it with a discovery call — avoid being surprised at renewal.`,
      cta: 'Schedule call',
    });
  }

  /* Tailwinds / headwinds context — always shown last */
  const headlineTail = c.demandSlope > 0.5
    ? 'New product launch + post-Lunar-New-Year restock are tailwinds.'
    : c.industry === 'Apparel' ? 'Fall/Winter restock tailwind; container glut on Trans-Pac is a margin headwind.'
    : c.industry === 'Renewables' ? 'IRA-driven panel imports are a tailwind; AD/CVD reviews on solar cells are a headwind.'
    : c.industry === 'Pharma' ? 'Tailwind: cold-chain expansion in EU. Headwind: tighter GDP audits at FRA.'
    : 'Stable industry demand. Watch for spot-rate volatility on TPEB if Red Sea reroutes ease.';
  actions.push({
    icon: 'info', tone: 'brand',
    title: `Understand tailwinds & headwinds`,
    reason: `${headlineTail} Forecast band widens to ±${Math.round((m.forecastBand[2].high - m.forecastBand[2].low) / 2)} TEU at month +3 — model uncertainty is ${recentDeltaPct < 0 ? 'high' : 'moderate'}.`,
    cta: 'View context',
  });

  return actions;
}

/* Margin recommendation — stable / reduce / increase + projected impact on
   conversion, volume, and revenue. Uses simple elasticity:
     each +1pp markup ≈ −8pp conversion; each −1pp markup ≈ +8pp conversion
*/
function deriveMarginRecommendation(c, m) {
  const PORTFOLIO_AVG = 7.8;
  const currentMarkup = c.markup;
  const currentConv = c.convCurrent;
  const currentVolume = m.wonPerMonth;
  const currentRevenue = m.monthlyRevenue;

  let stance, delta;
  if (currentMarkup < PORTFOLIO_AVG - 0.8 && currentConv > 40 && c.convDelta >= 0) {
    /* Compressed margin, strong conversion → INCREASE */
    stance = 'increase';
    delta = +1.0;
  } else if (currentMarkup > PORTFOLIO_AVG + 1.0 && c.convDelta < -2) {
    /* High markup, conversion softening → REDUCE */
    stance = 'reduce';
    delta = -1.0;
  } else if (c.marginVariance > 1.8) {
    /* High variance → tighten (stable but with playbook update) */
    stance = 'stable';
    delta = 0;
  } else if (currentConv < 25) {
    /* Low conversion across the board → REDUCE */
    stance = 'reduce';
    delta = -0.8;
  } else {
    stance = 'stable';
    delta = 0;
  }

  const newMarkup = +(currentMarkup + delta).toFixed(1);
  /* Elasticity model */
  const convDeltaPp = Math.round(-delta * 8);
  const newConv = Math.max(5, Math.min(95, currentConv + convDeltaPp));
  const volRatio = newConv / currentConv;
  const newVolume = Math.max(1, Math.round(currentVolume * volRatio));
  /* Revenue = volume × avg ticket; avg ticket scales with markup */
  const ticketRatio = (1 + newMarkup / 100) / (1 + currentMarkup / 100);
  const newRevenue = Math.round(currentRevenue * volRatio * ticketRatio);
  const revenueDeltaPct = Math.round(((newRevenue - currentRevenue) / currentRevenue) * 100);

  return {
    stance, delta, newMarkup, newConv, newVolume, newRevenue, revenueDeltaPct, convDeltaPp,
    currentMarkup, currentConv, currentVolume, currentRevenue,
  };
}

window.PlanningScreen = function PlanningScreen({ role }) {
  const customers = window.CUSTOMER_HEALTH;
  const [sortBy, setSortBy] = React.useState('intervention'); // revenue | intervention | health
  const [filter, setFilter] = React.useState('all'); // all | enterprise | growth | standard
  const [q, setQ] = React.useState('');
  const [drawerId, setDrawerId] = React.useState(null);

  const sorted = React.useMemo(() => {
    let arr = [...customers];
    if (filter !== 'all') arr = arr.filter(c => c.tier.toLowerCase() === filter);
    if (q) arr = arr.filter(c => c.name.toLowerCase().includes(q.toLowerCase()));
    if (sortBy === 'revenue')     arr.sort((a, b) => b.volume - a.volume);
    if (sortBy === 'intervention') arr.sort((a, b) => b.actions.length * 10 + b.flags.length * 5 - (a.actions.length * 10 + a.flags.length * 5));
    if (sortBy === 'health')      arr.sort((a, b) => a.composite - b.composite);
    return arr;
  }, [customers, sortBy, filter, q]);

  /* Overall metrics */
  const totalRevenue = customers.reduce((s, c) => s + c.volume, 0);
  const avgHealth = Math.round(customers.reduce((s, c) => s + c.composite * c.volume, 0) / totalRevenue);
  const needInt = customers.filter(c => c.actions.length > 0 && c.composite < 65).length;
  const surging = customers.filter(c => c.demandDeltaPct >= 30).length;
  const totalRfqs = customers.reduce((s, c) => s + c.rfqs, 0);
  const avgMarkup = +(customers.reduce((s, c) => s + c.markup * c.volume, 0) / totalRevenue).toFixed(1);

  const selected = customers.find(c => c.name === drawerId);

  return (
    <div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* Header */}
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between' }}>
        <div>
          <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em' }}>Demand Profiling</div>
          <div style={{ fontSize: 13, color: 'var(--fg-3)', marginTop: 4, display: 'flex', alignItems: 'center', gap: 8 }}>
            <window.Icon name="sparkles" size={12} color="#7C5CFF"/>
            <span>Each customer has a <strong style={{ color: 'var(--fg-1)' }}>Customer Score (0–100)</strong> — higher means more chance of winning revenue. Refreshed 6m ago.</span>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          <button className="btn btn-sm btn-secondary">Last quarter</button>
          <button className="btn btn-sm btn-secondary" style={{ background: 'var(--brand-50)', borderColor: 'var(--brand-200)', color: 'var(--brand-700)' }}>This quarter</button>
          <button className="btn btn-sm btn-secondary"><window.Icon name="download" size={12}/> Export</button>
        </div>
      </div>

      {/* Overall metrics */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 12 }}>
        <div className="kpi">
          <div className="kpi-label">Active customers</div>
          <div className="kpi-value tnum">{customers.length}</div>
          <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>{customers.filter(c => c.tier === 'Enterprise').length} Enterprise · {customers.filter(c => c.tier === 'Growth').length} Growth</div>
        </div>
        <div className="kpi">
          <div className="kpi-label">Avg Customer Score · revenue-weighted</div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <div className="kpi-value tnum" style={{ color: avgHealth >= 60 ? 'var(--success-700)' : avgHealth >= 45 ? 'var(--warning-700)' : 'var(--danger-700)' }}>{avgHealth}</div>
            <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>/100</div>
          </div>
          <PortfolioBar customers={customers}/>
        </div>
        <div className="kpi">
          <div className="kpi-label">Intervention suggested</div>
          <div className="kpi-value tnum" style={{ color: 'var(--warning-700)', display: 'flex', alignItems: 'center', gap: 6 }}>
            <span className="agent-dot pulse" style={{ background: 'var(--warning-500)', boxShadow: '0 0 0 0 rgba(247,144,9,.45)' }}/>
            {needInt}
          </div>
          <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>Agent drafted actions for all</div>
        </div>
        <div className="kpi">
          <div className="kpi-label">Demand surging</div>
          <div className="kpi-value tnum" style={{ color: 'var(--brand-700)' }}>{surging}</div>
          <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>Capacity check recommended</div>
        </div>
        <div className="kpi">
          <div className="kpi-label">Portfolio · 12m</div>
          <div className="kpi-value tnum">${(totalRevenue / 1e6).toFixed(1)}M</div>
          <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>{totalRfqs} enquiries · avg markup {avgMarkup}%</div>
        </div>
      </div>

      {/* Controls */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
        <div style={{ fontSize: 11, color: 'var(--fg-3)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>Sort by</div>
        <div style={{ display: 'flex', gap: 4, padding: 2, background: 'var(--n-75)', borderRadius: 8 }}>
          {[
            { id: 'intervention', label: 'Intervention suggested', icon: 'sparkles' },
            { id: 'health',       label: 'Lowest health',          icon: 'alert' },
            { id: 'revenue',      label: 'Highest revenue',        icon: 'trend-up' },
          ].map(s => (
            <button key={s.id} onClick={() => setSortBy(s.id)} className="chip" style={{
              border: 0, background: sortBy === s.id ? 'white' : 'transparent',
              boxShadow: sortBy === s.id ? 'var(--shadow-xs)' : 'none',
            }}>
              <window.Icon name={s.icon} size={11}/>
              {s.label}
            </button>
          ))}
        </div>

        <div className="divider-v"/>

        <div style={{ display: 'flex', gap: 6 }}>
          {['all', 'enterprise', 'growth', 'standard'].map(f => (
            <button key={f} className={`chip ${filter === f ? 'active' : ''}`} onClick={() => setFilter(f)}
              style={{ textTransform: 'capitalize' }}>
              {f}
            </button>
          ))}
        </div>

        <div style={{ flex: 1 }}/>

        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, background: 'white', border: '1px solid var(--n-150)', borderRadius: 8, padding: '6px 10px', width: 240 }}>
          <window.Icon name="search" size={13} color="#98A2B3"/>
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Search customer…" style={{ border: 0, outline: 0, flex: 1, fontSize: 12.5, minWidth: 0, background: 'transparent' }}/>
        </div>
      </div>

      {/* Customer cards grid */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(380px, 1fr))', gap: 14 }}>
        {sorted.map(c => (
          <CustomerHealthCard key={c.name} c={c} onOpen={() => setDrawerId(c.name)}/>
        ))}
      </div>

      {/* Drill-down drawer */}
      {selected && <CustomerDrawer c={selected} onClose={() => setDrawerId(null)}/>}
    </div>
  );
};

/* ============== Portfolio bar (healthy/moderate/critical proportion) ============== */
function PortfolioBar({ customers }) {
  const healthy = customers.filter(c => c.composite >= 60).length;
  const moderate = customers.filter(c => c.composite >= 45 && c.composite < 60).length;
  const critical = customers.filter(c => c.composite < 45).length;
  const total = healthy + moderate + critical;
  return (
    <div style={{ display: 'flex', height: 6, borderRadius: 999, overflow: 'hidden', background: 'var(--n-75)', marginTop: 4 }}>
      <div style={{ width: `${(healthy / total) * 100}%`, background: 'var(--success-500)' }} title={`${healthy} healthy`}/>
      <div style={{ width: `${(moderate / total) * 100}%`, background: 'var(--warning-500)' }} title={`${moderate} moderate`}/>
      <div style={{ width: `${(critical / total) * 100}%`, background: 'var(--danger-500)' }} title={`${critical} critical`}/>
    </div>
  );
}

/* ============== Customer Health Card — simplified ==============
   Single hero score = "Customer Score" (win-likelihood × revenue potential).
   Card surfaces only what's actionable: score, revenue, trend, one flag/action.
*/
function CustomerHealthCard({ c, onOpen }) {
  const meta = c.composite >= 75 ? { label: 'Hot lead',   color: 'var(--success-700)', bar: 'var(--success-500)' }
             : c.composite >= 60 ? { label: 'Healthy',    color: 'var(--success-700)', bar: 'var(--success-500)' }
             : c.composite >= 45 ? { label: 'Watch',      color: 'var(--warning-700)', bar: 'var(--warning-500)' }
             : c.composite >= 30 ? { label: 'At risk',    color: '#C2410C',            bar: '#F97316' }
             : { label: 'Critical', color: 'var(--danger-700)', bar: 'var(--danger-500)' };

  const m = useCustomerMetrics(c);
  const capturePct = m.totalPotential > 0 ? Math.round((m.wonPerMonth / m.totalPotential) * 100) : 0;

  /* Pick the single most-actionable signal */
  const topFlag = c.flags[0];
  const topAction = c.actions[0];

  /* Tooltip text explaining the score */
  const scoreTooltip = 'Customer Score blends win likelihood (conversion trend) with revenue potential (demand trend + margin) on a 0–100 scale. Higher = more chance of winning revenue.';

  return (
    <div className="card customer-card"
      onClick={onOpen}
      onMouseEnter={e => { e.currentTarget.style.boxShadow = 'var(--shadow-md)'; e.currentTarget.style.borderColor = 'var(--n-200)'; }}
      onMouseLeave={e => { e.currentTarget.style.boxShadow = 'var(--shadow-xs)'; e.currentTarget.style.borderColor = 'var(--n-100)'; }}
      style={{
        padding: 0, cursor: 'pointer', overflow: 'hidden',
        transition: 'border-color .12s, box-shadow .12s',
        display: 'flex', flexDirection: 'column',
      }}
    >
      {/* Top: customer + score */}
      <div style={{ padding: '16px 18px 14px', display: 'flex', gap: 14, alignItems: 'flex-start' }}>
        {/* Score capsule */}
        <div
          title={scoreTooltip}
          style={{
            display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
            width: 76, padding: '10px 0',
            background: 'var(--n-25)', border: '1px solid var(--n-100)', borderRadius: 10,
            flexShrink: 0,
          }}>
          <div className="mono tnum" style={{ fontSize: 30, fontWeight: 700, color: meta.color, lineHeight: 1, letterSpacing: '-0.02em' }}>{c.composite}</div>
          <div style={{ fontSize: 9, color: 'var(--fg-4)', marginTop: 1, fontWeight: 600 }}>/ 100</div>
          <div style={{ fontSize: 10, color: meta.color, marginTop: 5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.04em' }}>{meta.label}</div>
        </div>

        {/* Customer info */}
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
            <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: '-0.01em' }}>{c.name}</div>
            <span className="badge b-neutral" style={{ fontSize: 10 }}>{c.tier}</span>
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--fg-3)', marginTop: 3 }}>{c.industry} · AM {c.am}</div>

          {/* Score explainer */}
          <div style={{ fontSize: 10.5, color: 'var(--fg-4)', marginTop: 8, lineHeight: 1.4, display: 'flex', alignItems: 'center', gap: 4 }}>
            <window.Icon name="info" size={10} color="#98A2B3"/>
            <span>Win likelihood × revenue potential</span>
          </div>
        </div>
      </div>

      {/* Simplified metric strip — booked vs potential + a single meta line */}
      <div style={{
        background: 'var(--n-25)',
        borderTop: '1px solid var(--n-75)',
        borderBottom: '1px solid var(--n-75)',
        padding: '12px 18px',
        display: 'flex', flexDirection: 'column', gap: 8,
      }}>
        {/* Primary: booked → potential */}
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, flexWrap: 'wrap' }}>
          <div style={{ display: 'inline-flex', alignItems: 'baseline', gap: 4 }}>
            <span className="mono tnum" style={{ fontSize: 22, fontWeight: 700, color: meta.color, letterSpacing: '-0.01em', lineHeight: 1 }}>{m.wonPerMonth}</span>
            <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--fg-3)' }}>TEU / mo</span>
          </div>
          <span style={{ fontSize: 11, color: 'var(--fg-4)' }}>of</span>
          <div style={{ display: 'inline-flex', alignItems: 'baseline', gap: 4 }}>
            <span className="mono tnum" style={{ fontSize: 16, fontWeight: 700, color: 'var(--fg-2)' }}>{m.totalPotential}</span>
            <span style={{ fontSize: 11, color: 'var(--fg-3)' }}>potential</span>
          </div>
          <div style={{ flex: 1 }}/>
          <span className="mono tnum" style={{ fontSize: 12, fontWeight: 700, color: capturePct >= 50 ? 'var(--success-700)' : capturePct >= 30 ? 'var(--warning-700)' : 'var(--danger-700)' }}>
            {capturePct}% capture
          </span>
        </div>

        {/* Progress bar — booked share of potential */}
        <div style={{ height: 4, borderRadius: 999, background: 'var(--n-100)', overflow: 'hidden' }}>
          <div style={{ width: `${Math.min(capturePct, 100)}%`, height: '100%', background: meta.bar, borderRadius: 999 }}/>
        </div>

        {/* Inline meta line — sub-stats */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, fontSize: 11.5, color: 'var(--fg-3)', flexWrap: 'wrap' }}>
          <span>
            <span className="mono tnum" style={{ color: 'var(--fg-1)', fontWeight: 700 }}>${(m.monthlyRevenue / 1000).toFixed(0)}K</span>
            <span style={{ color: 'var(--fg-3)' }}> / mo</span>
          </span>
          <span style={{ color: 'var(--n-200)' }}>·</span>
          <span>
            <span className="mono tnum" style={{ color: 'var(--fg-1)', fontWeight: 700 }}>{c.convCurrent}%</span>
            <span style={{ color: 'var(--fg-3)' }}> win rate</span>
            {c.convDelta !== 0 && (
              <span className="mono tnum" style={{ marginLeft: 4, color: c.convDelta > 0 ? 'var(--success-700)' : 'var(--danger-700)', fontWeight: 600 }}>
                {c.convDelta > 0 ? '+' : ''}{c.convDelta}pp
              </span>
            )}
          </span>
          <span style={{ color: 'var(--n-200)' }}>·</span>
          <span>
            <span className="mono tnum" style={{ color: 'var(--fg-1)', fontWeight: 700 }}>{c.openRfqs}</span>
            <span style={{ color: 'var(--fg-3)' }}> open</span>
          </span>
        </div>
      </div>

      {/* Footer: one signal */}
      <div style={{ padding: '10px 18px 12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, minHeight: 40 }}>
        {topAction ? (
          <span className="agent-chip" style={{ fontSize: 11 }}>
            <window.Icon name="sparkles" size={10}/>
            {topAction.title}
          </span>
        ) : topFlag ? (
          <span className={`badge b-${topFlag.severity}`} style={{ fontSize: 10.5 }}>
            <window.Icon name="alert" size={9}/>
            {topFlag.label}
          </span>
        ) : (
          <span className="badge b-success" style={{ fontSize: 10.5 }}>
            <window.Icon name="check" size={9}/>
            On track
          </span>
        )}
        <span style={{ fontSize: 11, color: 'var(--brand-600)', fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 4 }}>
          View details <window.Icon name="arrow-r" size={11}/>
        </span>
      </div>
    </div>
  );
}

function Stat({ label, value }) {
  return (
    <div>
      <div style={{ fontSize: 10, color: 'var(--fg-4)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>{label}</div>
      <div className="mono tnum" style={{ fontSize: 14, fontWeight: 700, color: 'var(--fg-1)', marginTop: 1 }}>{value}</div>
    </div>
  );
}

function MiniScore({ label, score, primary, secondary, delta, neutral }) {
  const color = score >= 60 ? 'var(--success-700)' : score >= 40 ? 'var(--warning-700)' : 'var(--danger-700)';
  const bg = score >= 60 ? 'var(--success-500)' : score >= 40 ? 'var(--warning-500)' : 'var(--danger-500)';
  const deltaColor = delta == null ? 'var(--fg-3)'
    : neutral ? (Math.abs(delta) < 15 ? 'var(--fg-2)' : delta > 0 ? 'var(--brand-700)' : 'var(--warning-700)')
    : (delta > 0 ? 'var(--success-700)' : delta < 0 ? 'var(--danger-700)' : 'var(--fg-3)');
  return (
    <div style={{ padding: '10px 12px', borderRight: '1px solid var(--n-75)', display: 'flex', flexDirection: 'column', gap: 3 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <span style={{ fontSize: 10, color: 'var(--fg-3)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>{label}</span>
        <span className="mono tnum" style={{ fontSize: 11, fontWeight: 700, color }}>{score}</span>
      </div>
      <div style={{ height: 3, borderRadius: 999, background: 'var(--n-75)', overflow: 'hidden' }}>
        <div style={{ width: `${score}%`, height: '100%', background: bg }}/>
      </div>
      <div className="mono tnum" style={{ fontSize: 13, fontWeight: 600, color: 'var(--fg-1)', marginTop: 2 }}>{primary}</div>
      <div style={{ fontSize: 10.5, color: deltaColor, fontWeight: 500 }}>{secondary}</div>
    </div>
  );
}

/* ============== Customer Drawer (large drill-down) ============== */
function CustomerDrawer({ c, onClose }) {
  const meta = c.composite >= 60 ? { label: 'Healthy', color: 'var(--success-700)', bg: 'var(--success-50)' }
             : c.composite >= 45 ? { label: 'Moderate', color: 'var(--warning-700)', bg: 'var(--warning-50)' }
             : c.composite >= 30 ? { label: 'Weak', color: '#C2410C', bg: '#FFF7ED' }
             : { label: 'Critical', color: 'var(--danger-700)', bg: 'var(--danger-50)' };
  const m = useCustomerMetrics(c);

  return (
    <div className="drawer-overlay" onClick={onClose}>
      <div className="drawer" onClick={e => e.stopPropagation()} style={{ width: 880, maxWidth: '95vw' }}>
        {/* Header */}
        <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--n-100)' }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between' }}>
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6 }}>
                <span className="badge b-neutral" style={{ fontSize: 10.5 }}>{c.tier}</span>
                <span style={{ fontSize: 11, color: 'var(--fg-3)' }}>{c.industry} · AM {c.am} · {c.tenure}</span>
              </div>
              <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.01em' }}>{c.name}</div>
              <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 5, display: 'flex', gap: 16, flexWrap: 'wrap' }}>
                <span>Containers <strong className="mono tnum" style={{ color: 'var(--fg-1)' }}>{m.wonPerMonth}</strong> TEU/mo</span>
                <span>Monthly $ <strong className="mono tnum" style={{ color: 'var(--fg-1)' }}>${(m.monthlyRevenue / 1000).toFixed(0)}K</strong></span>
                <span>Total potential <strong className="mono tnum" style={{ color: 'var(--fg-1)' }}>{m.totalPotential}</strong> TEU/mo</span>
                <span>Win rate <strong className="mono tnum" style={{ color: 'var(--fg-1)' }}>{c.convCurrent}%</strong></span>
                <span>Open enquiries <strong className="mono tnum" style={{ color: 'var(--fg-1)' }}>{c.openRfqs}</strong></span>
              </div>
            </div>
            <button className="icon-btn" onClick={onClose}><window.Icon name="x" size={16}/></button>
          </div>
        </div>

        {/* Body scroll */}
        <div style={{ flex: 1, overflow: 'auto', padding: 24, display: 'flex', flexDirection: 'column', gap: 22 }}>

          {/* ============================================================
              1. CUSTOMER HEALTH CARD — conversion, demand captured, margin
              ============================================================ */}
          <section>
            <div style={{ background: `linear-gradient(180deg, ${meta.bg}, white 80%)`, border: `1px solid ${meta.color}33`, borderRadius: 12, padding: 18 }}>
              <div style={{ display: 'grid', gridTemplateColumns: '140px 1fr', gap: 20, alignItems: 'center' }}>
                <window.CircularGauge score={c.composite} color={meta.color}/>
                <div>
                  <div style={{ fontSize: 10.5, fontWeight: 600, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Customer health</div>
                  <div style={{ fontSize: 22, fontWeight: 600, color: meta.color, letterSpacing: '-0.01em' }}>{meta.label}</div>
                  <div style={{ fontSize: 12, color: 'var(--fg-2)', marginTop: 4, lineHeight: 1.5 }}>
                    Score blends three signals — <strong>conversion</strong>, <strong>demand capture</strong>, and <strong>margin</strong>. Each section below drills into one and proposes next steps.
                  </div>
                  {c.flags.length > 0 && (
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 8 }}>
                      {c.flags.map((f, j) => (
                        <span key={j} className={`badge b-${f.severity}`} style={{ fontSize: 10.5 }}>
                          <window.Icon name="alert" size={9}/>
                          {f.label} · {f.detail}
                        </span>
                      ))}
                    </div>
                  )}
                </div>
              </div>

              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10, marginTop: 14, paddingTop: 14, borderTop: '1px solid white' }}>
                <window.SubScoreCard
                  title="Conversion · 40%"
                  icon="trend-up"
                  score={c.conversionScore}
                  primary={`${c.convCurrent}% win rate`}
                  secondary={`${c.convDelta > 0 ? '+' : ''}${c.convDelta}pp vs prior 6m`}
                />
                <window.SubScoreCard
                  title="Demand captured · 30%"
                  icon="package"
                  score={c.demandScore}
                  primary={`${m.totalPotential > 0 ? Math.round((m.wonPerMonth / m.totalPotential) * 100) : 0}% capture`}
                  secondary={`${m.wonPerMonth} of ${m.totalPotential} TEU/mo`}
                />
                <window.SubScoreCard
                  title="Margin · 30%"
                  icon="gauge"
                  score={c.marginScore}
                  primary={`${c.markup}% avg`}
                  secondary={`Variance ±${c.marginVariance}pp`}
                />
              </div>
            </div>
          </section>

          {/* ============================================================
              2. LANE-WISE DEMAND — plot + 3 suggestion cards
              ============================================================ */}
          <section>
            <SectionTitle
              title="Lane-wise demand"
              subtitle="Per lane: what the customer ships, what reaches us, what we win — and the markup we run there"
            />
            <LaneOpportunityChart m={m}/>
            {m.laneActions.length > 0 && (
              <SuggestionStack
                label="Lane opportunity"
                items={m.laneActions}
              />
            )}
          </section>

          {/* ============================================================
              3. DEMAND TREND — interactive plot + 3 suggestion cards
              ============================================================ */}
          <section>
            <SectionTitle
              title="Demand trend"
              subtitle="Total customer demand · enquired · won · account capture — with 3-month forecast"
            />
            <DemandProfileChart c={c} m={m}/>
            <SuggestionStack
              label="Demand actions"
              items={deriveDemandActions(c, m)}
            />
          </section>

          {/* ============================================================
              4. MARGIN TREND — quoted/accepted/variance + conversion overlay
              ============================================================ */}
          <section>
            <SectionTitle
              title="Margin trend"
              subtitle="Average quoted vs accepted markup, variance band, and conversion overlay (right axis)"
            />
            <MarginDrillDown c={c} m={m}/>
          </section>

        </div>
      </div>
    </div>
  );
}

/* ============== Suggestion stack — section-level action cards ============== */
function SuggestionStack({ label, items }) {
  if (!items || items.length === 0) return null;
  return (
    <div style={{ marginTop: 12 }}>
      <div style={{
        display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8,
        fontSize: 10.5, fontWeight: 700, color: 'var(--agent-700)',
        textTransform: 'uppercase', letterSpacing: '0.08em',
      }}>
        <window.Icon name="sparkles" size={11} color="#7C5CFF"/>
        <span>Agent suggestions · {label}</span>
        <span style={{ flex: 1, height: 1, background: 'var(--n-100)' }}/>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {items.map((a, i) => <window.ActionCard key={i} {...a}/>)}
      </div>
    </div>
  );
}

function SectionTitle({ title, subtitle }) {
  return (
    <div style={{ marginBottom: 10 }}>
      <div style={{ fontSize: 13.5, fontWeight: 600 }}>{title}</div>
      {subtitle && <div style={{ fontSize: 11.5, color: 'var(--fg-3)', marginTop: 2 }}>{subtitle}</div>}
    </div>
  );
}

/* ============== Margin drill-down — dual-axis: markup (left) + conversion (right) ============== */
function MarginDrillDown({ c, m }) {
  const history = c.marginHistory; // 12 months of {month, quoted, accepted}
  const convTrend = c.convTrend;   // 12 months of conversion %
  const w = 720, h = 220, padL = 40, padR = 44, padT = 14, padB = 30;

  /* Left axis (markup %) bounds */
  const marginVals = history.flatMap(h => [h.quoted, h.accepted]);
  const lMax = Math.max(...marginVals) + 1.2;
  const lMin = Math.max(0, Math.min(...marginVals) - 1.2);
  const lRange = lMax - lMin;
  /* Right axis (conversion %) bounds */
  const rMax = Math.max(...convTrend) * 1.15;
  const rMin = Math.max(0, Math.min(...convTrend) * 0.7);
  const rRange = rMax - rMin;

  const x = (i) => padL + (i / (history.length - 1)) * (w - padL - padR);
  const yL = (v) => padT + (1 - (v - lMin) / lRange) * (h - padT - padB);
  const yR = (v) => padT + (1 - (v - rMin) / rRange) * (h - padT - padB);

  /* Variance band — accepted ± marginVariance/2 — drawn as polygon */
  const halfV = c.marginVariance / 2;
  const bandHi = history.map((p, i) => [x(i), yL(p.accepted + halfV)]);
  const bandLo = history.map((p, i) => [x(i), yL(p.accepted - halfV)]);
  const bandPolygon = [
    ...bandHi,
    ...bandLo.slice().reverse(),
  ].map(([X, Y]) => `${X.toFixed(1)},${Y.toFixed(1)}`).join(' ');

  const quotedPts = history.map((p, i) => `${x(i).toFixed(1)},${yL(p.quoted).toFixed(1)}`).join(' ');
  const acceptedPts = history.map((p, i) => `${x(i).toFixed(1)},${yL(p.accepted).toFixed(1)}`).join(' ');
  const convPts = convTrend.map((v, i) => `${x(i).toFixed(1)},${yR(v).toFixed(1)}`).join(' ');

  const lastQuoted = history[history.length - 1].quoted;
  const lastAccepted = history[history.length - 1].accepted;
  const gap = +(lastQuoted - lastAccepted).toFixed(1);

  /* Accepted trend over 6m */
  const acc6 = history.slice(-6).reduce((a, p) => a + p.accepted, 0) / 6;
  const accPrev6 = history.slice(0, 6).reduce((a, p) => a + p.accepted, 0) / 6;
  const accDelta = +(acc6 - accPrev6).toFixed(1);
  const trendArrow = accDelta > 0.2 ? '▲' : accDelta < -0.2 ? '▼' : '◀';

  /* Rich agent recommendation */
  const rec = deriveMarginRecommendation(c, m);
  const stanceCfg = {
    increase: {
      label: 'Increase markup',
      icon: 'trend-up',
      bg: 'var(--agent-50)', border: 'var(--agent-200)', color: 'var(--agent-700)', btn: 'btn-agent',
      headline: `Compressed margin with strong conversion — there's headroom. Test ${rec.currentMarkup}% → ${rec.newMarkup}% on the next 5 RFQs.`,
    },
    reduce: {
      label: 'Reduce markup',
      icon: 'trend-down',
      bg: 'var(--warning-50)', border: 'var(--warning-200)', color: 'var(--warning-700)', btn: 'btn-secondary',
      headline: `Conversion is softening at the current markup. Reducing to ${rec.newMarkup}% trades a thinner per-shipment margin for more wins — and a higher monthly total.`,
    },
    stable: {
      label: 'Hold steady',
      icon: 'check-circle',
      bg: 'var(--success-50)', border: 'var(--success-100)', color: 'var(--success-700)', btn: 'btn-secondary',
      headline: c.marginVariance > 1.8
        ? `Avg markup is healthy but variance is high (±${c.marginVariance}pp). Hold at ${rec.currentMarkup}% and tighten the playbook — sweet spot is ±0.6pp.`
        : `Markup behavior is stable around ${rec.currentMarkup}%. Hold — agent will flag if conversion or competitive signal shifts.`,
    },
  }[rec.stance];

  return (
    <div className="card" style={{ padding: 16 }}>
      {/* Metric tiles */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, marginBottom: 14 }}>
        <MetricTile label="Avg quoted" value={`${c.quotedAvg.toFixed(1)}%`} sub="trailing 12m"/>
        <MetricTile label="Avg accepted" value={`${c.acceptedAvg.toFixed(1)}%`} sub={`gap −${gap}pp vs quoted`} color="var(--success-700)"/>
        <MetricTile label="Variance" value={`±${c.marginVariance}pp`} sub={c.marginVariance > 1.6 ? 'High — tighten playbook' : 'Within normal range'}/>
        <MetricTile
          label="Trend · 6m vs prior"
          value={`${trendArrow} ${accDelta > 0 ? '+' : ''}${accDelta}pp`}
          sub={`Conversion ${c.convCurrent}% · ${c.convDelta > 0 ? '+' : ''}${c.convDelta}pp`}
          color={accDelta > 0.2 ? 'var(--success-700)' : accDelta < -0.2 ? 'var(--danger-700)' : 'var(--fg-1)'}
        />
      </div>

      <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}>
        {/* Gridlines (left axis = markup) */}
        {[0, 0.25, 0.5, 0.75, 1].map(t => (
          <g key={t}>
            <line x1={padL} y1={padT + t * (h - padT - padB)} x2={w - padR} y2={padT + t * (h - padT - padB)} stroke="#F2F4F7" strokeWidth="1"/>
            <text x={padL - 6} y={padT + t * (h - padT - padB) + 3} fontSize="9.5" fill="#98A2B3" textAnchor="end" fontFamily="JetBrains Mono">
              {(lMax - t * lRange).toFixed(1)}%
            </text>
            {/* Right axis labels (conversion) */}
            <text x={w - padR + 6} y={padT + t * (h - padT - padB) + 3} fontSize="9.5" fill="#1659CB" textAnchor="start" fontFamily="JetBrains Mono">
              {Math.round(rMax - t * rRange)}%
            </text>
          </g>
        ))}
        {/* Axis labels */}
        <text x={padL - 4} y={padT - 4} fontSize="9" fill="#475467" textAnchor="end" fontWeight="600">Markup</text>
        <text x={w - padR + 4} y={padT - 4} fontSize="9" fill="#1659CB" textAnchor="start" fontWeight="600">Conv %</text>

        {/* Variance band around accepted */}
        <polygon points={bandPolygon} fill="#12B76A" opacity="0.10"/>

        {/* Conversion line (right axis, blue) */}
        <polyline points={convPts} fill="none" stroke="#1659CB" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" strokeDasharray="0"/>
        {convTrend.map((v, i) => <circle key={`c${i}`} cx={x(i)} cy={yR(v)} r="2.4" fill="#1659CB" stroke="white" strokeWidth="1.4"/>)}

        {/* Quoted line (purple) */}
        <polyline points={quotedPts} fill="none" stroke="#7C5CFF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
        {history.map((p, i) => <circle key={`q${i}`} cx={x(i)} cy={yL(p.quoted)} r="2.5" fill="white" stroke="#7C5CFF" strokeWidth="1.5"/>)}

        {/* Accepted line (green) — drawn on top */}
        <polyline points={acceptedPts} fill="none" stroke="#12B76A" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"/>
        {history.map((p, i) => <circle key={`a${i}`} cx={x(i)} cy={yL(p.accepted)} r="2.6" fill="white" stroke="#12B76A" strokeWidth="1.6"/>)}

        {/* Month labels */}
        {history.map((p, i) => <text key={i} x={x(i)} y={h - 6} fontSize="9.5" fill="#98A2B3" textAnchor="middle">{p.month}</text>)}
      </svg>

      {/* Legend */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, fontSize: 11, color: 'var(--fg-2)', marginTop: 6, flexWrap: 'wrap' }}>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span style={{ width: 18, height: 2, background: '#7C5CFF', borderRadius: 2 }}/>
          Quoted markup
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span style={{ width: 18, height: 2, background: '#12B76A', borderRadius: 2 }}/>
          Accepted markup
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span style={{ width: 14, height: 8, background: '#12B76A', opacity: 0.18, borderRadius: 2 }}/>
          Variance band (±{halfV.toFixed(1)}pp)
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span style={{ width: 18, height: 2, background: '#1659CB', borderRadius: 2 }}/>
          Conversion · right axis
        </span>
      </div>

      {/* Rich agent recommendation with impact projection */}
      <div style={{
        marginTop: 16,
        background: stanceCfg.bg, border: `1px solid ${stanceCfg.border}`,
        borderRadius: 12, overflow: 'hidden',
      }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '14px 16px 12px' }}>
          <div style={{
            width: 32, height: 32, borderRadius: 8,
            background: 'white', color: stanceCfg.color, border: `1px solid ${stanceCfg.border}`,
            display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
          }}>
            <window.Icon name={stanceCfg.icon} size={16}/>
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
              <span style={{ fontSize: 10.5, fontWeight: 700, color: stanceCfg.color, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Agent recommendation</span>
              <span style={{
                fontSize: 11, fontWeight: 700, color: stanceCfg.color,
                background: 'white', border: `1px solid ${stanceCfg.border}`,
                padding: '2px 8px', borderRadius: 999, letterSpacing: '0.04em',
              }}>{stanceCfg.label}</span>
            </div>
            <div style={{ fontSize: 12.5, color: 'var(--fg-2)', lineHeight: 1.5 }}>{stanceCfg.headline}</div>
          </div>
          <button className={`btn btn-sm ${stanceCfg.btn}`}>
            {rec.stance === 'stable' ? 'Confirm hold' : `Apply ${rec.newMarkup}%`}
          </button>
        </div>

        {/* Impact projection grid — only for reduce/increase */}
        {rec.stance !== 'stable' && (
          <div style={{
            display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0,
            borderTop: `1px solid ${stanceCfg.border}`, background: 'white',
          }}>
            <ImpactCell
              label="Markup"
              from={`${rec.currentMarkup}%`}
              to={`${rec.newMarkup}%`}
              delta={`${rec.delta > 0 ? '+' : ''}${rec.delta.toFixed(1)}pp`}
              deltaColor={rec.stance === 'increase' ? 'var(--success-700)' : 'var(--warning-700)'}
              positive={rec.stance === 'increase'}
            />
            <ImpactCell
              label="Conversion"
              from={`${rec.currentConv}%`}
              to={`${rec.newConv}%`}
              delta={`${rec.convDeltaPp > 0 ? '+' : ''}${rec.convDeltaPp}pp`}
              deltaColor={rec.convDeltaPp > 0 ? 'var(--success-700)' : 'var(--warning-700)'}
              positive={rec.convDeltaPp > 0}
            />
            <ImpactCell
              label="Volume"
              from={`${rec.currentVolume} TEU/mo`}
              to={`${rec.newVolume} TEU/mo`}
              delta={`${rec.newVolume - rec.currentVolume > 0 ? '+' : ''}${rec.newVolume - rec.currentVolume} TEU`}
              deltaColor={rec.newVolume >= rec.currentVolume ? 'var(--success-700)' : 'var(--warning-700)'}
              positive={rec.newVolume >= rec.currentVolume}
            />
            <ImpactCell
              label="Revenue · monthly"
              from={`$${(rec.currentRevenue / 1000).toFixed(0)}K`}
              to={`$${(rec.newRevenue / 1000).toFixed(0)}K`}
              delta={`${rec.revenueDeltaPct > 0 ? '+' : ''}${rec.revenueDeltaPct}%`}
              deltaColor={rec.revenueDeltaPct >= 0 ? 'var(--success-700)' : 'var(--warning-700)'}
              positive={rec.revenueDeltaPct >= 0}
              last
            />
          </div>
        )}
        {rec.stance === 'stable' && (
          <div style={{
            borderTop: `1px solid ${stanceCfg.border}`, background: 'white',
            padding: '10px 16px', display: 'flex', alignItems: 'center', gap: 8,
            fontSize: 11.5, color: 'var(--fg-3)',
          }}>
            <window.Icon name="info" size={12} color="#98A2B3"/>
            <span>Holding at <strong className="mono" style={{ color: 'var(--fg-1)' }}>{rec.currentMarkup}%</strong> projects ~<strong className="mono" style={{ color: 'var(--fg-1)' }}>{rec.currentVolume} TEU/mo</strong> at <strong className="mono" style={{ color: 'var(--fg-1)' }}>{rec.currentConv}%</strong> conversion · ~<strong className="mono" style={{ color: 'var(--fg-1)' }}>${(rec.currentRevenue / 1000).toFixed(0)}K/mo</strong>. Re-evaluate at next QBR.</span>
          </div>
        )}
      </div>
    </div>
  );
}

function ImpactCell({ label, from, to, delta, deltaColor, positive, last }) {
  return (
    <div style={{
      padding: '10px 14px',
      borderRight: last ? 0 : '1px solid var(--n-100)',
      display: 'flex', flexDirection: 'column', gap: 3,
    }}>
      <div style={{ fontSize: 10, color: 'var(--fg-3)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em' }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 2 }}>
        <span className="mono tnum" style={{ fontSize: 11.5, color: 'var(--fg-3)', textDecoration: 'line-through' }}>{from}</span>
        <window.Icon name="arrow-r" size={10} color="#98A2B3"/>
        <span className="mono tnum" style={{ fontSize: 14, fontWeight: 700, color: 'var(--fg-1)' }}>{to}</span>
      </div>
      <div style={{ display: 'inline-flex', alignItems: 'center', gap: 3, marginTop: 1 }}>
        <span style={{
          fontSize: 10.5, fontWeight: 700, color: deltaColor,
          display: 'inline-flex', alignItems: 'center', gap: 2,
        }}>
          <span>{positive ? '▲' : '▼'}</span>
          <span className="mono tnum">{delta}</span>
        </span>
      </div>
    </div>
  );
}

function MetricTile({ label, value, sub, color }) {
  return (
    <div style={{ background: 'var(--n-25)', border: '1px solid var(--n-100)', borderRadius: 8, padding: '10px 12px' }}>
      <div style={{ fontSize: 10.5, color: 'var(--fg-3)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>{label}</div>
      <div className="mono tnum" style={{ fontSize: 17, fontWeight: 700, marginTop: 4, color: color || 'var(--fg-1)' }}>{value}</div>
      <div style={{ fontSize: 10.5, color: 'var(--fg-3)', marginTop: 2 }}>{sub}</div>
    </div>
  );
}

/* ============== Demand profile chart — total / enquired / won + forecast ============== */
function DemandProfileChart({ c, m }) {
  const allMonths = [...m.months, ...m.forecastMonths];
  const histCount = m.months.length;
  const totalCount = allMonths.length;
  const w = 760, h = 220, padL = 44, padR = 16, padT = 14, padB = 30;
  const all = [
    ...m.totalSeries, ...m.enquiredSeries, ...m.wonSeries,
    ...m.forecastSeries, ...m.forecastBand.map(f => f.high),
  ];
  const max = Math.max(...all) * 1.12;
  const x = (i) => padL + (i / (totalCount - 1)) * (w - padL - padR);
  const y = (v) => padT + (1 - v / max) * (h - padT - padB);

  const totalPts = m.totalSeries.map((v, i) => [x(i), y(v)]);
  const enquiredPts = m.enquiredSeries.map((v, i) => [x(i), y(v)]);
  const wonPts = m.wonSeries.map((v, i) => [x(i), y(v)]);
  const fcExpectedPts = m.forecastSeries.map((v, i) => [x(histCount + i), y(v)]);
  const bandLow = m.forecastBand.map((f, i) => [x(histCount + i - 1 + 1), y(f.low)]);
  const bandHi  = m.forecastBand.map((f, i) => [x(histCount + i - 1 + 1), y(f.high)]);
  /* Bridge band from last historical point */
  const lastHistX = x(histCount - 1);
  const lastHistTotalY = y(m.totalSeries[histCount - 1]);
  const bandPolygon = [
    [lastHistX, lastHistTotalY],
    ...bandHi.map(([X, Y]) => [X, Y]),
    ...bandLow.slice().reverse().map(([X, Y]) => [X, Y]),
  ].map(([X, Y]) => `${X.toFixed(1)},${Y.toFixed(1)}`).join(' ');

  const fmtLine = (pts) => pts.map(([X, Y]) => `${X.toFixed(1)},${Y.toFixed(1)}`).join(' ');
  const totalLine = fmtLine(totalPts);
  const fcLine = fmtLine([[lastHistX, lastHistTotalY], ...fcExpectedPts]);
  const enquiredLine = fmtLine(enquiredPts);
  const wonLine = fmtLine(wonPts);

  /* Won-area for emphasis */
  const wonArea = `${padL},${h - padB} ${wonLine} ${x(histCount - 1)},${h - padB}`;

  const capturePct = m.totalPotential > 0 ? Math.round((m.wonPerMonth / m.totalPotential) * 100) : 0;
  const upsidePct = Math.max(0, 100 - capturePct);

  return (
    <div className="card" style={{ padding: 16 }}>
      {/* KPI strip */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, marginBottom: 14 }}>
        <PlanMetric label="Total customer demand" value={`${m.totalPotential}`} unit="TEU/mo" color="#475467"/>
        <PlanMetric label="Demand enquired" value={`${m.enquiredPerMonth}`} unit="TEU/mo" color="#1659CB"/>
        <PlanMetric label="Demand won" value={`${m.wonPerMonth}`} unit="TEU/mo" color="#12B76A"/>
        <PlanMetric label="Account capture" value={`${capturePct}%`} unit={`${upsidePct}% upside`} color={capturePct >= 50 ? 'var(--success-700)' : 'var(--warning-700)'}/>
      </div>

      <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}>
        {/* Gridlines */}
        {[0, 0.25, 0.5, 0.75, 1].map(t => (
          <g key={t}>
            <line x1={padL} y1={padT + t * (h - padT - padB)} x2={w - padR} y2={padT + t * (h - padT - padB)} stroke="#F2F4F7" strokeWidth="1"/>
            <text x={padL - 6} y={padT + t * (h - padT - padB) + 3} fontSize="9.5" fill="#98A2B3" textAnchor="end" fontFamily="JetBrains Mono">
              {Math.round((1 - t) * max)}
            </text>
          </g>
        ))}
        {/* Forecast separator */}
        <line x1={x(histCount - 1)} y1={padT} x2={x(histCount - 1)} y2={h - padB} stroke="#A3A3A3" strokeWidth="1" strokeDasharray="3 3"/>
        <rect x={x(histCount - 1)} y={padT} width={w - padR - x(histCount - 1)} height={h - padT - padB} fill="#7C5CFF" opacity="0.04"/>
        <text x={x(histCount - 1) + 4} y={padT + 10} fontSize="9.5" fill="#7C5CFF" fontFamily="Inter" fontWeight="600">Forecast →</text>

        {/* Forecast band (total demand) */}
        <polygon points={bandPolygon} fill="#475467" opacity="0.12"/>

        {/* Won area (light) */}
        <polygon points={wonArea} fill="#12B76A" opacity="0.10"/>

        {/* Total demand — historical */}
        <polyline points={totalLine} fill="none" stroke="#475467" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
        {/* Total demand — forecast (dashed) */}
        <polyline points={fcLine} fill="none" stroke="#475467" strokeWidth="2" strokeDasharray="5 4" strokeLinecap="round" strokeLinejoin="round"/>

        {/* Enquired */}
        <polyline points={enquiredLine} fill="none" stroke="#1659CB" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>

        {/* Won */}
        <polyline points={wonLine} fill="none" stroke="#12B76A" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"/>

        {/* Dots */}
        {totalPts.map(([X, Y], i) => <circle key={`t${i}`} cx={X} cy={Y} r="2.5" fill="white" stroke="#475467" strokeWidth="1.5"/>)}
        {fcExpectedPts.map(([X, Y], i) => <circle key={`f${i}`} cx={X} cy={Y} r="3" fill="#475467" opacity="0.7"/>)}
        {enquiredPts.map(([X, Y], i) => <circle key={`e${i}`} cx={X} cy={Y} r="2.5" fill="white" stroke="#1659CB" strokeWidth="1.5"/>)}
        {wonPts.map(([X, Y], i) => <circle key={`w${i}`} cx={X} cy={Y} r="2.6" fill="#12B76A" stroke="white" strokeWidth="1.5"/>)}

        {/* X labels */}
        {allMonths.map((mo, i) => (
          <text key={i} x={x(i)} y={h - 8} fontSize="9.5" fill={i >= histCount ? '#7C5CFF' : '#98A2B3'} textAnchor="middle" fontWeight={i >= histCount ? 600 : 400}>
            {i >= histCount ? `+${i - histCount + 1}m` : mo}
          </text>
        ))}
        {/* y label */}
        <text x={6} y={padT + 8} fontSize="9.5" fill="#98A2B3" fontFamily="JetBrains Mono">TEU/mo</text>
      </svg>

      {/* Legend */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, fontSize: 11.5, color: 'var(--fg-2)', marginTop: 6, flexWrap: 'wrap' }}>
        <LegendDot color="#475467" label="Total customer demand" dashed/>
        <LegendDot color="#1659CB" label="Demand enquired (to us)"/>
        <LegendDot color="#12B76A" label="Demand won" filled/>
        <div style={{ flex: 1 }}/>
        <span style={{ fontSize: 11, color: 'var(--fg-3)' }}>
          Upside <strong style={{ color: 'var(--warning-700)' }}>+{m.totalPotential - m.wonPerMonth} TEU/mo</strong> if we close the gap
        </span>
      </div>
    </div>
  );
}

function PlanMetric({ label, value, unit, color }) {
  return (
    <div style={{ padding: '10px 12px', background: 'var(--n-25)', border: '1px solid var(--n-100)', borderRadius: 8 }}>
      <div style={{ fontSize: 10, color: 'var(--fg-3)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>{label}</div>
      <div className="mono tnum" style={{ fontSize: 19, fontWeight: 700, color: color || 'var(--fg-1)', marginTop: 3, letterSpacing: '-0.01em' }}>{value}</div>
      <div style={{ fontSize: 10.5, color: 'var(--fg-3)', marginTop: 1 }}>{unit}</div>
    </div>
  );
}

function LegendDot({ color, label, dashed, filled }) {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
      {filled ? (
        <span style={{ width: 12, height: 4, borderRadius: 2, background: color }}/>
      ) : dashed ? (
        <svg width="20" height="6"><line x1="0" y1="3" x2="20" y2="3" stroke={color} strokeWidth="2" strokeDasharray="3 3"/></svg>
      ) : (
        <span style={{ width: 18, height: 2, background: color, borderRadius: 2 }}/>
      )}
      <span style={{ color: 'var(--fg-2)' }}>{label}</span>
    </span>
  );
}

/* ============== Lane opportunity — stacked horizontal bars + time window ============== */
function LaneOpportunityChart({ m }) {
  const [windowKey, setWindowKey] = React.useState('3m');
  const windows = [
    { id: '1m', label: '1 month', mult: 1 },
    { id: '3m', label: '3 months', mult: 3 },
    { id: '6m', label: '6 months', mult: 6 },
    { id: '12m', label: '12 months', mult: 12 },
  ];
  const win = windows.find(w => w.id === windowKey) || windows[1];

  /* Scale lane numbers by window multiplier */
  const lanes = m.lanesPerformance.map(l => ({
    ...l,
    laneDemand: l.laneDemand * win.mult,
    received: l.received * win.mult,
    converted: l.converted * win.mult,
    gap: l.gap * win.mult,
    lostInEnquiry: l.lostInEnquiry * win.mult,
  })).sort((a, b) => b.gap - a.gap);

  const totals = lanes.reduce((acc, l) => ({
    demand: acc.demand + l.laneDemand,
    received: acc.received + l.received,
    converted: acc.converted + l.converted,
    gap: acc.gap + l.gap,
  }), { demand: 0, received: 0, converted: 0, gap: 0 });
  const overallConv = totals.received > 0 ? Math.round((totals.converted / totals.received) * 100) : 0;
  const captureOfDemand = totals.demand > 0 ? Math.round((totals.converted / totals.demand) * 100) : 0;

  const maxBar = Math.max(...lanes.map(l => l.laneDemand)) || 1;

  return (
    <div className="card" style={{ padding: 16 }}>
      {/* Header: window selector + KPI strip */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14, flexWrap: 'wrap' }}>
        <div style={{ display: 'flex', gap: 3, padding: 2, background: 'var(--n-75)', borderRadius: 8 }}>
          {windows.map(opt => (
            <button key={opt.id} onClick={() => setWindowKey(opt.id)}
              className="chip" style={{
                border: 0, padding: '0 10px', height: 26,
                background: windowKey === opt.id ? 'white' : 'transparent',
                boxShadow: windowKey === opt.id ? 'var(--shadow-xs)' : 'none',
                fontSize: 11.5, fontWeight: 600,
              }}>
              {opt.label}
            </button>
          ))}
        </div>
        <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>Sorted by demand not received (desc)</div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, marginBottom: 16 }}>
        <PlanMetric label="Total customer demand" value={`${totals.demand}`} unit={`TEU · ${win.label}`} color="#475467"/>
        <PlanMetric label="Enquiries received" value={`${totals.received}`} unit={`TEU · ${Math.round((totals.received / totals.demand) * 100) || 0}% of demand`} color="#1659CB"/>
        <PlanMetric label="Enquiries converted" value={`${totals.converted}`} unit={`TEU · ${overallConv}% of received`} color="#12B76A"/>
        <PlanMetric label="Demand not received" value={`${totals.gap}`} unit={`TEU · ${captureOfDemand}% account capture`} color="var(--warning-700)"/>
      </div>

      {/* Bars */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        {/* Header row for the bars */}
        <div style={{ display: 'grid', gridTemplateColumns: '124px 1fr 74px 88px', gap: 12, fontSize: 10, color: 'var(--fg-3)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>
          <div>Lane</div>
          <div>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, marginRight: 14 }}>
              <span style={{ width: 10, height: 10, borderRadius: 2, background: '#12B76A' }}/> Won
            </span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, marginRight: 14 }}>
              <span style={{ width: 10, height: 10, borderRadius: 2, background: '#1659CB' }}/> Received not won
            </span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
              <span style={{ width: 10, height: 10, borderRadius: 2, background: 'repeating-linear-gradient(45deg, #FECACA 0 4px, #FFE4E1 4px 8px)' }}/> Demand not received
            </span>
          </div>
          <div style={{ textAlign: 'right' }}>Avg markup</div>
          <div style={{ textAlign: 'right' }}>Total · Conv</div>
        </div>

        {lanes.map((l, i) => {
          const wPct = (l.converted / maxBar) * 100;
          const lostPct = (l.lostInEnquiry / maxBar) * 100;
          const gapPct = (l.gap / maxBar) * 100;
          /* Markup color tone: vs portfolio average ~7.8% */
          const mkColor = l.avgMarkup >= 9 ? 'var(--success-700)'
            : l.avgMarkup >= 6.5 ? 'var(--fg-1)'
            : 'var(--warning-700)';
          const mkChip = l.avgMarkup >= 9 ? 'var(--success-50)'
            : l.avgMarkup >= 6.5 ? 'var(--n-50)'
            : 'var(--warning-50)';
          return (
            <div key={i} style={{ display: 'grid', gridTemplateColumns: '124px 1fr 74px 88px', gap: 12, alignItems: 'center' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
                <window.ModeIcon mode={l.mode} size={12} color="#667085"/>
                <span className="mono" style={{ fontSize: 12, fontWeight: 700 }}>{l.origin}→{l.dest}</span>
              </div>
              <div style={{
                position: 'relative', height: 22,
                background: 'var(--n-50)',
                borderRadius: 5, overflow: 'hidden', display: 'flex',
              }}>
                {l.converted > 0 && (
                  <div title={`Won ${l.converted} TEU`} style={{
                    width: `${wPct}%`, background: '#12B76A',
                    display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
                    paddingRight: 6, color: 'white', fontSize: 10.5, fontWeight: 700, fontFamily: 'JetBrains Mono',
                  }}>{wPct > 10 ? l.converted : ''}</div>
                )}
                {l.lostInEnquiry > 0 && (
                  <div title={`Received ${l.lostInEnquiry} TEU · not won`} style={{
                    width: `${lostPct}%`, background: '#1659CB',
                    display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
                    paddingRight: 6, color: 'white', fontSize: 10.5, fontWeight: 700, fontFamily: 'JetBrains Mono',
                  }}>{lostPct > 10 ? l.lostInEnquiry : ''}</div>
                )}
                {l.gap > 0 && (
                  <div title={`Demand not received: ${l.gap} TEU`} style={{
                    width: `${gapPct}%`,
                    background: 'repeating-linear-gradient(45deg, #FECACA 0 6px, #FFF1F0 6px 12px)',
                    border: '1px dashed #F97066',
                    borderLeft: 0,
                    display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
                    paddingRight: 6, color: 'var(--danger-700)', fontSize: 10.5, fontWeight: 700, fontFamily: 'JetBrains Mono',
                  }}>{gapPct > 10 ? l.gap : ''}</div>
                )}
              </div>
              {/* Markup chip */}
              <div title={`Average markup on this lane: ${l.avgMarkup}%`} style={{
                textAlign: 'right',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'flex-end',
              }}>
                <span style={{
                  display: 'inline-flex', alignItems: 'baseline', gap: 2,
                  padding: '3px 8px', borderRadius: 999, background: mkChip,
                  border: '1px solid var(--n-100)',
                }}>
                  <span className="mono tnum" style={{ fontSize: 12, fontWeight: 700, color: mkColor }}>{l.avgMarkup}</span>
                  <span style={{ fontSize: 10, color: 'var(--fg-3)', fontWeight: 600 }}>%</span>
                </span>
              </div>
              <div style={{ textAlign: 'right', minWidth: 0 }}>
                <div className="mono tnum" style={{ fontSize: 12.5, fontWeight: 700 }}>{l.laneDemand} TEU</div>
                <div style={{ fontSize: 10.5, color: l.conversionPct >= 50 ? 'var(--success-700)' : l.conversionPct >= 25 ? 'var(--warning-700)' : 'var(--danger-700)', fontWeight: 600 }}>
                  {l.conversionPct}% conv
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ============== Conversion chart ============== */
function ConversionChart({ values }) {
  const w = 700, h = 110, padL = 28, padR = 12, padT = 10, padB = 20;
  const max = Math.max(...values) * 1.1;
  const min = Math.max(0, Math.min(...values) * 0.85);
  const range = max - min;
  const x = (i) => padL + (i / (values.length - 1)) * (w - padL - padR);
  const y = (v) => padT + (1 - (v - min) / range) * (h - padT - padB);
  const pts = values.map((v, i) => [x(i), y(v)]);
  const line = pts.map(([X, Y]) => `${X.toFixed(1)},${Y.toFixed(1)}`).join(' ');
  const area = `${padL},${h - padB} ${line} ${w - padR},${h - padB}`;
  const months = ['Jun','Jul','Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar','Apr','May'];
  return (
    <div className="card" style={{ padding: 12 }}>
      <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`}>
        {[0, 0.5, 1].map(t => (
          <line key={t} x1={padL} y1={padT + t * (h - padT - padB)} x2={w - padR} y2={padT + t * (h - padT - padB)} stroke="#F2F4F7" strokeWidth="1"/>
        ))}
        <polygon points={area} fill="#12B76A" opacity="0.10"/>
        <polyline points={line} fill="none" stroke="#12B76A" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
        {pts.map(([X, Y], i) => <circle key={i} cx={X} cy={Y} r="2.5" fill="white" stroke="#12B76A" strokeWidth="1.5"/>)}
        {months.map((m, i) => <text key={i} x={x(i)} y={h - 4} fontSize="9.5" fill="#98A2B3" textAnchor="middle">{m}</text>)}
        <text x={4} y={padT + 6} fontSize="9" fill="#98A2B3" fontFamily="JetBrains Mono">%</text>
      </svg>
    </div>
  );
}
