Files
doc_ai_frontend/docs/superpowers/plans/2026-03-26-landing-refactor.md
yoge 409bbf742e feat: optimize docs pages and add 4 new doc articles (en + zh)
- Rewrote DocsListPage and DocDetailPage with landing.css aesthetic
  (icon cards, skeleton loader, prose styles, CTA box)
- Added docs-specific CSS to landing.css
- Created image-to-latex, copy-to-word, ocr-accuracy, pdf-extraction
  articles in both English and Chinese
- Updated DocsSeoSection guide cards to link to real doc slugs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 16:15:22 +08:00

54 KiB
Raw Blame History

Landing Page Refactor Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace all marketing home components with the UI from texpixel-landing.html, preserving exact visual fidelity while integrating with React Router, AuthContext, and existing app infrastructure.

Architecture: Extract the reference HTML's <style> block into a scoped landing.css (imported only in MarketingLayout), convert each HTML section into a focused React component, and convert the reference's <script> behaviors into useEffect hooks and a shared useScrollReveal hook.

Tech Stack: React 18, TypeScript, React Router v6, Tailwind CSS (workspace only), custom CSS (marketing only), useAuth / useLanguage contexts.

Spec: docs/superpowers/specs/2026-03-26-landing-refactor-design.md Reference: texpixel-landing.html (lines 10-1593 = CSS, lines 1602-2183 = HTML, lines 2185-2289 = JS)


File Map

Create

  • src/styles/landing.css — all marketing CSS, scoped under .marketing-page
  • src/hooks/useScrollReveal.ts — IntersectionObserver for .reveal elements
  • src/components/home/ProductSuiteSection.tsx.product-suite section
  • src/components/home/ShowcaseSection.tsx.showcase section
  • src/components/home/TestimonialsSection.tsx.user-love carousel section
  • src/components/home/DocsSeoSection.tsx.docs-seo section

Modify

  • index.html — add Lora + JetBrains Mono fonts
  • src/components/layout/MarketingLayout.tsx — import CSS, add .marketing-page wrapper + glow blobs
  • src/components/layout/MarketingNavbar.tsx — replace with reference nav design
  • src/components/layout/Footer.tsx — replace with reference footer design
  • src/components/home/HeroSection.tsx — replace with reference hero + typing effect
  • src/components/home/FeaturesSection.tsx — replace with reference core features
  • src/components/home/PricingSection.tsx — replace with reference pricing
  • src/pages/HomePage.tsx — update section order, add new sections, add scroll reveal
  • src/lib/translations.ts — remove dead contact key from marketing.nav

Delete

  • src/components/home/HowItWorksSection.tsx
  • src/components/home/ContactSection.tsx

Task 1: Add Fonts to index.html

Files:

  • Modify: index.html:15

  • Step 1: Add Lora and JetBrains Mono to the Google Fonts link

Replace the existing fonts line (line 15) in index.html:

<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@500;600;700;800&family=DM+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet" />

With:

<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@500;600;700;800&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,300&family=Lora:ital,wght@0,400;0,600;0,700;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
  • Step 2: Verify build still passes
npm run typecheck

Expected: no errors

  • Step 3: Commit
git add index.html
git commit -m "feat: add Lora and JetBrains Mono fonts for landing page"

Task 2: Extract Landing CSS

Files:

  • Create: src/styles/landing.css

  • Step 1: Create the styles directory and file

mkdir -p src/styles
  • Step 2: Copy the CSS from the reference and apply scoping

Copy lines 101593 from texpixel-landing.html (everything inside the <style> tag) into src/styles/landing.css.

Then apply these transformations to prevent workspace bleed:

Original selector Replace with
body { .marketing-page {
body::before { .marketing-page::before {
html { scroll-behavior: smooth; } Remove this line (already in index.css via Tailwind base)
section { position: relative; z-index: 1; } .marketing-page section { position: relative; z-index: 1; }
nav { .marketing-page nav {
footer { (the footer padding rule) .marketing-page footer {

Leave :root { ... } untouched — the variable names (--primary, --bg, etc.) don't conflict with the workspace's variables (--color-primary, --color-bg).

All class-based selectors (.hero, .btn, .product-card, etc.) need no change — they are unique enough to not affect the workspace.

At the very end of the file, add the scroll reveal animation rule used by JS:

.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.55s ease, transform 0.55s ease;
}
.reveal.visible {
  opacity: 1;
  transform: translateY(0);
}
.reveal-delay-1 { transition-delay: 0.10s; }
.reveal-delay-2 { transition-delay: 0.20s; }
.reveal-delay-3 { transition-delay: 0.30s; }
  • Step 3: Verify the file exists
ls src/styles/landing.css

Expected: file listed

  • Step 4: Commit
git add src/styles/landing.css
git commit -m "feat: extract landing page CSS from reference HTML"

Task 3: Update MarketingLayout

Files:

  • Modify: src/components/layout/MarketingLayout.tsx

  • Step 1: Replace MarketingLayout.tsx

import { Outlet } from 'react-router-dom';
import MarketingNavbar from './MarketingNavbar';
import Footer from './Footer';
import '../../styles/landing.css';

export default function MarketingLayout() {
  return (
    <div className="marketing-page">
      <div className="glow-blob glow-blob-1" />
      <div className="glow-blob glow-blob-2" />
      <div className="glow-blob glow-blob-3" />
      <MarketingNavbar />
      <main>
        <Outlet />
      </main>
      <Footer />
    </div>
  );
}
  • Step 2: Verify typecheck passes
npm run typecheck

Expected: no errors

  • Step 3: Commit
git add src/components/layout/MarketingLayout.tsx
git commit -m "feat: wire landing.css into MarketingLayout with .marketing-page scope"

Task 4: Create useScrollReveal Hook

Files:

  • Create: src/hooks/useScrollReveal.ts

  • Step 1: Create hooks directory and hook file

// src/hooks/useScrollReveal.ts
import { useEffect } from 'react';

export function useScrollReveal() {
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.classList.add('visible');
            observer.unobserve(e.target);
          }
        });
      },
      { threshold: 0.12, rootMargin: '0px 0px -40px 0px' }
    );

    document.querySelectorAll('.reveal').forEach((el) => observer.observe(el));

    return () => observer.disconnect();
  }, []);
}
  • Step 2: Verify typecheck
