Getting started with your project
01
We begin by understanding your vision and investment goals while outlining a clear, transparent budget for the development journey ahead.
Understanding the market
02
We provide insight into demand, ROI and positioning to ensure every decision is grounded in strong market understanding.
Finding the
Right Site
03
Through trusted networks and local expertise, we present carefully vetted land opportunities aligned with your investment strategy.
Securing the land
04
We negotiate lease terms and oversee legal due diligence to ensure clarity and security before development begins.
Designing your vision
05
Our in-house team translates your vision into a refined architectural concept designed for both beauty and longevity.
Building with care
06
Construction is managed end-to-end with transparent costs, clear timelines and meticulous attention to quality.
Delivering the project
07
From groundbreaking to completion, we oversee every stage to ensure precision, accountability and smooth delivery.
Market & manage
08
Once complete, we position the property for strong occupancy and oversee ongoing management to protect long-term value.
document.addEventListener('DOMContentLoaded', function () {
var section = document.querySelector('[data-portfolio="section"]');
if (!section) return;
gsap.registerPlugin(ScrollTrigger, SplitText, ScrollToPlugin);
// ── CONFIG ────────────────────────────────────────────────────────────────
var ITEM_COUNT = 8;
var SCROLL_PER = 800;
var MOBILE_SCROLL_PER = 500;
// ── COLLECT ELEMENTS ──────────────────────────────────────────────────────
var navItems = gsap.utils.toArray('[data-portfolio-nav-item]');
var progressFill = document.querySelector('[data-portfolio-progress="fill"]');
var portfolioItems = gsap.utils.toArray('[data-portfolio-item]');
var items = [];
for (var i = 0; i < ITEM_COUNT; i++) {
var bg = document.querySelector('[data-portfolio-bg="' + i + '"]');
var intro = document.querySelector('[data-portfolio-intro="' + i + '"]');
var cont = document.querySelector('[data-portfolio-content="' + i + '"]');
var split = null;
if (intro) {
split = new SplitText(intro, { type: 'lines,words', linesClass: 'portfolio-line', wordsClass: 'portfolio-word' });
split.lines.forEach(function(l) { l.style.display = 'block'; l.style.overflow = 'hidden'; });
split.words.forEach(function(w) { w.style.display = 'inline-block'; });
}
items.push({ bg: bg, intro: intro, content: cont, split: split, nav: navItems[i] || null, el: portfolioItems[i] || null, tlShow: null, tlHide: null });
}
gsap.utils.toArray('[data-portfolio-item]').slice(ITEM_COUNT).forEach(function(el) {
gsap.set(el, { display: 'none' });
});
// ── INITIAL STATE ─────────────────────────────────────────────────────────
for (var j = 0; j < ITEM_COUNT; j++) {
if (items[j].bg) gsap.set(items[j].bg, { opacity: 0, visibility: 'hidden', zIndex: j });
if (items[j].intro) gsap.set(items[j].intro, { autoAlpha: 0 });
if (items[j].content) gsap.set(items[j].content, { autoAlpha: 0 });
if (items[j].split) gsap.set(items[j].split.words, { opacity: 0, y: '101%' });
if (items[j].el) items[j].el.style.pointerEvents = 'none';
}
gsap.set(navItems, { opacity: 0, x: '100%' });
if (progressFill) gsap.set(progressFill, { scaleY: 0, opacity: 0, transformOrigin: 'top center' });
// ── STATE ─────────────────────────────────────────────────────────────────
var zIdx = ITEM_COUNT;
var current = -1;
// ── SHOW FIRST SLIDE IMMEDIATELY (no black flash) ─────────────────────
(function() {
var first = items[0];
if (first.bg) gsap.set(first.bg, { opacity: 1, visibility: 'visible' });
if (first.intro) gsap.set(first.intro, { autoAlpha: 1 });
if (first.split) gsap.set(first.split.words, { opacity: 1, y: 0 });
if (first.content) gsap.set(first.content, { autoAlpha: 1 });
if (first.el) first.el.style.pointerEvents = 'auto';
current = 0;
})();
// ── BUILD SHOW TIMELINE ───────────────────────────────────────────────
function buildShowTL(idx) {
var item = items[idx];
var tl = gsap.timeline({ paused: true });
if (item.bg) {
tl.fromTo(item.bg, { opacity: 0 }, { opacity: 1, duration: 0.4, ease: 'none' }, 0);
var img = item.bg.querySelector('img');
if (img) tl.fromTo(img, { scale: 1.1 }, { scale: 1, duration: 1.6, ease: 'power4.out' }, 0);
}
if (item.intro) tl.set(item.intro, { autoAlpha: 1 }, 0);
if (item.split && item.split.lines) {
item.split.lines.forEach(function(line, li) {
var words = line.querySelectorAll('.portfolio-word');
tl.fromTo(words, { opacity: 0, y: '101%' }, { opacity: 1, y: 0, duration: 1, ease: 'power4.out' }, 0.1 + li * 0.08);
});
}
if (item.content) tl.fromTo(item.content, { autoAlpha: 0 }, { autoAlpha: 1, duration: 0.4, ease: 'none' }, 0.5);
return tl;
}
// ── BUILD HIDE TIMELINE ───────────────────────────────────────────────
function buildHideTL(idx) {
var item = items[idx];
var tl = gsap.timeline({ paused: true });
if (item.bg) tl.to(item.bg, { opacity: 0, duration: 0.4, ease: 'none' }, 0);
if (item.split) tl.to(item.split.words, { opacity: 0, duration: 0.4, ease: 'power4.out' }, 0);
if (item.content) tl.to(item.content, { opacity: 0, duration: 0.2, ease: 'none' }, 0);
if (item.intro) tl.set(item.intro, { autoAlpha: 0 }, 0.45);
if (item.content) tl.set(item.content, { autoAlpha: 0 }, 0.25);
if (item.bg) tl.set(item.bg, { visibility: 'hidden' }, 0.45);
return tl;
}
// ── ACTIVATE / DEACTIVATE ─────────────────────────────────────────────
function activateItem(idx) {
var item = items[idx];
zIdx++;
if (item.bg) {
item.bg.style.zIndex = zIdx;
gsap.set(item.bg, { visibility: 'visible' });
}
if (item.el) item.el.style.pointerEvents = 'auto';
if (item.nav) item.nav.classList.add('is-active');
navItems.forEach(function(n, ni) {
gsap.to(n, { opacity: ni === idx ? 1 : 0.35, duration: 0.3, overwrite: true });
});
if (item.tlHide) item.tlHide.pause();
if (item.tlShow) {
item.tlShow.restart();
} else {
item.tlShow = buildShowTL(idx);
item.tlShow.play();
}
}
function deactivateItem(idx) {
var item = items[idx];
if (item.el) item.el.style.pointerEvents = 'none';
if (item.nav) item.nav.classList.remove('is-active');
if (item.tlShow) item.tlShow.pause();
if (item.tlHide) {
item.tlHide.restart();
} else {
item.tlHide = buildHideTL(idx);
item.tlHide.play();
}
}
// ── CHAPTER SWITCH ────────────────────────────────────────────────────
function goTo(idx) {
if (idx === current) return;
if (current >= 0) deactivateItem(current);
current = idx;
activateItem(idx);
}
// ── NAV SHOW / HIDE ──────────────────────────────────────────────────
var tlNavShow = null, tlNavHide = null;
function showNav() {
if (tlNavHide) tlNavHide.pause();
tlNavShow = gsap.timeline();
tlNavShow.fromTo(navItems, { opacity: 0, x: '100%' }, { opacity: 0.35, x: 0, duration: 0.6, ease: 'power4.out', stagger: 0.05 }, 0);
if (progressFill) tlNavShow.fromTo(progressFill, { scaleY: 0, opacity: 0 }, { scaleY: 1, opacity: 1, duration: 0.6, ease: 'power4.out' }, 0);
}
function hideNav() {
if (tlNavShow) tlNavShow.pause();
tlNavHide = gsap.timeline();
tlNavHide.to(navItems, { opacity: 0, x: '100%', duration: 0.4, ease: 'power4.out', stagger: { each: 0.02, from: 'end' } }, 0);
if (progressFill) tlNavHide.to(progressFill, { opacity: 0, duration: 0.4, ease: 'power4.out' }, 0);
}
// ── RESPONSIVE SCROLL TRIGGERS ──────────────────────────────────────
var mm = gsap.matchMedia();
var navVisible = false;
function resetSection() {
navVisible = false;
hideNav();
if (current >= 0) deactivateItem(current);
current = -1;
zIdx = ITEM_COUNT;
section.style.pointerEvents = 'none';
}
// ── DESKTOP ───────────────────────────────────────────────────────────
mm.add('(min-width: 768px)', function() {
ScrollTrigger.create({
trigger: section,
start: 'top top',
end: '+=' + (ITEM_COUNT * SCROLL_PER),
pin: true,
pinSpacing: true,
anticipatePin: 1,
onEnter: function() {
section.style.pointerEvents = '';
},
onUpdate: function(self) {
if (progressFill) gsap.set(progressFill, { scaleY: self.progress });
section.style.setProperty('--progress', self.progress);
if (!navVisible && self.isActive) {
navVisible = true;
showNav();
}
var idx = Math.min(Math.floor(self.progress * ITEM_COUNT), ITEM_COUNT - 1);
goTo(idx);
},
onLeave: resetSection,
onLeaveBack: resetSection,
onEnterBack: function() {
section.style.pointerEvents = '';
if (!navVisible) {
navVisible = true;
showNav();
}
}
});
navItems.forEach(function(nav, i) {
nav.addEventListener('click', function(e) {
e.preventDefault();
var st = ScrollTrigger.getAll().filter(function(t) { return t.trigger === section; })[0];
if (!st) return;
gsap.to(window, { scrollTo: { y: st.start + ((i + 0.5) / ITEM_COUNT) * (st.end - st.start), autoKill: false }, duration: 0.9, ease: 'power3.inOut' });
});
});
});
// ── MOBILE ────────────────────────────────────────────────────────────
mm.add('(max-width: 767px)', function() {
gsap.set(navItems, { display: 'none' });
if (progressFill) gsap.set(progressFill, { display: 'none' });
ScrollTrigger.create({
trigger: section,
start: 'top top',
end: '+=' + (ITEM_COUNT * MOBILE_SCROLL_PER),
pin: true,
pinSpacing: true,
onEnter: function() {
section.style.pointerEvents = '';
},
onUpdate: function(self) {
section.style.setProperty('--progress', self.progress);
var idx = Math.min(Math.floor(self.progress * ITEM_COUNT), ITEM_COUNT - 1);
goTo(idx);
},
onLeave: function() {
if (current >= 0) deactivateItem(current);
current = -1;
zIdx = ITEM_COUNT;
section.style.pointerEvents = 'none';
},
onLeaveBack: function() {
if (current >= 0) deactivateItem(current);
current = -1;
zIdx = ITEM_COUNT;
section.style.pointerEvents = 'none';
},
onEnterBack: function() {
section.style.pointerEvents = '';
}
});
});
});