feat: implement premium pinned 2.5D gopuram chronicles timeline

This commit is contained in:
Alaguraj0361 2026-07-14 15:27:22 +05:30
parent 7b2bbf0a31
commit db9fd93c69

View File

@ -1,234 +1,342 @@
"use client";
import { useEffect, useRef } from "react";
import { useRef } from "react";
import { motion, useScroll, useTransform } from "framer-motion";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import Image from "next/image";
gsap.registerPlugin(ScrollTrigger);
export default function RestaurantStory() {
const sectionRef = useRef<HTMLDivElement>(null);
const parchmentRef = useRef<HTMLDivElement>(null);
const titleRef = useRef<HTMLHeadingElement>(null);
// Framer Motion scroll tracking for simple parallax of background and frames
// High-performance scroll tracking bound to the section's pinning viewport
const { scrollYProgress } = useScroll({
target: sectionRef,
offset: ["start end", "end start"],
offset: ["start start", "end end"],
});
const yBackground = useTransform(scrollYProgress, [0, 1], ["-10%", "10%"]);
const yImage1 = useTransform(scrollYProgress, [0, 1], ["0%", "-15%"]);
const yImage2 = useTransform(scrollYProgress, [0, 1], ["10%", "-5%"]);
// Slide 1 (1928) - Active from progress 0.0 to 0.33
const imgOpacity1 = useTransform(scrollYProgress, [0, 0.25, 0.33, 0.40], [1, 1, 0, 0]);
const imgScale1 = useTransform(scrollYProgress, [0, 0.33], [1.0, 1.08]);
const textOpacity1 = useTransform(scrollYProgress, [0, 0.25, 0.33], [1, 1, 0]);
const textY1 = useTransform(scrollYProgress, [0, 0.25, 0.33], [0, 0, -40]);
const bgYearOpacity1 = useTransform(scrollYProgress, [0, 0.25, 0.33], [0.12, 0.12, 0]);
const bgYearY1 = useTransform(scrollYProgress, [0, 0.33], [0, -80]);
useEffect(() => {
const parchment = parchmentRef.current;
if (!parchment) return;
// Slide 2 (1972) - Active from progress 0.33 to 0.66
const imgOpacity2 = useTransform(scrollYProgress, [0.25, 0.33, 0.58, 0.66], [0, 1, 1, 0]);
const imgScale2 = useTransform(scrollYProgress, [0.25, 0.33, 0.66], [0.95, 1.0, 1.08]);
const textOpacity2 = useTransform(scrollYProgress, [0.25, 0.33, 0.58, 0.66], [0, 1, 1, 0]);
const textY2 = useTransform(scrollYProgress, [0.25, 0.33, 0.58, 0.66], [40, 0, 0, -40]);
const bgYearOpacity2 = useTransform(scrollYProgress, [0.25, 0.33, 0.58, 0.66], [0, 0.12, 0.12, 0]);
const bgYearY2 = useTransform(scrollYProgress, [0.25, 0.66], [80, -80]);
const ctx = gsap.context(() => {
// Vintage paper reveal: expands vertically like a scroll unrolling
gsap.fromTo(
parchment,
{
clipPath: "polygon(0 48%, 100% 48%, 100% 52%, 0 52%)",
scale: 0.95,
},
{
clipPath: "polygon(0 0, 100% 0, 100% 100%, 0 100%)",
scale: 1,
duration: 2,
ease: "power3.inOut",
scrollTrigger: {
trigger: parchment,
start: "top 80%",
end: "top 30%",
scrub: true,
},
}
);
// Text ink reveal: lines fade and spread slightly on scroll
const textLines = parchment.querySelectorAll(".ink-reveal");
textLines.forEach((line) => {
gsap.fromTo(
line,
{ opacity: 0.1, filter: "blur(4px)", y: 15 },
{
opacity: 1,
filter: "blur(0px)",
y: 0,
duration: 1.2,
scrollTrigger: {
trigger: line,
start: "top 85%",
end: "top 60%",
scrub: true,
},
}
);
});
}, parchment);
return () => ctx.revert();
}, []);
// Slide 3 (2026) - Active from progress 0.66 to 1.0
const imgOpacity3 = useTransform(scrollYProgress, [0.58, 0.66, 0.90], [0, 1, 1]);
const imgScale3 = useTransform(scrollYProgress, [0.58, 0.66, 1.0], [0.95, 1.0, 1.08]);
const textOpacity3 = useTransform(scrollYProgress, [0.58, 0.66, 0.90], [0, 1, 1]);
const textY3 = useTransform(scrollYProgress, [0.58, 0.66, 1.0], [40, 0, 0]);
const bgYearOpacity3 = useTransform(scrollYProgress, [0.58, 0.66, 0.90], [0, 0.12, 0.12]);
const bgYearY3 = useTransform(scrollYProgress, [0.58, 1.0], [80, 0]);
const timelineEvents = [
{
year: "1928",
title: "The Palace Foundation",
text: "Our journey began in a grand Chettinad palace. Built with Burmese teak and Italian marble, the mansion served as a sanctuary of hospitality, welcoming royal emissaries and regional chieftains with legendary feasts.",
tag: "Circa 1928",
},
{
year: "1972",
title: "Preserving Ancient Spices",
text: "The royal recipes were transcribed from palm leaves. Master spice-blenders preserved the art of hand-pounding cardamoms, cinnamon, and red sun-dried chilies in stone mortars, ensuring the flavors remained pristine.",
tag: "Circa 1972",
},
{
year: "2026",
title: "The Modern Legacy Sanctuary",
text: "Today, My Dosa Place brings this heritage into the modern era. We unite century-old cooking methodologies with contemporary culinary presentation, inviting you to dine like royalty in Waterloo.",
tag: "Royal Dining",
},
];
return (
<section
ref={sectionRef}
className="relative min-h-screen py-24 px-6 md:px-12 flex flex-col justify-center items-center overflow-hidden bg-[#f6f3ea]"
className="relative h-[300vh] bg-[#f6f3ea] select-none"
>
{/* Traditional Temple Borders */}
<div className="absolute top-0 left-0 w-full h-[6px] bg-gradient-to-r from-gold-dark via-gold-light to-gold-dark opacity-25" />
<div className="container max-w-6xl relative z-10 grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
{/* Sticky Pinned Container */}
<div className="sticky top-0 w-full h-screen flex flex-col lg:flex-row items-center justify-center overflow-hidden px-6 sm:px-12 md:px-24 gap-12 lg:gap-24">
{/* Left Side: Vintage Parchment Scroll */}
<div className="lg:col-span-7 w-full">
{/* Left Side: Gopuram Arched Frame Visual Court */}
<div className="w-full lg:w-1/2 flex items-center justify-center relative h-[45vh] lg:h-[70vh]">
{/* Subtle Outer Temple Halo */}
<div className="absolute w-[360px] h-[360px] sm:w-[480px] sm:h-[480px] rounded-full bg-gold/10 blur-[80px] -z-10 pointer-events-none" />
{/* Golden Corner Trim Background Shield */}
<div className="absolute inset-y-0 w-full max-w-[340px] sm:max-w-[420px] bg-[#fcf9f2]/60 border border-gold/15 rounded-t-[180px] -z-10 p-4 shadow-[inset_0_0_40px_rgba(216,195,150,0.05)]">
<div className="absolute inset-2 border border-gold/10 rounded-t-[170px]" />
</div>
{/* Main Gopuram Image Stack Frame */}
<div
ref={parchmentRef}
className="relative w-full py-16 px-8 md:px-12 rounded-lg shadow-xl border border-gold/30 overflow-hidden"
className="w-[300px] h-[380px] sm:w-[380px] sm:h-[480px] relative overflow-hidden shadow-2xl z-10"
style={{
background: "linear-gradient(135deg, #fcf9f2 0%, #f6eeda 100%)",
boxShadow: "inset 0 0 35px rgba(24, 135, 84, 0.05), 0 20px 40px -10px rgba(28, 45, 36, 0.08)",
clipPath: "url(#gopuram-arch)",
WebkitClipPath: "url(#gopuram-arch)",
}}
>
{/* Distressed Paper Edges and Watermark */}
<div className="absolute inset-0 bg-[radial-gradient(#cda570_1px,transparent_1px)] [background-size:24px_24px] opacity-10 pointer-events-none" />
<div className="absolute inset-4 border border-maroon/10 pointer-events-none" />
<div className="absolute inset-[18px] border-2 border-double border-maroon/20 pointer-events-none" />
{/* Brass Corner Accents */}
<div className="absolute top-4 left-4 w-4 h-4 border-t border-l border-maroon/40" />
<div className="absolute top-4 right-4 w-4 h-4 border-t border-r border-maroon/40" />
<div className="absolute bottom-4 left-4 w-4 h-4 border-b border-l border-maroon/40" />
<div className="absolute bottom-4 right-4 w-4 h-4 border-b border-r border-maroon/40" />
{/* Scroll Header */}
<div className="text-center mb-12 flex flex-col items-center">
<span className="font-sans text-[10px] tracking-[0.4em] uppercase text-maroon-light font-bold mb-2">
O U R C H R O N I C L E S
</span>
<h2
ref={titleRef}
className="font-serif text-3xl md:text-5xl font-bold tracking-wide text-maroon uppercase"
>
OUR DOSA CHRONICLES
</h2>
<div className="w-20 h-[1.5px] bg-maroon/30 mt-3" />
</div>
{/* Timeline List */}
<div className="flex flex-col gap-10 relative pl-4 md:pl-8 border-l border-maroon/15">
{timelineEvents.map((event, idx) => (
<div key={idx} className="relative group ink-reveal">
{/* Timeline Node Ring */}
<div className="absolute -left-[21px] md:-left-[37px] top-1.5 w-[13px] h-[13px] rounded-full bg-[#eae0d0] border-[3px] border-maroon group-hover:scale-125 transition-transform duration-300 shadow-sm" />
{/* Event Meta */}
<div className="flex flex-col md:flex-row md:items-baseline gap-1 md:gap-4 mb-2">
<span className="font-serif text-xl md:text-2xl font-black text-maroon-light tracking-wider">
{event.year}
</span>
<h3 className="font-serif text-base md:text-lg font-bold text-wood uppercase tracking-wider">
{event.title}
</h3>
</div>
{/* Event Text */}
<p className="font-sans text-sm text-stone-800 leading-relaxed max-w-lg">
{event.text}
</p>
</div>
))}
</div>
{/* Bottom Seal Decoration */}
<div className="flex justify-center mt-12">
<div className="w-10 h-10 rounded-full border border-maroon/30 flex items-center justify-center font-serif text-[10px] text-maroon font-bold tracking-wider relative">
DR
<div className="absolute inset-1 border border-dashed border-maroon/20 rounded-full" />
</div>
</div>
</div>
</div>
{/* Right Side: Vintage Framed Pictures with Parallax */}
<div className="lg:col-span-5 flex flex-col gap-8 relative items-center justify-center">
{/* Framed Image 1: Mansion Detail */}
<motion.div
className="w-72 h-96 relative border-8 border-wood bg-darkbg rounded shadow-[0_15px_35px_rgba(0,0,0,0.6)] p-2 z-10"
style={{ y: yImage1 }}
>
{/* Brass Inner Frame Border */}
<div className="absolute inset-1 border border-gold-dark/40" />
<div className="w-full h-full relative overflow-hidden">
<Image
src="/images/my_dosa_place_interior_royal.jpg"
alt="Vintage Teak Columns"
fill
sizes="(max-width: 768px) 100vw, 30vw"
className="object-cover brightness-100 hover:scale-105 transition-transform duration-700"
/>
</div>
{/* Title Tag */}
<div className="absolute -bottom-4 left-1/2 -translate-x-1/2 bg-wood border border-gold/40 px-3 py-1 text-[10px] tracking-widest text-[#188754] uppercase font-serif font-bold">
Circa 1928
</div>
</motion.div>
{/* Framed Image 2: Gopuram Temple Arch styled layout */}
<div className="relative self-end lg:-mr-12 -mt-16 z-20 flex flex-col items-center">
{/* Image 1: 1928 Mansion */}
<motion.div
className="w-56 h-72 relative overflow-hidden shadow-2xl"
style={{
y: yImage2,
clipPath: "url(#gopuram-arch)",
WebkitClipPath: "url(#gopuram-arch)"
}}
style={{ opacity: imgOpacity1, scale: imgScale1 }}
className="absolute inset-0 w-full h-full"
>
<div className="w-full h-full relative">
<Image
src="/images/my_dosa_place_food_royal.png"
alt="Traditional Serving"
fill
sizes="(max-width: 768px) 100vw, 25vw"
className="object-cover brightness-100 hover:scale-105 transition-transform duration-700"
/>
</div>
<Image
src="/images/my_dosa_place_interior_webgl.jpg"
alt="Chettinad Palace Mansion"
fill
sizes="(max-width: 1024px) 100vw, 40vw"
className="object-cover brightness-[0.95]"
priority
/>
</motion.div>
{/* Title Tag (Placed outside the clip-path container so it isn't cropped) */}
<div className="absolute -bottom-4 bg-wood border border-gold/40 px-3 py-1 text-[9px] tracking-widest text-[#188754] uppercase font-serif z-30 font-bold shadow-md">
Royal Dining
</div>
{/* Image 2: 1972 Spice Altar */}
<motion.div
style={{ opacity: imgOpacity2, scale: imgScale2 }}
className="absolute inset-0 w-full h-full"
>
<Image
src="/images/south_indian_welcome.png"
alt="South Indian Traditional Welcome"
fill
sizes="(max-width: 1024px) 100vw, 40vw"
className="object-cover brightness-[0.95]"
/>
</motion.div>
{/* Image 3: 2026 Thali Feast */}
<motion.div
style={{ opacity: imgOpacity3, scale: imgScale3 }}
className="absolute inset-0 w-full h-full"
>
<Image
src="/images/my_dosa_place_food_webgl.jpg"
alt="Royal Feast Banquet"
fill
sizes="(max-width: 1024px) 100vw, 40vw"
className="object-cover brightness-[0.95]"
/>
</motion.div>
{/* Inset Double Border Stroke Overlay */}
<svg
className="absolute inset-0 w-full h-full pointer-events-none z-30"
viewBox="0 0 100 100"
preserveAspectRatio="none"
>
<path
d="M 50,1.5
C 64,1.5 71.5,7 71.5,15
C 71.5,18 73,19 75,20
C 83.5,22 86.5,28 86.5,34
C 86.5,37 88.5,38 90.5,39
C 95.5,41 97.5,47 97.5,54
L 97.5,98.5
L 2.5,98.5
L 2.5,54
C 2.5,47 4.5,41 9.5,39
C 11.5,38 13.5,37 13.5,34
C 13.5,28 16.5,22 25,20
C 27,19 28.5,18 28.5,15
C 28.5,7 36,1.5 50,1.5 Z"
fill="none"
stroke="#d8c396"
strokeWidth="1.2"
strokeLinecap="round"
opacity="0.85"
/>
<path
d="M 50,3.5
C 62.5,3.5 69.5,8.5 69.5,16
C 69.5,18.5 71,19.5 73,20.5
C 81,22.5 84,28 84,33.5
C 84,36 86,37 88,38
C 93,40 94.5,45.5 94.5,52
L 94.5,96
L 5.5,96
L 5.5,52
C 5.5,45.5 7,40 12,38
C 14,37 16,36 16,33.5
C 16,28 19,22.5 27,20.5
C 29,19.5 30.5,18.5 30.5,16
C 30.5,8.5 37.5,3.5 50,3.5 Z"
fill="none"
stroke="#d8c396"
strokeWidth="0.5"
strokeLinecap="round"
opacity="0.45"
/>
</svg>
</div>
{/* Active Era Tag Frame (Bottom Overlay) */}
<div className="absolute bottom-[-16px] z-20 flex flex-col items-center">
{/* Tag 1 */}
<motion.div
style={{ opacity: imgOpacity1 }}
className="absolute bg-[#8b2635] border border-gold/45 px-4 py-1 text-[10px] tracking-[0.25em] text-[#fcfaf2] uppercase font-serif font-semibold shadow-md whitespace-nowrap"
>
{timelineEvents[0].tag}
</motion.div>
{/* Tag 2 */}
<motion.div
style={{ opacity: imgOpacity2 }}
className="absolute bg-[#8b2635] border border-gold/45 px-4 py-1 text-[10px] tracking-[0.25em] text-[#fcfaf2] uppercase font-serif font-semibold shadow-md whitespace-nowrap"
>
{timelineEvents[1].tag}
</motion.div>
{/* Tag 3 */}
<motion.div
style={{ opacity: imgOpacity3 }}
className="absolute bg-[#8b2635] border border-gold/45 px-4 py-1 text-[10px] tracking-[0.25em] text-[#fcfaf2] uppercase font-serif font-semibold shadow-md whitespace-nowrap"
>
{timelineEvents[2].tag}
</motion.div>
</div>
{/* Background Ambient Glow */}
<div className="absolute w-80 h-80 rounded-full bg-maroon/20 blur-[100px] -z-10 pointer-events-none" />
</div>
{/* Right Side: Narrative Parallax Slides */}
<div className="w-full lg:w-1/2 flex flex-col justify-center relative h-[45vh] lg:h-[70vh]">
{/* Giant Outline Parallax Background Years */}
<div className="absolute inset-0 flex items-center justify-end pointer-events-none overflow-hidden select-none -z-10">
<motion.span
style={{
opacity: bgYearOpacity1,
y: bgYearY1,
WebkitTextStroke: "1.5px rgba(216, 195, 150, 0.28)",
color: "transparent",
}}
className="absolute text-[24vw] lg:text-[18vw] font-serif font-black tracking-tighter"
>
1928
</motion.span>
<motion.span
style={{
opacity: bgYearOpacity2,
y: bgYearY2,
WebkitTextStroke: "1.5px rgba(216, 195, 150, 0.28)",
color: "transparent",
}}
className="absolute text-[24vw] lg:text-[18vw] font-serif font-black tracking-tighter"
>
1972
</motion.span>
<motion.span
style={{
opacity: bgYearOpacity3,
y: bgYearY3,
WebkitTextStroke: "1.5px rgba(216, 195, 150, 0.28)",
color: "transparent",
}}
className="absolute text-[24vw] lg:text-[18vw] font-serif font-black tracking-tighter"
>
2026
</motion.span>
</div>
{/* Section Header */}
<div className="mb-8 select-none">
<span className="font-sans text-[10px] tracking-[0.45em] uppercase text-[#188754] font-bold block mb-1">
O U R C H R O N I C L E S
</span>
<h2 className="font-serif text-3xl sm:text-4xl lg:text-5xl font-bold tracking-wider text-[#188754] uppercase">
DOSA CHRONICLES
</h2>
<div className="w-24 h-[1px] bg-gradient-to-r from-[#d8c396] to-transparent mt-3" />
</div>
{/* Interactive Slides Container */}
<div className="relative w-full max-w-xl h-[220px] sm:h-[180px] flex items-center">
{/* Slide 1 */}
<motion.div
style={{ opacity: textOpacity1, y: textY1 }}
className="absolute inset-x-0 flex flex-col justify-center pointer-events-none"
>
<div className="flex items-center gap-3 mb-2">
<span className="font-serif text-xl sm:text-2xl font-black text-[#8b2635] tracking-widest">
{timelineEvents[0].year}
</span>
<span className="h-[1px] w-6 bg-gold/45" />
<h3 className="font-serif text-sm sm:text-base font-bold text-stone-800 uppercase tracking-widest">
{timelineEvents[0].title}
</h3>
</div>
<p className="font-sans text-xs sm:text-sm text-stone-700 leading-relaxed text-justify tracking-wide">
{timelineEvents[0].text}
</p>
</motion.div>
{/* Slide 2 */}
<motion.div
style={{ opacity: textOpacity2, y: textY2 }}
className="absolute inset-x-0 flex flex-col justify-center pointer-events-none"
>
<div className="flex items-center gap-3 mb-2">
<span className="font-serif text-xl sm:text-2xl font-black text-[#8b2635] tracking-widest">
{timelineEvents[1].year}
</span>
<span className="h-[1px] w-6 bg-gold/45" />
<h3 className="font-serif text-sm sm:text-base font-bold text-stone-800 uppercase tracking-widest">
{timelineEvents[1].title}
</h3>
</div>
<p className="font-sans text-xs sm:text-sm text-stone-700 leading-relaxed text-justify tracking-wide">
{timelineEvents[1].text}
</p>
</motion.div>
{/* Slide 3 */}
<motion.div
style={{ opacity: textOpacity3, y: textY3 }}
className="absolute inset-x-0 flex flex-col justify-center pointer-events-none"
>
<div className="flex items-center gap-3 mb-2">
<span className="font-serif text-xl sm:text-2xl font-black text-[#8b2635] tracking-widest">
{timelineEvents[2].year}
</span>
<span className="h-[1px] w-6 bg-gold/45" />
<h3 className="font-serif text-sm sm:text-base font-bold text-stone-800 uppercase tracking-widest">
{timelineEvents[2].title}
</h3>
</div>
<p className="font-sans text-xs sm:text-sm text-stone-700 leading-relaxed text-justify tracking-wide">
{timelineEvents[2].text}
</p>
</motion.div>
</div>
{/* Royal Seal Watermark */}
<div className="flex items-center gap-3 mt-8 opacity-75 select-none">
<div className="w-9 h-9 rounded-full border border-[#8b2635]/30 flex items-center justify-center font-serif text-[10px] text-[#8b2635] font-bold tracking-widest relative">
DR
<div className="absolute inset-0.5 border border-dashed border-[#8b2635]/20 rounded-full" />
</div>
<span className="font-serif text-[10px] tracking-[0.25em] text-[#8b2635] uppercase font-bold">
Heritage Sanctuary
</span>
</div>
</div>
</div>
{/* SVG ClipPath Definition for the Gopuram Temple Arch frame */}
<svg width="0" height="0" className="absolute pointer-events-none">
<defs>
@ -249,8 +357,6 @@ export default function RestaurantStory() {
</clipPath>
</defs>
</svg>
</div>
</section>
);
}