// Act XV — THE NUMBERS
// God-mode redesign 2026-04-28: beat-swap, 4 panels.
// Old version was 5 beats and ~1257px tall — clipped on projector.
//   b0  Five-year P&L (revenue bars + net income + breakeven punctum at Y3)
//   b1  Customers + Opex detail
//   b2  Key Risks ledger
//   b3  Bridge to Thank You — "Conservative. Defensible. On track."
//
// Closes rubric Cat 6 (Financial Projections, 15 pts) + Cat 7 (Risks, 10 pts).

const NUM_PROJECTIONS = [
  { y: 'Y1', year: '2027', inst: 5,   stud: '600',     rev: '$360K',  opex: '$1.4M', net: -1.16, netStr: '$(1.16M)', barFrac: 0.020 },
  { y: 'Y2', year: '2028', inst: 22,  stud: '4,800',   rev: '$1.4M',  opex: '$1.9M', net: -0.50, netStr: '$(500K)',  barFrac: 0.076 },
  { y: 'Y3', year: '2029', inst: 80,  stud: '23,600',  rev: '$3.93M', opex: '$3.0M', net:  0.93, netStr: '$0.93M',   barFrac: 0.214 },
  { y: 'Y4', year: '2030', inst: 180, stud: '64,000',  rev: '$9.2M',  opex: '$5.0M', net:  4.20, netStr: '$4.2M',    barFrac: 0.500 },
  { y: 'Y5', year: '2031', inst: 320, stud: '128,000', rev: '$18.4M', opex: '$8.0M', net: 10.40, netStr: '$10.4M',   barFrac: 1.000 },
];

const NUM_RISKS = [
  { kind: 'Legal · IP',      risk: 'Museum partnership friction over digitization rights and revenue share.',                              mitigation: '80/20 revenue share favors the museum. Three anchor LOIs signed before seed close lock the supply side.', tint: 'var(--rust)' },
  { kind: 'Market · GTM',    risk: 'Institutional procurement cycles run 12–18 months; seed runway is shorter.',                            mitigation: 'Department-level pilots first (FIT proof point), then campus expansion. Single-decision-maker entry beats committee-led.', tint: 'var(--gold)' },
  { kind: 'Operational',     risk: 'Content licensing costs from museums could compress gross margin below 60% if revenue share scales unfavorably.', mitigation: 'Aligned-incentive structure — museum wins more when we win more. Fixed-floor licensing for archival deals locked in seed period.', tint: 'var(--ochre)' },
  { kind: 'Competitive',     risk: 'Google Arts & Culture or a flagship museum (V&A · MFIT) launches a directly comparable platform.',     mitigation: 'Curriculum-mapped collections + LMS integration creates 12-month switching cost after Y1. Educator dashboards become the moat.', tint: 'var(--sage)' },
  { kind: 'Technical',       risk: '3D model fidelity and viewer performance at scale (10,000+ garments rendered concurrently).',          mitigation: 'CLO3D pipeline production-tested on Augusta + Lubov heirloom dresses. Three.js + meshopt compression demonstrated under MVP load.', tint: 'var(--parchment-dim)' },
];

