11import { motion } from 'framer-motion' ;
22import { useTranslation } from 'react-i18next' ;
33
4- const products = [
4+ const firstParty = [
5+ { id : 'science-node' , nameKey : 'science_node' , subtitleKey : 'science_node_subtitle' , descKey : 'science_node_description' , statusKey : 'status_available' , highlight : true } ,
6+ { id : 'eyes' , nameKey : 'eyes' , subtitleKey : 'eyes_subtitle' , descKey : 'eyes_description' , statusKey : 'status_available' , highlight : false } ,
7+ { id : 'dongle' , nameKey : 'dongle' , subtitleKey : 'dongle_subtitle' , descKey : 'dongle_description' , statusKey : 'status_available' , highlight : false } ,
8+ ] ;
9+
10+ const thirdParty = [
511 { id : 'syringe-pump' , nameKey : 'syringe_pump' , subtitleKey : 'syringe_pump_subtitle' , statusKey : 'status_available' } ,
612 { id : 'liquid-handler' , nameKey : 'liquid_handler' , subtitleKey : 'liquid_handler_subtitle' , statusKey : 'status_available' } ,
713 { id : 'xyz-stage' , nameKey : 'xyz_stage' , subtitleKey : 'xyz_stage_subtitle' , statusKey : 'status_coming_soon' } ,
@@ -31,8 +37,8 @@ export default function ProductShowcase() {
3137 </ p >
3238 </ motion . div >
3339
34- < div className = "mt-20 grid grid-cols-1 gap-4 sm :grid-cols-2" >
35- { products . map ( ( product , i ) => {
40+ < div className = "mt-20 grid grid-cols-1 gap-4 lg :grid-cols-2" >
41+ { firstParty . map ( ( product , i ) => {
3642 const isAvailable = product . statusKey === 'status_available' ;
3743 return (
3844 < motion . div
@@ -41,27 +47,32 @@ export default function ProductShowcase() {
4147 whileInView = { { opacity : 1 , y : 0 } }
4248 viewport = { { once : true } }
4349 transition = { { duration : 0.6 , delay : i * 0.1 } }
44- className = "group relative aspect-[4/3] overflow-hidden rounded-2xl bg-white/[0.02] ring-1 ring-white/[0.06] transition-all hover:ring-white/[0.12] cursor-pointer"
50+ className = { `group relative overflow-hidden rounded-2xl bg-white/[0.02] ring-1 ring-white/[0.06] transition-all hover:ring-white/[0.12] cursor-pointer ${
51+ product . highlight ? 'lg:col-span-2 aspect-[21/9]' : 'aspect-[4/3]'
52+ } `}
4553 >
4654 < div className = "absolute inset-0 flex items-center justify-center" >
4755 < div className = "relative" >
48- < div className = "h-24 w-24 rounded-2xl border border-white/[0.06] rotate-12 transition-transform group-hover:rotate-6" />
49- < div className = " absolute inset-0 h-24 w-24 rounded-2xl border border-white/[0.04] -rotate-6 transition-transform group-hover:rotate-0" />
56+ < div className = { ` rounded-2xl border border-white/[0.06] rotate-12 transition-transform group-hover:rotate-6 ${ product . highlight ? 'h-32 w-32' : 'h-24 w-24' } ` } />
57+ < div className = { ` absolute inset-0 rounded-2xl border border-white/[0.04] -rotate-6 transition-transform group-hover:rotate-0 ${ product . highlight ? 'h-32 w-32' : 'h-24 w-24' } ` } />
5058 </ div >
5159 </ div >
5260
5361 < div className = "absolute inset-x-0 bottom-0 p-6 bg-gradient-to-t from-black/80 via-black/40 to-transparent" >
54- < div className = "flex items-end justify-between" >
55- < div >
56- < h3 className = "text-lg font-semibold text-white" >
62+ < div className = "flex items-end justify-between gap-4 " >
63+ < div className = "flex-1" >
64+ < h3 className = { ` font-semibold text-white ${ product . highlight ? 'text-xl' : 'text-lg' } ` } >
5765 { t ( `landing.products.${ product . nameKey } ` ) }
5866 </ h3 >
5967 < p className = "mt-0.5 text-sm text-white/40 font-mono" >
6068 { t ( `landing.products.${ product . subtitleKey } ` ) }
6169 </ p >
70+ < p className = "mt-2 text-sm text-white/25 leading-relaxed max-w-lg" >
71+ { t ( `landing.products.${ product . descKey } ` ) }
72+ </ p >
6273 </ div >
6374 < span
64- className = { `rounded-full px-3 py-1 text-[11px] font-mono uppercase tracking-wider ${
75+ className = { `shrink-0 rounded-full px-3 py-1 text-[11px] font-mono uppercase tracking-wider ${
6576 isAvailable
6677 ? 'bg-white/[0.08] text-white/60 ring-1 ring-white/[0.1]'
6778 : 'bg-white/[0.03] text-white/25 ring-1 ring-white/[0.05]'
@@ -76,6 +87,54 @@ export default function ProductShowcase() {
7687 } ) }
7788 </ div >
7889
90+ { /* Third-party compatible devices */ }
91+ < motion . div
92+ initial = { { opacity : 0 , y : 30 } }
93+ whileInView = { { opacity : 1 , y : 0 } }
94+ viewport = { { once : true } }
95+ transition = { { duration : 0.7 } }
96+ className = "mt-24"
97+ >
98+ < p className = "font-mono text-xs uppercase tracking-[0.2em] text-white/30" >
99+ { t ( 'landing.products.third_party_label' ) }
100+ </ p >
101+ < h3 className = "mt-4 text-2xl sm:text-3xl font-bold tracking-tight text-white" >
102+ { t ( 'landing.products.third_party_title' ) }
103+ </ h3 >
104+ </ motion . div >
105+
106+ < div className = "mt-12 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4" >
107+ { thirdParty . map ( ( product , i ) => {
108+ const isAvailable = product . statusKey === 'status_available' ;
109+ return (
110+ < motion . div
111+ key = { product . id }
112+ initial = { { opacity : 0 , y : 20 } }
113+ whileInView = { { opacity : 1 , y : 0 } }
114+ viewport = { { once : true } }
115+ transition = { { duration : 0.5 , delay : i * 0.08 } }
116+ className = "group relative overflow-hidden rounded-xl bg-white/[0.02] ring-1 ring-white/[0.06] transition-all hover:ring-white/[0.12] cursor-pointer p-5"
117+ >
118+ < h4 className = "text-sm font-semibold text-white" >
119+ { t ( `landing.products.${ product . nameKey } ` ) }
120+ </ h4 >
121+ < p className = "mt-1 text-xs text-white/35 font-mono" >
122+ { t ( `landing.products.${ product . subtitleKey } ` ) }
123+ </ p >
124+ < span
125+ className = { `mt-4 inline-block rounded-full px-2.5 py-0.5 text-[10px] font-mono uppercase tracking-wider ${
126+ isAvailable
127+ ? 'bg-white/[0.06] text-white/50 ring-1 ring-white/[0.08]'
128+ : 'bg-white/[0.03] text-white/20 ring-1 ring-white/[0.04]'
129+ } `}
130+ >
131+ { t ( `landing.products.${ product . statusKey } ` ) }
132+ </ span >
133+ </ motion . div >
134+ ) ;
135+ } ) }
136+ </ div >
137+
79138 < motion . p
80139 initial = { { opacity : 0 } }
81140 whileInView = { { opacity : 1 } }
0 commit comments