// scenes.jsx — PicturePledge explainer (30s, 1920x1080)
// Loaded after animations.jsx.

// ─── tokens (from BitPledging design system) ───────────────────────────────
const PP = {
  cream:       '#fef9f2',  // page background
  bgSoft:      '#faf3e7',
  bgWarm:      '#fbe9d0',
  surface:     '#ffffff',
  orange:      '#ff6b35',  // paprika — primary brand
  orangeDark:  '#e54a0e',
  orangeWarm:  '#ff8a5c',
  gold:        '#ffd23f',
  jade:        '#06a77d',
  ink:         '#1a1714',
  inkSoft:     '#4a423a',
  inkMute:     '#8a7f72',
  rule:        '#ebe2d2',
  ruleStrong:  '#d8cdba',
  // back-compat aliases used elsewhere
  green:       '#06a77d',
  greenAccent: '#06a77d',
};
const FF = {
  display: '"Fraunces", Georgia, serif',
  ui: '"Familjen Grotesk", "Inter", system-ui, sans-serif',
  mono: '"JetBrains Mono", ui-monospace, monospace',
};

// Warm cream tile tones (matches BitPledging tonePalette)
const TILE_TONES = [
  '#efe4cf', '#e8d9bd', '#f3ead8', '#dfc9a3',
  '#e7d4b1', '#f0e4cb', '#d9c2a0', '#ece0c5',
];

// Reusable PP wordmark — 2×2 colored squares + serif name (matches brand logo)
function Wordmark({ size = 56, color = PP.ink, showText = true }) {
  const dotSize = size * 0.82;
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: size * 0.24 }}>
      <div style={{
        width: dotSize, height: dotSize,
        display: 'grid', gridTemplateColumns: '1fr 1fr',
        gridTemplateRows: '1fr 1fr', gap: size * 0.06,
      }}>
        <div style={{ background: PP.orange, borderRadius: size * 0.08 }}/>
        <div style={{ background: PP.gold,   borderRadius: size * 0.08 }}/>
        <div style={{ background: PP.jade,   borderRadius: size * 0.08 }}/>
        <div style={{ background: PP.ink,    borderRadius: size * 0.08 }}/>
      </div>
      {showText && (
        <div style={{
          fontFamily: FF.display, fontWeight: 700, fontSize: size,
          letterSpacing: '-0.025em', color, lineHeight: 1,
        }}>
          PicturePledge
        </div>
      )}
    </div>
  );
}

// ─── Real team photo — the image being revealed ────────────────────────────
const TEAM_PHOTO_URL = 'assets/team-photo.jpeg';

function TeamPhoto({ width, height }) {
  return (
    <div style={{
      width, height, position: 'relative', overflow: 'hidden', background: '#1a1714',
    }}>
      <img src={TEAM_PHOTO_URL} alt="" style={{
        width: '100%', height: '100%', objectFit: 'cover', display: 'block',
      }}/>
    </div>
  );
}

// ─── Jigsaw piece path geometry ────────────────────────────────────────────
// One edge of a piece, traced from (x1,y1) to (x2,y2). `tab` is +1 (knob bulges
// outward, away from piece center), -1 (socket carved into piece), or 0
// (straight edge — for outer border of the whole jigsaw).
// outX/outY is the unit perpendicular pointing OUT of the piece.
function edgePath(x1, y1, x2, y2, tab, outX, outY) {
  if (tab === 0) return `L ${x2} ${y2} `;
  const dx = x2 - x1, dy = y2 - y1; // unit (cells are 1×1)
  // Parameterized point: t along edge, p perpendicular (positive = out).
  const pt = (t, p) => ({
    x: x1 + t * dx + p * tab * outX,
    y: y1 + t * dy + p * tab * outY,
  });
  const f = (pp) => `${pp.x.toFixed(4)} ${pp.y.toFixed(4)}`;
  // Classic jigsaw knob: necks in at 0.35/0.65, bulb peaks at 0.5 perp 0.28
  return `L ${f(pt(0.35, 0))} `
       + `C ${f(pt(0.42, 0))} ${f(pt(0.30, 0.18))} ${f(pt(0.38, 0.22))} `
       + `C ${f(pt(0.42, 0.32))} ${f(pt(0.58, 0.32))} ${f(pt(0.62, 0.22))} `
       + `C ${f(pt(0.70, 0.18))} ${f(pt(0.58, 0))} ${f(pt(0.65, 0))} `
       + `L ${f(pt(1, 0))} `;
}

// Build a piece outline (closed path) for piece at column c, row r.
// hEdges[r][c]: horizontal edge between row r and row r+1, in {-1, +1}.
//   +1 = knob points down (into row r+1), -1 = up.
// vEdges[r][c]: vertical edge between col c and col c+1, in {-1, +1}.
//   +1 = knob points right (into col c+1), -1 = left.
function jigsawPiecePath(c, r, cols, rows, hEdges, vEdges) {
  // For each side of THIS piece, figure out if the shared edge has a tab
  // pointing OUT of this piece (+1), INTO it (-1), or doesn't exist (0).
  const top    = r === 0          ? 0 : (hEdges[r-1][c] === +1 ? -1 : +1);
  const bottom = r === rows - 1   ? 0 : (hEdges[r][c]   === +1 ? +1 : -1);
  const left   = c === 0          ? 0 : (vEdges[r][c-1] === +1 ? -1 : +1);
  const right  = c === cols - 1   ? 0 : (vEdges[r][c]   === +1 ? +1 : -1);

  let p = `M ${c} ${r} `;
  p += edgePath(c,   r,   c+1, r,   top,    0, -1); // top:    out = up
  p += edgePath(c+1, r,   c+1, r+1, right,  1,  0); // right:  out = right
  p += edgePath(c+1, r+1, c,   r+1, bottom, 0,  1); // bottom: out = down
  p += edgePath(c,   r+1, c,   r,   left,  -1,  0); // left:   out = left
  return p + 'Z';
}

