// app.jsx — Main Catchflow System Quest animation timeline
// 1080x1920 mobile vertical, ~20 seconds

const STAGE_W = 1080;
const STAGE_H = 1920;
const DURATION = 22;

// ── Scene 1: Ambient awakening (0 - 3s) ─────────────────────────────────────
function Scene_Awaken() {
  const time = useTime();
  const { t: tr } = useLang();
  const t = time;
  // Title fade in/out
  const titleOp = animate({ from: 0, to: 1, start: 0.6, end: 1.4 })(t)
    - animate({ from: 0, to: 1, start: 2.4, end: 3.0 })(t);
  const subOp = animate({ from: 0, to: 1, start: 1.0, end: 1.8 })(t)
    - animate({ from: 0, to: 1, start: 2.4, end: 3.0 })(t);

  // Pulse ring grows during awakening
  return (
    <Sprite start={0} end={3.2}>
      <ParticleField count={70} time={t} width={STAGE_W} height={STAGE_H}/>

      <PulseRing cx={STAGE_W/2} cy={STAGE_H/2} time={t} start={0.2} period={2.2} maxR={520} thickness={1.5}/>
      <PulseRing cx={STAGE_W/2} cy={STAGE_H/2} time={t} start={1.0} period={2.2} maxR={520} thickness={1.5}/>
      <PulseRing cx={STAGE_W/2} cy={STAGE_H/2} time={t} start={1.8} period={2.2} maxR={520} thickness={1.5}/>

      {/* Central glyph */}
      <div style={{
        position: 'absolute',
        left: STAGE_W/2, top: STAGE_H/2 - 60,
        transform: 'translate(-50%, -50%)',
        width: 180, height: 180,
        opacity: clamp(t / 0.8, 0, 1) - clamp((t - 2.4) / 0.6, 0, 1),
      }}>
        <div style={{
          position: 'absolute', inset: 0,
          background: 'radial-gradient(circle, rgba(125,235,255,0.35) 0%, transparent 70%)',
          borderRadius: '50%',
          filter: `blur(${2 + Math.sin(t * 4) * 1}px)`,
        }}/>
        <div style={{
          position: 'absolute', inset: 30,
          border: `3px solid ${THEME.cyan}`,
          clipPath: 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)',
          boxShadow: `0 0 30px ${THEME.cyan}`,
          transform: `rotate(${t * 30}deg)`,
        }}/>
        <div style={{
          position: 'absolute', inset: 60,
          background: THEME.cyan,
          clipPath: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)',
          boxShadow: `0 0 20px ${THEME.cyan}`,
          opacity: 0.6 + Math.sin(t * 6) * 0.3,
        }}/>
      </div>

      {/* Title */}
      <div style={{
        position: 'absolute',
        left: STAGE_W/2, top: STAGE_H/2 + 200,
        transform: 'translate(-50%, 0)',
        textAlign: 'center',
        opacity: titleOp,
      }}>
        <div style={{
          fontFamily: THEME.mono,
          fontSize: 22,
          color: THEME.cyan,
          letterSpacing: '0.5em',
          marginBottom: 14,
          textTransform: 'uppercase',
        }}>{tr.systemOnline}</div>
        <div style={{
          fontFamily: THEME.font,
          fontSize: 84,
          fontWeight: 700,
          color: THEME.ink,
          letterSpacing: '0.16em',
          textShadow: `0 0 24px ${THEME.cyan}`,
        }}>{tr.brand}</div>
        <div style={{
          fontFamily: THEME.mono,
          fontSize: 24,
          color: THEME.ash,
          letterSpacing: '0.3em',
          marginTop: 8,
          opacity: subOp,
          whiteSpace: 'nowrap',
        }}>{tr.subtitle}</div>
      </div>
    </Sprite>
  );
}

