/*
 * Orbit SaaS — LTV / CAC Command Center (v2)
 *
 * Dark-theme analytics dashboard. Core subscription data from IBM Telco
 * Customer Churn (public). Acquisition channels & CAC simulated.
 */

const {
  LineChart, Line, BarChart, Bar, AreaChart, Area,
  XAxis, YAxis, CartesianGrid, Tooltip, Legend,
  ResponsiveContainer, Cell, ComposedChart, ReferenceLine,
} = window.Recharts;

// ─── Design tokens — desaturated, high-contrast on dark ────────────────────
// Ref: numerro.io dark-mode best practices — 4–5 contrasting colors,
// no pure black, desaturated palette for legibility.
const T = {
  bg:         "#0F1923",
  surface:    "#15202E",
  surfaceAlt: "#1A2940",
  border:     "#233044",
  borderHi:   "#334D6E",
  text:       "#E8EDF2",
  textSec:    "#9BAFC4",
  textMuted:  "#6B839E",
  textDim:    "#4E6680",
  accent:     "#5AAFCA",
  teal:       "#4ECDB4",
  tealDim:    "#2A7A68",
  gold:       "#D4A86A",
  goldDim:    "#8A6B38",
  lavender:   "#9B8EC4",
  lavenderDim:"#5E4F8A",
  slate:      "#7A91A8",
  green:      "#4ECDB4",
  greenDim:   "#2A7A68",
  amber:      "#D4A86A",
  amberDim:   "#8A6B38",
  rose:       "#D4A86A",
  cyan:       "#5AAFCA",
};

const CHANNEL_C = {
  "Organic Search": "#4ECDB4",
  "Content":        "#5AAFCA",
  "Referral":       "#9B8EC4",
  "Paid Search":    "#D4A86A",
  "Paid Social":    "#C48E6A",
  "Partnerships":   "#7A91A8",
};

// ─── Realistic SaaS data (18-month window) ──────────────────────────────────

const KPI = {
  arr: 3803829, mrr: 316986, customers: 7043, active: 5174,
  arpu: 64.76, blendedCac: 376, blendedLtv: 2744,
  ltvCac: 7.3, payback: 7.7, monthlyChurn: 0.82,
  nrr: 94.2, grossMargin: 78.5, burnMultiple: 1.2,
};

// Cohort retention heatmap — quarterly cohorts, monthly retention
const COHORT_HEATMAP = [
  { cohort: "Q1 2023", size: 892,  m0:100, m1:82, m2:72, m3:65, m4:60, m5:56, m6:52, m7:50, m8:48, m9:46, m10:44, m11:43, m12:42 },
  { cohort: "Q2 2023", size: 845,  m0:100, m1:80, m2:70, m3:63, m4:58, m5:55, m6:51, m7:49, m8:47, m9:45, m10:43, m11:42, m12:41 },
  { cohort: "Q3 2023", size: 910,  m0:100, m1:83, m2:73, m3:67, m4:62, m5:58, m6:55, m7:52, m8:50, m9:48, m10:47, m11:null, m12:null },
  { cohort: "Q4 2023", size: 878,  m0:100, m1:85, m2:76, m3:69, m4:64, m5:61, m6:57, m7:55, m8:53, m9:null, m10:null, m11:null, m12:null },
  { cohort: "Q1 2024", size: 935,  m0:100, m1:84, m2:75, m3:68, m4:63, m5:59, m6:56, m7:null, m8:null, m9:null, m10:null, m11:null, m12:null },
  { cohort: "Q2 2024", size: 920,  m0:100, m1:86, m2:77, m3:71, m4:66, m5:null, m6:null, m7:null, m8:null, m9:null, m10:null, m11:null, m12:null },
  { cohort: "Q3 2024", size: 880,  m0:100, m1:87, m2:78, m3:null, m4:null, m5:null, m6:null, m7:null, m8:null, m9:null, m10:null, m11:null, m12:null },
  { cohort: "Q4 2024", size: 783,  m0:100, m1:88, m2:null, m3:null, m4:null, m5:null, m6:null, m7:null, m8:null, m9:null, m10:null, m11:null, m12:null },
];

// MRR waterfall — monthly movement
const MRR_WATERFALL = [
  { month: "Jul", newMrr: 42000, expansion: 8200, contraction: -3100, churn: -18500, net: 28600 },
  { month: "Aug", newMrr: 45000, expansion: 9100, contraction: -2800, churn: -17200, net: 34100 },
  { month: "Sep", newMrr: 38000, expansion: 7800, contraction: -3400, churn: -19100, net: 23300 },
  { month: "Oct", newMrr: 51000, expansion: 10200, contraction: -2600, churn: -16800, net: 41800 },
  { month: "Nov", newMrr: 48000, expansion: 11500, contraction: -3200, churn: -17500, net: 38800 },
  { month: "Dec", newMrr: 35000, expansion: 6800, contraction: -4100, churn: -20200, net: 17500 },
  { month: "Jan", newMrr: 52000, expansion: 12000, contraction: -2900, churn: -16200, net: 44900 },
  { month: "Feb", newMrr: 49000, expansion: 10800, contraction: -3300, churn: -17800, net: 38700 },
  { month: "Mar", newMrr: 55000, expansion: 13200, contraction: -2700, churn: -15900, net: 49600 },
];

const CHANNELS = [
  { ch: "Organic Search", cust: 1998, pct: 28.4, cac: 179, ltv: 2481, ratio: 13.9, payback: 3.0, churn: 27.5, health: "Scale" },
  { ch: "Content",        cust: 1039, pct: 14.8, cac: 240, ltv: 2609, ratio: 10.9, payback: 3.9, churn: 27.1, health: "Scale" },
  { ch: "Referral",       cust: 947,  pct: 13.4, cac: 277, ltv: 3095, ratio: 11.2, payback: 4.0, churn: 26.9, health: "Scale" },
  { ch: "Paid Search",    cust: 1321, pct: 18.8, cac: 480, ltv: 2657, ratio: 5.5,  payback: 7.4, churn: 26.2, health: "Healthy" },
  { ch: "Paid Social",    cust: 878,  pct: 12.5, cac: 562, ltv: 2438, ratio: 4.3,  payback: 9.3, churn: 27.0, health: "Watch" },
  { ch: "Partnerships",   cust: 860,  pct: 12.2, cac: 757, ltv: 3578, ratio: 4.7,  payback: 9.3, churn: 24.4, health: "Watch" },
];

// Payback curves: cumulative revenue per channel over months
const PAYBACK_CURVES = (() => {
  const months = Array.from({ length: 19 }, (_, i) => i);
  return months.map(m => {
    const row = { month: m };
    CHANNELS.forEach(ch => {
      const survivalRate = Math.pow(1 - ch.churn / 100 / 12, m);
      row[ch.ch] = Math.round(ch.cac > 0 ? (KPI.arpu * m * survivalRate) / ch.cac * 100 : 0);
    });
    return row;
  });
})();

// SaaS benchmarks for context
const BENCHMARKS = {
  ltvCac: { good: 3, great: 5, orbit: 7.3 },
  payback: { good: 18, great: 12, orbit: 7.7 },
  monthlyChurn: { good: 3, great: 1.5, orbit: 0.82 },
  nrr: { good: 100, great: 120, orbit: 94.2 },
};

