feat: redesign pricing section with beta notice and i18n pricing

- 4-card layout: Free / Monthly / Quarterly / Lifetime License
- zh shows RMB ¥, en shows $ with localized prices
- Monthly ¥19.9 (edu ¥12.9 / $1.99), Quarterly ¥49.9 (edu ¥29.9 / $7.99)
- Diagonal corner ribbons (green "限时免费") on Monthly & Quarterly
- Desktop card renamed to "永久授权 / Lifetime License" with Coming Soon ribbon
- Desktop tag badge distinguishes it as a native offline app
- Beta notice banner: all plans free during beta, no payment required
- CTA unified to "免费体验 / Try Free" linking to /app
- Cards use flex-column + align-items:stretch for consistent equal height

Made-with: Cursor
This commit is contained in:
2026-03-27 00:06:39 +08:00
parent a6eb79f530
commit 6e4df89b23
16 changed files with 2300 additions and 389 deletions

View File

@@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useLanguage } from '../../contexts/LanguageContext';
import { useAuth } from '../../contexts/AuthContext';
import AuthModal from '../AuthModal';
export default function MarketingNavbar() {
const { language, setLanguage } = useLanguage();
@@ -11,6 +12,7 @@ export default function MarketingNavbar() {
const [scrolled, setScrolled] = useState(false);
const [activeSection, setActiveSection] = useState('');
const [userMenuOpen, setUserMenuOpen] = useState(false);
const [showAuthModal, setShowAuthModal] = useState(false);
const userMenuRef = useRef<HTMLDivElement>(null);
// Scroll: sticky style + active nav section
@@ -52,90 +54,94 @@ export default function MarketingNavbar() {
: [];
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>
<>
<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>
<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>
<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>
{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>
)}
</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>
)}
{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">
<button className="btn-cta" onClick={() => setShowAuthModal(true)}>
{language === 'zh' ? '登录' : 'Login'}
</button>
</div>
)}
</div>
</div>
</div>
</nav>
</nav>
{showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
</>
);
}