// ─── Pixel grid overlay (jigsaw pieces) ────────────────────────────────────
// Covers the photo with N×M cream-toned jigsaw pieces. As `revealCount` rises,
// pieces fade out (in a stable shuffled order), revealing the photo underneath.
function PixelGrid({ cols, rows, revealCount, seed = 7 }) {
  const { rankByIndex, hEdges, vEdges } = React.useMemo(() => {
    let s = seed;
    const rand = () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };

    // 1) Random reveal order
    const tiles = [];
    for (let i = 0; i < cols * rows; i++) tiles.push(i);
    for (let i = tiles.length - 1; i > 0; i--) {
      const j = Math.floor(rand() * (i + 1));
      [tiles[i], tiles[j]] = [tiles[j], tiles[i]];
    }
    const rankByIndex = new Map();
    tiles.forEach((idx, rank) => rankByIndex.set(idx, rank));

    // 2) Horizontal edges between row r and r+1 (rows-1 strips × cols cells)
    const hEdges = [];
    for (let r = 0; r < rows - 1; r++) {
      const row = [];
      for (let c = 0; c < cols; c++) row.push(rand() < 0.5 ? +1 : -1);
      hEdges.push(row);
    }
    // 3) Vertical edges between col c and c+1 (rows cells × cols-1 strips)
    const vEdges = [];
    for (let r = 0; r < rows; r++) {
      const row = [];
      for (let c = 0; c < cols - 1; c++) row.push(rand() < 0.5 ? +1 : -1);
      vEdges.push(row);
    }
    return { rankByIndex, hEdges, vEdges };
  }, [cols, rows, seed]);

  return (
    <svg
      viewBox={`-0.4 -0.4 ${cols + 0.8} ${rows + 0.8}`}
      preserveAspectRatio="none"
      style={{
        position: 'absolute',
        // The viewBox is padded by 0.4 cells on each side to accommodate the
        // knobs that stick out of border pieces. We offset & oversize the SVG
        // by that same fraction so the cell area [0..cols]×[0..rows] still
        // perfectly aligns with the underlying photo container.
        left:   `${(-0.4 / cols) * 100}%`,
        top:    `${(-0.4 / rows) * 100}%`,
        width:  `${((cols + 0.8) / cols) * 100}%`,
        height: `${((rows + 0.8) / rows) * 100}%`,
        pointerEvents: 'none',
        overflow: 'visible',
      }}
    >
      {Array.from({ length: cols * rows }, (_, i) => {
        const c = i % cols, r = Math.floor(i / cols);
        const revealed = rankByIndex.get(i) < revealCount;
        const d = jigsawPiecePath(c, r, cols, rows, hEdges, vEdges);
        const tone = TILE_TONES[(c * 7 + r * 13) % TILE_TONES.length];
        return (
          <path
            key={i}
            d={d}
            fill={tone}
            stroke="rgba(0,0,0,0.16)"
            strokeWidth={0.012}
            vectorEffect="non-scaling-stroke"
            style={{
              opacity: revealed ? 0 : 1,
              transition: 'opacity 520ms cubic-bezier(0.2, 0.8, 0.3, 1)',
              willChange: 'opacity',
            }}
          />
        );
      })}
    </svg>
  );
}

// ─── Browser chrome ────────────────────────────────────────────────────────
function BrowserChrome({ width, height, url, children, x = 0, y = 0, scale = 1, opacity = 1 }) {
  return (
    <div style={{
      position: 'absolute',
      left: x, top: y, width, height,
      transform: `scale(${scale})`,
      transformOrigin: 'center',
      opacity,
      background: '#fff',
      borderRadius: 18,
      boxShadow: '0 30px 80px rgba(22,17,13,0.18), 0 8px 24px rgba(22,17,13,0.08)',
      overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
    }}>
      <div style={{
        height: 44, background: '#f1ece3',
        borderBottom: `1px solid ${PP.rule}`,
        display: 'flex', alignItems: 'center', gap: 8, padding: '0 16px',
        flexShrink: 0,
      }}>
        <div style={{ display: 'flex', gap: 6 }}>
          {['#ff5f57','#febc2e','#28c840'].map((c,i) => (
            <div key={i} style={{ width: 12, height: 12, borderRadius: 6, background: c }}/>
          ))}
        </div>
        <div style={{
          marginLeft: 16, padding: '5px 16px',
          background: '#fff', border: `1px solid ${PP.rule}`, borderRadius: 8,
          fontFamily: FF.mono, fontSize: 13, color: PP.inkSoft,
          minWidth: 280, textAlign: 'center', flex: '0 1 auto',
        }}>
          {url}
        </div>
      </div>
      <div style={{ flex: 1, overflow: 'hidden', position: 'relative', background: PP.cream }}>
        {children}
      </div>
    </div>
  );
}

// ─── SCENE 1 — HOOK / LANDING PAGE HERO (0 → 4s) ──────────────────────────
// Mimics picturepledge.com hero: nav + headline + description + CTAs + live grid demo.
function SceneHook() {
  const { localTime, duration } = useSprite();

  // exit
  const exit = clamp((localTime - (duration - 0.5)) / 0.5, 0, 1);
  const exitOp = 1 - Easing.easeInCubic(exit);

  const browserW = 1740, browserH = 960;

  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: PP.cream,
      opacity: exitOp,
    }}>
      <BrowserChrome
        width={browserW} height={browserH}
        url="picturepledge.com"
        x={(1920 - browserW)/2} y={(1080 - browserH)/2}
      >
        <LandingHero localTime={localTime}/>
      </BrowserChrome>
    </div>
  );
}

