// SAKAN Project Intelligence Report — presentation layer.
//
// Consumes computed report data from reportEngine.js (buildReportData) —
// owns NO business logic and NO mock data. Same approved visual structure
// (Header / Recommendation / Decision Fork / Budget / Timeline-or-
// Bottlenecks / Risk Register / Team / CEO Summary / Key Takeaways / CEO
// mode) across all three scopes: single project, combined projects, all
// projects — buildReportData() is the one thing that changes per scope.
//
// Loaded as its own Babel-transformed <script type="text/babel"
// data-type="module"> in index.html (before SakanPlatform.jsx's own script
// tag) and exposes itself as `window.SakanIntelligenceReport` — a plain
// cross-file `import` of a .jsx file from inside another Babel-transformed
// script does not work under this project's browser-Babel setup (the
// importing file's JSX gets transformed, but a plain native `import` of a
// *second* .jsx file is resolved by the browser's native module loader,
// which cannot parse JSX — confirmed by hitting exactly that
// "Unexpected token '<'" error while building this). reportEngine.js has no
// JSX, so it imports natively without any of this.

import React, { useMemo, useState } from 'react';
import {
  ResponsiveContainer, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip,
  ReferenceLine, BarChart, Bar, Cell, PieChart, Pie, Legend,
} from 'recharts';
import {
  ArrowRight, Printer, Eye, EyeOff, AlertTriangle, TrendingUp, Clock, ShieldAlert, Circle, ArrowLeft,
} from 'lucide-react';
import { buildReportData, stageVariance } from './reportEngine.js';

/* ============================================================================
   DESIGN TOKENS — unchanged from the approved prototype
   ============================================================================ */
const C = {
  blue: '#2a78d6', blueSoft: '#cde2fb',
  green: '#0ca30c', greenSoft: '#d9f2d9',
  orange: '#eb6834', orangeSoft: '#fbe0d3',
  red: '#d03b3b', redSoft: '#f8d9d9',
  purple: '#4a3aa7', purpleSoft: '#e3dff5',
  ink: '#0b0b0f', inkSecondary: '#52545c', inkMuted: '#8a8d97',
  grid: '#e5e7eb', page: '#F7F8FA', card: '#FFFFFF', border: '#E5E7EB',
};
const SEVERITY = {
  critical: { label: 'HIGH', color: C.red, soft: C.redSoft, Icon: ShieldAlert },
  warning: { label: 'MEDIUM', color: C.orange, soft: C.orangeSoft, Icon: AlertTriangle },
  monitor: { label: 'LOW', color: C.green, soft: C.greenSoft, Icon: Circle },
};
const HEALTH_TONE = { 'On Track': 'green', 'At Risk': 'warning', Behind: 'critical', Complete: 'green' };

const fmtUSD = (n) => (n == null ? '—' : `$${Math.round(n).toLocaleString('en-US')}`);
const fmtUSDk = (n) => (n == null ? '—' : n >= 1000 ? `$${(n / 1000).toFixed(0)}K` : fmtUSD(n));
const fmtDate = (d) => (d ? new Date(d + 'T00:00:00').toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '—');
const fmtDateRange = (start, end) => (start && end ? `${fmtDate(start)} – ${fmtDate(end)}` : 'Not available');
const fmtDays = (n) => (n == null ? '—' : `${n} day${Math.abs(n) === 1 ? '' : 's'}`);
// Threshold is a % of the stage's own planned duration, not a fixed day
// count — 5 days late on a 3-day stage and 5 days late on a 60-day stage
// are not the same severity. A display-threshold choice, not a data claim.
function varianceTone(variance, plannedDays) {
  if (variance == null) return null;
  if (variance <= 0) return 'green';
  const ratio = plannedDays ? variance / plannedDays : 1;
  return ratio > 0.15 ? 'red' : 'orange';
}

/* ============================================================================
   PRIMITIVES
   ============================================================================ */
function Card({ children, className = '', style = {} }) {
  return <div className={`rounded-xl bg-white ${className}`} style={{ border: `1px solid ${C.border}`, ...style }}>{children}</div>;
}
function SectionTitle({ eyebrow, title, right }) {
  return (
    <div className="flex items-end justify-between mb-4 flex-wrap gap-2">
      <div>
        {eyebrow && <div className="text-[11px] font-semibold uppercase tracking-wider mb-1" style={{ color: C.inkMuted }}>{eyebrow}</div>}
        <h2 className="text-[15px] font-semibold" style={{ color: C.ink }}>{title}</h2>
      </div>
      {right}
    </div>
  );
}
function SeverityBadge({ level }) {
  const s = SEVERITY[level] || SEVERITY.monitor;
  const Icon = s.Icon;
  return (
    <span className="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-semibold" style={{ background: s.soft, color: s.color }}>
      <Icon className="w-3 h-3" />{s.label}
    </span>
  );
}
// Health uses its own real vocabulary (On Track/At Risk/Behind/Complete) -
// deliberately separate from SeverityBadge's risk-register HIGH/MEDIUM/LOW,
// which means something different (a single risk's severity, not a
// project's overall health).
function HealthPill({ health }) {
  const color = health === 'Behind' ? C.red : health === 'At Risk' ? C.orange : C.green;
  const soft = health === 'Behind' ? C.redSoft : health === 'At Risk' ? C.orangeSoft : C.greenSoft;
  return <span className="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-semibold" style={{ background: soft, color }}><Circle className="w-2 h-2 fill-current" />{health}</span>;
}
/* ============================================================================
   EXECUTIVE HEADER — single-project scope only. Pure stat card matching the
   reference: identity row, schedule row, financial row. No narrative text
   inside this card (RecommendationBanner carries that, directly below).
   ============================================================================ */