// ── Scene 2: Notification appears (3 - 5s) ──────────────────────────────────
function Scene_Notification() {
  const time = useTime();
  const { t: tr } = useLang();
  const t = time - 3;
  if (t < 0 || t > 2.5) return null;

  // Notification slides down from top
  const slideY = interpolate([0, 0.6], [-300, 80], Easing.easeOutBack)(t);
  const op = interpolate([0, 0.4, 1.8, 2.5], [0, 1, 1, 0])(t);

  return (
    <>
      <ParticleField count={40} time={time} width={STAGE_W} height={STAGE_H}/>
      <div style={{
        position: 'absolute',
        left: 60, top: slideY,
        width: STAGE_W - 120, height: 220,
        opacity: op,
      }}>
        {/* Panel */}
        <div style={{
          position: 'absolute', inset: 0,
          background: 'linear-gradient(135deg, rgba(8,28,72,0.92) 0%, rgba(2,8,24,0.95) 100%)',
          clipPath: 'polygon(28px 0, 100% 0, 100% calc(100% - 28px), calc(100% - 28px) 100%, 0 100%, 0 28px)',
          boxShadow: `0 0 40px rgba(125,235,255,0.4)`,
        }}/>
        {/* Edge glow */}
        <div style={{
          position: 'absolute', inset: -1,
          background: THEME.cyan,
          clipPath: 'polygon(28px 0, 100% 0, 100% calc(100% - 28px), calc(100% - 28px) 100%, 0 100%, 0 28px, 30px 2px, 2px 30px, 2px calc(100% - 2px), calc(100% - 30px) calc(100% - 2px), calc(100% - 2px) calc(100% - 30px), calc(100% - 2px) 2px, 30px 2px)',
          filter: `drop-shadow(0 0 12px ${THEME.cyan})`,
        }}/>
        {/* Content */}
        <div style={{
          position: 'absolute', left: 50, top: 36,
          fontFamily: THEME.mono,
          fontSize: 20,
          color: THEME.cyan,
          letterSpacing: '0.3em',
          whiteSpace: 'nowrap',
        }}>{tr.notification}</div>
        <div style={{
          position: 'absolute', left: 50, top: 80,
          fontFamily: THEME.font,
          fontSize: 52,
          fontWeight: 700,
          color: THEME.ink,
          letterSpacing: '0.06em',
          textShadow: `0 0 18px ${THEME.cyan}`,
          whiteSpace: 'nowrap',
        }}>{tr.dailyQuest}</div>
        <div style={{
          position: 'absolute', left: 50, top: 152, right: 160,
          fontFamily: THEME.mono,
          fontSize: 18,
          color: THEME.ash,
          letterSpacing: '0.14em',
          whiteSpace: 'nowrap',
          overflow: 'hidden',
        }}>{tr.threeObjectives}</div>

        {/* Hex icon right */}
        <div style={{
          position: 'absolute', right: 50, top: '50%',
          transform: 'translateY(-50%)',
          width: 90, height: 90,
        }}>
          <div style={{
            position: 'absolute', inset: 0,
            background: THEME.cyan,
            clipPath: 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)',
            opacity: 0.25,
          }}/>
          <div style={{
            position: 'absolute', inset: 12,
            border: `2px solid ${THEME.cyan}`,
            clipPath: 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)',
          }}/>
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontFamily: THEME.font,
            fontSize: 44, fontWeight: 700,
            color: THEME.cyan,
            textShadow: `0 0 14px ${THEME.cyan}`,
          }}>!</div>
        </div>
      </div>
    </>
  );
}