function NumbersChartPanel({ vis }) {
  const eased = 1 - Math.pow(1 - vis, 3);
  return (
    <div style={{
      position: 'absolute', inset: 0,
      opacity: eased, transform: `translateY(${(1 - eased) * 24}px)`,
      transition: 'opacity 0.7s ease, transform 0.7s ease',
      pointerEvents: vis > 0.05 ? 'auto' : 'none',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: '0 80px',
    }}>
      <div style={{ width: '100%', maxWidth: 1480 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 16, marginBottom: 18 }}>
          <span className="label-text" style={{ color: 'var(--sage)', letterSpacing: '0.32em' }}>Five-year P&amp;L · 2027 — 2031</span>
          <div style={{ flex: 1, height: 1, background: 'rgba(143,166,143,0.22)' }} />
          <span className="label-text" style={{ color: 'var(--accent-key)' }}>
            ◆ breakeven mid-Yr 3 · $2.4M cash to breakeven
          </span>
        </div>
        <h3 className="font-display" style={{
          fontSize: 'clamp(40px, 4.4vw, 64px)',
          lineHeight: 0.98, letterSpacing: '-0.02em',
          color: 'var(--parchment)', margin: '0 0 36px', textTransform: 'uppercase',
        }}>
          What it <span style={{ color: 'var(--gold)', fontStyle: 'italic' }}>looks like</span>.
        </h3>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 16 }}>
          {NUM_PROJECTIONS.map((row, i) => {
            const profitable = row.net > 0;
            return (
              <div key={row.y} style={{
                position: 'relative',
                padding: '24px 20px 20px',
                background: profitable ? 'rgba(201,168,76,0.06)' : 'rgba(185,106,74,0.05)',
                border: `1px solid ${profitable ? 'rgba(201,168,76,0.22)' : 'rgba(255,255,255,0.08)'}`,
                borderTop: `2px solid ${profitable ? 'var(--accent-key)' : 'rgba(255,255,255,0.12)'}`,
              }}>
                {i === 2 && (
                  <div style={{
                    position: 'absolute', left: -22, top: '32%',
                    width: 30, height: 30, borderRadius: '50%',
                    background: 'var(--petrol-deep)',
                    border: '2px solid var(--accent-key)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    color: 'var(--accent-key)', fontSize: 14,
                    fontFamily: 'var(--font-display)', fontStyle: 'italic',
                    boxShadow: '0 0 18px rgba(201,168,76,0.5)',
                    zIndex: 3,
                  }} title="Breakeven mid-Y3">◆</div>
                )}
                <div className="label-text" style={{ color: 'var(--taupe)' }}>{row.y} · {row.year}</div>
                <div className="font-display" style={{
                  fontSize: 44, color: 'var(--parchment)',
                  marginTop: 10, lineHeight: 1, letterSpacing: '-0.02em',
                }}>{row.rev}</div>
                <div className="label-text" style={{ color: 'var(--sage)', marginTop: 4 }}>Revenue</div>
                <div style={{
                  position: 'relative', height: 5, marginTop: 16, marginBottom: 14,
                  background: 'rgba(255,255,255,0.08)',
                }}>
                  <div style={{
                    position: 'absolute', left: 0, top: 0, bottom: 0,
                    width: `${row.barFrac * 100}%`,
                    background: profitable ? 'var(--accent-key)' : 'var(--sage)',
                  }} />
                </div>
                <div style={{
                  padding: '8px 12px',
                  display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 8,
                  background: profitable ? 'rgba(201,168,76,0.14)' : 'rgba(185,106,74,0.14)',
                  border: `1px solid ${profitable ? 'rgba(201,168,76,0.4)' : 'rgba(185,106,74,0.4)'}`,
                }}>
                  <span className="label-text" style={{ color: profitable ? 'var(--accent-key)' : 'var(--rust)' }}>Net</span>
                  <span className="font-display" style={{
                    fontSize: 18, color: profitable ? 'var(--accent-key)' : 'var(--rust)',
                    letterSpacing: '-0.01em',
                  }}>{row.netStr}</span>
                </div>
              </div>
            );
          })}
        </div>
        <div style={{ marginTop: 18, display: 'flex', justifyContent: 'space-between' }}>
          <span className="font-editorial" style={{
            fontSize: 13, color: 'var(--parchment-dim)', fontStyle: 'italic',
          }}>
            Y2 Series A · $3M projected · supports Y3-Y5 · 124% NRR drives compounding
          </span>
          <span className="label-text" style={{ color: 'var(--taupe)' }}>
            ※ scaffolded — VALIDATION_CHECKLIST §6.1
          </span>
        </div>
      </div>
    </div>
  );
}