function ExecutiveHeader({ data, projects, onSwitchProject }) {
  const p = data.project;
  const healthLabel = data.health;
  const healthColor = healthLabel === 'Behind' ? C.red : healthLabel === 'At Risk' ? C.orange : C.green;
  const healthSoft = healthLabel === 'Behind' ? C.redSoft : healthLabel === 'At Risk' ? C.orangeSoft : C.greenSoft;
  const slack = data.forecast.slackDays;
  const isLate = slack != null && slack < 0;

  return (
    <Card className="p-6 md:p-7 mb-6">
      <div className="flex flex-wrap items-start justify-between gap-3 mb-1">
        {projects && projects.length > 1 ? (
          <select
            value={p.id}
            onChange={(e) => onSwitchProject(e.target.value)}
            className="text-xl md:text-2xl font-semibold tracking-tight bg-transparent border-none outline-none -ml-1 print:appearance-none"
            style={{ color: C.ink }}
          >
            {projects.map(pr => <option key={pr.id} value={pr.id}>{pr.name}</option>)}
          </select>
        ) : (
          <h1 className="text-xl md:text-2xl font-semibold tracking-tight" style={{ color: C.ink }}>{p.name}</h1>
        )}
        <span className="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-semibold shrink-0" style={{ background: healthSoft, color: healthColor }}>
          <Circle className="w-2 h-2 fill-current" />{healthLabel === 'Behind' ? 'BEHIND' : healthLabel === 'At Risk' ? 'AT RISK' : healthLabel === 'Complete' ? 'COMPLETE' : 'ON TRACK'}
        </span>
      </div>
      <div className="text-xs mb-5" style={{ color: C.inkSecondary }}>Status: <span className="font-medium" style={{ color: C.ink }}>{p.status}</span></div>

      <div className="grid grid-cols-2 md:grid-cols-5 gap-5 pb-5 mb-5" style={{ borderBottom: `1px solid ${C.border}` }}>
        <div>
          <div className="text-[11px] font-medium mb-1.5" style={{ color: C.inkMuted }}>Progress</div>
          <div className="text-2xl font-semibold mb-1.5" style={{ color: C.blue }}>{data.progress}%</div>
          <div className="h-1.5 rounded-full overflow-hidden" style={{ background: C.grid }}>
            <div className="h-full rounded-full" style={{ width: `${data.progress}%`, background: C.blue }} />
          </div>
        </div>
        <StatCell label="Elapsed" value={fmtDays(data.elapsedDays)} />
        <StatCell label="Remaining (est.)" value={fmtDays(data.remainingDays)} />
        <StatCell label="Target Date" value={fmtDate(p.target)} />
        <StatCell
          label="Forecast Completion"
          value={fmtDate(data.forecast.onTimeDate)}
          warning={isLate ? `${Math.abs(slack)} day${Math.abs(slack) === 1 ? '' : 's'} behind schedule` : null}
        />
      </div>

      <div className="grid grid-cols-2 md:grid-cols-4 gap-5">
        <StatCell label="Total Committed" value={fmtUSD(data.cost.committed)} />
        <StatCell label="Cost / Unit" value={data.cost.costPerUnit ? `$${data.cost.costPerUnit.toFixed(2)}` : '—'} />
        <StatCell label="MOQ" value={data.moq || '—'} />
        <StatCell
          label="Budget Remaining"
          value={data.cost.approvedBudget != null ? fmtUSD(data.cost.contingencyRemaining) : 'Not tracked'}
          tone={data.cost.approvedBudget != null ? (data.cost.contingencyRemaining < 0 ? 'red' : 'green') : null}
        />
      </div>
    </Card>
  );
}
function StatCell({ label, value, warning, tone }) {
  return (
    <div>
      <div className="text-[11px] font-medium mb-1" style={{ color: C.inkMuted }}>{label}</div>
      <div className="text-base font-semibold" style={{ color: tone === 'red' ? C.red : tone === 'green' ? C.green : C.ink }}>{value}</div>
      {warning && (
        <div className="flex items-center gap-1 text-[11px] font-medium mt-1" style={{ color: C.red }}>
          <AlertTriangle className="w-3 h-3" /> {warning}
        </div>
      )}
    </div>
  );
}
function RecommendationBanner({ data }) {
  return (
    <Card className="p-5 mb-6" style={{ background: C.blueSoft, border: 'none' }}>
      <div className="flex items-start gap-3">
        <div className="w-7 h-7 rounded-full flex items-center justify-center shrink-0 mt-0.5" style={{ background: C.blue }}>
          <ArrowRight className="w-3.5 h-3.5 text-white" />
        </div>
        <div>
          <p className="text-[15px] leading-relaxed font-semibold" style={{ color: C.ink }}>{data.recommendation.headline}</p>
          <p className="text-[13px] leading-relaxed mt-1" style={{ color: C.inkSecondary }}>{data.recommendation.detail}</p>
        </div>
      </div>
    </Card>
  );
}

/* ============================================================================
   PORTFOLIO EXECUTIVE HEADER — Combined / All-projects scope. Same identity
   + schedule + financial layout as the single-project ExecutiveHeader, but
   every field is either a real aggregate (cost-weighted progress, project
   counts, total committed) or explicitly labeled as such — no single
   Elapsed/Remaining/Target/Cost-per-Unit/MOQ row, since those don't
   meaningfully aggregate across projects with different start/target dates
   and products; fabricating a combined value there would be worse than
   omitting it.
   ============================================================================ */
