// funnel-scenes.jsx — "§ 06 · the vetting funnel"
// 15s loop: 150 dots at tier 1 → sieve → 22 → sieve → 8 → sieve → 5 name cards
// Each sieve: horizontal moss line sweeps down, rejected dots fade & drift down.
// Final: 5 dots expand into editorial name-cards with italic-serif Greek initials.

const F_PALETTE = {
  cream:    '#f5f0e8',
  creamDk:  '#ebe4d6',
  ink:      '#0f1713',
  ink70:    'rgba(15,23,19,0.70)',
  ink55:    'rgba(15,23,19,0.55)',
  ink40:    'rgba(15,23,19,0.40)',
  ink25:    'rgba(15,23,19,0.25)',
  ink12:    'rgba(15,23,19,0.12)',
  ink08:    'rgba(15,23,19,0.08)',
  moss700:  '#32553f',
  moss600:  '#3f6b54',
  moss500:  '#4d7e65',
  moss400:  '#6fa37a',
  moss300:  '#93b89a',
  moss200:  '#b8cdbb',
  moss100:  '#d7e1d4',
  amber:    '#cd8a4b',
  amberSoft:'#e2a568',
  amberGlow:'rgba(205,138,75,0.35)',
};
const F_FONTS = {
  display: '"Space Grotesk", system-ui, sans-serif',
  serif:   '"Instrument Serif", Georgia, serif',
  mono:    '"JetBrains Mono", ui-monospace, monospace',
};

const fEase = (t) => 1 - Math.pow(1 - t, 3);
const fEaseIn = (t) => t * t;
const fSmooth = (t) => t * t * (3 - 2 * t);
const fClamp = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v));

// ─── Film grain ─────────────────────────────────────────────────────────
function FFilmGrain({ opacity = 0.07 }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none',
      opacity, mixBlendMode: 'multiply',
      backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.14  0 0 0 0 0.12  0 0 0 0 0.08  0 0 0 1 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>")`,
      backgroundSize: '220px 220px',
    }} />
  );
}

// ─── Corner chrome ──────────────────────────────────────────────────────
function FCornerChrome() {
  const time = useTime();
  const pulse = 0.5 + 0.5 * Math.sin(time * Math.PI * 1.1);

  return (
    <>
      <img
        src="assets/digital-heroes-logo.png"
        alt=""
        style={{
          position: 'absolute', left: 56, top: 44,
          height: 52, width: 'auto',
          opacity: 0.42,
          pointerEvents: 'none',
        }}
      />
      <div style={{
        position: 'absolute', right: 56, top: 58,
        fontFamily: F_FONTS.mono,
        fontSize: 12, letterSpacing: '0.16em',
        color: F_PALETTE.ink40,
        textTransform: 'uppercase',
      }}>
        vol. 01 — selection
      </div>
      <div style={{
        position: 'absolute', right: 56, bottom: 56,
        display: 'flex', alignItems: 'center', gap: 12,
        fontFamily: F_FONTS.mono,
        fontSize: 12, letterSpacing: '0.14em',
        color: F_PALETTE.ink70,
        textTransform: 'uppercase',
        opacity: 0.75,
      }}>
        <span>§ 06 · vetting funnel</span>
        <div style={{
          width: 8, height: 8, borderRadius: 4,
          background: F_PALETTE.moss500,
          opacity: 0.45 + 0.55 * pulse,
          boxShadow: `0 0 ${6 + 10*pulse}px ${F_PALETTE.moss400}`,
        }}/>
      </div>
      <div style={{
        position: 'absolute', left: 56, bottom: 56,
        fontFamily: F_FONTS.mono,
        fontSize: 12, letterSpacing: '0.14em',
        color: F_PALETTE.ink40,
      }}>
        fig.&nbsp;&nbsp;06·i — filtration
      </div>
    </>
  );
}

// ─── Timing map ─────────────────────────────────────────────────────────
// 0-2:   tier1 appear
// 2-5:   sieve1 → tier2
// 5-8:   sieve2 → tier3
// 8-11:  sieve3 → tier4 (5 dots)
// 11-14: name-card reveal + amber bloom
// 14-15: reverse

