// Pages — full Framer Motion animations

const { useState: useS, useEffect: useE, useRef: useR } = React;
const _FM = window.Motion || {};
var { motion, AnimatePresence, useScroll, useTransform, useSpring, useMotionValue } = _FM;
if (!motion) {
  motion = new Proxy({}, {
    get: (_, tag) => React.forwardRef((inputProps, ref) => {
      const props = Object.assign({}, inputProps || {});
      const children = props.children;
      delete props.children;
      delete props.initial;
      delete props.animate;
      delete props.exit;
      delete props.transition;
      delete props.whileHover;
      delete props.whileTap;
      delete props.layout;
      delete props.variants;
      delete props.custom;
      return React.createElement(tag, Object.assign({}, props, { ref }), children);
    }),
  });
}
if (!AnimatePresence) AnimatePresence = ({ children }) => React.createElement(React.Fragment, null, children);
if (!useScroll) useScroll = () => ({ scrollYProgress: 0 });
if (!useTransform) useTransform = (_value, _input, output) => Array.isArray(output) ? output[0] : 0;
if (!useSpring) useSpring = (value) => value;
if (!useMotionValue) useMotionValue = (initial) => ({ get: () => initial, set: () => {} });

function highlightTranscriptText(text = '') {
  const pattern = /(lemon water|seven and nine hours|fifty percent|528 Hz)/ig;
  return String(text).split(pattern).map((piece, index) => (
    /^(lemon water|seven and nine hours|fifty percent|528 Hz)$/i.test(piece)
      ? <mark key={`${piece}-${index}`}>{piece}</mark>
      : piece
  ));
}

