refactor(theme): 营销页 bg-white token 化(暗黑模式 Phase 2 · marketing 批次)

问题:Tailwind 原生 bg-white 是硬编码值,深色主题下不翻转,
导致整页/整 section 恒为纯白,与转深色的页头页脚割裂。
(globals.css:--color-bg-primary-rgb 浅色 255 255 255 → 深色 10 14 20)

改动:10 文件 67 处 bg-white → 项目 token,按语义二分类
- bg-bg-primary  (33 处):页面/大块骨架(min-h-screen 容器、<section>、<main>)
- bg-bg-elevated (34 处):浮起层(带 border 的卡片、表单输入、图标盒、grid 单元格)
  --color-bg-elevated 浅色 #FFFFFF → 深色 #151B23,同样翻转。

浅色主题下 bg-white / bg-bg-primary / bg-bg-elevated 三者同值(纯白),
故本次改动在浅色主题下零像素差异,只修复深色主题。

新增审计工具:
- scripts/audit/hardcoded-color-audit.mjs:硬编码颜色分级盘点
  (含 alpha 误报修正:bg-white/NN 半透明叠加两主题均成立,判 INFO 非 P0)
- scripts/audit/darkmode-bg-verify.mjs:直接验证背景随主题翻转
  (浅色应纯白、深色应 rgb(10,14,20) 且无残留纯白块)

审计结果:marketing P0 67 → 0;全站 P0 97 → 30
(剩余 components 16 / other 8 / layout 3 / ui-kit 3 为第二批;admin 本就为 0)

门禁:
- tsc 0 error
- eslint 0 error(50 warnings 全为既有,改动文件未新增)
- jest 129/130 套件通过,1548 通过 / 2 跳过 / **0 断言失败**
  唯一失败 src/lib/admin-api.test.ts 为**沙箱环境限制非代码问题**:
  CODEBUDDY_BROKER_DENY 拦截 readFileSync,单独复验同样失败,
  与本次改动零交集(改动仅限 src/app/(marketing)/ 下 10 个页面组件)