const TIER_APPEAR_END = 2.0;
const SIEVE1_START = 2.0, SIEVE1_END = 5.0;
const SIEVE2_START = 5.0, SIEVE2_END = 8.0;
const SIEVE3_START = 8.0, SIEVE3_END = 11.0;
const CARDS_START = 11.0, CARDS_END = 14.0;
const REVERSE_START = 14.0;

function reverseAlpha(t) {
  if (t < REVERSE_START) return 0;
  return (t - REVERSE_START) / (15 - REVERSE_START);
}

// ─── Dot grid layout ────────────────────────────────────────────────────
// All tiers are centered horizontally. We use a single large pool of 150 dots
// arranged in a compact grid. Each dot has a seeded random and a role:
// whether it survives each sieve (1, 2, 3) and at which tier-position.
// Layout is vertical — dots sit at ~y=380 initially, but we treat the funnel
// as 4 stacked destinations: tier1 center y=440, tier2 center y=500, etc.
// But actually, per brief, tier1 fills the TOP THIRD and each subsequent tier
// crystallizes LOWER. So: tier1 y=420, tier2 y=520, tier3 y=620, tier4 y=720.

const TIER_Y = [430, 570, 710, 830];

// Total dots and how many survive each sieve.
// 150 at tier 1, 22 at tier 2, 8 at tier 3, 5 at tier 4.
const TIER_COUNTS = [150, 22, 8, 5];

// Seeded RNG for deterministic dot layout
function seededRand(i, seed = 1) {
  const x = Math.sin(i * 12.9898 + seed * 78.233) * 43758.5453;
  return x - Math.floor(x);
}

// Generate the dot roster. Each dot has:
//   - id
//   - tier1Pos: {x,y} in a wide grid at top
//   - tier2Pos, tier3Pos, tier4Pos: the compact positions if the dot survives
//   - survives: which tier it survives to (1, 2, 3, or 4)
function buildDots() {
  const dots = [];
  const total = TIER_COUNTS[0]; // 150
  const cols = 25;
  const rows = Math.ceil(total / cols); // 6
  const xSpacing = 44;
  const ySpacing = 30;
  const gridW = (cols - 1) * xSpacing;
  const gridH = (rows - 1) * ySpacing;
  const t1cx = 960, t1cy = TIER_Y[0];
  const gridLeft = t1cx - gridW / 2;
  const gridTop = t1cy - gridH / 2;

  for (let i = 0; i < total; i++) {
    const col = i % cols;
    const row = Math.floor(i / cols);
    // Slight jitter so it looks organic, not a perfect grid
    const jx = (seededRand(i, 1) - 0.5) * 8;
    const jy = (seededRand(i, 2) - 0.5) * 8;
    dots.push({
      id: i,
      tier1: { x: gridLeft + col * xSpacing + jx, y: gridTop + row * ySpacing + jy },
    });
  }

  // Pick survivors. Pre-seed specific indices for tiers 2/3/4 so they look
  // scattered but well-spaced. Use a pseudo-random selection.
  // Shuffle indices by seeded rand and take first N as survivors at each stage.
  const order = Array.from({ length: total }, (_, i) => i)
    .sort((a, b) => seededRand(a, 7) - seededRand(b, 7));
  const tier2Survivors = order.slice(0, TIER_COUNTS[1]); // 22
  const tier3Survivors = tier2Survivors.slice(0, TIER_COUNTS[2]).sort((a,b) => seededRand(a, 11) - seededRand(b, 11)).slice(0, TIER_COUNTS[2]); // reshuffle pick of 8 from 22
  // actually: we want tier3 ⊂ tier2, tier4 ⊂ tier3
  const tier3FromTier2 = [...tier2Survivors].sort((a,b) => seededRand(a, 13) - seededRand(b, 13)).slice(0, TIER_COUNTS[2]);
  const tier4FromTier3 = [...tier3FromTier2].sort((a,b) => seededRand(a, 17) - seededRand(b, 17)).slice(0, TIER_COUNTS[3]);

  const t2Set = new Set(tier2Survivors);
  const t3Set = new Set(tier3FromTier2);
  const t4Set = new Set(tier4FromTier3);

  // Assign survives level
  dots.forEach((d) => {
    if (t4Set.has(d.id)) d.survives = 4;
    else if (t3Set.has(d.id)) d.survives = 3;
    else if (t2Set.has(d.id)) d.survives = 2;
    else d.survives = 1;
  });

  // Compute tier2/3/4 positions as compact centered rows.
  // Tier 2: 22 dots in 2 rows of 11
  const layoutTier = (ids, cols, cy) => {
    const rows = Math.ceil(ids.length / cols);
    // Wider spacing for the final 5-card tier so name cards (240px) don't overlap.
    let xSp;
    if (cols <= 5) xSp = 280;
    else if (cols > 10) xSp = 52;
    else xSp = 80;
    const ySp = 40;
    const w = (cols - 1) * xSp;
    const h = (rows - 1) * ySp;
    const left = 960 - w / 2;
    const top = cy - h / 2;
    return ids.map((id, k) => {
      const c = k % cols;
      const r = Math.floor(k / cols);
      return { id, x: left + c * xSp, y: top + r * ySp };
    });
  };

  const t2Layout = layoutTier(tier2Survivors, 11, TIER_Y[1]);
  const t3Layout = layoutTier(tier3FromTier2, 8, TIER_Y[2]);
  const t4Layout = layoutTier(tier4FromTier3, 5, TIER_Y[3]);

  const t2Map = new Map(t2Layout.map(p => [p.id, p]));
  const t3Map = new Map(t3Layout.map(p => [p.id, p]));
  const t4Map = new Map(t4Layout.map(p => [p.id, p]));

  dots.forEach(d => {
    d.tier2 = t2Map.get(d.id);
    d.tier3 = t3Map.get(d.id);
    d.tier4 = t4Map.get(d.id);
  });

  return dots;
}