function PortfolioExecutiveHeader({ data }) {
  const healthLabel = data.health;
  const healthColor = healthLabel === 'Behind' ? C.red : healthLabel === 'At Risk' ? C.orange : C.green;
  const healthSoft = healthLabel === 'Behind' ? C.redSoft : healthLabel === 'At Risk' ? C.orangeSoft : C.greenSoft;
  const projectCount = data.projectIds.length;

  return (
    <Card className="p-6 md:p-7 mb-6">
      <div className="flex flex-wrap items-start justify-between gap-3 mb-1">
        <h1 className="text-xl md:text-2xl font-semibold tracking-tight" style={{ color: C.ink }}>{data.title}</h1>
        <span className="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-semibold shrink-0" style={{ background: healthSoft, color: healthColor }}>
          <Circle className="w-2 h-2 fill-current" />{healthLabel === 'Behind' ? 'BEHIND' : healthLabel === 'At Risk' ? 'AT RISK' : 'ON TRACK'}
        </span>
      </div>
      <div className="text-xs mb-5" style={{ color: C.inkSecondary }}>
        {projectCount} project{projectCount === 1 ? '' : 's'} in scope · {data.risks.length > 0 ? `${data.risks.length} open risk${data.risks.length > 1 ? 's' : ''}` : 'no open risks'}
      </div>

      <div className="grid grid-cols-2 md:grid-cols-4 gap-5 pb-5 mb-5" style={{ borderBottom: `1px solid ${C.border}` }}>
        <div>
          <div className="text-[11px] font-medium mb-1.5" style={{ color: C.inkMuted }}>Progress (cost-weighted)</div>
          <div className="text-2xl font-semibold mb-1.5" style={{ color: C.blue }}>{data.progress}%</div>
          <div className="h-1.5 rounded-full overflow-hidden" style={{ background: C.grid }}>
            <div className="h-full rounded-full" style={{ width: `${data.progress}%`, background: C.blue }} />
          </div>
        </div>
        <StatCell label="Projects At Risk" value={String(data.projectsAtRisk)} tone={data.projectsAtRisk > 0 ? 'red' : undefined} />
        <StatCell label="Projects Behind" value={String(data.projectsBehind)} tone={data.projectsBehind > 0 ? 'red' : undefined} />
        <StatCell label="Forecast Completion" value={fmtDate(data.forecast.onTimeDate)} />
      </div>
      <div className="text-[11px] -mt-3.5 mb-5" style={{ color: C.inkMuted }}>Portfolio forecast is set by the last project to finish — {data.forecast.drivenBy || 'no in-scope project has a forecast yet'}.</div>

      <div className="grid grid-cols-2 md:grid-cols-3 gap-5">
        <StatCell label="Total Committed" value={fmtUSD(data.cost.committed)} />
        <StatCell label="Total Budgeted" value={fmtUSD(data.cost.budgeted)} />
        <StatCell
          label="Budget Remaining"
          value={data.cost.approvedBudget != null ? fmtUSD(data.cost.contingencyRemaining) : 'Not tracked'}
          tone={data.cost.approvedBudget != null ? (data.cost.contingencyRemaining < 0 ? 'red' : 'green') : null}
        />
      </div>
      {data.cost.approvedBudget == null && (
        <div className="text-[11px] mt-2" style={{ color: C.inkMuted }}>
          Budget remaining rolls up only when every project in scope has a budget set in the same currency — otherwise summing would misrepresent the total.
        </div>
      )}
    </Card>
  );
}

/* ============================================================================
   DECISION FORK — built from real dates (today / decision point / onTime /
   delayed), not hardcoded. Recalculates any time the underlying sample-round
   or bid dates change, since it's fed straight from computeForecast().
   ============================================================================ */
function DecisionFork({ data }) {
  const f = data.forecast;
  const points = useMemo(() => {
    const rows = [];
    const startProgress = 0;
    rows.push({ date: fmtDate(f.today), actual: data.progress });
    if (f.decisionDate) {
      rows.push({ date: fmtDate(f.decisionDate), actual: data.progress, onTime: data.progress, delayed: data.progress });
    } else {
      rows.push({ date: fmtDate(f.today), onTime: data.progress, delayed: data.progress });
    }
    rows.push({ date: fmtDate(f.onTimeDate), onTime: 100 });
    if (f.delayedDate && f.delayedDate !== f.onTimeDate) rows.push({ date: fmtDate(f.delayedDate), delayed: 100 });
    return rows;
  }, [f, data.progress]);

  const hasFork = f.delayedDate && f.delayedDate !== f.onTimeDate;

  return (
    <Card className="p-6 mb-6">
      <SectionTitle
        eyebrow="4. Predictive Forecast"
        title={hasFork ? 'Decision fork' : 'Completion forecast'}
        right={
          <div className="flex items-center gap-1.5 text-[11px] font-medium px-2.5 py-1 rounded-full" style={{ background: C.purpleSoft, color: C.purple }}>
            <TrendingUp className="w-3 h-3" /> {f.confidence}% forecast confidence
          </div>
        }
      />
      {data.scope !== 'single' && (
        <p className="text-[11px] -mt-2 mb-4" style={{ color: C.inkMuted }}>
          The portfolio isn't complete until every project is — this forecast is driven by {f.drivenBy || 'the last project to finish'}, the project currently forecast to finish latest.
        </p>
      )}
      <div style={{ width: '100%', height: 260 }}>
        <ResponsiveContainer>
          <LineChart data={points} margin={{ top: 10, right: 16, left: 4, bottom: 0 }}>
            <CartesianGrid vertical={false} stroke={C.grid} />
            <XAxis dataKey="date" tick={{ fontSize: 11, fill: C.inkMuted }} tickLine={false} axisLine={{ stroke: C.grid }} />
            <YAxis tick={{ fontSize: 11, fill: C.inkMuted }} tickLine={false} axisLine={false} tickFormatter={(v) => `${v}%`} width={34} domain={[0, 100]} />
            <Tooltip formatter={(v, name) => [`${v}%`, name === 'actual' ? 'Progress' : name === 'onTime' ? 'If resolved on time' : 'If it slips']} contentStyle={{ border: `1px solid ${C.border}`, borderRadius: 8, fontSize: 12 }} />
            {f.decisionDate && <ReferenceLine x={fmtDate(f.decisionDate)} stroke={C.ink} strokeDasharray="4 4" strokeWidth={1} label={{ value: 'Decision point', position: 'top', fontSize: 11, fill: C.ink, fontWeight: 600 }} />}
            <Line type="monotone" dataKey="actual" stroke={C.blue} strokeWidth={2} dot={false} connectNulls={false} />
            <Line type="monotone" dataKey="onTime" stroke={C.green} strokeWidth={2} dot={{ r: 3, fill: C.green }} connectNulls={false} />
            {hasFork && <Line type="monotone" dataKey="delayed" stroke={C.orange} strokeWidth={2} dot={{ r: 3, fill: C.orange }} connectNulls={false} strokeDasharray="5 3" />}
          </LineChart>
        </ResponsiveContainer>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-2 gap-3 mt-4">
        <ForkOutcome color={C.green} label={f.decisionDate ? `If approved by ${fmtDate(f.decisionDate)}` : 'Current path'} result={`${fmtDate(f.onTimeDate)} completion`} detail={data.forecast.decisionDriver ? `Driven by ${data.forecast.decisionDriver.samplingProjectName} — ${data.forecast.decisionDriver.round}.` : 'No open decision currently driving the schedule.'} />
        {hasFork && <ForkOutcome color={C.orange} label="If the current slip continues" result={`${fmtDate(f.delayedDate)} completion`} detail="Based on the current overrun rate on the open sampling round." />}
      </div>
    </Card>
  );
}
function ForkOutcome({ color, label, result, detail }) {
  return (
    <div className="rounded-lg px-4 py-3 flex items-start gap-3" style={{ background: '#fafafa', border: `1px solid ${C.border}` }}>
      <span className="w-2.5 h-2.5 rounded-full mt-1.5 shrink-0" style={{ background: color }} />
      <div className="min-w-0">
        <div className="text-[11px] font-semibold uppercase tracking-wide" style={{ color: C.inkMuted }}>{label}</div>
        <div className="text-sm font-semibold" style={{ color: C.ink }}>{result}</div>
        <div className="text-xs mt-0.5" style={{ color: C.inkSecondary }}>{detail}</div>
      </div>
    </div>
  );
}

