x
This commit is contained in:
1 parent
3f9db48df4
commit
9a5a30778e
5 files changed
+227
-30
No files matched your search
@@ -160,6 +160,53 @@ export default function ProductStoryScrollForm({ payload, onChange, onBrowseMedi
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Visual Theme Selector (Dark Theater vs Light Luxury) */}
|
||||||
|
<div className="p-4 rounded-2xl bg-night-950 border border-white/10 space-y-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<label className="text-xs font-bold uppercase tracking-widest text-slate-300 flex items-center gap-2">
|
||||||
|
<Layers className="w-4 h-4 text-cyan-glow" />
|
||||||
|
ธีมการแสดงผลของ Story (Visual Theme)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onChange({ ...payload, storyTheme: 'dark' })}
|
||||||
|
className={`p-3.5 rounded-xl border text-left transition-all cursor-pointer ${
|
||||||
|
(payload.storyTheme || 'dark') === 'dark'
|
||||||
|
? 'bg-cyan-glow/10 border-cyan-glow/50 text-white ring-1 ring-cyan-glow/30'
|
||||||
|
: 'bg-white/5 border-white/10 text-slate-400 hover:text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<span className="text-xs font-black uppercase tracking-wider">Dark Theater (Apple Pro)</span>
|
||||||
|
{(payload.storyTheme || 'dark') === 'dark' && <CheckCircle className="w-4 h-4 text-cyan-glow" />}
|
||||||
|
</div>
|
||||||
|
<p className="text-[11px] text-slate-400 leading-relaxed">
|
||||||
|
ฉากมืดสไตล์โรงภาพยนตร์ แสงนีออนและ Radial Glow เรืองแสงรอบสินค้า
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onChange({ ...payload, storyTheme: 'light' })}
|
||||||
|
className={`p-3.5 rounded-xl border text-left transition-all cursor-pointer ${
|
||||||
|
payload.storyTheme === 'light'
|
||||||
|
? 'bg-cyan-glow/10 border-cyan-glow/50 text-white ring-1 ring-cyan-glow/30'
|
||||||
|
: 'bg-white/5 border-white/10 text-slate-400 hover:text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<span className="text-xs font-black uppercase tracking-wider">Light Studio (Clean Luxury)</span>
|
||||||
|
{payload.storyTheme === 'light' && <CheckCircle className="w-4 h-4 text-cyan-glow" />}
|
||||||
|
</div>
|
||||||
|
<p className="text-[11px] text-slate-400 leading-relaxed">
|
||||||
|
ฉากสว่าง หรูหรา คลีนตา กลมกลืนกับธีมสีครีม/ขาวของเว็บไซต์ ไม่เป็นกล่องดำ
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 1. Core Hero Showcase Image */}
|
{/* 1. Core Hero Showcase Image */}
|
||||||
<div className="p-4 rounded-2xl bg-night-950 border border-white/10 space-y-4">
|
<div className="p-4 rounded-2xl bg-night-950 border border-white/10 space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import prisma from "../lib/prisma";
|
import prisma from "../lib/prisma";
|
||||||
// Trigger reload: support story_scroll and apple_watch_tour in ProductLayoutSectionPayloadSchema
|
// Trigger reload: support storyTheme in ProductLayoutSectionPayloadSchema
|
||||||
import {
|
import {
|
||||||
LocalizedStringSchema,
|
LocalizedStringSchema,
|
||||||
HeroSectionPayloadSchema,
|
HeroSectionPayloadSchema,
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ function ProductMainSection({
|
|||||||
const storyScenes = Array.isArray(payload.storyScenes) ? (payload.storyScenes as any[]) : undefined
|
const storyScenes = Array.isArray(payload.storyScenes) ? (payload.storyScenes as any[]) : undefined
|
||||||
const endCtaTitle = (payload.endCtaTitle as string) || undefined
|
const endCtaTitle = (payload.endCtaTitle as string) || undefined
|
||||||
const endCtaButtonText = (payload.endCtaButtonText as string) || undefined
|
const endCtaButtonText = (payload.endCtaButtonText as string) || undefined
|
||||||
|
const storyTheme = (payload.storyTheme as string) || 'dark'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-12">
|
<div className="space-y-12">
|
||||||
@@ -161,6 +162,7 @@ function ProductMainSection({
|
|||||||
lang="th"
|
lang="th"
|
||||||
endCtaTitle={endCtaTitle}
|
endCtaTitle={endCtaTitle}
|
||||||
endCtaButtonText={endCtaButtonText}
|
endCtaButtonText={endCtaButtonText}
|
||||||
|
storyTheme={storyTheme}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ interface Props {
|
|||||||
lang: string
|
lang: string
|
||||||
endCtaTitle?: string
|
endCtaTitle?: string
|
||||||
endCtaButtonText?: string
|
endCtaButtonText?: string
|
||||||
|
storyTheme?: 'dark' | 'light' | string
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_SCENES: StoryScene[] = [
|
const DEFAULT_SCENES: StoryScene[] = [
|
||||||
@@ -37,7 +38,7 @@ const DEFAULT_SCENES: StoryScene[] = [
|
|||||||
id: 'material',
|
id: 'material',
|
||||||
kicker: '02 / MATERIAL',
|
kicker: '02 / MATERIAL',
|
||||||
title: 'CERAMIC\nCOMPOUND.',
|
title: 'CERAMIC\nCOMPOUND.',
|
||||||
description: 'สูตรผสมเนื้อเซรามิกเกรดพรีเมียม สึกหรอน้อย เสียงเบียบเงียบ ไร้แรงสั่นสะเทือน และลดฝุ่นผงจับจานเบรกอย่างเห็นได้ชัด',
|
description: 'สูตรผสมเนื้อเซรามิกเกรดพรีเมียม สึกหรอน้อย เสียงเงียบ ไร้แรงสั่นสะเทือน และลดฝุ่นผงจับจานเบรกอย่างเห็นได้ชัด',
|
||||||
chips: ['Low Noise', 'Low Vibration', 'Cleaner Braking'],
|
chips: ['Low Noise', 'Low Vibration', 'Cleaner Braking'],
|
||||||
theme: 'material',
|
theme: 'material',
|
||||||
},
|
},
|
||||||
@@ -87,11 +88,14 @@ export default function CinematicStoryScroll({
|
|||||||
lang,
|
lang,
|
||||||
endCtaTitle,
|
endCtaTitle,
|
||||||
endCtaButtonText,
|
endCtaButtonText,
|
||||||
|
storyTheme = 'dark',
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const [activeScene, setActiveScene] = useState<string>('hero')
|
const [activeScene, setActiveScene] = useState<string>('hero')
|
||||||
const [isStoryVisible, setIsStoryVisible] = useState(false)
|
const [isStoryVisible, setIsStoryVisible] = useState(false)
|
||||||
const storyRef = useRef<HTMLDivElement | null>(null)
|
const storyRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
|
const isLight = storyTheme === 'light'
|
||||||
|
|
||||||
const activeScenes = useMemo(() => {
|
const activeScenes = useMemo(() => {
|
||||||
if (scenes && scenes.length > 0) return scenes
|
if (scenes && scenes.length > 0) return scenes
|
||||||
return DEFAULT_SCENES
|
return DEFAULT_SCENES
|
||||||
@@ -136,7 +140,10 @@ export default function CinematicStoryScroll({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{ threshold: 0.55 }
|
{
|
||||||
|
rootMargin: '-20% 0px -20% 0px',
|
||||||
|
threshold: [0.1, 0.4],
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
triggers.forEach((t) => observer.observe(t))
|
triggers.forEach((t) => observer.observe(t))
|
||||||
@@ -167,25 +174,53 @@ export default function CinematicStoryScroll({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active scene index
|
const scrollToScene = (index: number) => {
|
||||||
|
const storyEl = storyRef.current
|
||||||
|
if (!storyEl) return
|
||||||
|
const triggers = storyEl.querySelectorAll<HTMLElement>('.story-trigger')
|
||||||
|
if (triggers[index]) {
|
||||||
|
triggers[index].scrollIntoView({ behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const activeIndex = Math.max(0, activeScenes.findIndex(s => s.id === activeScene))
|
const activeIndex = Math.max(0, activeScenes.findIndex(s => s.id === activeScene))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={storyRef} className="relative w-full bg-[#0c1320] text-white overflow-hidden transition-colors duration-700 select-none">
|
<div
|
||||||
|
ref={storyRef}
|
||||||
|
className={`relative w-full overflow-hidden transition-colors duration-700 select-none ${
|
||||||
|
isLight
|
||||||
|
? 'bg-[#f7f5ed] text-slate-900 border-y border-line-primary/20'
|
||||||
|
: 'bg-[#0b101c] text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{/* Top Atmosphere Gradient Blend (prevents harsh abrupt cut into dark) */}
|
||||||
|
{!isLight && (
|
||||||
|
<div className="absolute top-0 left-0 right-0 h-32 bg-gradient-to-b from-surface-page via-[#0b101c]/60 to-[#0b101c] pointer-events-none z-20" />
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Side Progress Dots (Desktop) */}
|
{/* Side Progress Dots (Desktop) */}
|
||||||
<div
|
<div
|
||||||
className={`fixed right-6 top-1/2 -translate-y-1/2 z-40 hidden lg:flex flex-col gap-2.5 transition-opacity duration-300 pointer-events-none ${
|
className={`fixed right-6 top-1/2 -translate-y-1/2 z-40 hidden lg:flex flex-col gap-2.5 transition-opacity duration-300 pointer-events-auto ${
|
||||||
isStoryVisible ? 'opacity-100' : 'opacity-0'
|
isStoryVisible ? 'opacity-100' : 'opacity-0'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{activeScenes.map((s, idx) => (
|
{activeScenes.map((s, idx) => (
|
||||||
<div
|
<button
|
||||||
key={s.id}
|
key={s.id}
|
||||||
className={`w-2 rounded-full transition-all duration-300 ${
|
type="button"
|
||||||
|
onClick={() => scrollToScene(idx)}
|
||||||
|
className={`rounded-full transition-all duration-300 cursor-pointer ${
|
||||||
activeIndex === idx
|
activeIndex === idx
|
||||||
? 'h-7 bg-white shadow-lg shadow-white/30'
|
? isLight
|
||||||
: 'h-2 bg-white/20'
|
? 'w-2.5 h-7 bg-brand-primary shadow-md'
|
||||||
|
: 'w-2.5 h-7 bg-white shadow-lg shadow-white/40'
|
||||||
|
: isLight
|
||||||
|
? 'w-2.5 h-2.5 bg-slate-300 hover:bg-slate-400'
|
||||||
|
: 'w-2.5 h-2.5 bg-white/20 hover:bg-white/50'
|
||||||
}`}
|
}`}
|
||||||
|
title={`Scene 0${idx + 1}`}
|
||||||
|
aria-label={`Jump to scene 0${idx + 1}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -195,6 +230,54 @@ export default function CinematicStoryScroll({
|
|||||||
{/* Sticky 100vh Stage */}
|
{/* Sticky 100vh Stage */}
|
||||||
<div className="sticky top-0 h-screen w-full overflow-hidden flex items-center justify-center">
|
<div className="sticky top-0 h-screen w-full overflow-hidden flex items-center justify-center">
|
||||||
{/* Ambient Reactive Glow Backdrops */}
|
{/* Ambient Reactive Glow Backdrops */}
|
||||||
|
{isLight ? (
|
||||||
|
<>
|
||||||
|
{/* Light Theme Reactive Glows */}
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 transition-opacity duration-700 pointer-events-none ${
|
||||||
|
activeScene === 'heat' ? 'opacity-100' : 'opacity-0'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
background: 'radial-gradient(circle at 35% 50%, rgba(255, 120, 30, 0.16), transparent 45%), linear-gradient(180deg, #fef7ee 0%, #faeee0 100%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 transition-opacity duration-700 pointer-events-none ${
|
||||||
|
activeScene === 'material' ? 'opacity-100' : 'opacity-0'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
background: 'radial-gradient(circle at 35% 50%, rgba(30, 120, 255, 0.14), transparent 45%), linear-gradient(180deg, #f4f8fe 0%, #edf3fd 100%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 transition-opacity duration-700 pointer-events-none ${
|
||||||
|
activeScene === 'friction' ? 'opacity-100' : 'opacity-0'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
backgroundImage: 'linear-gradient(rgba(0,0,0,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,0.04) 1px, transparent 1px)',
|
||||||
|
backgroundSize: '40px 40px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 transition-opacity duration-700 pointer-events-none ${
|
||||||
|
activeScene === 'durability' ? 'opacity-100' : 'opacity-0'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
background: 'radial-gradient(circle at 40% 60%, rgba(216, 179, 90, 0.18), transparent 45%), linear-gradient(120deg, #faf6ed 0%, #f3ece0 100%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 transition-opacity duration-700 pointer-events-none ${
|
||||||
|
activeScene === 'hero' ? 'opacity-100' : 'opacity-0'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
background: 'radial-gradient(circle at 35% 50%, rgba(20, 103, 255, 0.12), transparent 42%), linear-gradient(180deg, #f7f5ed 0%, #f0ede3 100%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{/* Dark Theater Reactive Glows */}
|
||||||
<div
|
<div
|
||||||
className={`absolute inset-0 transition-opacity duration-700 pointer-events-none ${
|
className={`absolute inset-0 transition-opacity duration-700 pointer-events-none ${
|
||||||
activeScene === 'heat' ? 'opacity-100' : 'opacity-0'
|
activeScene === 'heat' ? 'opacity-100' : 'opacity-0'
|
||||||
@@ -236,6 +319,8 @@ export default function CinematicStoryScroll({
|
|||||||
background: 'radial-gradient(circle at 35% 50%, rgba(20, 103, 255, 0.3), transparent 35%), linear-gradient(180deg, #0c1320 0%, #0a0f18 100%)',
|
background: 'radial-gradient(circle at 35% 50%, rgba(20, 103, 255, 0.3), transparent 35%), linear-gradient(180deg, #0c1320 0%, #0a0f18 100%)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Desktop Layout: Left Showcase Visual, Right Copy */}
|
{/* Desktop Layout: Left Showcase Visual, Right Copy */}
|
||||||
<div className="relative w-full max-w-7xl mx-auto px-6 md:px-12 h-full flex flex-col lg:flex-row items-center justify-between gap-8 z-10">
|
<div className="relative w-full max-w-7xl mx-auto px-6 md:px-12 h-full flex flex-col lg:flex-row items-center justify-between gap-8 z-10">
|
||||||
@@ -243,7 +328,17 @@ export default function CinematicStoryScroll({
|
|||||||
<div className="w-full lg:w-1/2 flex items-center justify-center relative mt-16 lg:mt-0">
|
<div className="w-full lg:w-1/2 flex items-center justify-center relative mt-16 lg:mt-0">
|
||||||
<div
|
<div
|
||||||
className={`relative w-64 h-64 sm:w-80 sm:h-80 md:w-96 md:h-96 lg:w-[440px] lg:h-[440px] rounded-full transition-all duration-700 flex items-center justify-center ${
|
className={`relative w-64 h-64 sm:w-80 sm:h-80 md:w-96 md:h-96 lg:w-[440px] lg:h-[440px] rounded-full transition-all duration-700 flex items-center justify-center ${
|
||||||
activeScene === 'heat'
|
isLight
|
||||||
|
? activeScene === 'heat'
|
||||||
|
? 'scale-105 border-[14px] border-orange-400 shadow-[0_20px_60px_rgba(255,102,0,0.25)]'
|
||||||
|
: activeScene === 'material'
|
||||||
|
? 'scale-110 border-[14px] border-blue-500 shadow-[0_20px_60px_rgba(20,103,255,0.22)]'
|
||||||
|
: activeScene === 'control'
|
||||||
|
? 'scale-95 -rotate-12 border-[14px] border-cyan-500 shadow-[0_20px_50px_rgba(6,182,212,0.2)]'
|
||||||
|
: activeScene === 'durability'
|
||||||
|
? 'scale-100 border-[14px] border-[#d8b35a] shadow-[0_20px_50px_rgba(216,179,90,0.25)]'
|
||||||
|
: 'scale-100 border-[14px] border-slate-300 shadow-[0_20px_60px_rgba(0,0,0,0.08)]'
|
||||||
|
: activeScene === 'heat'
|
||||||
? 'scale-105 border-[16px] border-orange-500 shadow-[0_0_90px_rgba(255,102,0,0.6)]'
|
? 'scale-105 border-[16px] border-orange-500 shadow-[0_0_90px_rgba(255,102,0,0.6)]'
|
||||||
: activeScene === 'material'
|
: activeScene === 'material'
|
||||||
? 'scale-110 border-[16px] border-blue-500 shadow-[0_0_80px_rgba(20,103,255,0.5)]'
|
? 'scale-110 border-[16px] border-blue-500 shadow-[0_0_80px_rgba(20,103,255,0.5)]'
|
||||||
@@ -254,10 +349,12 @@ export default function CinematicStoryScroll({
|
|||||||
: 'scale-100 border-[16px] border-[#1467ff] shadow-[0_0_70px_rgba(20,103,255,0.4)]'
|
: 'scale-100 border-[16px] border-[#1467ff] shadow-[0_0_70px_rgba(20,103,255,0.4)]'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
background: 'radial-gradient(circle, rgba(12, 19, 32, 0.95) 45%, rgba(6, 10, 18, 0.98) 100%)',
|
background: isLight
|
||||||
|
? 'radial-gradient(circle, #ffffff 50%, #f4f0e6 100%)'
|
||||||
|
: 'radial-gradient(circle, rgba(16, 24, 40, 0.95) 45%, rgba(8, 14, 24, 0.98) 100%)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Product Image */}
|
{/* Product Image Stage */}
|
||||||
{mainShowcaseImage ? (
|
{mainShowcaseImage ? (
|
||||||
<div className="relative w-4/5 h-4/5 p-4 flex items-center justify-center">
|
<div className="relative w-4/5 h-4/5 p-4 flex items-center justify-center">
|
||||||
<Image
|
<Image
|
||||||
@@ -265,20 +362,24 @@ export default function CinematicStoryScroll({
|
|||||||
alt={resolvedProductName}
|
alt={resolvedProductName}
|
||||||
fill
|
fill
|
||||||
sizes="(max-width: 1024px) 320px, 440px"
|
sizes="(max-width: 1024px) 320px, 440px"
|
||||||
className="object-contain drop-shadow-[0_20px_35px_rgba(0,0,0,0.7)] transition-transform duration-700"
|
className="object-contain drop-shadow-[0_20px_35px_rgba(0,0,0,0.4)] transition-transform duration-700"
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center p-6 space-y-2">
|
<div className="text-center p-6 space-y-2">
|
||||||
<span className="text-4xl">🏎️</span>
|
<span className="text-4xl">🏎️</span>
|
||||||
<p className="text-xs uppercase tracking-widest text-slate-400 font-bold">Premium Racing Spec</p>
|
<p className={`text-xs uppercase tracking-widest font-bold ${isLight ? 'text-slate-500' : 'text-slate-400'}`}>
|
||||||
|
Premium Spec
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Pulsing Core Ring */}
|
{/* Pulsing Core Ring */}
|
||||||
<div
|
<div
|
||||||
className="absolute inset-4 rounded-full border border-white/10 pointer-events-none animate-spin-slow"
|
className={`absolute inset-4 rounded-full border pointer-events-none animate-spin-slow ${
|
||||||
|
isLight ? 'border-slate-300/60' : 'border-white/10'
|
||||||
|
}`}
|
||||||
style={{ animationDuration: '40s' }}
|
style={{ animationDuration: '40s' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -298,24 +399,36 @@ export default function CinematicStoryScroll({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{/* Kicker */}
|
{/* Kicker */}
|
||||||
<div className="text-xs md:text-sm font-black tracking-[0.2em] uppercase text-cyan-300">
|
<div
|
||||||
|
className={`text-xs md:text-sm font-black tracking-[0.2em] uppercase ${
|
||||||
|
isLight ? 'text-brand-primary' : 'text-cyan-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{scene.kicker}
|
{scene.kicker}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Headline */}
|
{/* Headline */}
|
||||||
<h2 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-black tracking-tight leading-[0.95] whitespace-pre-line text-white">
|
<h2
|
||||||
|
className={`text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-black tracking-tight leading-[0.95] whitespace-pre-line ${
|
||||||
|
isLight ? 'text-slate-900' : 'text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{scene.title}
|
{scene.title}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{/* Metric Highlight (e.g. 300°C) */}
|
{/* Metric Highlight (e.g. 300°C) */}
|
||||||
{scene.metric && (
|
{scene.metric && (
|
||||||
<div className="text-5xl sm:text-6xl md:text-7xl font-black tracking-tighter text-transparent bg-clip-text bg-gradient-to-r from-orange-400 via-amber-300 to-yellow-200">
|
<div className="text-5xl sm:text-6xl md:text-7xl font-black tracking-tighter text-transparent bg-clip-text bg-gradient-to-r from-orange-500 via-amber-500 to-yellow-500">
|
||||||
{scene.metric}
|
{scene.metric}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<p className="text-sm md:text-base text-slate-300 leading-relaxed max-w-md">
|
<p
|
||||||
|
className={`text-sm md:text-base leading-relaxed max-w-md ${
|
||||||
|
isLight ? 'text-slate-600' : 'text-slate-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{scene.description}
|
{scene.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -325,7 +438,11 @@ export default function CinematicStoryScroll({
|
|||||||
{scene.chips.map((chip, idx) => (
|
{scene.chips.map((chip, idx) => (
|
||||||
<span
|
<span
|
||||||
key={idx}
|
key={idx}
|
||||||
className="px-3.5 py-1.5 rounded-full text-xs font-semibold bg-white/10 border border-white/20 text-slate-200 backdrop-blur-sm"
|
className={`px-3.5 py-1.5 rounded-full text-xs font-semibold backdrop-blur-sm ${
|
||||||
|
isLight
|
||||||
|
? 'bg-slate-900/5 border border-slate-300 text-slate-800'
|
||||||
|
: 'bg-white/10 border border-white/20 text-slate-200'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{chip}
|
{chip}
|
||||||
</span>
|
</span>
|
||||||
@@ -336,14 +453,22 @@ export default function CinematicStoryScroll({
|
|||||||
{/* Fitment Grid for Fitment Scene */}
|
{/* Fitment Grid for Fitment Scene */}
|
||||||
{scene.id === 'fitment' && fitmentModels.length > 0 && (
|
{scene.id === 'fitment' && fitmentModels.length > 0 && (
|
||||||
<div className="pt-2">
|
<div className="pt-2">
|
||||||
<p className="text-xs uppercase font-bold tracking-wider text-slate-400 mb-2">
|
<p
|
||||||
รุ่นรถยอดนิยมที่รองรับ ({fitmentModels.length} รุ่น):
|
className={`text-xs uppercase font-bold tracking-wider mb-2 ${
|
||||||
|
isLight ? 'text-slate-500' : 'text-slate-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
รุ่นรถที่รองรับ ({fitmentModels.length} รุ่น):
|
||||||
</p>
|
</p>
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2 max-h-48 overflow-y-auto custom-scrollbar pr-1">
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2 max-h-48 overflow-y-auto custom-scrollbar pr-1">
|
||||||
{fitmentModels.slice(0, 18).map((model, idx) => (
|
{fitmentModels.slice(0, 18).map((model, idx) => (
|
||||||
<span
|
<span
|
||||||
key={idx}
|
key={idx}
|
||||||
className="px-3 py-2 rounded-xl text-center text-xs font-bold bg-white/10 border border-white/15 text-white truncate"
|
className={`px-3 py-2 rounded-xl text-center text-xs font-bold truncate ${
|
||||||
|
isLight
|
||||||
|
? 'bg-white border border-slate-200 text-slate-900 shadow-sm'
|
||||||
|
: 'bg-white/10 border border-white/15 text-white'
|
||||||
|
}`}
|
||||||
title={model}
|
title={model}
|
||||||
>
|
>
|
||||||
{model}
|
{model}
|
||||||
@@ -370,20 +495,42 @@ export default function CinematicStoryScroll({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* End CTA Section */}
|
{/* End CTA Section */}
|
||||||
<section className="relative z-10 min-h-[50vh] flex flex-col items-center justify-center text-center px-6 py-20 bg-gradient-to-b from-[#0c1320] via-[#09101a] to-[#060a12] border-t border-white/10 space-y-5">
|
<section
|
||||||
<div className="text-xs uppercase font-black tracking-[0.25em] text-cyan-400">
|
className={`relative z-10 min-h-[50vh] flex flex-col items-center justify-center text-center px-6 py-20 border-t space-y-5 ${
|
||||||
|
isLight
|
||||||
|
? 'bg-[#f0ede3] border-slate-200 text-slate-900'
|
||||||
|
: 'bg-gradient-to-b from-[#0b101c] via-[#09101a] to-surface-page border-white/10 text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`text-xs uppercase font-black tracking-[0.25em] ${
|
||||||
|
isLight ? 'text-brand-primary' : 'text-cyan-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
END OF PRODUCT STORY
|
END OF PRODUCT STORY
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-3xl sm:text-5xl md:text-6xl font-black text-white tracking-tight">
|
<h3
|
||||||
|
className={`text-3xl sm:text-5xl md:text-6xl font-black tracking-tight ${
|
||||||
|
isLight ? 'text-slate-900' : 'text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{endCtaTitle || 'SELECT YOUR MODEL.'}
|
{endCtaTitle || 'SELECT YOUR MODEL.'}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm md:text-base text-slate-400 max-w-md mx-auto leading-relaxed">
|
<p
|
||||||
พร้อมยกระดับสมรรถนะการเบรกให้มอเตอร์ไซค์ของคุณ สั่งซื้อได้ทันทีตรงรุ่น
|
className={`text-sm md:text-base max-w-md mx-auto leading-relaxed ${
|
||||||
|
isLight ? 'text-slate-600' : 'text-slate-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
พร้อมยกระดับสมรรถนะให้รถของคุณ สั่งซื้อได้ทันทีตรงรุ่น
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={scrollToPurchase}
|
onClick={scrollToPurchase}
|
||||||
className="mt-4 px-8 py-4 rounded-2xl font-black text-sm uppercase tracking-wider bg-gradient-to-r from-cyan-400 to-blue-500 hover:from-cyan-300 hover:to-blue-400 text-night-950 shadow-xl shadow-cyan-500/25 transition-all transform hover:scale-105 active:scale-95 cursor-pointer"
|
className={`mt-4 px-8 py-4 rounded-2xl font-black text-sm uppercase tracking-wider transition-all transform hover:scale-105 active:scale-95 cursor-pointer shadow-xl ${
|
||||||
|
isLight
|
||||||
|
? 'bg-brand-primary hover:bg-brand-primary-hover text-white shadow-brand-primary/25'
|
||||||
|
: 'bg-gradient-to-r from-cyan-400 to-blue-500 hover:from-cyan-300 hover:to-blue-400 text-slate-950 shadow-cyan-500/25'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{endCtaButtonText || 'Choose Your Bike / สั่งซื้อรุ่นของคุณ'}
|
{endCtaButtonText || 'Choose Your Bike / สั่งซื้อรุ่นของคุณ'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1125,6 +1125,7 @@ export const ProductLayoutSectionPayloadSchema = z.object({
|
|||||||
showBreadcrumbs: z.boolean().optional(),
|
showBreadcrumbs: z.boolean().optional(),
|
||||||
showBrandMeta: z.boolean().optional(),
|
showBrandMeta: z.boolean().optional(),
|
||||||
storyHeroImage: z.string().optional(),
|
storyHeroImage: z.string().optional(),
|
||||||
|
storyTheme: z.enum(['dark', 'light']).optional(),
|
||||||
storyScenes: z.array(StorySceneSchema).optional(),
|
storyScenes: z.array(StorySceneSchema).optional(),
|
||||||
endCtaTitle: z.string().optional(),
|
endCtaTitle: z.string().optional(),
|
||||||
endCtaButtonText: z.string().optional(),
|
endCtaButtonText: z.string().optional(),
|
||||||
|
|||||||
Reference in new issue
Block a user