const DOTS = buildDots();

// For each dot, compute its (x, y, radius, opacity) at time t.
// A dot that is rejected at sieve N fades & drifts down during SIEVE{N}_START..END.
// A surviving dot morphs its position from tierN to tier(N+1) during the sieve window.
function dotState(dot, t) {
  // Appearance (tier 1) animates in 0 → TIER_APPEAR_END with stagger
  const appearDelay = (dot.id / DOTS.length) * 0.9;
  const appearT = fClamp((t - appearDelay) / 0.8);
  let alpha = fEase(appearT);
  let pos = { ...dot.tier1 };
  let radius = 5.5;

  // Sieve 1: rejects non-survivors (survives === 1), morphs survivors to tier2
  if (t >= SIEVE1_START) {
    const k = fClamp((t - SIEVE1_START) / (SIEVE1_END - SIEVE1_START));
    if (dot.survives === 1) {
      // Rejected. Fade + drift down with stagger based on x-position (sweep left→right)
      const xStagger = (dot.tier1.x - 410) / 1100; // 0..1 by x
      const rejT = fClamp((k * 1.3) - xStagger * 0.5);
      alpha = (1 - fEase(rejT)) * alpha;
      pos.y += rejT * 280 + fEaseIn(rejT) * 120;
    } else {
      // Survives. Lerp to tier2 position with eased motion.
      const morphT = fSmooth(k);
      pos = {
        x: dot.tier1.x + (dot.tier2.x - dot.tier1.x) * morphT,
        y: dot.tier1.y + (dot.tier2.y - dot.tier1.y) * morphT,
      };
      radius = 5.5 + 1.2 * morphT;
    }
  }
  if (t >= SIEVE2_START && dot.survives >= 2) {
    const k = fClamp((t - SIEVE2_START) / (SIEVE2_END - SIEVE2_START));
    if (dot.survives === 2) {
      const xStagger = (dot.tier2.x - 650) / 600;
      const rejT = fClamp((k * 1.3) - xStagger * 0.5);
      alpha = (1 - fEase(rejT)) * alpha;
      pos = { x: dot.tier2.x, y: dot.tier2.y + rejT * 280 + fEaseIn(rejT) * 120 };
    } else {
      const morphT = fSmooth(k);
      pos = {
        x: dot.tier2.x + (dot.tier3.x - dot.tier2.x) * morphT,
        y: dot.tier2.y + (dot.tier3.y - dot.tier2.y) * morphT,
      };
      radius = 6.7 + 1.3 * morphT;
    }
  }
  if (t >= SIEVE3_START && dot.survives >= 3) {
    const k = fClamp((t - SIEVE3_START) / (SIEVE3_END - SIEVE3_START));
    if (dot.survives === 3) {
      const xStagger = (dot.tier3.x - 700) / 560;
      const rejT = fClamp((k * 1.3) - xStagger * 0.5);
      alpha = (1 - fEase(rejT)) * alpha;
      pos = { x: dot.tier3.x, y: dot.tier3.y + rejT * 220 + fEaseIn(rejT) * 120 };
    } else {
      const morphT = fSmooth(k);
      pos = {
        x: dot.tier3.x + (dot.tier4.x - dot.tier3.x) * morphT,
        y: dot.tier3.y + (dot.tier4.y - dot.tier3.y) * morphT,
      };
      radius = 8.0 + 1.5 * morphT;
    }
  }

  // During CARDS phase, the surviving 5 dots fade out as their card expands.
  if (t >= CARDS_START && dot.survives === 4) {
    const k = fClamp((t - CARDS_START) / 0.6);
    alpha *= (1 - fEase(k) * 0.9); // dots dim to 10%
  }

  return { pos, alpha, radius };
}

