1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title></title> 8 <link rel="icon" type="image/png" href="./assets/svg/favicon.png"> 9 10 <!-- Google Tag Manager --> 11
11<script>
vendor: 335 bytes, lines 11-15
11(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 12 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 13 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 14 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 15 })(window,document,'script','dataLayer','
15GTM-MQV3P4H
vendor: 3 bytes, line 15
15');
15</script>
15 16 <!-- End Google Tag Manager --> 17 18 <!-- Fonts (loaded via @font-face in variables.css) --> 19 20 <!-- GSAP --> 21
21<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
21 22
22<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/SplitText.min.js"></script>
22 23 24 <!-- intl-tel-input --> 25 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/build/css/intlTelInput.css"> 26 27 <!-- Styles --> 28 <link rel="stylesheet" href="./css/reset.css"> 29 <link rel="stylesheet" href="./css/variables.css"> 30 <link rel="stylesheet" href="./css/layout.css"> 31 <link rel="stylesheet" href="./css/modal.css"> 32 <link rel="stylesheet" href="./css/intl-tel-input.css"> 33 <link rel="stylesheet" href="./css/animations.css"> 34</head> 35 36<body> 37 38 <!-- Google Tag Manager (noscript) -->
vendor: 70 bytes, lines 38-39
38 39 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
39GTM-MQV3P4H
vendor: 89 bytes, lines 39-41
39" 40 height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 41
41<!-- End Google Tag Manager (noscript) --> 42 43 <!-- ââ Page âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 44 <main class="page"> 45 46 <!-- Hero --> 47 <section class="hero" data-animate="hero"> 48 <div class="hero__overlay"></div> 49 </section> 50 51 <!-- UI Layer --> 52 <div class="ui"> 53 54 <!-- Logo --> 55 <div class="logo" data-animate="logo"> 56 <img class="logo__img" alt=""> 57 <a class="logo__phase-btn" href="#" target="_blank" rel="noopener noreferrer"></a> 58 </div> 59 60 <!-- Content (centered) --> 61 <div class="content"> 62 63 <!-- Headline --> 64 <div class="headline"> 65 <span class="headline__line headline__line--left" id="headline-1" data-animate="headline-1"></span> 66 <span class="headline__line headline__line--right" id="headline-2" data-animate="headline-2"></span> 67 </div> 68 69 <div class="bottom-group"> 70 <!-- Bottom Bar --> 71 <div class="bottom-bar"> 72 <span class="bottom-bar__line"></span> 73 <div class="bottom-bar__feature"> 74 <span class="bottom-bar__label" id="bar-label"></span> 75 <div class="bottom-bar__text"> 76 <div class="bottom-bar__text-title" id="bar-feature-title"></div> 77 <p class="bottom-bar__text-body" id="bar-feature-body"></p> 78 </div> 79 </div> 80 <button class="bottom-bar__cta" id="cta-btn" data-modal-open> 81 <span id="cta-text"></span> 82 </button> 83 </div> 84 85 </div> 86 87 </div><!-- /content --> 88 89 <!-- Footer --> 90 <footer class="footer"> 91 <div class="footer__main"> 92 93 <!-- Brand + JV logos --> 94 <div class="footer__brand"> 95 <img class="footer__brand-logo" src="./assets/svg/footer-logo.svg" 96 alt="A joint venture between TRX Tun Razak Exchange and Lendlease"> 97 </div> 98 99 <!-- Links + Social --> 100 <div class="footer__right"> 101 <div class="footer__links" id="footer-links"> 102 <!-- Injected by content.js --> 103 </div> 104 <div class="footer__social" id="footer-social"> 105 <!-- Injected by content.js --> 106 </div> 107 </div> 108 109 </div> 110 </footer> 111 112 </div><!-- /ui --> 113 114 </main> 115 116 <!-- ââ Splash ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 117 <div class="splash" id="splash"> 118 <img class="splash__logo" alt=""> 119 </div> 120 121 <!-- ââ Backdrop ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 122 <div class="backdrop" id="backdrop"></div> 123 124 <!-- ââ Modal âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 125 <aside class="modal" id="modal" role="dialog" aria-modal="true" aria-label=""> 126 <div class="modal__container"> 127 128 <div class="modal__buttons"> 129 <!-- Back (shown on policy view) --> 130 <button class="modal__back" id="policy-back" aria-label="Back" type="button" style="display:none;"> 131 <svg viewBox="0 0 24 24"> 132 <polyline points="15 18 9 12 15 6" /> 133 </svg> 134 </button> 135 136 <!-- Close --> 137 <button class="modal__close" id="modal-close" aria-label="Close"> 138 <svg viewBox="0 0 24 24" fill="none"> 139 <line x1="18" y1="6" x2="6" y2="18" /> 140 <line x1="6" y1="6" x2="18" y2="18" /> 141 </svg> 142 </button> 143 </div> 144 145 146 <div class="modal__inner"> 147 148 <!-- Form content --> 149 <div id="form-content" data-animate="form-content"> 150 <form class="form" id="registration-form" action="https://submit-form.com/pVGPCTieH" method="POST" novalidate> 151 152 153 <!-- Salutation + Full Name row --> 154 <div class="field-row"> 155 156 <!-- Salutation --> 157 <div class="field field--salutation" data-field="salutation"> 158 <select class="field__input field__input--select" name="salutation" id="salutation" required> 159 <option value="" disabled selected><!-- Placeholder injected by content.js --></option> 160 <!-- Options injected by content.js --> 161 </select>
162 <span class="field__error" role="alert"></span> 163 </div> 164 165 <!-- Full Name --> 166 <div class="field field--name" data-field="fullName"> 167 <input class="field__input" type="text" name="fullName" id="fullName" placeholder=" " autocomplete="name" 168 required> 169 <label class="field__label" for="fullName"></label> 170 <span class="field__error" role="alert"></span> 171 </div> 172 173 </div> 174 175 <!-- Email --> 176 <div class="field" data-field="email"> 177 <input class="field__input" type="email" name="email" id="email" placeholder=" " autocomplete="email" 178 required> 179 <label class="field__label" for="email"></label> 180 <span class="field__error" role="alert"></span> 181 </div> 182 183 <!-- Phone --> 184 <div class="field" data-field="phone"> 185 <input class="field__input" type="tel" name="phone" id="phone" placeholder=" " autocomplete="tel" 186 required> 187 <label class="field__label" for="phone"></label> 188 <span class="field__error" role="alert"></span> 189 </div> 190 191 <!-- Nationality --> 192 <div class="field" data-field="nationality"> 193 <input type="hidden" name="nationality" id="nationality" value=""> 194 <div class="dropdown" id="dropdown-nationality"> 195 <button class="dropdown__trigger" type="button" aria-haspopup="listbox" aria-expanded="false"> 196 <span class="dropdown__value" id="nationality-value"></span> 197 <span class="dropdown__chevron"> 198 <img src="./assets/svg/chevron.svg" alt=""> 199 </span> 200 </button> 201 <div class="dropdown__menu" id="nationality-menu"> 202 <input type="text" class="dropdown__search" id="nationality-search" placeholder="Search..." autocomplete="off"> 203 <ul class="dropdown__list" id="nationality-list" role="listbox"> 204 <!-- Options injected by content.js --> 205 </ul> 206 </div> 207 </div> 208 <span class="field__error" role="alert"></span> 209 </div> 210 211 <!-- Unit Type --> 212 <div class="field" data-field="unitType"> 213 <input type="hidden" name="unitType" id="unitType" value=""> 214 <div class="dropdown" id="dropdown-unitType"> 215 <button class="dropdown__trigger" type="button" aria-haspopup="listbox" aria-expanded="false"> 216 <span class="dropdown__value" id="dropdown-value"></span> 217 <span class="dropdown__chevron"> 218 <img src="./assets/svg/chevron.svg" alt=""> 219 </span> 220 </button> 221 <ul class="dropdown__menu" id="dropdown-menu" role="listbox"> 222 <!-- Options injected by content.js --> 223 </ul> 224 </div> 225 <span class="field__error" role="alert"></span> 226 </div> 227 228 <!-- Checkboxes --> 229 <div class="form__checkboxes"> 230 <label class="checkbox" data-field="agreePrivacy"> 231 <input class="checkbox__input" type="checkbox" name="agreePrivacy" id="agreePrivacy" required> 232 <span class="checkbox__box"></span> 233 <span class="checkbox__label" id="checkbox-privacy-label"></span> 234 </label> 235 236 <label class="checkbox" data-field="agreeDisclaimer"> 237 <input class="checkbox__input" type="checkbox" name="agreeDisclaimer" id="agreeDisclaimer" required> 238 <span class="checkbox__box"></span> 239 <span class="checkbox__label" id="checkbox-disclaimer-label"></span> 240 </label> 241 </div> 242 243 <!-- Submit + Disclaimer --> 244 <div class="form__footer"> 245 246 <!-- API error --> 247 <div class="form__api-error" id="form-api-error" role="alert"></div> 248 <button class="form__submit" id="form-submit" type="submit">
249 <span class="btn-text" id="submit-text"></span> 250 </button> 251 </div> 252 253 </form> 254 </div><!-- /form-content --> 255 256 <!-- Success message --> 257 <div class="success-message" id="success-message" data-animate="success-content"> 258 <h2 class="success-message__header" id="success-header"></h2> 259 <p class="success-message__text" id="success-text"></p> 260 </div> 261 262 <!-- Error message --> 263 <div class="success-message" id="error-message" data-animate="error-content"> 264 <h2 class="success-message__header" id="error-header"></h2> 265 <p class="success-message__text" id="error-text"></p> 266 </div> 267 268 <!-- Policy view (replaces form content) --> 269 <div class="modal__policy" id="policy-view"> 270 <div class="modal__policy-header"> 271 <h2 class="modal__policy-title" id="policy-title"></h2> 272 </div> 273 <hr class="modal__policy-divider"> 274 <div class="modal__policy-body" id="policy-body"></div> 275 </div> 276 277 </div><!-- /modal__inner --> 278 279 </div> 280 </aside> 281 282 <!-- ââ Scripts âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 283
283<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/js/intlTelInput.min.js"></script>
283 284
284<script src="https://unpkg.com/@botpoison/browser" async></script>
284 285
285<script src="./js/content.js"></script>
285 286
286<script src="./js/animations.js"></script>
286 287
287<script src="./js/modal.js"></script>
287 288
288<script src="./js/form.js"></script>
288 289 290
290<script> 291 // ââ Inject content ââââââââââââââââââââââââââââââââââââ 292 293 // Page title 294 document.title = CONTENT.pageTitle; 295 296 // Logo + splash 297 document.querySelector('.logo__img').src = CONTENT.logoSrc; 298 document.querySelector('.logo__img').alt = CONTENT.logoAlt; 299 document.querySelector('.splash__logo').src = CONTENT.logoSrc; 300 document.querySelector('.splash__logo').alt = CONTENT.logoAlt; 301 document.querySelector('.logo__phase-btn').textContent = CONTENT.phaseTag; 302 303 // Headline 304 document.getElementById('headline-1').textContent = CONTENT.headlineLine1; 305 document.getElementById('headline-2').textContent = CONTENT.headlineLine2; 306 307 // Bottom bar 308 document.getElementById('bar-label').textContent = CONTENT.whatToExpectLabel; 309 const feature = CONTENT.features[0]; 310 document.getElementById('bar-feature-title').textContent = feature.number + '\u2003' + feature.title; 311 document.getElementById('bar-feature-body').textContent = feature.body; 312 document.getElementById('cta-text').textContent = CONTENT.ctaButton; 313 314 // Salutation options 315 const salutationSelect = document.getElementById('salutation'); 316 salutationSelect.options[0].textContent = CONTENT.formFields.salutation; 317 CONTENT.salutations.forEach(s => { 318 const opt = document.createElement('option'); 319 opt.value = s; 320 opt.textContent = s; 321 salutationSelect.appendChild(opt); 322 }); 323 324 // Form field labels 325 document.querySelector('label[for="fullName"]').textContent = CONTENT.formFields.fullName; 326 document.querySelector('label[for="email"]').textContent = CONTENT.formFields.email; 327 document.querySelector('label[for="phone"]').textContent = CONTENT.formFields.phone; 328 // Nationality placeholder set by dropdown injection below 329 330 // Modal aria-label 331 document.getElementById('modal').setAttribute('aria-label', CONTENT.ctaButton); 332 333 // Submit + success 334 document.getElementById('submit-text').textContent = CONTENT.submitButton; 335 document.getElementById('success-header').textContent = CONTENT.successHeader; 336 document.getElementById('success-text').textContent = CONTENT.successMessage; 337 document.getElementById('error-header').textContent = CONTENT.errorHeader; 338 document.getElementById('error-text').textContent = CONTENT.errorMessage; 339 340 // Checkbox consent 341 document.getElementById('checkbox-privacy-label').innerHTML = 342 CONTENT.checkboxPrivacy.en + '<br><em>' + CONTENT.checkboxPrivacy.ms + '</em>'; 343 document.getElementById('checkbox-disclaimer-label').innerHTML = 344 CONTENT.checkboxDisclaimer.en + '<br><em>' + CONTENT.checkboxDisclaimer.ms + '</em>'; 345 346 // Dropdown options 347 const dropdownMenu = document.getElementById('dropdown-menu'); 348 const dropdownValue = document.getElementById('dropdown-value'); 349 dropdownValue.textContent = CONTENT.formFields.unitType; 350 dropdownValue.classList.add('is-placeholder'); 351 CONTENT.unitTypes.forEach(type => { 352 const li = document.createElement('li'); 353 li.className = 'dropdown__item'; 354 li.dataset.value = type; 355 li.textContent = type; 356 li.setAttribute('role', 'option'); 357 dropdownMenu.appendChild(li); 358 }); 359 360 // Nationality dropdown options 361 const nationalityList = document.getElementById('nationality-list'); 362 const nationalityValue = document.getElementById('nationality-value'); 363 nationalityValue.textContent = CONTENT.formFields.nationality; 364 nationalityValue.classList.add('is-placeholder'); 365 document.getElementById('nationality-search').placeholder = 'Search ' + CONTENT.formFields.nationality.toLowerCase() + '...'; 366 CONTENT.nationalities.forEach(name => { 367 const li = document.createElement('li'); 368 li.className = 'dropdown__item'; 369 li.dataset.value = name; 370 li.textContent = name; 371 li.setAttribute('role', 'option'); 372 nationalityList.appendChild(li); 373 }); 374 375 // ââ Policy View (inside modal) ââââââââââââââââââââââââââââââââââââââââââ 376 (() => { 377 const formContent = document.getElementById('form-content'); 378 const policyView = document.getElementById('policy-view');
379 const policyTitle = document.getElementById('policy-title'); 380 const policyBody = document.getElementById('policy-body'); 381 const backBtn = document.getElementById('policy-back'); 382 383 function showPolicy(title, paragraphs) { 384 return (e) => { 385 e.preventDefault(); 386 e.stopPropagation(); 387 policyTitle.textContent = title; 388 policyBody.innerHTML = ''; 389 paragraphs.forEach(item => { 390 if (typeof item === 'string') { 391 const p = document.createElement('p'); 392 p.textContent = item; 393 policyBody.appendChild(p); 394 } else if (item && item.heading) { 395 const h = document.createElement('h4'); 396 h.className = 'policy__heading'; 397 h.textContent = item.heading; 398 policyBody.appendChild(h); 399 } else if (item && item.list) { 400 const ul = document.createElement('ul'); 401 ul.className = 'policy__list'; 402 item.list.forEach(li => { 403 const liEl = document.createElement('li'); 404 liEl.textContent = li; 405 ul.appendChild(liEl); 406 }); 407 policyBody.appendChild(ul); 408 } 409 }); 410 Animations.formToPolicy(formContent, policyView); 411 policyBody.scrollTop = 0; 412 }; 413 } 414 415 function hidePolicy() { 416 Animations.policyToForm(policyView, formContent); 417 } 418 419 document.querySelectorAll('[data-open-privacy]').forEach(link => { 420 link.addEventListener('click', showPolicy(CONTENT.privacyPolicyTitle, CONTENT.privacyPolicy)); 421 }); 422 423 document.querySelectorAll('[data-open-disclaimer]').forEach(link => { 424 link.addEventListener('click', showPolicy(CONTENT.websiteDisclaimerTitle, CONTENT.websiteDisclaimer || [])); 425 }); 426 427 backBtn.addEventListener('click', hidePolicy); 428 })(); 429 430 // ââ Footer ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 431 (() => { 432 const f = CONTENT.footer; 433 434 // Links 435 const linksEl = document.getElementById('footer-links'); 436 const socialIcons = { 437 Facebook: '<svg viewBox="0 0 40 40" fill="none"><path d="M22.5 20h-2v7h-3v-7h-1.5v-2.5h1.5V16c0-2 1-3.2 3.2-3.2h2.3v2.5h-1.4c-.7 0-.9.3-.9.9v1.3h2.3L22.5 20z" fill="currentColor"/></svg>', 438 Instagram: '<svg viewBox="0 0 40 40" fill="none"><rect x="13" y="13" width="14" height="14" rx="3.5" stroke="currentColor" stroke-width="1.5"/><circle cx="20" cy="20" r="3.5" stroke="currentColor" stroke-width="1.5"/><circle cx="25.5" cy="14.5" r="1" fill="currentColor"/></svg>', 439 }; 440 441 f.links.forEach(link => { 442 const a = document.createElement('a'); 443 a.className = 'footer__link'; 444 a.href = link.url; 445 a.target = '_blank'; 446 a.rel = 'noopener noreferrer'; 447 a.innerHTML = '<span class="footer__link-bar footer__link-bar--' + link.color + '"></span>' + link.label; 448 linksEl.appendChild(a); 449 }); 450 451 // Social icons 452 const socialEl = document.getElementById('footer-social'); 453 f.social.forEach(s => { 454 const a = document.createElement('a'); 455 a.className = 'footer__social-link'; 456 a.href = s.url; 457 a.target = '_blank'; 458 a.rel = 'noopener noreferrer'; 459 a.setAttribute('aria-label', s.label); 460 a.innerHTML = socialIcons[s.label] || ''; 461 socialEl.appendChild(a); 462 }); 463 })(); 464 465 // ââ Init modules âââââââââââââââââââââââââââââââââââââââââââââââââââââââ 466 Modal.init(); 467 Form.init(); 468 Animations.init(); 469 </script>
469 470 471</body> 472 473</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.