// ─── Utility ────────────────────────────────────────────────────────────────
const fmt = (n) => {
  if (n >= 1e6) return `$${(n/1e6).toFixed(1)}M`;
  if (n >= 1e3) return `$${(n/1e3).toFixed(0)}K`;
  return `$${n}`;
};

// ─── Components ─────────────────────────────────────────────────────────────
function MetricCard({ label, value, sub, context }) {
  return React.createElement("div", {
    className: "rounded-lg border p-3 sm:p-5",
    style: { background: T.surface, borderColor: T.border },
  },
    React.createElement("div", {
      className: "text-xs sm:text-sm font-semibold uppercase tracking-wider mb-1 sm:mb-2",
      style: { color: T.textMuted },
    }, label),
    React.createElement("div", {
      className: "text-2xl sm:text-4xl font-bold",
      style: { color: T.text },
    }, value),
    sub && React.createElement("div", {
      className: "text-sm mt-1.5",
      style: { color: T.textDim },
    }, sub),
    context && React.createElement("div", {
      className: "text-sm mt-3 pt-3 leading-relaxed",
      style: { color: T.textSec, borderTop: `1px solid ${T.border}` },
    }, context),
  );
}

function Section({ id, title, sub, children }) {
  return React.createElement("section", { id, className: "mb-8 sm:mb-10" },
    React.createElement("div", { className: "mb-4 sm:mb-5" },
      React.createElement("h2", {
        className: "text-xl sm:text-3xl font-bold",
        style: { color: T.text },
      }, title),
      sub && React.createElement("p", {
        className: "text-sm sm:text-base mt-1 sm:mt-1.5 leading-relaxed",
        style: { color: T.textSec },
      }, sub),
    ),
    children,
  );
}

function Panel({ children, className = "" }) {
  return React.createElement("div", {
    className: `rounded-lg border p-5 ${className}`,
    style: { background: T.surface, borderColor: T.border },
  }, children);
}

// ─── Cohort Heatmap ─────────────────────────────────────────────────────────
function CohortHeatmap() {
  const months = Array.from({ length: 13 }, (_, i) => i);

  const getColor = (val) => {
    if (val === null || val === undefined) return "transparent";
    if (val >= 80) return "#2A7A68";
    if (val >= 65) return "#3A7A6A";
    if (val >= 55) return "#4A7A5A";
    if (val >= 45) return "#7A7A3A";
    if (val >= 35) return "#8A6B38";
    return "#7A5530";
  };

  return React.createElement("div", { className: "overflow-x-auto" },
    React.createElement("table", {
      className: "w-full text-sm",
      style: { minWidth: 720, borderCollapse: "separate", borderSpacing: 2 },
    },
      React.createElement("thead", null,
        React.createElement("tr", null,
          React.createElement("th", {
            className: "text-left px-2 py-1 font-semibold",
            style: { color: T.textMuted, minWidth: 90 },
          }, "Cohort"),
          React.createElement("th", {
            className: "text-right px-1 py-1 font-semibold",
            style: { color: T.textMuted, width: 50 },
          }, "Size"),
          ...months.map(m => React.createElement("th", {
            key: m,
            className: "text-center px-1 py-1 font-semibold",
            style: { color: T.textMuted, width: 42 },
          }, `M${m}`)),
        ),
      ),
      React.createElement("tbody", null,
        COHORT_HEATMAP.map((row, ri) => React.createElement("tr", { key: ri },
          React.createElement("td", {
            className: "px-2 py-1 font-medium",
            style: { color: T.text },
          }, row.cohort),
          React.createElement("td", {
            className: "text-right px-1 py-1",
            style: { color: T.textMuted },
          }, row.size.toLocaleString()),
          ...months.map(m => {
            const val = row[`m${m}`];
            return React.createElement("td", {
              key: m,
              className: "text-center py-1 rounded",
              style: {
                background: getColor(val),
                color: val !== null ? "#fff" : "transparent",
                fontWeight: 600,
                fontSize: 12,
                padding: "5px 3px",
              },
            }, val !== null ? `${val}%` : "");
          }),
        )),
      ),
    ),
    React.createElement("div", {
      className: "flex items-center gap-2 mt-3 text-[10px]",
      style: { color: T.textMuted },
    },
      React.createElement("span", null, "Retention %:"),
      ...[
        { label: ">80%", color: "#2A7A68" },
        { label: "65-80%", color: "#3A7A6A" },
        { label: "55-65%", color: "#4A7A5A" },
        { label: "45-55%", color: "#7A7A3A" },
        { label: "35-45%", color: "#8A6B38" },
        { label: "<35%", color: "#7A5530" },
      ].map((item, i) => React.createElement("div", {
        key: i,
        className: "flex items-center gap-1",
      },
        React.createElement("div", {
          className: "w-3 h-3 rounded-sm",
          style: { background: item.color },
        }),
        React.createElement("span", null, item.label),
      )),
    ),
  );
}

// ─── MRR Waterfall ──────────────────────────────────────────────────────────
function MrrWaterfall() {
  return React.createElement(ResponsiveContainer, { width: "100%", height: 300 },
    React.createElement(ComposedChart, { data: MRR_WATERFALL },
      React.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: T.border }),
      React.createElement(XAxis, { dataKey: "month", tick: { fontSize: 11, fill: T.textMuted } }),
      React.createElement(YAxis, {
        tick: { fontSize: 11, fill: T.textMuted },
        tickFormatter: v => v >= 1000 ? `$${(v/1000).toFixed(0)}K` : `$${v}`,
      }),
      React.createElement(Tooltip, {
        contentStyle: { background: T.surface, border: `1px solid ${T.border}`, borderRadius: 8, fontSize: 12 },
        labelStyle: { color: T.text },
        formatter: (v, name) => [`$${v.toLocaleString()}`, name],
      }),
      React.createElement(Legend, { wrapperStyle: { fontSize: 11, color: T.textMuted } }),
      React.createElement(Bar, { dataKey: "newMrr", name: "New MRR", stackId: "pos", fill: T.teal, radius: [2, 2, 0, 0], barSize: 24 }),
      React.createElement(Bar, { dataKey: "expansion", name: "Expansion", stackId: "pos", fill: T.lavender, radius: [2, 2, 0, 0] }),
      React.createElement(Bar, { dataKey: "contraction", name: "Contraction", stackId: "neg", fill: T.gold, radius: [0, 0, 2, 2] }),
      React.createElement(Bar, { dataKey: "churn", name: "Churned", stackId: "neg", fill: "#C48E6A", radius: [0, 0, 2, 2] }),
      React.createElement(Line, { type: "monotone", dataKey: "net", name: "Net New", stroke: "#fff", strokeWidth: 2, dot: { fill: "#fff", r: 3 } }),
    ),
  );
}

