Initial commit: thinxtra.co company site

This commit is contained in:
Semih
2026-04-27 13:23:14 +00:00
commit 26bfaf1277
49 changed files with 3927 additions and 0 deletions

15
.dockerignore Normal file
View File

@@ -0,0 +1,15 @@
node_modules
dist
.git
.gitignore
.DS_Store
*.log
.env
.env.local
.vite
.vscode
.idea
README.md
docker-compose*.yml
Dockerfile
.dockerignore

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
node_modules
dist
.DS_Store
*.log
.env
.env.local
.vite
.coolify

31
Dockerfile Normal file
View File

@@ -0,0 +1,31 @@
ARG NODE_VERSION=22-alpine
ARG NGINX_VERSION=1.27-alpine
FROM node:${NODE_VERSION} AS builder
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@10.29.3 --activate
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY tsconfig.json tsconfig.app.json tsconfig.node.json vite.config.ts index.html ./
COPY public ./public
COPY src ./src
RUN pnpm build
FROM nginx:${NGINX_VERSION} AS runtime
RUN apk add --no-cache wget && rm -rf /var/cache/apk/*
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD wget -qO- http://127.0.0.1/healthz || exit 1
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -0,0 +1,23 @@
services:
web:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
expose:
- "80"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/healthz || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 5s
labels:
- "traefik.docker.network=coolify"
networks:
- default
- coolify
networks:
coolify:
external: true

80
index.html Normal file
View File

@@ -0,0 +1,80 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#0A0A0B" />
<meta name="color-scheme" content="dark" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="canonical" href="https://thinxtra.co/" />
<title>Thinxtra — SaaS Studio for Engineering Teams</title>
<meta
name="description"
content="Thinxtra is a US-based SaaS studio. We design, engineer, and scale software products with senior teams that ship — from MVP to production at scale."
/>
<meta name="robots" content="index,follow" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Thinxtra" />
<meta property="og:title" content="Thinxtra — SaaS Studio for Engineering Teams" />
<meta
property="og:description"
content="A senior product studio for SaaS teams. Strategy, design, and engineering — shipped weekly."
/>
<meta property="og:url" content="https://thinxtra.co/" />
<meta property="og:image" content="https://thinxtra.co/og-image.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Thinxtra — SaaS Studio for Engineering Teams" />
<meta
name="twitter:description"
content="A senior product studio for SaaS teams. Strategy, design, and engineering — shipped weekly."
/>
<meta name="twitter:image" content="https://thinxtra.co/og-image.png" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Thinxtra",
"alternateName": "Thinxtra Studio",
"url": "https://thinxtra.co",
"logo": "https://thinxtra.co/favicon.svg",
"description": "Thinxtra is a US-based SaaS studio. We design, engineer, and scale software products from MVP to production.",
"foundingDate": "2024",
"areaServed": "Worldwide",
"address": {
"@type": "PostalAddress",
"addressCountry": "US"
},
"contactPoint": {
"@type": "ContactPoint",
"contactType": "Sales",
"email": "hello@thinxtra.co",
"availableLanguage": ["English"]
},
"sameAs": [
"https://www.linkedin.com/company/thinxtra",
"https://x.com/thinxtra",
"https://github.com/thinxtra"
]
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

63
nginx.conf Normal file
View File

@@ -0,0 +1,63 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
index index.html;
charset utf-8;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
image/svg+xml
font/ttf
font/otf;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header Permissions-Policy "interest-cohort=(), browsing-topics=()" always;
# Hashed asset bundles — long cache
location ~* ^/assets/.+\.(?:js|css|woff2?|ttf|otf|svg|png|jpe?g|webp|avif|gif|ico)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
try_files $uri =404;
}
# Static images / icons / data files (root)
location ~* \.(?:woff2?|ttf|otf|svg|png|jpe?g|webp|avif|gif|ico|xml|txt)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
access_log off;
try_files $uri =404;
}
# SPA fallback: never cache the entry document
location / {
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
try_files $uri $uri/ /index.html;
}
# Health endpoint for Coolify / Traefik
location = /healthz {
access_log off;
default_type text/plain;
return 200 "ok\n";
}
}

32
package.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "thinxtra",
"private": true,
"version": "0.1.0",
"type": "module",
"packageManager": "pnpm@10.29.3",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview --host 0.0.0.0 --port 4173",
"typecheck": "tsc -b --noEmit"
},
"dependencies": {
"framer-motion": "^11.18.2",
"gsap": "^3.13.0",
"lenis": "^1.1.20",
"lucide-react": "^0.468.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@resvg/resvg-js": "^2.6.2",
"@tailwindcss/vite": "^4.0.0",
"@types/node": "^25.6.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"tailwindcss": "^4.0.0",
"typescript": "^5.7.2",
"vite": "^6.0.7"
}
}

1723
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
public/favicon-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

8
public/favicon.svg Normal file
View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
<rect width="32" height="32" rx="8" fill="#0A0A0B"/>
<g fill="none" stroke="#F5F5F7" stroke-width="2" stroke-linecap="round">
<path d="M7 9 L25 23"/>
<path d="M25 7 L9 25"/>
</g>
<circle cx="24.5" cy="7.5" r="2" fill="#7C5CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 336 B

BIN
public/og-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

44
public/og-image.svg Normal file
View File

@@ -0,0 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630">
<defs>
<radialGradient id="g1" cx="22%" cy="40%" r="55%">
<stop offset="0%" stop-color="#7C5CFF" stop-opacity="0.55"/>
<stop offset="100%" stop-color="#7C5CFF" stop-opacity="0"/>
</radialGradient>
<radialGradient id="g2" cx="80%" cy="65%" r="48%">
<stop offset="0%" stop-color="#22D3EE" stop-opacity="0.35"/>
<stop offset="100%" stop-color="#22D3EE" stop-opacity="0"/>
</radialGradient>
<linearGradient id="g3" x1="0" x2="1" y1="0" y2="1">
<stop offset="0" stop-color="#F5F5F7"/>
<stop offset="0.7" stop-color="#A78BFA"/>
<stop offset="1" stop-color="#22D3EE"/>
</linearGradient>
<pattern id="grid" width="48" height="48" patternUnits="userSpaceOnUse">
<path d="M 48 0 L 0 0 0 48" fill="none" stroke="#1F1F23" stroke-width="1"/>
</pattern>
</defs>
<rect width="1200" height="630" fill="#0A0A0B"/>
<rect width="1200" height="630" fill="url(#grid)" opacity="0.6"/>
<rect width="1200" height="630" fill="url(#g1)"/>
<rect width="1200" height="630" fill="url(#g2)"/>
<g transform="translate(80,90)" fill="none" stroke="#F5F5F7" stroke-width="3" stroke-linecap="round">
<path d="M0 8 L40 40"/>
<path d="M40 0 L4 36"/>
<circle cx="38" cy="2" r="4" fill="#7C5CFF" stroke="none"/>
</g>
<text x="138" y="125" fill="#F5F5F7" font-family="Inter, sans-serif" font-size="28" font-weight="600" letter-spacing="-0.5">thinxtra</text>
<text x="80" y="320" fill="url(#g3)" font-family="Inter, sans-serif" font-size="104" font-weight="700" letter-spacing="-4">SaaS studio for</text>
<text x="80" y="430" fill="url(#g3)" font-family="Inter, sans-serif" font-size="104" font-weight="700" letter-spacing="-4">teams that ship.</text>
<text x="80" y="510" fill="#8A8A92" font-family="Inter, sans-serif" font-size="26" font-weight="400">Strategy · Design · Engineering — shipped weekly.</text>
<g transform="translate(80,560)" font-family="JetBrains Mono, monospace" font-size="14" fill="#8A8A92">
<text x="0" y="14">thinxtra.co</text>
<circle cx="120" cy="9" r="3" fill="#7C5CFF"/>
<text x="135" y="14">United States</text>
<circle cx="280" cy="9" r="3" fill="#22D3EE"/>
<text x="295" y="14">Senior team</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

5
public/robots.txt Normal file
View File

@@ -0,0 +1,5 @@
User-agent: *
Allow: /
Disallow: /healthz
Sitemap: https://thinxtra.co/sitemap.xml

8
public/sitemap.xml Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://thinxtra.co/</loc>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>

24
scripts/gen-images.mjs Normal file
View File

@@ -0,0 +1,24 @@
import { readFileSync, writeFileSync } from 'node:fs'
import { Resvg } from '@resvg/resvg-js'
const ogSvg = readFileSync('public/og-image.svg', 'utf8')
const og = new Resvg(ogSvg, {
fitTo: { mode: 'width', value: 1200 },
background: '#0A0A0B',
})
writeFileSync('public/og-image.png', og.render().asPng())
console.log('og-image.png written')
const faviconSvg = readFileSync('public/favicon.svg', 'utf8')
const apple = new Resvg(faviconSvg, {
fitTo: { mode: 'width', value: 180 },
background: '#0A0A0B',
})
writeFileSync('public/apple-touch-icon.png', apple.render().asPng())
console.log('apple-touch-icon.png written')
const fav32 = new Resvg(faviconSvg, {
fitTo: { mode: 'width', value: 32 },
})
writeFileSync('public/favicon-32.png', fav32.render().asPng())
console.log('favicon-32.png written')

34
src/App.tsx Normal file
View File

@@ -0,0 +1,34 @@
import { useLenis } from '@/lib/lenis'
import { useCursorBlob } from '@/lib/cursor'
import { Nav } from '@/components/Nav'
import { Hero } from '@/components/Hero'
import { Services } from '@/components/Services'
import { Capabilities } from '@/components/Capabilities'
import { Process } from '@/components/Process'
import { Work } from '@/components/Work'
import { About } from '@/components/About'
import { FAQ } from '@/components/FAQ'
import { CTABand } from '@/components/CTABand'
import { Footer } from '@/components/Footer'
export default function App() {
useLenis()
useCursorBlob()
return (
<>
<Nav />
<main className="relative">
<Hero />
<Services />
<Capabilities />
<Process />
<Work />
<About />
<FAQ />
<CTABand />
</main>
<Footer />
</>
)
}

5
src/assets/logo.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round">
<path d="M5 7 L23 21" />
<path d="M23 5 L7 23" />
<circle cx="22.5" cy="5.5" r="1.5" fill="currentColor" stroke="none" />
</svg>

After

Width:  |  Height:  |  Size: 296 B

69
src/components/About.tsx Normal file
View File

@@ -0,0 +1,69 @@
import { motion } from 'framer-motion'
import { Counter } from './ui/Counter'
import { fadeUp, stagger } from '@/lib/motion'
const stats = [
{ label: 'Products shipped', to: 47, suffix: '+' },
{ label: 'Avg. time to first ship', to: 14, suffix: ' days' },
{ label: 'Client NPS (last 12 mo)', to: 72, suffix: '' },
]
export function About() {
return (
<section id="about" className="relative py-28 md:py-36">
<div className="container-x grid md:grid-cols-[1.3fr_1fr] gap-14 items-end">
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-15% 0px' }}
variants={stagger}
>
<motion.div variants={fadeUp} className="pill mb-6">
<span className="pill-dot" />
The studio
</motion.div>
<motion.h2
variants={fadeUp}
className="text-display text-[clamp(2rem,4.6vw,3.5rem)] tracking-tight"
>
We don&apos;t scale by hiring juniors.{' '}
<span className="text-[var(--color-muted)]">
We scale by being deliberate about who we work with.
</span>
</motion.h2>
<motion.p
variants={fadeUp}
className="mt-6 max-w-2xl text-[var(--color-muted)] leading-relaxed text-base md:text-lg"
>
Thinxtra is a small US-based studio of engineers, designers, and product partners with a
decade of work in venture-backed SaaS. We take on a handful of engagements at a time so
every team gets the people they hired and the bar stays where it should be.
</motion.p>
</motion.div>
<motion.dl
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-15% 0px' }}
variants={stagger}
className="grid grid-cols-1 sm:grid-cols-3 md:grid-cols-1 gap-5"
>
{stats.map((s) => (
<motion.div
key={s.label}
variants={fadeUp}
className="card p-6 md:p-7"
>
<dt className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--color-muted-2)]">
{s.label}
</dt>
<dd className="mt-3 text-display text-4xl md:text-5xl tracking-tight">
<Counter to={s.to} suffix={s.suffix} />
</dd>
</motion.div>
))}
</motion.dl>
</div>
</section>
)
}

View File

@@ -0,0 +1,64 @@
import { motion } from 'framer-motion'
import { ArrowUpRight, Mail } from 'lucide-react'
import { fadeUp, stagger } from '@/lib/motion'
export function CTABand() {
return (
<section id="contact" className="relative py-24 md:py-28">
<div className="container-x">
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-10% 0px' }}
variants={stagger}
className="relative overflow-hidden rounded-[var(--radius-card)] border border-[var(--color-hairline)] p-10 md:p-16"
style={{
background:
'radial-gradient(120% 120% at 0% 0%, rgba(124,92,255,0.18), transparent 55%), radial-gradient(120% 120% at 100% 100%, rgba(34,211,238,0.12), transparent 55%), var(--color-surface)',
}}
>
<div
aria-hidden="true"
className="absolute inset-0 grid-bg opacity-40"
/>
<div className="relative z-10 grid md:grid-cols-[1.4fr_1fr] gap-10 items-end">
<div>
<motion.div variants={fadeUp} className="pill mb-6">
<span className="pill-dot" />
Let&apos;s talk
</motion.div>
<motion.h2
variants={fadeUp}
className="text-display gradient-text text-[clamp(2.25rem,5vw,4.25rem)] tracking-tight max-w-3xl"
>
Have a problem worth solving?
</motion.h2>
<motion.p
variants={fadeUp}
className="mt-5 max-w-xl text-[var(--color-muted)] leading-relaxed"
>
Send us a paragraph about what you&apos;re building. We&apos;ll come back within a
business day with whether and how we can help.
</motion.p>
</div>
<motion.div variants={fadeUp} className="flex flex-col items-stretch gap-3 md:items-end">
<a
href="mailto:hello@thinxtra.co?subject=Project%20enquiry"
className="btn btn-primary justify-between md:justify-center md:px-6"
>
<span className="flex items-center gap-2">
<Mail size={16} />
hello@thinxtra.co
</span>
<ArrowUpRight size={16} />
</a>
<p className="text-xs font-mono text-[var(--color-muted-2)] uppercase tracking-[0.18em] text-right">
Replies within 1 business day · United States · GMT-5
</p>
</motion.div>
</div>
</motion.div>
</div>
</section>
)
}

View File

@@ -0,0 +1,53 @@
import { motion } from 'framer-motion'
import { capabilities } from '@/content/capabilities'
export function Capabilities() {
const row = [...capabilities, ...capabilities]
return (
<section
aria-label="Capabilities"
className="relative border-y border-[var(--color-hairline)] py-10 overflow-hidden"
style={{
background:
'linear-gradient(180deg, color-mix(in oklab, var(--color-surface) 60%, transparent), transparent)',
}}
>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-y-0 left-0 w-32 z-10"
style={{
background:
'linear-gradient(to right, var(--color-bg), transparent)',
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-y-0 right-0 w-32 z-10"
style={{
background:
'linear-gradient(to left, var(--color-bg), transparent)',
}}
/>
<motion.div
className="marquee-track"
animate={{ x: ['0%', '-50%'] }}
transition={{
duration: 36,
ease: 'linear',
repeat: Infinity,
}}
>
{row.map((cap, i) => (
<span
key={cap + i}
className="inline-flex items-center gap-2 px-5 py-2 rounded-full border border-[var(--color-hairline)] bg-[var(--color-surface)] font-mono text-sm text-[var(--color-muted)] whitespace-nowrap"
>
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-violet)]" />
{cap}
</span>
))}
</motion.div>
</section>
)
}

104
src/components/FAQ.tsx Normal file
View File

@@ -0,0 +1,104 @@
import { useState } from 'react'
import { AnimatePresence, motion } from 'framer-motion'
import { Plus } from 'lucide-react'
import { faq } from '@/content/faq'
import { fadeUp, stagger } from '@/lib/motion'
export function FAQ() {
const [open, setOpen] = useState<number | null>(0)
return (
<section id="faq" className="relative py-28 md:py-36">
<div className="container-x grid md:grid-cols-[1fr_1.6fr] gap-12">
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-15% 0px' }}
variants={stagger}
>
<motion.div variants={fadeUp} className="pill mb-6">
<span className="pill-dot" />
FAQ
</motion.div>
<motion.h2
variants={fadeUp}
className="text-display text-[clamp(2rem,4.6vw,3.5rem)] tracking-tight"
>
Common questions.
</motion.h2>
<motion.p
variants={fadeUp}
className="mt-5 text-[var(--color-muted)] leading-relaxed"
>
Don&apos;t see yours? Email{' '}
<a
className="underline-link"
href="mailto:hello@thinxtra.co"
>
hello@thinxtra.co
</a>{' '}
we reply within a business day.
</motion.p>
</motion.div>
<motion.ul
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-10% 0px' }}
variants={stagger}
className="divide-y divide-[var(--color-hairline)] border-y border-[var(--color-hairline)]"
>
{faq.map((item, i) => {
const isOpen = open === i
return (
<motion.li key={item.q} variants={fadeUp}>
<button
type="button"
aria-expanded={isOpen}
onClick={() => setOpen(isOpen ? null : i)}
className="w-full flex items-center justify-between gap-6 py-6 text-left group"
>
<span className="text-base md:text-lg font-medium tracking-tight pr-4">
{item.q}
</span>
<span
className={
'shrink-0 flex h-9 w-9 items-center justify-center rounded-full border transition-colors ' +
(isOpen
? 'bg-[var(--color-violet)] border-[var(--color-violet)] text-white'
: 'border-[var(--color-hairline)] text-[var(--color-muted)] group-hover:text-[var(--color-text)]')
}
>
<motion.span
animate={{ rotate: isOpen ? 45 : 0 }}
transition={{ duration: 0.3, ease: [0.2, 0.8, 0.2, 1] }}
className="flex"
>
<Plus size={16} />
</motion.span>
</span>
</button>
<AnimatePresence initial={false}>
{isOpen && (
<motion.div
key="content"
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.35, ease: [0.2, 0.8, 0.2, 1] }}
className="overflow-hidden"
>
<p className="pb-6 pr-12 text-[var(--color-muted)] leading-relaxed">
{item.a}
</p>
</motion.div>
)}
</AnimatePresence>
</motion.li>
)
})}
</motion.ul>
</div>
</section>
)
}

105
src/components/Footer.tsx Normal file
View File

@@ -0,0 +1,105 @@
import { Github, Linkedin } from 'lucide-react'
import logo from '@/assets/logo.svg'
const year = new Date().getFullYear()
export function Footer() {
return (
<footer className="relative border-t border-[var(--color-hairline)] py-12 md:py-16">
<div className="container-x grid gap-10 md:grid-cols-[1.4fr_1fr_1fr_1fr]">
<div>
<div className="flex items-center gap-2 text-[var(--color-text)]">
<img src={logo} alt="" width={22} height={22} />
<span className="font-semibold tracking-tight">thinxtra</span>
</div>
<p className="mt-4 max-w-sm text-sm text-[var(--color-muted)] leading-relaxed">
A US-based SaaS studio. Senior teams designing, engineering, and scaling software
products that matter.
</p>
</div>
<div>
<h4 className="font-mono text-[11px] uppercase tracking-[0.2em] text-[var(--color-muted-2)]">
Studio
</h4>
<ul className="mt-4 space-y-2 text-sm text-[var(--color-muted)]">
<li>
<a href="#services" className="underline-link">
Services
</a>
</li>
<li>
<a href="#process" className="underline-link">
Process
</a>
</li>
<li>
<a href="#work" className="underline-link">
Work
</a>
</li>
<li>
<a href="#about" className="underline-link">
Studio
</a>
</li>
</ul>
</div>
<div>
<h4 className="font-mono text-[11px] uppercase tracking-[0.2em] text-[var(--color-muted-2)]">
Contact
</h4>
<ul className="mt-4 space-y-2 text-sm text-[var(--color-muted)]">
<li>
<a href="mailto:hello@thinxtra.co" className="underline-link">
hello@thinxtra.co
</a>
</li>
<li>United States · Remote</li>
</ul>
</div>
<div>
<h4 className="font-mono text-[11px] uppercase tracking-[0.2em] text-[var(--color-muted-2)]">
Elsewhere
</h4>
<ul className="mt-4 flex items-center gap-3">
<li>
<a
aria-label="LinkedIn"
href="https://www.linkedin.com/company/thinxtra"
className="flex h-9 w-9 items-center justify-center rounded-full border border-[var(--color-hairline)] text-[var(--color-muted)] hover:text-[var(--color-text)] hover:border-[var(--color-hairline-2)] transition"
>
<Linkedin size={15} />
</a>
</li>
<li>
<a
aria-label="GitHub"
href="https://github.com/thinxtra"
className="flex h-9 w-9 items-center justify-center rounded-full border border-[var(--color-hairline)] text-[var(--color-muted)] hover:text-[var(--color-text)] hover:border-[var(--color-hairline-2)] transition"
>
<Github size={15} />
</a>
</li>
<li>
<a
aria-label="X"
href="https://x.com/thinxtra"
className="flex h-9 w-9 items-center justify-center rounded-full border border-[var(--color-hairline)] text-[var(--color-muted)] hover:text-[var(--color-text)] hover:border-[var(--color-hairline-2)] transition"
>
<span className="font-mono text-sm">X</span>
</a>
</li>
</ul>
</div>
</div>
<div className="container-x mt-12 flex flex-col md:flex-row items-start md:items-center justify-between gap-3 pt-6 border-t border-[var(--color-hairline)] text-xs font-mono text-[var(--color-muted-2)] uppercase tracking-[0.18em]">
<span>© {year} Thinxtra Studio · All rights reserved</span>
<span>Built in TypeScript · Deployed on Coolify</span>
</div>
</footer>
)
}

147
src/components/Hero.tsx Normal file
View File

@@ -0,0 +1,147 @@
import { useEffect, useRef } from 'react'
import { gsap } from 'gsap'
import { ArrowDown, ArrowUpRight } from 'lucide-react'
import { GradientOrb } from './ui/GradientOrb'
import { GridBackground } from './ui/GridBackground'
import { scrollToId } from '@/lib/lenis'
import { trust } from '@/content/capabilities'
const HEADLINE_LINES = [
['Software', 'studios', 'for'],
['teams', 'that', 'ship.'],
]
export function Hero() {
const root = useRef<HTMLElement>(null)
useEffect(() => {
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (!root.current) return
const ctx = gsap.context(() => {
const words = root.current!.querySelectorAll<HTMLElement>('[data-anim="word"]')
const sub = root.current!.querySelector('[data-anim="sub"]')
const cta = root.current!.querySelector('[data-anim="cta"]')
const meta = root.current!.querySelector('[data-anim="meta"]')
if (reduced) {
gsap.set([words, sub, cta, meta], { autoAlpha: 1, y: 0 })
return
}
gsap.set(words, { y: '110%', opacity: 0 })
gsap.set([sub, cta, meta], { y: 24, opacity: 0 })
const tl = gsap.timeline({ defaults: { ease: 'expo.out' } })
tl.to(words, {
y: '0%',
opacity: 1,
duration: 1.1,
stagger: 0.05,
delay: 0.1,
})
.to(sub, { y: 0, opacity: 1, duration: 0.8 }, '-=0.6')
.to(cta, { y: 0, opacity: 1, duration: 0.7 }, '-=0.5')
.to(meta, { y: 0, opacity: 1, duration: 0.7 }, '-=0.5')
}, root)
return () => ctx.revert()
}, [])
return (
<section
ref={root}
id="top"
className="relative isolate overflow-hidden pt-36 pb-28 md:pt-44 md:pb-36"
>
<GridBackground />
<GradientOrb />
<div className="container-x relative z-10">
<div className="flex justify-center mb-8">
<span className="pill">
<span className="pill-dot" />
Now booking Q3 engagements
</span>
</div>
<h1 className="text-display text-center text-[clamp(2.75rem,7.4vw,6.5rem)] mx-auto max-w-[18ch]">
{HEADLINE_LINES.map((line, li) => (
<span key={li} className="block overflow-hidden pb-1">
{line.map((word, wi) => (
<span
key={wi}
data-anim="word"
className="inline-block will-change-transform gradient-text"
style={{ marginRight: '0.18em' }}
>
{word}
</span>
))}
</span>
))}
</h1>
<p
data-anim="sub"
className="mx-auto mt-7 max-w-2xl text-center text-[var(--color-muted)] text-base md:text-lg leading-relaxed"
>
Thinxtra is a US-based SaaS studio. Senior engineers, designers, and product partners
embedded with you, shipping production every week from idea to scale.
</p>
<div
data-anim="cta"
className="mt-9 flex flex-col sm:flex-row items-center justify-center gap-3"
>
<a
href="#contact"
onClick={(e) => {
e.preventDefault()
scrollToId('contact')
}}
className="btn btn-primary"
>
Start a project
<ArrowUpRight size={16} />
</a>
<a
href="#work"
onClick={(e) => {
e.preventDefault()
scrollToId('work')
}}
className="btn btn-ghost"
>
Selected work
</a>
</div>
<div
data-anim="meta"
className="mt-20 flex flex-col items-center gap-5"
>
<div className="flex items-center gap-2 text-xs font-mono text-[var(--color-muted-2)] uppercase tracking-[0.18em]">
Backed teams from
</div>
<ul className="flex flex-wrap justify-center gap-x-7 gap-y-3 text-sm text-[var(--color-muted)]">
{trust.map((t) => (
<li key={t} className="font-medium tracking-tight">
{t}
</li>
))}
</ul>
</div>
<button
type="button"
aria-label="Scroll to services"
onClick={() => scrollToId('services')}
className="absolute left-1/2 -translate-x-1/2 bottom-2 hidden md:flex items-center gap-2 text-xs font-mono uppercase tracking-[0.2em] text-[var(--color-muted-2)] hover:text-[var(--color-text)] transition-colors"
>
Scroll
<ArrowDown size={14} className="animate-bounce" />
</button>
</div>
</section>
)
}

108
src/components/Nav.tsx Normal file
View File

@@ -0,0 +1,108 @@
import { useEffect, useState } from 'react'
import { Menu, X, ArrowUpRight } from 'lucide-react'
import { scrollToId } from '@/lib/lenis'
import logo from '@/assets/logo.svg'
const links = [
{ id: 'services', label: 'Services' },
{ id: 'process', label: 'Process' },
{ id: 'work', label: 'Work' },
{ id: 'about', label: 'Studio' },
{ id: 'faq', label: 'FAQ' },
]
export function Nav() {
const [scrolled, setScrolled] = useState(false)
const [open, setOpen] = useState(false)
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 12)
onScroll()
window.addEventListener('scroll', onScroll, { passive: true })
return () => window.removeEventListener('scroll', onScroll)
}, [])
function handleClick(e: React.MouseEvent<HTMLAnchorElement>, id: string) {
e.preventDefault()
setOpen(false)
scrollToId(id)
}
return (
<header
className={
'fixed inset-x-0 top-0 z-50 transition-all duration-300 ' +
(scrolled
? 'backdrop-blur-xl bg-[color-mix(in_oklab,var(--color-bg)_75%,transparent)] border-b border-[var(--color-hairline)]'
: 'border-b border-transparent')
}
>
<nav className="container-x flex h-16 items-center justify-between gap-6">
<a
href="#top"
onClick={(e) => handleClick(e, 'top')}
className="flex items-center gap-2 text-[var(--color-text)]"
aria-label="Thinxtra — home"
>
<img src={logo} alt="" width={24} height={24} className="text-[var(--color-text)]" />
<span className="text-[15px] font-semibold tracking-tight">thinxtra</span>
<span className="ml-1 hidden sm:inline pill" style={{ padding: '0.15rem 0.5rem' }}>
<span className="pill-dot" />
US studio
</span>
</a>
<ul className="hidden md:flex items-center gap-7 text-sm text-[var(--color-muted)]">
{links.map((l) => (
<li key={l.id}>
<a
href={'#' + l.id}
onClick={(e) => handleClick(e, l.id)}
className="underline-link hover:text-[var(--color-text)] transition-colors"
>
{l.label}
</a>
</li>
))}
</ul>
<div className="flex items-center gap-3">
<a
href="#contact"
onClick={(e) => handleClick(e, 'contact')}
className="btn btn-primary text-sm h-9 px-4"
>
Start a project
<ArrowUpRight size={16} />
</a>
<button
type="button"
aria-label="Toggle menu"
className="md:hidden btn btn-ghost h-9 w-9 px-0"
onClick={() => setOpen((v) => !v)}
>
{open ? <X size={18} /> : <Menu size={18} />}
</button>
</div>
</nav>
{open && (
<div className="md:hidden border-t border-[var(--color-hairline)] bg-[var(--color-bg)]">
<ul className="container-x py-4 flex flex-col gap-2">
{links.map((l) => (
<li key={l.id}>
<a
href={'#' + l.id}
onClick={(e) => handleClick(e, l.id)}
className="block py-2 text-[var(--color-text)]"
>
{l.label}
</a>
</li>
))}
</ul>
</div>
)}
</header>
)
}

122
src/components/Process.tsx Normal file
View File

@@ -0,0 +1,122 @@
import { useEffect, useRef } from 'react'
import { motion } from 'framer-motion'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import { process } from '@/content/process'
import { fadeUp, stagger } from '@/lib/motion'
gsap.registerPlugin(ScrollTrigger)
export function Process() {
const root = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!root.current) return
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (reduced) return
const ctx = gsap.context(() => {
const bar = root.current!.querySelector('[data-process-bar]')
if (!bar) return
gsap.fromTo(
bar,
{ scaleY: 0 },
{
scaleY: 1,
ease: 'none',
scrollTrigger: {
trigger: root.current,
start: 'top 70%',
end: 'bottom 60%',
scrub: 0.6,
},
},
)
}, root)
return () => ctx.revert()
}, [])
return (
<section id="process" className="relative py-28 md:py-36">
<div className="container-x">
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-15% 0px' }}
variants={stagger}
className="max-w-2xl"
>
<motion.div variants={fadeUp} className="pill mb-6">
<span className="pill-dot" />
How we work
</motion.div>
<motion.h2
variants={fadeUp}
className="text-display text-[clamp(2rem,4.6vw,3.5rem)] tracking-tight"
>
Predictable rhythm.{' '}
<span className="text-[var(--color-muted)]">No mystery, no theatre.</span>
</motion.h2>
</motion.div>
<div ref={root} className="relative mt-16 grid md:grid-cols-[1fr_2fr] gap-10 md:gap-16">
<div className="hidden md:block sticky top-28 self-start">
<div className="font-mono text-xs uppercase tracking-[0.2em] text-[var(--color-muted-2)]">
Engagement timeline
</div>
<div className="mt-6 relative h-72 w-px bg-[var(--color-hairline)]">
<div
data-process-bar
className="absolute inset-0 origin-top w-px bg-gradient-to-b from-[var(--color-violet)] via-[var(--color-violet-2)] to-[var(--color-cyan)]"
style={{ transform: 'scaleY(0)' }}
/>
</div>
</div>
<motion.ol
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-10% 0px' }}
variants={stagger}
className="space-y-6"
>
{process.map((step) => (
<motion.li
key={step.id}
variants={fadeUp}
className="card p-7 md:p-9"
>
<div className="card-glow" />
<div className="flex items-baseline justify-between gap-4">
<span className="font-mono text-sm text-[var(--color-violet-2)]">
{step.index}
</span>
<span className="font-mono text-xs uppercase tracking-[0.18em] text-[var(--color-muted-2)]">
{step.duration}
</span>
</div>
<h3 className="mt-2 text-2xl md:text-3xl font-semibold tracking-tight">
{step.title}
</h3>
<p className="mt-3 text-[var(--color-muted)] leading-relaxed">
{step.description}
</p>
<ul className="mt-5 flex flex-wrap gap-2">
{step.outputs.map((o) => (
<li
key={o}
className="font-mono text-xs px-3 py-1.5 rounded-full border border-[var(--color-hairline)] bg-[var(--color-surface)] text-[var(--color-muted)]"
>
{o}
</li>
))}
</ul>
</motion.li>
))}
</motion.ol>
</div>
</div>
</section>
)
}

View File

@@ -0,0 +1,83 @@
import { motion } from 'framer-motion'
import { ArrowUpRight } from 'lucide-react'
import { services } from '@/content/services'
import { fadeUp, stagger } from '@/lib/motion'
export function Services() {
return (
<section id="services" className="relative py-28 md:py-36">
<div className="container-x">
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-15% 0px' }}
variants={stagger}
className="max-w-2xl"
>
<motion.div variants={fadeUp} className="pill mb-6">
<span className="pill-dot" />
What we do
</motion.div>
<motion.h2
variants={fadeUp}
className="text-display text-[clamp(2rem,4.6vw,3.5rem)] tracking-tight"
>
A senior product team {' '}
<span className="text-[var(--color-muted)]">embedded, on demand.</span>
</motion.h2>
<motion.p
variants={fadeUp}
className="mt-5 text-[var(--color-muted)] text-base md:text-lg leading-relaxed"
>
Four practices, one team. We bring strategy, design, and engineering under one roof so
your roadmap moves in weeks not quarters.
</motion.p>
</motion.div>
<motion.ul
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-10% 0px' }}
variants={stagger}
className="mt-14 grid grid-cols-1 md:grid-cols-2 gap-5"
>
{services.map((s) => {
const Icon = s.icon
return (
<motion.li
key={s.id}
variants={fadeUp}
whileHover={{ y: -4 }}
transition={{ type: 'spring', stiffness: 250, damping: 22 }}
className="card p-7 md:p-8"
>
<div className="card-glow" />
<div className="flex items-start justify-between gap-4">
<div className="flex h-11 w-11 items-center justify-center rounded-xl border border-[var(--color-hairline)] bg-[var(--color-surface)] text-[var(--color-violet-2)]">
<Icon size={20} strokeWidth={1.6} />
</div>
<ArrowUpRight
size={18}
className="text-[var(--color-muted-2)] group-hover:text-[var(--color-text)] transition-colors"
/>
</div>
<h3 className="mt-6 text-xl md:text-2xl font-semibold tracking-tight">
{s.title}
</h3>
<p className="mt-3 text-[var(--color-muted)] leading-relaxed">{s.summary}</p>
<ul className="mt-6 space-y-2 font-mono text-[13px] text-[var(--color-muted)]">
{s.bullets.map((b) => (
<li key={b} className="flex items-center gap-2">
<span className="h-1 w-1 rounded-full bg-[var(--color-violet)]" />
{b}
</li>
))}
</ul>
</motion.li>
)
})}
</motion.ul>
</div>
</section>
)
}

99
src/components/Work.tsx Normal file
View File

@@ -0,0 +1,99 @@
import { motion } from 'framer-motion'
import { ArrowUpRight } from 'lucide-react'
import { work } from '@/content/work'
import { fadeUp, stagger } from '@/lib/motion'
export function Work() {
return (
<section id="work" className="relative py-28 md:py-36">
<div className="container-x">
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-15% 0px' }}
variants={stagger}
className="flex flex-col md:flex-row md:items-end md:justify-between gap-6"
>
<div className="max-w-xl">
<motion.div variants={fadeUp} className="pill mb-6">
<span className="pill-dot" />
Selected work
</motion.div>
<motion.h2
variants={fadeUp}
className="text-display text-[clamp(2rem,4.6vw,3.5rem)] tracking-tight"
>
Products we helped{' '}
<span className="text-[var(--color-muted)]">put into the world.</span>
</motion.h2>
</div>
<motion.p
variants={fadeUp}
className="md:max-w-xs text-[var(--color-muted)] leading-relaxed"
>
A snapshot. We work under NDA on the rest. Reach out for full case studies and
references.
</motion.p>
</motion.div>
<motion.div
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: '-10% 0px' }}
variants={stagger}
className="mt-14 grid grid-cols-1 lg:grid-cols-3 gap-5"
>
{work.map((w) => (
<motion.article
key={w.id}
variants={fadeUp}
whileHover={{ y: -6 }}
transition={{ type: 'spring', stiffness: 220, damping: 20 }}
className="card group p-7 md:p-8 flex flex-col"
>
<div className="card-glow" />
<div
aria-hidden="true"
className={`relative h-44 -mx-7 md:-mx-8 -mt-7 md:-mt-8 mb-6 overflow-hidden rounded-t-[var(--radius-card)] bg-gradient-to-br ${w.gradient}`}
>
<div className="absolute inset-0 grid-bg opacity-50" />
<div className="absolute inset-0 flex items-end p-5">
<span className="font-mono text-xs text-[var(--color-text)]/70 uppercase tracking-[0.18em]">
{w.category}
</span>
</div>
</div>
<div className="flex items-baseline justify-between gap-3">
<span className="font-mono text-xs text-[var(--color-muted-2)] uppercase tracking-[0.18em]">
{w.client}
</span>
<ArrowUpRight
size={16}
className="text-[var(--color-muted-2)] group-hover:text-[var(--color-text)] transition-colors"
/>
</div>
<h3 className="mt-3 text-xl md:text-[1.4rem] font-semibold tracking-tight">
{w.title}
</h3>
<p className="mt-3 text-[var(--color-muted)] leading-relaxed text-[0.95rem]">
{w.blurb}
</p>
<dl className="mt-6 grid grid-cols-2 gap-4 pt-5 border-t border-[var(--color-hairline)]">
{w.metrics.map((m) => (
<div key={m.label}>
<dt className="font-mono text-[11px] uppercase tracking-[0.18em] text-[var(--color-muted-2)]">
{m.label}
</dt>
<dd className="mt-1 text-lg font-semibold tracking-tight">{m.value}</dd>
</div>
))}
</dl>
</motion.article>
))}
</motion.div>
</div>
</section>
)
}

View File

@@ -0,0 +1,36 @@
import { type AnchorHTMLAttributes, type ButtonHTMLAttributes, type ReactNode } from 'react'
type Variant = 'primary' | 'ghost'
interface CommonProps {
children: ReactNode
variant?: Variant
className?: string
}
type AsAnchor = CommonProps & AnchorHTMLAttributes<HTMLAnchorElement> & { href: string }
type AsButton = CommonProps & ButtonHTMLAttributes<HTMLButtonElement> & { href?: undefined }
export function Button(props: AsAnchor | AsButton) {
const { variant = 'primary', className = '', children, ...rest } = props as CommonProps & {
href?: string
[key: string]: unknown
}
const base = 'btn ' + (variant === 'primary' ? 'btn-primary' : 'btn-ghost') + ' ' + className
if ('href' in rest && rest.href) {
const { href, ...anchorRest } = rest as AnchorHTMLAttributes<HTMLAnchorElement> & {
href: string
}
return (
<a href={href} className={base} {...anchorRest}>
{children}
</a>
)
}
return (
<button className={base} {...(rest as ButtonHTMLAttributes<HTMLButtonElement>)}>
{children}
</button>
)
}

View File

@@ -0,0 +1,41 @@
import { useEffect, useRef, useState } from 'react'
import { useInView } from 'framer-motion'
interface CounterProps {
to: number
suffix?: string
duration?: number
decimals?: number
}
export function Counter({ to, suffix = '', duration = 1.6, decimals = 0 }: CounterProps) {
const ref = useRef<HTMLSpanElement>(null)
const inView = useInView(ref, { once: true, margin: '-10% 0px' })
const [value, setValue] = useState(0)
useEffect(() => {
if (!inView) return
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (reduced) {
setValue(to)
return
}
const start = performance.now()
const ease = (t: number) => 1 - Math.pow(1 - t, 3)
let raf = 0
function tick(now: number) {
const t = Math.min(1, (now - start) / (duration * 1000))
setValue(to * ease(t))
if (t < 1) raf = requestAnimationFrame(tick)
}
raf = requestAnimationFrame(tick)
return () => cancelAnimationFrame(raf)
}, [inView, to, duration])
return (
<span ref={ref}>
{value.toFixed(decimals)}
{suffix}
</span>
)
}

View File

@@ -0,0 +1,39 @@
import { useEffect, useRef } from 'react'
import { gsap } from 'gsap'
export function GradientOrb() {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (reduced || !ref.current) return
const ctx = gsap.context(() => {
gsap.to(ref.current, {
x: 60,
y: -40,
scale: 1.08,
duration: 8,
ease: 'sine.inOut',
yoyo: true,
repeat: -1,
})
}, ref)
return () => ctx.revert()
}, [])
return (
<div
aria-hidden="true"
ref={ref}
className="pointer-events-none absolute -z-0 left-1/2 top-1/3 -translate-x-1/2 -translate-y-1/2"
style={{
width: 'min(1100px, 110vw)',
height: 'min(700px, 70vw)',
filter: 'blur(80px)',
background:
'radial-gradient(50% 50% at 35% 50%, rgba(124,92,255,0.55) 0%, rgba(124,92,255,0) 70%), radial-gradient(45% 60% at 75% 60%, rgba(34,211,238,0.32) 0%, rgba(34,211,238,0) 70%)',
opacity: 0.9,
}}
/>
)
}

View File

@@ -0,0 +1,8 @@
export function GridBackground() {
return (
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 grid-bg"
/>
)
}

View File

@@ -0,0 +1,31 @@
export const capabilities: string[] = [
'TypeScript',
'React',
'Next.js',
'NestJS',
'Postgres',
'Redis',
'AWS',
'Vercel',
'Stripe',
'OpenAI',
'Anthropic',
'pgvector',
'Tailwind CSS',
'Framer Motion',
'Playwright',
'GitHub Actions',
'Terraform',
'OpenTelemetry',
'Sentry',
'Linear',
]
export const trust: string[] = [
'Y Combinator',
'Andreessen Horowitz',
'Sequoia',
'Lightspeed',
'Index Ventures',
'First Round',
]

31
src/content/faq.ts Normal file
View File

@@ -0,0 +1,31 @@
export interface FAQItem {
q: string
a: string
}
export const faq: FAQItem[] = [
{
q: 'Are you a US company?',
a: 'Yes. Thinxtra is incorporated in the United States. Our team is fully remote with senior engineers and designers across North America and Europe.',
},
{
q: 'How do you price?',
a: 'Two models: (1) fixed-scope discovery and design sprints with milestone billing; (2) an embedded senior pod billed monthly. Most teams start with a 2-week paid discovery before committing.',
},
{
q: 'How fast can you start?',
a: 'Typically two to three weeks from a signed engagement. We are deliberate about staffing — we do not take on work we cannot ship with senior people.',
},
{
q: 'Who owns the IP?',
a: 'You do, in full. Code, design files, dashboards, runbooks, and credentials transfer to your team at every milestone.',
},
{
q: 'What stacks do you work in?',
a: 'Default: TypeScript, React, Node/NestJS, Postgres, AWS or Vercel, OpenAI/Anthropic. We can adapt — but only when there is a real reason to.',
},
{
q: 'Do you sign NDAs and BAAs?',
a: 'Yes. We routinely sign NDAs, MSAs, DPAs, and BAAs. Reach out if you have a security questionnaire — we will turn it around quickly.',
},
]

47
src/content/process.ts Normal file
View File

@@ -0,0 +1,47 @@
export interface ProcessStep {
id: string
index: string
title: string
duration: string
description: string
outputs: string[]
}
export const process: ProcessStep[] = [
{
id: 'discover',
index: '01',
title: 'Discover',
duration: 'Week 1',
description:
'We map your users, revenue model, and constraints. No deck — a written brief and an opinionated direction.',
outputs: ['Product brief', 'Architecture options', 'Risk register'],
},
{
id: 'design',
index: '02',
title: 'Design',
duration: 'Week 23',
description:
'Interaction flows, design system tokens, and a clickable prototype your team can pressure-test.',
outputs: ['Figma library', 'Prototype', 'Token + theme spec'],
},
{
id: 'build',
index: '03',
title: 'Build',
duration: 'Week 310',
description:
'A senior pod ships features behind flags. Demos every Friday. Production from the first PR.',
outputs: ['Deployed product', 'Test + CI suite', 'Runbooks'],
},
{
id: 'scale',
index: '04',
title: 'Scale',
duration: 'Ongoing',
description:
'Observability, on-call, and a quarterly roadmap. We hand off to your team — or stay as the engineering org until you hire one.',
outputs: ['SLOs + dashboards', 'Quarterly OKRs', 'Hiring plan'],
},
]

66
src/content/services.ts Normal file
View File

@@ -0,0 +1,66 @@
import {
Boxes,
Cpu,
Layers,
Sparkles,
type LucideIcon,
} from 'lucide-react'
export interface Service {
id: string
title: string
summary: string
bullets: string[]
icon: LucideIcon
}
export const services: Service[] = [
{
id: 'product',
title: 'SaaS Product Engineering',
summary:
'A senior pod that owns delivery — frontend, backend, infra, and design — under one roof.',
bullets: [
'TypeScript · React · Node · Postgres',
'Weekly demos, biweekly releases',
'Production from day one',
],
icon: Boxes,
},
{
id: 'mvp',
title: 'MVP to Scale',
summary:
'From validated prototype to multi-tenant, observable, billable production — without a rewrite.',
bullets: [
'Architecture & feature flags',
'Stripe billing & metering',
'Observability & SLOs',
],
icon: Layers,
},
{
id: 'ai',
title: 'AI Integration',
summary:
'Embed agents and LLM workflows into your product with evals, guardrails, and clear UX.',
bullets: [
'OpenAI · Anthropic · open models',
'RAG, evals, prompt caching',
'Streaming UX patterns',
],
icon: Sparkles,
},
{
id: 'platform',
title: 'Design Systems & Platform',
summary:
'Tokens, component library, CI, and the unglamorous foundations your team will compound on.',
bullets: [
'Figma → code parity',
'Storybook · CI · review tooling',
'Performance budgets',
],
icon: Cpu,
},
]

51
src/content/work.ts Normal file
View File

@@ -0,0 +1,51 @@
export interface Work {
id: string
client: string
category: string
title: string
blurb: string
metrics: { label: string; value: string }[]
gradient: string
}
export const work: Work[] = [
{
id: 'lattice',
client: 'Lattice Health',
category: 'AI · Healthcare',
title: 'Clinical copilot for primary care.',
blurb:
'A streaming agent that drafts SOAP notes from ambient audio. Shipped to 800 clinicians in 11 weeks.',
metrics: [
{ label: 'Time saved / visit', value: '11 min' },
{ label: 'Adoption', value: '92%' },
],
gradient: 'from-violet-500/30 via-fuchsia-500/15 to-transparent',
},
{
id: 'orbital',
client: 'Orbital Logistics',
category: 'SaaS · Operations',
title: 'Multi-tenant freight platform.',
blurb:
'Replatformed a Rails monolith to a typed, multi-tenant Postgres + NestJS stack with metered billing.',
metrics: [
{ label: 'P95 latency', value: '74%' },
{ label: 'ARR (yr 1)', value: '$4.2M' },
],
gradient: 'from-cyan-500/25 via-violet-500/15 to-transparent',
},
{
id: 'beacon',
client: 'Beacon Studio',
category: 'Design System · Platform',
title: 'Design system + token pipeline.',
blurb:
'Figma variables → code in one source of truth. Deployed across 4 product lines with zero drift.',
metrics: [
{ label: 'Component reuse', value: '6.4×' },
{ label: 'Ship velocity', value: '+38%' },
],
gradient: 'from-emerald-400/20 via-cyan-500/15 to-transparent',
},
]

300
src/index.css Normal file
View File

@@ -0,0 +1,300 @@
@import 'tailwindcss';
@theme {
--color-bg: #0a0a0b;
--color-surface: #111114;
--color-surface-2: #16161b;
--color-hairline: #1f1f23;
--color-hairline-2: #2a2a30;
--color-text: #f5f5f7;
--color-muted: #8a8a92;
--color-muted-2: #5d5d66;
--color-violet: #7c5cff;
--color-violet-2: #a78bfa;
--color-cyan: #22d3ee;
--font-sans:
'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, sans-serif;
--font-display:
'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
sans-serif;
--font-mono:
'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
--radius-card: 1.25rem;
--radius-pill: 9999px;
}
@layer base {
*,
*::before,
*::after {
border-color: var(--color-hairline);
}
html {
background-color: var(--color-bg);
color: var(--color-text);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
scroll-behavior: smooth;
}
html,
body {
overflow-x: clip;
}
body {
font-family: var(--font-sans);
font-feature-settings: 'cv11', 'ss01', 'ss03';
background-color: var(--color-bg);
color: var(--color-text);
min-height: 100dvh;
}
::selection {
background-color: color-mix(in oklab, var(--color-violet) 35%, transparent);
color: var(--color-text);
}
*:focus-visible {
outline: 2px solid var(--color-violet);
outline-offset: 3px;
border-radius: 4px;
}
/* Lenis */
html.lenis,
html.lenis body {
height: auto;
}
.lenis.lenis-smooth {
scroll-behavior: auto !important;
}
.lenis.lenis-smooth [data-lenis-prevent] {
overscroll-behavior: contain;
}
.lenis.lenis-stopped {
overflow: clip;
}
}
@layer components {
.container-x {
width: 100%;
max-width: 1200px;
margin-inline: auto;
padding-inline: 1.25rem;
}
@media (min-width: 768px) {
.container-x {
padding-inline: 2rem;
}
}
.hairline {
border: 1px solid var(--color-hairline);
}
.pill {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0.75rem;
border-radius: var(--radius-pill);
border: 1px solid var(--color-hairline);
background-color: color-mix(in oklab, var(--color-surface) 80%, transparent);
backdrop-filter: blur(8px);
font-size: 0.75rem;
font-family: var(--font-mono);
color: var(--color-muted);
letter-spacing: 0.04em;
}
.pill-dot {
width: 6px;
height: 6px;
border-radius: 9999px;
background: var(--color-violet);
box-shadow: 0 0 12px color-mix(in oklab, var(--color-violet) 80%, transparent);
}
.grid-bg {
background-image:
linear-gradient(to right, color-mix(in oklab, #ffffff 4%, transparent) 1px, transparent 1px),
linear-gradient(to bottom, color-mix(in oklab, #ffffff 4%, transparent) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(ellipse 90% 60% at 50% 35%, #000 35%, transparent 80%);
-webkit-mask-image: radial-gradient(ellipse 90% 60% at 50% 35%, #000 35%, transparent 80%);
}
.text-display {
font-family: var(--font-display);
font-weight: 600;
letter-spacing: -0.045em;
line-height: 0.95;
}
.gradient-text {
background: linear-gradient(
120deg,
var(--color-text) 0%,
var(--color-text) 35%,
var(--color-violet-2) 65%,
var(--color-cyan) 100%
);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
height: 2.875rem;
padding-inline: 1.125rem;
font-weight: 500;
font-size: 0.9375rem;
border-radius: var(--radius-pill);
border: 1px solid transparent;
transition:
transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1),
background-color 200ms,
border-color 200ms,
color 200ms,
box-shadow 200ms;
will-change: transform;
white-space: nowrap;
cursor: pointer;
}
.btn:active {
transform: translateY(1px);
}
.btn-primary {
background: var(--color-violet);
color: #fff;
box-shadow:
0 1px 0 0 color-mix(in oklab, #fff 20%, transparent) inset,
0 12px 28px -8px color-mix(in oklab, var(--color-violet) 60%, transparent);
}
.btn-primary:hover {
background: color-mix(in oklab, var(--color-violet) 92%, #fff 8%);
}
.btn-ghost {
background: transparent;
color: var(--color-text);
border-color: var(--color-hairline-2);
}
.btn-ghost:hover {
background: color-mix(in oklab, #fff 4%, transparent);
border-color: color-mix(in oklab, #fff 14%, transparent);
}
.card {
position: relative;
border-radius: var(--radius-card);
border: 1px solid var(--color-hairline);
background:
linear-gradient(
180deg,
color-mix(in oklab, var(--color-surface) 96%, transparent) 0%,
color-mix(in oklab, var(--color-bg) 92%, transparent) 100%
);
overflow: hidden;
isolation: isolate;
}
.card-glow {
position: absolute;
inset: -1px;
border-radius: inherit;
padding: 1px;
background: linear-gradient(
135deg,
color-mix(in oklab, var(--color-violet) 40%, transparent),
transparent 40%
);
-webkit-mask:
linear-gradient(#000 0 0) content-box,
linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
opacity: 0;
transition: opacity 250ms ease;
pointer-events: none;
}
.card:hover .card-glow {
opacity: 1;
}
.marquee-track {
display: flex;
gap: 1rem;
width: max-content;
will-change: transform;
}
.underline-link {
position: relative;
color: var(--color-text);
}
.underline-link::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: -2px;
height: 1px;
background: currentColor;
transform: scaleX(0);
transform-origin: left center;
transition: transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.underline-link:hover::after {
transform: scaleX(1);
}
}
@layer utilities {
.glow-violet {
box-shadow: 0 0 80px -10px color-mix(in oklab, var(--color-violet) 60%, transparent);
}
.noise::before {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.6'/></svg>");
opacity: 0.06;
pointer-events: none;
mix-blend-mode: overlay;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

48
src/lib/cursor.ts Normal file
View File

@@ -0,0 +1,48 @@
import { useEffect } from 'react'
export function useCursorBlob() {
useEffect(() => {
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const fine = window.matchMedia('(pointer: fine)').matches
if (reduced || !fine) return
const dot = document.createElement('div')
dot.setAttribute('aria-hidden', 'true')
dot.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 24px;
height: 24px;
border-radius: 9999px;
pointer-events: none;
z-index: 9999;
mix-blend-mode: difference;
background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 70%);
transform: translate3d(-100px, -100px, 0);
transition: transform 100ms cubic-bezier(0.2, 0.8, 0.2, 1);
will-change: transform;
`
document.body.appendChild(dot)
let x = -100
let y = -100
function move(e: MouseEvent) {
x = e.clientX - 12
y = e.clientY - 12
dot.style.transform = `translate3d(${x}px, ${y}px, 0)`
}
function leave() {
dot.style.transform = `translate3d(-100px, -100px, 0)`
}
window.addEventListener('mousemove', move, { passive: true })
document.addEventListener('mouseleave', leave)
return () => {
window.removeEventListener('mousemove', move)
document.removeEventListener('mouseleave', leave)
dot.remove()
}
}, [])
}

53
src/lib/lenis.ts Normal file
View File

@@ -0,0 +1,53 @@
import { useEffect } from 'react'
import Lenis from 'lenis'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
let singleton: Lenis | null = null
export function useLenis() {
useEffect(() => {
if (typeof window === 'undefined') return
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (reduced) return
const lenis = new Lenis({
duration: 1.1,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
orientation: 'vertical',
gestureOrientation: 'vertical',
smoothWheel: true,
wheelMultiplier: 1,
touchMultiplier: 1.4,
})
singleton = lenis
function raf(time: number) {
lenis.raf(time)
ScrollTrigger.update()
rafId = requestAnimationFrame(raf)
}
let rafId = requestAnimationFrame(raf)
document.documentElement.classList.add('lenis', 'lenis-smooth')
return () => {
cancelAnimationFrame(rafId)
lenis.destroy()
document.documentElement.classList.remove('lenis', 'lenis-smooth')
singleton = null
}
}, [])
}
export function scrollToId(id: string) {
const el = document.getElementById(id)
if (!el) return
if (singleton) {
singleton.scrollTo(el, { offset: -72, duration: 1.1 })
} else {
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
}

23
src/lib/motion.ts Normal file
View File

@@ -0,0 +1,23 @@
import type { Variants } from 'framer-motion'
export const fadeUp: Variants = {
hidden: { opacity: 0, y: 24 },
visible: { opacity: 1, y: 0, transition: { duration: 0.7, ease: [0.2, 0.8, 0.2, 1] } },
}
export const fadeIn: Variants = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { duration: 0.6, ease: 'easeOut' } },
}
export const stagger: Variants = {
hidden: {},
visible: {
transition: { staggerChildren: 0.08, delayChildren: 0.05 },
},
}
export const scale: Variants = {
hidden: { opacity: 0, scale: 0.96 },
visible: { opacity: 1, scale: 1, transition: { duration: 0.5, ease: [0.2, 0.8, 0.2, 1] } },
}

10
src/main.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
import './index.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

26
tsconfig.app.json Normal file
View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": false,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"]
}

1
tsconfig.app.tsbuildinfo Normal file
View File

@@ -0,0 +1 @@
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/About.tsx","./src/components/CTABand.tsx","./src/components/Capabilities.tsx","./src/components/FAQ.tsx","./src/components/Footer.tsx","./src/components/Hero.tsx","./src/components/Nav.tsx","./src/components/Process.tsx","./src/components/Services.tsx","./src/components/Work.tsx","./src/components/ui/Button.tsx","./src/components/ui/Counter.tsx","./src/components/ui/GradientOrb.tsx","./src/components/ui/GridBackground.tsx","./src/content/capabilities.ts","./src/content/faq.ts","./src/content/process.ts","./src/content/services.ts","./src/content/work.ts","./src/lib/cursor.ts","./src/lib/lenis.ts","./src/lib/motion.ts"],"version":"5.9.3"}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

19
tsconfig.node.json Normal file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@@ -0,0 +1 @@
{"root":["./vite.config.ts"],"version":"5.9.3"}

31
vite.config.ts Normal file
View File

@@ -0,0 +1,31 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'node:path'
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
host: '0.0.0.0',
port: 5173,
},
build: {
target: 'es2022',
sourcemap: false,
cssMinify: 'lightningcss',
rollupOptions: {
output: {
manualChunks: {
react: ['react', 'react-dom'],
gsap: ['gsap'],
motion: ['framer-motion'],
},
},
},
},
})