npm run typecheck

Expected: no errors

  • Step 3: Commit
git add src/hooks/useScrollReveal.ts
git commit -m "feat: add useScrollReveal hook for intersection-based fade-in"

Task 5: Replace MarketingNavbar

Files:

  • Modify: src/components/layout/MarketingNavbar.tsx

The reference navbar has: sticky bar, SVG logo, nav links, lang switch, user avatar dropdown (auth-aware), CTA button.

  • Step 1: Replace MarketingNavbar.tsx
import { useState, useEffect, useRef } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';
import { useAuth } from '../../contexts/AuthContext';

export default function MarketingNavbar() {
  const { language, setLanguage } = useLanguage();
  const { user, signOut } = useAuth();
  const location = useLocation();
  const isHome = location.pathname === '/';
  const [scrolled, setScrolled] = useState(false);
  const [activeSection, setActiveSection] = useState('');
  const [userMenuOpen, setUserMenuOpen] = useState(false);
  const userMenuRef = useRef<HTMLDivElement>(null);

  // Scroll: sticky style + active nav section
  useEffect(() => {
    const handleScroll = () => {
      setScrolled(window.scrollY > 10);
      if (!isHome) return;
      let current = '';
      document.querySelectorAll('section[id]').forEach((s) => {
        if (window.scrollY >= (s as HTMLElement).offsetTop - 100) {
          current = s.id;
        }
      });
      setActiveSection(current);
    };
    window.addEventListener('scroll', handleScroll, { passive: true });
    return () => window.removeEventListener('scroll', handleScroll);
  }, [isHome]);

  // Close user menu on outside click
  useEffect(() => {
    const handle = (e: MouseEvent) => {
      if (userMenuRef.current && !userMenuRef.current.contains(e.target as Node)) {
        setUserMenuOpen(false);
      }
    };
    document.addEventListener('mousedown', handle);
    return () => document.removeEventListener('mousedown', handle);
  }, []);

  const navLinks = [
    { href: '/', label: language === 'zh' ? '首页' : 'Home', isRouter: true },
    { href: '/docs', label: language === 'zh' ? '文档' : 'Docs', isRouter: true },
    { href: '/blog', label: language === 'zh' ? '博客' : 'Blog', isRouter: true },
  ];

  const anchorLinks = isHome
    ? [{ href: '#pricing', label: language === 'zh' ? '价格' : 'Pricing' }]
    : [];

  return (
    <nav style={{ opacity: scrolled ? 1 : undefined }}>
      <div className="nav-inner">
        <Link to="/" className="nav-logo">
          <div className="logo-icon">
            <svg viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
              <path d="M4 6h16M4 10h10M4 14h12M4 18h8" />
            </svg>
          </div>
          <span style={{ fontFamily: "'Lora', serif" }}>TexPixel</span>
        </Link>

        <ul className="nav-links">
          {navLinks.map((link) => (
            <li key={link.href}>
              <Link
                to={link.href}
                className={location.pathname === link.href ? 'active' : ''}
              >
                {link.label}
              </Link>
            </li>
          ))}
          {anchorLinks.map((link) => (
            <li key={link.href}>
              <a
                href={link.href}
                className={activeSection === link.href.slice(1) ? 'active' : ''}
              >
                {link.label}
              </a>
            </li>
          ))}
        </ul>

        <div className="nav-right">
          <button
            className="lang-switch"
            onClick={() => setLanguage(language === 'zh' ? 'en' : 'zh')}
          >
            {language === 'zh' ? 'EN' : '中文'}
          </button>

          {user ? (
            <div className="nav-user" ref={userMenuRef} style={{ position: 'relative' }}>
              <div
                className="nav-avatar"
                onClick={() => setUserMenuOpen((o) => !o)}
                style={{ cursor: 'pointer' }}
              >
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
                  <circle cx="12" cy="8" r="4" />
                  <path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" />
                </svg>
              </div>
              {userMenuOpen && (
                <div className="nav-user-menu" style={{ display: 'block' }}>
                  <div className="nav-menu-divider" />
                  <Link to="/app" className="nav-menu-item" onClick={() => setUserMenuOpen(false)}>
                    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
                      <rect x="2" y="3" width="20" height="14" rx="2" />
                      <path d="M8 21h8M12 17v4" />
                    </svg>
                    {language === 'zh' ? '启动应用' : 'Launch App'}
                  </Link>
                  <div className="nav-menu-divider" />
                  <button
                    className="nav-menu-item nav-menu-logout"
                    onClick={() => { signOut(); setUserMenuOpen(false); }}
                    style={{ background: 'none', border: 'none', width: '100%', textAlign: 'left', cursor: 'pointer' }}
                  >
                    {language === 'zh' ? '退出登录' : 'Sign Out'}
                  </button>
                </div>
              )}
            </div>
          ) : (
            <div className="nav-login-btn">
              <Link to="/app" className="btn-cta" style={{ display: 'inline-block', lineHeight: '52px', padding: '0 24px', textDecoration: 'none' }}>
                {language === 'zh' ? '免费试用' : 'Try Free'}
              </Link>
            </div>
          )}
        </div>
      </div>
    </nav>
  );
}
  • Step 2: Verify typecheck
npm run typecheck

Expected: no errors

  • Step 3: Commit
git add src/components/layout/MarketingNavbar.tsx
git commit -m "feat: replace MarketingNavbar with reference design"

Task 6: Replace HeroSection

Files:

  • Modify: src/components/home/HeroSection.tsx

The hero has: left text column, right mock window with upload zone + typing LaTeX output.

  • Step 1: Replace HeroSection.tsx
import { useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';

const LATEX_LINES = [
  '<span class="code-kw">\\frac</span>{-b \\pm <span class="code-kw">\\sqrt</span>{b² - 4ac}}{2a}',
  '<span class="code-kw">\\int</span>_0^1 x^2\\,dx',
  '<span class="code-kw">\\sum</span>_{i=<span class="code-num">1</span>}^n \\frac{1}{i^2}',
];

export default function HeroSection() {
  const { language } = useLanguage();
  const codeRef = useRef<HTMLDivElement>(null);

  // Typing cycling effect
  useEffect(() => {
    let idx = 0;
    const interval = setInterval(() => {
      idx = (idx + 1) % LATEX_LINES.length;
      if (codeRef.current) {
        codeRef.current.innerHTML = LATEX_LINES[idx] + '<span class="cursor-blink"></span>';
      }
    }, 3500);
    return () => clearInterval(interval);
  }, []);

  return (
    <section className="hero">
      <div className="container">
        <div className="hero-inner">
          <div className="hero-left">
            <h1 className="hero-title">
              {language === 'zh' ? (
                <>数学公式<br />秒级转换为<br /><em className="latex-word">LaTeX</em></>
              ) : (
                <>Math Formulas<br />Converted to<br /><em className="latex-word">LaTeX</em></>
              )}
            </h1>

            <p className="hero-desc">
              {language === 'zh'
                ? 'AI 驱动的复杂数学公式识别,支持手写与论文截图,毫秒级输出 LaTeX、Markdown 与 Word 原生公式。'
                : 'AI-powered recognition for complex math formulas. Supports handwriting and paper screenshots. Instant LaTeX, Markdown, and Word output.'}
            </p>

            <div className="hero-actions">
              <Link to="/app" className="btn btn-primary">
                <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
                  <path d="M5 3l14 9-14 9V3z" />
                </svg>
                {language === 'zh' ? '免费试用 TexPixel' : 'Try TexPixel Free'}
              </Link>
              <a href="#products" className="btn btn-secondary">
                {language === 'zh' ? '了解更多 →' : 'Learn More →'}
              </a>
            </div>

            <div className="hero-trust">
              <div className="trust-item">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
                  <circle cx="12" cy="12" r="10" /><path d="M12 8v4l3 3" />
                </svg>
                {language === 'zh' ? '秒级输出' : 'Sub-second output'}
              </div>
              <div className="trust-item">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
                  <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
                  <polyline points="14 2 14 8 20 8" />
                </svg>
                {language === 'zh' ? '支持 PDF · 手写 · 截图' : 'PDF · Handwriting · Screenshots'}
              </div>
              <div className="trust-item">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2">
                  <path d="M20 7H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z" />
                  <circle cx="12" cy="12" r="1" />
                </svg>
                {language === 'zh' ? '免费套餐可用' : 'Free plan available'}
              </div>
            </div>
          </div>

          <div className="hero-right">
            <div className="mock-window">
              <div className="window-topbar">
                <div className="window-dots">
                  <div className="window-dot wd-red" />
                  <div className="window-dot wd-yellow" />
                  <div className="window-dot wd-green" />
                </div>
                <div className="window-url">
                  <svg className="url-lock" viewBox="0 0 12 14">
                    <rect x="1" y="6" width="10" height="7" rx="2" />
                    <path d="M3 6V4a3 3 0 0 1 6 0v2" fill="none" stroke="#8CC9BE" strokeWidth="1.4" />
                  </svg>
                  texpixel.com/app
                </div>
              </div>
              <div className="window-body">
                <div className="upload-zone">
                  <div className="upload-icon-wrap">
                    <svg viewBox="0 0 24 24">
                      <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
                      <polyline points="17 8 12 3 7 8" />
                      <line x1="12" y1="3" x2="12" y2="15" />
                    </svg>
                  </div>
                  <div className="upload-text">
                    {language === 'zh' ? '点击、拖拽或粘贴文件开始解析' : 'Click, drag or paste a file to start'}
                  </div>
                  <div className="upload-sub">
                    {language === 'zh' ? '支持 PNG · JPG · PDF · 手写截图' : 'PNG · JPG · PDF · Handwriting'}
                  </div>
                </div>

                <div className="output-zone">
                  <div className="output-header">
                    <span className="output-label">LaTeX Output</span>
                    <span className="output-badge">{language === 'zh' ? '识别完成' : 'Done'}</span>
                  </div>
                  <div
                    className="output-code"
                    ref={codeRef}
                    // eslint-disable-next-line react/no-danger
                    dangerouslySetInnerHTML={{
                      __html: LATEX_LINES[0] + '<span class="cursor-blink"></span>',
                    }}
                  />
                  <div className="output-actions">
                    <button className="output-btn output-btn-copy">
                      {language === 'zh' ? '复制 LaTeX' : 'Copy LaTeX'}
                    </button>
                    <button className="output-btn output-btn-word">
                      {language === 'zh' ? '复制到 Word' : 'Copy to Word'}
                    </button>
                  </div>
                </div>

                <div className="window-status">
                  <div className="status-time">{language === 'zh' ? '识别耗时 0.38s' : 'Recognized in 0.38s'}</div>
                  <div className="status-format">
                    <div className="fmt-tag">LaTeX</div>
                    <div className="fmt-tag">Markdown</div>
                    <div className="fmt-tag">Word</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}
  • Step 2: Verify typecheck
npm run typecheck

Expected: no errors

  • Step 3: Commit
git add src/components/home/HeroSection.tsx
git commit -m "feat: replace HeroSection with reference design + typing effect"

Task 7: Create ProductSuiteSection

Files:

  • Create: src/components/home/ProductSuiteSection.tsx

  • Step 1: Create ProductSuiteSection.tsx

import { Link } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';

export default function ProductSuiteSection() {
  const { language } = useLanguage();

  return (
    <section className="product-suite" id="products">
      <div className="container">
        <div className="section-header reveal">
          <div className="eyebrow">Product Matrix</div>
          <h2 className="section-title">
            {language === 'zh' ? '覆盖所有公式工作流' : 'Built for every formula workflow'}
          </h2>
          <p className="section-desc">
            {language === 'zh'
              ? '从浏览器即取即用,到扩展一键复制,再到桌面端离线处理——一个工具,覆盖学生写作业、研究者整理文献、工程师记录推导的全部场景。'
              : 'From instant browser use to one-click extension copy to offline desktop — one tool for students, researchers, and engineers.'}
          </p>
        </div>

        <div className="cards-3">
          <div className="product-card reveal reveal-delay-1">
            <div className="card-icon icon-orange">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
                <rect x="2" y="3" width="20" height="14" rx="2" />
                <path d="M8 21h8M12 17v4" />
              </svg>
            </div>
            <div className="card-title">Web App</div>
            <div className="card-desc">
              {language === 'zh'
                ? '浏览器内即时识别,无需安装。上传截图或手写图片,秒级输出 LaTeX。'
                : 'Instant recognition in browser, no install needed. Upload screenshot or handwriting, get LaTeX in seconds.'}
            </div>
            <Link to="/app" className="card-link">
              {language === 'zh' ? '浏览器即时识别 →' : 'Instant formula recognition in browser →'}
            </Link>
          </div>

          <div className="product-card reveal reveal-delay-2">
            <div className="card-icon icon-teal">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
                <polyline points="14 2 14 8 20 8" />
                <path d="M8 13h8M8 17h5" />
              </svg>
            </div>
            <div className="card-title">Extension</div>
            <div className="card-desc">
              {language === 'zh'
                ? '浏览器扩展,一键将 ChatGPT、Claude 输出的公式复制为 Word 原生数学公式。'
                : 'Browser extension — one-click copy of formulas from ChatGPT or Claude as native Word equations.'}
            </div>
            <a href="#" className="card-link">
              {language === 'zh' ? '从 LLM 复制公式到 Word →' : 'Copy formulas from LLMs to Word →'}
            </a>
          </div>

          <div className="product-card reveal reveal-delay-3">
            <div className="card-icon icon-lavender">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
                <rect x="2" y="3" width="20" height="14" rx="2" />
                <path d="M2 8h20M8 3v5" />
              </svg>
            </div>
            <div className="card-title">Desktop</div>
            <div className="card-desc">
              {language === 'zh'
                ? '桌面端离线处理,适合论文批量提取与隐私保护场景,一次购买终身使用。'
                : 'Offline desktop app for batch PDF extraction and privacy-sensitive work. One-time purchase.'}
            </div>
            <a href="#pricing" className="card-link">
              {language === 'zh' ? '查看桌面版定价 →' : 'See Desktop pricing →'}
            </a>
          </div>
        </div>
      </div>
    </section>
  );
}
  • Step 2: Verify typecheck
npm run typecheck

Expected: no errors

  • Step 3: Commit
git add src/components/home/ProductSuiteSection.tsx
git commit -m "feat: add ProductSuiteSection"

Task 8: Replace FeaturesSection (Core Features)

Files:

  • Modify: src/components/home/FeaturesSection.tsx

  • Step 1: Replace FeaturesSection.tsx

import { useLanguage } from '../../contexts/LanguageContext';

export default function FeaturesSection() {
  const { language } = useLanguage();

  return (
    <section className="core-features">
      <div className="container">
        <div className="section-header reveal">
          <div className="eyebrow">Core Features</div>
          <h2 className="section-title">
            {language === 'zh'
              ? '学生留下来的三个理由'
              : 'Three reasons students stay with TexPixel'}
          </h2>
        </div>

        <div className="cards-3">
          <div className="feature-card reveal reveal-delay-1">
            <div className="feature-mini">
              <span className="feature-speed"> t &lt; 1s</span>
            </div>
            <div className="card-title">{language === 'zh' ? '极速识别' : 'Sub-second Recognition'}</div>
            <div className="card-desc">
              {language === 'zh'
                ? '上传截图LaTeX 随即出现。拍下笔记,无需等待,直接复制。'
                : 'Upload a screenshot, LaTeX appears instantly. Take a photo of your notes and copy right away.'}
            </div>
          </div>

          <div className="feature-card reveal reveal-delay-2">
            <div className="feature-mini" style={{ fontSize: '13px', color: 'var(--text-body)' }}>
              <span style={{ fontFamily: "'JetBrains Mono', monospace", color: 'var(--primary)' }}>
                \int_0^1 x^2 dx
              </span>
            </div>
            <div className="card-title">{language === 'zh' ? '复杂公式支持' : 'Complex Formula Support'}</div>
            <div className="card-desc">
              {language === 'zh'
                ? '矩阵、积分、求和、化学式全部支持。多行公式对齐、角标嵌套一次识别。'
                : 'Matrices, integrals, summations, chemical formulas — all supported. Multi-line alignment and nested scripts in one pass.'}
            </div>
          </div>

          <div className="feature-card reveal reveal-delay-3">
            <div className="feature-mini">
              <span style={{ fontFamily: "'JetBrains Mono', monospace", color: 'var(--text-body)', fontSize: '13px' }}>
                \mathbf{'{A}'}<sup style={{ fontSize: '9px', color: 'var(--teal)' }}>1</sup>b
              </span>
            </div>
            <div className="card-title">{language === 'zh' ? '高准确度' : 'High Accuracy'}</div>
            <div className="card-desc">
              {language === 'zh'
                ? '论文级别识别准确率。在 arXiv 截图测试集上准确率超过 95%,持续迭代提升中。'
                : 'Publication-grade accuracy. Over 95% on arXiv screenshot benchmarks, continuously improving.'}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}
  • Step 2: Verify typecheck
npm run typecheck
  • Step 3: Commit
git add src/components/home/FeaturesSection.tsx
git commit -m "feat: replace FeaturesSection with reference core features design"

Task 9: Create ShowcaseSection

Files:

  • Create: src/components/home/ShowcaseSection.tsx

  • Step 1: Create ShowcaseSection.tsx

import { Link } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';

export default function ShowcaseSection() {
  const { language } = useLanguage();

  return (
    <section className="showcase">
      <div className="container">
        <div className="section-header reveal">
          <div className="eyebrow">Live Examples</div>
          <h2 className="section-title">
            {language === 'zh' ? '真实案例演示' : 'Try Real Examples'}
          </h2>
          <p className="section-desc">
            {language === 'zh'
              ? '看看 TexPixel 如何处理真实的论文截图与手写笔记。'
              : 'See how TexPixel handles real paper screenshots and handwritten notes.'}
          </p>
        </div>

        <div className="showcase-cards">
          <div className="showcase-card reveal reveal-delay-1">
            <div className="showcase-header">
              <div className="showcase-tag">{language === 'zh' ? '复杂公式' : 'Complex Formula'}</div>
              <div className="showcase-title">
                {language === 'zh' ? '论文截图到可复制 LaTeX' : 'Paper Screenshot to Copyable LaTeX'}
              </div>
              <div className="showcase-sub">arXiv PDF · 0.41s</div>
            </div>
            <div className="showcase-body">
              <div className="sc-input">
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '15px', opacity: 0.6 }}>
                   ¹/ᵢ² · · · [{language === 'zh' ? '图片占位' : 'image placeholder'}]
                </span>
              </div>
              <div className="sc-arrow"></div>
              <div
                className="sc-output"
                dangerouslySetInnerHTML={{
                  __html: '<span class="kw">\\sum</span>_{i=<span class="num">1</span>}^{n}<span class="kw">\\frac</span>{<span class="num">1</span>}{i^<span class="num">2</span>}',
                }}
              />
            </div>
            <div className="showcase-footer">
              <Link to="/app" className="btn btn-secondary" style={{ height: '44px', fontSize: '14px', padding: '0 20px' }}>
                {language === 'zh' ? '试试这个例子 →' : 'Try this example →'}
              </Link>
            </div>
          </div>

          <div className="showcase-card reveal reveal-delay-2">
            <div className="showcase-header">
              <div className="showcase-tag" style={{ background: 'rgba(140,201,190,0.15)', color: 'var(--teal)' }}>
                {language === 'zh' ? '手写公式' : 'Handwritten Formula'}
              </div>
              <div className="showcase-title">
                {language === 'zh' ? '课堂笔记到标准表达式' : 'Classroom Notes to Standard Expression'}
              </div>
              <div className="showcase-sub">{language === 'zh' ? '手机拍摄笔记 · 0.38s' : 'Phone photo of notes · 0.38s'}</div>
            </div>
            <div className="showcase-body">
              <div className="sc-input">
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '15px', opacity: 0.6 }}>
                  lim sin(x)/x [{language === 'zh' ? '手写' : 'handwritten'}]
                </span>
              </div>
              <div className="sc-arrow"></div>
              <div
                className="sc-output"
                dangerouslySetInnerHTML={{
                  __html: '<span class="kw">\\lim</span>_{x<span class="br">\\to</span> <span class="num">0</span>}<span class="kw">\\frac</span>{<span class="kw">\\sin</span> x}{x}',
                }}
              />
            </div>
            <div className="showcase-footer">
              <Link to="/app" className="btn btn-secondary" style={{ height: '44px', fontSize: '14px', padding: '0 20px' }}>
                {language === 'zh' ? '试试这个例子 →' : 'Try this example →'}
              </Link>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}
  • Step 2: Verify typecheck