function NumbersDetailPanel({ vis }) {
  const eased = 1 - Math.pow(1 - vis, 3);
  return (
    <div style={{
      position: 'absolute', inset: 0,
      opacity: eased, transform: `translateY(${(1 - eased) * 24}px)`,
      transition: 'opacity 0.7s ease, transform 0.7s ease',
      pointerEvents: vis > 0.05 ? 'auto' : 'none',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: '0 80px',
    }}>
      <div style={{ width: '100%', maxWidth: 1480 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 16, marginBottom: 18 }}>
          <span className="label-text" style={{ color: 'var(--sage)', letterSpacing: '0.32em' }}>Customer ramp · cost discipline</span>
          <div style={{ flex: 1, height: 1, background: 'rgba(143,166,143,0.22)' }} />
        </div>
        <h3 className="font-display" style={{
          fontSize: 'clamp(40px, 4.4vw, 64px)',
          lineHeight: 0.98, letterSpacing: '-0.02em',
          color: 'var(--parchment)', margin: '0 0 36px', textTransform: 'uppercase',
        }}>
          The <span style={{ color: 'var(--gold)', fontStyle: 'italic' }}>compounding</span> beneath the chart.
        </h3>
        <div style={{
          display: 'grid', gridTemplateColumns: '120px repeat(5, 1fr)',
          gap: 0,
          border: '1px solid rgba(143,166,143,0.18)',
          background: 'rgba(7,50,66,0.45)',
        }}>
          {/* header row */}
          <div style={{ padding: '14px 16px', borderBottom: '1px solid rgba(143,166,143,0.2)' }}>
            <span className="label-text" style={{ color: 'var(--taupe)' }}>Metric</span>
          </div>
          {NUM_PROJECTIONS.map((row, i) => (
            <div key={row.y} style={{
              padding: '14px 16px',
              borderBottom: '1px solid rgba(143,166,143,0.2)',
              borderLeft: '1px solid rgba(143,166,143,0.12)',
              background: i === 4 ? 'rgba(201,168,76,0.06)' : 'transparent',
            }}>
              <span className="label-text" style={{ color: i === 4 ? 'var(--accent-key)' : 'var(--sage)' }}>
                {row.y} · {row.year}
              </span>
            </div>
          ))}
          {/* Institutional row */}
          <div style={{ padding: '16px', borderBottom: '1px dotted rgba(143,166,143,0.18)' }}>
            <span className="font-editorial" style={{ fontSize: 14, color: 'var(--parchment)' }}>Institutional</span>
          </div>
          {NUM_PROJECTIONS.map((row, i) => (
            <div key={`inst-${row.y}`} style={{
              padding: '16px', borderLeft: '1px solid rgba(143,166,143,0.12)',
              borderBottom: '1px dotted rgba(143,166,143,0.18)',
              background: i === 4 ? 'rgba(201,168,76,0.04)' : 'transparent',
            }}>
              <span className="font-display" style={{
                fontSize: 22, color: 'var(--parchment)', letterSpacing: '-0.005em',
              }}>{row.inst}</span>
            </div>
          ))}
          {/* Students row */}
          <div style={{ padding: '16px', borderBottom: '1px dotted rgba(143,166,143,0.18)' }}>
            <span className="font-editorial" style={{ fontSize: 14, color: 'var(--parchment)' }}>Students</span>
          </div>
          {NUM_PROJECTIONS.map((row, i) => (
            <div key={`stud-${row.y}`} style={{
              padding: '16px', borderLeft: '1px solid rgba(143,166,143,0.12)',
              borderBottom: '1px dotted rgba(143,166,143,0.18)',
              background: i === 4 ? 'rgba(201,168,76,0.04)' : 'transparent',
            }}>
              <span className="font-display" style={{
                fontSize: 22, color: 'var(--parchment)', letterSpacing: '-0.005em',
              }}>{row.stud}</span>
            </div>
          ))}
          {/* Opex row */}
          <div style={{ padding: '16px' }}>
            <span className="font-editorial" style={{ fontSize: 14, color: 'var(--parchment)' }}>Opex</span>
          </div>
          {NUM_PROJECTIONS.map((row, i) => (
            <div key={`opex-${row.y}`} style={{
              padding: '16px', borderLeft: '1px solid rgba(143,166,143,0.12)',
              background: i === 4 ? 'rgba(201,168,76,0.04)' : 'transparent',
            }}>
              <span className="font-display" style={{
                fontSize: 22, color: 'var(--parchment-dim)', letterSpacing: '-0.005em',
                fontStyle: 'italic',
              }}>{row.opex}</span>
            </div>
          ))}
        </div>
        <div style={{ marginTop: 22, display: 'flex', alignItems: 'baseline', gap: 24 }}>
          <span className="label-text" style={{ color: 'var(--gold)' }}>◆ The wedge</span>
          <span className="font-editorial" style={{
            fontSize: 15, color: 'var(--parchment)', fontStyle: 'italic', lineHeight: 1.5,
          }}>
            Revenue grows 51× from Y1 to Y5. Opex grows 5.7×. That's the compounding.
          </span>
        </div>
      </div>
    </div>
  );
}

