// THE STORY — interactive CHAPTER EXPLORER (one screen, 2560×ADAPTIVE, no scroll). // // Layout matches the approved reference comp: // · LEFT: editorial header (shared ScreenHeader) → CHAPTER label → the active // chapter's spread → a glass PAGER PILL (← 05 / 05 →) pinned bottom-left. // · RIGHT: a vertical CHAPTER TIMELINE RAIL (five numbered, clickable nodes on // a gold spine; the active one glows + reads VIEWING →). // · A giant faint chapter numeral floats in the gutter between the two. // Every chapter is tappable and cross-fades the left spread — fully functional. // Cream/gold editorial theme + shared ScreenHeader for cross-screen consistency. // Root is inset:0 with TOP-anchored header and BOTTOM-anchored pager/rail so the // spread fills the adaptive canvas (1600 → 1920) with no empty band. const STORY_KEYS_ID = 'uni-story-chapters-keys'; function ensureStoryKeys() { if (typeof document === 'undefined') return; if (document.getElementById(STORY_KEYS_ID)) return; const s = document.createElement('style'); s.id = STORY_KEYS_ID; s.textContent = ` @keyframes uniStAurora { 0%{transform:translate3d(-3%,-2%,0) scale(1.05) rotate(0);opacity:.5} 50%{transform:translate3d(4%,3%,0) scale(1.18) rotate(8deg);opacity:.8} 100%{transform:translate3d(-3%,-2%,0) scale(1.05) rotate(0);opacity:.5} } @keyframes uniStBreathe { 0%,100%{transform:scale(1);opacity:.30} 50%{transform:scale(1.14);opacity:.55} } @keyframes uniStUnder { from{transform:scaleX(0)} to{transform:scaleX(1)} } @keyframes uniStStage { from{opacity:0;transform:translateY(18px)} to{opacity:1;transform:translateY(0)} } @keyframes uniStNum { from{opacity:0;transform:translateY(34px) scale(0.965)} to{opacity:.05;transform:translateY(0) scale(1)} } @keyframes uniStTicker{ from{transform:translateX(0)} to{transform:translateX(-50%)} } @keyframes uniStPulse { 0%,100%{box-shadow:0 0 0 0 rgba(201,160,94,0.0)} 50%{box-shadow:0 0 0 7px rgba(201,160,94,0.16)} } @keyframes uniStNudge { 0%,100%{transform:translateX(0)} 50%{transform:translateX(5px)} } @keyframes uniStRise { from{opacity:0;transform:translateY(16px)} to{opacity:1;transform:translateY(0)} } `; document.head.appendChild(s); } const ST_EASE = 'cubic-bezier(0.22,1,0.36,1)'; function storyReveal(t, start, span, dy) { const e = ease.outQuart(clamp((t - start) / span, 0, 1)); return { opacity: e, transform: `translateY(${(1 - e) * (dy ?? 24)}px)` }; } function StoryCount({ t, start, span, value }) { const e = ease.outQuart(clamp((t - start) / span, 0, 1)); return <>{Math.round(value * e).toLocaleString('en-IN')}; } // A spec card — mono label stacked OVER a large serif value, filling its cell. // Replaces the old label-left / value-right row that left a wide empty gutter. function StorySpec({ label, value, icon, i }) { return (
{label}
{value}
); } // A project image that fills a flex cell. Subtle ease-out entrance (opacity + // translateY, transform/opacity only). A soft ivory gradient sits behind it, so // if the file ever 404s the onError hides the and the placeholder shows — // no broken-image icon. Optional overlay children (scrim + caption) render above. function StoryImg({ src, alt, radius = 18, delay = 0, pos = 'center', style, children }) { return (
{alt}{ e.currentTarget.style.display='none'; }}/> {children}
); } // A caption overlay (scrim + mono kicker + serif line) pinned to an image's foot. function ImgCaption({ kicker, title }) { return ( <>
{kicker}
{title}
); } // Inline line-icon set for the chapter stages. Single stroke weight, gold via // currentColor, 24-unit viewBox. Keeps the cards lively without any raster asset. function StoryGlyph({ type, size = 40, stroke = 'currentColor' }) { const P = { fill:'none', stroke, strokeWidth:1.5, strokeLinecap:'round', strokeLinejoin:'round' }; const G = { heritage: , live: , tenants: , area: , layers: , tag: , floors: , towers: , home: , key: , star: , }; return {G[type]||G.star}; } // Tenant crest + sector for the Phase-1 Stratum proof grid. Monogram = a refined // initial crest (no external brand logos — kiosk runs offline); sector is the // occupier's well-known industry, not a fabricated metric. const TENANT_META = { 'Reliance Retail': { mono:'RR', cat:'Retail', logo:'reliance-retail.svg' }, 'Accenture': { mono:'Ac', cat:'Consulting', logo:'accenture.svg' }, 'Kraft Heinz': { mono:'KH', cat:'FMCG', logo:'kraft-heinz.svg' }, 'Ericsson': { mono:'Er', cat:'Telecom', logo:'ericsson.svg' }, 'Citi': { mono:'Ci', cat:'Banking', logo:'citi.svg' }, 'Tech Mahindra': { mono:'TM', cat:'IT Services', logo:'tech-mahindra.svg' }, 'Siemens': { mono:'Si', cat:'Engineering', logo:'siemens.svg' }, 'Kotak Mahindra': { mono:'KM', cat:'Financial', logo:'kotak.svg' }, 'Central Bank of India': { mono:'CB', cat:'Banking', logo:'central-bank.svg' }, 'Tata AIG': { mono:'TA', cat:'Insurance', logo:'tata-aig.png' }, 'New India Assurance': { mono:'NI', cat:'Insurance', logo:'new-india.svg' }, 'Active 8': { mono:'A8', cat:'Lifestyle', logo:null }, }; function Story() { ensureStoryKeys(); const t = useLoop(); const P = PROJECT; const S = P.stats; const tenants = STRATUM_TENANTS; const H = (typeof window !== 'undefined' && window.UNIVERSE_CANVAS && window.UNIVERSE_CANVAS.H) || 1600; const dens = clamp((H - 1600) / 320); // 0 on 16:10 tablet → 1 on iPad Pro 4:3 // Land on The Vision (chapter 01) by default. const [active, setActive] = React.useState(0); // chapter index 0–4 const [touched, setTouched] = React.useState(false); // has the user clicked a chapter yet? const pick = (i) => { setActive(i); setTouched(true); }; // arrow-key navigation between chapters React.useEffect(() => { const onKey = (e) => { if (e.key === 'ArrowDown' || e.key === 'ArrowRight') { setActive(a=>Math.min(4,a+1)); setTouched(true); } if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') { setActive(a=>Math.max(0,a-1)); setTouched(true); } }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, []); const STATS = [ { key:'homes', value:S.homes, suffix:'', label:'Residences', sub:'All 4 BHK + penthouses' }, { key:'towers', value:S.towers, suffix:'', label:'Towers', sub:'Five mirrored clusters' }, { key:'amen', value:S.amenities,suffix:'', label:'Amenities', sub:'Sport · wellness · social' }, { key:'sqft', value:28, suffix:' L', label:'Sq.ft built-up', sub:'≈ 28,28,402 sq.ft' }, ]; const TEAM = [ { role:'Architect', name:S.architect, loc:'Master plan & towers' }, { role:'Structural', name:S.structural, loc:'Consulting engineers' }, { role:'Landscape', name:S.landscape, loc:'Sausalito, California' }, { role:'Interior', name:S.interior, loc:'Singapore' }, { role:'Lighting', name:S.lighting, loc:'Architectural lighting' }, ]; const CHAPTERS = [ { key:'vision', num:'01', title:'The Vision', teaser:'Why the Universe' }, { key:'developer', num:'02', title:'The Developer', teaser:'35+ years · Stratum live' }, { key:'design', num:'03', title:'Design Team', teaser:'Five world studios' }, { key:'master', num:'04', title:'Master Plan', teaser:'Ten towers · 624 homes' }, { key:'stratum', num:'05', title:'Phase-1 Stratum', teaser:'Reliance · Citi · Siemens' }, ]; const ch = CHAPTERS[active]; // ── LEFT-STAGE content per chapter (rendered on the ivory stage, not a modal) ── function stageBody(key) { if (key === 'vision') { return (
{P.developer.toUpperCase()} · {P.phase.toUpperCase()}
Center of everything.
{P.pitch}
{/* cinematic client render — fills the mid-height, brings the hero in */}
The Universe at dusk{ e.currentTarget.style.opacity=0; }}/>
Artist's impression
The Universe at dusk
NEHRU NAGAR · AHMEDABAD
{/* live proof band — sits at the bottom under the render */}
{STATS.map((d,i)=>(
{d.suffix}
{d.label}
{d.sub}
))}
); } if (key === 'developer') { const doubled = [...tenants, ...tenants]; const PROOF = [ { cap:'Heritage', icon:'heritage', k:'35', sup:'+', u:'Years of craft', d:'Timeless spaces across Ahmedabad' }, { cap:'Execution', icon:'live', k:'Live', sup:'', u:'Phase 1 · Stratum', d:'Operational and trading today' }, { cap:'Tenants', icon:'tenants', k:String(tenants.length), sup:'', u:'Marquee occupiers', d:'Global names already in residence' }, ]; return (
{/* lead */}
For over three-and-a-half decades, {P.developer} has crafted timeless spaces across {P.city}. Phase 1, Stratum @ Venus Grounds, is already operational: a commercial landmark home to global names. the Universe rises beside it as the residential crown.
{/* three proof pillars — fill the middle band */}
{PROOF.map((p,i)=>(
{p.cap}
{/* icon badge fills the middle void of the pillar */}
{p.k}{p.sup}
{p.u}
{p.d}
))}
{/* tenant proof marquee — anchored to the bottom */}
STRATUM · PHASE-1 TENANTS, ALREADY OPERATIONAL
{doubled.map((name,i)=>(
{name}
))}
); } if (key === 'design') { return (
Five specialist studios, each a leader in its craft, brought together for one Ahmedabad address.
{/* full-width editorial roster — each studio a tall row that fills the column */}
{TEAM.map((m,i)=>(
0{i+1}
{m.role}
{m.name}
{m.loc}
))}
); } if (key === 'master') { const SPECS = [ { label:'Plot area', value:S.plotArea, icon:'area' }, { label:'Built-up area', value:S.builtUp, icon:'layers' }, { label:'Saleable', value:S.saleable, icon:'tag' }, { label:'Storeys', value:S.storeys, icon:'floors' }, { label:'Towers', value:String(S.towers), icon:'towers' }, { label:'Residences', value:`${S.homes} homes`, icon:'home' }, { label:'Typology', value:S.typology, icon:'key' }, { label:'Amenities', value:`${S.amenities} curated`, icon:'star' }, ]; return (
Ten towers and {S.homes} exclusively 4 BHK homes on a {S.plotArea} plot, master-planned by {S.architect}.
{/* spec grid — 2 × 4 stacked-label cards that fill the full height evenly */}
{SPECS.map((d,i)=>())}
); } if (key === 'stratum') { return (
Phase 1, Stratum @ Venus Grounds, is live and trading: proof of execution before a single Universe home is sold. {tenants.length} marquee occupiers already call it home.
{/* tenant proof grid — stretches to fill the full vertical height */}
{tenants.map((name,i)=>{ const m=TENANT_META[name]||{mono:name.slice(0,2).toUpperCase(), cat:'Occupier', logo:null}; return (
{/* real occupier logo on a clean white plate; monogram only if no logo */} {m.logo ? {name} : {m.mono}}
{name} {m.cat}
);})}
); } return null; } // ── right chapter-rail width (no enclosing panel) ───────────────────────── const RAIL_W = 672; return (
{/* ambient backdrop */}
{/* giant faint chapter numeral — floats in the gutter behind the gap */} {/* shared header — left title block + right brand lockup (sits atop the panel) */} {/* ════════════════ LEFT — the active chapter spread ════════════════ */}
{/* chapter label */}
CHAPTER {ch.num}
{ch.title}
{/* the cross-fading spread — fills the available height */}
{stageBody(ch.key)}
{/* ── glass PAGER PILL (← 05 / 05 →) bottom-left ── */}
{ch.num} / 05
{/* ════════════════ RIGHT — chapter rail (no panel), tall fields ════════════════ */}
{/* rail header — the call to interact (aligns with the left chapter label) */}
EXPLORE THE STORY
TAP A CHAPTER {!touched && }
{/* the timeline — five tall fields that fill the full height */}
{/* gold spine — node-01 centre → node-05 centre, behind the fields */}
{CHAPTERS.map((c,i)=>{ const on=i===active; const start=0.4+i*0.07; const e=ease.outQuart(clamp((t-start)/0.7,0,1)); return ( ); })}
); } window.Story = Story;