refactor(project): 全面清理项目代码并重命名项目 #19

Merged
zhangxiang merged 9 commits from dev into main 2026-04-29 21:49:02 +08:00
86 changed files with 688 additions and 634 deletions
Showing only changes of commit 1f591fe2b4 - Show all commits
+27 -7
View File
@@ -6,7 +6,9 @@ import { useRef, useMemo } from 'react';
import { COMPANY_INFO, STATS } from '@/lib/constants';
import { Card, CardContent } from '@/components/ui/card';
import { PageHeader } from '@/components/ui/page-header';
import { Lightbulb, Users, Target, MapPin, Mail } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { StaticLink } from '@/components/ui/static-link';
import { Lightbulb, Users, Target, MapPin, Mail, ArrowRight } from 'lucide-react';
export function AboutClient() {
const contentRef = useRef(null);
@@ -136,7 +138,7 @@ export function AboutClient() {
{STATS.map((stat, idx) => (
<Card key={idx} className="text-center border-[#E5E5E5]">
<CardContent className="pt-6">
<div className="text-3xl sm:text-4xl font-bold text-[#C41E3A] mb-2">{stat.value}</div>
<div className="text-3xl sm:text-4xl font-bold text-[var(--color-brand-primary)] mb-2">{stat.value}</div>
<div className="text-sm text-[#5C5C5C]">{stat.label}</div>
</CardContent>
</Card>
@@ -159,7 +161,7 @@ export function AboutClient() {
transition={{ duration: 0.5, delay: 0.4 + idx * 0.1 }}
className="flex items-start gap-4 p-6 bg-[#FFFBF5] rounded-xl border border-[#E5E5E5] hover:border-[#1C1C1C] transition-all duration-300"
>
<div className="w-12 h-12 rounded-lg bg-[#C41E3A] flex items-center justify-center shrink-0">
<div className="w-12 h-12 rounded-lg bg-[var(--color-brand-primary)] flex items-center justify-center shrink-0">
<value.icon className="w-6 h-6 text-white" />
</div>
<div>
@@ -188,7 +190,7 @@ export function AboutClient() {
className="flex flex-col md:flex-row md:items-start gap-4 p-6 bg-[#FFFBF5] rounded-xl border border-[#E5E5E5]"
>
<div className="md:w-32 shrink-0">
<span className="text-sm font-medium text-[#C41E3A]">{milestone.date}</span>
<span className="text-sm font-medium text-[var(--color-brand-primary)]">{milestone.date}</span>
</div>
<div className="flex-1">
<h3 className="font-semibold text-[#1A1A2E] mb-1">{milestone.title}</h3>
@@ -209,7 +211,7 @@ export function AboutClient() {
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="flex items-center gap-3">
<div className="p-2 bg-white rounded-lg">
<MapPin className="w-5 h-5 text-[#C41E3A]" />
<MapPin className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div>
<p className="text-sm text-[#5C5C5C]"></p>
@@ -218,16 +220,34 @@ export function AboutClient() {
</div>
<div className="flex items-center gap-3">
<div className="p-2 bg-white rounded-lg">
<Mail className="w-5 h-5 text-[#C41E3A]" />
<Mail className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div>
<p className="text-sm text-[#5C5C5C]"></p>
<p className="text-sm font-medium text-[#1C1C1C]">{COMPANY_INFO.email}</p>
<a href={`mailto:${COMPANY_INFO.email}`} className="text-sm font-medium text-[#1C1C1C] hover:text-[var(--color-brand-primary)] transition-colors">
{COMPANY_INFO.email}
</a>
</div>
</div>
</div>
</motion.div>
{/* Bottom CTA */}
<div className="mt-16 text-center py-16 bg-[#F5F5F5] rounded-2xl">
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
</h3>
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
</p>
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
<StaticLink href="/contact">
<ArrowRight className="ml-2 w-4 h-4" />
</StaticLink>
</Button>
</div>
</motion.div>
</div>
</div>
+19 -19
View File
@@ -84,7 +84,7 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
<div className="container-wide relative z-10 pt-32 pb-20">
<BackButton />
<div className="max-w-4xl mt-8">
<Badge className="mb-4 bg-[#C41E3A]/10 text-[#C41E3A] hover:bg-[#C41E3A]/20">
<Badge className="mb-4 bg-[var(--color-brand-primary)]/10 text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)]/20">
{caseItem.category}
</Badge>
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-semibold text-[#1C1C1C] mb-2">
@@ -105,9 +105,9 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
>
<div className="lg:col-span-2 space-y-12">
{/* 客户遇到的成长瓶颈 */}
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
<MessageCircle className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
@@ -120,9 +120,9 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</section>
{/* 我们如何智连未来 */}
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
<Target className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
@@ -138,9 +138,9 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
{/* 共同成长的故事 */}
{caseItem.keyMoments && caseItem.keyMoments.length > 0 && (
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
<Clock className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
@@ -154,7 +154,7 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
className="p-4 bg-white rounded-lg border border-[#E5E5E5]"
>
<div className="flex items-start gap-3">
<Quote className="w-5 h-5 text-[#C41E3A] flex-shrink-0 mt-0.5" />
<Quote className="w-5 h-5 text-[var(--color-brand-primary)] flex-shrink-0 mt-0.5" />
<div>
<h4 className="font-semibold text-[#1C1C1C] mb-2">
{moment.title}
@@ -172,9 +172,9 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
{/* 今天,他们走到了哪里 */}
{caseItem.results && caseItem.results.length > 0 && (
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
<Award className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
@@ -185,10 +185,10 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
{caseItem.results.map((result, index) => (
<div
key={index}
className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors"
className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[var(--color-brand-primary)] transition-colors"
>
<TrendingUp className="w-8 h-8 text-[#C41E3A] mb-3" />
<div className="text-2xl font-semibold text-[#C41E3A] mb-1">
<TrendingUp className="w-8 h-8 text-[var(--color-brand-primary)] mb-3" />
<div className="text-2xl font-semibold text-[var(--color-brand-primary)] mb-1">
{result.value}
</div>
<div className="text-sm text-[#737373]">
@@ -202,9 +202,9 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
{/* 客户证言精选 */}
{caseItem.testimonial && (
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
<Quote className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
@@ -212,12 +212,12 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</h2>
</div>
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5]">
<Quote className="w-8 h-8 text-[#C41E3A] mb-4" />
<Quote className="w-8 h-8 text-[var(--color-brand-primary)] mb-4" />
<p className="text-lg text-[#1C1C1C] leading-relaxed mb-4">
{caseItem.testimonial.quote}
</p>
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-[#C41E3A] rounded-full flex items-center justify-center">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center">
<span className="text-white font-semibold"></span>
</div>
<div>
@@ -266,13 +266,13 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</dl>
</div>
<div className="p-6 bg-gradient-to-br from-[#C41E3A] to-[#8B1429] rounded-lg text-white">
<div className="p-6 bg-gradient-to-br from-[var(--color-brand-primary)] to-[#8B1429] rounded-lg text-white">
<h3 className="text-lg font-semibold mb-2"></h3>
<p className="text-sm text-white/80 mb-4">
</p>
<Button
className="w-full bg-white text-[#C41E3A] hover:bg-white/90"
className="w-full bg-white text-[var(--color-brand-primary)] hover:bg-white/90"
asChild
>
<StaticLink href="/contact"></StaticLink>
+21 -59
View File
@@ -7,9 +7,10 @@ import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { PageHeader } from '@/components/ui/page-header';
import { Search, ArrowLeft, Building2, Calendar, TrendingUp, ChevronLeft, ChevronRight, Filter } from 'lucide-react';
import { Search, ArrowLeft, Building2, Calendar, TrendingUp, Filter, SearchX } from 'lucide-react';
import { StaticLink } from '@/components/ui/static-link';
import { motion } from 'framer-motion';
import { Pagination } from '@/components/ui/pagination';
const industries = ['全部', ...Array.from(new Set(CASES.map((c) => c.industry)))];
const ITEMS_PER_PAGE = 6;
@@ -40,7 +41,6 @@ export default function CasesPage() {
const handlePageChange = (page: number) => {
setCurrentPage(page);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleIndustryChange = (industry: string) => {
@@ -60,7 +60,7 @@ export default function CasesPage() {
description="我们与优秀的企业同行,共同成长,共创未来"
/>
<div className="container-wide relative z-10 py-16" ref={contentRef}>
<div className="container-wide relative z-10 py-16" ref={contentRef} id="page-content">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -81,7 +81,7 @@ export default function CasesPage() {
onClick={() => handleIndustryChange(industry)}
className={
selectedIndustry === industry
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
? 'bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white'
: ''
}
>
@@ -106,7 +106,16 @@ export default function CasesPage() {
{paginatedCases.length === 0 ? (
<div className="text-center py-20">
<p className="text-xl text-[#5C5C5C]"></p>
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-[#F5F5F5] flex items-center justify-center">
<SearchX className="w-8 h-8 text-[#5C5C5C]" />
</div>
<p className="text-lg text-[#5C5C5C] mb-4"></p>
<button
onClick={() => { setSearchQuery(''); setSelectedIndustry('全部'); setCurrentPage(1); }}
className="text-sm text-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary-hover)] font-medium transition-colors"
>
</button>
</div>
) : (
<>
@@ -124,7 +133,7 @@ export default function CasesPage() {
>
<div className="relative h-48 bg-gradient-to-br from-[#F5F5F5] to-[#E5E5E5] overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center">
<Building2 className="w-24 h-24 text-[#C41E3A]/20 group-hover:scale-110 transition-transform duration-300" />
<Building2 className="w-24 h-24 text-[var(--color-brand-primary)]/20 group-hover:scale-110 transition-transform duration-300" />
</div>
<div className="absolute top-4 right-4">
<Badge className="bg-white/90 text-[#1C1C1C] hover:bg-white">
@@ -135,11 +144,11 @@ export default function CasesPage() {
<div className="p-6">
<div className="flex items-center gap-2 mb-3">
<Building2 className="w-5 h-5 text-[#C41E3A]" />
<Building2 className="w-5 h-5 text-[var(--color-brand-primary)]" />
<span className="font-semibold text-[#1C1C1C]">{caseItem.client}</span>
</div>
<h3 className="text-xl font-bold text-[#1C1C1C] mb-3 group-hover:text-[#C41E3A] transition-colors">
<h3 className="text-xl font-bold text-[#1C1C1C] mb-3 group-hover:text-[var(--color-brand-primary)] transition-colors">
{caseItem.title}
</h3>
@@ -160,7 +169,7 @@ export default function CasesPage() {
{caseItem.description}
</p>
<div className="flex items-center text-[#C41E3A] font-medium group-hover:translate-x-2 transition-transform">
<div className="flex items-center text-[var(--color-brand-primary)] font-medium group-hover:translate-x-2 transition-transform">
<ArrowLeft className="w-4 h-4 ml-2 rotate-180" />
</div>
@@ -170,43 +179,7 @@ export default function CasesPage() {
))}
</div>
{totalPages > 1 && (
<div className="flex justify-center items-center gap-2 mt-8">
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
<ChevronLeft className="w-4 h-4" />
</Button>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<Button
key={page}
variant={currentPage === page ? 'default' : 'outline'}
size="icon"
onClick={() => handlePageChange(page)}
className={
currentPage === page
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
: ''
}
>
{page}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
<Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={handlePageChange} scrollTargetId="page-content" />
<div className="text-center mt-4 text-[#5C5C5C] text-sm">
{paginatedCases.length} {filteredCases.length}
@@ -229,19 +202,9 @@ export default function CasesPage() {
<p className="text-lg text-[#5C5C5C] mb-8 max-w-2xl mx-auto">
</p>
<div className="flex justify-center gap-4">
<Button
<Button
size="lg"
variant="outline"
asChild
>
<StaticLink href="/contact">
</StaticLink>
</Button>
<Button
size="lg"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
asChild
>
<StaticLink href="/contact">
@@ -249,7 +212,6 @@ export default function CasesPage() {
<ArrowLeft className="ml-2 w-4 h-4 rotate-180" />
</StaticLink>
</Button>
</div>
</div>
</motion.div>
</div>
+13 -13
View File
@@ -162,7 +162,7 @@ function ContactFormContent() {
/>
)}
<section className="section-padding relative overflow-hidden" ref={sectionRef}>
<section className="py-24 relative overflow-hidden" ref={sectionRef}>
<div className="absolute inset-0 pointer-events-none">
<div className="absolute inset-0 bg-gradient-radial from-[rgba(79,70,229,0.03)] via-transparent to-transparent" />
</div>
@@ -175,11 +175,11 @@ function ContactFormContent() {
`}
>
<div className="flex items-center gap-3 mb-4">
<div className="w-8 h-px bg-linear-to-r from-[#1C1C1C] to-[#C41E3A]" />
<div className="w-8 h-px bg-linear-to-r from-[#1C1C1C] to-[var(--color-brand-primary)]" />
<span className="text-sm text-[#5C5C5C] tracking-wide" data-testid="page-badge"></span>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h1>
<p className="mt-4 text-[#5C5C5C] max-w-2xl" data-testid="page-description">
@@ -198,19 +198,19 @@ function ContactFormContent() {
<h2 className="text-lg font-semibold text-[#1C1C1C] mb-6"></h2>
<div className="space-y-4" data-testid="contact-info">
<div className="flex items-start gap-4 group" data-testid="email-info">
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<Mail className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-sm text-[#5C5C5C] mb-1"></p>
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[#C41E3A] transition-colors duration-200" data-testid="email-link">
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[var(--color-brand-primary)] transition-colors duration-200" data-testid="email-link">
{COMPANY_INFO.email}
</a>
</div>
</div>
<div className="flex items-start gap-4 group" data-testid="address-info">
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<MapPin className="w-5 h-5 text-white" />
</div>
<div>
@@ -223,33 +223,33 @@ function ContactFormContent() {
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]" data-testid="work-hours-card">
<div className="flex items-center gap-2 mb-3">
<Clock className="w-4 h-4 text-[#C41E3A]" />
<Clock className="w-4 h-4 text-[var(--color-brand-primary)]" />
<h2 className="text-sm font-medium text-[#1C1C1C]"></h2>
</div>
<div className="space-y-1">
<div className="flex justify-between text-sm" data-testid="work-hours-row">
<span className="text-[#5C5C5C]"></span>
<span className="text-[#C41E3A]">9:00 - 18:00</span>
<span className="text-[var(--color-brand-primary)]">9:00 - 18:00</span>
</div>
</div>
</div>
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]">
<div className="flex items-center gap-2 mb-3">
<HeadphonesIcon className="w-4 h-4 text-[#C41E3A]" />
<HeadphonesIcon className="w-4 h-4 text-[var(--color-brand-primary)]" />
<h2 className="text-sm font-medium text-[#1C1C1C]"></h2>
</div>
<div className="space-y-3">
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
<p className="text-sm text-[#5C5C5C]"> 2 </p>
</div>
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
<p className="text-sm text-[#5C5C5C]"></p>
</div>
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
<p className="text-sm text-[#5C5C5C]"></p>
</div>
</div>
@@ -268,7 +268,7 @@ function ContactFormContent() {
{isSubmitted ? (
<div className="text-center py-12 flex-1 flex items-center justify-center">
<div className="w-16 h-16 bg-[#C41E3A] rounded-full flex items-center justify-center mx-auto mb-4 animate-stamp-in">
<div className="w-16 h-16 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center mx-auto mb-4 animate-stamp-in">
<CheckCircle2 className="w-8 h-8 text-white" />
</div>
<h4 className="text-xl font-semibold text-[#1A1A2E] mb-2"></h4>
+6
View File
@@ -105,6 +105,8 @@ function HomeContent({ heroStats }: { heroStats: ReactNode }) {
{/* 墨韵分割线 */}
<div className="ink-divider" />
<ServicesSection />
{/* 墨韵分割线 */}
<div className="ink-divider" />
<HomeSolutionsSection />
{/* 墨韵分割线 */}
<div className="ink-divider" />
@@ -112,7 +114,11 @@ function HomeContent({ heroStats }: { heroStats: ReactNode }) {
{/* 墨韵分割线 */}
<div className="ink-divider" />
<AboutSection />
{/* 墨韵分割线 */}
<div className="ink-divider" />
<TeamSection />
{/* 墨韵分割线 */}
<div className="ink-divider" />
<NewsSection />
</main>
);
+1
View File
@@ -16,6 +16,7 @@ const breadcrumbMap: Record<string, { label: string; href: string }> = {
'/products': { label: '产品服务', href: '/products' },
'/solutions': { label: '行业方案', href: '/solutions' },
'/news': { label: '新闻动态', href: '/news' },
'/cases': { label: '案例展示', href: '/cases' },
'/contact': { label: '联系我们', href: '/contact' },
'/team': { label: '核心团队', href: '/team' },
};
@@ -28,7 +28,7 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
<div className="container-wide relative z-10 pt-32 pb-20">
<BackButton />
<div className="max-w-4xl">
<div className="inline-block px-4 py-2 bg-[#C41E3A]/10 rounded-full text-[#C41E3A] text-sm mb-6">
<div className="inline-block px-4 py-2 bg-[var(--color-brand-primary)]/10 rounded-full text-[var(--color-brand-primary)] text-sm mb-6">
{news.category}
</div>
<h1 className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
@@ -61,12 +61,12 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
/>
</div>
) : (
<div className="aspect-video bg-linear-to-br from-[#C41E3A]/10 to-[#1C1C1C]/10 rounded-lg mb-8 flex items-center justify-center">
<div className="aspect-video bg-linear-to-br from-[var(--color-brand-primary)]/10 to-[#1C1C1C]/10 rounded-lg mb-8 flex items-center justify-center">
<span className="text-6xl">📰</span>
</div>
)}
<p className="text-xl text-[#5C5C5C] leading-relaxed mb-8 border-l-4 border-[#C41E3A] pl-6">
<p className="text-xl text-[#5C5C5C] leading-relaxed mb-8 border-l-4 border-[var(--color-brand-primary)] pl-6">
{news.excerpt}
</p>
@@ -92,7 +92,7 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full bg-linear-to-br from-[#C41E3A]/10 to-[#1C1C1C]/10 flex items-center justify-center">
<div className="w-full h-full bg-linear-to-br from-[var(--color-brand-primary)]/10 to-[#1C1C1C]/10 flex items-center justify-center">
<span className="text-4xl">📰</span>
</div>
)}
@@ -100,7 +100,7 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
<Badge variant="secondary" className="mb-2">
{related.category}
</Badge>
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-2 line-clamp-2 group-hover:text-[#C41E3A] transition-colors">
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-2 line-clamp-2 group-hover:text-[var(--color-brand-primary)] transition-colors">
{related.title}
</h3>
<p className="text-sm text-[#5C5C5C] line-clamp-2">
@@ -120,7 +120,7 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
</StaticLink>
</Button>
<Button size="lg" className="bg-[#C41E3A] hover:bg-[#A01830] text-white" asChild>
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
<StaticLink href="/contact">
</StaticLink>
+34 -45
View File
@@ -8,9 +8,10 @@ import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { PageHeader } from '@/components/ui/page-header';
import { Search, Calendar, Filter, ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react';
import { Search, Calendar, Filter, ArrowRight, SearchX } from 'lucide-react';
import { StaticLink } from '@/components/ui/static-link';
import { motion } from 'framer-motion';
import { Pagination } from '@/components/ui/pagination';
const categories = ['全部', '公司新闻', '产品发布', '研发动态'];
const ITEMS_PER_PAGE = 9;
@@ -40,7 +41,6 @@ export default function NewsListPage() {
const handlePageChange = (page: number) => {
setCurrentPage(page);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleCategoryChange = (category: string) => {
@@ -60,7 +60,7 @@ export default function NewsListPage() {
description="了解睿新致远最新动态,把握行业发展脉搏"
/>
<div className="container-wide relative z-10 py-12" ref={contentRef}>
<div className="container-wide relative z-10 py-12" ref={contentRef} id="page-content">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
@@ -80,7 +80,7 @@ export default function NewsListPage() {
onClick={() => handleCategoryChange(category)}
className={
selectedCategory === category
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
? 'bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white'
: ''
}
>
@@ -105,7 +105,16 @@ export default function NewsListPage() {
{paginatedNews.length === 0 ? (
<div className="text-center py-20">
<p className="text-xl text-[#5C5C5C]"></p>
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-[#F5F5F5] flex items-center justify-center">
<SearchX className="w-8 h-8 text-[#5C5C5C]" />
</div>
<p className="text-lg text-[#5C5C5C] mb-4"></p>
<button
onClick={() => { setSearchQuery(''); setSelectedCategory('全部'); setCurrentPage(1); }}
className="text-sm text-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary-hover)] font-medium transition-colors"
>
</button>
</div>
) : (
<>
@@ -118,7 +127,7 @@ export default function NewsListPage() {
transition={{ duration: 0.5, delay: 0.2 + index * 0.1 }}
>
<StaticLink href={`/news/${newsItem.id}`}>
<Card className="h-full hover:shadow-lg transition-shadow cursor-pointer border-[#E5E5E5] hover:border-[#C41E3A]">
<Card className="h-full hover:shadow-lg transition-shadow cursor-pointer border-[#E5E5E5] hover:border-[var(--color-brand-primary)]">
<CardContent className="p-0">
{newsItem.image ? (
<div className="aspect-video bg-gray-100 overflow-hidden">
@@ -129,7 +138,7 @@ export default function NewsListPage() {
/>
</div>
) : (
<div className="aspect-video bg-gradient-to-br from-[#C41E3A]/10 to-[#1C1C1C]/10 flex items-center justify-center mb-4">
<div className="aspect-video bg-gradient-to-br from-[var(--color-brand-primary)]/10 to-[#1C1C1C]/10 flex items-center justify-center mb-4">
<span className="text-4xl">📰</span>
</div>
)}
@@ -147,7 +156,7 @@ export default function NewsListPage() {
<p className="text-[#5C5C5C] text-sm line-clamp-3 mb-4">
{newsItem.excerpt}
</p>
<div className="flex items-center text-[#C41E3A] text-sm font-medium group">
<div className="flex items-center text-[var(--color-brand-primary)] text-sm font-medium group">
<ArrowRight className="ml-1 w-4 h-4" />
</div>
@@ -159,49 +168,29 @@ export default function NewsListPage() {
))}
</div>
{totalPages > 1 && (
<div className="flex justify-center items-center gap-2 mt-8">
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
<ChevronLeft className="w-4 h-4" />
</Button>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<Button
key={page}
variant={currentPage === page ? 'default' : 'outline'}
size="icon"
onClick={() => handlePageChange(page)}
className={
currentPage === page
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
: ''
}
>
{page}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
<Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={handlePageChange} scrollTargetId="page-content" />
<div className="text-center mt-4 text-[#5C5C5C] text-sm">
{paginatedNews.length} {filteredNews.length}
</div>
</>
)}
{/* Bottom CTA */}
<div className="mt-12 text-center py-16 bg-[#F5F5F5] rounded-2xl">
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
</h3>
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
</p>
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
<StaticLink href="/contact">
<ArrowRight className="ml-2 w-4 h-4" />
</StaticLink>
</Button>
</div>
</div>
</div>
);
+22 -49
View File
@@ -9,10 +9,11 @@ import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { RippleButton } from '@/components/ui/ripple-button';
import { PageHeader } from '@/components/ui/page-header';
import { Search, ArrowLeft, Check, TrendingUp, ChevronLeft, ChevronRight, Filter } from 'lucide-react';
import { Search, ArrowLeft, Check, TrendingUp, Filter, SearchX } from 'lucide-react';
import { StaticLink } from '@/components/ui/static-link';
import { motion } from 'framer-motion';
import { InkCard } from '@/lib/animations';
import { Pagination } from '@/components/ui/pagination';
const categories = ['全部', '企业软件', '数据产品'];
const ITEMS_PER_PAGE = 6;
@@ -42,7 +43,6 @@ export default function ProductsPage() {
const handlePageChange = (page: number) => {
setCurrentPage(page);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleCategoryChange = (category: string) => {
@@ -62,7 +62,7 @@ export default function ProductsPage() {
description="自主研发的企业级产品,助力企业高效运营,实现数字化转型"
/>
<div className="container-wide relative z-10 py-16" ref={contentRef}>
<div className="container-wide relative z-10 py-16" ref={contentRef} id="page-content">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -83,7 +83,7 @@ export default function ProductsPage() {
onClick={() => handleCategoryChange(category)}
className={
selectedCategory === category
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
? 'bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white'
: ''
}
>
@@ -108,7 +108,16 @@ export default function ProductsPage() {
{paginatedProducts.length === 0 ? (
<div className="text-center py-20">
<p className="text-xl text-[#5C5C5C]"></p>
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-[#F5F5F5] flex items-center justify-center">
<SearchX className="w-8 h-8 text-[#5C5C5C]" />
</div>
<p className="text-lg text-[#5C5C5C] mb-4"></p>
<button
onClick={() => { setSearchQuery(''); setSelectedCategory('全部'); setCurrentPage(1); }}
className="text-sm text-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary-hover)] font-medium transition-colors"
>
</button>
</div>
) : (
<>
@@ -116,7 +125,7 @@ export default function ProductsPage() {
{paginatedProducts.map((product) => (
<InkCard
key={product.id}
className="group cursor-pointer rounded-xl border border-[#E5E5E5] bg-white p-0 overflow-hidden hover:border-[#C41E3A] transition-colors"
className="group cursor-pointer rounded-xl border border-[#E5E5E5] bg-white p-0 overflow-hidden hover:border-[var(--color-brand-primary)] transition-colors"
>
<StaticLink href={`/products/${product.id}`}>
<Card className="h-full border-0 shadow-none bg-transparent">
@@ -124,7 +133,7 @@ export default function ProductsPage() {
<Badge variant="secondary" className="w-fit mb-3">
{product.category}
</Badge>
<CardTitle className="group-hover:text-[#C41E3A] transition-colors">{product.title}</CardTitle>
<CardTitle className="group-hover:text-[var(--color-brand-primary)] transition-colors">{product.title}</CardTitle>
</CardHeader>
<CardContent className="flex-1 flex flex-col">
<CardDescription className="text-base leading-relaxed mb-4 flex-1">
@@ -139,7 +148,7 @@ export default function ProductsPage() {
key={idx}
className="inline-flex items-center text-xs px-2 py-1 bg-[#FAFAFA] text-[#3D3D3D] rounded border border-[#E5E5E5]"
>
<Check className="w-3 h-3 mr-1 text-[#C41E3A]" />
<Check className="w-3 h-3 mr-1 text-[var(--color-brand-primary)]" />
{feature}
</span>
))}
@@ -148,20 +157,20 @@ export default function ProductsPage() {
<div className="mb-4">
<p className="text-sm font-medium text-[#1C1C1C] mb-2 flex items-center">
<TrendingUp className="w-4 h-4 mr-1 text-[#C41E3A]" />
<TrendingUp className="w-4 h-4 mr-1 text-[var(--color-brand-primary)]" />
</p>
<ul className="space-y-1">
{product.benefits.map((benefit, idx) => (
<li key={idx} className="text-xs text-[#5C5C5C] flex items-start">
<span className="text-[#C41E3A] mr-1.5"></span>
<span className="text-[var(--color-brand-primary)] mr-1.5"></span>
{benefit}
</li>
))}
</ul>
</div>
<div className="w-full mt-auto px-4 py-2 text-center text-sm font-medium border border-[#E5E5E5] rounded-md group-hover:bg-[#C41E3A] group-hover:text-white group-hover:border-[#C41E3A] transition-colors">
<div className="w-full mt-auto px-4 py-2 text-center text-sm font-medium border border-[#E5E5E5] rounded-md group-hover:bg-[var(--color-brand-primary)] group-hover:text-white group-hover:border-[var(--color-brand-primary)] transition-colors">
<ArrowLeft className="ml-2 w-4 h-4 rotate-180 inline" />
</div>
@@ -172,43 +181,7 @@ export default function ProductsPage() {
))}
</div>
{totalPages > 1 && (
<div className="flex justify-center items-center gap-2 mt-8">
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
<ChevronLeft className="w-4 h-4" />
</Button>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<Button
key={page}
variant={currentPage === page ? 'default' : 'outline'}
size="icon"
onClick={() => handlePageChange(page)}
className={
currentPage === page
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
: ''
}
>
{page}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
<Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={handlePageChange} scrollTargetId="page-content" />
<div className="text-center mt-4 text-[#5C5C5C] text-sm">
{paginatedProducts.length} {filteredProducts.length}
@@ -232,7 +205,7 @@ export default function ProductsPage() {
</p>
<StaticLink href="/contact">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 bg-[#C41E3A] hover:bg-[#A01830] text-white rounded-lg text-sm font-medium transition-colors">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white rounded-lg text-sm font-medium transition-colors">
<ArrowLeft className="w-4 h-4 rotate-180" />
</RippleButton>
+3 -2
View File
@@ -55,14 +55,15 @@ function InlineCTABanner() {
<RippleButton
href="/contact"
rippleColor="rgba(196, 30, 58, 0.3)"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
>
</RippleButton>
<RippleButton
href="/contact"
variant="outline"
rippleColor="rgba(196, 30, 58, 0.2)"
className="border border-[#E5E5E5] text-[#5C5C5C] hover:text-[#C41E3A] hover:border-[#C41E3A]/30 px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
className="border-2 border-[var(--color-brand-primary)] text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)] hover:text-white px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
>
</RippleButton>
+19 -46
View File
@@ -7,9 +7,10 @@ import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { PageHeader } from '@/components/ui/page-header';
import { Search, ArrowLeft, Code, BarChart3, Lightbulb, Puzzle, ChevronLeft, ChevronRight, Filter } from 'lucide-react';
import { Search, ArrowLeft, Code, BarChart3, Lightbulb, Puzzle, Filter, SearchX } from 'lucide-react';
import { StaticLink } from '@/components/ui/static-link';
import { motion } from 'framer-motion';
import { Pagination } from '@/components/ui/pagination';
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
Code,
@@ -46,7 +47,6 @@ export default function ServicesPage() {
const handlePageChange = (page: number) => {
setCurrentPage(page);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleCategoryChange = (category: string) => {
@@ -66,7 +66,7 @@ export default function ServicesPage() {
description="专业技术团队,为您提供全方位的数字化解决方案"
/>
<div className="container-wide relative z-10 py-16" ref={contentRef}>
<div className="container-wide relative z-10 py-16" ref={contentRef} id="page-content">
<div className="max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -87,7 +87,7 @@ export default function ServicesPage() {
onClick={() => handleCategoryChange(category)}
className={
selectedCategory === category
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
? 'bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white'
: ''
}
>
@@ -112,7 +112,16 @@ export default function ServicesPage() {
{paginatedServices.length === 0 ? (
<div className="text-center py-20">
<p className="text-xl text-[#5C5C5C]"></p>
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-[#F5F5F5] flex items-center justify-center">
<SearchX className="w-8 h-8 text-[#5C5C5C]" />
</div>
<p className="text-lg text-[#5C5C5C] mb-4"></p>
<button
onClick={() => { setSearchQuery(''); setSelectedCategory('全部'); setCurrentPage(1); }}
className="text-sm text-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary-hover)] font-medium transition-colors"
>
</button>
</div>
) : (
<>
@@ -132,11 +141,11 @@ export default function ServicesPage() {
>
<div className="p-8">
<div className="flex items-start gap-4 mb-4">
<div className="w-14 h-14 rounded-xl bg-[#F5F5F5] flex items-center justify-center group-hover:bg-[#C41E3A] transition-all duration-300">
<div className="w-14 h-14 rounded-xl bg-[#F5F5F5] flex items-center justify-center group-hover:bg-[var(--color-brand-primary)] transition-all duration-300">
{Icon && <Icon className="w-7 h-7 text-[#1C1C1C] group-hover:text-white transition-colors" />}
</div>
<div className="flex-1">
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-2 group-hover:text-[#C41E3A] transition-colors">
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-2 group-hover:text-[var(--color-brand-primary)] transition-colors">
{service.title}
</h3>
<p className="text-[#5C5C5C] text-sm leading-relaxed">
@@ -153,7 +162,7 @@ export default function ServicesPage() {
</Badge>
))}
</div>
<div className="flex items-center text-[#C41E3A] font-medium group-hover:translate-x-2 transition-transform">
<div className="flex items-center text-[var(--color-brand-primary)] font-medium group-hover:translate-x-2 transition-transform">
<ArrowLeft className="w-4 h-4 ml-2 rotate-180" />
</div>
@@ -165,43 +174,7 @@ export default function ServicesPage() {
})}
</div>
{totalPages > 1 && (
<div className="flex justify-center items-center gap-2 mt-8">
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
<ChevronLeft className="w-4 h-4" />
</Button>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<Button
key={page}
variant={currentPage === page ? 'default' : 'outline'}
size="icon"
onClick={() => handlePageChange(page)}
className={
currentPage === page
? 'bg-[#C41E3A] hover:bg-[#A01830] text-white'
: ''
}
>
{page}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
)}
<Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={handlePageChange} scrollTargetId="page-content" />
<div className="text-center mt-4 text-[#5C5C5C] text-sm">
{paginatedServices.length} {filteredServices.length}
@@ -226,7 +199,7 @@ export default function ServicesPage() {
</p>
<Button
size="lg"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
asChild
>
<StaticLink href="/contact">
@@ -39,7 +39,7 @@ export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps)
<div className="max-w-4xl mx-auto text-center">
<SealStamp
delay={0.1}
className="inline-block px-4 py-2 bg-[#C41E3A]/20 rounded-full text-[#C41E3A] text-sm mb-6"
className="inline-block px-4 py-2 bg-[var(--color-brand-primary)]/20 rounded-full text-[var(--color-brand-primary)] text-sm mb-6"
>
{solution.industry}
</SealStamp>
@@ -61,14 +61,14 @@ export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps)
<RippleButton
href="/contact"
variant="outline"
className="border-2 border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A] hover:text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
className="border-2 border-[var(--color-brand-primary)] text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)] hover:text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
rippleColor="rgba(196, 30, 58, 0.2)"
>
</RippleButton>
<RippleButton
href="/contact"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
rippleColor="rgba(255, 255, 255, 0.3)"
>
@@ -90,12 +90,12 @@ export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps)
{solution.challenges.map((challenge, index) => (
<StaggerItem key={index}>
<InkCard
className="p-6 bg-[#FFFBF5] rounded-2xl border border-[#C41E3A]/10"
className="p-6 bg-[#FFFBF5] rounded-2xl border border-[var(--color-brand-primary)]/10"
hoverScale={1.02}
hoverShadow="0 20px 40px rgba(196, 30, 58, 0.08)"
>
<div className="flex items-start gap-3">
<span className="flex-shrink-0 w-8 h-8 rounded-full bg-[#C41E3A]/10 flex items-center justify-center text-[#C41E3A] font-bold text-sm">
<span className="flex-shrink-0 w-8 h-8 rounded-full bg-[var(--color-brand-primary)]/10 flex items-center justify-center text-[var(--color-brand-primary)] font-bold text-sm">
{index + 1}
</span>
<p className="text-[#1C1C1C] leading-relaxed">{challenge}</p>
@@ -118,7 +118,7 @@ export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps)
{solution.solutions.map((item, index) => (
<StaggerItem key={index}>
<div className="flex items-start gap-4 p-6 bg-white rounded-2xl border border-[#E5E5E5]">
<CheckCircle className="w-6 h-6 text-[#C41E3A] flex-shrink-0 mt-0.5" />
<CheckCircle className="w-6 h-6 text-[var(--color-brand-primary)] flex-shrink-0 mt-0.5" />
<p className="text-[#1C1C1C] leading-relaxed text-lg">{item}</p>
</div>
</StaggerItem>
@@ -144,11 +144,11 @@ export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps)
return (
<FadeUp key={productId}>
<StaticLink href={`/products/${productId}`}>
<div className="group p-6 bg-[#F8F8F8] rounded-2xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-all">
<span className="inline-block px-3 py-1 bg-[#C41E3A]/10 text-[#C41E3A] text-xs font-semibold rounded-full mb-3">
<div className="group p-6 bg-[#F8F8F8] rounded-2xl border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 transition-all">
<span className="inline-block px-3 py-1 bg-[var(--color-brand-primary)]/10 text-[var(--color-brand-primary)] text-xs font-semibold rounded-full mb-3">
{product.category}
</span>
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2 group-hover:text-[#C41E3A] transition-colors">
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2 group-hover:text-[var(--color-brand-primary)] transition-colors">
{product.title}
</h3>
<p className="text-sm text-[#5C5C5C] line-clamp-2">{product.description}</p>
@@ -163,7 +163,7 @@ export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps)
)}
{/* Section 6: CTA */}
<section className="relative py-24 md:py-32 bg-gradient-to-r from-[#C41E3A] to-[#E85D75] overflow-hidden">
<section className="relative py-24 md:py-32 bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] overflow-hidden">
<FloatingElement
amplitude={8}
duration={5}
@@ -198,7 +198,7 @@ export function SolutionDetailClient({ solutionId }: SolutionDetailClientProps)
href="/contact"
variant="secondary"
rippleColor="rgba(196, 30, 58, 0.3)"
className="bg-white text-[#C41E3A] px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center w-full sm:w-auto"
className="bg-white text-[var(--color-brand-primary)] px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center w-full sm:w-auto"
>
</RippleButton>
+6 -15
View File
@@ -63,19 +63,19 @@ export default function SolutionsPage() {
{SOLUTIONS.map((solution, index) => (
<FadeUp key={solution.id} delay={index * 0.1}>
<StaticLink href={`/solutions/${solution.id}`}>
<div className="group p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-all hover:shadow-lg">
<div className="group p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 transition-all hover:shadow-lg">
<div className="flex items-center gap-2 mb-3">
<span className="inline-block px-3 py-1 bg-[#C41E3A]/10 text-[#C41E3A] text-xs font-semibold rounded-full">
<span className="inline-block px-3 py-1 bg-[var(--color-brand-primary)]/10 text-[var(--color-brand-primary)] text-xs font-semibold rounded-full">
{solution.industry}
</span>
</div>
<h3 className="text-xl font-bold text-[#1C1C1C] mb-2 group-hover:text-[#C41E3A] transition-colors">
<h3 className="text-xl font-bold text-[#1C1C1C] mb-2 group-hover:text-[var(--color-brand-primary)] transition-colors">
{solution.title}
</h3>
<p className="text-[#5C5C5C] text-sm mb-4 line-clamp-2">
{solution.description}
</p>
<div className="flex items-center gap-1 text-[#C41E3A] text-sm font-semibold">
<div className="flex items-center gap-1 text-[var(--color-brand-primary)] text-sm font-semibold">
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</div>
@@ -102,17 +102,9 @@ export default function SolutionsPage() {
<p className="text-lg text-[#5C5C5C] mb-8 max-w-2xl mx-auto">
</p>
<div className="flex justify-center gap-4">
<Button
<Button
size="lg"
variant="outline"
asChild
>
<StaticLink href="/contact"></StaticLink>
</Button>
<Button
size="lg"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
asChild
>
<StaticLink href="/contact">
@@ -120,7 +112,6 @@ export default function SolutionsPage() {
<ArrowRight className="ml-2 w-4 h-4" />
</StaticLink>
</Button>
</div>
</div>
</motion.div>
</div>
+14 -9
View File
@@ -64,10 +64,10 @@ export function TeamClient() {
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center"></h2>
<div className="space-y-4 max-w-3xl mx-auto text-center">
<p className="text-[#5C5C5C] leading-relaxed">
<span className="text-[#C41E3A] font-medium"></span><span className="text-[#C41E3A] font-medium"></span> 12
<span className="text-[var(--color-brand-primary)] font-medium"></span><span className="text-[var(--color-brand-primary)] font-medium"></span> 12
</p>
<p className="text-[#5C5C5C] leading-relaxed">
<span className="text-[#C41E3A] font-medium"> IT </span>
<span className="text-[var(--color-brand-primary)] font-medium"> IT </span>
</p>
<p className="text-[#5C5C5C] leading-relaxed">
@@ -90,8 +90,8 @@ export function TeamClient() {
transition={{ duration: 0.5, delay: 0.2 + idx * 0.1 }}
className={idx >= 3 ? 'md:col-span-1 lg:col-start-1' : ''}
>
<div className="bg-white rounded-xl p-6 border border-[#E5E5E5] hover:border-[#C41E3A]/30 hover:shadow-md transition-all duration-300 h-full">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center mb-4">
<div className="bg-white rounded-xl p-6 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-md transition-all duration-300 h-full">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center mb-4">
<Icon className="w-6 h-6 text-white" />
</div>
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{item.title}</h3>
@@ -103,17 +103,22 @@ export function TeamClient() {
</div>
</div>
{/* CTA */}
{/* Bottom CTA */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.5 }}
className="text-center"
className="mt-16 text-center py-16 bg-[#F5F5F5] rounded-2xl"
>
<p className="text-lg text-[#5C5C5C] mb-6"></p>
<Button size="lg" asChild>
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
</h3>
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
</p>
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
<StaticLink href="/contact">
<ArrowRight className="ml-2 w-4 h-4" />
</StaticLink>
</Button>
+9 -9
View File
@@ -21,10 +21,10 @@ export default function Error({
<div className="container-wide px-4 py-20">
<div className="max-w-2xl mx-auto text-center">
<div className="mb-8">
<div className="w-24 h-24 bg-[#C41E3A]/10 rounded-full flex items-center justify-center mx-auto mb-6">
<AlertTriangle className="w-12 h-12 text-[#C41E3A]" />
<div className="w-24 h-24 bg-[var(--color-brand-primary)]/10 rounded-full flex items-center justify-center mx-auto mb-6">
<AlertTriangle className="w-12 h-12 text-[var(--color-brand-primary)]" />
</div>
<div className="w-32 h-1 bg-[#C41E3A] mx-auto" />
<div className="w-32 h-1 bg-[var(--color-brand-primary)] mx-auto" />
</div>
<h1 className="text-3xl font-bold text-[#1C1C1C] mb-4">
@@ -53,7 +53,7 @@ export default function Error({
<Button
size="lg"
onClick={reset}
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
>
<RefreshCw className="w-5 h-5 mr-2" />
@@ -81,8 +81,8 @@ export default function Error({
href="/contact"
className="flex items-center p-4 bg-white rounded-lg hover:shadow-md transition-shadow group"
>
<div className="w-10 h-10 bg-[#C41E3A]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[#C41E3A]/20 transition-colors">
<AlertTriangle className="w-5 h-5 text-[#C41E3A]" />
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[var(--color-brand-primary)]/20 transition-colors">
<AlertTriangle className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div className="text-left">
<div className="font-semibold text-[#1C1C1C]"></div>
@@ -94,8 +94,8 @@ export default function Error({
href="/services"
className="flex items-center p-4 bg-white rounded-lg hover:shadow-md transition-shadow group"
>
<div className="w-10 h-10 bg-[#C41E3A]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[#C41E3A]/20 transition-colors">
<RefreshCw className="w-5 h-5 text-[#C41E3A]" />
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[var(--color-brand-primary)]/20 transition-colors">
<RefreshCw className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div className="text-left">
<div className="font-semibold text-[#1C1C1C]"></div>
@@ -107,7 +107,7 @@ export default function Error({
<div className="mt-8 text-sm text-[#5C5C5C]">
{' '}
<StaticLink href="/contact" className="text-[#C41E3A] hover:underline">
<StaticLink href="/contact" className="text-[var(--color-brand-primary)] hover:underline">
</StaticLink>
</div>
+41 -40
View File
@@ -98,6 +98,19 @@
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
/* z-index 层级 */
--z-sticky: 40;
--z-dropdown: 50;
--z-modal: 100;
--z-toast: 200;
/* border-radius */
--radius-sm: 6px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-2xl: 24px;
}
@layer base {
@@ -304,36 +317,36 @@
}
.transition-smooth {
transition: all var(--transition-normal) var(--ease-out);
transition: color var(--transition-normal) var(--ease-out), background-color var(--transition-normal) var(--ease-out), border-color var(--transition-normal) var(--ease-out), opacity var(--transition-normal) var(--ease-out);
}
.transition-fast {
transition: all var(--transition-fast) var(--ease-out);
transition: color var(--transition-fast) var(--ease-out), background-color var(--transition-fast) var(--ease-out), border-color var(--transition-fast) var(--ease-out), opacity var(--transition-fast) var(--ease-out);
}
/* 渐变背景 - Wickret 风格 */
.bg-gradient-modern {
background: linear-gradient(135deg, var(--color-dark-bg) 0%, #1a1a2e 50%, #16213e 100%);
background: linear-gradient(135deg, #1C1C1C 0%, #1a1a2e 50%, #16213e 100%);
}
.bg-gradient-brand {
background: linear-gradient(135deg, var(--color-gradient-start), var(--color-gradient-mid), var(--color-gradient-end));
background: linear-gradient(135deg, #C41E3A, #E04A68, #1C1C1C);
}
.bg-gradient-radial {
background: radial-gradient(ellipse at center, var(--color-gradient-start) 0%, transparent 70%);
background: radial-gradient(ellipse at center, #C41E3A 0%, transparent 70%);
}
/* 渐变文字 */
.text-gradient-brand {
background: linear-gradient(135deg, var(--color-gradient-start), var(--color-gradient-mid), var(--color-gradient-end));
background: linear-gradient(135deg, #C41E3A, #E04A68, #1C1C1C);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.text-gradient-cyan {
background: linear-gradient(135deg, var(--color-gradient-cyan), var(--color-gradient-mid));
background: linear-gradient(135deg, #C41E3A, #E04A68);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -341,7 +354,7 @@
/* 发光效果 */
.bg-glow-red {
background: radial-gradient(circle at center, var(--color-accent-red-glow) 0%, transparent 70%);
background: radial-gradient(circle at center, rgba(196, 30, 58, 0.3) 0%, transparent 70%);
}
.bg-glow-purple {
@@ -532,7 +545,7 @@
position: relative;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(8px);
transition: all 0.3s var(--ease-out);
transition: border-color 0.3s var(--ease-out), transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}
.geometric-card::before,
@@ -543,7 +556,7 @@
height: 12px;
border: 1px solid rgba(196, 30, 58, 0.15);
opacity: 0;
transition: all 0.3s var(--ease-out);
transition: opacity 0.3s var(--ease-out), transform 0.3s var(--ease-out);
}
.geometric-card::before {
@@ -619,7 +632,7 @@
.scroll-reveal {
opacity: 0;
transform: translateY(30px);
transition: all 0.8s var(--ease-out);
transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out);
}
.scroll-reveal.revealed {
@@ -827,21 +840,21 @@
@keyframes expandWidth {
0% {
width: 0;
transform: scaleX(0);
opacity: 0;
}
100% {
width: 100%;
transform: scaleX(1);
opacity: 1;
}
}
@keyframes typewriter {
from {
width: 0;
transform: scaleX(0);
}
to {
width: 100%;
transform: scaleX(1);
}
}
@@ -907,6 +920,7 @@
.animate-expand-width {
animation: expandWidth 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
transform-origin: left;
}
.hover-lift {
@@ -988,7 +1002,7 @@
border-radius: inherit;
opacity: 0;
transform: scale(1.1);
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.seal-stamp:hover::before {
@@ -1032,7 +1046,7 @@
border-radius: 8px;
padding: 12px 32px;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
transition: background-color 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.btn-primary:hover {
@@ -1047,7 +1061,7 @@
border-radius: 8px;
padding: 12px 32px;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
transition: background-color 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.btn-secondary:hover {
@@ -1063,7 +1077,7 @@
border-radius: 8px;
padding: 12px 32px;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
transition: background-color 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.btn-outline:hover {
@@ -1077,7 +1091,7 @@
border: 1px solid var(--color-border-primary);
border-radius: 12px;
padding: 24px;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
transition: border-color 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.card-health:hover {
@@ -1087,13 +1101,13 @@
}
.icon-container-primary {
background: linear-gradient(135deg, rgba(0, 94, 184, 0.1), rgba(0, 163, 224, 0.1));
background: linear-gradient(135deg, rgba(196, 30, 58, 0.1), rgba(224, 74, 104, 0.1));
border-radius: 12px;
transition: box-shadow 0.3s ease;
}
.icon-container-primary:hover {
box-shadow: 0 4px 12px rgba(0, 94, 184, 0.15);
box-shadow: 0 4px 12px rgba(196, 30, 58, 0.15);
}
.icon-container-brand {
@@ -1131,19 +1145,6 @@ body {
overflow-x: hidden;
}
/* 优化移动端文字大小 */
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.75rem;
}
h3 {
font-size: 1.375rem;
}
/* 优化移动端按钮和链接的触摸目标 */
a, button {
min-height: 44px;
+1 -1
View File
@@ -134,7 +134,7 @@ export default function RootLayout({
>
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-[9999] focus:px-4 focus:py-2 focus:bg-[#C41E3A] focus:text-white focus:rounded-md focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-[#C41E3A]"
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-[9999] focus:px-4 focus:py-2 focus:bg-[var(--color-brand-primary)] focus:text-white focus:rounded-md focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-[var(--color-brand-primary)]"
>
</a>
+12 -12
View File
@@ -10,10 +10,10 @@ export default function NotFound() {
<div className="container-wide px-4 py-20">
<div className="max-w-2xl mx-auto text-center">
<div className="mb-8">
<h1 className="text-[120px] font-bold text-[#C41E3A] leading-none mb-4">
<h1 className="text-[120px] font-bold text-[var(--color-brand-primary)] leading-none mb-4">
404
</h1>
<div className="w-32 h-1 bg-[#C41E3A] mx-auto mb-6" />
<div className="w-32 h-1 bg-[var(--color-brand-primary)] mx-auto mb-6" />
</div>
<h2 className="text-3xl font-bold text-[#1C1C1C] mb-4">
@@ -29,7 +29,7 @@ export default function NotFound() {
<Button
size="lg"
asChild
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
>
<StaticLink href="/">
<Home className="w-5 h-5 mr-2" />
@@ -57,8 +57,8 @@ export default function NotFound() {
href="/about"
className="flex items-center p-4 bg-white rounded-lg hover:shadow-md transition-shadow group"
>
<div className="w-10 h-10 bg-[#C41E3A]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[#C41E3A]/20 transition-colors">
<Building2 className="w-5 h-5 text-[#C41E3A]" />
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[var(--color-brand-primary)]/20 transition-colors">
<Building2 className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div className="text-left">
<div className="font-semibold text-[#1C1C1C]"></div>
@@ -70,8 +70,8 @@ export default function NotFound() {
href="/services"
className="flex items-center p-4 bg-white rounded-lg hover:shadow-md transition-shadow group"
>
<div className="w-10 h-10 bg-[#C41E3A]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[#C41E3A]/20 transition-colors">
<Briefcase className="w-5 h-5 text-[#C41E3A]" />
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[var(--color-brand-primary)]/20 transition-colors">
<Briefcase className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div className="text-left">
<div className="font-semibold text-[#1C1C1C]"></div>
@@ -83,8 +83,8 @@ export default function NotFound() {
href="/products"
className="flex items-center p-4 bg-white rounded-lg hover:shadow-md transition-shadow group"
>
<div className="w-10 h-10 bg-[#C41E3A]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[#C41E3A]/20 transition-colors">
<Package className="w-5 h-5 text-[#C41E3A]" />
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[var(--color-brand-primary)]/20 transition-colors">
<Package className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div className="text-left">
<div className="font-semibold text-[#1C1C1C]"></div>
@@ -96,8 +96,8 @@ export default function NotFound() {
href="/cases"
className="flex items-center p-4 bg-white rounded-lg hover:shadow-md transition-shadow group"
>
<div className="w-10 h-10 bg-[#C41E3A]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[#C41E3A]/20 transition-colors">
<Trophy className="w-5 h-5 text-[#C41E3A]" />
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-lg flex items-center justify-center mr-4 group-hover:bg-[var(--color-brand-primary)]/20 transition-colors">
<Trophy className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<div className="text-left">
<div className="font-semibold text-[#1C1C1C]"></div>
@@ -109,7 +109,7 @@ export default function NotFound() {
<div className="mt-8 text-sm text-[#5C5C5C]">
{' '}
<StaticLink href="/contact" className="text-[#C41E3A] hover:underline">
<StaticLink href="/contact" className="text-[var(--color-brand-primary)] hover:underline">
</StaticLink>
</div>
+1 -1
View File
@@ -8,7 +8,7 @@ export const metadata: Metadata = {
export default function PrivacyPolicyPage() {
return (
<div className="min-h-screen bg-white">
<div className="bg-gradient-to-br from-[#C41E3A] to-[#1C1C1C] py-20">
<div className="bg-gradient-to-br from-[var(--color-brand-primary)] to-[#1C1C1C] py-20">
<div className="container-wide">
<h1 className="text-4xl md:text-5xl font-bold text-white mb-4">
+2 -2
View File
@@ -8,7 +8,7 @@ export const metadata: Metadata = {
export default function TermsOfServicePage() {
return (
<div className="min-h-screen bg-white">
<div className="bg-gradient-to-br from-[#C41E3A] to-[#1C1C1C] py-20">
<div className="bg-gradient-to-br from-[var(--color-brand-primary)] to-[#1C1C1C] py-20">
<div className="container-wide">
<h1 className="text-4xl md:text-5xl font-bold text-white mb-4">
@@ -168,7 +168,7 @@ export default function TermsOfServicePage() {
</ul>
</section>
<section className="bg-[#FFFBF5] p-6 rounded-lg border-l-4 border-[#C41E3A]">
<section className="bg-[#FFFBF5] p-6 rounded-lg border-l-4 border-[var(--color-brand-primary)]">
<p className="text-[#1C1C1C] font-medium mb-2"></p>
<p className="text-[#5C5C5C]">2026425</p>
</section>
+6 -6
View File
@@ -107,7 +107,7 @@ export function CookieConsent() {
使{' '}
<a
href="/privacy"
className="text-[#C41E3A] hover:text-[#A01830] underline font-medium"
className="text-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary-hover)] underline font-medium"
>
</a>
@@ -132,7 +132,7 @@ export function CookieConsent() {
<button
onClick={handleAcceptAll}
disabled={isAnimating}
className="px-4 py-2 text-sm font-medium text-white bg-[#C41E3A] rounded-lg hover:bg-[#A01830] transition-colors disabled:opacity-50"
className="px-4 py-2 text-sm font-medium text-white bg-[var(--color-brand-primary)] rounded-lg hover:bg-[var(--color-brand-primary-hover)] transition-colors disabled:opacity-50"
>
</button>
@@ -159,7 +159,7 @@ export function CookieConsent() {
type="checkbox"
checked
disabled
className="mt-1 h-4 w-4 rounded border-gray-300 text-[#C41E3A] focus:ring-[#C41E3A] cursor-not-allowed"
className="mt-1 h-4 w-4 rounded border-gray-300 text-[var(--color-brand-primary)] focus:ring-[var(--color-brand-primary)] cursor-not-allowed"
aria-label="必要 Cookie"
/>
<div className="flex-1">
@@ -178,7 +178,7 @@ export function CookieConsent() {
type="checkbox"
checked={preferences.analytics}
onChange={() => handleTogglePreference('analytics')}
className="mt-1 h-4 w-4 rounded border-gray-300 text-[#C41E3A] focus:ring-[#C41E3A] cursor-pointer"
className="mt-1 h-4 w-4 rounded border-gray-300 text-[var(--color-brand-primary)] focus:ring-[var(--color-brand-primary)] cursor-pointer"
aria-label="分析 Cookie"
/>
<div className="flex-1">
@@ -194,7 +194,7 @@ export function CookieConsent() {
type="checkbox"
checked={preferences.marketing}
onChange={() => handleTogglePreference('marketing')}
className="mt-1 h-4 w-4 rounded border-gray-300 text-[#C41E3A] focus:ring-[#C41E3A] cursor-pointer"
className="mt-1 h-4 w-4 rounded border-gray-300 text-[var(--color-brand-primary)] focus:ring-[var(--color-brand-primary)] cursor-pointer"
aria-label="营销 Cookie"
/>
<div className="flex-1">
@@ -217,7 +217,7 @@ export function CookieConsent() {
<button
onClick={handleSaveCustom}
disabled={isAnimating}
className="px-4 py-2 text-sm font-medium text-white bg-[#C41E3A] rounded-lg hover:bg-[#A01830] transition-colors disabled:opacity-50"
className="px-4 py-2 text-sm font-medium text-white bg-[var(--color-brand-primary)] rounded-lg hover:bg-[var(--color-brand-primary-hover)] transition-colors disabled:opacity-50"
>
</button>
@@ -28,7 +28,7 @@ interface Particle {
export function DataParticleFlow({
className = '',
particleCount = 50,
color = '#C41E3A',
color = 'var(--color-brand-primary)',
intensity = 'normal',
shape = 'circle',
effect = 'default',
+1 -1
View File
@@ -10,7 +10,7 @@ interface GradientFlowProps {
export function GradientFlow({
className = '',
colors = ['#C41E3A', '#D4A574', '#8B4513', '#2F4F4F'],
colors = ['var(--color-brand-primary)', '#D4A574', '#8B4513', '#2F4F4F'],
duration = 15
}: GradientFlowProps) {
return (
+1 -1
View File
@@ -11,7 +11,7 @@ interface SubtleDotsProps {
export function SubtleDots({
className = '',
color = '#C41E3A',
color = 'var(--color-brand-primary)',
count = 12
}: SubtleDotsProps) {
const [dots, setDots] = useState<Array<{
+34 -17
View File
@@ -16,30 +16,47 @@ interface BreadcrumbProps {
export function Breadcrumb({ items }: BreadcrumbProps) {
return (
<nav
aria-label="breadcrumb"
aria-label="面包屑导航"
className="flex items-center text-xs md:text-sm text-[#5C5C5C] py-3 md:py-4"
style={{ lineHeight: '1' }}
>
<StaticLink
href="/"
className="hover:text-[#C41E3A] transition-colors shrink-0"
aria-label="返回首页"
style={{ minHeight: 0, minWidth: 0 }}
>
<Home className="w-3.5 h-3.5" />
</StaticLink>
{items.map((item, index) => (
<Fragment key={index}>
<ChevronRight className="w-3 h-3 text-[#CCCCCC] shrink-0 mx-1" />
<ol className="flex items-center list-none m-0 p-0">
<li className="flex items-center">
<StaticLink
href={item.href}
className="hover:text-[#C41E3A] transition-colors whitespace-nowrap"
href="/"
className="hover:text-[var(--color-brand-primary)] transition-colors shrink-0"
aria-label="返回首页"
style={{ minHeight: 0, minWidth: 0 }}
>
{item.label}
<Home className="w-3.5 h-3.5" />
</StaticLink>
</Fragment>
))}
</li>
{items.map((item, index) => {
const isLast = index === items.length - 1;
return (
<Fragment key={index}>
<li className="flex items-center" aria-hidden="true">
<ChevronRight className="w-3 h-3 text-[#CCCCCC] shrink-0 mx-1" />
</li>
<li className="flex items-center">
{isLast ? (
<span className="text-[#1C1C1C] font-medium whitespace-nowrap" aria-current="page">
{item.label}
</span>
) : (
<StaticLink
href={item.href}
className="hover:text-[var(--color-brand-primary)] transition-colors whitespace-nowrap"
style={{ minHeight: 0, minWidth: 0 }}
>
{item.label}
</StaticLink>
)}
</li>
</Fragment>
);
})}
</ol>
</nav>
);
}
+18 -16
View File
@@ -7,7 +7,7 @@ export function Footer() {
return (
<footer className="bg-[#F5F5F5] py-12" data-testid="footer" role="contentinfo">
{/* 顶部渐变装饰线 */}
<div className="h-[2px] bg-gradient-to-r from-transparent via-[#C41E3A]/50 to-transparent" />
<div className="h-[2px] bg-gradient-to-r from-transparent via-[var(--color-brand-primary)]/50 to-transparent" />
<div className="container-wide">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-8">
<div className="bg-white rounded-xl p-6 border border-[#E5E5E5] shadow-sm hover:shadow-md hover:-translate-y-1 transition-all duration-300" data-testid="card-brand">
@@ -49,7 +49,7 @@ export function Footer() {
<li key={item.id}>
<StaticLink
href={item.href}
className="text-[#3D3D3D] hover:text-[#C41E3A] transition-all duration-200 inline-block hover:translate-x-1"
className="text-[#3D3D3D] hover:text-[var(--color-brand-primary)] transition-all duration-200 inline-block hover:translate-x-1"
>
{item.label}
</StaticLink>
@@ -61,22 +61,22 @@ export function Footer() {
<h3 className="font-semibold text-lg mb-4 text-[#1C1C1C]"></h3>
<ul className="space-y-2.5">
<li>
<StaticLink href="/services/software" className="text-[#3D3D3D] hover:text-[#C41E3A] transition-all duration-200 inline-block hover:translate-x-1">
<StaticLink href="/services/software" className="text-[#3D3D3D] hover:text-[var(--color-brand-primary)] transition-all duration-200 inline-block hover:translate-x-1">
</StaticLink>
</li>
<li>
<StaticLink href="/services/data" className="text-[#3D3D3D] hover:text-[#C41E3A] transition-all duration-200 inline-block hover:translate-x-1">
<StaticLink href="/services/data" className="text-[#3D3D3D] hover:text-[var(--color-brand-primary)] transition-all duration-200 inline-block hover:translate-x-1">
</StaticLink>
</li>
<li>
<StaticLink href="/services/consulting" className="text-[#3D3D3D] hover:text-[#C41E3A] transition-all duration-200 inline-block hover:translate-x-1">
<StaticLink href="/services/consulting" className="text-[#3D3D3D] hover:text-[var(--color-brand-primary)] transition-all duration-200 inline-block hover:translate-x-1">
</StaticLink>
</li>
<li>
<StaticLink href="/services/solutions" className="text-[#3D3D3D] hover:text-[#C41E3A] transition-all duration-200 inline-block hover:translate-x-1">
<StaticLink href="/services/solutions" className="text-[#3D3D3D] hover:text-[var(--color-brand-primary)] transition-all duration-200 inline-block hover:translate-x-1">
</StaticLink>
</li>
@@ -88,12 +88,14 @@ export function Footer() {
<h3 className="font-semibold text-lg mb-6 text-[#1C1C1C]"></h3>
<ul className="space-y-4">
<li className="flex items-start gap-3">
<MapPin className="w-5 h-5 text-[#C41E3A] mt-0.5 shrink-0" />
<MapPin className="w-5 h-5 text-[var(--color-brand-primary)] mt-0.5 shrink-0" />
<span className="text-[#3D3D3D]">{COMPANY_INFO.address}</span>
</li>
<li className="flex items-center gap-3">
<Mail className="w-5 h-5 text-[#C41E3A] shrink-0" />
<span className="text-[#3D3D3D]">{COMPANY_INFO.email}</span>
<Mail className="w-5 h-5 text-[var(--color-brand-primary)] shrink-0" />
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#3D3D3D] hover:text-[var(--color-brand-primary)] transition-colors">
{COMPANY_INFO.email}
</a>
</li>
</ul>
<div className="mt-6 pt-6 border-t border-[#E5E5E5]">
@@ -119,10 +121,10 @@ export function Footer() {
© {new Date().getFullYear()} {COMPANY_INFO.name}
</p>
<div className="flex gap-6">
<StaticLink href="/privacy" className="text-[#5C5C5C] hover:text-[#C41E3A] text-sm transition-colors duration-200">
<StaticLink href="/privacy" className="text-[#5C5C5C] hover:text-[var(--color-brand-primary)] text-sm transition-colors duration-200">
</StaticLink>
<StaticLink href="/terms" className="text-[#5C5C5C] hover:text-[#C41E3A] text-sm transition-colors duration-200">
<StaticLink href="/terms" className="text-[#5C5C5C] hover:text-[var(--color-brand-primary)] text-sm transition-colors duration-200">
</StaticLink>
</div>
@@ -134,16 +136,16 @@ export function Footer() {
href="https://beian.miit.gov.cn/"
target="_blank"
rel="noopener noreferrer"
className="hover:text-[#C41E3A] transition-colors duration-200"
className="hover:text-[var(--color-brand-primary)] transition-colors duration-200"
>
{COMPANY_INFO.icp}
</a>
<span className="hidden sm:inline">|</span>
<a
href="https://beian.mps.gov.cn/#/query/webSearch?code=51010602003285"
target="_blank"
<a
href="https://beian.mps.gov.cn/#/query/webSearch?code=51010602003285"
target="_blank"
rel="noreferrer"
className="hover:text-[#C41E3A] transition-colors duration-200 inline-flex items-center gap-1"
className="hover:text-[var(--color-brand-primary)] transition-colors duration-200 inline-flex items-center gap-1"
>
<Image
src="/images/beian-icon.png"
+7 -7
View File
@@ -65,10 +65,10 @@ function HeaderContent() {
}
}, [isOpen]);
const handleNavClick = useCallback((e: React.MouseEvent<HTMLAnchorElement>, item: NavigationItem) => {
e.preventDefault();
window.location.href = item.href;
const handleNavClick = useCallback((href: string) => {
// Close mobile menu, then navigate (StaticLink delegates navigation to onClick when present)
setIsOpen(false);
window.location.href = href;
}, []);
const isActive = useCallback((item: NavigationItem) => {
@@ -124,7 +124,7 @@ function HeaderContent() {
<StaticLink
key={item.id}
href={item.href}
onClick={(e) => handleNavClick(e, item)}
onClick={() => handleNavClick(item.href)}
className={`
relative px-3 py-1.5 text-sm font-medium
transition-all duration-300
@@ -142,7 +142,7 @@ function HeaderContent() {
{item.label}
<span
className={`
absolute bottom-0 left-1/2 -translate-x-1/2 w-6 h-0.5 bg-[#C41E3A] rounded-full
absolute bottom-0 left-1/2 -translate-x-1/2 w-6 h-0.5 bg-[var(--color-brand-primary)] rounded-full
transition-all duration-200 ease-out
${isActive(item)
? 'opacity-100 scale-x-100'
@@ -223,12 +223,12 @@ function HeaderContent() {
>
<StaticLink
href={item.href}
onClick={(e) => handleNavClick(e, item)}
onClick={() => handleNavClick(item.href)}
className={`
block px-4 py-4 text-base font-medium rounded-lg
transition-all duration-200
${isActive(item)
? 'text-[#1C1C1C] bg-[#F5F5F5] border-l-4 border-[#C41E3A]'
? 'text-[#1C1C1C] bg-[#F5F5F5] border-l-4 border-[var(--color-brand-primary)]'
: 'text-[#3D3D3D] hover:text-[#1C1C1C] hover:bg-[#F5F5F5]'
}
`}
+2 -2
View File
@@ -50,7 +50,7 @@ export function MobileMenu({ className }: MobileMenuProps) {
<button
onClick={() => setIsOpen(!isOpen)}
onKeyDown={(e) => handleKeyDown(e)}
className="p-3 rounded-md hover:bg-[#F5F5F5] transition-colors focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2 min-w-[48px] min-h-[48px] flex items-center justify-center"
className="p-3 rounded-md hover:bg-[#F5F5F5] transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-primary)] focus:ring-offset-2 min-w-[48px] min-h-[48px] flex items-center justify-center"
aria-label={isOpen ? '关闭菜单' : '打开菜单'}
aria-expanded={isOpen}
aria-controls="mobile-menu-panel"
@@ -83,7 +83,7 @@ export function MobileMenu({ className }: MobileMenuProps) {
<button
onClick={() => handleNavClick(item.href)}
onKeyDown={(e) => handleKeyDown(e, item.href)}
className="block w-full text-left px-4 py-4 text-[#171717] hover:bg-[#FEF2F4] hover:text-[#C41E3A] rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-inset min-h-[48px]"
className="block w-full text-left px-4 py-4 text-[#171717] hover:bg-[#FEF2F4] hover:text-[var(--color-brand-primary)] rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-primary)] focus:ring-inset min-h-[48px]"
>
{item.label}
</button>
+7 -6
View File
@@ -2,7 +2,7 @@
import { StaticLink } from '@/components/ui/static-link';
import { usePathname } from 'next/navigation';
import { Home, Briefcase, Package, FileText, User } from 'lucide-react';
import { Home, Briefcase, Package, FileText, MessageSquare } from 'lucide-react';
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
@@ -11,7 +11,7 @@ const tabs = [
{ id: 'services', label: '服务', href: '/services', icon: Briefcase },
{ id: 'products', label: '产品', href: '/products', icon: Package },
{ id: 'news', label: '新闻', href: '/news', icon: FileText },
{ id: 'contact', label: '联系', href: '/contact', icon: User },
{ id: 'contact', label: '联系', href: '/contact', icon: MessageSquare },
];
export function MobileTabBar() {
@@ -30,7 +30,7 @@ export function MobileTabBar() {
};
return (
<nav className="fixed bottom-0 left-0 right-0 z-50 md:hidden bg-white/95 backdrop-blur-xl border-t border-[#E5E5E5] safe-area-inset-bottom">
<nav aria-label="底部快捷导航" className="fixed bottom-0 left-0 right-0 z-50 md:hidden bg-white/95 backdrop-blur-xl border-t border-[#E5E5E5] safe-area-inset-bottom">
<div className="flex items-center justify-around h-16">
{tabs.map((tab) => {
const Icon = tab.icon;
@@ -40,19 +40,20 @@ export function MobileTabBar() {
<StaticLink
key={tab.id}
href={tab.href}
aria-current={active ? 'page' : undefined}
className="flex flex-col items-center justify-center flex-1 h-full relative group min-h-12"
>
<div className="relative flex flex-col items-center justify-center py-2">
<Icon
className={cn(
'w-6 h-6 transition-colors',
active ? 'text-[#C41E3A]' : 'text-[#5C5C5C] group-hover:text-[#1C1C1C]'
active ? 'text-[var(--color-brand-primary)]' : 'text-[#5C5C5C] group-hover:text-[#1C1C1C]'
)}
/>
<span
className={cn(
'text-xs mt-1 transition-colors',
active ? 'text-[#C41E3A] font-medium' : 'text-[#5C5C5C]'
active ? 'text-[var(--color-brand-primary)] font-medium' : 'text-[#5C5C5C]'
)}
>
{tab.label}
@@ -60,7 +61,7 @@ export function MobileTabBar() {
{active && (
<motion.div
layoutId="activeTab"
className="absolute -bottom-1 w-8 h-0.5 bg-[#C41E3A] rounded-full"
className="absolute -bottom-1 w-8 h-0.5 bg-[var(--color-brand-primary)] rounded-full"
transition={{ type: 'spring', stiffness: 380, damping: 30 }}
/>
)}
+4 -4
View File
@@ -29,7 +29,7 @@ export function ProductFooter() {
return (
<footer className="bg-[#F8F8F8]" role="contentinfo">
{/* 顶部装饰线 */}
<div className="h-[2px] bg-gradient-to-r from-transparent via-[#C41E3A]/50 to-transparent" />
<div className="h-[2px] bg-gradient-to-r from-transparent via-[var(--color-brand-primary)]/50 to-transparent" />
{/* CTA 区域 */}
<div className="container-wide py-16">
@@ -44,7 +44,7 @@ export function ProductFooter() {
<StaticLink href="/contact">
<RippleButton
rippleColor="rgba(196, 30, 58, 0.3)"
className="px-8 py-3 bg-[#C41E3A] text-white rounded-lg font-medium text-base"
className="px-8 py-3 bg-[var(--color-brand-primary)] text-white rounded-lg font-medium text-base"
>
</RippleButton>
@@ -78,7 +78,7 @@ export function ProductFooter() {
<StaticLink
key={link.href}
href={link.href}
className="text-[#999999] hover:text-[#C41E3A] text-sm transition-colors duration-200"
className="text-[#999999] hover:text-[var(--color-brand-primary)] text-sm transition-colors duration-200"
>
{link.label}
</StaticLink>
@@ -89,7 +89,7 @@ export function ProductFooter() {
<div className="flex items-center gap-4">
<a
href={`mailto:${COMPANY_INFO.email}`}
className="flex items-center gap-2 text-[#999999] hover:text-[#C41E3A] text-sm transition-colors duration-200"
className="flex items-center gap-2 text-[#999999] hover:text-[var(--color-brand-primary)] text-sm transition-colors duration-200"
>
<FloatingElement amplitude={3} duration={3}>
<Mail className="w-4 h-4" />
+2 -2
View File
@@ -61,7 +61,7 @@ function ProductHeaderContent() {
{/* 立即咨询 CTA */}
<RippleButton
href="/contact"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-5 py-2 rounded-lg text-sm font-semibold inline-flex items-center gap-2"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white px-5 py-2 rounded-lg text-sm font-semibold inline-flex items-center gap-2"
rippleColor="rgba(255, 255, 255, 0.3)"
>
<Phone className="w-4 h-4" />
@@ -74,7 +74,7 @@ function ProductHeaderContent() {
<Button
variant="outline"
size="sm"
className="border-[#E5E5E5] text-[#5C5C5C] hover:text-[#1C1C1C] hover:bg-[#F5F5F5] hover:border-[#C41E3A]/30 transition-all duration-200 text-sm"
className="border-[#E5E5E5] text-[#5C5C5C] hover:text-[#1C1C1C] hover:bg-[#F5F5F5] hover:border-[var(--color-brand-primary)]/30 transition-all duration-200 text-sm"
>
<ArrowLeft className="w-4 h-4 mr-2" />
+4 -4
View File
@@ -28,7 +28,7 @@ export function ServiceFooter() {
return (
<footer className="bg-[#F8F8F8]" role="contentinfo">
{/* 顶部装饰线 */}
<div className="h-[2px] bg-gradient-to-r from-transparent via-[#C41E3A]/50 to-transparent" />
<div className="h-[2px] bg-gradient-to-r from-transparent via-[var(--color-brand-primary)]/50 to-transparent" />
{/* CTA 区域 */}
<div className="container-wide py-16">
@@ -43,7 +43,7 @@ export function ServiceFooter() {
<StaticLink href="/contact">
<RippleButton
rippleColor="rgba(196, 30, 58, 0.3)"
className="px-8 py-3 bg-[#C41E3A] text-white rounded-lg font-medium text-base"
className="px-8 py-3 bg-[var(--color-brand-primary)] text-white rounded-lg font-medium text-base"
>
</RippleButton>
@@ -74,7 +74,7 @@ export function ServiceFooter() {
<StaticLink
key={link.href}
href={link.href}
className="text-[#999999] hover:text-[#C41E3A] text-sm transition-colors duration-200"
className="text-[#999999] hover:text-[var(--color-brand-primary)] text-sm transition-colors duration-200"
>
{link.label}
</StaticLink>
@@ -83,7 +83,7 @@ export function ServiceFooter() {
<div className="flex items-center gap-4">
<a
href={`mailto:${COMPANY_INFO.email}`}
className="flex items-center gap-2 text-[#999999] hover:text-[#C41E3A] text-sm transition-colors duration-200"
className="flex items-center gap-2 text-[#999999] hover:text-[var(--color-brand-primary)] text-sm transition-colors duration-200"
>
<FloatingElement amplitude={3} duration={3}>
<Mail className="w-4 h-4" />
+2 -2
View File
@@ -57,7 +57,7 @@ function ServiceHeaderContent() {
</StaticLink>
<RippleButton
href="/contact"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-5 py-2 rounded-lg text-sm font-semibold inline-flex items-center gap-2"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white px-5 py-2 rounded-lg text-sm font-semibold inline-flex items-center gap-2"
rippleColor="rgba(255, 255, 255, 0.3)"
>
<Phone className="w-4 h-4" />
@@ -68,7 +68,7 @@ function ServiceHeaderContent() {
<Button
variant="outline"
size="sm"
className="border-[#E5E5E5] text-[#5C5C5C] hover:text-[#1C1C1C] hover:bg-[#F5F5F5] hover:border-[#C41E3A]/30 transition-all duration-200 text-sm"
className="border-[#E5E5E5] text-[#5C5C5C] hover:text-[#1C1C1C] hover:bg-[#F5F5F5] hover:border-[var(--color-brand-primary)]/30 transition-all duration-200 text-sm"
>
<ArrowLeft className="w-4 h-4 mr-2" />
@@ -23,13 +23,13 @@ function BenefitCard({ benefit }: { benefit: string }) {
return (
<StaggerItem>
<InkCard
className="p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-colors"
className="p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 transition-colors"
hoverScale={1.02}
hoverShadow="0 20px 40px rgba(196, 30, 58, 0.08)"
>
{numberInfo && (
<div className="mb-4">
<span className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-[#C41E3A] to-[#E85D75] bg-clip-text text-transparent">
<span className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] bg-clip-text text-transparent">
<CountUp
end={numberInfo.number}
suffix={numberInfo.suffix}
@@ -5,7 +5,7 @@ import { RippleButton } from '@/components/ui/ripple-button';
export function ProductCTASection() {
return (
<section className="relative py-24 md:py-32 bg-gradient-to-r from-[#C41E3A] to-[#E85D75] overflow-hidden">
<section className="relative py-24 md:py-32 bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] overflow-hidden">
{/* 右上角装饰圆形 */}
<FloatingElement amplitude={8} duration={5} delay={0.5} className="absolute -top-20 -right-20 pointer-events-none">
<div className="w-[280px] h-[280px] bg-white/10 rounded-full" />
@@ -36,7 +36,7 @@ export function ProductCTASection() {
href="/contact"
variant="secondary"
rippleColor="rgba(196, 30, 58, 0.3)"
className="bg-white text-[#C41E3A] px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center w-full sm:w-auto"
className="bg-white text-[var(--color-brand-primary)] px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center w-full sm:w-auto"
>
</RippleButton>
@@ -33,7 +33,7 @@ function FeatureItem({
<div className="order-2 md:order-1">
{/* 编号 - InkReveal 模糊揭示 */}
<InkReveal delay={0}>
<span className="block text-7xl md:text-8xl font-mono text-[#C41E3A]/10 mb-4">
<span className="block text-7xl md:text-8xl font-mono text-[var(--color-brand-primary)]/10 mb-4">
{number}
</span>
</InkReveal>
@@ -63,8 +63,8 @@ function FeatureItem({
hoverShadow="0 25px 50px rgba(196, 30, 58, 0.15)"
>
<PulseElement scale={1.08} duration={2.5}>
<div className="w-24 h-24 rounded-full bg-[#C41E3A]/10 flex items-center justify-center">
<div className="w-12 h-12 rounded-full bg-[#C41E3A]/20" />
<div className="w-24 h-24 rounded-full bg-[var(--color-brand-primary)]/10 flex items-center justify-center">
<div className="w-12 h-12 rounded-full bg-[var(--color-brand-primary)]/20" />
</div>
</PulseElement>
</InkCard>
@@ -113,15 +113,16 @@ export function ProductFeaturesSection({ product }: ProductFeaturesSectionProps)
<div className="flex flex-col sm:flex-row gap-3 justify-center">
<RippleButton
href="/contact"
rippleColor="rgba(196, 30, 58, 0.3)"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
variant="outline"
rippleColor="rgba(196, 30, 58, 0.2)"
className="border-2 border-[var(--color-brand-primary)] text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)] hover:text-white px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
>
</RippleButton>
<RippleButton
href="/contact"
rippleColor="rgba(196, 30, 58, 0.2)"
className="border border-[#E5E5E5] text-[#5C5C5C] hover:text-[#C41E3A] hover:border-[#C41E3A]/30 px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
className="border border-[#E5E5E5] text-[#5C5C5C] hover:text-[var(--color-brand-primary)] hover:border-[var(--color-brand-primary)]/30 px-6 py-3 rounded-lg font-semibold inline-flex items-center justify-center"
>
</RippleButton>
@@ -51,7 +51,7 @@ export function ProductHeroSection({ product }: ProductHeroSectionProps) {
<InkBackground />
<DataParticleFlow
particleCount={80}
color="#C41E3A"
color="var(--color-brand-primary)"
intensity="subtle"
shape="square"
effect="pulse"
@@ -63,7 +63,7 @@ export function ProductHeroSection({ product }: ProductHeroSectionProps) {
{/* 分类标签 - 印章按压效果 */}
<SealStamp
delay={0.1}
className="inline-block px-4 py-2 bg-[#C41E3A]/20 rounded-full text-[#C41E3A] text-sm mb-6"
className="inline-block px-4 py-2 bg-[var(--color-brand-primary)]/20 rounded-full text-[var(--color-brand-primary)] text-sm mb-6"
>
</SealStamp>
@@ -22,7 +22,7 @@ export function ProductOverviewSection({ product }: ProductOverviewSectionProps)
{/* 朱砂红装饰线 - InkReveal 入场 */}
<InkReveal delay={0.2}>
<div className="w-16 h-1 bg-[#C41E3A] rounded-full mb-8" />
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-8" />
</InkReveal>
{/* 概述文字 - InkReveal 包裹整段,替代 TextReveal */}
@@ -35,14 +35,14 @@ function PricingCard({
className={`
relative p-6 md:p-8 rounded-2xl
${isRecommended
? 'bg-white border-2 border-[#C41E3A] text-[#1C1C1C]'
? 'bg-white border-2 border-[var(--color-brand-primary)] text-[#1C1C1C]'
: 'bg-white border border-[#E5E5E5]'
}
`}
>
{isRecommended && (
<PulseElement scale={1.08} duration={2} className="absolute -top-3 left-1/2 -translate-x-1/2 z-10">
<div className="bg-[#C41E3A] text-white px-4 py-1 rounded-full text-sm font-semibold whitespace-nowrap">
<div className="bg-[var(--color-brand-primary)] text-white px-4 py-1 rounded-full text-sm font-semibold whitespace-nowrap">
</div>
</PulseElement>
@@ -57,7 +57,7 @@ function PricingCard({
<ul className="space-y-3 mb-8">
{features.map((feature, index) => (
<li key={index} className="flex items-center gap-2">
<Check className="w-5 h-5 text-[#C41E3A]" />
<Check className="w-5 h-5 text-[var(--color-brand-primary)]" />
<span className="text-[#5C5C5C]">
{feature}
</span>
@@ -71,8 +71,8 @@ function PricingCard({
className={`
block w-full py-3 rounded-lg font-semibold text-center
${isRecommended
? 'bg-[#C41E3A] text-white'
: 'border border-[#E5E5E5] text-[#5C5C5C] hover:text-[#C41E3A] hover:border-[#C41E3A]/30 bg-white'
? 'bg-[var(--color-brand-primary)] text-white'
: 'border border-[#E5E5E5] text-[#5C5C5C] hover:text-[var(--color-brand-primary)] hover:border-[var(--color-brand-primary)]/30 bg-white'
}
`}
>
@@ -26,12 +26,12 @@ function ProcessStep({
<StaggerItem className="flex items-start gap-6">
<div className="flex-shrink-0">
<SealStamp delay={index * 0.15}>
<div className="w-12 h-12 rounded-full bg-[#C41E3A] flex items-center justify-center text-white font-bold text-lg">
<div className="w-12 h-12 rounded-full bg-[var(--color-brand-primary)] flex items-center justify-center text-white font-bold text-lg">
{index + 1}
</div>
</SealStamp>
{index < total - 1 && (
<div className="w-0.5 h-16 bg-gradient-to-b from-[#C41E3A]/40 to-[#C41E3A]/10 ml-6 mt-2" />
<div className="w-0.5 h-16 bg-gradient-to-b from-[var(--color-brand-primary)]/40 to-[var(--color-brand-primary)]/10 ml-6 mt-2" />
)}
</div>
@@ -25,11 +25,11 @@ export function ProductSpecsSection({ product }: ProductSpecsSectionProps) {
{product.specs.map((spec, index) => (
<StaggerItem key={index}>
<InkCard
className="flex items-center gap-4 p-4 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-colors"
className="flex items-center gap-4 p-4 bg-white rounded-lg border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 transition-colors"
hoverScale={1.02}
hoverShadow="0 12px 24px rgba(196, 30, 58, 0.06)"
>
<div className="w-1 h-8 bg-[#C41E3A] rounded-full flex-shrink-0" />
<div className="w-1 h-8 bg-[var(--color-brand-primary)] rounded-full flex-shrink-0" />
<span className="text-[#1C1C1C]">{spec}</span>
</InkCard>
</StaggerItem>
+18 -14
View File
@@ -6,13 +6,13 @@ import { useRef } from 'react';
import { StaticLink } from '@/components/ui/static-link';
import { RippleButton } from '@/components/ui/ripple-button';
import { COMPANY_INFO } from '@/lib/constants';
import { ArrowRight, CheckCircle2 } from 'lucide-react';
import { ArrowRight, Target, HeartHandshake, Award } from 'lucide-react';
import { useReducedMotion } from '@/hooks/use-reduced-motion';
const VALUES = [
{ title: '务实', description: '不追逐风口,只做真正为客户创造价值的事。' },
{ title: '陪伴', description: '交付只是开始,长期陪跑才是我们的承诺。' },
{ title: '专业', description: '用扎实的工程能力和行业经验赢得信任。' },
{ title: '务实', description: '不追逐风口,只做真正为客户创造价值的事。', icon: Target },
{ title: '陪伴', description: '交付只是开始,长期陪跑才是我们的承诺。', icon: HeartHandshake },
{ title: '专业', description: '用扎实的工程能力和行业经验赢得信任。', icon: Award },
];
export function AboutSection() {
@@ -21,7 +21,7 @@ export function AboutSection() {
const shouldReduceMotion = useReducedMotion();
return (
<section id="about" role="region" aria-labelledby="about-heading" className="py-24 bg-[#FAFAFA] relative" ref={ref}>
<section id="about" role="region" aria-labelledby="about-heading" className="py-24 bg-[#F5F5F5] relative" ref={ref}>
{/* 网格背景 */}
<div className="absolute inset-0 bg-[linear-gradient(rgba(28,28,28,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(28,28,28,0.02)_1px,transparent_1px)] bg-size-[40px_40px]" />
@@ -34,8 +34,9 @@ export function AboutSection() {
>
{/* 标题 */}
<div className="text-center mb-12">
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
<h2 id="about-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
<span className="tracking-tight text-[#C41E3A]" style={{ fontFamily: "var(--font-aoyagi-reisho), 'Aoyagi Reisho', 'Ma Shan Zheng', 'ZCOOL XiaoWei', 'STKaiti', 'KaiTi', serif", fontWeight: 'normal', WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale', textRendering: 'optimizeLegibility' }}>{COMPANY_INFO.shortName}</span>
<span className="tracking-tight text-[var(--color-brand-primary)]" style={{ fontFamily: "var(--font-aoyagi-reisho), 'Aoyagi Reisho', 'Ma Shan Zheng', 'ZCOOL XiaoWei', 'STKaiti', 'KaiTi', serif", fontWeight: 'normal', WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale', textRendering: 'optimizeLegibility' }}>{COMPANY_INFO.shortName}</span>
</h2>
<p className="text-lg text-[#5C5C5C] mb-8">
{COMPANY_INFO.slogan}
@@ -49,9 +50,9 @@ export function AboutSection() {
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.1 }}
className="bg-white rounded-2xl p-8 mb-12 border border-[#E5E5E5]"
>
<p className="text-lg text-[#5C5C5C] leading-relaxed text-center mb-6">
&ldquo;&lsquo;&rsquo;&lsquo;&rsquo;&rdquo;
</p>
<blockquote className="text-lg text-[#5C5C5C] leading-relaxed text-center mb-6 italic">
<p>&ldquo;&lsquo;&rsquo;&lsquo;&rsquo;&rdquo;</p>
</blockquote>
<p className="text-[#1C1C1C] font-medium text-center">
</p>
@@ -64,18 +65,21 @@ export function AboutSection() {
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.15 }}
className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16"
>
{VALUES.map((value) => (
{VALUES.map((value) => {
const Icon = value.icon;
return (
<div
key={value.title}
className="bg-white rounded-xl p-6 border border-[#E5E5E5] text-center"
>
<div className="w-10 h-10 bg-[#C41E3A]/10 rounded-full flex items-center justify-center mx-auto mb-3">
<CheckCircle2 className="w-5 h-5 text-[#C41E3A]" />
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-full flex items-center justify-center mx-auto mb-3">
<Icon className="w-5 h-5 text-[var(--color-brand-primary)]" />
</div>
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{value.title}</h3>
<p className="text-sm text-[#5C5C5C] leading-relaxed">{value.description}</p>
</div>
))}
);
})}
</motion.div>
{/* CTA */}
@@ -86,7 +90,7 @@ export function AboutSection() {
className="text-center"
>
<StaticLink href="/about">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[#C41E3A] hover:text-[#C41E3A] transition-colors">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary)] transition-colors">
<ArrowRight className="w-4 h-4" />
</RippleButton>
+9 -16
View File
@@ -7,7 +7,6 @@ import { StaticLink } from '@/components/ui/static-link';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { TouchSwipe } from '@/components/ui/touch-swipe';
import { CASES } from '@/lib/constants';
import { ArrowRight, Building2, Hotel, Factory, Landmark, Sprout, TrendingUp } from 'lucide-react';
@@ -26,7 +25,7 @@ const industryColorMap: Record<string, { bg: string; icon: string; badge: string
'智慧农业': { bg: 'from-green-50 to-lime-50', icon: 'text-green-600', badge: 'bg-green-50 text-green-700' },
};
const defaultColors = { bg: 'from-[#F5F5F5] to-[#EDEDED]', icon: 'text-[#C41E3A]/30', badge: 'bg-white/90 text-[#1C1C1C]' };
const defaultColors = { bg: 'from-[#F5F5F5] to-[#EDEDED]', icon: 'text-[var(--color-brand-primary)]/30', badge: 'bg-white/90 text-[#1C1C1C]' };
export function CasesSection() {
const ref = useRef(null);
@@ -45,19 +44,14 @@ export function CasesSection() {
className="text-center max-w-3xl mx-auto mb-16"
>
<h2 id="cases-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h2>
<p className="text-lg text-[#5C5C5C] max-w-2xl mx-auto">
</p>
</motion.div>
<TouchSwipe
onSwipeLeft={() => {}}
onSwipeRight={() => {}}
className="md:hidden"
>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{CASES.map((caseItem, index) => {
const IndustryIcon = industryIconMap[caseItem.industry] || Building2;
const colors = industryColorMap[caseItem.industry] || defaultColors;
@@ -69,7 +63,7 @@ export function CasesSection() {
transition={{ duration: 0.5, delay: 0.1 + index * 0.1 }}
>
<StaticLink href={`/cases/${caseItem.id}`}>
<Card className="h-full group cursor-pointer border-[#E5E5E5] hover:border-[#C41E3A]/40 hover:shadow-lg hover:-translate-y-1 transition-all duration-300 overflow-hidden">
<Card className="h-full group cursor-pointer border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/40 hover:shadow-lg hover:-translate-y-1 transition-all duration-300 overflow-hidden">
{/* 行业图标区域 - 使用差异化配色 */}
<div className={`relative h-44 bg-gradient-to-br ${colors.bg} flex items-center justify-center overflow-hidden`}>
{/* 装饰性几何元素 */}
@@ -92,7 +86,7 @@ export function CasesSection() {
key={i}
className="inline-flex items-center gap-1 text-[10px] px-2 py-0.5 bg-white/80 backdrop-blur-sm rounded-full text-[#1C1C1C] font-medium"
>
<TrendingUp className="w-2.5 h-2.5 text-[#C41E3A]" />
<TrendingUp className="w-2.5 h-2.5 text-[var(--color-brand-primary)]" />
{result.value}
</span>
))}
@@ -102,16 +96,16 @@ export function CasesSection() {
<CardContent className="p-5">
<div className="flex items-center gap-2 mb-2">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full shrink-0" />
<span className="text-xs text-[#5C5C5C] truncate">{caseItem.client}</span>
</div>
<h3 className="text-base font-semibold text-[#1C1C1C] mb-2 group-hover:text-[#C41E3A] transition-colors line-clamp-2">
<h3 className="text-base font-semibold text-[#1C1C1C] mb-2 group-hover:text-[var(--color-brand-primary)] transition-colors line-clamp-2">
{caseItem.title}
</h3>
<p className="text-[#5C5C5C] text-sm line-clamp-2 mb-3">
{caseItem.description}
</p>
<div className="flex items-center text-[#C41E3A] text-xs font-medium opacity-0 group-hover:opacity-100 transition-opacity">
<div className="flex items-center text-[var(--color-brand-primary)] text-xs font-medium opacity-0 md:group-hover:opacity-100 md:opacity-0 transition-opacity">
<ArrowRight className="ml-1 w-3 h-3" />
</div>
@@ -121,8 +115,7 @@ export function CasesSection() {
</motion.div>
);
})}
</div>
</TouchSwipe>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
+19 -13
View File
@@ -136,7 +136,7 @@ export function ContactSection() {
}
return (
<section id="contact" role="region" aria-labelledby="contact-heading" className="section-padding relative bg-white overflow-hidden" ref={sectionRef}>
<section id="contact" role="region" aria-labelledby="contact-heading" className="py-24 relative bg-white overflow-hidden" ref={sectionRef}>
{showToast && (
<Toast
message={toastMessage}
@@ -157,11 +157,11 @@ export function ContactSection() {
`}
>
<div className="flex items-center gap-3 mb-4">
<div className="w-8 h-px bg-linear-to-r from-[#1C1C1C] to-[#C41E3A]" />
<div className="w-8 h-px bg-linear-to-r from-[#1C1C1C] to-[var(--color-brand-primary)]" />
<span className="text-sm text-[#5C5C5C] tracking-wide"></span>
</div>
<h2 id="contact-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
<span className="text-[#C41E3A]"></span>
<span className="text-[var(--color-brand-primary)]"></span>
</h2>
<p className="mt-4 text-[#5C5C5C] max-w-2xl">
@@ -180,19 +180,19 @@ export function ContactSection() {
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-6"></h3>
<div className="space-y-4">
<div className="flex items-start gap-4 group">
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<Mail className="w-5 h-5 text-white" />
</div>
<div>
<p className="text-sm text-[#5C5C5C] mb-1"></p>
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[#C41E3A] transition-colors duration-200">
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[var(--color-brand-primary)] transition-colors duration-200">
{COMPANY_INFO.email}
</a>
</div>
</div>
<div className="flex items-start gap-4 group">
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
<MapPin className="w-5 h-5 text-white" />
</div>
<div>
@@ -205,33 +205,33 @@ export function ContactSection() {
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]" aria-label="工作时间" data-testid="work-hours-card">
<div className="flex items-center gap-2 mb-3">
<Clock className="w-4 h-4 text-[#C41E3A]" />
<Clock className="w-4 h-4 text-[var(--color-brand-primary)]" />
<h4 className="text-sm font-medium text-[#1C1C1C]"></h4>
</div>
<div className="space-y-1">
<div className="flex justify-between text-sm" data-testid="work-hours-row">
<span className="text-[#5C5C5C]"></span>
<span className="text-[#C41E3A]">9:00 - 18:00</span>
<span className="text-[var(--color-brand-primary)]">9:00 - 18:00</span>
</div>
</div>
</div>
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]">
<div className="flex items-center gap-2 mb-3">
<HeadphonesIcon className="w-4 h-4 text-[#C41E3A]" />
<HeadphonesIcon className="w-4 h-4 text-[var(--color-brand-primary)]" />
<h4 className="text-sm font-medium text-[#1C1C1C]"></h4>
</div>
<div className="space-y-3">
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
<p className="text-sm text-[#5C5C5C]"> 2 </p>
</div>
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
<p className="text-sm text-[#5C5C5C]"></p>
</div>
<div className="flex items-start gap-2">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
<p className="text-sm text-[#5C5C5C]"></p>
</div>
</div>
@@ -277,11 +277,17 @@ export function ContactSection() {
{isSubmitted ? (
<div className="text-center py-12 flex-1 flex items-center justify-center" data-testid="success-message">
<div className="w-16 h-16 bg-[#C41E3A] rounded-full flex items-center justify-center mx-auto mb-4 animate-stamp-in">
<div className="w-16 h-16 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center mx-auto mb-4 animate-stamp-in">
<CheckCircle2 className="w-8 h-8 text-white" />
</div>
<h4 className="text-xl font-semibold text-[#1A1A2E] mb-2"></h4>
<p className="text-[#718096]"></p>
<button
onClick={() => setIsSubmitted(false)}
className="mt-6 inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary)] transition-colors"
>
</button>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-5 flex-1 flex flex-col">
+10 -9
View File
@@ -80,7 +80,7 @@ export function HeroDescription(_props: HeroContentProps) {
<div className="mb-10">
<BlurReveal delay={0.3}>
<p className="text-xl sm:text-2xl text-[#4A5568] mb-4">
<span className="font-semibold bg-gradient-to-r from-[#C41E3A] via-[#E04A68] to-[#C41E3A] bg-clip-text text-transparent">
<span className="font-semibold bg-gradient-to-r from-[var(--color-brand-primary)] via-[#E04A68] to-[var(--color-brand-primary)] bg-clip-text text-transparent">
</span>
</p>
@@ -110,7 +110,7 @@ export function HeroButtons({ isVisible }: HeroContentProps) {
className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-8"
>
<MagneticButton strength={0.4}>
<Button size="lg" className="min-w-45 bg-[#C41E3A] hover:bg-[#A01830] text-white shadow-[0_6px_20px_rgba(196,30,58,0.3)]" asChild>
<Button size="lg" className="min-w-45 bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white shadow-[0_6px_20px_rgba(196,30,58,0.3)]" asChild>
<StaticLink href="/contact">
<ArrowRight className="w-4 h-4 ml-2" />
@@ -149,9 +149,9 @@ export function HeroFeatures({ isVisible }: HeroContentProps) {
animate={isVisible ? { opacity: 1, scale: 1 } : {}}
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.4, delay: 0.4 + index * 0.1 }}
whileHover={shouldReduceMotion ? {} : { scale: 1.05, y: -2 }}
className="flex items-center gap-2 px-4 py-2 rounded-full bg-[#FAFAFA] border border-[#E5E5E5] transition-all duration-300 hover:border-[#1C1C1C] hover:shadow-md cursor-default"
className="flex items-center gap-2 px-4 py-2 rounded-full bg-[#FAFAFA] border border-[#E5E5E5] transition-all duration-300 hover:border-[#1C1C1C] hover:shadow-md cursor-pointer"
>
<feature.icon className="w-4 h-4 text-[#C41E3A]" />
<feature.icon className="w-4 h-4 text-[var(--color-brand-primary)]" />
<span className="text-sm text-[#3D3D3D]">{feature.text}</span>
</motion.div>
))}
@@ -188,7 +188,7 @@ export function HeroStats() {
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.4 }}
className="pt-16 border-t border-[#E2E8F0]"
>
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-12">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-12">
{STATS.map((stat, index) => (
<HeroStatItem
key={stat.label}
@@ -209,8 +209,9 @@ function HeroStatItem({ stat, index, shouldAnimate, shouldReduceMotion }: {
shouldAnimate: boolean;
shouldReduceMotion: boolean;
}) {
const numericValue = parseInt(stat.value.replace(/\D/g, ''));
const suffix = stat.value.replace(/[\d]/g, '');
const numMatch = stat.value.match(/[\d.]+/);
const numericValue = numMatch ? parseFloat(numMatch[0]) : 0;
const suffix = stat.value.replace(/[\d.]+/, '');
return (
<motion.div
@@ -220,13 +221,13 @@ function HeroStatItem({ stat, index, shouldAnimate, shouldReduceMotion }: {
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.5, delay: index * 0.1, type: 'spring', stiffness: 100 }}
whileHover={shouldReduceMotion ? {} : { scale: 1.05, y: -5 }}
>
<div className="text-4xl sm:text-5xl font-bold text-[#C41E3A] mb-3">
<div className="text-4xl sm:text-5xl font-bold text-[var(--color-brand-primary)] mb-3">
{shouldAnimate ? (
<CountUp
end={numericValue}
suffix={suffix}
duration={2000}
className="text-4xl sm:text-5xl font-bold text-[#C41E3A]"
className="text-4xl sm:text-5xl font-bold text-[var(--color-brand-primary)]"
/>
) : (
<span className="text-[#CBD5E0]">0{suffix}</span>
+4 -4
View File
@@ -46,19 +46,19 @@ export function HeroSection({ heroStats }: { heroStats: ReactNode }) {
id="home"
ref={sectionRef}
aria-labelledby="hero-heading"
className="relative min-h-screen flex items-center overflow-hidden bg-linear-to-b from-[#FAFAFA] to-white"
className="relative min-h-[85vh] flex items-center overflow-hidden bg-linear-to-b from-[#FAFAFA] to-white"
>
<InkBackground />
<DataParticleFlow
particleCount={60}
color="#C41E3A"
color="var(--color-brand-primary)"
intensity="subtle"
shape="square"
effect="pulse"
/>
<SubtleDots color="#C41E3A" count={8} />
<SubtleDots color="var(--color-brand-primary)" count={8} />
<div className="container-wide py-24 md:py-32 lg:py-40 relative z-10">
<div className="container-wide py-16 md:py-24 lg:py-32 relative z-10">
<div className="max-w-4xl mx-auto text-center">
<HeroContent isVisible={isVisible} />
<HeroTitle isVisible={isVisible} />
+1 -1
View File
@@ -9,7 +9,7 @@ export function HeroStatsSSR() {
key={stat.label}
className="group cursor-default text-center"
>
<div className="text-4xl sm:text-5xl font-bold text-[#C41E3A] mb-3">
<div className="text-4xl sm:text-5xl font-bold text-[var(--color-brand-primary)] mb-3">
{stat.value}
</div>
<div className="text-sm text-[#718096] group-hover:text-[#4A5568] transition-colors">
@@ -19,14 +19,14 @@ const SOLUTIONS_OVERVIEW = [
icon: Cpu,
title: '技术伙伴',
subtitle: '信息技术解决方案',
description: '让技术真正为业务服务。不追逐"最火"的技术,只选择"最对"的技术。',
description: '全栈技术能力,从设计到交付提供一站式技术服务',
points: ['业务场景调研', '技术方案定制', '敏捷交付迭代'],
},
{
icon: Users,
title: '同行伙伴',
subtitle: '长期陪跑服务',
description: '交付只是开始,陪伴才是常态。项目上线那天,是我们真正成为伙伴的开始。',
description: '长期陪伴成长,持续优化迭代,与客户共同进步',
points: ['专属客户成功经理', '季度业务复盘', '7×24小时响应'],
},
];
@@ -36,7 +36,7 @@ export function HomeSolutionsSection() {
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section id="solutions" role="region" aria-labelledby="solutions-heading" className="py-24 bg-[#F5F7FA] relative overflow-hidden" ref={ref}>
<section id="solutions" role="region" aria-labelledby="solutions-heading" className="py-24 bg-[#F5F5F5] relative overflow-hidden" ref={ref}>
<div className="container-wide relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -44,8 +44,9 @@ export function HomeSolutionsSection() {
transition={{ duration: 0.6 }}
className="text-center max-w-3xl mx-auto mb-16"
>
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
<h2 id="solutions-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h2>
<p className="text-lg text-[#5C5C5C]">
@@ -62,17 +63,17 @@ export function HomeSolutionsSection() {
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.1 + idx * 0.15 }}
>
<div className="bg-white rounded-2xl p-8 border border-[#E5E5E5] hover:border-[#C41E3A]/30 hover:shadow-lg transition-all duration-300 h-full flex flex-col">
<div className="w-14 h-14 bg-[#C41E3A] rounded-2xl flex items-center justify-center mb-6">
<div className="bg-white rounded-2xl p-8 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-lg transition-all duration-300 h-full flex flex-col">
<div className="w-14 h-14 bg-[var(--color-brand-primary)] rounded-2xl flex items-center justify-center mb-6">
<Icon className="w-7 h-7 text-white" />
</div>
<h3 className="text-xl font-bold text-[#1C1C1C] mb-1">{item.title}</h3>
<p className="text-sm text-[#C41E3A] font-medium mb-3">{item.subtitle}</p>
<p className="text-sm text-[var(--color-brand-primary)] font-medium mb-3">{item.subtitle}</p>
<p className="text-sm text-[#5C5C5C] leading-relaxed mb-6 flex-1">{item.description}</p>
<ul className="space-y-2">
{item.points.map((point, i) => (
<li key={i} className="flex items-start gap-2 text-sm text-[#1C1C1C]">
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-1.5 shrink-0" />
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-1.5 shrink-0" />
{point}
</li>
))}
@@ -4,7 +4,9 @@ import { motion } from 'framer-motion';
import { useInView } from 'framer-motion';
import { useRef } from 'react';
import { METHODOLOGY } from '@/lib/constants/methodology';
import { CheckCircle2 } from 'lucide-react';
import { CheckCircle2, ArrowRight } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { StaticLink } from '@/components/ui/static-link';
export function MethodologySection() {
const ref = useRef(null);
@@ -20,7 +22,7 @@ export function MethodologySection() {
className="text-center max-w-3xl mx-auto mb-16"
>
<h2 id="methodology-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h2>
<p className="text-lg text-[#5C5C5C]">
@@ -29,7 +31,7 @@ export function MethodologySection() {
<div className="relative">
{/* 连接线 */}
<div className="hidden lg:block absolute top-24 left-[12.5%] right-[12.5%] h-0.5 bg-gradient-to-r from-[#C41E3A]/20 via-[#C41E3A]/40 to-[#C41E3A]/20" />
<div className="hidden lg:block absolute top-24 left-[12.5%] right-[12.5%] h-0.5 bg-gradient-to-r from-[var(--color-brand-primary)]/20 via-[var(--color-brand-primary)]/40 to-[var(--color-brand-primary)]/20" />
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{METHODOLOGY.map((phase, idx) => (
@@ -39,14 +41,14 @@ export function MethodologySection() {
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.2 + idx * 0.15 }}
>
<div className="relative bg-[#FFFBF5] rounded-2xl p-8 border border-[#E5E5E5] hover:border-[#C41E3A]/30 hover:shadow-lg transition-all duration-300 h-full">
<div className="relative bg-[#FFFBF5] rounded-2xl p-8 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-lg transition-all duration-300 h-full">
{/* 阶段编号 */}
<div className="w-12 h-12 bg-[#C41E3A] rounded-full flex items-center justify-center mb-6 text-white font-bold text-xl">
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center mb-6 text-white font-bold text-xl">
{phase.number}
</div>
<h3 className="text-xl font-bold text-[#1C1C1C] mb-1">{phase.title}</h3>
<p className="text-sm text-[#C41E3A] font-medium mb-3">{phase.subtitle}</p>
<p className="text-sm text-[var(--color-brand-primary)] font-medium mb-3">{phase.subtitle}</p>
<p className="text-sm text-[#5C5C5C] leading-relaxed mb-6">{phase.description}</p>
<div className="mb-4">
@@ -54,7 +56,7 @@ export function MethodologySection() {
<ul className="space-y-1.5">
{phase.activities.map((activity, i) => (
<li key={i} className="flex items-start gap-2 text-xs text-[#3D3D3D]">
<CheckCircle2 className="w-3.5 h-3.5 text-[#C41E3A] mt-0.5 shrink-0" />
<CheckCircle2 className="w-3.5 h-3.5 text-[var(--color-brand-primary)] mt-0.5 shrink-0" />
{activity}
</li>
))}
@@ -66,7 +68,7 @@ export function MethodologySection() {
<ul className="space-y-1.5">
{phase.deliverables.map((deliverable, i) => (
<li key={i} className="flex items-start gap-2 text-xs text-[#5C5C5C]">
<span className="w-1.5 h-1.5 bg-[#C41E3A]/60 rounded-full mt-1.5 shrink-0" />
<span className="w-1.5 h-1.5 bg-[var(--color-brand-primary)]/60 rounded-full mt-1.5 shrink-0" />
{deliverable}
</li>
))}
@@ -77,6 +79,20 @@ export function MethodologySection() {
))}
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.6 }}
className="text-center mt-12"
>
<Button variant="outline" size="lg" className="group" asChild>
<StaticLink href="/contact">
<ArrowRight className="ml-2 w-4 h-4 transition-transform group-hover:translate-x-1" />
</StaticLink>
</Button>
</motion.div>
</div>
</section>
);
+11 -10
View File
@@ -5,6 +5,7 @@ import { useInView } from 'framer-motion';
import { useRef } from 'react';
import { StaticLink } from '@/components/ui/static-link';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { ArrowRight, Calendar } from 'lucide-react';
import { NEWS } from '@/lib/constants';
@@ -18,15 +19,16 @@ export function NewsSection() {
return (
<section id="news" role="region" aria-labelledby="news-heading" className="py-24 bg-[#F5F5F5]" ref={ref}>
<div className="container-custom">
<div className="container-wide">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5 }}
className="text-center max-w-3xl mx-auto mb-16"
>
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
<h2 id="news-heading" className="text-3xl sm:text-4xl lg:text-5xl font-bold text-[#1C1C1C] mb-6">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h2>
<p className="text-lg text-[#5C5C5C]">
@@ -61,7 +63,7 @@ export function NewsSection() {
</CardDescription>
<StaticLink
href={`/news/${newsItem.id}`}
className="inline-flex items-center text-sm font-medium text-[#1C1C1C] hover:text-[#C41E3A] transition-colors group/link"
className="inline-flex items-center text-sm font-medium text-[#1C1C1C] hover:text-[var(--color-brand-primary)] transition-colors group/link"
>
<ArrowRight className="ml-1 w-4 h-4 transition-transform group-hover/link:translate-x-1" />
@@ -83,13 +85,12 @@ export function NewsSection() {
transition={{ duration: 0.6, delay: 0.5 }}
className="mt-12 text-center"
>
<StaticLink
href="/news"
className="inline-flex items-center text-sm font-medium text-[#1C1C1C] hover:text-[#C41E3A] transition-colors bg-transparent border-none cursor-pointer group"
>
<ArrowRight className="ml-1 w-4 h-4 transition-transform group-hover:translate-x-1" />
</StaticLink>
<Button variant="outline" size="lg" className="group" asChild>
<StaticLink href="/news">
<ArrowRight className="ml-2 w-4 h-4 transition-transform group-hover:translate-x-1" />
</StaticLink>
</Button>
</motion.div>
</div>
</section>
+11 -11
View File
@@ -16,8 +16,8 @@ export function ProductsSection() {
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section id="products" role="region" aria-labelledby="products-heading" className="py-24 bg-[#F5F7FA] relative overflow-hidden" ref={ref}>
<div className="absolute top-1/2 left-0 w-[400px] h-[400px] bg-[rgba(79,70,229,0.03)] rounded-full blur-3xl" />
<section id="products" role="region" aria-labelledby="products-heading" className="py-24 bg-white relative overflow-hidden" ref={ref}>
<div className="absolute top-1/2 left-0 w-[400px] h-[400px] bg-[rgba(196,30,58,0.03)] rounded-full blur-3xl" />
<div className="absolute top-1/3 right-0 w-[300px] h-[300px] bg-[rgba(196,30,58,0.02)] rounded-full blur-3xl" />
<div className="container-wide relative z-10">
<motion.div
@@ -26,9 +26,9 @@ export function ProductsSection() {
transition={{ duration: 0.6 }}
className="text-left max-w-3xl mx-auto mb-16"
>
<div className="w-16 h-1 bg-[#C41E3A] rounded-full mb-6" />
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
<h2 id="products-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h2>
<p className="text-lg text-[#5C5C5C]">
@@ -36,11 +36,11 @@ export function ProductsSection() {
</motion.div>
{PRODUCTS.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 items-stretch">
{PRODUCTS.map((product) => (
<InkCard
key={product.id}
className="group cursor-pointer rounded-xl border border-[#E5E5E5] bg-white p-0 overflow-hidden hover:border-[#C41E3A] transition-colors"
className="group cursor-pointer rounded-xl border border-[#E5E5E5] bg-white p-0 overflow-hidden hover:border-[var(--color-brand-primary)] transition-colors"
>
<StaticLink href={`/products/${product.id}`}>
<Card className="h-full flex flex-col border-0 shadow-none bg-transparent">
@@ -63,7 +63,7 @@ export function ProductsSection() {
key={idx}
className="inline-flex items-center text-xs px-2 py-1 bg-[#FAFAFA] text-[#3D3D3D] rounded border border-[#E5E5E5]"
>
<Check className="w-3 h-3 mr-1 text-[#C41E3A]" />
<Check className="w-3 h-3 mr-1 text-[var(--color-brand-primary)]" />
{feature}
</span>
))}
@@ -72,20 +72,20 @@ export function ProductsSection() {
<div className="mb-4">
<p className="text-sm font-medium text-[#1C1C1C] mb-2 flex items-center">
<TrendingUp className="w-4 h-4 mr-1 text-[#C41E3A]" />
<TrendingUp className="w-4 h-4 mr-1 text-[var(--color-brand-primary)]" />
</p>
<ul className="space-y-1">
{product.benefits.map((benefit, idx) => (
<li key={idx} className="text-xs text-[#5C5C5C] flex items-start">
<span className="text-[#C41E3A] mr-1.5"></span>
<span className="text-[var(--color-brand-primary)] mr-1.5"></span>
{benefit}
</li>
))}
</ul>
</div>
<div className="w-full mt-auto px-4 py-2 text-center text-sm font-medium border border-[#E5E5E5] rounded-md group-hover:bg-[#A01830] group-hover:text-white group-hover:border-[#A01830] transition-colors">
<div className="w-full mt-auto px-4 py-2 text-center text-sm font-medium border border-[#E5E5E5] rounded-md group-hover:bg-[var(--color-brand-primary-hover)] group-hover:text-white group-hover:border-[var(--color-brand-primary-hover)] transition-colors">
<ArrowRight className="ml-2 w-4 h-4 inline" />
</div>
@@ -120,7 +120,7 @@ export function ProductsSection() {
</p>
<StaticLink href="/contact">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 bg-[#C41E3A] hover:bg-[#A01830] text-white rounded-lg text-sm font-medium transition-colors">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white rounded-lg text-sm font-medium transition-colors">
<ArrowRight className="w-4 h-4" />
</RippleButton>
+9 -9
View File
@@ -31,11 +31,11 @@ export function ServicesSection() {
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-left max-w-3xl mx-auto mb-16"
className="text-center max-w-3xl mx-auto mb-16"
>
<div className="w-16 h-1 bg-[#C41E3A] rounded-full mb-6" />
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
<h2 id="services-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h2>
<p className="text-lg text-[#5C5C5C] max-w-2xl">
@@ -43,23 +43,23 @@ export function ServicesSection() {
</motion.div>
{SERVICES.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div className="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-6">
{SERVICES.map((service) => {
const Icon = iconMap[service.icon];
return (
<InkCard
key={service.id}
className="rounded-xl border border-[#E5E5E5] bg-white p-6 hover:border-[#C41E3A] transition-colors"
className="rounded-xl border border-[#E5E5E5] bg-white p-6 hover:border-[var(--color-brand-primary)] transition-colors"
>
<StaticLink href={`/services/${service.id}`}>
<Card className="p-0 h-full border-0 shadow-none bg-transparent group cursor-pointer">
<CardContent className="p-0">
<div className="w-12 h-12 rounded-xl bg-[#F5F5F5] flex items-center justify-center mb-4 group-hover:bg-[#C41E3A] transition-all duration-300">
<div className="w-12 h-12 rounded-xl bg-[#F5F5F5] flex items-center justify-center mb-4 group-hover:bg-[var(--color-brand-primary)] transition-all duration-300">
{Icon && <Icon className="w-6 h-6 text-[#1C1C1C] group-hover:text-white transition-colors" />}
</div>
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-3 group-hover:text-[#C41E3A] transition-colors">{service.title}</h3>
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-3 group-hover:text-[var(--color-brand-primary)] transition-colors">{service.title}</h3>
<p className="text-[#5C5C5C] text-sm leading-relaxed">{service.description}</p>
<div className="mt-4 flex items-center text-[#C41E3A] text-sm font-medium opacity-0 group-hover:opacity-100 transition-opacity">
<div className="mt-4 flex items-center text-[var(--color-brand-primary)] text-sm font-medium opacity-0 md:group-hover:opacity-100 md:opacity-0 transition-opacity">
<ArrowRight className="ml-1 w-4 h-4" />
</div>
@@ -83,7 +83,7 @@ export function ServicesSection() {
className="text-center mt-12"
>
<StaticLink href="/services">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[#C41E3A] hover:text-[#C41E3A] transition-colors">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary)] transition-colors">
<ArrowRight className="w-4 h-4" />
</RippleButton>
+12 -11
View File
@@ -14,7 +14,7 @@ const TEAM_MEMBERS = [
specialties: ['企业战略', '数字化转型', '组织管理'],
bio: '15年+企业服务经验,深耕数字化转型领域,擅长从战略高度为企业规划数字化路径。',
icon: Target,
accentColor: 'from-[#C41E3A] to-[#E85D75]',
accentColor: 'from-[var(--color-brand-primary)] to-[#E85D75]',
},
{
name: '联合创始人兼CTO',
@@ -22,7 +22,7 @@ const TEAM_MEMBERS = [
specialties: ['系统架构', '云原生', '微服务'],
bio: '12年+技术架构经验,主导过多个大型企业级系统的设计与交付,精通分布式系统。',
icon: GraduationCap,
accentColor: 'from-[#C41E3A] to-[#D94466]',
accentColor: 'from-[var(--color-brand-primary)] to-[#D94466]',
},
{
name: '技术总监',
@@ -30,7 +30,7 @@ const TEAM_MEMBERS = [
specialties: ['全栈开发', '数据工程', 'DevOps'],
bio: '10年+全栈开发经验,专注于高质量软件交付和工程效能提升,推动敏捷实践落地。',
icon: Briefcase,
accentColor: 'from-[#C41E3A] to-[#C41E3A]',
accentColor: 'from-[var(--color-brand-primary)] to-[var(--color-brand-primary)]',
},
{
name: '咨询总监',
@@ -38,7 +38,7 @@ const TEAM_MEMBERS = [
specialties: ['业务咨询', '流程优化', '项目管理'],
bio: '10年+管理咨询经验,擅长客户需求深度分析和解决方案设计,确保项目精准落地。',
icon: Users,
accentColor: 'from-[#C41E3A] to-[#A01830]',
accentColor: 'from-[var(--color-brand-primary)] to-[var(--color-brand-primary-hover)]',
},
];
@@ -54,7 +54,7 @@ export function TeamSection() {
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section id="team" role="region" aria-labelledby="team-heading" className="py-24 bg-[#FAFAFA] relative overflow-hidden" ref={ref}>
<section id="team" role="region" aria-labelledby="team-heading" className="py-24 bg-white relative overflow-hidden" ref={ref}>
{/* 背景装饰 */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[600px] h-[600px] bg-[rgba(196,30,58,0.02)] rounded-full blur-3xl" />
@@ -66,8 +66,9 @@ export function TeamSection() {
transition={{ duration: 0.6 }}
className="text-center max-w-3xl mx-auto mb-16"
>
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
<h2 id="team-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
<span className="text-[#C41E3A] font-calligraphy"></span>
<span className="text-[var(--color-brand-primary)] font-calligraphy"></span>
</h2>
<p className="text-lg text-[#5C5C5C] leading-relaxed">
IT企业的核心团队
@@ -86,7 +87,7 @@ export function TeamSection() {
key={stat.label}
className="text-center py-4 px-3 bg-white rounded-xl border border-[#E5E5E5]"
>
<div className="text-2xl sm:text-3xl font-bold bg-gradient-to-r from-[#C41E3A] to-[#E85D75] bg-clip-text text-transparent mb-1">
<div className="text-2xl sm:text-3xl font-bold bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] bg-clip-text text-transparent mb-1">
{stat.value}
</div>
<div className="text-xs sm:text-sm text-[#5C5C5C]">{stat.label}</div>
@@ -105,7 +106,7 @@ export function TeamSection() {
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.2 + idx * 0.12 }}
>
<div className="bg-white rounded-2xl p-6 border border-[#E5E5E5] hover:border-[#C41E3A]/30 hover:shadow-lg transition-all duration-300 h-full flex flex-col group">
<div className="bg-white rounded-2xl p-6 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-lg transition-all duration-300 h-full flex flex-col group">
{/* 头像区 */}
<div className="flex items-center gap-4 mb-4">
<div className={`w-14 h-14 rounded-2xl bg-gradient-to-br ${member.accentColor} flex items-center justify-center shrink-0 group-hover:scale-105 transition-transform duration-300`}>
@@ -113,7 +114,7 @@ export function TeamSection() {
</div>
<div className="min-w-0">
<h3 className="text-base font-bold text-[#1C1C1C] truncate">{member.name}</h3>
<span className="text-xs text-[#C41E3A] font-medium">{member.initials}</span>
<span className="text-xs text-[var(--color-brand-primary)] font-medium">{member.initials}</span>
</div>
</div>
@@ -125,7 +126,7 @@ export function TeamSection() {
{member.specialties.map((spec) => (
<span
key={spec}
className="inline-flex items-center text-xs px-2.5 py-1 bg-[#FAFAFA] text-[#3D3D3D] rounded-full border border-[#E5E5E5] group-hover:border-[#C41E3A]/20 transition-colors"
className="inline-flex items-center text-xs px-2.5 py-1 bg-[#FAFAFA] text-[#3D3D3D] rounded-full border border-[#E5E5E5] group-hover:border-[var(--color-brand-primary)]/20 transition-colors"
>
{spec}
</span>
@@ -145,7 +146,7 @@ export function TeamSection() {
className="text-center"
>
<StaticLink href="/team">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[#C41E3A] hover:text-[#C41E3A] transition-colors">
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary)] transition-colors">
<ArrowRight className="w-4 h-4" />
</RippleButton>
@@ -44,7 +44,7 @@ export function ServiceCasesSection({ service }: { service: Service }) {
<StaggerItem key={caseItem.id}>
<StaticLink href={`/cases/${caseItem.id}`}>
<InkCard
className="p-6 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-colors h-full"
className="p-6 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 transition-colors h-full"
hoverScale={1.02}
>
{/* 行业标签 */}
@@ -30,7 +30,7 @@ export function ServiceChallengesSection({ service }: ServiceChallengesSectionPr
</ScrollReveal>
{/* 朱砂红装饰线 */}
<div className="w-16 h-1 bg-[#C41E3A] mb-6" />
<div className="w-16 h-1 bg-[var(--color-brand-primary)] mb-6" />
{/* 副标题描述 */}
<InkReveal delay={0.2}>
@@ -44,7 +44,7 @@ export function ServiceChallengesSection({ service }: ServiceChallengesSectionPr
{service.challenges.map((challenge, index) => (
<StaggerItem key={index}>
<InkCard
className="p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-colors"
className="p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 transition-colors"
hoverScale={1.02}
hoverShadow="0 20px 40px rgba(196, 30, 58, 0.08)"
>
@@ -12,7 +12,7 @@ import { RippleButton } from '@/components/ui/ripple-button';
export function ServiceCTASection() {
return (
<section className="relative py-24 md:py-32 bg-gradient-to-r from-[#C41E3A] to-[#E85D75] overflow-hidden">
<section className="relative py-24 md:py-32 bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] overflow-hidden">
{/* 右上角装饰圆形 */}
<FloatingElement amplitude={8} duration={5} delay={0.5} className="absolute -top-20 -right-20 pointer-events-none">
<div className="w-[280px] h-[280px] bg-white/10 rounded-full" />
@@ -46,7 +46,7 @@ export function ServiceCTASection() {
href="/contact"
variant="secondary"
rippleColor="rgba(196, 30, 58, 0.3)"
className="bg-white text-[#C41E3A] px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center w-full sm:w-auto"
className="bg-white text-[var(--color-brand-primary)] px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center w-full sm:w-auto"
>
</RippleButton>
@@ -41,7 +41,7 @@ export function ServiceFeaturesSection({ service }: ServiceFeaturesSectionProps)
{service.features.map((feature, index) => (
<StaggerItem key={index}>
<div className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-[#C41E3A] mt-0.5 flex-shrink-0" />
<CheckCircle2 className="w-5 h-5 text-[var(--color-brand-primary)] mt-0.5 flex-shrink-0" />
<FadeUp delay={index * 0.05}>
<span className="text-[#1C1C1C] leading-relaxed">{feature}</span>
</FadeUp>
@@ -61,7 +61,7 @@ export function ServiceHeroSection({ service }: ServiceHeroSectionProps) {
<InkBackground />
<DataParticleFlow
particleCount={60}
color="#C41E3A"
color="var(--color-brand-primary)"
intensity="subtle"
shape="square"
effect="pulse"
@@ -73,7 +73,7 @@ export function ServiceHeroSection({ service }: ServiceHeroSectionProps) {
{/* 分类标签 - 印章按压效果 */}
<SealStamp
delay={0.1}
className="inline-block px-4 py-2 bg-[#C41E3A]/20 rounded-full text-[#C41E3A] text-sm mb-6"
className="inline-block px-4 py-2 bg-[var(--color-brand-primary)]/20 rounded-full text-[var(--color-brand-primary)] text-sm mb-6"
>
</SealStamp>
@@ -98,14 +98,14 @@ export function ServiceHeroSection({ service }: ServiceHeroSectionProps) {
<RippleButton
href="/contact"
variant="outline"
className="border-2 border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A] hover:text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
className="border-2 border-[var(--color-brand-primary)] text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)] hover:text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
rippleColor="rgba(196, 30, 58, 0.2)"
>
</RippleButton>
<RippleButton
href="/contact"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
rippleColor="rgba(255, 255, 255, 0.3)"
>
@@ -113,7 +113,7 @@ export function ServiceHeroSection({ service }: ServiceHeroSectionProps) {
<RippleButton
href="#challenges"
variant="outline"
className="border-2 border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A]/5 px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
className="border-2 border-[var(--color-brand-primary)] text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)]/5 px-8 py-4 rounded-lg text-lg font-semibold inline-flex items-center justify-center"
rippleColor="rgba(196, 30, 58, 0.2)"
>
@@ -31,13 +31,13 @@ function OutcomeCard({ outcome }: { outcome: { value: string; label: string } })
return (
<StaggerItem>
<InkCard
className="p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[#C41E3A]/30 transition-colors text-center"
className="p-6 md:p-8 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 transition-colors text-center"
hoverScale={1.02}
>
{/* 数字动画区域 */}
<div className="mb-4">
{numberValue !== null ? (
<span className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-[#C41E3A] to-[#E85D75] bg-clip-text text-transparent">
<span className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] bg-clip-text text-transparent">
<CountUp
end={numberValue}
duration={2000}
@@ -46,7 +46,7 @@ function OutcomeCard({ outcome }: { outcome: { value: string; label: string } })
{outcome.value.replace(/(\d+)/, '')}
</span>
) : (
<span className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-[#C41E3A] to-[#E85D75] bg-clip-text text-transparent">
<span className="text-4xl md:text-5xl font-bold bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] bg-clip-text text-transparent">
{outcome.value}
</span>
)}
@@ -36,13 +36,13 @@ function ProcessStep({
{/* 左侧:编号圆形 + 渐变连接线 */}
<div className="flex-shrink-0">
<SealStamp delay={index * 0.15}>
<div className="w-12 h-12 rounded-full bg-[#C41E3A] flex items-center justify-center text-white font-bold text-lg">
<div className="w-12 h-12 rounded-full bg-[var(--color-brand-primary)] flex items-center justify-center text-white font-bold text-lg">
{index + 1}
</div>
</SealStamp>
{/* 最后一步不显示连接线 */}
{index < total - 1 && (
<div className="w-0.5 h-16 bg-gradient-to-b from-[#C41E3A]/40 to-[#C41E3A]/10 ml-6 mt-2" />
<div className="w-0.5 h-16 bg-gradient-to-b from-[var(--color-brand-primary)]/40 to-[var(--color-brand-primary)]/10 ml-6 mt-2" />
)}
</div>
@@ -29,11 +29,11 @@ export function AccompanySection() {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.4 }}
className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-12 border border-[#C41E3A]/20"
className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-12 border border-[var(--color-brand-primary)]/20"
>
{/* 标题区域:图标 + 标题 + 副标题 */}
<div className="flex items-start gap-6 mb-8">
<div className="w-16 h-16 bg-[#C41E3A] rounded-2xl flex items-center justify-center flex-shrink-0">
<div className="w-16 h-16 bg-[var(--color-brand-primary)] rounded-2xl flex items-center justify-center flex-shrink-0">
<Users className="w-8 h-8 text-white" />
</div>
<div>
@@ -73,7 +73,7 @@ export function AccompanySection() {
<div className="mb-8">
<ScrollReveal variants={slideInLeftVariants}>
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-4 flex items-center gap-2">
<CheckCircle2 className="w-6 h-6 text-[#C41E3A]" />
<CheckCircle2 className="w-6 h-6 text-[var(--color-brand-primary)]" />
</h3>
</ScrollReveal>
@@ -81,7 +81,7 @@ export function AccompanySection() {
{valuePoints.map((point) => (
<StaggerItem key={point}>
<div className="flex items-start gap-3 p-4 bg-white rounded-lg border border-[#E5E5E5]">
<div className="w-2 h-2 bg-[#C41E3A] rounded-full mt-2" />
<div className="w-2 h-2 bg-[var(--color-brand-primary)] rounded-full mt-2" />
<span className="text-[#1C1C1C]">{point}</span>
</div>
</StaggerItem>
@@ -94,7 +94,7 @@ export function AccompanySection() {
<div className="flex justify-center">
<Button
size="lg"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
asChild
>
<StaticLink href="/contact">
@@ -28,11 +28,11 @@ export function ConsultingSection() {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-12 border border-[#C41E3A]/20"
className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-12 border border-[var(--color-brand-primary)]/20"
>
{/* 标题区域:图标 + 标题 + 副标题 */}
<div className="flex items-start gap-6 mb-8">
<div className="w-16 h-16 bg-[#C41E3A] rounded-2xl flex items-center justify-center flex-shrink-0">
<div className="w-16 h-16 bg-[var(--color-brand-primary)] rounded-2xl flex items-center justify-center flex-shrink-0">
<Lightbulb className="w-8 h-8 text-white" />
</div>
<div>
@@ -72,7 +72,7 @@ export function ConsultingSection() {
<div className="mb-8">
<ScrollReveal variants={slideInLeftVariants}>
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-4 flex items-center gap-2">
<CheckCircle2 className="w-6 h-6 text-[#C41E3A]" />
<CheckCircle2 className="w-6 h-6 text-[var(--color-brand-primary)]" />
</h3>
</ScrollReveal>
@@ -80,7 +80,7 @@ export function ConsultingSection() {
{valuePoints.map((point) => (
<StaggerItem key={point}>
<div className="flex items-start gap-3 p-4 bg-white rounded-lg border border-[#E5E5E5]">
<div className="w-2 h-2 bg-[#C41E3A] rounded-full mt-2" />
<div className="w-2 h-2 bg-[var(--color-brand-primary)] rounded-full mt-2" />
<span className="text-[#1C1C1C]">{point}</span>
</div>
</StaggerItem>
@@ -93,7 +93,7 @@ export function ConsultingSection() {
<div className="flex justify-center">
<Button
size="lg"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
asChild
>
<StaticLink href="/contact">
@@ -29,11 +29,11 @@ export function TechSolutionSection() {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.2 }}
className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-12 border border-[#C41E3A]/20"
className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-12 border border-[var(--color-brand-primary)]/20"
>
{/* 标题区域:图标 + 标题 + 副标题 */}
<div className="flex items-start gap-6 mb-8">
<div className="w-16 h-16 bg-[#C41E3A] rounded-2xl flex items-center justify-center flex-shrink-0">
<div className="w-16 h-16 bg-[var(--color-brand-primary)] rounded-2xl flex items-center justify-center flex-shrink-0">
<Cpu className="w-8 h-8 text-white" />
</div>
<div>
@@ -73,7 +73,7 @@ export function TechSolutionSection() {
<div className="mb-8">
<ScrollReveal variants={slideInLeftVariants}>
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-4 flex items-center gap-2">
<CheckCircle2 className="w-6 h-6 text-[#C41E3A]" />
<CheckCircle2 className="w-6 h-6 text-[var(--color-brand-primary)]" />
</h3>
</ScrollReveal>
@@ -81,7 +81,7 @@ export function TechSolutionSection() {
{valuePoints.map((point) => (
<StaggerItem key={point}>
<div className="flex items-start gap-3 p-4 bg-white rounded-lg border border-[#E5E5E5]">
<div className="w-2 h-2 bg-[#C41E3A] rounded-full mt-2" />
<div className="w-2 h-2 bg-[var(--color-brand-primary)] rounded-full mt-2" />
<span className="text-[#1C1C1C]">{point}</span>
</div>
</StaggerItem>
@@ -95,7 +95,7 @@ export function TechSolutionSection() {
<Button
size="lg"
variant="outline"
className="border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A] hover:text-white"
className="border-[var(--color-brand-primary)] text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)] hover:text-white"
asChild
>
<StaticLink href="/cases">
+1 -1
View File
@@ -109,7 +109,7 @@ interface GeometricCardProps extends HTMLMotionProps<'div'> {
export function GeometricCard({
children,
className = '',
cornerColor = '#C41E3A',
cornerColor = 'var(--color-brand-primary)',
...props
}: GeometricCardProps) {
return (
+1 -1
View File
@@ -14,7 +14,7 @@ export function BackButton() {
<Button
variant="ghost"
size="sm"
className="text-[#5C5C5C] hover:text-[#C41E3A] hover:bg-transparent h-auto py-2 px-3"
className="text-[#5C5C5C] hover:text-[var(--color-brand-primary)] hover:bg-transparent h-auto py-2 px-3"
onClick={() => window.history.back()}
>
<ArrowLeft className="w-4 h-4 mr-2" />
+1 -1
View File
@@ -35,7 +35,7 @@ export function BackToTop() {
exit={shouldReduceMotion ? {} : { opacity: 0, y: 20, scale: 0.8 }}
transition={{ duration: 0.2, ease: 'easeOut' }}
onClick={scrollToTop}
className="fixed bottom-8 right-8 z-50 p-3 bg-[#C41E3A] text-white rounded-full shadow-lg hover:bg-[#A01830] hover:shadow-xl transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2"
className="fixed bottom-8 right-8 z-50 p-3 bg-[var(--color-brand-primary)] text-white rounded-full shadow-lg hover:bg-[var(--color-brand-primary-hover)] hover:shadow-xl transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-primary)] focus:ring-offset-2"
aria-label="返回顶部"
title="返回顶部"
style={{
+2 -2
View File
@@ -11,11 +11,11 @@ const badgeVariants = cva(
{
variants: {
variant: {
default: "bg-[#C41E3A] text-white border-transparent shadow-sm",
default: "bg-[var(--color-brand-primary)] text-white border-transparent shadow-sm",
secondary:
"bg-[#1C1C1C] text-white border-transparent shadow-sm",
destructive:
"bg-[#C41E3A] text-white border-transparent hover:bg-[#A01830]",
"bg-[var(--color-brand-primary)] text-white border-transparent hover:bg-[var(--color-brand-primary-hover)]",
outline:
"border-[#1C1C1C] text-[#1C1C1C] bg-transparent hover:bg-[#F5F5F5]",
ghost: "text-[#5C5C5C] hover:text-[#1C1C1C] hover:bg-[#F5F5F5]",
+3 -3
View File
@@ -12,17 +12,17 @@ const buttonVariants = cva(
variants: {
variant: {
default:
"bg-[#C41E3A] text-white hover:bg-[#A01830] hover:shadow-[0_4px_12px_rgba(196,30,58,0.25)] hover:-translate-y-0.5 active:scale-[0.98]",
"bg-[var(--color-brand-primary)] text-white hover:bg-[var(--color-brand-primary-hover)] hover:shadow-[0_4px_12px_rgba(196,30,58,0.25)] hover:-translate-y-0.5 active:scale-[0.98]",
secondary:
"bg-[#1C1C1C] text-white hover:bg-[#0A0A0A] hover:shadow-[0_4px_12px_rgba(28,28,28,0.25)] hover:-translate-y-0.5 active:scale-[0.98]",
destructive:
"bg-[#C41E3A] text-white hover:bg-[#A01830] focus-visible:ring-[#C41E3A]",
"bg-[var(--color-brand-primary)] text-white hover:bg-[var(--color-brand-primary-hover)] focus-visible:ring-[var(--color-brand-primary)]",
outline:
"border-2 border-[#1C1C1C] bg-transparent text-[#1C1C1C]",
ghost:
"text-[#3D3D3D] hover:bg-[#F5F5F5] hover:text-[#1C1C1C]",
link:
"text-[#1C1C1C] underline-offset-4 hover:underline hover:text-[#C41E3A]",
"text-[#1C1C1C] underline-offset-4 hover:underline hover:text-[var(--color-brand-primary)]",
},
size: {
default: "h-11 px-4 py-2",
+1 -1
View File
@@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-primary)] focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
+1 -1
View File
@@ -54,7 +54,7 @@ export class ErrorBoundary extends Component<Props, State> {
</p>
<button
onClick={() => this.setState({ hasError: false, error: undefined })}
className="px-6 py-2.5 bg-[#C41E3A] text-white rounded-lg hover:bg-[#A01830] transition-colors focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2"
className="px-6 py-2.5 bg-[var(--color-brand-primary)] text-white rounded-lg hover:bg-[var(--color-brand-primary-hover)] transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-primary)] focus:ring-offset-2"
aria-label="重试"
>
+6 -6
View File
@@ -21,13 +21,13 @@ function FlipDigit({ digit, prevDigit }: FlipDigitProps) {
<div className="absolute inset-0 bg-gradient-to-b from-white via-white to-gray-50 rounded-lg shadow-xl overflow-hidden border border-gray-200">
{/* 上半部分 */}
<div className="absolute top-0 left-0 right-0 h-1/2 bg-gradient-to-b from-white to-gray-50 border-b border-gray-300 overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[#C41E3A]">
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[var(--color-brand-primary)]">
{digit}
</div>
</div>
{/* 下半部分 */}
<div className="absolute bottom-0 left-0 right-0 h-1/2 bg-gradient-to-b from-white to-gray-50 overflow-hidden">
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[#C41E3A]">
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[var(--color-brand-primary)]">
{digit}
</div>
</div>
@@ -48,7 +48,7 @@ function FlipDigit({ digit, prevDigit }: FlipDigitProps) {
backfaceVisibility: 'hidden',
}}
>
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[#C41E3A]">
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[var(--color-brand-primary)]">
{prevDigit}
</div>
</motion.div>
@@ -66,7 +66,7 @@ function FlipDigit({ digit, prevDigit }: FlipDigitProps) {
backfaceVisibility: 'hidden',
}}
>
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[#C41E3A]">
<div className="absolute inset-0 flex items-center justify-center text-4xl sm:text-5xl md:text-6xl font-bold text-[var(--color-brand-primary)]">
{digit}
</div>
</motion.div>
@@ -134,9 +134,9 @@ export function FlipClock({ years, months, days }: FlipClockProps) {
<div className="flex flex-wrap justify-center items-center gap-4 sm:gap-6 mb-6">
<FlipCard value={years} label="年" maxDigits={2} />
<div className="text-2xl sm:text-3xl font-bold text-[#C41E3A]">:</div>
<div className="text-2xl sm:text-3xl font-bold text-[var(--color-brand-primary)]">:</div>
<FlipCard value={months} label="个月" maxDigits={2} />
<div className="text-2xl sm:text-3xl font-bold text-[#C41E3A]">:</div>
<div className="text-2xl sm:text-3xl font-bold text-[var(--color-brand-primary)]">:</div>
<FlipCard value={days} label="天" maxDigits={3} />
</div>
+2 -2
View File
@@ -53,11 +53,11 @@ export function FloatingCTA() {
<motion.button
whileHover={shouldReduceMotion ? {} : { scale: 1.05, y: -2 }}
whileTap={shouldReduceMotion ? {} : { scale: 0.95 }}
className="group relative flex items-center gap-2 px-5 py-3 rounded-full bg-[#C41E3A] text-white shadow-[0_4px_20px_rgba(196,30,58,0.35)] hover:shadow-[0_6px_28px_rgba(196,30,58,0.45)] transition-shadow"
className="group relative flex items-center gap-2 px-5 py-3 rounded-full bg-[var(--color-brand-primary)] text-white shadow-[0_4px_20px_rgba(196,30,58,0.35)] hover:shadow-[0_6px_28px_rgba(196,30,58,0.45)] transition-shadow"
aria-label="立即咨询"
>
{/* 脉冲光晕 */}
<span className="absolute inset-0 rounded-full bg-[#C41E3A] animate-ping opacity-20" />
<span className="absolute inset-0 rounded-full bg-[var(--color-brand-primary)] animate-ping opacity-20" />
<MessageCircle className="w-5 h-5 relative z-10" />
<span className="text-sm font-medium relative z-10 hidden sm:inline"></span>
</motion.button>
+5 -5
View File
@@ -56,7 +56,7 @@ interface InkSplashProps {
export function InkSplash({
size = 60,
color = '#C41E3A',
color = 'var(--color-brand-primary)',
opacity = 0.2,
delay = 0,
className = ''
@@ -92,7 +92,7 @@ interface SealStampProps {
export function SealStamp({
size = 40,
color = '#C41E3A',
color = 'var(--color-brand-primary)',
opacity = 0.15,
delay = 0,
className = ''
@@ -209,7 +209,7 @@ interface BrushStrokeProps {
export function BrushStroke({
width = 150,
height = 30,
color = '#C41E3A',
color = 'var(--color-brand-primary)',
opacity = 0.12,
delay = 0,
className = ''
@@ -322,7 +322,7 @@ export function FloatingInk({ count = 15, className = '' }: FloatingInkProps) {
<InkDrop
size={el.size}
opacity={el.opacity}
color="#C41E3A"
color="var(--color-brand-primary)"
delay={el.delay}
/>
)}
@@ -482,7 +482,7 @@ export function InkDecoration({ variant = 'balanced', className = '' }: InkDecor
size={pos.size}
opacity={pos.opacity}
blur={pos.blur}
color={pos.isRed ? '#C41E3A' : '#1C1C1C'}
color={pos.isRed ? 'var(--color-brand-primary)' : '#1C1C1C'}
delay={i * 0.1}
/>
</motion.div>
+3 -3
View File
@@ -21,7 +21,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
className="block text-sm font-medium text-[#3D3D3D] mb-2"
>
{label}
{props.required && <span className="text-[#C41E3A] ml-1">*</span>}
{props.required && <span className="text-[var(--color-brand-primary)] ml-1">*</span>}
</label>
)}
<input
@@ -33,7 +33,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
"file:text-foreground placeholder:text-[#8C8C8C] selection:bg-[#1C1C1C] selection:text-white h-14 min-h-[56px] w-full min-w-0 rounded-lg border border-[#E5E5E5] bg-[#FAFAFA] px-4 py-3 text-base text-[#1C1C1C] shadow-sm transition-all duration-300 outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 touch-manipulation md:h-12 md:min-h-[44px] md:py-2",
"focus-visible:border-[#1C1C1C] focus-visible:ring-2 focus-visible:ring-[#1C1C1C]/50 focus-visible:shadow-lg focus-visible:shadow-[#1C1C1C]/20",
"hover:border-[#3D3D3D]",
error && "border-[#C41E3A] focus-visible:border-[#C41E3A] focus-visible:ring-[#C41E3A]/50",
error && "border-[var(--color-brand-primary)] focus-visible:border-[var(--color-brand-primary)] focus-visible:ring-[var(--color-brand-primary)]/50",
className
)}
ref={ref}
@@ -43,7 +43,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
{...props}
/>
{error && (
<p id={errorId} className="mt-1 text-sm text-[#C41E3A]" role="alert" data-testid="error-message">
<p id={errorId} className="mt-1 text-sm text-[var(--color-brand-primary)]" role="alert" data-testid="error-message">
{error}
</p>
)}
+2 -2
View File
@@ -56,7 +56,7 @@ export function InsightCard({
</div>
</div>
<h3 className="text-lg font-semibold text-[#171717] mb-2 line-clamp-2 group-hover:text-[#C41E3A] transition-colors">
<h3 className="text-lg font-semibold text-[#171717] mb-2 line-clamp-2 group-hover:text-[var(--color-brand-primary)] transition-colors">
{title}
</h3>
@@ -72,7 +72,7 @@ export function InsightCard({
<a
href={href}
className="inline-flex items-center gap-1 text-sm font-medium text-[#C41E3A] hover:gap-2 transition-all"
className="inline-flex items-center gap-1 text-sm font-medium text-[var(--color-brand-primary)] hover:gap-2 transition-all"
>
<ArrowRight className="w-4 h-4" />
+1 -1
View File
@@ -84,7 +84,7 @@ export function OptimizedImage({
{/* 加载动画 */}
{!isLoaded && (
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-8 h-8 border-2 border-gray-200 border-t-[#C41E3A] rounded-full animate-spin" />
<div className="w-8 h-8 border-2 border-gray-200 border-t-[var(--color-brand-primary)] rounded-full animate-spin" />
</div>
)}
+2 -2
View File
@@ -24,12 +24,12 @@ export function PageHeader({ badge, title, description, className = '' }: PageHe
<InkBackground />
<DataParticleFlow
particleCount={40}
color="#C41E3A"
color="var(--color-brand-primary)"
intensity="subtle"
shape="square"
effect="pulse"
/>
<SubtleDots color="#C41E3A" count={6} />
<SubtleDots color="var(--color-brand-primary)" count={6} />
<div className="container-wide relative z-10 pt-8 pb-12" ref={ref}>
<motion.div
+1 -1
View File
@@ -359,7 +359,7 @@ export function LoadingTransition({
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="w-8 h-8 border-2 border-[#C41E3A] border-t-transparent rounded-full"
className="w-8 h-8 border-2 border-[var(--color-brand-primary)] border-t-transparent rounded-full"
/>
</div>
)}
+88
View File
@@ -0,0 +1,88 @@
'use client';
import { Button } from '@/components/ui/button';
import { ChevronLeft, ChevronRight } from 'lucide-react';
interface PaginationProps {
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
/** ID of the element to scroll to when page changes (instead of window top) */
scrollTargetId?: string;
}
export function Pagination({ currentPage, totalPages, onPageChange, scrollTargetId }: PaginationProps) {
const handlePageChange = (page: number) => {
onPageChange(page);
// Scroll to content area instead of page top
if (scrollTargetId) {
const el = document.getElementById(scrollTargetId);
if (el) {
const offset = 80; // header height
const top = el.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top, behavior: 'smooth' });
return;
}
}
window.scrollTo({ top: 0, behavior: 'smooth' });
};
if (totalPages <= 1) {return null;}
const getVisiblePages = () => {
const pages: (number | 'ellipsis')[] = [];
if (totalPages <= 7) {
for (let i = 1; i <= totalPages; i++) {pages.push(i);}
} else {
pages.push(1);
if (currentPage > 3) {pages.push('ellipsis');}
const start = Math.max(2, currentPage - 1);
const end = Math.min(totalPages - 1, currentPage + 1);
for (let i = start; i <= end; i++) {pages.push(i);}
if (currentPage < totalPages - 2) {pages.push('ellipsis');}
pages.push(totalPages);
}
return pages;
};
return (
<nav aria-label="分页" className="flex items-center justify-center gap-1 mt-8">
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
aria-label="上一页"
>
<ChevronLeft className="w-4 h-4" />
</Button>
{getVisiblePages().map((page, index) =>
page === 'ellipsis' ? (
<span key={`ellipsis-${index}`} className="px-2 text-[#5C5C5C]" aria-hidden="true">
...
</span>
) : (
<Button
key={page}
variant={currentPage === page ? 'default' : 'outline'}
size="icon"
onClick={() => handlePageChange(page)}
aria-label={`${page}`}
aria-current={currentPage === page ? 'page' : undefined}
>
{page}
</Button>
)
)}
<Button
variant="outline"
size="icon"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
aria-label="下一页"
>
<ChevronRight className="w-4 h-4" />
</Button>
</nav>
);
}
+4 -4
View File
@@ -11,19 +11,19 @@ const rippleButtonVariants = cva(
variants: {
variant: {
default:
'bg-[#C41E3A] text-white hover:bg-[#A01830] hover:shadow-[0_8px_20px_rgba(196,30,58,0.35)]',
'bg-[var(--color-brand-primary)] text-white hover:bg-[var(--color-brand-primary-hover)] hover:shadow-[0_8px_20px_rgba(196,30,58,0.35)]',
secondary:
'bg-[#1C1C1C] text-white hover:bg-[#0A0A0A] hover:shadow-[0_8px_20px_rgba(28,28,28,0.35)]',
destructive:
'bg-[#C41E3A] text-white hover:bg-[#A01830] focus-visible:ring-[#C41E3A]',
'bg-[var(--color-brand-primary)] text-white hover:bg-[var(--color-brand-primary-hover)] focus-visible:ring-[var(--color-brand-primary)]',
outline:
'border-2 border-[#1C1C1C] bg-transparent text-[#1C1C1C] hover:bg-[#F5F5F5] hover:shadow-[0_4px_12px_rgba(28,28,28,0.2)]',
ghost:
'text-[#3D3D3D] hover:bg-[#F5F5F5] hover:text-[#1C1C1C]',
link:
'text-[#1C1C1C] underline-offset-4 hover:underline hover:text-[#C41E3A]',
'text-[#1C1C1C] underline-offset-4 hover:underline hover:text-[var(--color-brand-primary)]',
seal:
'bg-[#C41E3A] text-white font-semibold hover:bg-[#A01830] shadow-[0_6px_20px_rgba(196,30,58,0.3)]',
'bg-[var(--color-brand-primary)] text-white font-semibold hover:bg-[var(--color-brand-primary-hover)] shadow-[0_6px_20px_rgba(196,30,58,0.3)]',
},
size: {
default: 'h-11 px-4 py-2.5',
+1 -1
View File
@@ -397,7 +397,7 @@ interface ProgressIndicatorProps {
color?: string;
}
export function ProgressIndicator({ className = '', color = '#C41E3A' }: ProgressIndicatorProps) {
export function ProgressIndicator({ className = '', color = 'var(--color-brand-primary)' }: ProgressIndicatorProps) {
const { scrollYProgress } = useScroll();
const scaleX = useSpring(scrollYProgress, { stiffness: 100, damping: 30 });
+1 -1
View File
@@ -41,7 +41,7 @@ export function ScrollProgress() {
aria-valuemax={100}
>
<motion.div
className="h-full bg-gradient-to-r from-[#C41E3A] to-[#E04A68] origin-left"
className="h-full bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E04A68] origin-left"
style={{
scaleX,
boxShadow: '0 0 10px rgba(196, 30, 58, 0.3)',
+2 -2
View File
@@ -22,14 +22,14 @@ export function TestimonialCard({
}: TestimonialCardProps) {
return (
<div className="relative p-8 rounded-lg border border-[#E5E5E5]/50 bg-white hover:shadow-md transition-shadow">
<Quote className="absolute top-6 right-6 w-8 h-8 text-[#C41E3A]/10" />
<Quote className="absolute top-6 right-6 w-8 h-8 text-[var(--color-brand-primary)]/10" />
{rating > 0 && (
<div className="flex gap-1 mb-4">
{Array.from({ length: rating }).map((_, i) => (
<svg
key={i}
className="w-4 h-4 text-[#C41E3A]"
className="w-4 h-4 text-[var(--color-brand-primary)]"
fill="currentColor"
viewBox="0 0 20 20"
>
+3 -3
View File
@@ -21,7 +21,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
className="block text-sm font-medium text-[#3D3D3D] mb-2"
>
{label}
{props.required && <span className="text-[#C41E3A] ml-1">*</span>}
{props.required && <span className="text-[var(--color-brand-primary)] ml-1">*</span>}
</label>
)}
<textarea
@@ -32,7 +32,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
"placeholder:text-[#8C8C8C] selection:bg-[#1C1C1C] selection:text-white min-h-[140px] w-full rounded-lg border border-[#E5E5E5] bg-[#FAFAFA] px-4 py-3 text-base text-[#1C1C1C] shadow-sm transition-all duration-300 outline-none disabled:cursor-not-allowed disabled:opacity-50 md:min-h-[80px] md:text-sm md:py-2 resize-none",
"focus-visible:border-[#1C1C1C] focus-visible:ring-2 focus-visible:ring-[#1C1C1C]/50 focus-visible:shadow-lg focus-visible:shadow-[#1C1C1C]/20",
"hover:border-[#3D3D3D]",
error && "border-[#C41E3A] focus-visible:border-[#C41E3A] focus-visible:ring-[#C41E3A]/50",
error && "border-[var(--color-brand-primary)] focus-visible:border-[var(--color-brand-primary)] focus-visible:ring-[var(--color-brand-primary)]/50",
className
)}
ref={ref}
@@ -42,7 +42,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
{...props}
/>
{error && (
<p id={errorId} className="mt-1 text-sm text-[#C41E3A]" role="alert">
<p id={errorId} className="mt-1 text-sm text-[var(--color-brand-primary)]" role="alert">
{error}
</p>
)}
+7 -7
View File
@@ -32,23 +32,23 @@ export function TouchButton({
const variants = {
primary: `
bg-[#C41E3A] text-white
hover:bg-[#A01830]
focus-visible:ring-[#C41E3A]
bg-[var(--color-brand-primary)] text-white
hover:bg-[var(--color-brand-primary-hover)]
focus-visible:ring-[var(--color-brand-primary)]
${isPressed ? 'bg-[#8B1429]' : ''}
`,
secondary: `
bg-[#F5F5F5] text-[#171717]
hover:bg-[#E5E5E5]
border border-[#E5E5E5]
focus-visible:ring-[#C41E3A]
focus-visible:ring-[var(--color-brand-primary)]
${isPressed ? 'bg-[#D4D4D4]' : ''}
`,
ghost: `
bg-transparent text-[#525252]
hover:bg-[#FEF2F4] hover:text-[#C41E3A]
focus-visible:ring-[#C41E3A]
${isPressed ? 'bg-[#FCE8EC] text-[#C41E3A]' : ''}
hover:bg-[#FEF2F4] hover:text-[var(--color-brand-primary)]
focus-visible:ring-[var(--color-brand-primary)]
${isPressed ? 'bg-[#FCE8EC] text-[var(--color-brand-primary)]' : ''}
`,
};
+2 -2
View File
@@ -1,6 +1,6 @@
'use client';
import { useState, useCallback, useRef, useEffect, memo } from 'react';
import { useState, useCallback, useRef, useEffect, memo, useMemo } from 'react';
import { cn } from '@/lib/utils';
interface SwipeableProps {
@@ -157,7 +157,7 @@ export const PullToRefresh = memo(function PullToRefresh({
style={{ height: pullDistance }}
>
<div className={cn(
'w-6 h-6 border-2 border-[#C41E3A] border-t-transparent rounded-full',
'w-6 h-6 border-2 border-[var(--color-brand-primary)] border-t-transparent rounded-full',
isRefreshing && 'animate-spin'
)} />
</div>