function NumbersRisksPanel({ vis }) {
  const eased = 1 - Math.pow(1 - vis, 3);
  return (
    <div style={{
      position: 'absolute', inset: 0,
      opacity: eased, transform: `translateY(${(1 - eased) * 24}px)`,
      transition: 'opacity 0.7s ease, transform 0.7s ease',
      pointerEvents: vis > 0.05 ? 'auto' : 'none',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: '0 80px',
    }}>
      <div style={{ width: '100%', maxWidth: 1480 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 16, marginBottom: 18 }}>
          <span className="label-text" style={{ color: 'var(--rust)', letterSpacing: '0.32em' }}>Key risks · mitigations</span>
          <div style={{ flex: 1, height: 1, background: 'rgba(185,106,74,0.3)' }} />
          <span className="label-text" style={{ color: 'var(--taupe)' }}>Five named · five mitigated</span>
        </div>
        <h3 className="font-display" style={{
          fontSize: 'clamp(40px, 4.4vw, 64px)',
          lineHeight: 0.98, letterSpacing: '-0.02em',
          color: 'var(--parchment)', margin: '0 0 28px', textTransform: 'uppercase',
        }}>
          What could <span style={{ color: 'var(--rust)', fontStyle: 'italic' }}>break it</span>.
        </h3>
        <div style={{
          borderTop: '1px solid rgba(255,255,255,0.18)',
          borderBottom: '1px solid rgba(255,255,255,0.18)',
        }}>
          <div style={{
            display: 'grid', gridTemplateColumns: '160px 1.4fr 1.6fr',
            gap: 24, padding: '12px 0',
            borderBottom: '1px solid rgba(255,255,255,0.16)',
          }}>
            <span className="label-text" style={{ color: 'var(--taupe)' }}>Dimension</span>
            <span className="label-text" style={{ color: 'var(--taupe)' }}>The risk</span>
            <span className="label-text" style={{ color: 'var(--taupe)' }}>How we cut it</span>
          </div>
          {NUM_RISKS.map((r) => (
            <div key={r.kind} style={{
              display: 'grid', gridTemplateColumns: '160px 1.4fr 1.6fr',
              gap: 24, alignItems: 'flex-start',
              padding: '14px 0',
              borderBottom: '1px solid rgba(255,255,255,0.06)',
            }}>
              <div className="label-text" style={{ color: r.tint, letterSpacing: '0.22em' }}>{r.kind}</div>
              <div className="font-editorial" style={{
                fontSize: 14, color: 'var(--parchment)', lineHeight: 1.5, fontStyle: 'italic',
              }}>{r.risk}</div>
              <div className="font-editorial" style={{
                fontSize: 13, color: 'var(--parchment-dim)', lineHeight: 1.55,
              }}>{r.mitigation}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function NumbersBridgePanel({ vis }) {
  const eased = 1 - Math.pow(1 - vis, 3);
  return (
    <div style={{
      position: 'absolute', inset: 0,
      opacity: eased, transform: `translateY(${(1 - eased) * 24}px)`,
      transition: 'opacity 0.7s ease, transform 0.7s ease',
      pointerEvents: vis > 0.05 ? 'auto' : 'none',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: '0 80px',
    }}>
      <div style={{ width: '100%', maxWidth: 1100, textAlign: 'center' }}>
        <span className="label-text" style={{
          color: 'var(--sage)', fontSize: 11, letterSpacing: '0.36em',
        }}>The bridge</span>
        <PullQuote align="center" size="clamp(48px, 5.4vw, 84px)" italic>
          Conservative. <span style={{ color: 'var(--accent-key)' }}>Defensible.</span> On track.
        </PullQuote>
        <p className="font-editorial" style={{
          marginTop: 28, fontSize: 18, lineHeight: 1.55,
          color: 'var(--parchment-dim)', maxWidth: 760, margin: '28px auto 0',
          fontStyle: 'italic', textWrap: 'pretty',
        }}>
          Numbers above. Risks named, risks mitigated. The story is supposed to land here, not in the next slide.
        </p>
        <div style={{ marginTop: 40 }}>
          <span className="label-text" style={{ color: 'var(--accent-key)' }}>Next · the curtain</span>
          <div className="font-display" style={{
            fontSize: 28, color: 'var(--parchment)',
            marginTop: 6, letterSpacing: '-0.01em', fontStyle: 'italic',
          }}>
            Thank you →
          </div>
        </div>
      </div>
    </div>
  );
}

function ActNumbersInner({ index, total, act }) {
  const p0 = useBeatProgress(0);
  const p1 = useBeatProgress(1);
  const p2 = useBeatProgress(2);

  // Bridge panel cut 2026-04-29 — Risks panel ends with "Conservative.
  // Defensible. On track." takeaway, no separate bridge needed.
  const visChart  = panelVis(p0, p1);
  const visDetail = panelVis(p1, p2);
  const visRisks  = p2;  // last panel holds

  return (
    <ActFrame act={act} index={index} total={total} bg="var(--petrol-deep)" bleed>
      <div style={{ position: 'absolute', inset: 0, zIndex: 4 }}>
        <NumbersChartPanel vis={visChart} />
        <NumbersDetailPanel vis={visDetail} />
        <NumbersRisksPanel vis={visRisks} />
      </div>
    </ActFrame>
  );
}

function ActNumbers({ index, total, act }) {
  return (
    <ScrollBeats beats={3} bg="var(--petrol-deep)">
      <ActNumbersInner index={index} total={total} act={act} />
    </ScrollBeats>
  );
}

window.ActNumbers = ActNumbers;