// ═══════════════════════════════════════════════════════════════════════════════
// HOME PAGE
// ═══════════════════════════════════════════════════════════════════════════════
const HomePage = ({ go, setUrl, url, onAnalyze }) => {
  const heroRef = useR(null);
  const { scrollYProgress } = useScroll({ target: heroRef, offset: ['start start', 'end start'] });
  const heroY = useTransform(scrollYProgress, [0, 1], [0, 120]);
  const heroOpacity = useTransform(scrollYProgress, [0, 0.6], [1, 0]);

  const particles = [
    { x: 10, y: 20, delay: 0, size: 6 },
    { x: 85, y: 15, delay: 1.2, size: 10 },
    { x: 70, y: 60, delay: 0.5, size: 7 },
    { x: 20, y: 75, delay: 2, size: 5 },
    { x: 92, y: 80, delay: 0.8, size: 8 },
    { x: 50, y: 10, delay: 1.8, size: 4 },
  ];

  return (
    <>
      {/* HERO */}
      <motion.div className="hero" ref={heroRef} style={{ position: 'relative' }}>
        {/* Floating particles */}
        <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', pointerEvents: 'none' }}>
          {particles.map((p, i) => <FloatingParticle key={i} {...p} />)}
        </div>

        {/* Parallax bg circle */}
        <motion.div style={{
          position: 'absolute', top: '-30%', right: '-10%', width: '60vw', height: '60vw',
          borderRadius: '50%', background: 'radial-gradient(circle, oklch(0.48 0.18 268 / 0.04) 0%, transparent 70%)',
          pointerEvents: 'none', y: heroY,
        }} />

        <motion.div className="container" style={{ position: 'relative', zIndex: 1, opacity: heroOpacity }}>
          <div className="hero-grid">
            <div>
              {/* Eyebrow */}
              <Reveal delay={0}>
                <motion.span className="eyebrow" whileHover={{ scale: 1.03 }}>
                  <span className="dot"></span>Trusted AI fact-checking for social video
                </motion.span>
              </Reveal>

              {/* Headline — word-by-word stagger */}
              <h1 aria-label="Fact-Check Any Social Media Video in Seconds" style={{ margin: '22px 0 24px', padding: 0 }}>
                {['Fact-check', 'any', 'social', 'media', 'video'].map((word, i) => (
                  <motion.span
                    key={i}
                    style={{ display: 'inline-block', marginRight: '0.25em', fontSize: 'clamp(44px, 6.6vw, 78px)', fontWeight: 600, letterSpacing: '-0.035em', lineHeight: 1.02 }}
                    initial={{ opacity: 0, y: 30, filter: 'blur(6px)' }}
                    animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
                    transition={{ delay: 0.15 + i * 0.08, duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
                  >
                    {word}
                  </motion.span>
                ))}
                <motion.span
                  style={{ display: 'inline-block', fontSize: 'clamp(44px, 6.6vw, 78px)', lineHeight: 1.02 }}
                  initial={{ opacity: 0, y: 30, filter: 'blur(6px)' }}
                  animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
                  transition={{ delay: 0.15 + 5 * 0.08, duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
                >
                  <span className="serif accent">in seconds.</span>
                </motion.span>
              </h1>

              <Reveal delay={0.55}>
                <p className="lede">
                  Paste a TikTok, YouTube, Instagram, X, or Facebook video link. VidVerdict transcribes claims, checks evidence, and shows what's true, false, misleading, or unsupported.
                </p>
              </Reveal>

              <Reveal delay={0.7}>
                <div className="url-card">
                  <UrlInput value={url} onChange={setUrl} onSubmit={onAnalyze} />
                  <motion.div
                    className="trust-line"
                    initial={{ opacity: 0 }}
                    animate={{ opacity: 1 }}
                    transition={{ delay: 0.9, duration: 0.5 }}
                  >
                    <I.CheckCircle size={14} />
                    No signup required for first checks
                  </motion.div>
                </div>
              </Reveal>

              {/* Badges with stagger */}
              <div className="badges" style={{ marginTop: 28 }}>
                {[
                  { icon: <I.Sparkles size={12} />, label: 'AI Verified' },
                  { icon: <I.FileCheck size={12} />, label: 'Source-backed' },
                  { icon: <I.Zap size={12} />, label: 'Fast Results' },
                  { icon: <I.Lock size={12} />, label: 'Privacy Focused' },
                ].map((b, i) => (
                  <motion.span
                    key={b.label}
                    className="badge"
                    initial={{ opacity: 0, scale: 0.8, y: 10 }}
                    animate={{ opacity: 1, scale: 1, y: 0 }}
                    transition={{ delay: 0.85 + i * 0.07, duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
                    whileHover={{ scale: 1.05, y: -2 }}
                    whileTap={{ scale: 0.96 }}
                  >
                    {b.icon} {b.label}
                  </motion.span>
                ))}
              </div>
            </div>

            {/* Hero Visual */}
            <Reveal delay={0.3} variants={scaleIn}>
              <HeroVisual />
            </Reveal>
          </div>
        </motion.div>
      </motion.div>

      {/* HOW IT WORKS */}
      <section className="block" id="how-it-works">
        <div className="container">
          <Reveal><div className="section-eyebrow">How it works</div></Reveal>
          <Reveal delay={0.06}>
            <h2 className="section-title">Three steps from link to <span className="serif">verdict.</span></h2>
          </Reveal>
          <Reveal delay={0.12}><div className="section-sub">No browser extension. No account. Just paste a URL.</div></Reveal>

          <div className="steps">
            {[
              { n: '01', t: 'Paste a Link', d: 'Drop any public video URL from TikTok, Instagram, YouTube, X, or Facebook.', i: <I.Link size={28} /> },
              { n: '02', t: 'AI Extracts Claims', d: 'Speech becomes text and factual claims are detected and isolated from opinion.', i: <I.Mic size={28} /> },
              { n: '03', t: 'Get Truth Report', d: 'Receive source-backed verdicts with confidence and citations, instantly.', i: <I.FileCheck size={28} /> },
            ].map((s, i) => (
              <Reveal key={s.n} delay={0.1 + i * 0.1}>
                <motion.div
                  className="step-card"
                  whileHover={{ y: -6, transition: { duration: 0.3, ease: [0.16, 1, 0.3, 1] } }}
                  whileTap={{ scale: 0.98 }}
                >
                  <div className="step-head">
                    <motion.div
                      className="step-num"
                      initial={{ opacity: 0, x: -10 }}
                      whileInView={{ opacity: 1, x: 0 }}
                      viewport={{ once: true }}
                      transition={{ delay: 0.2 + i * 0.1 }}
                    >
                      {s.n}
                    </motion.div>
                    <motion.div
                      className="step-icon-box"
                      whileHover={{ rotate: 3, scale: 1.04 }}
                      transition={{ type: 'spring', stiffness: 300, damping: 20 }}
                    >
                      {React.cloneElement(s.i, { size: 20 })}
                    </motion.div>
                  </div>
                  <div className="step-title">{s.t}</div>
                  <div className="step-desc">{s.d}</div>
                  <div className="step-flow">{i < 2 ? 'Next step' : 'Ready'}</div>

                  {/* Connecting line */}
                  {i < 2 && (
                    <motion.div style={{
                      position: 'absolute', top: 56, right: -10, width: 20, height: 1,
                      background: 'linear-gradient(90deg, var(--line-2), transparent)',
                      display: 'none',
                    }} />
                  )}
                </motion.div>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      {/* SAMPLE REPORT */}
      <section className="block">
        <div className="container">
          <Reveal><div className="section-eyebrow">A real report</div></Reveal>
          <Reveal delay={0.06}><h2 className="section-title">See what a verdict <span className="serif">looks like.</span></h2></Reveal>
          <Reveal delay={0.12}><div className="section-sub">An interactive sample. Click around — every section is real.</div></Reveal>
          <Reveal delay={0.18}><SampleReport go={go} /></Reveal>
        </div>
      </section>

      {/* COMPARISON */}
      <section className="block">
        <div className="container">
          <Reveal><div className="section-eyebrow">The difference</div></Reveal>
          <Reveal delay={0.06}><h2 className="section-title">Manual research vs. <span className="serif">VidVerdict.</span></h2></Reveal>
          <Reveal delay={0.12}>
            <div className="compare">
              <div className="compare-col left">
                <div className="compare-h">Manual research</div>
                {[
                  [<I.Clock size={16} />, 'Hours of digging'],
                  [<I.X size={16} />, 'Confusing source quality'],
                  [<I.Globe size={16} />, 'Random links from search'],
                  [<I.Heart size={16} />, 'Driven by emotion'],
                ].map(([icon, text], i) => (
                  <motion.div
                    key={text}
                    className="compare-row"
                    initial={{ opacity: 0, x: -16 }}
                    whileInView={{ opacity: 1, x: 0 }}
                    viewport={{ once: true }}
                    transition={{ delay: 0.1 + i * 0.08, duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
                  >
                    {icon} {text}
                  </motion.div>
                ))}
              </div>
              <div className="compare-col right">
                <div className="compare-h us">
                  <img className="logo-full small" src="assets/wordmark.png" alt="VidVerdict" width="122" height="31" decoding="async" />
                  <span className="compare-badge">AI checked</span>
                </div>
                {[
                  [<I.Zap size={16} style={{ color: 'var(--accent)' }} />, 'Minutes, not hours'],
                  [<I.CheckCircle size={16} style={{ color: 'var(--accent)' }} />, 'Clear verdict per claim'],
                  [<I.Shield size={16} style={{ color: 'var(--accent)' }} />, 'Trusted institutional sources'],
                  [<I.FileCheck size={16} style={{ color: 'var(--accent)' }} />, 'Driven by evidence'],
                ].map(([icon, text], i) => (
                  <motion.div
                    key={text}
                    className="compare-row"
                    style={{ color: 'var(--ink)' }}
                    initial={{ opacity: 0, x: 16 }}
                    whileInView={{ opacity: 1, x: 0 }}
                    viewport={{ once: true }}
                    transition={{ delay: 0.1 + i * 0.08, duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
                    whileHover={{ x: 4 }}
                  >
                    {icon} {text}
                  </motion.div>
                ))}
              </div>
            </div>
          </Reveal>
        </div>
      </section>

      {/* USE CASES */}
      <section className="block">
        <div className="container">
          <Reveal><div className="section-eyebrow">Use cases</div></Reveal>
          <Reveal delay={0.06}><h2 className="section-title">For every video that <span className="serif">made you pause.</span></h2></Reveal>
          <div className="usecases">
            {[
              { i: <I.Stethoscope size={18} />, t: 'Health Advice', d: 'Wellness tips, supplements, miracle cures.' },
              { i: <I.Globe size={18} />, t: 'Political Claims', d: 'Speeches, campaign clips, viral statements.' },
              { i: <I.Coin size={18} />, t: 'Finance Gurus', d: 'Stock picks, crypto calls, "guaranteed" returns.' },
              { i: <I.Newspaper size={18} />, t: 'Breaking News', d: 'Out-of-context clips and viral headlines.' },
              { i: <I.Atom size={18} />, t: 'Science Videos', d: 'Pop-science explainers and study summaries.' },
              { i: <I.Star size={18} />, t: 'Influencer Facts', d: 'Lifestyle claims and product testimonials.' },
            ].map((u, i) => (
              <Reveal key={u.t} delay={0.06 + i * 0.05}>
                <motion.div
                  className="usecase"
                  whileHover={{ y: -5, transition: { duration: 0.25, ease: [0.16, 1, 0.3, 1] } }}
                  whileTap={{ scale: 0.97 }}
                >
                  <motion.div
                    className="usecase-icon"
                    whileHover={{ rotate: [0, -8, 8, 0], scale: 1.15, backgroundColor: 'var(--accent)', color: '#fff' }}
                    transition={{ duration: 0.4 }}
                  >
                    {u.i}
                  </motion.div>
                  <div className="usecase-title">{u.t}</div>
                  <div className="usecase-desc">{u.d}</div>
                </motion.div>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      {/* TESTIMONIALS */}
      <section className="block">
        <div className="container">
          <Reveal><div className="section-eyebrow">Reviews</div></Reveal>
          <Reveal delay={0.06}><h2 className="section-title">What people are <span className="serif">saying.</span></h2></Reveal>
          <div className="testimonials">
            {[
              { q: '"This should exist everywhere. It belongs in every browser, every newsroom, every classroom."', n: 'Dr. Maya Holm', r: 'Public health researcher', img: 'assets/avatar-maya.svg' },
              { q: '"Finally a tool for truth. The source citations are what sold me."', n: 'Noah Reyes', r: 'Investigative journalist', img: 'assets/avatar-noah.svg' },
              { q: '"I checked twelve viral videos in an afternoon. Five were nonsense."', n: 'Aria Chen', r: 'Investor & podcaster', img: 'assets/avatar-aria.svg' },
            ].map((t, i) => (
              <Reveal key={i} delay={0.1 + i * 0.1}>
                <motion.div
                  className="testimonial"
                  whileHover={{ y: -4, boxShadow: 'var(--shadow-lg)', transition: { duration: 0.3, ease: [0.16, 1, 0.3, 1] } }}
                >
                  <motion.div
                    className="testimonial-quote"
                    initial={{ opacity: 0 }}
                    whileInView={{ opacity: 1 }}
                    viewport={{ once: true }}
                    transition={{ delay: 0.2 + i * 0.1, duration: 0.6 }}
                  >
                    {t.q}
                  </motion.div>
                  <div className="testimonial-by">
                    <motion.img
                      className="avatar"
                      src={t.img}
                      alt=""
                      whileHover={{ scale: 1.15, borderColor: 'var(--accent)' }}
                    />
                    <div>
                      <div className="testimonial-name">{t.n}</div>
                      <div className="testimonial-role">{t.r}</div>
                    </div>
                  </div>
                </motion.div>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      {/* FINAL CTA */}
      <section className="final-cta" id="final-cta">
        <div className="container">
          <h2>
            Stop guessing. <span className="serif-em">Start verifying.</span>
          </h2>
          <div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 12, flexWrap: 'wrap' }}>
            <MagBtn className="btn btn-accent btn-lg" onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}>
              <span key="label">Analyze a video</span><I.ArrowRight key="icon" size={16} style={{ color: '#fff' }} />
            </MagBtn>
            <MagBtn className="btn btn-secondary btn-lg" onClick={() => go('recent')}>
              Explore checks
            </MagBtn>
          </div>
        </div>
      </section>
    </>
  );
};

// ── Trust Strip ──────────────────────────────────────────────────────────────
const TrustStrip = () => {
  const items = ['YouTube', 'TikTok', 'Instagram', 'X', 'Facebook', 'Reuters', 'BBC', 'The Verge'];
  return (
    <div className="trust-strip">
      <div className="container trust-strip-inner">
        <div className="trust-label">Built for the places where claims spread fast</div>
        <div className="trust-logo-grid">
          {items.map((t, i) => (
            <motion.div
              key={t}
              className="trust-logo"
              initial={{ opacity: 0, y: 8 }}
              whileInView={{ opacity: 1, y: 0 }}
              viewport={{ once: true }}
              transition={{ delay: i * 0.04, duration: 0.35 }}
              whileHover={{ y: -2, color: 'var(--ink)' }}
            >
              {t}
            </motion.div>
          ))}
        </div>
      </div>
    </div>
  );
};

// ── Hero Visual ──────────────────────────────────────────────────────────────
const HeroVisual = () => {
  const [activeIdx, setActiveIdx] = useS(null);
  const items = [
    { kind: 'accurate', dot: 'vd-green', text: '7–9 hours of sleep is recommended', tag: 'Accurate' },
    { kind: 'misleading', dot: 'vd-amber', text: 'Coffee reduces cancer risk by 50%', tag: 'Misleading' },
    { kind: 'accurate', dot: 'vd-green', text: 'Cold showers reduce sick days', tag: 'Accurate' },
  ];

  return (
    <div className="hero-visual">
      <div className="preview-stack">
        <motion.div
          className="stack-card stack-bg-1"
          animate={{ rotate: [-3, -2.5, -3], y: [0, -4, 0] }}
          transition={{ duration: 5, repeat: Infinity, ease: 'easeInOut' }}
        />
        <motion.div
          className="stack-card stack-bg-2"
          animate={{ rotate: [1.5, 2, 1.5], y: [0, -3, 0] }}
          transition={{ duration: 6, repeat: Infinity, ease: 'easeInOut', delay: 0.5 }}
        />
        <motion.div
          className="stack-card stack-main"
          animate={{ y: [0, -8, 0] }}
          transition={{ duration: 7, repeat: Infinity, ease: 'easeInOut' }}
        >
          {/* Thumb */}
          <motion.div
            className="preview-thumb"
            whileHover={{ scale: 1.02 }}
            transition={{ duration: 0.3 }}
          >
            <img src="assets/hero-video-preview.jpg" alt="VidVerdict sample video preview" width="1280" height="720" loading="eager" decoding="async" fetchPriority="high" />
            <motion.div
              className="play-btn"
              whileHover={{ scale: 1.15, boxShadow: '0 8px 24px rgba(20,20,26,0.2)' }}
              whileTap={{ scale: 0.9 }}
              animate={{ scale: [1, 1.05, 1] }}
              transition={{ duration: 2.5, repeat: Infinity, ease: 'easeInOut' }}
            />
          </motion.div>

          <div className="preview-meta">
            <div>
              <div className="preview-title">5 morning habits…</div>
              <div className="preview-platform">TikTok · @wellness_daily · 1:24</div>
            </div>
            <Verdict kind="mostly" label="Mostly Accurate" />
          </div>

          <div className="score-row">
            <SmallRing score={82} />
            <div style={{ flex: 1 }}>
              <div className="score-num">82<small> /100</small></div>
              <div className="score-label">3 claims checked · 2 accurate · 1 misleading</div>
            </div>
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {items.map((item, i) => (
              <motion.div
                key={i}
                className={`claim-mini ${item.kind}`}
                initial={{ opacity: 0, x: 20 }}
                animate={{ opacity: 1, x: 0 }}
                transition={{ delay: 0.8 + i * 0.15, duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
                whileHover={{ scale: 1.02, x: 3 }}
                onHoverStart={() => setActiveIdx(i)}
                onHoverEnd={() => setActiveIdx(null)}
              >
                <motion.span
                  className={`verdict-dot ${item.dot}`}
                  animate={{ scale: activeIdx === i ? [1, 1.4, 1] : 1 }}
                  transition={{ duration: 0.4 }}
                />
                <span className="verdict-text">{item.text}</span>
                <motion.span
                  className="verdict-tag"
                  animate={{ y: activeIdx === i ? -1 : 0 }}
                  transition={{ duration: 0.2 }}
                >
                  {item.tag}
                </motion.span>
              </motion.div>
            ))}
          </div>
        </motion.div>
      </div>
    </div>
  );
};

// ── Sample Report ─────────────────────────────────────────────────────────────
const SampleReport = ({ go }) => {
  const [tab, setTab] = useS('claims');
  const r = SAMPLE_REPORTS['wellness-coffee'];
  const tabs = ['claims', 'transcript', 'sources'];

  return (
    <motion.div
      className="sample-card"
      whileHover={{ boxShadow: 'var(--shadow-lg)' }}
      transition={{ duration: 0.3 }}
    >
      <div className="sample-top">
        <motion.div
          className="sample-media"
          whileHover={{ scale: 1.012 }}
          transition={{ duration: 0.3 }}
        >
          <img src="assets/hero-video-preview.jpg" alt="Sample social video report preview" width="1280" height="720" loading="lazy" decoding="async" />
          <motion.div
            className="play-btn"
            whileHover={{ scale: 1.14, boxShadow: '0 12px 30px rgba(20,20,26,0.22)' }}
            whileTap={{ scale: 0.92 }}
          />
        </motion.div>

        <div className="sample-header">
          <div>
            <div className="sample-kicker">Interactive Verdict Preview</div>
            <div className="sample-title">{r.title}</div>
            <div className="sample-meta">
              <span>{r.platform}</span>
              <span>·</span>
              <span>{r.creator}</span>
              <span>·</span>
              <span>{r.duration}</span>
            </div>
          </div>

          <div className="sample-summary-card">
            <SmallRing score={r.score} size={58} stroke={5} />
            <div>
              <div className="sample-score-num">{r.score}<small> /100</small></div>
              <div className="sample-score-label">{r.headline}</div>
              <div className="sample-score-copy">Five claims checked with source-backed verdicts and confidence notes.</div>
            </div>
          </div>
        </div>
      </div>

      {/* Tabs */}
      <div className="sample-tabs" style={{ position: 'relative' }}>
        {tabs.map(t => (
          <div key={t} className={`sample-tab ${tab === t ? 'active' : ''}`} onClick={() => setTab(t)} style={{ textTransform: 'capitalize', position: 'relative' }}>
            {t}
          </div>
        ))}
      </div>

      <AnimatePresence mode="wait">
        <motion.div
          key={tab}
          className="sample-body"
          initial={{ opacity: 0, y: 8 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -8 }}
          transition={{ duration: 0.25, ease: [0.16, 1, 0.3, 1] }}
        >
          {tab === 'overview' && (
            <div className="sample-overview">
              <div className="sample-summary-text">{r.summary}</div>
              <div className="sample-stat-grid">
                {[
                  { l: 'Accurate', n: r.counts.accurate, c: 'accurate' },
                  { l: 'Mostly', n: r.counts.mostly, c: 'mostly' },
                  { l: 'Misleading', n: r.counts.misleading, c: 'misleading' },
                  { l: 'False', n: r.counts.false, c: 'false' },
                  { l: 'No Evidence', n: r.counts.unverified, c: 'unverified' },
                ].map((s, i) => (
                  <motion.div
                    key={s.l}
                    className={`sample-stat ${s.c}`}
                    initial={{ opacity: 0, scale: 0.85, y: 12 }}
                    animate={{ opacity: 1, scale: 1, y: 0 }}
                    transition={{ delay: i * 0.07, duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
                    whileHover={{ scale: 1.04, y: -2, boxShadow: 'var(--shadow-sm)' }}
                  >
                    <div className="sample-stat-top">
                      <div className="sample-stat-num">{s.n}</div>
                      <div className="sample-stat-dot"></div>
                    </div>
                    <div className="sample-stat-label">{s.l}</div>
                  </motion.div>
                ))}
              </div>
            </div>
          )}

          {tab === 'claims' && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {r.claims.slice(0, 3).map((c, i) => (
                <motion.div
                  key={i}
                  style={{ padding: 16, border: '1px solid var(--line)', borderRadius: 10, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16 }}
                  initial={{ opacity: 0, x: -16 }}
                  animate={{ opacity: 1, x: 0 }}
                  transition={{ delay: i * 0.08, duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
                  whileHover={{ x: 4, borderColor: 'var(--line-2)' }}
                >
                  <div style={{ flex: 1, fontSize: 15, fontWeight: 500 }}>{c.text}</div>
                  <Verdict kind={c.verdict} label={c.verdictLabel} />
                </motion.div>
              ))}
              <div style={{ textAlign: 'center', marginTop: 12 }}>
                <MagBtn className="btn btn-secondary" onClick={() => go('result', { id: 'wellness-coffee' })}>
                  <span key="label">Open full report</span><I.ArrowRight key="icon" size={14} />
                </MagBtn>
              </div>
            </div>
          )}

          {tab === 'transcript' && (
            <div>
              {r.transcript.map((row, i) => (
                <motion.div
                  key={i}
                  className="transcript-row"
                  initial={{ opacity: 0 }}
                  animate={{ opacity: 1 }}
                  transition={{ delay: i * 0.06 }}
                  whileHover={{ backgroundColor: 'var(--surface)', borderRadius: 8, paddingLeft: 16, paddingRight: 16 }}
                >
                  <div className="transcript-time">{row.t}</div>
                  <div className="transcript-text">{row.text}</div>
                </motion.div>
              ))}
            </div>
          )}

          {tab === 'sources' && (
            <div className="src-list">
              {[...new Set(r.claims.flatMap(c => c.sources.map(s => s.domain)))].map((d, i) => {
                const src = r.claims.flatMap(c => c.sources).find(s => s.domain === d);
                return (
                  <motion.div
                    key={i}
                    className="src-link"
                    initial={{ opacity: 0, x: -12 }}
                    animate={{ opacity: 1, x: 0 }}
                    transition={{ delay: i * 0.06, duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
                    whileHover={{ x: 4, borderColor: 'var(--ink-4)' }}
                  >
                    <div className="src-favicon">{d[0].toUpperCase()}</div>
                    <div className="src-meta">
                      <div className="src-title">{src.title}</div>
                      <div className="src-domain">{d}</div>
                    </div>
                    <motion.div whileHover={{ x: 3 }}><I.ArrowRight size={14} style={{ color: 'var(--ink-4)' }} /></motion.div>
                  </motion.div>
                );
              })}
            </div>
          )}
        </motion.div>
      </AnimatePresence>
    </motion.div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
// LOADING PAGE
// ═══════════════════════════════════════════════════════════════════════════════
const getYouTubeThumbnail = (rawUrl) => {
  try {
    const parsed = new URL(rawUrl);
    const host = parsed.hostname.replace(/^www\./, '').toLowerCase();
    let id = '';
    if (host === 'youtu.be') {
      id = parsed.pathname.split('/').filter(Boolean)[0] || '';
    } else if (host.includes('youtube.com')) {
      id = parsed.searchParams.get('v') || '';
      const parts = parsed.pathname.split('/').filter(Boolean);
      if (!id && ['embed', 'shorts', 'live'].includes(parts[0])) id = parts[1] || '';
    }
    return id ? `https://i.ytimg.com/vi/${encodeURIComponent(id)}/hqdefault.jpg` : '';
  } catch {
    return '';
  }
};

const LoadingPage = ({ url, demo = false, maxVideoMinutes, onDone, onError }) => {
  const [stepIdx, setStepIdx] = useS(0);
  const [serverDone, setServerDone] = useS(false);
  const [statusText, setStatusText] = useS('Reading the video.');

  useE(() => {
    let cancelled = false;
    if (demo) {
      const timer = setTimeout(() => {
        if (cancelled) return;
        setStatusText('Building your demo report.');
        setServerDone(true);
        setStepIdx(STEPS.length);
        setTimeout(() => !cancelled && onDone(SAMPLE_REPORTS['wellness-coffee']), 500);
      }, 3200);
      return () => {
        cancelled = true;
        clearTimeout(timer);
      };
    }
    const analyze = async () => {
      try {
        const res = await fetch('/api/analyze', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ url, maxVideoMinutes }),
        });
        const data = await res.json();
        if (!res.ok) throw new Error(data.error || 'Analysis failed.');
        if (!cancelled) {
          setStatusText('Building your report.');
          setServerDone(true);
          setStepIdx(STEPS.length);
          setTimeout(() => !cancelled && onDone(data.report), 500);
        }
      } catch (err) {
        if (!cancelled) onError(err.message);
      }
    };
    analyze();
    return () => { cancelled = true; };
  }, [url, demo, maxVideoMinutes]);

  useE(() => {
    if (stepIdx >= STEPS.length - 1 && !serverDone) return;
    if (stepIdx >= STEPS.length) return;
    const t = setTimeout(() => setStepIdx(s => s + 1), stepIdx === 0 ? 600 : 700 + Math.random() * 400);
    return () => clearTimeout(t);
  }, [stepIdx, serverDone]);

  const progress = Math.min(100, Math.round((stepIdx / STEPS.length) * 100));
  const loadingThumb = getYouTubeThumbnail(url) || 'assets/hero-video-preview.jpg';

  return (
    <motion.div
      className="loading-shell"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.4 }}
    >
      <div className="container">
        <motion.div
          className="loading-card"
          style={{ margin: '0 auto' }}
          initial={{ y: 40, opacity: 0 }}
          animate={{ y: 0, opacity: 1 }}
          transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
        >
          <motion.div
            style={{ fontSize: 12, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 14 }}
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            transition={{ delay: 0.2 }}
          >
            Analyzing
          </motion.div>
          <motion.div
            style={{ fontSize: 32, fontWeight: 600, letterSpacing: '-0.025em', lineHeight: 1.1, marginBottom: 8 }}
            initial={{ opacity: 0, x: -20 }}
            animate={{ opacity: 1, x: 0 }}
            transition={{ delay: 0.3, duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
          >
            {statusText}
          </motion.div>
          <div style={{ fontSize: 15, color: 'var(--ink-3)', wordBreak: 'break-all', maxWidth: 520 }}>
            {url || 'https://www.tiktok.com/@wellness_daily/video/7234891'}
          </div>

          <motion.div
            className="loading-thumb"
            style={{ marginTop: 28 }}
            initial={{ opacity: 0, scale: 0.95 }}
            animate={{ opacity: 1, scale: 1 }}
            transition={{ delay: 0.4, duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
          >
            <img src={loadingThumb} alt="Video thumbnail while VidVerdict analyzes the link" width="1280" height="720" loading="eager" decoding="async" />
            <div className="shimmer"></div>
          </motion.div>

          <div className="progress-track">
            <motion.div
              className="progress-bar"
              initial={{ width: 0 }}
              animate={{ width: progress + '%' }}
              transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
            />
          </div>
          <div style={{ marginTop: 8, fontSize: 12, color: 'var(--ink-4)', display: 'flex', justifyContent: 'space-between' }}>
            <motion.span
              key={progress}
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
            >
              {progress}% complete
            </motion.span>
            <span>{serverDone ? 'Almost done' : 'Working...'}</span>
          </div>

          <div className="step-list">
            {STEPS.map((s, i) => {
              const state = i < stepIdx ? 'done' : i === stepIdx ? 'active' : 'pending';
              return (
                <motion.div
                  key={s.id}
                  className={`step-row ${state}`}
                  animate={state === 'active' ? { x: [0, 4, 0] } : {}}
                  transition={{ duration: 0.4 }}
                >
                  <motion.div
                    className="step-icon"
                    animate={state === 'done' ? { scale: [0, 1.2, 1], backgroundColor: 'var(--accent)' } : {}}
                    transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
                  >
                    {state === 'done' && (
                      <motion.div
                        className="step-check"
                        initial={{ scale: 0 }}
                        animate={{ scale: 1 }}
                        transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
                      >
                        <I.Check size={13} color="#fff" strokeWidth={3.25} />
                      </motion.div>
                    )}
                  </motion.div>
                  <motion.div
                    className="step-name"
                    animate={state === 'active' ? { color: 'var(--ink)' } : {}}
                  >
                    {s.name}
                  </motion.div>
                  {state === 'done' && (
                    <motion.div
                      initial={{ opacity: 0, scale: 0.8 }}
                      animate={{ opacity: 1, scale: 1 }}
                      style={{ marginLeft: 'auto', fontSize: 11, color: 'var(--green)', fontWeight: 500 }}
                    >
                      Done
                    </motion.div>
                  )}
                </motion.div>
              );
            })}
          </div>
        </motion.div>
      </div>
    </motion.div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
// RESULTS PAGE
// ═══════════════════════════════════════════════════════════════════════════════
const ResultsPage = ({ id, reports = {}, go, plan = 'guest', limits = {} }) => {
  const r = reports[id] || SAMPLE_REPORTS[id] || SAMPLE_REPORTS['wellness-coffee'];
  const [tab, setTab] = useS('claims');
  const [openIds, setOpenIds] = useS(new Set());
  const activeLimits = {
    claimLimit: plan === 'guest' ? 2 : Infinity,
    sourceLimit: plan === 'guest' ? 0 : plan === 'basic' ? 5 : 20,
    transcript: plan !== 'guest',
    share: plan === 'pro' || plan === 'team',
    ...limits,
  };
  const visibleClaims = Number.isFinite(activeLimits.claimLimit) ? r.claims.slice(0, activeLimits.claimLimit) : r.claims;
  const lockedClaims = Math.max(0, r.claims.length - visibleClaims.length);

  const toggle = (i) => {
    const ns = new Set(openIds);
    ns.has(i) ? ns.delete(i) : ns.add(i);
    setOpenIds(ns);
  };

  const tabs = ['claims', 'transcript', 'sources'];
  const allSources = r.claims.flatMap(c => c.sources);
  const visibleSources = allSources.slice(0, activeLimits.sourceLimit || 0);
  const sourceCount = allSources.length;
  const avgConfidence = r.claims.length
    ? Math.round(r.claims.reduce((sum, c) => sum + Number(c.confidence || 0), 0) / r.claims.length)
    : 0;

  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.4 }}
    >
      <div className="result-hero">
        <div className="container">
          {/* Breadcrumb */}
          <motion.div
            style={{ fontSize: 13, color: 'var(--ink-3)', marginBottom: 20, display: 'flex', gap: 8, alignItems: 'center' }}
            initial={{ opacity: 0, y: -10 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.5 }}
          >
            <motion.span onClick={() => go('home')} whileHover={{ color: 'var(--ink)' }}>Home</motion.span>
            <span style={{ color: 'var(--ink-4)' }}>/</span>
            <motion.span onClick={() => go('recent')} whileHover={{ color: 'var(--ink)' }}>Reports</motion.span>
            <span style={{ color: 'var(--ink-4)' }}>/</span>
            <span style={{ color: 'var(--ink-2)' }}>{r.platform} · {r.creator}</span>
          </motion.div>

          <div className="result-grid">
            {/* Sidebar */}
            <motion.div
              initial={{ opacity: 0, x: -30 }}
              animate={{ opacity: 1, x: 0 }}
              transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
            >
              <motion.div
                className="result-thumb-card"
                whileHover={{ boxShadow: 'var(--shadow-lg)' }}
              >
                <div className="result-thumb">
                  <img src={r.thumbnailUrl || "assets/hero-video-preview.jpg"} alt={`${r.title} thumbnail`} width="1280" height="720" loading="eager" decoding="async" />
                  <motion.div
                    className="play-btn"
                    whileHover={{ scale: 1.2 }}
                    whileTap={{ scale: 0.9 }}
                    animate={{ scale: [1, 1.08, 1] }}
                    transition={{ duration: 2.5, repeat: Infinity, ease: 'easeInOut' }}
                  />
                </div>
                <div className="result-meta">
                  <div className="result-title">{r.title}</div>
                  {[
                    ['Platform', r.platform],
                    ['Creator', r.creator],
                    ['Duration', r.duration],
                    ['Date checked', r.date],
                  ].map(([k, v], i) => (
                    <motion.div
                      key={k}
                      className="meta-row"
                      initial={{ opacity: 0, x: -10 }}
                      animate={{ opacity: 1, x: 0 }}
                      transition={{ delay: 0.3 + i * 0.07, duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
                    >
                      <span className="k">{k}</span><span className="v">{v}</span>
                    </motion.div>
                  ))}
                </div>
              </motion.div>
            </motion.div>

            {/* Main content */}
            <motion.div
              className="result-panel"
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.6, delay: 0.15, ease: [0.16, 1, 0.3, 1] }}
            >
              {/* Score card */}
              <motion.div
                className="score-card"
                initial={{ opacity: 0, scale: 0.95 }}
                animate={{ opacity: 1, scale: 1 }}
                transition={{ duration: 0.6, delay: 0.2, ease: [0.16, 1, 0.3, 1] }}
                whileHover={{ boxShadow: 'var(--shadow-lg)' }}
              >
                <ScoreRing score={r.score} size={96} stroke={7} />
                <div>
                  <motion.div
                    className="score-headline"
                    initial={{ opacity: 0, x: 20 }}
                    animate={{ opacity: 1, x: 0 }}
                    transition={{ delay: 0.5, duration: 0.5 }}
                  >
                    {r.headline}
                  </motion.div>
                  <div className="score-stats">
                    {[
                      [r.claims.length || 5, 'Claims'],
                      [sourceCount || 9, 'Sources'],
                      [`${avgConfidence || 87}%`, 'Avg confidence'],
                    ].map(([num, lbl], i) => (
                      <motion.div
                        key={lbl}
                        className="score-stat"
                        initial={{ opacity: 0, y: 10 }}
                        animate={{ opacity: 1, y: 0 }}
                        transition={{ delay: 0.6 + i * 0.08, duration: 0.4 }}
                        whileHover={{ scale: 1.06 }}
                      >
                        <div className="num">{num}</div>
                        <div className="lbl">{lbl}</div>
                      </motion.div>
                    ))}
                  </div>
                </div>
              </motion.div>

              {/* Share bar */}
              <div className="share-bar">
                {[
                  { icon: <I.Copy size={14} />, label: 'Copy Link', locked: !activeLimits.share, action: () => { navigator.clipboard?.writeText(window.location.href); showToast('Report link copied'); } },
                  { icon: <I.Download size={14} />, label: 'Download PDF', locked: !activeLimits.share, action: () => showToast('PDF export starting…') },
                  { icon: <I.Twitter size={14} />, label: 'Share on X', locked: !activeLimits.share, action: () => showToast('Opening X share…') },
                  { icon: <I.Linkedin size={14} />, label: 'Share on LinkedIn', locked: !activeLimits.share, action: () => showToast('Opening LinkedIn share…') },
                ].map((b, i) => (
                  <motion.button
                    key={b.label}
                    className={`btn btn-secondary ${b.locked ? 'locked-action' : ''}`}
                    onClick={() => b.locked ? (showToast('Upgrade to Pro to export and share reports'), go('pricing')) : b.action()}
                    initial={{ opacity: 0, y: 10 }}
                    animate={{ opacity: 1, y: 0 }}
                    transition={{ delay: 0.4 + i * 0.07, duration: 0.4 }}
                    whileHover={{ y: -2, boxShadow: 'var(--shadow)' }}
                    whileTap={{ scale: 0.96 }}
                  >
                    {b.locked ? <I.Lock size={14} /> : b.icon} {b.label}
                  </motion.button>
                ))}
              </div>

              {/* Tabs */}
              <div className="res-tabs-wrap">
                <div className="res-tabs-label">Report sections</div>
                <div className="res-tabs" style={{ position: 'relative' }}>
                  {tabs.map(t => (
                    <div
                      key={t}
                      className={`res-tab ${tab === t ? 'active' : ''} ${(t === 'transcript' && !activeLimits.transcript) ? 'locked-tab' : ''}`}
                      onClick={() => {
                        if (t === 'transcript' && !activeLimits.transcript) {
                          showToast('Sign up free to unlock the transcript');
                          go('pricing');
                          return;
                        }
                        setTab(t);
                      }}
                      style={{ textTransform: 'capitalize', position: 'relative' }}
                    >
                      {t === 'claims' ? 'Facts' : t} {t === 'claims' && <span style={{ color: 'var(--ink-4)', marginLeft: 4 }}>{r.claims.length}</span>}
                    </div>
                  ))}
                </div>
              </div>

              {/* Tab content */}
              <AnimatePresence mode="wait">
                <motion.div
                  key={tab}
                  initial={{ opacity: 0, y: 12 }}
                  animate={{ opacity: 1, y: 0 }}
                  exit={{ opacity: 0, y: -12 }}
                  transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
                >
                  {tab === 'claims' && (
                    <div className="claims-list">
                      {visibleClaims.map((c, i) => (
                        <motion.div
                          key={i}
                          className={`claim-card ${openIds.has(i) ? 'open' : ''}`}
                          onClick={() => toggle(i)}
                          initial={{ opacity: 0, y: 16 }}
                          animate={{ opacity: 1, y: 0 }}
                          transition={{ delay: i * 0.08, duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
                          whileHover={{ boxShadow: 'var(--shadow)' }}
                          layout
                        >
                          <div className="claim-top">
                            <div style={{ flex: 1 }}>
                              <div className="claim-text">{c.text}</div>
                            </div>
                            <div className="claim-verdict-wrap">
                              <Verdict kind={c.verdict} label={c.verdictLabel} />
                              <motion.div
                                className="claim-chevron"
                                animate={{ rotate: openIds.has(i) ? 180 : 0 }}
                                transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
                              >
                                <I.ChevronDown size={16} />
                              </motion.div>
                            </div>
                          </div>
                          <AnimatePresence>
                            {openIds.has(i) && (
                              <motion.div
                                className="claim-detail"
                                style={{ display: 'block' }}
                                onClick={(e) => e.stopPropagation()}
                                initial={{ opacity: 0, height: 0 }}
                                animate={{ opacity: 1, height: 'auto' }}
                                exit={{ opacity: 0, height: 0 }}
                                transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
                              >
                                <div className="claim-confidence">Confidence {c.confidence}%</div>
                                <div className="claim-expl">{c.explanation}</div>
                                <motion.div
                                  className="claim-expand"
                                  onClick={() => toggle(i)}
                                  whileHover={{ color: 'var(--ink)' }}
                                >
                                  <span>Hide evidence & sources</span>
                                  <I.ChevronDown size={16} />
                                </motion.div>
                                <div className="evidence-h">Evidence summary</div>
                                <div className="evidence-box">{c.evidence}</div>
                                <div className="evidence-h" style={{ marginTop: 16 }}>Sources ({activeLimits.sourceLimit ? Math.min(c.sources.length, activeLimits.sourceLimit) : 0})</div>
                                {activeLimits.sourceLimit === 0 ? (
                                  <div className="locked-source-box">
                                    <I.Lock size={16} />
                                    <span>Sign up free to unlock source links.</span>
                                  </div>
                                ) : (
                                <div className="src-list">
                                  {c.sources.slice(0, activeLimits.sourceLimit).map((s, j) => (
                                    <motion.div
                                      key={j}
                                      className="src-link"
                                      initial={{ opacity: 0, x: -10 }}
                                      animate={{ opacity: 1, x: 0 }}
                                      transition={{ delay: j * 0.06 }}
                                      whileHover={{ x: 4, borderColor: 'var(--ink-4)' }}
                                    >
                                      <div className="src-favicon">{s.domain[0].toUpperCase()}</div>
                                      <div className="src-meta">
                                        <div className="src-title">{s.title}</div>
                                        <div className="src-domain">{s.domain}</div>
                                      </div>
                                      <motion.div whileHover={{ x: 3 }}><I.ArrowRight size={14} style={{ color: 'var(--ink-4)' }} /></motion.div>
                                    </motion.div>
                                  ))}
                                </div>
                                )}
                              </motion.div>
                            )}
                          </AnimatePresence>
                        </motion.div>
                      ))}
                      {lockedClaims > 0 && (
                        <motion.div className="claim-upgrade-card" initial={{ opacity: 0, y: 14 }} animate={{ opacity: 1, y: 0 }}>
                          <I.Lock size={18} />
                          <div>
                            <strong>{lockedClaims} more facts are hidden on Guest.</strong>
                            <span>Sign up free to see all claims, transcript, and source links.</span>
                          </div>
                          <button className="btn btn-accent" onClick={() => go('pricing')}>Unlock</button>
                        </motion.div>
                      )}
                    </div>
                  )}

                  {tab === 'transcript' && (
                    <div style={{ marginTop: 28, border: '1px solid var(--line)', borderRadius: 16, background: '#fff', padding: 24 }}>
                      {r.transcript.map((row, i) => (
                        <motion.div
                          key={i}
                          className="transcript-row"
                          initial={{ opacity: 0, x: -10 }}
                          animate={{ opacity: 1, x: 0 }}
                          transition={{ delay: i * 0.05, duration: 0.4 }}
                          whileHover={{ backgroundColor: 'var(--surface)', borderRadius: 8, paddingLeft: 12 }}
                        >
                          <div className="transcript-time">{row.t}</div>
                          <div className="transcript-text">{highlightTranscriptText(row.text)}</div>
                        </motion.div>
                      ))}
                    </div>
                  )}

                  {tab === 'sources' && (
                    <div style={{ marginTop: 28 }} className="src-list">
                      {visibleSources.length === 0 && (
                        <div className="locked-source-box large">
                          <I.Lock size={18} />
                          <span>Sources are blurred on Guest. Sign up free to unlock source links.</span>
                          <button className="btn btn-accent" onClick={() => go('pricing')}>See plans</button>
                        </div>
                      )}
                      {visibleSources.map((s, i) => (
                        <motion.div
                          key={i}
                          className="src-link"
                          initial={{ opacity: 0, y: 10 }}
                          animate={{ opacity: 1, y: 0 }}
                          transition={{ delay: i * 0.06, duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
                          whileHover={{ x: 4, borderColor: 'var(--ink-4)' }}
                        >
                          <div className="src-favicon">{s.domain[0].toUpperCase()}</div>
                          <div className="src-meta">
                            <div className="src-title">{s.title}</div>
                            <div className="src-domain">{s.domain}</div>
                          </div>
                          <motion.div whileHover={{ x: 3 }}><I.ArrowRight size={14} style={{ color: 'var(--ink-4)' }} /></motion.div>
                        </motion.div>
                      ))}
                    </div>
                  )}
                </motion.div>
              </AnimatePresence>
            </motion.div>
          </div>
        </div>
      </div>
      <div style={{ height: 80 }}></div>
    </motion.div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
// EXPLORE PAGE
// ═══════════════════════════════════════════════════════════════════════════════
const PlatformBadge = ({ platform = 'Video' }) => {
  const name = String(platform || 'Video');
  const key = name.toLowerCase();
  const initial = key.includes('youtube') ? '▶' : key.includes('instagram') ? '◎' : key.includes('tiktok') ? '♪' : key === 'x' ? 'X' : key.includes('facebook') ? 'f' : '▶';
  return (
    <div className={`platform-badge ${key.replace(/[^a-z0-9]+/g, '-')}`}>
      <span>{initial}</span>{name}
    </div>
  );
};

const relativeCheckedAt = (value) => {
  if (!value) return 'Just now';
  const then = new Date(value).getTime();
  if (!Number.isFinite(then)) return 'Recently';
  const diff = Math.max(0, Date.now() - then);
  const minutes = Math.floor(diff / 60000);
  if (minutes < 1) return 'Just now';
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  return `${days}d ago`;
};

const RecentPage = ({ go, reports = {} }) => {
  const uniqueReports = Object.values(reports)
    .filter((r) => r && r.url && !String(r.id || '').startsWith('r'))
    .reduce((items, report) => {
      const key = report.canonicalUrl || report.url;
      const existing = items.get(key);
      if (!existing || new Date(report.checkedAt || 0) > new Date(existing.checkedAt || 0)) {
        items.set(key, report);
      }
      return items;
    }, new Map());

  const explore = Array.from(uniqueReports.values())
    .sort((a, b) => new Date(b.checkedAt || 0) - new Date(a.checkedAt || 0));

  return (
    <motion.div
      className="container"
      style={{ padding: '64px 32px 96px' }}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.4 }}
    >
      <Reveal><div className="section-eyebrow">Explore</div></Reveal>
      <Reveal delay={0.06}><h2 className="section-title">Recent <span className="serif">checks.</span></h2></Reveal>
      <Reveal delay={0.12}><div className="section-sub">Browse the latest public verdicts across YouTube, TikTok, Instagram, X, and Facebook.</div></Reveal>

      {explore.length === 0 ? (
        <Reveal delay={0.18}>
          <div className="recent-empty">
            <div className="recent-empty-icon"><I.Search size={22} /></div>
            <div>
              <div className="recent-empty-title">No checks yet</div>
              <div className="recent-empty-copy">Analyze a public video to start building the Explore feed.</div>
            </div>
            <MagBtn className="btn btn-accent" onClick={() => go('home')}>
              <span key="label">Analyze Now</span><I.ArrowRight key="icon" size={14} style={{ color: '#fff' }} />
            </MagBtn>
          </div>
        </Reveal>
      ) : (
        <div className="recent-grid">
          {explore.map((r, idx) => (
            <motion.div
              key={r.id}
              className="recent-card"
              onClick={() => go('result', { id: r.id })}
              initial={{ opacity: 0, y: 24, scale: 0.96 }}
              animate={{ opacity: 1, y: 0, scale: 1 }}
              transition={{ delay: idx * 0.07, duration: 0.55, ease: [0.16, 1, 0.3, 1] }}
              whileHover={{ y: -6, boxShadow: 'var(--shadow-lg)', scale: 1.01, transition: { duration: 0.25 } }}
              whileTap={{ scale: 0.98 }}
            >
              <div className="recent-thumb">
                <img src={r.thumbnailUrl || 'assets/hero-video-preview.jpg'} alt={`${r.title} thumbnail`} width="1280" height="720" loading="lazy" decoding="async" />
                <div className="recent-thumb-shade"></div>
                <div className="recent-score-badge">
                  <motion.span
                    style={{ color: r.score >= 75 ? 'var(--green)' : r.score >= 50 ? 'var(--amber)' : 'var(--red)' }}
                    animate={{ opacity: [1, 0.55, 1] }}
                    transition={{ duration: 2, repeat: Infinity, delay: idx * 0.3 }}
                  >●</motion.span> {r.score}/100
                </div>
                <PlatformBadge platform={r.platform} />
              </div>
              <div className="recent-meta">
                <div className="recent-title">{r.title}</div>
                <div className="recent-sub">
                  <span>{r.creator || 'Unknown creator'}</span>
                  <span>{relativeCheckedAt(r.checkedAt)}</span>
                </div>
              </div>
            </motion.div>
          ))}
        </div>
      )}
    </motion.div>
  );
};

const PRICING_PLANS = [
  {
    id: 'guest',
    badge: 'No account',
    name: 'Guest',
    audience: 'First-time visitors',
    price: '$0',
    cadence: 'always',
    note: 'No signup needed',
    cta: 'Try now — no signup',
    features: [
      ['yes', '2 checks/day'],
      ['yes', 'Max 5 min video'],
      ['yes', 'Overall verdict + score'],
      ['yes', 'Top 2 claims only'],
      ['no', 'Sources blurred'],
      ['no', 'No transcript'],
      ['no', 'No history'],
      ['no', 'No PDF / share'],
    ],
  },
  {
    id: 'basic',
    badge: 'Free account',
    name: 'Basic',
    audience: 'Casual users',
    price: '$0',
    cadence: 'month',
    note: 'Email signup · 7-day Pro trial included',
    cta: 'Sign up free',
    features: [
      ['yes', '15 checks/month'],
      ['yes', 'Max 8 min video'],
      ['yes', '120 min pool/month'],
      ['yes', 'All claims shown'],
      ['yes', 'Full transcript'],
      ['yes', '5 sources per report'],
      ['yes', '30-day history'],
      ['no', 'No PDF / share link'],
    ],
  },
  {
    id: 'pro',
    badge: 'Most popular',
    name: 'Pro',
    audience: 'Journalists, students, creators',
    price: '$19',
    cadence: 'month',
    note: '$15/mo billed yearly — save 21%',
    cta: 'Start Pro — $19/mo',
    popular: true,
    features: [
      ['yes', '150 checks/month'],
      ['yes', 'Max 30 min video'],
      ['yes', '1,500 min pool/month'],
      ['yes', 'All claims + deep analysis'],
      ['yes', '20 sources per report'],
      ['yes', 'Priority processing'],
      ['yes', 'PDF export + share link'],
      ['yes', 'Transcript download'],
      ['yes', 'Unlimited history'],
      ['yes', 'Email alerts — viral clips'],
    ],
  },
  {
    id: 'team',
    badge: 'Team',
    name: 'Team',
    audience: 'Newsrooms, agencies, NGOs',
    price: '$99',
    cadence: 'month',
    note: '$79/mo billed yearly · 5 seats included',
    cta: 'Start Team — $99/mo',
    team: true,
    features: [
      ['yes', '1,000 checks/month'],
      ['yes', 'Max 90 min video'],
      ['yes', '5,000 min pool/month'],
      ['yes', '5 seats included'],
      ['yes', 'Team dashboard'],
      ['yes', 'Shared report folders'],
      ['yes', 'Collaboration notes'],
      ['yes', 'Branded PDF reports'],
      ['yes', 'Batch URL upload (CSV)'],
      ['yes', 'Priority support'],
    ],
  },
];

const FEATURE_MATRIX = [
  ['Checks', '2/day', '15/mo', '150/mo', '1,000/mo'],
  ['Max video length', '5 min', '8 min', '30 min', '90 min'],
  ['Minutes pool/mo', '—', '120 min', '1,500 min', '5,000 min'],
  ['Claims shown', 'Top 2', 'All', 'All + deep', 'All + deep'],
  ['Sources/report', 'Blurred', '5', '20', '20'],
  ['Transcript', 'No', 'View only', 'Download', 'Download'],
  ['History', 'None', '30 days', 'Forever', 'Forever'],
  ['PDF + share link', 'No', 'No', 'Yes', 'Branded'],
  ['Email alerts', 'No', 'No', 'Yes', 'Yes'],
  ['Batch CSV upload', 'No', 'No', 'No', 'Yes'],
  ['Collaboration notes', 'No', 'No', 'No', 'Yes'],
  ['Seats', '—', '1', '1', '5 included'],
  ['7-day Pro trial', 'No', 'Yes — on signup', '—', '—'],
  ['Support', 'None', 'Email', 'Email', 'Priority'],
];

const PricingPage = ({ go, currentPlan = 'guest', onChoosePlan }) => (
  <motion.div className="pricing-page" initial={{ opacity: 0, y: 18 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.45 }}>
    <div className="container">
      <Reveal>
        <div className="section-eyebrow">Pricing</div>
      </Reveal>
      <Reveal delay={0.06}>
        <h1 className="pricing-title">Simple pricing for <span className="serif">clear verdicts.</span></h1>
      </Reveal>
      <Reveal delay={0.12}>
        <p className="pricing-sub">Start free. Upgrade when you need longer videos, deeper reports, or team workflows.</p>
      </Reveal>

      <div className="pricing-grid">
        {PRICING_PLANS.map((plan, idx) => (
          <motion.div
            key={plan.id}
            className={`pricing-card ${plan.popular ? 'popular' : ''} ${plan.team ? 'team' : ''}`}
            initial={{ opacity: 0, y: 24 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ delay: idx * 0.07, duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
            whileHover={{ y: -5, boxShadow: 'var(--shadow-lg)' }}
          >
            <div className={`pricing-badge ${plan.popular ? 'popular' : ''} ${plan.team ? 'team' : ''}`}>{plan.badge}</div>
            <h2>{plan.name}</h2>
            <p className="pricing-audience">{plan.audience}</p>
            <div className="pricing-price">
              <span>{plan.price}</span>
              <small>/ {plan.cadence}</small>
            </div>
            <div className="pricing-note">{plan.note}</div>
            <div className="pricing-rule"></div>
            <div className="pricing-features">
              {plan.features.map(([state, label]) => (
                <div className={`pricing-feature ${state}`} key={label}>
                  <span>{state === 'yes' ? <I.Check size={16} /> : '–'}</span>
                  <div>{label}</div>
                </div>
              ))}
            </div>
            <button
              className={`pricing-cta ${plan.popular ? 'primary' : ''}`}
              onClick={() => onChoosePlan(plan.id)}
            >
              {currentPlan === plan.id ? 'Current plan' : plan.cta}
            </button>
          </motion.div>
        ))}
      </div>

      <section className="pricing-matrix-section">
        <Reveal>
          <div className="section-eyebrow">Everything at a glance</div>
        </Reveal>
        <div className="pricing-table-wrap">
          <table className="pricing-table">
            <thead>
              <tr>
                {['Feature', 'Guest', 'Basic', 'Pro', 'Team'].map((h) => <th key={h}>{h}</th>)}
              </tr>
            </thead>
            <tbody>
              {FEATURE_MATRIX.map((row) => (
                <tr key={row[0]}>
                  {row.map((cell, i) => (
                    <td key={`${row[0]}-${i}`} className={i >= 3 ? 'strong' : ''}>{cell}</td>
                  ))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </section>

    </div>
  </motion.div>
);

const SignInPage = ({ go, onSignIn, authClient, authReady, authError }) => {
  const [mode, setMode] = useS('signin');
  const [name, setName] = useS('');
  const [email, setEmail] = useS('');
  const [password, setPassword] = useS('');
  const [loading, setLoading] = useS(false);
  const [message, setMessage] = useS('');
  const [error, setError] = useS('');

  const profileFromUser = (authUser, provider = 'Email') => {
    const metadata = authUser?.user_metadata || {};
    const userEmail = authUser?.email || metadata.email || email.trim();
    return {
      id: authUser?.id || userEmail,
      name: metadata.full_name || metadata.name || name.trim() || (userEmail ? userEmail.split('@')[0] : 'VidVerdict user'),
      email: userEmail,
      avatar: metadata.avatar_url || metadata.picture || 'assets/rizwan-kabir.jpg',
      provider,
    };
  };

  const resetFeedback = () => {
    setMessage('');
    setError('');
  };

  const requireAuthClient = () => {
    if (authClient && authReady) return true;
    setError(authError || 'Sign-in is still loading. Please try again in a moment.');
    return false;
  };

  const continueWithGoogle = async () => {
    resetFeedback();
    if (!requireAuthClient()) return;
    setLoading(true);
    const { error: googleError } = await authClient.auth.signInWithOAuth({
      provider: 'google',
      options: { redirectTo: `${window.location.origin}/account` },
    });
    if (googleError) {
      setError(googleError.message || 'Google sign-in could not start.');
      setLoading(false);
    }
  };

  const continueWithEmail = async () => {
    resetFeedback();
    if (!requireAuthClient()) return;
    const cleanEmail = email.trim();
    if (!cleanEmail) {
      setError('Enter your email address.');
      return;
    }
    if (!password || password.length < 6) {
      setError('Use a password with at least 6 characters.');
      return;
    }

    setLoading(true);
    try {
      if (mode === 'signup') {
        const { data, error: signUpError } = await authClient.auth.signUp({
          email: cleanEmail,
          password,
          options: {
            data: { full_name: name.trim() || cleanEmail.split('@')[0] },
            emailRedirectTo: `${window.location.origin}/account`,
          },
        });
        if (signUpError) throw signUpError;
        if (data?.session?.user) {
          onSignIn(profileFromUser(data.session.user, 'Email'));
        } else {
          setMessage('Check your email to confirm your account, then sign in.');
        }
      } else {
        const { data, error: signInError } = await authClient.auth.signInWithPassword({
          email: cleanEmail,
          password,
        });
        if (signInError) throw signInError;
        if (data?.user) onSignIn(profileFromUser(data.user, 'Email'));
      }
    } catch (authProblem) {
      setError(authProblem.message || 'Could not continue. Please try again.');
    } finally {
      setLoading(false);
    }
  };

  const switchMode = (nextMode) => {
    setMode(nextMode);
    resetFeedback();
  };

  return (
    <motion.div
      className="auth-page"
      initial={{ opacity: 0, y: 18 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
    >
      <div className="container">
        <div className="auth-shell">
          <div className="auth-copy">
            <motion.img className="auth-logo" src="assets/wordmark.png" alt="VidVerdict" initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.05, duration: 0.5 }} />
            <motion.h1 initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.12, duration: 0.55 }}>
              Your checks, saved in one place.
            </motion.h1>
            <motion.p initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.18, duration: 0.55 }}>
              Sign in to keep a private history of videos you have verified, revisit reports, and get ready for saved plans and pricing.
            </motion.p>
            <motion.div className="auth-benefits" initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.24, duration: 0.55 }}>
              {[
                ['Saved history', 'Every report stays easy to find.'],
                ['Clean account', 'No clutter, no dashboard noise.'],
                ['Plan ready', 'Built for upcoming pricing tiers.'],
              ].map(([title, copy]) => (
                <div className="auth-benefit" key={title}>
                  <span><I.Check size={14} /></span>
                  <div>
                    <strong>{title}</strong>
                    <small>{copy}</small>
                  </div>
                </div>
              ))}
            </motion.div>
          </div>

          <motion.div className="auth-card" initial={{ opacity: 0, y: 22, scale: 0.98 }} animate={{ opacity: 1, y: 0, scale: 1 }} transition={{ delay: 0.18, duration: 0.55, ease: [0.16, 1, 0.3, 1] }}>
            <div className="auth-card-head">
              <div className="auth-card-icon"><I.Lock size={18} /></div>
              <div>
                <h2>{mode === 'signup' ? 'Create account' : 'Sign in'}</h2>
                <p>{mode === 'signup' ? 'Start saving your VidVerdict reports.' : 'Continue to your VidVerdict account.'}</p>
              </div>
            </div>

            <div className="auth-toggle" role="tablist" aria-label="Authentication mode">
              <button className={mode === 'signin' ? 'active' : ''} onClick={() => switchMode('signin')} type="button">Sign in</button>
              <button className={mode === 'signup' ? 'active' : ''} onClick={() => switchMode('signup')} type="button">Create account</button>
            </div>

            <button className="google-btn" onClick={continueWithGoogle} disabled={loading}>
              <span className="google-mark">G</span>
              Continue with Google
            </button>

            <div className="auth-divider"><span>or use email</span></div>

            {mode === 'signup' && (
              <label className="auth-field">
                <span>Name</span>
                <input value={name} onChange={(event) => setName(event.target.value)} type="text" placeholder="Rizwan Kabir" autoComplete="name" />
              </label>
            )}

            <label className="auth-field">
              <span>Email</span>
              <input value={email} onChange={(event) => setEmail(event.target.value)} type="email" placeholder="you@example.com" autoComplete="email" />
            </label>

            <label className="auth-field">
              <span>Password</span>
              <input value={password} onChange={(event) => setPassword(event.target.value)} type="password" placeholder="Minimum 6 characters" autoComplete={mode === 'signup' ? 'new-password' : 'current-password'} onKeyDown={(event) => event.key === 'Enter' && continueWithEmail()} />
            </label>

            <button className="btn btn-accent auth-submit" onClick={continueWithEmail} disabled={loading}>
              <span key="label">{loading ? 'Please wait...' : mode === 'signup' ? 'Create account' : 'Sign in'}</span><I.ArrowRight key="icon" size={14} style={{ color: '#fff' }} />
            </button>

            {error && <div className="auth-note error">{error}</div>}
            {!error && message && <div className="auth-note success">{message}</div>}
            {!error && !message && authError && <div className="auth-note error">{authError}</div>}
            {!error && !message && !authError && <div className="auth-note">Email and Google sign-in are powered by Supabase Auth.</div>}
          </motion.div>
        </div>
      </div>
    </motion.div>
  );
};

const AccountPage = ({ go, user, reports = {}, history = [], onSignOut }) => {
  const reportByHistory = history
    .map((item) => {
      const report =
        reports[item.reportId] ||
        Object.values(reports).find((r) => r.url === item.url || r.canonicalUrl === item.url) ||
        SAMPLE_REPORTS[item.reportId];
      return report ? { ...report, historyCheckedAt: item.checkedAt || report.checkedAt } : null;
    })
    .filter(Boolean);

  if (!user) {
    return (
      <motion.div className="account-page" initial={{ opacity: 0, y: 18 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.45 }}>
        <div className="container">
          <div className="account-empty">
            <div className="account-empty-icon"><I.Lock size={22} /></div>
            <h1>Sign in to view your history</h1>
            <p>Your saved checks and account settings will appear here.</p>
            <button className="btn btn-accent btn-lg" onClick={() => go('signin')}>Sign in</button>
          </div>
        </div>
      </motion.div>
    );
  }

  const trialActive = user.trialEndsAt && new Date(user.trialEndsAt).getTime() > Date.now() && user.plan === 'basic';
  const planLabel = user.plan === 'team' ? 'Team' : user.plan === 'pro' ? 'Pro' : trialActive ? 'Pro trial' : 'Basic';

  return (
    <motion.div className="account-page" initial={{ opacity: 0, y: 18 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.45 }}>
      <div className="container">
        <div className="account-hero">
          <div className="account-person">
            <img src={user.avatar || 'assets/rizwan-kabir.jpg'} alt="" width="56" height="56" />
            <div>
              <div className="account-kicker">Account</div>
              <h1>{user.name || 'VidVerdict user'}</h1>
              <p>{user.email}</p>
            </div>
          </div>
          <div className="account-actions">
            <span className="plan-pill">{planLabel}</span>
            <button className="btn btn-secondary" onClick={() => go('home')}>Analyze video</button>
            <button className="btn btn-ghost" onClick={onSignOut}>Sign out</button>
          </div>
        </div>

        <div className="account-stats">
          {[
            ['Checks', String(reportByHistory.length)],
            ['Saved reports', String(reportByHistory.length)],
            ['Plan', 'Beta'],
          ].map(([label, value]) => (
            <div className="account-stat" key={label}>
              <span>{value}</span>
              <small>{label}</small>
            </div>
          ))}
        </div>

        <div className="account-section-head">
          <div>
            <div className="section-eyebrow">History</div>
            <h2>Recent checks</h2>
          </div>
          <button className="btn btn-secondary" onClick={() => go('recent')}>Explore public checks</button>
        </div>

        {reportByHistory.length === 0 ? (
          <div className="account-history-empty">
            <I.Search size={22} />
            <div>
              <strong>No history yet</strong>
              <p>Analyze a public video and your report will appear here.</p>
            </div>
            <button className="btn btn-accent" onClick={() => go('home')}>Analyze Now</button>
          </div>
        ) : (
          <div className="account-history-grid">
            {reportByHistory.map((report, index) => (
              <motion.div
                className="account-history-card"
                key={`${report.id}-${report.url}`}
                onClick={() => go('result', { id: report.id })}
                initial={{ opacity: 0, y: 18 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ delay: index * 0.05, duration: 0.45 }}
                whileHover={{ y: -4, boxShadow: 'var(--shadow)' }}
                whileTap={{ scale: 0.98 }}
              >
                <div className="account-history-thumb">
                  <img src={report.thumbnailUrl || 'assets/hero-video-preview.jpg'} alt={`${report.title} thumbnail`} width="1280" height="720" loading="lazy" decoding="async" />
                  <PlatformBadge platform={report.platform} />
                </div>
                <div className="account-history-body">
                  <div className="account-history-title">{report.title}</div>
                  <div className="account-history-meta">
                    <span>{report.platform || 'Video'}</span>
                    <span>{relativeCheckedAt(report.historyCheckedAt || report.checkedAt)}</span>
                  </div>
                  <div className="account-history-bottom">
                    <span className="verdict mostly"><span className="verdict-dot-sm"></span>{report.headline || 'Report ready'}</span>
                    <strong>{report.score}/100</strong>
                  </div>
                </div>
              </motion.div>
            ))}
          </div>
        )}
      </div>
    </motion.div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
// AI NEWS PAGE
// ═══════════════════════════════════════════════════════════════════════════════
const AI_NEWS_POSTS = [
  {
    slug: 'trusting-ai-generated-video',
    title: 'The new rules for trusting AI-generated video',
    excerpt: 'Synthetic clips are getting easier to make and harder to spot. Here is what creators, journalists, and everyday viewers should watch for next.',
    category: 'AI literacy',
    read: '6 min read',
    date: 'May 3, 2026',
    author: 'Rizwan Kabir',
    authorImage: 'assets/rizwan-kabir.jpg',
    tags: ['Synthetic media', 'Video verification', 'AI literacy'],
    img: 'assets/the-new-rules-for-trusting-ai-generated-video.jpg',
    sections: [
      ['Synthetic video is no longer a future problem', 'AI-generated media now moves through the same feeds as everything else: news clips, creator explainers, ads, political commentary, and screenshots of screenshots. The challenge is not only spotting what is fake. It is knowing when a clip has enough context to be trusted.'],
      ['Look for provenance before perfection', 'A polished video can still be misleading, and a messy video can still be real. Start with source trails: who first published it, whether the upload has a known original, and whether the claim appears anywhere outside a repost chain.'],
      ['Treat confidence as a signal, not a verdict', 'Detection tools can help flag synthetic patterns, but they should not be the entire decision. The strongest verification usually combines technical checks, source history, timestamps, and the factual claims being made in the clip.'],
    ],
  },
  {
    slug: 'deepfake-doctors-tiktok-medical-misinformation-2026',
    title: 'Deepfake Doctors Are Selling Fake Cures on TikTok — And Real Physicians Cannot Stop It',
    excerpt: 'AI is cloning real doctors’ faces and voices to push unproven supplements on TikTok, Instagram, and Facebook. CBS News confirmed the scam is live right now and growing fast.',
    category: 'Deepfakes',
    read: '5 min read',
    date: 'May 2, 2026',
    author: 'Rizwan Kabir',
    authorImage: 'assets/rizwan-kabir.jpg',
    tags: ['Deepfake doctors TikTok', 'AI medical misinformation 2026', 'Fake health videos AI', 'TikTok deepfake scam'],
    img: 'assets/deepfake-doctors.jpg',
    sections: [
      ['Real physicians, entirely fake videos', 'A pediatric rehabilitation specialist discovered his face and voice had been used in an AI-generated TikTok ad selling vitamin supplements he had never endorsed. CBS News confirmed the scam in April 2026, finding multiple licensed physicians whose likenesses had been hijacked for health product promotions. The victims only learned about the videos when patients began asking whether the endorsements were real. The doctors had zero recourse to have the content removed before it went viral.'],
      ['How the AI fraud pipeline works', 'Scammers harvest short clips from legitimate medical content — conference talks, YouTube explainers, Instagram educational reels — and feed them into AI video and voice synthesis tools. The output places a real doctor\'s face onto a scripted sales pitch, with lip movements synced to the script and the physician\'s voice cadence replicated in full. The fake video is then paired with a polished e-commerce funnel designed to collect payment before the viewer has time to question the source. AARP researchers confirmed these attacks disproportionately target adults over 55, who report higher default trust in video evidence over text.'],
      ['Why platforms cannot keep up', 'Most social platforms apply AI-generated content labels at upload. But when a video is screen-recorded and re-posted — the standard method for spreading viral content — those labels disappear entirely. By the time a deepfake doctor video reaches its peak circulation, it has been reshared tens of thousands of times without any disclosure. Platform moderation systems flag nudity and violence far faster than synthetic medical misinformation, which often looks visually identical to legitimate health content.'],
      ['Four things to check before trusting a health video', 'Search the doctor\'s full name alongside the product name. Genuine endorsements appear on the physician\'s own verified channels, not solely in paid ads. Check whether facial movements sync naturally with the audio — AI lip sync still produces subtle mismatches around the jaw and lower teeth. Look for the original clip the deepfake was based on: most medical deepfakes are derived from a discoverable source video. If a video is actively selling something, apply the same skepticism you would to any unsolicited medical advice from a stranger.'],
    ],
  },
  {
    slug: 'ai-voice-cloning-indistinguishable-threshold-2026',
    title: 'Voice Cloning Has Crossed the Indistinguishable Threshold — 1 in 10 Americans Are Already Victims',
    excerpt: 'Researchers, Fortune, and the UN confirmed in 2026 that AI voice cloning is now indistinguishable from real speech. Three seconds of audio is all it takes to clone anyone.',
    category: 'Voice AI',
    read: '4 min read',
    date: 'Apr 30, 2026',
    author: 'Maya Rahman',
    tags: ['AI voice cloning 2026', 'Voice deepfake scam detection', 'AI audio fraud statistics', 'Voice clone indistinguishable'],
    img: 'assets/voice-cloning.jpg',
    sections: [
      ['The indistinguishable threshold is here', 'A December 2025 Fortune investigation, independently confirmed by UN News in March 2026, found that AI voice cloning technology has crossed what security researchers call the indistinguishable threshold. A three-second audio sample — the length of a typical voicemail greeting — now provides enough data to synthesize a voice clone with natural intonation, rhythm, emphasis, emotional coloring, pauses, and breathing noise. In controlled tests, 70 percent of listeners could not tell the difference between a real recording and a clone. The McAfee Consumer Survey placed the direct victimization rate at 1 in 10 Americans as of early 2026.'],
      ['Where the attacks are hitting hardest', 'The UN Office on Drugs and Crime confirmed in March 2026 that voice cloning has become a standard tool for organized fraud. The most documented attack vectors are: family emergency calls impersonating a child or grandchild in distress and requesting urgent wire transfers; CEO fraud where a cloned executive voice authorizes a finance team to move funds; and Medicare and insurance scam calls that use a cloned doctor\'s voice to extract a four-digit verification code. Some major US retailers now report receiving over 1,000 AI-generated scam calls per day. The FBI\'s Internet Crime Complaint Center logged a 400 percent increase in voice-cloning fraud reports between 2024 and early 2026.'],
      ['Social media is the training library', 'Every public TikTok, Instagram reel, YouTube video, podcast, or conference recording is potential training data. Consumer-grade voice cloning tools — several available as browser apps for under $20 per month — can generate a working clone within minutes of being handed an audio sample. Security researchers note that people who post regular video content are at statistically elevated risk: their voice patterns are extensively documented, their speech cadence is predictable, and samples are permanently archived across multiple platforms even after the original post is deleted.'],
      ['How to protect yourself and spot cloned audio', 'Establish a family safe-word for emergency calls — a phrase that exists nowhere online and must be spoken before any financial decision is made. For media verification, focus on what the cloned voice is actually claiming rather than who appears to be speaking: voice authority does not make a claim accurate. Tools like VidVerdict analyze the factual content of video claims against evidence, treating the speaker as one signal among many rather than the final authority. When evaluating suspicious audio, listen for unnatural pauses before proper nouns, slight pitch inconsistencies mid-sentence, and breathing patterns that do not match the apparent emotion of the speaker.'],
    ],
  },
  {
    slug: 'openai-gpt-5-5-agentic-ai-what-it-means-2026',
    title: 'GPT-5.5 Does Not Just Answer Questions Anymore — It Works Unsupervised for Hours',
    excerpt: 'OpenAI shipped GPT-5.5 on April 24, 2026. TechCrunch called it a step toward a super app. The real shift is what happens when AI starts taking actions without waiting for you.',
    category: 'AI Models',
    read: '5 min read',
    date: 'Apr 28, 2026',
    author: 'Daniel Reyes',
    tags: ['GPT-5.5 release April 2026', 'OpenAI agentic AI explained', 'GPT-5.5 vs GPT-5.4', 'AI agent autonomous tasks'],
    img: 'assets/chatgpt5.5.jpg',
    sections: [
      ['What GPT-5.5 does that previous models could not', 'OpenAI shipped GPT-5.5 and GPT-5.5 Pro on April 24, 2026, rolling out to Plus, Pro, Business, and Enterprise users in ChatGPT and the Codex API. The defining capability is not raw benchmark performance — it is what OpenAI internally calls agentic behavior. GPT-5.5 understands a stated goal, generates a multi-step plan, and executes across tools — web search, code execution, spreadsheet creation, form-filling, software operation — without pausing for user confirmation at each step. TechCrunch described it as the clearest step yet toward a unified AI super app that replaces category-specific tools with a single model that can do all of them.'],
      ['Why this creates a new class of misinformation risk', 'An AI that autonomously researches, writes, and publishes is a qualitatively different threat than one that assists a human writer. GPT-5.5-class models can produce articles, social posts, video scripts, and opinion pieces at scale, seeding them into news feeds faster than human fact-checkers can respond. Stanford Internet Observatory researchers noted in April 2026 that automated content farms using GPT-5-class models had produced over 200,000 political articles in the preceding 60 days — with no bylines, no disclosures, and no accountability trail. The content passed basic platform spam filters because it was topically coherent, grammatically correct, and linked to real events.'],
      ['The efficiency and enterprise story', 'OpenAI\'s release notes emphasize that GPT-5.5 uses significantly fewer tokens to complete equivalent tasks compared to GPT-5.4, making it substantially cheaper to deploy at scale. It launched on Amazon Bedrock the same week, meaning enterprise customers with AWS infrastructure can integrate it with minimal engineering overhead. Novo Nordisk, which announced a full-company OpenAI partnership in May 2026, is among the first wave of large organizations planning GPT-5.5 deployment across drug discovery pipelines, clinical trial documentation, and commercial operations. The model\'s lower cost per task makes previously impractical automation economically viable for mid-market companies.'],
      ['The governance gap no one is ready for', 'The critical question for the next 90 days is not whether GPT-5.5 is capable — it demonstrably is — but what oversight structures exist by the time it is widely deployed. The EU AI Act\'s August 2026 compliance deadline covers some automated content generation contexts under its high-risk AI classification. The United States has no equivalent federal law. Platform-level content policies are currently the primary check on how autonomous AI agents interact with public information spaces, and those policies were written for human-authored content, not AI systems that plan, act, and publish independently.'],
    ],
  },
  {
    slug: 'eu-ai-act-august-2026-deadline-what-it-bans',
    title: 'The EU AI Act August 2026 Deadline Is Weeks Away — Here Is Exactly What Gets Banned',
    excerpt: 'August 2, 2026 is the hard compliance date for high-risk AI under EU law. Companies using AI in hiring, credit, or healthcare face binding obligations. Some uses are banned outright.',
    category: 'Policy',
    read: '5 min read',
    date: 'Apr 26, 2026',
    author: 'Noah Bennett',
    tags: ['EU AI Act August 2026 deadline', 'High-risk AI compliance requirements', 'AI regulation what is banned', 'EU AI Act US companies'],
    img: 'assets/eu-ai-act-august.jpg',
    sections: [
      ['August 2, 2026 is the hard line', 'The EU AI Act — Regulation EU 2024/1689, the first comprehensive AI law in the world — entered into force in August 2024 and reaches its most consequential compliance date on August 2, 2026. From that date, any company deploying AI systems classified as high-risk under Annex III and serving EU users must meet binding requirements covering documentation, algorithmic transparency, human oversight mechanisms, and active discrimination prevention. Fines for non-compliance are set at 3 percent of global annual turnover or 15 million euros, whichever is greater. Holland and Knight confirmed in April 2026 that US companies serving EU markets have no legal safe harbor from these obligations regardless of where they are headquartered.'],
      ['What counts as high-risk AI under Annex III', 'Eight domains are classified as high-risk: biometric identification systems, AI in critical infrastructure, education and vocational training tools, employment and HR systems (including resume screening and performance monitoring), essential private and public services such as credit scoring and insurance pricing, law enforcement AI, migration and border control systems, and AI used in the administration of justice. Any system that automates or significantly influences a consequential decision about a person falls within scope. Social media recommendation algorithms are not currently classified as high-risk, though the European Commission has flagged them for a future review cycle.'],
      ['Outright bans already in effect since February 2025', 'The AI Act\'s prohibited practices under Article 5 entered application on February 2, 2025 — over a year ago. Banned systems include real-time remote biometric identification in public spaces for law enforcement purposes (with narrow, court-supervised exceptions), AI that exploits psychological vulnerabilities or manipulates behavior below the level of conscious awareness, social scoring systems operated by public authorities, and predictive policing tools that rely solely on profiling without individualized assessment. These bans apply to any AI provider whose system affects people inside the EU, regardless of where the provider is based or where the servers are located.'],
      ['What US companies must do before August', 'Required actions identified by compliance firms including Gunderson Dettmer and Secure Privacy include: auditing which deployed AI systems fall under Annex III scope; completing conformity assessments for each in-scope system; registering high-risk systems in the EU\'s public AI database; implementing human oversight mechanisms with documented escalation procedures; and appointing an EU-based legal representative if the company has no EU establishment. The European Commission\'s Digital Omnibus proposal to delay some requirements remains pending Parliamentary approval as of May 2026. Legal counsel universally advise against treating that proposal as a reliable safe harbor given the approval timeline uncertainty.'],
    ],
  },
];

const aiNewsDisplayKey = (post) => {
  const value = post?.title || post?.slug || '';
  return String(value)
    .toLowerCase()
    .replace(/['"]/g, '')
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
};

const cleanArticleHeadingText = (value) => String(value || '')
  .replace(/^\s*#{1,6}\s+/g, '')
  .replace(/\*\*/g, '')
  .trim();

const renderArticleInline = (text) => {
  const raw = String(text || '');
  const pattern = /(\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)|(https?:\/\/[^\s)]+)|\(([a-z0-9][a-z0-9.-]+\.[a-z]{2,})(?:\/([^)\s]+))?\))/gi;
  const parts = [];
  let lastIndex = 0;
  let match;
  while ((match = pattern.exec(raw)) !== null) {
    if (match.index > lastIndex) parts.push(raw.slice(lastIndex, match.index));
    const markdownLabel = match[2];
    const markdownUrl = match[3];
    const bareUrl = match[4];
    const domain = match[5];
    const path = match[6];
    const href = markdownUrl || bareUrl || `https://${domain}${path ? `/${path}` : ''}`;
    const label = markdownLabel || domain || bareUrl;
    const trailing = bareUrl?.match(/[.,;:!?]+$/)?.[0] || '';
    const cleanHref = bareUrl && trailing ? bareUrl.slice(0, -trailing.length) : href;
    const cleanLabel = bareUrl && trailing ? bareUrl.slice(0, -trailing.length) : label;
    parts.push(
      <a key={`${cleanHref}-${match.index}`} className="ai-article-link" href={cleanHref} target="_blank" rel="noreferrer">
        {cleanLabel}
      </a>
    );
    if (trailing) parts.push(trailing);
    lastIndex = pattern.lastIndex;
  }
  if (lastIndex < raw.length) parts.push(raw.slice(lastIndex));
  return parts;
};

const renderArticleBody = (text) => String(text || '')
  .replace(/(^|\n)\s*#{1,6}\s+/g, '$1')
  .split(/\n{2,}/)
  .map((paragraph) => paragraph.trim())
  .filter(Boolean)
  .map((paragraph, index) => <p key={index}>{renderArticleInline(paragraph)}</p>);

const isParagraphLikeArticleHeading = (text) => {
  const clean = cleanArticleHeadingText(text);
  const words = clean.split(/\s+/).filter(Boolean).length;
  const sentences = clean.split(/[.!?]\s+/).filter(Boolean).length;
  return clean.length > 95 || words > 14 || (words > 9 && /[.!?]$/.test(clean)) || sentences > 1;
};

const renderArticleSection = (heading, text, index) => {
  const cleanHeading = cleanArticleHeadingText(heading);
  const bodyText = isParagraphLikeArticleHeading(cleanHeading)
    ? `${cleanHeading}\n\n${text || ''}`.trim()
    : String(text || '').trim();
  return (
    <Reveal key={`${cleanHeading || 'section'}-${index}`} delay={0.06 + index * 0.04}>
      <section>
        {cleanHeading && !isParagraphLikeArticleHeading(cleanHeading) ? <h2>{cleanHeading}</h2> : null}
        {renderArticleBody(bodyText)}
      </section>
    </Reveal>
  );
};

const AiNewsPage = ({ go }) => {
  const [generatedPosts, setGeneratedPosts] = useS(null);

  useE(() => {
    let cancelled = false;
    fetch('/api/ai-news')
      .then((res) => res.ok ? res.json() : { posts: [] })
      .then(({ posts = [] }) => {
        if (!cancelled && Array.isArray(posts)) setGeneratedPosts(posts);
      })
      .catch(() => {
        if (!cancelled) setGeneratedPosts([]);
      });
    return () => { cancelled = true; };
  }, []);

  const isLoadingPosts = generatedPosts === null;
  const allPosts = isLoadingPosts ? [] : [...generatedPosts, ...AI_NEWS_POSTS]
    .filter((post, index, list) => {
      const key = aiNewsDisplayKey(post);
      return list.findIndex((item) => aiNewsDisplayKey(item) === key) === index;
    })
    .sort((a, b) => new Date(b.publishedAt || b.date).getTime() - new Date(a.publishedAt || a.date).getTime());
  const [featured, ...posts] = allPosts;

  return (
    <motion.div
      className="container ai-news-page"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.4 }}
    >
      <Reveal><div className="section-eyebrow">AI News</div></Reveal>
      <Reveal delay={0.06}>
        <h2 className="section-title">Clear reporting on AI, trust, and <span className="serif">digital truth.</span></h2>
      </Reveal>

      {isLoadingPosts ? (
        <Reveal delay={0.14}>
          <div className="ai-feature ai-feature-loading" aria-hidden="true">
            <div className="ai-feature-copy">
              <div className="ai-skeleton-line short"></div>
              <div className="ai-skeleton-title"></div>
              <div className="ai-skeleton-title mid"></div>
              <div className="ai-skeleton-line"></div>
              <div className="ai-skeleton-line wide"></div>
            </div>
            <div className="ai-feature-image"></div>
          </div>
        </Reveal>
      ) : featured ? (
        <>
          <Reveal delay={0.14}>
            <div className="ai-feature">
              <div className="ai-feature-copy">
                <div className="ai-meta">{featured.category} · {featured.read}</div>
                <h1>{featured.title}</h1>
                <p>{featured.excerpt}</p>
                <button className="ai-read-link" onClick={() => go('aiPost', { slug: featured.slug })}>Read article <I.ArrowRight size={14} /></button>
              </div>
              <div className="ai-feature-image">
                <img src={featured.img} alt={featured.title} width="960" height="600" loading="eager" decoding="async" fetchPriority="high" />
              </div>
            </div>
          </Reveal>

          <div className="ai-post-grid">
            {posts.slice(0, 5).map((post, i) => (
              <Reveal key={post.title} delay={0.08 + i * 0.05}>
                <motion.article
                  className="ai-post-card"
                  onClick={() => go('aiPost', { slug: post.slug })}
                  whileHover={{ y: -5, boxShadow: '0 8px 32px rgba(0,0,0,0.10)' }}
                  transition={{ duration: 0.22 }}
                >
                  <div className="ai-post-image">
                    <img src={post.img} alt={post.title} width="640" height="400" loading="lazy" decoding="async" />
                  </div>
                  <div className="ai-post-body">
                    <div className="ai-meta">{post.category} · {post.read}</div>
                    <h3>{post.title}</h3>
                    <p>{post.excerpt}</p>
                    <span className="ai-post-read">Read <I.ArrowRight size={11} /></span>
                  </div>
                </motion.article>
              </Reveal>
            ))}
          </div>
        </>
      ) : null}
    </motion.div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
// AI ARTICLE PAGE
// ═══════════════════════════════════════════════════════════════════════════════
const AiPostPage = ({ slug, go, generatedPosts = [] }) => {
  const [localGeneratedPosts, setLocalGeneratedPosts] = useS(generatedPosts);

  useE(() => {
    if (generatedPosts.length) {
      setLocalGeneratedPosts(generatedPosts);
      return;
    }
    let cancelled = false;
    fetch('/api/ai-news')
      .then((res) => res.ok ? res.json() : { posts: [] })
      .then(({ posts = [] }) => {
        if (!cancelled && Array.isArray(posts)) setLocalGeneratedPosts(posts);
      })
      .catch(() => {});
    return () => { cancelled = true; };
  }, [generatedPosts.length, slug]);

  const allPosts = [...localGeneratedPosts, ...AI_NEWS_POSTS]
    .filter((post, index, list) => {
      const key = aiNewsDisplayKey(post);
      return list.findIndex((item) => aiNewsDisplayKey(item) === key) === index;
    })
    .sort((a, b) => new Date(b.publishedAt || b.date).getTime() - new Date(a.publishedAt || a.date).getTime());
  const post = allPosts.find((item) => item.slug === slug);
  const related = post ? allPosts.filter((item) => item.slug !== post.slug).slice(0, 3) : [];
  const allTags = Array.from(new Set(allPosts.flatMap((item) => item.tags || []))).slice(0, 8);

  useE(() => {
    if (!post) return;
    const prev = document.title;
    document.title = `${post.title} | VidVerdict`;
    const metaDesc = document.querySelector('meta[name="description"]');
    const prevDesc = metaDesc ? metaDesc.content : '';
    if (metaDesc) metaDesc.content = post.excerpt;
    const metaOgTitle = document.querySelector('meta[property="og:title"]');
    if (metaOgTitle) metaOgTitle.content = `${post.title} | VidVerdict`;
    const metaOgDesc = document.querySelector('meta[property="og:description"]');
    if (metaOgDesc) metaOgDesc.content = post.excerpt;
    const metaOgImg = document.querySelector('meta[property="og:image"]');
    if (metaOgImg) metaOgImg.content = post.img;
    const canonical = document.querySelector('link[rel="canonical"]');
    const prevCanonical = canonical ? canonical.href : '';
    if (canonical) canonical.href = `https://vidverdict.com/ai-news/${post.slug}`;
    const ldJson = document.createElement('script');
    ldJson.type = 'application/ld+json';
    ldJson.id = `ld-article-${post.slug}`;
    ldJson.textContent = JSON.stringify({
      '@context': 'https://schema.org',
      '@type': 'NewsArticle',
      headline: post.title,
      description: post.excerpt,
      image: [post.img],
      datePublished: post.date,
      dateModified: post.date,
      author: { '@type': 'Person', name: post.author },
      publisher: { '@type': 'Organization', name: 'VidVerdict', logo: { '@type': 'ImageObject', url: 'https://vidverdict.com/assets/Full Logo.svg' } },
      mainEntityOfPage: { '@type': 'WebPage', '@id': `https://vidverdict.com/ai-news/${post.slug}` },
      keywords: (post.tags || []).join(', '),
      articleSection: post.category,
      wordCount: (post.sections || []).reduce((n, [, t]) => n + t.split(' ').length, 0),
    });
    document.head.appendChild(ldJson);
    return () => {
      document.title = prev;
      if (metaDesc) metaDesc.content = prevDesc;
      if (canonical) canonical.href = prevCanonical;
      const existing = document.getElementById(`ld-article-${post.slug}`);
      if (existing) existing.remove();
    };
  }, [post?.slug]);

  if (!post) {
    return (
      <motion.div
        className="container ai-article-page"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ duration: 0.4 }}
      >
        <div className="ai-breadcrumb">
          <span onClick={() => go('home')}>Home</span>
          <span>/</span>
          <span onClick={() => go('aiNews')}>AI News</span>
        </div>
        <div className="ai-article-layout">
          <article className="ai-article-main">
            <h1>Loading article...</h1>
            <p className="ai-article-lede">Fetching the latest AI News article.</p>
          </article>
        </div>
      </motion.div>
    );
  }

  return (
    <motion.div
      className="container ai-article-page"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.4 }}
    >
      <div className="ai-breadcrumb">
        <span onClick={() => go('home')}>Home</span>
        <span>/</span>
        <span onClick={() => go('aiNews')}>AI News</span>
        <span>/</span>
        <strong>{post.category}</strong>
      </div>

      <div className="ai-article-layout">
        <article className="ai-article-main">
          <Reveal>
            <div className="ai-article-topline">
              <div className="ai-article-meta-row">
                <span className="ai-author-pill">
                  <span className="ai-author-avatar">
                    {post.authorImage ? <img src={post.authorImage} alt={post.author} width="24" height="24" loading="lazy" decoding="async" onError={(e) => { e.currentTarget.style.display = 'none'; }} /> : post.author.slice(0, 1)}
                    <span>{post.author.slice(0, 1)}</span>
                  </span>
                  {post.author}
                </span>
                <span>{post.category}</span>
                <span><img src="assets/clock.svg" alt="" width="16" height="16" />{post.read}</span>
                <span><img src="assets/calendar-2.svg" alt="" width="16" height="16" />{post.date}</span>
              </div>
              <button className="ai-save-btn" aria-label="Save article"><I.Heart size={18} /></button>
            </div>
          </Reveal>
          <Reveal delay={0.06}>
            <h1>{cleanArticleHeadingText(post.title)}</h1>
          </Reveal>
          <Reveal delay={0.12}>
            <p className="ai-article-lede">{post.excerpt}</p>
          </Reveal>
          <Reveal delay={0.18}>
            <div className="ai-article-hero">
              <img src={post.img} alt={cleanArticleHeadingText(post.title)} width="1200" height="660" loading="eager" decoding="async" fetchPriority="high" />
            </div>
          </Reveal>

          <div className="ai-article-content">
            {(post.sections || []).map(([heading, text], i) => renderArticleSection(heading, text, i))}
          </div>
        </article>

        <aside className="ai-article-sidebar">
          <div className="ai-side-card">
            <div className="ai-side-title">Share</div>
            <div className="ai-share-row">
              {[
                ['X', <I.Twitter size={17} />],
                ['LinkedIn', <I.Linkedin size={17} />],
                ['Copy', <I.Copy size={17} />],
                ['More', <I.Share size={17} />],
              ].map(([label, icon]) => (
                <button
                  key={label}
                  className="ai-share-btn"
                  aria-label={label}
                  onClick={() => {
                    if (label === 'Copy') {
                      navigator.clipboard?.writeText(window.location.href);
                      showToast('Article link copied');
                    }
                  }}
                >
                  {icon}
                </button>
              ))}
            </div>
          </div>

          <div className="ai-side-card">
            <div className="ai-side-title">Tags</div>
            <div className="ai-tag-list">
              {(post.tags || []).map((tag) => <span key={tag}>{tag}</span>)}
            </div>
          </div>

          <div className="ai-side-card">
            <div className="ai-side-title">Related articles</div>
            <div className="ai-related-list">
              {related.map((item) => (
                <div key={item.slug} className="ai-related-item" onClick={() => go('aiPost', { slug: item.slug })}>
                  <img src={item.img} alt={item.title} width="74" height="58" loading="lazy" decoding="async" />
                  <div>
                    <div>{item.title}</div>
                    <span>{item.date}</span>
                  </div>
                </div>
              ))}
            </div>
          </div>

          <div className="ai-side-card">
            <div className="ai-side-title">All tags</div>
            <div className="ai-tag-list">
              {allTags.map((tag) => <span key={tag}>{tag}</span>)}
            </div>
          </div>
        </aside>
      </div>
    </motion.div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
// ABOUT PAGE
// ═══════════════════════════════════════════════════════════════════════════════
const AboutPage = () => (
  <motion.div
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ duration: 0.4 }}
  >
    <div className="container">
      <div className="about-hero">
        <Reveal><div className="section-eyebrow">About</div></Reveal>
        <Reveal delay={0.06}>
          <div className="about-h">A small tool with a <span className="serif">simple goal:</span> help people separate evidence from noise.</div>
        </Reveal>
        <Reveal delay={0.15}>
          <div className="about-lede">VidVerdict was built because the cost of being wrong online has never been higher — and the time to verify a 60-second clip has never been shorter.</div>
        </Reveal>
      </div>
    </div>
    <div className="container" style={{ paddingBottom: 96 }}>
      <div className="principles">
        {[
          { n: '01', t: 'Evidence over emotion.', d: 'Every verdict cites institutional sources first: governments, universities, journals, fact-checkers.' },
          { n: '02', t: 'Honest uncertainty.', d: 'When the evidence is thin, we say so. "No evidence" is a verdict — and an important one.' },
          { n: '03', t: 'Privacy by default.', d: 'We never store your viewing history. Reports are public only when you choose to share them.' },
          { n: '04', t: 'Transparent methodology.', d: 'Every claim shows how we got there: what was checked, what was found, what we ignored.' },
        ].map((p, i) => (
          <motion.div
            key={p.n}
            className="principle"
            initial={{ opacity: 0, y: 20 }}
            whileInView={{ opacity: 1, y: 0 }}
            viewport={{ once: true }}
            transition={{ delay: i * 0.1, duration: 0.55, ease: [0.16, 1, 0.3, 1] }}
            whileHover={{ backgroundColor: 'var(--surface)' }}
          >
            <motion.div
              className="principle-num"
              whileHover={{ scale: 1.2, color: 'var(--accent)' }}
            >
              {p.n}
            </motion.div>
            <div className="principle-title">{p.t}</div>
            <div className="principle-desc">{p.d}</div>
          </motion.div>
        ))}
      </div>
    </div>
  </motion.div>
);

// ═══════════════════════════════════════════════════════════════════════════════
// LEGAL PAGES
// ═══════════════════════════════════════════════════════════════════════════════
const PrivacyPage = () => (
  <motion.div className="container" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
    <div className="legal">
      <div className="updated">Last updated · April 2026</div>
      <h1>Privacy</h1>
      <p>VidVerdict is built around a simple promise: we don't need to know who you are to fact-check a video.</p>
      <h2>What we collect</h2>
      <p>When you submit a public video URL, we fetch the video, transcribe it, and analyze the claims it contains. We store the resulting report so you can revisit it. We do not link reports to your identity unless you create an account.</p>
      <h2>What we don't collect</h2>
      <p>We don't track which videos you've viewed, we don't sell your data, and we don't profile you across the web.</p>
      <h2>Cookies</h2>
      <p>We use a single first-party cookie to remember your accent-color preference and whether you've dismissed the welcome banner.</p>
      <h2>Contact</h2>
      <p>privacy@vidverdict.com</p>
    </div>
  </motion.div>
);

const TermsPage = () => (
  <motion.div className="container" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}>
    <div className="legal">
      <div className="updated">Last updated · April 2026</div>
      <h1>Terms</h1>
      <p>By using VidVerdict you agree to the terms below. They are written to be readable.</p>
      <h2>What VidVerdict is</h2>
      <p>VidVerdict is an AI-assisted analysis tool. It produces verdicts and confidence estimates based on automated speech-to-text and evidence retrieval. It is not a journalist, not a lawyer, and not a substitute for human judgement on serious matters.</p>
      <h2>What you agree to</h2>
      <p>You agree to submit only public video URLs that you have the right to share. You will not use VidVerdict to harass, defame, or coordinate against others.</p>
      <h2>Limitations</h2>
      <p>Verdicts may be wrong. Confidence is an estimate, not a guarantee. We are not liable for decisions you make based on a report.</p>
      <h2>Changes</h2>
      <p>We may update these terms. We'll mark the date above and surface significant changes in the product.</p>
    </div>
  </motion.div>
);

const AnalyticsPage = ({ go }) => {
  const [token, setToken] = useS(() => localStorage.getItem('vv_admin_analytics_token') || '');
  const [draft, setDraft] = useS(token);
  const [stats, setStats] = useS(null);
  const [error, setError] = useS('');
  const [loading, setLoading] = useS(false);

  const loadStats = async (nextToken = token) => {
    if (!nextToken) {
      setError('Enter your private analytics token.');
      return;
    }
    setLoading(true);
    setError('');
    try {
      const res = await fetch(`/api/analytics?token=${encodeURIComponent(nextToken)}`);
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || 'Could not load analytics.');
      localStorage.setItem('vv_admin_analytics_token', nextToken);
      setToken(nextToken);
      setStats(data);
    } catch (err) {
      setStats(null);
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  useE(() => {
    if (token) loadStats(token);
  }, []);

  const statCards = stats ? [
    ['Total visits', stats.totalVisits],
    ['Last 24 hours', stats.visits24h],
    ['Unique 24h', stats.uniqueVisitors24h],
    ['Last 7 days', stats.visits7d],
  ] : [];

  const table = (title, rows = []) => (
    <div className="analytics-panel">
      <h3>{title}</h3>
      {rows.length ? rows.map((row) => (
        <div className="analytics-row" key={`${title}-${row.name}`}>
          <span>{row.name}</span>
          <strong>{row.count}</strong>
        </div>
      )) : <div className="analytics-empty">No data yet</div>}
    </div>
  );

  return (
    <motion.div className="container analytics-page" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.45 }}>
      <div className="section-kicker">Private</div>
      <h1 className="analytics-title">Visitor analytics</h1>
      <p className="analytics-subtitle">A private snapshot of visits, referrers, countries, devices, and the most viewed pages.</p>

      {!stats && (
        <div className="analytics-login">
          <input
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            placeholder="Private analytics token"
            type="password"
          />
          <button className="btn btn-accent" onClick={() => loadStats(draft)} disabled={loading}>
            {loading ? 'Checking...' : 'Open analytics'}
          </button>
        </div>
      )}
      {error && <div className="analytics-error">{error}</div>}

      {stats && (
        <>
          <div className="analytics-actions">
            <button className="btn btn-ghost" onClick={() => loadStats(token)} disabled={loading}>{loading ? 'Refreshing...' : 'Refresh'}</button>
            <button className="btn btn-ghost" onClick={() => { localStorage.removeItem('vv_admin_analytics_token'); setStats(null); setToken(''); setDraft(''); }}>Lock</button>
          </div>
          <div className="analytics-stat-grid">
            {statCards.map(([label, value]) => (
              <div className="analytics-stat" key={label}>
                <div>{label}</div>
                <strong>{value}</strong>
              </div>
            ))}
          </div>
          <div className="analytics-grid">
            {table('Top pages', stats.topPages)}
            {table('Referrers', stats.topReferrers)}
            {table('Countries', stats.countries)}
            {table('Devices', stats.devices)}
          </div>
          <div className="analytics-panel">
            <h3>Recent visits</h3>
            {(stats.recent || []).map((visit, index) => (
              <div className="analytics-recent" key={`${visit.at}-${index}`}>
                <span>{new Date(visit.at).toLocaleString()}</span>
                <strong>{visit.path}</strong>
                <span>{visit.country} · {visit.device}</span>
              </div>
            ))}
          </div>
        </>
      )}
    </motion.div>
  );
};

Object.assign(window, { HomePage, LoadingPage, ResultsPage, RecentPage, PricingPage, AiNewsPage, AiPostPage, SignInPage, AccountPage, AnalyticsPage, AboutPage, PrivacyPage, TermsPage, TrustStrip, HeroVisual, SampleReport });
