// Inventory · Choice — the entry screen at #/inventory. // Two routes into the address, now as cinematic full-bleed image cards: // • "Choose Tower" → towers (the 10-tower master-plan selector) — DOMINANT // ~70% card carrying the REAL master plan (client 2026-07-08): the aerial // photo + the 10 authored gradient tower cutouts from window.TOWER_GEO, // alive with the same SMIL stop-color cycling the #/towers screen runs, // slow Ken-Burns drift, LIVE availability read straight off the CRM // inventory feed (data.jsx). Falls back to the flat render if TOWER_GEO // is unavailable. // • "Explore G+2 Floors" → gplus (Ground/First/Second podium explorer) — ~30% // card, vertical aerial of the amenity courtyard. // Header, back button, breadcrumb and top-right brand lockup match the other // inside screens (FloorSelect / FloorPlate / Inventory). Every label is real // HTML — no baked text images. Imagery degrades gracefully (onError hides the // img and each card still reads on its cream base). const IVC_KEYS_ID = 'uni-invchoice-keys-v2'; function ensureIvcKeys() { if (typeof document === 'undefined' || document.getElementById(IVC_KEYS_ID)) return; const s = document.createElement('style'); s.id = IVC_KEYS_ID; s.textContent = ` @keyframes ivcUp { from { opacity:0; transform: translateY(28px); } to { opacity:1; transform: translateY(0); } } /* slow, tasteful Ken-Burns drift — transform only, 28s loop, very subtle */ @keyframes ivcKen { 0% { transform: scale(1.06) translate3d(0,0,0); } 100% { transform: scale(1.15) translate3d(-1.6%,-1.4%,0); } } @keyframes ivcPulse { 0%,100% { transform: scale(1); opacity:1; } 50% { transform: scale(2.1); opacity:0; } } .ivc-card { transition: box-shadow 300ms ease, transform 200ms cubic-bezier(0.22,1,0.36,1); } .ivc-card:active { transform: scale(0.98); } .ivc-media { transition: transform 700ms cubic-bezier(0.22,1,0.36,1); } .ivc-img { will-change: transform; } .ivc-img.ivc-ken { animation: ivcKen 28s ease-in-out infinite alternate; } .ivc-frame { transition: border-color 300ms ease, box-shadow 300ms ease; } .ivc-arrow { transition: transform 320ms cubic-bezier(0.22,1,0.36,1), background 260ms ease, color 260ms ease, border-color 260ms ease; } .ivc-scrim { transition: opacity 320ms ease; } .ivc-card:hover { box-shadow: 0 46px 100px rgba(40,28,10,0.34); } .ivc-card:hover .ivc-media { transform: scale(1.025); } .ivc-card:hover .ivc-frame { border-color: rgba(224,184,110,0.85); box-shadow: inset 0 0 0 1px rgba(255,240,205,0.30); } .ivc-card:hover .ivc-scrim { opacity: 0.86; } .ivc-card:hover .ivc-arrow { transform: translateX(8px); background: var(--gold); color:#241a0a; border-color: var(--gold); } /* live-plan cover box: centered, height-bound (card aspect is always taller than the 1672/941 photo) — the drift keyframe must carry the centering translateX so the two transforms don't fight. */ .ivc-plan { position:absolute; top:0; bottom:0; left:50%; height:100%; aspect-ratio: 1672 / 941; transform: translateX(-50%) scale(1.04); } .ivc-plan.ivc-ken { animation: ivcKenPlan 30s ease-in-out infinite alternate; } @keyframes ivcKenPlan { 0% { transform: translateX(-50%) scale(1.05); } 100% { transform: translateX(-51.4%) scale(1.13); } } @media (prefers-reduced-motion: reduce){ .ivc-img.ivc-ken, .ivc-plan.ivc-ken { animation: none; } } `; document.head.appendChild(s); } // ── The REAL living master plan, framed inside the Choose Tower card ───────── // Photo + the 10 verbatim tower cutouts (authored userSpaceOnUse gradients from // the client SVG), breathing with staggered SMIL stop-color cycles — the same // life the #/towers screen has, purely decorative here (the card is the button). function LivePlanMedia() { const GEO = (typeof window !== 'undefined' && window.TOWER_GEO) || null; if (!GEO) return null; const reduce = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; const IDS = Object.keys(GEO.towers); return (
); } function ChoiceCard({ img, imgPos, ken, media, index, kicker, title, sub, badge, onGo, delay, glyph, flex, pad, titleSize }) { return ( ); } // Live-availability badge for the tower card — reads the SAME CRM feed the // Inventory map does (TOWERS × towerSummary + useInventory). Falls back to a // calm "600 RESIDENCES" while the feed is still loading. function LiveAvailBadge({ ready, available, total }) { return (