function LandingHero({ localTime }) {
  const navOp    = Easing.easeOutCubic(clamp(localTime / 0.35, 0, 1));
  const eyebrowOp= Easing.easeOutCubic(clamp((localTime - 0.35) / 0.4, 0, 1));
  const titleOp  = Easing.easeOutCubic(clamp((localTime - 0.5)  / 0.6, 0, 1));
  const descOp   = Easing.easeOutCubic(clamp((localTime - 1.0)  / 0.5, 0, 1));
  const ctaOp    = Easing.easeOutCubic(clamp((localTime - 1.3)  / 0.4, 0, 1));
  const demoOp   = Easing.easeOutCubic(clamp((localTime - 0.9)  / 0.5, 0, 1));

  // Live grid demo: reveals 0→62 of 100 pieces, $0 → $1,550 of $2,500
  const TOTAL = 100;
  const revealRaw = interpolate([1.4, 3.8], [0, 62], Easing.easeInOutCubic)(localTime);
  const revealCount = Math.floor(clamp(revealRaw, 0, TOTAL));
  const raised = Math.floor(interpolate([1.4, 3.8], [0, 1550], Easing.easeOutCubic)(localTime));
  const pct = Math.floor((revealCount / TOTAL) * 100);

  const demoW = 700, demoH = 525;

  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column' }}>
      {/* Nav */}
      <div style={{
        height: 68, padding: '0 40px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        borderBottom: `1px solid ${PP.rule}`,
        background: '#fff',
        opacity: navOp,
        transform: `translateY(${(1-navOp)*-6}px)`,
      }}>
        <Wordmark size={28}/>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 28,
          fontFamily: FF.ui, fontSize: 15, color: PP.inkSoft, fontWeight: 500,
        }}>
          <span>Grid demo</span>
          <span style={{ display: 'flex', gap: 10 }}>
            <span style={{ fontWeight: 700, color: PP.ink }}>EN</span>
            <span>ES</span>
          </span>
          <span>Sign in</span>
          <div style={{
            padding: '9px 18px', background: PP.orange, color: '#fff',
            borderRadius: 8, fontWeight: 600, fontSize: 15,
            boxShadow: '0 4px 12px rgba(255,107,53,0.22)',
          }}>Start a campaign</div>
        </div>
      </div>

      {/* Eyebrow */}
      <div style={{
        textAlign: 'center', padding: '36px 0 0',
        fontFamily: FF.mono, fontSize: 14, letterSpacing: '0.24em',
        textTransform: 'uppercase', color: PP.orange, fontWeight: 600,
        opacity: eyebrowOp,
      }}>
        PicturePledge — fundraisers worth watching
      </div>

      {/* Hero body */}
      <div style={{
        flex: 1, padding: '20px 72px 36px',
        display: 'grid', gridTemplateColumns: '1.05fr 1fr', gap: 64,
        alignItems: 'center',
      }}>
        {/* LEFT */}
        <div>
          <div style={{
            fontFamily: FF.display, fontWeight: 800, fontSize: 76,
            color: PP.ink, letterSpacing: '-0.03em', lineHeight: 1.05,
            opacity: titleOp, transform: `translateY(${(1-titleOp)*14}px)`,
          }}>
            Your photo.<br/>
            Your goal.<br/>
            <span style={{ color: PP.orange }}>Revealed</span> one<br/>
            donation at a time.
          </div>
          <div style={{
            marginTop: 28, fontFamily: FF.ui, fontSize: 21, lineHeight: 1.5,
            color: PP.inkSoft, maxWidth: 560, fontWeight: 400,
            opacity: descOp, transform: `translateY(${(1-descOp)*10}px)`,
          }}>
            For teams, classrooms, causes, and the people you love.
            Donors give any amount, and the picture comes into focus together.
          </div>
          <div style={{
            marginTop: 32, display: 'flex', gap: 18, alignItems: 'center',
            opacity: ctaOp, transform: `translateY(${(1-ctaOp)*8}px)`,
          }}>
            <div style={{
              padding: '14px 26px', background: PP.orange, color: '#fff',
              borderRadius: 10, fontFamily: FF.display, fontWeight: 700, fontSize: 19,
              boxShadow: '0 10px 22px rgba(255,107,53,0.28)',
              letterSpacing: '-0.01em',
            }}>Start a campaign →</div>
            <div style={{
              padding: '14px 4px', color: PP.ink,
              fontFamily: FF.ui, fontWeight: 600, fontSize: 17,
              borderBottom: `2px solid ${PP.ink}`,
            }}>See the grid in action</div>
          </div>
        </div>

        {/* RIGHT — live grid demo */}
        <div style={{
          opacity: demoOp, transform: `translateY(${(1-demoOp)*16}px)`,
          display: 'flex', flexDirection: 'column', gap: 16, alignItems: 'stretch',
        }}>
          <div style={{
            position: 'relative', width: demoW, height: demoH,
            borderRadius: 14, overflow: 'hidden',
            boxShadow: '0 20px 50px rgba(22,17,13,0.18), 0 0 0 1px rgba(22,17,13,0.05)',
          }}>
            <TeamPhoto width={demoW} height={demoH}/>
            <PixelGrid cols={10} rows={10} width={demoW} height={demoH} revealCount={revealCount} seed={5}/>
          </div>
          <div style={{
            display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
            fontFamily: FF.ui,
          }}>
            <div style={{ fontSize: 17, color: PP.inkSoft }}>
              <span style={{
                fontWeight: 800, color: PP.ink, fontFamily: FF.display,
                fontVariantNumeric: 'tabular-nums',
              }}>{pct}%</span> revealed
            </div>
            <div style={{
              fontFamily: FF.display, fontWeight: 800, fontSize: 26, color: PP.ink,
              fontVariantNumeric: 'tabular-nums', letterSpacing: '-0.02em',
            }}>
              <span style={{ color: PP.orange }}>${raised.toLocaleString()}</span>
              <span style={{ color: PP.inkSoft, fontWeight: 600 }}> of $2,500</span>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── SCENE 2 — SETUP (3 → 10s) ─────────────────────────────────────────────
