← back to Norma

app/pulse/page.tsx

784 lines

'use client';

import React, { useEffect, useState, useCallback } from 'react';
import Link from 'next/link';
import {
  ArrowRight, Users, FileText, Tag, TrendingUp,
  ChevronRight, Target, Sparkles, Bookmark, BookmarkCheck,
  Activity, User, LogIn,
} from 'lucide-react';

/* ────────────────────────── Types ────────────────────────── */

interface Petition {
  id: string;
  title: string;
  body: string | null;
  description: string | null;
  target: string | null;
  category: string | null;
  signature_count: number | null;
  signature_goal: number | null;
  is_featured: boolean;
  tags: string[] | null;
  created_at: string;
}

interface TopicCategory {
  category: string;
  count: number;
}

interface SessionInfo {
  authenticated: boolean;
  user?: string;
  role?: string;
}

/* ────────────────────────── Rolling Brand Animation ─────── */

const ROLL_PHRASES = ['Your Voice', 'Your Power', 'Your Petition', 'Your Future'];
const ROLL_INTERVAL = 2800;

function RollingText() {
  const [idx, setIdx] = useState(0);
  const [animKey, setAnimKey] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setIdx(prev => (prev + 1) % ROLL_PHRASES.length);
      setAnimKey(prev => prev + 1);
    }, ROLL_INTERVAL);
    return () => clearInterval(timer);
  }, []);

  return (
    <span style={{ display: 'inline-block', perspective: 600, verticalAlign: 'bottom' }}>
      <span
        key={animKey}
        style={{
          display: 'inline-block',
          animation: `poaRollIn 0.7s cubic-bezier(0.23, 1, 0.32, 1) forwards`,
          color: '#DAA520',
          willChange: 'transform, opacity',
        }}
      >
        {ROLL_PHRASES[idx]}
      </span>
      <style>{`
        @keyframes poaRollIn {
          0% { opacity: 0; transform: rotateX(-90deg) translateY(20px); }
          30% { opacity: 0.6; }
          100% { opacity: 1; transform: rotateX(0deg) translateY(0); }
        }
        @keyframes poaPulse {
          0%, 100% { box-shadow: 0 0 0 0 rgba(218,165,32,0.4); }
          50% { box-shadow: 0 0 20px 8px rgba(218,165,32,0.15); }
        }
        @keyframes poaGlow {
          0%, 100% { text-shadow: 0 0 10px rgba(218,165,32,0.3); }
          50% { text-shadow: 0 0 30px rgba(218,165,32,0.6), 0 0 60px rgba(218,165,32,0.2); }
        }
        @keyframes poaFloat {
          0%, 100% { transform: translateY(0); }
          50% { transform: translateY(-4px); }
        }
        @keyframes poaMarquee {
          0% { transform: translateX(0); }
          100% { transform: translateX(-50%); }
        }
      `}</style>
    </span>
  );
}

function POABadge() {
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 8,
      padding: '8px 20px', borderRadius: 9999,
      background: 'linear-gradient(135deg, rgba(218,165,32,0.2), rgba(218,165,32,0.05))',
      border: '1px solid rgba(218,165,32,0.4)',
      animation: 'poaPulse 3s ease-in-out infinite, poaFloat 4s ease-in-out infinite',
      fontFamily: 'Georgia, serif', fontWeight: 700, fontSize: 14,
      letterSpacing: 3, color: '#DAA520', textTransform: 'uppercase',
    }}>
      <span style={{
        width: 8, height: 8, borderRadius: '50%',
        backgroundColor: '#DAA520',
        animation: 'poaPulse 2s ease-in-out infinite',
      }} />
      Pulse of America
    </span>
  );
}

