// upprofiler — /score (free public tool) + /alerts

const WHOP_LINK = 'https://whop.com/linkupbyjasmin?a=whpreviews';

// Custom scanning visual — a rotating sweep arc + a flickering number.
// Avoids reusing ScoreRing because its value-change handler resets the
// dashoffset, which causes a "blink" when value changes faster than the
// internal 1.6s transition.
function ScanningView() {
  const stages = [
    { main: 'Reading your profile…',           sub: 'What are you actually claiming?' },
    { main: 'Pulling 30 days of receipts…',    sub: 'Every post. Every comment. Every word.' },
    { main: 'Comparing claims to content…',    sub: 'Is this the same human writing both?' },
    { main: 'Counting the gap…',               sub: 'Drift gets you buried. Conviction gets distributed.' },
    { main: 'Coach J is being honest…',        sub: 'No nice stuff. Just the data.' },
  ];
  const [stageIndex, setStageIndex] = useState(0);
  const [n, setN] = useState(0);

  useEffect(() => {
    const flickerEvery = 90;
    const stageEvery = 1700;

    const flicker = setInterval(() => {
      // Range tightens as stages progress.
      const ranges = [[0, 100], [15, 100], [30, 95], [45, 88]];
      const [lo, hi] = ranges[stageIndex] || [45, 88];
      setN(Math.floor(lo + Math.random() * (hi - lo + 1)));
    }, flickerEvery);

    const advance = setInterval(() => {
      setStageIndex(i => Math.min(stages.length - 1, i + 1));
    }, stageEvery);

    return () => { clearInterval(flicker); clearInterval(advance); };
  }, [stageIndex]);

  const size = 260;
  const strokeWidth = 14;
  const r = (size - strokeWidth) / 2;
  const c = 2 * Math.PI * r;
  // Sweep arc: ~25% of the ring is filled with the gradient, rotated.
  const arcLength = c * 0.25;
  const gapLength = c - arcLength;

  return (
    <div style={{ textAlign: 'center' }}>
      <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 32 }}>
        <div style={{ width: size, height: size, position: 'relative' }}>
          <svg width={size} height={size} style={{ position: 'absolute', inset: 0, animation: 'lup-spin 1.6s linear infinite' }}>
            <defs>
              <linearGradient id="scan-grad" x1="0" y1="0" x2="1" y2="1">
                <stop offset="0%" stopColor="#0D9488" stopOpacity="0" />
                <stop offset="100%" stopColor="#14B8A6" stopOpacity="1" />
              </linearGradient>
            </defs>
            <circle cx={size / 2} cy={size / 2} r={r}
              className="ring-track" strokeWidth={strokeWidth} fill="none" />
            <circle cx={size / 2} cy={size / 2} r={r}
              stroke="url(#scan-grad)"
              strokeWidth={strokeWidth}
              fill="none"
              strokeLinecap="round"
              strokeDasharray={`${arcLength} ${gapLength}`}
              transform={`rotate(-90 ${size / 2} ${size / 2})`} />
          </svg>
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', flexDirection: 'column',
            alignItems: 'center', justifyContent: 'center',
          }}>
            <div style={{
              fontSize: size * 0.30, fontWeight: 700, lineHeight: 1,
              color: 'var(--pink)', letterSpacing: '-0.04em',
              fontVariantNumeric: 'tabular-nums',
              minWidth: '3ch', textAlign: 'center',
            }}>
              {n}
            </div>
            <div style={{ marginTop: size * 0.04, display: 'flex', alignItems: 'center', gap: 8 }}>
              <span className="pulse-dot" />
              <span className="label-mono" style={{ fontSize: Math.max(11, size * 0.032) }}>Scanning</span>
            </div>
          </div>
        </div>
      </div>
      <h2 style={{ fontSize: 36, fontWeight: 800, letterSpacing: '-0.03em' }}>{stages[stageIndex].main}</h2>
      <div style={{
        marginTop: 12, fontSize: 17, color: 'var(--ink-soft)',
        fontStyle: 'italic', maxWidth: '52ch', margin: '12px auto 0',
        opacity: 0.9,
      }}>
        "{stages[stageIndex].sub}"
      </div>
      <div className="col gap-8 mt-32" style={{ maxWidth: 480, margin: '32px auto 0' }}>
        {stages.map((s, i) => {
          const status = i < stageIndex ? 'done' : i === stageIndex ? 'active' : 'pending';
          return (
            <div key={s.main} className="row gap-12" style={{
              padding: 12, background: 'var(--surface)', borderRadius: 8,
              border: '1px solid var(--line)',
              opacity: status === 'pending' ? 0.4 : 1,
              transition: 'opacity 0.3s',
            }}>
              <span style={{
                width: 8, height: 8, borderRadius: 100,
                background: status === 'done' ? 'var(--good)' : 'var(--pink)',
                animation: status === 'active' ? 'pulse 1.2s ease-in-out infinite' : 'none',
              }} />
              <span style={{ flex: 1, textAlign: 'left', fontWeight: 500, fontSize: 14 }}>
                {s.main.replace(/…$/, '')}
              </span>
              <span className="label-mono" style={{ color: status === 'done' ? 'var(--good)' : 'var(--mute)' }}>
                {status === 'done' ? '✓' : status === 'active' ? '…' : ''}
              </span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function urlFromRoute(route) {
  if (!route) return '';
  const q = route.indexOf('?');
  if (q === -1) return '';
  const params = new URLSearchParams(route.slice(q + 1));
  return params.get('url') || '';
}

function unlockTokenFromRoute(route) {
  if (!route) return '';
  const q = route.indexOf('?');
  if (q === -1) return '';
  const params = new URLSearchParams(route.slice(q + 1));
  return params.get('unlock') || '';
}

function unlockKey(url) {
  return `upprofiler-unlocked:${url}`;
}

// Score-band → suggested caption. Personalisable by the user before posting.
function suggestedCaption(score, firstName) {
  const n = score;
  if (n >= 85) {
    return `Just scored ${n}/100 on upprofiler — the free LinkedIn conviction test from Jasmin Alić's Link Up community.

Apparently my profile and my last 30 posts are telling the same story. (Big relief.)

Take 90 seconds to score yours 👇`;
  }
  if (n >= 70) {
    return `${n}/100. That's where my upprofiler conviction score landed.

Mostly aligned, but a few claims on my profile aren't showing up in my posts. Quiet drift — the kind that costs reach without you noticing.

upprofiler is the free LinkedIn audit Jasmin Alić built. Worth 90 seconds. Score yours 👇`;
  }
  if (n >= 55) {
    return `Just took the upprofiler conviction test. ${n}/100.

Verdict: mixed. Some of what my profile claims… isn't really showing up in what I post about. The gap is the throttle. Time to close it.

Score yours free 👇`;
  }
  return `Honest score: ${n}/100 on upprofiler.

The diagnosis: my profile and my posts read like two different people. Worth knowing.

This is the free conviction test from Jasmin Alić's Link Up community. Try yours, get an honest answer in 90 seconds 👇`;
}

function PageScore({ onNavigate, route }) {
  const initial = urlFromRoute(route);
  const [url, setUrl] = useState(initial);
  const [step, setStep] = useState(initial ? 'scanning' : 'input'); // input | scanning | result | error
  const [result, setResult] = useState(null);
  const [error, setError] = useState(null);

  // Email gate state
  const [unlocked, setUnlocked] = useState(() => {
    if (!initial) return false;
    try { return localStorage.getItem(unlockKey(initial)) === '1'; } catch { return false; }
  });
  const [email, setEmail] = useState('');
  const [emailSending, setEmailSending] = useState(false);
  const [emailError, setEmailError] = useState('');

  // Email gate scroll-target ref + one-shot pulse counter.
  // Bumped every time a 'Unlock with email' pill is clicked so the gate
  // re-renders with a fresh .cta-flash animation key.
  const emailGateRef = useRef(null);
  const [emailFlashKey, setEmailFlashKey] = useState(0);

  function jumpToEmailGate() {
    if (emailGateRef.current) {
      emailGateRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
    }
    setEmailFlashKey(k => k + 1);
  }

  // Share UI state
  const [caption, setCaption] = useState('');
  const [captionCopied, setCaptionCopied] = useState(false);
  const captionDefault = useMemo(() => {
    if (!result) return '';
    const firstName = (result.profile.name || '').split(' ')[0] || 'I';
    return suggestedCaption(result.score, firstName);
  }, [result]);
  // Reset caption when result arrives.
  useEffect(() => { if (result) setCaption(captionDefault); }, [captionDefault, result]);

  // AI Profile Rewrite state
  const [rewrite, setRewrite] = useState(null);          // { headline, about }
  const [rewriteStatus, setRewriteStatus] = useState('idle'); // idle | loading | ready | error
  const [rewriteError, setRewriteError] = useState('');
  const [rewriteTab, setRewriteTab] = useState('headline');
  const [copiedField, setCopiedField] = useState('');

  // Post hooks state — keyed by theme
  const [hooksByTheme, setHooksByTheme] = useState({}); // { theme: { status, hooks?, error? } }

  // If the route URL changes (e.g. user lands here via deep-link or hits Back/Forward),
  // re-trigger scanning with the new URL.
  useEffect(() => {
    if (initial && initial !== url) {
      setUrl(initial);
      setResult(null);
      setError(null);
      setStep('scanning');
    }
  }, [initial]);

  // Cross-device unlock: when the score URL carries an ?unlock=<token>
  // query (from a welcome email link), validate it against the DB and
  // flip the local unlock state if it belongs to a real subscriber for
  // this profile. Once consumed we strip the token from the hash so the
  // URL is shareable without leaking the secret.
  useEffect(() => {
    if (!initial) return;
    const token = unlockTokenFromRoute(route);
    if (!token || unlocked) return;
    (async () => {
      try {
        const resp = await fetch(`/api/check-unlock?url=${encodeURIComponent(initial)}&token=${encodeURIComponent(token)}`);
        if (!resp.ok) return;
        try { localStorage.setItem(unlockKey(initial), '1'); } catch {}
        setUnlocked(true);
        // Strip the token from the URL hash so the page stays shareable.
        const clean = `/score?url=${encodeURIComponent(initial)}`;
        if (window.location.hash !== `#${clean}`) {
          window.location.hash = clean;
        }
      } catch {}
    })();
  }, [route, initial, unlocked]);

  // Track whether the current scan should bypass cache (set by "Re-score now")
  const [forceFresh, setForceFresh] = useState(false);

  // Fire the API call when entering scanning step
  useEffect(() => {
    if (step !== 'scanning' || !url) return;
    let cancelled = false;
    (async () => {
      try {
        const freshParam = forceFresh ? '&fresh=1' : '';
        const resp = await fetch(`/api/score?url=${encodeURIComponent(url)}${freshParam}`);
        const data = await resp.json();
        if (cancelled) return;
        if (!resp.ok) {
          setError(data);
          setStep('error');
          return;
        }
        setResult(data);
        setStep('result');
        setForceFresh(false); // reset so subsequent re-renders don't keep forcing fresh
      } catch (e) {
        if (cancelled) return;
        setError({ error: 'network', message: 'Network hiccup. Try again in a moment.' });
        setStep('error');
      }
    })();
    return () => { cancelled = true; };
  }, [step, url]);

  function start() {
    const v = url.trim();
    if (!v) return;
    if (!/linkedin\.com\/in\//i.test(v)) {
      setError({ error: 'invalid_url', message: 'Use a linkedin.com/in/... URL.' });
      setStep('error');
      return;
    }
    setError(null);
    setResult(null);
    setStep('scanning');
  }

  function reset() {
    setResult(null);
    setError(null);
    setStep('input');
    setEmail('');
    setEmailError('');
    setForceFresh(false);
    window.location.hash = '/score';
  }

  function reScoreFresh() {
    if (!url) return;
    setResult(null);
    setError(null);
    setRewrite(null);
    setRewriteStatus('idle');
    setHooksByTheme({});
    setForceFresh(true);
    setStep('scanning');
  }

  // Format relative time for "scored X ago" display
  function relTime(iso) {
    if (!iso) return '';
    const ts = new Date(iso).getTime();
    if (Number.isNaN(ts)) return '';
    const s = Math.floor((Date.now() - ts) / 1000);
    if (s < 60) return 'just now';
    if (s < 3600) return `${Math.floor(s / 60)} min ago`;
    if (s < 86400) return `${Math.floor(s / 3600)} hr ago`;
    if (s < 604800) return `${Math.floor(s / 86400)} day${Math.floor(s / 86400) === 1 ? '' : 's'} ago`;
    return new Date(iso).toLocaleDateString();
  }

  async function submitEmail(e) {
    if (e && e.preventDefault) e.preventDefault();
    const v = email.trim();
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) {
      setEmailError("That doesn't look like an email.");
      return;
    }
    setEmailError('');
    setEmailSending(true);
    try {
      const resp = await fetch('/api/email', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          email: v,
          linkedinUrl: url,
          score: result.score,
          name: result.profile.name,
          photo: result.profile.photo,
          summary: result.summary,
          gaps: result.gaps,
          alignment: result.alignment,
        }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) {
        setEmailError(data.message || 'Email send failed. Try again.');
        return;
      }
      try { localStorage.setItem(unlockKey(url), '1'); } catch {}
      setUnlocked(true);
    } catch (err) {
      setEmailError('Network hiccup. Try again.');
    } finally {
      setEmailSending(false);
    }
  }

  // Bump this when the OG image visual changes (logo, layout, colors)
  // so browsers + LinkedIn re-fetch instead of serving a stale cached PNG.
  const OG_VERSION = '5';

  function buildShareUrl() {
    if (!result) return '';
    const shareParams = new URLSearchParams({
      url,
      name: result.profile.name || '',
      score: String(result.score),
      ...(result.profile.photo ? { photo: result.profile.photo } : {}),
      v: OG_VERSION,
    });
    return `${window.location.origin}/api/share?${shareParams.toString()}`;
  }

  function buildOgImageUrl() {
    if (!result) return '';
    const ogParams = new URLSearchParams({
      name: result.profile.name || '',
      score: String(result.score),
      ...(result.profile.photo ? { photo: result.profile.photo } : {}),
      v: OG_VERSION,
    });
    return `${window.location.origin}/api/og?${ogParams.toString()}`;
  }

  async function copyCaption() {
    try {
      await navigator.clipboard.writeText(caption);
      setCaptionCopied(true);
      setTimeout(() => setCaptionCopied(false), 2400);
      return true;
    } catch {
      setCaptionCopied(false);
      return false;
    }
  }

  async function shareToLinkedIn() {
    if (!result) return;
    // LinkedIn's share-offsite URL doesn't accept caption text (deprecated).
    // Next-best: copy the caption to clipboard, then open the share dialog
    // with the OG-preview URL — user pastes the caption once they're there.
    await copyCaption().catch(() => {});
    const linkedinShare = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(buildShareUrl())}`;
    window.open(linkedinShare, '_blank', 'noopener,noreferrer');
  }

  function previewShareImage() {
    if (!result) return;
    window.open(buildOgImageUrl(), '_blank', 'noopener,noreferrer');
  }

  async function generateRewrite() {
    if (!result) return;
    if (!unlocked) return; // gated; UI already shows lock pill
    setRewriteStatus('loading');
    setRewriteError('');
    try {
      const resp = await fetch('/api/rewrite', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          linkedinUrl: url,
          profile: result.profile,
          posts: result._posts || [],
          alignment: result.alignment,
          gaps: result.gaps,
          score: result.score,
        }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) {
        setRewriteError(data.message || 'Rewrite failed. Try again.');
        setRewriteStatus('error');
        return;
      }
      setRewrite(data);
      setRewriteStatus('ready');
    } catch (e) {
      setRewriteError('Network hiccup. Try again.');
      setRewriteStatus('error');
    }
  }

  async function copyText(field, text) {
    try {
      await navigator.clipboard.writeText(text);
      setCopiedField(field);
      setTimeout(() => setCopiedField(''), 2000);
    } catch {}
  }

  async function generateHooksFor(theme) {
    if (!result) return;
    setHooksByTheme(h => ({ ...h, [theme]: { status: 'loading' } }));
    try {
      const resp = await fetch('/api/hooks', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          theme,
          profile: result.profile,
          posts: result._posts || [],
        }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) {
        setHooksByTheme(h => ({ ...h, [theme]: { status: 'error', error: data.message || 'Failed.' } }));
        return;
      }
      setHooksByTheme(h => ({ ...h, [theme]: { status: 'ready', hooks: data.hooks || [] } }));
    } catch {
      setHooksByTheme(h => ({ ...h, [theme]: { status: 'error', error: 'Network hiccup.' } }));
    }
  }

  return (
    <div>
      <header className="nav" style={{ borderBottom: 'none', background: 'transparent' }}>
        <a onClick={() => onNavigate('/')} style={{ cursor: 'pointer' }}><Logo /></a>
        <div className="nav-right">
          <ThemeToggle />
          <button className="btn btn-tertiary btn-sm" onClick={() => onNavigate('/')}>Back</button>
        </div>
      </header>

      <section style={{ background: 'var(--gradient-hero)', padding: '80px 64px 120px', minHeight: 'calc(100vh - 64px)' }}>
        <div style={{ maxWidth: 1100, margin: '0 auto' }}>
          {step === 'input' && (
            <div style={{ textAlign: 'center' }}>
              <span className="tag soft">Free · no signup, no upsell</span>
              <h1 style={{ fontSize: 64, fontWeight: 800, letterSpacing: '-0.035em', lineHeight: 1.05, marginTop: 24 }}>
                Score your profile in <span className="pink">90 seconds.</span>
              </h1>
              <p style={{ fontSize: 19, color: 'var(--ink-soft)', marginTop: 24, maxWidth: '54ch', margin: '24px auto 0' }}>
                Paste your LinkedIn URL. I'll measure how convincingly your profile and your last 30 days of posts tell the same story.
                One number. The biggest gaps. Honest answer.
              </p>
              <form onSubmit={(e) => { e.preventDefault(); start(); }} style={{ marginTop: 48, maxWidth: 620, margin: '48px auto 0' }}>
                <div style={{ display: 'flex', gap: 8, background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 12, padding: 6, boxShadow: 'var(--shadow-card)' }}>
                  <input
                    value={url}
                    onChange={(e) => setUrl(e.target.value)}
                    placeholder="https://linkedin.com/in/jasminalic"
                    spellCheck={false}
                    autoComplete="off"
                    style={{
                      flex: 1, padding: '14px 18px', fontSize: 16, fontFamily: 'var(--font-display)',
                      border: 'none', background: 'transparent', outline: 'none', color: 'var(--ink)',
                    }}
                  />
                  <button type="submit" className="btn btn-primary btn-lg" style={{ padding: '14px 24px', fontSize: 16 }}>
                    Score me →
                  </button>
                </div>
              </form>
              <div className="label-mono" style={{ marginTop: 24 }}>Public profile data only · we never post anything</div>
            </div>
          )}
          {step === 'scanning' && (
            <ScanningView />
          )}
          {step === 'error' && (
            <div style={{ textAlign: 'center' }}>
              <span className="tag warn">Hit a snag</span>
              <h2 style={{ fontSize: 36, fontWeight: 800, letterSpacing: '-0.03em', marginTop: 16 }}>
                {error?.message || 'Something went sideways.'}
              </h2>
              {error?.detail && (
                <div className="muted" style={{ marginTop: 12, fontFamily: 'var(--font-mono)', fontSize: 12 }}>{error.detail}</div>
              )}
              <button className="btn btn-primary btn-lg" style={{ marginTop: 32 }} onClick={reset}>Try another URL</button>
            </div>
          )}
          {step === 'result' && result && (
            <div>
              <div style={{ textAlign: 'center', marginBottom: 32 }}>
                <span className="tag soft">Your free audit</span>
                <h2 style={{ fontSize: 44, fontWeight: 800, letterSpacing: '-0.03em', marginTop: 16, textWrap: 'balance' }}>
                  {result.profile.name ? `${result.profile.name.split(' ')[0]}, your conviction is ` : 'Your conviction is '}
                  <span className="pink">{result.score}</span>.
                </h2>
                <div className="muted" style={{ marginTop: 12 }}>
                  Based on {result.posts.count} post{result.posts.count === 1 ? '' : 's'} in the last 30 days
                  {result.profile.followerCount ? ` · ${result.profile.followerCount.toLocaleString()} followers` : ''}
                </div>
                {result.delta && result.previous && (
                  <div className="row gap-8" style={{ justifyContent: 'center', marginTop: 14, flexWrap: 'wrap', alignItems: 'center' }}>
                    <span style={{
                      display: 'inline-flex', alignItems: 'center', gap: 8,
                      padding: '6px 14px', borderRadius: 100,
                      background: result.delta.score > 0 ? 'rgba(31,171,137,0.12)' :
                                  result.delta.score < 0 ? 'rgba(244,165,43,0.12)' :
                                  'var(--bg)',
                      border: `1px solid ${
                        result.delta.score > 0 ? 'rgba(31,171,137,0.35)' :
                        result.delta.score < 0 ? 'rgba(244,165,43,0.35)' :
                        'var(--line)'
                      }`,
                      fontFamily: 'var(--font-mono)',
                      fontSize: 12, fontWeight: 600, letterSpacing: 1,
                      textTransform: 'uppercase',
                      color: result.delta.score > 0 ? 'var(--good)' :
                             result.delta.score < 0 ? 'var(--warn)' :
                             'var(--mute)',
                    }}>
                      {result.delta.score > 0 ? `↑ +${result.delta.score}` :
                       result.delta.score < 0 ? `↓ ${result.delta.score}` :
                       '→ No change'}
                      <span style={{ color: 'var(--mute)', fontWeight: 400, textTransform: 'none', letterSpacing: 0.5 }}>
                        vs {relTime(result.previous.scoredAt)} ({result.previous.score})
                      </span>
                    </span>
                  </div>
                )}
                {result.cached && (
                  <div className="row gap-12" style={{ justifyContent: 'center', marginTop: 14, flexWrap: 'wrap', alignItems: 'center' }}>
                    <span className="label-mono" style={{ color: 'var(--mute)' }}>
                      ⟳ Cached · scored {relTime(result.scoredAt)}
                    </span>
                    <button className="btn btn-tertiary btn-sm" onClick={reScoreFresh} style={{ padding: '4px 12px', fontSize: 11 }}>
                      Re-score now →
                    </button>
                  </div>
                )}
              </div>
              <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 32 }}>
                <ScoreRing value={result.score} size={300} delta={null} animateKey="result" />
              </div>
              <div className="card" style={{ background: 'var(--surface)', padding: 32, marginBottom: 24 }}>
                <div className="row between" style={{ marginBottom: 20 }}>
                  <div className="label-mono">Profile</div>
                  <div className="label-mono">{result.profile.location}</div>
                </div>
                <div className="row" style={{ gap: 20, alignItems: 'flex-start' }}>
                  {result.profile.photo ? (
                    <img
                      src={result.profile.photo}
                      alt={result.profile.name}
                      width={80}
                      height={80}
                      referrerPolicy="no-referrer"
                      onError={(e) => { e.target.style.display = 'none'; }}
                      style={{
                        width: 80, height: 80, borderRadius: 100, objectFit: 'cover',
                        border: '2px solid var(--line)', flexShrink: 0,
                      }}
                    />
                  ) : (
                    <div className="nav-avatar" style={{ width: 80, height: 80, fontSize: 26, flexShrink: 0 }}>
                      {(result.profile.name || '?').split(' ').map(n => n[0]).slice(0, 2).join('')}
                    </div>
                  )}
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.01em' }}>
                      {result.profile.name}
                    </div>
                    {result.profile.followerCount ? (
                      <div className="label-mono" style={{ marginTop: 4 }}>
                        {result.profile.followerCount.toLocaleString()} followers
                        {result.profile.connectionsCount ? ` · ${result.profile.connectionsCount.toLocaleString()} connections` : ''}
                      </div>
                    ) : null}
                    <div style={{ color: 'var(--ink-soft)', marginTop: 10, fontSize: 14, lineHeight: 1.5 }}>
                      {result.profile.headline}
                    </div>
                  </div>
                </div>
              </div>
              {/* Alignment card */}
              <div className="card" style={{ background: 'var(--surface)', padding: 32, marginBottom: 24 }}>
                <div className="row between" style={{ marginBottom: 20, alignItems: 'flex-start' }}>
                  <div>
                    <div className="label-mono">Profile-content alignment</div>
                    <div style={{ fontSize: 44, fontWeight: 800, color: 'var(--pink)', letterSpacing: '-0.03em', lineHeight: 1, marginTop: 8, fontVariantNumeric: 'tabular-nums' }}>
                      {result.alignment.overlapPercent}%
                    </div>
                  </div>
                  <div style={{ flex: 1, marginLeft: 32 }}>
                    <div className="label-mono" style={{ marginBottom: 6 }}>What you claim and what you post</div>
                    <div style={{ fontSize: 15, color: 'var(--ink-soft)', lineHeight: 1.55 }}>
                      {result.summary || (result.alignment.overlapPercent >= 60
                        ? 'Strong overlap. Profile and posts tell the same story.'
                        : result.alignment.overlapPercent >= 35
                          ? 'Some drift. A few claims aren\'t showing up in your posts.'
                          : 'High drift. Your profile and your posts read like two different people.')}
                    </div>
                  </div>
                </div>
                {result.alignment.sharedThemes.length > 0 && (
                  <div style={{ marginTop: 16 }}>
                    <div className="label-mono" style={{ marginBottom: 10 }}>On-brand themes (in both)</div>
                    <div className="chip-row">
                      {result.alignment.sharedThemes.map(w => (
                        <span key={w} className="chip shared">{w}</span>
                      ))}
                    </div>
                  </div>
                )}
              </div>

              {/* Share preview — mock LinkedIn post with editable caption */}
              <div className="card" style={{ background: 'var(--surface)', padding: 28, marginBottom: 24 }}>
                <div className="row between" style={{ marginBottom: 18, alignItems: 'flex-end' }}>
                  <div>
                    <div className="label-mono" style={{ color: 'var(--pink)' }}>Share preview</div>
                    <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.01em', marginTop: 4 }}>
                      This is what your post will look like
                    </div>
                  </div>
                  <button className="btn btn-tertiary btn-sm" onClick={previewShareImage}>
                    Open image →
                  </button>
                </div>

                {/* Mock LinkedIn post card */}
                <div style={{
                  background: 'var(--bg)',
                  border: '1px solid var(--line)',
                  borderRadius: 12,
                  overflow: 'hidden',
                }}>
                  {/* Author row */}
                  <div className="row" style={{ padding: '14px 16px', gap: 12, alignItems: 'flex-start' }}>
                    {result.profile.photo ? (
                      <img
                        src={result.profile.photo}
                        alt=""
                        width={48} height={48}
                        referrerPolicy="no-referrer"
                        style={{ width: 48, height: 48, borderRadius: 48, objectFit: 'cover', flexShrink: 0 }}
                      />
                    ) : (
                      <div className="nav-avatar" style={{ width: 48, height: 48, fontSize: 17, flexShrink: 0 }}>
                        {(result.profile.name || '?').split(' ').map(n => n[0]).slice(0, 2).join('')}
                      </div>
                    )}
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontWeight: 700, fontSize: 14, letterSpacing: '-0.01em' }}>
                        {result.profile.name || 'You'}
                      </div>
                      {result.profile.headline && (
                        <div style={{
                          fontSize: 12, color: 'var(--mute)', marginTop: 1, lineHeight: 1.3,
                          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 460,
                        }}>
                          {result.profile.headline}
                        </div>
                      )}
                      <div className="label-mono" style={{ marginTop: 3, fontSize: 10 }}>
                        Just now · 🌐
                      </div>
                    </div>
                  </div>

                  {/* Editable caption */}
                  <div style={{ padding: '0 16px 14px' }}>
                    <textarea
                      value={caption}
                      onChange={(e) => setCaption(e.target.value)}
                      spellCheck={false}
                      rows={5}
                      style={{
                        width: '100%',
                        background: 'transparent',
                        border: '1px dashed var(--line)',
                        borderRadius: 8,
                        padding: 10,
                        fontFamily: 'var(--font-display)',
                        fontSize: 14,
                        lineHeight: 1.5,
                        color: 'var(--ink)',
                        resize: 'vertical',
                        outline: 'none',
                      }}
                      onFocus={(e) => e.target.style.borderColor = 'var(--pink)'}
                      onBlur={(e) => e.target.style.borderColor = 'var(--line)'}
                    />
                    <div className="row between" style={{ marginTop: 6 }}>
                      <div className="label-mono" style={{ fontSize: 10 }}>
                        Editable · we copy this to your clipboard when you share
                      </div>
                      <button
                        className="btn btn-tertiary btn-sm"
                        onClick={copyCaption}
                        style={{ padding: '4px 8px', fontSize: 11 }}
                      >
                        {captionCopied ? '✓ Copied' : 'Copy caption'}
                      </button>
                    </div>
                  </div>

                  {/* The OG image preview */}
                  <a href={buildOgImageUrl()} target="_blank" rel="noopener" style={{ display: 'block', borderTop: '1px solid var(--line)', borderBottom: '1px solid var(--line)' }}>
                    <img
                      src={buildOgImageUrl()}
                      alt="Your score preview"
                      style={{
                        width: '100%', height: 'auto', display: 'block',
                        aspectRatio: '1200 / 630', background: '#060B16',
                      }}
                    />
                  </a>

                  {/* Mock engagement row */}
                  <div className="row between" style={{ padding: '8px 16px 12px' }}>
                    <div className="label-mono" style={{ fontSize: 10 }}>
                      👍 You and 124 others · 18 comments · 6 reposts
                    </div>
                    <div className="label-mono" style={{ fontSize: 10 }}>(preview)</div>
                  </div>
                </div>

                {/* Action row */}
                <div className="row gap-12" style={{ marginTop: 18, alignItems: 'center', flexWrap: 'wrap' }}>
                  <button className="btn btn-primary" onClick={shareToLinkedIn} style={{ flex: '1 1 240px' }}>
                    Copy caption + Share to LinkedIn →
                  </button>
                  {captionCopied && (
                    <div className="label-mono" style={{ color: 'var(--good)', fontSize: 12 }}>
                      ✓ Caption copied. Paste it when LinkedIn opens.
                    </div>
                  )}
                </div>
                <div className="label-mono" style={{ marginTop: 10, fontSize: 11, color: 'var(--mute)' }}>
                  LinkedIn doesn't accept pre-filled captions via URL, so we copy yours to the clipboard automatically.
                </div>
              </div>

              {/* Action plan card — gaps reframed as actions, top gap badged as Quick win */}
              <div className="card" style={{ background: 'var(--surface)', padding: 32 }}>
                <div className="row between" style={{ marginBottom: 16, alignItems: 'flex-end' }}>
                  <div>
                    <div className="label-mono" style={{ color: 'var(--pink)' }}>Your action plan</div>
                    <div style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.01em', marginTop: 4 }}>
                      {result.gaps.length === 0 ? 'Keep going — no major gaps.' : 'What to do this week.'}
                    </div>
                  </div>
                  {result.gaps.length > 0 && (
                    <span className="label-mono">{result.gaps.length} action{result.gaps.length === 1 ? '' : 's'}</span>
                  )}
                </div>
                {result.gaps.length === 0 && (
                  <div style={{ fontSize: 16, color: 'var(--ink-soft)' }}>
                    Profile and posts are aligned. Keep going. — Coach J ❤️
                  </div>
                )}
                <div className="col gap-12">
                  {result.gaps.map((g, i) => {
                    const isPreview = i === 0;
                    const showFull = unlocked || isPreview;
                    const isQuickWin = i === 0;
                    return (
                      <div key={i} className="row" style={{
                        padding: 16, borderRadius: 8,
                        border: `1px solid ${isQuickWin ? 'var(--pink-soft)' : 'var(--line)'}`,
                        background: isQuickWin ? 'var(--pink-wash)' : 'transparent',
                        gap: 16, alignItems: 'flex-start',
                        position: 'relative', overflow: 'hidden',
                      }}>
                        <span style={{ fontFamily: 'var(--font-mono)', color: 'var(--pink)', fontWeight: 500, marginTop: 2 }}>
                          {String(i + 1).padStart(2, '0')}
                        </span>
                        <div style={{ flex: 1, filter: showFull ? 'none' : 'blur(5px)', userSelect: showFull ? 'auto' : 'none' }}>
                          <div className="row gap-8" style={{ flexWrap: 'wrap', alignItems: 'center', marginBottom: 4 }}>
                            {isQuickWin && (
                              <span className="tag pink" style={{ fontSize: 9 }}>★ Quick win · est. +5 to +9</span>
                            )}
                          </div>
                          <div style={{ fontSize: 15, fontWeight: 600, lineHeight: 1.4 }}>{g.title}</div>
                          {g.detail && <div style={{ fontSize: 14, color: 'var(--ink-soft)', marginTop: 4, lineHeight: 1.5 }}>{g.detail}</div>}
                        </div>
                        {!showFull && (
                          <button onClick={jumpToEmailGate} className="tag soft" style={{ alignSelf: 'center', cursor: 'pointer', border: 'none', fontFamily: 'inherit' }}>
                            🔒 Unlock with email
                          </button>
                        )}
                      </div>
                    );
                  })}
                </div>

                {/* AI Profile Rewrite — gated behind email unlock */}
                <div style={{
                  marginTop: 32,
                  background: 'var(--bg)',
                  border: '1px solid var(--line)',
                  borderRadius: 12,
                  padding: 24,
                  position: 'relative',
                }}>
                  <div className="row between" style={{ alignItems: 'flex-start', flexWrap: 'wrap', gap: 12, marginBottom: rewriteStatus === 'idle' ? 12 : 18 }}>
                    <div>
                      <div className="label-mono" style={{ color: 'var(--pink)' }}>★ AI Profile Rewrite</div>
                      <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.01em', marginTop: 4 }}>
                        Close the gap with a new headline + About.
                      </div>
                      <div style={{ fontSize: 13, color: 'var(--ink-soft)', marginTop: 4, lineHeight: 1.5 }}>
                        AI rewrites your profile copy to match what you actually post. Edits in 30 days = re-score.
                      </div>
                    </div>
                    {!unlocked && (
                      <div className="row gap-8" style={{ alignItems: 'center', flexWrap: 'wrap' }}>
                        <button onClick={jumpToEmailGate} className="tag soft" style={{ cursor: 'pointer', border: 'none', fontFamily: 'inherit' }}>
                          🔒 Unlock with email
                        </button>
                      </div>
                    )}
                    {unlocked && rewriteStatus === 'idle' && (
                      <button className="btn btn-primary" onClick={generateRewrite} style={{ padding: '12px 20px', fontSize: 14, whiteSpace: 'nowrap' }}>
                        Generate rewrite →
                      </button>
                    )}
                    {unlocked && rewriteStatus === 'ready' && (
                      <button className="btn btn-tertiary btn-sm" onClick={generateRewrite} style={{ padding: '6px 12px', fontSize: 12 }}>
                        ↻ Regenerate
                      </button>
                    )}
                  </div>

                  {!unlocked && (
                    <div style={{
                      marginTop: 14,
                      padding: '12px 14px',
                      background: 'var(--pink-wash)',
                      border: '1px dashed var(--pink-soft)',
                      borderRadius: 8,
                      fontSize: 13,
                      color: 'var(--ink-soft)',
                      lineHeight: 1.5,
                    }}>
                      Drop your email below to unlock the rewrite — AI generates a new headline + About in Coach J's voice, with copy buttons. Free.
                    </div>
                  )}

                  {rewriteStatus === 'loading' && (
                    <div className="row gap-12" style={{ padding: '20px 0', alignItems: 'center' }}>
                      <div style={{
                        width: 28, height: 28, borderRadius: 100,
                        border: '3px solid var(--line)',
                        borderTopColor: 'var(--pink)',
                        animation: 'lup-spin 0.9s linear infinite',
                      }} />
                      <div>
                        <div style={{ fontSize: 14, fontWeight: 600 }}>AI is rewriting your profile…</div>
                        <div className="label-mono" style={{ marginTop: 2 }}>About 10–15 seconds. Reading your posts to match your voice.</div>
                      </div>
                    </div>
                  )}

                  {rewriteStatus === 'error' && (
                    <div className="row gap-12" style={{
                      padding: '12px 14px',
                      background: 'rgba(244,165,43,0.08)',
                      border: '1px solid rgba(244,165,43,0.25)',
                      borderRadius: 8, alignItems: 'center',
                    }}>
                      <span className="tag warn">Snag</span>
                      <span style={{ fontSize: 13, color: 'var(--ink-soft)', flex: 1 }}>{rewriteError}</span>
                      <button className="btn btn-tertiary btn-sm" onClick={generateRewrite}>Retry</button>
                    </div>
                  )}

                  {rewriteStatus === 'ready' && rewrite && (
                    <div>
                      <div className="row gap-8" style={{ marginBottom: 14 }}>
                        {['headline', 'about'].map(t => (
                          <button
                            key={t}
                            onClick={() => setRewriteTab(t)}
                            className="label-mono"
                            style={{
                              padding: '6px 14px', borderRadius: 100,
                              background: rewriteTab === t ? 'var(--pink)' : 'transparent',
                              color: rewriteTab === t ? '#fff' : 'var(--mute)',
                              border: `1px solid ${rewriteTab === t ? 'var(--pink)' : 'var(--line)'}`,
                              cursor: 'pointer',
                            }}>
                            {t === 'headline' ? 'Headline' : 'About'}
                          </button>
                        ))}
                      </div>

                      {rewriteTab === 'headline' && (
                        <div className="col gap-12">
                          <div style={{ padding: 14, background: 'rgba(244,165,43,0.06)', borderRadius: 8, borderLeft: '3px solid var(--warn)' }}>
                            <div className="label-mono" style={{ color: 'var(--warn)', marginBottom: 6 }}>Before · your current</div>
                            <div style={{ fontSize: 14, color: 'var(--ink-soft)', lineHeight: 1.55 }}>{rewrite.headline.before || '(empty)'}</div>
                          </div>
                          <div style={{ padding: 14, background: 'var(--pink-wash)', borderRadius: 8, borderLeft: '3px solid var(--pink)' }}>
                            <div className="row between" style={{ marginBottom: 6, alignItems: 'flex-end' }}>
                              <div className="label-mono" style={{ color: 'var(--pink-deep)' }}>After · proposed</div>
                              <button className="btn btn-tertiary btn-sm" onClick={() => copyText('headline', rewrite.headline.after)} style={{ padding: '2px 10px', fontSize: 11 }}>
                                {copiedField === 'headline' ? '✓ Copied' : 'Copy headline'}
                              </button>
                            </div>
                            <div style={{ fontSize: 15, fontWeight: 500, color: 'var(--ink)', lineHeight: 1.55 }}>{rewrite.headline.after}</div>
                          </div>
                          {rewrite.headline.reasoning && (
                            <div className="row gap-8" style={{ padding: 10, background: 'var(--surface)', borderRadius: 6 }}>
                              <span className="label-mono" style={{ color: 'var(--pink)' }}>Why</span>
                              <span style={{ fontSize: 12, color: 'var(--ink-soft)', lineHeight: 1.5 }}>{rewrite.headline.reasoning}</span>
                            </div>
                          )}
                        </div>
                      )}

                      {rewriteTab === 'about' && (
                        <div className="col gap-12">
                          <div style={{ padding: 14, background: 'rgba(244,165,43,0.06)', borderRadius: 8, borderLeft: '3px solid var(--warn)' }}>
                            <div className="label-mono" style={{ color: 'var(--warn)', marginBottom: 6 }}>Before · your current</div>
                            <div style={{ fontSize: 13, color: 'var(--ink-soft)', lineHeight: 1.55, whiteSpace: 'pre-wrap', maxHeight: 180, overflow: 'auto' }}>
                              {rewrite.about.before || '(empty)'}
                            </div>
                          </div>
                          <div style={{ padding: 14, background: 'var(--pink-wash)', borderRadius: 8, borderLeft: '3px solid var(--pink)' }}>
                            <div className="row between" style={{ marginBottom: 6, alignItems: 'flex-end' }}>
                              <div className="label-mono" style={{ color: 'var(--pink-deep)' }}>After · proposed</div>
                              <button className="btn btn-tertiary btn-sm" onClick={() => copyText('about', rewrite.about.after)} style={{ padding: '2px 10px', fontSize: 11 }}>
                                {copiedField === 'about' ? '✓ Copied' : 'Copy About'}
                              </button>
                            </div>
                            <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--ink)', lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{rewrite.about.after}</div>
                          </div>
                          {rewrite.about.reasoning && (
                            <div className="row gap-8" style={{ padding: 10, background: 'var(--surface)', borderRadius: 6 }}>
                              <span className="label-mono" style={{ color: 'var(--pink)' }}>Why</span>
                              <span style={{ fontSize: 12, color: 'var(--ink-soft)', lineHeight: 1.5 }}>{rewrite.about.reasoning}</span>
                            </div>
                          )}
                        </div>
                      )}

                      <div className="label-mono" style={{ marginTop: 14, fontSize: 10, color: 'var(--mute)' }}>
                        Want unlimited rewrites + Jasmin's review? <a href={WHOP_LINK} target="_blank" rel="noopener" style={{ color: 'var(--pink)', textDecoration: 'underline' }}>Inside Link Up</a>.
                      </div>
                    </div>
                  )}
                </div>

                {/* What you're missing — themes with hook generators (silent themes only) */}
                {(result.alignment.profileOnlyThemes.length > 0 || result.alignment.postsOnlyThemes.length > 0) && (
                  <div style={{ marginTop: 24, position: 'relative' }}>
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, filter: unlocked ? 'none' : 'blur(5px)', userSelect: unlocked ? 'auto' : 'none' }}>
                      <div>
                        <div className="label-mono" style={{ marginBottom: 10, color: 'var(--warn)' }}>Claimed but silent · post about these</div>
                        <div className="col gap-8">
                          {result.alignment.profileOnlyThemes.map(w => {
                            const h = hooksByTheme[w];
                            return (
                              <div key={w} className="col gap-8">
                                <div className="row between" style={{ alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                                  <span className="chip warn">{w}</span>
                                  <button
                                    className="btn btn-tertiary btn-sm"
                                    disabled={!unlocked || h?.status === 'loading'}
                                    onClick={() => generateHooksFor(w)}
                                    style={{ padding: '4px 10px', fontSize: 11 }}
                                  >
                                    {h?.status === 'loading' ? 'Writing…' :
                                     h?.status === 'ready' ? '↻ More hooks' :
                                     '💡 Get post hooks'}
                                  </button>
                                </div>
                                {h?.status === 'ready' && h.hooks?.length > 0 && (
                                  <div className="col gap-6" style={{ paddingLeft: 8, borderLeft: '2px solid var(--pink-soft)', marginLeft: 4 }}>
                                    {h.hooks.map((hook, hi) => (
                                      <div key={hi} className="row" style={{ gap: 8, alignItems: 'flex-start', padding: '6px 10px', background: 'var(--surface)', borderRadius: 6 }}>
                                        <div style={{ flex: 1, fontSize: 13, lineHeight: 1.4 }}>
                                          <div style={{ fontWeight: 500, color: 'var(--ink)' }}>"{hook.text}"</div>
                                          {hook.angle && <div className="label-mono" style={{ marginTop: 2, fontSize: 9 }}>{hook.angle}</div>}
                                        </div>
                                        <button
                                          className="btn btn-tertiary btn-sm"
                                          onClick={() => copyText(`hook-${w}-${hi}`, hook.text)}
                                          style={{ padding: '2px 8px', fontSize: 11 }}
                                        >
                                          {copiedField === `hook-${w}-${hi}` ? '✓' : 'Copy'}
                                        </button>
                                      </div>
                                    ))}
                                  </div>
                                )}
                                {h?.status === 'error' && (
                                  <div className="label-mono" style={{ color: 'var(--warn)', fontSize: 10, paddingLeft: 8 }}>{h.error}</div>
                                )}
                              </div>
                            );
                          })}
                        </div>
                      </div>
                      <div>
                        <div className="label-mono" style={{ marginBottom: 10, color: 'var(--good)' }}>Posted but unclaimed · add to profile</div>
                        <div className="chip-row">
                          {result.alignment.postsOnlyThemes.map(w => (
                            <span key={w} className="chip good">{w}</span>
                          ))}
                        </div>
                      </div>
                    </div>
                  </div>
                )}

                {/* Email gate — clickable lock pills jump here and trigger a one-shot pulse */}
                {!unlocked && (
                  <div ref={emailGateRef} key={emailFlashKey} className={emailFlashKey > 0 ? 'cta-flash' : ''} style={{
                    marginTop: 32, padding: 28,
                    background: 'var(--gradient-hero)', borderRadius: 12,
                    border: '1px solid var(--pink-soft)',
                    scrollMarginTop: 80,
                  }}>
                    <div className="label-mono" style={{ color: 'var(--pink)' }}>Unlock the full report</div>
                    <div style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.01em', marginTop: 6 }}>
                      Get all gaps + monthly re-scores in your inbox.
                    </div>
                    <div style={{ color: 'var(--ink-soft)', marginTop: 6, fontSize: 14, lineHeight: 1.55 }}>
                      Free. Your score changes when your content does — we'll send you a fresh number once a month so you can watch it move. Unsubscribe anytime.
                    </div>
                    <form onSubmit={submitEmail} style={{ marginTop: 18 }}>
                      <div style={{
                        display: 'flex', gap: 8,
                        background: 'var(--surface)',
                        border: `1px solid ${emailError ? 'var(--warn)' : 'var(--line)'}`,
                        borderRadius: 10, padding: 5,
                      }}>
                        <input
                          type="email"
                          value={email}
                          onChange={(e) => { setEmail(e.target.value); if (emailError) setEmailError(''); }}
                          placeholder="you@email.com"
                          autoComplete="email"
                          style={{
                            flex: 1, padding: '12px 14px', fontSize: 15, fontFamily: 'var(--font-display)',
                            border: 'none', background: 'transparent', outline: 'none', color: 'var(--ink)',
                          }}
                        />
                        <button type="submit" className="btn btn-primary" disabled={emailSending} style={{ padding: '10px 20px', fontSize: 14 }}>
                          {emailSending ? 'Sending…' : 'Unlock →'}
                        </button>
                      </div>
                      {emailError && <div className="label-mono" style={{ color: 'var(--warn)', marginTop: 8 }}>{emailError}</div>}
                    </form>
                  </div>
                )}
                {unlocked && (
                  <div style={{
                    marginTop: 24, padding: 16,
                    background: 'rgba(31,171,137,0.06)',
                    border: '1px solid rgba(31,171,137,0.2)',
                    borderRadius: 10,
                  }}>
                    <div className="row gap-12">
                      <span className="tag good">✓ Sent</span>
                      <span style={{ fontSize: 14, color: 'var(--ink-soft)' }}>
                        Full report on its way to your inbox. Monthly re-score starts next month.
                      </span>
                    </div>
                  </div>
                )}

                {/* Link Up CTA (always shown, affiliate-tagged) */}
                <div style={{ marginTop: 32, padding: 24, background: 'var(--gradient-hero)', borderRadius: 12, textAlign: 'center' }}>
                  <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.01em' }}>
                    Want the rest of the work? <span className="pink">Join Link Up.</span>
                  </div>
                  <div style={{ color: 'var(--ink-soft)', marginTop: 8, fontSize: 14 }}>
                    Coaching, community, real feedback — from Jasmin and the crew, in 70+ countries.
                  </div>
                  <a href={WHOP_LINK} target="_blank" rel="noopener" className="btn btn-primary" style={{ marginTop: 16, textDecoration: 'none' }}>
                    See what's inside Link Up →
                  </a>
                </div>
                <div className="row gap-12" style={{ justifyContent: 'center', marginTop: 24 }}>
                  <button className="btn btn-tertiary btn-sm" onClick={reset}>Score another profile</button>
                </div>
              </div>
            </div>
          )}
        </div>
      </section>
    </div>
  );
}

function PageAlerts({ onNavigate, score: globalScore }) {
  const alerts = [
    {
      date: '28 Apr 2026', time: '06:14 GMT', kind: 'major',
      title: 'Dwell-time reweighted +22%',
      summary: 'LinkedIn shifted dwell-weighting overnight. Carousels and 800–1,200 char posts win. Reactions matter less than they did yesterday. Time to ship a carousel.',
      impact: -0.4, posts: 30,
    },
    {
      date: '11 Apr 2026', time: '14:02 GMT', kind: 'minor',
      title: 'Comment-velocity window extended to 4 hours',
      summary: 'The first 4-hour engagement window now matters more vs 2-hour. You finally have flexibility on post timing — go enjoy your morning coffee.',
      impact: +1.1, posts: 12,
    },
    {
      date: '02 Apr 2026', time: '09:30 GMT', kind: 'major',
      title: 'External link penalty softened',
      summary: 'Native links no longer get buried as hard. Newsletters and case-study links recovered ~14% reach. If you\'ve been hiding your offer in comments, stop.',
      impact: +2.3, posts: 8,
    },
    {
      date: '24 Mar 2026', time: '12:45 GMT', kind: 'minor',
      title: 'Hashtag relevance check tightened',
      summary: 'Off-topic hashtags now actively reduce reach instead of being ignored. Audit yours — drop the ones that don\'t map to your story pillars.',
      impact: -1.6, posts: 22,
    },
  ];
  return (
    <div>
      <Nav current="/alerts" onNavigate={onNavigate} score={globalScore} delta={9} />
      <div className="page">
        <PageHeader
          id="/alerts"
          name="Heads-up alerts"
          sub="When LinkedIn shifts the rules — and they will — we rescore your last 30 posts and tell you the play. So you never wake up wondering why reach tanked."
          tag={{ kind: 'pink', label: 'Auto-rescoring on' }}
          right={<span className="label-mono">4 events · last 60d</span>}
        />

        <div className="col gap-16">
          {alerts.map((a, i) => (
            <div key={i} className="card hoverable" style={{ padding: 28 }}>
              <div className="row between" style={{ marginBottom: 12 }}>
                <div className="row gap-12">
                  <span className={`tag ${a.kind === 'major' ? 'pink' : ''}`}>{a.kind === 'major' ? 'Major shift' : 'Minor shift'}</span>
                  <span className="label-mono">{a.date} · {a.time}</span>
                </div>
                <div className="row gap-16">
                  <div className="label-mono">Re-scored <strong style={{ color: 'var(--ink)' }}>{a.posts}</strong> posts</div>
                  <div style={{ fontFamily: 'var(--font-mono)', fontWeight: 500, color: a.impact >= 0 ? 'var(--good)' : 'var(--warn)' }}>
                    {a.impact >= 0 ? '↑' : '↓'} {Math.abs(a.impact)} conviction
                  </div>
                </div>
              </div>
              <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em', marginBottom: 8 }}>{a.title}</div>
              <div style={{ color: 'var(--ink-soft)', fontSize: 15, lineHeight: 1.55 }}>{a.summary}</div>
              <div className="row gap-12 mt-16">
                <button className="btn btn-tertiary btn-sm">See affected posts →</button>
                <button className="btn btn-tertiary btn-sm" onClick={() => onNavigate('/check')}>Draft something now →</button>
              </div>
            </div>
          ))}
        </div>
      </div>
      <Footer />
    </div>
  );
}

window.PageScore = PageScore;
window.PageAlerts = PageAlerts;