function SceneSetup() {
  const { localTime, duration } = useSprite();

  // master fade in/out
  const inOp = Easing.easeOutCubic(clamp(localTime / 0.4, 0, 1));
  const outOp = 1 - Easing.easeInCubic(clamp((localTime - (duration - 0.4)) / 0.4, 0, 1));
  const op = inOp * outOp;

  // Three step phases within 6s window:
  //  step1 (upload): 0.15 - 1.55
  //  step2 (goal):   1.7  - 3.2
  //  step3 (link):   3.5  - 5.3
  const step1 = clamp((localTime - 0.15) / 1.4, 0, 1);
  const step2 = clamp((localTime - 1.7)  / 1.5, 0, 1);
  const step3 = clamp((localTime - 3.5)  / 1.8, 0, 1);

  const browserW = 1500, browserH = 880;

  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: PP.cream,
      opacity: op,
    }}>
      {/* heading */}
      <div style={{
        position: 'absolute', top: 56, left: 0, right: 0,
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12,
      }}>
        <div style={{
          fontFamily: FF.mono, fontSize: 18, letterSpacing: '0.24em',
          textTransform: 'uppercase', color: PP.orange, fontWeight: 600,
        }}>
          ◆ Three steps to launch
        </div>
        <div style={{
          fontFamily: FF.display, fontWeight: 800, fontSize: 56,
          color: PP.ink, letterSpacing: '-0.025em',
        }}>
          Set up in under two minutes.
        </div>
      </div>

      {/* browser frame */}
      <BrowserChrome
        width={browserW} height={browserH}
        url="picturepledge.com/create"
        x={(1920 - browserW)/2} y={220}
      >
        <SetupSteps step1={step1} step2={step2} step3={step3} t={localTime}/>
      </BrowserChrome>
    </div>
  );
}

function SetupSteps({ step1, step2, step3, t }) {
  // 3-column layout, each column lights up as its step progresses
  const cols = [
    { label: '1', title: 'Upload a photo', active: step1 > 0, progress: step1 },
    { label: '2', title: 'Set your goal', active: step2 > 0, progress: step2 },
    { label: '3', title: 'Share your link', active: step3 > 0, progress: step3 },
  ];

  return (
    <div style={{
      position: 'absolute', inset: 0,
      padding: 56,
      display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 28,
    }}>
      {/* col 1: upload photo */}
      <StepCard idx="1" title="Upload your team photo" active={step1 > 0.02} done={step1 > 0.95}>
        <UploadVisual progress={step1}/>
      </StepCard>

      {/* col 2: set goal */}
      <StepCard idx="2" title="Name it & set your goal" active={step2 > 0.02} done={step2 > 0.95}>
        <GoalVisual progress={step2}/>
      </StepCard>

      {/* col 3: share link */}
      <StepCard idx="3" title="Share your link" active={step3 > 0.02} done={step3 > 0.95}>
        <ShareVisual progress={step3} t={t}/>
      </StepCard>
    </div>
  );
}

