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

1524 lines
54 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`:
```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:
```html
<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**
```bash
npm run typecheck
```
Expected: no errors
- [ ] **Step 3: Commit**
```bash
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**
```bash
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:
```css
.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**
```bash
ls src/styles/landing.css
```
Expected: file listed
- [ ] **Step 4: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
Expected: no errors
- [ ] **Step 3: Commit**
```bash
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**
```typescript
// 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**
```bash
npm run typecheck
```
Expected: no errors
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
Expected: no errors
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
Expected: no errors
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
Expected: no errors
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
- [ ] **Step 3: Commit**
```bash
git add src/components/home/ShowcaseSection.tsx
git commit -m "feat: add ShowcaseSection with formula examples"
```
---
## Task 10: Create TestimonialsSection (Carousel)
**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.
```tsx
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**
```bash
npm run typecheck
```
Expected: no errors
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
npm run typecheck
```
- [ ] **Step 3: Commit**
```bash
git add src/components/home/DocsSeoSection.tsx
git commit -m "feat: add DocsSeoSection with guide cards"
```
---
## Task 13: Replace Footer
**Files:**
- Modify: `src/components/layout/Footer.tsx`
- [ ] **Step 1: Replace Footer.tsx**
```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**
```bash
npm run typecheck
```
- [ ] **Step 3: Commit**
```bash
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**
```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**
```bash
rm src/components/home/HowItWorksSection.tsx
rm src/components/home/ContactSection.tsx
```
- [ ] **Step 4: Verify typecheck and build**
```bash
npm run typecheck && npm run build
```
Expected: no TypeScript errors, build succeeds
- [ ] **Step 5: Commit**
```bash
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**
```bash
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**
```bash
npm run build
```
Expected: success with no errors
- [ ] **Step 4: Final commit**
```bash
git add -A
git commit -m "chore: final verification — landing refactor complete"
```