// ============================================================
// Kost Hero Motion — 12s loop
// One tap fans out into 6 modules, then resolves into a dashboard.
// ============================================================

const { Stage, Sprite, useTime, interpolate, Easing } = window;

// ---------- helpers ----------
const lerp = (a, b, t) => a + (b - a) * t;
const ease = Easing.easeInOutCubic;
const easeOut = Easing.easeOutCubic;
const iv = (t, input, output, easing = ease) => interpolate(input, output, easing)(t);
const clamp01 = (x) => Math.max(0, Math.min(1, x));

// ---------- tokens ----------
const CYAN = '#28B4DC';
const CYAN_BRIGHT = '#5FD1F4';
const CYAN_INK = '#0E7A9C';
const SUCCESS = '#2AA981';
const FONT_DISPLAY = '"SF Pro Display","Inter","Helvetica Neue",sans-serif';
const FONT_TEXT = '"SF Pro Text","Inter","Helvetica Neue",sans-serif';
const FONT_MONO = '"SF Mono","JetBrains Mono",ui-monospace,Menlo,monospace';

// ---------- timeline ----------
const DURATION = 12;
const T = {
  introIn:        [0.0, 0.8],
  // POS phase
  posIn:          [0.6, 1.4],
  tapRipple:      [1.4, 2.6],
  amountCount:    [1.6, 2.4],
  approved:       [2.4, 3.0],
  // Fan-out phase
  raysGrow:       [3.0, 4.6],
  // Modules light up sequentially (each 0.45s wide, staggered 0.32s)
  modules: [
    [4.4, 4.85],
    [4.72, 5.17],
    [5.04, 5.49],
    [5.36, 5.81],
    [5.68, 6.13],
    [6.0,  6.45],
  ],
  // Convergence — rays pull data back to center, dashboard materializes
  converge:       [7.0, 8.4],
  dashIn:         [7.8, 8.8],
  dashFill:       [8.6, 10.4],
  eligiblePop:    [10.0, 10.7],
  tagline:        [9.6, 10.6],
  outro:          [11.4, 12.0],
};

// 6 modules — clockwise from top.
// Positions are around the center (960, 540) at radius 380.
const CENTER = { x: 960, y: 540 };
const RADIUS = 380;
const MODULES = [
  { id: 'pay',     label: 'Payment Processing',  sub: 'Tap · Insert · Swipe',     chip: 'AUTH · 200ms',           angle: -90 },  // top
  { id: 'rtr',     label: 'Real-Time Settlement',sub: 'RTR · Interac · EFT',     chip: 'Settled in 12s',          angle: -30 },  // top-right
  { id: 'openbk',  label: 'Open Banking',         sub: 'OFX · CDR feeds',         chip: '6 institutions linked',   angle: 30 },   // bottom-right
  { id: 'cash',    label: 'Cashflow Dashboard',   sub: 'Live ledger · forecast',  chip: '+$47.20 today',           angle: 90 },   // bottom
  { id: 'fee',     label: 'Fee Transparency',     sub: 'Per-txn breakdown',       chip: '$0.34 · 0.72%',           angle: 150 },  // bottom-left
  { id: 'wc',      label: 'Working Capital',      sub: 'Underwritten on cashflow',chip: 'Eligible $25,000',        angle: 210 },  // top-left
];

const polar = (cx, cy, r, deg) => {
  const rad = (deg * Math.PI) / 180;
  return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
};