// ── Scene 3: Quest panel (5.2 - 17s) ────────────────────────────────────────
function Scene_QuestPanel() {
  const time = useTime();
  const { t: tr } = useLang();
  const t = time - 5.2;
  if (t < 0 || t > 12.0) return null;

  // Panel scale-in
  const panelScale = interpolate([0, 0.6], [0.85, 1], Easing.easeOutBack)(t);
  const panelOp = interpolate([0, 0.4, 11.4, 12.0], [0, 1, 1, 0])(t);

  // Quest completion timestamps (in scene-local time)
  const completeTimes = [3.0, 5.2, 7.4]; // when each quest completes
  const cursorPath = [
    { time: 2.2, x: 540, y: 1130 },   // before quest 1
    { time: 3.0, x: 200, y: 1130 },   // tap quest 1
    { time: 4.4, x: 540, y: 1300 },
    { time: 5.2, x: 200, y: 1300 },   // tap quest 2
    { time: 6.6, x: 540, y: 1470 },
    { time: 7.4, x: 200, y: 1470 },   // tap quest 3
    { time: 8.4, x: 800, y: 1700 },   // off panel
  ];

  // Lerp cursor position along path
  const cursor = (() => {
    if (t < cursorPath[0].time) return { ...cursorPath[0], op: clamp((t - 1.4) / 0.4, 0, 1), tap: 0 };
    if (t > cursorPath[cursorPath.length - 1].time) return { ...cursorPath[cursorPath.length - 1], op: clamp((9.5 - t) / 0.4, 0, 1), tap: 0 };
    for (let i = 0; i < cursorPath.length - 1; i++) {
      const a = cursorPath[i], b = cursorPath[i + 1];
      if (t >= a.time && t <= b.time) {
        const localT = (t - a.time) / (b.time - a.time);
        const eased = Easing.easeInOutCubic(localT);
        return {
          x: a.x + (b.x - a.x) * eased,
          y: a.y + (b.y - a.y) * eased,
          op: 1,
          tap: 0,
        };
      }
    }
    return { ...cursorPath[0], op: 1, tap: 0 };
  })();

  // Tap pulse — fires within 0.4s after each completeTime
  let tap = 0;
  for (const ct of completeTimes) {
    if (t >= ct && t < ct + 0.4) {
      tap = (t - ct) / 0.4;
    }
  }

  // Quest statuses
  const questStatus = (idx) => t >= completeTimes[idx] ? 'complete' : 'pending';

  // XP fill: starts at 0.42, hits target at each completion
  const xpStops = [
    { time: 0, fill: 0.42 },
    { time: completeTimes[0] + 0.6, fill: 0.58 },
    { time: completeTimes[1] + 0.6, fill: 0.74 },
    { time: completeTimes[2] + 0.6, fill: 0.96 },
  ];
  const xpFill = (() => {
    if (t <= xpStops[0].time) return xpStops[0].fill;
    for (let i = 0; i < xpStops.length - 1; i++) {
      const a = xpStops[i], b = xpStops[i + 1];
      if (t >= a.time && t <= b.time) {
        const lt = (t - a.time) / (b.time - a.time);
        return a.fill + (b.fill - a.fill) * Easing.easeOutCubic(lt);
      }
    }
    return xpStops[xpStops.length - 1].fill;
  })();

  // Camera zoom-in at end (focus on level up around t = 9.0+)
  const camScale = interpolate([8.5, 9.5], [1, 1.08], Easing.easeInOutCubic)(t);
  const camY = interpolate([8.5, 9.5], [0, -40], Easing.easeInOutCubic)(t);

  // Level up flash at t = 9.5
  const levelUpStart = 9.4;
  const lvlT = t - levelUpStart;
  const showLevelUp = lvlT > 0 && lvlT < 2.5;
  const lvlOp = lvlT > 0 ? interpolate([0, 0.3, 2.0, 2.5], [0, 1, 1, 0])(lvlT) : 0;
  const lvlScale = lvlT > 0 ? interpolate([0, 0.4], [0.4, 1], Easing.easeOutBack)(lvlT) : 1;
  // White flash
  const flashOp = lvlT > 0 && lvlT < 0.6 ? (1 - lvlT / 0.6) * 0.85 : 0;

  return (
    <>
      <ParticleField count={50} time={time} width={STAGE_W} height={STAGE_H}/>

      <div style={{
        position: 'absolute', inset: 0,
        opacity: panelOp,
        transform: `scale(${panelScale * camScale}) translate(0px, ${camY}px)`,
        transformOrigin: 'center center',
      }}>
        {/* Header */}
        <div style={{
          position: 'absolute', left: 60, top: 220,
          right: 60,
        }}>
          <div style={{
            fontFamily: THEME.mono,
            fontSize: 24,
            color: THEME.cyan,
            letterSpacing: '0.5em',
            marginBottom: 12,
          }}>{tr.questDate}</div>
          <div style={{
            fontFamily: THEME.font,
            fontSize: 80,
            fontWeight: 700,
            color: THEME.ink,
            letterSpacing: '0.06em',
            textShadow: `0 0 24px ${THEME.cyan}`,
            lineHeight: 0.95,
          }}>{tr.roadTo}<br/>{tr.rankD}</div>
        </div>

        <CornerMarks x={50} y={1050} width={STAGE_W - 100} height={550} size={32} thickness={3} color={THEME.cyan}/>

        {/* Quest rows */}
        <QuestRow x={80} y={1080} width={STAGE_W - 160}
          label={tr.questDeepWork}
          xp={120}
          status={questStatus(0)}
          time={t}
          completeAt={completeTimes[0]}
          clearedText={tr.cleared}
          pendingText={tr.pending}
        />
        <QuestRow x={80} y={1240} width={STAGE_W - 160}
          label={tr.questRun}
          xp={80}
          status={questStatus(1)}
          time={t}
          completeAt={completeTimes[1]}
          clearedText={tr.cleared}
          pendingText={tr.pending}
        />
        <QuestRow x={80} y={1400} width={STAGE_W - 160}
          label={tr.questSleep}
          xp={60}
          status={questStatus(2)}
          time={t}
          completeAt={completeTimes[2]}
          clearedText={tr.cleared}
          pendingText={tr.pending}
        />

        {/* XP Bar */}
        <XPBar x={80} y={1620} width={STAGE_W - 160} fill={xpFill}
          rank={lvlT > 0.4 ? 'D' : 'E'}
          label={tr.expRank(lvlT > 0.4 ? 'D' : 'E')}
        />

        {/* Footer reward */}
        <div style={{
          position: 'absolute', left: 80, top: 1730,
          right: 80, height: 80,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          fontFamily: THEME.mono,
          fontSize: 18,
          color: THEME.ash,
          letterSpacing: '0.16em',
          whiteSpace: 'nowrap',
        }}>
          <span>{tr.reward}</span>
          <span style={{ color: THEME.cyan }}>{tr.streak}</span>
        </div>

        {/* Cursor — only during interaction window */}
        {t > 1.3 && t < 8.6 && (
          <Cursor x={cursor.x} y={cursor.y} opacity={cursor.op} tap={tap}/>
        )}
      </div>

      {/* LEVEL UP overlay */}
      {showLevelUp && (
        <>
          {/* White flash */}
          {flashOp > 0 && (
            <div style={{
              position: 'absolute', inset: 0,
              background: 'radial-gradient(circle at center, rgba(255,255,255,0.95) 0%, rgba(125,235,255,0.6) 40%, transparent 70%)',
              opacity: flashOp,
              mixBlendMode: 'screen',
            }}/>
          )}
          <div style={{
            position: 'absolute',
            left: STAGE_W/2, top: STAGE_H/2,
            transform: `translate(-50%, -50%) scale(${lvlScale})`,
            opacity: lvlOp,
            textAlign: 'center',
            zIndex: 100,
          }}>
            <div style={{
              fontFamily: THEME.mono,
              fontSize: 26,
              color: THEME.cyan,
              letterSpacing: '0.5em',
              marginBottom: 18,
              whiteSpace: 'nowrap',
            }}>{tr.notification}</div>
            <div style={{
              fontFamily: THEME.font,
              fontSize: 140,
              fontWeight: 700,
              color: THEME.ink,
              letterSpacing: '0.06em',
              textShadow: `0 0 40px ${THEME.cyan}, 0 0 80px ${THEME.cyan}`,
              lineHeight: 0.9,
            }}>{tr.levelUp1}<br/>{tr.levelUp2}</div>
            <div style={{
              fontFamily: THEME.mono,
              fontSize: 30,
              color: THEME.cyan,
              letterSpacing: '0.35em',
              marginTop: 30,
              textShadow: `0 0 18px ${THEME.cyan}`,
              whiteSpace: 'nowrap',
            }}>{tr.rankChange}</div>
            <div style={{
              fontFamily: THEME.mono,
              fontSize: 20,
              color: THEME.ash,
              letterSpacing: '0.25em',
              marginTop: 18,
              whiteSpace: 'nowrap',
            }}>{tr.xpGain}</div>
          </div>
        </>
      )}
    </>
  );
}