function StepCard({ idx, title, active, done, children }) {
  return (
    <div style={{
      background: PP.surface,
      border: `2px solid ${active ? PP.orange : PP.rule}`,
      borderRadius: 16,
      padding: 24,
      display: 'flex', flexDirection: 'column', gap: 16,
      transition: 'border-color 240ms ease, box-shadow 240ms ease',
      boxShadow: active ? '0 10px 30px rgba(255,107,53,0.16)' : '0 2px 6px rgba(22,17,13,0.04)',
      position: 'relative',
      opacity: active ? 1 : 0.55,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <div style={{
          width: 36, height: 36, borderRadius: 18,
          background: done ? PP.green : (active ? PP.orange : PP.rule),
          color: '#fff', fontFamily: FF.display, fontWeight: 800,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 18, transition: 'background 240ms',
        }}>
          {done ? '✓' : idx}
        </div>
        <div style={{
          fontFamily: FF.display, fontWeight: 700, fontSize: 22,
          color: PP.ink, letterSpacing: '-0.01em',
        }}>{title}</div>
      </div>
      <div style={{ flex: 1, minHeight: 0 }}>{children}</div>
    </div>
  );
}

function UploadVisual({ progress }) {
  // dropzone -> photo preview slides into it
  const photoY = interpolate([0, 0.45, 0.8], [-180, -180, 0], Easing.easeOutBack)(progress);
  const photoOp = interpolate([0.2, 0.5], [0, 1])(progress);
  const dropPulse = progress < 0.5 ? 1 + Math.sin(progress * 12) * 0.02 : 1;

  return (
    <div style={{
      width: '100%', height: 460, position: 'relative',
      borderRadius: 12, overflow: 'hidden',
      border: `2px dashed ${PP.rule}`,
      background: PP.cream,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      transform: `scale(${dropPulse})`,
    }}>
      {progress < 0.4 && (
        <div style={{
          display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16,
          color: PP.inkSoft, fontFamily: FF.ui,
        }}>
          <div style={{
            width: 80, height: 80, borderRadius: 16,
            background: '#fff', border: `2px solid ${PP.rule}`,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 40,
          }}>
            <svg width="36" height="36" viewBox="0 0 24 24" fill="none">
              <path d="M21 16V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11l5-5z" stroke={PP.orange} strokeWidth="2"/>
              <circle cx="9" cy="9" r="2" stroke={PP.orange} strokeWidth="2"/>
              <path d="m3 19 5-5 4 4 4-4 5 5" stroke={PP.orange} strokeWidth="2"/>
            </svg>
          </div>
          <div style={{ fontWeight: 600, fontSize: 18, color: PP.ink }}>Drop your photo here</div>
          <div style={{ fontSize: 14 }}>JPG or PNG · up to 25 MB</div>
        </div>
      )}
      {/* photo "drops in" */}
      <div style={{
        position: 'absolute', inset: 16,
        transform: `translateY(${photoY}px)`,
        opacity: photoOp,
        borderRadius: 8, overflow: 'hidden',
        boxShadow: '0 8px 24px rgba(22,17,13,0.18)',
      }}>
        <TeamPhoto width={360} height={420}/>
      </div>
      {/* progress bar on hover-in */}
      {progress > 0.5 && progress < 0.92 && (
        <div style={{
          position: 'absolute', bottom: 16, left: 16, right: 16,
          height: 6, background: PP.rule, borderRadius: 3, overflow: 'hidden',
        }}>
          <div style={{
            width: `${clamp((progress - 0.5)/0.4, 0, 1) * 100}%`,
            height: '100%', background: PP.orange,
            transition: 'width 80ms linear',
          }}/>
        </div>
      )}
    </div>
  );
}

function GoalVisual({ progress }) {
  // type the campaign title, then goal amount
  const titleStr = 'State Tournament Travel';
  const goalStr  = '$5,000';
  const typedTitle = titleStr.slice(0, Math.floor(interpolate([0.05, 0.45], [0, titleStr.length])(progress)));
  const typedGoal  = goalStr.slice(0, Math.floor(interpolate([0.5, 0.9], [0, goalStr.length])(progress)));
  const showCursor = progress < 0.95;

  const pieceCount = Math.floor(interpolate([0.65, 0.95], [0, 250])(progress));

  return (
    <div style={{
      width: '100%', display: 'flex', flexDirection: 'column', gap: 20,
      fontFamily: FF.ui,
    }}>
      <div>
        <div style={{ fontSize: 13, color: PP.inkSoft, fontWeight: 600, marginBottom: 8, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Campaign name</div>
        <div style={{
          padding: '14px 16px', background: '#fff',
          border: `2px solid ${progress > 0.05 && progress < 0.5 ? PP.orange : PP.rule}`,
          borderRadius: 10, fontSize: 22, fontWeight: 600, color: PP.ink,
          minHeight: 30, fontFamily: FF.display,
        }}>
          {typedTitle}{showCursor && progress > 0.05 && progress < 0.5 && <span style={{ opacity: Math.floor(progress * 20) % 2 ? 0 : 1 }}>|</span>}
        </div>
      </div>
      <div>
        <div style={{ fontSize: 13, color: PP.inkSoft, fontWeight: 600, marginBottom: 8, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Goal amount</div>
        <div style={{
          padding: '14px 16px', background: '#fff',
          border: `2px solid ${progress > 0.5 && progress < 0.95 ? PP.orange : PP.rule}`,
          borderRadius: 10, fontSize: 32, fontWeight: 800, color: PP.ink,
          minHeight: 44, fontFamily: FF.display,
          display: 'flex', alignItems: 'center', gap: 4,
        }}>
          {typedGoal}{showCursor && progress > 0.5 && progress < 0.95 && <span style={{ opacity: Math.floor(progress * 20) % 2 ? 0 : 1 }}>|</span>}
        </div>
      </div>
      <div>
        <div style={{ fontSize: 13, color: PP.inkSoft, fontWeight: 600, marginBottom: 8, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Pieces</div>
        <div style={{
          padding: '12px 16px', background: '#fff',
          border: `2px solid ${PP.rule}`,
          borderRadius: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        }}>
          <div style={{ fontSize: 16, color: PP.inkSoft }}>Pixel grid · 25×10</div>
          <div style={{
            fontFamily: FF.mono, fontSize: 18, fontWeight: 600,
            color: progress > 0.65 ? PP.green : PP.inkSoft,
            transition: 'color 240ms',
          }}>{pieceCount} pieces</div>
        </div>
      </div>
    </div>
  );
}

function ShareVisual({ progress, t }) {
  const linkVisible = progress > 0.05;
  const copied = progress > 0.55;
  const sharePop = progress > 0.7;

  return (
    <div style={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 18 }}>
      {/* the link card */}
      <div style={{
        padding: '14px 16px', background: '#fff',
        border: `2px solid ${PP.rule}`,
        borderRadius: 10, display: 'flex', alignItems: 'center', gap: 12,
        opacity: linkVisible ? 1 : 0,
        transform: linkVisible ? 'translateY(0)' : 'translateY(12px)',
        transition: 'all 320ms ease',
      }}>
        <div style={{
          fontFamily: FF.mono, fontSize: 15, color: PP.ink, flex: 1,
          overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis',
        }}>
          picturepledge.com/<span style={{ color: PP.orange, fontWeight: 700 }}>albion-u12</span>
        </div>
        <div style={{
          padding: '7px 14px', borderRadius: 7,
          background: copied ? PP.green : PP.ink,
          color: '#fff', fontFamily: FF.ui, fontSize: 13, fontWeight: 600,
          display: 'flex', alignItems: 'center', gap: 6,
          transition: 'background 220ms',
        }}>
          {copied ? '✓ Copied' : 'Copy'}
        </div>
      </div>

      {/* share targets pop in */}
      <div style={{
        display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10,
        opacity: sharePop ? 1 : 0,
        transform: sharePop ? 'translateY(0)' : 'translateY(8px)',
        transition: 'all 360ms ease 80ms',
      }}>
        {[
          { label: 'Email roster', icon: '@', color: '#2b6cb0' },
          { label: 'Text message', icon: '✉', color: '#38a169' },
          { label: 'Facebook', icon: 'ƒ', color: '#1877f2' },
          { label: 'QR code', icon: '▦', color: PP.ink },
        ].map((s, i) => (
          <div key={i} style={{
            padding: '12px 14px', background: '#fff', border: `1px solid ${PP.rule}`,
            borderRadius: 8, display: 'flex', alignItems: 'center', gap: 10,
            fontFamily: FF.ui, fontSize: 14, fontWeight: 600, color: PP.ink,
            opacity: sharePop ? 1 : 0,
            transform: sharePop ? 'translateY(0)' : `translateY(${10 + i*4}px)`,
            transition: `all 320ms ease ${120 + i*70}ms`,
          }}>
            <div style={{
              width: 28, height: 28, borderRadius: 6, background: s.color,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              color: '#fff', fontWeight: 800, fontSize: 16,
            }}>{s.icon}</div>
            {s.label}
          </div>
        ))}
      </div>

      {/* go-live button */}
      <div style={{
        marginTop: 12,
        padding: '16px 20px', background: progress > 0.85 ? PP.orange : PP.rule,
        color: progress > 0.85 ? '#fff' : PP.inkSoft,
        borderRadius: 10, fontFamily: FF.display, fontWeight: 700, fontSize: 20,
        textAlign: 'center', letterSpacing: '-0.01em',
        transition: 'background 280ms, color 280ms',
        boxShadow: progress > 0.9 ? '0 8px 24px rgba(255,107,53,0.3)' : 'none',
        transform: progress > 0.92 ? 'scale(1.02)' : 'scale(1)',
      }}>
        {progress > 0.95 ? '🎉 Campaign live!' : 'Launch campaign →'}
      </div>
    </div>
  );
}

// ─── SCENE 3 — CAMPAIGN PAGE + REVEAL (10 → 27s) ──────────────────────────
function SceneCampaign({ revealStart, donations }) {
  const { localTime, duration } = useSprite();
  const inOp = Easing.easeOutCubic(clamp(localTime / 0.5, 0, 1));
  const outOp = 1 - Easing.easeInCubic(clamp((localTime - (duration - 0.4)) / 0.4, 0, 1));
  const op = inOp * outOp;

  // donations is array sorted by `time` (within this scene's local time).
  // Compute current total + revealed pieces.
  const GOAL = 5000;
  const TOTAL_PIECES = 250;

  const elapsedDonations = donations.filter(d => d.time <= localTime);
  const raised = elapsedDonations.reduce((s, d) => s + d.amount, 0);
  const cappedRaised = Math.min(raised, GOAL);
  const rawRevealed = Math.floor((cappedRaised / GOAL) * TOTAL_PIECES);

  // Smoothly animate the revealed count between discrete donation events
  // for visual continuity (don't lock to step changes)
  const lastDonationT = elapsedDonations.length ? elapsedDonations[elapsedDonations.length - 1].time : 0;
  const tSinceLast = localTime - lastDonationT;
  const easeOutBurst = clamp(tSinceLast / 0.6, 0, 1);

  // Find pieces revealed BEFORE last donation
  const priorRaised = elapsedDonations.slice(0, -1).reduce((s, d) => s + d.amount, 0);
  const priorRevealed = Math.floor((Math.min(priorRaised, GOAL) / GOAL) * TOTAL_PIECES);
  const targetRevealed = rawRevealed;
  const animatedRevealed = Math.floor(priorRevealed + (targetRevealed - priorRevealed) * easeOutBurst);

  // After last donation, snap to fully revealed if at goal
  const showFinalBurst = localTime > 11; // last donation hits goal around 10-11s
  const revealedPieces = showFinalBurst ? TOTAL_PIECES : animatedRevealed;

  // Active donor cards (visible for ~2.2s after their time)
  const activeDonors = elapsedDonations.filter(d => localTime - d.time < 2.2);

  // Leaderboard: latest 6 donations
  const leaderboard = elapsedDonations.slice().reverse().slice(0, 6);

  const browserW = 1700, browserH = 940;
  const photoW = 1040, photoH = 720;

  return (
    <div style={{
      position: 'absolute', inset: 0, background: PP.cream, opacity: op,
    }}>
      <BrowserChrome
        width={browserW} height={browserH}
        url="picturepledge.com/albion-u12"
        x={(1920 - browserW)/2} y={(1080 - browserH)/2}
      >
        <CampaignBody
          photoW={photoW} photoH={photoH}
          revealedPieces={revealedPieces}
          totalPieces={TOTAL_PIECES}
          raised={cappedRaised}
          goal={GOAL}
          leaderboard={leaderboard}
        />
      </BrowserChrome>

      {/* Floating donor name cards */}
      {activeDonors.map((d, i) => {
        const age = localTime - d.time;
        const inP = clamp(age / 0.35, 0, 1);
        const outP = clamp((age - 1.6) / 0.6, 0, 1);
        const op = Easing.easeOutCubic(inP) * (1 - Easing.easeInCubic(outP));
        // slide from right
        const x = interpolate([0, 0.5, 1.6, 2.2], [80, 0, -10, -80], Easing.easeOutCubic)(age);
        const y = d.lane * 84;
        return (
          <div key={`donor-${i}-${d.time}`} style={{
            position: 'absolute',
            right: 80 + x, top: 200 + y,
            opacity: op,
            background: '#fff',
            borderRadius: 12,
            padding: '12px 18px',
            boxShadow: '0 12px 32px rgba(22,17,13,0.18), 0 0 0 1px rgba(22,17,13,0.04)',
            display: 'flex', alignItems: 'center', gap: 14,
            fontFamily: FF.ui,
            minWidth: 280,
            borderLeft: `4px solid ${PP.orange}`,
          }}>
            <div style={{
              width: 40, height: 40, borderRadius: 20,
              background: `linear-gradient(135deg, ${PP.orange}, ${PP.orangeWarm})`,
              color: '#fff', fontFamily: FF.display, fontWeight: 800,
              fontSize: 18, display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              {d.name.split(' ').map(w => w[0]).slice(0,2).join('')}
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 15, fontWeight: 700, color: PP.ink, letterSpacing: '-0.01em' }}>
                {d.name}
              </div>
              <div style={{ fontSize: 12, color: PP.inkSoft, marginTop: 2 }}>
                just donated
              </div>
            </div>
            <div style={{
              fontFamily: FF.display, fontWeight: 800, fontSize: 24,
              color: PP.green,
            }}>
              ${d.amount}
            </div>
          </div>
        );
      })}

      {/* Final celebration overlay */}
      {showFinalBurst && <GoalReached t={localTime - 11}/>}
    </div>
  );
}

function CampaignBody({ photoW, photoH, revealedPieces, totalPieces, raised, goal, leaderboard }) {
  const pct = Math.floor((raised / goal) * 100);
  return (
    <div style={{
      position: 'absolute', inset: 0, padding: 36,
      display: 'grid', gridTemplateColumns: `${photoW}px 1fr`, gap: 36,
    }}>
      {/* Left: title + photo */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16, minWidth: 0 }}>
        <div>
          <div style={{
            display: 'inline-block', padding: '4px 10px',
            background: '#fff5ef', border: `1px solid ${PP.orange}33`,
            borderRadius: 5, fontFamily: FF.mono, fontSize: 12,
            color: PP.orangeDark, letterSpacing: '0.12em', textTransform: 'uppercase',
            fontWeight: 600,
          }}>Albion FC · U-12</div>
          <div style={{
            marginTop: 8, fontFamily: FF.display, fontWeight: 800, fontSize: 34,
            color: PP.ink, letterSpacing: '-0.02em',
          }}>State Tournament Travel</div>
        </div>
        <div style={{
          position: 'relative', width: photoW, height: photoH,
          borderRadius: 12, overflow: 'hidden',
          boxShadow: '0 12px 32px rgba(22,17,13,0.16)',
        }}>
          <TeamPhoto width={photoW} height={photoH}/>
          <PixelGrid
            cols={25} rows={10}
            width={photoW} height={photoH}
            revealCount={revealedPieces} seed={11}
          />
        </div>
      </div>

      {/* Right: stats + leaderboard */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 22, minWidth: 0 }}>
        {/* big raised number */}
        <div>
          <div style={{ display: 'baseline', display: 'flex', alignItems: 'baseline', gap: 12 }}>
            <div style={{
              fontFamily: FF.display, fontWeight: 800, fontSize: 56,
              color: PP.ink, letterSpacing: '-0.03em',
              fontVariantNumeric: 'tabular-nums',
            }}>${raised.toLocaleString()}</div>
            <div style={{
              fontFamily: FF.ui, color: PP.inkSoft, fontSize: 18,
            }}>of ${goal.toLocaleString()}</div>
          </div>
          <div style={{
            marginTop: 14, height: 14, background: PP.rule, borderRadius: 7,
            overflow: 'hidden', position: 'relative',
          }}>
            <div style={{
              height: '100%', width: `${pct}%`,
              background: `linear-gradient(90deg, ${PP.orange}, ${PP.orangeWarm})`,
              borderRadius: 7,
              transition: 'width 360ms cubic-bezier(0.22, 0.61, 0.36, 1)',
            }}/>
          </div>
          <div style={{
            marginTop: 10, display: 'flex', justifyContent: 'space-between',
            fontFamily: FF.mono, fontSize: 14, color: PP.inkSoft,
          }}>
            <span>{Math.floor((revealedPieces / totalPieces) * 100)}% revealed</span>
            <span>{revealedPieces} / {totalPieces} pieces</span>
          </div>
        </div>

        {/* donate button */}
        <div style={{
          padding: '14px 20px', background: PP.orange, color: '#fff',
          borderRadius: 10, fontFamily: FF.display, fontWeight: 700, fontSize: 22,
          textAlign: 'center', boxShadow: '0 6px 16px rgba(255,107,53,0.3)',
          letterSpacing: '-0.01em',
        }}>
          Donate · reveal more →
        </div>

        {/* leaderboard */}
        <div>
          <div style={{
            fontFamily: FF.ui, fontSize: 13, color: PP.inkSoft,
            fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 12,
          }}>Recent supporters</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {leaderboard.length === 0 && (
              <div style={{
                padding: 16, fontFamily: FF.ui, fontSize: 14, color: PP.inkSoft,
                background: PP.cream, borderRadius: 8, textAlign: 'center',
              }}>Be the first to donate →</div>
            )}
            {leaderboard.map((d, i) => (
              <div key={`lb-${i}`} style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '10px 14px',
                background: i === 0 ? '#fff5ef' : '#fff',
                border: `1px solid ${i === 0 ? PP.orange+'33' : PP.rule}`,
                borderRadius: 9,
                animation: i === 0 ? 'lbpulse 0.6s ease' : 'none',
              }}>
                <div style={{
                  width: 32, height: 32, borderRadius: 16,
                  background: i === 0 ? PP.orange : '#eee2d4',
                  color: i === 0 ? '#fff' : PP.inkSoft,
                  fontFamily: FF.display, fontWeight: 800, fontSize: 14,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  {d.name.split(' ').map(w => w[0]).slice(0,2).join('')}
                </div>
                <div style={{ flex: 1, fontFamily: FF.ui, fontSize: 15, fontWeight: 600, color: PP.ink }}>
                  {d.name}
                </div>
                <div style={{ fontFamily: FF.mono, fontSize: 15, fontWeight: 700, color: PP.green }}>
                  ${d.amount}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

function GoalReached({ t }) {
  if (t < 0) return null;
  const op = Easing.easeOutCubic(clamp(t / 0.5, 0, 1));
  const scale = interpolate([0, 0.5, 1.0], [0.7, 1.05, 1.0], [Easing.easeOutBack, Easing.easeInOutCubic])(t);

  // pulsing confetti dots
  const dots = React.useMemo(() => {
    const ds = [];
    for (let i = 0; i < 30; i++) {
      ds.push({
        x: 30 + Math.random() * 40,
        y: 30 + Math.random() * 40,
        c: i % 3 === 0 ? PP.orange : i % 3 === 1 ? PP.green : '#f1b340',
        d: Math.random() * 0.5,
        dx: (Math.random() - 0.5) * 600,
        dy: (Math.random() - 0.7) * 500,
      });
    }
    return ds;
  }, []);

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      pointerEvents: 'none',
    }}>
      {/* confetti */}
      {dots.map((d, i) => {
        const dt = clamp((t - d.d) / 1.4, 0, 1);
        const e = Easing.easeOutCubic(dt);
        return (
          <div key={i} style={{
            position: 'absolute',
            left: `${d.x}%`, top: `${d.y}%`,
            width: 14, height: 14, borderRadius: 3,
            background: d.c,
            opacity: (1 - dt) * 0.95,
            transform: `translate(${d.dx * e}px, ${d.dy * e + (e*e)*400}px) rotate(${e * 540}deg)`,
          }}/>
        );
      })}

      {/* center card */}
      <div style={{
        background: '#fff',
        borderRadius: 20,
        padding: '28px 56px',
        boxShadow: '0 30px 80px rgba(22,17,13,0.3)',
        opacity: op,
        transform: `scale(${scale})`,
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
        border: `3px solid ${PP.green}`,
      }}>
        <div style={{
          fontFamily: FF.mono, fontSize: 14, letterSpacing: '0.24em',
          textTransform: 'uppercase', color: PP.green, fontWeight: 700,
        }}>◆ Goal reached</div>
        <div style={{
          fontFamily: FF.display, fontWeight: 900, fontSize: 72,
          color: PP.ink, letterSpacing: '-0.03em',
        }}>$5,000</div>
        <div style={{
          fontFamily: FF.ui, fontSize: 20, color: PP.inkSoft, fontWeight: 600,
        }}>100% revealed · 38 supporters</div>
      </div>
    </div>
  );
}

// ─── SCENE 5 — CTA (27 → 30s) ──────────────────────────────────────────────
function SceneCTA() {
  const { localTime, duration } = useSprite();
  const inOp = Easing.easeOutCubic(clamp(localTime / 0.5, 0, 1));
  const sub = clamp((localTime - 0.5) / 0.5, 0, 1);
  const link = clamp((localTime - 1.0) / 0.5, 0, 1);

  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: PP.cream,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      flexDirection: 'column', gap: 36,
    }}>
      <div style={{
        opacity: inOp, transform: `translateY(${(1-inOp)*16}px) scale(${0.96 + 0.04 * inOp})`,
      }}>
        <Wordmark size={120}/>
      </div>
      <div style={{
        opacity: sub, transform: `translateY(${(1-sub)*12}px)`,
        fontFamily: FF.display, fontWeight: 700, fontSize: 56, color: PP.ink,
        letterSpacing: '-0.025em', textAlign: 'center', maxWidth: 1200,
      }}>
        Your team's next big trip,<br/>revealed one donation at a time.
      </div>
      <div style={{
        opacity: link, transform: `translateY(${(1-link)*8}px)`,
        marginTop: 20,
        display: 'flex', alignItems: 'center', gap: 18,
      }}>
        <div style={{
          padding: '18px 36px', background: PP.orange, color: '#fff',
          borderRadius: 12, fontFamily: FF.display, fontWeight: 700, fontSize: 28,
          letterSpacing: '-0.015em',
          boxShadow: '0 12px 32px rgba(255,107,53,0.32)',
        }}>
          Start your campaign →
        </div>
        <div style={{
          fontFamily: FF.mono, fontSize: 26, color: PP.ink, fontWeight: 600,
        }}>
          picturepledge.com
        </div>
      </div>
    </div>
  );
}

// ─── Donations script ──────────────────────────────────────────────────────
// times are LOCAL to the campaign scene (Scene 3). Scene 3 runs from 10s→27s in master timeline.
// We want donations to flow from ~1s to ~10s of local time, then goal at 10-11s.
const DONATIONS = [
  { time: 0.6,  name: 'Sarah Chen',           amount: 50,  lane: 0 },
  { time: 1.3,  name: 'Garcia Family',        amount: 100, lane: 1 },
  { time: 2.0,  name: 'Coach Martinez',       amount: 200, lane: 2 },
  { time: 2.7,  name: 'Anonymous',            amount: 25,  lane: 0 },
  { time: 3.3,  name: 'James Kowalski',       amount: 75,  lane: 1 },
  { time: 3.9,  name: 'Albion Alumni',         amount: 250, lane: 2 },
  { time: 4.5,  name: 'Davis Auto',           amount: 500, lane: 0 },
  { time: 5.2,  name: 'Patel Family',         amount: 100, lane: 1 },
  { time: 5.8,  name: 'Grandma Joyce',        amount: 40,  lane: 2 },
  { time: 6.4,  name: 'Tom Reyes',            amount: 60,  lane: 0 },
  { time: 7.0,  name: 'The Lin Family',       amount: 150, lane: 1 },
  { time: 7.6,  name: 'Nora Park',            amount: 75,  lane: 2 },
  { time: 8.2,  name: 'Hawkins Pizza Co.',    amount: 400, lane: 0 },
  { time: 8.8,  name: 'Maya Okafor',          amount: 200, lane: 1 },
  { time: 9.4,  name: 'The Booster Club',     amount: 700, lane: 2 },
  { time: 10.0, name: 'Coach Williams',       amount: 1500, lane: 0 },  // tips it over the top
  { time: 10.5, name: 'Anonymous',            amount: 75, lane: 1 },
];

// ─── Master App ────────────────────────────────────────────────────────────
function App() {
  // Update video root data-screen-label with current second so comments are timestamp-aware
  const labelRef = React.useRef(null);
  return (
    <div ref={labelRef} data-screen-label="00s" style={{ width: '100%', height: '100%' }}>
      <Stage width={1920} height={1080} duration={30} background={PP.cream} persistKey="picturepledge-explainer">
        {/* Update timestamp label */}
        <TimestampLabel rootRef={labelRef}/>

        {/* Scene 1: Landing-page hero 0→4s */}
        <Sprite start={0} end={4.2}><SceneHook/></Sprite>

        {/* Scene 2: Setup 4→10s */}
        <Sprite start={4} end={10.2}><SceneSetup/></Sprite>

        {/* Scene 3+4+5: Campaign launches + reveal + goal hit  10→27s */}
        <Sprite start={10} end={27.2}>
          <SceneCampaign donations={DONATIONS}/>
        </Sprite>

        {/* Scene 6: CTA 27→30s */}
        <Sprite start={27} end={30}><SceneCTA/></Sprite>

        {/* Global keyframes */}
        <style>{`
          @keyframes lbpulse {
            0% { transform: translateX(-12px); opacity: 0; }
            100% { transform: translateX(0); opacity: 1; }
          }
        `}</style>
      </Stage>
    </div>
  );
}

// Pin a data-screen-label="01s" etc. to the video root each second, so commenters
// get a clean timestamp reference.
function TimestampLabel({ rootRef }) {
  const t = useTime();
  React.useEffect(() => {
    if (rootRef.current) {
      const sec = String(Math.floor(t)).padStart(2, '0');
      rootRef.current.setAttribute('data-screen-label', `${sec}s`);
    }
  }, [Math.floor(t)]);
  return null;
}

window.App = App;
