refactor(animations): converge P1 motion durations to ≤300ms target

- 151 scripted + 1 manual duration fixes across 46 files
- framer entrance 0.5/0.6s -> 0.3s
- Tailwind hover -> duration-150, entrance -> duration-300
- preserve flip-clock/page-transition per semantic decision
- design-system.ts dead-code deferred
- P1 violations 156 -> 5; P0 remains 0; OK class 249 -> 400
- verified: tsc clean, eslint 0 errors
- verified: 130/130 jest suites, 1573 passed
- verified: visual regression 46/46 zero-pixel diff

Co-Authored-By: 阿睿 <workbuddy@tencent.com>
This commit is contained in:
2026-09-20 11:50:58 +08:00
co-authored by 阿睿
parent 33564b7e9c
commit 2aa0e21319
46 changed files with 151 additions and 151 deletions
@@ -134,7 +134,7 @@ function MetricsStrip({ data }: { data: AboutData }) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.6, delay: i * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: i * 0.08, ease: EASE_OUT }}
className="text-center lg:text-left" className="text-center lg:text-left"
> >
<div className={`text-3xl sm:text-4xl md:text-5xl font-black tracking-tight mb-2 ${i === 0 ? 'text-brand' : 'text-ink'}`}> <div className={`text-3xl sm:text-4xl md:text-5xl font-black tracking-tight mb-2 ${i === 0 ? 'text-brand' : 'text-ink'}`}>
@@ -264,7 +264,7 @@ function MilestonesSection({ data }: { data: AboutData }) {
initial={{ opacity: 0, x: -20 }} initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.6, delay: idx * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: idx * 0.08, ease: EASE_OUT }}
className="relative pl-8 sm:pl-12 md:pl-16 pb-10 sm:pb-12 md:pb-14 last:pb-0" className="relative pl-8 sm:pl-12 md:pl-16 pb-10 sm:pb-12 md:pb-14 last:pb-0"
> >
<div className="absolute left-0 top-2 bottom-0 w-px bg-border-primary last:hidden" /> <div className="absolute left-0 top-2 bottom-0 w-px bg-border-primary last:hidden" />
@@ -75,7 +75,7 @@ export default function BrandContent() {
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.1, ease: EASE_OUT }}
className="mb-8 sm:mb-10" className="mb-8 sm:mb-10"
> >
<span className="inline-flex items-center gap-2 px-3 py-1.5 bg-brand/[0.08] border border-brand/20 rounded-full"> <span className="inline-flex items-center gap-2 px-3 py-1.5 bg-brand/[0.08] border border-brand/20 rounded-full">
@@ -106,7 +106,7 @@ export default function BrandContent() {
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.4, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.4, ease: EASE_OUT }}
className="flex flex-wrap gap-6 sm:gap-10 md:gap-14 mb-10" className="flex flex-wrap gap-6 sm:gap-10 md:gap-14 mb-10"
> >
{brandStats.map((stat, i) => ( {brandStats.map((stat, i) => (
@@ -125,7 +125,7 @@ export default function BrandContent() {
<motion.p <motion.p
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.5, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.5, ease: EASE_OUT }}
className="text-lg sm:text-xl text-text-secondary/80 max-w-2xl leading-relaxed" className="text-lg sm:text-xl text-text-secondary/80 max-w-2xl leading-relaxed"
> >
2026 1 2026 1
@@ -162,7 +162,7 @@ export default function BrandContent() {
{brandValues.map((item, i) => ( {brandValues.map((item, i) => (
<ScrollReveal key={item.title} delay={i * 0.1}> <ScrollReveal key={item.title} delay={i * 0.1}>
<div className="group relative bg-bg-primary border border-border-primary p-8 h-full transition-all duration-300 hover:border-brand/20 hover:shadow-md"> <div className="group relative bg-bg-primary border border-border-primary p-8 h-full transition-all duration-300 hover:border-brand/20 hover:shadow-md">
<div className="absolute top-0 left-0 right-0 h-0.5 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-500 origin-left" /> <div className="absolute top-0 left-0 right-0 h-0.5 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
<div className="w-12 h-12 flex items-center justify-center bg-brand/[0.06] text-brand mb-6 transition-colors duration-300 group-hover:bg-brand group-hover:text-white"> <div className="w-12 h-12 flex items-center justify-center bg-brand/[0.06] text-brand mb-6 transition-colors duration-300 group-hover:bg-brand group-hover:text-white">
<item.icon className="w-5 h-5" /> <item.icon className="w-5 h-5" />
</div> </div>
@@ -25,10 +25,10 @@ function CaseCard({ caseItem, index }: { caseItem: CaseStudy; index: number }) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.6, delay: index * 0.06, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.06, ease: EASE_OUT }}
className="group relative block overflow-hidden border border-border-primary hover:border-border-secondary bg-white hover:bg-bg-secondary transition-all duration-500 flex flex-col h-full" className="group relative block overflow-hidden border border-border-primary hover:border-border-secondary bg-white hover:bg-bg-secondary transition-all duration-150 flex flex-col h-full"
> >
<div className="absolute top-0 left-0 h-0 w-[2px] group-hover:h-full transition-all duration-500 origin-top bg-brand" /> <div className="absolute top-0 left-0 h-0 w-[2px] group-hover:h-full transition-all duration-150 origin-top bg-brand" />
<div className="p-6 sm:p-7 lg:p-8 flex flex-col flex-1 relative z-10"> <div className="p-6 sm:p-7 lg:p-8 flex flex-col flex-1 relative z-10">
<div className="flex items-center gap-3 mb-5"> <div className="flex items-center gap-3 mb-5">
@@ -207,7 +207,7 @@ export default function CasesContentV3({
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.6, delay: i * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: i * 0.08, ease: EASE_OUT }}
className="text-center lg:text-left" className="text-center lg:text-left"
> >
<div className={`text-3xl sm:text-4xl md:text-5xl font-black tracking-tight mb-2 ${ <div className={`text-3xl sm:text-4xl md:text-5xl font-black tracking-tight mb-2 ${
+1 -1
View File
@@ -179,7 +179,7 @@ function HeroSection({ heroData }: {
// reduced-motion 守卫:与 ScrollReveal 同范式——reduce 时传空 initial/animate // reduced-motion 守卫:与 ScrollReveal 同范式——reduce 时传空 initial/animate
// 元素直接以自然(可见)状态渲染,零位移/淡入;默认(非 reduce,含首帧 null)正常动画。 // 元素直接以自然(可见)状态渲染,零位移/淡入;默认(非 reduce,含首帧 null)正常动画。
const rise = (y: number, delay: number, duration = 0.35) => ({ const rise = (y: number, delay: number, duration = 0.3) => ({
initial: reduceMotion ? {} : { opacity: 0, y }, initial: reduceMotion ? {} : { opacity: 0, y },
animate: reduceMotion ? {} : { opacity: 1, y: 0 }, animate: reduceMotion ? {} : { opacity: 1, y: 0 },
transition: { duration: reduceMotion ? 0 : duration, delay: reduceMotion ? 0 : delay, ease: EASE_OUT }, transition: { duration: reduceMotion ? 0 : duration, delay: reduceMotion ? 0 : delay, ease: EASE_OUT },
@@ -40,7 +40,7 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
<motion.div <motion.div
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: EASE }} transition={{ duration: 0.3, ease: EASE }}
className="max-w-4xl" className="max-w-4xl"
> >
<Badge className="mb-4 bg-[var(--color-brand-bg)] text-[var(--color-brand)] hover:bg-[var(--color-brand)]/20 border-0"> <Badge className="mb-4 bg-[var(--color-brand-bg)] text-[var(--color-brand)] hover:bg-[var(--color-brand)]/20 border-0">
@@ -67,7 +67,7 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.6, ease: EASE }} transition={{ duration: 0.3, ease: EASE }}
className="max-w-4xl" className="max-w-4xl"
> >
{/* Featured Image */} {/* Featured Image */}
@@ -110,7 +110,7 @@ export function NewsDetailClient({ news }: NewsDetailClientProps) {
<img <img
src={related.image} src={related.image}
alt={related.title} alt={related.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-150"
/> />
) : ( ) : (
<div className="w-full h-full bg-gradient-to-br from-[var(--color-brand-bg)] to-[var(--color-bg-tertiary)] flex items-center justify-center"> <div className="w-full h-full bg-gradient-to-br from-[var(--color-brand-bg)] to-[var(--color-bg-tertiary)] flex items-center justify-center">
+4 -4
View File
@@ -23,13 +23,13 @@ function NewsCard({ newsItem, index }: { newsItem: NewsItem; index: number }) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.5, delay: index * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.08, ease: EASE_OUT }}
> >
<StaticLink <StaticLink
href={`/news/${newsItem.id}`} href={`/news/${newsItem.id}`}
className="group relative block border border-border-primary hover:border-brand/30 bg-white transition-all duration-500 hover:-translate-y-2 overflow-hidden h-full" className="group relative block border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 hover:-translate-y-2 overflow-hidden h-full"
> >
<div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-500 origin-top" /> <div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-150 origin-top" />
{newsItem.image && !imageError ? ( {newsItem.image && !imageError ? (
<div className="aspect-video bg-bg-secondary overflow-hidden"> <div className="aspect-video bg-bg-secondary overflow-hidden">
@@ -37,7 +37,7 @@ function NewsCard({ newsItem, index }: { newsItem: NewsItem; index: number }) {
src={newsItem.image} src={newsItem.image}
alt={newsItem.title} alt={newsItem.title}
onError={() => setImageError(true)} onError={() => setImageError(true)}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-150"
/> />
</div> </div>
) : ( ) : (
@@ -15,20 +15,20 @@ function RelatedNewsCard({ news, index }: { news: NewsItem; index: number }) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.5, delay: index * 0.1, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.1, ease: EASE_OUT }}
> >
<a <a
href={`/news/${news.id}`} href={`/news/${news.id}`}
className="group relative block border border-border-primary hover:border-brand/30 bg-white transition-all duration-500 hover:-translate-y-2 overflow-hidden h-full" className="group relative block border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 hover:-translate-y-2 overflow-hidden h-full"
> >
<div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-500 origin-top" /> <div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-150 origin-top" />
<div className="aspect-video bg-bg-secondary overflow-hidden"> <div className="aspect-video bg-bg-secondary overflow-hidden">
{news.image ? ( {news.image ? (
<img <img
src={news.image} src={news.image}
alt={news.title} alt={news.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-150"
/> />
) : ( ) : (
<div className="w-full h-full bg-gradient-to-br from-brand/[0.08] to-bg-secondary flex items-center justify-center"> <div className="w-full h-full bg-gradient-to-br from-brand/[0.08] to-bg-secondary flex items-center justify-center">
@@ -183,9 +183,9 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{PAIN_POINTS.map((item, index) => ( {PAIN_POINTS.map((item, index) => (
<div <div
key={index} key={index}
className="group relative border border-border-primary hover:border-brand/30 bg-white p-8 lg:p-10 transition-all duration-500 hover:-translate-y-1 overflow-hidden" className="group relative border border-border-primary hover:border-brand/30 bg-white p-8 lg:p-10 transition-all duration-150 hover:-translate-y-1 overflow-hidden"
> >
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" /> <div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
<div className="w-14 h-14 bg-brand/10 border border-brand/20 flex items-center justify-center mb-6"> <div className="w-14 h-14 bg-brand/10 border border-brand/20 flex items-center justify-center mb-6">
<item.icon className="w-7 h-7 text-brand" /> <item.icon className="w-7 h-7 text-brand" />
@@ -220,7 +220,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{UPGRADE_BENEFITS.map((item, index) => ( {UPGRADE_BENEFITS.map((item, index) => (
<div <div
key={index} key={index}
className="text-center p-8 border border-border-primary bg-white hover:border-brand/30 hover:bg-bg-secondary transition-all duration-500 group" className="text-center p-8 border border-border-primary bg-white hover:border-brand/30 hover:bg-bg-secondary transition-all duration-150 group"
> >
<div className="w-12 h-12 mx-auto mb-6 bg-brand/10 border border-brand/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300"> <div className="w-12 h-12 mx-auto mb-6 bg-brand/10 border border-brand/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<item.icon className="w-6 h-6 text-brand" /> <item.icon className="w-6 h-6 text-brand" />
@@ -254,9 +254,9 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{FEATURE_MODULES.map((item, index) => ( {FEATURE_MODULES.map((item, index) => (
<div <div
key={index} key={index}
className="group relative border border-border-primary hover:border-brand/30 bg-white p-8 transition-all duration-500 hover:-translate-y-1 overflow-hidden" className="group relative border border-border-primary hover:border-brand/30 bg-white p-8 transition-all duration-150 hover:-translate-y-1 overflow-hidden"
> >
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" /> <div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="w-12 h-12 bg-brand/10 border border-brand/20 flex items-center justify-center shrink-0"> <div className="w-12 h-12 bg-brand/10 border border-brand/20 flex items-center justify-center shrink-0">
@@ -357,7 +357,7 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
{certifications.map((cert, index) => ( {certifications.map((cert, index) => (
<div <div
key={index} key={index}
className="group text-center p-8 border border-border-primary bg-white hover:border-brand/30 hover:bg-bg-secondary transition-all duration-500 min-w-[200px]" className="group text-center p-8 border border-border-primary bg-white hover:border-brand/30 hover:bg-bg-secondary transition-all duration-150 min-w-[200px]"
> >
<div className="w-16 h-16 mx-auto mb-4 bg-brand/10 border border-brand/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300"> <div className="w-16 h-16 mx-auto mb-4 bg-brand/10 border border-brand/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<Shield className="w-8 h-8 text-brand" /> <Shield className="w-8 h-8 text-brand" />
@@ -136,7 +136,7 @@ function FeaturesSection({ features }: FeaturesSectionProps) {
{features.map((feature, idx) => ( {features.map((feature, idx) => (
<div <div
key={idx} key={idx}
className="group relative p-8 bg-bg-primary hover:bg-bg-secondary transition-all duration-500" className="group relative p-8 bg-bg-primary hover:bg-bg-secondary transition-all duration-150"
> >
<div className="flex items-center gap-5"> <div className="flex items-center gap-5">
<div className="w-10 h-10 rounded-xl bg-brand-soft flex items-center justify-center text-brand shrink-0"> <div className="w-10 h-10 rounded-xl bg-brand-soft flex items-center justify-center text-brand shrink-0">
@@ -179,7 +179,7 @@ function BenefitsSection({ benefits }: BenefitsSectionProps) {
{benefits.map((benefit, idx) => ( {benefits.map((benefit, idx) => (
<div <div
key={idx} key={idx}
className="group relative p-8 bg-bg-primary hover:bg-bg-secondary transition-all duration-500" className="group relative p-8 bg-bg-primary hover:bg-bg-secondary transition-all duration-150"
> >
<div className="flex items-center gap-6"> <div className="flex items-center gap-6">
<div className="w-12 h-12 rounded-2xl bg-brand-soft/50 flex items-center justify-center text-brand shrink-0"> <div className="w-12 h-12 rounded-2xl bg-brand-soft/50 flex items-center justify-center text-brand shrink-0">
@@ -232,9 +232,9 @@ function ProcessSection({ process }: ProcessSectionProps) {
{process.map((step, idx) => ( {process.map((step, idx) => (
<div <div
key={idx} key={idx}
className="group relative text-center p-8 bg-bg-primary hover:bg-bg-secondary transition-all duration-500" className="group relative text-center p-8 bg-bg-primary hover:bg-bg-secondary transition-all duration-150"
> >
<div className="relative z-10 w-20 h-20 rounded-3xl bg-bg-secondary border border-border-primary group-hover:border-brand/50 flex items-center justify-center mx-auto mb-8 transition-all duration-500"> <div className="relative z-10 w-20 h-20 rounded-3xl bg-bg-secondary border border-border-primary group-hover:border-brand/50 flex items-center justify-center mx-auto mb-8 transition-all duration-150">
<span className="text-3xl font-bold text-brand">{idx + 1}</span> <span className="text-3xl font-bold text-brand">{idx + 1}</span>
</div> </div>
@@ -67,7 +67,7 @@ function StatItem({ value, suffix, label, start, delay }: { value: number; suffi
<motion.div <motion.div
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
animate={start ? { opacity: 1, y: 0 } : {}} animate={start ? { opacity: 1, y: 0 } : {}}
transition={{ delay, duration: 0.6, ease: EASE_OUT }} transition={{ delay, duration: 0.3, ease: EASE_OUT }}
> >
<div className="text-4xl lg:text-5xl font-bold text-ink tracking-tight leading-none mb-2"> <div className="text-4xl lg:text-5xl font-bold text-ink tracking-tight leading-none mb-2">
{count}{suffix} {count}{suffix}
@@ -171,7 +171,7 @@ function ProductCard({ product, size = 'small' }: { product: Product; size?: 'la
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.6, ease: EASE_OUT }} transition={{ duration: 0.3, ease: EASE_OUT }}
className="h-full" className="h-full"
> >
<StaticLink <StaticLink
@@ -425,7 +425,7 @@ function SuiteCombosSection({ products }: { products: Product[] }) {
<StaticLink <StaticLink
key={idx} key={idx}
href="/solutions" href="/solutions"
className="group p-10 lg:p-12 bg-white hover:bg-bg-secondary transition-all duration-500 overflow-hidden" className="group p-10 lg:p-12 bg-white hover:bg-bg-secondary transition-all duration-150 overflow-hidden"
> >
<div> <div>
<div className="flex items-center gap-4 mb-6"> <div className="flex items-center gap-4 mb-6">
@@ -445,7 +445,7 @@ function SuiteCombosSection({ products }: { products: Product[] }) {
</span> </span>
</div> </div>
<h3 className="text-xl lg:text-2xl font-bold text-ink mb-3 group-hover:translate-x-1.5 transition-transform duration-500"> <h3 className="text-xl lg:text-2xl font-bold text-ink mb-3 group-hover:translate-x-1.5 transition-transform duration-150">
{combo.label} {combo.label}
</h3> </h3>
@@ -74,7 +74,7 @@ export function StandaloneProductClient({ item }: StandaloneProductClientProps)
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5 }} transition={{ duration: 0.3 }}
className="max-w-3xl mx-auto text-center" className="max-w-3xl mx-auto text-center"
> >
<div className="mb-6 flex justify-center"> <div className="mb-6 flex justify-center">
@@ -133,7 +133,7 @@ export function StandaloneProductClient({ item }: StandaloneProductClientProps)
initial={{ opacity: 0, x: -20 }} initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5 }} transition={{ duration: 0.3 }}
> >
<h2 className="mb-6 text-2xl font-bold text-ink"></h2> <h2 className="mb-6 text-2xl font-bold text-ink"></h2>
{features.length > 0 ? ( {features.length > 0 ? (
@@ -167,7 +167,7 @@ export function StandaloneProductClient({ item }: StandaloneProductClientProps)
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5 }} transition={{ duration: 0.3 }}
className="mb-10" className="mb-10"
> >
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
@@ -210,7 +210,7 @@ export function StandaloneProductClient({ item }: StandaloneProductClientProps)
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5 }} transition={{ duration: 0.3 }}
className="mb-10" className="mb-10"
> >
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
@@ -173,8 +173,8 @@ function FeaturesSection({ service }: { service: Service }) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.6, delay: index * 0.06, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.06, ease: EASE_OUT }}
className="group relative p-8 sm:p-10 transition-all duration-500 hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 bg-bg-primary" className="group relative p-8 sm:p-10 transition-all duration-150 hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 bg-bg-primary"
> >
<div className="relative z-10"> <div className="relative z-10">
<Icon className="w-8 h-8 mb-6 text-brand" /> <Icon className="w-8 h-8 mb-6 text-brand" />
@@ -227,8 +227,8 @@ function ProcessSection({ service }: { service: Service }) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.6, delay: index * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.08, ease: EASE_OUT }}
className="relative group p-6 sm:p-8 bg-bg-primary hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 transition-all duration-500" className="relative group p-6 sm:p-8 bg-bg-primary hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 transition-all duration-150"
> >
<div <div
className="w-2 h-2 rounded-full mb-6" className="w-2 h-2 rounded-full mb-6"
@@ -263,7 +263,7 @@ function CTASection() {
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, ease: EASE_OUT }} transition={{ duration: 0.3, ease: EASE_OUT }}
className="max-w-3xl" className="max-w-3xl"
> >
<h2 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-extrabold text-ink tracking-tight leading-[0.95] mb-8"> <h2 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-extrabold text-ink tracking-tight leading-[0.95] mb-8">
@@ -209,7 +209,7 @@ function ServicesGridSection({ services, pageCopy }: { services?: Service[]; pag
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.6, delay: i * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: i * 0.08, ease: EASE_OUT }}
> >
<StaticLink <StaticLink
href={`/services/${service.id}`} href={`/services/${service.id}`}
@@ -122,9 +122,9 @@ function ChallengesSection({ challenges }: ChallengesSectionProps) {
{challenges.map((challenge, idx) => ( {challenges.map((challenge, idx) => (
<div <div
key={idx} key={idx}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-500" className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-150"
> >
<div className="absolute left-0 top-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-500 origin-top" /> <div className="absolute left-0 top-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-150 origin-top" />
<div className="flex items-start gap-6"> <div className="flex items-start gap-6">
<div className="w-10 h-10 rounded-xl bg-brand-soft/50 flex items-center justify-center text-brand shrink-0 mt-0.5"> <div className="w-10 h-10 rounded-xl bg-brand-soft/50 flex items-center justify-center text-brand shrink-0 mt-0.5">
<span className="text-lg font-bold">{idx + 1}</span> <span className="text-lg font-bold">{idx + 1}</span>
@@ -169,9 +169,9 @@ function SolutionsSection({ solutions }: SolutionsSectionProps) {
{solutions.map((solution, idx) => ( {solutions.map((solution, idx) => (
<div <div
key={idx} key={idx}
className="group relative p-10 bg-white hover:bg-bg-secondary transition-all duration-500" className="group relative p-10 bg-white hover:bg-bg-secondary transition-all duration-150"
> >
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" /> <div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
<div className="flex items-start gap-6"> <div className="flex items-start gap-6">
<div className="w-14 h-14 rounded-2xl bg-brand-soft flex items-center justify-center text-brand shrink-0"> <div className="w-14 h-14 rounded-2xl bg-brand-soft flex items-center justify-center text-brand shrink-0">
@@ -226,9 +226,9 @@ function ValuePropositionSection({ valueProposition }: ValuePropositionProps) {
return ( return (
<div <div
key={idx} key={idx}
className="group relative p-10 text-center bg-white hover:bg-bg-secondary transition-all duration-500" className="group relative p-10 text-center bg-white hover:bg-bg-secondary transition-all duration-150"
> >
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-1/3 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-center" /> <div className="absolute top-0 left-1/2 -translate-x-1/2 w-1/3 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-center" />
<div className="w-20 h-20 rounded-3xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-8"> <div className="w-20 h-20 rounded-3xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-8">
{icon} {icon}
@@ -279,9 +279,9 @@ function SuiteCombinationSection({ suiteCombination, products }: SuiteCombinatio
{primaryProducts.map((product) => product && ( {primaryProducts.map((product) => product && (
<div <div
key={product.id} key={product.id}
className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-500 text-center lg:col-span-2" className="group relative p-8 bg-white hover:bg-bg-secondary transition-all duration-150 text-center lg:col-span-2"
> >
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" /> <div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
<div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-6"> <div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-6">
<Package className="w-8 h-8" /> <Package className="w-8 h-8" />
@@ -303,7 +303,7 @@ function SuiteCombinationSection({ suiteCombination, products }: SuiteCombinatio
</ScrollReveal> </ScrollReveal>
<ScrollReveal delay={0.3} className="lg:col-span-2"> <ScrollReveal delay={0.3} className="lg:col-span-2">
<div className="group relative p-8 bg-brand-soft/10 hover:bg-bg-secondary transition-all duration-500 text-center h-full flex flex-col justify-center"> <div className="group relative p-8 bg-brand-soft/10 hover:bg-bg-secondary transition-all duration-150 text-center h-full flex flex-col justify-center">
<div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-6"> <div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand mx-auto mb-6">
<Users className="w-8 h-8" /> <Users className="w-8 h-8" />
</div> </div>
@@ -129,7 +129,7 @@ function SolutionCard({ solution, index, products, featured = false }: { solutio
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.6, delay: index * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.08, ease: EASE_OUT }}
> >
<StaticLink <StaticLink
href={`/solutions/${solution.id}`} href={`/solutions/${solution.id}`}
+12 -12
View File
@@ -65,14 +65,14 @@ function StrengthCard({ strength, index }: { strength: StrengthData; index: numb
return ( return (
<motion.div <motion.div
key={strength.title} key={strength.title}
className="group relative border border-border-primary hover:border-brand/30 bg-white transition-all duration-500 overflow-hidden" className="group relative border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 overflow-hidden"
initial={shouldReduceMotion ? {} : { opacity: 0, y: 30 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.6, delay: index * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.08, ease: EASE_OUT }}
whileHover={{ y: -6 }} whileHover={{ y: -6 }}
> >
<div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-500 origin-top" /> <div className="absolute top-0 left-0 bottom-0 w-1 bg-brand scale-y-0 group-hover:scale-y-100 transition-transform duration-150 origin-top" />
<div className="p-8 sm:p-10"> <div className="p-8 sm:p-10">
<div className="flex items-center gap-4 mb-6"> <div className="flex items-center gap-4 mb-6">
@@ -88,7 +88,7 @@ function StrengthCard({ strength, index }: { strength: StrengthData; index: numb
<p className="text-text-secondary text-sm leading-relaxed">{strength.description}</p> <p className="text-text-secondary text-sm leading-relaxed">{strength.description}</p>
</div> </div>
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-brand/40 via-brand/10 to-transparent scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" /> <div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-brand/40 via-brand/10 to-transparent scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
</motion.div> </motion.div>
); );
} }
@@ -100,14 +100,14 @@ function CultureCard({ culture, index }: { culture: CultureData; index: number }
return ( return (
<motion.div <motion.div
key={culture.title} key={culture.title}
className="group relative border border-border-primary hover:border-brand/30 bg-white transition-all duration-500 text-center overflow-hidden" className="group relative border border-border-primary hover:border-brand/30 bg-white transition-all duration-150 text-center overflow-hidden"
initial={shouldReduceMotion ? {} : { opacity: 0, y: 30 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.6, delay: index * 0.1, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.1, ease: EASE_OUT }}
whileHover={{ y: -8 }} whileHover={{ y: -8 }}
> >
<div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left" /> <div className="absolute top-0 left-0 right-0 h-1 bg-brand scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-left" />
<div className="p-8 sm:p-10 lg:p-12"> <div className="p-8 sm:p-10 lg:p-12">
<div className="w-16 h-16 mx-auto mb-6 flex items-center justify-center border border-brand/30 bg-brand/[0.08] text-brand"> <div className="w-16 h-16 mx-auto mb-6 flex items-center justify-center border border-brand/30 bg-brand/[0.08] text-brand">
@@ -120,7 +120,7 @@ function CultureCard({ culture, index }: { culture: CultureData; index: number }
<p className="text-text-secondary leading-relaxed">{culture.description}</p> <p className="text-text-secondary leading-relaxed">{culture.description}</p>
</div> </div>
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-brand/20 to-transparent scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-center" /> <div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-brand/20 to-transparent scale-x-0 group-hover:scale-x-100 transition-transform duration-150 origin-center" />
</motion.div> </motion.div>
); );
} }
@@ -152,7 +152,7 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.1, ease: EASE_OUT }}
className="mb-8 sm:mb-10" className="mb-8 sm:mb-10"
> >
<span className="inline-flex items-center gap-2 px-3 py-1.5 bg-brand/[0.08] border border-brand/20 rounded-full"> <span className="inline-flex items-center gap-2 px-3 py-1.5 bg-brand/[0.08] border border-brand/20 rounded-full">
@@ -183,7 +183,7 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.4, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.4, ease: EASE_OUT }}
className="flex flex-wrap gap-6 sm:gap-10 md:gap-14 mb-10 sm:mb-12" className="flex flex-wrap gap-6 sm:gap-10 md:gap-14 mb-10 sm:mb-12"
> >
{stats.map((stat, i) => ( {stats.map((stat, i) => (
@@ -204,7 +204,7 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
<motion.p <motion.p
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.5, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.5, ease: EASE_OUT }}
className="text-lg sm:text-xl text-text-secondary/80 max-w-2xl leading-relaxed mb-12 sm:mb-14" className="text-lg sm:text-xl text-text-secondary/80 max-w-2xl leading-relaxed mb-12 sm:mb-14"
> >
{teamData.heroDescription} {teamData.heroDescription}
@@ -215,7 +215,7 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.6, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.6, ease: EASE_OUT }}
className="flex flex-col sm:flex-row sm:items-center gap-4 sm:gap-6" className="flex flex-col sm:flex-row sm:items-center gap-4 sm:gap-6"
> >
<CTAButton href={teamData.heroPrimaryCtaHref || '/contact'}> <CTAButton href={teamData.heroPrimaryCtaHref || '/contact'}>
+1 -1
View File
@@ -12,7 +12,7 @@ const pageVariants: Variants = {
opacity: 1, opacity: 1,
y: 0, y: 0,
transition: { transition: {
duration: 0.35, duration: 0.3,
ease: [0.16, 1, 0.3, 1] as [number, number, number, number], ease: [0.16, 1, 0.3, 1] as [number, number, number, number],
}, },
}, },
+5 -5
View File
@@ -49,7 +49,7 @@ export function MethodologyFramework({
initial={{ opacity: 0, x: -30 }} initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-50px' }} viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.6, delay: index * 0.08, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.08, ease: EASE_OUT }}
> >
<div className="flex gap-6 lg:gap-10"> <div className="flex gap-6 lg:gap-10">
<div className="flex flex-col items-center pt-2"> <div className="flex flex-col items-center pt-2">
@@ -121,7 +121,7 @@ export function TechStackShowcase({ title, subtitle, categories }: TechStackShow
{categories.map((category, index) => ( {categories.map((category, index) => (
<div <div
key={index} key={index}
className="group relative p-8 border border-border-primary bg-bg-primary hover:border-border-secondary hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 transition-all duration-500" className="group relative p-8 border border-border-primary bg-bg-primary hover:border-border-secondary hover:bg-bg-secondary hover:shadow-lg hover:-translate-y-1 transition-all duration-150"
> >
<h3 className="text-lg font-bold text-text-primary mb-5">{category.name}</h3> <h3 className="text-lg font-bold text-text-primary mb-5">{category.name}</h3>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
@@ -196,7 +196,7 @@ export function DeliveryTimeline({
<div className="lg:w-1/2 pl-16 lg:pl-0"> <div className="lg:w-1/2 pl-16 lg:pl-0">
<div className={cn( <div className={cn(
'p-8 lg:p-10 border border-border-primary bg-bg-secondary hover:border-border-secondary transition-all duration-500', 'p-8 lg:p-10 border border-border-primary bg-bg-secondary hover:border-border-secondary transition-all duration-150',
index % 2 === 0 ? 'lg:ml-12' : 'lg:mr-12' index % 2 === 0 ? 'lg:ml-12' : 'lg:mr-12'
)}> )}>
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
@@ -295,7 +295,7 @@ export function FAQSection({ title, subtitle, items }: FAQSectionProps) {
height: openIndex === index ? 'auto' : 0, height: openIndex === index ? 'auto' : 0,
opacity: openIndex === index ? 1 : 0, opacity: openIndex === index ? 1 : 0,
}} }}
transition={{ duration: 0.4, ease: EASE_OUT }} transition={{ duration: 0.3, ease: EASE_OUT }}
className="overflow-hidden" className="overflow-hidden"
> >
<div className="px-6 lg:px-7 pb-7 pt-1 text-text-secondary leading-relaxed"> <div className="px-6 lg:px-7 pb-7 pt-1 text-text-secondary leading-relaxed">
@@ -348,7 +348,7 @@ export function DataProofSection({ title, subtitle, metrics }: DataProofSectionP
{metrics.map((metric, index) => ( {metrics.map((metric, index) => (
<div <div
key={index} key={index}
className="group relative p-10 lg:p-12 bg-bg-secondary hover:bg-bg-secondary hover:shadow-lg transition-all duration-500" className="group relative p-10 lg:p-12 bg-bg-secondary hover:bg-bg-secondary hover:shadow-lg transition-all duration-150"
> >
<div className="relative z-10"> <div className="relative z-10">
<div className="text-5xl lg:text-6xl font-bold tracking-tighter mb-4"> <div className="text-5xl lg:text-6xl font-bold tracking-tighter mb-4">
+3 -3
View File
@@ -50,7 +50,7 @@ export function TestimonialSection({
<motion.div <motion.div
className="flex" className="flex"
animate={{ x: `-${activeIndex * 100}%` }} animate={{ x: `-${activeIndex * 100}%` }}
transition={{ duration: 0.6, ease: EASE_OUT }} transition={{ duration: 0.3, ease: EASE_OUT }}
> >
{testimonials.map((testimonial, index) => ( {testimonials.map((testimonial, index) => (
<div key={index} className="w-full shrink-0"> <div key={index} className="w-full shrink-0">
@@ -94,7 +94,7 @@ export function TestimonialSection({
key={index} key={index}
onClick={() => setActiveIndex(index)} onClick={() => setActiveIndex(index)}
className={cn( className={cn(
'h-2 rounded-full transition-all duration-500', 'h-2 rounded-full transition-all duration-300',
activeIndex === index activeIndex === index
? 'w-8' ? 'w-8'
: 'w-2 bg-border-primary hover:bg-border-secondary' : 'w-2 bg-border-primary hover:bg-border-secondary'
@@ -133,7 +133,7 @@ export function LogoWall({ title, logos }: LogoWallProps) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.05, ease: EASE_OUT }} transition={{ duration: 0.3, delay: index * 0.05, ease: EASE_OUT }}
className="flex items-center justify-center p-8 bg-bg-primary hover:bg-bg-secondary transition-colors duration-300" className="flex items-center justify-center p-8 bg-bg-primary hover:bg-bg-secondary transition-colors duration-300"
> >
<div className="text-center"> <div className="text-center">
+1 -1
View File
@@ -14,7 +14,7 @@ export function CaseStudyCard({ study, index }: CaseStudyCardProps) {
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-50px' }} viewport={{ once: true, margin: '-50px' }}
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.3, delay: index * 0.1 }}
className="rounded-xl border border-gray-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md" className="rounded-xl border border-gray-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-md"
> >
<div className="mb-3 flex items-center gap-2"> <div className="mb-3 flex items-center gap-2">
+2 -2
View File
@@ -37,7 +37,7 @@ export function DetailCTASection({
initial={{ opacity: 0, y: 32 }} initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }} viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="max-w-3xl" className="max-w-3xl"
> >
{/* 标签 */} {/* 标签 */}
@@ -77,7 +77,7 @@ export function DetailCTASection({
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }} whileInView={{ opacity: 1 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.5, duration: 0.6 }} transition={{ delay: 0.5, duration: 0.3 }}
className="mt-10 text-sm text-white/50" className="mt-10 text-sm text-white/50"
> >
&lt; 2 · 7×24 · &lt; 2 · 7×24 ·
+3 -3
View File
@@ -69,7 +69,7 @@ export function DetailHero({
scale: 1, scale: 1,
filter: 'blur(0px)', filter: 'blur(0px)',
transition: { transition: {
duration: 0.7, duration: 0.3,
ease: [0.22, 1, 0.36, 1] as const, ease: [0.22, 1, 0.36, 1] as const,
}, },
}, },
@@ -136,7 +136,7 @@ export function DetailHero({
opacity: 1, opacity: 1,
y: 0, y: 0,
transition: { transition: {
duration: 0.65, duration: 0.3,
delay: 0.4, delay: 0.4,
ease: [0.22, 1, 0.36, 1] as const, ease: [0.22, 1, 0.36, 1] as const,
}, },
@@ -156,7 +156,7 @@ export function DetailHero({
opacity: 1, opacity: 1,
y: 0, y: 0,
transition: { transition: {
duration: 0.6, duration: 0.3,
delay: 0.55, delay: 0.55,
ease: [0.22, 1, 0.36, 1] as const, ease: [0.22, 1, 0.36, 1] as const,
}, },
@@ -147,7 +147,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
duration: 0.5, duration: 0.3,
delay: index * 0.06, delay: index * 0.06,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
@@ -188,7 +188,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
duration: 0.4, duration: 0.3,
delay: index * 0.06, delay: index * 0.06,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
@@ -223,7 +223,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
delay: 0.3 + index * 0.05, delay: 0.3 + index * 0.05,
duration: 0.35, duration: 0.3,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
className="flex items-start gap-3 text-sm text-text-secondary" className="flex items-start gap-3 text-sm text-text-secondary"
@@ -78,7 +78,7 @@ export function DetailTrustSection({
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="mb-12" className="mb-12"
> >
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
@@ -102,7 +102,7 @@ export function DetailTrustSection({
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ transition={{
duration: 0.5, duration: 0.3,
delay: index * 0.08, delay: index * 0.08,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
@@ -123,7 +123,7 @@ export function DetailTrustSection({
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="mb-12" className="mb-12"
> >
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[0.95]"> <h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-black text-ink tracking-tight leading-[0.95]">
@@ -139,7 +139,7 @@ export function DetailTrustSection({
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ transition={{
duration: 0.5, duration: 0.3,
delay: index * 0.08, delay: index * 0.08,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
@@ -156,7 +156,7 @@ export function DetailTrustSection({
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="text-center" className="text-center"
> >
<h3 className="text-lg font-semibold text-text-secondary mb-6"></h3> <h3 className="text-lg font-semibold text-text-secondary mb-6"></h3>
+7 -7
View File
@@ -77,7 +77,7 @@ export function ListPageHero({
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="max-w-4xl mx-auto text-center" className="max-w-4xl mx-auto text-center"
> >
{/* Dynamic Badge */} {/* Dynamic Badge */}
@@ -86,7 +86,7 @@ export function ListPageHero({
initial={shouldReduceMotion ? {} : { opacity: 0, scale: 0.95, y: 8 }} initial={shouldReduceMotion ? {} : { opacity: 0, scale: 0.95, y: 8 }}
whileInView={{ opacity: 1, scale: 1, y: 0 }} whileInView={{ opacity: 1, scale: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.1, duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ delay: 0.1, duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full text-sm font-medium mb-6 backdrop-blur-sm" className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full text-sm font-medium mb-6 backdrop-blur-sm"
style={{ backgroundColor: badgeStyle.bg, color: badgeStyle.text, border: `1px solid ${badgeStyle.border}` }} style={{ backgroundColor: badgeStyle.bg, color: badgeStyle.text, border: `1px solid ${badgeStyle.border}` }}
> >
@@ -100,7 +100,7 @@ export function ListPageHero({
initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.15, duration: 0.7, ease: [0.16, 1, 0.3, 1] }} transition={{ delay: 0.15, duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight mb-6 text-[var(--color-text-primary)]" className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight mb-6 text-[var(--color-text-primary)]"
> >
{title} {title}
@@ -111,7 +111,7 @@ export function ListPageHero({
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.25, duration: 0.65, ease: [0.16, 1, 0.3, 1] }} transition={{ delay: 0.25, duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-lg md:text-xl max-w-2xl mx-auto leading-relaxed mb-4 text-[var(--color-text-secondary)]" className="text-lg md:text-xl max-w-2xl mx-auto leading-relaxed mb-4 text-[var(--color-text-secondary)]"
> >
{subtitle} {subtitle}
@@ -123,7 +123,7 @@ export function ListPageHero({
initial={shouldReduceMotion ? {} : { opacity: 0, y: 18 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 18 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.35, duration: 0.6, ease: [0.16, 1, 0.3, 1] }} transition={{ delay: 0.35, duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-base max-w-xl mx-auto mb-10 text-[var(--color-text-muted)]" className="text-base max-w-xl mx-auto mb-10 text-[var(--color-text-muted)]"
> >
{description} {description}
@@ -136,7 +136,7 @@ export function ListPageHero({
initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.4, duration: 0.7, ease: [0.16, 1, 0.3, 1] }} transition={{ delay: 0.4, duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="flex flex-wrap justify-center gap-8 md:gap-12 mt-12 pt-12 border-t border-[var(--color-border-primary)]" className="flex flex-wrap justify-center gap-8 md:gap-12 mt-12 pt-12 border-t border-[var(--color-border-primary)]"
> >
{stats.map((stat, index) => ( {stats.map((stat, index) => (
@@ -145,7 +145,7 @@ export function ListPageHero({
initial={shouldReduceMotion ? {} : { opacity: 0, scale: 0.95 }} initial={shouldReduceMotion ? {} : { opacity: 0, scale: 0.95 }}
whileInView={{ opacity: 1, scale: 1 }} whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.5 + index * 0.08, duration: 0.45, ease: [0.16, 1, 0.3, 1] }} transition={{ delay: 0.5 + index * 0.08, duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-center min-w-[100px]" className="text-center min-w-[100px]"
> >
<div className="text-3xl md:text-4xl font-bold tracking-tight text-[var(--color-text-primary)]"> <div className="text-3xl md:text-4xl font-bold tracking-tight text-[var(--color-text-primary)]">
+2 -2
View File
@@ -24,7 +24,7 @@ export function ProductCard({ product }: ProductCardProps) {
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
duration: 0.65, duration: 0.3,
ease: [0.22, 1, 0.36, 1] as const, ease: [0.22, 1, 0.36, 1] as const,
}} }}
className="group" className="group"
@@ -37,7 +37,7 @@ export function ProductCard({ product }: ProductCardProps) {
alt={product.title} alt={product.title}
fill fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
className="object-cover group-hover:scale-105 transition-transform duration-500" className="object-cover group-hover:scale-105 transition-transform duration-150"
/> />
<div className="absolute top-4 left-4 flex gap-2"> <div className="absolute top-4 left-4 flex gap-2">
+3 -3
View File
@@ -63,7 +63,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
delay: index * 0.06, delay: index * 0.06,
duration: 0.5, duration: 0.3,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
className="bain-card p-6" className="bain-card p-6"
@@ -105,7 +105,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
delay: index * 0.06, delay: index * 0.06,
duration: 0.4, duration: 0.3,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
className="bain-card p-5 flex items-start gap-3" className="bain-card p-5 flex items-start gap-3"
@@ -135,7 +135,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
delay: index * 0.08, delay: index * 0.08,
duration: 0.45, duration: 0.3,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
className="bain-card p-5" className="bain-card p-5"
@@ -37,7 +37,7 @@ export function SolutionCard({ solution }: SolutionCardProps) {
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
duration: 0.65, duration: 0.3,
ease: [0.22, 1, 0.36, 1] as const, ease: [0.22, 1, 0.36, 1] as const,
}} }}
className="group" className="group"
@@ -50,7 +50,7 @@ export function SolutionCard({ solution }: SolutionCardProps) {
src={solution.image} src={solution.image}
alt={solution.title} alt={solution.title}
fill fill
className="object-cover group-hover:scale-105 transition-transform duration-500" className="object-cover group-hover:scale-105 transition-transform duration-150"
/> />
) : ( ) : (
<Icon className="w-16 h-16 text-brand/20 group-hover:text-brand/40 transition-colors" /> <Icon className="w-16 h-16 text-brand/20 group-hover:text-brand/40 transition-colors" />
@@ -117,7 +117,7 @@ export function ServiceCard({ service }: ServiceCardProps) {
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
duration: 0.65, duration: 0.3,
ease: [0.22, 1, 0.36, 1] as const, ease: [0.22, 1, 0.36, 1] as const,
}} }}
className="group" className="group"
+5 -5
View File
@@ -45,7 +45,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
initial={{ opacity: 0, x: -30 }} initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
> >
<div className="bain-card p-8"> <div className="bain-card p-8">
<h3 className="text-xl font-bold text-ink mb-6 flex items-center gap-3"> <h3 className="text-xl font-bold text-ink mb-6 flex items-center gap-3">
@@ -63,7 +63,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
delay: index * 0.08, delay: index * 0.08,
duration: 0.45, duration: 0.3,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
className="flex items-start gap-3 text-text-secondary" className="flex items-start gap-3 text-text-secondary"
@@ -81,7 +81,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
initial={{ opacity: 0, x: 30 }} initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.7, delay: 0.15, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, delay: 0.15, ease: [0.22, 1, 0.36, 1] }}
> >
<div className="bain-card p-8"> <div className="bain-card p-8">
<h3 className="text-xl font-bold text-ink mb-6 flex items-center gap-3"> <h3 className="text-xl font-bold text-ink mb-6 flex items-center gap-3">
@@ -99,7 +99,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
delay: 0.25 + index * 0.08, delay: 0.25 + index * 0.08,
duration: 0.45, duration: 0.3,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
className="flex items-start gap-3 text-text-secondary" className="flex items-start gap-3 text-text-secondary"
@@ -142,7 +142,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
viewport={{ once: true }} viewport={{ once: true }}
transition={{ transition={{
delay: index * 0.08, delay: index * 0.08,
duration: 0.5, duration: 0.3,
ease: [0.22, 1, 0.36, 1], ease: [0.22, 1, 0.36, 1],
}} }}
className="bain-card p-7" className="bain-card p-7"
+2 -2
View File
@@ -44,14 +44,14 @@ export function CaseCard({
return ( return (
<div <div
className={`group relative overflow-hidden p-6 sm:p-8 md:p-10 lg:p-14 flex flex-col transition-all duration-500 bg-[var(--color-bg-primary)] border border-[var(--color-border-primary)] hover:bg-[var(--color-bg-secondary)] ${className}`} className={`group relative overflow-hidden p-6 sm:p-8 md:p-10 lg:p-14 flex flex-col transition-all duration-150 bg-[var(--color-bg-primary)] border border-[var(--color-border-primary)] hover:bg-[var(--color-bg-secondary)] ${className}`}
> >
{/* Background image */} {/* Background image */}
{image && ( {image && (
<div <div
aria-hidden="true" aria-hidden="true"
style={{ backgroundImage: `url(${image})` }} style={{ backgroundImage: `url(${image})` }}
className="absolute inset-0 bg-cover bg-center transition-opacity duration-500 opacity-[0.02] group-hover:opacity-[0.05]" className="absolute inset-0 bg-cover bg-center transition-opacity duration-150 opacity-[0.02] group-hover:opacity-[0.05]"
/> />
)} )}
+3 -3
View File
@@ -63,7 +63,7 @@ function CaseDetailHero({ data }: CaseDetailHeroProps) {
className="flex flex-wrap items-center gap-3 mb-8" className="flex flex-wrap items-center gap-3 mb-8"
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: EASE }} transition={{ duration: 0.3, ease: EASE }}
> >
<span <span
className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full text-xs font-medium" className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full text-xs font-medium"
@@ -408,7 +408,7 @@ function ResultsSection({ data }: { data: CaseDetailData }) {
return ( return (
<div <div
key={i} key={i}
className="p-10 sm:p-12 text-center transition-all duration-500 hover:bg-white/[0.05] group relative overflow-hidden" className="p-10 sm:p-12 text-center transition-all duration-150 hover:bg-white/[0.05] group relative overflow-hidden"
style={{ backgroundColor: 'var(--color-brand)' }} style={{ backgroundColor: 'var(--color-brand)' }}
> >
{m.highlight && ( {m.highlight && (
@@ -416,7 +416,7 @@ function ResultsSection({ data }: { data: CaseDetailData }) {
</div> </div>
)} )}
<div className="font-sans tabular-nums text-[clamp(40px,8vw,64px)] font-black leading-none mb-3 text-white transition-transform duration-500 group-hover:scale-110"> <div className="font-sans tabular-nums text-[clamp(40px,8vw,64px)] font-black leading-none mb-3 text-white transition-transform duration-150 group-hover:scale-110">
{isNumeric && inView && !shouldReduceMotion ? ( {isNumeric && inView && !shouldReduceMotion ? (
<AnimatedCounter <AnimatedCounter
value={num} value={num}
@@ -40,7 +40,7 @@ export function ChallengeSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }} viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="mb-14" className="mb-14"
> >
<div className="flex items-center gap-4 mb-4"> <div className="flex items-center gap-4 mb-4">
+2 -2
View File
@@ -40,7 +40,7 @@ export function CTASection({
initial={shouldReduceMotion ? {} : { opacity: 0, scale: 0.96, y: 16 }} initial={shouldReduceMotion ? {} : { opacity: 0, scale: 0.96, y: 16 }}
whileInView={{ opacity: 1, scale: 1, y: 0 }} whileInView={{ opacity: 1, scale: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-center max-w-3xl mx-auto relative" className="text-center max-w-3xl mx-auto relative"
> >
<div className="absolute -top-8 left-1/2 -translate-x-1/2 hidden md:block"> <div className="absolute -top-8 left-1/2 -translate-x-1/2 hidden md:block">
@@ -50,7 +50,7 @@ export function CTASection({
initial={shouldReduceMotion ? {} : { opacity: 0, y: -8 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: -8 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.4, delay: 0.15 }} transition={{ duration: 0.3, delay: 0.15 }}
className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-[var(--color-brand-soft)] border border-[var(--color-border-primary)] mb-8" className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-[var(--color-brand-soft)] border border-[var(--color-border-primary)] mb-8"
> >
<Sparkles className="w-3.5 h-3.5 text-[var(--color-brand)]" /> <Sparkles className="w-3.5 h-3.5 text-[var(--color-brand)]" />
+3 -3
View File
@@ -43,7 +43,7 @@ export function HeroSectionV2() {
const fadeUp = (delay: number) => ({ const fadeUp = (delay: number) => ({
initial: shouldReduceMotion ? {} : { opacity: 0, y: 24 }, initial: shouldReduceMotion ? {} : { opacity: 0, y: 24 },
animate: { opacity: 1, y: 0 }, animate: { opacity: 1, y: 0 },
transition: { duration: 0.5, delay, ease: EASE }, transition: { duration: 0.3, delay, ease: EASE },
}); });
return ( return (
@@ -106,7 +106,7 @@ export function HeroSectionV2() {
<motion.div <motion.div
initial={shouldReduceMotion ? {} : { opacity: 0, y: 32 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 32 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.5, ease: EASE }} transition={{ duration: 0.3, delay: 0.5, ease: EASE }}
> >
<div <div
ref={cardRef} ref={cardRef}
@@ -122,7 +122,7 @@ export function HeroSectionV2() {
onMouseLeave={() => setIsCardHovered(false)} onMouseLeave={() => setIsCardHovered(false)}
> >
<div <div
className="absolute inset-0 rounded-2xl pointer-events-none transition-opacity duration-500" className="absolute inset-0 rounded-2xl pointer-events-none transition-opacity duration-300"
style={{ style={{
opacity: isCardHovered ? 1 : 0, opacity: isCardHovered ? 1 : 0,
background: `radial-gradient(400px circle at ${mousePos.x}px ${mousePos.y}px, rgba(var(--color-brand-rgb), 0.06), transparent 40%)`, background: `radial-gradient(400px circle at ${mousePos.x}px ${mousePos.y}px, rgba(var(--color-brand-rgb), 0.06), transparent 40%)`,
+4 -4
View File
@@ -25,16 +25,16 @@ export function IndustryGrid({ items, className = '' }: IndustryGridProps) {
return ( return (
<div <div
key={i} key={i}
className="group relative overflow-hidden py-8 sm:py-10 px-4 sm:px-6 text-center cursor-pointer transition-all duration-400 bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-secondary)]" className="group relative overflow-hidden py-8 sm:py-10 px-4 sm:px-6 text-center cursor-pointer transition-all duration-150 bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-secondary)]"
role="button" role="button"
tabIndex={0} tabIndex={0}
> >
<div className="relative z-10"> <div className="relative z-10">
<Icon className="w-7 h-7 sm:w-8 sm:h-8 mx-auto mb-2 sm:mb-3 transition-transform duration-400 group-hover:scale-110 text-[var(--color-text-primary)] group-hover:text-[var(--color-brand)]" /> <Icon className="w-7 h-7 sm:w-8 sm:h-8 mx-auto mb-2 sm:mb-3 transition-transform duration-150 group-hover:scale-110 text-[var(--color-text-primary)] group-hover:text-[var(--color-brand)]" />
<div className="text-[13px] sm:text-[14px] font-medium transition-colors duration-400 text-[var(--color-text-primary)] group-hover:text-[var(--color-brand)]"> <div className="text-[13px] sm:text-[14px] font-medium transition-colors duration-150 text-[var(--color-text-primary)] group-hover:text-[var(--color-brand)]">
{item.name} {item.name}
</div> </div>
<div className="text-[10px] sm:text-[11px] mt-1 sm:mt-1.5 transition-colors duration-400 text-[var(--color-text-subtle)] group-hover:text-[var(--color-text-muted)]"> <div className="text-[10px] sm:text-[11px] mt-1 sm:mt-1.5 transition-colors duration-150 text-[var(--color-text-subtle)] group-hover:text-[var(--color-text-muted)]">
{item.count} {item.count}
</div> </div>
</div> </div>
+2 -2
View File
@@ -41,7 +41,7 @@ export function InsightCard({
<div <div
aria-hidden="true" aria-hidden="true"
style={{ backgroundImage: `url(${image})` }} style={{ backgroundImage: `url(${image})` }}
className="absolute inset-0 bg-cover bg-center transition-transform duration-700 group-hover:scale-105 opacity-10" className="absolute inset-0 bg-cover bg-center transition-transform duration-150 group-hover:scale-105 opacity-10"
/> />
)} )}
@@ -67,7 +67,7 @@ export function InsightCard({
return ( return (
<div <div
className={`group p-6 sm:p-8 md:p-10 transition-all duration-400 cursor-pointer flex flex-col bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-secondary)] ${className}`} className={`group p-6 sm:p-8 md:p-10 transition-all duration-150 cursor-pointer flex flex-col bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-secondary)] ${className}`}
> >
<div className="text-[10px] tracking-[2px] uppercase font-medium mb-3 md:mb-4" <div className="text-[10px] tracking-[2px] uppercase font-medium mb-3 md:mb-4"
style={{ color: 'var(--color-brand)' }}> style={{ color: 'var(--color-brand)' }}>
@@ -19,7 +19,7 @@ export function MethodologySection() {
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}} animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-center max-w-3xl mx-auto mb-14" className="text-center max-w-3xl mx-auto mb-14"
> >
<h2 id="methodology-heading" className="text-3xl sm:text-4xl font-semibold text-[var(--color-text-primary)] mb-4"> <h2 id="methodology-heading" className="text-3xl sm:text-4xl font-semibold text-[var(--color-text-primary)] mb-4">
+5 -5
View File
@@ -29,12 +29,12 @@ export function ProductCard({
return ( return (
<StaticLink <StaticLink
href={href} href={href}
className={`group relative block p-6 sm:p-8 lg:p-10 bg-[var(--color-bg-primary)] border border-[var(--color-border-primary)] rounded-sm transition-all duration-500 overflow-hidden ${className}`} className={`group relative block p-6 sm:p-8 lg:p-10 bg-[var(--color-bg-primary)] border border-[var(--color-border-primary)] rounded-sm transition-all duration-300 overflow-hidden ${className}`}
style={{ minHeight: '44px', minWidth: '44px' }} style={{ minHeight: '44px', minWidth: '44px' }}
> >
{/* Left accent bar — visible on hover */} {/* Left accent bar — visible on hover */}
<div <div
className="absolute left-0 top-0 bottom-0 w-[3px] bg-[var(--color-brand)] transition-transform duration-500 origin-top" className="absolute left-0 top-0 bottom-0 w-[3px] bg-[var(--color-brand)] transition-transform duration-300 origin-top"
style={{ transform: 'scaleY(0)' }} style={{ transform: 'scaleY(0)' }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.transform = 'scaleY(1)'; e.currentTarget.style.transform = 'scaleY(1)';
@@ -44,7 +44,7 @@ export function ProductCard({
{/* Content */} {/* Content */}
<div className="relative z-10"> <div className="relative z-10">
{/* Icon: 装饰性图标,对屏幕阅读器隐藏 */} {/* Icon: 装饰性图标,对屏幕阅读器隐藏 */}
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-lg bg-[var(--color-brand-bg)] flex items-center justify-center mb-4 sm:mb-5 transition-transform duration-500 group-hover:scale-110" aria-hidden="true"> <div className="w-10 h-10 sm:w-12 sm:h-12 rounded-lg bg-[var(--color-brand-bg)] flex items-center justify-center mb-4 sm:mb-5 transition-transform duration-150 group-hover:scale-110" aria-hidden="true">
{icon} {icon}
</div> </div>
@@ -73,9 +73,9 @@ export function ProductCard({
</div> </div>
{/* Hover background lift */} {/* Hover background lift */}
<div className="absolute inset-0 bg-[var(--color-bg-hover)] opacity-0 transition-opacity duration-500 pointer-events-none" /> <div className="absolute inset-0 bg-[var(--color-bg-hover)] opacity-0 transition-opacity duration-150 pointer-events-none" />
<div <div
className="absolute inset-0 opacity-0 transition-opacity duration-500 pointer-events-none" className="absolute inset-0 opacity-0 transition-opacity duration-300 pointer-events-none"
onMouseEnter={(e) => { e.currentTarget.style.opacity = '1'; }} onMouseEnter={(e) => { e.currentTarget.style.opacity = '1'; }}
/> />
</StaticLink> </StaticLink>
@@ -17,7 +17,7 @@ export function ProductMatrixSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }} viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="flex flex-col md:flex-row md:items-end md:justify-between gap-4 mb-14" className="flex flex-col md:flex-row md:items-end md:justify-between gap-4 mb-14"
> >
<div> <div>
+1 -1
View File
@@ -27,7 +27,7 @@ export function QuestionCard({
className={`group md:grid md:grid-cols-2 border-b border-[var(--color-border-primary)] relative overflow-hidden ${className}`} className={`group md:grid md:grid-cols-2 border-b border-[var(--color-border-primary)] relative overflow-hidden ${className}`}
> >
{/* Left accent bar — visible on hover */} {/* Left accent bar — visible on hover */}
<div className="absolute left-0 top-0 bottom-0 w-[3px] hidden md:block transition-transform duration-500 origin-top bg-[var(--color-brand)]" <div className="absolute left-0 top-0 bottom-0 w-[3px] hidden md:block transition-transform duration-300 origin-top bg-[var(--color-brand)]"
style={{ transform: 'scaleY(0)' }} style={{ transform: 'scaleY(0)' }}
onMouseEnter={(e) => { e.currentTarget.style.transform = 'scaleY(1)'; }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'scaleY(1)'; }}
/> />
+3 -3
View File
@@ -35,7 +35,7 @@ export function SectionHeader({
initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-40px' }} viewport={{ once: true, margin: '-40px' }}
transition={{ duration: 0.6, ease: EASE }} transition={{ duration: 0.3, ease: EASE }}
> >
{/* Eyebrow label with brand accent bar */} {/* Eyebrow label with brand accent bar */}
<div className="flex items-center gap-3 mb-4 md:mb-5"> <div className="flex items-center gap-3 mb-4 md:mb-5">
@@ -51,7 +51,7 @@ export function SectionHeader({
initial={shouldReduceMotion ? {} : { scaleY: 0 }} initial={shouldReduceMotion ? {} : { scaleY: 0 }}
whileInView={{ scaleY: 1 }} whileInView={{ scaleY: 1 }}
viewport={{ once: true, margin: '-40px' }} viewport={{ once: true, margin: '-40px' }}
transition={{ duration: 0.4, delay: 0.1, ease: EASE }} transition={{ duration: 0.3, delay: 0.1, ease: EASE }}
/> />
<motion.div <motion.div
className="text-[10px] tracking-[4px] uppercase font-medium" className="text-[10px] tracking-[4px] uppercase font-medium"
@@ -63,7 +63,7 @@ export function SectionHeader({
initial={shouldReduceMotion ? {} : { opacity: 0, x: -8 }} initial={shouldReduceMotion ? {} : { opacity: 0, x: -8 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: '-40px' }} viewport={{ once: true, margin: '-40px' }}
transition={{ duration: 0.4, delay: 0.2, ease: EASE }} transition={{ duration: 0.3, delay: 0.2, ease: EASE }}
> >
{label} {label}
</motion.div> </motion.div>
+1 -1
View File
@@ -34,7 +34,7 @@ export function ServiceCard({
initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-40px' }} viewport={{ once: true, margin: '-40px' }}
transition={{ duration: 0.6, delay, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, delay, ease: [0.22, 1, 0.36, 1] }}
> >
<div className="p-6 md:p-8"> <div className="p-6 md:p-8">
{/* Title */} {/* Title */}
@@ -40,7 +40,7 @@ export function SocialProofSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }} viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-center max-w-3xl mx-auto mb-16" className="text-center max-w-3xl mx-auto mb-16"
> >
<h2 id="social-proof-heading" className="text-3xl sm:text-4xl font-semibold text-[var(--color-text-primary)] mb-4"> <h2 id="social-proof-heading" className="text-3xl sm:text-4xl font-semibold text-[var(--color-text-primary)] mb-4">
@@ -56,7 +56,7 @@ export function SocialProofSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="grid grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8 mb-16" className="grid grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8 mb-16"
> >
{METRICS.map((metric, idx) => { {METRICS.map((metric, idx) => {
@@ -67,7 +67,7 @@ export function SocialProofSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.5, delay: idx * 0.1, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, delay: idx * 0.1, ease: [0.16, 1, 0.3, 1] }}
className="text-center group" className="text-center group"
> >
<div className="w-12 h-12 rounded-full bg-[var(--color-brand)]/5 flex items-center justify-center mx-auto mb-4 group-hover:bg-[var(--color-brand)]/10 transition-colors duration-300"> <div className="w-12 h-12 rounded-full bg-[var(--color-brand)]/5 flex items-center justify-center mx-auto mb-4 group-hover:bg-[var(--color-brand)]/10 transition-colors duration-300">
@@ -95,7 +95,7 @@ export function SocialProofSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.5, delay: 0.2 + idx * 0.1, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, delay: 0.2 + idx * 0.1, ease: [0.16, 1, 0.3, 1] }}
className="relative p-6 md:p-8 rounded-2xl bg-[var(--color-bg-section)] border border-[var(--color-border-primary)] hover:border-[rgba(var(--color-brand-rgb),0.2)] transition-all duration-300" className="relative p-6 md:p-8 rounded-2xl bg-[var(--color-bg-section)] border border-[var(--color-border-primary)] hover:border-[rgba(var(--color-brand-rgb),0.2)] transition-all duration-300"
> >
<Quote className="w-8 h-8 text-[var(--color-brand)]/15 mb-4" strokeWidth={1.5} /> <Quote className="w-8 h-8 text-[var(--color-brand)]/15 mb-4" strokeWidth={1.5} />
+4 -4
View File
@@ -59,7 +59,7 @@ export function WhyUsSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-100px' }} viewport={{ once: true, margin: '-100px' }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="mb-14" className="mb-14"
> >
<div className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-[var(--color-brand-bg)] border border-[var(--color-brand)]/10 mb-4"> <div className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-[var(--color-brand-bg)] border border-[var(--color-brand)]/10 mb-4">
@@ -83,7 +83,7 @@ export function WhyUsSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 24, scale: 0.96 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 24, scale: 0.96 }}
whileInView={{ opacity: 1, y: 0, scale: 1 }} whileInView={{ opacity: 1, y: 0, scale: 1 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.5, delay: idx * 0.08, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, 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-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"
> >
<div className="flex items-start justify-between mb-4"> <div className="flex items-start justify-between mb-4">
@@ -134,7 +134,7 @@ export function WhyUsSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 16 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.5, delay: 0.3, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, delay: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="text-center" className="text-center"
> >
<p className="text-sm text-[var(--color-text-muted)] mb-6"></p> <p className="text-sm text-[var(--color-text-muted)] mb-6"></p>
@@ -151,7 +151,7 @@ export function WhyUsSection() {
initial={shouldReduceMotion ? {} : { opacity: 0, y: 16 }} initial={shouldReduceMotion ? {} : { opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }} viewport={{ once: true, margin: '-60px' }}
transition={{ duration: 0.5, delay: 0.4, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, delay: 0.4, ease: [0.16, 1, 0.3, 1] }}
className="mt-16 pt-10 border-t border-[var(--color-border-primary)]" className="mt-16 pt-10 border-t border-[var(--color-border-primary)]"
> >
<p className="text-xs text-center text-[var(--color-text-subtle)] mb-6 tracking-wide uppercase"> / </p> <p className="text-xs text-center text-[var(--color-text-subtle)] mb-6 tracking-wide uppercase"> / </p>
+1 -1
View File
@@ -92,7 +92,7 @@ export function BrandStamp({ children }: { children: React.ReactNode }) {
initial={shouldReduceMotion ? {} : { scale: 1.3, opacity: 0, rotate: -12 }} initial={shouldReduceMotion ? {} : { scale: 1.3, opacity: 0, rotate: -12 }}
whileInView={{ scale: 1, opacity: 1, rotate: -5 }} whileInView={{ scale: 1, opacity: 1, rotate: -5 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="inline-block px-3 py-1.5 border-2 border-[var(--color-brand)] rounded-sm" className="inline-block px-3 py-1.5 border-2 border-[var(--color-brand)] rounded-sm"
style={{ style={{
transform: 'rotate(-5deg)', transform: 'rotate(-5deg)',
+2 -2
View File
@@ -72,7 +72,7 @@ export function StatsShowcase({
<div <div
key={i} key={i}
className={cn( className={cn(
'relative p-8 sm:p-10 md:p-12 text-center transition-all duration-500 group', 'relative p-8 sm:p-10 md:p-12 text-center transition-all duration-300 group',
isDark ? 'bg-dark-bg hover:bg-dark-bg-secondary' : 'bg-bg-primary hover:bg-bg-hover' isDark ? 'bg-dark-bg hover:bg-dark-bg-secondary' : 'bg-bg-primary hover:bg-bg-hover'
)} )}
> >
@@ -131,7 +131,7 @@ export function StatsShowcase({
{item.accent && ( {item.accent && (
<div <div
className="absolute top-0 left-1/2 -translate-x-1/2 w-12 h-0.5 opacity-0 group-hover:opacity-100 transition-opacity duration-500" className="absolute top-0 left-1/2 -translate-x-1/2 w-12 h-0.5 opacity-0 group-hover:opacity-100 transition-opacity duration-150"
style={{ backgroundColor: item.accent }} style={{ backgroundColor: item.accent }}
/> />
)} )}