// Act V — THE CUSTOMER
// Closes rubric Cat 2 (Customer / Demand / CAC, 15 pts).
// God-mode redesign 2026-04-28: beat-swap pattern. 5 beats, each panel
// occupies the stage in turn — no more 1400px stack that clipped at projector.
//
//   b0  Persona — Emma Heels (the student funnel)
//   b1  Persona — Dr. Harris Grey (the scholar funnel)
//   b2  Acquisition · CAC by channel (ledger)
//   b3  Retention · LTV (chip row)
//   b4  Bridge — from customer to artifact
//
// Numbers scaffolded with industry anchors. Search `// VALIDATE:` for
// placeholders Svetlana must confirm. See VALIDATION_CHECKLIST §2.

// Renamed shared globals (was PERSONAS / CHANNELS / RETENTION) to avoid the
// type='text/babel' shared-scope collision that clobbered Act III in 2026-04-27
// when validation also declared `const PARTNERS = ...`.
const CUSTOMER_PERSONAS = [
  {
    id: 'emma',
    img: 'assets/persona-emma.webp',
    name: 'Emma Heels',
    age: 27,
    role: 'Fashion Design Student',
    location: 'New York, NY',
    tier: 'Pro · $14.99 / mo',
    tint: 'var(--gold)',
    pain: 'Cannot reach the campus archive when she needs it. Studies vintage on IG, YouTube, and Substack instead.',
    value: 'Twenty-four-hour archive access + AI-styled annotation. Homework she can do at midnight.',
    discovery: 'Khanna Shalumova Substack → organic TikTok / IG → student-rate trial → institutional pull.',
  },
  {
    id: 'grey',
    img: 'assets/persona-grey.webp',
    name: 'Dr. Harris Grey',
    age: 53,
    role: 'PhD · Fashion Historian',
    location: 'Faculty appointment',
    tier: 'Institutional + Pro · $24K / yr',
    tint: 'var(--sage)',
    pain: 'Writing his next book on a teaching schedule. Travel-grade research only happens during sabbatical.',
    value: 'Full technical metadata + APA-ready citation export. Book moves from sabbatical-paced to weekly-paced.',
    discovery: 'NASAD conference → BoF Education → university procurement.',
  },
];

const CUSTOMER_CHANNELS = [
  { name: 'Direct outbound · Institutional', cac: '$3,200', mix: '38%', cycle: '6–12 mo' },
  { name: 'Conferences · NASAD · BoF · ASU+GSV', cac: '$2,800', mix: '18%', cycle: '4–9 mo' },
  { name: 'Content · Khanna Shalumova Substack', cac: '$180',   mix: '14%', cycle: '< 30 d' },
  { name: 'LinkedIn paid · faculty + dean', cac: '$450',   mix: '10%', cycle: '2–6 mo' },
  { name: 'Student tier · TikTok + IG', cac: '$28',    mix: '20%', cycle: '< 14 d' },
];

const CUSTOMER_RETENTION = [
  { k: 'Institutional renewal', v: '88%',  unit: '/ yr',  foot: 'Capterra 2024 EdTech' },
  { k: 'Student churn',         v: '6%',   unit: '/ mo',  foot: 'ChartMogul $9.99 D2C' },
  { k: 'Net revenue retention', v: '124%', unit: 'NRR',   foot: 'Pro upsells, institutional pool' },
  { k: 'LTV · institutional',   v: '$78K', unit: '5-yr',  foot: 'Carries forward to Tiers & Econ' },
];

// Helper — visibility window for a beat in a swap. Each panel is "in" while
// its beat is fully revealed AND the next beat hasn't started taking over.
// At the last panel, no fade-out — it holds.
function panelVis(curP, nextP) {
  return Math.max(0, curP * (1 - (nextP || 0)));
}

