11"use client" ;
22
3- import { useRef } from "react" ;
3+ import { useRef , useState } from "react" ;
44import {
55 motion ,
66 useReducedMotion ,
77 useScroll ,
88 useTransform ,
9- type MotionValue ,
109 type Variants ,
1110} from "framer-motion" ;
1211import { SectionReveal , tm } from "@/lib/motion/tray-framer" ;
@@ -29,6 +28,12 @@ const stack = [
2928 [ "Realtime queues" , "live / websocket" ] ,
3029] as const ;
3130
31+ const handoffRows = [
32+ [ "A-217" , "Masala dosa" , "Ready" , "OTP 4821" ] ,
33+ [ "B-044" , "Veg thali" , "Prep 04m" , "Paid" ] ,
34+ [ "C-103" , "Filter coffee" , "Called" , "Counter 2" ] ,
35+ ] as const ;
36+
3237const lineReveal : Variants = {
3338 hidden : { y : "112%" , opacity : 0 , filter : "blur(8px)" } ,
3439 show : {
@@ -54,7 +59,7 @@ function KitchenProofSection() {
5459 return (
5560 < SectionReveal
5661 as = "section"
57- className = "tl-kitchen-proof relative overflow-hidden bg-[var(--tray-ink)] px-5 py-[clamp(5rem,9vw,8.5rem )] text-[var(--tray-cream)] sm:px-8 lg:px-10"
62+ className = "tl-kitchen-proof relative overflow-hidden px-5 py-[clamp(3rem,4.8vw,4.4rem )] text-[var(--tray-cream)] sm:px-8 lg:px-10"
5863 amount = { 0.35 }
5964 >
6065 < motion . div
@@ -74,55 +79,140 @@ function KitchenProofSection() {
7479 backgroundSize : "96px 96px" ,
7580 } }
7681 />
77- < div className = "relative mx-auto max-w-4xl" >
78- < motion . p
79- variants = { lineReveal }
80- className = "font-code text-[0.64rem] uppercase tracking-[0.28em] text-[rgba(250,248,243,0.42)]"
81- >
82- From the kitchen
83- </ motion . p >
82+ < div className = "relative mx-auto grid max-w-6xl items-center gap-10 lg:grid-cols-[minmax(0,0.95fr)_minmax(21rem,0.7fr)]" >
83+ < div >
84+ < motion . p
85+ variants = { lineReveal }
86+ className = "font-code text-[0.64rem] uppercase tracking-[0.28em] text-[rgba(250,248,243,0.42)]"
87+ >
88+ From the kitchen
89+ </ motion . p >
8490
85- < motion . blockquote
86- className = "mt-7 font-editorial text-[clamp(3rem,9vw,6.8rem)] font-black italic leading-[0.92] tracking-[-0.035em]"
87- variants = { { hidden : { } , show : { transition : { staggerChildren : 0.09 } } } }
88- >
89- { quoteLines . map ( ( line ) => (
90- < span key = { line } className = "block overflow-hidden pb-[0.09em]" >
91+ < motion . blockquote
92+ className = "mt-5 max-w-[14.5ch] font-editorial text-[clamp(2.25rem,5.2vw,4.25rem)] font-black italic leading-[0.96] tracking-[-0.02em]"
93+ variants = { { hidden : { } , show : { transition : { staggerChildren : 0.09 } } } }
94+ >
95+ { quoteLines . map ( ( line ) => (
96+ < span key = { line } className = "block overflow-hidden pb-[0.09em]" >
97+ < motion . span variants = { lineReveal } className = "block" >
98+ { line }
99+ </ motion . span >
100+ </ span >
101+ ) ) }
102+ < span className = "block overflow-hidden pb-[0.09em]" >
91103 < motion . span variants = { lineReveal } className = "block" >
92- { line }
104+ < span className = "text-[#ff3a22]" > Lunch </ span > ends on time.
93105 </ motion . span >
94106 </ span >
95- ) ) }
96- < span className = "block overflow-hidden pb-[0.09em]" >
97- < motion . span variants = { lineReveal } className = "block" >
98- < span className = "text-[#ff1f12]" > Lunch</ span > ends on time.
99- </ motion . span >
100- </ span >
101- </ motion . blockquote >
107+ </ motion . blockquote >
102108
103- < motion . p
104- variants = { lineReveal }
105- className = "mt-5 font-code text-[0.58rem] uppercase tracking-[0.2em] text-[rgba(250,248,243,0.35)]"
106- >
107- Kitchen supervisor / campus canteen
108- </ motion . p >
109+ < motion . p
110+ variants = { lineReveal }
111+ className = "mt-5 font-code text-[0.58rem] uppercase tracking-[0.2em] text-[rgba(250,248,243,0.35)]"
112+ >
113+ Kitchen supervisor / campus canteen
114+ </ motion . p >
115+ </ div >
116+
117+ < KitchenHandoffBoard reduce = { reduce } />
109118 </ div >
110119 </ SectionReveal >
111120 ) ;
112121}
113122
123+ function KitchenHandoffBoard ( { reduce } : { reduce : boolean | null } ) {
124+ return (
125+ < motion . aside
126+ variants = { {
127+ hidden : { opacity : 0 , x : 28 , filter : "blur(8px)" } ,
128+ show : {
129+ opacity : 1 ,
130+ x : 0 ,
131+ filter : "blur(0px)" ,
132+ transition : { duration : 0.68 , ease : tm . easeSharp , delay : 0.2 } ,
133+ } ,
134+ } }
135+ className = "relative overflow-hidden rounded-[var(--tray-radius-md)] border border-[rgba(251,252,248,0.16)] bg-[rgba(251,252,248,0.055)] p-4 shadow-[0_26px_80px_rgba(0,0,0,0.22)] backdrop-blur"
136+ >
137+ { ! reduce && (
138+ < motion . span
139+ aria-hidden
140+ className = "absolute left-0 top-0 h-px w-full bg-[linear-gradient(90deg,transparent,rgba(255,58,34,0.95),transparent)]"
141+ animate = { { x : [ "-100%" , "100%" ] } }
142+ transition = { { duration : 2.6 , repeat : Infinity , ease : "linear" } }
143+ />
144+ ) }
145+ < div className = "mb-4 flex items-center justify-between gap-4" >
146+ < div >
147+ < p className = "font-code text-[0.58rem] uppercase tracking-[0.2em] text-[rgba(250,248,243,0.42)]" >
148+ Live handoff board
149+ </ p >
150+ < p className = "mt-1 font-ui text-sm font-semibold text-[var(--tray-cream)]" >
151+ Kitchen queue / OTP pickup
152+ </ p >
153+ </ div >
154+ < span className = "rounded-full border border-[rgba(63,230,163,0.28)] bg-[rgba(63,230,163,0.12)] px-2.5 py-1 font-code text-[0.58rem] uppercase tracking-[0.12em] text-[#72f0b9]" >
155+ Live
156+ </ span >
157+ </ div >
158+
159+ < div className = "space-y-2.5" >
160+ { handoffRows . map ( ( [ code , item , state , meta ] , index ) => (
161+ < motion . div
162+ key = { code }
163+ variants = { {
164+ hidden : { opacity : 0 , y : 16 } ,
165+ show : {
166+ opacity : 1 ,
167+ y : 0 ,
168+ transition : { duration : 0.46 , delay : 0.32 + index * 0.08 , ease : tm . ease } ,
169+ } ,
170+ } }
171+ className = "group grid grid-cols-[4.5rem_1fr_auto] items-center gap-3 rounded-[var(--tray-radius-sm)] border border-[rgba(251,252,248,0.1)] bg-[rgba(16,20,25,0.22)] px-3 py-3 transition-all duration-300 hover:-translate-y-0.5 hover:border-[rgba(255,58,34,0.42)] hover:bg-[rgba(251,252,248,0.09)]"
172+ >
173+ < span className = "font-code text-[0.68rem] text-[rgba(250,248,243,0.52)]" > { code } </ span >
174+ < span className = "min-w-0" >
175+ < span className = "block truncate font-ui text-sm font-semibold text-[var(--tray-cream)]" >
176+ { item }
177+ </ span >
178+ < span className = "mt-1 block font-code text-[0.55rem] uppercase tracking-[0.14em] text-[rgba(250,248,243,0.35)]" >
179+ { meta }
180+ </ span >
181+ </ span >
182+ < span className = "rounded-[var(--tray-radius-sm)] bg-[rgba(255,58,34,0.12)] px-2 py-1 font-code text-[0.58rem] uppercase tracking-[0.1em] text-[#ff725f]" >
183+ { state }
184+ </ span >
185+ </ motion . div >
186+ ) ) }
187+ </ div >
188+
189+ < motion . div
190+ variants = { lineReveal }
191+ className = "mt-4 border-t border-[rgba(251,252,248,0.1)] pt-4 font-code text-[0.6rem] uppercase tracking-[0.14em] text-[rgba(250,248,243,0.36)]"
192+ >
193+ Board calls order / student shows code / queue closes clean.
194+ </ motion . div >
195+ </ motion . aside >
196+ ) ;
197+ }
198+
114199function StackProofSection ( ) {
115200 const ref = useRef < HTMLElement | null > ( null ) ;
201+ const [ revealedCards , setRevealedCards ] = useState < number [ ] > ( [ ] ) ;
116202 const { scrollYProgress } = useScroll ( {
117203 target : ref ,
118204 offset : [ "start 70%" , "end 55%" ] ,
119205 } ) ;
120206
207+ function revealCard ( index : number ) {
208+ setRevealedCards ( ( current ) => ( current . includes ( index ) ? current : [ ...current , index ] ) ) ;
209+ }
210+
121211 return (
122212 < section
123213 ref = { ref }
124214 id = "built-with"
125- className = "tl-built-with relative overflow-hidden px-5 py-[clamp(5rem,9vw,8rem )] sm:px-8 lg:px-10"
215+ className = "tl-built-with relative overflow-hidden px-5 py-[clamp(3.75rem,6.5vw,5.75rem )] sm:px-8 lg:px-10"
126216 >
127217 < div className = "mx-auto max-w-4xl" >
128218 < motion . p
@@ -140,7 +230,7 @@ function StackProofSection() {
140230 opacity : useTransform ( scrollYProgress , [ 0.03 , 0.22 ] , [ 0 , 1 ] ) ,
141231 y : useTransform ( scrollYProgress , [ 0.03 , 0.22 ] , [ 28 , 0 ] ) ,
142232 } }
143- className = "mt-5 max-w-3xl font-editorial text-[clamp(3.3rem,8vw,6.2rem )] font-black leading-[0.9 ] tracking-[-0.05em ] text-[var(--tray-ink)]"
233+ className = "mt-5 max-w-3xl font-editorial text-[clamp(2.65rem,5.7vw,4.85rem )] font-black leading-[0.92 ] tracking-[-0.035em ] text-[var(--tray-ink)]"
144234 >
145235 A boring stack,
146236 < br />
@@ -164,7 +254,8 @@ function StackProofSection() {
164254 name = { name }
165255 detail = { detail }
166256 index = { index }
167- progress = { scrollYProgress }
257+ revealed = { revealedCards . includes ( index ) }
258+ onReveal = { ( ) => revealCard ( index ) }
168259 />
169260 ) ) }
170261 </ div >
@@ -177,32 +268,55 @@ function StackCard({
177268 name,
178269 detail,
179270 index,
180- progress,
271+ revealed,
272+ onReveal,
181273} : {
182274 name : string ;
183275 detail : string ;
184276 index : number ;
185- progress : MotionValue < number > ;
277+ revealed : boolean ;
278+ onReveal : ( ) => void ;
186279} ) {
187- const start = 0.06 + index * 0.035 ;
188- const end = start + 0.14 ;
189- const opacity = useTransform ( progress , [ start , end ] , [ 0 , 1 ] ) ;
190- const y = useTransform ( progress , [ start , end ] , [ 34 , 0 ] ) ;
191- const scale = useTransform ( progress , [ start , end ] , [ 0.94 , 1 ] ) ;
192-
193280 return (
194281 < motion . article
195- style = { { opacity, y, scale } }
196- className = "group min-h-[5.3rem] rounded-[var(--tray-radius-md)] border border-[var(--tray-border)] bg-[var(--tray-surface-strong)] px-4 py-4 shadow-[0_18px_42px_rgba(16,20,25,0.06)] transition-colors duration-300 hover:border-[var(--tray-ink)]"
282+ tabIndex = { 0 }
283+ onPointerEnter = { onReveal }
284+ onMouseOver = { onReveal }
285+ onMouseMove = { onReveal }
286+ onMouseEnter = { onReveal }
287+ onFocus = { onReveal }
288+ initial = { { opacity : 0 , y : 18 } }
289+ whileInView = { { opacity : 1 , y : 0 } }
290+ viewport = { { once : true , amount : 0.35 } }
291+ transition = { { delay : index * 0.035 , duration : 0.42 , ease : tm . easeSharp } }
292+ className = { `group relative min-h-[5.45rem] overflow-hidden rounded-[var(--tray-radius-md)] border px-4 py-4 outline-none transition-all duration-300 hover:-translate-y-0.5 hover:border-solid hover:border-[var(--tray-ink)] hover:bg-[var(--tray-surface-strong)] hover:shadow-[0_18px_42px_rgba(16,20,25,0.08)] focus-visible:-translate-y-0.5 focus-visible:border-solid focus-visible:border-[var(--tray-ink)] focus-visible:bg-[var(--tray-surface-strong)] focus-visible:shadow-[0_18px_42px_rgba(16,20,25,0.08)] ${
293+ revealed
294+ ? "border-solid border-[var(--tray-border-strong)] bg-[var(--tray-surface-strong)] shadow-[0_18px_42px_rgba(16,20,25,0.08)]"
295+ : "border-dashed border-[rgba(16,20,25,0.2)] bg-[rgba(251,252,248,0.26)]"
296+ } `}
197297 >
198- < h3 className = "flex flex-wrap gap-x-1.5 font-ui text-[0.92rem] font-black tracking-0 text-[var(--tray-ink)]" >
199- { name . split ( " " ) . map ( ( part ) => (
200- < span key = { part } > { part } </ span >
201- ) ) }
202- </ h3 >
203- < p className = "mt-2 font-code text-[0.54rem] uppercase tracking-[0.18em] text-[var(--tray-muted)]" >
204- { detail }
205- </ p >
298+ < span
299+ aria-hidden
300+ className = { `absolute right-3 top-3 font-code text-[0.58rem] uppercase tracking-[0.16em] text-[var(--tray-muted)] transition-opacity duration-300 group-hover:opacity-0 group-focus-visible:opacity-0 ${
301+ revealed ? "opacity-0" : "opacity-45"
302+ } `}
303+ >
304+ { String ( index + 1 ) . padStart ( 2 , "0" ) }
305+ </ span >
306+ < div
307+ className = { `transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] group-hover:translate-y-0 group-hover:opacity-100 group-hover:blur-0 group-focus-visible:translate-y-0 group-focus-visible:opacity-100 group-focus-visible:blur-0 ${
308+ revealed ? "translate-y-0 opacity-100 blur-0" : "translate-y-3 opacity-0 blur-[3px]"
309+ } `}
310+ >
311+ < h3 className = "flex flex-wrap gap-x-1.5 font-ui text-[0.92rem] font-black tracking-0 text-[var(--tray-ink)]" >
312+ { name . split ( " " ) . map ( ( part ) => (
313+ < span key = { part } > { part } </ span >
314+ ) ) }
315+ </ h3 >
316+ < p className = "mt-2 font-code text-[0.54rem] uppercase tracking-[0.18em] text-[var(--tray-muted)]" >
317+ { detail }
318+ </ p >
319+ </ div >
206320 </ motion . article >
207321 ) ;
208322}
0 commit comments