// ─── Dots layer ─────────────────────────────────────────────────────────
function DotsLayer() {
  const time = useTime();
  const rev = reverseAlpha(time);
  // During reverse, crossfade: old tier-04 state fades out, fresh tier-01 grid fades in.

  const renderDots = (t, maskAlpha) => (
    <svg
      width="1920" height="1080"
      viewBox="0 0 1920 1080"
      style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}
    >
      {DOTS.map((dot) => {
        const s = dotState(dot, t);
        const a = s.alpha * maskAlpha;
        if (a < 0.01) return null;
        const isSurvivor4 = dot.survives === 4;
        let fill = F_PALETTE.moss500;
        if (isSurvivor4 && time >= CARDS_START && time < REVERSE_START) {
          fill = F_PALETTE.moss600;
        }
        return (
          <circle
            key={dot.id}
            cx={s.pos.x}
            cy={s.pos.y}
            r={s.radius}
            fill={fill}
            opacity={a}
          />
        );
      })}
    </svg>
  );

  if (rev <= 0) {
    return renderDots(time, 1);
  }
  // Crossfade: frozen tier-04 state fades out; fresh tier-01 dots don't need to
  // appear during reverse since at loop start (t=0) they're invisible anyway.
  return renderDots(14, 1 - rev);
}

// ─── Sieve sweep lines ──────────────────────────────────────────────────
// A moss-green horizontal line that sweeps down through the tier being filtered.
function SieveSweep({ start, end, fromY, toY }) {
  const time = useTime();
  if (time < start - 0.1 || time > end + 0.2) return null;
  const k = fClamp((time - start) / (end - start));
  const y = fromY + (toY - fromY) * fEase(k);
  // Fade in quick, then fade out
  let alpha;
  if (k < 0.15) alpha = k / 0.15;
  else if (k > 0.85) alpha = (1 - k) / 0.15;
  else alpha = 1;
  alpha *= 0.85;

  return (
    <>
      {/* Sweep line */}
      <div style={{
        position: 'absolute',
        left: 260, top: y,
        width: 1400, height: 2,
        background: `linear-gradient(to right, transparent, ${F_PALETTE.moss500}, ${F_PALETTE.moss400}, ${F_PALETTE.moss500}, transparent)`,
        opacity: alpha,
        boxShadow: `0 0 16px ${F_PALETTE.moss300}`,
        transition: 'none',
      }}/>
      {/* Soft glow trail above the line */}
      <div style={{
        position: 'absolute',
        left: 260, top: y - 28,
        width: 1400, height: 30,
        background: `linear-gradient(to bottom, transparent, rgba(111,163,122,0.12))`,
        opacity: alpha * 0.6,
        pointerEvents: 'none',
        transition: 'none',
      }}/>
    </>
  );
}