npm run typecheck
  • Step 3: Commit
git add src/components/home/ShowcaseSection.tsx
git commit -m "feat: add ShowcaseSection with formula examples"

Files:

  • Create: src/components/home/TestimonialsSection.tsx

  • Step 1: Create TestimonialsSection.tsx

6 testimonials, 3 visible at a time = 4 pages. Carousel uses CSS transform: translateX on a flex track.

import { useState, useEffect, useCallback, useRef } from 'react';
import { useLanguage } from '../../contexts/LanguageContext';

const TESTIMONIALS = [
  {
    quote: '写论文的时候截图粘进去LaTeX 就出来了。以前每个公式都要手敲,现在一个截图解决,省了我大概 40% 的时间。',
    name: '林同学',
    role: '数学系研究生 · 北京大学',
    avatarBg: 'var(--teal)',
    avatarColor: '#1a5c54',
    avatarLetter: '林',
    stars: '★★★★★',
  },
  {
    quote: '手写推导拍一张,马上就是干净的 LaTeX。对我这种每天要整理大量笔记的人来说真的是刚需级别的工具。',
    name: '田晓雯',
    role: '物理学博士候选人 · 清华大学',
    avatarBg: 'var(--lavender)',
    avatarColor: '#3d3870',
    avatarLetter: '田',
    stars: '★★★★★',
  },
  {
    quote: "I use it every day for my thesis. The accuracy on complex integrals and matrix expressions is surprisingly good — way better than anything I've tried before.",
    name: 'Sarah M.',
    role: 'Applied Math PhD · MIT',
    avatarBg: 'var(--rose)',
    avatarColor: '#7a2e1e',
    avatarLetter: 'S',
    stars: '★★★★★',
  },
  {
    quote: 'Desktop 版的离线功能对我来说很重要,论文数据不想上传到云端。买断价格也合理,不用每个月担心订阅。',
    name: '陈博士',
    role: '生物信息学研究员 · 中科院',
    avatarBg: 'var(--gold)',
    avatarColor: '#6f5800',
    avatarLetter: '陈',
    stars: '★★★★★',
  },
  {
    quote: 'The browser extension is a game changer. I copy equations from Claude or ChatGPT straight into Word as native math — no more reformatting nightmares.',
    name: 'Alex K.',
    role: 'Engineering Student · TU Berlin',
    avatarBg: '#8CB4C9',
    avatarColor: '#1a3d52',
    avatarLetter: 'A',
    stars: '★★★★★',
  },
  {
    quote: '教材扫描件里的公式以前完全没法用,现在截图一框就搞定。连化学方程式都能识别,超出我的预期。',
    name: '王梓涵',
    role: '化学工程本科 · 浙江大学',
    avatarBg: '#C9A88C',
    avatarColor: '#4a2e14',
    avatarLetter: '王',
    stars: '★★★★☆',
  },
];