function BrandMarquee() {
  const items = ['POA', '\u2605', 'PULSE OF AMERICA', '\u2605', 'YOUR VOICE MATTERS', '\u2605', 'CIVIC ACTION', '\u2605'];
  const doubled = [...items, ...items];
  return (
    <div style={{
      overflow: 'hidden', width: '100%', position: 'absolute', bottom: 0,
      borderTop: '1px solid rgba(218,165,32,0.15)',
      backgroundColor: 'rgba(0,0,0,0.1)',
      padding: '8px 0',
    }}>
      <div style={{
        display: 'flex', gap: 40, whiteSpace: 'nowrap',
        animation: 'poaMarquee 30s linear infinite',
        width: 'max-content',
      }}>
        {doubled.map((item, i) => (
          <span key={i} style={{
            fontSize: 11, fontWeight: 700, letterSpacing: 4,
            color: 'rgba(218,165,32,0.4)', fontFamily: 'system-ui, sans-serif',
            textTransform: 'uppercase',
          }}>
            {item}
          </span>
        ))}
      </div>
    </div>
  );
}

/* ────────────────────────── Colors ────────────────────────── */

const C = {
  darkGreen: '#1B4332',
  gold: '#DAA520',
  cream: '#FAFAF5',
  text: '#1a1a1a',
  textLight: '#fafafa',
  textMuted: '#6b7280',
  border: '#e5e7eb',
  greenLight: '#d1fae5',
};

/* ────────────────────────── Helpers ────────────────────────── */

function categoryLabel(cat: string | null): string {
  if (!cat) return 'General';
  return cat.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
}

function categoryColor(cat: string | null): string {
  const colors: Record<string, string> = {
    debt_cancellation: '#dc2626',
    consumer_protection: '#6366f1',
    voting_rights: '#7c3aed',
    legal: '#d97706',
    trending: '#059669',
    education: '#0891b2',
    environment: '#16a34a',
    healthcare: '#e11d48',
  };
  return colors[cat || ''] || '#6b7280';
}

function progressPercent(count: number | null, goal: number | null): number {
  if (!goal || goal <= 0) return 0;
  const c = count || 0;
  return Math.min(100, Math.round((c / goal) * 100));
}