// ── Scene 4: Phone reveal — pulls back to show the app (17.5 - 22s) ─────────
function Scene_PhoneReveal() {
  const time = useTime();
  const { t: tr } = useLang();
  const t = time - 17.2;
  if (t < 0) return null;

  // Phone slides up from below
  const phoneY = interpolate([0, 1.0], [400, 100], Easing.easeOutCubic)(t);
  const phoneOp = interpolate([0, 0.5], [0, 1])(t);
  const phoneScale = interpolate([0, 1.0], [0.92, 1], Easing.easeOutCubic)(t);

  const phoneW = 760;
  const phoneH = 1500;
  const phoneX = (STAGE_W - phoneW) / 2;

  // Tagline
  const tagOp = interpolate([1.5, 2.0], [0, 1])(t);

  return (
    <>
      <ParticleField count={50} time={time} width={STAGE_W} height={STAGE_H}/>

      {/* Background hex grid behind phone */}
      <div style={{
        position: 'absolute', inset: 0,
        background: `
          radial-gradient(ellipse at center, rgba(20,60,140,0.25) 0%, transparent 60%)
        `,
        opacity: phoneOp,
      }}/>

      <PhoneShell
        x={phoneX} y={phoneY}
        width={phoneW} height={phoneH}
        opacity={phoneOp}
        scale={phoneScale}
        screenChildren={
          <div style={{ position: 'absolute', inset: 0, background: THEME.bgDeep }}>
            <StatusBar time={time}/>

            {/* Header */}
            <div style={{
              position: 'absolute', left: 36, top: 90, right: 36,
            }}>
              <div style={{
                fontFamily: THEME.mono,
                fontSize: 14,
                color: THEME.cyan,
                letterSpacing: '0.4em',
                marginBottom: 6,
              }}>{tr.rankD2}</div>
              <div style={{
                fontFamily: THEME.font,
                fontSize: 40,
                fontWeight: 700,
                color: THEME.ink,
                letterSpacing: '0.04em',
                textShadow: `0 0 14px ${THEME.cyan}`,
                whiteSpace: 'nowrap',
              }}>{tr.goodHunt1}<br/>{tr.goodHunt2}</div>
            </div>

            {/* Mini XP bar */}
            <div style={{ position: 'absolute', left: 36, top: 280, right: 36 }}>
              <div style={{
                fontFamily: THEME.mono, fontSize: 12, color: THEME.cyanDim,
                letterSpacing: '0.16em', marginBottom: 6,
                display: 'flex', justifyContent: 'space-between',
                whiteSpace: 'nowrap',
              }}>
                <span>{tr.miniExp}</span>
                <span>{tr.miniStreak}</span>
              </div>
              <div style={{
                width: '100%', height: 8,
                background: 'rgba(125,235,255,0.1)',
                position: 'relative',
                clipPath: 'polygon(0 0, 100% 0, calc(100% - 4px) 100%, 4px 100%)',
              }}>
                <div style={{
                  position: 'absolute', left: 0, top: 0, bottom: 0,
                  width: '96%',
                  background: `linear-gradient(90deg, ${THEME.blue}, ${THEME.cyan})`,
                  boxShadow: `0 0 10px ${THEME.cyan}`,
                  clipPath: 'polygon(0 0, 100% 0, calc(100% - 4px) 100%, 4px 100%)',
                }}/>
              </div>
            </div>

            {/* Mini quest rows (all complete) */}
            <div style={{
              position: 'absolute', left: 36, top: 340, right: 36,
              fontFamily: THEME.mono,
              fontSize: 13,
              color: THEME.cyan,
              letterSpacing: '0.3em',
              marginBottom: 10,
            }}>{tr.todayCleared}</div>

            {[
              { label: tr.miniDeep, xp: 120 },
              { label: tr.miniRun, xp: 80 },
              { label: tr.miniSleep, xp: 60 },
            ].map((q, i) => (
              <div key={i} style={{
                position: 'absolute', left: 36, right: 36,
                top: 380 + i * 100,
                height: 84,
              }}>
                <div style={{
                  position: 'absolute', inset: 0,
                  background: 'linear-gradient(90deg, rgba(20,60,45,0.5) 0%, rgba(8,20,18,0.6) 100%)',
                  clipPath: 'polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px)',
                  border: `1px solid ${THEME.green}`,
                  filter: `drop-shadow(0 0 6px rgba(124,255,178,0.4))`,
                }}/>
                <div style={{
                  position: 'absolute', left: 18, top: '50%', transform: 'translateY(-50%)',
                  width: 36, height: 36,
                }}>
                  <svg viewBox="0 0 36 36" style={{ position: 'absolute', inset: 0, overflow: 'visible' }}>
                    <polygon points="18,1.5 33.5,10.5 33.5,25.5 18,34.5 2.5,25.5 2.5,10.5"
                      fill={THEME.green}
                      style={{ filter: `drop-shadow(0 0 6px ${THEME.green})` }}/>
                    <path d="M9 18 L15 24 L27 12" stroke="#03060d" strokeWidth="3" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
                  </svg>
                </div>
                <div style={{
                  position: 'absolute', left: 70, right: 16, top: 18,
                  fontFamily: THEME.font,
                  fontSize: 18, fontWeight: 600,
                  color: THEME.ash,
                  textDecoration: 'line-through',
                  letterSpacing: '0.04em',
                  textTransform: 'uppercase',
                  whiteSpace: 'nowrap',
                  overflow: 'hidden',
                }}>{q.label}</div>
                <div style={{
                  position: 'absolute', left: 70, right: 16, top: 46,
                  fontFamily: THEME.mono,
                  fontSize: 11,
                  color: THEME.green,
                  letterSpacing: '0.18em',
                  whiteSpace: 'nowrap',
                }}>+{q.xp} XP · {tr.cleared}</div>
              </div>
            ))}

            {/* Tomorrow tease */}
            <div style={{
              position: 'absolute', left: 36, right: 36, top: 720,
              fontFamily: THEME.mono,
              fontSize: 13,
              color: THEME.cyan,
              letterSpacing: '0.3em',
            }}>{tr.tomorrow}</div>
            <div style={{
              position: 'absolute', left: 36, right: 36, top: 750,
              fontFamily: THEME.font,
              fontSize: 20, fontWeight: 600,
              color: THEME.ink,
              letterSpacing: '0.04em',
              textTransform: 'uppercase',
              whiteSpace: 'nowrap',
            }}>{tr.newQuest}</div>

            {/* Bottom nav */}
            <div style={{
              position: 'absolute', left: 36, right: 36, bottom: 64,
              height: 70,
              display: 'flex', justifyContent: 'space-around', alignItems: 'center',
              background: 'rgba(8, 20, 44, 0.7)',
              clipPath: 'polygon(16px 0, calc(100% - 16px) 0, 100% 100%, 0 100%)',
              border: `1px solid ${THEME.cyanDim}`,
            }}>
              {tr.nav.map((label, i) => (
                <div key={i} style={{
                  fontFamily: THEME.mono,
                  fontSize: 12,
                  color: i === 0 ? THEME.cyan : THEME.ash,
                  letterSpacing: '0.3em',
                  textShadow: i === 0 ? `0 0 8px ${THEME.cyan}` : 'none',
                }}>{label}</div>
              ))}
            </div>
          </div>
        }
      />

      {/* Tagline overlay — bottom, beneath the phone */}
      <div style={{
        position: 'absolute',
        left: STAGE_W / 2, bottom: 60,
        transform: 'translateX(-50%)',
        textAlign: 'center',
        opacity: tagOp,
        whiteSpace: 'nowrap',
      }}>
        <div style={{
          fontFamily: THEME.font,
          fontSize: 64,
          fontWeight: 700,
          color: THEME.ink,
          letterSpacing: '0.16em',
          textShadow: `0 0 20px ${THEME.cyan}`,
        }}>{tr.brand}</div>
        <div style={{
          fontFamily: THEME.mono,
          fontSize: 20,
          color: THEME.cyan,
          letterSpacing: '0.4em',
          marginTop: 8,
          whiteSpace: 'nowrap',
        }}>{tr.tagline}</div>
      </div>
    </>
  );
}

