- Replace decorative numbering and side-stripe borders with structured copy - Add scenario positioning, capability metrics and verifiable dimensions - Refine service timelines, deliverables and case-study narratives - Update content constants to support CMS seeding
186 lines
7.4 KiB
TypeScript
186 lines
7.4 KiB
TypeScript
'use client';
|
||
|
||
import { motion } from 'framer-motion';
|
||
import {
|
||
CheckCircle2,
|
||
Zap,
|
||
Clock,
|
||
Users,
|
||
Award,
|
||
TrendingUp,
|
||
type LucideIcon,
|
||
} from 'lucide-react';
|
||
import { TiltCard, InkGlowCard } from './micro-interactions';
|
||
import { SectionHeader, InkDivider } from './brand-elements';
|
||
import type { Service } from '@/lib/constants/services';
|
||
|
||
interface ServiceValueSectionProps {
|
||
service: Service;
|
||
}
|
||
|
||
export function ServiceValueSection({ service }: ServiceValueSectionProps) {
|
||
const icons: LucideIcon[] = [Zap, Clock, Users, Award, TrendingUp];
|
||
|
||
return (
|
||
<section className="relative bg-white py-24 lg:py-32 overflow-hidden">
|
||
<div className="container-wide relative">
|
||
<SectionHeader
|
||
title={`为什么选择我们的${service.title}?`}
|
||
subtitle={service.overview}
|
||
/>
|
||
|
||
<div className="mt-16">
|
||
<div className="flex items-center gap-4 mb-8">
|
||
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-[#C41E3A] to-[#99182d] flex items-center justify-center shadow-md shadow-brand/20">
|
||
<Zap className="w-6 h-6 text-white" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-ink">核心能力</h3>
|
||
<p className="text-sm text-text-muted mt-0.5">我们提供的服务特色</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
{service.features.map((feature, index) => {
|
||
const Icon = icons[index % icons.length]!;
|
||
|
||
return (
|
||
<InkGlowCard
|
||
key={feature}
|
||
active={index === 0}
|
||
className="group p-6 hover:border-brand/20"
|
||
>
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{
|
||
delay: index * 0.08,
|
||
duration: 0.5,
|
||
ease: [0.22, 1, 0.36, 1] as const,
|
||
}}
|
||
>
|
||
<div className="flex items-start gap-4">
|
||
<div
|
||
className={`shrink-0 w-11 h-11 rounded-xl flex items-center justify-center transition-all duration-300 shadow-sm ${
|
||
index === 0
|
||
? 'bg-gradient-to-br from-[#C41E3A] to-[#99182d] text-white'
|
||
: 'bg-bg-secondary text-text-secondary group-hover:from-[#C41E3A] group-hover:to-[#99182d] group-hover:text-white'
|
||
}`}
|
||
>
|
||
<Icon className="w-5.5 h-5.5" />
|
||
</div>
|
||
<p className="text-sm font-medium text-text-secondary leading-relaxed pt-2">
|
||
{feature.split(':')[1] || feature}
|
||
</p>
|
||
</div>
|
||
</motion.div>
|
||
</InkGlowCard>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 32 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true, margin: '-80px' }}
|
||
transition={{ duration: 0.75, delay: 0.3 }}
|
||
className="mt-16"
|
||
>
|
||
<InkDivider variant="subtle" />
|
||
|
||
<div className="flex items-center gap-4 mb-8">
|
||
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-green-500 to-emerald-600 flex items-center justify-center shadow-md shadow-green-500/20">
|
||
<Award className="w-6 h-6 text-white" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-ink">服务优势</h3>
|
||
<p className="text-sm text-text-muted mt-0.5">选择我们的理由</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||
{service.benefits.map((benefit, index) => (
|
||
<motion.div
|
||
key={index}
|
||
initial={{ opacity: 0, x: index % 2 === 0 ? -20 : 20 }}
|
||
whileInView={{ opacity: 1, x: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{
|
||
delay: 0.4 + index * 0.06,
|
||
duration: 0.5,
|
||
ease: [0.22, 1, 0.36, 1] as const,
|
||
}}
|
||
className="group p-6 rounded-2xl bg-bg-secondary border border-border-primary hover:border-brand/30 hover:shadow-md hover:-translate-y-1 transition-all duration-300"
|
||
>
|
||
<div className="flex items-start gap-3">
|
||
<CheckCircle2 className="w-5 h-5 text-green-600 mt-0.5 shrink-0 group-hover:scale-110 transition-transform" />
|
||
<p className="text-sm font-medium text-text-secondary leading-relaxed">{benefit}</p>
|
||
</div>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
</motion.div>
|
||
|
||
{service.process && service.process.length > 0 && (
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 32 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true, margin: '-80px' }}
|
||
transition={{ duration: 0.75, delay: 0.45 }}
|
||
className="mt-20"
|
||
>
|
||
<InkDivider variant="decorative" />
|
||
|
||
<div className="text-center mb-12">
|
||
<h3 className="text-2xl md:text-3xl font-bold tracking-tight text-ink">
|
||
服务流程
|
||
</h3>
|
||
<p className="text-text-secondary mt-3">标准化流程保障服务质量</p>
|
||
</div>
|
||
|
||
<div className="relative">
|
||
<div className="absolute left-8 top-0 bottom-0 w-0.5 bg-gradient-to-b from-[#C41E3A] via-blue-400 to-green-400 hidden md:block" />
|
||
|
||
<div className="space-y-6">
|
||
{service.process.map((step, index) => (
|
||
<motion.div
|
||
key={step}
|
||
initial={{ opacity: 0, x: -30 }}
|
||
whileInView={{ opacity: 1, x: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{
|
||
delay: 0.5 + index * 0.1,
|
||
duration: 0.55,
|
||
ease: [0.22, 1, 0.36, 1] as const,
|
||
}}
|
||
className="relative flex gap-6 group"
|
||
>
|
||
<div className="relative z-10 shrink-0 w-16 flex justify-center">
|
||
<div className="w-4 h-4 rounded-full bg-brand group-hover:scale-110 transition-transform duration-300" />
|
||
</div>
|
||
|
||
<TiltCard
|
||
tiltAmount={5}
|
||
glareEnable={false}
|
||
className="flex-1 p-6 rounded-2xl bg-white border border-border-primary hover:border-brand/30 hover:shadow-md transition-all"
|
||
>
|
||
<h4 className="font-bold text-ink mb-2">
|
||
{step.split(':')[0]}
|
||
</h4>
|
||
<p className="text-sm text-text-secondary leading-relaxed">
|
||
{step.split(':')[1] || step}
|
||
</p>
|
||
</TiltCard>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|