const VISIBLE = 3;
const TOTAL = TESTIMONIALS.length;
const PAGES = TOTAL - VISIBLE + 1; // 4

export default function TestimonialsSection() {
  const { language } = useLanguage();
  const [currentPage, setCurrentPage] = useState(0);
  const trackRef = useRef<HTMLDivElement>(null);
  const autoTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);

  const goTo = useCallback((idx: number) => {
    const clamped = Math.max(0, Math.min(idx, PAGES - 1));
    setCurrentPage(clamped);
    if (trackRef.current) {
      const cardW = trackRef.current.parentElement!.offsetWidth;
      const gap = 20;
      const singleW = (cardW - gap * (VISIBLE - 1)) / VISIBLE;
      const offset = clamped * (singleW + gap);
      trackRef.current.style.transform = `translateX(-${offset}px)`;
    }
  }, []);

  const resetAuto = useCallback(() => {
    if (autoTimerRef.current) clearInterval(autoTimerRef.current);
    autoTimerRef.current = setInterval(() => {
      setCurrentPage((prev) => {
        const next = (prev + 1) % PAGES;
        goTo(next);
        return next;
      });
    }, 5000);
  }, [goTo]);

  useEffect(() => {
    resetAuto();
    return () => { if (autoTimerRef.current) clearInterval(autoTimerRef.current); };
  }, [resetAuto]);

  useEffect(() => {
    const handleResize = () => goTo(currentPage);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, [currentPage, goTo]);

  const handleGoTo = (idx: number) => {
    goTo(idx);
    resetAuto();
  };

  return (
    <section className="user-love">
      <div className="container">
        <div className="section-header reveal">
          <div className="eyebrow">User Love</div>
          <h2 className="section-title">
            {language === 'zh' ? '全球学生都在用' : 'Loved by students worldwide'}
          </h2>
          <p className="section-desc">
            {language === 'zh' ? '来自真实用户的反馈。' : 'Feedback from real users.'}
          </p>
        </div>

        <div className="testimonial-wrap reveal">
          <div
            className="testimonial-track"
            ref={trackRef}
            style={{ transition: 'transform 0.4s ease' }}
          >
            {TESTIMONIALS.map((t, i) => (
              <div key={i} className="testimonial-card">
                <div className="t-quote">"</div>
                <p className="t-body">{t.quote}</p>
                <div className="t-footer">
                  <div
                    className="t-avatar"
                    style={{ background: t.avatarBg, color: t.avatarColor }}
                  >
                    {t.avatarLetter}
                  </div>
                  <div>
                    <div className="t-name">{t.name}</div>
                    <div className="t-role">{t.role}</div>
                  </div>
                  <div className="t-stars">{t.stars}</div>
                </div>
              </div>
            ))}
          </div>

          <div className="testimonial-nav">
            <button
              className="t-nav-btn"
              onClick={() => handleGoTo(currentPage - 1)}
              aria-label={language === 'zh' ? '上一条' : 'Previous'}
            >
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
                <path d="M15 18l-6-6 6-6" />
              </svg>
            </button>
            <div className="t-dots">
              {Array.from({ length: PAGES }).map((_, i) => (
                <div
                  key={i}
                  className={`t-dot${i === currentPage ? ' active' : ''}`}
                  onClick={() => handleGoTo(i)}
                />
              ))}
            </div>
            <button
              className="t-nav-btn"
              onClick={() => handleGoTo(currentPage + 1)}
              aria-label={language === 'zh' ? '下一条' : 'Next'}
            >
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
                <path d="M9 18l6-6-6-6" />
              </svg>
            </button>
          </div>
        </div>
      </div>
    </section>
  );
}
  • Step 2: Verify typecheck