function CustomerPersonaPanel({ persona, vis, isLast }) {
  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.6s ease, transform 0.6s ease',
      pointerEvents: vis > 0.05 ? 'auto' : 'none',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: '0 80px',
    }}>
      <div style={{
        display: 'grid', gridTemplateColumns: '1fr 1.1fr', gap: 80,
        alignItems: 'center', maxWidth: 1480, width: '100%',
      }}>
        {/* Image side */}
        <div style={{
          aspectRatio: '4 / 5',
          background: 'rgba(0,0,0,0.18)',
          border: `1px solid ${persona.tint}`,
          borderTop: `3px solid ${persona.tint}`,
          overflow: 'hidden',
          position: 'relative',
        }}>
          <img src={persona.img} alt={persona.name}
            style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 18%',
                     filter: 'sepia(0.06) saturate(0.95) contrast(1.04) brightness(0.99)' }} />
          <div style={{
            position: 'absolute', bottom: 0, left: 0, right: 0,
            padding: '14px 18px',
            background: 'linear-gradient(0deg, rgba(0,24,37,0.92), rgba(0,24,37,0))',
          }}>
            <span className="label-text" style={{ color: persona.tint, letterSpacing: '0.32em' }}>
              Persona · {persona.id === 'emma' ? '01 · The Student' : '02 · The Scholar'}
            </span>
          </div>
        </div>
        {/* Text side */}
        <div style={{ paddingRight: 20 }}>
          <h3 className="font-display" style={{
            fontSize: 'clamp(48px, 5vw, 78px)',
            lineHeight: 0.98, letterSpacing: '-0.02em',
            color: 'var(--parchment)',
            margin: 0, textTransform: 'uppercase',
          }}>
            {persona.name}
          </h3>
          <div className="font-editorial" style={{
            marginTop: 10, fontSize: 17, fontStyle: 'italic',
            color: persona.tint, letterSpacing: '0.01em',
          }}>
            {persona.age} · {persona.role} · {persona.location}
          </div>
          <div style={{
            display: 'inline-block', marginTop: 18,
            padding: '6px 14px',
            border: `1px solid ${persona.tint}`,
            color: persona.tint,
          }} className="label-text">
            {persona.tier}
          </div>

          <div style={{ marginTop: 26 }}>
            <div className="label-text" style={{ color: 'var(--sage)', marginBottom: 6 }}>The pain</div>
            <p className="font-editorial" style={{
              fontSize: 17, lineHeight: 1.55,
              color: 'var(--parchment-dim)', margin: 0, textWrap: 'pretty', maxWidth: 540,
            }}>{persona.pain}</p>
          </div>
          <div style={{ marginTop: 18 }}>
            <div className="label-text" style={{ color: 'var(--sage)', marginBottom: 6 }}>What VintaVerse changes</div>
            <p className="font-editorial" style={{
              fontSize: 17, lineHeight: 1.55,
              color: 'var(--parchment)', margin: 0, fontStyle: 'italic',
              textWrap: 'pretty', maxWidth: 540,
            }}>{persona.value}</p>
          </div>
          <div style={{ marginTop: 18, paddingTop: 14, borderTop: '1px dotted rgba(143,166,143,0.3)' }}>
            <span className="label-text" style={{ color: 'var(--gold)', marginRight: 8 }}>Discovery path</span>
            <span className="font-editorial" style={{
              fontSize: 14, color: 'var(--parchment-dim)', fontStyle: 'italic',
            }}>{persona.discovery}</span>
          </div>
        </div>
      </div>
    </div>
  );
}