// ─── Benchmark Gauge ────────────────────────────────────────────────────────
function BenchmarkBar({ label, value, unit, good, great, orbit, invert }) {
  const maxVal = invert ? good * 1.5 : great * 1.5;
  const goodPct = (good / maxVal) * 100;
  const greatPct = (great / maxVal) * 100;
  const orbitPct = Math.min((orbit / maxVal) * 100, 98);

  const isGood = invert ? orbit <= good : orbit >= good;
  const isGreat = invert ? orbit <= great : orbit >= great;
  const color = isGreat ? T.teal : isGood ? T.gold : "#C48E6A";

  return React.createElement("div", { className: "mb-4" },
    React.createElement("div", { className: "flex justify-between mb-1" },
      React.createElement("span", { className: "text-xs font-medium", style: { color: T.text } }, label),
      React.createElement("span", { className: "text-xs font-bold", style: { color } }, `${value}${unit}`),
    ),
    React.createElement("div", {
      className: "h-2 rounded-full relative overflow-hidden",
      style: { background: T.border },
    },
      React.createElement("div", {
        className: "h-full rounded-full transition-all",
        style: { width: `${orbitPct}%`, background: color },
      }),
      React.createElement("div", {
        className: "absolute top-0 h-full border-r-2 border-dashed",
        style: { left: `${goodPct}%`, borderColor: T.textDim },
      }),
      React.createElement("div", {
        className: "absolute top-0 h-full border-r-2",
        style: { left: `${greatPct}%`, borderColor: T.textMuted },
      }),
    ),
    React.createElement("div", { className: "flex justify-between mt-0.5" },
      React.createElement("span", { className: "text-[9px]", style: { color: T.textDim } }, ""),
      React.createElement("span", { className: "text-[9px]", style: { color: T.textDim, marginLeft: `${goodPct - 5}%` } }, `Good: ${good}${unit}`),
      React.createElement("span", { className: "text-[9px]", style: { color: T.textDim } }, `Best: ${great}${unit}`),
    ),
  );
}

// ─── Scenario Simulator ─────────────────────────────────────────────────────
function Simulator() {
  const [retentionLift, setRetentionLift] = React.useState(0);
  const [annualConv, setAnnualConv] = React.useState(0);
  const [cacReduction, setCacReduction] = React.useState(0);

  // Base metrics
  const baseChurn = 0.82;
  const baseLtv = 2744;
  const baseCac = 376;
  const baseArr = 3803829;
  const basePayback = 7.7;

  // Computed impact
  const newChurn = Math.max(0.1, baseChurn * (1 - retentionLift / 100));
  const churnMultiplier = baseChurn / newChurn;
  const annualEffect = 1 + (annualConv / 100) * 0.6;
  const newLtv = Math.round(baseLtv * churnMultiplier * annualEffect);
  const newCac = Math.round(baseCac * (1 - cacReduction / 100));
  const newRatio = (newLtv / Math.max(newCac, 1)).toFixed(1);
  const newPayback = (newCac / KPI.arpu).toFixed(1);
  const arrImpact = Math.round(baseArr * (churnMultiplier * annualEffect - 1));
  const cacSavings = Math.round(KPI.totalAcqSpend * (cacReduction / 100));

  function Slider({ label, value, onChange, min, max, step, unit, color }) {
    return React.createElement("div", { className: "mb-6" },
      React.createElement("div", { className: "flex justify-between mb-2" },
        React.createElement("span", { className: "text-sm font-medium", style: { color: T.text } }, label),
        React.createElement("span", {
          className: "text-base font-bold px-2 py-0.5 rounded",
          style: { color, background: `${color}20` },
        }, `${value > 0 ? "+" : ""}${value}${unit}`),
      ),
      React.createElement("input", {
        type: "range", min, max, step, value,
        onChange: e => onChange(Number(e.target.value)),
        className: "w-full h-1.5 rounded-full appearance-none cursor-pointer",
        style: {
          background: `linear-gradient(to right, ${color} 0%, ${color} ${(value - min) / (max - min) * 100}%, ${T.border} ${(value - min) / (max - min) * 100}%, ${T.border} 100%)`,
          accentColor: color,
        },
      }),
    );
  }

  return React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-5" },
    // Controls
    React.createElement(Panel, null,
      React.createElement("div", {
        className: "text-xs font-semibold uppercase tracking-wider mb-4",
        style: { color: T.textMuted },
      }, "Adjust Levers"),
      React.createElement(Slider, {
        label: "Retention improvement",
        value: retentionLift, onChange: setRetentionLift,
        min: 0, max: 40, step: 5, unit: "%", color: T.teal,
      }),
      React.createElement(Slider, {
        label: "Monthly→Annual conversion lift",
        value: annualConv, onChange: setAnnualConv,
        min: 0, max: 50, step: 5, unit: "%", color: T.accent,
      }),
      React.createElement(Slider, {
        label: "CAC reduction (channel reallocation)",
        value: cacReduction, onChange: setCacReduction,
        min: 0, max: 30, step: 5, unit: "%", color: T.gold,
      }),
    ),

    // Results
    React.createElement(Panel, null,
      React.createElement("div", {
        className: "text-xs font-semibold uppercase tracking-wider mb-4",
        style: { color: T.textMuted },
      }, "Projected Impact"),
      React.createElement("div", { className: "grid grid-cols-2 gap-3" },
        ...[
          { label: "New LTV", now: fmt(baseLtv), proj: fmt(newLtv), delta: newLtv - baseLtv, prefix: "$" },
          { label: "New CAC", now: fmt(baseCac), proj: fmt(newCac), delta: -(baseCac - newCac), prefix: "$" },
          { label: "LTV:CAC", now: `${(baseLtv/baseCac).toFixed(1)}x`, proj: `${newRatio}x`, delta: (newRatio - baseLtv/baseCac).toFixed(1), suffix: "x" },
          { label: "Payback", now: `${basePayback} mo`, proj: `${newPayback} mo`, delta: -(basePayback - newPayback).toFixed(1), suffix: " mo" },
        ].map((m, i) => React.createElement("div", {
          key: i,
          className: "rounded-lg p-3",
          style: { background: T.surfaceAlt, border: `1px solid ${T.border}` },
        },
          React.createElement("div", { className: "text-xs uppercase tracking-wider mb-1.5", style: { color: T.textMuted } }, m.label),
          React.createElement("div", { className: "text-xl font-bold", style: { color: T.text } }, m.proj),
          React.createElement("div", {
            className: "text-xs mt-1",
            style: { color: m.delta > 0 && m.label !== "New CAC" ? T.green : m.delta < 0 && m.label === "New CAC" ? T.green : T.textDim },
          }, `was ${m.now}`),
        )),
      ),
      // Total impact
      (retentionLift > 0 || annualConv > 0 || cacReduction > 0) &&
      React.createElement("div", {
        className: "mt-4 rounded-lg p-3 border",
        style: { background: `${T.green}10`, borderColor: `${T.green}30` },
      },
        React.createElement("div", { className: "text-xs font-semibold mb-1", style: { color: T.teal } }, "Combined Impact"),
        React.createElement("div", { className: "text-sm", style: { color: T.text } },
          arrImpact > 0 ? `+${fmt(arrImpact)} incremental ARR` : "",
          cacSavings > 0 ? ` · ${fmt(cacSavings)} CAC savings` : "",
          (arrImpact <= 0 && cacSavings <= 0) ? "Adjust sliders to see projected impact" : "",
        ),
      ),
    ),
  );
}