function formatNumber(n: number | null): string {
  if (n === null || n === undefined) return '0';
  if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`;
  if (n >= 1000) return `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}K`;
  return n.toString();
}

/* ────────────────────────── Petition Card ────────────────────────── */

function PetitionCard({
  petition,
  isTracked,
  onTrack,
  isAuthenticated,
}: {
  petition: Petition;
  isTracked: boolean;
  onTrack: (id: string, track: boolean) => void;
  isAuthenticated: boolean;
}) {
  const preview = (petition.body || petition.description || '').slice(0, 120);
  const pct = progressPercent(petition.signature_count, petition.signature_goal);
  const [tracking, setTracking] = useState(false);

  async function handleTrack(e: React.MouseEvent) {
    e.preventDefault();
    e.stopPropagation();
    if (tracking) return;
    setTracking(true);
    try {
      onTrack(petition.id, !isTracked);
    } finally {
      setTracking(false);
    }
  }

  return (
    <div
      style={{
        backgroundColor: '#fff', borderRadius: 16, overflow: 'hidden',
        boxShadow: '0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06)',
        transition: 'transform 200ms, box-shadow 200ms',
        display: 'flex', flexDirection: 'column', height: '100%',
        border: petition.is_featured ? `2px solid ${C.gold}` : '1px solid #e5e7eb',
      }}
      onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.boxShadow = '0 10px 25px rgba(0,0,0,0.1)'; }}
      onMouseLeave={e => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 1px 3px rgba(0,0,0,0.08)'; }}
    >
      {/* Featured badge */}
      {petition.is_featured && (
        <div style={{ backgroundColor: C.gold, color: C.darkGreen, padding: '6px 16px', fontSize: 12, fontWeight: 700, fontFamily: 'system-ui, sans-serif', display: 'flex', alignItems: 'center', gap: 6 }}>
          <Sparkles size={14} />
          FEATURED PETITION
        </div>
      )}

      <div style={{ padding: 24, flex: 1, display: 'flex', flexDirection: 'column' }}>
        {/* Category badge + Track button */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
          <span style={{
            display: 'inline-block', fontSize: 11, fontWeight: 600,
            fontFamily: 'system-ui, sans-serif', textTransform: 'uppercase',
            letterSpacing: '0.5px', padding: '4px 10px', borderRadius: 9999,
            backgroundColor: `${categoryColor(petition.category)}15`,
            color: categoryColor(petition.category),
          }}>
            {categoryLabel(petition.category)}
          </span>

          {isAuthenticated && (
            <button
              onClick={handleTrack}
              disabled={tracking}
              title={isTracked ? 'Untrack petition' : 'Track petition'}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                padding: '4px 10px', borderRadius: 6, fontSize: 12, fontWeight: 600,
                fontFamily: 'system-ui, sans-serif',
                backgroundColor: isTracked ? 'rgba(218,165,32,0.1)' : 'transparent',
                border: `1px solid ${isTracked ? C.gold : C.border}`,
                color: isTracked ? C.gold : C.textMuted,
                cursor: 'pointer', transition: 'all 150ms',
              }}
            >
              {isTracked ? <BookmarkCheck size={13} /> : <Bookmark size={13} />}
              {isTracked ? 'Tracked' : 'Track'}
            </button>
          )}
        </div>

        {/* Title */}
        <h3 style={{ fontSize: 18, fontWeight: 700, lineHeight: '26px', margin: '0 0 8px', color: C.text, fontFamily: 'Georgia, serif' }}>
          <Link href={`/pulse/petition/${petition.id}`} style={{ textDecoration: 'none', color: 'inherit' }}>
            {petition.title}
          </Link>
        </h3>

        {/* Preview */}
        <p style={{ fontSize: 14, lineHeight: '22px', color: C.textMuted, margin: '0 0 12px', fontFamily: 'system-ui, sans-serif', flex: 1 }}>
          {preview}{preview.length >= 120 ? '...' : ''}
        </p>

        {/* Target */}
        {petition.target && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 16, fontSize: 13, color: C.darkGreen, fontFamily: 'system-ui, sans-serif' }}>
            <Target size={14} />
            <span style={{ fontWeight: 500 }}>{petition.target}</span>
          </div>
        )}

        {/* Progress */}
        <div>
          {petition.signature_goal && petition.signature_goal > 0 ? (
            <>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6, fontSize: 13, fontFamily: 'system-ui, sans-serif' }}>
                <span style={{ fontWeight: 600, color: C.text }}>
                  {formatNumber(petition.signature_count)} signatures
                </span>
                <span style={{ color: C.textMuted }}>
                  Goal: {formatNumber(petition.signature_goal)}
                </span>
              </div>
              <div style={{ height: 6, borderRadius: 3, backgroundColor: '#e5e7eb', overflow: 'hidden' }}>
                <div style={{
                  height: '100%', borderRadius: 3, backgroundColor: C.gold,
                  width: `${pct}%`, transition: 'width 500ms ease',
                }} />
              </div>
            </>
          ) : (
            <div style={{ fontSize: 13, fontWeight: 600, color: C.text, fontFamily: 'system-ui, sans-serif' }}>
              {formatNumber(petition.signature_count)} signatures
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────── Quick Links Bar ────────────────────────── */

function QuickLinksBar({ session }: { session: SessionInfo }) {
  if (!session.authenticated) return null;

  return (
    <section style={{
      maxWidth: 1200, margin: '0 auto', padding: '0 24px',
    }}>
      <div style={{
        display: 'flex', gap: 12, padding: '20px 0',
        flexWrap: 'wrap', justifyContent: 'center',
      }}>
        {[
          { href: '/pulse/activity', label: 'My Activity', icon: Activity, color: C.darkGreen },
          { href: '/pulse/tracked', label: 'Tracked Petitions', icon: Bookmark, color: C.gold },
          { href: '/pulse/profile', label: 'My Profile', icon: User, color: '#6366f1' },
        ].map(link => (
          <Link
            key={link.href}
            href={link.href}
            style={{
              display: 'inline-flex', alignItems: 'center', gap: 8,
              padding: '10px 20px', borderRadius: 10,
              backgroundColor: '#fff', border: '1px solid ' + C.border,
              color: C.text, textDecoration: 'none',
              fontSize: 14, fontWeight: 500, fontFamily: 'system-ui, sans-serif',
              transition: 'all 150ms',
              boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
            }}
            onMouseEnter={e => { e.currentTarget.style.borderColor = link.color; e.currentTarget.style.backgroundColor = '#fafafa'; }}
            onMouseLeave={e => { e.currentTarget.style.borderColor = C.border; e.currentTarget.style.backgroundColor = '#fff'; }}
          >
            <link.icon size={16} style={{ color: link.color }} />
            {link.label}
          </Link>
        ))}
      </div>
    </section>
  );
}

/* ────────────────────────── Main Page ────────────────────────── */

export default function PulseLandingPage() {
  const [petitions, setPetitions] = useState<Petition[]>([]);
  const [topics, setTopics] = useState<TopicCategory[]>([]);
  const [loading, setLoading] = useState(true);
  const [stats, setStats] = useState({ signatures: 0, petitions: 0, topics: 0 });
  const [session, setSession] = useState<SessionInfo>({ authenticated: false });
  const [trackedIds, setTrackedIds] = useState<Set<string>>(new Set());
  const [categoryAffinity, setCategoryAffinity] = useState<Record<string, number>>({});

  // Load session and personalization data
  useEffect(() => {
    fetch('/api/auth/session', { credentials: 'include' })
      .then(r => r.json())
      .then(data => {
        if (data.authenticated) {
          setSession({ authenticated: true, user: data.user, role: data.role });

          // Load tracked petitions and activity for personalization
          Promise.all([
            fetch('/api/pulse/tracked?page=1&limit=100', { credentials: 'include' }).then(r => r.ok ? r.json() : { tracked: [] }),
            fetch('/api/pulse/activity?page=1&limit=100', { credentials: 'include' }).then(r => r.ok ? r.json() : { activity: [] }),
          ]).then(([trackedData, activityData]) => {
            // Build tracked set
            const ids = new Set<string>((trackedData.tracked || []).map((t: { petition_id: string }) => t.petition_id));
            setTrackedIds(ids);

            // Build category affinity from signed + tracked petitions
            const affinity: Record<string, number> = {};
            for (const t of (trackedData.tracked || [])) {
              const cat = t.category || 'other';
              affinity[cat] = (affinity[cat] || 0) + 1;
            }
            for (const a of (activityData.activity || [])) {
              const cat = a.category || 'other';
              affinity[cat] = (affinity[cat] || 0) + 2; // signatures weigh more
            }
            setCategoryAffinity(affinity);
          }).catch(() => {});
        }
      })
      .catch(() => {});
  }, []);

  // Load petitions and topics
  useEffect(() => {
    async function load() {
      try {
        const [petRes, topicRes] = await Promise.all([
          fetch('/api/pulse/petitions?limit=12&sort=featured'),
          fetch('/api/petitions/topics?limit=12&source=pulse&org=false'),
        ]);

        if (petRes.ok) {
          const petData = await petRes.json();
          setPetitions(petData.petitions || []);
          const totalSigs = (petData.petitions || []).reduce(
            (sum: number, p: Petition) => sum + (p.signature_count || 0), 0
          );
          setStats(prev => ({
            ...prev,
            signatures: totalSigs,
            petitions: petData.total || petData.petitions?.length || 0,
          }));
        }

        if (topicRes.ok) {
          const topicData = await topicRes.json();
          setTopics(topicData.categories || []);
          setStats(prev => ({ ...prev, topics: (topicData.categories || []).length }));
        }
      } catch (err) {
        console.error('Failed to load landing page data:', err);
      } finally {
        setLoading(false);
      }
    }
    load();
  }, []);

  // Weight petitions by category affinity if user is authenticated
  const sortedPetitions = React.useMemo(() => {
    if (!session.authenticated || Object.keys(categoryAffinity).length === 0) {
      return petitions;
    }

    // Score each petition by category affinity + featured status
    return [...petitions].sort((a, b) => {
      const scoreA = (a.is_featured ? 100 : 0) + (categoryAffinity[a.category || 'other'] || 0) * 10;
      const scoreB = (b.is_featured ? 100 : 0) + (categoryAffinity[b.category || 'other'] || 0) * 10;
      return scoreB - scoreA;
    });
  }, [petitions, categoryAffinity, session.authenticated]);

  // Handle track/untrack
  const handleTrack = useCallback(async (petitionId: string, track: boolean) => {
    if (!session.authenticated) return;

    try {
      if (track) {
        const res = await fetch('/api/pulse/tracked', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          credentials: 'include',
          body: JSON.stringify({ petition_id: petitionId }),
        });
        if (res.ok) {
          setTrackedIds(prev => new Set(prev).add(petitionId));
        }
      } else {
        const res = await fetch('/api/pulse/tracked', {
          method: 'DELETE',
          headers: { 'Content-Type': 'application/json' },
          credentials: 'include',
          body: JSON.stringify({ petition_id: petitionId }),
        });
        if (res.ok) {
          setTrackedIds(prev => { const s = new Set(prev); s.delete(petitionId); return s; });
        }
      }
    } catch {
      // Silently fail
    }
  }, [session.authenticated]);

  return (
    <div>
      {/* ──── Hero Section ──── */}
      <section style={{
        backgroundColor: C.darkGreen, color: C.textLight, position: 'relative',
        overflow: 'hidden',
      }}>
        {/* Decorative background pattern */}
        <div style={{
          position: 'absolute', inset: 0, opacity: 0.05,
          backgroundImage: 'radial-gradient(circle at 25% 50%, #DAA520 1px, transparent 1px), radial-gradient(circle at 75% 50%, #DAA520 1px, transparent 1px)',
          backgroundSize: '60px 60px',
        }} />

        <div style={{ maxWidth: 1200, margin: '0 auto', padding: '80px 24px 72px', position: 'relative', zIndex: 1 }}>
          <div style={{ maxWidth: 720, margin: '0 auto', textAlign: 'center' }}>
            {/* Animated POA Badge */}
            <div style={{ marginBottom: 20 }}>
              <POABadge />
            </div>

            {/* Rolling Headline */}
            <h1 style={{
              fontSize: 'clamp(36px, 5vw, 55px)', fontWeight: 'bold', lineHeight: 1.15,
              margin: '0 0 20px', fontFamily: 'Georgia, serif',
            }}>
              <RollingText />.{' '}
              <span style={{ opacity: 0.85 }}>Your Petition.</span>
            </h1>

            {/* Subtitle — personalized if authenticated */}
            <p style={{
              fontSize: 'clamp(16px, 2vw, 20px)', lineHeight: '30px', opacity: 0.85,
              margin: '0 0 36px', fontFamily: 'system-ui, sans-serif',
            }}>
              {session.authenticated
                ? `Welcome back, ${session.user}. Continue making a difference through collective action.`
                : 'Join thousands making a difference through collective action. Create, sign, and share petitions that drive real change.'}
            </p>

            {/* CTA buttons */}
            <div style={{ display: 'flex', gap: 16, justifyContent: 'center', flexWrap: 'wrap', marginBottom: 48 }}>
              <Link
                href="/pulse/petitions/create"
                style={{
                  display: 'inline-flex', alignItems: 'center', gap: 8,
                  backgroundColor: C.gold, color: C.darkGreen, textDecoration: 'none',
                  fontSize: 16, fontWeight: 700, fontFamily: 'system-ui, sans-serif',
                  padding: '16px 32px', borderRadius: 9999, transition: 'transform 150ms, background-color 150ms',
                }}
                onMouseEnter={e => { e.currentTarget.style.transform = 'scale(1.03)'; e.currentTarget.style.backgroundColor = '#c8941a'; }}
                onMouseLeave={e => { e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.backgroundColor = C.gold; }}
              >
                Create A Petition
                <ArrowRight size={18} />
              </Link>

              <Link
                href="/pulse/petitions"
                style={{
                  display: 'inline-flex', alignItems: 'center', gap: 8,
                  backgroundColor: 'transparent', color: C.textLight, textDecoration: 'none',
                  fontSize: 16, fontWeight: 600, fontFamily: 'system-ui, sans-serif',
                  padding: '16px 32px', borderRadius: 9999, border: '2px solid rgba(255,255,255,0.4)',
                  transition: 'border-color 150ms, background-color 150ms',
                }}
                onMouseEnter={e => { e.currentTarget.style.borderColor = '#fff'; e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.08)'; }}
                onMouseLeave={e => { e.currentTarget.style.borderColor = 'rgba(255,255,255,0.4)'; e.currentTarget.style.backgroundColor = 'transparent'; }}
              >
                Browse Petitions
              </Link>

              {!session.authenticated && (
                <Link
                  href="/login?returnTo=/pulse"
                  style={{
                    display: 'inline-flex', alignItems: 'center', gap: 8,
                    backgroundColor: 'transparent', color: C.textLight, textDecoration: 'none',
                    fontSize: 16, fontWeight: 500, fontFamily: 'system-ui, sans-serif',
                    padding: '16px 24px', borderRadius: 9999,
                    transition: 'opacity 150ms', opacity: 0.75,
                  }}
                  onMouseEnter={e => (e.currentTarget.style.opacity = '1')}
                  onMouseLeave={e => (e.currentTarget.style.opacity = '0.75')}
                >
                  <LogIn size={18} />
                  Sign In
                </Link>
              )}
            </div>

            {/* Stats row */}
            <div style={{
              display: 'flex', justifyContent: 'center', gap: 0, flexWrap: 'wrap',
            }}>
              {[
                { icon: <Users size={20} />, value: stats.signatures > 0 ? `${formatNumber(stats.signatures)}+` : '5,000+', label: 'Signatures' },
                { icon: <FileText size={20} />, value: stats.petitions > 0 ? `${stats.petitions}+` : '50+', label: 'Active Petitions' },
                { icon: <Tag size={20} />, value: stats.topics > 0 ? `${stats.topics}` : '12', label: 'Topics' },
              ].map((stat, i) => (
                <div
                  key={stat.label}
                  style={{
                    padding: '16px 32px', textAlign: 'center',
                    borderLeft: i > 0 ? '1px solid rgba(255,255,255,0.15)' : 'none',
                  }}
                >
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, marginBottom: 4 }}>
                    <span style={{ color: C.gold }}>{stat.icon}</span>
                    <span style={{ fontSize: 24, fontWeight: 'bold', fontFamily: 'Georgia, serif' }}>{stat.value}</span>
                  </div>
                  <span style={{ fontSize: 13, opacity: 0.7, fontFamily: 'system-ui, sans-serif' }}>{stat.label}</span>
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* Brand Marquee */}
        <BrandMarquee />

        {/* Wave divider */}
        <div style={{ position: 'relative', height: 40, overflow: 'hidden' }}>
          <svg viewBox="0 0 1200 40" preserveAspectRatio="none" style={{ position: 'absolute', bottom: 0, width: '100%', height: 40 }}>
            <path d="M0,20 Q300,40 600,20 T1200,20 L1200,40 L0,40 Z" fill={C.cream} />
          </svg>
        </div>
      </section>

      {/* ──── Quick Links (authenticated only) ──── */}
      <QuickLinksBar session={session} />

      {/* ──── Featured Petitions ──── */}
      <section style={{ maxWidth: 1200, margin: '0 auto', padding: '48px 24px 64px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 32, flexWrap: 'wrap', gap: 12 }}>
          <div>
            <h2 style={{ fontSize: 36, fontWeight: 'bold', color: C.text, margin: 0, fontFamily: 'Georgia, serif' }}>
              {session.authenticated && Object.keys(categoryAffinity).length > 0
                ? 'Recommended For You'
                : 'Featured Petitions'}
            </h2>
            <p style={{ fontSize: 16, color: C.textMuted, margin: '8px 0 0', fontFamily: 'system-ui, sans-serif' }}>
              {session.authenticated && Object.keys(categoryAffinity).length > 0
                ? 'Petitions matching your interests and activity'
                : 'Active campaigns making a difference right now'}
            </p>
          </div>
          <Link
            href="/pulse/petitions"
            style={{
              display: 'inline-flex', alignItems: 'center', gap: 6,
              color: C.darkGreen, textDecoration: 'none', fontSize: 15, fontWeight: 600,
              fontFamily: 'system-ui, sans-serif',
            }}
          >
            View all petitions
            <ChevronRight size={18} />
          </Link>
        </div>

        {loading ? (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))', gap: 24 }}>
            {[1, 2, 3].map(i => (
              <div key={i} style={{ backgroundColor: '#fff', borderRadius: 16, height: 280, border: '1px solid #e5e7eb' }}>
                <div style={{ padding: 24 }}>
                  <div style={{ height: 14, backgroundColor: '#e5e7eb', borderRadius: 7, width: '40%', marginBottom: 16 }} />
                  <div style={{ height: 20, backgroundColor: '#e5e7eb', borderRadius: 7, width: '90%', marginBottom: 8 }} />
                  <div style={{ height: 20, backgroundColor: '#e5e7eb', borderRadius: 7, width: '70%', marginBottom: 16 }} />
                  <div style={{ height: 14, backgroundColor: '#e5e7eb', borderRadius: 7, width: '100%', marginBottom: 8 }} />
                  <div style={{ height: 14, backgroundColor: '#e5e7eb', borderRadius: 7, width: '80%' }} />
                </div>
              </div>
            ))}
          </div>
        ) : sortedPetitions.length === 0 ? (
          <div style={{ textAlign: 'center', padding: 60, color: C.textMuted, fontFamily: 'system-ui, sans-serif' }}>
            <FileText size={48} style={{ opacity: 0.3, marginBottom: 16 }} />
            <p style={{ fontSize: 18 }}>No active petitions yet. Be the first to create one!</p>
          </div>
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))', gap: 24 }}>
            {sortedPetitions.slice(0, 6).map(p => (
              <PetitionCard
                key={p.id}
                petition={p}
                isTracked={trackedIds.has(p.id)}
                onTrack={handleTrack}
                isAuthenticated={session.authenticated}
              />
            ))}
          </div>
        )}

        {/* Show more if there are more than 6 */}
        {!loading && sortedPetitions.length > 6 && (
          <div style={{ textAlign: 'center', marginTop: 32 }}>
            <Link
              href="/pulse/petitions"
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 8,
                backgroundColor: '#fff', color: C.darkGreen, textDecoration: 'none',
                fontSize: 15, fontWeight: 600, fontFamily: 'system-ui, sans-serif',
                padding: '12px 28px', borderRadius: 9999, border: '2px solid ' + C.darkGreen,
                transition: 'all 150ms',
              }}
              onMouseEnter={e => { e.currentTarget.style.backgroundColor = C.darkGreen; e.currentTarget.style.color = '#fff'; }}
              onMouseLeave={e => { e.currentTarget.style.backgroundColor = '#fff'; e.currentTarget.style.color = C.darkGreen; }}
            >
              View All {stats.petitions}+ Petitions
              <ArrowRight size={16} />
            </Link>
          </div>
        )}
      </section>

      {/* ──── Topics Section ──── */}
      {topics.length > 0 && (
        <section style={{ backgroundColor: '#fff', borderTop: '1px solid #e5e7eb', borderBottom: '1px solid #e5e7eb' }}>
          <div style={{ maxWidth: 1200, margin: '0 auto', padding: '64px 24px' }}>
            <div style={{ textAlign: 'center', marginBottom: 36 }}>
              <h2 style={{ fontSize: 36, fontWeight: 'bold', color: C.text, margin: 0, fontFamily: 'Georgia, serif' }}>
                Browse by Topic
              </h2>
              <p style={{ fontSize: 16, color: C.textMuted, margin: '8px 0 0', fontFamily: 'system-ui, sans-serif' }}>
                Find petitions that matter to you
              </p>
            </div>
            <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 12 }}>
              {topics.slice(0, 12).map(t => {
                const hasAffinity = categoryAffinity[t.category] > 0;
                return (
                  <Link
                    key={t.category}
                    href={`/pulse/petitions?category=${encodeURIComponent(t.category)}`}
                    style={{
                      display: 'inline-flex', alignItems: 'center', gap: 8,
                      padding: '10px 20px', borderRadius: 9999,
                      backgroundColor: hasAffinity ? 'rgba(218,165,32,0.08)' : C.cream,
                      border: `1px solid ${hasAffinity ? C.gold : C.border}`,
                      color: C.text, textDecoration: 'none',
                      fontSize: 14, fontWeight: hasAffinity ? 600 : 500,
                      fontFamily: 'system-ui, sans-serif',
                      transition: 'all 150ms',
                    }}
                    onMouseEnter={e => { e.currentTarget.style.backgroundColor = C.darkGreen; e.currentTarget.style.color = '#fff'; e.currentTarget.style.borderColor = C.darkGreen; }}
                    onMouseLeave={e => { e.currentTarget.style.backgroundColor = hasAffinity ? 'rgba(218,165,32,0.08)' : C.cream; e.currentTarget.style.color = C.text; e.currentTarget.style.borderColor = hasAffinity ? C.gold : C.border; }}
                  >
                    <Tag size={14} style={{ opacity: 0.6 }} />
                    {categoryLabel(t.category)}
                    <span style={{ fontSize: 12, opacity: 0.6 }}>({t.count})</span>
                  </Link>
                );
              })}
            </div>
          </div>
        </section>
      )}

      {/* ──── CTA Banner ──── */}
      <section style={{ backgroundColor: C.darkGreen, position: 'relative', overflow: 'hidden' }}>
        <div style={{
          position: 'absolute', inset: 0, opacity: 0.05,
          backgroundImage: 'radial-gradient(circle, #DAA520 1px, transparent 1px)',
          backgroundSize: '40px 40px',
        }} />
        <div style={{ maxWidth: 800, margin: '0 auto', padding: '72px 24px', textAlign: 'center', position: 'relative', zIndex: 1 }}>
          <h2 style={{ fontSize: 'clamp(28px, 4vw, 42px)', fontWeight: 'bold', color: C.textLight, margin: '0 0 16px', fontFamily: 'Georgia, serif' }}>
            Ready to Make a <span style={{ color: C.gold }}>Difference</span>?
          </h2>
          <p style={{ fontSize: 18, color: 'rgba(255,255,255,0.8)', margin: '0 0 32px', fontFamily: 'system-ui, sans-serif', lineHeight: '28px' }}>
            Every petition starts with one voice. Add yours today and help shape the future.
          </p>
          <Link
            href="/pulse/petitions/create"
            style={{
              display: 'inline-flex', alignItems: 'center', gap: 8,
              backgroundColor: C.gold, color: C.darkGreen, textDecoration: 'none',
              fontSize: 18, fontWeight: 700, fontFamily: 'system-ui, sans-serif',
              padding: '18px 40px', borderRadius: 9999, transition: 'transform 150ms, background-color 150ms',
            }}
            onMouseEnter={e => { e.currentTarget.style.transform = 'scale(1.03)'; e.currentTarget.style.backgroundColor = '#c8941a'; }}
            onMouseLeave={e => { e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.backgroundColor = C.gold; }}
          >
            Start Your Petition
            <TrendingUp size={20} />
          </Link>
        </div>
      </section>
    </div>
  );
}