// V2 — "Unhinged Editorial" — uses Professor Curious app colors (#6947BF primary)
(() => {
  const { useState, useEffect, useRef } = React;
  const { SCREENS } = window;

  const V2 = {
    paper: '#F5F1FB',
    paperDark: '#E6DDF4',
    ink: '#1A1230',
    inkMuted: 'rgba(26,18,48,0.55)',
    inkFaint: 'rgba(26,18,48,0.12)',
    accent: '#6947BF',
    accent2: '#412785',
    tint: '#F0EDF9',
    hi: '#D7C8F0',
    gold: '#FFB038',
    font: '"Nunito", ui-sans-serif, system-ui, sans-serif',
    serif: '"DM Serif Display", Georgia, serif',
  };

  if (!document.getElementById('v2fonts')) {
    const l = document.createElement('link');
    l.id = 'v2fonts'; l.rel = 'stylesheet';
    l.href = 'https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=Nunito:wght@400;600;700;800;900&display=swap';
    document.head.appendChild(l);
  }

  // Professor Curious — inlined face SVG with animated eyes / glasses / mouth / head bob
  function ProfFace({ size = 140, mood = 'smug' }) {
    const [t, setT] = useState(0);
    useEffect(() => {
      let r; const s = performance.now();
      const tk = n => { setT((n - s) / 1000); r = requestAnimationFrame(tk); };
      r = requestAnimationFrame(tk); return () => cancelAnimationFrame(r);
    }, []);

    // Blink: closed for ~140ms every ~3.5s, with a smooth open/close
    const blinkPhase = (t % 3.5);
    const blinkAmt = blinkPhase < 0.07
      ? blinkPhase / 0.07            // closing
      : blinkPhase < 0.14
        ? 1 - (blinkPhase - 0.07) / 0.07  // opening
        : 0;                          // open
    // Pupil look: gentle drift left/right
    const lookX = Math.sin(t * 0.9) * 1.6;
    const lookY = Math.sin(t * 0.7 + 1) * 0.8;
    // Head bob & sway
    const bob = Math.sin(t * 1.5) * 1.2;
    const sway = Math.sin(t * 0.6) * 0.8;
    // Glasses jiggle (subtle springy nudge)
    const glassesNudge = Math.sin(t * 2.2) * 0.4;
    // Mouth scale (cheer = open and pulse)
    const mouthPulse = mood === 'cheer' ? 1 + Math.sin(t * 6) * 0.12 : 1;

    // Mood biases
    const moodBias = ({
      smug:     { tilt: -1.5, browL: 0,  browR: 0,  smile: 0 },
      happy:    { tilt:  0,   browL: 0,  browR: 0,  smile: 1 },
      cheer:    { tilt:  2,   browL: -1, browR: -1, smile: 1 },
      thinking: { tilt: -3,   browL: 0,  browR: -3, smile: -0.4 },
      eyebrow:  { tilt:  1,   browL: 0,  browR: -3, smile: 0.2 },
      squint:   { tilt:  0,   browL: 0,  browR: 0,  smile: 0.4 },
    }[mood]) || { tilt: 0, browL: 0, browR: 0, smile: 0 };

    const headRot = moodBias.tilt + sway;

    // Pupils — squash vertically as blink approaches 1
    const pupilSquash = 1 - blinkAmt * 0.95;
    const pupilWhiteSize = 1 - blinkAmt;
    // Mouth open size for cheer
    const mouthOpen = mood === 'cheer';

    return (
      <svg
        width={size}
        height={size * (152 / 165)}
        viewBox="0 0 165 152"
        style={{ display: 'inline-block', overflow: 'visible' }}
      >
        <g style={{ transform: `translate(0px, ${bob}px) rotate(${headRot}deg)`, transformOrigin: '82px 130px', transition: 'transform 80ms' }}>
          {/* Hair / head silhouette (silver halo) */}
          <path d="M75.2468 14.1209C66.7025 15.3524 59.9443 0.741291 45.2226 5.13789C30.5009 9.53459 33.4873 20.4393 28.9645 25.9226C24.4416 31.406 15.852 27.9861 12.2776 36.782C8.70317 45.5778 14.5649 47.1231 12.2776 52.9307C9.99027 58.7384 3.55417 59.6945 2.29477 67.2011C1.03534 74.7077 7.81247 81.4159 7.33307 86.8564C6.85367 92.2975 0 92.2975 0 96.8235C0 101.35 8.38797 103.935 10.3328 108.945C12.2776 113.955 5.59757 113.544 5.59757 117.053C5.59757 120.562 11.4128 122.807 13.8924 123.128C16.372 123.45 21.4184 122.653 21.4184 122.653C21.4184 122.653 20.9575 128.35 27.6767 132.44C34.396 136.531 45.2226 131.537 45.2226 131.537H120.915C120.915 131.537 127.531 136.236 132.494 134.769C137.457 133.302 137.849 127.812 141.539 126.344C145.23 124.876 148.042 126.037 150.957 125.844C153.873 125.65 157.722 121.92 156.955 120.818C156.187 119.716 154.383 120.562 153.361 117.053C152.34 113.544 164.575 106.804 164.575 103.819C164.575 100.833 159.12 98.9795 157.722 93.2935C156.324 87.6082 163.894 82.7905 163.421 73.4432C162.949 64.0959 155.556 64.0075 152.34 58.4691C149.123 52.9307 151.44 38.2305 149.123 34.2031C146.805 30.1758 143.971 29.0598 131.039 25.9226C118.107 22.7854 128.599 5.13789 106.748 0.741291C84.8958 -3.65537 83.791 12.8895 75.2468 14.1209Z" fill="#E3E3E3"></path>

          {/* Hair brow tuft accent */}
          <path d="M61.3589 45.1802C61.3589 45.1802 57.9957 41.2981 52.7314 43.821C47.4671 46.3439 45.2148 54.8183 45.2148 54.8183" stroke="#A9BBBF" strokeWidth="3.58488" strokeLinecap="round" strokeLinejoin="round" fill="none"></path>

          {/* Cheeks (peach blobs) */}
          <path d="M39.0015 90.9445C38.5132 87.1318 38.0112 77.1066 31.3821 74.7296C24.753 72.3526 17.178 77.8315 16.1345 84.388C15.0909 90.9445 15.5634 95.4085 19.4463 100.452C23.3291 105.496 31.235 107.981 35.5922 105.496C39.9495 103.011 39.4899 94.7575 39.0015 90.9445Z" fill="#F2A37E"></path>
          <path d="M24.2412 85.0469C24.2412 85.0469 27.4516 85.6242 29.5888 88.5665C31.726 91.5085 33.2869 96.0675 33.2869 96.0675" stroke="#D77052" strokeWidth="2.15092" strokeLinecap="round" strokeLinejoin="round" fill="none"></path>
          <path d="M126.588 90.9445C127.076 87.1318 127.578 77.1066 134.208 74.7296C140.837 72.3526 148.412 77.8315 149.455 84.388C150.499 90.9445 150.026 95.4085 146.143 100.452C142.26 105.496 134.355 107.981 129.997 105.496C125.64 103.011 126.1 94.7575 126.588 90.9445Z" fill="#F2A37E"></path>
          <path d="M141.344 85.0469C141.344 85.0469 138.134 85.6242 135.996 88.5665C133.859 91.5085 132.298 96.0675 132.298 96.0675" stroke="#D77052" strokeWidth="2.15092" strokeLinecap="round" strokeLinejoin="round" fill="none"></path>

          {/* Skin (face shape) */}
          <path d="M36.0515 57.8142C36.7385 45.0267 46.4582 37.6214 58.6423 40.6028C58.6423 40.6028 80.0275 45.8354 80.4073 45.8354C80.7193 45.8354 104.847 40.6028 104.847 40.6028C118.583 37.6236 129.405 46.219 129.975 60.5617L131.518 99.4105C132.482 123.671 114.449 142.868 90.6959 142.868H74.0311C50.2 142.868 32.5089 123.763 33.8155 99.4395L36.0515 57.8142Z" fill="#FCC59E"></path>

          {/* Glasses + eyes — group jiggles slightly */}
          <g style={{ transform: `translate(0px, ${glassesNudge}px)`, transformOrigin: '82px 80px', transition: 'transform 60ms' }}>
            {/* Right (viewer's right) — purple frame */}
            <path d="M106.752 60.6953C117.91 60.6956 126.958 69.7829 126.958 80.9961C126.958 92.2095 117.91 101.298 106.752 101.298C95.5927 101.298 86.5444 92.2095 86.5444 80.9961C86.5446 69.7828 95.5937 60.6953 106.752 60.6953Z" fill="#4D0084" stroke="#4D0084" strokeWidth="0.716975"></path>
            {/* Left (viewer's left) — purple frame */}
            <path d="M57.8242 60.6953C68.9822 60.6956 78.0301 69.7829 78.0303 80.9961C78.0303 92.2095 68.9823 101.298 57.8242 101.298C46.666 101.298 37.6172 92.2095 37.6172 80.9961C37.6174 69.7828 46.6661 60.6953 57.8242 60.6953Z" fill="#4D0084" stroke="#4D0084" strokeWidth="0.716975"></path>
            {/* Bridge */}
            <path d="M77.5918 79.3805C79.0497 78.8975 80.6327 78.6328 82.2873 78.6328C83.9418 78.6328 85.5249 78.8975 86.9827 79.3805" stroke="#4D0084" strokeWidth="5.01883" fill="#4D0084"></path>

            {/* Right lens (white) */}
            <path d="M106.753 64.7031C115.761 64.7031 123.063 72.0055 123.063 81.0133C123.063 90.0215 115.761 97.3235 106.753 97.3235C97.7448 97.3235 90.4424 90.0215 90.4424 81.0133C90.4424 72.0055 97.7448 64.7031 106.753 64.7031Z" fill="white"></path>
            {/* Left lens (white) */}
            <path d="M57.8209 64.7031C66.8287 64.7031 74.1311 72.0055 74.1311 81.0133C74.1311 90.0215 66.8287 97.3235 57.8209 97.3235C48.813 97.3235 41.5107 90.0215 41.5107 81.0133C41.5107 72.0055 48.813 64.7031 57.8209 64.7031Z" fill="white"></path>

            {/* Eyes — clipped to lens area, animated pupils */}
            <defs>
              <clipPath id="pf_lensR">
                <circle cx="106.753" cy="81.0133" r="16.31" />
              </clipPath>
              <clipPath id="pf_lensL">
                <circle cx="57.8209" cy="81.0133" r="16.31" />
              </clipPath>
            </defs>

            {/* Right eye (viewer's right ≈ left side of head from prof's POV) */}
            <g clipPath="url(#pf_lensR)">
              <g style={{ transform: `translate(${lookX}px, ${lookY}px)` }}>
                <ellipse cx="106.5" cy="81" rx="9.9" ry={9.9 * pupilSquash} fill="#414141" />
                <circle cx="102.5" cy="78" r={2.3 * pupilWhiteSize} fill="white" />
              </g>
            </g>
            {/* Left eye */}
            <g clipPath="url(#pf_lensL)">
              <g style={{ transform: `translate(${lookX}px, ${lookY}px)` }}>
                <ellipse cx="58.5" cy="81" rx="9.9" ry={9.9 * pupilSquash} fill="#414141" />
                <circle cx="62.2" cy="78" r={2.3 * pupilWhiteSize} fill="white" />
              </g>
            </g>

            {/* Closed-eye lashes — drawn when blinking; clipped to lens */}
            {blinkAmt > 0.6 && (
              <g stroke="#414141" strokeWidth="2" strokeLinecap="round" fill="none" opacity={(blinkAmt - 0.6) / 0.4}>
                <path d="M 98 81 Q 106.5 84 115 81" />
                <path d="M 49.3 81 Q 57.8 84 66.3 81" />
              </g>
            )}
          </g>

          {/* Mustache pieces */}
          <path d="M47.6256 53.8332C47.0082 56.5346 48.5909 58.2001 50.164 59.4808C51.737 60.7615 54.1064 62.0451 59.4215 63.0971C64.7367 64.1491 67.4982 63.6948 69.5816 63.0971C71.6648 62.4993 75.0221 59.4808 75.2413 56.5346C75.4605 53.5884 70.9977 51.3483 62.2513 50.0884C53.5048 48.8285 48.243 51.1318 47.6256 53.8332Z" fill="#E3E3E3"></path>
          <path d="M117.125 53.8332C117.742 56.5346 116.159 58.2001 114.586 59.4808C113.013 60.7615 110.644 62.0451 105.329 63.0971C100.014 64.1491 97.2518 63.6948 95.1688 63.0971C93.0848 62.4993 89.7279 59.4808 89.5087 56.5346C89.2896 53.5884 93.7528 51.3483 102.499 50.0884C111.245 48.8285 116.507 51.1318 117.125 53.8332Z" fill="#E3E3E3"></path>

          {/* Beard (gray fluff) */}
          <path d="M59.2173 112.186C48.2955 115.244 46.5938 123.38 46.5938 132.02C46.5938 140.661 50.7876 143.808 55.5854 144.463C60.3833 145.117 64.6619 140.661 64.6619 140.661C64.6619 140.661 67.3528 151.638 82.5677 151.638C97.7828 151.638 100.41 140.661 100.41 140.661C100.41 140.661 102.842 145.586 109.836 144.172C116.831 142.758 118.175 130.7 118.175 130.7C118.175 130.7 119.495 121.64 112.229 115.303C104.964 108.966 70.139 109.128 59.2173 112.186Z" fill="#D9D9D9"></path>
          {/* Beard inner shadow */}
          <path d="M68.6904 99.8733C65.4853 100.776 59.2325 101.727 55.8698 103.485C52.507 105.243 48.5416 109.276 47.1172 111.187C45.6928 113.098 45.1257 115.325 45.653 116.537C46.1802 117.748 48.8791 119.468 49.4737 119.789C50.0682 120.111 52.2922 121.57 54.4607 121.57C56.6293 121.57 60.9342 119.567 62.4855 118.472C64.0367 117.378 68.6904 114.093 68.6904 114.093C68.6904 114.093 72.4563 117.378 75.4832 117.378C78.5101 117.378 82.3414 114.093 82.3414 114.093C82.3414 114.093 86.4926 117.468 89.9325 117.468C93.3728 117.468 97.2738 112.599 97.2738 112.599C97.2738 112.599 99.7208 116.607 101.488 118.038C103.256 119.468 105.461 120.883 108.522 121.051C111.583 121.219 117.309 119.539 118.714 118.038C120.118 116.537 119.717 114.461 118.714 112.999C117.711 111.536 116.672 110.477 114.015 107.529C111.357 104.581 102.77 101.498 97.2738 99.8733C91.7778 98.2473 71.8956 98.9703 68.6904 99.8733Z" fill="#E3E3E3"></path>

          {/* Mouth — animated */}
          <g style={{
            transform: `translate(0px, ${mouthOpen ? -1 : 0}px) scale(${mouthOpen ? mouthPulse : 1}, ${mouthOpen ? mouthPulse * 1.1 : 1 + moodBias.smile * 0.15})`,
            transformOrigin: '82px 100px', transition: 'transform 100ms',
          }}>
            <path d="M82.4939 86.125C76.8865 86.125 75.0403 90.7255 73.9597 92.4605C72.8789 94.1955 67.9226 101.131 69.7279 106.887C71.533 112.642 80.7745 112.643 82.4939 112.643C84.2133 112.643 91.9218 112.099 94.9108 107.511C97.8998 102.922 91.9218 94.1955 91.0133 92.4605C90.1051 90.7255 88.1014 86.125 82.4939 86.125Z" fill="#F07A61"></path>
            <path d="M92.7118 107.216C92.7118 108.547 88.1557 111.275 82.5355 111.275C76.9154 111.275 72.3594 108.547 72.3594 107.216C72.3594 103.41 76.9154 107.596 82.5355 107.596C88.1557 107.596 92.7118 103.791 92.7118 107.216Z" fill="#CF6A55"></path>
            <path d="M77.5984 107.859C79.1485 108.396 80.2625 109.262 80.0865 109.792C79.9105 110.323 78.5111 110.318 76.961 109.781C75.4109 109.244 74.2969 108.378 74.4729 107.847C74.6489 107.317 76.0483 107.322 77.5984 107.859Z" fill="#680C0C"></path>
            <path d="M87.2896 107.828C88.8118 107.213 90.2091 107.137 90.4107 107.659C90.6122 108.18 89.5416 109.101 88.0194 109.716C86.4972 110.331 85.0999 110.407 84.8984 109.886C84.6968 109.364 85.7674 108.443 87.2896 107.828Z" fill="#680C0C"></path>
          </g>
        </g>
      </svg>
    );
  }

  function Squiggle({ width = 80, color = V2.accent, thick = 3 }) {
    return <svg width={width} height="10" viewBox="0 0 80 10" className="pc-breath" style={{ display: 'block' }}>
      <path d="M 2 5 Q 12 0 22 5 T 42 5 T 62 5 T 78 5" stroke={color} strokeWidth={thick} fill="none" strokeLinecap="round" />
    </svg>;
  }

  function PaperProgress({ value, total }) {
    const pct = (value / total) * 100;
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{
          flex: 1, height: 10, position: 'relative',
          background: `repeating-linear-gradient(90deg, ${V2.inkFaint} 0 8px, transparent 8px 14px)`,
          borderRadius: 100,
        }}>
          <div style={{
            position: 'absolute', inset: 0, width: `${pct}%`, height: '100%',
            background: V2.accent, borderRadius: 100,
            transition: 'width 420ms cubic-bezier(.2,.8,.2,1)',
          }} />
        </div>
        <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 14, color: V2.ink }}>ch. {value}</div>
      </div>
    );
  }

  function StampBtn({ children, onClick, disabled, variant = 'primary' }) {
    const [p, setP] = useState(false);
    const isPrim = variant === 'primary';
    return (
      <button onClick={onClick} disabled={disabled}
        onMouseDown={() => setP(true)} onMouseUp={() => setP(false)} onMouseLeave={() => setP(false)}
        style={{
          width: '100%', padding: '18px 20px', border: `2.5px solid ${V2.ink}`, borderRadius: 14,
          background: isPrim ? V2.accent : V2.paper, color: isPrim ? '#fff' : V2.ink,
          fontFamily: V2.font, fontWeight: 900, fontSize: 16, letterSpacing: 0.3,
          textTransform: 'uppercase', cursor: disabled ? 'not-allowed' : 'pointer',
          opacity: disabled ? 0.4 : 1,
          boxShadow: p ? `2px 2px 0 ${V2.ink}` : `5px 5px 0 ${V2.ink}`,
          transform: p ? 'translate(3px, 3px)' : 'translate(0, 0)',
          transition: 'all 90ms',
        }}>{children}</button>
    );
  }

  function Note({ children, on, onClick, tilt = 0, bg = '#fff' }) {
    return (
      <button onClick={onClick} style={{
        width: '100%', padding: '14px 16px', marginBottom: 10,
        background: on ? V2.hi : bg,
        border: `2.5px solid ${on ? V2.accent : V2.ink}`, borderRadius: 12,
        transform: `rotate(${tilt}deg)`,
        boxShadow: on ? `3px 3px 0 ${V2.accent}` : `2px 2px 0 ${V2.ink}`,
        cursor: 'pointer', textAlign: 'left', fontFamily: V2.font,
        fontWeight: 800, fontSize: 15, color: V2.ink,
        display: 'flex', alignItems: 'center', gap: 12, transition: 'all 140ms',
      }}>{children}</button>
    );
  }

  // App shell. Three rows that own their own positioning so it feels like a
  // clean website on any device: sticky progress at top, scrolling content,
  // sticky CTA at bottom — no paper-grain background, no phone-style chrome.
  function Screen({ idx, total, children, onBack, cta, showProg = true, contentPad = '20px 22px 24px' }) {
    return (
      <div style={{
        width: '100%', height: '100%',
        background: '#FFFFFF', fontFamily: V2.font, color: V2.ink,
        display: 'flex', flexDirection: 'column', minHeight: 0,
      }}>
        {showProg && (
          <div style={{
            display: 'flex', alignItems: 'center', gap: 12,
            padding: '12px 18px',
            paddingTop: 'max(12px, env(safe-area-inset-top))',
            background: '#FFFFFF',
            borderBottom: `1px solid ${V2.inkFaint}`,
            flexShrink: 0,
          }}>
            {onBack && idx > 0 ? (
              <button onClick={onBack} aria-label="Back" style={{
                width: 32, height: 32, borderRadius: 8, border: `2px solid ${V2.ink}`,
                background: '#FFFFFF', color: V2.ink, cursor: 'pointer',
                fontSize: 16, fontWeight: 900, padding: 0, lineHeight: 1, fontFamily: V2.font,
                flexShrink: 0,
              }}>←</button>
            ) : <div style={{ width: 32, flexShrink: 0 }} />}
            <div style={{ flex: 1, minWidth: 0 }}><PaperProgress value={idx + 1} total={total} /></div>
          </div>
        )}
        <div className="hide-sb" style={{
          flex: 1, overflowY: 'auto', WebkitOverflowScrolling: 'touch',
          padding: contentPad, position: 'relative', minHeight: 0,
        }}>{children}</div>
        {cta && (
          <div style={{
            padding: '12px 18px',
            paddingBottom: 'max(12px, env(safe-area-inset-bottom))',
            background: '#FFFFFF',
            borderTop: `1px solid ${V2.inkFaint}`,
            boxShadow: '0 -10px 28px rgba(26,18,48,0.04)',
            flexShrink: 0,
          }}>{cta}</div>
        )}
      </div>
    );
  }

  // Back-compat: old call-sites used <StickyCTA> at the bottom of content.
  // The new shell pulls CTAs into a real sticky footer via Screen's `cta` prop,
  // so this just renders inline as a fallback if something still reaches for it.
  function StickyCTA({ children }) {
    return <div style={{ marginTop: 12 }}>{children}</div>;
  }

  function Kicker({ children }) {
    return <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13, color: V2.accent, letterSpacing: 0.4, marginBottom: 4 }}>— {children} —</div>;
  }
  function Title({ children, size = 36 }) {
    return <h1 style={{ fontFamily: V2.serif, fontWeight: 400, fontSize: size, lineHeight: 1.02, margin: '0 0 12px', color: V2.ink, letterSpacing: -0.5 }}>{children}</h1>;
  }
  function Lede({ children }) {
    return <p style={{ fontFamily: V2.font, fontWeight: 600, fontSize: 15.5, lineHeight: 1.45, margin: '0 0 22px', color: V2.inkMuted }}>{children}</p>;
  }
  function Fade({ children, delay = 0, k }) {
    const [s, setS] = useState(false);
    useEffect(() => { setS(false); const t = setTimeout(() => setS(true), delay); return () => clearTimeout(t); }, [k]);
    return <div style={{ opacity: s ? 1 : 0, transform: s ? 'translateY(0) rotate(0)' : 'translateY(8px) rotate(-0.5deg)', transition: 'all 560ms cubic-bezier(.2,.8,.2,1)' }}>{children}</div>;
  }

  function S1({ idx, total, onNext }) {
    const subjects = ['AP Calc', 'O-Chem', 'SAT Math', 'Bio', 'Physics', 'Stats', 'Lit', 'Econ'];
    const [subjIdx, setSubjIdx] = useState(0);
    useEffect(() => {
      const t = setInterval(() => setSubjIdx(i => (i + 1) % subjects.length), 1600);
      return () => clearInterval(t);
    }, []);
    return (
      <Screen
        idx={idx} total={total}
        showProg={false}
        contentPad="14px 22px 24px"
        cta={<>
          <StampBtn onClick={onNext}>Get started</StampBtn>
          <div style={{ textAlign: 'center', marginTop: 10, fontSize: 13.5, color: V2.inkMuted, fontWeight: 700 }}>
            Already have an account? <span style={{ color: V2.accent, fontWeight: 900, textDecoration: 'underline', cursor: 'pointer' }}>Log in</span>
          </div>
        </>}
      >
        {/* Top-left rating sticker pill */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start', marginBottom: 6, position: 'relative', zIndex: 2 }}>
          <div style={{
            display: 'inline-flex', alignItems: 'center', gap: 8,
            background: '#fff', border: `1.5px solid ${V2.ink}`, borderRadius: 999,
            padding: '5px 11px 5px 6px', boxShadow: `1.5px 1.5px 0 ${V2.ink}`,
          }}>
            <span style={{
              width: 20, height: 20, borderRadius: '50%', background: V2.gold,
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 11, color: V2.ink, fontWeight: 900,
            }}>★</span>
            <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: 0.4, textTransform: 'uppercase', color: V2.ink }}>
              4.8 · 25k students
            </span>
          </div>
        </div>

        {/* subtle marginalia formulas */}
        <div style={{ position: 'absolute', top: 70, left: 14, fontFamily: V2.serif, fontStyle: 'italic', fontSize: 12, color: V2.inkMuted, transform: 'rotate(-8deg)', opacity: 0.6, pointerEvents: 'none' }}>
          E = mc<sup>2</sup>
        </div>
        <div style={{ position: 'absolute', top: 60, right: 18, fontFamily: V2.serif, fontStyle: 'italic', fontSize: 12, color: V2.inkMuted, transform: 'rotate(6deg)', opacity: 0.6, pointerEvents: 'none' }}>
          ∫ dx
        </div>

        <Fade delay={80}>
          <div style={{ textAlign: 'center', marginTop: 4, position: 'relative' }}>
            <span className="pc-twinkle" style={{ position: 'absolute', top: 8, left: '26%', fontSize: 18, color: V2.gold, animationDelay: '0s' }}>✦</span>
            <span className="pc-twinkle" style={{ position: 'absolute', top: 22, right: '22%', fontSize: 14, color: V2.accent, animationDelay: '0.6s' }}>✦</span>
            <ProfFace size={148} mood="smug" />
          </div>
        </Fade>
        <Fade delay={380}>
          <h1 style={{ fontFamily: V2.serif, fontWeight: 400, fontSize: 42, lineHeight: 0.98, textAlign: 'center', margin: '8px 0 4px', color: V2.ink, letterSpacing: -1 }}>
            Stuck on{' '}
            <span style={{
              display: 'inline-block', position: 'relative',
              fontStyle: 'italic', color: V2.accent,
              minWidth: 140, textAlign: 'left',
            }}>
              <span key={subjIdx} style={{ display: 'inline-block', animation: 'pcBob 0.5s ease-out' }}>{subjects[subjIdx]}?</span>
            </span>
            <br />
            <span style={{ fontStyle: 'italic' }}>We got you.</span>
          </h1>
        </Fade>
        <Fade delay={500}><div style={{ display: 'flex', justifyContent: 'center', margin: '4px 0 10px' }}><Squiggle width={80} /></div></Fade>
        <Fade delay={560}>
          <p style={{ textAlign: 'center', fontFamily: V2.font, fontWeight: 600, fontSize: 15, lineHeight: 1.4, color: V2.inkMuted, padding: '0 14px', margin: 0 }}>
            Snap any problem. Call Professor Curious. Get it explained until it <i>clicks</i>.
          </p>
        </Fade>

        {/* "how it works" kicker */}
        <Fade delay={620}>
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10, justifyContent: 'center',
            margin: '20px 0 10px',
          }}>
            <span style={{ width: 18, height: 1, background: V2.accent, opacity: 0.5 }} />
            <span style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13, color: V2.accent, fontWeight: 400 }}>how it works</span>
            <span style={{ width: 18, height: 1, background: V2.accent, opacity: 0.5 }} />
          </div>
        </Fade>

        {/* three numbered steps */}
        <Fade delay={680}>
          <div style={{ display: 'flex', alignItems: 'stretch', justifyContent: 'center', gap: 8 }}>
            {[
              { icon: '📸', label: 'Snap your\nproblem' },
              { icon: '📞', label: 'Call your\nprofessor' },
              { icon: '🎓', label: 'Ace the\nexam' },
            ].map((c, i) => (
              <div key={i} style={{
                flex: 1, padding: '10px 6px 12px', textAlign: 'center',
                background: '#fff', border: `1.5px solid ${V2.inkFaint}`, borderRadius: 12,
              }}>
                <div style={{ fontFamily: V2.serif, fontSize: 12, fontWeight: 400, color: V2.accent, letterSpacing: 0.5, marginBottom: 2 }}>
                  {String(i + 1).padStart(2, '0')}
                </div>
                <div style={{ fontSize: 22, marginBottom: 4, lineHeight: 1 }}>{c.icon}</div>
                <div style={{ fontFamily: V2.font, fontSize: 11.5, lineHeight: 1.2, color: V2.ink, fontWeight: 800, whiteSpace: 'pre-line' }}>{c.label}</div>
              </div>
            ))}
          </div>
        </Fade>

        {/* peer credibility strip */}
        <Fade delay={760}>
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10,
            margin: '14px 0 0', padding: '10px 12px',
            background: V2.tint, borderRadius: 10,
          }}>
            <span style={{ fontSize: 18, lineHeight: 1 }}>🎓</span>
            <div style={{ fontSize: 12, lineHeight: 1.35, color: V2.ink, fontWeight: 700 }}>
              Trusted by students at{' '}
              <span style={{ fontWeight: 900 }}>Stanford</span>,{' '}
              <span style={{ fontWeight: 900 }}>MIT</span>,{' '}
              <span style={{ fontWeight: 900 }}>UCLA</span> &{' '}
              <span style={{ fontWeight: 900 }}>NYU</span>.
            </div>
          </div>
        </Fade>
      </Screen>
    );
  }

  function S2({ idx, total, onNext, onBack, data, setData }) {
    const s = SCREENS[1]; const sel = data.goal ?? s.defaultSelected;
    const tilts = [-0.5, 0.6, -0.3, 0.4, -0.6, 0.3];
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext} disabled={sel === undefined}>{s.cta}</StampBtn>}>
        <Fade delay={60} k={idx}>
          <Kicker>chapter 2 · the goal</Kicker>
          <Title>What's your <span style={{ fontStyle: 'italic', color: V2.accent }}>biggest</span> goal?</Title>
          <Lede>{s.sub}</Lede>
        </Fade>
        {s.options.map((o, i) => (
          <Fade key={i} delay={120 + i * 45} k={idx}>
            <Note on={sel === i} onClick={() => setData({ ...data, goal: i })} tilt={tilts[i]}>
              <span className="pc-bob2" style={{ fontSize: 22, display: 'inline-block', animationDelay: `${i * 0.3}s` }}>{o.icon}</span>
              <span style={{ flex: 1 }}>{o.label}</span>
              {sel === i && <span style={{ fontSize: 20, color: V2.accent }}>✓</span>}
            </Note>
          </Fade>
        ))}
      </Screen>
    );
  }

  function S3({ idx, total, onNext, onBack, data, setData }) {
    const s = SCREENS[2]; const sel = data.pains ?? s.defaultSelected;
    const toggle = i => setData({ ...data, pains: sel.includes(i) ? sel.filter(x => x !== i) : [...sel, i] });
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext} disabled={sel.length === 0}>{s.cta}</StampBtn>}>
        <Fade delay={60} k={idx}>
          <Kicker>chapter 3 · the confession</Kicker>
          <Title>What makes studying <span style={{ fontStyle: 'italic' }}>hard</span>?</Title>
          <Lede>{s.sub}</Lede>
        </Fade>
        {s.options.map((o, i) => (
          <Fade key={i} delay={120 + i * 35} k={idx}>
            <Note on={sel.includes(i)} onClick={() => toggle(i)} tilt={i % 2 ? 0.4 : -0.4}>
              <span className="pc-bob2" style={{ fontSize: 20, display: 'inline-block', animationDelay: `${i * 0.35}s` }}>{o.icon}</span>
              <span style={{ flex: 1 }}>{o.label}</span>
            </Note>
          </Fade>
        ))}
      </Screen>
    );
  }

  function S4({ idx, total, onNext, onBack, data }) {
    const s = SCREENS[3];
    const reviews = pickReviews(data, 3);
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext}>{s.cta}</StampBtn>}>
        <Fade delay={60} k={idx}>
          <Kicker>field reports</Kicker>
          <Title size={34}>You're <span style={{ fontStyle: 'italic' }}>not</span> alone.</Title>
        </Fade>
        <Fade delay={180} k={idx}>
          <div style={{ fontFamily: V2.serif, fontSize: 72, lineHeight: 1, color: V2.ink, letterSpacing: -2, margin: '2px 0 4px' }}>
            25,000<span style={{ color: V2.accent }}>+</span>
          </div>
          <div style={{ fontSize: 14, fontWeight: 700, color: V2.inkMuted, marginBottom: 14 }}>students already study smarter with Professor Curious.</div>
          <div style={{
            display: 'inline-flex', alignItems: 'center', gap: 8,
            padding: '6px 12px', marginBottom: 18,
            border: `1.5px dashed ${V2.accent}`, borderRadius: 100, background: V2.tint,
            fontSize: 11, fontWeight: 800, letterSpacing: 0.4, textTransform: 'uppercase', color: V2.ink,
          }}>
            <span style={{ fontSize: 13 }}>📂</span> stories from students like you
          </div>
        </Fade>
        {reviews.map((t, i) => (
          <Fade key={i} delay={260 + i * 120} k={idx}>
            <div className="pc-sway" style={{
              position: 'relative', padding: '16px 18px', marginBottom: 14,
              background: ['#fff', V2.tint, V2.hi][i % 3],
              border: `2px solid ${V2.ink}`, borderRadius: 10,
              ['--rot']: `${i % 2 ? 0.6 : -0.6}deg`,
              transform: `rotate(${i % 2 ? 0.6 : -0.6}deg)`,
              animationDelay: `${i * 0.5}s`,
              boxShadow: `3px 3px 0 ${V2.ink}`,
            }}>
              <div style={{ position: 'absolute', top: -12, left: 16, fontSize: 52, fontFamily: V2.serif, color: V2.accent, lineHeight: 1 }}>"</div>
              <div style={{ fontSize: 14, fontWeight: 700, lineHeight: 1.4, color: V2.ink, paddingTop: 6 }}>{t.quote}</div>
              <div style={{ marginTop: 10, fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13, color: V2.inkMuted }}>
                — {t.name}{t.subject ? `, ${t.subject}` : ''} · <span style={{ color: V2.gold }}>★★★★★</span>
              </div>
            </div>
          </Fade>
        ))}
        <Fade delay={680} k={idx}>
          <div style={{ padding: '10px 14px', border: `2px dashed ${V2.accent}`, borderRadius: 10, margin: '14px 0', fontSize: 12, fontWeight: 800, color: V2.ink, textAlign: 'center', background: V2.tint }}>{s.credibility}</div>
        </Fade>
      </Screen>
    );
  }

  function S5({ idx, total, onNext, onBack }) {
    const s = SCREENS[4];
    const [top, setTop] = useState(0);
    const [drag, setDrag] = useState({ x: 0, active: false });
    const startRef = useRef(0);
    const swipe = d => { setDrag({ x: d * 400, active: false }); setTimeout(() => { setDrag({ x: 0, active: false }); setTop(v => v + 1); }, 220); };
    const onDown = e => { startRef.current = (e.touches?.[0] ?? e).clientX; setDrag({ x: 0, active: true }); };
    const onMove = e => { if (!drag.active) return; const x = (e.touches?.[0] ?? e).clientX; setDrag({ x: x - startRef.current, active: true }); };
    const onUp = () => { if (Math.abs(drag.x) > 80) swipe(drag.x > 0 ? 1 : -1); else setDrag({ x: 0, active: false }); };
    const done = top >= s.cards.length;
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext} disabled={!done}>{s.cta}</StampBtn>}>
        <Fade delay={60} k={idx}><Kicker>be honest</Kicker><Title>Does this sound like <span style={{ fontStyle: 'italic' }}>you</span>?</Title><Lede>Swipe right for yes, left for no.</Lede></Fade>
        <div style={{ position: 'relative', height: 320, marginBottom: 8 }}>
          {!done && s.cards.map((c, i) => {
            if (i < top) return null;
            const depth = i - top; if (depth > 2) return null;
            const isTop = depth === 0;
            const rot = isTop ? drag.x * 0.05 : (depth === 1 ? -1.2 : 1);
            return (
              <div key={i}
                onMouseDown={isTop ? onDown : undefined} onMouseMove={isTop ? onMove : undefined}
                onMouseUp={isTop ? onUp : undefined} onMouseLeave={isTop ? onUp : undefined}
                onTouchStart={isTop ? onDown : undefined} onTouchMove={isTop ? onMove : undefined} onTouchEnd={isTop ? onUp : undefined}
                style={{
                  position: 'absolute', inset: 0,
                  transform: `translate(${isTop ? drag.x : 0}px, ${depth * 8}px) rotate(${rot}deg) scale(${1 - depth * 0.04})`,
                  transition: drag.active ? 'none' : 'transform 260ms cubic-bezier(.2,.8,.2,1)',
                  background: ['#fff', V2.tint, V2.paperDark][depth],
                  border: `2.5px solid ${V2.ink}`, borderRadius: 14, boxShadow: `5px 5px 0 ${V2.ink}`,
                  padding: 24, display: 'flex', flexDirection: 'column', justifyContent: 'center', textAlign: 'center',
                  userSelect: 'none', cursor: isTop ? 'grab' : 'default', zIndex: 10 - depth,
                }}>
                <div style={{ position: 'absolute', top: 20, left: 20, transform: 'rotate(-12deg)', fontFamily: V2.serif, fontWeight: 700, fontStyle: 'italic', fontSize: 30, color: V2.accent, opacity: isTop ? Math.max(0, drag.x / 80) : 0 }}>yes.</div>
                <div style={{ position: 'absolute', top: 20, right: 20, transform: 'rotate(12deg)', fontFamily: V2.serif, fontWeight: 700, fontStyle: 'italic', fontSize: 30, color: V2.ink, opacity: isTop ? Math.max(0, -drag.x / 80) : 0 }}>nope.</div>
                <div style={{ fontSize: 12, fontWeight: 900, color: V2.inkMuted, letterSpacing: 1.5, textTransform: 'uppercase', marginBottom: 14 }}>Confession {i + 1}</div>
                <div style={{ fontFamily: V2.serif, fontWeight: 400, fontSize: 22, lineHeight: 1.2, color: V2.ink, letterSpacing: -0.3 }}>"{c}"</div>
              </div>
            );
          })}
          {done && (
            <div style={{
              position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column',
              alignItems: 'center', justifyContent: 'center', gap: 12,
              background: '#fff', border: `2.5px solid ${V2.ink}`, borderRadius: 14, boxShadow: `5px 5px 0 ${V2.ink}`,
            }}>
              <ProfFace size={100} mood="happy" />
              <div style={{ fontFamily: V2.serif, fontSize: 22, color: V2.ink, textAlign: 'center', padding: '0 20px' }}>
                Noted. Prof has <i>notes</i>.
              </div>
            </div>
          )}
        </div>
        <div style={{ display: 'flex', gap: 14, justifyContent: 'center', margin: '14px 0 10px' }}>
          <button onClick={() => !done && swipe(-1)} disabled={done} style={{
            width: 56, height: 56, borderRadius: 12, border: `2.5px solid ${V2.ink}`,
            background: V2.paper, color: V2.ink, fontSize: 26, fontWeight: 900, cursor: done ? 'default' : 'pointer',
            boxShadow: `3px 3px 0 ${V2.ink}`, opacity: done ? 0.3 : 1,
          }}>✕</button>
          <button onClick={() => !done && swipe(1)} disabled={done} style={{
            width: 56, height: 56, borderRadius: 12, border: `2.5px solid ${V2.ink}`,
            background: V2.accent, color: '#fff', fontSize: 26, fontWeight: 900, cursor: done ? 'default' : 'pointer',
            boxShadow: `3px 3px 0 ${V2.ink}`, opacity: done ? 0.3 : 1,
          }}>♥</button>
        </div>
      </Screen>
    );
  }

  function S6({ idx, total, onNext, onBack }) {
    const s = SCREENS[5];
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext}>{s.cta}</StampBtn>}>
        <Fade delay={60} k={idx}><Kicker>the remedy</Kicker><Title>Here's how Prof <span style={{ fontStyle: 'italic', color: V2.accent }}>fixes</span> that.</Title><Lede>Based on what you told us.</Lede></Fade>
        {s.items.map((it, i) => (
          <Fade key={i} delay={140 + i * 100} k={idx}>
            <div className="pc-bob" style={{
              display: 'flex', gap: 14, padding: 16, marginBottom: 12,
              background: '#fff', border: `2.5px solid ${V2.ink}`, borderRadius: 12,
              boxShadow: `3px 3px 0 ${V2.ink}`, transform: `rotate(${i % 2 ? 0.3 : -0.3}deg)`,
              animationDelay: `${i * 0.7}s`,
            }}>
              <div className="pc-wiggle" style={{
                width: 44, height: 44, borderRadius: 10, flexShrink: 0,
                background: V2.accent, border: `2px solid ${V2.ink}`,
                display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 22,
                animationDelay: `${i * 0.4}s`,
              }}>{it.icon}</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 12, color: V2.inkMuted, textDecoration: 'line-through', marginBottom: 4 }}>{it.pain}</div>
                <div style={{ fontSize: 14, fontWeight: 800, lineHeight: 1.35, color: V2.ink }}>{it.sol}</div>
              </div>
            </div>
          </Fade>
        ))}
      </Screen>
    );
  }

  function S7({ idx, total, onNext, onBack, data, setData }) {
    const s = SCREENS[6]; const sel = data.subjects ?? s.defaultSelected;
    const toggle = i => setData({ ...data, subjects: sel.includes(i) ? sel.filter(x => x !== i) : [...sel, i] });
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext} disabled={sel.length === 0}>{s.cta}</StampBtn>}>
        <Fade delay={60} k={idx}><Kicker>the syllabus</Kicker><Title>Pick your <span style={{ fontStyle: 'italic' }}>subjects</span>.</Title><Lede>{s.sub}</Lede></Fade>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 16 }}>
          {s.options.map((o, i) => {
            const on = sel.includes(i);
            return (
              <Fade key={i} delay={100 + i * 30} k={idx}>
                <button onClick={() => toggle(i)} style={{
                  width: '100%', padding: '18px 14px',
                  background: on ? V2.hi : '#fff',
                  border: `2.5px solid ${on ? V2.accent : V2.ink}`, borderRadius: 12,
                  boxShadow: on ? `3px 3px 0 ${V2.accent}` : `2px 2px 0 ${V2.ink}`,
                  fontFamily: V2.font, fontWeight: 800, fontSize: 14, color: V2.ink,
                  cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 10, textAlign: 'left',
                  transform: `rotate(${(i % 3 - 1) * 0.3}deg)`,
                }}>
                  <span className="pc-bob2" style={{ fontSize: 22, display: 'inline-block', animationDelay: `${i * 0.25}s` }}>{o.icon}</span><span>{o.label}</span>
                </button>
              </Fade>
            );
          })}
        </div>
      </Screen>
    );
  }

  function S8({ idx, total, onNext }) {
    const s = SCREENS[7]; const [li, setLi] = useState(0); const [prog, setProg] = useState(0);
    useEffect(() => {
      const d = setInterval(() => setLi(v => Math.min(v + 1, s.lines.length - 1)), 850);
      const start = performance.now(); let raf;
      const tick = n => { const p = Math.min(1, (n - start) / 3400); setProg(p); if (p < 1) raf = requestAnimationFrame(tick); else setTimeout(onNext, 400); };
      raf = requestAnimationFrame(tick); return () => { clearInterval(d); cancelAnimationFrame(raf); };
    }, []);
    return (
      <Screen idx={idx} total={total} showProg={true}>
        <div style={{ height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 24, paddingBottom: 40 }}>
          <div style={{ position: 'relative' }}>
            <ProfFace size={160} mood="thinking" />
            {/* Floating sparkles around prof while he thinks */}
            <span className="pc-sparkle" style={{ position: 'absolute', top: 6, left: -8, fontSize: 22, color: V2.accent, animationDelay: '0s' }}>✦</span>
            <span className="pc-sparkle" style={{ position: 'absolute', top: 32, right: -16, fontSize: 16, color: V2.gold, animationDelay: '0.6s' }}>✦</span>
            <span className="pc-sparkle" style={{ position: 'absolute', bottom: 30, left: -18, fontSize: 14, color: V2.accent, animationDelay: '1.1s' }}>✦</span>
            <span className="pc-sparkle" style={{ position: 'absolute', bottom: 0, right: 4, fontSize: 18, color: V2.gold, animationDelay: '1.5s' }}>✦</span>
          </div>
          <Kicker>brewing</Kicker>
          <div style={{ fontFamily: V2.serif, fontWeight: 400, fontSize: 26, color: V2.ink, textAlign: 'center', minHeight: 60, padding: '0 20px', letterSpacing: -0.3, fontStyle: 'italic' }}>"{s.lines[li]}"</div>
          <div style={{ width: 240 }}><PaperProgress value={Math.ceil(prog * 4)} total={4} /></div>
          {/* preload professor gifs so they're cached by the time the call screen mounts */}
          <div aria-hidden="true" style={{ position: 'absolute', width: 1, height: 1, opacity: 0, pointerEvents: 'none', overflow: 'hidden' }}>
            <img src="static/prof-hello.gif" alt="" decoding="async" />
            <img src="static/prof-talk.gif" alt="" decoding="async" />
          </div>
        </div>
      </Screen>
    );
  }

  function S9({ idx, total, onNext, onBack, data, setData }) {
    const s = SCREENS[8];
    const [pickedIdx, setPickedIdx] = useState(null);
    const sample = pickedIdx != null ? s.samples[pickedIdx] : null;
    const [phase, setPhase] = useState('pick'); // pick → ringing → call → ended
    const [step, setStep] = useState(0);
    const [secs, setSecs] = useState(0);
    const [ringNonce, setRingNonce] = useState(0);
    useEffect(() => {
      if (phase === 'ringing') {
        const t = setTimeout(() => setPhase('call'), 2500);
        return () => clearTimeout(t);
      }
      if (phase === 'call' && sample) {
        const id = setInterval(() => setStep(v => {
          if (v >= sample.steps.length - 1) {
            clearInterval(id);
            // hold on the last step, then transition to "ended"
            setTimeout(() => setPhase('ended'), 1400);
            return v;
          }
          return v + 1;
        }), 1500);
        const tk = setInterval(() => setSecs(v => {
          if (v >= 12) { clearInterval(tk); return 12; }
          return v + 1;
        }), 1000);
        return () => { clearInterval(id); clearInterval(tk); };
      }
      if (phase === 'ended') {
        // freeze the seconds counter
        return;
      }
    }, [phase]);
    const choose = (i) => {
      setPickedIdx(i); setStep(0); setSecs(0); setPhase('ringing');
      setRingNonce(n => n + 1);
      const picked = s.samples[i];
      setData({ ...data, askedSubject: picked.subject, askedProblem: picked.problem });
    };
    const fmt = (n) => `${String(Math.floor(n / 60)).padStart(2, '0')}:${String(n % 60).padStart(2, '0')}`;
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext} disabled={phase !== 'ended'}>{s.cta}</StampBtn>}>
        <style>{`
          @keyframes v2pop { from { opacity: 0; transform: translateY(6px) } to { opacity: 1; transform: translateY(0) } }
          @keyframes pcWave { 0%, 100% { transform: scaleY(0.4) } 50% { transform: scaleY(1.2) } }
          @keyframes pcDotPulse { 0%, 80%, 100% { opacity: 0.2 } 40% { opacity: 1 } }
          @keyframes pcRing { 0% { transform: scale(0.6); opacity: 0.7 } 100% { transform: scale(2); opacity: 0 } }
          .pc-typing-dot { display: inline-block; font-size: 22px; line-height: 1; animation: pcDotPulse 1.2s ease-in-out infinite; }
        `}</style>
        <Fade delay={60} k={idx}>
          <Kicker>live demonstration</Kicker>
          <Title size={28}>Ask Prof <span style={{ fontStyle: 'italic' }}>anything.</span></Title>
          <Lede>{s.sub}</Lede>
        </Fade>
        {phase === 'pick' && (
          <Fade delay={140} k={idx}>
            <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13, color: V2.inkMuted, marginBottom: 10 }}>tap a question to call Prof:</div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 10 }}>
              {s.samples.map((sm, i) => (
                <button key={i} onClick={() => choose(i)} className="pc-bob2"
                  style={{
                    padding: '14px 12px', textAlign: 'left',
                    background: '#fff', border: `2.5px solid ${V2.ink}`, borderRadius: 12,
                    boxShadow: `3px 3px 0 ${V2.ink}`, cursor: 'pointer',
                    transform: `rotate(${(i % 2 ? 0.4 : -0.4)}deg)`,
                    animationDelay: `${i * 0.3}s`,
                    fontFamily: V2.font, color: V2.ink,
                    display: 'flex', flexDirection: 'column', gap: 6, minHeight: 110,
                  }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                    <span style={{ fontSize: 22 }}>{sm.icon}</span>
                    <span style={{
                      fontSize: 9, fontWeight: 900, color: V2.accent,
                      letterSpacing: 0.6, textTransform: 'uppercase',
                      border: `1.5px solid ${V2.accent}`, borderRadius: 100, padding: '2px 6px',
                      display: 'inline-flex', alignItems: 'center', gap: 4,
                    }}>📞 ask</span>
                  </div>
                  <span style={{ fontSize: 10, fontWeight: 900, color: V2.inkMuted, letterSpacing: 0.5, textTransform: 'uppercase' }}>{sm.subject}</span>
                  <span style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13.5, color: V2.ink, lineHeight: 1.3 }}>"{sm.problem}"</span>
                </button>
              ))}
            </div>
            <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 12, color: V2.inkMuted, textAlign: 'center', marginTop: 6 }}>
              or type your own — Prof never sleeps.
            </div>
          </Fade>
        )}

        {phase === 'ringing' && sample && (
          <div style={{
            background: V2.ink, borderRadius: 14, border: `2.5px solid ${V2.ink}`, boxShadow: `5px 5px 0 ${V2.ink}`,
            padding: 28, height: 320, position: 'relative', overflow: 'hidden',
            display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 8,
            animation: 'v2pop 500ms cubic-bezier(.2,.8,.2,1)',
          }}>
            {/* avatar with rings centered on it */}
            <div style={{ position: 'relative', width: 96, height: 96, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 6 }}>
              <div style={{ position: 'absolute', inset: 0, borderRadius: '50%', border: `2px solid ${V2.accent}`, animation: 'pcRing 1.6s ease-out infinite' }} />
              <div style={{ position: 'absolute', inset: 0, borderRadius: '50%', border: `2px solid ${V2.accent}`, animation: 'pcRing 1.6s ease-out 0.5s infinite' }} />
              <div style={{
                position: 'relative', width: 96, height: 96, borderRadius: '50%', overflow: 'hidden',
                border: `3px solid ${V2.paper}`, background: V2.tint, display: 'flex', alignItems: 'center', justifyContent: 'center',
              }}>
                <img key={`ring-${ringNonce}`} src="static/prof-hello.gif" alt="Professor calling" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
              </div>
            </div>
            <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13, color: V2.paper, opacity: 0.8 }}>calling…</div>
            <div style={{ fontFamily: V2.serif, fontWeight: 400, fontSize: 22, color: V2.paper, letterSpacing: -0.3 }}>Professor Curious</div>
            <div style={{ fontSize: 10, fontWeight: 900, color: V2.accent, letterSpacing: 1, textTransform: 'uppercase', marginTop: 2 }}>{sample.subject}</div>
          </div>
        )}

        {(phase === 'call' || phase === 'ended') && sample && (() => {
          const isEnded = phase === 'ended';
          const visibleSteps = isEnded ? sample.steps.length - 1 : step;
          return (
            <div style={{ animation: 'v2pop 500ms cubic-bezier(.2,.8,.2,1)' }}>
              {/* live call header */}
              <div style={{
                background: V2.ink, borderRadius: 14, border: `2.5px solid ${V2.ink}`, boxShadow: `4px 4px 0 ${V2.ink}`,
                padding: '14px 16px', marginBottom: 12, color: V2.paper,
                display: 'flex', alignItems: 'center', gap: 12,
                transition: 'opacity 400ms ease',
              }}>
                <div style={{
                  width: 44, height: 44, borderRadius: '50%', overflow: 'hidden', flexShrink: 0,
                  background: V2.tint, border: `2px solid ${V2.paper}`,
                  filter: isEnded ? 'grayscale(0.6)' : 'none',
                  transition: 'filter 400ms ease',
                }}>
                  <img key={`talk-${ringNonce}`} src="static/prof-talk.gif" alt="Professor" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: V2.serif, fontWeight: 400, fontSize: 16 }}>Professor Curious</div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, fontWeight: 800, color: V2.accent, transition: 'color 400ms ease' }}>
                    <span style={{
                      width: 8, height: 8, borderRadius: '50%',
                      background: isEnded ? V2.inkMuted : '#3CCB7F',
                      boxShadow: isEnded ? 'none' : '0 0 8px #3CCB7F',
                      display: 'inline-block',
                      transition: 'background 400ms ease, box-shadow 400ms ease',
                    }} />
                    {isEnded ? 'ENDED' : 'LIVE'} · {fmt(secs)}
                  </div>
                </div>
                {/* waveform */}
                <div style={{ display: 'flex', alignItems: 'center', gap: 2, height: 22 }}>
                  {[12, 18, 9, 22, 14, 19, 8, 15, 11, 20].map((h, i) => (
                    <span key={i} style={{
                      width: 2.5, height: h, background: isEnded ? V2.inkMuted : V2.accent, borderRadius: 2,
                      opacity: isEnded ? 0.5 : 1,
                      animation: isEnded ? 'none' : `pcWave 0.9s ease-in-out ${i * 0.07}s infinite`,
                      transform: isEnded ? 'scaleY(0.3)' : undefined,
                      transition: 'background 400ms ease, opacity 400ms ease, transform 400ms ease',
                    }} />
                  ))}
                </div>
              </div>

              {/* transcript bubbles */}
              <div style={{
                background: '#fff', border: `2.5px solid ${V2.ink}`, boxShadow: `3px 3px 0 ${V2.ink}`,
                borderRadius: 12, padding: '12px 14px', marginBottom: 10,
              }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4, fontSize: 10, fontWeight: 900, color: V2.inkMuted, letterSpacing: 0.5 }}>
                  <span style={{ fontSize: 12 }}>🙋</span> YOU · ASKED
                </div>
                <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 15, color: V2.ink, lineHeight: 1.35 }}>"{sample.problem}"</div>
              </div>

              {sample.steps.slice(0, visibleSteps + 1).map((st, i) => (
                <div key={i} style={{ display: 'flex', gap: 10, marginBottom: 10, animation: 'v2pop 500ms cubic-bezier(.2,.8,.2,1)' }}>
                  <ProfFace size={36} mood={i === sample.steps.length - 1 ? 'cheer' : 'smug'} />
                  <div style={{
                    background: i === sample.steps.length - 1 ? V2.accent : '#fff',
                    color: i === sample.steps.length - 1 ? '#fff' : V2.ink,
                    border: `2px solid ${V2.ink}`, borderRadius: 10, padding: '10px 14px',
                    fontFamily: V2.serif, fontSize: 14.5, fontWeight: 400, lineHeight: 1.4, flex: 1,
                  }}>{st}</div>
                </div>
              ))}
              {!isEnded && visibleSteps < sample.steps.length - 1 && (
                <div style={{ display: 'flex', gap: 10, marginBottom: 6, opacity: 0.6 }}>
                  <ProfFace size={36} mood="thinking" />
                  <div style={{
                    background: '#fff', border: `2px dashed ${V2.ink}`, borderRadius: 10,
                    padding: '10px 14px', fontFamily: V2.serif, fontSize: 14, color: V2.inkMuted,
                  }}>
                    <span className="pc-typing-dot" style={{ animationDelay: '0s' }}>•</span>
                    <span className="pc-typing-dot" style={{ animationDelay: '0.2s', marginLeft: 3 }}>•</span>
                    <span className="pc-typing-dot" style={{ animationDelay: '0.4s', marginLeft: 3 }}>•</span>
                  </div>
                </div>
              )}

              <button onClick={() => { setPickedIdx(null); setStep(0); setSecs(0); setPhase('pick'); }}
                style={{
                  marginTop: 6, background: 'transparent', border: 'none',
                  fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13, color: V2.inkMuted,
                  textDecoration: 'underline', cursor: 'pointer', padding: '4px 0',
                }}>
                ← ask a different question
              </button>
            </div>
          );
        })()}
      </Screen>
    );
  }

  function S10({ idx, total, onNext, onBack, data }) {
    const s = SCREENS[9];
    const subjectTag = data?.askedSubject ? `${data.askedSubject.toUpperCase()} · ANSWERED` : 'PROFESSOR CURIOUS · LIVE';
    const PLANS = {
      'Math': ['Fractions deep-dive', 'Ratios & proportions', 'Word problems'],
      'Chemistry': ['States of matter', 'Solutions & mixtures', 'Reaction basics'],
      'Physics': ['Forces & motion', 'Gravity in orbit', 'Energy basics'],
      'English': ['Figurative language', 'Sentence structure', 'Essay craft'],
    };
    const plan = PLANS[data?.askedSubject] || ['Pick a subject', 'Daily warm-ups', 'Weekly check-in'];
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<StampBtn onClick={onNext}>{s.cta}</StampBtn>}>
        <Fade delay={60} k={idx}><Kicker>the receipt</Kicker><Title>First explanation, <span style={{ fontStyle: 'italic' }}>served.</span></Title><Lede>{s.sub}</Lede></Fade>
        <Fade delay={180} k={idx}>
          <div className="pc-breath" style={{
            padding: 20, borderRadius: 12, background: V2.accent, color: '#fff', marginBottom: 14,
            border: `2.5px solid ${V2.ink}`, boxShadow: `5px 5px 0 ${V2.accent2}`,
          }}>
            <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 12, opacity: 0.85, marginBottom: 4 }}>answered in</div>
            <div style={{ fontFamily: V2.serif, fontSize: 64, lineHeight: 0.9, letterSpacing: -2 }}>{s.solveTime}</div>
            <div style={{ fontSize: 12, fontWeight: 700, marginTop: 6 }}>{subjectTag}</div>
          </div>
        </Fade>
        <Fade delay={280} k={idx}>
          <div style={{ padding: 16, borderRadius: 12, background: '#fff', border: `2.5px solid ${V2.ink}`, boxShadow: `3px 3px 0 ${V2.ink}`, marginBottom: 12 }}>
            <div style={{ fontFamily: V2.serif, fontStyle: 'italic', fontSize: 13, color: V2.accent, marginBottom: 10 }}>
              your {data?.askedSubject?.toLowerCase() || 'study'} plan — preview
            </div>
            {plan.map((t, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 0', borderTop: i > 0 ? `1px dashed ${V2.ink}` : 'none' }}>
                <div style={{ width: 28, height: 28, borderRadius: 6, background: i === 0 ? V2.accent : V2.tint, color: i === 0 ? '#fff' : V2.ink, border: `2px solid ${V2.ink}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 900, fontSize: 13 }}>{i + 1}</div>
                <div style={{ fontSize: 14, fontWeight: 800 }}>{t}</div>
                <div style={{ marginLeft: 'auto', fontFamily: V2.serif, fontStyle: 'italic', fontSize: 12, color: V2.inkMuted }}>{i === 0 ? 'today' : `day ${i + 1}`}</div>
              </div>
            ))}
          </div>
        </Fade>
        <Fade delay={360} k={idx}>
          <div style={{ padding: '14px 16px', border: `2px dashed ${V2.accent}`, background: V2.tint, borderRadius: 10, fontSize: 13, fontWeight: 800, color: V2.ink, marginBottom: 14 }}>
            <span style={{ color: V2.accent }}>+27%</span> — avg score lift for daily users.
          </div>
        </Fade>
      </Screen>
    );
  }

  function S11({ idx, total, onNext, onBack }) {
    const s = SCREENS[10];
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<>
          <StampBtn onClick={onNext}>{s.cta}</StampBtn>
          <div style={{ textAlign: 'center', marginTop: 10, fontFamily: V2.serif, fontStyle: 'italic', fontSize: 15, color: V2.inkMuted, cursor: 'pointer' }} onClick={onNext}>{s.secondary}</div>
        </>}>
        <Fade delay={60} k={idx}><Kicker>the streak</Kicker><Title>Don't break the <span style={{ fontStyle: 'italic' }}>chain</span>.</Title><Lede>{s.sub}</Lede></Fade>
        <Fade delay={180} k={idx}>
          <div style={{
            background: '#fff', padding: 14, borderRadius: 12, marginBottom: 18,
            border: `2.5px solid ${V2.ink}`, boxShadow: `3px 3px 0 ${V2.ink}`,
            display: 'flex', gap: 12,
          }}>
            <ProfFace size={44} mood="smug" />
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, fontWeight: 900, color: V2.inkMuted, letterSpacing: 0.5 }}>
                <span>PROFESSOR CURIOUS</span><span>NOW</span>
              </div>
              <div style={{ fontSize: 14, fontWeight: 800, color: V2.ink, marginTop: 2 }}>
                <span className="pc-wiggle" style={{ display: 'inline-block', ['--rot']: '0deg' }}>🔥</span> Day 7 streak — don't break it.
              </div>
              <div style={{ fontSize: 13, fontWeight: 600, color: V2.inkMuted, marginTop: 2 }}>10 min of Algebra is all it takes.</div>
            </div>
          </div>
        </Fade>
        {s.benefits.map((b, i) => (
          <Fade key={i} delay={280 + i * 100} k={idx}>
            <div style={{ display: 'flex', gap: 14, padding: '12px 0', alignItems: 'flex-start', borderBottom: i < s.benefits.length - 1 ? `1px dashed ${V2.ink}` : 'none' }}>
              <span className="pc-bob2" style={{ fontSize: 22, flexShrink: 0, animationDelay: `${i * 0.5}s`, display: 'inline-block' }}>{b.icon}</span>
              <span style={{ fontSize: 14, fontWeight: 700, color: V2.ink, lineHeight: 1.4 }}>{b.text}</span>
            </div>
          </Fade>
        ))}
      </Screen>
    );
  }

  function SEmail({ idx, total, onNext, onBack, data, setData }) {
    const email = data.email ?? '';
    const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<>
          <StampBtn onClick={onNext} disabled={!valid}>Save my progress</StampBtn>
          <div style={{ textAlign: 'center', marginTop: 10, fontFamily: V2.serif, fontStyle: 'italic', fontSize: 14, color: V2.inkMuted, cursor: 'pointer' }} onClick={onNext}>
            I'll add it later
          </div>
        </>}>
        <Fade delay={60} k={idx}>
          <Kicker>save your progress</Kicker>
          <Title>Where should we send your <span style={{ fontStyle: 'italic', color: V2.accent }}>study plan</span>?</Title>
          <Lede>So Prof can save your progress and remind you to come back. No spam. Pinky promise.</Lede>
        </Fade>
        <Fade delay={180} k={idx}>
          <div style={{
            background: '#fff', border: `2.5px solid ${V2.ink}`, borderRadius: 12,
            boxShadow: `3px 3px 0 ${V2.ink}`, padding: '4px 6px', marginBottom: 14,
            display: 'flex', alignItems: 'center', gap: 8,
          }}>
            <span style={{ fontSize: 22, padding: '0 6px 0 10px' }}>✉️</span>
            <input
              type="email" autoComplete="email" inputMode="email"
              placeholder="you@school.edu"
              value={email}
              onChange={(e) => setData({ ...data, email: e.target.value })}
              style={{
                flex: 1, padding: '14px 8px', border: 'none', outline: 'none',
                background: 'transparent', fontFamily: V2.font, fontWeight: 700, fontSize: 16,
                color: V2.ink, letterSpacing: 0.2,
              }}
            />
            {valid && <span style={{ fontSize: 18, color: V2.accent, paddingRight: 12 }}>✓</span>}
          </div>
        </Fade>
        <Fade delay={260} k={idx}>
          <div style={{
            display: 'flex', alignItems: 'flex-start', gap: 10, padding: '12px 14px',
            border: `1.5px dashed ${V2.ink}`, borderRadius: 10, background: V2.tint, marginBottom: 16,
          }}>
            <span style={{ fontSize: 18 }}>🔒</span>
            <div style={{ fontSize: 12.5, fontWeight: 700, color: V2.ink, lineHeight: 1.4 }}>
              We use this to save your study plan and send your daily streak nudge. Unsubscribe anytime.
            </div>
          </div>
        </Fade>
      </Screen>
    );
  }

  // Pick a tailored review based on chapter 2 (goal) + chapter 3 (pains) selections.
  // Each review has a goal tag and a primary pain tag; we score against the user's picks.
  // Goal indices (ch.2): 0 SAT, 1 ACT, 2 AP, 3 Grades, 4 Homework, 5 Exploring
  // Pain  indices (ch.3): 0 NoOneAtNight, 1 TutorsExpensive, 2 ScoreStuck, 3 NoFocus, 4 LoseMotivation, 5 TestAnxiety, 6 Cramming
  const REVIEW_BANK = [
    // SAT (0)
    { goal: 0, pain: 2, subject: 'SAT', name: 'Jordan M., 11th grade', tag: 'score plateau', quote: "I plateaued at 1200 for months. Six weeks with Prof and the trap questions stopped tricking me — landed a 1390." },
    { goal: 0, pain: 0, subject: 'SAT', name: 'Maya R., 12th grade', tag: 'late-night studier', quote: "It's 1am and I'm stuck on a no-calc problem? Prof picks up. My SAT math went 620 → 740 in two months." },
    { goal: 0, pain: 1, subject: 'SAT', name: 'Devon P., 11th grade', tag: 'no tutor budget', quote: "Tutors near me wanted $90/hr. Prof costs less than one session and I use him every single night." },
    { goal: 0, pain: 5, subject: 'SAT', name: 'Aisha K., 12th grade', tag: 'test anxiety', quote: "I used to spiral the night before. Now I do a 15-min Prof recap and walk in calm. +160 points." },
    { goal: 0, pain: 6, subject: 'SAT', name: 'Tyler J., 11th grade', tag: 'reformed crammer', quote: "Stopped cramming. 20 min/day with Prof for 8 weeks beat the all-nighters I pulled last year." },
    // ACT (1)
    { goal: 1, pain: 2, subject: 'ACT', name: 'Hannah L., 11th grade', tag: 'score plateau', quote: "Stuck at a 24 forever. Prof drilled me on ACT science pacing — jumped to a 31 in one cycle." },
    { goal: 1, pain: 3, subject: 'ACT', name: 'Marcus T., 12th grade', tag: 'lost focus', quote: "I had no idea where to start. Prof built me a 6-week plan and I just followed it. 26 → 32." },
    { goal: 1, pain: 0, subject: 'ACT', name: 'Riley S., 11th grade', tag: 'studies at night', quote: "Practice tests at 11pm with Prof reviewing every miss right after — that's the secret. +5 composite." },
    { goal: 1, pain: 4, subject: 'ACT', name: 'Brooke A., 12th grade', tag: 'low motivation', quote: "Streaks got me hooked. 47 days in, I actually look forward to ACT prep, which is wild to type." },
    // AP (2)
    { goal: 2, pain: 2, subject: 'AP Chem', name: 'Priya R., 11th grade', tag: 'score plateau', quote: "I asked Prof the same buffer problem three times. He never made me feel dumb. Got my 5." },
    { goal: 2, pain: 0, subject: 'AP Bio', name: 'Sam W., 12th grade', tag: 'late-night studier', quote: "Photosynthesis at 2am made sense for the first time. Prof literally drew it for me. AP Bio: 5." },
    { goal: 2, pain: 5, subject: 'AP US History', name: 'Diego F., 11th grade', tag: 'test anxiety', quote: "DBQs used to wreck me. Prof's outline trick turned panic into a checklist. APUSH: 4." },
    { goal: 2, pain: 3, subject: 'AP Calc BC', name: 'Olivia G., 12th grade', tag: 'lost focus', quote: "The full-syllabus map Prof built showed me what I actually didn't know. Calc BC: 5." },
    { goal: 2, pain: 1, subject: 'AP Physics', name: 'Noah B., 11th grade', tag: 'no tutor budget', quote: "AP Physics tutors are $120/hr near me. Prof cost less than one session for the whole semester." },
    // Grades (3)
    { goal: 3, pain: 0, subject: 'Algebra II', name: 'Sarah K., 10th grade', tag: 'late-night studier', quote: "Homework due at 8am. 11pm panic. Prof walked me through every step — pulled my Algebra grade C → A-." },
    { goal: 3, pain: 4, subject: 'Geometry', name: 'Ethan C., 9th grade', tag: 'low motivation', quote: "Daily 10-min Prof check-ins turned a D in Geometry into a B+ over one quarter." },
    { goal: 3, pain: 2, subject: 'Chemistry', name: 'Zoe H., 10th grade', tag: 'score plateau', quote: "I studied for hours and got Cs. Prof showed me I was memorizing, not understanding. Now I'm at an A-." },
    { goal: 3, pain: 6, subject: 'World History', name: 'Liam D., 10th grade', tag: 'reformed crammer', quote: "Stopped cramming, started 15 min/night with Prof. World History went from C+ to A." },
    // Homework help fast (4)
    { goal: 4, pain: 0, subject: 'Math', name: 'Carter V., 9th grade', tag: 'late-night studier', quote: "Snap a problem at midnight, Prof explains in seconds. Saved me on like 30 homework nights." },
    { goal: 4, pain: 3, subject: 'Physics', name: 'Mia L., 11th grade', tag: 'lost focus', quote: "Prof doesn't just give answers — he asks me what I tried first. I actually learn the homework now." },
    { goal: 4, pain: 1, subject: 'English', name: 'Jaden O., 10th grade', tag: 'no tutor budget', quote: "My parents can't afford a tutor. Prof reads my essay drafts every week. English: B → A." },
    // Exploring (5)
    { goal: 5, pain: 4, subject: 'Mixed', name: 'Ava P., 12th grade', tag: 'curious learner', quote: "Came in skeptical. Stayed because Prof actually explains why things work. Now I use it for everything." },
    { goal: 5, pain: 3, subject: 'Mixed', name: 'Kai N., 11th grade', tag: 'curious learner', quote: "Best part is asking dumb 'why' questions and getting real answers. School never felt this curious." },
  ];

  function scoreReview(r, goal, pains) {
    let score = 0;
    if (goal != null && r.goal === goal) score += 10;
    if (pains.includes(r.pain)) score += 5;
    if (pains[0] != null && pains[0] === r.pain) score += 1;
    return score;
  }

  function pickReview(data) {
    const goal = data?.goal;
    const pains = Array.isArray(data?.pains) ? data.pains : [];
    let best = null; let bestScore = -1;
    for (const r of REVIEW_BANK) {
      const s = scoreReview(r, goal, pains);
      if (s > bestScore) { bestScore = s; best = r; }
    }
    if (bestScore < 5) {
      return {
        subject: 'SAT',
        name: SCREENS[11].testimonial.name,
        tag: 'verified user',
        quote: SCREENS[11].testimonial.quote,
      };
    }
    return best;
  }

  // Pick N tailored reviews — prefer matching goal, then cover distinct pains so the
  // trio doesn't feel repetitive. Falls back to default SCREENS testimonials if data is empty.
  function pickReviews(data, n = 3) {
    const goal = data?.goal;
    const pains = Array.isArray(data?.pains) ? data.pains : [];
    if (goal == null && pains.length === 0) {
      // fallback to the originals — still annotate to match the new card shape
      return SCREENS[3].testimonials.map(t => ({
        subject: t.tag, name: t.name, tag: t.tag.toLowerCase(), quote: t.quote,
      }));
    }
    const ranked = REVIEW_BANK
      .map(r => ({ r, s: scoreReview(r, goal, pains) }))
      .sort((a, b) => b.s - a.s);
    const out = []; const usedPains = new Set(); const usedNames = new Set();
    // first pass: take highest-scoring with distinct pain
    for (const { r, s } of ranked) {
      if (out.length >= n) break;
      if (s <= 0) break;
      if (usedPains.has(r.pain)) continue;
      out.push(r); usedPains.add(r.pain); usedNames.add(r.name);
    }
    // second pass: relax the distinct-pain rule if we still need more
    for (const { r, s } of ranked) {
      if (out.length >= n) break;
      if (s <= 0) break;
      if (usedNames.has(r.name)) continue;
      out.push(r); usedNames.add(r.name);
    }
    // last resort: pad with originals
    if (out.length < n) {
      for (const t of SCREENS[3].testimonials) {
        if (out.length >= n) break;
        if (usedNames.has(t.name)) continue;
        out.push({ subject: t.tag, name: t.name, tag: t.tag.toLowerCase(), quote: t.quote });
        usedNames.add(t.name);
      }
    }
    return out.slice(0, n);
  }

  function S12({ idx, total, onNext, onBack, data, setData }) {
    const s = SCREENS[11]; const plan = data.plan ?? s.defaultPlan;
    const review = pickReview(data);
    const [offering, setOffering] = useState(null);
    const [loadErr, setLoadErr] = useState(null);
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState(null);

    useEffect(() => {
      let alive = true;
      window.rcGetOffering().then(res => {
        if (!alive) return;
        if (res?.ok) setOffering(res);
        else setLoadErr(res?.error || 'Could not load plans.');
      });
      return () => { alive = false; };
    }, []);

    const livePkg = (id) => offering && (id === 'annual' ? offering.annual : offering.monthly);
    const liveLabel = (p) => {
      const pkg = livePkg(p.id);
      const live = pkg && window.rcPriceLabel(pkg);
      return live || p.per;
    };
    const liveSub = (p) => {
      const pkg = livePkg(p.id);
      const wp = pkg?.webBillingProduct;
      const trial = wp?.introPrice || wp?.defaultSubscriptionOption?.trial;
      if (trial && p.id === 'annual') {
        const days = trial.periodDuration?.match(/P(\d+)D/)?.[1] || trial.period?.value || 7;
        const fmt = wp?.currentPrice?.formattedPrice || wp?.price?.formattedPrice;
        return fmt ? `${days}-day free trial, then ${fmt}/yr` : p.sub;
      }
      return p.sub;
    };

    const onPurchase = async () => {
      setErr(null);
      const pkg = livePkg(plan);
      if (!pkg) {
        setErr('Plans are still loading — give it a sec.');
        return;
      }
      setBusy(true);
      const res = await window.rcPurchase(pkg);
      setBusy(false);
      if (res.cancelled) return; // user closed the Stripe sheet — no-op
      if (!res.ok) { setErr(res.error?.message || 'Purchase failed. Try again.'); return; }
      setData({ ...data, purchase: { redemptionInfo: res.redemptionInfo, plan, purchasedAt: Date.now() } });
      onNext();
    };

    return (
      <Screen idx={idx} total={total} onBack={onBack}
        cta={<>
          <StampBtn onClick={onPurchase} disabled={busy || !offering || !!loadErr}>
            {busy ? 'Processing…' : loadErr ? 'Plans unavailable' : offering ? s.cta : 'Loading plans…'}
          </StampBtn>
          {(err || loadErr) && (
            <div style={{ marginTop: 8, fontSize: 12, fontWeight: 700, color: '#C0392B', textAlign: 'center', lineHeight: 1.4 }}>{err || loadErr}</div>
          )}
          <div style={{ textAlign: 'center', marginTop: 8, fontSize: 11, fontWeight: 700, color: V2.inkMuted, lineHeight: 1.4, fontFamily: V2.serif, fontStyle: 'italic' }}>
            {s.fine}
            <div style={{ marginTop: 4 }}>
              <span style={{ color: V2.accent, fontWeight: 800, textDecoration: 'underline' }}>{s.restore}</span>
              {' · '}
              <span style={{ color: V2.accent, fontWeight: 800, textDecoration: 'underline' }}>{s.more}</span>
            </div>
          </div>
        </>}>
        <Fade delay={60} k={idx}><Kicker>the deal</Kicker><Title>Your test-day <span style={{ fontStyle: 'italic', color: V2.accent }}>confidence</span> starts here.</Title><Lede>{s.sub}</Lede></Fade>
        <Fade delay={180} k={idx}>
          <div style={{
            padding: 16, marginBottom: 16, border: `2.5px solid ${V2.ink}`, borderRadius: 12,
            background: V2.tint, boxShadow: `3px 3px 0 ${V2.ink}`, transform: 'rotate(-0.6deg)',
          }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
              <div style={{ fontSize: 14, color: V2.gold }}>★★★★★</div>
              <div style={{
                fontSize: 9, fontWeight: 900, color: V2.accent, letterSpacing: 0.6,
                border: `1.5px solid ${V2.accent}`, borderRadius: 100, padding: '2px 7px',
                textTransform: 'uppercase',
              }}>{review.subject}</div>
            </div>
            <div style={{ fontFamily: V2.serif, fontSize: 18, color: V2.ink, lineHeight: 1.2, fontStyle: 'italic' }}>"{review.quote}"</div>
            <div style={{ marginTop: 8, fontSize: 12, fontWeight: 800, color: V2.inkMuted }}>— {review.name}</div>
          </div>
        </Fade>
        {s.plans.map((p, i) => {
          const on = plan === p.id;
          return (
            <Fade key={p.id} delay={260 + i * 80} k={idx}>
              <button onClick={() => setData({ ...data, plan: p.id })} style={{
                width: '100%', padding: 16, marginBottom: 10, borderRadius: 12,
                border: `2.5px solid ${on ? V2.accent : V2.ink}`, background: on ? V2.hi : '#fff',
                boxShadow: on ? `4px 4px 0 ${V2.accent}` : `2px 2px 0 ${V2.ink}`,
                display: 'flex', alignItems: 'center', gap: 14, cursor: 'pointer',
                fontFamily: V2.font, textAlign: 'left', position: 'relative',
              }}>
                {p.badge && (
                  <div style={{
                    position: 'absolute', top: -10, right: 10,
                    padding: '4px 10px', borderRadius: 6, background: V2.accent, color: '#fff',
                    fontSize: 10, fontWeight: 900, letterSpacing: 1,
                    border: `2px solid ${V2.ink}`, transform: 'rotate(4deg)',
                  }}>{p.badge}</div>
                )}
                <div style={{
                  width: 20, height: 20, borderRadius: 10, border: `2.5px solid ${V2.ink}`,
                  background: on ? V2.accent : V2.paper,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  color: '#fff', fontSize: 12, fontWeight: 900, flexShrink: 0,
                }}>{on ? '✓' : ''}</div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: V2.serif, fontSize: 20, color: V2.ink }}>{p.label}</div>
                  <div style={{ fontSize: 12, fontWeight: 700, color: V2.inkMuted, marginTop: 2 }}>{liveSub(p)}</div>
                </div>
                <div style={{ fontFamily: V2.serif, fontSize: 18, color: V2.ink }}>{liveLabel(p)}</div>
              </button>
            </Fade>
          );
        })}
        <Fade delay={440} k={idx}>
          <div style={{ padding: '12px 14px', marginTop: 4, marginBottom: 14, border: `1px dashed ${V2.ink}`, borderRadius: 10 }}>
            {s.benefits.map((b, i) => (
              <div key={i} style={{ display: 'flex', gap: 8, padding: '4px 0', fontSize: 13, fontWeight: 700, color: V2.ink }}>
                <span style={{ color: V2.accent, fontWeight: 900 }}>✓</span> {b}
              </div>
            ))}
          </div>
        </Fade>
      </Screen>
    );
  }

  // Post-purchase: hand the anonymous web purchase off to the iOS/Android app.
  // The deep-link is one-time-use and expires in 60 minutes. We show a live
  // countdown so the user feels the urgency, plus a "we'll email it" fallback
  // for desktop visitors whose phone isn't in their hand.
  function SRedeem({ idx, total, data }) {
    const url = window.rcRedeemUrl(data?.purchase?.redemptionInfo);
    const purchasedAt = data?.purchase?.purchasedAt || Date.now();
    const computeLeft = () => Math.max(0, Math.floor((purchasedAt + REDEEM_TTL_MS - Date.now()) / 1000));
    const [left, setLeft] = useState(computeLeft);
    useEffect(() => {
      const t = setInterval(() => setLeft(computeLeft()), 1000);
      return () => clearInterval(t);
    }, [purchasedAt]);
    const mm = String(Math.floor(left / 60)).padStart(2, '0');
    const ss = String(left % 60).padStart(2, '0');
    return (
      <Screen idx={idx} total={total} showProg={false}
        cta={<>
          <StampBtn onClick={() => url && (window.location.href = url)} disabled={!url || left === 0}>
            {left === 0 ? 'Link expired — restore in app' : 'Open the app'}
          </StampBtn>
          <div style={{ textAlign: 'center', marginTop: 8, fontSize: 11, fontWeight: 700, color: V2.inkMuted, fontFamily: V2.serif, fontStyle: 'italic' }}>
            {data?.email ? `We also emailed the link to ${data.email}.` : 'Open this page on your phone to redeem.'}
          </div>
        </>}>
        <Fade delay={60} k={idx}>
          <Kicker>you're in</Kicker>
          <Title>One tap and <span style={{ fontStyle: 'italic', color: V2.accent }}>Professor Curious</span> is yours.</Title>
          <Lede>Open the link below on your phone — your subscription unlocks the moment the app launches.</Lede>
        </Fade>
        <Fade delay={200} k={idx}>
          <div style={{
            padding: 16, marginBottom: 14, border: `2.5px solid ${V2.ink}`, borderRadius: 12,
            background: V2.tint, boxShadow: `3px 3px 0 ${V2.ink}`,
          }}>
            <div style={{ fontSize: 11, fontWeight: 900, color: V2.accent, letterSpacing: 0.6, textTransform: 'uppercase', marginBottom: 6 }}>
              Link expires in
            </div>
            <div style={{ fontFamily: V2.serif, fontSize: 38, color: V2.ink, lineHeight: 1 }}>{mm}:{ss}</div>
            <div style={{ marginTop: 10, fontSize: 13, fontWeight: 700, color: V2.inkMuted }}>
              One-time-use deep-link. Open it on the device you'll study on.
            </div>
          </div>
        </Fade>
        {!url && (
          <Fade delay={320} k={idx}>
            <div style={{ padding: 14, border: `1px dashed ${V2.ink}`, borderRadius: 10, fontSize: 13, fontWeight: 700, color: V2.ink }}>
              Couldn't build a redemption link from the purchase result. Check the RC dashboard's redemption host config.
            </div>
          </Fade>
        )}
      </Screen>
    );
  }

  // Reload-safe state: ?step=N pins the screen index in the URL and
  // localStorage holds the funnel `data` so a refresh — or full browser
  // restart — lands the user back on the same screen with state intact.
  // Critical for the post-purchase redeem screen: the deep-link is one-time-use
  // and expires in 60 min, so we stamp purchase.purchasedAt on success and the
  // SRedeem countdown is derived from it (not from screen mount).
  const STEP_PARAM = 'step';
  const DATA_KEY = 'pc_v2_data';
  const REDEEM_TTL_MS = 60 * 60 * 1000;
  const TOTAL_LIST = 14;

  function readInitialIdx() {
    try {
      const n = parseInt(new URLSearchParams(window.location.search).get(STEP_PARAM) || '', 10);
      if (Number.isFinite(n) && n >= 0 && n < TOTAL_LIST) return n;
    } catch {}
    return 0;
  }
  function readInitialData() {
    try {
      const d = JSON.parse(localStorage.getItem(DATA_KEY) || '{}') || {};
      // Drop a stale purchase so the redeem screen doesn't show an expired link.
      if (d.purchase?.purchasedAt && Date.now() - d.purchase.purchasedAt > REDEEM_TTL_MS) {
        delete d.purchase;
      }
      return d;
    } catch { return {}; }
  }
  function writeStepParam(idx) {
    try {
      const url = new URL(window.location.href);
      if (idx === 0) url.searchParams.delete(STEP_PARAM);
      else url.searchParams.set(STEP_PARAM, String(idx));
      window.history.replaceState(null, '', url.toString());
    } catch {}
  }

  function V2App() {
    const [idx, setIdx] = useState(readInitialIdx);
    const [data, setData] = useState(readInitialData);
    useEffect(() => { window.rcInit(); }, []);
    useEffect(() => { writeStepParam(idx); }, [idx]);
    useEffect(() => {
      try { localStorage.setItem(DATA_KEY, JSON.stringify(data)); } catch {}
    }, [data]);
    const list = [S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, SEmail, S12, SRedeem];
    const total = list.length;
    const next = () => setIdx(Math.min(idx + 1, total - 1));
    const SKIP_ON_BACK = new Set([7, 13]); // loader + post-purchase — don't land here on back
    const back = () => setIdx(prev => {
      let n = Math.max(prev - 1, 0);
      while (n > 0 && SKIP_ON_BACK.has(n)) n -= 1;
      return n;
    });
    const shared = { idx, total, onNext: next, onBack: back, data, setData };
    const Comp = list[idx];
    return <div key={idx} style={{ width: '100%', height: '100%' }}><Comp {...shared} /></div>;
  }

  window.V2App = V2App;
})();