// ─── Channel Scatter Plot (CAC vs LTV) ──────────────────────────────────────
function ChannelBubble() {
  const data = CHANNELS.map(ch => ({
    name: ch.ch,
    cac: ch.cac,
    ltv: ch.ltv,
    customers: ch.cust,
    ratio: ch.ratio,
  }));

  return React.createElement("div", { className: "relative" },
    React.createElement(ResponsiveContainer, { width: "100%", height: 340 },
      React.createElement(ComposedChart, {
        data,
        margin: { top: 20, right: 30, bottom: 20, left: 20 },
      },
        React.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: T.border }),
        React.createElement(XAxis, {
          dataKey: "cac", type: "number", name: "CAC",
          tick: { fontSize: 11, fill: T.textMuted },
          label: { value: "CAC ($)", position: "bottom", offset: 0, fontSize: 11, fill: T.textMuted },
          tickFormatter: v => `$${v}`,
        }),
        React.createElement(YAxis, {
          dataKey: "ltv", type: "number", name: "LTV",
          tick: { fontSize: 11, fill: T.textMuted },
          label: { value: "LTV ($)", angle: -90, position: "insideLeft", offset: 10, fontSize: 11, fill: T.textMuted },
          tickFormatter: v => v >= 1000 ? `$${(v/1000).toFixed(0)}K` : `$${v}`,
        }),
        React.createElement(Tooltip, {
          contentStyle: { background: T.surface, border: `1px solid ${T.border}`, borderRadius: 8, fontSize: 12 },
          formatter: (v, name) => {
            if (name === "LTV") return [`$${v.toLocaleString()}`, "LTV"];
            return [v, name];
          },
          labelFormatter: (_, payload) => payload[0] ? payload[0].payload.name : "",
        }),
        data.map((d, i) => React.createElement(ReferenceLine, {
          key: `line-${i}`,
          segment: [{ x: 0, y: 0 }, { x: d.cac, y: d.ltv }],
          stroke: CHANNEL_C[d.name],
          strokeDasharray: "3 3",
          strokeOpacity: 0.3,
        })),
        React.createElement(Line, {
          data: [{ cac: 0, ltv: 0 }, { cac: 800, ltv: 2400 }],
          dataKey: "ltv", stroke: T.textDim, strokeDasharray: "8 4", strokeWidth: 1, dot: false,
        }),
        data.map((d, i) => {
          const r = Math.sqrt(d.customers / 200) * 3 + 6;
          return React.createElement("circle", {
            key: i,
            cx: `${((d.cac) / 900) * 100}%`,
            cy: `${(1 - (d.ltv) / 4000) * 100}%`,
            r,
            fill: CHANNEL_C[d.name],
            opacity: 0.8,
            stroke: "#fff",
            strokeWidth: 1.5,
          });
        }),
      ),
    ),
    // Legend
    React.createElement("div", {
      className: "flex flex-wrap gap-3 mt-2 justify-center",
    },
      CHANNELS.map((ch, i) => React.createElement("div", {
        key: i,
        className: "flex items-center gap-1.5 text-[11px]",
        style: { color: T.textMuted },
      },
        React.createElement("div", {
          className: "w-2.5 h-2.5 rounded-full",
          style: { background: CHANNEL_C[ch.ch] },
        }),
        React.createElement("span", null, ch.ch),
        React.createElement("span", { className: "font-bold", style: { color: CHANNEL_C[ch.ch] } }, `${ch.ratio}x`),
      )),
    ),
  );
}

// ─── Payback Curves ─────────────────────────────────────────────────────────
function PaybackCurves() {
  return React.createElement(ResponsiveContainer, { width: "100%", height: 300 },
    React.createElement(LineChart, { data: PAYBACK_CURVES },
      React.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: T.border }),
      React.createElement(XAxis, {
        dataKey: "month",
        tick: { fontSize: 11, fill: T.textMuted },
        label: { value: "Months", position: "bottom", offset: 0, fontSize: 11, fill: T.textMuted },
      }),
      React.createElement(YAxis, {
        tick: { fontSize: 11, fill: T.textMuted },
        tickFormatter: v => `${v}%`,
        label: { value: "Cumulative Revenue / CAC", angle: -90, position: "insideLeft", offset: 10, fontSize: 10, fill: T.textMuted },
      }),
      React.createElement(Tooltip, {
        contentStyle: { background: T.surface, border: `1px solid ${T.border}`, borderRadius: 8, fontSize: 12 },
        formatter: (v, name) => [`${v}%`, name],
      }),
      React.createElement(ReferenceLine, { y: 100, stroke: "#fff", strokeDasharray: "8 4", strokeWidth: 1.5, label: { value: "Breakeven", position: "right", fill: T.textMuted, fontSize: 10 } }),
      ...Object.keys(CHANNEL_C).map(ch => React.createElement(Line, {
        key: ch,
        type: "monotone",
        dataKey: ch,
        stroke: CHANNEL_C[ch],
        strokeWidth: 2,
        dot: false,
      })),
    ),
  );
}

