const { Button, Card, Badge, GenerateOrb } = window.TaglyDesignSystem_1a793a;
const { ResultsPanel } = window.TaglyResults;

const PLATFORMS = ['YouTube', 'TikTok', 'Instagram', 'Facebook', 'LinkedIn', 'X', 'Threads', 'Pinterest', 'Reddit'];
const DEFAULT_TRENDING_UPDATED = '27 Jul 2026';
const DEFAULT_TRENDING = { YouTube: ['travel vlog', 'gaming setup', 'study with me', 'morning routine'], TikTok: ['aesthetic room', 'day in my life', 'cooking hack', 'outfit ideas'], Instagram: ['real estate', 'coffee shop', 'skincare routine', 'small business'], Facebook: ['local business', 'community group', 'garage sale', 'family reunion'], LinkedIn: ['career advice', 'remote work', 'personal branding', 'job search tips'], X: ['tech news', 'personal finance', 'startup life', 'current events'], Threads: ['daily thoughts', 'hot takes', 'community updates', 'behind the scenes'], Pinterest: ['home decor', 'recipe ideas', 'wedding inspo', 'outfit boards'], Reddit: ['ask for advice', 'niche communities', 'product reviews', 'discussion threads'] };
const DEFAULT_TRENDING_DETAILED = {
  YouTube: [['travel vlog', 'up'], ['gaming setup', 'up'], ['study with me', 'steady'], ['morning routine', 'up'], ['ai tools 2026', 'up'], ['home workout', 'steady']],
  TikTok: [['aesthetic room', 'up'], ['day in my life', 'steady'], ['cooking hack', 'up'], ['outfit ideas', 'steady'], ['get ready with me', 'up'], ['desk setup', 'up']],
  Instagram: [['real estate', 'steady'], ['coffee shop', 'up'], ['skincare routine', 'up'], ['small business', 'steady'], ['photo dump', 'up'], ['minimal outfits', 'up']],
  Facebook: [['local business', 'steady'], ['community group', 'up'], ['garage sale', 'steady'], ['family reunion', 'steady'], ['neighborhood events', 'up'], ['home renovation', 'up']],
  LinkedIn: [['career advice', 'up'], ['remote work', 'steady'], ['personal branding', 'up'], ['job search tips', 'up'], ['ai in the workplace', 'up'], ['leadership lessons', 'steady']],
  X: [['tech news', 'up'], ['personal finance', 'steady'], ['startup life', 'up'], ['current events', 'up'], ['ai debate', 'up'], ['sports takes', 'steady']],
  Threads: [['daily thoughts', 'up'], ['hot takes', 'up'], ['community updates', 'steady'], ['behind the scenes', 'up'], ['creator news', 'steady'], ['weekend recap', 'up']],
  Pinterest: [['home decor', 'up'], ['recipe ideas', 'steady'], ['wedding inspo', 'up'], ['outfit boards', 'up'], ['travel moodboard', 'steady'], ['diy projects', 'up']],
  Reddit: [['ask for advice', 'up'], ['niche communities', 'steady'], ['product reviews', 'up'], ['discussion threads', 'up'], ['AMA', 'steady'], ['megathread', 'up']],
};
/* runtime-loaded from trending-data.json — refreshed periodically; module-level
   objects hold the DEFAULT fallback until the fetch below resolves. */
let TRENDING = DEFAULT_TRENDING;
let TRENDING_DETAILED = DEFAULT_TRENDING_DETAILED;
let TRENDING_UPDATED = DEFAULT_TRENDING_UPDATED;
const trendingListeners = [];
fetch('trending-data.json').then(r => r.json()).then(data => {
  const flat = {}, detailed = {};
  Object.entries(data.terms || {}).forEach(([k, arr]) => { flat[k] = arr.map(([term]) => term); detailed[k] = arr; });
  TRENDING = flat; TRENDING_DETAILED = detailed;
  TRENDING_UPDATED = data.updated ? new Date(data.updated + 'T00:00:00').toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) : DEFAULT_TRENDING_UPDATED;
  trendingListeners.forEach(fn => fn());
}).catch(() => {});
function useTrendingData(){
  const [, force] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => { trendingListeners.push(force); return () => { const i = trendingListeners.indexOf(force); if (i > -1) trendingListeners.splice(i, 1); }; }, []);
}
const LEARN_GUIDES = [
  { t: 'How many tags should you actually use', d: 'YouTube rewards 8–15 precise tags. TikTok and Instagram cap effective reach around 3–5 hero hashtags plus a handful of niche ones — dumping 30 generic tags reads as spam to both viewers and the algorithm. LinkedIn works best with 3–5 topic tags, not hashtag walls.' },
  { t: 'Hashtags vs. keywords — different jobs', d: 'Hashtags are discovery hooks inside a platform\u2019s own search and feed. Keywords are what people type into Google or YouTube search before they ever open the app. Tagly generates both because a title needs keywords and a caption needs hashtags — using only one leaves reach on the table.' },
  { t: 'Mix broad, niche, and long-tail', d: 'A broad tag (#travel) has huge volume and huge competition. A niche tag (#soloeurotrip) has less volume but a better shot at ranking. Long-tail phrases ("how to plan a solo trip on a budget") are what search engines reward. Tagly\u2019s Hashtags / Keywords / Long-tail tabs mirror this exact hierarchy — use all three, not just the biggest numbers.' },
  { t: 'Match tags to the platform, not just the topic', d: 'The same topic needs different tags per platform: TikTok favors trend-tied tags (#fyp), Instagram favors community tags (#instagood), LinkedIn favors professional framing over hashtags at all. Switching the platform toggle before generating re-weights every tag for that platform\u2019s conventions.' },
];
const PLATFORM_CHEATSHEET = [
  { p: 'YouTube', count: '8–15 tags', style: 'Mix of broad + exact-match keywords in tags field; front-load the title with the primary keyword.' },
  { p: 'TikTok', count: '3–5 hashtags', style: 'One trend tag (#fyp), one topic tag, one niche tag. Caption keywords matter as much as hashtags.' },
  { p: 'Instagram', count: '3–8 hashtags', style: 'Community + niche tags outperform mega-tags. Reels captions should still read like sentences.' },
  { p: 'Facebook', count: '1–2 hashtags', style: 'Hashtags barely affect reach — focus on the post text and group targeting instead.' },
  { p: 'LinkedIn', count: '3–5 hashtags', style: 'Topic hashtags at the end of the post, framed around industry and role, not virality.' },
  { p: 'X', count: '1–2 hashtags', style: 'One trending hashtag max — X rewards concise, discourse-driven text over tag stacking.' },
  { p: 'Threads', count: '3–5 hashtags', style: 'Conversational tags; Threads favors reply-driven engagement over reach chasing.' },
  { p: 'Pinterest', count: '6–8 hashtags', style: 'Descriptive, keyword-rich tags — Pinterest behaves like a visual search engine.' },
  { p: 'Reddit', count: '0–2 keywords', style: 'No hashtags — pick the right subreddit and use keywords naturally in your title.' },
];
const RECENT_KEY = 'tagly_recent_v1';
const RECENT_MAX = 5;
const MAX_TOPIC_LEN = 60;

function loadRecent() { try { const l = JSON.parse(localStorage.getItem(RECENT_KEY)); return Array.isArray(l) ? l.slice(0, RECENT_MAX) : []; } catch (e) { return []; } }
function saveRecent(list) { try { localStorage.setItem(RECENT_KEY, JSON.stringify(list.slice(0, RECENT_MAX))); } catch (e) {} }

/* strip control chars / collapse whitespace / hard cap length so nothing
   odd reaches the ranking engine or gets rendered back into the page */
function sanitizeTopic(raw) {
  return (raw || '').replace(/[\u0000-\u001f\u007f<>]/g, '').replace(/\s+/g, ' ').slice(0, MAX_TOPIC_LEN);
}
function hasContent(topic) { return /[a-z0-9]/i.test(topic); }

function Toast({ message }) {
  if (!message) return null;
  return (
    <div style={{
      position: 'fixed', bottom: 24, left: '50%', transform: 'translateX(-50%)', zIndex: 60,
      background: 'var(--tg-ink)', color: '#fff', fontFamily: 'var(--font-sans)', fontSize: 13.5, fontWeight: 500,
      padding: '11px 18px', borderRadius: 'var(--radius-full)', boxShadow: 'var(--shadow-float)',
    }}>{message}</div>
  );
}