/* ============================================================================
   BUDGET
   ============================================================================ */
function BudgetSection({ data }) {
  const { cost } = data;
  const hasBudget = cost.approvedBudget != null;
  return (
    <Card className="p-6 mb-6">
      <SectionTitle eyebrow="2. Cost Breakdown" title={data.scope === 'single' ? 'Cost committed vs. contingency' : 'Committed cost across projects'} />
      {!cost.breakdown.length && !hasBudget ? (
        <p className="text-sm" style={{ color: C.inkMuted }}>No committed cost on record yet — no approved quotations, sample rounds, or production components.</p>
      ) : (
        <div className={cost.breakdown.length ? 'grid grid-cols-1 md:grid-cols-[220px_1fr] gap-6 items-center' : ''}>
          {cost.breakdown.length > 0 && (
            <div className="relative" style={{ width: '100%', height: 200 }}>
              <ResponsiveContainer>
                <PieChart>
                  <Pie data={cost.breakdown} dataKey="value" nameKey="key" innerRadius={62} outerRadius={88} paddingAngle={2} stroke={C.card} strokeWidth={2}>
                    {cost.breakdown.map((d, i) => <Cell key={d.key} fill={[C.blue, C.purple, C.orange, C.green][i % 4]} />)}
                  </Pie>
                  <Tooltip formatter={(v, n) => [fmtUSD(v), n]} contentStyle={{ border: `1px solid ${C.border}`, borderRadius: 8, fontSize: 12 }} />
                </PieChart>
              </ResponsiveContainer>
              <div className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
                <div className="text-xl font-semibold" style={{ color: C.ink }}>{fmtUSDk(cost.committed)}</div>
                <div className="text-[10px]" style={{ color: C.inkMuted }}>committed</div>
              </div>
            </div>
          )}
          <div>
            {cost.breakdown.length > 0 && (
              <div className="flex flex-wrap gap-x-6 gap-y-2 mb-4">
                {cost.breakdown.map((d, i) => (
                  <div key={d.key} className="flex items-center gap-2 text-xs">
                    <span className="w-2.5 h-2.5 rounded-sm" style={{ background: [C.blue, C.purple, C.orange, C.green][i % 4] }} />
                    <span style={{ color: C.inkSecondary }}>{d.key}</span>
                    <span className="font-semibold" style={{ color: C.ink }}>{fmtUSDk(d.value)}</span>
                  </div>
                ))}
              </div>
            )}
            <div className="grid grid-cols-3 gap-4 pt-4" style={cost.breakdown.length ? { borderTop: `1px solid ${C.border}` } : undefined}>
              <BudgetStat label="Committed" value={fmtUSD(cost.committed)} />
              <BudgetStat label="Budgeted (not yet committed)" value={fmtUSD(cost.budgeted)} />
              <BudgetStat label="Contingency left" value={hasBudget ? fmtUSD(cost.contingencyRemaining) : 'Not tracked'} tone={hasBudget ? 'green' : undefined} />
            </div>
            {!hasBudget && <p className="text-[11px] mt-2" style={{ color: C.inkMuted }}>No estimated budget set on this project yet, so utilization/contingency can't be computed — only real committed spend is shown.</p>}
            {hasBudget && cost.budgetCurrency && cost.budgetCurrency !== 'USD' && (
              <p className="text-[11px] mt-2" style={{ color: C.inkMuted }}>Budget set in {cost.budgetCurrency}; shown here at face value alongside committed/budgeted spend, none of which are FX-converted.</p>
            )}
          </div>
        </div>
      )}
    </Card>
  );
}
function BudgetStat({ label, value, tone }) {
  return <div><div className="text-[11px]" style={{ color: C.inkMuted }}>{label}</div><div className="text-base font-semibold" style={{ color: tone === 'green' ? C.green : C.ink }}>{value}</div></div>;
}

/* ============================================================================
   TIMELINE — Section 1. Left: Stage/Planned/Actual/Variance table, straight
   from computeStageTimeline()'s per-row real dates ("Not available"/
   "Pending" where the schema has no source, never a guessed date). Right:
   a real date-scaled Gantt (plain-div, not a bar chart — a floating date
   range per stage isn't a "value from zero" chart) with a planned outline
   bar, a solid actual bar colored by variance, and a today line.
   Single-project only (a portfolio timeline reads as noise across N
   different start dates); combined/all scope shows Bottlenecks instead.
   ============================================================================ */
// Stage status vocabulary, deliberately literal:
//   Measured    - stage concluded; actualStart/actualEnd are both real
//   In Progress - stage has started; actualEnd isn't final yet
//   Not Started - no real record yet that this stage began
// "Not Available" is NOT a stage-progress state - it's a value-level
// fallback for a specific field with no data source (e.g. Quotation's
// planned duration, or a Measured stage's variance when no planned-
// duration source exists for it at all). Conflating the two would hide
// the difference between "hasn't happened" and "can't be known".
const STAGE_STATUS_LABEL = { measured: 'Measured', 'in-progress': 'In Progress', 'not-started': 'Not Started' };
const STAGE_STATUS_COLOR = { measured: 'ink', 'in-progress': 'blue', 'not-started': 'inkMuted' };

function TimelineTable({ rows }) {
  return (
    <table className="w-full text-sm">
      <thead>
        <tr className="text-left text-[11px] uppercase tracking-wide" style={{ color: C.inkMuted }}>
          <th className="font-medium pb-2">Stage</th><th className="font-medium pb-2">Status</th><th className="font-medium pb-2">Planned</th><th className="font-medium pb-2">Actual</th><th className="font-medium pb-2">Variance</th>
        </tr>
      </thead>
      <tbody>
        {rows.map(r => {
          const variance = stageVariance(r);
          const tone = varianceTone(variance, r.plannedDays);
          const toneColor = tone === 'red' ? C.red : tone === 'orange' ? C.orange : tone === 'green' ? C.green : C.inkMuted;
          // Variance is only ever a number for a Measured stage with a real
          // planned-duration source. Measured-but-no-planned-data (Quotation)
          // shows "Not available" for that one field; In Progress/Not Started
          // show a dash - their Status cell already explains why there's no
          // variance, so repeating "In Progress"/"Not Started" here would be
          // redundant rather than clarifying.
          const varianceLabel = r.status !== 'measured' ? '—' : variance == null ? 'Not available' : `${variance > 0 ? '+' : ''}${variance} days`;
          return (
            <tr key={r.name} style={{ borderTop: `1px solid ${C.border}` }}>
              <td className="py-2.5 font-medium" style={{ color: C.ink }}>{r.name}</td>
              <td className="py-2.5" style={{ color: C[STAGE_STATUS_COLOR[r.status]] }}>{STAGE_STATUS_LABEL[r.status]}</td>
              <td className="py-2.5" style={{ color: C.inkSecondary }}>{r.plannedStart ? fmtDateRange(r.plannedStart, r.plannedEnd) : 'Not available'}</td>
              <td className="py-2.5" style={{ color: C.inkSecondary }}>{r.status === 'not-started' ? '—' : r.actualStart ? fmtDateRange(r.actualStart, r.actualEnd || todayISO()) : 'Not available'}</td>
              <td className="py-2.5 font-medium flex items-center gap-1.5" style={{ color: toneColor }}>
                {r.status === 'measured' && <span className="w-1.5 h-1.5 rounded-full shrink-0" style={{ background: toneColor }} />}{varianceLabel}
              </td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}
function todayISO() { return new Date().toISOString().slice(0, 10); }

function TimelineGantt({ rows }) {
  const today = todayISO();
  const allDates = rows.flatMap(r => [r.actualStart, r.actualEnd, r.plannedStart, r.plannedEnd]).filter(Boolean).map(d => new Date(d));
  allDates.push(new Date(today));
  if (allDates.length < 2) return null;
  const min = new Date(Math.min(...allDates)), max = new Date(Math.max(...allDates));
  const span = Math.max(1, max - min);
  const pctOf = (d) => Math.max(0, Math.min(100, ((new Date(d) - min) / span) * 100));
  const todayPct = pctOf(today);
  const ticks = Array.from({ length: 5 }, (_, i) => new Date(min.getTime() + (span * i) / 4));

  return (
    <div>
      <div className="flex text-[10px] mb-2" style={{ color: C.inkMuted }}>
        {ticks.map((t, i) => <div key={i} className="flex-1 text-center">{t.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}</div>)}
      </div>
      <div className="space-y-3">
        {rows.map(r => {
          const variance = stageVariance(r);
          const tone = varianceTone(variance, r.plannedDays);
          const actualColor = tone === 'red' ? C.red : tone === 'orange' ? C.orange : r.status === 'not-started' ? C.grid : C.blue;
          const plannedLeft = r.plannedStart ? pctOf(r.plannedStart) : null;
          const plannedWidth = r.plannedStart && r.plannedEnd ? Math.max(2, pctOf(r.plannedEnd) - plannedLeft) : null;
          const actualEndForBar = r.actualEnd || (r.status === 'in-progress' ? today : null);
          const actualLeft = r.actualStart ? pctOf(r.actualStart) : null;
          const actualWidth = r.actualStart && actualEndForBar ? Math.max(2, pctOf(actualEndForBar) - actualLeft) : null;
          return (
            <div key={r.name} className="relative h-6" style={{ background: '#fafafa', borderRadius: 4 }}>
              <div className="absolute top-0 bottom-0 w-px z-10" style={{ left: `${todayPct}%`, background: C.red }} />
              {plannedLeft != null && (
                <div className="absolute top-0.5 bottom-0.5 rounded" style={{ left: `${plannedLeft}%`, width: `${plannedWidth}%`, border: `1.5px dashed ${C.inkMuted}` }} title={`Planned: ${fmtDateRange(r.plannedStart, r.plannedEnd)}`} />
              )}
              {actualLeft != null && (
                <div className="absolute top-1.5 bottom-1.5 rounded" style={{ left: `${actualLeft}%`, width: `${actualWidth}%`, background: actualColor }} title={`Actual: ${fmtDateRange(r.actualStart, actualEndForBar)}`} />
              )}
            </div>
          );
        })}
      </div>
      <div className="flex items-center gap-4 mt-3 text-[11px]" style={{ color: C.inkSecondary }}>
        <span className="inline-flex items-center gap-1.5"><span className="w-3 h-2 rounded-sm" style={{ border: `1.5px dashed ${C.inkMuted}` }} />Planned</span>
        <span className="inline-flex items-center gap-1.5"><span className="w-3 h-2 rounded-sm" style={{ background: C.blue }} />Actual</span>
        <span className="inline-flex items-center gap-1.5"><span className="w-px h-3" style={{ background: C.red }} />Today</span>
      </div>
    </div>
  );
}

function TimelineSection({ data }) {
  const rows = data.forecast.timeline;
  if (!rows.length) return null;
  const worst = rows.filter(r => stageVariance(r) != null).sort((a, b) => stageVariance(b) - stageVariance(a))[0];
  const worstVariance = worst ? stageVariance(worst) : 0;

  return (
    <Card className="p-6 mb-6">
      <SectionTitle
        eyebrow="1. Timeline"
        title="Actual vs. planned"
        right={worstVariance > 0 ? (
          <div className="flex items-center gap-1.5 text-[11px] font-semibold px-2.5 py-1 rounded-full" style={{ background: C.orangeSoft, color: C.orange }}>
            <Clock className="w-3 h-3" /> {worst.name} +{worstVariance} days
          </div>
        ) : null}
      />
      <div className="grid grid-cols-1 lg:grid-cols-[280px_1fr] gap-6">
        <TimelineTable rows={rows} />
        <TimelineGantt rows={rows} />
      </div>
    </Card>
  );
}

// Section 3 — This Project vs. Team Average, per stage. "Team Average" is
// computeHistoricalBaseline()'s real per-stage mean across completed
// projects (null until at least one exists). There is no industry-benchmark
// field or data source anywhere in the schema, so that column is never
// invented — it's an explicit "No benchmark data" line instead. Bottleneck
// flag is specifically "this project's actual > the team's own historical
// average" for that stage, not the quoted-lead-time variance shown in
// Section 1 (a different, complementary comparison).
function StageDurationSection({ data }) {
  const rows = data.forecast.timeline;
  const durationData = rows
    .filter(r => r.actualDays != null && r.status !== 'not-started')
    .map(r => ({ name: r.name, thisProject: r.actualDays, teamAverage: data.historical.available ? (data.historical.byStage[r.name] ?? null) : null }));
  if (!durationData.length) return null;
  const bottleneck = durationData.filter(d => d.teamAverage != null && d.thisProject > d.teamAverage).sort((a, b) => (b.thisProject - b.teamAverage) - (a.thisProject - a.teamAverage))[0];

  return (
    <Card className="p-6 mb-6">
      <SectionTitle
        eyebrow="3. Stage Duration Analysis"
        title="This project vs. team average"
        right={bottleneck ? (
          <div className="flex items-center gap-1.5 text-[11px] font-semibold px-2.5 py-1 rounded-full" style={{ background: C.orangeSoft, color: C.orange }}>
            <Clock className="w-3 h-3" /> {bottleneck.name} — {bottleneck.thisProject}d vs {bottleneck.teamAverage}d avg — BOTTLENECK
          </div>
        ) : null}
      />
      <div style={{ width: '100%', height: 180 }}>
        <ResponsiveContainer>
          <BarChart data={durationData} margin={{ top: 4, right: 8, left: 4, bottom: 0 }} barGap={4}>
            <CartesianGrid vertical={false} stroke={C.grid} />
            <XAxis dataKey="name" tick={{ fontSize: 11, fill: C.inkMuted }} tickLine={false} axisLine={{ stroke: C.grid }} />
            <YAxis tick={{ fontSize: 11, fill: C.inkMuted }} tickLine={false} axisLine={false} width={30} />
            <Tooltip contentStyle={{ border: `1px solid ${C.border}`, borderRadius: 8, fontSize: 12 }} formatter={(v, n) => [v == null ? 'No benchmark data' : `${v}d`, n]} />
            <Legend verticalAlign="top" align="right" height={24} iconType="circle" iconSize={8} wrapperStyle={{ fontSize: 11, color: C.inkSecondary }} />
            <Bar dataKey="thisProject" name="This Project" radius={[4, 4, 0, 0]} barSize={20}>
              {durationData.map((d) => <Cell key={d.name} fill={d.teamAverage != null && d.thisProject > d.teamAverage ? C.orange : C.blue} />)}
            </Bar>
            <Bar dataKey="teamAverage" name="Team Average" fill={C.blueSoft} radius={[4, 4, 0, 0]} barSize={20} />
          </BarChart>
        </ResponsiveContainer>
      </div>
      {!data.historical.available && (
        <p className="text-[11px] mt-3" style={{ color: C.inkMuted }}>No benchmark data — {data.historical.note}</p>
      )}
      <p className="text-[11px] mt-2" style={{ color: C.inkMuted }}>No industry-benchmark field exists in SAKAN — only this project's own real duration and, once completed projects exist, the team's own historical average are ever shown.</p>
    </Card>
  );
}

// Stage Duration Analysis, portfolio form: average variance per stage across
// every MEASURED (concluded) stage in scope, same gating as the single-
// project stageVariance() — an in-progress stage never contributes. No
// external/industry benchmark is shown here either, for the same reason as
// the single-project StageDurationSection: SAKAN has no benchmark data
// source, so nothing is invented.
function BottlenecksSection({ data }) {
  return (
    <Card className="p-6 mb-6">
      <SectionTitle eyebrow="3. Stage Duration Analysis" title="Bottlenecks across projects" />
      {!data.bottlenecks?.length ? (
        <p className="text-sm" style={{ color: C.inkMuted }}>No stage bottlenecks detected — no in-scope project has both a concluded stage and a planned duration to compare yet.</p>
      ) : (
        <div className="space-y-2">
          {data.bottlenecks.map(b => (
            <div key={b.name} className="flex items-center justify-between rounded-lg px-4 py-3" style={{ background: '#fafafa', border: `1px solid ${C.border}` }}>
              <span className="text-sm font-medium" style={{ color: C.ink }}>{b.name}</span>
              <span className="text-xs" style={{ color: C.inkSecondary }}>+{b.avgVariance}d average, across {b.count} project{b.count > 1 ? 's' : ''}</span>
            </div>
          ))}
        </div>
      )}
    </Card>
  );
}

function ProjectComparisonSection({ data }) {
  if (!data.projects) return null;
  return (
    <Card className="p-6 mb-6">
      <SectionTitle eyebrow="Portfolio Detail" title="Project-by-project comparison" />
      <table className="w-full text-sm">
        <thead>
          <tr className="text-left text-[11px] uppercase tracking-wide" style={{ color: C.inkMuted }}>
            <th className="font-medium pb-2">Project</th><th className="font-medium pb-2">Progress</th><th className="font-medium pb-2">Health</th>
            <th className="font-medium pb-2">Committed</th><th className="font-medium pb-2">Target</th><th className="font-medium pb-2">Forecast</th>
          </tr>
        </thead>
        <tbody>
          {data.projects.map(p => (
            <tr key={p.id} style={{ borderTop: `1px solid ${C.border}` }}>
              <td className="py-2.5 font-medium" style={{ color: C.ink }}>{p.name}</td>
              <td className="py-2.5" style={{ color: C.inkSecondary }}>{p.progress}%</td>
              <td className="py-2.5"><HealthPill health={p.health} /></td>
              <td className="py-2.5" style={{ color: C.inkSecondary }}>{fmtUSDk(p.committed)}</td>
              <td className="py-2.5" style={{ color: C.inkSecondary }}>{fmtDate(p.target)}</td>
              <td className="py-2.5 font-medium" style={{ color: C.ink }}>{fmtDate(p.forecast)}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </Card>
  );
}

/* ============================================================================
   RISK REGISTER
   ============================================================================ */
// Risk | Description | Impact | Action | Owner | Due — ranked Critical(HIGH)
// -> Warning(MEDIUM) -> Monitor(LOW), matching computeRisks()'s own sort.
// `impact` is real per-risk-type text set alongside each detector in
// reportEngine.js (2026-08-11), not a generic filler line.
function RiskRegisterSection({ data }) {
  return (
    <Card className="p-6 mb-6">
      <SectionTitle eyebrow="6. Risk Register" title="Ranked by severity" />
      {!data.risks.length ? (
        <p className="text-sm" style={{ color: C.inkMuted }}>No open risks detected — nothing overdue, blocked, or expiring on record.</p>
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full text-sm" style={{ minWidth: 640 }}>
            <thead>
              <tr className="text-left text-[11px] uppercase tracking-wide" style={{ color: C.inkMuted }}>
                <th className="font-medium pb-2 pr-3">Risk Level</th>
                <th className="font-medium pb-2 pr-3">Risk</th>
                <th className="font-medium pb-2 pr-3">Impact</th>
                <th className="font-medium pb-2 pr-3">Action</th>
                <th className="font-medium pb-2 pr-3">Owner</th>
                <th className="font-medium pb-2">Due</th>
              </tr>
            </thead>
            <tbody>
              {data.risks.map((r, i) => (
                <tr key={i} style={{ borderTop: `1px solid ${C.border}`, background: i === 0 ? SEVERITY[r.severity].soft : 'transparent' }}>
                  <td className="py-3 pr-3 align-top"><SeverityBadge level={r.severity} /></td>
                  <td className="py-3 pr-3 align-top max-w-[220px]">
                    <div className="font-semibold" style={{ color: C.ink }}>{r.title}</div>
                    {r.project && data.scope !== 'single' && <span className="inline-block mt-1 text-[10px] font-medium px-1.5 py-0.5 rounded" style={{ background: C.purpleSoft, color: C.purple }}>{r.project}</span>}
                  </td>
                  <td className="py-3 pr-3 align-top text-xs max-w-[220px]" style={{ color: C.inkSecondary }}>{r.impact || r.detail}</td>
                  <td className="py-3 pr-3 align-top text-xs max-w-[180px]" style={{ color: C.inkSecondary }}>{r.action}</td>
                  <td className="py-3 pr-3 align-top text-xs font-medium" style={{ color: C.ink }}>{r.owner}</td>
                  <td className="py-3 align-top text-xs font-medium" style={{ color: r.severity === 'critical' ? C.red : C.ink }}>{fmtDate(r.due)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </Card>
  );
}

/* ============================================================================
   TEAM
   ============================================================================ */
// Tasks Assigned / Completed / Completion Rate are real (tasks table).
// Avg Response Time and Blockers have no data source anywhere in the schema
// (see computeTeamPerformance's own comment) and always render "Not tracked"
// - never a guess, and not a column silently dropped either, since a
// missing capability is itself something management should be able to see.
function TeamSection({ data }) {
  if (!data.team?.length) return null;
  return (
    <Card className="p-6 mb-6">
      <SectionTitle eyebrow="5. Team" title="Team performance" />
      <table className="w-full text-sm">
        <thead>
          <tr className="text-left text-[11px] uppercase tracking-wide" style={{ color: C.inkMuted }}>
            <th className="font-medium pb-2">Team Member</th><th className="font-medium pb-2">Tasks Assigned</th><th className="font-medium pb-2">Completed</th>
            <th className="font-medium pb-2">Completion Rate</th><th className="font-medium pb-2">Avg Response Time</th><th className="font-medium pb-2">Blockers</th>
          </tr>
        </thead>
        <tbody>
          {data.team.map((t) => (
            <tr key={t.name} style={{ borderTop: `1px solid ${C.border}` }}>
              <td className="py-2.5 font-medium" style={{ color: C.ink }}>
                <span className="inline-flex items-center gap-2">
                  <span className="w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-semibold text-white" style={{ background: C.purple }}>{t.name.split(' ').map((n) => n[0]).join('').slice(0, 2)}</span>
                  {t.name}
                </span>
              </td>
              <td className="py-2.5" style={{ color: C.inkSecondary }}>{t.assigned}</td>
              <td className="py-2.5" style={{ color: C.inkSecondary }}>{t.completed}</td>
              <td className="py-2.5 font-medium" style={{ color: t.completionRate == null ? C.inkMuted : t.completionRate >= 90 ? C.green : C.ink }}>{t.completionRate == null ? '—' : `${t.completionRate}%`}</td>
              <td className="py-2.5" style={{ color: C.inkMuted }}>Not tracked</td>
              <td className="py-2.5" style={{ color: C.inkMuted }}>Not tracked</td>
            </tr>
          ))}
        </tbody>
      </table>
    </Card>
  );
}

/* ============================================================================
   CEO SUMMARY — situation / main issue / recommendation / consequence, from
   computeCEOSummary(). Single-project scope only for now (Batch 3 extends
   the equivalent to Combined/All).
   ============================================================================ */
function CEOSummarySection({ data }) {
  if (!data.ceoSummary) return null;
  const s = data.ceoSummary;
  return (
    <Card className="p-6 mb-6">
      <SectionTitle eyebrow="7. CEO Summary" title="Strategic narrative" />
      <div className="space-y-3 text-sm leading-relaxed" style={{ color: C.inkSecondary }}>
        <p><span className="font-semibold" style={{ color: C.ink }}>Situation: </span>{s.situation}</p>
        <p><span className="font-semibold" style={{ color: C.ink }}>{data.risks.length ? 'Main issue: ' : 'Status: '}</span>{s.mainIssue}</p>
        <p><span className="font-semibold" style={{ color: C.ink }}>Recommendation: </span>{s.recommendation}</p>
        <p><span className="font-semibold" style={{ color: C.ink }}>Consequence: </span>{s.consequence}</p>
      </div>
    </Card>
  );
}

/* ============================================================================
   KEY TAKEAWAYS — from computeKeyTakeaways(), each tied to a real signal.
   ============================================================================ */
function KeyTakeawaysSection({ data }) {
  if (!data.keyTakeaways?.length) return null;
  return (
    <Card className="p-6 mb-6">
      <SectionTitle eyebrow="8. Key Takeaways" title="What to remember" />
      <ul className="space-y-2.5">
        {data.keyTakeaways.map((k, i) => (
          <li key={i} className="flex items-start gap-2.5 text-sm" style={{ color: C.ink }}>
            {k.tone === 'good' ? <Circle className="w-4 h-4 mt-0.5 shrink-0 fill-current" style={{ color: C.green }} /> : <AlertTriangle className="w-4 h-4 mt-0.5 shrink-0" style={{ color: C.orange }} />}
            {k.text}
          </li>
        ))}
      </ul>
    </Card>
  );
}

/* ============================================================================
   MAIN — SakanIntelligenceReport({ ctx, scope, projectIds, onBack })
   `ctx` is the live useApp() object; nothing here talks to Supabase itself.
   ============================================================================ */
function SakanLogoMark() {
  return (
    <div className="flex items-center gap-2">
      <div className="w-6 h-6 rounded-[6px] rotate-45" style={{ background: C.ink }} />
      <span className="text-lg font-bold tracking-tight -ml-1" style={{ color: C.ink }}>SAKAN</span>
    </div>
  );
}

function SakanIntelligenceReport({ ctx, scope, projectIds, onBack }) {
  const [ceoMode, setCeoMode] = useState(false);
  // Project switcher (single scope only): switching projects recomputes the
  // whole report from that project's real data using the SAME already-fetched
  // ctx — Generate already force-refreshed it, so no new fetch is needed to
  // switch between two projects inside one already-fresh report session.
  const [activeProjectId, setActiveProjectId] = useState(projectIds[0]);
  const effectiveProjectIds = scope === 'single' ? [activeProjectId] : projectIds;
  const data = useMemo(() => buildReportData(ctx, { scope, projectIds: effectiveProjectIds }), [ctx, scope, effectiveProjectIds]);
  const visibleProjects = useMemo(() => ctx.projects.filter(p => !p.isArchived).sort((a, b) => a.name.localeCompare(b.name)), [ctx.projects]);

  if (!data) {
    return (
      <div className="p-8 text-center text-sm" style={{ color: C.inkMuted }}>
        No project data available for this selection yet.
      </div>
    );
  }

  return (
    <div className="min-h-screen" style={{ background: C.page, fontFamily: 'Inter, system-ui, -apple-system, "Segoe UI", sans-serif' }}>
      <div className="max-w-[1080px] mx-auto px-4 md:px-6 py-6 md:py-10">
        {onBack && (
          <button onClick={onBack} className="inline-flex items-center gap-1.5 text-xs font-medium mb-4 print:hidden" style={{ color: C.inkMuted }}>
            <ArrowLeft className="w-3.5 h-3.5" /> Back
          </button>
        )}

        <div className="flex items-center justify-between mb-6">
          <SakanLogoMark />
          <div className="text-center">
            <h1 className="text-lg md:text-xl font-bold tracking-tight" style={{ color: C.ink }}>PROJECT OVERVIEW</h1>
            <div className="text-[11px]" style={{ color: C.inkMuted }}>Generated on {new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</div>
          </div>
          <div className="flex items-center gap-2 print:hidden">
            <button onClick={() => setCeoMode((v) => !v)} className="inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 rounded-lg transition-colors"
              style={{ background: ceoMode ? C.ink : '#fff', color: ceoMode ? '#fff' : C.ink, border: `1px solid ${ceoMode ? C.ink : C.border}` }}>
              {ceoMode ? <EyeOff className="w-3.5 h-3.5" /> : <Eye className="w-3.5 h-3.5" />} CEO VIEW
            </button>
            <button onClick={() => window.print()} className="inline-flex items-center gap-1.5 text-xs font-medium px-3 py-1.5 rounded-lg" style={{ background: '#fff', color: C.ink, border: `1px solid ${C.border}` }} title="Real PDF export lands in Phase 2 — this uses your browser's print-to-PDF for now">
              <Printer className="w-3.5 h-3.5" /> Export PDF
            </button>
          </div>
        </div>

        {data.scope === 'single' ? (
          <ExecutiveHeader data={data} projects={visibleProjects} onSwitchProject={setActiveProjectId} />
        ) : (
          <PortfolioExecutiveHeader data={data} />
        )}
        <RecommendationBanner data={data} />
        <DecisionFork data={data} />
        <BudgetSection data={data} />
        {!ceoMode && data.scope === 'single' && <TimelineSection data={data} />}
        {!ceoMode && data.scope === 'single' && <StageDurationSection data={data} />}
        {!ceoMode && data.scope !== 'single' && <BottlenecksSection data={data} />}
        {!ceoMode && data.scope !== 'single' && <ProjectComparisonSection data={data} />}
        <RiskRegisterSection data={data} />
        {!ceoMode && <TeamSection data={data} />}
        <CEOSummarySection data={data} />
        <KeyTakeawaysSection data={data} />

        <div className="text-center text-[11px] mt-2 mb-4" style={{ color: C.inkMuted }}>
          Generated from live SAKAN data · Confidential — internal use
        </div>
      </div>
    </div>
  );
}

window.SakanIntelligenceReport = SakanIntelligenceReport;
export default SakanIntelligenceReport;
