| 1 | --- |
| 2 | import { getImage, Image } from "astro:assets"; |
| 3 | import appIcon from "../assets/icon.webp"; |
| 4 | import socialCard from "../assets/social-card.webp"; |
| 5 | import dmSansLatinUrl from "../assets/fonts/dm-sans-latin.woff2?url"; |
| 6 | import "../styles/fonts.css"; |
| 7 | import { |
| 8 | ANDROID_PLAY_STORE_URL, |
| 9 | GITHUB_REPOSITORY_URL, |
| 10 | IOS_APP_STORE_URL, |
| 11 | MARKETING_STATS, |
| 12 | } from "../lib/site"; |
| 13 | |
| 14 | interface Props { |
| 15 | title?: string; |
| 16 | description?: string; |
| 17 | pageClass?: string; |
| 18 | } |
| 19 | |
| 20 | const { |
| 21 | title = "T3 Code", |
| 22 | description = "T3 Code. The open-source control plane for coding agents.", |
| 23 | pageClass, |
| 24 | } = Astro.props; |
| 25 | |
| 26 | // Social preview card, shared by og:image and twitter:image. Link unfurlers |
| 27 | // (Instagram, iMessage, X, Slack) want a 1200x630 jpg or png at an absolute URL. |
| 28 | // The source is a 2x export so the downscale stays sharp. |
| 29 | const socialImage = await getImage({ |
| 30 | src: socialCard, |
| 31 | width: 1200, |
| 32 | height: 630, |
| 33 | format: "jpg", |
| 34 | quality: 90, |
| 35 | }); |
| 36 | const socialImageUrl = new URL(socialImage.src, Astro.site); |
| 37 | const socialImageAlt = |
| 38 | "T3 Code: the open-source control plane for coding agents, with Claude Code, Codex, Cursor, OpenCode, Grok and Antigravity."; |
| 39 | const canonicalUrl = new URL(Astro.url.pathname, Astro.site); |
| 40 | --- |
| 41 | |
| 42 | <html lang="en"> |
| 43 | <head> |
| 44 | <meta charset="UTF-8" /> |
| 45 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 46 | <meta name="description" content={description} /> |
| 47 | <link rel="canonical" href={canonicalUrl} /> |
| 48 | <meta property="og:type" content="website" /> |
| 49 | <meta property="og:site_name" content="T3 Code" /> |
| 50 | <meta property="og:url" content={canonicalUrl} /> |
| 51 | <meta property="og:title" content={title} /> |
| 52 | <meta property="og:description" content={description} /> |
| 53 | <meta property="og:image" content={socialImageUrl} /> |
| 54 | <meta property="og:image:width" content="1200" /> |
| 55 | <meta property="og:image:height" content="630" /> |
| 56 | <meta property="og:image:alt" content={socialImageAlt} /> |
| 57 | <meta name="twitter:card" content="summary_large_image" /> |
| 58 | <meta name="twitter:site" content="@t3dotgg" /> |
| 59 | <meta name="twitter:title" content={title} /> |
| 60 | <meta name="twitter:description" content={description} /> |
| 61 | <meta name="twitter:image" content={socialImageUrl} /> |
| 62 | <meta name="twitter:image:alt" content={socialImageAlt} /> |
| 63 | <link |
| 64 | rel="preload" |
| 65 | href={dmSansLatinUrl} |
| 66 | as="font" |
| 67 | type="font/woff2" |
| 68 | crossorigin="anonymous" |
| 69 | /> |
| 70 | <link rel="icon" href="/favicon.ico" sizes="48x48" /> |
| 71 | <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32" /> |
| 72 | <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16" /> |
| 73 | <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> |
| 74 | <title>{title}</title> |
| 75 | </head> |
| 76 | <body> |
| 77 | <div class:list={["page", pageClass]}> |
| 78 | <nav class="nav"> |
| 79 | <div class="nav-inner"> |
| 80 | <a href="/" class="nav-brand" aria-label="T3 Code home"> |
| 81 | <svg |
| 82 | class="nav-brand-mark" |
| 83 | viewBox="15.5309 37 94.3941 56.96" |
| 84 | fill="currentColor" |
| 85 | aria-hidden="true" |
| 86 | > |
| 87 | <path d="M33.4509 93V47.56H15.5309V37H64.3309V47.56H46.4109V93H33.4509ZM86.7253 93.96C82.832 93.96 78.9653 93.4533 75.1253 92.44C71.2853 91.3733 68.032 89.88 65.3653 87.96L70.4053 78.04C72.5386 79.5867 75.0186 80.8133 77.8453 81.72C80.672 82.6267 83.5253 83.08 86.4053 83.08C89.6586 83.08 92.2186 82.44 94.0853 81.16C95.952 79.88 96.8853 78.12 96.8853 75.88C96.8853 73.7467 96.0586 72.0667 94.4053 70.84C92.752 69.6133 90.0853 69 86.4053 69H80.4853V60.44L96.0853 42.76L97.5253 47.4H68.1653V37H107.365V45.4L91.8453 63.08L85.2853 59.32H89.0453C95.9253 59.32 101.125 60.8667 104.645 63.96C108.165 67.0533 109.925 71.0267 109.925 75.88C109.925 79.0267 109.099 81.9867 107.445 84.76C105.792 87.48 103.259 89.6933 99.8453 91.4C96.432 93.1067 92.0586 93.96 86.7253 93.96Z" /> |
| 88 | </svg> |
| 89 | <span class="nav-brand-name">Code</span> |
| 90 | </a> |
| 91 | <div class="nav-right"> |
| 92 | <a class="nav-link" href="/download">Download</a> |
| 93 | <a |
| 94 | class="nav-stars" |
| 95 | href={GITHUB_REPOSITORY_URL} |
| 96 | target="_blank" |
| 97 | rel="noopener noreferrer" |
| 98 | aria-label={`Star T3 Code on GitHub — ${MARKETING_STATS.githubStars} stars`} |
| 99 | > |
| 100 | <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> |
| 101 | <path d="M12 2.3l2.9 5.88 6.49.94-4.7 4.58 1.11 6.47L12 17.12l-5.8 3.05 1.11-6.47-4.7-4.58 6.49-.94L12 2.3z"/> |
| 102 | </svg> |
| 103 | <span><strong>{MARKETING_STATS.githubStars}</strong> GitHub stars</span> |
| 104 | </a> |
| 105 | </div> |
| 106 | </div> |
| 107 | </nav> |
| 108 | |
| 109 | <main class="main"> |
| 110 | <slot /> |
| 111 | </main> |
| 112 | |
| 113 | <footer class="footer"> |
| 114 | <div class="footer-inner"> |
| 115 | <div class="footer-brand"> |
| 116 | <Image |
| 117 | src={appIcon} |
| 118 | alt="" |
| 119 | width={20} |
| 120 | height={20} |
| 121 | densities={[1, 2, 3]} |
| 122 | format="webp" |
| 123 | quality={90} |
| 124 | loading="lazy" |
| 125 | decoding="async" |
| 126 | /> |
| 127 | <span>&copy; {new Date().getFullYear()} T3 Tools Inc · MIT licensed</span> |
| 128 | </div> |
| 129 | <div class="footer-links"> |
| 130 | <a href={GITHUB_REPOSITORY_URL} target="_blank" rel="noopener noreferrer">GitHub</a> |
| 131 | <a href="https://discord.gg/jn4EGJjrvv" target="_blank" rel="noopener noreferrer">Discord</a> |
| 132 | <a href="/download">Download</a> |
| 133 | <a href={IOS_APP_STORE_URL} target="_blank" rel="noopener noreferrer">iOS app</a> |
| 134 | <a href={ANDROID_PLAY_STORE_URL} target="_blank" rel="noopener noreferrer">Android app</a> |
| 135 | <a href="/terms-of-service">Terms</a> |
| 136 | <a href="/privacy-policy">Privacy</a> |
| 137 | <a href="/security-policy">Security</a> |
| 138 | </div> |
| 139 | </div> |
| 140 | </footer> |
| 141 | </div> |
| 142 | |
| 143 | <script> |
| 144 | const nav = document.querySelector(".nav"); |
| 145 | const onScroll = () => { |
| 146 | if (!nav) return; |
| 147 | if (window.scrollY > 12) nav.classList.add("is-scrolled"); |
| 148 | else nav.classList.remove("is-scrolled"); |
| 149 | }; |
| 150 | window.addEventListener("scroll", onScroll, { passive: true }); |
| 151 | onScroll(); |
| 152 | </script> |
| 153 | </body> |
| 154 | </html> |
| 155 | |
| 156 | <style is:global> |
| 157 | :root { |
| 158 | --bg: #09090b; |
| 159 | --bg-elev: #0c0c0e; |
| 160 | --bg-card: #111113; |
| 161 | --fg: #fafafa; |
| 162 | --fg-muted: #a1a1aa; |
| 163 | --fg-dim: #71717a; |
| 164 | --fg-faint: #52525b; |
| 165 | --border: rgba(255, 255, 255, 0.08); |
| 166 | --border-strong: rgba(255, 255, 255, 0.14); |
| 167 | --accent-h: 250; |
| 168 | --accent: oklch(0.68 0.17 var(--accent-h)); |
| 169 | --accent-dim: oklch(0.68 0.17 var(--accent-h) / 0.15); |
| 170 | --ok: oklch(0.72 0.16 150); |
| 171 | --warn: oklch(0.76 0.15 75); |
| 172 | --font-sans: "DM Sans", -apple-system, BlinkMacSystemFont, system-ui, sans-serif; |
| 173 | --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; |
| 174 | --radius: 12px; |
| 175 | --radius-sm: 8px; |
| 176 | --radius-lg: 16px; |
| 177 | } |
| 178 | |
| 179 | * { |
| 180 | box-sizing: border-box; |
| 181 | margin: 0; |
| 182 | padding: 0; |
| 183 | } |
| 184 | |
| 185 | html { |
| 186 | color-scheme: dark; |
| 187 | scroll-behavior: smooth; |
| 188 | } |
| 189 | |
| 190 | body { |
| 191 | background: var(--bg); |
| 192 | color: var(--fg); |
| 193 | font-family: var(--font-sans); |
| 194 | font-feature-settings: "ss01", "ss02"; |
| 195 | -webkit-font-smoothing: antialiased; |
| 196 | -moz-osx-font-smoothing: grayscale; |
| 197 | min-height: 100vh; |
| 198 | overflow-x: hidden; |
| 199 | line-height: 1.5; |
| 200 | } |
| 201 | |
| 202 | body::after { |
| 203 | content: ""; |
| 204 | position: fixed; |
| 205 | inset: 0; |
| 206 | pointer-events: none; |
| 207 | opacity: 0.035; |
| 208 | background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); |
| 209 | background-repeat: repeat; |
| 210 | background-size: 256px 256px; |
| 211 | z-index: 9999; |
| 212 | } |
| 213 | |
| 214 | a { |
| 215 | color: inherit; |
| 216 | text-decoration: none; |
| 217 | } |
| 218 | |
| 219 | button { |
| 220 | font-family: inherit; |
| 221 | cursor: pointer; |
| 222 | border: 0; |
| 223 | background: none; |
| 224 | color: inherit; |
| 225 | } |
| 226 | |
| 227 | img, |
| 228 | svg { |
| 229 | display: block; |
| 230 | max-width: 100%; |
| 231 | } |
| 232 | |
| 233 | .eyebrow { |
| 234 | font-family: var(--font-mono); |
| 235 | font-size: 11px; |
| 236 | letter-spacing: 0.14em; |
| 237 | text-transform: uppercase; |
| 238 | color: var(--fg-dim); |
| 239 | font-weight: 500; |
| 240 | } |
| 241 | |
| 242 | .display { |
| 243 | font-weight: 500; |
| 244 | letter-spacing: -0.035em; |
| 245 | line-height: 1.05; |
| 246 | } |
| 247 | |
| 248 | .container { |
| 249 | width: 100%; |
| 250 | max-width: 1240px; |
| 251 | margin: 0 auto; |
| 252 | padding: 0 32px; |
| 253 | } |
| 254 | |
| 255 | .btn { |
| 256 | display: inline-flex; |
| 257 | align-items: center; |
| 258 | justify-content: center; |
| 259 | gap: 8px; |
| 260 | font-weight: 500; |
| 261 | font-size: 14px; |
| 262 | padding: 10px 18px; |
| 263 | border-radius: 8px; |
| 264 | transition: transform 0.18s ease, background 0.18s ease, |
| 265 | border-color 0.18s ease, color 0.18s ease; |
| 266 | white-space: nowrap; |
| 267 | } |
| 268 | |
| 269 | .btn-primary { |
| 270 | background: var(--fg); |
| 271 | color: #09090b; |
| 272 | font-weight: 600; |
| 273 | } |
| 274 | |
| 275 | .btn-primary:hover { |
| 276 | transform: translateY(-1px); |
| 277 | background: #fff; |
| 278 | } |
| 279 | |
| 280 | .btn-primary:active { |
| 281 | transform: translateY(0); |
| 282 | } |
| 283 | |
| 284 | .btn-ghost { |
| 285 | color: var(--fg-muted); |
| 286 | border: 1px solid var(--border); |
| 287 | background: transparent; |
| 288 | } |
| 289 | |
| 290 | .btn-ghost:hover { |
| 291 | color: var(--fg); |
| 292 | border-color: var(--border-strong); |
| 293 | background: rgba(255, 255, 255, 0.02); |
| 294 | } |
| 295 | |
| 296 | .btn-lg { |
| 297 | padding: 14px 22px; |
| 298 | font-size: 15px; |
| 299 | border-radius: 10px; |
| 300 | } |
| 301 | |
| 302 | .tile { |
| 303 | position: relative; |
| 304 | border: 1px solid var(--border); |
| 305 | border-radius: var(--radius); |
| 306 | background: linear-gradient(180deg, rgba(255, 255, 255, 0.015), rgba(255, 255, 255, 0)); |
| 307 | overflow: hidden; |
| 308 | } |
| 309 | |
| 310 | :focus-visible { |
| 311 | outline: 2px solid var(--accent); |
| 312 | outline-offset: 2px; |
| 313 | border-radius: 4px; |
| 314 | } |
| 315 | |
| 316 | @keyframes fade-in { |
| 317 | from { |
| 318 | opacity: 0; |
| 319 | transform: translateY(8px); |
| 320 | } |
| 321 | to { |
| 322 | opacity: 1; |
| 323 | transform: translateY(0); |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | /* Page-load sequence: [data-rise] plays once with delay --d. */ |
| 328 | @keyframes rise { |
| 329 | from { opacity: 0; transform: translateY(16px); } |
| 330 | to { opacity: 1; transform: none; } |
| 331 | } |
| 332 | [data-rise] { |
| 333 | animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both; |
| 334 | animation-delay: var(--d, 0ms); |
| 335 | } |
| 336 | @media (prefers-reduced-motion: reduce) { |
| 337 | [data-rise] { animation: none; } |
| 338 | } |
| 339 | </style> |
| 340 | |
| 341 | <style> |
| 342 | .page { |
| 343 | min-height: 100vh; |
| 344 | display: flex; |
| 345 | flex-direction: column; |
| 346 | overflow-x: clip; |
| 347 | } |
| 348 | |
| 349 | .nav { |
| 350 | position: sticky; |
| 351 | top: 0; |
| 352 | z-index: 50; |
| 353 | backdrop-filter: blur(18px); |
| 354 | -webkit-backdrop-filter: blur(18px); |
| 355 | background: color-mix(in srgb, var(--bg) 75%, transparent); |
| 356 | border-bottom: 1px solid transparent; |
| 357 | transition: border-color 0.3s ease; |
| 358 | } |
| 359 | |
| 360 | .nav.is-scrolled { |
| 361 | border-bottom-color: var(--border); |
| 362 | } |
| 363 | |
| 364 | .nav-inner { |
| 365 | max-width: 1240px; |
| 366 | margin: 0 auto; |
| 367 | padding: 12px 32px; |
| 368 | display: flex; |
| 369 | align-items: center; |
| 370 | justify-content: space-between; |
| 371 | gap: 24px; |
| 372 | } |
| 373 | |
| 374 | .nav-brand { |
| 375 | display: inline-flex; |
| 376 | align-items: baseline; |
| 377 | gap: 4px; |
| 378 | color: var(--fg); |
| 379 | transition: color 0.2s ease; |
| 380 | } |
| 381 | |
| 382 | .nav-brand:hover { |
| 383 | color: #fff; |
| 384 | } |
| 385 | |
| 386 | .nav-brand-mark { |
| 387 | height: 1cap; |
| 388 | width: auto; |
| 389 | flex: none; |
| 390 | } |
| 391 | |
| 392 | .nav-brand-name { |
| 393 | font-size: 16px; |
| 394 | font-weight: 500; |
| 395 | letter-spacing: -0.025em; |
| 396 | line-height: 1; |
| 397 | color: var(--fg-muted); |
| 398 | } |
| 399 | |
| 400 | .nav-right { |
| 401 | display: inline-flex; |
| 402 | align-items: center; |
| 403 | gap: 8px; |
| 404 | } |
| 405 | |
| 406 | .nav-link { |
| 407 | padding: 0 10px; |
| 408 | color: var(--fg-muted); |
| 409 | font-size: 13px; |
| 410 | letter-spacing: -0.01em; |
| 411 | white-space: nowrap; |
| 412 | transition: color 0.18s ease; |
| 413 | } |
| 414 | |
| 415 | .nav-link:hover { |
| 416 | color: var(--fg); |
| 417 | } |
| 418 | |
| 419 | .nav-stars { |
| 420 | display: inline-flex; |
| 421 | align-items: center; |
| 422 | gap: 7px; |
| 423 | height: 36px; |
| 424 | padding: 0 14px; |
| 425 | border: 1px solid var(--border); |
| 426 | border-radius: 999px; |
| 427 | background: rgba(255, 255, 255, 0.02); |
| 428 | color: var(--fg-muted); |
| 429 | font-size: 13px; |
| 430 | letter-spacing: -0.01em; |
| 431 | white-space: nowrap; |
| 432 | transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease; |
| 433 | } |
| 434 | |
| 435 | .nav-stars:hover { |
| 436 | color: var(--fg); |
| 437 | border-color: var(--border-strong); |
| 438 | background: rgba(255, 255, 255, 0.04); |
| 439 | } |
| 440 | |
| 441 | .nav-stars strong { |
| 442 | color: var(--fg); |
| 443 | font-weight: 600; |
| 444 | } |
| 445 | |
| 446 | .nav-stars svg { |
| 447 | color: var(--warn); |
| 448 | flex-shrink: 0; |
| 449 | } |
| 450 | |
| 451 | .main { |
| 452 | flex: 1; |
| 453 | display: flex; |
| 454 | flex-direction: column; |
| 455 | } |
| 456 | |
| 457 | .footer { |
| 458 | margin-top: auto; |
| 459 | border-top: 1px solid var(--border); |
| 460 | padding: 40px 0 32px; |
| 461 | } |
| 462 | |
| 463 | .footer-inner { |
| 464 | max-width: 1240px; |
| 465 | margin: 0 auto; |
| 466 | padding: 0 32px; |
| 467 | display: flex; |
| 468 | flex-wrap: wrap; |
| 469 | gap: 24px; |
| 470 | align-items: center; |
| 471 | justify-content: space-between; |
| 472 | } |
| 473 | |
| 474 | .footer-brand { |
| 475 | display: flex; |
| 476 | align-items: center; |
| 477 | gap: 10px; |
| 478 | font-size: 13px; |
| 479 | color: var(--fg-dim); |
| 480 | } |
| 481 | |
| 482 | .footer-brand img { |
| 483 | width: 20px; |
| 484 | height: 20px; |
| 485 | border-radius: 4px; |
| 486 | } |
| 487 | |
| 488 | .footer-links { |
| 489 | display: flex; |
| 490 | flex-wrap: wrap; |
| 491 | justify-content: flex-end; |
| 492 | gap: 20px; |
| 493 | } |
| 494 | |
| 495 | .footer-links a { |
| 496 | font-size: 13px; |
| 497 | color: var(--fg-dim); |
| 498 | transition: color 0.2s ease; |
| 499 | } |
| 500 | |
| 501 | .footer-links a:hover { |
| 502 | color: var(--fg); |
| 503 | } |
| 504 | |
| 505 | @media (max-width: 820px) { |
| 506 | .nav-inner, |
| 507 | .footer-inner { |
| 508 | padding-left: 20px; |
| 509 | padding-right: 20px; |
| 510 | } |
| 511 | |
| 512 | .legal-document-page .nav { |
| 513 | display: none; |
| 514 | } |
| 515 | |
| 516 | .footer { |
| 517 | padding-bottom: max(32px, env(safe-area-inset-bottom)); |
| 518 | } |
| 519 | |
| 520 | .footer-inner { |
| 521 | align-items: flex-start; |
| 522 | flex-direction: column; |
| 523 | gap: 28px; |
| 524 | } |
| 525 | |
| 526 | .footer-links { |
| 527 | display: grid; |
| 528 | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| 529 | width: 100%; |
| 530 | gap: 14px 20px; |
| 531 | justify-content: initial; |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | @media (max-width: 420px) { |
| 536 | .nav-inner { |
| 537 | gap: 12px; |
| 538 | } |
| 539 | |
| 540 | .nav-stars { |
| 541 | height: 34px; |
| 542 | gap: 6px; |
| 543 | padding: 0 12px; |
| 544 | font-size: 12px; |
| 545 | } |
| 546 | } |
| 547 | </style> |