// ============================================================
// Background — hex grid + ambient glow
// ============================================================
function Background({ t }) {
  const glowPulse = 0.05 + 0.05 * Math.sin(t * 0.7);
  return (
    <>
      {/* radial glow that follows the action */}
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(ellipse 60% 55% at 50% 50%, rgba(40,180,220,${glowPulse + 0.06}) 0%, transparent 70%)`,
        pointerEvents: 'none',
      }} />
      {/* dot grid */}
      <svg style={{ position: 'absolute', inset: 0, opacity: 0.18 }} width="1920" height="1080">
        <defs>
          <pattern id="dots" width="40" height="40" patternUnits="userSpaceOnUse">
            <circle cx="1" cy="1" r="0.8" fill="rgba(255,255,255,0.25)"/>
          </pattern>
          <radialGradient id="fade" cx="50%" cy="50%" r="60%">
            <stop offset="0%" stopColor="black" stopOpacity="0"/>
            <stop offset="60%" stopColor="black" stopOpacity="0.6"/>
            <stop offset="100%" stopColor="black" stopOpacity="1"/>
          </radialGradient>
          <mask id="fadeMask">
            <rect width="1920" height="1080" fill="white"/>
            <rect width="1920" height="1080" fill="url(#fade)"/>
          </mask>
        </defs>
        <rect width="1920" height="1080" fill="url(#dots)" mask="url(#fadeMask)"/>
      </svg>
      {/* faint concentric circles */}
      <svg style={{ position: 'absolute', inset: 0, opacity: 0.12 }} width="1920" height="1080">
        {[160, 260, 380, 500].map(r => (
          <circle key={r} cx={CENTER.x} cy={CENTER.y} r={r} fill="none" stroke="rgba(40,180,220,0.6)" strokeWidth="1" strokeDasharray="2 6"/>
        ))}
      </svg>
    </>
  );
}

// ============================================================
// POS Terminal at center (during phases 1–6)
// ============================================================
function CenterPOS({ t, opacity, scale }) {
  const amountP = clamp01(iv(t, T.amountCount, [0, 1], easeOut));
  const amount = lerp(0, 47.20, amountP);
  const showApproved = t >= T.approved[0];

  // Tap ripple
  const inRipple = t > T.tapRipple[0] && t < T.tapRipple[1];

  return (
    <div style={{
      position: 'absolute', left: CENTER.x, top: CENTER.y,
      transform: `translate(-50%, -50%) scale(${scale})`,
      opacity,
      transformOrigin: 'center',
    }}>
      {/* Terminal body */}
      <div style={{
        width: 280, height: 400,
        background: 'linear-gradient(160deg, #1f1f22 0%, #0a0a0c 100%)',
        borderRadius: 36, padding: 16,
        boxShadow: '0 80px 140px -30px rgba(40,180,220,0.45), 0 30px 60px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.08)',
        position: 'relative',
      }}>
        {/* Screen */}
        <div style={{
          background: '#000', borderRadius: 24, height: 290, padding: 28,
          display: 'flex', flexDirection: 'column',
          boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.06)',
        }}>
          <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)', letterSpacing: 0.7, textTransform: 'uppercase' }}>Total</div>
          <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.7)', marginTop: 2 }}>Lumiere Coffee · #2847</div>
          <div style={{
            fontFamily: FONT_DISPLAY, fontSize: 56, fontWeight: 600,
            color: '#fff', letterSpacing: -1.2, marginTop: 14,
            fontVariantNumeric: 'tabular-nums', lineHeight: 1,
          }}>
            ${amount.toFixed(2)}
          </div>
          <div style={{ marginTop: 'auto', fontSize: 13, fontWeight: 500, color: showApproved ? CYAN_BRIGHT : 'rgba(255,255,255,0.85)' }}>
            {showApproved ? '✓ Approved' : 'Tap to pay'}
          </div>
        </div>
        {/* Reader area */}
        <div style={{
          position: 'absolute', bottom: 16, left: 16, right: 16, height: 78,
          background: 'linear-gradient(180deg, #2c2c30, #1a1a1c)',
          borderRadius: 18, display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <div style={{
            width: 42, height: 42, border: `2px solid ${CYAN}`, borderRadius: '50%',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <div style={{ width: 18, height: 18, border: `2px solid ${CYAN}`, borderRadius: '50%' }}/>
          </div>
        </div>
      </div>

      {/* NFC ripples */}
      {inRipple && [0, 0.5, 1].map((delay, i) => {
        const localT = t - T.tapRipple[0] - delay * 0.4;
        if (localT < 0) return null;
        const cycle = (localT % 1.2) / 1.2;
        return (
          <div key={i} style={{
            position: 'absolute', left: '50%', top: 320,
            width: 70 + cycle * 100, height: 70 + cycle * 100,
            borderRadius: '50%',
            border: `2px solid ${CYAN_BRIGHT}`,
            opacity: (1 - cycle) * 0.7,
            transform: 'translate(-50%, -50%)',
            pointerEvents: 'none',
          }}/>
        );
      })}
    </div>
  );
}

// ============================================================
// A single module card around the perimeter
// ============================================================
function ModuleNode({ mod, idx, t }) {
  const pos = polar(CENTER.x, CENTER.y, RADIUS, mod.angle);
  const window_ = T.modules[idx];
  // Activation: 0 before window, 1 once activated, stays 1
  const active = t >= window_[0];
  const litP = clamp01(iv(t, window_, [0, 1], easeOut));
  // Pre-state: faint placeholder visible after raysGrow begins
  const preP = clamp01(iv(t, [T.raysGrow[0] + 0.4, T.raysGrow[0] + 1.4], [0, 1], ease));

  const baseOpacity = lerp(0, 0.45, preP);
  const opacity = lerp(baseOpacity, 1, litP);
  // Outro: fade modules slightly during convergence to draw attention to center
  const fadeOut = clamp01(iv(t, [T.converge[0] + 0.4, T.converge[1]], [0, 0.55], ease));
  const finalOpacity = opacity * (1 - fadeOut);
  // Final outro
  const outroFade = clamp01(iv(t, T.outro, [1, 0], ease));

  const lit = litP > 0.1;

  return (
    <div style={{
      position: 'absolute',
      left: pos.x, top: pos.y,
      transform: 'translate(-50%, -50%)',
      opacity: finalOpacity * outroFade,
      pointerEvents: 'none',
    }}>
      {/* Glow halo when lit */}
      {lit && (
        <div style={{
          position: 'absolute', inset: -30, borderRadius: 24,
          background: `radial-gradient(circle, rgba(40,180,220,${0.4 * litP}) 0%, transparent 70%)`,
          filter: 'blur(16px)',
        }}/>
      )}
      <div style={{
        position: 'relative',
        width: 280,
        background: lit ? 'rgba(20,30,38,0.92)' : 'rgba(15,20,25,0.6)',
        border: `1px solid ${lit ? CYAN : 'rgba(255,255,255,0.12)'}`,
        borderRadius: 14,
        padding: '14px 16px',
        backdropFilter: 'blur(10px)',
        transform: `scale(${lerp(0.92, 1, litP)})`,
        boxShadow: lit ? `0 0 0 1px ${CYAN}33, 0 16px 40px rgba(40,180,220,0.18)` : 'none',
        transition: 'none',
      }}>
        {/* Top row: index + label */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
          <div style={{
            width: 22, height: 22, borderRadius: '50%',
            background: lit ? CYAN : 'rgba(255,255,255,0.08)',
            color: lit ? '#000' : 'rgba(255,255,255,0.5)',
            fontSize: 11, fontWeight: 700, fontFamily: FONT_MONO,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>{idx + 1}</div>
          <div style={{
            fontFamily: FONT_DISPLAY, fontSize: 15, fontWeight: 600,
            color: lit ? '#fff' : 'rgba(255,255,255,0.8)', letterSpacing: 0.1,
          }}>{mod.label}</div>
        </div>
        <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.55)', marginBottom: 10 }}>
          {mod.sub}
        </div>
        {/* Chip */}
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '5px 10px', borderRadius: 6,
          background: lit ? `${CYAN}22` : 'rgba(255,255,255,0.04)',
          border: `1px solid ${lit ? CYAN + '66' : 'rgba(255,255,255,0.08)'}`,
          fontFamily: FONT_MONO, fontSize: 11, fontWeight: 500,
          color: lit ? CYAN_BRIGHT : 'rgba(255,255,255,0.5)',
          letterSpacing: 0.2,
        }}>
          <span style={{
            width: 6, height: 6, borderRadius: '50%',
            background: lit ? CYAN_BRIGHT : 'rgba(255,255,255,0.2)',
            boxShadow: lit ? `0 0 8px ${CYAN_BRIGHT}` : 'none',
          }}/>
          {mod.chip}
        </div>
      </div>
    </div>
  );
}

// ============================================================
// Rays from center to each module + traveling packets
// ============================================================
function Rays({ t }) {
  const growP = clamp01(iv(t, T.raysGrow, [0, 1], ease));
  const fadeOut = clamp01(iv(t, [T.converge[0] + 0.4, T.converge[1]], [0, 0.7], ease));
  const outroFade = clamp01(iv(t, T.outro, [1, 0], ease));

  return (
    <svg style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} width="1920" height="1080">
      <defs>
        <radialGradient id="rayGrad" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor={CYAN_BRIGHT} stopOpacity="0.0"/>
          <stop offset="20%" stopColor={CYAN_BRIGHT} stopOpacity="0.9"/>
          <stop offset="100%" stopColor={CYAN} stopOpacity="0.2"/>
        </radialGradient>
      </defs>

      {MODULES.map((mod, idx) => {
        const end = polar(CENTER.x, CENTER.y, RADIUS - 90, mod.angle);
        // Distance from center to end
        const dx = end.x - CENTER.x;
        const dy = end.y - CENTER.y;
        // Ray grows out: shrink end toward center based on growP
        const cur = {
          x: CENTER.x + dx * growP,
          y: CENTER.y + dy * growP,
        };

        // Module activation packet — fires once, traveling outward
        const win = T.modules[idx];
        const packetT = clamp01(iv(t, [win[0] - 0.4, win[0] + 0.1], [0, 1], easeOut));
        const packetVisible = t > win[0] - 0.4 && t < win[0] + 0.2;
        const px = CENTER.x + dx * packetT;
        const py = CENTER.y + dy * packetT;

        // Convergence packet — pulls back to center
        const convT = clamp01(iv(t, [T.converge[0] + idx * 0.06, T.converge[0] + 0.5 + idx * 0.06], [0, 1], ease));
        const convVisible = t > T.converge[0] + idx * 0.06 && t < T.converge[0] + 0.7 + idx * 0.06;
        const cpx = end.x - dx * convT;
        const cpy = end.y - dy * convT;

        const moduleLit = t >= win[0];
        const lineOpacity = lerp(0.3, moduleLit ? 0.85 : 0.45, growP) * (1 - fadeOut) * outroFade;

        return (
          <g key={mod.id}>
            {/* Faint ray (always while growing) */}
            <line
              x1={CENTER.x} y1={CENTER.y}
              x2={cur.x} y2={cur.y}
              stroke={CYAN}
              strokeWidth={moduleLit ? 1.6 : 1}
              opacity={lineOpacity}
              strokeLinecap="round"
            />
            {/* Outward packet on activation */}
            {packetVisible && (
              <>
                <circle cx={px} cy={py} r="4" fill={CYAN_BRIGHT}/>
                <circle cx={px} cy={py} r="10" fill={CYAN_BRIGHT} opacity="0.3"/>
              </>
            )}
            {/* Convergence packet */}
            {convVisible && (
              <>
                <circle cx={cpx} cy={cpy} r="3.5" fill={CYAN_BRIGHT} opacity={1 - convT * 0.3}/>
                <circle cx={cpx} cy={cpy} r="9" fill={CYAN_BRIGHT} opacity={(1 - convT) * 0.4}/>
              </>
            )}
          </g>
        );
      })}

      {/* Center node halo */}
      <circle cx={CENTER.x} cy={CENTER.y} r={lerp(40, 60, growP)} fill={CYAN} opacity={0.05 * outroFade}/>
    </svg>
  );
}

// ============================================================
// Final dashboard — materializes after convergence
// ============================================================
function Dashboard({ t }) {
  const opacity = clamp01(iv(t, T.dashIn, [0, 1], ease));
  const scale = lerp(0.92, 1, clamp01(iv(t, T.dashIn, [0, 1], easeOut)));
  if (opacity <= 0.01) return null;

  // Animated values
  const fillP = clamp01(iv(t, T.dashFill, [0, 1], easeOut));
  const sales = lerp(0, 4827.40, fillP);
  const balance = lerp(0, 482902.45, fillP);
  // Chart progress
  const chartP = clamp01(iv(t, [T.dashFill[0] + 0.3, T.dashFill[1] + 0.2], [0, 1], ease));

  // Eligible pop
  const eligibleP = clamp01(iv(t, T.eligiblePop, [0, 1], easeOut));
  const eligibleScale = lerp(0.7, 1, eligibleP);

  const outroFade = clamp01(iv(t, T.outro, [1, 0], ease));

  return (
    <div style={{
      position: 'absolute', left: CENTER.x, top: CENTER.y,
      transform: `translate(-50%, -50%) scale(${scale})`,
      opacity: opacity * outroFade,
      width: 620,
    }}>
      <div style={{
        background: '#0c0d10',
        border: `1px solid ${CYAN}40`,
        borderRadius: 18,
        boxShadow: `0 80px 160px -30px rgba(40,180,220,0.45), 0 0 0 1px ${CYAN}22`,
        overflow: 'hidden',
      }}>
        {/* Window chrome */}
        <div style={{
          height: 36, background: '#15171b',
          display: 'flex', alignItems: 'center', padding: '0 16px', gap: 6,
          borderBottom: '1px solid rgba(255,255,255,0.06)',
        }}>
          <span style={{ width: 9, height: 9, borderRadius: '50%', background: 'rgba(255,255,255,0.16)' }}/>
          <span style={{ width: 9, height: 9, borderRadius: '50%', background: 'rgba(255,255,255,0.16)' }}/>
          <span style={{ width: 9, height: 9, borderRadius: '50%', background: 'rgba(255,255,255,0.16)' }}/>
          <span style={{ marginLeft: 16, fontSize: 11, color: 'rgba(255,255,255,0.4)', fontFamily: FONT_MONO }}>
            app.kost.ca · Today
          </span>
          <span style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, color: SUCCESS, fontFamily: FONT_MONO }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: SUCCESS, boxShadow: `0 0 8px ${SUCCESS}` }}/>
            Live
          </span>
        </div>

        <div style={{ padding: 24 }}>
          {/* Top metrics row */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, marginBottom: 18 }}>
            <Metric label="Today's sales" value={`$${sales.toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`} sub="14 transactions" trend="+12% vs. yesterday"/>
            <Metric label="Available balance" value={`$${balance.toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`} sub="Operating · CAD" trend="Settled in real time"/>
          </div>

          {/* Chart */}
          <div style={{ marginBottom: 16 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
              <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.55)', letterSpacing: 0.4, textTransform: 'uppercase' }}>Projected cashflow · 14 days</div>
              <div style={{ fontSize: 11, color: CYAN_BRIGHT, fontFamily: FONT_MONO }}>+$8,340 forecast</div>
            </div>
            <svg width="100%" height="90" viewBox="0 0 580 90" preserveAspectRatio="none">
              <defs>
                <linearGradient id="dashGrad" x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0%" stopColor={CYAN} stopOpacity="0.45"/>
                  <stop offset="100%" stopColor={CYAN} stopOpacity="0"/>
                </linearGradient>
                <clipPath id="dashClip"><rect x="0" y="0" width={580 * chartP} height="90"/></clipPath>
              </defs>
              {/* baseline */}
              <line x1="0" y1="60" x2="580" y2="60" stroke="rgba(255,255,255,0.06)" strokeWidth="1" strokeDasharray="3 4"/>
              <g clipPath="url(#dashClip)">
                <path d="M0 70 Q40 64 80 60 T160 50 T240 54 T320 38 T400 42 T480 28 T580 18 L580 90 L0 90 Z" fill="url(#dashGrad)"/>
                <path d="M0 70 Q40 64 80 60 T160 50 T240 54 T320 38 T400 42 T480 28 T580 18" fill="none" stroke={CYAN_BRIGHT} strokeWidth="2"/>
              </g>
            </svg>
          </div>

          {/* Settlement row */}
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            padding: '12px 14px', background: 'rgba(42,169,129,0.08)',
            border: '1px solid rgba(42,169,129,0.3)', borderRadius: 10, marginBottom: 14,
          }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <span style={{ width: 8, height: 8, borderRadius: '50%', background: SUCCESS, boxShadow: `0 0 8px ${SUCCESS}` }}/>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600 }}>RTR settlement received</div>
                <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.55)', fontFamily: FONT_MONO }}>+$47.20 · ID #TX-2847 · 12s ago</div>
              </div>
            </div>
            <div style={{ fontSize: 11, color: SUCCESS, fontFamily: FONT_MONO }}>Settled</div>
          </div>

          {/* Eligible pill */}
          <div style={{
            display: 'flex', alignItems: 'center', gap: 12,
            padding: '14px 16px',
            background: `linear-gradient(90deg, ${CYAN}26, transparent)`,
            border: `1px solid ${CYAN}66`,
            borderRadius: 12,
            transform: `scale(${eligibleScale})`,
            opacity: eligibleP,
            transformOrigin: 'left center',
          }}>
            <div style={{
              width: 36, height: 36, borderRadius: 10,
              background: CYAN, color: '#000',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontFamily: FONT_DISPLAY, fontSize: 18, fontWeight: 700,
            }}>$</div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 600 }}>Eligible for Working Capital</div>
              <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.6)' }}>Up to $25,000 · underwritten on your live cashflow</div>
            </div>
            <div style={{
              padding: '6px 12px', background: CYAN, color: '#000',
              borderRadius: 999, fontSize: 12, fontWeight: 600,
            }}>Review</div>
          </div>
        </div>
      </div>
    </div>
  );
}

function Metric({ label, value, sub, trend }) {
  return (
    <div style={{
      padding: 14,
      background: 'rgba(255,255,255,0.025)',
      border: '1px solid rgba(255,255,255,0.06)',
      borderRadius: 10,
    }}>
      <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.55)', letterSpacing: 0.4, textTransform: 'uppercase' }}>{label}</div>
      <div style={{
        fontFamily: FONT_DISPLAY, fontSize: 28, fontWeight: 600,
        letterSpacing: -0.5, fontVariantNumeric: 'tabular-nums', marginTop: 4,
      }}>{value}</div>
      <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.6)', marginTop: 2 }}>{sub}</div>
      <div style={{ fontSize: 11, color: SUCCESS, marginTop: 2, fontFamily: FONT_MONO }}>{trend}</div>
    </div>
  );
}

// ============================================================
// Title card / tagline
// ============================================================
function Title({ t }) {
  // Title only visible during intro phase
  const opacity = clamp01(iv(t, [0.0, 0.6, T.posIn[1] + 0.2, T.posIn[1] + 0.8], [0, 1, 1, 0], ease));
  const y = lerp(12, 0, clamp01(iv(t, [0, 0.6], [0, 1], ease)));
  if (opacity <= 0.01) return null;
  return (
    <div style={{
      position: 'absolute', top: 100, left: 0, right: 0, textAlign: 'center',
      opacity, transform: `translateY(${y}px)`,
    }}>
      <div style={{
        fontSize: 12, letterSpacing: 1.6, textTransform: 'uppercase',
        color: CYAN_BRIGHT, fontWeight: 600, marginBottom: 14,
      }}>The financial operating system</div>
      <div style={{
        fontFamily: FONT_DISPLAY, fontSize: 64, fontWeight: 600,
        letterSpacing: -1, lineHeight: 1.05,
      }}>
        One transaction. <span style={{ color: CYAN_BRIGHT }}>Six layers of value.</span>
      </div>
    </div>
  );
}

function Tagline({ t }) {
  const opacity = clamp01(iv(t, T.tagline, [0, 1], ease)) * clamp01(iv(t, T.outro, [1, 0], ease));
  const y = lerp(8, 0, clamp01(iv(t, T.tagline, [0, 1], ease)));
  if (opacity <= 0.01) return null;
  return (
    <div style={{
      position: 'absolute', bottom: 80, left: 0, right: 0, textAlign: 'center',
      opacity, transform: `translateY(${y}px)`,
    }}>
      <div style={{
        fontFamily: FONT_DISPLAY, fontSize: 38, fontWeight: 600, letterSpacing: -0.4,
      }}>Not a terminal. <span style={{ color: CYAN_BRIGHT }}>An operating system.</span></div>
      <div style={{ marginTop: 12, fontSize: 16, color: 'rgba(255,255,255,0.55)', fontFamily: FONT_TEXT }}>
        kost.ca — built for Canadian SMEs
      </div>
    </div>
  );
}

// ============================================================
// Phase indicator (subtle, bottom)
// ============================================================
function PhaseLabel({ t }) {
  const labels = [
    { range: [T.posIn[0], T.tapRipple[1]], text: 'Customer taps to pay' },
    { range: [T.raysGrow[0], T.modules[0][0]], text: 'One transaction enters the platform' },
    { range: [T.modules[0][0], T.modules[5][1]], text: 'Six modules activate in sequence' },
    { range: [T.converge[0], T.dashFill[1]], text: 'All data resolves into one dashboard' },
  ];
  const cur = labels.find(l => t >= l.range[0] && t < l.range[1]);
  if (!cur) return null;
  const localT = t - cur.range[0];
  const dur = cur.range[1] - cur.range[0];
  const opacity = Math.min(1, localT / 0.4) * Math.min(1, (dur - localT) / 0.4);
  return (
    <div style={{
      position: 'absolute', bottom: 30, left: 0, right: 0, textAlign: 'center',
      fontFamily: FONT_MONO, fontSize: 12, color: 'rgba(255,255,255,0.4)',
      letterSpacing: 1.2, textTransform: 'uppercase',
      opacity,
    }}>
      {cur.text}
    </div>
  );
}

// ============================================================
// Brand mark + progress
// ============================================================
function Chrome({ t }) {
  return (
    <>
      <div style={{
        position: 'absolute', top: 50, left: 70,
        display: 'flex', alignItems: 'center', gap: 12,
      }}>
        <img src="assets/logo-mark-transparent.png" style={{ height: 26 }} onError={(e) => e.target.style.display='none'}/>
        <span style={{ fontFamily: FONT_DISPLAY, fontWeight: 600, fontSize: 20, letterSpacing: 0.6 }}>KOST</span>
      </div>
      <div style={{
        position: 'absolute', bottom: 12, left: 70, right: 70, height: 1,
        background: 'rgba(255,255,255,0.06)',
      }}>
        <div style={{
          height: '100%', width: `${(t / DURATION) * 100}%`,
          background: CYAN, opacity: 0.5,
        }}/>
      </div>
    </>
  );
}

// ============================================================
// Root scene
// ============================================================
function Scene() {
  const t = useTime() % DURATION;

  // POS state
  const posOpacity = clamp01(iv(t, T.posIn, [0, 1], ease)) * (1 - clamp01(iv(t, [T.converge[0] - 0.2, T.dashIn[0]], [0, 1], ease)));
  const posScale = lerp(0.85, 1, clamp01(iv(t, T.posIn, [0, 1], easeOut))) *
                   lerp(1, 0.6, clamp01(iv(t, [T.converge[0] - 0.2, T.dashIn[0]], [0, 1], ease)));

  return (
    <div style={{
      width: 1920, height: 1080, position: 'relative',
      background: '#000', overflow: 'hidden',
      fontFamily: FONT_TEXT, color: '#fff',
    }}>
      <Background t={t}/>
      <Chrome t={t}/>
      <Title t={t}/>

      {/* Rays sit beneath nodes/dashboard so circles overlay */}
      <Rays t={t}/>

      {/* Module nodes around perimeter */}
      {MODULES.map((m, i) => <ModuleNode key={m.id} mod={m} idx={i} t={t}/>)}

      {/* Center: POS first, then dashboard */}
      <CenterPOS t={t} opacity={posOpacity} scale={posScale}/>
      <Dashboard t={t}/>

      <Tagline t={t}/>
      <PhaseLabel t={t}/>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Stage width={1920} height={1080} fit="contain" duration={DURATION} loop>
    <Scene/>
  </Stage>
);