// ── Global background grid ──────────────────────────────────────────────────
function BackdropGrid() {
  const time = useTime();
  const drift = (time * 8) % 80;
  return (
    <>
      {/* Base radial */}
      <div style={{
        position: 'absolute', inset: 0,
        background: `
          radial-gradient(ellipse at 50% 30%, rgba(40,90,180,0.18) 0%, transparent 60%),
          radial-gradient(ellipse at 50% 90%, rgba(80,40,180,0.14) 0%, transparent 65%)
        `,
      }}/>
      {/* Hex grid — subtle */}
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: `
          linear-gradient(0deg, rgba(125,235,255,0.06) 1px, transparent 1px),
          linear-gradient(90deg, rgba(125,235,255,0.06) 1px, transparent 1px)
        `,
        backgroundSize: '80px 80px',
        backgroundPosition: `0 ${drift}px, ${drift}px 0`,
        maskImage: 'radial-gradient(ellipse at center, black 0%, transparent 80%)',
      }}/>
      {/* Vignette */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.6) 100%)',
        pointerEvents: 'none',
      }}/>
    </>
  );
}

// ── Frame label (for comments) ─────────────────────────────────────────────
function FrameLabel() {
  const time = useTime();
  const sec = Math.floor(time);
  React.useEffect(() => {
    const root = document.querySelector('[data-video-root]');
    if (root) root.setAttribute('data-screen-label', `t=${sec}s`);
  }, [sec]);
  return null;
}

// ── Root ────────────────────────────────────────────────────────────────────
function Root() {
  return (
    <div data-video-root style={{ position: 'absolute', inset: 0 }}>
      <FrameLabel/>
      <BackdropGrid/>
      <Scene_Awaken/>
      <Scene_Notification/>
      <Scene_QuestPanel/>
      <Scene_PhoneReveal/>
    </div>
  );
}

function App() {
  return (
    <LangProvider>
      <Stage
        width={STAGE_W}
        height={STAGE_H}
        duration={DURATION}
        background={THEME.bg}
        fps={60}
        controls={false}
        embed={true}
        persistKey="catchflow-hero"
      >
        <Root/>
      </Stage>
    </LangProvider>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>);