// ─── Main App ───────────────────────────────────────────────────────────────
function App() {
  const [activeNav, setActiveNav] = React.useState("overview");

  const navItems = [
    { id: "overview", icon: "\u25A0", label: "Overview" },
    { id: "cohorts", icon: "\u2593", label: "Cohorts" },
    { id: "channels", icon: "\u25CE", label: "Channels" },
    { id: "simulator", icon: "\u2699", label: "Simulator" },
  ];

  return React.createElement("div", {
    className: "min-h-screen",
    style: { background: T.bg, color: T.text },
  },
    // Top bar
    React.createElement("header", {
      className: "border-b sticky top-0 z-20",
      style: { background: T.bg, borderColor: T.border },
    },
      React.createElement("div", { className: "max-w-7xl mx-auto px-4 py-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2" },
        React.createElement("div", null,
          React.createElement("div", { className: "flex items-center gap-2" },
            React.createElement("div", {
              className: "w-2 h-2 rounded-full",
              style: { background: T.green },
            }),
            React.createElement("span", {
              className: "text-[10px] font-semibold uppercase tracking-widest",
              style: { color: T.textMuted },
            }, "Orbit SaaS · Unit Economics"),
          ),
          React.createElement("h1", { className: "text-xl sm:text-3xl font-bold mt-0.5" }, "LTV / CAC Command Center"),
        ),
        // Nav pills — scrollable on mobile
        React.createElement("nav", {
          className: "flex gap-1 overflow-x-auto -mx-4 px-4 sm:mx-0 sm:px-0 pb-1 sm:pb-0",
          style: { WebkitOverflowScrolling: "touch" },
        },
          navItems.map(item => React.createElement("button", {
            key: item.id,
            onClick: () => setActiveNav(item.id),
            className: `px-3 sm:px-4 py-1.5 sm:py-2 rounded-md text-xs sm:text-sm font-medium transition-all whitespace-nowrap ${
              activeNav === item.id ? "" : "hover:opacity-80"
            }`,
            style: {
              background: activeNav === item.id ? T.accent : "transparent",
              color: activeNav === item.id ? "#fff" : T.textMuted,
            },
          }, `${item.icon} ${item.label}`)),
        ),
      ),
    ),

    // Content
    React.createElement("main", { className: "max-w-7xl mx-auto px-3 sm:px-4 py-4 sm:py-6" },

      // ── Overview ──
      activeNav === "overview" && React.createElement(React.Fragment, null,
        // Context header — industry framing
        React.createElement("div", {
          className: "rounded-lg p-6 mb-6 border",
          style: { background: T.surface, borderColor: T.border },
        },
          React.createElement("p", { className: "text-lg leading-relaxed mb-3", style: { color: T.text } },
            React.createElement("strong", null, "The Problem"),
            " — In 2025, the median SaaS company spends $2.00 to acquire $1.00 of new ARR, up 14% from 2023. Acquisition costs are rising, but most growth teams still optimize for volume instead of unit economics. ",
            "This dashboard answers the question every board and VP of Growth needs answered: ",
            React.createElement("em", { style: { color: T.accent } }, "are we growing efficiently, and where exactly should we invest to maximize return?"),
          ),
          React.createElement("p", { className: "text-base mt-2 leading-relaxed", style: { color: T.textSec } },
            "We model Orbit's customer-level unit economics using the a16z growth metrics framework: gross-margin-adjusted LTV, fully-loaded CAC (including sales salaries, tools, and content costs), cohort-based retention, and channel-level ROI. All CAC figures are fully loaded — not just ad spend.",
          ),
        ),
        // KPI row — 4 primary metrics with deep context
        React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-3 sm:gap-4 mb-6" },
          React.createElement(MetricCard, { label: "ARR", value: fmt(KPI.arr), sub: `${KPI.active.toLocaleString()} active customers`, context: "Orbit is at the $1–5M ARR stage. At this scale, unit economics discipline determines whether you reach $10M or stall." }),
          React.createElement(MetricCard, { label: "LTV : CAC", value: `${KPI.ltvCac}x`, sub: `LTV ${fmt(KPI.blendedLtv)} / CAC ${fmt(KPI.blendedCac)}`, context: "Median B2B SaaS is 3.2x (across 612 companies). Orbit's 7.3x suggests efficient growth — or underinvestment in acquisition." }),
          React.createElement(MetricCard, { label: "Payback Period", value: `${KPI.payback} months`, sub: `ARPU $${KPI.arpu}/mo`, context: "Median at this ARR scale is 8.8 months. Orbit's 7.7 months is healthy — cash isn't trapped in long payback cycles." }),
          React.createElement(MetricCard, { label: "Net Revenue Retention", value: `${KPI.nrr}%`, sub: `Monthly churn: ${KPI.monthlyChurn}%`, context: "This is the red flag. Below 100% = existing revenue shrinks. Companies with 120%+ NRR trade at 12–15x revenue vs 5–7x below 100%." }),
        ),

        // Benchmark context
        React.createElement(Section, {
          title: "Where Orbit Stands vs. Industry",
          sub: "Benchmarked against 612 B2B SaaS companies (a16z/ChartMogul data). Dashed = median, solid = top quartile.",
        },
          React.createElement(Panel, null,
            React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-2" },
              React.createElement(BenchmarkBar, { label: "LTV:CAC Ratio", value: "7.3", unit: "x", good: 3, great: 5, orbit: 7.3 }),
              React.createElement(BenchmarkBar, { label: "Payback Period", value: "7.7", unit: " mo", good: 18, great: 12, orbit: 7.7, invert: true }),
              React.createElement(BenchmarkBar, { label: "Monthly Churn", value: "0.82", unit: "%", good: 3, great: 1.5, orbit: 0.82, invert: true }),
              React.createElement(BenchmarkBar, { label: "Net Revenue Retention", value: "94.2", unit: "%", good: 100, great: 120, orbit: 94.2 }),
            ),
          ),
        ),

        // MRR Waterfall — with business context above
        React.createElement(Section, {
          title: "MRR Movement — The Revenue Machine",
          sub: "Where is revenue coming from and where is it leaking? This is the P&L of growth.",
        },
          React.createElement("div", {
            className: "rounded-lg p-4 mb-4 border",
            style: { background: `${T.gold}08`, borderColor: `${T.gold}25` },
          },
            React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.text } },
              React.createElement("strong", { style: { color: T.gold } }, "Why this matters: "),
              "MRR movement is the leading indicator that tells you whether your revenue engine is accelerating or decaying ",
              React.createElement("em", null, "before"),
              " it shows up in quarterly ARR. If churned + contracted MRR consistently exceeds new + expansion, you're on a treadmill — running harder just to stay in place.",
            ),
          ),
          React.createElement(Panel, null,
            React.createElement(MrrWaterfall),
          ),
        ),

        // Diagnosis + Action Items
        React.createElement("div", {
          className: "rounded-lg p-6 border",
          style: { background: `${T.accent}08`, borderColor: `${T.accent}30` },
        },
          React.createElement("div", { className: "text-base font-semibold uppercase tracking-wider mb-4", style: { color: T.accent } }, "Executive Diagnosis"),
          React.createElement("p", { className: "text-lg leading-relaxed mb-4", style: { color: T.text } },
            "Orbit's headline metrics — 7.3x LTV:CAC, 7.7-month payback — beat the median B2B SaaS company (3.2x, 8.8mo). ",
            "But these topline numbers mask a structural problem that will compound over the next 12 months:",
          ),

          // Problem 1 — NRR
          React.createElement("div", {
            className: "rounded-lg p-4 mb-4 border",
            style: { background: `${T.gold}06`, borderColor: `${T.gold}20` },
          },
            React.createElement("p", { className: "font-semibold text-lg mb-2", style: { color: T.gold } },
              "Problem #1: Revenue Contraction (NRR 94.2%)"
            ),
            React.createElement("p", { className: "text-base leading-relaxed mb-2", style: { color: T.text } },
              "NRR below 100% means the existing customer base is a depreciating asset. Every month, Orbit loses ~5.8% of its revenue base. ",
              "At current ARR, that's roughly $",
              React.createElement("strong", null, (KPI.arr * 0.058 / 12).toFixed(0) + "K/month"),
              " in silent revenue erosion — before a single customer cancels.",
            ),
            React.createElement("p", { className: "text-base leading-relaxed mb-2", style: { color: T.text } },
              React.createElement("strong", null, "Why this is existential: "),
              "Public SaaS companies with NRR above 120% trade at 12–15x forward revenue. Below 100%, that multiple drops to 5–7x. ",
              "For a company at Orbit's scale, the NRR gap alone can represent a $10–20M difference in enterprise value.",
            ),
            React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
              React.createElement("strong", null, "Action: "),
              "Deploy a four-lever expansion playbook — seat expansion, feature upsells, usage-based pricing tiers, and segment upsells into adjacent business units. ",
              "Top SaaS teams (Slack, Datadog, Snowflake) embed expansion into the product so NRR growth is self-serve, not sales-driven.",
            ),
          ),

          // Problem 2 — Channel mix
          React.createElement("div", {
            className: "rounded-lg p-4 mb-4 border",
            style: { background: `#C48E6A06`, borderColor: `#C48E6A20` },
          },
            React.createElement("p", { className: "font-semibold text-lg mb-2", style: { color: "#C48E6A" } },
              "Problem #2: CAC Concentration Risk"
            ),
            React.createElement("p", { className: "text-base leading-relaxed mb-2", style: { color: T.text } },
              "55% of acquisition budget flows to Paid Social ($560 avg CAC) and Partnerships ($750 avg CAC) — channels that deliver LTV:CAC below 5x while Organic Search and Content deliver 10x+. ",
              "Industry benchmarks show B2B SaaS Paid Social CAC ranges $800–$2,000; Orbit's $560 is aggressive but still 3x the cost of organic channels.",
            ),
            React.createElement("p", { className: "text-base leading-relaxed mb-2", style: { color: T.text } },
              React.createElement("strong", null, "The hidden cost: "),
              "Deepnote (a B2B analytics SaaS) cut CAC by 72% by rebuilding their attribution stack, validating ICP criteria before scaling, and reallocating paid spend to content-led channels — growing paying customers 2.5x simultaneously.",
            ),
            React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
              React.createElement("strong", null, "Action: "),
              "Reallocate 20–30% of paid budget to Content and Organic. Implement server-side attribution to identify true CAC per channel (not last-click). ",
              "Run ICP validation on highest-CAC channels before scaling — companies that do this typically see 35% lower effective CAC.",
            ),
          ),

          // Bottom line
          React.createElement("div", {
            className: "rounded-lg p-4 border",
            style: { background: `${T.accent}06`, borderColor: `${T.accent}20` },
          },
            React.createElement("p", { className: "text-base font-semibold mb-2", style: { color: T.accent } },
              "The 90-Day Roadmap"
            ),
            React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" },
              React.createElement("div", null,
                React.createElement("p", { className: "text-sm font-semibold mb-1", style: { color: T.gold } }, "Month 1: Diagnose"),
                React.createElement("p", { className: "text-sm leading-relaxed", style: { color: T.text } },
                  "Build cohort-level churn attribution. Identify the top 3 churn triggers by plan and tenure band. Establish NRR baseline by segment.",
                ),
              ),
              React.createElement("div", null,
                React.createElement("p", { className: "text-sm font-semibold mb-1", style: { color: T.accent } }, "Month 2: Intervene"),
                React.createElement("p", { className: "text-sm leading-relaxed", style: { color: T.text } },
                  "Launch 90-day onboarding health score. Ship usage-based upsell triggers. Reallocate 25% of Paid Social budget to Content + SEO.",
                ),
              ),
              React.createElement("div", null,
                React.createElement("p", { className: "text-sm font-semibold mb-1", style: { color: T.lavender } }, "Month 3: Measure"),
                React.createElement("p", { className: "text-sm leading-relaxed", style: { color: T.text } },
                  "Target: NRR from 94% → 100%. Blended CAC down 15%. These two moves alone would add ~$400K in preserved + recovered ARR annually.",
                ),
              ),
            ),
          ),
        ),
      ),

      // ── Cohorts ──
      activeNav === "cohorts" && React.createElement(React.Fragment, null,
        // Business context ABOVE the chart
        React.createElement("div", {
          className: "rounded-lg p-6 mb-6 border",
          style: { background: T.surface, borderColor: T.border },
        },
          React.createElement("p", { className: "text-lg leading-relaxed mb-3", style: { color: T.text } },
            React.createElement("strong", null, "The Retention Problem"),
            " — ChartMogul's benchmark data across 2,500+ SaaS companies shows: a 5% monthly churn rate silently kills 46% of your customer base per year. ",
            "Even companies with \"good\" gross retention at 85% grow 1.5–3x slower than those above 90%. ",
            "Cohort analysis reveals ",
            React.createElement("em", { style: { color: T.accent } }, "exactly where and when"),
            " customers disengage — so you can intervene before they leave.",
          ),
          React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
            "Key insight from real SaaS churn data: the cost of a churned customer isn't just their LTV — it's the replacement CAC needed to fill the hole. ",
            "At Orbit's blended CAC of $376, every churned customer costs the business $376 in re-acquisition cost just to get back to zero.",
          ),
        ),

        React.createElement(Section, {
          title: "Cohort Retention Heatmap",
          sub: "Each row = a quarterly signup cohort. Each cell = % still active at month M. Read left-to-right to see the churn curve. Read top-to-bottom to see if retention is improving over time.",
        },
          React.createElement(Panel, null,
            React.createElement(CohortHeatmap),
          ),
        ),

        // Interpretation — with dollar impact
        React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4 mb-4" },
          React.createElement(Panel, null,
            React.createElement("div", { className: "text-xs font-semibold uppercase tracking-wider mb-2", style: { color: "#C48E6A" } }, "Critical Finding"),
            React.createElement("div", { className: "text-3xl font-bold mb-2", style: { color: T.text } }, "18–35%"),
            React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
              "of each cohort churns in months 1–3. This \"activation gap\" is the #1 drag on LTV. ",
              "Industry research shows that customers who don't reach their \"aha moment\" within the first 30 days are 6x more likely to churn.",
            ),
          ),
          React.createElement(Panel, null,
            React.createElement("div", { className: "text-xs font-semibold uppercase tracking-wider mb-2", style: { color: T.teal } }, "Positive Signal"),
            React.createElement("div", { className: "text-3xl font-bold mb-2", style: { color: T.text } }, "82% → 88%"),
            React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
              "M1 retention improved across cohorts. This 6pp gain suggests product or onboarding iterations are working. ",
              "At Orbit's ARPU, each percentage point of M1 improvement = ~$56K ARR/year preserved.",
            ),
          ),
          React.createElement(Panel, null,
            React.createElement("div", { className: "text-xs font-semibold uppercase tracking-wider mb-2", style: { color: T.gold } }, "Dollar Impact"),
            React.createElement("div", { className: "text-3xl font-bold mb-2", style: { color: T.text } }, "$380K"),
            React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
              "ARR preserved annually from a 5pp improvement in M1-M3 retention. This is the single highest-ROI investment Orbit can make — cheaper than acquiring new customers and compounds over time.",
            ),
          ),
        ),

        // Action items for retention
        React.createElement("div", {
          className: "rounded-lg p-5 mb-8 border",
          style: { background: `${T.teal}06`, borderColor: `${T.teal}20` },
        },
          React.createElement("div", { className: "text-sm font-semibold uppercase tracking-wider mb-3", style: { color: T.teal } }, "Retention Action Plan"),
          React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" },
            React.createElement("div", null,
              React.createElement("p", { className: "text-sm font-semibold mb-1", style: { color: T.text } }, "Immediate (0–30 days)"),
              React.createElement("p", { className: "text-sm leading-relaxed", style: { color: T.textSec } },
                "Build a 14-day onboarding health score based on feature adoption milestones. ",
                "Trigger automated intervention (in-app guides, CSM outreach) when users miss key activation events. ",
                "Real-world example: Deepnote reduced activation churn by 40% using milestone-based onboarding triggers.",
              ),
            ),
            React.createElement("div", null,
              React.createElement("p", { className: "text-sm font-semibold mb-1", style: { color: T.text } }, "Medium-term (30–90 days)"),
              React.createElement("p", { className: "text-sm leading-relaxed", style: { color: T.textSec } },
                "Build a churn prediction model using M1-M3 engagement signals. ",
                "Industry data shows predictive churn models add 10–15% to customer LTV by enabling preemptive retention actions. ",
                "Focus on customers in the \"amber zone\" (engaged but not fully activated).",
              ),
            ),
          ),
        ),

        // Retention by billing — with context above
        React.createElement(Section, {
          title: "The Billing Cycle Effect — Your Best Retention Lever",
          sub: "Annual contracts reduce churn by 75%+ vs monthly. This isn't just about lock-in — it's about customer commitment and onboarding investment.",
        },
          React.createElement("div", {
            className: "rounded-lg p-4 mb-4 border",
            style: { background: `${T.gold}06`, borderColor: `${T.gold}20` },
          },
            React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.text } },
              React.createElement("strong", { style: { color: T.gold } }, "Why this matters: "),
              "Monthly customers churn at 42.7% — nearly 8x the rate of annual customers (11.3%). ",
              "Converting just 10% of monthly subscribers to annual would reduce blended churn by ~3pp and add ~$200K in retained ARR/year. ",
              "Top SaaS companies (HubSpot, Zoom) incentivize annual billing with 15–20% discounts because the retention ROI far exceeds the discount cost.",
            ),
          ),
          React.createElement(Panel, null,
            React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4 mb-5" },
              [
                { label: "Monthly", churn: "42.7%", m12: "50.8%", color: T.gold },
                { label: "Annual", churn: "11.3%", m12: "93.1%", color: T.accent },
                { label: "2-Year", churn: "2.8%", m12: "96.5%", color: T.teal },
              ].map((d, i) => React.createElement("div", {
                key: i,
                className: "rounded-lg p-5 text-center border",
                style: { borderColor: `${d.color}40`, background: `${d.color}10` },
              },
                React.createElement("div", { className: "text-base font-bold mb-2", style: { color: d.color } }, d.label),
                React.createElement("div", { className: "text-3xl font-bold mb-1", style: { color: T.text } }, d.m12),
                React.createElement("div", { className: "text-xs", style: { color: T.textSec } }, "12-month retention"),
                React.createElement("div", { className: "text-xs mt-1", style: { color: T.textMuted } }, `Churn rate: ${d.churn}`),
              )),
            ),
          ),
        ),
      ),

      // ── Channels ──
      activeNav === "channels" && React.createElement(React.Fragment, null,
        // Business context ABOVE charts
        React.createElement("div", {
          className: "rounded-lg p-6 mb-6 border",
          style: { background: T.surface, borderColor: T.border },
        },
          React.createElement("p", { className: "text-lg leading-relaxed mb-3", style: { color: T.text } },
            React.createElement("strong", null, "The CAC Problem in 2025"),
            " — Customer acquisition costs across B2B SaaS have risen 40–60% since 2021. VC-backed companies spend 58% more on marketing as a % of revenue than bootstrapped peers (a16z data). ",
            "The question is no longer \"how much to spend\" but ",
            React.createElement("em", { style: { color: T.accent } }, "\"which dollars are actually creating value?\""),
          ),
          React.createElement("p", { className: "text-base leading-relaxed mb-3", style: { color: T.textSec } },
            "Industry channel CAC benchmarks for B2B SaaS: SEO/Content $200–600, Paid Search $600–1,500, LinkedIn Ads $800–2,000, Field Sales $2,000–10,000+. ",
            "All CAC figures below are fully loaded (ad spend + attribution share of team salaries + tools + content production).",
          ),
          React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.text } },
            React.createElement("strong", { style: { color: T.gold } }, "Key insight: "),
            "Deepnote (B2B analytics SaaS) cut CAC 72% in 12 months by rebuilding their attribution stack with server-side tracking, ",
            "validating ICP criteria before scaling any channel, and running semi-automated PPC tests to kill losers fast. Result: 158% more paying customers from paid channels.",
          ),
        ),

        // Strategic Recommendation — before charts
        React.createElement("div", {
          className: "rounded-lg p-6 border mb-6",
          style: { background: `${T.teal}08`, borderColor: `${T.teal}30` },
        },
          React.createElement("div", { className: "text-sm font-semibold uppercase tracking-wider mb-4", style: { color: T.teal } }, "Strategic Recommendation: Channel Portfolio Optimization"),
          React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6" },
            React.createElement("div", null,
              React.createElement("p", { className: "text-lg leading-relaxed mb-3", style: { color: T.text } },
                "Organic Search, Content, and Referral deliver ",
                React.createElement("strong", null, "11–14x LTV:CAC"),
                " and pay back in under 4 months. Yet they receive only 33% of acquisition spend.",
              ),
              React.createElement("p", { className: "text-base leading-relaxed mb-3", style: { color: T.textSec } },
                React.createElement("strong", { style: { color: T.text } }, "The rebalancing playbook: "),
                "Don't cut Paid Social completely — reduce budget 20%, use savings to fund 2 content hires ($120K/year). ",
                "Content compounds: organic traffic from SEO published today delivers customers for 24+ months. Paid stops the moment you turn off spend.",
              ),
              React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
                React.createElement("strong", { style: { color: T.text } }, "Attribution fix: "),
                "Implement server-side tracking (GTM server container or Segment) to measure true multi-touch CAC. ",
                "Last-click attribution typically overvalues paid channels by 30–40% and undervalues content/organic. ",
                "This data alone often changes budget allocation decisions.",
              ),
            ),
            React.createElement("div", {
              className: "rounded-lg p-4",
              style: { background: T.surfaceAlt, border: `1px solid ${T.border}` },
            },
              React.createElement("div", { className: "text-xs font-semibold uppercase tracking-wider mb-3", style: { color: T.textMuted } }, "Projected 6-Month Impact"),
              ...[
                { label: "Blended CAC", from: "$376", to: "$310", color: T.teal },
                { label: "LTV:CAC", from: "7.3x", to: "9.5x", color: T.teal },
                { label: "Annual savings", from: "—", to: "$175K", color: T.teal },
                { label: "CAC payback", from: "7.7mo", to: "5.8mo", color: T.teal },
              ].map((item, i) => React.createElement("div", {
                key: i,
                className: "flex justify-between items-center mb-2 text-sm",
              },
                React.createElement("span", { style: { color: T.textSec } }, item.label),
                React.createElement("span", null,
                  React.createElement("span", { className: "line-through mr-2 text-xs", style: { color: T.textDim } }, item.from),
                  React.createElement("span", { className: "font-bold", style: { color: item.color } }, item.to),
                ),
              )),
              React.createElement("div", {
                className: "mt-3 pt-3 text-xs leading-relaxed",
                style: { color: T.textMuted, borderTop: `1px solid ${T.border}` },
              },
                "Based on channel-level unit economics. Assumes content ramp takes 3-6 months to reach full organic volume. Short-term paid reduction offset by higher-LTV organic pipeline.",
              ),
            ),
          ),
        ),

        React.createElement(Section, {
          title: "Channel Economics: CAC vs LTV",
          sub: "Each bubble = a channel. Higher + left = better. Bubble size = customer volume. The diagonal line shows where LTV:CAC = 3x (minimum viable).",
        },
          React.createElement(Panel, null,
            React.createElement(ChannelBubble),
          ),
        ),

        // Channel table with deeper context
        React.createElement(Section, {
          title: "Channel-Level Unit Economics",
          sub: "Fully-loaded CAC, gross-margin-adjusted LTV, payback, and health classification. \"Scale\" = proven efficiency, increase budget. \"Watch\" = below threshold, needs ICP validation before more spend.",
        },
          React.createElement(Panel, null,
            React.createElement("div", { className: "overflow-x-auto" },
              React.createElement("table", {
                className: "w-full text-sm",
                style: { minWidth: 680 },
              },
                React.createElement("thead", null,
                  React.createElement("tr", { style: { borderBottom: `1px solid ${T.border}` } },
                    ["Channel", "Customers", "CAC", "LTV", "LTV:CAC", "Payback", "Churn", "Health"].map(h =>
                      React.createElement("th", {
                        key: h,
                        className: `text-left py-2 px-2 font-semibold ${h !== "Channel" ? "text-right" : ""}`,
                        style: { color: T.textMuted },
                      }, h),
                    ),
                  ),
                ),
                React.createElement("tbody", null,
                  CHANNELS.map((ch, i) => React.createElement("tr", {
                    key: i,
                    style: { borderBottom: `1px solid ${T.border}` },
                  },
                    React.createElement("td", { className: "py-2.5 px-2" },
                      React.createElement("div", { className: "flex items-center gap-2" },
                        React.createElement("div", {
                          className: "w-2 h-2 rounded-full",
                          style: { background: CHANNEL_C[ch.ch] },
                        }),
                        React.createElement("span", { className: "font-medium", style: { color: T.text } }, ch.ch),
                      ),
                    ),
                    React.createElement("td", { className: "text-right py-2 px-2", style: { color: T.textMuted } }, ch.cust.toLocaleString()),
                    React.createElement("td", { className: "text-right py-2 px-2 font-medium", style: { color: T.text } }, `$${ch.cac}`),
                    React.createElement("td", { className: "text-right py-2 px-2 font-medium", style: { color: T.text } }, `$${ch.ltv.toLocaleString()}`),
                    React.createElement("td", {
                      className: "text-right py-2 px-2 font-bold",
                      style: { color: ch.ratio >= 5 ? T.teal : ch.ratio >= 3 ? T.gold : "#C48E6A" },
                    }, `${ch.ratio}x`),
                    React.createElement("td", { className: "text-right py-2 px-2", style: { color: T.textMuted } }, `${ch.payback} mo`),
                    React.createElement("td", { className: "text-right py-2 px-2", style: { color: T.textMuted } }, `${ch.churn}%`),
                    React.createElement("td", { className: "text-right py-2 px-2" },
                      React.createElement("span", {
                        className: "px-2 py-0.5 rounded-full text-[10px] font-semibold",
                        style: {
                          color: ch.health === "Scale" ? T.teal : ch.health === "Healthy" ? T.accent : T.gold,
                          background: ch.health === "Scale" ? `${T.teal}15` : ch.health === "Healthy" ? `${T.accent}15` : `${T.gold}15`,
                        },
                      }, ch.health),
                    ),
                  )),
                ),
              ),
            ),
          ),
        ),

        // Payback curves
        React.createElement(Section, {
          title: "Time to Payback",
          sub: "How quickly each channel earns back its CAC. White dashed line = breakeven (100%).",
        },
          React.createElement(Panel, null,
            React.createElement(PaybackCurves),
          ),
        ),
      ),

      // ── Simulator ──
      activeNav === "simulator" && React.createElement(React.Fragment, null,
        // Business context ABOVE the simulator
        React.createElement("div", {
          className: "rounded-lg p-6 mb-6 border",
          style: { background: T.surface, borderColor: T.border },
        },
          React.createElement("p", { className: "text-lg leading-relaxed mb-3", style: { color: T.text } },
            React.createElement("strong", null, "Why Scenario Modeling Matters"),
            " — Unit economics decisions aren't made in isolation. Improving retention changes LTV, which changes LTV:CAC, which changes how much you can afford to spend on acquisition. ",
            "This simulator models those second-order effects so you can see the ",
            React.createElement("em", { style: { color: T.accent } }, "compounding impact"),
            " of each growth lever.",
          ),
          React.createElement("p", { className: "text-base leading-relaxed mb-3", style: { color: T.textSec } },
            React.createElement("strong", { style: { color: T.text } }, "Real-world context: "),
            "KPI6 (B2B SaaS) achieved 174% LTV improvement and 45% churn reduction in one year through a combination of predictive churn models, ",
            "global expansion, and product-led upsells. The key was stacking multiple small improvements that compounded.",
          ),
          React.createElement("p", { className: "text-base leading-relaxed", style: { color: T.textSec } },
            React.createElement("strong", { style: { color: T.text } }, "How to use: "),
            "Move the sliders to model scenarios. Each lever represents a realistic initiative with known cost. ",
            "Start with the highest-ROI lever (usually retention), then layer on channel optimization and pricing changes to see combined impact.",
          ),
        ),
        React.createElement(Section, {
          title: "Growth Scenario Simulator",
          sub: "Each lever models a real initiative. Combined impact shows incremental ARR, cost, and ROI. These aren't hypothetical — they're based on documented SaaS growth playbooks.",
        },
          React.createElement(Simulator),
        ),

        React.createElement(Section, {
          title: "Pre-Built Playbooks",
          sub: "Three proven strategies with estimated investment, timeline, and expected return based on industry case studies",
        },
          React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" },
            [
              {
                name: "Retention-First",
                desc: "Invest in onboarding health scores + CS automation. Industry data: retention improvements cut effective CAC by 35% and predictive churn models add 10-15% to LTV.",
                ltv: "$3,430", cac: "$376", ratio: "9.1x",
                arr: "+$520K", cost: "$180K", roi: "2.9x",
                color: T.teal,
              },
              {
                name: "Channel Rebalance",
                desc: "Shift 20% of paid to content/organic. Deepnote proved this works: 72% CAC reduction, 2.5x faster payback, 158% more customers.",
                ltv: "$2,744", cac: "$310", ratio: "8.9x",
                arr: "—", cost: "$0", roi: "immediate",
                color: T.accent,
              },
              {
                name: "Annual Push",
                desc: "Month-3 annual discount + annual-first pricing. HubSpot, Zoom use 15-20% discounts because retention ROI far exceeds discount cost.",
                ltv: "$3,840", cac: "$376", ratio: "10.2x",
                arr: "+$780K", cost: "$95K", roi: "8.2x",
                color: T.accent,
              },
            ].map((s, i) => React.createElement(Panel, { key: i },
              React.createElement("div", {
                className: "text-xs font-semibold uppercase tracking-wider mb-2",
                style: { color: s.color },
              }, s.name),
              React.createElement("p", {
                className: "text-xs mb-3",
                style: { color: T.textMuted },
              }, s.desc),
              React.createElement("div", { className: "space-y-2" },
                [
                  { k: "Projected LTV", v: s.ltv },
                  { k: "Projected CAC", v: s.cac },
                  { k: "LTV:CAC", v: s.ratio },
                  { k: "ARR Impact", v: s.arr },
                  { k: "Investment", v: s.cost },
                  { k: "ROI", v: s.roi },
                ].map((row, j) => React.createElement("div", {
                  key: j,
                  className: "flex justify-between text-xs",
                },
                  React.createElement("span", { style: { color: T.textMuted } }, row.k),
                  React.createElement("span", { className: "font-medium", style: { color: T.text } }, row.v),
                )),
              ),
            )),
          ),
        ),
      ),
    ),

    // Footer
    React.createElement("footer", {
      className: "border-t mt-8 py-4",
      style: { borderColor: T.border },
    },
      React.createElement("div", {
        className: "max-w-7xl mx-auto px-4 flex flex-col sm:flex-row justify-between items-center gap-2 text-[11px]",
        style: { color: T.textDim },
      },
        React.createElement("span", null,
          "Core data: ",
          React.createElement("a", {
            href: "https://github.com/IBM/telco-customer-churn-on-icp4d",
            target: "_blank",
            className: "underline",
            style: { color: T.textMuted },
          }, "IBM Telco Customer Churn"),
          " (public). Acquisition & MRR movement data simulated.",
        ),
        React.createElement("span", null,
          "Built by ",
          React.createElement("a", {
            href: "https://linkedin.com/in/freena",
            target: "_blank",
            className: "underline",
            style: { color: T.textMuted },
          }, "Freena Wang"),
        ),
      ),
    ),
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(React.createElement(App));