- 暗黑模式背景验证 18/18 PASS(9 路由 × 浅深双版)
This commit is contained in:
2026-09-04 13:46:29 +08:00
parent 8f4a860180
commit 846585af1f
13 changed files with 339 additions and 67 deletions
@@ -26,7 +26,7 @@ function CaseCard({ caseItem, index }: { caseItem: CaseStudy; index: number }) {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.3, delay: index * 0.06, ease: EASE_OUT }}
className="group relative block overflow-hidden border border-border-primary hover:border-border-secondary bg-white hover:bg-bg-secondary transition-all duration-150 flex flex-col h-full"
className="group relative block overflow-hidden border border-border-primary hover:border-border-secondary bg-bg-elevated hover:bg-bg-secondary transition-all duration-150 flex flex-col h-full"
>
<div className="absolute top-0 left-0 h-0 w-[2px] group-hover:h-full transition-all duration-150 origin-top bg-brand" />
@@ -120,9 +120,9 @@ export default function CasesContentV3({
}, [selectedIndustry, casesProp]);
return (
<div className="min-h-screen bg-white text-ink overflow-x-hidden">
<div className="min-h-screen bg-bg-primary text-ink overflow-x-hidden">
{/* Hero Section */}
<section className="relative min-h-[70vh] flex items-center overflow-hidden bg-white">
<section className="relative min-h-[70vh] flex items-center overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-28 sm:py-36 md:py-40">
<div className="max-w-4xl">
<motion.div
@@ -224,7 +224,7 @@ export default function CasesContentV3({
</section>
{/* Cases List Section */}
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">
<div className="flex flex-col lg:flex-row lg:items-end lg:justify-between gap-8 mb-12 sm:mb-16 md:mb-20">
<motion.div
@@ -205,7 +205,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
const commitmentItems = data.commitmentItems ?? [];
return (
<div className="min-h-screen bg-white text-ink overflow-x-hidden">
<div className="min-h-screen bg-bg-primary text-ink overflow-x-hidden">
<BreadcrumbSchema items={[{ name: '首页', href: '/' }, { name: '联系我们', href: '/contact' }]} />
{showToast && (
<Toast
@@ -217,7 +217,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
)}
{/* Hero Section */}
<section className="relative min-h-[80vh] flex items-center overflow-hidden bg-white">
<section className="relative min-h-[80vh] flex items-center overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-28 sm:py-36 md:py-44 lg:py-56">
<div className="max-w-4xl">
<motion.div
@@ -285,7 +285,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
<h2 className="text-xl font-bold text-ink mb-8">{data.contactTitle || ''}</h2>
<div className="space-y-6" data-testid="contact-info">
<div className="flex items-start gap-4" data-testid="email-info">
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0" aria-hidden="true">
<div className="w-12 h-12 border border-border-primary bg-bg-elevated flex items-center justify-center shrink-0" aria-hidden="true">
<Mail className="w-5 h-5 text-text-secondary" />
</div>
<div className="mt-3.5">
@@ -301,7 +301,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
</div>
<div className="flex items-start gap-4" data-testid="address-info">
<div className="w-12 h-12 border border-border-primary bg-white flex items-center justify-center shrink-0" aria-hidden="true">
<div className="w-12 h-12 border border-border-primary bg-bg-elevated flex items-center justify-center shrink-0" aria-hidden="true">
<MapPin className="w-5 h-5 text-text-secondary" />
</div>
<div className="mt-3.5">
@@ -314,7 +314,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
</div>
</div>
<div className="relative border border-border-primary bg-white p-8 overflow-hidden">
<div className="relative border border-border-primary bg-bg-elevated p-8 overflow-hidden">
<div className="absolute top-0 left-0 bottom-0 w-0.5 bg-brand/60" />
<div className="flex items-center gap-3 mb-4">
<Clock className="w-5 h-5 text-brand" aria-hidden="true" />
@@ -326,7 +326,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
</div>
</div>
<div className="relative border border-border-primary bg-white p-8 overflow-hidden">
<div className="relative border border-border-primary bg-bg-elevated p-8 overflow-hidden">
<div className="absolute top-0 left-0 bottom-0 w-0.5 bg-brand/60" />
<div className="flex items-center gap-3 mb-4">
<HeadphonesIcon className="w-5 h-5 text-text-secondary" aria-hidden="true" />
@@ -351,7 +351,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
transition={{ duration: 0.3, delay: 0.1, ease: EASE_OUT }}
className="lg:col-span-3"
>
<div className="relative border border-border-primary bg-white p-6 sm:p-8 lg:p-10 h-full overflow-hidden" id="contact-form-section">
<div className="relative border border-border-primary bg-bg-elevated p-6 sm:p-8 lg:p-10 h-full overflow-hidden" id="contact-form-section">
<div className="absolute top-0 left-0 right-0 h-0.5 bg-brand" />
<h2 className="text-xl font-bold text-ink mb-8">{data.formTitle || ''}</h2>
@@ -411,7 +411,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
onChange={(e) => handleChange('name', e.target.value)}
onBlur={(e) => handleBlur('name', e.target.value)}
error={errors.name}
className="bg-white border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
className="bg-bg-elevated border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
/>
</div>
<div className="relative">
@@ -434,7 +434,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
onChange={(e) => handleChange('phone', e.target.value)}
onBlur={(e) => handleBlur('phone', e.target.value)}
error={errors.phone}
className="bg-white border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
className="bg-bg-elevated border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
/>
</div>
</div>
@@ -450,7 +450,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
onChange={(e) => handleChange('email', e.target.value)}
onBlur={(e) => handleBlur('email', e.target.value)}
error={errors.email}
className="bg-white border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
className="bg-bg-elevated border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
/>
<Input
name="subject"
@@ -463,7 +463,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
onChange={(e) => handleChange('subject', e.target.value)}
onBlur={(e) => handleBlur('subject', e.target.value)}
error={errors.subject}
className="bg-white border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
className="bg-bg-elevated border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
/>
<Textarea
name="message"
@@ -477,7 +477,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
onChange={(e) => handleChange('message', e.target.value)}
onBlur={(e) => handleBlur('message', e.target.value)}
error={errors.message}
className="bg-white border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
className="bg-bg-elevated border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
/>
<Button
type="submit"
@@ -507,7 +507,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
</section>
{/* CTA Section */}
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">
<motion.div
initial={{ opacity: 0, y: 30 }}
@@ -545,7 +545,7 @@ function ContactFormContent({ data }: { data: ContactData }) {
function EmptyState() {
return (
<div className="min-h-screen bg-white flex items-center justify-center">
<div className="min-h-screen bg-bg-primary flex items-center justify-center">
<div className="text-text-muted text-lg">内容暂未发布</div>
</div>
);
@@ -562,7 +562,7 @@ export default function ContactContentV3({ data }: { data: Record<string, unknow
return (
<Suspense
fallback={
<div className="min-h-screen bg-white flex items-center justify-center">
<div className="min-h-screen bg-bg-primary flex items-center justify-center">
<div className="animate-pulse text-text-muted">加载中...</div>
</div>
}
@@ -28,7 +28,7 @@ export function MethodologyContent({ data }: MethodologyContentProps) {
return (
<main>
{/* Hero:品牌化定位,轻量 CTA */}
<section className="relative min-h-[60vh] flex items-center overflow-hidden bg-white">
<section className="relative min-h-[60vh] flex items-center overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-24 sm:py-28 lg:py-32">
<div className="max-w-4xl">
<motion.div
@@ -84,7 +84,7 @@ export function MethodologyContent({ data }: MethodologyContentProps) {
{phases.map((phase, idx) => (
<motion.div
key={phase.title || idx}
className="relative overflow-hidden bg-white border border-border-primary transition-all duration-300 hover:-translate-y-1 hover:shadow-lg"
className="relative overflow-hidden bg-bg-elevated border border-border-primary transition-all duration-300 hover:-translate-y-1 hover:shadow-lg"
>
<div className="p-7 md:p-8">
<div className="w-11 h-11 rounded-full flex items-center justify-center mb-5 text-base font-bold bg-brand/10 text-brand">
@@ -131,7 +131,7 @@ export function MethodologyContent({ data }: MethodologyContentProps) {
</StaggerReveal>
</div>
) : (
<div className="bg-white border border-border-primary p-10 sm:p-16 text-center">
<div className="bg-bg-elevated border border-border-primary p-10 sm:p-16 text-center">
<h3 className="text-xl font-bold text-ink mb-3">方法论内容即将发布</h3>
<p className="text-text-secondary max-w-xl mx-auto leading-relaxed">
我们正在沉淀与首批客户共创过程中的方法论实践,完成后将在此呈现完整框架。
@@ -142,7 +142,7 @@ export function MethodologyContent({ data }: MethodologyContentProps) {
</section>
{/* CTA:轻量导向服务与联系 */}
<section className="relative py-20 sm:py-28 lg:py-32 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 lg:py-32 overflow-hidden bg-bg-primary">
<div className="absolute top-0 left-0 right-0 h-px bg-border-primary" />
<div className="max-w-container mx-auto px-6 lg:px-10">
<ScrollReveal className="text-center max-w-3xl mx-auto">
+5 -5
View File
@@ -27,7 +27,7 @@ function NewsCard({ newsItem, index }: { newsItem: NewsItem; index: number }) {
>
<StaticLink
href={`/news/${newsItem.id}`}
className="group relative block border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 hover:-translate-y-2 overflow-hidden h-full"
className="group relative block border border-border-primary hover:border-brand/30 bg-bg-elevated transition-all duration-150 hover:-translate-y-2 overflow-hidden h-full"
>
<div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-150 origin-top" />
@@ -119,9 +119,9 @@ export default function NewsContentV3({ news, pageCopy }: { news?: NewsItem[]; p
};
return (
<div className="min-h-screen bg-white text-ink overflow-x-hidden">
<div className="min-h-screen bg-bg-primary text-ink overflow-x-hidden">
{/* Hero Section */}
<section className="relative min-h-[75vh] flex items-center overflow-hidden bg-white">
<section className="relative min-h-[75vh] flex items-center overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-28 sm:py-36 md:py-44 lg:py-56">
<div className="max-w-4xl">
<ScrollReveal>
@@ -173,7 +173,7 @@ export default function NewsContentV3({ news, pageCopy }: { news?: NewsItem[]; p
placeholder="搜索新闻..."
value={searchQuery}
onChange={handleSearchChange}
className="pl-12 h-12 bg-white border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
className="pl-12 h-12 bg-bg-elevated border-border-primary text-ink placeholder:text-text-muted focus:border-brand/50"
/>
</div>
</ScrollReveal>
@@ -237,7 +237,7 @@ export default function NewsContentV3({ news, pageCopy }: { news?: NewsItem[]; p
</section>
{/* CTA Section */}
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-bg-primary">
<div className="absolute top-0 left-0 right-0 h-px bg-border-primary" />
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">
@@ -19,7 +19,7 @@ function RelatedNewsCard({ news, index }: { news: NewsItem; index: number }) {
>
<a
href={`/news/${news.id}`}
className="group relative block border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 hover:-translate-y-2 overflow-hidden h-full"
className="group relative block border border-border-primary hover:border-brand/30 bg-bg-elevated transition-all duration-150 hover:-translate-y-2 overflow-hidden h-full"
>
<div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-150 origin-top" />
@@ -59,7 +59,7 @@ interface NewsDetailContentV3Props {
export default function NewsDetailContentV3({ news, relatedNews = [] }: NewsDetailContentV3Props) {
return (
<div className="min-h-screen bg-white text-ink overflow-x-hidden">
<div className="min-h-screen bg-bg-primary text-ink overflow-x-hidden">
{/* Hero Section */}
<section className="relative pt-28 sm:pt-32 md:pt-40 pb-16 sm:pb-20 md:pb-24 overflow-hidden">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">
@@ -95,7 +95,7 @@ export default function NewsDetailContentV3({ news, relatedNews = [] }: NewsDeta
</section>
{/* Article Body Section */}
<section className="relative pb-20 sm:pb-28 md:pb-36 overflow-hidden bg-white">
<section className="relative pb-20 sm:pb-28 md:pb-36 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10">
<ScrollReveal className="max-w-4xl mx-auto">
{/* Featured Image */}
@@ -125,7 +125,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
const certifications = (data.certifications ?? []) as Array<{ name: string; issuer: string; link?: string }>;
return (
<div className="min-h-screen bg-white text-ink overflow-x-hidden">
<div className="min-h-screen bg-bg-primary text-ink overflow-x-hidden">
{/* Hero Section */}
<section className="relative pt-32 pb-32 lg:pt-40 lg:pb-40 overflow-hidden">
<div className="max-w-container mx-auto px-6 lg:px-10 relative z-10">
@@ -183,7 +183,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{PAIN_POINTS.map((item, index) => (
<div
key={index}
className="group relative border border-border-primary hover:border-brand/30 bg-white p-8 lg:p-10 transition-all duration-150 hover:-translate-y-1 overflow-hidden"
className="group relative border border-border-primary hover:border-brand/30 bg-bg-elevated p-8 lg:p-10 transition-all duration-150 hover:-translate-y-1 overflow-hidden"
>
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
@@ -200,7 +200,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
</section>
{/* Benefits Section */}
<section className="relative py-24 lg:py-32 overflow-hidden bg-white">
<section className="relative py-24 lg:py-32 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-6 lg:px-10 relative z-10">
<ScrollReveal className="max-w-3xl mb-16">
<SectionLabel>Upgrade Benefits</SectionLabel>
@@ -220,7 +220,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{UPGRADE_BENEFITS.map((item, index) => (
<div
key={index}
className="text-center p-8 border border-border-primary bg-white hover:border-brand/30 hover:bg-bg-secondary transition-all duration-150 group"
className="text-center p-8 border border-border-primary bg-bg-elevated hover:border-brand/30 hover:bg-bg-secondary transition-all duration-150 group"
>
<div className="w-12 h-12 mx-auto mb-6 bg-brand/10 border border-brand/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<item.icon className="w-6 h-6 text-brand" />
@@ -254,7 +254,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{FEATURE_MODULES.map((item, index) => (
<div
key={index}
className="group relative border border-border-primary hover:border-brand/30 bg-white p-8 transition-all duration-150 hover:-translate-y-1 overflow-hidden"
className="group relative border border-border-primary hover:border-brand/30 bg-bg-elevated p-8 transition-all duration-150 hover:-translate-y-1 overflow-hidden"
>
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
@@ -274,7 +274,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
</section>
{/* Process Section */}
<section className="relative py-24 lg:py-32 overflow-hidden bg-white">
<section className="relative py-24 lg:py-32 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-6 lg:px-10 relative z-10">
<ScrollReveal className="max-w-3xl mb-16">
<SectionLabel>Upgrade Process</SectionLabel>
@@ -331,7 +331,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
</section>
{/* Certifications Section */}
<section className="relative py-24 lg:py-32 overflow-hidden bg-white">
<section className="relative py-24 lg:py-32 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-6 lg:px-10 relative z-10">
<ScrollReveal className="max-w-3xl mb-16 text-center mx-auto">
<SectionLabel className="justify-center">
@@ -357,7 +357,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{certifications.map((cert, index) => (
<div
key={index}
className="group text-center p-8 border border-border-primary bg-white hover:border-brand/30 hover:bg-bg-secondary transition-all duration-150 min-w-[200px]"
className="group text-center p-8 border border-border-primary bg-bg-elevated hover:border-brand/30 hover:bg-bg-secondary transition-all duration-150 min-w-[200px]"
>
<div className="w-16 h-16 mx-auto mb-4 bg-brand/10 border border-brand/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<Shield className="w-8 h-8 text-brand" />
@@ -90,7 +90,7 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) {
const ctaSecondary = (pageCopy?.productsCtaSecondary as string) || '查看行业方案';
return (
<section className="relative min-h-[85vh] flex items-center overflow-hidden bg-white">
<section className="relative min-h-[85vh] flex items-center overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-24 sm:py-28 md:py-32 lg:py-40">
<div className="max-w-4xl">
<motion.h1
@@ -176,7 +176,7 @@ function ProductCard({ product, size = 'small' }: { product: Product; size?: 'la
>
<StaticLink
href={linkHref}
className="group relative block overflow-hidden border border-border-primary hover:border-border-secondary bg-white hover:bg-bg-secondary hover:-translate-y-1 hover:shadow-lg transition-all duration-300 flex flex-col h-full after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-full after:origin-left after:scale-x-0 after:bg-brand after:transition-transform after:duration-300 group-hover:after:scale-x-100"
className="group relative block overflow-hidden border border-border-primary hover:border-border-secondary bg-bg-elevated hover:bg-bg-secondary hover:-translate-y-1 hover:shadow-lg transition-all duration-300 flex flex-col h-full after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-full after:origin-left after:scale-x-0 after:bg-brand after:transition-transform after:duration-300 group-hover:after:scale-x-100"
{...(isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
>
<div className={cn('p-6 sm:p-7 lg:p-8 flex flex-col flex-1', isLarge && 'lg:p-10')}>
@@ -233,7 +233,7 @@ function BundleOverview({ products, pageCopy }: { products: Product[]; pageCopy?
const bundleDescription = (pageCopy?.productsBundleDescription as string) || '企业套装以组合形式解决核心管理问题,专业产品聚焦独立场景深度赋能。';
return (
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-bg-primary">
<div className="absolute top-0 left-0 right-0 h-px bg-border-primary" />
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10">
@@ -265,7 +265,7 @@ function BundleOverview({ products, pageCopy }: { products: Product[]; pageCopy?
<div className="space-y-3">
{enterpriseProducts.map((p) => (
<div key={p.id} className="flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-white border border-border-primary flex items-center justify-center text-text-secondary">
<div className="w-8 h-8 rounded-lg bg-bg-elevated border border-border-primary flex items-center justify-center text-text-secondary">
{PRODUCT_ICONS[p.title] || <Package className="w-4 h-4" />}
</div>
<div>
@@ -277,7 +277,7 @@ function BundleOverview({ products, pageCopy }: { products: Product[]; pageCopy?
</div>
</div>
<div className="bg-white p-8 sm:p-10 lg:p-12">
<div className="bg-bg-elevated p-8 sm:p-10 lg:p-12">
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-accent-blue-soft flex items-center justify-center text-accent-blue">
<Cpu className="w-5 h-5" />
@@ -353,7 +353,7 @@ function SpecializedProductsSection({ products }: { products: Product[] }) {
if (specializedProducts.length === 0) return null;
return (
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-bg-primary">
<div className="absolute top-0 left-0 right-0 h-px bg-border-primary" />
<div className="absolute bottom-0 left-0 right-0 h-px bg-border-primary" />
@@ -425,7 +425,7 @@ function SuiteCombosSection({ products }: { products: Product[] }) {
<StaticLink
key={idx}
href="/solutions"
className="group p-10 lg:p-12 bg-white hover:bg-bg-secondary transition-all duration-150 overflow-hidden"
className="group p-10 lg:p-12 bg-bg-elevated hover:bg-bg-secondary transition-all duration-150 overflow-hidden"
>
<div>
<div className="flex items-center gap-4 mb-6">
@@ -475,7 +475,7 @@ function CTASection({ pageCopy }: { pageCopy?: PageCopy | null }) {
const ctaTitleLines = ((pageCopy?.productsCtaTitle as string) || '以自研产品\n支撑数字化落地').split('\n');
const ctaDescription = (pageCopy?.productsCtaDescription as string) || '从企业套装到专业产品,以自研能力与专业服务,陪伴企业完成数字化升级。';
return (
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-bg-primary">
<div className="absolute top-0 left-0 right-0 h-px bg-border-primary" />
<div className="absolute bottom-0 left-0 right-0 h-px bg-border-primary" />
@@ -40,7 +40,7 @@ function DetailHero({ solution }: DetailHeroProps) {
const industryIcon = INDUSTRY_ICONS[solution.industry] || <Factory className="w-8 h-8" />;
return (
<section className="relative min-h-[80vh] flex items-center overflow-hidden bg-white pt-24">
<section className="relative min-h-[80vh] flex items-center overflow-hidden bg-bg-primary pt-24">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-20 sm:py-24 md:py-28 lg:py-32">
<motion.div
initial={{ opacity: 0, y: 30 }}
@@ -122,7 +122,7 @@ function ChallengesSection({ challenges }: ChallengesSectionProps) {
{challenges.map((challenge, idx) => (
<div
key={idx}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-150"
className="group relative p-8 bg-bg-elevated hover:bg-bg-secondary transition-all duration-150"
>
<div className="absolute left-0 top-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-150 origin-top" />
<div className="flex items-start gap-6">
@@ -147,7 +147,7 @@ interface SolutionsSectionProps {
function SolutionsSection({ solutions }: SolutionsSectionProps) {
return (
<section className="relative py-20 sm:py-28 md:py-32 lg:py-40 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-32 lg:py-40 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10">
<ScrollReveal className="text-center max-w-3xl mx-auto mb-16 sm:mb-20 md:mb-20">
<SectionLabel className="justify-center">
@@ -169,7 +169,7 @@ function SolutionsSection({ solutions }: SolutionsSectionProps) {
{solutions.map((solution, idx) => (
<div
key={idx}
className="group relative p-10 bg-white hover:bg-bg-secondary transition-all duration-150"
className="group relative p-10 bg-bg-elevated hover:bg-bg-secondary transition-all duration-150"
>
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
@@ -226,7 +226,7 @@ function ValuePropositionSection({ valueProposition }: ValuePropositionProps) {
return (
<div
key={idx}
className="group relative p-10 text-center bg-white hover:bg-bg-secondary transition-all duration-150"
className="group relative p-10 text-center bg-bg-elevated hover:bg-bg-secondary transition-all duration-150"
>
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-1/3 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-center" />
@@ -257,7 +257,7 @@ function SuiteCombinationSection({ suiteCombination, products }: SuiteCombinatio
.filter(Boolean);
return (
<section className="relative py-20 sm:py-28 md:py-32 lg:py-40 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-32 lg:py-40 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10">
<ScrollReveal className="text-center max-w-3xl mx-auto mb-16 sm:mb-20 md:mb-20">
<SectionLabel className="justify-center">
@@ -279,7 +279,7 @@ function SuiteCombinationSection({ suiteCombination, products }: SuiteCombinatio
{primaryProducts.map((product) => product && (
<div
key={product.id}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-150 text-center lg:col-span-2"
className="group relative p-8 bg-bg-elevated hover:bg-bg-secondary transition-all duration-150 text-center lg:col-span-2"
>
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
@@ -296,7 +296,7 @@ function SuiteCombinationSection({ suiteCombination, products }: SuiteCombinatio
))}
</StaggerReveal>
<ScrollReveal delay={0.2} className="flex items-center justify-center lg:col-span-1 bg-white">
<ScrollReveal delay={0.2} className="flex items-center justify-center lg:col-span-1 bg-bg-elevated">
<div className="w-20 h-20 rounded-full bg-brand-soft/30 border-2 border-brand/30 flex items-center justify-center text-brand font-bold text-xl">
+
</div>
@@ -29,7 +29,7 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) {
const ctaSecondary = (pageCopy?.solutionsCtaSecondary as string) || '浏览产品';
return (
<section className="relative min-h-[85vh] flex items-center overflow-hidden bg-white">
<section className="relative min-h-[85vh] flex items-center overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-28 sm:py-36 md:py-44 lg:py-56">
<div className="max-w-4xl">
<motion.h1
@@ -125,7 +125,7 @@ function SolutionCard({ solution, index, products, featured = false }: { solutio
<motion.div
data-testid={`solution-card-${solution.id}`}
data-featured={featured ? 'true' : 'false'}
className={featured ? 'md:col-span-2 bg-white' : 'md:col-span-1 bg-white'}
className={featured ? 'md:col-span-2 bg-bg-elevated' : 'md:col-span-1 bg-bg-elevated'}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }}
@@ -133,7 +133,7 @@ function SolutionCard({ solution, index, products, featured = false }: { solutio
>
<StaticLink
href={`/solutions/${solution.id}`}
className="group relative block transition-all duration-300 hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 bg-white border border-border-primary hover:border-brand/20 after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-full after:origin-left after:scale-x-0 after:bg-brand after:transition-transform after:duration-300 group-hover:after:scale-x-100"
className="group relative block transition-all duration-300 hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 bg-bg-elevated border border-border-primary hover:border-brand/20 after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-full after:origin-left after:scale-x-0 after:bg-brand after:transition-transform after:duration-300 group-hover:after:scale-x-100"
>
<div className="relative z-10 p-8 sm:p-10 md:p-12">
<div className="flex items-center gap-3 mb-6">
@@ -218,7 +218,7 @@ function CTASection({ pageCopy }: { pageCopy?: PageCopy | null }) {
const ctaTitle = (pageCopy?.solutionsCtaTitle as string) || '告诉我们您的行业场景';
const ctaDescription = (pageCopy?.solutionsCtaDescription as string) || '无论您处于哪个行业、哪个数字化阶段,我们都能为您推荐最合适的产品组合和服务包。';
return (
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-white">
<section className="relative py-20 sm:py-28 md:py-36 lg:py-44 overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">
<motion.div
initial={{ opacity: 0, y: 30 }}
@@ -251,7 +251,7 @@ export default function SolutionsContentV3({ solutions: solutionsProp, products,
const solutions = solutionsProp ?? [];
return (
<main className="bg-white text-ink">
<main className="bg-bg-primary text-ink">
<HeroSection pageCopy={pageCopy} />
<SolutionsGridSection solutions={solutions} products={products} pageCopy={pageCopy} />
<CTASection pageCopy={pageCopy} />
+7 -7
View File
@@ -65,7 +65,7 @@ function StrengthCard({ strength, index }: { strength: StrengthData; index: numb
return (
<motion.div
key={strength.title}
className="group relative border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 overflow-hidden"
className="group relative border border-border-primary hover:border-brand/30 bg-bg-elevated transition-all duration-150 overflow-hidden"
initial={shouldReduceMotion ? {} : { opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }}
@@ -100,7 +100,7 @@ function CultureCard({ culture, index }: { culture: CultureData; index: number }
return (
<motion.div
key={culture.title}
className="group relative border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 text-center overflow-hidden"
className="group relative border border-border-primary hover:border-brand/30 bg-bg-elevated transition-all duration-150 text-center overflow-hidden"
initial={shouldReduceMotion ? {} : { opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }}
@@ -136,16 +136,16 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
// Empty state when no CMS data is available
if (!data || Object.keys(data).length === 0) {
return (
<div className="min-h-screen bg-white flex items-center justify-center">
<div className="min-h-screen bg-bg-primary flex items-center justify-center">
<div className="text-text-muted text-lg">内容暂未发布</div>
</div>
);
}
return (
<div className="min-h-screen bg-white text-ink overflow-x-hidden">
<div className="min-h-screen bg-bg-primary text-ink overflow-x-hidden">
{/* ============ L1: Hero 团队情感入口 ============ */}
<section className="relative min-h-[85vh] flex items-center overflow-hidden bg-white">
<section className="relative min-h-[85vh] flex items-center overflow-hidden bg-bg-primary">
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10 w-full py-28 sm:py-36 md:py-44 lg:py-56">
<div className="max-w-4xl">
{/* 眉标 Badge */}
@@ -270,7 +270,7 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">
<ScrollReveal>
<div className="max-w-4xl mx-auto p-8 sm:p-10 lg:p-16 border border-border-primary bg-white">
<div className="max-w-4xl mx-auto p-8 sm:p-10 lg:p-16 border border-border-primary bg-bg-elevated">
<div className="flex items-center gap-3 mb-8 justify-center">
<div className="w-10 h-px bg-brand" />
<span className="text-[13px] tracking-[0.15em] font-medium text-brand">
@@ -296,7 +296,7 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
{/* ============ L3: 团队文化信任证明 ============ */}
{culture.length > 0 && (
<section className="relative py-24 sm:py-32 md:py-40 overflow-hidden bg-white">
<section className="relative py-24 sm:py-32 md:py-40 overflow-hidden bg-bg-primary">
<div className="absolute top-0 left-0 right-0 h-px bg-border-primary" />
<div className="max-w-container mx-auto px-4 sm:px-6 lg:px-10 relative z-10">