function ProModal({ onClose, onViewPricing }) {
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 70, padding: 20 }}>
      <div onClick={e => e.stopPropagation()} style={{ width: 400, maxWidth: '100%' }}>
        <Card style={{ padding: 28 }}>
          <Badge variant="pro" style={{ marginBottom: 12 }}>Pro</Badge>
          <h3 style={{ margin: '0 0 6px', fontSize: 21, color: 'var(--text-heading)' }}>Go Pro</h3>
          <p style={{ margin: '0 0 18px', fontSize: 14, color: 'var(--text-muted)', lineHeight: 1.5 }}>
            Unlock the full tag set on every generation, CSV/API export, saved history, and an ad-free workspace.
          </p>
          <div style={{ display: 'grid', gap: 10, marginBottom: 20 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14, color: 'var(--text-body)' }}><span>Monthly</span><strong style={{ color: 'var(--text-heading)' }}>$3.99/mo</strong></div>
            <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14, color: 'var(--text-body)' }}><span>Annual</span><strong style={{ color: 'var(--text-heading)' }}>$29/yr</strong></div>
          </div>
          <div style={{ display: 'flex', gap: 10 }}>
            <Button variant="secondary" size="sm" onClick={onClose}>Not now</Button>
            <Button size="sm" onClick={onViewPricing}>See pricing</Button>
          </div>
        </Card>
      </div>
    </div>
  );
}

function SoonBadge() { return <Badge variant="neutral" style={{ fontSize: 9, padding: '2px 6px', fontWeight: 700, letterSpacing: '0.4px', textTransform: 'uppercase' }}>Soon</Badge>; }

function LanguageSwitcher({ lang, onChange }) {
  const { STRINGS } = window.TaglyI18n;
  return (
    <select value={lang} onChange={e => onChange(e.target.value)} aria-label={STRINGS[lang].language_label} className="tagly-focusable" style={{ height: 32, borderRadius: 'var(--radius-full)', border: '1px solid var(--border-default)', background: 'var(--surface-canvas)', color: 'var(--text-body)', fontSize: 12.5, fontFamily: 'var(--font-sans)', padding: '0 10px', cursor: 'pointer' }}>
      {Object.keys(STRINGS).map(code => <option key={code} value={code}>{STRINGS[code].name}</option>)}
    </select>
  );
}

function AppNav({ view, onNavigate, onGoPro, onGoHome, lang, onLangChange, t }) {
  const tabs = [['generate', t.nav_generate], ['trending', t.nav_trending], ['learn', t.nav_learn], ['pricing', t.nav_pricing]];
  return (
    <header style={{ display: 'flex', alignItems: 'center', height: 72, padding: '0 40px', background: 'var(--surface-canvas)', borderBottom: '1px solid var(--border-default)', fontFamily: 'var(--font-sans)', boxSizing: 'border-box', gap: 8 }}>
      <span onClick={onGoHome} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, flex: '0 0 auto', cursor: 'pointer' }}>
        <span style={{ color: 'var(--accent)', fontSize: 28, lineHeight: 1 }} aria-hidden>#</span>
        <span style={{ fontSize: 26, fontWeight: 700, color: 'var(--text-heading)', letterSpacing: '-0.5px' }}>Tagly</span>
      </span>
      <nav style={{ display: 'flex', gap: 28, margin: '0 auto', overflowX: 'auto' }}>
        {tabs.map(([key, label]) => {
          const isActive = view === key;
          return (
            <button key={key} type="button" onClick={() => onNavigate(key)} style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', gap: 5, background: 'none', border: 'none', cursor: 'pointer', padding: '24px 2px', fontSize: 15, fontWeight: 600, color: isActive ? 'var(--text-heading)' : 'var(--text-muted)' }}>
              {label}
              {isActive && <span aria-hidden style={{ position: 'absolute', left: 0, right: 0, bottom: 16, height: 2, background: 'var(--tg-ink)', borderRadius: 2 }} />}
            </button>
          );
        })}
      </nav>
      <div style={{ flex: '0 0 auto', display: 'flex', alignItems: 'center', gap: 10 }}>
        <LanguageSwitcher lang={lang} onChange={onLangChange} />
        <Button variant="tertiary" size="sm" onClick={onGoPro} style={{ whiteSpace: 'nowrap' }}>{t.nav_gopro}</Button>
      </div>
    </header>
  );
}

function TrendingView({ onPick }) {
  useTrendingData();
  const [platform, setPlatform] = React.useState('YouTube');
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 32 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Curated weekly · updated {TRENDING_UPDATED}</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 34, color: 'var(--text-heading)', margin: '0 0 10px' }}>What creators are tagging right now</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 15.5, maxWidth: 560, margin: '0 auto' }}>Pick a topic below to skip typing entirely — it jumps straight into a full ranked tag set for that platform.</p>
      </div>
      <PlatformTabs value={platform} onChange={setPlatform} />
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 12, marginTop: 20 }}>
        {TRENDING_DETAILED[platform].map(([topic, momentum], i) => (
          <Card key={i} hoverFloat style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', cursor: 'pointer' }} onClick={() => onPick(topic, platform)}>
            <div>
              <div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 4 }}>#{i + 1} on {platform}</div>
              <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)' }}>{topic}</div>
            </div>
            <span style={{ fontSize: 13, fontWeight: 600, color: momentum === 'up' ? 'var(--tg-success)' : 'var(--text-muted)' }}>{momentum === 'up' ? '↑ rising' : '→ steady'}</span>
          </Card>
        ))}
      </div>
    </section>
  );
}

const HOW_STEPS = [
  { label: 'Pick a platform', detail: 'Choose YouTube, TikTok, Instagram, Facebook, LinkedIn, X, Threads, Pinterest, or Reddit.' },
  { label: 'Type a topic', detail: 'Just a word or two — "travel vlog", "home workout", "small business".' },
  { label: 'Get ranked tags', detail: 'Hashtags, keywords, and long-tail phrases appear instantly, grouped by purpose.' },
  { label: 'Copy & post', detail: 'Click any tag to copy it, or copy the whole set in one click.' },
];

function HowItWorksAnimated() {
  const [step, setStep] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setStep(s => (s + 1) % HOW_STEPS.length), 2400);
    return () => clearInterval(id);
  }, []);
  const demoTags = ['#travelvlog', '#wanderlust', '#solotravel', '#travelgram'];
  return (
    <Card style={{ marginBottom: 40, padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: '22px 24px 0', fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)' }}>How Tagly works</div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 0 }}>
        <div style={{ padding: '18px 24px 24px', display: 'flex', flexDirection: 'column', gap: 10 }}>
          {HOW_STEPS.map((s, i) => (
            <div key={i} onClick={() => setStep(i)} style={{ cursor: 'pointer', display: 'flex', gap: 12, alignItems: 'flex-start', padding: '10px 12px', borderRadius: 'var(--radius-md)', background: step === i ? 'var(--surface-soft)' : 'transparent', transition: 'background 200ms ease' }}>
              <span style={{ flexShrink: 0, width: 24, height: 24, borderRadius: '50%', background: step === i ? 'var(--tg-primary)' : 'var(--border-default)', color: step === i ? '#fff' : 'var(--text-muted)', fontSize: 12, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 200ms ease' }}>{i + 1}</span>
              <div>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: step === i ? 'var(--text-heading)' : 'var(--text-body)' }}>{s.label}</div>
                {step === i && <div style={{ fontSize: 12.5, color: 'var(--text-muted)', marginTop: 3, lineHeight: 1.5 }}>{s.detail}</div>}
              </div>
            </div>
          ))}
        </div>
        <div style={{ background: 'var(--surface-soft)', padding: 24, display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: 220 }}>
          <div style={{ width: '100%', maxWidth: 280 }}>
            {step === 0 && (
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, justifyContent: 'center' }}>
                {['YouTube', 'TikTok', 'Instagram', 'X'].map((p, i) => (
                  <span key={p} style={{ fontSize: 12, fontWeight: 600, padding: '6px 12px', borderRadius: 999, background: i === 0 ? 'var(--tg-primary)' : '#fff', color: i === 0 ? '#fff' : 'var(--text-body)', border: i === 0 ? 'none' : '1px solid var(--border-default)', animation: 'tagly-pop 400ms ease both', animationDelay: `${i * 80}ms` }}>{p}</span>
                ))}
              </div>
            )}
            {step === 1 && (
              <div style={{ background: '#fff', borderRadius: 999, border: '1px solid var(--border-default)', height: 40, display: 'flex', alignItems: 'center', padding: '0 16px', fontSize: 13.5, color: 'var(--text-heading)', fontFamily: 'var(--font-sans)', whiteSpace: 'nowrap', overflow: 'hidden' }}>
                travel vlog<span style={{ display: 'inline-block', width: 1.5, height: 15, background: 'var(--tg-primary)', marginLeft: 2, animation: 'tagly-blink 900ms step-end infinite' }} />
              </div>
            )}
            {step === 2 && (
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, justifyContent: 'center' }}>
                {demoTags.map((tag, i) => (
                  <span key={tag} style={{ fontFamily: 'var(--font-mono)', fontSize: 12, padding: '5px 11px', borderRadius: 999, background: '#fff', border: '1px solid var(--border-default)', color: 'var(--text-body)', animation: 'tagly-pop 350ms ease both', animationDelay: `${i * 90}ms` }}>{tag}</span>
                ))}
              </div>
            )}
            {step === 3 && (
              <div style={{ textAlign: 'center' }}>
                <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 13.5, fontWeight: 600, color: 'var(--tg-success, #1a8a4a)', background: '#fff', border: '1px solid var(--border-soft)', borderRadius: 999, padding: '8px 18px', animation: 'tagly-pop 350ms ease both' }}>
                  <span>&#10003;</span> Copied 4 tags!
                </div>
              </div>
            )}
          </div>
        </div>
      </div>
    </Card>
  );
}

