-
- 关键数据
-
-
- 用真实数据说话,让效果可衡量、可验证
-
-
+
+
+
+ 数据说话
+
+
+ 用真实效果证明价值
+
+
+ 来自真实客户的使用数据,让效果可衡量、可验证
+
+
+
+
+ {dataProofs.map((proof, index) => (
+
+
+ {proof.metric}
+ {proof.description}
+
+ ))}
+
)}
{caseStudies.length > 0 && (
-
-
- 客户案例
-
-
- 来自真实客户的成功实践,看看他们如何实现转型突破
-
-
+
+
+
+ 客户案例
+
+
+ 他们已经实现了转型突破
+
+
+
+
{caseStudies.map((study, index) => (
-
+
+
+
))}
)}
{certifications.length > 0 && (
-
-
- 资质认证
-
-
- 通过权威机构认证,保障安全合规
-
-
-
-
-
+
+ 资质认证
+
+
)}
diff --git a/src/components/detail/index.ts b/src/components/detail/index.ts
new file mode 100644
index 0000000..4805ef4
--- /dev/null
+++ b/src/components/detail/index.ts
@@ -0,0 +1,22 @@
+export { DetailHero } from './detail-hero';
+
+export { ProductValueSection } from './detail-product-value';
+
+export { DetailTrustSection } from './detail-trust-section';
+
+export { DetailCTASection } from './detail-cta-section';
+
+export { CaseStudyCard } from './detail-case-study';
+export { CertificationList } from './detail-certification';
+export { CrossRecommendGrid } from './detail-cross-recommend';
+
+export { SolutionValueSection } from './solution-value';
+export { ServiceValueSection } from './service-value';
+
+export { ListPageHero } from './list-page-hero';
+export { ProductCard } from './product-card';
+export { SolutionCard } from './solution-service-card';
+
+export { TiltCard, PressableButton, InkGlowCard, HoverLink } from './micro-interactions';
+
+export { BrandSeal, CalligraphyText, InkDivider, SectionHeader } from './brand-elements';
diff --git a/src/components/detail/list-page-hero.tsx b/src/components/detail/list-page-hero.tsx
new file mode 100644
index 0000000..1994aff
--- /dev/null
+++ b/src/components/detail/list-page-hero.tsx
@@ -0,0 +1,166 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import { useReducedMotion } from '@/hooks/use-reduced-motion';
+
+interface ListPageHeroProps {
+ title: string;
+ subtitle: string;
+ description?: string;
+ stats?: Array<{ value: string; label: string }>;
+ badge?: {
+ text: string;
+ variant?: 'brand' | 'blue' | 'green' | 'neutral';
+ };
+}
+
+const BADGE_VARIANTS = {
+ brand: {
+ bg: 'var(--color-brand-bg)',
+ text: 'var(--color-brand)',
+ border: 'rgba(var(--color-brand-rgb), 0.15)',
+ dot: 'var(--color-brand)',
+ },
+ blue: {
+ bg: 'rgba(var(--color-accent-blue-rgb), 0.08)',
+ text: 'var(--color-accent-blue)',
+ border: 'rgba(var(--color-accent-blue-rgb), 0.15)',
+ dot: 'var(--color-accent-blue)',
+ },
+ green: {
+ bg: 'rgba(34, 197, 94, 0.08)',
+ text: '#16a34a',
+ border: 'rgba(34, 197, 94, 0.15)',
+ dot: '#22c55e',
+ },
+ neutral: {
+ bg: 'var(--color-bg-section)',
+ text: 'var(--color-text-secondary)',
+ border: 'var(--color-border-primary)',
+ dot: 'var(--color-text-subtle)',
+ },
+};
+
+export function ListPageHero({
+ title,
+ subtitle,
+ description,
+ stats,
+ badge,
+}: ListPageHeroProps) {
+ const shouldReduceMotion = useReducedMotion();
+ const badgeStyle = BADGE_VARIANTS[badge?.variant || 'brand'];
+
+ return (
+
+ {/* Multi-layer background */}
+
+
+
+
+ {/* Subtle grid texture */}
+
+
+ {/* Decorative accent line */}
+
+
+
+
+ {/* Dynamic Badge */}
+ {badge && badge.text && (
+
+
+ {badge?.text || ''}
+
+ )}
+
+ {/* Title */}
+
+ {title}
+
+
+ {/* Subtitle */}
+
+ {subtitle}
+
+
+ {/* Description */}
+ {description && (
+
+ {description}
+
+ )}
+
+ {/* Stats Row */}
+ {stats && stats.length > 0 && (
+
+ {stats.map((stat, index) => (
+
+
+ {stat.value}
+
+ {stat.label}
+
+ ))}
+
+ )}
+
+
+
+ {/* Bottom fade */}
+
+
+ );
+}
diff --git a/src/components/detail/micro-interactions.tsx b/src/components/detail/micro-interactions.tsx
new file mode 100644
index 0000000..04ec38d
--- /dev/null
+++ b/src/components/detail/micro-interactions.tsx
@@ -0,0 +1,261 @@
+'use client';
+
+import { motion, useMotionValue, useTransform, useSpring } from 'framer-motion';
+import { useRef, ReactNode, useState, useCallback } from 'react';
+import { ArrowUpRight } from 'lucide-react';
+
+interface TiltCardProps {
+ children: ReactNode;
+ className?: string;
+ tiltAmount?: number;
+ glareEnable?: boolean;
+ glareMaxOpacity?: number;
+ scaleOnHover?: number;
+}
+
+export function TiltCard({
+ children,
+ className = '',
+ tiltAmount = 6,
+ glareEnable = true,
+ glareMaxOpacity = 0.06,
+ scaleOnHover = 1.01,
+}: TiltCardProps) {
+ const ref = useRef
(null);
+ const x = useMotionValue(0.5);
+ const y = useMotionValue(0.5);
+ const [isHovered, setIsHovered] = useState(false);
+
+ const rotateX = useSpring(useTransform(y, [0, 1], [tiltAmount, -tiltAmount]), {
+ stiffness: 280,
+ damping: 24,
+ mass: 0.8,
+ });
+ const rotateY = useSpring(useTransform(x, [0, 1], [-tiltAmount, tiltAmount]), {
+ stiffness: 280,
+ damping: 24,
+ mass: 0.8,
+ });
+ const scale = useSpring(isHovered ? scaleOnHover : 1, {
+ stiffness: 300,
+ damping: 28,
+ mass: 0.6,
+ });
+
+ const handleMouseMove = useCallback((e: React.MouseEvent) => {
+ if (!ref.current) return;
+ const rect = ref.current.getBoundingClientRect();
+ x.set((e.clientX - rect.left) / rect.width);
+ y.set((e.clientY - rect.top) / rect.height);
+ }, [x, y]);
+
+ const handleMouseEnter = useCallback(() => {
+ setIsHovered(true);
+ }, []);
+
+ const handleMouseLeave = useCallback(() => {
+ setIsHovered(false);
+ x.set(0.5);
+ y.set(0.5);
+ }, [x, y]);
+
+ const glareX = useTransform(x, [0, 1], ['0%', '100%']);
+ const glareY = useTransform(y, [0, 1], ['0%', '100%']);
+ const glareOpacity = useTransform(
+ y,
+ [0, 0.5, 1],
+ [glareMaxOpacity * 0.3, glareMaxOpacity, glareMaxOpacity * 0.2]
+ );
+
+ return (
+
+ {children}
+ {glareEnable && (
+
+ )}
+
+ );
+}
+
+interface PressableButtonProps {
+ children: ReactNode;
+ className?: string;
+ onClick?: () => void;
+ variant?: 'primary' | 'secondary' | 'ghost';
+ href?: string;
+}
+
+export function PressableButton({
+ children,
+ className = '',
+ onClick,
+ variant = 'primary',
+ href,
+}: PressableButtonProps) {
+ const baseStyles = {
+ primary:
+ 'bg-brand text-white shadow-sm hover:shadow-brand-hover',
+ secondary:
+ 'bg-white text-ink border border-border-primary hover:border-brand/30 hover:shadow-md',
+ ghost:
+ 'bg-transparent text-brand hover:bg-brand/5',
+ };
+
+ const content = (
+
+ {children}
+
+ );
+
+ if (href) {
+ return (
+
+ {content}
+
+ );
+ }
+
+ return content;
+}
+
+interface InkGlowCardProps {
+ children: ReactNode;
+ className?: string;
+ active?: boolean;
+ color?: string;
+ glowSize?: number;
+}
+
+export function InkGlowCard({ children, className = '', active = false, color = '#C41E3A', glowSize = 280 }: InkGlowCardProps) {
+ const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
+ const [isHovered, setIsHovered] = useState(false);
+ const ref = useRef(null);
+
+ const handleMouseMove = useCallback((e: React.MouseEvent) => {
+ if (!ref.current) return;
+ const rect = ref.current.getBoundingClientRect();
+ setMousePos({
+ x: e.clientX - rect.left,
+ y: e.clientY - rect.top,
+ });
+ }, []);
+
+ const handleMouseEnter = useCallback(() => {
+ setIsHovered(true);
+ }, []);
+
+ const handleMouseLeave = useCallback(() => {
+ setIsHovered(false);
+ }, []);
+
+ return (
+
+ {active && (
+
+ )}
+
+ {children}
+
+ );
+}
+
+interface HoverLinkProps {
+ children: ReactNode;
+ href: string;
+ className?: string;
+ showArrow?: boolean;
+ color?: 'default' | 'brand';
+}
+
+export function HoverLink({
+ children,
+ href,
+ className = '',
+ showArrow = false,
+ color = 'default',
+}: HoverLinkProps) {
+ return (
+
+
+ {children}
+
+
+ {showArrow && (
+
+ )}
+
+ );
+}
diff --git a/src/components/detail/product-card.tsx b/src/components/detail/product-card.tsx
new file mode 100644
index 0000000..c059430
--- /dev/null
+++ b/src/components/detail/product-card.tsx
@@ -0,0 +1,86 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import Link from 'next/link';
+import Image from 'next/image';
+import { ArrowRight, CheckCircle2 } from 'lucide-react';
+
+interface ProductCardProps {
+ product: {
+ id: string;
+ title: string;
+ description: string;
+ image: string;
+ category: string;
+ status: string;
+ features?: string[];
+ };
+}
+
+export function ProductCard({ product }: ProductCardProps) {
+ return (
+
+
+
+
+
+
+
+
+ {product.category}
+
+ {product.status === '已发布' && (
+
+
+ 已发布
+
+ )}
+
+
+
+
+
+ {product.title}
+
+
+
+ {product.description}
+
+
+ {product.features && product.features.length > 0 && (
+
+ {product.features.slice(0, 3).map((feature) => (
+
+ {feature}
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/detail/service-value.tsx b/src/components/detail/service-value.tsx
new file mode 100644
index 0000000..9283de9
--- /dev/null
+++ b/src/components/detail/service-value.tsx
@@ -0,0 +1,189 @@
+'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 (
+
+
+
+
+
+
+
+
+ {service.features.map((feature, index) => {
+ const Icon = icons[index % icons.length]!;
+
+ return (
+
+
+
+
+
+
+
+ {feature.split(':')[1] || feature}
+
+
+
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+ {service.benefits.map((benefit, index) => (
+
+
+
+ ))}
+
+
+
+ {service.process && service.process.length > 0 && (
+
+
+
+
+
+ 服务流程
+
+
标准化流程保障服务质量
+
+
+
+
+
+
+ {service.process.map((step, index) => (
+
+
+
+ {String(index + 1).padStart(2, '0')}
+
+
+
+
+
+ {step.split(':')[0]}
+
+
+ {step.split(':')[1] || step}
+
+
+
+ ))}
+
+
+
+ )}
+
+
+ );
+}
diff --git a/src/components/detail/solution-service-card.tsx b/src/components/detail/solution-service-card.tsx
new file mode 100644
index 0000000..e7cdb96
--- /dev/null
+++ b/src/components/detail/solution-service-card.tsx
@@ -0,0 +1,162 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import Link from 'next/link';
+import Image from 'next/image';
+import { ArrowRight, Building2, Users, Cpu, Package } from 'lucide-react';
+
+interface SolutionCardProps {
+ solution: {
+ id: string;
+ title: string;
+ industry: string;
+ description: string;
+ image?: string;
+ challenges?: string[];
+ suiteCombination?: {
+ primaryProducts: string[];
+ complementaryServices: string[];
+ rationale: string;
+ };
+ };
+}
+
+export function SolutionCard({ solution }: SolutionCardProps) {
+ const industryIcons = {
+ 制造业: Building2,
+ 零售: Users,
+ 金融: Cpu,
+ 政务: Building2,
+ };
+
+ const Icon = industryIcons[solution.industry as keyof typeof industryIcons] || Building2;
+
+ return (
+
+
+
+
+ {solution.image ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {solution.industry}
+
+ {solution.suiteCombination && solution.suiteCombination.primaryProducts.length > 0 && (
+
+
+ {solution.suiteCombination.primaryProducts.length} 产品组合
+
+ )}
+
+
+
+
+
+ {solution.title}
+
+
+
+ {solution.description}
+
+
+ {solution.challenges && solution.challenges.length > 0 && (
+
+ {solution.challenges.slice(0, 2).map((challenge) => (
+
+
+ {challenge}
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ );
+}
+
+interface ServiceCardProps {
+ service: {
+ id: string;
+ title: string;
+ description: string;
+ features?: string[];
+ };
+}
+
+export function ServiceCard({ service }: ServiceCardProps) {
+ return (
+
+
+
+
+
+
+
+
+
+ {service.title}
+
+
+
+ {service.description}
+
+
+ {service.features && service.features.length > 0 && (
+
+ {service.features.slice(0, 3).map((feature) => (
+
+ {feature.split(':')[0]}
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/detail/solution-value.tsx b/src/components/detail/solution-value.tsx
new file mode 100644
index 0000000..a8c72a9
--- /dev/null
+++ b/src/components/detail/solution-value.tsx
@@ -0,0 +1,221 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import {
+ CheckCircle2,
+ Lightbulb,
+ Target,
+ TrendingUp,
+} from 'lucide-react';
+import { TiltCard } from './micro-interactions';
+import { SectionHeader, InkDivider } from './brand-elements';
+import type { Solution } from '@/lib/constants/solutions';
+
+interface SolutionValueSectionProps {
+ solution: Solution;
+}
+
+export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
+ return (
+
+
+
+ 为{solution.industry}量身定制
+ >
+ }
+ subtitle={solution.description}
+ icon={Lightbulb}
+ />
+
+
+
+
+
+
+
+
+ 行业痛点挑战
+
+
+
+ {solution.challenges.map((challenge, index) => (
+
+
+ {challenge}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ 睿新致远解决方案
+
+
+
+ {solution.solutions.map((solutionItem, index) => (
+
+
+ {solutionItem}
+
+ ))}
+
+
+
+
+
+ {solution.valueProposition && (
+
+
+
+
+
+ {solution.valueProposition.headline}
+
+
+
+
+ {solution.valueProposition.points.map((point, index) => (
+
+
+
+
+ {point.title}
+ {point.description}
+
+ ))}
+
+
+ )}
+
+ {solution.suiteCombination && (
+
+
+
+
+
+
+
+
+ 核
+
+ 核心产品组合
+
+
+ {solution.suiteCombination.primaryProducts.map((product, index) => (
+
+ {product}
+
+ ))}
+
+
+
+
+
+
+ 配
+
+ 配套专业服务
+
+
+ {solution.suiteCombination.complementaryServices.map((service, index) => (
+
+ {service}
+
+ ))}
+
+
+
+
+
+ 组合逻辑:{solution.suiteCombination.rationale}
+
+
+
+ )}
+
+
+ );
+}
diff --git a/src/components/sections/case-card.tsx b/src/components/sections/case-card.tsx
new file mode 100644
index 0000000..982a89b
--- /dev/null
+++ b/src/components/sections/case-card.tsx
@@ -0,0 +1,130 @@
+'use client';
+
+interface CaseImpact {
+ value: string;
+ label: string;
+}
+
+interface CaseCardProps {
+ /** Industry label (e.g. "制造业") */
+ industry: string;
+ /** Case title */
+ title: string;
+ /** Challenge the client faced (Bain-style) */
+ challenge: string;
+ /** Solution we provided (Bain-style) */
+ solution: string;
+ /** @deprecated Use challenge + solution instead */
+ context?: string;
+ /** Quantified impact metrics (2 items) */
+ impact: CaseImpact[];
+ /** Client quote */
+ quote: string;
+ /** Client name/title */
+ author: string;
+ /** Optional background image URL */
+ image?: string;
+ className?: string;
+}
+
+export function CaseCard({
+ industry,
+ title,
+ challenge,
+ solution,
+ context,
+ impact,
+ quote,
+ author,
+ image,
+ className = '',
+}: CaseCardProps) {
+ const displayChallenge = challenge || context || '';
+ const displaySolution = solution || '';
+
+ return (
+
+ {/* Background image */}
+ {image && (
+
+

+
+ )}
+
+
+ {/* Industry tag */}
+
+ {industry}
+
+
+ {/* Title */}
+
+ {title}
+
+
+ {/* Bain-style: Challenge + Solution */}
+
+ {/* Challenge */}
+
+
+ 挑战
+
+
+ {displayChallenge}
+
+
+
+ {/* Solution (only if provided) */}
+ {displaySolution && (
+
+
+ 方案
+
+
+ {displaySolution}
+
+
+ )}
+
+
+ {/* Impact numbers — Results (Bain-style, most visual weight) */}
+
+ {impact.map((item, i) => (
+
+
+ {item.value}
+
+
+ {item.label}
+
+
+ ))}
+
+
+ {/* Quote */}
+
+ “{quote}”
+
+
+ {author}
+
+
+
+ );
+}
diff --git a/src/components/sections/case-detail-page.tsx b/src/components/sections/case-detail-page.tsx
new file mode 100644
index 0000000..59c38ee
--- /dev/null
+++ b/src/components/sections/case-detail-page.tsx
@@ -0,0 +1,493 @@
+'use client';
+
+import { motion, useScroll, useTransform } from 'framer-motion';
+import { useRef, useState, useEffect } from 'react';
+import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal';
+import { SectionHeader } from '@/components/sections/section-header';
+import { ServiceCard } from '@/components/sections/service-card';
+import { ScrollProgress } from '@/components/ui/scroll-progress';
+import { useReducedMotion } from '@/hooks/use-reduced-motion';
+import { ArrowRight, Quote, Building2, Target, Lightbulb, TrendingUp } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { AnimatedCounter } from '@/components/ui/animated-counter';
+
+const EASE = [0.22, 1, 0.36, 1] as const;
+
+export interface CaseDetailData {
+ id: string;
+ client: string;
+ industry: string;
+ title: string;
+ subtitle: string;
+ challenge: string;
+ solution: string;
+ result: string;
+ metrics: { value: string; label: string; highlight?: boolean }[];
+ timeline: { phase: string; duration: string; description: string }[];
+ services: { id: string; title: string; description: string }[];
+ testimonial?: { quote: string; author: string; role: string };
+}
+
+interface CaseDetailHeroProps {
+ data: CaseDetailData;
+}
+
+function CaseDetailHero({ data }: CaseDetailHeroProps) {
+ const shouldReduceMotion = useReducedMotion();
+ const heroRef = useRef(null);
+ const { scrollYProgress } = useScroll({
+ target: heroRef,
+ offset: ['start start', 'end start'],
+ });
+
+ const opacity = useTransform(scrollYProgress, [0, 0.6], [1, 0]);
+ const contentY = useTransform(scrollYProgress, [0, 1], [0, 50]);
+
+ return (
+
+
+
+
+
+ {data.industry}
+
+
+ {data.client}
+
+
+
+ {/* Bain-style: big result number first */}
+
+
+ {data.metrics[0]?.value || '40%'}
+
+
+ {data.metrics[0]?.label || '核心成果提升'}
+
+
+
+
+ {data.title}
+
+
+
+ {data.subtitle}
+
+
+ {/* Quick stats row */}
+
+ {data.metrics.slice(1, 4).map((m, i) => (
+
+
+ {m.value}
+
+
+ {m.label}
+
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
+
+function ChallengeSection({ data }: { data: CaseDetailData }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ 面临的挑战
+
+
+
+ {data.challenge}
+
+
+
+
+
+
+ );
+}
+
+function SolutionSection({ data }: { data: CaseDetailData }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ 核心思路
+
+
+
+ {data.solution}
+
+
+
+
+
+
+
+
+ 实施路径
+
+
+
+ {data.timeline.map((t, i) => (
+
+
+ {i + 1}
+
+
+
+
{t.phase}
+
+ {t.duration}
+
+
+
+ {t.description}
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
+
+function ResultsSection({ data }: { data: CaseDetailData }) {
+ const resultsRef = useRef(null);
+ const [inView, setInView] = useState(false);
+ const shouldReduceMotion = useReducedMotion();
+
+ useEffect(() => {
+ if (!resultsRef.current) return;
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry?.isIntersecting) {
+ setInView(true);
+ observer.disconnect();
+ }
+ },
+ { threshold: 0.2 }
+ );
+ observer.observe(resultsRef.current);
+ return () => observer.disconnect();
+ }, []);
+
+ const parseValue = (val: string): { num: number; suffix: string; isNumeric: boolean } => {
+ const match = val.match(/^([\d.]+)(.*)$/);
+ if (match && match[1] && !isNaN(parseFloat(match[1]))) {
+ return { num: parseFloat(match[1]), suffix: match[2] || '', isNumeric: true };
+ }
+ return { num: 0, suffix: val, isNumeric: false };
+ };
+
+ return (
+
+ {/* Background decoration */}
+
+
+
+
+
+ Results
+
+
+ 可量化的业务成果
+
+
+ {data.result}
+
+
+
+
+ {data.metrics.slice(0, 4).map((m, i) => {
+ const { num, suffix, isNumeric } = parseValue(m.value);
+ const decimals = String(num).includes('.') ? String(num).split('.')[1]?.length ?? 0 : 0;
+ return (
+
+ {m.highlight && (
+
+ 核心指标
+
+ )}
+
+ {isNumeric && inView && !shouldReduceMotion ? (
+
+ ) : (
+ <>
+ {isNumeric ? num : suffix}
+ {isNumeric && suffix && (
+
{suffix}
+ )}
+ >
+ )}
+
+
+ {m.label}
+
+
+ );
+ })}
+
+
+ {data.testimonial && (
+
+
+
+
+
+ “{data.testimonial.quote}”
+
+
+
+
+
+
+
{data.testimonial.author}
+
{data.testimonial.role}
+
+
+
+
+
+ )}
+
+
+ );
+}
+
+function RelatedServicesSection({ services }: { services: CaseDetailData['services'] }) {
+ return (
+
+
+
+
+
+
+
+
+ {services.slice(0, 3).map((s, i) => (
+
+ ))}
+
+
+
+
+ );
+}
+
+function CTASection() {
+ return (
+
+
+
+
+ 您也想实现类似的
+ 业务突破
+ ?
+
+
+ 免费咨询,48小时内给出初步方案建议
+
+
+
+
+
+ );
+}
+
+export default function CaseDetailPage({ data }: { data: CaseDetailData }) {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/sections/challenge-section.tsx b/src/components/sections/challenge-section.tsx
index 221cae7..5b04c1a 100644
--- a/src/components/sections/challenge-section.tsx
+++ b/src/components/sections/challenge-section.tsx
@@ -49,7 +49,7 @@ export function ChallengeSection() {
- 您的挑战,我们的使命
+ 您的挑战,我们的使命
深入理解企业数字化进程中的核心痛点,提供针对性解决方案
diff --git a/src/components/sections/cta-section.tsx b/src/components/sections/cta-section.tsx
index e953bde..55ffd8c 100644
--- a/src/components/sections/cta-section.tsx
+++ b/src/components/sections/cta-section.tsx
@@ -1,7 +1,7 @@
'use client';
-import { useRef, useState } from 'react';
-import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion';
+import { useRef } from 'react';
+import { motion } from 'framer-motion';
import { StaticLink } from '@/components/ui/static-link';
import { Button } from '@/components/ui/button';
import { BrandStamp } from '@/components/ui/brand-visuals';
@@ -27,66 +27,14 @@ export function CTASection({
}: CTASectionProps) {
const shouldReduceMotion = useReducedMotion();
const containerRef = useRef(null);
- const mouseX = useMotionValue(0);
- const mouseY = useMotionValue(0);
- const [isHovered, setIsHovered] = useState(false);
-
- const springX = useSpring(mouseX, { stiffness: 80, damping: 20 });
- const springY = useSpring(mouseY, { stiffness: 80, damping: 20 });
-
- const glowX = useTransform(springX, [0, 1], ['-10%', '110%']);
- const glowY = useTransform(springY, [0, 1], ['-10%', '110%']);
-
- function handleMouseMove(e: React.MouseEvent) {
- if (!containerRef.current || shouldReduceMotion) {return;}
- const rect = containerRef.current.getBoundingClientRect();
- mouseX.set((e.clientX - rect.left) / rect.width);
- mouseY.set((e.clientY - rect.top) / rect.height);
- }
return (
setIsHovered(true)}
- onMouseLeave={() => setIsHovered(false)}
className="relative py-24 md:py-32 overflow-hidden"
- style={{ backgroundColor: 'var(--color-cta-bg)' }}
+ style={{ backgroundColor: 'var(--color-bg-secondary)' }}
>
-
- {!shouldReduceMotion && (
-
- )}
-
-
-
-
-
-
-
- 开始对话
+
+ 开始对话
-
+
{title}
-
+
{description}
-
+
免费咨询
-
+
30 分钟内响应
-
+
无销售压力
@@ -144,7 +92,6 @@ export function CTASection({
- 为什么选择我们 + 为什么选择我们
我们不是一家“做完就跑”的外包公司,而是愿意与您一起成长的数字化转型同行者 @@ -84,27 +84,27 @@ export function WhyUsSection() { whileInView={{ opacity: 1, y: 0, scale: 1 }} viewport={{ once: true, margin: '-60px' }} transition={{ duration: 0.5, delay: idx * 0.08, ease: [0.16, 1, 0.3, 1] }} - className="relative group p-6 md:p-8 rounded-2xl bg-[var(--color-bg-primary)] border border-[var(--color-border-primary)] hover:border-[rgba(var(--color-brand-primary-rgb),0.25)] transition-all duration-300" + className="relative group p-6 md:p-8 rounded-2xl bg-[var(--color-bg-primary)] border border-[var(--color-border-primary)] hover:border-[rgba(var(--color-brand-rgb),0.25)] transition-all duration-300" >