diff --git a/src/app/page.tsx b/src/app/page.tsx index 13876e8..446efa1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import Link from "next/link"; import DreamStages from "@/components/DreamStages"; @@ -105,8 +105,62 @@ export default function Home() { const [walk, setWalk] = useState(0); const [projTab, setProjTab] = useState<"current" | "upcoming">("current"); const [journey, setJourney] = useState(0); + const [revealed, setRevealed] = useState(1); + const [pinned, setPinned] = useState(false); + const [pinH, setPinH] = useState(0); + const journeyRef = useRef(null); const [why, setWhy] = useState(0); + // Each "stop" = one scroll step. Walk every card, revealing its items one by one. + const journeyStops = useMemo(() => { + const stops: { card: number; items: number }[] = []; + JOURNEY.forEach((j, c) => j.items.forEach((_, k) => stops.push({ card: c, items: k + 1 }))); + return stops; + }, []); + + // Pin the Journey section and drive card/item reveal from scroll progress. + useEffect(() => { + const STEP_VH = 0.6; // viewport-heights of scroll per reveal step + const small = window.matchMedia("(max-width: 768px)"); + const reduce = window.matchMedia("(prefers-reduced-motion: reduce)"); + + const layout = () => { + const on = !small.matches && !reduce.matches; + setPinned(on); + setPinH(on ? Math.round(journeyStops.length * STEP_VH * window.innerHeight + window.innerHeight) : 0); + }; + + let raf = 0; + const onScroll = () => { + if (raf) return; + raf = requestAnimationFrame(() => { + raf = 0; + const el = journeyRef.current; + if (!el || small.matches || reduce.matches) return; + const total = el.offsetHeight - window.innerHeight; + const scrolled = Math.min(Math.max(-el.getBoundingClientRect().top, 0), total); + const p = total > 0 ? scrolled / total : 0; + const idx = Math.min(journeyStops.length - 1, Math.max(0, Math.floor(p * journeyStops.length))); + setJourney(journeyStops[idx].card); + setRevealed(journeyStops[idx].items); + }); + }; + + layout(); + onScroll(); + window.addEventListener("scroll", onScroll, { passive: true }); + window.addEventListener("resize", layout); + small.addEventListener("change", layout); + reduce.addEventListener("change", layout); + return () => { + window.removeEventListener("scroll", onScroll); + window.removeEventListener("resize", layout); + small.removeEventListener("change", layout); + reduce.removeEventListener("change", layout); + if (raf) cancelAnimationFrame(raf); + }; + }, [journeyStops]); + return ( <> {/* ============================ HERO ============================ */} @@ -298,39 +352,76 @@ export default function Home() { {/* ======================== YOUR JOURNEY ====================== */}
-
-
-
-

Transparent Process

-

Your Journey

-
-

From intimate luxury villas to sprawling palatial estates, find the perfect canvas for your architectural dreams

-
- -
- {JOURNEY.map((j, i) => ( -
setJourney(i)}> -
- {j.title} -
-
-
- {j.week} - {j.no} -
-

{j.title}

-

{j.sub}

- {i === journey &&

{j.body}

} -
-
- {j.items.map(([label, day]) => ( -
- {label}{day} -
- ))} +
+
+
+
+
+

Transparent Process

+

Your Journey

+

From intimate luxury villas to sprawling palatial estates, find the perfect canvas for your architectural dreams

- ))} + +
+ {JOURNEY.map((j, i) => { + const active = i === journey; + return ( +
setJourney(i)} + > + {/* Left Image */} +
+ {j.title} +
+ + {/* Center Content */} +
+
+
+ {j.week} +

{j.title}

+
+
+ STEP + {j.no} +
+
+ +
+
+

{j.sub}

+ {active && ( +

{j.body}

+ )} +
+
+ {j.items.map(([label, day], k) => { + const show = !pinned || (active && k < revealed); + return ( +
+ {label} + {day} +
+ ); + })} +
+
+
+ + {/* Bottom Right Small Image */} +
+
+ ); + })} +
+
diff --git a/src/app/style.css b/src/app/style.css index a768a4e..b6b0455 100644 --- a/src/app/style.css +++ b/src/app/style.css @@ -536,32 +536,32 @@ /* Top Left */ .corner.tl { - top: 20px; - left: 20px; + top: 30px; + left: 30px; border-top: 1px solid #fff; border-left: 1px solid #fff; } /* Top Right */ .corner.tr { - top: 20px; - right: 20px; + top: 30px; + right: 30px; border-top: 1px solid #fff; border-right: 1px solid #fff; } /* Bottom Left */ .corner.bl { - bottom: 20px; - left: 20px; + bottom: 30px; + left: 30px; border-bottom: 1px solid #fff; border-left: 1px solid #fff; } /* Bottom Right */ .corner.br { - bottom: 20px; - right: 20px; + bottom: 30px; + right: 30px; border-bottom: 1px solid #fff; border-right: 1px solid #fff; } @@ -576,8 +576,17 @@ transition: transform 0.2s, background 0.2s; } .play-btn:hover { transform: translate(-50%, -50%) scale(1.08); background: rgba(255, 255, 255, 0.4); } - .walk-caption { position: absolute; left: 36px; bottom: 30px; color: #fff; font-family: var(--serif); font-size: 30px; } - .walk-meta { position: absolute; right: 36px; bottom: 30px; color: rgba(255, 255, 255, 0.8); font-size: 14px; } + .walk-caption { color: #fff; + font-family: var(--serif); + font-size: 23px; + position: absolute; + bottom: 48px; + left: 66px; } + .walk-meta { color: #fffc; + font-size: 14px; + position: absolute; + bottom: 50px; + right: 56px; } .walk-tabs { box-shadow: var(--shadow-sm); background: #fff; @@ -594,12 +603,22 @@ /* ----------------- dream realized (scroll-driven stages) ---------------- */ /* Tall track: the inner pins to the viewport while you scroll through it, and the active stage (0–3) re-treats the same villa view. */ - .dream { position: relative; height: 440vh; background: #061228; } + .dream { position: relative; height: 440vh; } .dream-sticky { - position: sticky; top: 0; height: 100vh; + position: sticky; top: 0; height: 95vh; overflow: hidden; display: flex; flex-direction: column; } - +.dream-inner:before { + content: ""; + border: 1px solid #fff; + border-image: initial; + width: 92%; + height: 81%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} /* one image that morphs across every stage */ .dream-bg { position: absolute; inset: 0; z-index: 0; @@ -607,7 +626,8 @@ transition: filter 1s ease, transform 1.4s ease; will-change: filter, transform; } - .dream[data-stage="0"] .dream-bg { filter: grayscale(1) brightness(0.74) contrast(1.05); transform: scale(1.12); } + .dream[data-stage="0"] .dream-bg { transform: scale(1.12); + background: linear-gradient(180deg, rgba(0, 0, 0, 0) 3.21%, rgba(0, 0, 0, 0.34) 72.64%, rgba(0, 0, 0, 0.5) 100%); } .dream[data-stage="1"] .dream-bg { filter: brightness(0.5) sepia(1) hue-rotate(168deg) saturate(6) contrast(1.2); transform: scale(1.08); } .dream[data-stage="2"] .dream-bg { filter: sepia(0.5) saturate(1.15) brightness(0.9) contrast(1.05); transform: scale(1.04); } .dream[data-stage="3"] .dream-bg { filter: none; transform: scale(1); } @@ -637,8 +657,31 @@ } /* copy block — vertically centred in the space above the cards */ - .dream-inner { position: relative; z-index: 3; flex: 1; display: flex; align-items: center; } - .dream-inner-in { display: flex; align-items: flex-start; gap: 28px; padding: 0 24px; } + .dream-inner::before { + content: ""; + width: 100px; + height: 2px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + border-width: 1px; + border-style: solid; + border-color: rgb(255, 255, 255); + border-image: initial; +} + .dream-inner { + z-index: 3; + flex: 1; + margin-top: 180px; + display: flex; + position: relative; + } + .dream-inner-in { align-items: flex-start; + gap: 28px; + padding: 0 24px; + display: flex; + min-width: 86%; } .dream-rail { display: flex; flex-direction: column; gap: 14px; padding-top: 8px; } .dream-dot { width: 14px; height: 14px; border-radius: 50%; @@ -660,43 +703,81 @@ .dream-sub { font-size: 16px; color: rgba(255, 255, 255, 0.88); line-height: 1.6; } /* bottom progress strip */ + .stage-cards.active h4 { + color: white; + } + .dream-rail { + display: none; + } .stage-cards { - position: relative; z-index: 3; - display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; - background: rgba(255, 255, 255, 0.12); + z-index: 3; + background: #ffffff1f; + grid-template-columns: repeat(4, 1fr); + gap: 1px; + display: grid; + position: relative; + top: -3px; } .stage-card { - background: rgba(8, 18, 38, 0.72); backdrop-filter: blur(6px); - padding: 24px 26px; color: rgba(255, 255, 255, 0.7); - transition: background 0.5s ease, color 0.5s ease; + color: #000000; + background: #ffffff; + padding: 24px 26px; + transition: background .5s, color .5s; } .stage-num { font-size: 12px; letter-spacing: 0.2em; opacity: 0.7; } - .stage-card h4 { font-family: var(--serif); font-size: 19px; margin: 8px 0; color: #fff; } - .stage-card p { font-size: 12.5px; line-height: 1.5; color: rgba(255, 255, 255, 0.6); } + .stage-card h4 { font-family: var(--serif); font-size: 19px; margin: 8px 0; color: black; } + .stage-card p { font-size: 12.5px; line-height: 1.5; color: black; } .stage-card.past { opacity: 0.9; } .stage-card.active { background: var(--blue); color: #fff; } + .stage-card.active h4 { color: #fff; } .stage-card.active p { color: rgba(255, 255, 255, 0.92); } .stage-card.active .stage-num { opacity: 0.95; } /* ---------------------------- landmark ---------------------------------- */ + .landmark { + background: #F5FAFF; + } + .proj-body { + padding-top: 20px; + } .proj-tabs { display: inline-flex; gap: 8px; background: #eef2f7; padding: 6px; border-radius: 100px; } .proj-tabs button { padding: 12px 24px; border-radius: 100px; font-size: 15px; color: var(--ink-2); font-weight: 500; } .proj-tabs button.active { background: var(--blue-2); color: #fff; } .proj-big { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-bottom: 24px; } .proj-small { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; } - .proj-card { background: #fff; border: 1px solid var(--line); border-radius: 20px; overflow: hidden; } + .proj-card { border: 1px solid var(--line); + background: #fff; + border-radius: 20px; + overflow: hidden; + background: #FFFFFF; +border: 1px solid #EFEFEF; +box-shadow: 5px 20px 65px rgba(0, 0, 0, 0.06); +border-radius: 20px; + padding: 25px; } .proj-card:hover { box-shadow: var(--shadow-sm); } .proj-media { position: relative; } - .proj-card.big .proj-media { height: 320px; } + .proj-card.big .proj-media { height: 320px; + border-radius: 20px; } .proj-card.small .proj-media { height: 220px; } - .proj-media img { width: 100%; height: 100%; object-fit: cover; } + .proj-media img { width: 100%; height: 100%; object-fit: cover; border-radius: 20px; } .proj-badge { position: absolute; top: 16px; right: 16px; background: var(--green-bright); color: #fff; font-size: 13px; font-weight: 600; padding: 7px 16px; border-radius: 100px; } .proj-badge.dark { background: var(--navy); } - .proj-body { padding: 26px; } + .proj-body h3 { font-family: var(--serif); font-size: 26px; margin-bottom: 12px; } .proj-card.small .proj-body h3 { font-size: 22px; } - .proj-body p { font-size: 14px; color: var(--ink-2); margin-bottom: 22px; } - .proj-stats { display: flex; gap: 28px; padding-top: 18px; border-top: 1px solid var(--line); } + .proj-body p { + color: var(--ink-2); + margin-bottom: 22px; + font-size: 16px; + width: 80%; + } + .proj-stats { + border-top: 1px solid var(--line); + gap: 28px; + padding-top: 18px; + display: flex; + justify-content: space-between; + } .proj-stats div { display: flex; flex-direction: column; } .proj-stats strong { font-family: var(--serif); font-weight: 400; font-size: 22px; } .proj-card.small .proj-stats strong { font-size: 20px; } @@ -704,30 +785,109 @@ /* ----------------------------- journey ---------------------------------- */ .journey-list { display: flex; flex-direction: column; gap: 16px; } - .journey-row { + .journey-card { display: grid; - grid-template-columns: 120px 1.4fr 1fr; + grid-template-columns: 300px 1fr; gap: 30px; - align-items: center; - background: #fff; - border: 1px solid var(--line); - border-radius: 18px; + align-items: stretch; padding: 22px; cursor: pointer; + position: relative; transition: box-shadow 0.2s, border-color 0.2s; +background: #FFFFFF; +border: 1px solid #EFEFEF; +box-shadow: 5px 20px 65px rgba(0, 0, 0, 0.06); +border-radius: 20px; } - .journey-row.open { box-shadow: var(--shadow-sm); border-color: #d3dae6; } - .journey-media { width: 110px; height: 130px; border-radius: 14px; overflow: hidden; } - .journey-media img { width: 100%; height: 100%; object-fit: cover; } - .journey-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } - .journey-week { font-size: 13px; color: var(--blue); font-weight: 600; letter-spacing: 0.04em; } - .journey-no { font-size: 22px; color: var(--ink-3); font-weight: 300; } - .journey-main h3 { font-family: var(--serif); font-size: 26px; margin-bottom: 8px; } - .journey-sub { font-family: var(--serif); font-size: 17px; color: var(--ink); line-height: 1.4; } - .journey-body { margin-top: 12px; font-size: 14px; color: var(--ink-2); } - .journey-items { display: flex; flex-direction: column; gap: 10px; } - .journey-item { display: flex; justify-content: space-between; font-size: 14px; color: var(--ink-2); padding-bottom: 8px; border-bottom: 1px dashed var(--line); } - .journey-item .day { color: var(--ink-3); } + .journey-card.active { box-shadow: var(--shadow-sm); border-color: #d3dae6; } + .journey-image { + border-radius: 14px; + width: 100%; + height: 380px; + overflow: hidden;} + .journey-image img { width: 100%; height: 100%; object-fit: cover; } + .journey-content { display: flex; flex-direction: column; } + .journey-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + border-bottom: 1px solid #8080802e; + padding-bottom: 20px; + margin-bottom: 8px; } + .journey-week { color: var(--blue); + letter-spacing: 3px; + font-size: 13px; + font-weight: 600; + text-transform: uppercase; } + .journey-top h3 { font-family: var(--serif); + margin-top: 8px; + font-size: 26px; } + .journey-step { display: flex; flex-direction: column; align-items: flex-end; } + .journey-step span { font-size: 11px; letter-spacing: 2px; text-transform: uppercase; color: var(--ink-3); } + .journey-step strong { font-size: 22px; color: var(--ink-3); font-weight: 300; } + .journey-middle { display: grid; grid-template-columns: 1.4fr 1fr; gap: 30px; flex: 1; } + .journey-sub { font-family: var(--serif); + color: var(--ink); + font-size: 21px; + line-height: 1.4; + width: 87%; + margin-top: 50px; } + .journey-body { color: var(--ink-2); + margin-top: 32px; + font-size: 16px; + width: 80%; + padding-bottom: 20px; } + .journey-right-content { display: flex; flex-direction: column; gap: 10px; } + .journey-item { + color: #000000; + border-bottom: 1px dashed var(--line); + justify-content: space-between; + padding-bottom: 8px; + font-size: 16px; + display: flex; + } + .journey-item .day { color: #070707; } + .journey-thumb { position: absolute; bottom: 22px; right: 22px; } + + /* ---- pinned scroll reveal (Your Journey) ---- */ + .journey-pin.is-pinned { position: relative; } + .journey-pin.is-pinned .journey-sticky { + position: sticky; + top: 0; + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; } + .journey-pin.is-pinned .journey-list { position: relative; display: block; } + .journey-pin.is-pinned .journey-card { + position: absolute; + left: 0; + right: 0; + top: 0; + z-index: 1; + opacity: 0; + visibility: hidden; + transform: translateY(34px) scale(0.985); + transition: opacity 0.6s ease, transform 0.6s ease; + pointer-events: none; } + .journey-pin.is-pinned .journey-card.active { + position: relative; + z-index: 3; + opacity: 1; + visibility: visible; + transform: none; + pointer-events: auto; } + /* next card peeks out below the active one, like a stacked deck */ + .journey-pin.is-pinned .journey-card.next { + z-index: 2; + opacity: 0.6; + visibility: visible; + transform: translateY(70px) scale(0.96); } + .journey-pin.is-pinned .journey-item { + opacity: 0; + transform: translateX(20px); + transition: opacity 0.5s ease, transform 0.5s ease; } + .journey-pin.is-pinned .journey-item.show { opacity: 1; transform: none; } /* ----------------------------- virtual ---------------------------------- */ .virtual-head { text-align: center; max-width: 640px; margin: 0 auto 44px; } @@ -1242,8 +1402,8 @@ .proj-big, .proj-small, .companies-grid, .testi-grid { grid-template-columns: 1fr 1fr; } .why-grid { grid-template-columns: 1fr; } .why-visual { height: 360px; } - .journey-row { grid-template-columns: 100px 1fr; } - .journey-items { grid-column: 1 / -1; } + .journey-card { grid-template-columns: 100px 1fr; } + .journey-middle { grid-template-columns: 1fr; } } @media (max-width: 768px) { .hero { height: 620px; margin: 0 12px; } @@ -1256,8 +1416,8 @@ .testi-card:nth-child(2) { margin-top: 0; } .dream-title { font-size: 40px; } .dream-inner { padding-bottom: 60px; } - .journey-row { grid-template-columns: 1fr; } - .journey-media { width: 100%; height: 180px; } + .journey-card { grid-template-columns: 1fr; } + .journey-image { width: 100%; height: 180px; } .head-aside { align-items: flex-start; } }