function LearnView({ onGenerate }) {
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 40 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Tagging strategy, not filler</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 34, color: 'var(--text-heading)', margin: '0 0 10px' }}>Get more from every tag set</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 15.5, maxWidth: 560, margin: '0 auto' }}>Short, practical guides on why Tagly is organized the way it is — and how to use the results.</p>
      </div>
      <HowItWorksAnimated />
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 16, marginBottom: 40 }}>
        {LEARN_GUIDES.map((g, i) => (
          <Card key={i}>
            <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 8 }}>{g.t}</div>
            <div style={{ fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.55 }}>{g.d}</div>
          </Card>
        ))}
      </div>
      <Card style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '18px 20px 0', fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)' }}>Platform cheat-sheet</div>
        <div style={{ display: 'grid', gridTemplateColumns: '110px 110px 1fr', padding: '14px 20px 18px', gap: '10px 16px', fontSize: 13.5 }}>
          <div style={{ color: 'var(--text-muted)', fontWeight: 600 }}>Platform</div>
          <div style={{ color: 'var(--text-muted)', fontWeight: 600 }}>Tag count</div>
          <div style={{ color: 'var(--text-muted)', fontWeight: 600 }}>Style</div>
          {PLATFORM_CHEATSHEET.map((row, i) => (
            <React.Fragment key={i}>
              <div style={{ color: 'var(--text-heading)', fontWeight: 600 }}>{row.p}</div>
              <div style={{ color: 'var(--text-body)' }}>{row.count}</div>
              <div style={{ color: 'var(--text-body)' }}>{row.style}</div>
            </React.Fragment>
          ))}
        </div>
      </Card>
      <div style={{ textAlign: 'center', marginTop: 32 }}>
        <Button onClick={onGenerate}>Try the generator</Button>
      </div>
    </section>
  );
}

function FooterLink({ label, soon, onClick }) {
  if (soon) return <button type="button" onClick={onClick} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'none', border: 'none', padding: 0, cursor: 'not-allowed', fontSize: 'var(--type-body-sm-size)', color: 'var(--text-muted)', opacity: 0.7, textAlign: 'left' }}>{label}<SoonBadge /></button>;
  return <button type="button" onClick={onClick} style={{ display: 'inline-flex', background: 'none', border: 'none', padding: 0, cursor: 'pointer', fontSize: 'var(--type-body-sm-size)', color: 'var(--text-body)', textAlign: 'left' }}>{label}</button>;
}

function ExtensionView({ onGenerate, onDownload }) {
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 36 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Chrome extension · beta</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 34, color: 'var(--text-heading)', margin: '0 0 10px' }}>Tagly, one click from any tab.</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 15.5, maxWidth: 560, margin: '0 auto' }}>Same ranking engine as the web app, in a popup — generate and copy tags without leaving YouTube Studio, TikTok, or Instagram.</p>
      </div>
      <Card style={{ marginBottom: 24 }}>
        <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 12 }}>Install it (not yet on the Chrome Web Store)</div>
        <ol style={{ margin: 0, paddingLeft: 20, fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.9 }}>
          <li>Download the extension package below and unzip it.</li>
          <li>Open <code style={{ background: 'var(--surface-soft)', padding: '2px 6px', borderRadius: 4 }}>chrome://extensions</code> and turn on <strong style={{ color: 'var(--text-heading)' }}>Developer mode</strong> (top right).</li>
          <li>Click <strong style={{ color: 'var(--text-heading)' }}>Load unpacked</strong> and select the unzipped <code style={{ background: 'var(--surface-soft)', padding: '2px 6px', borderRadius: 4 }}>chrome-extension</code> folder.</li>
          <li>Pin Tagly from the extensions toolbar icon — it's ready.</li>
        </ol>
      </Card>
      <div style={{ textAlign: 'center', display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
        <Button onClick={onDownload}>Download extension (.zip)</Button>
        <Button variant="secondary" onClick={onGenerate}>Use the web app instead</Button>
      </div>
    </section>
  );
}