// ─── Tier caption (the mono tier label + count) ─────────────────────────
// Captions swap at sieve endpoints. Each caption:
//   tier 01 · 15,000+ applicants
//   tier 02 · 2,100 passed portfolio review
//   tier 03 · 320 passed live-code test
//   tier 04 · 42 passed paid trial sprint
// After tier4 (at CARDS_START), caption becomes:
//   your named team · signed NDA · first commit in 72 hours
// Position: just below each tier's y

const TIER_CAPTIONS = [
  { num: '01', numText: '15,000+', body: 'applicants' },
  { num: '02', numText: '2,100',   body: 'passed portfolio review' },
  { num: '03', numText: '320',     body: 'passed live-code test' },
  { num: '04', numText: '42',      body: 'passed paid trial sprint' },
];

function currentTierIndex(t) {
  if (t < SIEVE1_END - 0.3) return 0;
  if (t < SIEVE2_END - 0.3) return 1;
  if (t < SIEVE3_END - 0.3) return 2;
  return 3;
}

function TierCaption() {
  const time = useTime();
  const rev = reverseAlpha(time);
  const idx = currentTierIndex(rev > 0 ? 0.5 : time);
  const cap = TIER_CAPTIONS[idx];

  // Caption placement per tier. Tier-01's 6-row grid is tall AND the section
  // title sits above it, so the caption lives BELOW the tier-01 grid.
  // Tiers 2/3/4 captions sit above their (smaller) clusters.
  const tierY = TIER_Y[idx];
  const clusterHalfHeight = [110, 45, 25, 25][idx];
  let captionY;
  if (idx === 0) captionY = tierY + clusterHalfHeight + 40;  // below tier-01 grid
  else           captionY = tierY - clusterHalfHeight - 110; // above the others

  // Fade-in on each caption change
  const boundaries = [0, SIEVE1_END - 0.3, SIEVE2_END - 0.3, SIEVE3_END - 0.3];
  const since = time - boundaries[idx];
  let alpha = fClamp(since / 0.4) * (1 - rev);

  // Fade OUT current caption in the 0.6s leading up to the next tier's crystallization
  // so it doesn't collide with survivors arriving in its row.
  const nextBoundary = idx < 3 ? boundaries[idx + 1] : CARDS_START - 0.3;
  const fadeOutStart = nextBoundary - 0.6;
  if (time >= fadeOutStart && time < nextBoundary) {
    alpha *= (1 - (time - fadeOutStart) / 0.6);
  }

  // Fade OUT tier-04 caption when cards begin arriving (they occupy the same row).
  if (idx === 3 && time >= CARDS_START - 0.3) {
    const fadeOut = fClamp((time - (CARDS_START - 0.3)) / 0.5);
    alpha *= (1 - fadeOut);
  }
  if (alpha < 0.01) return null;

  return (
    <div style={{
      position: 'absolute',
      left: 0, right: 0, top: captionY,
      textAlign: 'center',
      opacity: alpha,
      transition: 'none',
    }}>
      <div style={{
        fontFamily: F_FONTS.mono,
        fontSize: 12,
        letterSpacing: '0.22em',
        color: F_PALETTE.moss600,
        textTransform: 'uppercase',
        marginBottom: 10,
      }}>
        tier&nbsp;·&nbsp;{cap.num}
      </div>
      <div style={{
        fontFamily: F_FONTS.display,
        fontWeight: 500,
        fontSize: 40,
        letterSpacing: '-0.02em',
        color: F_PALETTE.ink,
        lineHeight: 1,
      }}>
        <span style={{
          fontFamily: F_FONTS.serif,
          fontStyle: 'italic',
          fontWeight: 400,
          color: F_PALETTE.moss600,
          fontSize: 52,
          letterSpacing: '-0.01em',
        }}>
          {cap.numText}
        </span>
        <span style={{
          fontFamily: F_FONTS.mono,
          fontSize: 16,
          letterSpacing: '0.1em',
          color: F_PALETTE.ink55,
          marginLeft: 16,
          textTransform: 'uppercase',
          verticalAlign: 'middle',
        }}>
          {cap.body}
        </span>
      </div>
    </div>
  );
}