npm run typecheck

Expected: no errors

  • Step 3: Commit
git add src/components/home/TestimonialsSection.tsx
git commit -m "feat: add TestimonialsSection with auto-advancing carousel"

Task 11: Replace PricingSection

Files:

  • Modify: src/components/home/PricingSection.tsx

  • Step 1: Replace PricingSection.tsx

import { Link } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';

export default function PricingSection() {
  const { language } = useLanguage();
  const zh = language === 'zh';

  return (
    <section className="pricing" id="pricing">
      <div className="container">
        <div className="section-header reveal">
          <div className="eyebrow">Pricing</div>
          <h2 className="section-title">
            {zh ? '选择适合你的方案' : 'Choose the plan that fits your workflow'}
          </h2>
          <p className="section-desc">
            {zh ? '从免费试用到永久桌面版,按需选择,无需绑定订阅。' : 'From free trial to lifetime desktop — pick what fits, no subscription lock-in.'}
          </p>
        </div>

        <div className="pricing-cards">
          <div className="pricing-card reveal reveal-delay-1">
            <div className="plan-name">Free</div>
            <div className="plan-price">$<span style={{ fontSize: '44px', color: 'var(--text-strong)' }}>0</span></div>
            <div className="plan-period">{zh ? '永久免费' : 'Forever free'}</div>
            <div className="plan-desc">For first-time use and quick screenshots.</div>
            <ul className="plan-features">
              <li>{zh ? '每月 30 次识别' : '30 recognitions / month'}</li>
              <li>LaTeX {zh ? '输出' : 'output'}</li>
              <li>Web App {zh ? '访问' : 'access'}</li>
              <li>{zh ? '基础公式支持' : 'Basic formula support'}</li>
            </ul>
            <Link to="/app" className="plan-btn">{zh ? '开始使用' : 'Get Started'}</Link>
          </div>

          <div className="pricing-card reveal reveal-delay-2">
            <div className="plan-name">Monthly</div>
            <div className="plan-price"><span>$</span>9</div>
            <div className="plan-period">{zh ? '每月 / 随时取消' : '/month · cancel anytime'}</div>
            <div className="plan-desc">Unlimited recognition for everyday study.</div>
            <ul className="plan-features">
              <li>{zh ? '无限次识别' : 'Unlimited recognitions'}</li>
              <li>LaTeX + Markdown</li>
              <li>{zh ? 'Word 原生公式' : 'Native Word equations'}</li>
              <li>{zh ? '优先处理队列' : 'Priority queue'}</li>
            </ul>
            <Link to="/app" className="plan-btn">{zh ? '开始使用' : 'Get Started'}</Link>
          </div>

          <div className="pricing-card reveal reveal-delay-3">
            <div className="plan-name">Quarterly</div>
            <div className="plan-price"><span>$</span>24</div>
            <div className="plan-period">{zh ? '每季 · 省 $3/月' : '/quarter · save $3/mo'}</div>
            <div className="plan-desc">Best value for semester-long heavy usage.</div>
            <ul className="plan-features">
              <li>{zh ? '无限次识别' : 'Unlimited recognitions'}</li>
              <li>{zh ? '全格式输出' : 'All output formats'}</li>
              <li>{zh ? '批量 PDF 提取' : 'Batch PDF extraction'}</li>
              <li>API {zh ? '访问Beta' : 'access (Beta)'}</li>
            </ul>
            <Link to="/app" className="plan-btn">{zh ? '开始使用' : 'Get Started'}</Link>
          </div>

          <div className="pricing-card featured reveal">
            <div className="featured-badge">{zh ? '永久版' : 'Lifetime'}</div>
            <div className="plan-name">Desktop</div>
            <div className="plan-price"><span>$</span>79</div>
            <div className="plan-period">{zh ? '一次购买 · 终身使用' : 'one-time · lifetime access'}</div>
            <div className="plan-desc">Lifetime access for offline and sensitive files.</div>
            <ul className="plan-features">
              <li>{zh ? '完全离线运行' : 'Fully offline'}</li>
              <li>{zh ? '无限次识别' : 'Unlimited recognitions'}</li>
              <li>{zh ? '批量 PDF 处理' : 'Batch PDF processing'}</li>
              <li>{zh ? '本地隐私保护' : 'Local privacy'}</li>
              <li>{zh ? '终身免费更新' : 'Lifetime free updates'}</li>
            </ul>
            <Link to="/app" className="plan-btn featured-btn">{zh ? '购买桌面版' : 'Buy Desktop'}</Link>
          </div>
        </div>
      </div>
    </section>
  );
}
  • Step 2: Verify typecheck