function EmailCapture({ flashToast }) {
  const [email, setEmail] = React.useState('');
  const [sent, setSent] = React.useState(false);
  async function submit(e) {
    e.preventDefault();
    if (!/^\S+@\S+\.\S+$/.test(email)) { flashToast('Enter a valid email'); return; }
    try {
      const res = await fetch('/api/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }) });
      if (!res.ok) throw new Error();
      setSent(true);
    } catch (e2) { flashToast('Could not save — needs the deployed Node server'); }
  }
  if (sent) return <div style={{ fontSize: 13.5, color: 'var(--tg-success)' }}>You're on the list — thanks!</div>;
  return (
    <form onSubmit={submit} style={{ display: 'flex', gap: 8 }}>
      <input type="email" required value={email} onChange={e => setEmail(e.target.value)} placeholder="you@email.com" aria-label="Email for launch updates" style={{ flex: 1, minWidth: 0, height: 36, borderRadius: 'var(--radius-full)', border: '1px solid var(--border-default)', padding: '0 14px', fontSize: 13, fontFamily: 'var(--font-sans)' }} />
      <Button size="sm" type="submit">Notify me</Button>
    </form>
  );
}

function ProductHuntBadge() {
  return (
    <div style={{ fontFamily: 'var(--font-sans)', border: '1px solid #e0e0e0', borderRadius: 12, padding: 20, maxWidth: 500, background: '#fff', boxShadow: '0 2px 8px rgba(0,0,0,0.05)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
        <img alt="Tagly" src="https://ph-files.imgix.net/c181e1a5-fac6-4810-811a-b3b2014d3d0d.png?auto=compress,format&codec=mozjpeg&cs=strip&fit=crop&h=80&w=80" style={{ width: 64, height: 64, borderRadius: 8, objectFit: 'cover', flexShrink: 0 }} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <h3 style={{ margin: 0, fontSize: 18, fontWeight: 600, color: '#1a1a1a', lineHeight: 1.3 }}>Tagly</h3>
          <p style={{ margin: '4px 0 0', fontSize: 14, color: '#666', lineHeight: 1.4 }}>Optimized hashtags &amp; keywords, instantly</p>
        </div>
      </div>
      <a href="https://www.producthunt.com/products/tagly-2?embed=true&utm_source=embed&utm_medium=post_embed" target="_blank" rel="noopener" style={{ display: 'inline-flex', alignItems: 'center', gap: 4, marginTop: 12, padding: '8px 16px', background: '#ff6154', color: '#fff', textDecoration: 'none', borderRadius: 8, fontSize: 14, fontWeight: 600 }}>Check it out on Product Hunt →</a>
    </div>
  );
}

function ContactView({ flashToast }) {
  const [form, setForm] = React.useState({ name: '', email: '', message: '' });
  const [sent, setSent] = React.useState(false);
  function update(k, v) { setForm(f => ({ ...f, [k]: v })); }
  async function submit(e) {
    e.preventDefault();
    if (!form.name || !/^\S+@\S+\.\S+$/.test(form.email) || !form.message) { flashToast('Fill in all fields with a valid email'); return; }
    try {
      const res = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form) });
      if (!res.ok) throw new Error();
      setSent(true);
    } catch (e2) {
      window.location.href = `mailto:contactus@tagly.click?subject=${encodeURIComponent('Message from ' + form.name)}&body=${encodeURIComponent(form.message + '\n\n' + form.email)}`;
    }
  }
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 36 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Get in touch</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 34, color: 'var(--text-heading)', margin: '0 0 10px' }}>Contact Tagly</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 15.5, maxWidth: 480, margin: '0 auto' }}>Questions, feedback, or partnership requests — reach us directly at <a href="mailto:contactus@tagly.click" style={{ color: 'var(--tg-primary)' }}>contactus@tagly.click</a>, or use the form below.</p>
      </div>
      <Card style={{ maxWidth: 480, margin: '0 auto' }}>
        {sent ? (
          <div style={{ textAlign: 'center', padding: '20px 0' }}>
            <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 6 }}>Message sent</div>
            <div style={{ fontSize: 13.5, color: 'var(--text-muted)' }}>We'll get back to you at {form.email}.</div>
          </div>
        ) : (
          <form onSubmit={submit} style={{ display: 'grid', gap: 14 }}>
            <input required value={form.name} onChange={e => update('name', e.target.value)} placeholder="Your name" aria-label="Your name" style={{ height: 42, borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', padding: '0 14px', fontSize: 14, fontFamily: 'var(--font-sans)' }} />
            <input required type="email" value={form.email} onChange={e => update('email', e.target.value)} placeholder="you@email.com" aria-label="Your email" style={{ height: 42, borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', padding: '0 14px', fontSize: 14, fontFamily: 'var(--font-sans)' }} />
            <textarea required value={form.message} onChange={e => update('message', e.target.value)} placeholder="How can we help?" aria-label="Your message" rows={5} style={{ borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', padding: '12px 14px', fontSize: 14, fontFamily: 'var(--font-sans)', resize: 'vertical' }} />
            <Button type="submit">Send message</Button>
          </form>
        )}
      </Card>
    </section>
  );
}

const LEGAL_EFFECTIVE_DATE = '25 July 2026';

function LegalDisclaimer() {
  return (
    <Card style={{ maxWidth: 760, margin: '0 auto 28px', background: 'var(--surface-soft)' }}>
      <div style={{ fontSize: 12.5, color: 'var(--text-muted)', lineHeight: 1.6 }}>
        This is a general template drafted to reflect UK law (UK GDPR, the Data Protection Act 2018, PECR, and the Consumer Rights Act 2015) and Tagly's actual data practices as of {LEGAL_EFFECTIVE_DATE}. It is not a substitute for advice from a qualified solicitor — have it reviewed before relying on it, especially as Tagly's features change.
      </div>
    </Card>
  );
}

function LegalSection({ title, children }) {
  return (
    <div style={{ marginBottom: 28 }}>
      <h2 style={{ fontSize: 17, color: 'var(--text-heading)', margin: '0 0 8px' }}>{title}</h2>
      <div style={{ fontSize: 14, color: 'var(--text-body)', lineHeight: 1.7 }}>{children}</div>
    </div>
  );
}

function PrivacyView() {
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 28 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Legal</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 32, color: 'var(--text-heading)', margin: '0 0 8px' }}>Privacy Policy</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 14 }}>Effective {LEGAL_EFFECTIVE_DATE}</p>
      </div>
      <LegalDisclaimer />
      <div style={{ maxWidth: 760, margin: '0 auto' }}>
        <LegalSection title="1. Who we are (data controller)">
          Tagly ("Tagly", "we", "us") provides a free hashtag and keyword generator at tagly.click. Business: Tagly. Address: United Kingdom. For the purposes of UK GDPR and the Data Protection Act 2018, Tagly is the data controller for the personal data described below. Contact: <a href="mailto:contactus@tagly.click">contactus@tagly.click</a>.
        </LegalSection>
        <LegalSection title="2. What we collect, and why">
          <strong style={{ color: 'var(--text-heading)' }}>Using the free generator:</strong> no account or personal data is required. Your recent searches and Pro status are stored only in your browser's local storage — we never receive or see this data.<br /><br />
          <strong style={{ color: 'var(--text-heading)' }}>Contact form &amp; "notify me" signups:</strong> if you email us or submit a form, we collect your name and email address, and any message you send, so we can reply or notify you about product updates. Legal basis: your consent.<br /><br />
          <strong style={{ color: 'var(--text-heading)' }}>Tagly Pro subscriptions:</strong> payments are handled entirely by Stripe. We never see or store your card details. Stripe shares with us only what's needed to identify your subscription status. Legal basis: performance of a contract.
        </LegalSection>
        <LegalSection title="3. Cookies and analytics">
          Tagly does not use tracking cookies. We use Plausible Analytics, a cookieless analytics tool that does not collect personal data or track you across sites. Because it doesn't use cookies or similar tracking technology, no cookie consent banner is required under UK PECR rules. Some strictly necessary local storage (not a cookie) is used on your device to remember your recent searches and Pro status — this never leaves your browser.
        </LegalSection>
        <LegalSection title="4. AI / automated processing">
          Tagly's tag rankings are generated by an automated scoring engine, not by manual review or by a third-party AI service processing your personal data. Topics you type are used only to generate results in your browser session and are not used to train any model or retained by us unless you separately submit them via the contact form.
        </LegalSection>
        <LegalSection title="5. Who we share data with">
          We share data only with the processors needed to run Tagly: Stripe (payments), Hostinger (hosting), and Plausible (privacy-friendly analytics). We do not sell personal data to anyone.
        </LegalSection>
        <LegalSection title="6. International transfers">
          Some processors (e.g. Stripe) may process data outside the UK. Where this happens, we rely on their standard contractual clauses or equivalent safeguards recognised under UK GDPR.
        </LegalSection>
        <LegalSection title="7. Data security">
          We use reasonable technical and organisational measures — including encrypted connections (HTTPS), restricted server access, and reputable processors — to protect the limited data we hold. No system is fully secure, and we cannot guarantee absolute security of data transmitted to us.
        </LegalSection>
        <LegalSection title="8. Retention">
          Contact messages and email signups are kept only as long as needed to respond or provide updates, or until you ask us to delete them. Subscription records are kept for as long as required for accounting and legal purposes.
        </LegalSection>
        <LegalSection title="9. Your rights">
          Under UK GDPR you have the right to access, correct, erase, or restrict your personal data, to receive a copy in a portable format, and to object to processing. To exercise any of these, email <a href="mailto:contactus@tagly.click">contactus@tagly.click</a>.
        </LegalSection>
        <LegalSection title="10. Complaints">
          If you believe we've mishandled your data, you can complain to us directly, or to the UK Information Commissioner's Office (ico.org.uk).
        </LegalSection>
        <LegalSection title="11. Children's privacy">
          Tagly is not directed at children under 13, and we do not knowingly collect personal data from children.
        </LegalSection>
        <LegalSection title="12. Changes to this policy">
          We may update this policy as Tagly's features change. Material changes will be reflected by updating the effective date above.
        </LegalSection>
        <LegalSection title="13. Contact">
          Business: Tagly. Address: United Kingdom. Email: <a href="mailto:contactus@tagly.click">contactus@tagly.click</a>.
        </LegalSection>
      </div>
    </section>
  );
}

