1<!DOCTYPE html> 2<html lang="en" dir="ltr"> 3 <head> 4<!-- Google tag (gtag.js) -->
vendor: 67 bytes, lines 4-5
4 5<script async="" src="https://www.googletagmanager.com/gtag/js?id=
5G-L2P3DB0T9T
vendor: 13 bytes, lines 5-6
5"> 6</script>
7<script> 8
vendor: 150 bytes, lines 8-12
8window.dataLayer = window.dataLayer || []; 9 function gtag(){dataLayer.push(arguments);} 10 gtag('js', new Date()); 11 12 gtag('config', '
12G-L2P3DB0T9T
vendor: 6 bytes, lines 12-13
12'); 13
13</script>
13 14<meta charset="utf-8"/> 15<meta content="IE=edge" http-equiv="X-UA-Compatible"/> 16<meta content="width=device-width, initial-scale=1.0" name="viewport"/> 17<title>About GATAI: AI Research & Software Engineering | GATAI</title> 18 19 20<link href="/assets/images\LOGGGGGGGGGGGGGGGGGGG-removebg-preview (1).png" rel="shortcut icon" type="image/svg+xml"/> 21<!-- 22 - custom css link 23 --> 24 25 26<!-- [Attributes by Finsweet] Disable scrolling --> 27 28<!-- Tailwind CSS via CDN --> 29 30<!-- Flatpickr CSS --> 31 32<!-- Heroicons (optional for icons) --> 33
34<script type="module" src="/assets/dist/assets/js-beh-D32igz99.js"></script>
vendor: 1 bytes, line 34
34
35<script type="module" src="/assets/dist/assets/js-accessibility-CbHkt0aF.js"></script>
vendor: 1 bytes, line 35
35
36<script type="module" src="/assets/dist/assets/js-sec-b3OiUrUj.js"></script>
vendor: 1 bytes, line 36
36
37<script type="application/json" data-gatai-graphic="abe">{}</script>
37 38 39 40 41<!-- MOBILE HEADER (shown only on small screens) --> 42<base href="/"/> 43<meta content="en" http-equiv="content-language"/> 44 45 46 47 48 49 50 51 52 53 54<meta content="GATAI Inc. is an innovation-led technology company blending applied research, advanced engineering, and disciplined delivery. We help organizations adopt AIâ¦" name="description"/> 55<meta content="About GATAI: AI Research & Software Engineering | GATAI" property="og:title"/> 56<meta content="GATAI Inc. is an innovation-led technology company blending applied research, advanced engineering, and disciplined delivery. We help organizations adopt AIâ¦" property="og:description"/> 57<meta content="About GATAI: AI Research & Software Engineering | GATAI" name="twitter:title"/> 58<meta content="GATAI Inc. is an innovation-led technology company blending applied research, advanced engineering, and disciplined delivery. We help organizations adopt AIâ¦" name="twitter:description"/> 59<link href="https://www.g-atai.com/about" rel="canonical"/><link href="https://www.g-atai.com/about" hreflang="en" rel="alternate"/><link href="https://www.g-atai.com/de/about" hreflang="de" rel="alternate"/><link href="https://www.g-atai.com/es/about" hreflang="es" rel="alternate"/><link href="https://www.g-atai.com/fr/about" hreflang="fr" rel="alternate"/><link href="https://www.g-atai.com/ja/about" hreflang="ja" rel="alternate"/><link href="https://www.g-atai.com/ko/about" hreflang="ko" rel="alternate"/><link href="https://www.g-atai.com/zh/about" hreflang="zh" rel="alternate"/><link href="https://www.g-atai.com/about" hreflang="x-default" rel="alternate"/><meta content="index, follow, max-image-preview:large" name="robots"/><meta content="https://www.g-atai.com/about" property="og:url"/><meta content="website" property="og:type"/><meta content="GATAI" property="og:site_name"/><meta content="en_US" property="og:locale"/><meta content="summary_large_image" name="twitter:card"/><meta content="https://www.g-atai.com/assets/images/1726256466608.jpg" property="og:image"/><meta content="https://www.g-atai.com/assets/images/1726256466608.jpg" name="twitter:image"/><meta content="GATAI" property="og:image:alt"/><meta content="GATAI" name="twitter:image:alt"/><meta content="https://www.g-atai.com/assets/images/1726256466608.jpg" property="og:image:secure_url"/><meta content="200" property="og:image:width"/><meta content="200" property="og:image:height"/><meta content="image/jpeg" property="og:image:type"/>
59<script type="application/ld+json">{"@context": "https://schema.org", "@graph": [{"@type": "Organization", "@id": "https://www.g-atai.com/#organization", "name": "G-ATAI", "url": "https://www.g-atai.com/", "logo": "https://www.g-atai.com/assets/images/1726256466608.jpg", "email": "[email protected]", "sameAs": ["https://www.linkedin.com/company/gatai-inc/"], "legalName": "GATAI Inc.", "alternateName": ["GATAI", "GATAI Inc."], "description": "AI consulting, agent development, business automation, custom AI and software engineering.", "contactPoint": {"@type": "ContactPoint", "contactType": "business inquiries", "email": "[email protected]", "url": "https://www.g-atai.com/contact", "availableLanguage": ["en", "ar", "de", "es", "fr", "ja", "ko", "zh"]}}, {"@type": "WebSite", "@id": "https://www.g-atai.com/#website", "name": "G-ATAI", "url": "https://www.g-atai.com/", "publisher": {"@id": "https://www.g-atai.com/#organization"}, "alternateName": "GATAI", "inLanguage": ["en", "ar", "de", "es", "fr", "ja", "ko", "zh"], "about": {"@id": "https://www.g-atai.com/#organization"}}, {"@type": "AboutPage", "@id": "https://www.g-atai.com/about#webpage", "url": "https://www.g-atai.com/about", "name": "About GATAI: AI Research & Software Engineering | GATAI", "description": "GATAI Inc. is an innovation-led technology company blending applied research, advanced engineering, and disciplined delivery. We help organizations adopt AIâ¦", "inLanguage": "en", "isPartOf": {"@id": "https://www.g-atai.com/#website"}, "about": {"@id": "https://www.g-atai.com/#organization"}, "primaryImageOfPage": {"@type": "ImageObject", "url": "https://www.g-atai.com/assets/images/1726256466608.jpg"}, "publisher": {"@id": "https://www.g-atai.com/#organization"}, "mainEntity": {"@id": "https://www.g-atai.com/#organization"}, "breadcrumb": {"@id": "https://www.g-atai.com/about#breadcrumbs"}}, {"@type": "BreadcrumbList", "@id": "https://www.g-atai.com/about#breadcrumbs", "itemListElement": [{"@type": "ListItem", "position": 1, "name": "G-ATAI", "item": "https://www.g-atai.com/"}, {"@type": "ListItem", "position": 2, "name": "About GATAI: AI Research & Software Engineering | GATAI", "item": "https://www.g-atai.com/about"}]}]}</script>
59<link href="/llms.txt" rel="describedby"/>
59<script type="module" src="/assets/dist/assets/js-site-quality-Bi_Z2Wko.js"></script>
59<script id="gti-analytics-config" type="application/json">{"endpoint": "/analytics/collect", "language": "en", "path": "/about"}</script>
vendor: 1 bytes, line 59
59
60<script type="module" src="/assets/dist/assets/js-analytics-tracker-DT6ZNaIH.js"></script>
60 61<link href="/assets/dist/assets/css-style-De4iCb1U.css" rel="stylesheet"/><link href="/assets/dist/assets/css-utilities-BRO72J3b.css" rel="stylesheet"/><link href="/assets/dist/assets/css-accessibility-sMZzalK3.css" rel="stylesheet"/><link href="/assets/dist/assets/css-j3-DGJho5jq.css" rel="stylesheet"/><link href="/assets/dist/assets/css-site-quality-BOajjm1-.css" rel="stylesheet"/><link href="/assets/dist/assets/css-commercial-t_RD2f6q.css" rel="stylesheet"/><style> 62:root{ 63 --bg: #050608; 64 --fg: rgba(238,244,250,.96); 65 --muted: rgba(238,244,250,.66); 66 67 /* driven by JS */ 68 --scroll: 0; /* 0..1 within active section */ 69 --lumen: 0; /* 0..1 page scroll */ 70} 71 72html, body { height: 100%; } 73 74body{ 75 background: var(--bg) !important; 76 background-image: none !important; 77 color: var(--fg); 78 overflow-x: hidden; 79} 80 81/* IMPORTANT: canvas must be ABOVE body bg, BELOW content */ 82#vortexBg{ 83 position: fixed; 84 inset: 0; 85 width: 100%; 86 height: 100%; 87 z-index: 0; /* <-- not negative */ 88 pointer-events: none; 89 opacity: .96; 90} 91 92/* keep content above vortex */ 93header, main, footer, 94.header, .roadmap-main{ 95 position: relative; 96 z-index: 2; 97} 98 99/* subtle vignette (lighter than before; donât crush the image) */ 100body::before{ 101 content:""; 102 position: fixed; 103 inset: 0; 104 pointer-events: none; 105 z-index: 1; 106 background: 107 radial-gradient(1000px 720px at 52% 46%, 108 rgba(0,0,0,0) 25%, 109 rgba(0,0,0,.35) 55%, 110 rgba(0,0,0,.70) 85%), 111 radial-gradient(900px 680px at 78% 22%, 112 rgba(70,240,255,.08), 113 rgba(0,0,0,0) 62%), 114 radial-gradient(900px 680px at 22% 72%, 115 rgba(80,255,220,.06), 116 rgba(0,0,0,0) 64%); 117} 118 119/* ---------------- MAIN LAYOUT ---------------- */ 120 121.roadmap-main{ 122 padding-top: 110px; /* header offset */ 123} 124 125.scn{ 126 min-height: 100vh; 127 padding: 70px 0; 128} 129 130.scn-inner{ 131 max-width: 1240px; 132 margin: 0 auto; 133 padding: 0 26px; 134} 135 136/* make hero text readable on dark */ 137.roadmap-main .hero, 138.roadmap-main .hero-content{ 139 background: transparent !important; 140} 141.roadmap-main .hero-title{ color: var(--fg) !important; } 142.roadmap-main .section-text{ color: var(--muted) !important; } 143 144/* ---------------- CENTER RAIL ---------------- */ 145 146.center-rail{ 147 position: fixed; 148 left: 50%; 149 top: 92px; 150 bottom: 70px; 151 transform: translateX(-50%); 152 width: 2px; 153 z-index: 3; 154 opacity: 0; 155 transition: opacity .28s ease; 156} 157 158.center-rail.is-on{ opacity: 1; } 159 160.center-rail__label{ 161 position: absolute; 162 top: -52px; 163 left: 50%; 164 transform: translateX(-50%); 165 padding: 10px 18px; 166 border-radius: 14px; 167 font-weight: 700; 168 letter-spacing: .22em; 169 font-size: 12px; 170 text-transform: uppercase; 171 color: rgba(10,12,16,.95); 172 background: rgba(245,247,250,.92); 173 box-shadow: 0 12px 40px rgba(0,0,0,.45); 174 opacity: 0; 175 transition: opacity .2s ease, transform .2s ease; 176} 177 178.center-rail.is-on .center-rail__label{ 179 opacity: 1; 180 transform: translateX(-50%) translateY(0); 181} 182 183.center-rail__track{ 184 position: absolute; 185 inset: 0; 186 background: rgba(255,255,255,.12); 187 border-radius: 999px; 188} 189 190.center-rail__fill{ 191 position: absolute; 192 top: 0; 193 left: 0; 194 right: 0; 195 height: 100%; 196 background: rgba(255,255,255,.92); 197 border-radius: 999px; 198 transform-origin: top; 199 transform: scaleY(var(--scroll)); 200 box-shadow: 0 0 26px rgba(255,255,255,.18); 201} 202 203/* ---------------- SPLIT SERVICES (left/right) ---------------- */ 204 205.split{ 206 display: grid; 207 grid-template-columns: 1fr 1fr; 208 gap: 22px; 209 align-items: start; 210} 211 212/* leave room around the rail */ 213.col--left{ padding-right: 54px; } 214.col--right{ padding-left: 54px; } 215 216@media (max-width: 980px){ 217 .center-rail{ display:none; } 218 .split{ grid-template-columns: 1fr; } 219 .col--left, .col--right{ padding: 0; } 220} 221 222/* ---------------- CARDS: softer, more dynamic ---------------- */ 223 224.focus-feature-card, 225.focus-feature-card{ 226 border-radius: 28px; 227 padding: 22px 22px 18px; 228 background: rgba(10, 12, 16, .52); 229 border: 1px solid rgba(255,255,255,.10); 230 backdrop-filter: blur(14px); 231 -webkit-backdrop-filter: blur(14px); 232 233 /* depth without âsharp blocksâ */ 234 box-shadow: 235 0 18px 60px rgba(0,0,0,.45), 236 0 0 0 1px rgba(255,255,255,.04) inset; 237 transition: transform .28s ease, border-color .28s ease, box-shadow .28s ease; 238} 239 240.focus-feature-card:hover, 241.focus-feature-card:hover{ 242 transform: translateY(-3px); 243 border-color: rgba(255,255,255,.16); 244 box-shadow: 245 0 22px 80px rgba(0,0,0,.55), 246 0 0 42px rgba(90,255,235, calc(.06 + .12 * var(--lumen))); 247} 248 249.focus-feature-card h3, 250.focus-feature-card h3{ 251 margin: 0; 252 font-size: 20px; 253 font-weight: 800; 254 color: var(--fg); 255} 256 257.focus-feature-card p, 258.focus-feature-card p{ 259 margin: 10px 0 16px; 260 line-height: 1.7; 261 color: rgba(238,244,250,.72); 262 font-size: 14px; 263} 264 265.focus-feature-card--wide{ 266 padding: 26px 26px 20px; 267} 268 269.svc-wide{ margin-top: 18px; } 270 271/* industry cards: clickable blocks */ 272.focus-feature-card{ 273 text-decoration: none; 274 display: flex; 275 flex-direction: column; 276 gap: 4px; 277} 278.focus-feature-card .btn.hero-btn-main{ 279 margin-top: 8px; 280 width: fit-content; 281 pointer-events: none; /* so whole card is the link */ 282} 283 284/* grid */ 285.industry-grid{ 286 display: grid; 287 grid-template-columns: repeat(3, minmax(0,1fr)); 288 gap: 18px; 289} 290@media (max-width: 980px){ .industry-grid{ grid-template-columns: 1fr; } } 291 292/* =========================================================
293 Split-Dots Background (matches your reference image) 294 ========================================================= */ 295:root{ 296 --rm-progress: 0; /* set by JS (0..1) */ 297 --rm-lumen: 0; /* set by JS (0..1) */ 298 299 /* seam */ 300 --rm-seam-w: 72px; /* center dark bar width */ 301 --rm-seam-line: 1px; /* thin center line */ 302 --rm-seam-alpha: 0.92; 303 304 /* dot tint */ 305 --rm-dot-cyan: 130, 245, 255; 306 --rm-dot-teal: 90, 255, 236; 307 308 /* depth tuning */ 309 --rm-dots-opacity: 0.95; /* global opacity for canvas */ 310 --rm-dots-contrast: 1.08; 311 --rm-dots-sat: 1.10; 312} 313 314/* Make sure the canvas is visible and not behind a body background */ 315#vortexBg{ 316 position: fixed !important; 317 inset: 0 !important; 318 width: 100% !important; 319 height: 100% !important; 320 z-index: 0 !important; 321 pointer-events: none !important; 322 323 /* makes dots âpopâ like the ref, without being heavy */ 324 opacity: var(--rm-dots-opacity); 325 filter: saturate(var(--rm-dots-sat)) contrast(var(--rm-dots-contrast)); 326 mix-blend-mode: screen; /* key: luminous look */ 327} 328 329/* Ensure page content is above the canvas */ 330header, .header, main, footer{ 331 position: relative; 332 z-index: 2; 333} 334 335/* Soft vignette / atmosphere (optional but helps match reference) */ 336body::before{ 337 content:""; 338 position: fixed; 339 inset: 0; 340 z-index: 1; /* above canvas, below content */ 341 pointer-events:none; 342 background: 343 radial-gradient(1100px 800px at 52% 45%, 344 rgba(0,0,0,0) 20%, 345 rgba(0,0,0,.42) 55%, 346 rgba(0,0,0,.78) 85%), 347 radial-gradient(900px 600px at 70% 20%, 348 rgba(var(--rm-dot-cyan), .10), 349 rgba(0,0,0,0) 62%), 350 radial-gradient(900px 600px at 30% 72%, 351 rgba(var(--rm-dot-teal), .08), 352 rgba(0,0,0,0) 62%); 353} 354 355/* The center seam overlay (created by JS if missing) */ 356.rm-seam{ 357 position: fixed; 358 top: 0; 359 left: 50%; 360 transform: translateX(-50%); 361 width: var(--rm-seam-w); 362 height: 100%; 363 z-index: 1; 364 pointer-events: none; 365 366 /* dark pillar + very subtle edge glow */ 367 background: 368 linear-gradient(to right, 369 rgba(0,0,0,0) 0%, 370 rgba(0,0,0, calc(var(--rm-seam-alpha) * 0.55)) 18%, 371 rgba(0,0,0, var(--rm-seam-alpha)) 50%, 372 rgba(0,0,0, calc(var(--rm-seam-alpha) * 0.55)) 82%, 373 rgba(0,0,0,0) 100%); 374 375 box-shadow: 376 0 0 42px rgba(var(--rm-dot-cyan), calc(.05 + .12 * var(--rm-lumen))); 377} 378 379/* thin line in the center */ 380.rm-seam::after{ 381 content:""; 382 position: absolute; 383 top: 0; 384 left: 50%; 385 width: var(--rm-seam-line); 386 height: 100%; 387 transform: translateX(-50%); 388 background: rgba(255,255,255, calc(0.05 + 0.10 * var(--rm-lumen))); 389} 390 391/* Mobile: seam slightly thinner */ 392@media (max-width: 720px){ 393 :root{ --rm-seam-w: 56px; } 394} 395/* ========================================================= 396 ser-fx.css Seam dots background (like reference) 397 ========================================================= */ 398 399:root{ 400 --rm-progress: 0; 401 --rm-lumen: 0.25; 402 --rm-seam-w: 72px; 403 404 --fx-bg: #050607; 405} 406 407/* IMPORTANT: your hero/sections had opaque backgrounds => they hide dots. 408 This forces them transparent so the canvas can be seen. */ 409.hero, .scn, main, .site-main, .roadmap-page, .roadmap-scn{ 410 background: transparent !important; 411 background-image: none !important; 412} 413 414/* Prefer the canvas ABOVE body background but BELOW content */ 415#vortexBg{ 416 position: fixed; 417 inset: 0; 418 width: 100%; 419 height: 100%; 420 pointer-events: none; 421 z-index: 1; 422} 423 424/* Content above canvas */ 425header, main, footer, .header, .footer, .site-main{ 426 position: relative; 427 z-index: 3; 428} 429 430/* Subtle vignette (keeps it âpremiumâ without hiding dots) */ 431body{ 432 background: var(--fx-bg) !important; 433 overflow-x: hidden; 434} 435body::before{ 436 content:""; 437 position: fixed; 438 inset: 0; 439 pointer-events: none; 440 z-index: 2; /* above canvas, below content */ 441 background: 442 radial-gradient(1200px 800px at 55% 40%, 443 rgba(0,0,0,0) 28%, 444 rgba(0,0,0,.35) 58%, 445 rgba(0,0,0,.78) 90%); 446} 447 448/* Seam overlay (center split) */ 449.rm-seam{ 450 position: fixed; 451 top: 0; bottom: 0; 452 left: 50%; 453 width: var(--rm-seam-w); 454 transform: translateX(-50%); 455 pointer-events: none; 456 z-index: 4; 457 458 background: 459 linear-gradient(to right, 460 rgba(0,0,0,0) 0%, 461 rgba(0,0,0,.55) 22%, 462 rgba(0,0,0,.86) 50%, 463 rgba(0,0,0,.55) 78%, 464 rgba(0,0,0,0) 100%); 465} 466 467/* Two thin glass rails */ 468.rm-seam::before, 469.rm-seam::after{ 470 content:""; 471 position:absolute; 472 top:0; bottom:0; 473 width: 2px; 474 opacity: .45; 475 background: rgba(120,245,255,.35); 476 box-shadow: 477 0 0 calc(10px + 18px * var(--rm-lumen)) rgba(64,242,232,.22); 478}
479.rm-seam::before{ left: 12px; } 480.rm-seam::after { right: 12px; } 481 482/* HUD */ 483#rmHud{ 484 position: fixed; 485 left: 50%; 486 top: 10px; 487 transform: translateX(-50%); 488 z-index: 6; 489 pointer-events: none; 490 opacity: 0; 491 transition: opacity .25s ease; 492} 493#rmHud.is-on{ opacity: 1; } 494 495#rmPill{ 496 display: inline-flex; 497 align-items: center; 498 justify-content: center; 499 padding: 10px 16px; 500 border-radius: 999px; 501 font-weight: 800; 502 font-size: 12px; 503 letter-spacing: .18em; 504 color: rgba(12,14,18,.92); 505 background: rgba(234,240,246,.92); 506 border: 1px solid rgba(255,255,255,.16); 507 box-shadow: 508 0 14px 48px rgba(0,0,0,.45), 509 0 0 calc(12px + 16px * var(--rm-lumen)) rgba(64,242,232,.16); 510 backdrop-filter: blur(10px); 511} 512 513#rmHud .rm-bar{ 514 width: 10px; 515 height: 70px; 516 margin: 10px auto 0; 517 border-radius: 999px; 518 background: rgba(255,255,255,.10); 519 box-shadow: inset 0 0 0 1px rgba(255,255,255,.08); 520 overflow: hidden; 521} 522#rmHud .rm-fill{ 523 width: 100%; 524 height: 100%; 525 transform-origin: top; 526 transform: scaleY(var(--rm-progress)); 527 background: rgba(255,255,255,.95); 528 box-shadow: 0 0 calc(10px + 16px * var(--rm-lumen)) rgba(255,255,255,.20); 529} 530 531</style><style> 532 /* blinking caret */ 533 #typewriter::after { 534 content: "|"; 535 animation: blink 1s steps(2, start) infinite; 536 } 537 @keyframes blink { 538 to { visibility: hidden; } 539 } 540 </style><style id="office-fx-styles"> 541 /* ---------- layering inside #features ---------- */ 542 #features{ position: relative; } 543 #features .office-fx{ position:absolute; inset:0; z-index:1; pointer-events:none; contain:layout paint; } 544 #features > .container{ position: relative; z-index:2; } 545 546 /* split background (left white / right black) */ 547 .office-split-bg{ position:absolute; inset:0; z-index:0; 548 background: linear-gradient(90deg, #ffffff 0 50%, #000000 50% 100%); 549 transition: filter .3s ease; filter: grayscale(1) contrast(1.06); 550 } 551 552 /* fade-to-black overlay that rises near section end */ 553 .office-fade-black{ position:absolute; inset:0; z-index:2; background:#000; opacity:0; transition:opacity .2s linear; } 554 555 /* dotted side rails */ 556 .office-rails{ position:absolute; inset:0; z-index:1; pointer-events:none; } 557 .office-rails::before, .office-rails::after{ content:""; position:absolute; top:6svh; bottom:6svh; width:72px; } 558 /* Left: blue + white dots on a subtle dark strip */ 559 .office-rails::before{ left:0; background: 560 radial-gradient(6px 6px at 12px 12px, rgba(255,255,255,.9), rgba(255,255,255,0) 60%), 561 radial-gradient(6px 6px at 40px 40px, rgba(86,190,255,.9), rgba(86,190,255,0) 60%), 562 linear-gradient(180deg, rgba(0,0,0,.34), rgba(0,0,0,.42)); 563 background-size: 48px 48px, 48px 48px, 100% 100%; 564 background-repeat: repeat, repeat, no-repeat; 565 mix-blend-mode: screen; 566 opacity:.9; 567 } 568 /* Right: blue + black dots blended on a lighter strip */ 569 .office-rails::after{ right:0; background: 570 radial-gradient(6px 6px at 16px 16px, rgba(0,0,0,.9), rgba(0,0,0,0) 60%), 571 radial-gradient(6px 6px at 44px 44px, rgba(86,190,255,.9), rgba(86,190,255,0) 60%), 572 linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.12)); 573 background-size: 48px 48px, 48px 48px, 100% 100%; 574 background-repeat: repeat, repeat, no-repeat; 575 mix-blend-mode: normal; 576 opacity:.9; 577 } 578 579 /* Give the dots a subtle parallax drift as you scroll (driven by CSS vars) */ 580 :root{ --railShift: 0px; } 581 .office-rails::before{ background-position: calc(var(--railShift)* .6) calc(var(--railShift)* .9), calc(var(--railShift)* .3) calc(var(--railShift)* .6), 0 0; } 582 .office-rails::after { background-position: calc(var(--railShift)*-.5) calc(var(--railShift)*-.8), calc(var(--railShift)*-.2) calc(var(--railShift)*-.5), 0 0; } 583 584 /* ---------- faux 3D office world ---------- */ 585 .office-world{ position:absolute; inset:0; z-index:1; perspective: 1600px; perspective-origin: 50% 50%; } 586 .office-track{ position:absolute; inset:0; transform-style: preserve-3d; will-change: transform; } 587 588 /* rooms are stacked forward in Z (we move the track backwards on scroll) */ 589 .room{ position:absolute; inset:10% 14%; border-radius: 18px; overflow:hidden; 590 background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(0,0,0,.3)); 591 box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 10px 40px rgba(0,0,0,.28); 592 transform-style: preserve-3d; }
593 .room::after{ /* depth grid lines to sell perspective */ 594 content:""; position:absolute; inset:0; 595 background: 596 linear-gradient(transparent 95%, rgba(255,255,255,.08) 0) top/100% 40px repeat-y, 597 linear-gradient(90deg, transparent 95%, rgba(255,255,255,.05) 0) left/40px 100% repeat-x; 598 mix-blend-mode: screen; opacity:.25; 599 } 600 601 .room.lobby { transform: translateZ(-200px); } 602 .room.corridor { transform: translateZ( 200px); } 603 .room.deep { transform: translateZ( 600px); } 604 605 /* doors positioned along Z, rotate open around inner edge */ 606 .door{ position:absolute; top:20%; bottom:20%; width:28%; min-width:220px; border-radius: 10px; 607 background: linear-gradient(180deg, rgba(255,255,255,.12), rgba(255,255,255,.02)); 608 box-shadow: inset 0 0 0 1px rgba(255,255,255,.08), 0 20px 60px rgba(0,0,0,.35); 609 transform-style: preserve-3d; transform-origin: left center; will-change: transform; } 610 .door .label{ position:absolute; left:14px; bottom:12px; font: 700 11px/1.2 ui-sans-serif,system-ui,Segoe UI,Inter,Arial; letter-spacing:.18em; text-transform:uppercase; color:#9ccff4; text-shadow:0 1px 0 rgba(0,0,0,.6) } 611 .door::before{ /* a frame */ 612 content:""; position:absolute; inset:0; border-radius:10px; pointer-events:none; 613 box-shadow: inset 0 0 0 2px rgba(121,208,255,.25); 614 } 615 .doorA{ left:16%; transform: translateZ(120px) rotateY(var(--openA, 0deg)); } 616 .doorB{ right:16%; transform: translateZ(420px) rotateY(var(--openB, 0deg)); transform-origin: right center; } 617 .doorB::before{ box-shadow: inset 0 0 0 2px rgba(121,208,255,.18); } 618 619 /* Optional silhouettes appearing deeper in the office */ 620 .silhouette{ position:absolute; bottom:14%; width:100px; height:140px; background: linear-gradient(#9ccff4,#2e6c8f); filter: blur(1px) grayscale(1); opacity:.0; border-radius:18px; box-shadow:0 6px 18px rgba(0,0,0,.35) } 621 .silhouette.one{ left:20%; transform: translateZ(520px) } 622 .silhouette.two{ right:24%; transform: translateZ(820px) } 623 624 /* Accessibility fallback label (hidden by default; JS toggles if WebGL/cpu issues) */ 625 .office-fallback{ position:absolute; right:14px; bottom:14px; z-index:3; color:#cdd9e6; font:600 11px/1 Inter,system-ui; letter-spacing:.14em; text-transform:uppercase; background:rgba(0,0,0,.4); border:1px solid rgba(255,255,255,.12); padding:.4rem .55rem; border-radius:10px; display:none } 626 .office-fallback.show{ display:block } 627 628 /* reduce motion: keep everything static */ 629 @media (prefers-reduced-motion: reduce){ 630 .office-track, .door{ transition:none !important; } 631 } 632</style><style id="dots3d-fx-styles"> 633 #features > .container{ position:relative; z-index: 3; } 634 .dots3d-layer{ position:absolute; inset:0; z-index:1; pointer-events:none; contain:layout paint; } 635 636 /* 50/50 split (left white / right black) */ 637 .dots3d-split-bg{ position:absolute; inset:0; z-index:0; 638 background: linear-gradient(90deg, #ffffff 0 50%, #000000 50% 100%); 639 filter: grayscale(1) contrast(1.08); 640 } 641 642 /* Blue/white + blue/black side rails */ 643 .dots3d-rails{ position:absolute; inset:0; z-index:1; pointer-events:none; } 644 .dots3d-rails::before, .dots3d-rails::after{ content:""; position:absolute; top:6svh; bottom:6svh; width:72px; } 645 .dots3d-rails::before{ left:0; background: 646 radial-gradient(6px 6px at 14px 16px, rgba(255,255,255,.95), rgba(255,255,255,0) 60%), 647 radial-gradient(6px 6px at 34px 34px, rgba(86,190,255,.95), rgba(86,190,255,0) 60%), 648 linear-gradient(180deg, rgba(0,0,0,.35), rgba(0,0,0,.42)); 649 background-size: 48px 48px, 48px 48px, 100% 100%; 650 opacity:.95; mix-blend-mode: screen; 651 } 652 .dots3d-rails::after{ right:0; background: 653 radial-gradient(6px 6px at 18px 18px, rgba(0,0,0,.95), rgba(0,0,0,0) 60%), 654 radial-gradient(6px 6px at 40px 40px, rgba(86,190,255,.95), rgba(86,190,255,0) 60%), 655 linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.12)); 656 background-size: 48px 48px, 48px 48px, 100% 100%; 657 opacity:.9; 658 } 659 :root{ --railShift: 0px; } 660 .dots3d-rails::before{ background-position: calc(var(--railShift)* .6) calc(var(--railShift)* .9), calc(var(--railShift)* .3) calc(var(--railShift)* .6), 0 0; } 661 .dots3d-rails::after { background-position: calc(var(--railShift)*-.5) calc(var(--railShift)*-.8), calc(var(--railShift)*-.2) calc(var(--railShift)*-.5), 0 0; } 662 663 /* Canvas + fade */ 664 canvas.dots3d-canvas{ position:absolute; inset:0; z-index:1; width:100%; height:100%; display:block; pointer-events:auto; } 665 .dots3d-fade-black{ position:absolute; inset:0; z-index:2; background:#000; opacity:0; transition: opacity .18s linear; } 666 667 /* Keep your existing vignette if you like */ 668 .dots3d-vignette{ position:absolute; inset:-10%; z-index:2; pointer-events:none; background: 669 radial-gradient(1400px 900px at 60% 45%, rgba(255,255,255,.05), transparent 60%), 670 radial-gradient(100% 100%, rgba(0,0,0,0), rgba(0,0,0,.72)); } 671</style><style id="dock-hotfix"> 672 /* Keep dock text visible regardless of .in */ 673 #textDock { color: var(--ink, #eaf6ff); } 674 #textDock .kicker, #textDock h2, #textDock .lead, #textDock .bullets li { 675 opacity: 1 !important; transform: none !important; 676 } 677 /* Caret */ 678 .tw-caret{display:inline-block;width:1px;height:1em;background:currentColor;margin-left:1px;vertical-align:-0.1em;animation:twblink .9s steps(2,start) infinite} 679 @keyframes twblink{50%{opacity:0}
679} 680</style><style> 681 /* ===== Force 3-column grids ===== 682 Targets: 683 - .features-list (your feature cards) 684 - .blog-list (your blog cards) 685 - .quicklink-list (footer columns) 686 Uses !important so it wins over prior media queries. 687 */ 688 .force-3cols .features-list, 689 .force-3cols .blog-list, 690 .force-3cols .quicklink-list { 691 display: grid !important; 692 grid-template-columns: repeat(3, minmax(0, 1fr)) !important; 693 gap: 25px !important; 694 } 695 696 /* Make feature cards equal height nicely */ 697 .force-3cols .features-list > li { display: flex; } 698 .force-3cols .features-card { 699 display: flex; flex-direction: column; width: 100%; 700 } 701 .force-3cols .features-card .card-text { flex: 1; } 702 703 /* Footer quick links: ensure children lay out cleanly as grid items */ 704 .force-3cols .quicklink-list > * { 705 min-width: 0; /* prevent overflow */ 706 } 707 708 /* Responsive fallbacks (optional but recommended) */ 709 @media (max-width: 900px) { 710 .force-3cols .features-list, 711 .force-3cols .blog-list, 712 .force-3cols .quicklink-list { 713 grid-template-columns: repeat(2, minmax(0, 1fr)) !important; 714 } 715 } 716 @media (max-width: 600px) { 717 .force-3cols .features-list, 718 .force-3cols .blog-list, 719 .force-3cols .quicklink-list { 720 grid-template-columns: 1fr !important; 721 } 722 } 723</style><style> 724 /* ===== FORCE 3 COLUMNS (no breakpoints) ===== */ 725 .force-3c .features-list, 726 .force-3c .service-content .service-list, 727 .force-3c .blog-list, 728 .force-3c .quicklink-list { 729 display: grid !important; 730 grid-template-columns: repeat(3, minmax(0, 1fr)) !important; 731 gap: 25px !important; 732 } 733 734 /* Make grid items expand correctly */ 735 .force-3c .features-list > li, 736 .force-3c .blog-list > li, 737 .force-3c .service-content .service-list > li, 738 .force-3c .service-content .service-list > form { 739 width: 100% !important; 740 } 741 742 /* Kill row-stacking margins that fight grid */ 743 .force-3c .service-item:not(:last-child) { margin-bottom: 0 !important; } 744 745 /* Ensure inner cards/buttons fill their grid cell */ 746 .force-3c .features-card, 747 .force-3c .blog-card, 748 .force-3c .service-content .service-list > form .service-item { 749 width: 100%; 750 } 751 752 /* If your services use <form class="service-item"><button class="service-item">â¦</button></form> */ 753 .force-3c .service-content .service-list > form.service-item { display: block !important; } 754 .force-3c .service-content .service-list > form.service-item > button.service-item { 755 width: 100%; display: flex; 756 } 757 758 /* OPTIONAL 759 If by "make it columns" you actually mean the LEFT RAIL list (#railList), 760 uncomment this block to render it in 3 columns instead of a vertical stack. 761 */ 762 /* 763 .force-3c #railList { 764 display: grid !important; 765 grid-auto-flow: column !important; 766 grid-auto-columns: 1fr !important; 767 grid-template-rows: repeat(3, auto) !important; 768 gap: 8px 14px !important; 769 height: auto !important; 770 transform: none !important; 771 } 772 .force-3c .railcol { overflow: visible !important; } 773 */ 774</style><style> 775 /* gatai-ai.css clean editorial, high-trust, responsive */ 776 777.gatai-page{ 778 --bg: #07080b; 779 --surface: rgba(255,255,255,.06); 780 --surface2: rgba(255,255,255,.08); 781 --text: rgba(255,255,255,.92); 782 --muted: rgba(255,255,255,.68); 783 --hair: rgba(255,255,255,.14); 784 --accent: #7fd8ff; 785 --warm: #ffb36a; 786 787 color: var(--text); 788 background: transparent; 789 position: relative; 790 isolation: isolate; 791} 792 793.gatai-shell{ 794 width: min(1120px, calc(100% - 40px)); 795 margin: 0 auto; 796} 797 798.gatai-hero{ 799 position: relative; 800 padding: clamp(48px, 6vw, 88px) 0 40px; 801} 802 803.gatai-hero::before{ 804 content:""; 805 position:absolute; inset:-1px 0 0 0; 806 pointer-events:none; 807 background: 808 radial-gradient(900px 520px at 12% 20%, rgba(127,216,255,.16), transparent 60%), 809 radial-gradient(760px 520px at 80% 5%, rgba(182,167,255,.12), transparent 55%), 810 linear-gradient(180deg, rgba(255,255,255,.06), transparent 35%), 811 repeating-linear-gradient(90deg, rgba(255,255,255,.03) 0 1px, transparent 1px 14px); 812 mask-image: linear-gradient(180deg, #000 0%, #000 65%, transparent 100%); 813 opacity: .85; 814} 815 816.gatai-hero__grid{ 817 display: grid; 818 grid-template-columns: 1.4fr .75fr; 819 gap: 26px; 820 align-items: start; 821} 822 823@media (max-width: 980px){ 824 .gatai-hero__grid{ grid-template-columns: 1fr; } 825} 826 827.gatai-eyebrow{ 828 margin: 0 0 10px; 829 font-size: 13px; 830 letter-spacing: .12em; 831 text-transform: uppercase; 832 color: var(--muted); 833} 834 835.gatai-h1{ 836 margin: 0 0 14px; 837 font-size: clamp(34px, 4.2vw, 56px); 838 line-height: 1.04; 839 letter-spacing: -0.02em; 840} 841 842.gatai-lead{ 843 margin: 0 0 18px; 844 font-size: 16px; 845 line-height: 1.6; 846 color: var(--muted); 847 max-width: 62ch; 848} 849 850.gatai-hero__actions{ 851 display:flex; 852 gap: 12px; 853 flex-wrap: wrap; 854 margin: 18px 0 22px; 855} 856 857.gatai-btn{ 858 display:inline-flex; 859 align-items:center; 860 justify-content:center; 861 gap: 10px; 862 padding: 12px 16px; 863 border-radius: 999px; 864 text-decoration:none; 865 font-weight: 600; 866 letter-spacing: .01em; 867 border: 1px solid var(--hair); 868 transition: transform .18s ease, background .18s ease, border-color .18s ease, opacity .18s ease; 869 user-select: none; 870} 871 872.gatai-btn--primary{ 873 background: linear-gradient(180deg, rgba(127,216,255,.18), rgba(127,216,255,.08)); 874 border-color: rgba(127,216,255,.28); 875 color: var(--text); 876} 877 878.gatai-btn--ghost{ 879 background: rgba(255,255,255,.04); 880 color: var(--text); 881} 882 883.gatai-btn--ghostInvert{ 884 background: rgba(255,255,255,.08); 885 border-color: rgba(255,255,255,.20); 886} 887 888.gatai-btn:hover{ transform: translateY(-1px); } 889.gatai-btn:active{ transform: translateY(0); opacity: .9; } 890 891.gatai-kpis{ 892 display:grid; 893 grid-template-columns: repeat(3, minmax(0, 1fr)); 894 gap: 10px; 895 max-width: 620px; 896} 897 898@media (max-width: 620px){ 899 .gatai-kpis{ grid-template-columns: 1fr; } 900} 901 902.gatai-kpi{ 903 border: 1px solid var(--hair); 904 background: rgba(255,255,255,.04); 905 border-radius: 14px; 906 padding: 12px 14px; 907} 908 909.gatai-kpi__label{ 910 display:block; 911 font-size: 12px; 912 letter-spacing: .10em; 913 text-transform: uppercase; 914 color: var(--muted); 915} 916 917.gatai-kpi__value{ 918 display:block; 919 font-size: 14px; 920 margin-top: 6px; 921} 922 923.gatai-toc{ 924 position: sticky; 925 top: 18px; 926 align-self: start; 927} 928 929@media (max-width: 980px){ 930 .gatai-toc{ display:none; } 931} 932 933.gatai-toc__card{ 934 border: 1px solid var(--hair); 935 background: rgba(0,0,0,.18); 936 backdrop-filter: blur(8px); 937 border-radius: 16px; 938 padding: 14px; 939} 940 941.gatai-toc__title{ 942 margin: 0 0 10px; 943 font-size: 12px; 944 letter-spacing: .12em; 945 text-transform: uppercase; 946 color: var(--muted); 947} 948 949.gatai-toc__link{ 950 display:block; 951 padding: 9px 10px; 952 border-radius: 10px; 953 text-decoration: none; 954 color: var(--muted); 955 border: 1px solid transparent; 956 transition: background .15s ease, border-color .15s ease, color .15s ease; 957} 958 959.gatai-toc__link:hover{ 960 background: rgba(255,255,255,.06); 961 color: var(--text); 962} 963 964.gatai-toc__link.is-active{ 965 background: rgba(127,216,255,.10); 966 border-color: rgba(127,216,255,.22); 967 color: var(--text); 968} 969 970.gatai-main{ padding: 6px 0 0; } 971 972.gatai-band{ 973 padding: clamp(46px, 5vw, 78px) 0; 974 border-top: 1px solid rgba(255,255,255,.08); 975} 976 977.gatai-band--light{ 978 background: transparent; 979} 980 981.gatai-band--alt{ 982 background: rgba(255,255,255,.04); 983} 984 985.gatai-band__grid{ 986 display:grid; 987 grid-template-columns: .95fr 1.15fr; 988 gap: 28px; 989 align-items: start; 990} 991 992@media (max-width: 920px){ 993 .gatai-band__grid{ grid-template-columns: 1fr; } 994} 995 996.gatai-h2{ 997 margin: 0 0 10px; 998 font-size: clamp(22px, 2.4vw, 30px); 999 letter-spacing: -0.01em; 1000} 1001 1002.gatai-h2--invert{ color: rgba(255,255,255,.96); } 1003 1004.gatai-sub{ 1005 margin: 0; 1006 color: var(--muted); 1007 line-height: 1.65; 1008 max-width: 60ch; 1009} 1010 1011.gatai-band__body p{ 1012 margin: 0 0 14px; 1013 line-height: 1.75; 1014 color: rgba(255,255,255,.84); 1015} 1016 1017.gatai-link{ 1018 display:inline-flex; 1019 margin-top: 6px; 1020 color: rgba(127,216,255,.92); 1021 text-decoration: none; 1022 border-bottom: 1px solid rgba(127,216,255,.26); 1023 padding-bottom: 2px; 1024} 1025 1026.gatai-link:hover{ border-bottom-color: rgba(127,216,255,.48); } 1027 1028.gatai-callout{ 1029 margin-top: 16px; 1030 border: 1px solid rgba(127,216,255,.18); 1031 background: rgba(127,216,255,.06); 1032 border-radius: 16px; 1033 padding: 14px 16px; 1034} 1035 1036.gatai-callout__title{ 1037 margin: 0 0 6px; 1038 font-size: 12px; 1039 letter-spacing: .12em; 1040 text-transform: uppercase; 1041 color: rgba(255,255,255,.72); 1042} 1043 1044.gatai-callout__text{ 1045 margin: 0; 1046 color: rgba(255,255,255,.86); 1047 line-height: 1.6; 1048} 1049 1050.gatai-cards{ 1051 display:grid; 1052 grid-template-columns: repeat(2, minmax(0, 1fr)); 1053 gap: 12px; 1054} 1055 1056.gatai-cards--two{ grid-template-columns: repeat(2, minmax(0, 1fr)); } 1057 1058@media (max-width: 740px){ 1059 .gatai-cards{ grid-template-columns: 1fr; } 1060 .gatai-cards--two{ grid-template-columns: 1fr; } 1061} 1062 1063.gatai-card{ 1064 border: 1px solid var(--hair); 1065 background: rgba(255,255,255,.04); 1066 border-radius: 16px; 1067 padding: 14px 14px 16px; 1068} 1069 1070.gatai-h3{ 1071 margin: 0 0 8px; 1072 font-size: 16px; 1073 letter-spacing: -0.01em; 1074} 1075 1076.gatai-card p{ 1077 margin: 0; 1078 color: var(--muted); 1079 line-height: 1.65; 1080} 1081 1082.gatai-list{ 1083 list-style: none; 1084 padding: 0; 1085 margin: 0; 1086 display: grid; 1087 gap: 10px; 1088} 1089 1090.gatai-list li{ 1091 position: relative; 1092 padding: 12px 12px 12px 14px; 1093 border: 1px solid var(--hair); 1094 background: rgba(255,255,255,.04); 1095 border-radius: 14px; 1096 color: rgba(255,255,255,.84); 1097 line-height: 1.65; 1098} 1099 1100.gatai-pillgrid{ 1101 display:grid; 1102 grid-template-columns: repeat(2, minmax(0, 1fr)); 1103 gap: 10px; 1104} 1105 1106@media (max-width: 740px){ 1107 .gatai-pillgrid{ grid-template-columns: 1fr; } 1108} 1109 1110.gatai-pill{ 1111 border: 1px solid var(--hair); 1112 background: rgba(255,255,255,.04); 1113 border-radius: 999px; 1114 padding: 12px 14px; 1115 color: rgba(255,255,255,.84); 1116 line-height: 1.55; 1117} 1118 1119.gatai-tags{ 1120 display:flex; 1121 flex-wrap: wrap; 1122 gap: 10px; 1123} 1124 1125.gatai-tag{ 1126 display:inline-flex; 1127 padding: 10px 12px; 1128 border-radius: 999px; 1129 border: 1px solid var(--hair); 1130 background: rgba(255,255,255,.04); 1131 color: rgba(255,255,255,.82); 1132} 1133 1134.gatai-note{ 1135 margin-top: 14px; 1136 color: var(--muted); 1137} 1138 1139.gatai-footer{ 1140 padding: clamp(52px, 6vw, 88px) 0; 1141 background: rgba(0,0,0,.55); 1142 border-top: 1px solid rgba(255,255,255,.10); 1143} 1144 1145.gatai-footer__grid{ 1146 display:grid; 1147 grid-template-columns: 1.2fr .8fr; 1148 gap: 18px; 1149 align-items: center; 1150} 1151 1152@media (max-width: 900px){ 1153 .gatai-footer__grid{ grid-template-columns: 1fr; } 1154} 1155 1156.gatai-footer__text{ 1157 margin: 0; 1158 color: rgba(255,255,255,.78); 1159 line-height: 1.7; 1160 max-width: 70ch; 1161} 1162 1163.gatai-footer__actions{ 1164 display:flex; 1165 justify-content: flex-end; 1166 gap: 12px; 1167 flex-wrap: wrap; 1168} 1169 1170@media (max-width: 900px){ 1171 .gatai-footer__actions{ justify-content: flex-start; } 1172} 1173 1174/* Reveal motion */ 1175.gatai-reveal{ 1176 opacity: 0; 1177 transform: translateY(10px); 1178 transition: opacity .55s ease, transform .55s ease; 1179 will-change: opacity, transform; 1180} 1181 1182.gatai-reveal.is-visible{ 1183 opacity: 1; 1184 transform: translateY(0); 1185} 1186 1187@media (prefers-reduced-motion: reduce){ 1188 .gatai-reveal{ opacity: 1; transform: none; transition: none; } 1189 .gatai-btn{ transition: none; } 1190} 1191 1192/* ===== GATAI CLIP SYSTEM (paste at end) ===== */ 1193 1194:root{ 1195 --clip-main: polygon( 1196 6% 0, 1197 100% 0, 1198 100% 80%, 1199 92% 100%, 1200 0 100%, 1201 0 16% 1202 ); 1203 1204 /* tweak sizes safely */ 1205 --clip-border: rgba(255,255,255,.14); 1206 --clip-bg: rgba(255,255,255,.04); 1207 --clip-bg-strong: rgba(255,255,255,.07); 1208} 1209 1210/* Use clip-path only if supported */ 1211@supports (clip-path: polygon(0 0)) { 1212 1213 /* Apply to most âcomponentsâ */ 1214 .gatai-card, 1215 .gatai-band, 1216 .gatai-panel, 1217 .gatai-pill, 1218 .gatai-chip, 1219 .gatai-btn, 1220 .gatai-toc, 1221 .gatai-toc__link, 1222 .gatai-metric, 1223 .gatai-quote, 1224 .gatai-callout, 1225 [data-clip]{ 1226 clip-path: var(--clip-main); 1227 border-radius: 0 !important; 1228 } 1229 1230 /* Border using pseudo-element (keeps your existing background) */ 1231 .gatai-card, 1232 .gatai-band, 1233 .gatai-panel, 1234 .gatai-callout, 1235 [data-clip]{ 1236 position: relative; 1237 background: var(--clip-bg); 1238 } 1239 1240 .gatai-card::before, 1241 .gatai-band::before, 1242 .gatai-panel::before, 1243 .gatai-callout::before, 1244 [data-clip]::before{ 1245 content:""; 1246 position:absolute; 1247 inset:0; 1248 pointer-events:none; 1249 clip-path: var(--clip-main); 1250 border: 1px solid var(--clip-border); 1251 opacity: 1; 1252 } 1253 1254 /* Slight hover lift without killing your style */ 1255 .gatai-card:hover, 1256 [data-clip]:hover{ 1257 background: var(--clip-bg-strong); 1258 } 1259} 1260 1261/* ===== Reveal animation (SAFE) ===== 1262 IMPORTANT: Content is visible by default. 1263 It only animates when .js exists. */ 1264 1265.fx-reveal{ opacity: 1; transform: none; } 1266 1267.js .fx-reveal{ 1268 opacity: 0; 1269 transform: translate3d(0, 14px, 0); 1270 transition: opacity .55s ease, transform .55s ease; 1271 will-change: opacity, transform; 1272} 1273 1274.js .fx-reveal.is-in{ 1275 opacity: 1; 1276 transform: translate3d(0, 0, 0); 1277} 1278 1279/* ===== Hover spotlight (works with JS vars --mx/--my) ===== */ 1280.fx-card{ position: relative; transform-style: preserve-3d; } 1281.fx-card::after{ 1282 content:""; 1283 position:absolute; 1284 inset:-1px; 1285 pointer-events:none; 1286 clip-path: var(--clip-main); 1287 opacity: 0; 1288 transition: opacity .18s ease; 1289 background: radial-gradient(
1290 520px circle at var(--mx, 50%) var(--my, 50%), 1291 rgba(120,245,255,.18), 1292 rgba(120,245,255,0) 60% 1293 ); 1294 mix-blend-mode: screen; 1295} 1296.fx-card.is-hover::after{ opacity: 1; } 1297 1298/* ===== TOC active ===== */ 1299.gatai-toc__link.is-active{ 1300 background: rgba(255,255,255,.06); 1301} 1302:root{ 1303 --gatai-clip: polygon(6% 0, 100% 0, 100% 80%, 92% 100%, 0 100%, 0 16%); 1304} 1305 1306.gatai-clip{ 1307 -webkit-clip-path: var(--gatai-clip); 1308 clip-path: var(--gatai-clip); 1309} 1310 1311.gatai-hero__visual{ min-height: 380px; } 1312@media (max-width: 960px){ 1313 .gatai-hero__visual{ min-height: 320px; } 1314} 1315 1316.gatai-scene{ 1317 position: relative; 1318 border: 1px solid rgba(255,255,255,.10); 1319 background: radial-gradient(1200px 600px at 20% 20%, rgba(255,255,255,.08), rgba(255,255,255,0)); 1320 overflow: hidden; 1321 padding: 18px; 1322} 1323 1324.gatai-scene__topline{ 1325 height: 1px; 1326 background: rgba(255,255,255,.12); 1327 margin-bottom: 14px; 1328} 1329 1330.gatai-scene__svg{ 1331 width: 100%; 1332 height: auto; 1333 display: block; 1334} 1335 1336.gatai-scene__grid path{ 1337 fill: none; 1338 stroke: rgba(255,255,255,.08); 1339 stroke-width: 1; 1340} 1341 1342.gatai-scene__path{ 1343 fill: none; 1344 stroke: rgba(255,255,255,.26); 1345 stroke-width: 2; 1346 stroke-linecap: round; 1347} 1348 1349.gatai-scene__node{ 1350 fill: rgba(255,255,255,.9); 1351 opacity: .9; 1352} 1353 1354.gatai-scene__ring{ 1355 fill: none; 1356 stroke: rgba(255,255,255,.22); 1357 stroke-width: 2; 1358 opacity: .9; 1359 transform-origin: 510px 240px; 1360} 1361 1362.gatai-scene__hudBg{ 1363 fill: rgba(255,255,255,.08); 1364 stroke: rgba(255,255,255,.10); 1365} 1366 1367.gatai-scene__hudFill{ 1368 fill: rgba(255,255,255,.65); 1369 opacity: .85; 1370 transform-origin: left center; 1371} 1372 1373.gatai-chip{ 1374 border: 1px solid rgba(255,255,255,.12); 1375 background: rgba(255,255,255,.06); 1376 padding: 8px 10px; 1377 font-size: 12px; 1378 letter-spacing: .2px; 1379 user-select: none; 1380} 1381 1382.gatai-reveal{ opacity: 1; transform: none; } 1383.gatai-reveal.is-in{ 1384 /* Optional: if you already use reveal transitions, keep yours. 1385 This class is used by the JS fallback too. */ 1386} 1387 1388[data-hovercard]{ 1389 transition: transform .18s ease, border-color .18s ease, background .18s ease; 1390 will-change: transform; 1391} 1392[data-hovercard]:hover{ 1393 transform: translateY(-4px); 1394 border-color: rgba(255,255,255,.22); 1395} 1396/* polygon clip you asked for */ 1397.gatai-clip{ 1398 clip-path: polygon(6% 0, 100% 0, 100% 80%, 92% 100%, 0 100%, 0 16%); 1399} 1400 1401/* scene wrapper */ 1402.gatai-scene--atlas{ 1403 position: relative; 1404 isolation: isolate; 1405 overflow: hidden; 1406} 1407 1408.gatai-scene--atlas .gatai-scene__svg{ 1409 width: 100%; 1410 height: auto; 1411 display: block; 1412} 1413 1414/* border / topline */ 1415.gatai-scene--atlas .gatai-scene__border{ 1416 fill: none; 1417 stroke: rgba(255,255,255,.22); 1418 stroke-width: 1; 1419} 1420 1421.gatai-scene--atlas .gatai-scene__topline{ 1422 position: absolute; 1423 top: 0; 1424 left: 0; 1425 right: 0; 1426 height: 1px; 1427 background: rgba(255,255,255,.18); 1428 pointer-events: none; 1429} 1430 1431/* blueprint grid */ 1432.gatai-scene--atlas .gatai-scene__grid path{ 1433 fill: none; 1434 stroke: rgba(255,255,255,.08); 1435 stroke-width: 1; 1436} 1437 1438/* dots */ 1439.gatai-scene--atlas .gatai-scene__dots{ 1440 opacity: .55; 1441} 1442 1443/* traces */ 1444.gatai-scene--atlas .gatai-scene__trace{ 1445 fill: none; 1446 stroke: rgba(255,255,255,.34); 1447 stroke-width: 2.25; 1448 stroke-linecap: round; 1449 stroke-linejoin: round; 1450 opacity: .92; 1451} 1452 1453/* nodes / vias */ 1454.gatai-scene--atlas .gatai-scene__node{ 1455 fill: rgba(255,255,255,.92); 1456 opacity: .85; 1457} 1458.gatai-scene--atlas .gatai-scene__via{ 1459 fill: rgba(255,255,255,.28); 1460 opacity: .8; 1461} 1462 1463/* modules */ 1464.gatai-scene--atlas .gatai-scene__moduleBody, 1465.gatai-scene--atlas .gatai-scene__hubBody, 1466.gatai-scene--atlas .gatai-scene__laneBody, 1467.gatai-scene--atlas .gatai-scene__panel{ 1468 fill: rgba(255,255,255,.06); 1469 stroke: rgba(255,255,255,.18); 1470 stroke-width: 1; 1471} 1472 1473.gatai-scene--atlas .gatai-scene__moduleInk, 1474.gatai-scene--atlas .gatai-scene__hubInk, 1475.gatai-scene--atlas .gatai-scene__laneInk{ 1476 fill: rgba(255,255,255,.22); 1477 opacity: .85; 1478} 1479 1480.gatai-scene--atlas .gatai-scene__micro, 1481.gatai-scene--atlas .gatai-scene__chiplet, 1482.gatai-scene--atlas .gatai-scene__tile{ 1483 fill: rgba(255,255,255,.08); 1484 stroke: rgba(255,255,255,.16); 1485 stroke-width: 1; 1486} 1487 1488/* orbits */ 1489.gatai-scene--atlas .gatai-scene__orbit{ 1490 fill: none; 1491 stroke: rgba(255,255,255,.12); 1492 stroke-width: 1.25; 1493 opacity: .9; 1494} 1495.gatai-scene--atlas .gatai-scene__sat{ 1496 fill: rgba(255,255,255,.80); 1497 opacity: .5; 1498} 1499 1500/* beam */ 1501.gatai-scene--atlas .gatai-scene__beam{ 1502 opacity: .45; 1503 mix-blend-mode: screen; 1504} 1505 1506/* HUD */ 1507.gatai-scene--atlas .gatai-scene__hudBg{ 1508 fill: rgba(255,255,255,.06); 1509 stroke: rgba(255,255,255,.16); 1510 stroke-width: 1; 1511} 1512.gatai-scene--atlas .gatai-scene__hudFill{ 1513 fill: rgba(255,255,255,.40); 1514 opacity: .85; 1515} 1516 1517/* hover states */ 1518.gatai-scene--atlas.is-hover .gatai-scene__trace{ stroke: rgba(255,255,255,.48); } 1519.gatai-scene--atlas.is-hover .gatai-scene__beam{ opacity: .70; } 1520 1521.gatai-scene--atlas [data-comp]{ 1522 transform-box: fill-box; 1523 transform-origin: center; 1524} 1525.gatai-scene--atlas .is-compHover{ 1526 filter: drop-shadow(0 10px 24px rgba(0,0,0,.28)); 1527} 1528 1529 1530.gatai-chip{ 1531 padding: 10px 12px; 1532 border: 1px solid rgba(255,255,255,.20); 1533 background: rgba(255,255,255,.06); 1534 color: rgba(255,255,255,.92); 1535 font-size: 12px; 1536 letter-spacing: .08em; 1537 text-transform: uppercase; 1538} 1539 1540</style><link rel="stylesheet" href="/assets/dist/assets/fonts-yGxpeqis.css"><link rel="stylesheet" href="/assets/dist/assets/common-CNCWxLWF.css"><link rel="preload" href="/assets/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2" as="font" type="font/woff2" crossorigin>
1540<script type="module" src="/assets/dist/assets/common-Cnvms9UH.js"></script>
1540</head> 1541<body class="site-interior"><a class="skip-link" href="#main-content">Skip to content</a> 1542<header class="header header-mobile fixed top-0 left-0 w-full z-50 border-collapse bg-white/10 backdrop-blur-lg border border-white/20 rounded-xl shadow-lg" 1543 data-header-mobile> 1544 <div class="container mobile-header-inner"> 1545 <!-- Logo --> 1546 <a class="logo" href="/"> 1547 <img alt="GATAI" decoding="async" height="281" src="/assets/images/ourlogo-06.png" width="887"/> 1548 </a> 1549<button 1550 class="action-link" 1551 type="button" 1552 aria-label="Accessibility menu" 1553 aria-haspopup="dialog" 1554 aria-expanded="false" 1555 data-a11y-toggle 1556> 1557 <ion-icon name="accessibility-outline"></ion-icon> 1558</button> 1559 1560 <!-- Right side: CTA + burger --> 1561 <div class="mobile-header-actions"> 1562 <a class="btn btn-secondary header-cta" href="/booking"> 1563 Book My AI Strategy Call 1564 <ion-icon name="chevron-forward-outline" aria-hidden="true"></ion-icon> 1565 </a> 1566 1567 <button class="nav-toggle-btn" 1568 type="button" 1569 aria-label="Toggle navigation" 1570 aria-expanded="false" 1571 data-nav-toggle-mobile> 1572 <ion-icon class="open" name="menu-outline" aria-hidden="true"></ion-icon> 1573 <ion-icon class="close" name="close-outline" aria-hidden="true"></ion-icon> 1574 </button> 1575 </div> 1576 </div> 1577 1578 <!-- Slide-down nav --> 1579<nav class="navbar navbar-mobile" aria-label="Mobile primary navigation"> 1580 1581 <ul> 1582 <li><a style="color: #eaf6ff;" href="/">Home</a></li> 1583 <li><a style="color: #eaf6ff;" href="/features">Features</a></li> 1584 <li><a style="color: #eaf6ff;" href="/service">Service</a></li> 1585 <li><a style="color: #eaf6ff;" href="/blog">Blog</a></li> 1586 <li><a style="color: #eaf6ff;" href="/intelligent-software-solutions">ISS</a></li> 1587 <li><a style="color: #eaf6ff;" href="/booking">Booking</a></li> 1588 <li><a style="color: #eaf6ff;" href="/booking/agents">Agents</a></li> 1589 <li><a style="color: #eaf6ff;" href="/booking/business">Business</a></li> 1590 <li><a style="color: #eaf6ff;" href="/booking/build-ai">Build AI</a></li> 1591 </ul> 1592 </nav> 1593</header>
1594<script type="module" src="/assets/dist/assets/js-mobile-nav-kC9IZ17W.js"></script>
1594 1595 1596<header class="header header--left three-cols" data-header> 1597 <div class="header__inner"> 1598 <!-- Row 1: NAV --> <div class="timelinecol"> 1599 <div class="tl" aria-hidden="true"> 1600 <div class="tl__track"></div> 1601 <div class="tl__fill"></div> 1602 <div class="tl__dot"></div> 1603 </div> 1604 </div> 1605 <div class="row--nav"> 1606 1607 1608 <nav class="navbar" aria-label="Primary"> 1609 <ul class="navbar-list nav-icons"> 1610 <li> 1611 <a class="navbar-link is-home" href="/" title="Home"> 1612 <ion-icon name="home-outline" aria-hidden="true"></ion-icon> 1613 <span class="label">Home</span> 1614 </a> 1615 </li> 1616 1617 <li> 1618 <a class="navbar-link" href="/features" title="Features"> 1619 <ion-icon name="apps-outline" aria-hidden="true"></ion-icon> 1620 <span class="label">Features</span> 1621 </a> 1622 </li> 1623 1624 <li> 1625 <a class="navbar-link" href="/service" title="Service"> 1626 <ion-icon name="briefcase-outline" aria-hidden="true"></ion-icon> 1627 <span class="label">Service</span> 1628 </a> 1629 </li> 1630 1631 <li> 1632 <a class="navbar-link" href="/blog" title="Blog"> 1633 <ion-icon name="newspaper-outline" aria-hidden="true"></ion-icon> 1634 <span class="label">Blog</span> 1635 </a> 1636 </li> 1637 1638 <li> 1639 <a class="navbar-link" href="/intelligent-software-solutions" title="ISS"> 1640 <ion-icon name="server-outline" aria-hidden="true"></ion-icon> 1641 <span class="label">ISS</span> 1642 </a> 1643 </li> 1644 1645 <li> 1646 <a class="navbar-link" href="/booking" title="Schedule a meeting"> 1647 <ion-icon name="calendar-outline" aria-hidden="true"></ion-icon> 1648 <span class="label">Booking</span> 1649 </a> 1650 </li> 1651 1652 </ul> 1653</nav> 1654<!-- Bottom quick actions --> 1655<!-- Bottom quick-actions nav (your code, A11Y turned into a button) --> 1656<nav class="navbar nav-bottom" aria-label="Quick actions"> 1657 <ul class="navbar-list nav-actions"> 1658 1659 <ul class="actions"> 1660 <li> 1661 <a class="action-link" 1662 href="/booking/agents" 1663 target="_blank" rel="noopener" 1664 aria-label="ai agents"> 1665 <ion-icon name="terminal-outline" aria-hidden="true"></ion-icon> 1666 <span class="label">AGENTS</span> 1667 </a> 1668 </li> 1669 1670 <li> 1671 <a class="action-link" 1672 href="/booking/business" 1673 target="_blank" rel="noopener" 1674 aria-label="ai business"> 1675 <ion-icon name="business-outline" aria-hidden="true"></ion-icon> 1676 <span class="label">BUSINESS</span> 1677 </a> 1678 </li> 1679</ul> 1680 1681 1682 <li> 1683 <!-- button instead of link so we can open the panel --> 1684<button 1685 class="action-link" 1686 type="button" 1687 aria-label="Accessibility menu" 1688 aria-haspopup="dialog" 1689 aria-expanded="false" 1690 data-a11y-toggle 1691> 1692 <ion-icon name="accessibility-outline"></ion-icon> 1693</button> 1694 1695 </li> 1696 1697 <li> 1698 <a class="action-link action-hot" href="/booking/build-ai" aria-label="Build a custom AI system"> 1699 <ion-icon name="barcode-outline" aria-hidden="true"></ion-icon> 1700 <span class="label">BUILD AI</span> 1701 </a> 1702 </li> 1703 </ul> 1704</nav> 1705 1706 1707 1708 1709 <button class="nav-toggle-btn" data-nav-toggle-btn aria-label="Toggle menu"> 1710 <ion-icon class="open" name="menu-outline"></ion-icon> 1711 <ion-icon class="close" name="close-outline"></ion-icon> 1712 </button> 1713 </div> 1714 1715 <div class="railcol"> 1716 <ol class="rail__list" id="railList" aria-label="Section navigator"></ol> 1717 </div> 1718 1719 1720 </div> 1721</header>
1722<script> 1723(() => { 1724 const path = location.pathname.replace(/\/+$/,'') || '/'; 1725 document.querySelectorAll('.nav-icons .navbar-link').forEach(a => { 1726 const href = a.getAttribute('href') || ''; 1727 const normalized = href.replace(/\/+$/,'') || '/'; 1728 if (normalized === path) a.setAttribute('aria-current', 'true'); 1729 }); 1730 1731 const sections = Array.from(document.querySelectorAll('section.scn')); 1732 if (!sections.length) return; 1733 if (sections.length){ 1734 const map = new Map([ 1735 ['/', document.querySelector('.nav-icons .navbar-link.is-home')], 1736 ['/features', document.querySelector('.nav-icons .navbar-link[title="Features"]')], 1737 ['/service', document.querySelector('.nav-icons .navbar-link[title="Service"]')], 1738 ['/blog', document.querySelector('.nav-icons .navbar-link[title="Blog"]')], 1739 ['/intelligent-software-solutions', document.querySelector('.nav-icons .navbar-link[title="ISS"]')], 1740 ['/booking', document.querySelector('.nav-icons .navbar-link[title="Schedule a meeting"]')], 1741 ]); 1742 1743 const setActiveBySection = (i) => { 1744 document.querySelectorAll('.nav-icons .navbar-link[aria-current]') 1745 .forEach(a => a.removeAttribute('aria-current')); 1746 if (i === 0) map.get('/')?.setAttribute('aria-current','true'); 1747 }; 1748 1749 let last = -1; 1750 const onScroll = () => { 1751 const vh = innerHeight || 1; 1752 let best = 0, bestOv = -1; 1753 for (let i=0;i<sections.length;i++){ 1754 const r = sections[i].getBoundingClientRect(); 1755 const ov = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top)); 1756 if (ov > bestOv){ bestOv = ov; best = i; } 1757 } 1758 if (best !== last){ last = best; setActiveBySection(best); } 1759 }; 1760 addEventListener('scroll', onScroll, {passive:true}); 1761 onScroll(); 1762 } 1763})(); 1764</script>
1764 1765 1766 1767 <!-- GATAI ABOUT / INNOVATION Page Section (PRO, clean, no inline styles) --> 1768<section class="gatai-page gatai-page--about" id="gatai-about"> 1769<section id="features" class="dots3d-wrap" data-diapo-root> 1770 <div class="diapo-panel"> 1771 <div class="diapo-content"> 1772 </div> 1773 </div> 1774 1775 <!-- HERO --> 1776 <header class="gatai-hero" aria-labelledby="gatai-hero-title"> 1777 <div class="gatai-shell"> 1778 <div class="gatai-hero__grid"> 1779 1780 <!-- Copy --> 1781 <div class="gatai-hero__copy"> 1782 <p class="gatai-eyebrow">GATAI Inc. ⢠Research ⢠Innovation ⢠Delivery</p> 1783 1784 <h1 class="gatai-h1" id="gatai-hero-title"> 1785 Engineering the next wave of AI and digital systems responsibly, at scale. 1786 </h1> 1787 1788 <p class="gatai-lead"> 1789 GATAI Inc. is an innovation-led technology company blending applied research, advanced engineering, 1790 and disciplined delivery. We help organizations adopt AI with confidence, modernize their digital stack, 1791 and move faster without sacrificing reliability or governance. 1792 </p> 1793 1794 <div class="gatai-hero__actions"> 1795 <a class="gatai-btn gatai-btn--primary" href="/booking">Talk to Our Team</a> 1796 <a class="gatai-btn gatai-btn--ghost" href="/case-studies">View Case Studies</a> 1797 </div> 1798 1799 <div class="gatai-kpis" role="list" aria-label="Company pillars"> 1800 <div class="gatai-kpi gatai-clip" role="listitem"> 1801 <span class="gatai-kpi__label">Innovation</span> 1802 <span class="gatai-kpi__value">Applied R&D</span> 1803 </div> 1804 <div class="gatai-kpi gatai-clip" role="listitem"> 1805 <span class="gatai-kpi__label">Execution</span> 1806 <span class="gatai-kpi__value">Production-grade</span> 1807 </div> 1808 <div class="gatai-kpi gatai-clip" role="listitem"> 1809 <span class="gatai-kpi__label">Trust</span> 1810 <span class="gatai-kpi__value">Governed AI</span> 1811 </div> 1812 </div> 1813 1814 </div> 1815 1816 <!-- Scene --> 1817 <!-- ABOUT â Hero Visual Scene --> 1818<div class="gatai-aboutHero__visual" aria-hidden="true"> 1819 <div class="gatai-scene gatai-scene--atlas gatai-clip" data-scene="about"> 1820 <div class="gatai-scene__topline"></div> 1821 1822 <svg class="gatai-scene__svg" viewBox="0 0 900 560" preserveAspectRatio="xMidYMid meet"> 1823 <defs> 1824 <!-- same polygon clip, converted to SVG coords --> 1825 <clipPath id="atlasClip"> 1826 <polygon points="54,0 900,0 900,448 828,560 0,560 0,90"></polygon> 1827 </clipPath> 1828 1829 <linearGradient id="atlasSheen" x1="0" y1="0" x2="1" y2="1"> 1830 <stop offset="0" stop-color="rgba(255,255,255,.10)"></stop> 1831 <stop offset=".55" stop-color="rgba(255,255,255,.04)"></stop> 1832 <stop offset="1" stop-color="rgba(255,255,255,.02)"></stop> 1833 </linearGradient> 1834 1835 <linearGradient id="atlasBeam" x1="0" y1="0" x2="1" y2="0"> 1836 <stop offset="0" stop-color="rgba(255,255,255,0)"></stop> 1837 <stop offset=".5" stop-color="rgba(255,255,255,.35)"></stop> 1838 <stop offset="1" stop-color="rgba(255,255,255,0)"></stop> 1839 </linearGradient> 1840 1841 <pattern id="atlasDots" width="22" height="22" patternUnits="userSpaceOnUse"> 1842 <circle cx="3" cy="3" r="1.25" fill="rgba(255,255,255,.14)"></circle> 1843 </pattern> 1844 </defs> 1845 1846 <g clip-path="url(#atlasClip)"> 1847 <!-- base --> 1848 <rect class="gatai-scene__board" x="18" y="14" width="864" height="532" rx="18" fill="url(#atlasSheen)"></rect> 1849 1850 <!-- dot grid --> 1851 <rect class="gatai-scene__dots" x="18" y="14" width="864" height="532" rx="18" fill="url(#atlasDots)"></rect> 1852 1853 <!-- subtle lines (blueprint feel) --> 1854 <g class="gatai-scene__grid"> 1855 <path d="M60 110 H840" /> 1856 <path d="M60 170 H840" /> 1857 <path d="M60 230 H840" /> 1858 <path d="M60 290 H840" /> 1859 <path d="M60 350 H840" /> 1860 <path d="M60 410 H840" /> 1861 <path d="M60 470 H840" /> 1862 1863 <path d="M120 70 V500" /> 1864 <path d="M220 70 V500" /> 1865 <path d="M320 70 V500" /> 1866 <path d="M420 70 V500" /> 1867 <path d="M520 70 V500" /> 1868 <path d="M620 70 V500" /> 1869 <path d="M720 70 V500" /> 1870 </g> 1871 1872 <!-- orbit ring (slow rotation on hover) --> 1873 <g class="gatai-scene__orbits"> 1874 <circle class="gatai-scene__orbit" cx="635" cy="210" r="92" /> 1875 <circle class="gatai-scene__orbit" cx="635" cy="210" r="56" /> 1876 <circle class="gatai-scene__sat" cx="727" cy="210" r="5" data-node /> 1877 <circle class="gatai-scene__sat" cx="635" cy="118" r="4" data-node /> 1878 <circle class="gatai-scene__sat" cx="585" cy="255" r="4" data-node /> 1879 </g> 1880 1881 <!-- modules (lots of components) --> 1882 <g class="gatai-scene__modules"> 1883 <!-- core (center-left) --> 1884 <g class="gatai-scene__module" data-comp> 1885 <rect class="gatai-scene__moduleBody" x="140" y="150" width="240" height="150" rx="18" /> 1886 <rect class="gatai-scene__moduleInk" x="162" y="174" width="140" height="10" rx="5" /> 1887 <rect class="gatai-scene__moduleInk" x="162" y="195" width="192" height="8" rx="4" /> 1888 <rect class="gatai-scene__moduleInk" x="162" y="214" width="168" height="8" rx="4" /> 1889 <!-- micro blocks --> 1890 <rect class="gatai-scene__micro" x="162" y="240" width="52" height="18" rx="6" /> 1891 <rect class="gatai-scene__micro" x="222" y="240" width="62" height="18" rx="6" /> 1892 <rect class="gatai-scene__micro" x="292" y="240" width="72" height="18" rx="6" /> 1893 <rect class="gatai-scene__micro" x="162" y="266" width="70" height="18" rx="6" /> 1894 <rect class="gatai-scene__micro" x="240" y="266" width="58" height="18" rx="6" /> 1895 <rect class="gatai-scene__micro" x="306" y="266" width="58" height="18" rx="6" /> 1896 </g> 1897 1898 <!-- right âinnovation hubâ --> 1899 <g class="gatai-scene__hub" data-comp> 1900 <rect class="gatai-scene__hubBody" x="520" y="130" width="300" height="170" rx="20" /> 1901 <rect class="gatai-scene__hubInk" x="548" y="154" width="170" height="10" rx="5" /> 1902 <rect class="gatai-scene__hubInk" x="548" y="176" width="230" height="8" rx="4" /> 1903 <rect class="gatai-scene__hubInk" x="548" y="196" width="190" height="8" rx="4" /> 1904 1905 <!-- component cluster --> 1906 <rect class="gatai-scene__chiplet" x="548" y="222" width="66" height="46" rx="12" /> 1907 <rect class="gatai-scene__chiplet" x="624" y="222" width="66" height="46" rx="12" /> 1908 <rect class="gatai-scene__chiplet" x="700" y="222" width="92" height="46" rx="12" /> 1909 <circle class="gatai-scene__node" cx="581" cy="245" r="5" data-node /> 1910 <circle class="gatai-scene__node" cx="657" cy="245" r="5" data-node /> 1911 <circle class="gatai-scene__node" cx="740" cy="245" r="5" data-node /> 1912 </g> 1913 1914 <!-- bottom âdelivery laneâ --> 1915 <g class="gatai-scene__lane" data-comp> 1916 <rect class="gatai-scene__laneBody" x="120" y="330" width="700" height="120" rx="20" /> 1917 <rect class="gatai-scene__laneInk" x="148" y="356" width="180" height="10" rx="5" /> 1918 <rect class="gatai-scene__laneInk" x="148" y="378" width="260" height="8" rx="4" /> 1919 <rect class="gatai-scene__laneInk" x="148" y="398" width="220" height="8" rx="4" /> 1920 1921 <!-- many tiles --> 1922 <g class="gatai-scene__tiles"> 1923 <rect class="gatai-scene__tile" x="430" y="356" width="58" height="34" rx="10" /> 1924 <rect class="gatai-scene__tile" x="494" y="356" width="58" height="34" rx="10" /> 1925 <rect class="gatai-scene__tile" x="558" y="356" width="58" height="34" rx="10" /> 1926 <rect class="gatai-scene__tile" x="622" y="356" width="58" height="34" rx="10" /> 1927 <rect class="gatai-scene__tile" x="686" y="356" width="84" height="34" rx="10" /> 1928 1929 <rect class="gatai-scene__tile" x="430" y="396" width="88" height="34" rx="10" /> 1930 <rect class="gatai-scene__tile" x="524" y="396" width="62" height="34" rx="10" /> 1931 <rect class="gatai-scene__tile" x="592" y="396" width="72" height="34" rx="10" /> 1932 <rect class="gatai-scene__tile" x="670" y="396" width="100" height="34" rx="10" /> 1933 </g> 1934 1935 <circle class="gatai-scene__node" cx="160" cy="430" r="6" data-node /> 1936 <circle class="gatai-scene__node" cx="820" cy="390" r="6" data-node /> 1937 </g> 1938 1939 <!-- side micro panels (extra components) --> 1940 <g class="gatai-scene__microPanels" data-comp> 1941 <rect class="gatai-scene__panel" x="70" y="120" width="56" height="56" rx="14" /> 1942 <rect class="gatai-scene__panel" x="70" y="190" width="56" height="72" rx="14" /> 1943 <rect class="gatai-scene__panel" x="70" y="276" width="56" height="54" rx="14" /> 1944 <circle class="gatai-scene__via" cx="98" cy="148" r="3" /> 1945 <circle class="gatai-scene__via" cx="98" cy="212" r="3" /> 1946 <circle class="gatai-scene__via" cx="98" cy="240" r="3" /> 1947 <circle class="gatai-scene__via" cx="98" cy="304" r="3" /> 1948 </g> 1949 </g> 1950 1951 <!-- flow traces (draw on scroll) --> 1952 <g class="gatai-scene__flows"> 1953 <path class="gatai-scene__trace" d="M380 220 C440 220, 470 220, 520 220" /> 1954 <path class="gatai-scene__trace" d="M260 300 C260 320, 260 330, 260 360" /> 1955 <path class="gatai-scene__trace" d="M670 300 C670 320, 670 330, 670 360" /> 1956 <path class="gatai-scene__trace" d="M410 225 C420 260, 440 3
195600, 520 340" /> 1957 <path class="gatai-scene__trace" d="M635 210 C610 260, 590 295, 560 330" /> 1958 <path class="gatai-scene__trace" d="M635 210 C690 260, 740 300, 800 345" /> 1959 1960 <!-- node chain --> 1961 <circle class="gatai-scene__node" cx="380" cy="220" r="6" data-node /> 1962 <circle class="gatai-scene__node" cx="520" cy="220" r="6" data-node /> 1963 <circle class="gatai-scene__node" cx="560" cy="330" r="6" data-node /> 1964 <circle class="gatai-scene__node" cx="800" cy="345" r="6" data-node /> 1965 </g> 1966 1967 <!-- scan beam --> 1968 <rect class="gatai-scene__beam" x="90" y="92" width="720" height="18" rx="9" fill="url(#atlasBeam)"></rect> 1969 1970 <!-- HUD --> 1971 <g class="gatai-scene__hud"> 1972 <rect class="gatai-scene__hudBg" x="70" y="505" width="760" height="26" rx="8" /> 1973 <rect class="gatai-scene__hudFill" x="80" y="513" width="160" height="10" rx="6" /> 1974 </g> 1975 </g> 1976 1977 <!-- clean border --> 1978 <rect class="gatai-scene__border" x="18" y="14" width="864" height="532" rx="18"></rect> 1979 </svg> 1980 1981 <div class="gatai-scene__labels"> 1982 <div class="gatai-chip gatai-clip">Innovation</div> 1983 <div class="gatai-chip gatai-clip">Teams</div> 1984 <div class="gatai-chip gatai-clip">Impact</div> 1985 </div> 1986 </div> 1987</div> 1988 1989 1990 </div> 1991 </div> 1992 </header> 1993 1994 <!-- CONTENT --> 1995 <main class="gatai-main"> 1996 <div class="diapo-panel"> 1997 <div class="diapo-content"> 1998 </div> 1999 </div> 2000 <!-- Who We Are --> 2001 <section id="who-we-are" class="gatai-band gatai-band--light gatai-reveal"> 2002 <div class="gatai-shell"> 2003 <div class="gatai-band__grid"> 2004 <div class="gatai-band__head"> 2005 <h2 class="gatai-h2">Who We Are</h2> 2006 <p class="gatai-sub"> 2007 A high-performance team built around innovation, precision, and long-term reliability. 2008 </p> 2009 </div> 2010 <div class="gatai-band__body"> 2011 <p> 2012 GATAI Inc. operates at the intersection of advanced AI and practical engineering. We translate complex ideas 2013 into systems that work under real constraints performance, security, governance, timelines, and users. 2014 </p> 2015 <p> 2016 Our teams combine research mindset with production discipline: we prototype quickly, validate rigorously, 2017 and ship solutions designed to last. 2018 </p> 2019 </div> 2020 </div> 2021 </div> 2022 </section> 2023 2024 <!-- How We Work --> 2025 <section id="how-we-work" class="gatai-band gatai-band--alt gatai-reveal"> 2026 <div class="gatai-shell"> 2027 <div class="gatai-band__grid"> 2028 <div class="gatai-band__head"> 2029 <h2 class="gatai-h2">How We Work</h2> 2030 <p class="gatai-sub"> 2031 From exploration to deployment a repeatable operating model. 2032 </p> 2033 </div> 2034 <div class="gatai-band__body"> 2035 <div class="gatai-cards gatai-cards--two"> 2036 <article class="gatai-card gatai-clip" data-hovercard> 2037 <h3 class="gatai-h3">Discover</h3> 2038 <p>Define the problem precisely, map constraints, and identify measurable success criteria.</p> 2039 </article> 2040 <article class="gatai-card gatai-clip" data-hovercard> 2041 <h3 class="gatai-h3">Prototype</h3> 2042 <p>Rapid experiments to validate feasibility, quality, and expected impact.</p> 2043 </article> 2044 <article class="gatai-card gatai-clip" data-hovercard> 2045 <h3 class="gatai-h3">Engineer</h3> 2046 <p>Production-grade implementation: performance, observability, and maintainability.</p> 2047 </article> 2048 <article class="gatai-card gatai-clip" data-hovercard> 2049 <h3 class="gatai-h3">Operate</h3> 2050 <p>Governance, monitoring, continuous improvement, and long-term lifecycle support.</p> 2051 </article> 2052 </div> 2053 2054 <div class="gatai-callout gatai-clip"> 2055 <p class="gatai-callout__title">Our principle</p> 2056 <p class="gatai-callout__text"> 2057 Innovation is only valuable when it becomes a reliable system people can trust and use. 2058 </p> 2059 </div> 2060 </div> 2061 </div> 2062 </div> 2063 </section> 2064 2065 <!-- Research & Innovation --> 2066 <section id="research-innovation" class="gatai-band gatai-band--light gatai-reveal"> 2067 <div class="gatai-shell"> 2068 <div class="gatai-band__grid"> 2069 <div class="gatai-band__head"> 2070 <h2 class="gatai-h2">Research & Innovation</h2> 2071 <p class="gatai-sub"> 2072 Staying ahead means investing in experimentation, evaluation, and real-world alignment. 2073 </p> 2074 </div> 2075 <div class="gatai-band__body"> 2076 <p> 2077 We run applied R&D cycles to test new ideas, benchmark approaches, and de-risk adoption. Our focus is 2078 practical innovation research that translates into measurable improvements in quality, speed, and robustness. 2079 </p> 2080 <p> 2081 Where relevant, we collaborate with academic communities and contribute to open ecosystems so clients benefit 2082 from modern methods without inheriting instability. 2083 </p> 2084 </div> 2085 </div> 2086 </div> 2087 </section> 2088 <div class="diapo-panel"> 2089 <div class="diapo-content"> 2090 </div> 2091 </div> 2092 <!-- Trust & Governance --> 2093 <section id="trust-governance" class="gatai-band gatai-band--alt gatai-reveal"> 2094 <div class="gatai-shell"> 2095 <div class="gatai-band__grid"> 2096 <div class="gatai-band__head"> 2097 <h2 class="gatai-h2">Trust & Governance</h2> 2098 <p class="gatai-sub"> 2099 Building advanced systems is not enough they must be governable. 2100 </p> 2101 </div> 2102 <div class="gatai-band__body"> 2103 <ul class="gatai-list"> 2104 <li><strong>Risk-aware delivery:</strong> structured evaluation, monitoring, and clear operational controls.</li> 2105 <li><strong>Transparency by design:</strong> explainable outputs where decisions matter.</li> 2106 <li><strong>Security mindset:</strong>
2106 privacy-first architecture and disciplined access boundaries.</li> 2107 <li><strong>Responsible deployment:</strong> human oversight, auditability, and documented decision flows.</li> 2108 </ul> 2109 <p class="gatai-note"> 2110 Our governance approach can align with widely used âtrustworthy AIâ frameworks and requirements 2111 when clients need them. 2112 </p> 2113 </div> 2114 </div> 2115 </div> 2116 </section> 2117 2118 <!-- Talent --> 2119 <section id="talent" class="gatai-band gatai-band--light gatai-reveal"> 2120 <div class="gatai-shell"> 2121 <div class="gatai-band__grid"> 2122 <div class="gatai-band__head"> 2123 <h2 class="gatai-h2">People & Capability</h2> 2124 <p class="gatai-sub"> 2125 Strong outcomes come from strong teams research, engineering, and operations in one loop. 2126 </p> 2127 </div> 2128 <div class="gatai-band__body"> 2129 <p> 2130 We structure teams to move fast without breaking quality: clear ownership, shared standards, 2131 and a culture of testing, documentation, and continuous improvement. 2132 </p> 2133 2134 <div class="gatai-pillgrid"> 2135 <div class="gatai-pill gatai-clip"><strong>Staffing that scales:</strong> the right specialists, at the right time.</div> 2136 <div class="gatai-pill gatai-clip"><strong>Execution discipline:</strong> predictable delivery, measurable outcomes.</div> 2137 <div class="gatai-pill gatai-clip"><strong>Innovation loop:</strong> research â prototype â production â learn.</div> 2138 </div> 2139 </div> 2140 </div> 2141 </div> 2142 </section> 2143 2144 <!-- CTA FOOTER --> 2145 <footer id="contact" class="gatai-footer gatai-reveal"> 2146 <div class="gatai-shell"> 2147 <div class="gatai-footer__grid gatai-clip"> 2148 <div> 2149 <h2 class="gatai-h2 gatai-h2--invert">Ready to build something that lasts?</h2> 2150 <p class="gatai-footer__text"> 2151 If youâre exploring AI adoption, modernization, or advanced digital systems, weâll help you move from 2152 ambition to a governed, production-grade reality. 2153 </p> 2154 </div> 2155 <div class="gatai-footer__actions"> 2156 <a class="gatai-btn gatai-btn--primary" href="/booking">Book a Call</a> 2157 <a class="gatai-btn gatai-btn--ghost gatai-btn--ghostInvert" href="/finance">Explore Finance</a> 2158 </div> 2159 </div> 2160 </div> 2161 </footer> 2162 2163 </main> 2164 </section> 2165</section> 2166 2167 2168 2169 2170 2171 2172<footer class="footer" style=" background-repeat: no-repeat; background-size: cover; 2173 background-position: center; 2174background-image: url('/assets/images/bgf.png')"> 2175 <div class="footer-top"> 2176 <div class="container"> 2177 <div class="footer-brand"> 2178 <a class="logo" href="#"> 2179 <img alt="GATAI" decoding="async" height="500" src="/assets/images/Untitled-2-03.png" width="500"/> 2180 </a> 2181 <p class="section-text"> 2182 To learn more about how our AI models can transform your business, visit our website or reach out to our team for a consultation. 2183 </p> 2184 <div class="location-text"> 2185 <div class="location-icon"> 2186 <ion-icon name="location-outline"> 2187 </ion-icon> 2188 </div> 2189 <address class="address"> 2190 8 The Green, Ste D 2191 </address> 2192 </div> 2193 <div class="contact"> 2194 <p class="h3 contact-title"> 2195 Contact us 2196 </p> 2197 <ul class="contact-list"> 2198 <li class="contact-item"> 2199 <div class="contact-icon"> 2200 <ion-icon name="mail-outline"> 2201 </ion-icon> 2202 </div> 2203 <a class="contact-link text-gray-300 hover:text-white" href="mailto:[email protected]"> 2204 [email protected] 2205 </a> 2206 </li> 2207 </ul> 2208 </div> 2209 </div> 2210 <div class="quicklink-box"> 2211 <p class="h3 quicklink-title"> 2212 Quick links 2213 </p> 2214 <ul class=""> 2215 <div> 2216 <ul> 2217 <h3 class="font-semibold mb-4"> 2218 Company 2219 </h3> 2220 <li> 2221 <a class="quicklink" href="/about"> 2222 About Us 2223 </a> 2224 </li> 2225 <li> 2226 <a class="quicklink" href="/careers"> 2227 Careers 2228 </a> 2229 </li> 2230 <li> 2231 <a class="quicklink" href="/contact"> 2232 Contact 2233 </a> 2234 </li> 2235 <li> 2236 <a class="quicklink" href="/privacy"> 2237 Privacy Policy 2238 </a> 2239 </li> 2240 <li> 2241 <a class="quicklink" href="/terms"> 2242 Terms of Service 2243 </a> 2244 </li> 2245 </ul> 2246 </div> 2247 <div> 2248 <ul> 2249 <h3 class="font-semibold mb-4"> 2250 Solutions 2251 </h3> 2252 <li> 2253 <a class="quicklink" href="/training"> 2254 AI Training 2255 </a> 2256 </li> 2257 <li> 2258 <a class="quicklink" href="/integration"> 2259 Integration & Deployment 2260 </a> 2261 </li> 2262 <li> 2263 <a class="quicklink" href="/optimizing"> 2264 Optimization 2265 </a> 2266 </li> 2267 <li> 2268 <a class="quicklink" href="/data-engineering"> 2269 Data Engineering 2270 </a> 2271 </li> 2272 <li> 2273 <a class="quicklink" href="/custom-ai-development"> 2274 Custom AI 2275 </a> 2276 </li> 2277 </ul> 2278 </div><div> 2279 <ul> 2280 <h3 class="font-semibold mb-4"> 2281 Resources 2282 </h3> 2283 <li> 2284 <a class="quicklink" href="/blog"> 2285 Blog 2286 </a> 2287 </li> 2288 <li> 2289 <a class="quicklink" href="/case-studies"> 2290 Case Studies 2291 </a> 2292 </li> 2293 <li> 2294 <a class="quicklink" href="/guides"> 2295 Guides & Reports 2296 </a> 2297 </li> 2298 <li> 2299 <a class="quicklink" href="/documentation"> 2300 API Docs 2301 </a> 2302 </li> 2303 <li> 2304 <a class="quicklink" href="/whitepapers"> 2305 Whitepapers 2306 </a> 2307 </li> 2308 <li> 2309 <a class="quicklink" href="/events"> 2310 Webinars & Events 2311 </a> 2312 </li> 2313 </ul> 2314 </div><div> 2315 <ul> 2316 <h3 class="font-semibold mb-4"> 2317 Industries 2318 </h3> 2319 <li> 2320 <a class="quicklink" href="/finance"> 2321 Finance & Investment 2322 </a> 2323 </li> 2324 <li> 2325 <a class="quicklink" href="/healthcare"> 2326 Healthcare 2327 </a> 2328 </li> 2329 <li> 2330 <a class="quicklink" href="/language-and-communication"> 2331 Language & Communication 2332 </a> 2333 </li> 2334 <li> 2335 <a class="quicklink" href="/marketing-and-advertising"> 2336 Retail & e-commerce 2337 </a> 2338 </li> 2339 <li> 2340 <a class="quicklink" href="/manufacturing"> 2341 Manufacturing 2342 </a> 2343 </li> 2344 <li> 2345 <a class="quicklink" href="/fashion-and-design"> 2346 Fashion & Design 2347 </a> 2348 </li> 2349 <li> 2350 <a class="quicklink" href="/travel-and-transportation"> 2351 Travel & Transportation 2352 </a> 2353 </li> 2354 <li> 2355 <a class="quicklink" href="/education"> 2356 Education 2357 </a> 2358 </li> 2359 <li> 2360 <a class="quicklink" href="/simulation-and-entertainment"> 2361 Simulation & Entertainment 2362 </a> 2363 </li> 2364 </ul> 2365 </ul></div>
2366 </div> 2367 2368 </div> 2369 </div> 2370 2371 </footer> 2372 2373 2374 2375 2376 2377
2378<script> 2379(function(){ 2380 // Build a minimal rail once 2381 const rail = document.createElement('aside'); 2382 rail.className = 'rail'; 2383 rail.innerHTML = ` 2384 <div class="rail__wrap" aria-hidden="true"> 2385 <div class="rail__track"></div> 2386 <div class="rail__fill"></div> 2387 <div class="rail__ticks"></div> 2388 <div class="rail__thumb"></div> 2389 </div> 2390 <div class="rail__label" aria-live="polite" aria-atomic="true"></div> 2391 `; 2392 document.body.appendChild(rail); 2393 2394 const label = rail.querySelector('.rail__label'); 2395 const sections = [...document.querySelectorAll('section.scn')]; 2396 if (!sections.length) return; 2397 2398 /* --- helpers --- */ 2399 const clamp01 = v => v<0?0:v>1?1:v; 2400 2401 function layoutRail(){ 2402 // nothing heavy: fill uses scaleY; just ensure thumb area doesn't exceed viewport 2403 // (CSS handles top/bottom clamps via svh) 2404 } 2405 2406 /* page progress across total scroll height */ 2407 function setPageProgress(){ 2408 const max = Math.max(1, document.documentElement.scrollHeight - innerHeight); 2409 const p = clamp01(scrollY / max); 2410 document.documentElement.style.setProperty('--scroll', p.toFixed(4)); 2411 } 2412 2413 /* choose the most visible section */ 2414 function activeSectionIndex(){ 2415 const vh = innerHeight || 1; 2416 let best = 0, bestOv = -1; 2417 for(let i=0;i<sections.length;i++){ 2418 const r = sections[i].getBoundingClientRect(); 2419 const ov = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top)); 2420 if(ov>bestOv){ bestOv = ov; best = i; } 2421 } 2422 return best; 2423 } 2424 2425 /* thumb tracks the center of the active section, mapped to rail height */ 2426 function setThumbPos(){ 2427 const wrap = rail.querySelector('.rail__wrap').getBoundingClientRect(); 2428 const usableTop = wrap.top, usableH = wrap.height; 2429 const r = sections[active]?.getBoundingClientRect(); 2430 const mid = (r.top + r.bottom)/2; 2431 const t = clamp01((mid - usableTop) / Math.max(1, usableH)); 2432 document.documentElement.style.setProperty('--thumb', t.toFixed(4)); 2433 } 2434 2435 /* typewriter for vertical label (replaces previous text) */ 2436 let active = 0, typingId = 0, raf; 2437 function setLabel(text){ 2438 cancelAnimationFrame(raf); 2439 typingId++; 2440 const id = typingId; 2441 let i = 0; 2442 label.textContent = ''; // remove existing immediately 2443 const speed = 28; // ms per char target 2444 2445 const step = (last=0)=>{ 2446 const now = performance.now(); 2447 if(now - last >= speed){ 2448 label.textContent = text.slice(0, ++i); 2449 last = now; 2450 } 2451 if(i < text.length && id === typingId){ 2452 raf = requestAnimationFrame(()=>step(last)); 2453 } 2454 }; 2455 raf = requestAnimationFrame(()=>step()); 2456 } 2457 2458 function updateActive(){ 2459 const i = activeSectionIndex(); 2460 if(i !== active){ 2461 active = i; 2462 const title = sections[i].querySelector('h2')?.textContent?.trim() || ''; 2463 setLabel(title.toUpperCase()); 2464 } 2465 } 2466 2467 /* tick loop (Lenis-aware) */ 2468 function onScrollTick(){ 2469 setPageProgress(); 2470 setThumbPos(); 2471 updateActive(); 2472 } 2473 2474 // Hook into Lenis if available 2475 if(window.lenis && typeof window.lenis.on === 'function'){ 2476 window.lenis.on('scroll', onScrollTick); 2477 }else{ 2478 addEventListener('scroll', onScrollTick, {passive:true}); 2479 } 2480 addEventListener('resize', ()=>{ layoutRail(); onScrollTick(); }, {passive:true}); 2481 2482 // initial 2483 layoutRail(); onScrollTick(); 2484})(); 2485</script>
2485 2486
2487<script> 2488/* Dots/progress overlay also mounted inside .timeline (non-blocking) */ 2489(function(){ 2490 const host = document.querySelector('.timeline'); 2491 if(!host || document.querySelector('.rail-attach--dots')) return; 2492 2493 const SCENE_COLORS = { 2494 logo:"#7bd7ff", features:"#86f1ff", services:"#9ff7d1", research:"#9ecbff", 2495 industries:"#ffbda1", deploy:"#7ff0d6", data:"#9cc2ff", custom:"#e699ff", 2496 agents:"#78f0ff", ethics:"#b3ffc1", fab:"#9ecbff", vortex:"#a7e0ff", 2497 lab:"#aaddff", server:"#8fe1ff", wafer:"#f6bfff", ops:"#a3c8ff", ai:"#95e2ff", helix:"#88f1f5" 2498 }; 2499 const sections = [...document.querySelectorAll('section.scn')]; 2500 if (!sections.length) return; 2501 2502 const rail = document.createElement('div'); 2503 rail.className = 'rail-attach rail-attach--dots'; 2504 rail.innerHTML = ` 2505 <div class="rail__progress" aria-hidden="true" style="position:absolute;inset:6svh auto 6svh 8px;width:2px;"> 2506 <span class="rail__track" style="position:absolute;inset:0;background:rgba(255,255,255,.14)"></span> 2507 <span class="rail__fill" style="position:absolute;left:0;right:0;top:0;height:calc(100% * var(--scroll));background:var(--rail-color);"></span> 2508 <ol class="rail__dots" style="position:absolute;inset:0;list-style:none;margin:0;padding:0"></ol> 2509 </div> 2510 <div class="rail__tip" style="position:absolute;left:22px;transform:translateY(var(--tip,0px));font:600 12px/1 Inter,system-ui;color:rgba(200,235,255,.85);pointer-events:none;"></div> 2511 `; 2512 host.appendChild(rail); 2513 Object.assign(rail.style, { position:'absolute', top:'0', bottom:'0', left:'0', width:'0', pointerEvents:'none', zIndex:'65' }); 2514 2515 const dotsEl = rail.querySelector('.rail__dots'); 2516 2517 const colorFor = (sec)=> SCENE_COLORS[(sec.dataset.scene||'').toLowerCase()] || '#79d0ff'; 2518 2519 function getPositions(){ 2520 const topY = innerHeight * 0.06; 2521 const usable = innerHeight * 0.88; 2522 const y0 = scrollY + topY; 2523 return sections.map(sec=>{ 2524 const r = sec.getBoundingClientRect(); 2525 const mid = r.top + r.height/2 + scrollY; 2526 return Math.max(0, Math.min(1, (mid - y0) / usable)); 2527 }); 2528 } 2529 2530 function buildDots(){ 2531 dotsEl.innerHTML = ''; 2532 const poss = getPositions(); 2533 poss.forEach((p,i)=>{ 2534 const li = document.createElement('li'); 2535 li.style.cssText = `position:absolute;left:0;right:0;top:${(p*100).toFixed(3)}%`; 2536 const b = document.createElement('span'); 2537 b.className = 'rail__dot'; 2538 b.style.cssText = `display:block;position:absolute;left:50%;transform:translate(-50%,-50%);width:10px;height:10px;border-radius:50%;background:${colorFor(sections[i])};box-shadow:0 0 0 3px rgba(121,208,255,.25)`; 2539 li.appendChild(b); dotsEl.appendChild(li); 2540 }); 2541 } 2542 buildDots(); 2543 addEventListener('resize', ()=> requestAnimationFrame(buildDots), {passive:true}); 2544 2545 function setPageProgress(){ 2546 const max = Math.max(1, document.documentElement.scrollHeight - innerHeight); 2547 document.documentElement.style.setProperty('--scroll', Math.min(1, Math.max(0, scrollY / max)).toFixed(4)); 2548 } 2549 2550 function updateThumbAndColor(){ 2551 let best = 0, bestOv = -1; 2552 const vh = innerHeight||1; 2553 sections.forEach((s,i)=>{ 2554 const r = s.getBoundingClientRect(); 2555 const ov = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top)) / Math.max(1, vh); 2556 if(ov > bestOv){ bestOv = ov; best = i; } 2557 }); 2558 document.documentElement.style.setProperty('--rail-color', colorFor(sections[best])); 2559 } 2560 2561 function onTick(){ 2562 setPageProgress(); 2563 updateThumbAndColor(); 2564 const poss = getPositions(); 2565 dotsEl.querySelectorAll('li').forEach((li,i)=> li.style.top = `${(poss[i]||0)*100}%`); 2566 } 2567 2568 if(window.lenis){ window.lenis.on('scroll', onTick); } 2569 else { addEventListener('scroll', onTick, {passive:true}); } 2570 onTick(); 2571})(); 2572</script>
2572 2573 2574 2575
2576<script> 2577(() => { 2578 const ready = (fn) => 2579 (document.readyState !== 'loading') 2580 ? fn() 2581 : addEventListener('DOMContentLoaded', fn, { once: true }); 2582 2583 ready(() => { 2584 const scope = document.getElementById('scnScope'); 2585 const dock = scope?.querySelector('#textDock'); 2586 if (!scope || !dock) return; 2587 2588 const prefersReduced = matchMedia('(prefers-reduced-motion: reduce)').matches; 2589 2590 const D = { 2591 kicker: dock.querySelector('.kicker'), 2592 title: dock.querySelector('#dock-title'), 2593 lead: dock.querySelector('#dock-lead'), 2594 body: dock.querySelector('#dock-body'), 2595 cta: dock.querySelector('#dock-cta'), 2596 inset: dock.querySelector('.dock-inset') 2597 }; 2598 if (!D.title || !D.lead || !D.body || !D.kicker || !D.inset) return; 2599 2600 /* ---------- FIX #1: caret style (was missing backticks) ---------- */ 2601 if (!document.getElementById('tw-caret-style')) { 2602 const s = document.createElement('style'); 2603 s.id = 'tw-caret-style'; 2604 s.textContent = `.tw-caret{display:inline-block;width:1px;height:1em;background:currentColor;margin-left:1px;vertical-align:-.1em;animation:twblink .6s step-start infinite}@keyframes twblink{50%{opacity:0}}`; 2605 document.head.appendChild(s); 2606 } 2607 2608 /* ---------- FIX #2: force remove gaps in the dock body ---------- */ 2609 // Style injector (very high-specificity + !important) 2610 if (!document.getElementById('dock-tight-style')) { 2611 const t = document.createElement('style'); 2612 t.id = 'dock-tight-style'; 2613 t.textContent = ` 2614 #textDock[data-tight="1"] #dock-body{gap:0!important;row-gap:0!important;column-gap:0!important} 2615 #textDock[data-tight="1"] #dock-body>*{margin:0!important;padding:0!important} 2616 `; 2617 document.head.appendChild(t); 2618 } 2619 // Toggle the tight mode and also set inline styles as a belt-and-suspenders 2620 dock.setAttribute('data-tight','1'); 2621 try { 2622 D.body.style.setProperty('gap','0','important'); 2623 D.body.style.setProperty('row-gap','0','important'); 2624 D.body.style.setProperty('column-gap','0','important'); 2625 } catch {} 2626 2627 /* ---------- helpers ---------- */ 2628 const clamp = (v,a,b)=>Math.max(a,Math.min(b,v)); 2629 let runId = 0; 2630 2631 function mountCaret(el, initial=''){ 2632 el.innerHTML=''; 2633 const tn=document.createTextNode(initial); 2634 const caret=document.createElement('span'); caret.className='tw-caret'; 2635 el.appendChild(tn); el.appendChild(caret); 2636 return {tn, caret}; 2637 } 2638 2639 function typeRAF(el, text, {cps=200, token=runId}={}){ 2640 if(!el) return Promise.resolve(); 2641 const {tn, caret}=mountCaret(el,''); 2642 let i=0, acc=0, last=performance.now(); 2643 return new Promise(res=>{ 2644 const step=(now)=>{ 2645 if(token!==runId){ caret.remove(); return res(); } 2646 const dt=now-last; last=now; 2647 acc += (dt*cps)/1000; 2648 const add=acc|0; 2649 if(add>0){ i=clamp(i+add,0,text.length); tn.data=text.slice(0,i); acc-=add; } 2650 if(i>=text.length){ caret.remove(); return res(); } 2651 requestAnimationFrame(step); 2652 }; 2653 requestAnimationFrame(step); 2654 }); 2655 } 2656 2657 function eraseRAF(el, {cps=420, token=runId}={}){ 2658 if(!el) return Promise.resolve(); 2659 const curr=el.textContent||''; 2660 const {tn, caret}=mountCaret(el, curr); 2661 let i=curr.length, acc=0, last=performance.now(); 2662 return new Promise(res=>{ 2663 const step=(now)=>{ 2664 if(token!==runId){ caret.remove(); return res(); } 2665 const dt=now-last; last=now; 2666 acc += (dt*cps)/1000; 2667 const sub=acc|0; 2668 if(sub>0){ i=clamp(i-sub,0,curr.length); tn.data=curr.slice(0,i); acc-=sub; } 2669 if(i<=0){ caret.remove(); el.textContent=''; return res(); } 2670 requestAnimationFrame(step); 2671 }; 2672 requestAnimationFrame(step); 2673 }); 2674 } 2675 2676 async function eraseBullets(ul, {cps=440, token=runId}={}){ 2677 const items=[...ul.querySelectorAll('li')].reverse(); 2678 for(const li of items){ 2679 await eraseRAF(li,{cps,token}); 2680 if(token!==runId) return; 2681 li.remove(); 2682 } 2683 } 2684 2685 /* ---------- content capture (EVERYTHING) ---------- */ 2686 const sections = [...scope.querySelectorAll('.scn')]; 2687 const copyMap = new Map(); 2688 2689 function extractBodyHTML(src){ 2690 const clone = src.cloneNode(true); 2691 clone.querySelectorAll('.kicker, h2, .lead, .cta').forEach(n=>n.remove()); 2692 clone.removeAttribute('aria-hidden'); 2693 return clone.innerHTML.trim(); 2694 } 2695 2696 sections.forEach(scn => { 2697 const src = scn.querySelector('.content'); if(!src) return; 2698 src.setAttribute('aria-hidden','true'); 2699 Object.assign(src.style, {visibility:'hidden',position:'absolute',pointerEvents:'none'}); 2700 2701 copyMap.set(scn, { 2702 kicker: (src.querySelector('.kicker')?.textContent||'').trim(), 2703 title: (src.querySelector('h2')?.textContent||'').trim(), 2704 lead: (src.querySelector('.lead')?.textContent||'').trim(), 2705 bodyHTML: extractBodyHTML(src), 2706 ctaHTML: src.querySelector('.cta')?.innerHTML || '' 2707 }); 2708 }); 2709 if(!copyMap.size) return; 2710 2711 /* ---------- four-cursor engine ---------- */ 2712 function pickPrimaryBodyEl(container){ 2713 return container.querySelector('h4, h5, p, .bullets li, ul li, blockquote p') || null; 2714 } 2715 2716 function setTyping(on){ dock.dataset.typing = on ? '1':'0'; } 2717 function setPhase(ph){ dock.setAttribute('data-phase', ph); } 2718 2719 // obstacle avoidance: look for sticky things and flip the dock 2720 const OBST_SEL = '.sticky-clock, .sticky, [data-sticky], .floating-btn, .sticky-cta'; 2721 function avoidObstacles(){
2722 const vp = {w: innerWidth, h: innerHeight}; 2723 const obs = [...document.querySelectorAll(OBST_SEL)]; 2724 if(!obs.length){ dock.classList.remove('dock--left','dock--right'); return; } 2725 let leftScore=0, rightScore=0; 2726 for(const o of obs){ 2727 const r=o.getBoundingClientRect(); 2728 if(r.bottom<=0 || r.top>=vp.h || r.right<=0 || r.left>=vp.w) continue; 2729 const area = Math.max(0, Math.min(vp.h,r.bottom)-Math.max(0,r.top)) * 2730 Math.max(0, Math.min(vp.w,r.right)-Math.max(0,r.left)); 2731 const center = (r.left+r.right)/2; 2732 if(center < vp.w/2) leftScore += area; else rightScore += area; 2733 } 2734 const side = leftScore <= rightScore ? 'left' : 'right'; 2735 dock.classList.toggle('dock--left', side==='left'); 2736 dock.classList.toggle('dock--right', side==='right'); 2737 } 2738 2739 let current=null, pending=null, tmr=0; 2740 2741 async function doSwap(scn){ 2742 if(current===scn) return; 2743 current=scn; 2744 const data=copyMap.get(scn); if(!data) return; 2745 2746 const token=++runId; 2747 document.dispatchEvent(new CustomEvent('dock:typing-start')); 2748 setTyping(true); 2749 setPhase('compose'); 2750 avoidObstacles(); 2751 2752 if(prefersReduced){ 2753 D.kicker.textContent=data.kicker; 2754 D.title.textContent =data.title; 2755 D.lead.textContent =data.lead; 2756 D.body.innerHTML =data.bodyHTML; 2757 D.cta.innerHTML =data.ctaHTML; 2758 setTyping(false); setPhase('pinTop'); 2759 document.dispatchEvent(new CustomEvent('dock:swap-done')); 2760 return; 2761 } 2762 2763 // OUT with up to 4 erasers in parallel 2764 const prevPrimary = D.body.querySelector('h4, h5, p, .bullets li, ul li, blockquote p'); 2765 await Promise.all([ 2766 eraseRAF(D.kicker,{cps:520,token}), 2767 eraseRAF(D.title, {cps:540,token}), 2768 eraseRAF(D.lead, {cps:540,token}), 2769 prevPrimary ? eraseRAF(prevPrimary,{cps:560,token}) : Promise.resolve() 2770 ]); 2771 if(token!==runId) return; 2772 2773 // Clear body & CTA in preparation 2774 D.body.innerHTML=''; 2775 D.cta.innerHTML=''; 2776 2777 // Build a staging fragment for the next body's content 2778 const frag = document.createElement('div'); 2779 frag.innerHTML = data.bodyHTML; 2780 frag.querySelectorAll('li, p, h4, h5, blockquote, pre, table').forEach(el=>{ 2781 el.classList.add('_stagger'); 2782 }); 2783 2784 // Decide primary body element to type into 2785 let primaryBody = pickPrimaryBodyEl(frag); 2786 let primaryText = ''; 2787 if(primaryBody){ 2788 primaryText = primaryBody.textContent.trim(); 2789 const clone = document.createElement(primaryBody.tagName.toLowerCase()); 2790 for (const a of primaryBody.getAttributeNames()) 2791 clone.setAttribute(a, primaryBody.getAttribute(a)); 2792 clone.classList.remove('_stagger'); 2793 frag.replaceChild(clone, primaryBody); 2794 primaryBody = clone; 2795 } 2796 2797 // Inject skeleton into live DOM 2798 while(frag.firstChild) D.body.appendChild(frag.firstChild); 2799 2800 // IN Stage A four cursors at once 2801 await Promise.all([ 2802 typeRAF(D.title, data.title, {cps:230, token}), 2803 typeRAF(D.lead, data.lead, {cps:225, token}), 2804 typeRAF(D.kicker, data.kicker, {cps:235, token}), 2805 primaryBody ? typeRAF(primaryBody, primaryText, {cps:210, token}) : Promise.resolve() 2806 ]); 2807 if(token!==runId) return; 2808 2809 // IN Stage B stagger the rest quickly 2810 const rest = D.body.querySelectorAll('._stagger'); 2811 rest.forEach((el,i)=> setTimeout(()=> el.classList.add('_on'), 18*i)); 2812 2813 // CTA last 2814 D.cta.innerHTML = data.ctaHTML; 2815 2816 setTyping(false); 2817 setPhase('pinTop'); 2818 document.dispatchEvent(new CustomEvent('dock:swap-done')); 2819 } 2820 2821 const scheduleSwap=(scn)=>{ pending=scn; clearTimeout(tmr); tmr=setTimeout(()=>doSwap(pending), 18); }; 2822 2823 // Observer to choose the section with most overlap 2824 try{ 2825 const io=new IntersectionObserver((entries)=>{ 2826 const vh=innerHeight||1; 2827 let best=null, bestOv=-1; 2828 for(const e of entries){ 2829 const r=e.target.getBoundingClientRect(); 2830 const ov=Math.max(0, Math.min(vh,r.bottom)-Math.max(0,r.top))/vh; 2831 if(ov>bestOv){ bestOv=ov; best=e.target; } 2832 } 2833 if(best && bestOv>0.48) scheduleSwap(best); 2834 }, {threshold:[0.25,0.5,0.75], root:null, rootMargin:'0% 0 0% 0'}); 2835 sections.forEach(s=>io.observe(s)); 2836 }catch{ 2837 const onScroll=()=>{ 2838 const vh=innerHeight||1; 2839 let best=sections[0], bestOv=-1; 2840 for(const s of sections){ 2841 const r=s.getBoundingClientRect(); 2842 const ov=Math.max(0, Math.min(vh,r.bottom)-Math.max(0,r.top))/vh; 2843 if(ov>bestOv){ bestOv=ov; best=s; } 2844 } 2845 if(bestOv>0.48) scheduleSwap(best); 2846 }; 2847 addEventListener('scroll', onScroll, {passive:true}); 2848 onScroll(); 2849 } 2850 2851 // Recompute obstacle avoidance on resize/scroll
2852 const ro = new ResizeObserver(()=> avoidObstacles()); 2853 ro.observe(document.documentElement); 2854 addEventListener('scroll', ()=> avoidObstacles(), {passive:true}); 2855 addEventListener('resize', ()=> avoidObstacles()); 2856 2857 // Phase flip near viewport extrema 2858 function phaseOnScroll(){ 2859 const r = dock.getBoundingClientRect(); 2860 const topDist = Math.abs(r.top - 12); 2861 const botDist = Math.abs((innerHeight - r.bottom) - 12); 2862 if(topDist < botDist) setPhase('pinTop'); else setPhase('pinBot'); 2863 } 2864 addEventListener('scroll', phaseOnScroll, {passive:true}); 2865 2866 // First paint 2867 scheduleSwap(sections[0]); 2868 avoidObstacles(); 2869 setPhase('compose'); 2870 }); 2871 /* === HARD NO-GAP MODE (drop in near top, after caret style) === */ 2872if (!document.getElementById('dock-tight-style')) { 2873 const t = document.createElement('style'); 2874 t.id = 'dock-tight-style'; 2875 t.textContent = ` 2876 #textDock[data-tight="1"] #dock-body, 2877 #textDock[data-tight="1"] #dock-body *{ 2878 margin:0!important; padding:0!important; border:0!important; 2879 gap:0!important; row-gap:0!important; column-gap:0!important; 2880 } 2881 `; 2882 document.head.appendChild(t); 2883} 2884document.getElementById('textDock')?.setAttribute('data-tight','1'); 2885 2886function tighten(root){ 2887 root.style.setProperty('gap','0','important'); 2888 root.style.setProperty('row-gap','0','important'); 2889 root.style.setProperty('column-gap','0','important'); 2890 root.querySelectorAll('*').forEach(el=>{ 2891 el.style.setProperty('margin','0','important'); 2892 el.style.setProperty('padding','0','important'); 2893 el.style.setProperty('gap','0','important'); 2894 el.style.setProperty('row-gap','0','important'); 2895 el.style.setProperty('column-gap','0','important'); 2896 }); 2897} 2898 2899})(); 2900</script>
2900 2901 2902 2903
2904<script> 2905(() => { 2906 const STYLE_ID = 'disable-bw-and-fade'; 2907 2908 function applyOverrides() { 2909 if (document.getElementById(STYLE_ID)) return; 2910 2911 // 1) Inject high-specificity CSS overrides 2912 const css = ` 2913 /* kill the half black/half white overlay */ 2914 .dots3d-wrap .dots-split-bg { display: none !important; } 2915 2916 /* stop page fading to black at end of scroll */ 2917 html::after { content: none !important; display: none !important; } 2918 2919 /* remove forced black & white everywhere */ 2920 .scn { filter: none !important; } 2921 .hero .rvh, 2922 .features .rvh, 2923 .features .dots3d-canvas { filter: none !important; } 2924 2925 /* ensure any per-section black fade is off */ 2926 .dots3d-wrap .dots-fade-black { opacity: 0 !important; display: none !important; } 2927 2928 /* extra safety if your JS animates this var */ 2929 :root { --scroll: 0 !important; } 2930 `; 2931 const style = document.createElement('style'); 2932 style.id = STYLE_ID; 2933 style.textContent = css; 2934 document.head.appendChild(style); 2935 2936 // 2) Remove already-rendered overlay nodes, if present 2937 document.querySelectorAll('.dots3d-wrap .dots-split-bg, .dots3d-wrap .dots-fade-black') 2938 .forEach(el => el.remove()); 2939 2940 // 3) Normalize any inline filters that might have been set 2941 document.querySelectorAll('.scn, .hero .rvh, .features .rvh, .features .dots3d-canvas') 2942 .forEach(el => el.style.filter = 'none'); 2943 } 2944 2945 // Optional: quick toggle via Alt+B while testing 2946 function removeOverrides() { 2947 document.getElementById(STYLE_ID)?.remove(); 2948 } 2949 function toggleOverrides() { 2950 if (document.getElementById(STYLE_ID)) removeOverrides(); else applyOverrides(); 2951 } 2952 window.__BWFade = { apply: applyOverrides, remove: removeOverrides, toggle: toggleOverrides }; 2953 2954 // Run once when DOM is ready 2955 if (document.readyState === 'loading') { 2956 document.addEventListener('DOMContentLoaded', applyOverrides, { once: true }); 2957 } else { 2958 applyOverrides(); 2959 } 2960 2961 // Alt+B toggle for debugging 2962 document.addEventListener('keydown', (e) => { 2963 if (e.altKey && e.key.toLowerCase() === 'b') toggleOverrides(); 2964 }); 2965})(); 2966</script>
2966 2967<!-- 2968 Office Scroll FX dropâin for your existing <section id="features" class="features dots3d-wrap"> ⦠2969 ⢠50/50 Black/White split background 2970 ⢠Blue-white dots (left) and blue-black dots (right) 2971 ⢠Scroll = walk forward; doors open as you pass; at the end everything fades to black 2972 ⢠No libraries. Works with your current markup. Can live above your canvases and below your text. 2973--> 2974 2975 2976 2977
2978<script> 2979/* ============================================================ 2980 GaTAI side rail + dotted spine + section animator (Lenis) 2981 - keeps your existing classes/structure intact 2982 - builds nav from .scn > .content h2 (or data-scene/kicker) 2983 - uses Lenis for smooth scrolling 2984 - drives CSS vars: --t (overlap 0..1), --mx/--my (parallax) 2985============================================================ */ 2986(function(){ 2987 const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 2988 const lenis = null; // Native document scrolling; no competing wheel handlers. 2989 2990 function raf(time){ 2991 if (!sections.length) return; 2992 if(lenis) lenis.raf(time); 2993 updateFrame(); 2994 requestAnimationFrame(raf); 2995 } 2996 requestAnimationFrame(raf); 2997 2998 /* ---- collect sections & ensure ids/labels ---- */ 2999 const sections = Array.from(document.querySelectorAll('section.scn')); 3000 if (!sections.length) return; 3001 sections.forEach((s,i)=>{ 3002 // label source 3003 const title = s.querySelector('.content h2')?.textContent?.trim() 3004 || s.dataset.scene 3005 || s.querySelector('.kicker')?.textContent?.trim() 3006 || `Section ${i+1}`; 3007 // ensure id 3008 if(!s.id){ 3009 const slug = title.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/(^-|-$)/g,''); 3010 s.id = 's-' + (slug || ('section-'+(i+1))); 3011 } 3012 // ensure data-scene for the big vertical label 3013 if(!s.dataset.scene){ s.setAttribute('data-scene', title); } 3014 // reveal hooks 3015 const box = s.querySelector('.content'); 3016 if(box){ 3017 box.classList.add('stagger'); 3018 box.querySelectorAll('h2,.kicker,.lead,.bullets,.cta').forEach(el=> el.setAttribute('data-animate','')); 3019 } 3020 }); 3021 3022 /* ---- build the rail ---- */ 3023 const list = document.getElementById('railList'); 3024 if (list) list.replaceChildren(); 3025 const items = sections.map((s,i)=>{ 3026 const label = s.querySelector('.content h2')?.textContent?.trim() 3027 || s.dataset.scene || `Section ${i+1}`; 3028 const li = document.createElement('li'); 3029 li.className = 'rail__item'; 3030 li.innerHTML = `<a class="rail__btn" href="#${s.id}"> 3031 <span class="rail__index">${String(i+1).padStart(2,'0')}/</span>${label}</a>`; 3032 list?.appendChild(li); 3033 return li; 3034 }); 3035 3036 function setActive(i){ 3037 items.forEach((li,idx)=>{ 3038 li.classList.toggle('is-active', idx===i); 3039 const an = li.querySelector('a'); 3040 if(an) an.setAttribute('aria-current', idx===i ? 'true' : 'false'); 3041 }); 3042 } 3043 3044 /* ---- anchor clicks use lenis if present ---- */ 3045 document.addEventListener('click', (e)=>{ 3046 const a = e.target.closest('a[href^="#"]'); if(!a) return; 3047 const id = a.getAttribute('href'); if(!id || id==='#') return; 3048 const el = document.querySelector(id); if(!el) return; 3049 e.preventDefault(); 3050 if(lenis) lenis.scrollTo(el, {offset:0}); 3051 else el.scrollIntoView({behavior:'smooth', block:'center'}); 3052 }); 3053 3054 /* ---- pointer parallax to CSS vars ---- */ 3055 let mx=0,my=0; 3056 window.addEventListener('pointermove', (e)=>{ 3057 const nx = (e.clientX/(innerWidth||1))*2-1, ny = -(e.clientY/(innerHeight||1))*2+1; 3058 mx += (nx-mx)*0.18; my += (ny-my)*0.18; 3059 const scn = document.elementFromPoint(e.clientX, e.clientY)?.closest?.('.scn'); 3060 if(scn){ scn.style.setProperty('--mx', mx.toFixed(3)); scn.style.setProperty('--my', my.toFixed(3)); } 3061 }, {passive:true}); 3062 3063 /* ---- intersection â --t + inview ---- */ 3064 const io = new IntersectionObserver((entries)=>{ 3065 entries.forEach(entry=>{ 3066 const el = entry.target; 3067 if(entry.isIntersecting){ el.classList.add('is-inview'); } 3068 const r = entry.boundingClientRect, vh = innerHeight||1; 3069 const overlap = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top)); 3070 const t = Math.max(0, Math.min(1, overlap / Math.max(1, vh))); 3071 el.style.setProperty('--t', t.toFixed(3)); 3072 }); 3073 }, {threshold:[0,.05,.25,.5,.75,1]}); 3074 sections.forEach(s=> io.observe(s)); 3075 3076 /* ---- per-frame: choose most-overlapped, move spine dot ---- */ 3077 const dot = document.querySelector('.timeline__dot'); 3078 function updateFrame(){
3079 const vh = innerHeight || 1; 3080 let best = 0, bestOv = -1; 3081 sections.forEach((s,i)=>{ 3082 const r = s.getBoundingClientRect(); 3083 const overlap = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top)); 3084 const t = overlap / Math.max(1, vh); 3085 if(t > bestOv){ bestOv = t; best = i; } 3086 }); 3087 setActive(best); 3088 if (!sections[best]) return; 3089 const rr = sections[best].getBoundingClientRect(); 3090 const y = (rr.top + rr.height*0.5) / vh; 3091 dot?.style.setProperty('--y', Math.min(1,Math.max(0,y)).toFixed(3)); 3092 } 3093 3094 /* ---- basic WebGL fallback flag for your .fallback blocks ---- */ 3095 try{ 3096 const c = document.createElement('canvas'); 3097 const ok = !!(window.WebGLRenderingContext && (c.getContext('webgl')||c.getContext('experimental-webgl'))); 3098 if(!ok) document.documentElement.classList.add('no-webgl'); 3099 }catch{ document.documentElement.classList.add('no-webgl'); } 3100})(); 3101</script>
3101 3102 3103
3104<script> 3105/* Vertical label that follows sections mounted inside .timeline */ 3106(function(){ 3107 const host = document.querySelector('.timeline'); 3108 if(!host || document.querySelector('.rail-attach--label')) return; 3109 3110 // container attached to the fixed timeline 3111 const rail = document.createElement('div'); 3112 rail.className = 'rail-attach rail-attach--label'; 3113 rail.innerHTML = ` 3114 <div class="rail__wrap" aria-hidden="true"></div> 3115 <div class="rail__label" aria-live="polite" aria-atomic="true"></div> 3116 `; 3117 host.appendChild(rail); 3118 3119 /* non-blocking overlay styles (JS-only to avoid CSS edits) */ 3120 Object.assign(rail.style, { 3121 position:'absolute', top:'0', bottom:'0', left:'0', width:'0', 3122 pointerEvents:'none', zIndex:'70' 3123 }); 3124 const wrap = rail.querySelector('.rail__wrap'); 3125 Object.assign(wrap.style, { position:'absolute', top:'6svh', bottom:'6svh', left:'8px', width:'2px' }); 3126 const label = rail.querySelector('.rail__label'); 3127 Object.assign(label.style, { 3128 position:'absolute', top:'6svh', bottom:'6svh', left:'22px', 3129 writingMode:'vertical-rl', textOrientation:'mixed', 3130 fontFamily:'Spectral, serif', letterSpacing:'.06em', 3131 fontSize:'clamp(6px,1vw,12px)', 3132 color:'rgba(180,220,255,.65)', textShadow:'0 1px 0 rgba(0,0,0,.5)' 3133 }); 3134 3135 const sections = [...document.querySelectorAll('section.scn')]; 3136 if (!sections.length) return; 3137 3138 const clamp01 = v => v<0?0:v>1?1:v; 3139 3140 function setPageProgress(){ 3141 const max = Math.max(1, document.documentElement.scrollHeight - innerHeight); 3142 document.documentElement.style.setProperty('--scroll', clamp01(scrollY / max).toFixed(4)); 3143 } 3144 3145 function activeSectionIndex(){ 3146 const vh = innerHeight || 1; 3147 let best = 0, bestOv = -1; 3148 for(let i=0;i<sections.length;i++){ 3149 const r = sections[i].getBoundingClientRect(); 3150 const ov = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top)); 3151 if(ov>bestOv){ bestOv = ov; best = i; } 3152 } 3153 return best; 3154 } 3155 3156 function setThumbPos(){ 3157 // use the timeline box as reference so label stays glued to the spine 3158 const wrapRect = host.getBoundingClientRect(); 3159 const usableTop = wrapRect.top, usableH = wrapRect.height; 3160 const r = sections[active]?.getBoundingClientRect(); 3161 const mid = (r.top + r.bottom)/2; 3162 const t = clamp01((mid - usableTop) / Math.max(1, usableH)); 3163 document.documentElement.style.setProperty('--thumb', t.toFixed(4)); 3164 } 3165 3166 let active = 0, typingId = 0, rafId; 3167 function setLabel(text){ 3168 cancelAnimationFrame(rafId); 3169 typingId++; 3170 const id = typingId; 3171 let i = 0; label.textContent = ''; 3172 const step = (last=0)=>{ 3173 const now = performance.now(); 3174 if(now - last >= 28){ label.textContent = text.slice(0, ++i); last = now; } 3175 if(i < text.length && id === typingId){ rafId = requestAnimationFrame(()=>step(last)); } 3176 }; 3177 rafId = requestAnimationFrame(()=>step()); 3178 } 3179 3180 function updateActive(){ 3181 const i = activeSectionIndex(); 3182 if(i !== active){ 3183 active = i; 3184 const title = sections[i].querySelector('h2')?.textContent?.trim() || ''; 3185 setLabel(title.toUpperCase()); 3186 } 3187 } 3188 3189 function onScrollTick(){ setPageProgress(); setThumbPos(); updateActive(); } 3190 3191 if(window.lenis && typeof window.lenis.on === 'function'){ 3192 window.lenis.on('scroll', onScrollTick); 3193 }else{ 3194 addEventListener('scroll', onScrollTick, {passive:true}); 3195 } 3196 addEventListener('resize', onScrollTick, {passive:true}); 3197 3198 // make sure your real nav stays above any overlays 3199 const nav = document.querySelector('nav.rail'); 3200 if(nav){ nav.style.zIndex = '90'; nav.style.pointerEvents = 'auto'; } 3201 3202 onScrollTick(); 3203})(); 3204</script>
3204 3205
3206<script> 3207(function(){ 3208 const prefersReduced = matchMedia('(prefers-reduced-motion: reduce)').matches; 3209 const lenis = null; // Native scrolling. 3210 3211 function raf(t){ if (!sections.length) return; if(lenis) lenis.raf(t); frame(); requestAnimationFrame(raf); } 3212 requestAnimationFrame(raf); 3213 3214 /* Collect scenes/sections */ 3215 const sections = Array.from(document.querySelectorAll('section.scn')); 3216 if (!sections.length) return; 3217 sections.forEach((s,i)=>{ 3218 const title = s.querySelector('.content h2')?.textContent?.trim() 3219 || s.dataset.scene || `Section ${i+1}`; 3220 if(!s.id){ 3221 const slug = title.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/(^-|-$)/g,''); 3222 s.id = slug ? `s-${slug}` : `section-${i+1}`; 3223 } 3224 if(!s.dataset.scene) s.setAttribute('data-scene', title); 3225 }); 3226 3227 /* Build rail list inside the header bottom-left lane */ 3228 const railUL = document.getElementById('railList'); 3229 const railItems = sections.map((s,i)=>{ 3230 const label = s.querySelector('.content h2')?.textContent?.trim() 3231 || s.dataset.scene || `Section ${i+1}`; 3232 const li = document.createElement('li'); 3233 li.className = 'rail__item'; 3234 li.innerHTML = `<a class="rail__btn" href="#${s.id}"> 3235 <span class="rail__index" aria-hidden="true">${String(i+1).padStart(2,'0')}/</span>${label} 3236 </a>`; 3237 railUL.appendChild(li); 3238 return li; 3239 }); 3240 3241 /* Smooth anchor scroll */ 3242 document.addEventListener('click', (e)=>{ 3243 const a = e.target.closest('a[href^="#"]'); if(!a) return; 3244 const id = a.getAttribute('href'); if(!id || id==='#') return; 3245 const el = document.querySelector(id); if(!el) return; 3246 e.preventDefault(); 3247 if(lenis) lenis.scrollTo(el, {offset:0}); 3248 else el.scrollIntoView({behavior:'smooth', block:'start'}); 3249 }); 3250 3251 /* Header timeline nodes */ 3252 const tl = { 3253 track: document.querySelector('.header--left .tl__track'), 3254 fill : document.querySelector('.header--left .tl__fill'), 3255 dot : document.querySelector('.header--left .tl__dot') 3256 }; 3257 3258 /* Active state + ARIA */ 3259 function setActive(i){ 3260 railItems.forEach((li,idx)=>{ 3261 li.classList.toggle('is-active', idx===i); 3262 const a = li.querySelector('.rail__btn'); 3263 if(a) a.setAttribute('aria-current', idx===i ? 'true' : 'false'); 3264 }); 3265 } 3266 3267 /* Center the active LI inside the non-scrollable rail window */ 3268 function centerRailOn(idx){ 3269 const host = railUL.parentElement; if(!host) return; 3270 const hostRect = host.getBoundingClientRect(); 3271 const ulRect = railUL.getBoundingClientRect(); 3272 const li = railItems[idx]; if(!li) return; 3273 const liRect = li.getBoundingClientRect(); 3274 3275 // current UL Y relative to host (translate we applied) 3276 const currentTx = parseFloat((railUL.style.getPropertyValue('--ty') || '0')); 3277 3278 // desired center offset so the LI center sits at host middle 3279 const hostMid = hostRect.top + hostRect.height / 2; 3280 const liMid = liRect.top + liRect.height / 2; 3281 const delta = hostMid - liMid; // how much the list should move 3282 3283 // compute new translate with clamping: don't expose blank space 3284 // measure full content height via last item 3285 const firstRect = railItems[0].getBoundingClientRect(); 3286 const lastRect = railItems[railItems.length-1].getBoundingClientRect(); 3287 const contentH = (lastRect.bottom - firstRect.top); 3288 const maxUp = 0; // topmost 3289 const maxDown = Math.min(0, hostRect.height - contentH); // bottommost (negative) 3290 let newTy = currentTx + delta; 3291 3292 newTy = Math.max(maxDown, Math.min(maxUp, newTy)); 3293 3294 // ease the movement a bit 3295 railUL.style.transition = 'transform .25s cubic-bezier(.2,.8,.2,1)'; 3296 railUL.style.transform = `translateY(${newTy}px)`; 3297 railUL.style.setProperty('--ty', newTy.toFixed(2)); // keep for next tick 3298 } 3299 3300 /* Helpers to find most-visible section and page progress */ 3301 function mostVisibleIndex(){ 3302 const vh = innerHeight || 1; 3303 let best = 0, bestOv = -1; 3304 for(let i=0;i<sections.length;i++){ 3305 const r = sections[i].getBoundingClientRect(); 3306 const ov = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top)); 3307 if(ov > bestOv){ bestOv = ov; best = i; } 3308 } 3309 return best; 3310 } 3311 function pageProgress01(){ 3312 const max = Math.max(1, document.documentElement.scrollHeight - innerHe
3312ight); 3313 return Math.min(1, Math.max(0, scrollY / max)); 3314 } 3315 3316 /* Per-frame: active section, center the rail, update timeline */ 3317 let active = -1; 3318 function frame(){ 3319 const i = mostVisibleIndex(); 3320 if(i !== active){ 3321 active = i; 3322 setActive(i); 3323 centerRailOn(i); 3324 } 3325 3326 const prog = pageProgress01(); 3327 if(tl.fill){ 3328 tl.fill.style.height = (prog * 100).toFixed(2) + '%'; 3329 } 3330 if(tl.dot){ 3331 // dot follows the active section's center relative to viewport inside the timeline column 3332 const r = sections[active].getBoundingClientRect(); 3333 const vh = innerHeight || 1; 3334 const y = ( (r.top + r.height * 0.5) / vh ); // 0..1 3335 tl.dot.style.top = (y * 100).toFixed(2) + '%'; 3336 } 3337 } 3338 3339 /* Lenis hook â keep timeline smooth */ 3340 if(lenis && typeof lenis.on === 'function'){ 3341 lenis.on('scroll', frame); 3342 }else{ 3343 addEventListener('scroll', frame, {passive:true}); 3344 } 3345 addEventListener('resize', ()=>{ if(active>=0){ centerRailOn(active); frame(); } }, {passive:true}); 3346 3347 /* Mobile menu toggle (existing class hooks) */ 3348 const toggle = document.querySelector('[data-nav-toggle-btn]'); 3349 if(toggle){ 3350 toggle.addEventListener('click', ()=>{ 3351 document.querySelector('.header').classList.toggle('nav-active'); 3352 toggle.classList.toggle('active'); 3353 }); 3354 } 3355 3356})(); 3357</script>
3357 3358 3359
3360<script> 3361 // Force 3 columns on key grids 3362 (function () { 3363 // add to <html> so it's very specific and easy to toggle later if needed 3364 document.documentElement.classList.add('force-3cols'); 3365 3366 // Optional: expose a toggle for debugging 3367 window.forceThreeCols = function (on = true) { 3368 document.documentElement.classList.toggle('force-3cols', !!on); 3369 }; 3370 })(); 3371</script>
3371 3372
3373<script> 3374 // Enable "force 3 columns" mode and fix service-list structure 3375 (function () { 3376 document.documentElement.classList.add('force-3c'); 3377 3378 // Make each <form.service-item> act as a grid item and its button fill it 3379 document.querySelectorAll('.service-content .service-list > form.service-item') 3380 .forEach(f => { 3381 f.classList.add('as-grid-item'); 3382 const btn = f.querySelector('button.service-item'); 3383 if (btn) btn.classList.add('fill'); 3384 }); 3385 3386 // Optional global toggle in console: forceThreeCols(false) to disable 3387 window.forceThreeCols = (on = true) => 3388 document.documentElement.classList.toggle('force-3c', !!on); 3389 })(); 3390</script>
3390 3391 3392
3393<script> 3394/* Force 3-column mode and keep page padding in sync with computed header width */ 3395(function () { 3396 const hdr = document.querySelector('header.header--left[data-header]'); 3397 if (!hdr) return; 3398 3399 // Ensure the class is present even if HTML wasn't edited 3400 hdr.classList.add('three-cols'); 3401 3402 function applyPadding() { 3403 // use the real rendered width so the content never overlaps 3404 const w = hdr.getBoundingClientRect().width; 3405 document.body.style.paddingLeft = innerWidth > 900 ? w + 'px' : '0px'; 3406 } 3407 3408 // Initial + on resize 3409 applyPadding(); 3410 addEventListener('resize', applyPadding, { passive: true }); 3411 3412 // If fonts load later and change widths, run once more 3413 if (document.fonts && document.fonts.ready) { 3414 document.fonts.ready.then(applyPadding).catch(() => {}); 3415 } 3416})(); 3417</script>
3417 3418<!-- ===== Corner Clock + Tall Attached Go To Top ===== --> 3419<div class="corner-clock" id="cornerClock" aria-live="off"> 3420 <!-- Tall attached button --> 3421 3422 3423 <!-- Clock card --> 3424 <div class="cc-card"> 3425 <div class="cc-inner" role="timer" aria-label="Local time"> 3426 <a class="logo" href="/"> 3427 <img alt="gatai logo" class="logo__img" decoding="async" height="200" src="/assets/images/1726256466608-removebg-preview.png" style="width: 80px;" width="200"/> 3428</a> 3429 <div class="cc-seg"> 3430 <div class="cc-val cc-day" id="ccDay">FR</div> 3431 <div class="cc-label">DAY</div> 3432 </div> 3433 3434 <div class="cc-sep" aria-hidden="true"></div> 3435 3436 <div class="cc-seg"> 3437 <div class="cc-val" id="ccHour">13</div> 3438 <div class="cc-label">HOURS</div> 3439 </div> 3440 3441 <div class="cc-sep" aria-hidden="true"></div> 3442 3443 <div class="cc-seg"> 3444 <div class="cc-val" id="ccMin">47</div> 3445 <div class="cc-label">MINUTES</div> 3446 </div> 3447 3448 <div class="cc-sep" aria-hidden="true"></div> 3449 3450 <div class="cc-seg"> 3451 <div class="cc-val" id="ccSec">18</div> 3452 <div class="cc-label">SECONDS</div> 3453 </div> 3454 3455 </div> 3456 3457 <div class="cc-date" id="ccDate" aria-hidden="true"></div> 3458 3459<div id="a11yBackdrop" hidden></div> 3460 3461<!-- Accessibility panel --> 3462<aside id="a11yPanel" 3463 role="dialog" 3464 aria-modal="true" 3465 aria-labelledby="a11yTitle" 3466 hidden> 3467 <header class="a11y-panel-head"> 3468 <div> 3469 <p class="a11y-panel-kicker">Accessibility</p> 3470 <h2 id="a11yTitle">Adjust how you view this site</h2> 3471 </div> 3472 <button type="button" 3473 class="a11y-close" 3474 data-a11y-close 3475 aria-label="Close accessibility menu"> 3476 <ion-icon name="close-outline" aria-hidden="true"></ion-icon> 3477 </button> 3478 </header> 3479 3480 <div class="a11y-panel-body"> 3481 <div class="a11y-tiles"> 3482 3483 <!-- Screen reader --> 3484 <button type="button" 3485 class="a11y-tile" 3486 data-a11y-action="screen-reader" 3487 aria-pressed="false"> 3488 <div class="a11y-tile-icon"> 3489 <ion-icon name="volume-high-outline"></ion-icon> 3490 </div> 3491 <div class="a11y-tile-text"> 3492 <span class="a11y-tile-title">Screen reader</span> 3493 <span class="a11y-tile-desc">Select text, press Alt+Shift+S</span> 3494 </div> 3495 </button> 3496 3497 <!-- Voice navigation --> 3498 <button type="button" 3499 class="a11y-tile" 3500 data-a11y-action="voice-nav" 3501 aria-pressed="false"> 3502 <div class="a11y-tile-icon"> 3503 <ion-icon name="mic-outline"></ion-icon> 3504 </div> 3505 <div class="a11y-tile-text"> 3506 <span class="a11y-tile-title">Voice navigation</span> 3507 <span class="a11y-tile-desc">Say âscroll down / upâ (Chrome)</span> 3508 </div> 3509 </button> 3510 3511 <!-- Invert colors (toggle) --> 3512 <button type="button" 3513 class="a11y-tile" 3514 data-a11y-action="invert-colors" 3515 aria-pressed="false"> 3516 <div class="a11y-tile-icon"> 3517 <ion-icon name="contrast-outline"></ion-icon> 3518 </div> 3519 <div class="a11y-tile-text"> 3520 <span class="a11y-tile-title">Invert colors</span> 3521 <span class="a11y-tile-desc">Invert all page colors</span> 3522 </div> 3523 </button> 3524 3525 <!-- Reading guide (vertical + horizontal) --> 3526 <button type="button" 3527 class="a11y-tile" 3528 data-a11y-action="reading-guide" 3529 aria-pressed="false"> 3530 <div class="a11y-tile-icon"> 3531 <ion-icon name="book-outline"></ion-icon> 3532 </div> 3533 <div class="a11y-tile-text"> 3534 <span class="a11y-tile-title">Reading guide</span> 3535 <span class="a11y-tile-desc">Vertical line + horizontal band</span> 3536 </div> 3537 </button> 3538 3539 <!-- Contrast theme (multi-state) --> 3540 <button type="button" 3541 class="a11y-tile" 3542 data-a11y-action="contrast-theme" 3543 data-a11y-type="cycle" 3544 aria-pressed="false"> 3545 <div class="a11y-tile-icon"> 3546 <ion-icon name="color-filter-outline"></ion-icon> 3547 </div> 3548 <div class="a11y-tile-text">
3549 <span class="a11y-tile-title">Light contrast</span> 3550 <span class="a11y-tile-desc js-a11y-label-contrast">Default</span> 3551 </div> 3552 <div class="a11y-state-bars" aria-hidden="true"> 3553 <span data-state="default"></span> 3554 <span data-state="light"></span> 3555 <span data-state="dark"></span> 3556 <span data-state="invert"></span> 3557 </div> 3558 </button> 3559 3560 <!-- Smart contrast --> 3561 <button type="button" 3562 class="a11y-tile" 3563 data-a11y-action="smart-contrast" 3564 aria-pressed="false"> 3565 <div class="a11y-tile-icon"> 3566 <ion-icon name="flash-outline"></ion-icon> 3567 </div> 3568 <div class="a11y-tile-text"> 3569 <span class="a11y-tile-title">Smart contrast</span> 3570 <span class="a11y-tile-desc">Stronger text/background contrast</span> 3571 </div> 3572 </button> 3573 3574 <!-- Highlight links --> 3575 <button type="button" 3576 class="a11y-tile" 3577 data-a11y-action="highlight-links" 3578 aria-pressed="false"> 3579 <div class="a11y-tile-icon"> 3580 <ion-icon name="link-outline"></ion-icon> 3581 </div> 3582 <div class="a11y-tile-text"> 3583 <span class="a11y-tile-title">Highlight links</span> 3584 <span class="a11y-tile-desc">Underline and outline all links</span> 3585 </div> 3586 </button> 3587 3588 <!-- Big cursor --> 3589 <button type="button" 3590 class="a11y-tile" 3591 data-a11y-action="big-cursor" 3592 aria-pressed="false"> 3593 <div class="a11y-tile-icon"> 3594 <ion-icon name="hand-left-outline"></ion-icon> 3595 </div> 3596 <div class="a11y-tile-text"> 3597 <span class="a11y-tile-title">Big cursor</span> 3598 <span class="a11y-tile-desc">Increase cursor size/visibility</span> 3599 </div> 3600 <div class="a11y-state-bars" aria-hidden="true"> 3601 <span data-state="on"></span> 3602 <span data-state="on"></span> 3603 <span data-state="on"></span> 3604 </div> 3605 </button> 3606 3607 <!-- Text spacing --> 3608 <button type="button" 3609 class="a11y-tile" 3610 data-a11y-action="text-spacing" 3611 aria-pressed="false"> 3612 <div class="a11y-tile-icon"> 3613 <ion-icon name="swap-vertical-outline"></ion-icon> 3614 </div> 3615 <div class="a11y-tile-text"> 3616 <span class="a11y-tile-title">Text spacing</span> 3617 <span class="a11y-tile-desc">More spacing between lines & words</span> 3618 </div> 3619 </button> 3620 3621 <!-- Pause animations --> 3622 <button type="button" 3623 class="a11y-tile" 3624 data-a11y-action="pause-animations" 3625 aria-pressed="false"> 3626 <div class="a11y-tile-icon"> 3627 <ion-icon name="pause-circle-outline"></ion-icon> 3628 </div> 3629 <div class="a11y-tile-text"> 3630 <span class="a11y-tile-title">Pause animations</span> 3631 <span class="a11y-tile-desc">Reduce motion & auto-play effects</span> 3632 </div> 3633 </button> 3634 3635 <!-- Hide images --> 3636 <button type="button" 3637 class="a11y-tile" 3638 data-a11y-action="hide-images" 3639 aria-pressed="false"> 3640 <div class="a11y-tile-icon"> 3641 <ion-icon name="image-outline"></ion-icon> 3642 </div> 3643 <div class="a11y-tile-text"> 3644 <span class="a11y-tile-title">Hide images</span> 3645 <span class="a11y-tile-desc">Fade out decorative images</span> 3646 </div> 3647 </button> 3648 3649 <!-- Bigger text --> 3650 <button type="button" 3651 class="a11y-tile" 3652 data-a11y-action="bigger-text" 3653 aria-pressed="false"> 3654 <div class="a11y-tile-icon"> 3655 <ion-icon name="text-outline"></ion-icon> 3656 </div> 3657 <div class="a11y-tile-text"> 3658 <span class="a11y-tile-title">Bigger text</span> 3659 <span class="a11y-tile-desc">Increase base font size</span> 3660 </div> 3661 </button> 3662 3663 <!-- Tooltips --> 3664 <button type="button" 3665 class="a11y-tile" 3666 data-a11y-action="tooltips" 3667 aria-pressed="false"> 3668 <div class="a11y-tile-icon"> 3669 <ion-icon name="information-circle-outline"></ion-icon> 3670 </div> 3671 <div class="a11y-tile-text">
3672 <span class="a11y-tile-title">Tooltips</span> 3673 <span class="a11y-tile-desc">Show hints on focus & hover</span> 3674 </div> 3675 </button> 3676 3677 <!-- Page structure --> 3678 <button type="button" 3679 class="a11y-tile" 3680 data-a11y-action="page-structure" 3681 aria-pressed="false"> 3682 <div class="a11y-tile-icon"> 3683 <ion-icon name="grid-outline"></ion-icon> 3684 </div> 3685 <div class="a11y-tile-text"> 3686 <span class="a11y-tile-title">Page structure</span> 3687 <span class="a11y-tile-desc">Outline headings & landmarks</span> 3688 </div> 3689 </button> 3690 3691 <!-- Line height (multi-state) --> 3692 <button type="button" 3693 class="a11y-tile" 3694 data-a11y-action="line-height" 3695 data-a11y-type="cycle" 3696 aria-pressed="false"> 3697 <div class="a11y-tile-icon"> 3698 <ion-icon name="swap-vertical-outline"></ion-icon> 3699 </div> 3700 <div class="a11y-tile-text"> 3701 <span class="a11y-tile-title">Line height</span> 3702 <span class="a11y-tile-desc js-a11y-label-line">Default</span> 3703 </div> 3704 <div class="a11y-state-bars" aria-hidden="true"> 3705 <span data-state="default"></span> 3706 <span data-state="1_5"></span> 3707 <span data-state="2"></span> 3708 </div> 3709 </button> 3710 3711 <!-- Text align (multi-state) --> 3712 <button type="button" 3713 class="a11y-tile" 3714 data-a11y-action="text-align" 3715 data-a11y-type="cycle" 3716 aria-pressed="false"> 3717 <div class="a11y-tile-icon"> 3718 <ion-icon name="reorder-three-outline"></ion-icon> 3719 </div> 3720 <div class="a11y-tile-text"> 3721 <span class="a11y-tile-title">Text align</span> 3722 <span class="a11y-tile-desc js-a11y-label-align">Default</span> 3723 </div> 3724 <div class="a11y-state-bars" aria-hidden="true"> 3725 <span data-state="default"></span> 3726 <span data-state="left"></span> 3727 <span data-state="justify"></span> 3728 </div> 3729 </button> 3730 3731 <!-- Dictionary --> 3732 <button type="button" 3733 class="a11y-tile" 3734 data-a11y-action="dictionary" 3735 aria-pressed="false"> 3736 <div class="a11y-tile-icon"> 3737 <ion-icon name="book-outline"></ion-icon> 3738 </div> 3739 <div class="a11y-tile-text"> 3740 <span class="a11y-tile-title">Dictionary</span> 3741 <span class="a11y-tile-desc">Double-click a word</span> 3742 </div> 3743 </button> 3744 3745 <!-- Saturation (multi-state) --> 3746 <button type="button" 3747 class="a11y-tile" 3748 data-a11y-action="saturation" 3749 data-a11y-type="cycle" 3750 aria-pressed="false"> 3751 <div class="a11y-tile-icon"> 3752 <ion-icon name="water-outline"></ion-icon> 3753 </div> 3754 <div class="a11y-tile-text"> 3755 <span class="a11y-tile-title">Saturation</span> 3756 <span class="a11y-tile-desc js-a11y-label-sat">Normal</span> 3757 </div> 3758 <div class="a11y-state-bars" aria-hidden="true"> 3759 <span data-state="default"></span> 3760 <span data-state="low"></span> 3761 <span data-state="high"></span> 3762 </div> 3763 </button> 3764 3765 </div> 3766 </div> 3767 3768 <footer class="a11y-panel-foot"> 3769 <button type="button" 3770 class="a11y-reset" 3771 data-a11y-reset> 3772 Reset all 3773 </button> 3774 <p class="a11y-note"> 3775 Your preferences are saved in this browser. 3776 </p> 3777 </footer> 3778</aside> 3779 3780<!-- Ionicons for icons -->
3781<script type="module" src="/assets/vendor/ionicons/dist/ionicons/ionicons.esm.js"></script>
vendor: 1 bytes, line 3781
3781
3782<script nomodule src="/assets/vendor/ionicons/dist/ionicons/ionicons.js"></script>
3782 3783<!-- Accessibility controller --> 3784<!-- Accessibility controller --> 3785 3786 3787 3788 </div> 3789 <button class="cc-top" id="ccTopBtn" aria-label="Go to top" title="Go to top"> 3790 <svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"> 3791 <path d="M6 15l6-6 6 6" fill="none" stroke="currentColor" stroke-width="2" 3792 stroke-linecap="round" stroke-linejoin="round"/> 3793 </svg> 3794 </button> 3795</div> 3796 3797
3798<script> 3799(() => { 3800 const root = document.getElementById('cornerClock'); 3801 const topBtn = document.getElementById('ccTopBtn'); 3802 const dayEl = document.getElementById('ccDay'); 3803 const hEl = document.getElementById('ccHour'); 3804 const mEl = document.getElementById('ccMin'); 3805 const sEl = document.getElementById('ccSec'); 3806 const dateEl = document.getElementById('ccDate'); 3807 if (!root || !topBtn || !dayEl || !hEl || !mEl || !sEl || !dateEl) return; 3808 3809 const prefersReduced = matchMedia('(prefers-reduced-motion: reduce)').matches; 3810 const DOW2 = ['SU','MO','TU','WE','TH','FR','SA']; 3811 const pad2 = n => String(n).padStart(2,'0'); 3812 3813 function setText(el, val){ 3814 if (el.textContent !== val){ 3815 el.textContent = val; 3816 if (!prefersReduced){ 3817 el.classList.add('tick'); 3818 setTimeout(() => el.classList.remove('tick'), 120); 3819 } 3820 } 3821 } 3822 3823 function render(){ 3824 const now = new Date(); 3825 setText(dayEl, DOW2[now.getDay()]); 3826 setText(hEl, pad2(now.getHours())); 3827 setText(mEl, pad2(now.getMinutes())); 3828 setText(sEl, pad2(now.getSeconds())); 3829 3830 const stamp = now.toLocaleString(undefined, { 3831 weekday:'short', 3832 year:'numeric', 3833 month:'short', 3834 day:'2-digit', 3835 hour:'2-digit', 3836 minute:'2-digit' 3837 }); 3838 3839 dateEl.textContent = `· G-ATAI Inc. · All rights reserved ${stamp} ·`; 3840 } 3841 3842 render(); 3843 const ms = 1000 - (Date.now() % 1000); 3844 setTimeout(() => { render(); setInterval(render, 1000); }, ms); 3845 3846 const threshold = 200; 3847 function updateTopButton(){ 3848 const show = (window.scrollY||0) > threshold; 3849 topBtn.classList.toggle('is-visible', show); 3850 root.classList.toggle('no-top', !show); 3851 } 3852 addEventListener('scroll', updateTopButton, { passive:true }); 3853 addEventListener('resize', updateTopButton, { passive:true }); 3854 updateTopButton(); 3855 3856 function goNativeAndTop(){ if(window.lenis) window.lenis.scrollTo(0); else window.scrollTo({top:0,behavior:matchMedia('(prefers-reduced-motion: reduce)').matches?'instant':'smooth'}); } 3857 3858 topBtn.addEventListener('click', (e) => { 3859 e.preventDefault(); 3860 goNativeAndTop(); 3861 }); 3862})(); 3863</script>
vendor: 1 bytes, line 3863
3863
3864<script> 3865(() => { 3866 const prefersHover = window.matchMedia("(hover: hover)").matches; 3867 3868 document.addEventListener("DOMContentLoaded", () => { 3869 const menus = document.querySelectorAll(".hero-menu"); 3870 if (!menus.length) return; 3871 3872 menus.forEach(menu => { 3873 const toggle = menu.querySelector(".hero-menu-toggle"); 3874 const panel = menu.querySelector(".hero-menu-panel"); 3875 const root = panel ? panel.querySelector(".hero-menu-root") : null; 3876 if (!toggle || !panel || !root) return; 3877 3878 // ---------- OPEN / CLOSE MAIN PANEL ---------- 3879 const closeMenu = () => { 3880 if (!menu.classList.contains("is-open")) return; 3881 menu.classList.remove("is-open", "is-flip"); 3882 toggle.setAttribute("aria-expanded", "false"); 3883 panel.setAttribute("aria-hidden", "true"); 3884 3885 // close any open sub-shapes 3886 root.querySelectorAll(".hero-menu-sub.is-open").forEach(sub => { 3887 sub.classList.remove("is-open"); 3888 }); 3889 }; 3890 3891 const openMenu = () => { 3892 menu.classList.add("is-open"); 3893 toggle.setAttribute("aria-expanded", "true"); 3894 panel.setAttribute("aria-hidden", "false"); 3895 3896 // decide if we flip the whole cyberpunk block above 3897 requestAnimationFrame(() => { 3898 const rect = panel.getBoundingClientRect(); 3899 const vh = window.innerHeight || document.documentElement.clientHeight; 3900 3901 if (rect.bottom > vh - 16) { 3902 menu.classList.add("is-flip"); 3903 } else { 3904 menu.classList.remove("is-flip"); 3905 } 3906 }); 3907 }; 3908 3909 toggle.setAttribute("aria-haspopup", "true"); 3910 toggle.setAttribute("aria-expanded", "false"); 3911 3912 toggle.addEventListener("click", e => { 3913 e.stopPropagation(); 3914 if (menu.classList.contains("is-open")) { 3915 closeMenu(); 3916 } else { 3917 openMenu(); 3918 } 3919 }); 3920 3921 // Close when clicking outside 3922 document.addEventListener("click", e => { 3923 if (!menu.classList.contains("is-open")) return; 3924 if (!menu.contains(e.target)) closeMenu(); 3925 }); 3926 3927 // Close with ESC 3928 document.addEventListener("keydown", e => { 3929 if (e.key === "Escape" && menu.classList.contains("is-open")) { 3930 closeMenu(); 3931 toggle.focus(); 3932 } 3933 }); 3934 3935 // ---------- SUBMENUS = CYBERPUNK FLY-OUT SHAPES ---------- 3936 const setupSubmenu = li => { 3937 const sub = li.querySelector(".hero-menu-sub"); 3938 const trigger = 3939 li.querySelector(".menu-parent") || 3940 li.querySelector("a, button"); 3941 3942 if (!sub || !trigger) return; 3943 3944 const openSub = () => { 3945 // close other subs inside this menu 3946 root.querySelectorAll(".hero-menu-sub.is-open").forEach(other => { 3947 if (other !== sub) other.classList.remove("is-open"); 3948 }); 3949 3950 sub.classList.add("is-open"); 3951 3952 // check if we need to flip this shape to the left 3953 requestAnimationFrame(() => { 3954 const rect = sub.getBoundingClientRect(); 3955 const vw = window.innerWidth || document.documentElement.clientWidth; 3956 3957 if (rect.right > vw - 16) { 3958 sub.classList.add("align-left"); 3959 } else if (rect.left < 16) { 3960 sub.classList.remove("align-left"); 3961 } 3962 }); 3963 }; 3964 3965 const closeSub = () => { 3966 sub.classList.remove("is-open"); 3967 }; 3968 3969 // DESKTOP / HOVER: shapes open like neon panels on hover 3970 if (prefersHover && window.innerWidth >= 768) { 3971 li.addEventListener("pointerenter", () => { 3972 if (!menu.classList.contains("is-open")) openMenu(); 3973 openSub(); 3974 }); 3975 3976 li.addEventListener("pointerleave", () => { 3977 closeSub(); 3978 }); 3979 } else { 3980 // MOBILE / TOUCH: tap parent to open / close 3981 trigger.addEventListener("click", evt => { 3982 const isOpen = sub.classList.contains("is-open"); 3983 if (isOpen) { 3984 closeSub(); 3985 } else { 3986 openSub(); 3987 } 3988 3989 // prevent instant navigation if this item has children 3990 if (trigger.tagName === "A") { 3991 evt.preventDefault(); 3992 } 3993 }); 3994 } 3995 }; 3996 3997 root.querySelectorAll("li.has-sub").forEach(setupSubmenu); 3998 3999 // Re-evaluate flip on resize (keeps panel shapes correct on large screens) 4000 window.addEventListener("resize", () => { 4001 if (!menu.classList.contains("is-open")) return; 4002 requestAnimationFrame(() => { 4003 const rect = panel.getBoundingClientRect(); 4004 const vh = window.innerHeight || document.documentElement.clientHeight; 4005 if (rect.bottom > vh - 16) { 4006 menu.classList.add("is-flip"); 4007 } else { 4008 menu.classList.remove("is-flip"); 4009 } 4010 }); 4011 }); 4012 }); 4013 }); 4014})(); 4015</script>
4015 4016
4017<script> 4018 4019// diapo-text-fadeout-only.js 4020// Behavior: 4021// - No fade-in. 4022// - Panel i text stays at opacity=1 until panel (i+1) rises enough. 4023// - As panel (i+1) approaches top (0), panel i text fades out to 0. 4024// - Incoming panel text always stays opacity=1. 4025 4026(function () { 4027 const clamp01 = (x) => (x < 0 ? 0 : x > 1 ? 1 : x); 4028 4029 const ROOT = 4030 document.querySelector("[data-diapo-root]") || 4031 document.querySelector("#features.diapo-root") || 4032 document.querySelector("#features"); 4033 4034 if (!ROOT) return; 4035 4036 const panels = Array.from(ROOT.querySelectorAll(".diapo-panel")); 4037 if (panels.length < 2) return; 4038 4039 // Which text parts to fade (NOT the shell) 4040 const targets = panels.map((p) => { 4041 const head = p.querySelector(".diapo-head"); 4042 const right = p.querySelector(".diapo-right"); 4043 const actions = p.querySelector(".diapo-actions"); 4044 const list = [head, right, actions].filter(Boolean); 4045 4046 list.forEach((el) => { 4047 el.style.willChange = "opacity, transform"; 4048 el.style.transformOrigin = "center center"; 4049 // default 4050 el.style.opacity = "1"; 4051 el.style.transform = "translateY(0px)"; 4052 el.setAttribute("aria-hidden", "false"); 4053 }); 4054 4055 return list; 4056 }); 4057 4058 // Smooth values to avoid jitter 4059 const aSmooth = new Array(panels.length).fill(1); 4060 let last = performance.now(); 4061 4062 function inView() { 4063 const r = ROOT.getBoundingClientRect(); 4064 return r.bottom > 0 && r.top < innerHeight; 4065 } 4066 4067 function applyText(i, a) { 4068 // tiny lift while fading out (keeps it readable) 4069 const y = (1 - a) * 14; // px 4070 for (const el of targets[i]) { 4071 el.style.opacity = String(a); 4072 el.style.transform = `translateY(${y}px)`; 4073 el.setAttribute("aria-hidden", a < 0.12 ? "true" : "false"); 4074 } 4075 } 4076 4077 function loop() { 4078 requestAnimationFrame(loop); 4079 4080 const now = performance.now(); 4081 let dt = (now - last) / 1000; 4082 last = now; 4083 if (dt > 0.25) dt = 0; 4084 4085 if (!inView() || document.hidden) return; 4086 4087 const rects = panels.map((p) => p.getBoundingClientRect()); 4088 4089 // Fade settings: 4090 // start fading previous when next panel's TOP enters this region (from below) 4091 const fadeStartTop = innerHeight * 0.45; // start fade when next panel top reaches 45% viewport 4092 const fadeEndTop = 0; // fully faded when next panel top hits top 4093 4094 // Panels: last never fades out (no next panel) 4095 for (let i = 0; i < panels.length; i++) { 4096 let targetAlpha = 1; 4097 4098 if (i < panels.length - 1) { 4099 const nextTop = rects[i + 1].top; 4100 4101 // If next panel is not yet high enough => keep 1 4102 // Once nextTop <= fadeStartTop => begin fade 4103 const prog = clamp01((fadeStartTop - nextTop) / Math.max(1, (fadeStartTop - fadeEndTop))); 4104 4105 // IMPORTANT: no fade-in for incoming; only fade-out for previous 4106 targetAlpha = 1 - prog; 4107 } 4108 4109 // Smooth alpha 4110 const k = 1 - Math.exp(-14 * dt); 4111 aSmooth[i] += (targetAlpha - aSmooth[i]) * k; 4112 4113 applyText(i, aSmooth[i]); 4114 } 4115 4116 // Optional: only top-most panel is clickable (prevents button overlap) 4117 // Comment this block out if you want all buttons clickable. 4118 let topmost = 0; 4119 for (let i = 0; i < rects.length; i++) { 4120 if (rects[i].top <= 1 && rects[i].bottom >= 1) topmost = i; // last one wins 4121 } 4122 for (let i = 0; i < panels.length; i++) { 4123 panels[i].style.pointerEvents = i === topmost ? "auto" : "none"; 4124 } 4125 } 4126 4127 loop(); 4128})(); 4129 4130</script>
4130 4131 4132 4133
4133<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4bc70e2c01a94c73b74392e4234840661791215815920" integrity="sha512-L0ha0OXavK/8okipN9F8BtP84dg9DUhPERbBXzwI6dgTA55d2+yweo3pn5CSFYs45/r8md2+xvUPtTdvTNRfjA==" data-cf-beacon='{"version":"2024.11.0","token":"07c4e554568b4c148eae807b11ab9093","r":1,"spa":2}' crossorigin="anonymous"></script>
4133 4134</body> 4135</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.