npm run typecheck
  • Step 3: Commit
git add src/components/home/PricingSection.tsx
git commit -m "feat: replace PricingSection with reference 4-tier pricing design"

Task 12: Create DocsSeoSection

Files:

  • Create: src/components/home/DocsSeoSection.tsx

  • Step 1: Create DocsSeoSection.tsx

import { Link } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';

const GUIDES = [
  {
    icon: <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>,
    icon2: <polyline points="14 2 14 8 20 8"/>,
    titleEn: 'How to convert image to LaTeX',
    titleZh: '图片转 LaTeX 完整指南',
    metaEn: '5 min read · Most popular',
    metaZh: '5 分钟 · 最受欢迎',
  },
  {
    icon: <><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M8 9h8M8 12h6M8 15h4"/></>,
    titleEn: 'Copy formula to Word — native equation format',
    titleZh: '复制公式到 Word — 原生公式格式',
    metaEn: '4 min read · Extension users',
    metaZh: '4 分钟 · 扩展用户',
  },
  {
    icon: <><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></>,
    titleEn: 'OCR math formula guide — accuracy tips',
    titleZh: 'OCR 数学公式指南 — 提高准确度',
    metaEn: '6 min read · Power users',
    metaZh: '6 分钟 · 进阶用户',
  },
  {
    icon: <><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M10 12l2 2 4-4"/></>,
    titleEn: 'PDF formula extraction — batch workflow',
    titleZh: 'PDF 公式批量提取工作流',
    metaEn: '8 min read · Desktop users',
    metaZh: '8 分钟 · 桌面版用户',
  },
];

export default function DocsSeoSection() {
  const { language } = useLanguage();
  const zh = language === 'zh';

  return (
    <section className="docs-seo" id="docs">
      <div className="container">
        <div className="section-header reveal">
          <div className="eyebrow">Guides</div>
          <h2 className="section-title">Image to LaTeX Guides</h2>
          <p className="section-desc">
            {zh
              ? '为学生、研究者和数学写作者准备的一步步工作流指南。'
              : 'Step-by-step workflows for students, researchers, and anyone writing math.'}
          </p>
        </div>

        <div className="doc-cards reveal">
          {GUIDES.map((g, i) => (
            <Link key={i} to="/docs" className="doc-card">
              <div className="doc-card-left">
                <div className="doc-icon">
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
                    {g.icon}
                    {g.icon2}
                  </svg>
                </div>
                <div>
                  <div className="doc-title">{zh ? g.titleZh : g.titleEn}</div>
                  <div className="doc-meta">{zh ? g.metaZh : g.metaEn}</div>
                </div>
              </div>
              <div className="doc-read">{zh ? '阅读指南 →' : 'Read Guide →'}</div>
            </Link>
          ))}
        </div>
      </div>
    </section>
  );
}
  • Step 2: Verify typecheck
npm run typecheck
  • Step 3: Commit
git add src/components/home/DocsSeoSection.tsx
git commit -m "feat: add DocsSeoSection with guide cards"

Files:

  • Modify: src/components/layout/Footer.tsx

  • Step 1: Replace Footer.tsx

import { Link } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';

