about marasi drive in process

This commit is contained in:
2025-06-11 18:28:07 +05:00
parent 61454b880a
commit 52cf826eea
55 changed files with 1099 additions and 573 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
VITE_API_URL=http://192.168.1.170:4002
VITE_API_URL=http://192.168.1.122:4002
# VITE_API_URL=http://194.26.138.94:4002
Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 KiB

Before

Width:  |  Height:  |  Size: 454 KiB

After

Width:  |  Height:  |  Size: 454 KiB

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

+382
View File
@@ -0,0 +1,382 @@
import { useRef, useState } from "react";
import {
dubaiMarinaDescription,
dubaiMarinaFeatures,
dubaiMarinaSlider,
dubaiMarinaDescriptionBadges,
sliderBadgesCategory,
} from "../data/aboutDubaiMarina";
import {
AnimatePresence,
motion,
useInView,
useScroll,
useTransform,
} from "motion/react";
import useWindowSize from "../hooks/useWindowSize";
import TextBox from "./ui/TextBox";
import SliderMobile from "./SliderMobile";
import clsx from "clsx";
import Slider from "./Slider";
import PlusIcon from "./icons/map/PlusIcon";
import EqualIcon from "./icons/EqualIcon";
import FullScreenButton from "./FullScreenButton";
function AboutDubaiMarina() {
const containerRef = useRef(null);
const sliderRef = useRef(null);
const mapRef = useRef(null);
const { width } = useWindowSize();
const [selectedCategorySlider, setSelectedCategorySlider] =
useState<keyof typeof dubaiMarinaSlider>("Wellness");
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start start", "end start"],
});
const firstSectionOpacity = useTransform(scrollYProgress, [0, 0.2], [1, 0]);
const secondSectionY = useTransform(
scrollYProgress,
[0, 0.4],
["100dvh", "0dvh"]
);
const isSliderInView = useInView(sliderRef, {
once: true,
amount: 0.1,
});
const isMapInView = useInView(mapRef, {
once: true,
margin: `0px 0px ${-window.innerHeight / 2}px 0px`,
});
return (
<div className="relative bg-white" ref={containerRef}>
<motion.section
className="2xl:h-[calc(100dvh-4.444vw)] w-full bg-white text-black 2xl:fixed top-[4.444vw] left-0 right-0 z-0 max-md:top-[15.556vw]]
max-md:bg-[url(/images/about-complex/dubai-marina/tower-mobile.png)] max-md:bg-cover max-md:bg-top max-md:bg-no-repeat max-md:aspect-[360/584]
max-2xl:bg-[url(/images/about-complex/dubai-marina/tower-tablet.png)] max-2xl:bg-cover max-2xl:bg-top max-2xl:bg-no-repeat max-2xl:aspect-[768/960]
"
style={{
opacity: width > 1439 ? firstSectionOpacity : 1,
}}
>
<div className="absolute inset-0 2xl:">
<img
src="/images/about-complex/dubai-marina/tower.jpg"
alt="dubai marina about"
className="h-full w-full object-contain object-bottom max-2xl:hidden"
/>
<img
src="/images/about-complex/dubai-marina/logo.png"
alt="dubai marina about"
className="absolute top-[5vw] right-[2.778vw] w-[9.931vw] h-[4.444vw] max-md:top-[6.667vw] max-md:right-[4.444vw] max-md:w-[21.389vw] max-md:h-[9.444vw] max-2xl:w-[18.62vw] max-2xl:h-[8.333vw]"
/>
</div>
<div className="relative h-full flex flex-col justify-between 2xl:gap-[4.444vw] flex-1 w-full pt-[5vw] pb-[2.5vw] pl-[2.222vw] max-md:px-[4.444vw] max-md:block ">
<div className="font-bold whitespace-pre-line space-y-8 max-md:space-y-4">
<h1 className="text-[5vw] leading-none tracking-[-0.07em] font-mixcase-unmixed font-[500] max-md:text-[8.889vw] max-2xl:text-[9.375vw]">
{`Rove Home
Dubai Marina`}
</h1>
<div className="space-y-2 font-[400]">
{dubaiMarinaDescriptionBadges.map((description) => (
<TextBox text={description} key={description} />
))}
</div>
</div>
<div className="space-y-8 max-md:hidden">
<h4 className="text-h4 text-[#00BED7] font-[500] whitespace-pre-line">
{`Own the last slice of
Dubai Marina, ROVE Style`}
</h4>
<p className="text-s text-[#0D1922B2] max-md:whitespace-pre-line min-2xl:whitespace-pre-line max-2xl:w-[34vw]">
{`With an extended playlist of life-enhancing
amenities, Rove Home is a complete ecosystem
that has everything you'll ever need.`}
</p>
</div>
</div>
</motion.section>
<motion.section
className="w-full text-white bg-white flex justify-center max-md:top-0 max-md:overflow-x-clip"
style={{
y: width > 1439 ? secondSectionY : 0,
zIndex: 1,
}}
>
<div className="w-full min-md:overflow-x-hidden">
<div className="flex flex-col items-center gap-[2.222vw] w-full max-md:py-[8.889vw] max-md:px-[4.444vw] max-2xl:p-6">
<div className="space-y-8 min-md:hidden self-start">
<h4 className="text-h4 text-[#00BED7] font-[500] whitespace-pre-line">
{`Own the last slice of Dubai
Marina, ROVE Style`}
</h4>
<p className="text-s text-[#0D1922B2] whitespace-pre-line ">
{`With an extended playlist of life-enhancing
amenities, Rove Home is a complete ecosystem
that has everything you'll ever need.`}
</p>
</div>
<div className="2xl:px-[2.222vw] flex pt-[7.222vw] flex-col items-center gap-[2.222vw] max-md:pt-[17.778vw]">
<h1 className="text-[3.889vw] text-[#0D1922] font-mixcase-unmixed max-md:text-[6.667vw] max-2xl:text-[7.682vw]">
Rove Home has it all
</h1>
<p className="text-s text-[#0D1922B2] text-center tracking-[-0.02em] whitespace-pre-line">
{`Rove Home Dubai Marina features modern-day
conveniences, carefully curated for an active
and social lifestyle.`}
</p>
</div>
<div
className="flex gap-[0.556vw] mt-[0.833vw] 2xl:h-[26.667vw] max-md:pl-[4.444vw] max-md:pr-[2vw] max-md:gap-[2.222vw] max-md:snap-x max-md:snap-mandatory max-md:py-[6.667vw] max-md:overflow-x-auto max-md:self-start max-md:w-screen max-md:-ml-[4.444vw] max-md:flex-nowrap max-md:justify-start
max-2xl:flex-wrap max-2xl:justify-center"
>
{dubaiMarinaFeatures.map((feature) => (
<div
key={feature.name}
className={`rounded-3xl 2xl:flex-1 2xl:h-[26.667vw] w-full flex flex-col items-center py-[1.667vw] justify-end bg-cover bg-center bg-no-repeat relative before:absolute before:inset-0 before:bg-[#0D1922]/10 before:z-1 before:rounded-3xl
max-2xl:w-[30.208vw] max-2xl:h-[41.146vw] max-2xl:flex-wrap
max-md:w-[91.111vw] max-md:h-[79.722vw] max-md:flex-none aspect-[268/384] max-md:aspect-[328/287] max-md:snap-alwaysa max-md:snap-center max-md:py-[4.444vw]
`}
style={{
backgroundImage: `url(${feature.image})`,
}}
>
<h5 className="text-h5 text-white tracking-[-0.02em] z-2">
{feature.name}
</h5>
</div>
))}
</div>
</div>
<div className="flex flex-col items-center pt-[4.444vw] px-8 gap-[4.444vw] max-md:px-[4.444vw] max-2xl:pr-[3.125vw] max-2xl:-mr-[3.125vw] max-2xl:p-6 max-2xl:items-start max-2xl:gap-[4.167vw]">
<h1 className="font-mixcase-unmixed 2xl:text-[3.889vw] text-[#0D1922] tracking-[-0.05em] max-md:text-2xl max-2xl:text-[7.292vw] max-2xl:self-center">
Dubai, <span className="text-[#0D192266]">within reach</span>
</h1>
<SliderMobile />
<div
className="z-1 flex w-full text-[#0D1922]/40 gap-[1.111vw] max-md:hidden
max-2xl:overflow-x-auto max-2xl:self-start max-2xl:snap-x max-2xl:snap-mandatory
max-2xl:[scrollbar-width:none] max-2xl:mt-[4.167vw]"
>
{dubaiMarinaDescription.map((descriptionItem) => (
<div
key={descriptionItem.title}
className="2xl:flex-1 text-center flex flex-col gap-[1.111vw] cursor-pointer hover:text-[#0D1922B2] transition-all duration-200
max-2xl:w-[45.833vw] max-2xl:h-[12.76vw] max-2xl:shrink-0"
onClick={() =>
setSelectedCategorySlider(descriptionItem.title)
}
>
<div className="h-[2px] bg-gray-300 w-full"></div>
<h5
className={clsx(
"text-h5 tracking-[-0.02em] mt-[0.556vw] font-[500]",
descriptionItem.title === selectedCategorySlider &&
"text-[#00BED7]"
)}
>
{descriptionItem.title}
</h5>
<p
className={clsx(
"text-s leading-[125%] tracking-[-0.02em] w-[16.389vw] self-center max-2xl:w-[30.729vw] ",
descriptionItem.title === selectedCategorySlider &&
"text-[#0D1922]"
)}
>
{descriptionItem.description}
</p>
</div>
))}
</div>
<AnimatePresence mode="wait">
<div
ref={sliderRef}
className="flex flex-col gap-8 max-md:hidden"
>
<motion.div
key={`slider-${selectedCategorySlider}`}
initial={{ opacity: 0, y: 40 }}
animate={
isSliderInView
? { opacity: 1, y: 0 }
: { opacity: 0, y: 140 }
}
exit={{ opacity: 0, y: -40 }}
transition={{ duration: 0.6, ease: "easeIn" }}
>
<Slider categoryName={selectedCategorySlider} />
</motion.div>
<motion.div
key={`badges-${selectedCategorySlider}`}
className="flex gap-[0.556vw] w-[63.333vw] flex-wrap justify-center max-2xl:w-[93.75vw]"
>
{sliderBadgesCategory[selectedCategorySlider].map(
(badgeItem, index) => (
<motion.div
key={badgeItem}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.3,
delay: index * 0.1,
ease: "easeOut",
}}
>
<TextBox text={badgeItem} />
</motion.div>
)
)}
</motion.div>
</div>
</AnimatePresence>
</div>
<div className="text-center w-full flex flex-col items-center gap-[2.222vw] bg-white max-md:gap-[6.667vw] max-2xl:gap-[3.125vw]">
<h1 className="font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[8.056vw] max-md:text-[6.667vw] max-md:w-full max-2xl:text-[7.292vw] max-2xl:w-[84.115vw]">
Dubai's first-ever combinable Apartments
</h1>
<span className="max-2xl:mt-[1.042vw]">
<TextBox text="On-demand" />
</span>
<p className="text-s text-[#0D1922B2] leading-[140%] tracking-[-0.02em] whitespace-pre-line">
{`Enjoy the option to combine 2 apartments and create
a larger space and configuration.`}
</p>
<div className="flex gap-4 relative max-md:flex-col max-md:gap-[2.222vw] max-md:w-full max-md:px-[4.444vw] max-2xl:mt-[7.292vw]">
<div
className="w-[19.028vw] text-[#0D1922] h-[27.778vw] p-[1.667vw] rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/studio.png)] bg-[length:7.778vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:25.556vw_58.333vw] max-md:p-[4.444vw] max-2xl:w-[25.521vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
Studio²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[18.500vw] self-center
max-md:top-[71.111vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[24vw]"
>
<span className="w-5 h-5">
<PlusIcon />
</span>
</div>
<div
className="w-[19.028vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/studio.png)] bg-[length:7.778vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:25.556vw_58.333vw] max-md:p-[4.444vw] max-2xl:w-[25.521vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
Studio²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[38.333vw] self-center
max-md:top-[148.611vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[52vw]"
>
<span className="w-5 h-5">
<EqualIcon />
</span>
</div>
<div
className="w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/1_bedroom.png)] bg-[length:15.972vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[111.111vw] max-md:bg-[length:63.889vw_71.389vw] max-md:p-[4.444vw] max-2xl:w-[40.625vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
1 Bedroom²
</h4>
</div>
</div>
<h5 className="2xl:hidden text-h5 text-[#0D1922B2] max-2xl:hidden">
or
</h5>
<div className="flex gap-4 relative max-md:flex-col max-md:gap-[2.222vw] max-md:w-full max-md:px-[4.444vw]">
<div
className="w-[19.028vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/2_studio.png)] bg-[length:13.125vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:38.889vw_52.5vw] max-md:p-[4.444vw] max-2xl:w-[25.521vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
Studio²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[18.500vw] self-center
max-md:top-[71.111vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[24vw]"
>
<span className="w-5 h-5">
<PlusIcon />
</span>
</div>
<div
className="w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/1_bedroom_rotated.png)] bg-[length:17.847vw_13.681vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:56.111vw_43.056vw] max-md:p-[4.444vw] max-2xl:w-[33.073vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
1 Bedroom²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[50.300vw] self-center
max-md:top-[148.611vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[59vw]"
>
<span className="w-5 h-5">
<EqualIcon />
</span>
</div>
<div
className="w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/2_bedroom.png)] bg-[length:19.028vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[111.111vw] max-md:bg-[length:76.267vw_71.389vw] max-md:p-[4.444vw] max-2xl:w-[33.073vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
2 Bedroom²
</h4>
</div>
</div>
</div>
<div className="flex flex-col items-center gap-[2.222vw] bg-white px-[2.222vw] pb-[8.333vw] max-md:gap-[6.667vw] max-md:px-[4.444vw] max-2xl:gap-[4.167vw]">
<h1
className="font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[7.222vw] text-center
max-md:text-[6.667vw] max-md:w-full max-2xl:text-[7.292vw] max-2xl:w-[84.115vw]"
>
Live central. Live centred
</h1>
<p className="text-s text-[#0D1922B2] leading-[140%] tracking-[-0.02em] text-center 2xl:whitespace-pre-line max-2xl:w-[66.146vw]">
{`Located in the heart of Dubai Marina, Rove Home Dubai Marina is where active
living meets modern convenience. Enjoy an energetic lifestyle surrounded by
trendy cafés, shops, and entertainment options all within reach.`}
</p>
<motion.img
ref={mapRef}
src="/images/about-complex/dubai-marina/central_map.png"
alt="central map"
className="rounded-3xl object-cover object-center aspect-[1376/609] max-md:hidden max-2xl:w-[93.75vw] max-2xl:h-[89.583vw] max-2xl:aspect-[720/688] max-2xl:mt-[1.563vw]"
initial={{ width: "47.083vw" }}
animate={
isMapInView ? { width: "95.556vw" } : { width: "47.083vw" }
}
transition={{ duration: 0.6, ease: "easeInOut" }}
/>
<div className="min-md:hidden relative w-[89.167vw] aspect-square overflow-hidden rounded-2xl">
<img
src="/images/about-complex/dubai-marina/central_map.png"
alt="central map"
className="w-full h-full object-cover object-center scale-120"
/>
<div className="absolute bottom-[1.111vw] right-[1.111vw] w-[11.111vw] h-[11.111vw]">
<FullScreenButton
isFullScreen={false}
onClick={() => {}}
onFullScreenChange={() => {}}
/>
</div>
</div>
</div>
</div>
</motion.section>
</div>
);
}
export default AboutDubaiMarina;
+165
View File
@@ -0,0 +1,165 @@
import {
marasiDriveFeatures,
marasiDriveDescriptionBadges,
marasiDriveExpandable,
} from "../data/aboutMarasiDrive";
import MarariDriveNeighboursSlider from "./MarasiDriveNeighboursSlider";
import MarasiDriveInteriorsSlider from "./MarasiDriveInteriorsSlider";
import TextBox from "./ui/TextBox";
import { useEffect, useRef } from "react";
import { useScroll } from "motion/react";
function AboutMarasiDrive() {
const target = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target,
});
useEffect(() => {
scrollYProgress.on("change", (latest) => {
console.log(latest);
});
}, [scrollYProgress]);
return (
<div className="relative">
<section className="sticky top-[4.444vw] w-full bg-white bg-[url(/images/about-complex/marasi-drive/tower.png)] bg-cover h-[calc(100dvh-4.444vw)] bg-no-repeat bg-[position:left_13.264vw_top] pl-[2.222vw] pt-[5vw] pb-[2.222vw] flex flex-col justify-between">
<div className="space-y-[1.667vw]">
<h1 className="text-[5vw] leading-none tracking-[-0.07em] font-mixcase-unmixed font-medium max-md:text-[8.889vw] whitespace-pre-line">
{`Rove Home
Marasi Drive`}
</h1>
<div className="space-y-[0.556vw]">
{marasiDriveDescriptionBadges.map((description) => (
<TextBox text={description} key={description} />
))}
</div>
</div>
<div className="space-y-[2.222vw] max-md:hidden">
<h4 className="text-h4 text-[#00BED7] font-medium whitespace-pre-line">
{`A home for the young
and young in heart`}
</h4>
<p className="text-s text-[#0D1922B2] max-md:whitespace-pre-line min-2xl:whitespace-pre-line">
{`The dynamic essence of Rove comes to life at our
new location in Marasi Drive, Business Bay. Enjoy
an urban living experience beyond the ordinary.`}
</p>
</div>
</section>
<section className="sticky pt-[9.444vw] px-[2.222vw] pb-[5.347vw] flex flex-col gap-[11.667vw] bg-white w-full overflow-clip">
<div className="flex flex-col gap-[4.444vw]">
<div className="flex flex-col gap-[2.222vw] items-center">
<h2 className="font-mixcase-unmixed text-h1 text-center">
What makes a Rove Home?
</h2>
<p className="opacity-70 text-s whitespace-pre-line text-center">
{`Experience the difference with Rove Home where modern amenities, trendy
interiors, and smart features cater to your unique style. Rove Home is your
destination for artful inspiration and cleverly activated spaces.`}
</p>
</div>
<div className="flex gap-[0.556vw]">
{marasiDriveFeatures.map(({ image, name }) => (
<div key={name}>
<img
src={image}
alt={name}
className="object-cover rounded-[1.667vw]"
/>
</div>
))}
</div>
</div>
<div className="flex flex-col gap-[4.444vw]">
<div className="flex flex-col gap-[2.222vw] items-center">
<h2 className="font-mixcase-unmixed text-h1 text-center">
Expandable Living Solutions
</h2>
<p className="text-s opacity-70 whitespace-pre-line text-center">
{`ORI introduces a revolutionary solution to apartment living,
where space is not just a constraint but an opportunity.`}
</p>
<div className="flex gap-[0.556vw]">
{["Simple", "Safe", "Effortless"].map((text) => (
<TextBox key={text} text={text} />
))}
</div>
</div>
<div className="flex gap-[1.111vw]">
{marasiDriveExpandable.map((image) => (
<div key={image}>
<img src={image} alt="Expandable Living Solutions" />
</div>
))}
</div>
</div>
<div className="flex flex-col 2xl:gap-[4.444vw]">
<div className="flex flex-col 2xl:gap-[2.222vw] items-center">
<h2 className="font-mixcase-unmixed text-h1 text-center">
Inspired interiors
</h2>
<p className="text-s opacity-70 whitespace-pre-line text-center">
{`Smart, flexible designs maximize every inch. The ORI Cloud Bed
expands space by 33%, while the Flexibed transforms living areas
into bedrooms. Multipurpose layouts adapt effortlessly—blending
innovation with modern urban living.`}
</p>
</div>
<MarasiDriveInteriorsSlider />
</div>
<div className="flex flex-col 2xl:gap-[4.444vw]">
<div className="flex flex-col items-center 2xl:gap-[2.222vw]">
<h2 className="font-mixcase-unmixed text-h1 text-center whitespace-pre-line">
{`A home for the young
and young in heart`}
</h2>
<p className="text-s opacity-70 whitespace-pre-line text-center">
{`The dynamic essence of Rove comes to life at our new
location in Marasi Drive, Business Bay. Enjoy an urban
living experience beyond the ordinary.`}
</p>
</div>
<img
className="rounded-[1.667vw]"
src="/images/about-complex/marasi-drive/buisness_bay.jpg"
alt=""
/>
</div>
<div className="h-[calc(27.639vw*3)]" ref={target}>
<div className="flex flex-col 2xl:gap-[4.444vw] sticky top-[5vw]">
<div className="flex flex-col items-center 2xl:gap-[2.222vw]">
<h2 className="font-mixcase-unmixed text-h1 text-center whitespace-pre-line">
{`Explore the neighbourhood`}
</h2>
<p className="text-s opacity-70 whitespace-pre-line text-center">
{`With Dubai's trendiest spots right at your doorstep, explore nearby
entertainment and dining experiences in just 15 minutes. Live your best life
at Rove Home Marasi Drive!`}
</p>
</div>
<MarariDriveNeighboursSlider scrollYProgress={scrollYProgress} />
</div>
</div>
<div className="flex flex-col gap-[3.333vw]">
<div className="flex flex-col gap-[2.222vw] items-center">
<h2 className="text-h1 font-mixcase-unmixed">
Rove around the city
</h2>
<p className="text-s opacity-70 whitespace-pre-line text-center">
Located in the heart of Dubai Marina, Rove Home Dubai Marina is
where active living meets modern convenience. Enjoy an energetic
lifestyle surrounded by trendy cafés, shops, and entertainment
options all within reach.
</p>
</div>
<div className="grid grid-cols-6 grid-rows-5 gap-x-[1.111vw] gap-y-[0.556vw]"></div>
</div>
</section>
<section></section>
</div>
);
}
export default AboutMarasiDrive;
+25 -9
View File
@@ -4,8 +4,11 @@ import MoreIcon from "./icons/MoreIcon";
import PrivacyPolicyButton from "./PrivacyPolicyButton";
import FullScreenButton from "./FullScreenButton";
import { useState } from "react";
import { AnimatePresence, motion } from "motion/react";
import CloseIcon from "./icons/CloseIcon";
function ButtonGroup() {
const [expanded, setExpanded] = useState(false);
const [isFullScreen, setIsFullScreen] = useState(false);
async function handleFullScreenClick() {
@@ -27,22 +30,35 @@ function ButtonGroup() {
return (
<>
<div className="absolute 2xl:bottom-[2.222vw] 2xl:right-[2.222vw] max-w-full flex justify-end items-center 2xl:gap-[0.556vw] gap-2 md:max-2xl:bottom-6 md:max-2xl:right-6 max-md:hidden">
<DisclaimerButton />
<PrivacyPolicyButton />
<FullScreenButton
isFullScreen={isFullScreen}
onFullScreenChange={setIsFullScreen}
onClick={handleFullScreenClick}
/>
<div className="absolute 2xl:bottom-[2.222vw] 2xl:right-[2.222vw] md:max-2xl:bottom-6 md:max-2xl:right-6 bottom-8 max-md:pb-8 right-4 max-md:overflow-hidden">
<AnimatePresence>
{(innerWidth >= 768 || expanded) && (
<motion.div
initial={innerWidth < 768 ? { y: "100%", opacity: 0 } : {}}
animate={innerWidth < 768 ? { y: "0%", opacity: 1 } : {}}
exit={innerWidth < 768 ? { y: "100%", opacity: 0 } : {}}
transition={{ bounce: 0 }}
className="max-w-full flex justify-end items-center 2xl:gap-[0.556vw] gap-2 max-md:flex-col"
>
<DisclaimerButton />
<PrivacyPolicyButton />
<FullScreenButton
isFullScreen={isFullScreen}
onFullScreenChange={setIsFullScreen}
onClick={handleFullScreenClick}
/>
</motion.div>
)}
</AnimatePresence>
</div>
<Button
onlyIcon
variant="secondary"
className="absolute md:hidden right-4 bottom-4"
onClick={() => setExpanded((prev) => !prev)}
>
<span className="size-5">
<MoreIcon />
{expanded ? <CloseIcon /> : <MoreIcon />}
</span>
</Button>
</>
+4 -3
View File
@@ -8,14 +8,15 @@ export default function BottomButton() {
return (
<Button
size="small"
onlyIcon={innerWidth < 768}
size={innerWidth >= 768 ? "small" : "medium"}
variant="secondary"
onClick={() => setModal(<DisclaimerModal />)}
>
<span className="2xl:size-[1.111vw] size-4">
<span className="2xl:size-[1.389vw] size-5">
<DisclaimerIcon />
</span>
<span>Disclaimer</span>
<span className="max-md:hidden">Disclaimer</span>
</Button>
);
}
+32 -12
View File
@@ -36,31 +36,51 @@ function Footer() {
<div className="2xl:space-y-[0.833vw] space-y-3 md:max-2xl:col-start-3 max-2xl:col-span-3 md:max-2xl:row-start-3 max-md:row-start-4 max-md:mt-6">
<p className="text-s text-[#0D1922]/40">Follow us for more:</p>
<div className="flex 2xl:gap-[0.278vw] gap-1">
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
<Link
to={"https://www.youtube.com/@IRTHgroup"}
target="_blank"
className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded"
>
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
<YoutubeIcon />
</div>
</div>
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
</Link>
<Link
to={"https://www.instagram.com/irth.group"}
target="_blank"
className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded"
>
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
<InstagramIcon />
</div>
</div>
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
</Link>
<Link
to={"https://www.facebook.com/irth.group"}
target="_blank"
className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded"
>
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
<FacebookIcon />
</div>
</div>
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
</Link>
<Link
to={"https://www.linkedin.com/company/irth-group-uae"}
target="_blank"
className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded"
>
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
<LinkedInIcon />
</div>
</div>
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
</Link>
<Link
to={"https://twitter.com/IRTHGroup"}
target="_blank"
className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded"
>
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
<TwitterIcon />
</div>
</div>
</Link>
</div>
</div>
@@ -78,7 +98,7 @@ function Footer() {
Unit Types
</Link>
<Link
to={"/about-irth"}
to={"/about"}
className="md:text-btn-l text-btn-m flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
>
About IRTH
@@ -92,7 +112,7 @@ function Footer() {
>
Favorites
{!!favoriteUnits.length && (
<div className="absolute top-0 right-0 translate-x-full max-2xl:-translate-y-full rounded-full w-5 flex items-center justify-center aspect-square bg-[#00BED7] text-white text-caption-s font-mono">
<div className="absolute top-0 right-0 translate-x-full max-2xl:-translate-y-full rounded-full w-4 flex items-center justify-center aspect-square bg-[#00BED7] text-white text-caption-s font-mono">
{favoriteUnits.length}
</div>
)}
+7 -2
View File
@@ -30,8 +30,13 @@ function FullScreenButton({
if (isMobileSafari) return null;
return (
<Button onlyIcon size="small" variant="secondary" onClick={handleClick}>
<span className="2xl:size-[1.111vw] size-4">
<Button
onlyIcon
size={innerWidth >= 768 ? "small" : "medium"}
variant="secondary"
onClick={handleClick}
>
<span className="2xl:size-[1.389vw] size-5">
{isFullScreen ? <CloseFullscreenIcon /> : <FullScreenIcon />}
</span>
</Button>
+2 -12
View File
@@ -249,7 +249,7 @@ function NavItem({ href, title }: { href: string; title: string }) {
>
{title}
{title === "Favorites" && !!favoriteUnits.length && (
<div className="absolute 2xl:top-1.5 2xl:right-1.5 top-1.5 right-1.5 rounded-full min-w-5 min-h-5 flex items-center justify-center aspect-square bg-[#00BED7] text-white text-caption-s text-center font-mono">
<div className="absolute top-1 right-1 rounded-full min-w-4 min-h-4 flex items-center justify-center aspect-square bg-[#00BED7] text-white text-caption-s text-center font-mono">
{favoriteUnits.length}
</div>
)}
@@ -257,17 +257,6 @@ function NavItem({ href, title }: { href: string; title: string }) {
);
}
// function ProfileBar() {
// return (
// <Button
// variant="secondary"
// className="2xl:mr-[2.222vw] mr-4 text-[#0D1922]/70 !bg-[#F3F3F2]"
// >
// Login
// </Button>
// );
// }
function BrochuresDropdown() {
const [opened, setOpened] = useState(false);
@@ -293,6 +282,7 @@ function BrochuresDropdown() {
<AnimatePresence>
{opened && (
<motion.div
key={"menu"}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
+48 -50
View File
@@ -13,7 +13,6 @@ import Compass from "./Compass";
import { getWeather } from "../api/weather";
import { isMobile } from "react-device-detect";
import useWindowSize from "../hooks/useWindowSize";
import TouchIcon from "./icons/map/TouchIcon";
import SelectedComplexCard from "./SelectedComplexCard";
import ButtonGroup from "./ButtonGroup";
@@ -181,8 +180,8 @@ function Map({ maxZoom = 1 }: MapProps) {
setZoom(newMinZoom);
const scaledWidth = originalSize.width * zoom;
const scaledHeight = originalSize.height * zoom;
const scaledWidth = originalSize.width * newMinZoom;
const scaledHeight = originalSize.height * newMinZoom;
const maxOffsetX = Math.max(0, scaledWidth - width);
const maxOffsetY = Math.max(0, scaledHeight - height);
@@ -199,11 +198,23 @@ function Map({ maxZoom = 1 }: MapProps) {
});
};
// Обработчик изменения полноэкранного режима
const handleFullscreenChange = () => {
// Увеличенная задержка для полного применения изменений размера экрана на мобильных
setTimeout(() => {
updateContainerSize();
}, 200);
};
updateContainerSize();
window.addEventListener("resize", updateContainerSize);
document.addEventListener("fullscreenchange", handleFullscreenChange);
return () => window.removeEventListener("resize", updateContainerSize);
return () => {
window.removeEventListener("resize", updateContainerSize);
document.removeEventListener("fullscreenchange", handleFullscreenChange);
};
}, [originalSize, zoom]);
useEffect(() => {
@@ -524,7 +535,8 @@ function Map({ maxZoom = 1 }: MapProps) {
lastTimestamp = now;
setCloudOffset((prev) => {
let next = prev + windSpeed * delta;
if (cloudImageWidth > 0 && next > 0) next -= cloudImageWidth;
if (cloudImageWidth > 0 && (next > 0 || prev > 0))
next -= cloudImageWidth;
return next;
});
cloudAnimationRef.current = requestAnimationFrame(animateClouds);
@@ -557,7 +569,7 @@ function Map({ maxZoom = 1 }: MapProps) {
<img
ref={mapRef}
src={`/images/map/map-new-${
containerRef.current.clientWidth < 1440 ? "mobile" : "desktop"
containerRef.current.clientWidth < 1440 ? "mobile-fhd" : "desktop"
}.jpg`}
alt="map"
className={clsx(
@@ -572,7 +584,7 @@ function Map({ maxZoom = 1 }: MapProps) {
)}
<div
className={clsx(
"pointer-events-none absolute max-w-none transition-opacity duration-300 bg-black",
"pointer-events-none absolute max-w-none transition-opacity duration-300 bg-black will-change-[transform,opacity]",
hoveredMarker ? "opacity-20" : "opacity-0"
)}
style={imageStyle}
@@ -581,7 +593,7 @@ function Map({ maxZoom = 1 }: MapProps) {
style={cloudStyle}
ref={cloudsRef}
className={clsx(
`absolute inset-0 pointer-events-none transition-[opacity] will-change-[opacity,scale,transform]`,
`absolute inset-0 pointer-events-none transition-[opacity] will-change-[opacity,scale,transform,opacity]`,
hoveredMarker && "opacity-80"
)}
>
@@ -590,18 +602,20 @@ function Map({ maxZoom = 1 }: MapProps) {
style={{
rotate: `${90 + windDirection}deg`,
transform: `translateX(${cloudOffset}px) translateZ(0px)`,
transformOrigin: "0 0",
// transformOrigin: "0 0",
}}
>
<img
className="flex-1 w-full"
ref={cloudImgRef}
src={`/images/map/clouds-${
windowWidth < 1440 ? "mobile" : "desktop"
windowWidth < 1440 ? "mobile-fhd" : "desktop"
}.png`}
/>
<img
className="flex-1 w-full"
src={`/images/map/clouds-${
windowWidth < 1440 ? "mobile" : "desktop"
windowWidth < 1440 ? "mobile-fhd" : "desktop"
}.png`}
/>
</div>
@@ -621,46 +635,30 @@ function Map({ maxZoom = 1 }: MapProps) {
))}
</div>
</div>
<WeatherWidget temperature={temperature} />
{!!temperature && <WeatherWidget temperature={temperature} />}
<AnimatePresence>
{isShowInstruction &&
(isMobile ? (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute left-1/2 -translate-x-1/2 top-1/2 backdrop-blur-sm -translate-y-1/2 pointer-events-none will-change-[opacity] bg-[#0D1922]/40 2xl:rounded-[0.556vw] rounded-lg 2xl:space-y-[0.833vw] space-y-3 2xl:p-[1.111vw] p-4 text-white"
>
<div className="flex items-center justify-center gap-4">
<span className="2xl:size-[3.333vw] size-12">
<MoveIcon />
</span>
<div className="w-px 2xl:h-[1.111vw] h-4 bg-white" />
<span className="2xl:size-[3.333vw] size-12">
<TouchIcon />
</span>
</div>
<p className="text-s text-center">Tap to move</p>
</motion.div>
) : (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute pointer-events-none left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 backdrop-blur-sm bg-[#0D1922]/40 2xl:rounded-[0.556vw] rounded-lg 2xl:space-y-[0.833vw] space-y-3 2xl:p-[1.111vw] p-4 text-white"
>
<div className="flex items-center justify-center gap-4">
<span className="2xl:size-[3.333vw] size-12">
<SearchIcon />
</span>
<div className="w-px 2xl:h-[1.111vw] h-4 bg-white" />
<span className="2xl:size-[3.333vw] size-12">
<MoveIcon />
</span>
</div>
<p className="text-s">Zoom and Move to select a location</p>
</motion.div>
))}
{isShowInstruction && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute pointer-events-none left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 backdrop-blur-md bg-[#0D1922]/40 2xl:rounded-[1.111vw] rounded-2xl 2xl:space-y-[0.833vw] space-y-3 2xl:px-[1.667vw] 2xl:py-[1.111vw] px-6 py-4 text-white 2xl:max-w-[13.611vw] max-w-[196px] w-full"
>
<div className="flex items-center justify-center 2xl:gap-[1.111vw] gap-4">
<span className="2xl:size-[3.333vw] size-12">
<SearchIcon />
</span>
<span className="2xl:size-[3.333vw] size-12">
<MoveIcon />
</span>
</div>
<p className="text-s text-center">
<span>Zoom and Move to</span>
<br />
<span>select a location</span>
</p>
</motion.div>
)}
</AnimatePresence>
<ButtonGroup />
<Compass />
@@ -0,0 +1,94 @@
import { useState } from "react";
import { marasiDriveInspiredInteriors } from "../data/aboutMarasiDrive";
import clsx from "clsx";
import Button from "./ui/Button";
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
import ChevronRightIcon from "./icons/ChevronRightIcon";
import { AnimatePresence, motion } from "motion/react";
import { useSwipeable } from "react-swipeable";
function MarasiDriveInteriorsSlider() {
const [currentIndex, setCurrentIndex] = useState(0);
const handlers = useSwipeable({
onSwipedLeft: () =>
setCurrentIndex(
Math.min(currentIndex + 1, marasiDriveInspiredInteriors.length - 1)
),
onSwipedRight: () => setCurrentIndex(Math.max(currentIndex - 1, 0)),
preventScrollOnSwipe: true,
touchEventOptions: {
passive: false,
},
trackMouse: true,
});
return (
<div className="relative" {...handlers}>
<motion.div
className="flex items-center gap-[6.667vw] min-h-[38.333vw]"
animate={{
transform: `translateX(calc(5vw - ${currentIndex - 1} * 27.222vw))`,
}}
>
<AnimatePresence>
{marasiDriveInspiredInteriors.map(({ image, name }, index) => (
<motion.div
animate={{
width: index === currentIndex ? "31.111vw" : "20.556vw",
}}
exit={{
width: index !== currentIndex ? "31.111vw" : "20.556vw",
}}
key={name}
className="shrink-0"
>
<motion.img
src={image}
alt={name}
className="object-cover rounded-[1.667vw] pointer-events-none"
animate={{
marginBottom: index !== currentIndex ? "1.667vw" : "2.222vw",
}}
onClick={() => setCurrentIndex(index)}
/>
<p
className={clsx(
"text-h4 text-center font-medium",
index !== currentIndex && "opacity-40"
)}
>
{name}
</p>
</motion.div>
))}
</AnimatePresence>
</motion.div>
<Button
onlyIcon
disabled={currentIndex === 0}
onClick={() => setCurrentIndex(Math.max(currentIndex - 1, 0))}
className="!bg-[#F3F3F2] absolute 2xl:left-[27.778vw] top-1/2 -translate-y-1/2"
>
<span className="2xl:size-[1.389vw] size-5 text-[#0D1922]">
<ChevronLeftIcon />
</span>
</Button>
<Button
onlyIcon
disabled={currentIndex === marasiDriveInspiredInteriors.length - 1}
className="!bg-[#F3F3F2] absolute 2xl:right-[27.778vw] top-1/2 -translate-y-1/2"
onClick={() =>
setCurrentIndex(
Math.min(currentIndex + 1, marasiDriveInspiredInteriors.length - 1)
)
}
>
<span className="2xl:size-[1.389vw] size-5 text-[#0D1922]">
<ChevronRightIcon />
</span>
</Button>
</div>
);
}
export default MarasiDriveInteriorsSlider;
+31
View File
@@ -0,0 +1,31 @@
function MarasiDriveMapCard({
title,
mins,
image,
className,
}: {
title: string;
mins: number;
image: string;
className?: string;
}) {
return (
<div
className={`rounded-[1.667vw] px-[0.833vw] py-[1.111vw] bg-[#F3F3F2] flex flex-col gap-[0.556vw]${
className ? " " + className : ""
}`}
>
<div className="space-y-[0.278vw]">
<p className="text-m">{title}</p>
<p className="text-s text-[#73787C]">{`${mins} mins`}</p>
</div>
<img
src={image}
className="rounded-[0.278vw] size-[3.333vw] object-cover self-end"
alt={title}
/>
</div>
);
}
export default MarasiDriveMapCard;
@@ -0,0 +1,69 @@
import {
AnimatePresence,
motion,
MotionValue,
useTransform,
} from "motion/react";
import { marasiDriveNeighbours } from "../data/aboutMarasiDrive";
function MarariDriveNeighboursSlider({
scrollYProgress,
}: {
scrollYProgress: MotionValue<number>;
}) {
const x = useTransform(
scrollYProgress,
[0, 1 / 3, 2 / 3],
["4.236vw", "-20.868vw", "-45.972vw"]
);
const width1 = useTransform(
scrollYProgress,
[0, 1 / 3, 2 / 3],
["20.139vw", "20.139vw", "20.139vw"]
);
const width2 = useTransform(
scrollYProgress,
[0, 1 / 3, 2 / 3],
["36.944vw", "20.139vw", "20.139vw"]
);
const width3 = useTransform(
scrollYProgress,
[0, 1 / 3, 2 / 3],
["20.139vw", "36.944vw", "20.139vw"]
);
const width4 = useTransform(
scrollYProgress,
[0, 1 / 3, 2 / 3],
["20.139vw", "20.139vw", "36.944vw"]
);
return (
<motion.div
className="flex items-center 2xl:gap-[4.965vw] min-h-[27.639vw]"
style={{ x }}
>
<AnimatePresence>
{marasiDriveNeighbours.map(({ name, image }, index) => (
<motion.div
key={name}
className="flex flex-col shrink-0 items-center gap-[1.667vw]"
style={{ width: [width1, width2, width3, width4][index] }}
>
<img
src={image}
alt={name}
className="2xl:rounded-[1.667vw] aspect-[290/205] object-cover w-full pointer-events-none"
/>
<p className={"text-h4 text-center font-medium"}>{name}</p>
</motion.div>
))}
</AnimatePresence>
</motion.div>
);
}
export default MarariDriveNeighboursSlider;
+13 -12
View File
@@ -42,19 +42,20 @@ function ModalContainer() {
return (
<AnimatePresence>
{modal && (
<motion.div
ref={rootRef}
// initial={{ opacity: 0 }}
// animate={{ opacity: 1 }}
// exit={{ opacity: 0 }}
className="h-full"
>
<div ref={rootRef} key={"modal"} className="h-full">
<div
ref={popoverRef}
className="bg-black/70 fixed inset-0 max-md:top-14a flex flex-col items-center justify-center amax-md:justify-start overflow-y-auto z-2"
className="bg-black/70 fixed inset-0 max-md:top-14 flex flex-col items-center justify-center overflow-y-auto z-10"
>
<div className="max-h-full">
<div ref={divRef} className="md:p-[1.08vw]">
<motion.div
ref={divRef}
className="md:p-[1.08vw]"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3, bounce: 0 }}
>
<div
ref={backdropRef}
className="absolute inset-0 cursor-pointer"
@@ -62,7 +63,7 @@ function ModalContainer() {
/>
<div
ref={containerRef}
className="relative w-full max-md:border-tmax-md:border-[#E2E2DC]"
className="relative w-full max-md:border-t border-[#E2E2DC]"
>
{modal}
<Button
@@ -77,10 +78,10 @@ function ModalContainer() {
</span>
</Button>
</div>
</div>
</motion.div>
</div>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
);
+2 -2
View File
@@ -8,12 +8,12 @@ export default function PrivacyPolicyButton() {
return (
<Button
size="small"
size={innerWidth >= 768 ? "small" : "medium"}
variant="secondary"
onlyIcon
onClick={() => setModal(<PrivacyPolicyModal />)}
>
<span className="2xl:size-[1.111vw] size-4">
<span className="2xl:size-[1.389vw] size-5">
<QuestionIcon />
</span>
</Button>
+2 -2
View File
@@ -234,8 +234,8 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
}}
/>
<Button
size="small"
className="absolute left-1/2 -translate-x-1/2 md:bottom-6 bottom-4 2xl:hidden"
size={innerWidth < 768 ? "medium" : "small"}
className="absolute left-1/2 -translate-x-1/2 md:bottom-6 bottom-4 2xl:hidden w"
onClick={() => navigate("floors")}
>
Select a Floor
+2 -2
View File
@@ -1,7 +1,7 @@
function FilledPlayIcon() {
return (
<svg viewBox="0 0 48 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M48 28 0 55.713V.287z" fill="currentColor" />
<svg viewBox="0 0 24 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24 14 0 27.857V.143z" fill="currentColor" />
</svg>
);
}
+88 -88
View File
@@ -1,158 +1,158 @@
export const roveHomeDescription = [
'Fullyfurnished apartments',
'Vibrant art installations',
'Inspiring design',
export const dubaiMarinaDescriptionBadges = [
"Fullyfurnished apartments",
"Vibrant art installations",
"Inspiring design",
] as const;
export const dubaiMarinaFeatures = [
{
name: 'Life-enhancing Amenties',
image: '/images/about-complex/dubai-marina/amenties.jpg',
name: "Life-enhancing Amenties",
image: "/images/about-complex/dubai-marina/amenties.jpg",
},
{
name: 'Community',
image: '/images/about-complex/dubai-marina/community.jpg',
name: "Community",
image: "/images/about-complex/dubai-marina/community.jpg",
},
{
name: 'Wellness Focus',
image: '/images/about-complex/dubai-marina/wellness.jpg',
name: "Wellness Focus",
image: "/images/about-complex/dubai-marina/wellness.jpg",
},
{
name: 'Rove-Style Design',
image: '/images/about-complex/dubai-marina/design.jpg',
name: "Rove-Style Design",
image: "/images/about-complex/dubai-marina/design.jpg",
},
{
name: 'Fully-Furnished Apartments',
image: '/images/about-complex/dubai-marina/furnished.jpg',
name: "Fully-Furnished Apartments",
image: "/images/about-complex/dubai-marina/furnished.jpg",
},
] as const;
export const dubaiMarinaDescription = [
{
title: 'Wellness',
description: 'Unlock your inner zen in our wellness playground',
title: "Wellness",
description: "Unlock your inner zen in our wellness playground",
},
{
title: 'Fitness',
description: 'Cancel all your membership. Your new home has it all',
title: "Fitness",
description: "Cancel all your membership. Your new home has it all",
},
{
title: 'Community',
description: 'Connect. Engage. Thrive.',
title: "Community",
description: "Connect. Engage. Thrive.",
},
{
title: 'Convenience',
description: 'Your smart living hub',
title: "Convenience",
description: "Your smart living hub",
},
] as const;
export const dubaiMarinaSlider = {
Wellness: [
{
title: 'Indoor Infinity Pool',
title: "Indoor Infinity Pool",
description:
'Breezy and open in winter and closed in summer this one-of-a-kind convertible pool takes relaxation to new heights, 165 meters to be precise!',
image: '/images/about-complex/dubai-marina/pool1.jpg',
"Breezy and open in winter and closed in summer this one-of-a-kind convertible pool takes relaxation to new heights, 165 meters to be precise!",
image: "/images/about-complex/dubai-marina/pool1.jpg",
},
{
title: 'Multi-sensory wellness pods',
title: "Multi-sensory wellness pods",
description:
'Set against a backdrop of dream-worthy views, immerse yourself in diverse wellness experiences including ultrashield oxygen, cold bucket showers and more.',
image: '/images/about-complex/dubai-marina/pool2.jpg',
"Set against a backdrop of dream-worthy views, immerse yourself in diverse wellness experiences including ultrashield oxygen, cold bucket showers and more.",
image: "/images/about-complex/dubai-marina/pool2.jpg",
},
],
Fitness: [
{
title: 'Semi-Olympic Leisure Pool',
title: "Semi-Olympic Leisure Pool",
description:
'Overlooking the city, enjoy a leisurely swim, do your daily 25-meter laps or just unwind at one of the cozy cabanas,',
image: '/images/about-complex/dubai-marina/fitness1.jpg',
"Overlooking the city, enjoy a leisurely swim, do your daily 25-meter laps or just unwind at one of the cozy cabanas,",
image: "/images/about-complex/dubai-marina/fitness1.jpg",
},
{
title: 'Boutique Fitness Studio',
title: "Boutique Fitness Studio",
description:
'Elevate your fitness regime at flagship Crank Ride & Shape studios blending fitness, fun, and thrill vibes.',
image: '/images/about-complex/dubai-marina/fitness2.jpg',
"Elevate your fitness regime at flagship Crank Ride & Shape studios blending fitness, fun, and thrill vibes.",
image: "/images/about-complex/dubai-marina/fitness2.jpg",
},
],
Community: [
{
title: 'Multi-Purpose Hall',
title: "Multi-Purpose Hall",
description:
'Designed for gatherings, gamings, and entertainment, step into a vibrant multi-functional indoor and outdoor space that can be rented for special events.',
image: '/images/about-complex/dubai-marina/community.jpg',
"Designed for gatherings, gamings, and entertainment, step into a vibrant multi-functional indoor and outdoor space that can be rented for special events.",
image: "/images/about-complex/dubai-marina/community.jpg",
},
{
title: 'Lobby Lounge',
title: "Lobby Lounge",
description:
'Enjoy multi-level seating, reading nooks, co-working spaces, Rove Café and more, for an unforgettable welcome, every time you come home.',
image: '/images/about-complex/dubai-marina/community2.jpg',
"Enjoy multi-level seating, reading nooks, co-working spaces, Rove Café and more, for an unforgettable welcome, every time you come home.",
image: "/images/about-complex/dubai-marina/community2.jpg",
},
],
Convenience: [
{
title: 'Rentable Guest Room',
title: "Rentable Guest Room",
description:
'A private space for guests to relax and unwind, complete with a comfortable seating area, a flat-screen TV, and a private bathroom.',
image: '/images/about-complex/dubai-marina/convenience1.jpg',
"A private space for guests to relax and unwind, complete with a comfortable seating area, a flat-screen TV, and a private bathroom.",
image: "/images/about-complex/dubai-marina/convenience1.jpg",
},
{
title: 'Entrance & Concierge',
description: 'An unforgettable welcome, every time you come home.',
image: '/images/about-complex/dubai-marina/design.jpg',
title: "Entrance & Concierge",
description: "An unforgettable welcome, every time you come home.",
image: "/images/about-complex/dubai-marina/design.jpg",
},
],
} as const;
export const sliderBadgesCategory = {
Wellness: [
'Vitality Pool',
'Reflexology Pool',
'Cold Plunge Pool',
'Convertible Indoor Infinity Pool',
'Zen Library Area',
'Finnish Sauna Pod',
'Aroma Steam Pod',
'Salt Steam Pod',
'Ultra Shield Oxygen Pod',
'Experience Shower Pods',
"Vitality Pool",
"Reflexology Pool",
"Cold Plunge Pool",
"Convertible Indoor Infinity Pool",
"Zen Library Area",
"Finnish Sauna Pod",
"Aroma Steam Pod",
"Salt Steam Pod",
"Ultra Shield Oxygen Pod",
"Experience Shower Pods",
],
Fitness: [
'State-of-the-Art Gym',
'Crank Fitness Studios Bike & Shape',
'Counter-current Pool',
'Rowing Workout',
'Aqua Cycling',
'Outdoor Semi-olympic Leisure Pool',
'Outdoor Gym Park',
'Pedal-powered Workstations',
"State-of-the-Art Gym",
"Crank Fitness Studios Bike & Shape",
"Counter-current Pool",
"Rowing Workout",
"Aqua Cycling",
"Outdoor Semi-olympic Leisure Pool",
"Outdoor Gym Park",
"Pedal-powered Workstations",
],
Community: [
'Multipurpose Area',
'Rentable Kitchen',
'Gaming Lounge',
'Arcade Zone and PlayStation',
'Indoor Cinema',
'Suspended Net Lounge',
'Karaoke Studios',
'Outdoor Cinema & Ampitheatre',
'Rove Café and Energise Bar',
'Co-working Spaces',
'Meeting Rooms & Pods',
'Lobby Lounge',
'Marina Views Chill Zone',
'Allotment Gardens',
"Multipurpose Area",
"Rentable Kitchen",
"Gaming Lounge",
"Arcade Zone and PlayStation",
"Indoor Cinema",
"Suspended Net Lounge",
"Karaoke Studios",
"Outdoor Cinema & Ampitheatre",
"Rove Café and Energise Bar",
"Co-working Spaces",
"Meeting Rooms & Pods",
"Lobby Lounge",
"Marina Views Chill Zone",
"Allotment Gardens",
],
Convenience: [
'Smart Home Digital Features',
'Organic Smart Gardens',
'Electric Vehicle Charging Stations',
'Bike/Scooter Rental and Storage',
'Free Wi-fi',
'24x7 Convenience Store',
'Rentable Guest Rooms',
'Changing Rooms',
'Rentable Storage',
'Concierge',
"Smart Home Digital Features",
"Organic Smart Gardens",
"Electric Vehicle Charging Stations",
"Bike/Scooter Rental and Storage",
"Free Wi-fi",
"24x7 Convenience Store",
"Rentable Guest Rooms",
"Changing Rooms",
"Rentable Storage",
"Concierge",
],
};
+120
View File
@@ -0,0 +1,120 @@
export const marasiDriveDescriptionBadges = [
"Expandable Living Solutions",
"Heartbeat Location",
"Inspired Interiors",
] as const;
export const marasiDriveFeatures = [
{
name: "Community",
image: "/images/about-complex/marasi-drive/community.jpg",
},
{
name: "Central Urban Locations",
image: "/images/about-complex/marasi-drive/location.jpg",
},
{
name: "Fully Loaded Amenties",
image: "/images/about-complex/marasi-drive/amenties.jpg",
},
{
name: "Rove-Inspired Design",
image: "/images/about-complex/marasi-drive/design.jpg",
},
{
name: "Add-on Services",
image: "/images/about-complex/marasi-drive/services.jpg",
},
] as const;
export const marasiDriveExpandable = [
"/images/about-complex/marasi-drive/living_solution1.jpg",
"/images/about-complex/marasi-drive/living_solution2.jpg",
"/images/about-complex/marasi-drive/living_solution3.jpg",
] as const;
export const marasiDriveInspiredInteriors = [
{
name: "Studio flex",
image: "/images/about-complex/marasi-drive/studio_flex.jpg",
},
{
name: "Studio²",
image: "/images/about-complex/marasi-drive/studio2.jpg",
},
{
name: "1 Bedroom²",
image: "/images/about-complex/marasi-drive/1bedroom2.jpg",
},
{
name: "2 Bedroom²",
image: "/images/about-complex/marasi-drive/2bedroom2.jpg",
},
] as const;
export const marasiDriveNeighbours = [
{
name: "City Walk",
image: "/images/about-complex/marasi-drive/city_walk.jpg",
},
{
name: "Downtown Dubai",
image: "/images/about-complex/marasi-drive/downtown.jpg",
},
{
name: "Marasi promenade",
image: "/images/about-complex/marasi-drive/promenade.jpg",
},
{
name: "Dubai Mall",
image: "/images/about-complex/marasi-drive/mall.jpg",
},
] as const;
export const marasiDriveMapCards = [
{
title: "Burj Khalifa",
mins: 10,
image: "/images/about-complex/marasi-drive/burj_khalifa.jpg",
},
{
title: "Dubai Fountain",
mins: 15,
image: "/images/about-complex/marasi-drive/fountain.jpg",
},
{
title: "Dubai Mall",
mins: 15,
image: "/images/about-complex/marasi-drive/mall.jpg",
},
{
title: "Dubai Opera",
mins: 15,
image: "/images/about-complex/marasi-drive/opera.jpg",
},
{
title: "Marasi Promenade",
mins: 15,
image: "/images/about-complex/marasi-drive/promenade.jpg",
},
{
title: "Rove Downtown Hotel",
mins: 15,
image: "/images/about-complex/marasi-drive/rovedowntown.jpg",
},
{
title: "Rove City Walk Hotel",
mins: 15,
image: "/images/about-complex/marasi-drive/rovecitywalk.jpg",
},
{
title: "Coca-Cola Arena",
mins: 15,
image: "/images/about-complex/marasi-drive/arena.jpg",
},
{
title: "Dubai International Airport",
mins: 15,
image: "/images/about-complex/marasi-drive/airport.jpg",
},
] as const;
+1 -1
View File
@@ -9,7 +9,7 @@ function DefaultLayout() {
return (
<div
className={clsx(
"min-h-dvh flex flex-col select-none bg-white",
"min-h-dvh flex flex-col select-none",
pathname.endsWith("/about") ? "bg-white" : "bg-[#F3F3F2]"
)}
>
+9 -376
View File
@@ -1,382 +1,15 @@
import {
useScroll,
useTransform,
motion,
useInView,
AnimatePresence,
} from "motion/react";
import { useRef, useState } from "react";
import {
dubaiMarinaFeatures,
dubaiMarinaDescription,
roveHomeDescription,
dubaiMarinaSlider,
sliderBadgesCategory,
} from "../data/aboutDubaiMarina";
import EqualIcon from "../components/icons/EqualIcon";
import TextBox from "../components/ui/TextBox";
import clsx from "clsx";
import Slider from "../components/Slider";
import PlusIcon from "../components/icons/map/PlusIcon";
import useWindowSize from "../hooks/useWindowSize";
import FullScreenButton from "../components/FullScreenButton";
import SliderMobile from "../components/SliderMobile";
import { useParams } from "react-router";
import AboutDubaiMarina from "../components/AboutDubaiMarina";
import AboutMarasiDrive from "../components/AboutMarasiDrive";
function AboutComplexPage() {
const containerRef = useRef(null);
const sliderRef = useRef(null);
const mapRef = useRef(null);
const { width } = useWindowSize();
const { complexName } = useParams();
const [selectedCategorySlider, setSelectedCategorySlider] =
useState<keyof typeof dubaiMarinaSlider>("Wellness");
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start start", "end start"],
});
const firstSectionOpacity = useTransform(scrollYProgress, [0, 0.2], [1, 0]);
const secondSectionY = useTransform(
scrollYProgress,
[0, 0.4],
["100dvh", "0dvh"]
);
const isSliderInView = useInView(sliderRef, {
once: true,
amount: 0.1,
});
const isMapInView = useInView(mapRef, {
once: true,
margin: `0px 0px ${-window.innerHeight / 2}px 0px`,
});
return (
<div className="relative bg-white" ref={containerRef}>
<motion.section
className="2xl:h-[calc(100dvh-4.444vw)] w-full bg-white text-black 2xl:fixed top-[4.444vw] left-0 right-0 z-0 max-md:top-[15.556vw]]
max-md:bg-[url(/images/about-complex/dubai-marina/tower-mobile.png)] max-md:bg-cover max-md:bg-top max-md:bg-no-repeat max-md:aspect-[360/584]
max-2xl:bg-[url(/images/about-complex/dubai-marina/tower-tablet.png)] max-2xl:bg-cover max-2xl:bg-top max-2xl:bg-no-repeat max-2xl:aspect-[768/960]
"
style={{
opacity: width > 1439 ? firstSectionOpacity : 1,
}}
>
<div className="absolute inset-0 2xl:">
<img
src="/images/about-complex/dubai-marina/tower.jpg"
alt="dubai marina about"
className="h-full w-full object-contain object-bottom max-2xl:hidden"
/>
<img
src="/images/about-complex/dubai-marina/logo.png"
alt="dubai marina about"
className="absolute top-[5vw] right-[2.778vw] w-[9.931vw] h-[4.444vw] max-md:top-[6.667vw] max-md:right-[4.444vw] max-md:w-[21.389vw] max-md:h-[9.444vw] max-2xl:w-[18.62vw] max-2xl:h-[8.333vw]"
/>
</div>
<div className="relative h-full flex flex-col justify-between 2xl:gap-[4.444vw] flex-1 w-full pt-[5vw] pb-[2.5vw] pl-[2.222vw] max-md:px-[4.444vw] max-md:block ">
<div className="font-bold whitespace-pre-line space-y-8 max-md:space-y-4">
<h1 className="text-[5vw] leading-none tracking-[-0.07em] font-mixcase-unmixed font-[500] max-md:text-[8.889vw] max-2xl:text-[9.375vw]">
{`Rove Home
Dubai Marina`}
</h1>
<div className="space-y-2 font-[400]">
{roveHomeDescription.map((description) => (
<TextBox text={description} key={description} />
))}
</div>
</div>
<div className="space-y-8 max-md:hidden">
<h4 className="text-h4 text-[#00BED7] font-[500] whitespace-pre-line">
{`Own the last slice of
Dubai Marina, ROVE Style`}
</h4>
<p className="text-s text-[#0D1922B2] max-md:whitespace-pre-line min-2xl:whitespace-pre-line max-2xl:w-[34vw]">
{`With an extended playlist of life-enhancing
amenities, Rove Home is a complete ecosystem
that has everything you'll ever need.`}
</p>
</div>
</div>
</motion.section>
<motion.section
className="w-full text-white bg-white flex justify-center max-md:top-0 max-md:overflow-x-clip"
style={{
y: width > 1439 ? secondSectionY : 0,
zIndex: 1,
}}
>
<div className="w-full min-md:overflow-x-hidden">
<div className="flex flex-col items-center gap-[2.222vw] w-full max-md:py-[8.889vw] max-md:px-[4.444vw] max-2xl:p-6">
<div className="space-y-8 min-md:hidden self-start">
<h4 className="text-h4 text-[#00BED7] font-[500] whitespace-pre-line">
{`Own the last slice of Dubai
Marina, ROVE Style`}
</h4>
<p className="text-s text-[#0D1922B2] whitespace-pre-line ">
{`With an extended playlist of life-enhancing
amenities, Rove Home is a complete ecosystem
that has everything you'll ever need.`}
</p>
</div>
<div className="2xl:px-[2.222vw] flex pt-[7.222vw] flex-col items-center gap-[2.222vw] max-md:pt-[17.778vw]">
<h1 className="text-[3.889vw] text-[#0D1922] font-mixcase-unmixed max-md:text-[6.667vw] max-2xl:text-[7.682vw]">
Rove Home has it all
</h1>
<p className="text-s text-[#0D1922B2] text-center tracking-[-0.02em] whitespace-pre-line">
{`Rove Home Dubai Marina features modern-day
conveniences, carefully curated for an active
and social lifestyle.`}
</p>
</div>
<div
className="flex gap-[0.556vw] mt-[0.833vw] 2xl:h-[26.667vw] max-md:pl-[4.444vw] max-md:pr-[2vw] max-md:gap-[2.222vw] max-md:snap-x max-md:snap-mandatory max-md:py-[6.667vw] max-md:overflow-x-auto max-md:self-start max-md:w-screen max-md:-ml-[4.444vw] max-md:flex-nowrap max-md:justify-start
max-2xl:flex-wrap max-2xl:justify-center"
>
{dubaiMarinaFeatures.map((feature) => (
<div
key={feature.name}
className={`rounded-3xl 2xl:flex-1 2xl:h-[26.667vw] w-full flex flex-col items-center py-[1.667vw] justify-end bg-cover bg-center bg-no-repeat relative before:absolute before:inset-0 before:bg-[#0D1922]/10 before:z-1 before:rounded-3xl
max-2xl:w-[30.208vw] max-2xl:h-[41.146vw] max-2xl:flex-wrap
max-md:w-[91.111vw] max-md:h-[79.722vw] max-md:flex-none aspect-[268/384] max-md:aspect-[328/287] max-md:snap-alwaysa max-md:snap-center max-md:py-[4.444vw]
`}
style={{
backgroundImage: `url(${feature.image})`,
}}
>
<h5 className="text-h5 text-white tracking-[-0.02em] z-2">
{feature.name}
</h5>
</div>
))}
</div>
</div>
<div className="flex flex-col items-center pt-[4.444vw] px-8 gap-[4.444vw] max-md:px-[4.444vw] max-2xl:pr-[3.125vw] max-2xl:-mr-[3.125vw] max-2xl:p-6 max-2xl:items-start max-2xl:gap-[4.167vw]">
<h1 className="font-mixcase-unmixed 2xl:text-[3.889vw] text-[#0D1922] tracking-[-0.05em] max-md:text-2xl max-2xl:text-[7.292vw] max-2xl:self-center">
Dubai, <span className="text-[#0D192266]">within reach</span>
</h1>
<SliderMobile />
<div
className="z-1 flex w-full text-[#0D1922]/40 gap-[1.111vw] max-md:hidden
max-2xl:overflow-x-auto max-2xl:self-start max-2xl:snap-x max-2xl:snap-mandatory
max-2xl:[scrollbar-width:none] max-2xl:mt-[4.167vw]"
>
{dubaiMarinaDescription.map((descriptionItem) => (
<div
key={descriptionItem.title}
className="2xl:flex-1 text-center flex flex-col gap-[1.111vw] cursor-pointer hover:text-[#0D1922B2] transition-all duration-200
max-2xl:w-[45.833vw] max-2xl:h-[12.76vw] max-2xl:shrink-0"
onClick={() =>
setSelectedCategorySlider(descriptionItem.title)
}
>
<div className="h-[2px] bg-gray-300 w-full"></div>
<h5
className={clsx(
"text-h5 tracking-[-0.02em] mt-[0.556vw] font-[500]",
descriptionItem.title === selectedCategorySlider &&
"text-[#00BED7]"
)}
>
{descriptionItem.title}
</h5>
<p
className={clsx(
"text-s leading-[125%] tracking-[-0.02em] w-[16.389vw] self-center max-2xl:w-[30.729vw] ",
descriptionItem.title === selectedCategorySlider &&
"text-[#0D1922]"
)}
>
{descriptionItem.description}
</p>
</div>
))}
</div>
<AnimatePresence mode="wait">
<div
ref={sliderRef}
className="flex flex-col gap-8 max-md:hidden"
>
<motion.div
key={`slider-${selectedCategorySlider}`}
initial={{ opacity: 0, y: 40 }}
animate={
isSliderInView
? { opacity: 1, y: 0 }
: { opacity: 0, y: 140 }
}
exit={{ opacity: 0, y: -40 }}
transition={{ duration: 0.6, ease: "easeIn" }}
>
<Slider categoryName={selectedCategorySlider} />
</motion.div>
<motion.div
key={`badges-${selectedCategorySlider}`}
className="flex gap-[0.556vw] w-[63.333vw] flex-wrap justify-center max-2xl:w-[93.75vw]"
>
{sliderBadgesCategory[selectedCategorySlider].map(
(badgeItem, index) => (
<motion.div
key={badgeItem}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.3,
delay: index * 0.1,
ease: "easeOut",
}}
>
<TextBox text={badgeItem} />
</motion.div>
)
)}
</motion.div>
</div>
</AnimatePresence>
</div>
<div className="text-center w-full flex flex-col items-center gap-[2.222vw] bg-white max-md:gap-[6.667vw] max-2xl:gap-[3.125vw]">
<h1 className="font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[8.056vw] max-md:text-[6.667vw] max-md:w-full max-2xl:text-[7.292vw] max-2xl:w-[84.115vw]">
Dubai's first-ever combinable Apartments
</h1>
<span className="max-2xl:mt-[1.042vw]">
<TextBox text="On-demand" />
</span>
<p className="text-s text-[#0D1922B2] leading-[140%] tracking-[-0.02em] whitespace-pre-line">
{`Enjoy the option to combine 2 apartments and create
a larger space and configuration.`}
</p>
<div className="flex gap-4 relative max-md:flex-col max-md:gap-[2.222vw] max-md:w-full max-md:px-[4.444vw] max-2xl:mt-[7.292vw]">
<div
className="w-[19.028vw] text-[#0D1922] h-[27.778vw] p-[1.667vw] rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/studio.png)] bg-[length:7.778vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:25.556vw_58.333vw] max-md:p-[4.444vw] max-2xl:w-[25.521vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
Studio²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[18.500vw] self-center
max-md:top-[71.111vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[24vw]"
>
<span className="w-5 h-5">
<PlusIcon />
</span>
</div>
<div
className="w-[19.028vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/studio.png)] bg-[length:7.778vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:25.556vw_58.333vw] max-md:p-[4.444vw] max-2xl:w-[25.521vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
Studio²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[38.333vw] self-center
max-md:top-[148.611vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[52vw]"
>
<span className="w-5 h-5">
<EqualIcon />
</span>
</div>
<div
className="w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/1_bedroom.png)] bg-[length:15.972vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[111.111vw] max-md:bg-[length:63.889vw_71.389vw] max-md:p-[4.444vw] max-2xl:w-[40.625vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
1 Bedroom²
</h4>
</div>
</div>
<h5 className="2xl:hidden text-h5 text-[#0D1922B2] max-2xl:hidden">
or
</h5>
<div className="flex gap-4 relative max-md:flex-col max-md:gap-[2.222vw] max-md:w-full max-md:px-[4.444vw]">
<div
className="w-[19.028vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/2_studio.png)] bg-[length:13.125vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:38.889vw_52.5vw] max-md:p-[4.444vw] max-2xl:w-[25.521vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
Studio²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[18.500vw] self-center
max-md:top-[71.111vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[24vw]"
>
<span className="w-5 h-5">
<PlusIcon />
</span>
</div>
<div
className="w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/1_bedroom_rotated.png)] bg-[length:17.847vw_13.681vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[75.556vw] max-md:bg-[length:56.111vw_43.056vw] max-md:p-[4.444vw] max-2xl:w-[33.073vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
1 Bedroom²
</h4>
</div>
<div
className="w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[50.300vw] self-center
max-md:top-[148.611vw] max-md:left-[45vw] max-2xl:top-[14.583vw] max-2xl:left-[59vw]"
>
<span className="w-5 h-5">
<EqualIcon />
</span>
</div>
<div
className="w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/2_bedroom.png)] bg-[length:19.028vw_17.847vw] bg-no-repeat bg-center
max-md:w-full max-md:h-[111.111vw] max-md:bg-[length:76.267vw_71.389vw] max-md:p-[4.444vw] max-2xl:w-[33.073vw] max-2xl:h-[34.375vw]"
>
<h4 className="text-h4 tracking-[-0.02em] font-[500] max-md:text-h5">
2 Bedroom²
</h4>
</div>
</div>
</div>
<div className="flex flex-col items-center gap-[2.222vw] bg-white px-[2.222vw] pb-[8.333vw] max-md:gap-[6.667vw] max-md:px-[4.444vw] max-2xl:gap-[4.167vw]">
<h1
className="font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[7.222vw] text-center
max-md:text-[6.667vw] max-md:w-full max-2xl:text-[7.292vw] max-2xl:w-[84.115vw]"
>
Live central. Live centred
</h1>
<p className="text-s text-[#0D1922B2] leading-[140%] tracking-[-0.02em] text-center 2xl:whitespace-pre-line max-2xl:w-[66.146vw]">
{`Located in the heart of Dubai Marina, Rove Home Dubai Marina is where active
living meets modern convenience. Enjoy an energetic lifestyle surrounded by
trendy cafés, shops, and entertainment options all within reach.`}
</p>
<motion.img
ref={mapRef}
src="/images/about-complex/dubai-marina/central_map.png"
alt="central map"
className="rounded-3xl object-cover object-center aspect-[1376/609] max-md:hidden max-2xl:w-[93.75vw] max-2xl:h-[89.583vw] max-2xl:aspect-[720/688] max-2xl:mt-[1.563vw]"
initial={{ width: "47.083vw" }}
animate={
isMapInView ? { width: "95.556vw" } : { width: "47.083vw" }
}
transition={{ duration: 0.6, ease: "easeInOut" }}
/>
<div className="min-md:hidden relative w-[89.167vw] aspect-square overflow-hidden rounded-2xl">
<img
src="/images/about-complex/dubai-marina/central_map.png"
alt="central map"
className="w-full h-full object-cover object-center scale-120"
/>
<div className="absolute bottom-[1.111vw] right-[1.111vw] w-[11.111vw] h-[11.111vw]">
<FullScreenButton
isFullScreen={false}
onClick={() => {}}
onFullScreenChange={() => {}}
/>
</div>
</div>
</div>
</div>
</motion.section>
</div>
);
if (complexName === "dubai-marina") {
return <AboutDubaiMarina />;
} else if (complexName === "marasi-drive") {
return <AboutMarasiDrive />;
} else return null;
}
export default AboutComplexPage;
+1
View File
@@ -54,6 +54,7 @@ function AboutIRTHPage() {
<video
src="/videos/About/IRTH 1_HIGHRES.mp4"
className="object-cover w-full h-full"
poster="/images/video-posters/about.jpg"
ref={videoRef}
muted
autoPlay
+1 -1
View File
@@ -42,7 +42,7 @@ function UnitPage() {
if (!unit) return null;
return (
<div className="2xl:p-[2.222vw] md:max-2xl:p-[3.125vw] p-4 max-2xl:pb-0 bg-white flex 2xl:gap-[2.222vw] md:max-2xl:gap-8 gap-6 max-2xl:flex-col">
<div className="2xl:p-[2.222vw] md:max-2xl:p-6 p-4 max-2xl:pb-0 bg-white flex 2xl:gap-[2.222vw] md:max-2xl:gap-8 gap-6 max-2xl:flex-col">
<div className="bg-[#F3F3F2] 2xl:aspect-[1028/664] md:max-2xl:aspect-[720/616] aspect-square 2xl:w-[71.389vw] w-full 2xl:rounded-[1.111vw] md:max-2xl:rounded-2xl rounded-xl" />
<div className="flex flex-col justify-between md:max-2xl:gap-6 gap-4 flex-1">
<div className="2xl:space-y-[1.667vw] space-y-6">