// ─── Final 5 name cards ─────────────────────────────────────────────────
const NAMES = [
  { glyph: 'α', role: 'tech lead',       handle: 'alpha' },
  { glyph: 'β', role: 'senior shopify',  handle: 'beta' },
  { glyph: 'γ', role: 'senior front-end',handle: 'gamma' },
  { glyph: 'δ', role: 'qa + performance',handle: 'delta' },
  { glyph: 'ε', role: 'pm + delivery',   handle: 'epsilon' },
];

function NameCards() {
  const time = useTime();
  const rev = reverseAlpha(time);
  if (time < CARDS_START - 0.2) return null;

  return (
    <>
      {DOTS.filter(d => d.survives === 4)
        .sort((a, b) => a.tier4.x - b.tier4.x)
        .map((d, i) => {
          const delay = i * 0.12;
          const k = fClamp((time - (CARDS_START + delay)) / 0.7);
          if (k <= 0) return null;
          const alpha = fEase(k) * (1 - rev);
          const scale = 0.5 + 0.5 * fEase(k);
          const cx = d.tier4.x;
          const cy = d.tier4.y;
          const name = NAMES[i];

          return (
            <div key={d.id} style={{
              position: 'absolute',
              left: cx - 120, top: cy - 90,
              width: 240, height: 200,
              opacity: alpha,
              transform: `scale(${scale})`,
              transformOrigin: 'center center',
              pointerEvents: 'none',
              transition: 'none',
            }}>
              {/* Card */}
              <div style={{
                position: 'absolute', inset: 0,
                background: F_PALETTE.cream,
                border: `1px solid ${F_PALETTE.moss300}`,
                boxShadow: `0 10px 30px rgba(50,85,63,0.12), inset 0 0 0 4px ${F_PALETTE.cream}`,
                borderRadius: 2,
              }}/>
              {/* Tick mono label */}
              <div style={{
                position: 'absolute', left: 16, top: 14,
                fontFamily: F_FONTS.mono,
                fontSize: 10,
                letterSpacing: '0.18em',
                color: F_PALETTE.moss600,
                textTransform: 'uppercase',
              }}>
                hero · 0{i+1}
              </div>
              {/* Big glyph */}
              <div style={{
                position: 'absolute',
                left: 0, right: 0, top: 30,
                textAlign: 'center',
                fontFamily: F_FONTS.serif,
                fontStyle: 'italic',
                fontWeight: 400,
                fontSize: 92,
                lineHeight: 1,
                color: F_PALETTE.moss600,
                letterSpacing: '-0.02em',
              }}>
                {name.glyph}
              </div>
              {/* Handle */}
              <div style={{
                position: 'absolute',
                left: 0, right: 0, bottom: 46,
                textAlign: 'center',
                fontFamily: F_FONTS.display,
                fontWeight: 600,
                fontSize: 16,
                letterSpacing: '-0.01em',
                color: F_PALETTE.ink,
              }}>
                @{name.handle}
              </div>
              {/* Role */}
              <div style={{
                position: 'absolute',
                left: 0, right: 0, bottom: 22,
                textAlign: 'center',
                fontFamily: F_FONTS.mono,
                fontSize: 10,
                letterSpacing: '0.12em',
                color: F_PALETTE.ink55,
                textTransform: 'uppercase',
              }}>
                {name.role}
              </div>
              {/* Bottom accent */}
              <div style={{
                position: 'absolute', left: 16, right: 16, bottom: 12,
                height: 2, background: F_PALETTE.moss500,
              }}/>
            </div>
          );
        })}
    </>
  );
}

// ─── Amber bloom ────────────────────────────────────────────────────────
function AmberFunnelBloom() {
  const time = useTime();
  const rev = reverseAlpha(time);
  if (time < CARDS_START - 0.1) return null;

  const k = fClamp((time - CARDS_START) / 1.2);
  const alpha = fEase(k) * (1 - rev);
  const breathe = 0.85 + 0.15 * Math.sin(time * Math.PI * 1.5);

  return (
    <div style={{
      position: 'absolute',
      left: 960 - 420, top: TIER_Y[3] - 360,
      width: 840, height: 720, borderRadius: '50%',
      background: `radial-gradient(ellipse, ${F_PALETTE.amberGlow}, transparent 65%)`,
      opacity: alpha * breathe * 0.55,
      pointerEvents: 'none',
      transition: 'none',
    }}/>
  );
}