export default function Footer() {
  const { language } = useLanguage();
  const zh = language === 'zh';

  return (
    <footer>
      <div className="container">
        <div className="footer-grid">
          <div className="footer-brand">
            <Link to="/" className="footer-logo">
              <div className="logo-icon" style={{ width: '32px', height: '32px', borderRadius: '9px' }}>
                <svg viewBox="0 0 24 24" style={{ width: '18px', height: '18px' }} fill="none" stroke="white" strokeWidth="2">
                  <path d="M4 6h16M4 10h10M4 14h12M4 18h8" />
                </svg>
              </div>
              <span>TexPixel</span>
            </Link>
            <p className="footer-tagline">
              {zh ? '为学生、研究者和数学写作者而设计。' : 'Designed for students, researchers, and anyone writing math.'}
            </p>
          </div>

          <div>
            <div className="footer-col-title">Product</div>
            <ul className="footer-links">
              <li><Link to="/app">Web App</Link></li>
              <li><a href="#">Extension</a></li>
              <li><a href="#pricing">{zh ? '桌面版' : 'Desktop'}</a></li>
              <li><a href="#">API (Beta)</a></li>
            </ul>
          </div>

          <div>
            <div className="footer-col-title">Docs</div>
            <ul className="footer-links">
              <li><Link to="/docs">Image to LaTeX</Link></li>
              <li><Link to="/docs">PDF to Markdown</Link></li>
              <li><Link to="/docs">{zh ? '手写识别' : 'Handwritten OCR'}</Link></li>
              <li><Link to="/docs">Word Equations</Link></li>
            </ul>
          </div>

          <div>
            <div className="footer-col-title">{zh ? '公司' : 'Company'}</div>
            <ul className="footer-links">
              <li><a href="#">{zh ? '关于我们' : 'About'}</a></li>
              <li><a href="#pricing">{zh ? '价格' : 'Pricing'}</a></li>
              <li><Link to="/blog">{zh ? '博客' : 'Blog'}</Link></li>
              <li><a href="#">{zh ? '联系我们' : 'Contact'}</a></li>
            </ul>
          </div>

          <div>
            <div className="footer-col-title">{zh ? '法律' : 'Legal'}</div>
            <ul className="footer-links">
              <li><a href="#">{zh ? '服务条款' : 'Terms of Service'}</a></li>
              <li><a href="#">{zh ? '隐私政策' : 'Privacy Policy'}</a></li>
              <li><a href="#">{zh ? 'Cookie 政策' : 'Cookie Policy'}</a></li>
            </ul>
          </div>
        </div>

        <div className="footer-bottom">
          <div className="footer-copy">© 2026 TexPixel. All rights reserved.</div>
          <div className="footer-made">
            Made with{' '}
            <svg viewBox="0 0 24 24" style={{ display: 'inline', width: '14px', height: '14px', verticalAlign: 'middle', fill: 'var(--rose)' }}>
              <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
            </svg>
            {' '}{zh ? '为全球学生而作' : 'for students worldwide'}
          </div>
        </div>
      </div>
    </footer>
  );
}
  • Step 2: Verify typecheck
npm run typecheck
  • Step 3: Commit
git add src/components/layout/Footer.tsx
git commit -m "feat: replace Footer with reference design"

Task 14: Update HomePage + Delete Old Components + Clean Translations

Files:

  • Modify: src/pages/HomePage.tsx

  • Modify: src/lib/translations.ts

  • Delete: src/components/home/HowItWorksSection.tsx

  • Delete: src/components/home/ContactSection.tsx

  • Step 1: Replace HomePage.tsx

import SEOHead from '../components/seo/SEOHead';
import HeroSection from '../components/home/HeroSection';
import ProductSuiteSection from '../components/home/ProductSuiteSection';
import FeaturesSection from '../components/home/FeaturesSection';
import ShowcaseSection from '../components/home/ShowcaseSection';
import TestimonialsSection from '../components/home/TestimonialsSection';
import PricingSection from '../components/home/PricingSection';
import DocsSeoSection from '../components/home/DocsSeoSection';
import { useScrollReveal } from '../hooks/useScrollReveal';
import { useLanguage } from '../contexts/LanguageContext';

export default function HomePage() {
  const { t } = useLanguage();
  useScrollReveal();

  return (
    <>
      <SEOHead
        title="TexPixel - AI Math Formula Recognition | LaTeX, MathML OCR Tool"
        description={t.marketing.hero.subtitle}
        path="/"
      />
      <HeroSection />
      <div className="section-divider" />
      <ProductSuiteSection />
      <FeaturesSection />
      <ShowcaseSection />
      <div className="section-divider" />
      <TestimonialsSection />
      <div className="section-divider" />
      <PricingSection />
      <div className="section-divider" />
      <DocsSeoSection />
    </>
  );
}
  • Step 2: Remove dead contact keys from translations.ts

In src/lib/translations.ts:

  • Remove contact: 'Contact', from the en.marketing.nav block (around line 115)

  • Remove contact: '联系我们', from the zh.marketing.nav block (around line 297)

  • Remove the entire en.marketing.contact block (the object starting with contact: { title: around line 165175)

  • Remove the entire zh.marketing.contact block (equivalent block in the zh section)

  • Step 3: Delete old components

rm src/components/home/HowItWorksSection.tsx
rm src/components/home/ContactSection.tsx
  • Step 4: Verify typecheck and build
npm run typecheck && npm run build

Expected: no TypeScript errors, build succeeds

  • Step 5: Commit
git add src/pages/HomePage.tsx src/lib/translations.ts
git rm src/components/home/HowItWorksSection.tsx src/components/home/ContactSection.tsx
git commit -m "feat: wire all landing sections in HomePage, remove obsolete components"

Task 15: Verify CSS Scoping Does Not Break Workspace

Files:

  • No code changes — verification only

  • Step 1: Run the dev server and navigate to /app

npm run dev

Open http://localhost:5173/app — verify:

  • The workspace AppLayout and WorkspacePage render correctly

  • No .marketing-page CSS rules leak (no warm beige background, no grid overlay on the workspace)

  • Step 2: Navigate to / (home)

Open http://localhost:5173/ — verify:

  • Background matches reference (#FFFBF7 warm cream with grid pattern)

  • All three glow blobs visible as ambient background

  • Navbar, Hero, all sections, and Footer match the reference HTML visually

  • Step 3: Run full build

npm run build

Expected: success with no errors

  • Step 4: Final commit
git add -A
git commit -m "chore: final verification — landing refactor complete"