function TermsView() {
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 28 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Legal</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 32, color: 'var(--text-heading)', margin: '0 0 8px' }}>Terms &amp; Conditions</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 14 }}>Effective {LEGAL_EFFECTIVE_DATE}</p>
      </div>
      <LegalDisclaimer />
      <div style={{ maxWidth: 760, margin: '0 auto' }}>
        <LegalSection title="1. Acceptance">
          By using tagly.click ("Tagly", the "Service"), you agree to these Terms &amp; Conditions and our Privacy Policy, which is incorporated by reference. If you don't agree, please don't use the Service.
        </LegalSection>
        <LegalSection title="2. Eligibility">
          You must be able to form a binding contract to use Tagly Pro (broadly, 18 or over, or the age of majority in your jurisdiction). The free generator has no age-gated account requirement but is not directed at children under 13.
        </LegalSection>
        <LegalSection title="3. The service and licence">
          Tagly generates hashtags, keywords, and related suggestions from a topic you provide. The core generator is free with no account required. We grant you a limited, non-exclusive, non-transferable licence to use the Service for its intended purpose. Tagly Pro is an optional paid subscription ($3.99/month or $29/year, billed via Stripe) that unlocks additional features.
        </LegalSection>
        <LegalSection title="4. Subscriptions and billing">
          Pro subscriptions renew automatically until cancelled. You can cancel anytime via the Stripe customer portal; cancellation takes effect at the end of the current billing period. Fees are non-refundable except where required by UK consumer law, including your rights under the Consumer Rights Act 2015.
        </LegalSection>
        <LegalSection title="5. Acceptable use">
          You agree not to misuse the Service — including scraping or overloading it, submitting unlawful or infringing content as a "topic," or attempting to reverse-engineer the ranking engine. We may suspend access for misuse.
        </LegalSection>
        <LegalSection title="6. Intellectual property">
          Tagly's software, design, and brand are our property. Hashtags, keywords, and other output generated for you are yours to use freely in your own content.
        </LegalSection>
        <LegalSection title="7. AI / automated output disclaimer">
          Tagly's tags, popularity scores, competition levels, and trend indicators are produced by an automated scoring engine and are estimates for guidance only. They are not guaranteed to reflect real-world performance on any platform, and you're responsible for reviewing generated output before use.
        </LegalSection>
        <LegalSection title="8. No warranty">
          Tagly is provided "as is" and "as available," without warranties of any kind to the extent permitted by law.
        </LegalSection>
        <LegalSection title="9. Limitation of liability">
          To the fullest extent permitted by law, Tagly is not liable for any indirect, incidental, or consequential loss arising from your use of the Service. Nothing in these Terms excludes or limits our liability for death or personal injury caused by negligence, for fraud, or for any other liability that cannot be excluded under English law.
        </LegalSection>
        <LegalSection title="10. Termination">
          We may suspend or terminate your access to the Service (including a Pro subscription, subject to applicable refund rights) if you breach these Terms. You may stop using the Service, or cancel Pro, at any time.
        </LegalSection>
        <LegalSection title="11. Force majeure">
          We are not liable for any failure or delay in performance caused by events beyond our reasonable control, including outages of third-party providers (e.g. hosting, payment, or analytics providers).
        </LegalSection>
        <LegalSection title="12. Severability">
          If any provision of these Terms is found unenforceable, the remaining provisions continue in full force and effect.
        </LegalSection>
        <LegalSection title="13. Assignment">
          You may not assign or transfer your rights under these Terms without our consent. We may assign these Terms in connection with a merger, acquisition, or sale of assets.
        </LegalSection>
        <LegalSection title="14. Entire agreement">
          These Terms, together with our Privacy Policy, constitute the entire agreement between you and Tagly regarding the Service, superseding any prior agreements.
        </LegalSection>
        <LegalSection title="15. Governing law">
          These Terms are governed by the laws of England and Wales, and the courts of England and Wales have exclusive jurisdiction over any dispute, without prejudice to any mandatory consumer protections in your country of residence.
        </LegalSection>
        <LegalSection title="16. Changes and contact">
          We may update these Terms from time to time; continued use of the Service after changes take effect constitutes acceptance. Business: Tagly. Address: United Kingdom. Email: <a href="mailto:contactus@tagly.click">contactus@tagly.click</a>.
        </LegalSection>
      </div>
    </section>
  );
}