// ─── Final caption (below cards, during reveal) ─────────────────────────
function FinalCaption() {
  const time = useTime();
  const rev = reverseAlpha(time);
  const k = fClamp((time - (CARDS_START + 0.8)) / 0.6) * (1 - rev);
  if (k < 0.01) return null;

  return (
    <div style={{
      position: 'absolute',
      left: 0, right: 0, top: TIER_Y[3] + 130,
      textAlign: 'center',
      opacity: k,
      transition: 'none',
    }}>
      <div style={{
        fontFamily: F_FONTS.mono,
        fontSize: 13,
        letterSpacing: '0.2em',
        color: F_PALETTE.amber,
        textTransform: 'uppercase',
        marginBottom: 12,
        fontWeight: 600,
      }}>
        your named team
      </div>
      <div style={{
        fontFamily: F_FONTS.display,
        fontWeight: 400,
        fontSize: 20,
        color: F_PALETTE.ink55,
        letterSpacing: '-0.005em',
      }}>
        signed NDA&nbsp;&nbsp;·&nbsp;&nbsp;first commit in{' '}
        <span style={{
          fontFamily: F_FONTS.serif,
          fontStyle: 'italic',
          fontWeight: 400,
          color: F_PALETTE.amber,
          fontSize: 26,
        }}>
          72 hours
        </span>
      </div>
    </div>
  );
}

// ─── Section title at top ───────────────────────────────────────────────
function FunnelSectionTitle() {
  const time = useTime();
  const rev = reverseAlpha(time);
  const alpha = fClamp(time / 0.8) * (1 - rev);

  return (
    <div style={{
      position: 'absolute',
      left: 0, right: 0, top: 200,
      textAlign: 'center',
      opacity: alpha,
      transition: 'none',
    }}>
      <div style={{
        fontFamily: F_FONTS.mono,
        fontSize: 12,
        letterSpacing: '0.2em',
        color: F_PALETTE.moss600,
        textTransform: 'uppercase',
        marginBottom: 16,
      }}>
        § 06 · the vetting funnel
      </div>
      <div style={{
        fontFamily: F_FONTS.display,
        fontWeight: 600,
        fontSize: 58,
        lineHeight: 1,
        letterSpacing: '-0.035em',
        color: F_PALETTE.ink,
      }}>
        15,000 in.{' '}
        <span style={{
          fontFamily: F_FONTS.serif,
          fontStyle: 'italic',
          fontWeight: 400,
          color: F_PALETTE.moss600,
          letterSpacing: '-0.02em',
        }}>
          five
        </span>
        {' '}out.
      </div>
    </div>
  );
}

// ─── Main scene ─────────────────────────────────────────────────────────
function FunnelScene() {
  return (
    <>
      {/* Background tint */}
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(ellipse at 50% 45%, ${F_PALETTE.cream} 0%, ${F_PALETTE.creamDk} 100%)`,
      }}/>

      <FunnelSectionTitle />
      <AmberFunnelBloom />
      <DotsLayer />

      {/* Three sieve sweeps — line sweeps from just above current tier to just below */}
      <SieveSweep start={SIEVE1_START} end={SIEVE1_END} fromY={TIER_Y[0] - 30} toY={TIER_Y[1] - 20} />
      <SieveSweep start={SIEVE2_START} end={SIEVE2_END} fromY={TIER_Y[1] - 30} toY={TIER_Y[2] - 20} />
      <SieveSweep start={SIEVE3_START} end={SIEVE3_END} fromY={TIER_Y[2] - 30} toY={TIER_Y[3] - 20} />

      <TierCaption />
      <NameCards />
      <FinalCaption />

      <FFilmGrain opacity={0.07} />
      <FCornerChrome />
    </>
  );
}

Object.assign(window, {
  FunnelScene,
  F_PALETTE,
  F_FONTS,
});