function CustomerChannelsPanel({ 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.6s ease, transform 0.6s 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)', fontSize: 11, letterSpacing: '0.32em' }}>
            Acquisition · CAC by channel
          </span>
          <div style={{ flex: 1, height: 1, background: 'rgba(143,166,143,0.25)' }} />
          <span className="label-text" style={{ color: 'var(--gold)' }}>
            ◆ blended CAC ≈ $1,840 · 24× LTV:CAC
          </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',
        }}>
          How they <span style={{ color: 'var(--gold)', fontStyle: 'italic' }}>discover us</span>.
        </h3>
        <div style={{
          display: 'grid', gridTemplateColumns: '2.5fr 110px 90px 110px',
          gap: 24, padding: '12px 0',
          borderTop: '1px solid rgba(255,255,255,0.18)',
          borderBottom: '1px solid rgba(255,255,255,0.18)',
        }}>
          <span className="label-text" style={{ color: 'var(--taupe)' }}>Channel</span>
          <span className="label-text" style={{ color: 'var(--taupe)' }}>CAC</span>
          <span className="label-text" style={{ color: 'var(--taupe)' }}>Mix</span>
          <span className="label-text" style={{ color: 'var(--taupe)' }}>Cycle</span>
        </div>
        {CUSTOMER_CHANNELS.map((c, i) => (
          <div key={c.name} style={{
            display: 'grid', gridTemplateColumns: '2.5fr 110px 90px 110px',
            gap: 24, alignItems: 'center',
            padding: '16px 0',
            borderBottom: '1px solid rgba(255,255,255,0.06)',
          }}>
            <div className="font-editorial" style={{
              fontSize: 18, color: 'var(--parchment)', lineHeight: 1.4,
            }}>{c.name}</div>
            <div className="font-display" style={{
              fontSize: 28, color: 'var(--gold)', fontStyle: 'italic', lineHeight: 1,
            }}>{c.cac}</div>
            <div className="label-text" style={{ color: 'var(--sage)', fontSize: 13, letterSpacing: '0.26em' }}>
              {c.mix}
            </div>
            <div className="label-text" style={{ color: 'var(--taupe)', fontSize: 12 }}>
              {c.cycle}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function CustomerRetentionPanel({ 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.6s ease, transform 0.6s ease',
      pointerEvents: vis > 0.05 ? 'auto' : 'none',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: '0 80px',
    }}>
      <div style={{ width: '100%', maxWidth: 1380, textAlign: 'center' }}>
        <span className="label-text" style={{ color: 'var(--sage)', fontSize: 11, letterSpacing: '0.32em' }}>
          Retention · LTV
        </span>
        <h3 className="font-display" style={{
          fontSize: 'clamp(48px, 5vw, 76px)',
          lineHeight: 0.98, letterSpacing: '-0.02em',
          color: 'var(--parchment)', margin: '14px 0 56px', textTransform: 'uppercase',
        }}>
          Both pay. <span style={{ color: 'var(--sage)', fontStyle: 'italic' }}>Both stay.</span>
        </h3>
        <div style={{
          display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)',
          gap: 0, alignItems: 'flex-start',
        }}>
          {CUSTOMER_RETENTION.map((r, i) => (
            <div key={r.k} style={{
              padding: '0 26px',
              borderRight: i < 3 ? '1px solid rgba(255,255,255,0.14)' : 'none',
            }}>
              <div className="label-text" style={{ color: 'var(--taupe)', marginBottom: 18 }}>{r.k}</div>
              <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'center', gap: 8 }}>
                <span className="font-display" style={{
                  fontSize: 'clamp(56px, 5.4vw, 84px)',
                  color: 'var(--sage)', fontStyle: 'italic', lineHeight: 1,
                }}>{r.v}</span>
                <span className="font-editorial" style={{
                  fontSize: 14, color: 'var(--taupe)', fontStyle: 'italic',
                }}>{r.unit}</span>
              </div>
              <div className="font-editorial" style={{
                fontSize: 12, color: 'var(--parchment-dim)',
                marginTop: 16, fontStyle: 'italic', lineHeight: 1.5,
              }}>{r.foot}</div>
            </div>
          ))}
        </div>
        <div style={{ marginTop: 56 }} className="label-text">
          <span style={{ color: 'var(--gold)' }}>◆</span>
          <span style={{ color: 'var(--parchment-dim)', marginLeft: 12, letterSpacing: '0.26em' }}>
            Industry anchors · Capterra 2024 · ChartMogul
          </span>
        </div>
      </div>
    </div>
  );
}

function CustomerBridgePanel({ 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(44px, 5vw, 76px)" italic>
          Two customer shapes. <span style={{ color: 'var(--gold)' }}>One archive.</span>
        </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',
        }}>
          The student funnel and the scholar funnel converge in the same archive.
          The next act puts the object that proves both right in front of you.
        </p>
        <div style={{ marginTop: 40 }}>
          <span className="label-text" style={{ color: 'var(--gold)' }}>Next · the object</span>
          <div className="font-display" style={{
            fontSize: 28, color: 'var(--parchment)',
            marginTop: 6, letterSpacing: '-0.01em', fontStyle: 'italic',
          }}>
            Lenglen · Patou · 1921 →
          </div>
        </div>
      </div>
    </div>
  );
}

function ActCustomerInner({ index, total, act }) {
  // 4 beats — Bridge panel cut 2026-04-29 (was just a "next →" pointer);
  // act now ends on the Retention punctum which carries the LTV closer.
  const p0 = useBeatProgress(0);
  const p1 = useBeatProgress(1);
  const p2 = useBeatProgress(2);
  const p3 = useBeatProgress(3);

  const visEmma     = panelVis(p0, p1);
  const visGrey     = panelVis(p1, p2);
  const visChannels = panelVis(p2, p3);
  const visReten    = p3;  // last panel holds

  return (
    <ActFrame act={act} index={index} total={total} bg="var(--petrol-deep)" bleed>
      <div style={{ position: 'absolute', inset: 0, zIndex: 4 }}>
        <CustomerPersonaPanel persona={CUSTOMER_PERSONAS[0]} vis={visEmma} />
        <CustomerPersonaPanel persona={CUSTOMER_PERSONAS[1]} vis={visGrey} />
        <CustomerChannelsPanel vis={visChannels} />
        <CustomerRetentionPanel vis={visReten} />
      </div>
    </ActFrame>
  );
}

function ActCustomer({ index, total, act }) {
  return (
    <ScrollBeats beats={4} bg="var(--petrol-deep)">
      <ActCustomerInner index={index} total={total} act={act} />
    </ScrollBeats>
  );
}

window.ActCustomer = ActCustomer;