function AppFooter({ onSoonClick, onGoPro, onPlatformShortcut, onNavigate, flashToast }) {
  const columns = [
    { title: 'Product', links: [
      { label: 'Generator', onClick: () => document.getElementById('generate-input')?.focus() },
      { label: 'Trending tags', onClick: () => onNavigate('trending') },
      { label: 'All tag guides', onClick: () => { window.location.href = 'seo/index.html'; } },
      { label: 'Favorites', onClick: () => onNavigate('favorites') },
      { label: 'Platform pages', onClick: () => { window.location.href = 'instagram-hashtag-generator.html'; } },
      { label: 'Chrome extension', onClick: () => onNavigate('extension') },
      { label: 'Pricing', onClick: () => onNavigate('pricing') },
    ] },
    { title: 'Creators', links: PLATFORMS.map(p => ({ label: `${p} tags`, onClick: () => onPlatformShortcut(p) })) },
    { title: 'Tagly', links: [
      { label: 'About', onClick: () => onNavigate('about') }, { label: 'Blog', soon: true }, { label: 'Contact', onClick: () => onNavigate('contact') }, { label: 'Status', soon: true },
    ] },
    { title: 'Stay updated', custom: <EmailCapture flashToast={flashToast} /> },
  ];
  return (
    <footer style={{ background: 'var(--surface-canvas)', borderTop: '1px solid var(--border-default)', padding: '48px 40px 32px', fontFamily: 'var(--font-sans)' }}>
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${columns.length}, 1fr)`, gap: 24, maxWidth: 'var(--container-wide)', margin: '0 auto' }} className="tagly-footer-grid">
        {columns.map((col, i) => (
          <div key={i}>
            <div style={{ fontSize: 'var(--type-title-sm-size)', fontWeight: 500, color: 'var(--text-heading)', marginBottom: 12 }}>{col.title}</div>
            {col.custom ? col.custom : (
            <ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'grid', gap: 10 }}>
              {col.links.map((l, j) => (
                <li key={j}><FooterLink label={l.label} soon={l.soon} onClick={l.soon ? () => onSoonClick(`${l.label} — coming soon`) : l.onClick} /></li>
              ))}
            </ul>
            )}
          </div>
        ))}
      </div>
      <div style={{ display: 'flex', justifyContent: 'center', maxWidth: 'var(--container-wide)', margin: '32px auto 0' }}>
        <ProductHuntBadge />
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, maxWidth: 'var(--container-wide)', margin: '32px auto 0', paddingTop: 24, borderTop: '1px solid var(--border-soft)', fontSize: 'var(--type-caption-sm-size)', color: 'var(--text-muted)' }}>
        <span>© 2026 Tagly</span>
        <span style={{ marginLeft: 'auto', display: 'flex', gap: 16 }}>
          <FooterLink label="Privacy" onClick={() => onNavigate('privacy')} />
          <FooterLink label="Terms" onClick={() => onNavigate('terms')} />
        </span>
      </div>
    </footer>
  );
}

function PlatformTabs({ value, onChange }) {
  return (
    <div style={{ display: 'flex', gap: 8, marginBottom: 16, flexWrap: 'wrap' }}>
      {PLATFORMS.map(p => (
        <button key={p} onClick={() => onChange(p)} style={{
          fontFamily: 'var(--font-sans)', fontSize: 14, fontWeight: 600, cursor: 'pointer',
          padding: '9px 18px', borderRadius: 'var(--radius-full)',
          border: value === p ? '1px solid transparent' : '1px solid var(--border-default)',
          background: value === p ? 'var(--accent)' : 'var(--surface-canvas)',
          color: value === p ? 'var(--text-on-accent)' : 'var(--text-body)',
          transition: 'background var(--duration-fast) var(--ease-standard)',
        }}>{p}</button>
      ))}
    </div>
  );
}

function ChipRow({ label, items, onPick }) {
  if (!items.length) return null;
  return (
    <div style={label ? { marginTop: 14 } : undefined}>
      {label && <div style={{ fontSize: 12.5, color: 'var(--text-muted)', marginBottom: 8 }}>{label}</div>}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, justifyContent: 'center' }}>
        {items.map((t, i) => (
          <button key={i} onClick={() => onPick(t)} style={{
            fontFamily: 'var(--font-sans)', fontSize: 13, cursor: 'pointer', color: 'var(--text-body)',
            background: 'var(--surface-soft)', border: '1px solid var(--border-soft)', borderRadius: 'var(--radius-full)', padding: '6px 14px',
          }}>{t}</button>
        ))}
      </div>
    </div>
  );
}

const FEATURES = [
  { t: 'Instant results', d: 'Ranking runs in your browser against a cached corpus — no server round-trip, no spinner.' },
  { t: 'No account, ever', d: 'Generate and copy unlimited tag sets anonymously. History stays on your device.' },
  { t: 'Platform-tuned', d: 'Hashtags and keywords are weighted for YouTube, TikTok, Instagram, Facebook, or LinkedIn conventions.' },
  { t: 'Free forever', d: 'The core loop never sits behind a paywall. Pro adds depth, not the basics.' },
];

const FAVORITES_KEY = 'tagly_favorites_v1';
function loadFavorites() { try { return JSON.parse(localStorage.getItem(FAVORITES_KEY)) || []; } catch (e) { return []; } }
function saveFavorites(list) { try { localStorage.setItem(FAVORITES_KEY, JSON.stringify(list)); } catch (e) {} }

function FavoritesView({ onPick, onGenerate }) {
  const [favs, setFavs] = React.useState(loadFavorites);
  function remove(f) {
    const next = favs.filter(x => !(x.topic === f.topic && x.platform === f.platform));
    setFavs(next); saveFavorites(next);
  }
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 32 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Saved privately in your browser</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 34, color: 'var(--text-heading)', margin: '0 0 10px' }}>Your favorite tag sets</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 15.5, maxWidth: 520, margin: '0 auto' }}>No account needed — favorites live only in this browser. Click one to regenerate it instantly.</p>
      </div>
      {favs.length === 0 ? (
        <div style={{ textAlign: 'center' }}>
          <p style={{ color: 'var(--text-muted)', fontSize: 14, marginBottom: 20 }}>Nothing saved yet. Generate a tag set and click ☆ Save to keep it here.</p>
          <Button onClick={onGenerate}>Go to generator</Button>
        </div>
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 12, maxWidth: 900, margin: '0 auto' }}>
          {favs.map((f, i) => (
            <Card key={i} hoverFloat style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, cursor: 'pointer' }} onClick={() => onPick(f)}>
              <div>
                <div style={{ fontSize: 14.5, fontWeight: 600, color: 'var(--text-heading)' }}>{f.topic}</div>
                <div style={{ fontSize: 12.5, color: 'var(--text-muted)' }}>{f.platform}</div>
              </div>
              <button onClick={e => { e.stopPropagation(); remove(f); }} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)', fontSize: 16 }} aria-label="Remove favorite">×</button>
            </Card>
          ))}
        </div>
      )}
    </section>
  );
}

function BatchGenerate({ platform, onResults, isOpen, onToggle }) {
  const [text, setText] = React.useState('');
  const [running, setRunning] = React.useState(false);
  function run() {
    const topics = text.split('\n').map(s => s.trim()).filter(Boolean).slice(0, 8);
    if (!topics.length) return;
    setRunning(true);
    setTimeout(() => {
      const all = topics.map(t => window.TaglyEngine.generate(t, platform)).filter(Boolean);
      onResults(all);
      setRunning(false);
    }, 300);
  }
  if (!isOpen) return (
    <button onClick={onToggle} style={{ fontSize: 12.5, color: 'var(--tg-primary)', background: 'none', border: 'none', cursor: 'pointer', marginTop: 4 }}>Batch generate multiple topics →</button>
  );
  return (
    <Card style={{ maxWidth: 640, width: '100%', marginTop: 12, textAlign: 'left' }}>
      <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 6 }}>Batch generate (one topic per line, up to 8)</div>
      <textarea value={text} onChange={e => setText(e.target.value)} rows={4} placeholder={'travel vlog\nhome workout\nsmall business'} style={{ width: '100%', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', padding: '10px 12px', fontSize: 13.5, fontFamily: 'var(--font-sans)', resize: 'vertical', boxSizing: 'border-box' }} />
      <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
        <Button size="sm" onClick={run} disabled={running}>{running ? 'Generating…' : `Generate all for ${platform}`}</Button>
        <Button size="sm" variant="secondary" onClick={onToggle}>Close</Button>
      </div>
    </Card>
  );
}

const FAQS = [
  { q: 'Is Tagly really free?', a: 'Yes. Generating and copying tags has no limit and never requires an account. Tagly Pro ($3.99/mo or $29/yr) adds the full tag set per generation, saved history, and CSV/API export.' },
  { q: 'Do I need to log in?', a: 'No. Recent searches are stored locally in your browser. Create an account only if you want Pro features synced across devices.' },
  { q: 'How are tags ranked?', a: 'Each tag gets a popularity score, a competition estimate, and a trend signal, computed against a cached keyword corpus refreshed nightly.' },
  { q: 'Which platforms are supported?', a: 'YouTube, TikTok, Instagram, Facebook, LinkedIn, X, Threads, Pinterest, and Reddit today, each with its own tag conventions and modifiers.' },
];

function Faq() {
  const [open, setOpen] = React.useState(0);
  return (
    <div style={{ maxWidth: 760, margin: '0 auto', display: 'grid', gap: 10 }}>
      {FAQS.map((f, i) => (
        <Card key={i} padding={0} style={{ overflow: 'hidden' }}>
          <button onClick={() => setOpen(open === i ? -1 : i)} style={{
            width: '100%', textAlign: 'left', background: 'none', border: 'none', cursor: 'pointer',
            padding: '18px 20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            fontFamily: 'var(--font-sans)', fontSize: 15, fontWeight: 600, color: 'var(--text-heading)',
          }}>
            {f.q}
            <span style={{ color: 'var(--tg-primary)', fontSize: 20, lineHeight: 1, transform: open === i ? 'rotate(45deg)' : 'none', transition: 'transform 150ms' }}>+</span>
          </button>
          {open === i && <p style={{ padding: '0 20px 18px', margin: 0, color: 'var(--text-body)', fontSize: 14, lineHeight: 1.55 }}>{f.a}</p>}
        </Card>
      ))}
    </div>
  );
}

function AboutView({ onGenerate }) {
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 40 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>About Tagly</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 34, color: 'var(--text-heading)', margin: '0 0 10px' }}>Optimized tags, without the wait.</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 15.5, maxWidth: 600, margin: '0 auto' }}>Tagly is a free tool that turns a topic into a ranked, platform-tuned set of hashtags and keywords in seconds — built for creators who'd rather be filming than digging through tag lists.</p>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 16, marginBottom: 40 }}>
        <Card>
          <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 8 }}>What it does</div>
          <div style={{ fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.55 }}>Type a topic, pick a platform, and get ranked hashtags, keywords, long-tail phrases, and related searches — each scored and copy-ready.</div>
        </Card>
        <Card>
          <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 8 }}>Why it's fast</div>
          <div style={{ fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.55 }}>Ranking runs in your browser against a cached corpus, not a server round-trip — so results feel instant instead of loading.</div>
        </Card>
        <Card>
          <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 8 }}>Who it's for</div>
          <div style={{ fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.55 }}>YouTube, TikTok, Instagram, Facebook, and LinkedIn creators who need tags that match each platform's own conventions.</div>
        </Card>
      </div>
      <Card style={{ marginBottom: 32 }}>
        <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 10 }}>What's free vs. Pro</div>
        <div style={{ fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.6 }}>
          Generating and copying tags is free forever, no account required. <strong style={{ color: 'var(--text-heading)' }}>Tagly Pro</strong> ($3.99/mo or $29/yr) unlocks the full ranked tag set per generation, CSV/API export, saved history, and removes ads.
        </div>
      </Card>
      <div style={{ textAlign: 'center' }}>
        <Button onClick={onGenerate}>Try the generator</Button>
      </div>
    </section>
  );
}

function RestoreAccessModal({ onClose, onRestored }) {
  const [email, setEmail] = React.useState('');
  const [status, setStatus] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  async function submit(e) {
    e.preventDefault();
    if (!/^\S+@\S+\.\S+$/.test(email)) { setStatus({ ok: false, message: 'Enter a valid email address' }); return; }
    setLoading(true);
    setStatus(null);
    try {
      const res = await fetch('/api/restore-access', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }) });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || 'Something went wrong');
      setStatus({ ok: !!data.active, message: data.message });
      if (data.active) {
        try { localStorage.setItem('tagly_pro_v1', JSON.stringify({ isPro: true, plan: data.plan })); } catch (err) {}
        onRestored(data.plan);
      }
    } catch (err) {
      setStatus({ ok: false, message: err.message });
    }
    setLoading(false);
  }
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 80, padding: 20 }}>
      <div onClick={e => e.stopPropagation()} style={{ width: 400, maxWidth: '100%' }}>
        <Card style={{ padding: 28 }}>
          <h3 style={{ margin: '0 0 6px', fontSize: 20, color: 'var(--text-heading)' }}>Restore Pro access</h3>
          <p style={{ margin: '0 0 18px', fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.5 }}>
            Already subscribed on another device or browser? Enter the email you used at checkout — no password needed.
          </p>
          <form onSubmit={submit} style={{ display: 'grid', gap: 10 }}>
            <input type="email" required value={email} onChange={e => setEmail(e.target.value)} placeholder="you@email.com" aria-label="Email used at checkout" style={{ height: 42, borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', padding: '0 14px', fontSize: 14, fontFamily: 'var(--font-sans)' }} />
            <Button type="submit" disabled={loading}>{loading ? 'Checking…' : 'Restore access'}</Button>
          </form>
          {status && <p style={{ marginTop: 12, fontSize: 13, color: status.ok ? 'var(--tg-success, #1a8a4a)' : 'var(--tg-error, #c13515)' }}>{status.message}</p>}
          <button onClick={onClose} style={{ marginTop: 14, background: 'none', border: 'none', cursor: 'pointer', fontSize: 12.5, color: 'var(--text-muted)', padding: 0 }}>Close</button>
        </Card>
      </div>
    </div>
  );
}

function PricingView({ isPro, flashToast, onRestored }) {
  const [loading, setLoading] = React.useState(null);
  const [showRestore, setShowRestore] = React.useState(false);
  async function startCheckout(plan) {
    setLoading(plan);
    try {
      const res = await fetch('/api/create-checkout-session', {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ plan }),
      });
      const data = await res.json();
      if (!res.ok || !data.url) throw new Error(data.error || 'Checkout unavailable');
      window.location.href = data.url;
    } catch (e) {
      flashToast('Checkout is not available right now — please try again shortly');
      setLoading(null);
    }
  }
  return (
    <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '56px 24px 80px' }}>
      <div style={{ textAlign: 'center', marginBottom: 36 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>Simple pricing</div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 34, color: 'var(--text-heading)', margin: '0 0 10px' }}>Free forever. Pro when you want more.</h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 15.5, maxWidth: 560, margin: '0 auto' }}>The core generate-and-copy loop never sits behind a paywall. Pro adds depth, not the basics.</p>
      </div>
      {isPro && <Card style={{ maxWidth: 480, margin: '0 auto 24px', textAlign: 'center' }}><Badge variant="pro" style={{ marginBottom: 8 }}>Pro</Badge><div style={{ fontSize: 14, color: 'var(--text-heading)' }}>You're on Tagly Pro — thanks for supporting the tool.</div></Card>}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 16, maxWidth: 920, margin: '0 auto' }}>
        <Card>
          <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 6 }}>Free</div>
          <div style={{ fontSize: 28, fontWeight: 700, color: 'var(--text-heading)', marginBottom: 12 }}>$0</div>
          <ul style={{ margin: '0 0 20px', paddingLeft: 18, fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.8 }}>
            <li>Unlimited generate &amp; copy</li><li>All 9 platforms</li><li>Top tags per category</li><li>Session history</li>
          </ul>
          <Button variant="secondary" size="sm" disabled style={{ width: '100%' }}>Current plan</Button>
        </Card>
        <Card style={{ border: '2px solid var(--tg-primary)', position: 'relative' }}>
          <Badge variant="pro" style={{ position: 'absolute', top: 16, right: 16 }}>Popular</Badge>
          <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 6 }}>Pro monthly</div>
          <div style={{ fontSize: 28, fontWeight: 700, color: 'var(--text-heading)', marginBottom: 12 }}>$3.99<span style={{ fontSize: 14, fontWeight: 400, color: 'var(--text-muted)' }}>/mo</span></div>
          <ul style={{ margin: '0 0 20px', paddingLeft: 18, fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.8 }}>
            <li>Full ranked tag set</li><li>CSV / JSON / API export</li><li>Synced saved history</li><li>Ad-free</li>
          </ul>
          <Button size="sm" style={{ width: '100%' }} onClick={() => startCheckout('monthly')} disabled={loading}>{loading === 'monthly' ? 'Redirecting…' : 'Subscribe monthly'}</Button>
        </Card>
        <Card>
          <div style={{ fontSize: 15.5, fontWeight: 600, color: 'var(--text-heading)', marginBottom: 6 }}>Pro annual</div>
          <div style={{ fontSize: 28, fontWeight: 700, color: 'var(--text-heading)', marginBottom: 12 }}>$29<span style={{ fontSize: 14, fontWeight: 400, color: 'var(--text-muted)' }}>/yr</span></div>
          <ul style={{ margin: '0 0 20px', paddingLeft: 18, fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.8 }}>
            <li>Everything in monthly</li><li>Save $18.88/yr (39% off monthly)</li><li>One annual charge</li>
          </ul>
          <Button variant="secondary" size="sm" style={{ width: '100%' }} onClick={() => startCheckout('annual')} disabled={loading}>{loading === 'annual' ? 'Redirecting…' : 'Subscribe annually'}</Button>
        </Card>
      </div>
      <p style={{ textAlign: 'center', fontSize: 12, color: 'var(--text-muted)', marginTop: 24 }}>
        Secure checkout via Stripe. Cancel anytime from the Stripe customer portal. Already subscribed on another device? <button onClick={() => setShowRestore(true)} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', color: 'var(--tg-primary)', fontSize: 12 }}>Restore access</button>.
      </p>
      {showRestore && <RestoreAccessModal onClose={() => setShowRestore(false)} onRestored={plan => { flashToast(`Pro (${plan}) restored on this device`); onRestored(); setShowRestore(false); }} />}
    </section>
  );
}

function App() {
  useTrendingData();
  const [view, setView] = React.useState(() => (location.hash.replace('#', '') || 'generate'));
  const [lang, setLang] = React.useState(() => window.TaglyI18n.detectDefault());
  const t = window.TaglyI18n.STRINGS[lang];
  React.useEffect(() => { window.TaglyI18n.setLang(lang); }, [lang]);
  const [platform, setPlatform] = React.useState('YouTube');
  const [keyword, setKeyword] = React.useState('');
  const [results, setResults] = React.useState(null);
  const [recent, setRecent] = React.useState(loadRecent);
  const [generating, setGenerating] = React.useState(false);
  const [inputError, setInputError] = React.useState(null);
  const [shake, setShake] = React.useState(false);
  const [toast, setToast] = React.useState(null);
  const [showPro, setShowPro] = React.useState(false);
  const [isPro, setIsPro] = React.useState(() => { try { return JSON.parse(localStorage.getItem('tagly_pro_v1'))?.isPro === true; } catch (e) { return false; } });
  const [batchOpen, setBatchOpen] = React.useState(false);
  const [batchResults, setBatchResults] = React.useState(null);
  const [context, setContext] = React.useState('');
  const [showContext, setShowContext] = React.useState(false);
  const debounceRef = React.useRef(null);
  const toastRef = React.useRef(null);
  const inputRef = React.useRef(null);

  function flashToast(msg) {
    setToast(msg);
    clearTimeout(toastRef.current);
    toastRef.current = setTimeout(() => setToast(null), 2200);
  }
  function flashError(msg) {
    setInputError(msg);
    setShake(true);
    setTimeout(() => setShake(false), 450);
    setTimeout(() => setInputError(e => (e === msg ? null : e)), 2400);
  }

  function runGenerate(topic, plat) {
    const clean = sanitizeTopic(topic ?? keyword).trim();
    const ctx = sanitizeTopic(context).trim();
    const combined = ctx ? `${clean} ${ctx}` : clean;
    const p = plat ?? platform;
    if (!clean || !hasContent(clean)) {
      flashError('Enter a topic to get started');
      return;
    }
    setGenerating(true);
    setInputError(null);
    setBatchResults(null);
    setTimeout(() => {
      const r = window.TaglyEngine.generate(combined, p);
      if (r) r.topic = clean;
      setResults(r);
      setGenerating(false);
      const next = [{ topic: clean, platform: p }, ...recent.filter(x => !(x.topic === clean && x.platform === p))].slice(0, RECENT_MAX);
      setRecent(next); saveRecent(next);
    }, 260);
  }

  function onKeywordChange(v) {
    const clean = sanitizeTopic(v);
    setKeyword(clean);
    if (inputError) setInputError(null);
    clearTimeout(debounceRef.current);
    if (clean.length > 1 && hasContent(clean)) debounceRef.current = setTimeout(() => runGenerate(clean, platform), 500);
  }

  function pickTrending(t) { setKeyword(t); runGenerate(t, platform); }
  function pickRecent(item) { setKeyword(item.topic); setPlatform(item.platform); runGenerate(item.topic, item.platform); }
  function clearRecent() { setRecent([]); saveRecent([]); }
  function platformShortcut(p) {
    setPlatform(p);
    document.getElementById('generate-input')?.scrollIntoView({ block: 'center' });
    setTimeout(() => inputRef.current?.focus(), 300);
  }
  function navigate(v) { setView(v); location.hash = v; window.scrollTo(0, 0); }
  function goHome() {
    setView('generate'); location.hash = 'generate';
    setKeyword(''); setResults(null); setInputError(null);
    window.scrollTo(0, 0);
  }
  React.useEffect(() => {
    const onHash = () => setView(location.hash.replace('#', '') || 'generate');
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);
  function fromTrending(topic, plat) {
    navigate('generate');
    setPlatform(plat);
    setKeyword(topic);
    setTimeout(() => runGenerate(topic, plat), 50);
  }
  function fromFavorite(f) {
    navigate('generate');
    setPlatform(f.platform);
    setKeyword(f.topic);
    setTimeout(() => runGenerate(f.topic, f.platform), 50);
  }
  function onExpandTopic(expanded) {
    setKeyword(expanded);
    runGenerate(expanded, platform);
  }
  React.useEffect(() => {
    const params = new URLSearchParams(location.search);
    const sharedTopic = params.get('topic');
    const sharedPlatform = params.get('platform');
    if (sharedTopic && sharedPlatform) {
      setPlatform(sharedPlatform);
      setKeyword(sharedTopic);
      setTimeout(() => runGenerate(sharedTopic, sharedPlatform), 50);
      flashToast('Viewing a shared result — generate your own anytime');
    }
    if (params.get('checkout') === 'success') {
      const sessionId = params.get('session_id');
      if (sessionId) {
        fetch(`/api/verify-session?session_id=${encodeURIComponent(sessionId)}`).then(r => r.json()).then(data => {
          if (data.paid) {
            setIsPro(true);
            try { localStorage.setItem('tagly_pro_v1', JSON.stringify({ isPro: true, plan: data.plan })); } catch (e) {}
            flashToast('Payment confirmed — Pro unlocked!');
          } else {
            flashToast('Could not confirm payment — contact support if you were charged');
          }
        }).catch(() => flashToast('Could not verify payment — contact support if you were charged'));
      }
      history.replaceState(null, '', location.pathname + location.hash);
    } else if (params.get('checkout') === 'cancel') {
      flashToast('Checkout canceled');
      history.replaceState(null, '', location.pathname + location.hash);
    }
  }, []);

  const canGenerate = hasContent(sanitizeTopic(keyword).trim());

  return (
    <React.Fragment>
      <AppNav view={view} onNavigate={navigate} onGoPro={() => setShowPro(true)} onGoHome={goHome} lang={lang} onLangChange={setLang} t={t} />

      {view === 'trending' && <TrendingView onPick={fromTrending} />}
      {view === 'learn' && <LearnView onGenerate={() => navigate('generate')} />}
      {view === 'about' && <AboutView onGenerate={() => navigate('generate')} />}
      {view === 'contact' && <ContactView flashToast={flashToast} />}
      {view === 'privacy' && <PrivacyView />}
      {view === 'terms' && <TermsView />}
      {view === 'extension' && <ExtensionView onGenerate={() => navigate('generate')} onDownload={() => flashToast('Extension .zip is in the chat panel — check your latest message')} />}
      {view === 'pricing' && <PricingView isPro={isPro} flashToast={flashToast} onRestored={() => setIsPro(true)} />}
      {view === 'favorites' && <FavoritesView onPick={fromFavorite} onGenerate={() => navigate('generate')} />}

      {view === 'generate' && <React.Fragment>
      <section style={{ padding: '48px 20px 32px', textAlign: 'center' }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--tg-primary)', marginBottom: 12 }}>
          {t.hero_eyebrow}
        </div>
        <h1 style={{ fontFamily: 'var(--font-sans)', fontSize: 'clamp(28px, 7vw, 40px)', lineHeight: 1.12, letterSpacing: '-0.02em', color: 'var(--text-heading)', margin: '0 0 12px', textWrap: 'balance' }}>
          {t.hero_title1}<br />{t.hero_title2}
        </h1>
        <p style={{ color: 'var(--text-muted)', fontSize: 16, maxWidth: 560, margin: '0 auto 32px' }}>
          {t.hero_sub}
        </p>

        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
          <PlatformTabs value={platform} onChange={p => { setPlatform(p); if (canGenerate) runGenerate(keyword, p); }} />
          <div className={shake ? 'tagly-shake' : ''} style={{
            display: 'flex', alignItems: 'center', gap: 4, background: 'var(--surface-canvas)',
            border: inputError ? '1px solid var(--tg-error, #c13515)' : '1px solid var(--border-default)', borderRadius: 'var(--radius-full)', boxShadow: 'var(--shadow-float)',
            height: 60, padding: '0 6px 0 20px', maxWidth: 640, width: '100%', boxSizing: 'border-box',
          }}>
            <input
              id="generate-input"
              ref={inputRef}
              aria-label="Topic to generate tags for"
              value={keyword}
              maxLength={MAX_TOPIC_LEN}
              onChange={e => onKeywordChange(e.target.value)}
              onKeyDown={e => { if (e.key === 'Enter') runGenerate(); }}
              placeholder={t.input_placeholder}
              style={{ flex: 1, minWidth: 0, height: 40, border: 'none', outline: 'none', background: 'transparent', fontFamily: 'var(--font-sans)', fontSize: 16, color: 'var(--text-heading)' }}
            />
            <GenerateOrb onClick={() => runGenerate()} label={generating ? t.btn_generating : t.btn_generate} extended size={44} disabled={!canGenerate || generating} aria-label={t.btn_generate} />
          </div>
          <div style={{ minHeight: 20, marginTop: 8 }}>
            {inputError && <span style={{ fontSize: 13, color: 'var(--tg-error, #c13515)' }}>{inputError}</span>}
          </div>
          <div style={{ marginTop: 4, marginBottom: 4 }}>
            {!showContext ? (
              <button onClick={() => setShowContext(true)} style={{ fontSize: 12.5, color: 'var(--text-muted)', background: 'none', border: 'none', cursor: 'pointer' }}>+ Add video/post description for sharper keywords</button>
            ) : (
              <div style={{ maxWidth: 480, margin: '0 auto', textAlign: 'left' }}>
                <textarea value={context} onChange={e => setContext(e.target.value)} maxLength={280} rows={2} placeholder="Optional: describe your video or post — e.g. 'a 10-minute vlog about backpacking through Vietnam on a tight budget'" style={{ width: '100%', boxSizing: 'border-box', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', padding: '8px 12px', fontSize: 13, fontFamily: 'var(--font-sans)', resize: 'vertical' }} />
                <div style={{ fontSize: 11.5, color: 'var(--text-muted)', marginTop: 4 }}>Used to widen keyword and niche matching — not shown publicly.</div>
              </div>
            )}
          </div>

          <div style={{ marginTop: 28, paddingTop: 24, borderTop: '1px solid var(--border-soft)', width: '100%', maxWidth: 560 }}>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'center', gap: 8, marginBottom: 12 }}>
              <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--tg-primary)' }}>{t.trending_label}</span>
              <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>curated · updated {TRENDING_UPDATED}</span>
            </div>
            <ChipRow items={TRENDING[platform]} onPick={pickTrending} />
          </div>

          {recent.length > 0 && (
            <div style={{ marginTop: 24, width: '100%', maxWidth: 560 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, justifyContent: 'center', marginBottom: 12 }}>
                <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--tg-primary)' }}>{t.recent_label}</span>
                <button onClick={clearRecent} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 11.5, color: 'var(--tg-primary)', padding: 0 }}>{t.clear_history}</button>
              </div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, justifyContent: 'center' }}>
                {recent.map((r, i) => (
                  <button key={i} onClick={() => pickRecent(r)} style={{
                    fontFamily: 'var(--font-sans)', fontSize: 13, cursor: 'pointer', color: 'var(--text-body)',
                    background: 'var(--surface-soft)', border: '1px solid var(--border-soft)', borderRadius: 'var(--radius-full)', padding: '6px 14px',
                  }}>{r.topic} · {r.platform}</button>
                ))}
              </div>
            </div>
          )}

          <div style={{ marginTop: 20 }}>
            <BatchGenerate platform={platform} isOpen={batchOpen} onToggle={() => setBatchOpen(o => !o)} onResults={list => { setBatchResults(list); setResults(null); }} />
          </div>
        </div>
      </section>

      {results && <ResultsPanel results={results} isPro={isPro} onGoPro={() => setShowPro(true)} onExpandTopic={onExpandTopic} />}
      {batchResults && (
        <div style={{ maxWidth: 'var(--container-tool)', margin: '0 auto', padding: '0 24px' }}>
          <div style={{ fontSize: 13, color: 'var(--text-muted)', margin: '0 0 12px', textAlign: 'center' }}>Batch results for {batchResults.length} topics</div>
          {batchResults.map((r, i) => (
            <div key={i} style={{ marginBottom: 32, borderTop: i > 0 ? '1px solid var(--border-default)' : 'none', paddingTop: i > 0 ? 24 : 0 }}>
              <ResultsPanel results={r} isPro={isPro} onGoPro={() => setShowPro(true)} onExpandTopic={onExpandTopic} />
            </div>
          ))}
        </div>
      )}

      <section style={{ maxWidth: 'var(--container-wide)', margin: '0 auto', padding: '24px 20px 64px' }}>
        <h2 style={{ textAlign: 'center', fontSize: 22, color: 'var(--text-heading)', marginBottom: 28 }}>{t.features_title}</h2>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 16 }}>
          {FEATURES.map((f, i) => (
            <Card key={i} hoverFloat>
              <div style={{ fontWeight: 600, fontSize: 15, color: 'var(--text-heading)', marginBottom: 6 }}>{f.t}</div>
              <div style={{ fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.5 }}>{f.d}</div>
            </Card>
          ))}
        </div>
      </section>

      <section style={{ padding: '24px 24px 80px' }}>
        <h2 style={{ textAlign: 'center', fontSize: 22, color: 'var(--text-heading)', marginBottom: 24 }}>{t.faq_title}</h2>
        <Faq />
      </section>
      </React.Fragment>}

      <AppFooter onSoonClick={flashToast} onGoPro={() => setShowPro(true)} onPlatformShortcut={platformShortcut} onNavigate={navigate} flashToast={flashToast} />
      <Toast message={toast} />
      {showPro && <ProModal onClose={() => setShowPro(false)} onViewPricing={() => { setShowPro(false); navigate('pricing'); }} />}
    </React.Fragment>
  );
}

window.TaglyApp = App;
