diff --git a/apps/web/.cta.json b/apps/web/.cta.json
new file mode 100644
index 00000000..dddfb016
--- /dev/null
+++ b/apps/web/.cta.json
@@ -0,0 +1,19 @@
+{
+ "projectName": "web",
+ "mode": "file-router",
+ "typescript": true,
+ "packageManager": "npm",
+ "includeExamples": false,
+ "tailwind": true,
+ "addOnOptions": {},
+ "envVarValues": {},
+ "git": false,
+ "install": true,
+ "routerOnly": false,
+ "version": 1,
+ "framework": "react",
+ "chosenAddOns": [
+ "cloudflare",
+ "shadcn"
+ ]
+}
\ No newline at end of file
diff --git a/apps/web/.cursorrules b/apps/web/.cursorrules
new file mode 100644
index 00000000..20159602
--- /dev/null
+++ b/apps/web/.cursorrules
@@ -0,0 +1,7 @@
+# shadcn instructions
+
+Use the latest version of Shadcn to install new components, like this command to add a button component:
+
+```bash
+pnpm dlx shadcn@latest add button
+```
diff --git a/apps/web/.gitignore b/apps/web/.gitignore
new file mode 100644
index 00000000..8b25bb54
--- /dev/null
+++ b/apps/web/.gitignore
@@ -0,0 +1,13 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+.env
+.nitro
+.tanstack
+.wrangler
+.output
+.vinxi
+__unconfig*
+todos.json
diff --git a/apps/web/.vscode/settings.json b/apps/web/.vscode/settings.json
new file mode 100644
index 00000000..00b5278e
--- /dev/null
+++ b/apps/web/.vscode/settings.json
@@ -0,0 +1,11 @@
+{
+ "files.watcherExclude": {
+ "**/routeTree.gen.ts": true
+ },
+ "search.exclude": {
+ "**/routeTree.gen.ts": true
+ },
+ "files.readonlyInclude": {
+ "**/routeTree.gen.ts": true
+ }
+}
diff --git a/apps/web/README.md b/apps/web/README.md
new file mode 100644
index 00000000..f6caa1e5
--- /dev/null
+++ b/apps/web/README.md
@@ -0,0 +1,215 @@
+Welcome to your new TanStack Start app!
+
+# Getting Started
+
+To run this application:
+
+```bash
+npm install
+npm run dev
+```
+
+# Building For Production
+
+To build this application for production:
+
+```bash
+npm run build
+```
+
+## Testing
+
+This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
+
+```bash
+npm run test
+```
+
+## Styling
+
+This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
+
+### Removing Tailwind CSS
+
+If you prefer not to use Tailwind CSS:
+
+1. Remove the demo pages in `src/routes/demo/`
+2. Replace the Tailwind import in `src/styles.css` with your own styles
+3. Remove `tailwindcss()` from the plugins array in `vite.config.ts`
+4. Uninstall the packages: `npm install @tailwindcss/vite tailwindcss -D`
+
+
+## Deploy to Cloudflare Workers
+
+This project uses the Cloudflare Vite plugin (configured in `vite.config.ts`) and `wrangler.jsonc`:
+
+1. Install Wrangler: `npm install -g wrangler`
+2. Authenticate: `wrangler login`
+3. Deploy: `npx wrangler deploy`
+
+For production env vars, run `wrangler secret put MY_VAR` for each secret listed in `.env.example`. Public (non-secret) vars go in `wrangler.jsonc` under `vars`.
+
+KV, D1, R2, and Durable Object bindings are configured in `wrangler.jsonc` — see https://developers.cloudflare.com/workers/wrangler/configuration/.
+
+
+## Shadcn
+
+Add components using the latest version of [Shadcn](https://ui.shadcn.com/).
+
+```bash
+pnpm dlx shadcn@latest add button
+```
+
+
+
+## Routing
+
+This project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`.
+
+### Adding A Route
+
+To add a new route to your application just add a new file in the `./src/routes` directory.
+
+TanStack will automatically generate the content of the route file for you.
+
+Now that you have two routes you can use a `Link` component to navigate between them.
+
+### Adding Links
+
+To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
+
+```tsx
+import { Link } from "@tanstack/react-router";
+```
+
+Then anywhere in your JSX you can use it like so:
+
+```tsx
+About
+```
+
+This will create a link that will navigate to the `/about` route.
+
+More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
+
+### Using A Layout
+
+In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you render `{children}` in the `shellComponent`.
+
+Here is an example layout that includes a header:
+
+```tsx
+import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
+
+export const Route = createRootRoute({
+ head: () => ({
+ meta: [
+ { charSet: 'utf-8' },
+ { name: 'viewport', content: 'width=device-width, initial-scale=1' },
+ { title: 'My App' },
+ ],
+ }),
+ shellComponent: ({ children }) => (
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+ ),
+})
+```
+
+More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
+
+## Server Functions
+
+TanStack Start provides server functions that allow you to write server-side code that seamlessly integrates with your client components.
+
+```tsx
+import { createServerFn } from '@tanstack/react-start'
+
+const getServerTime = createServerFn({
+ method: 'GET',
+}).handler(async () => {
+ return new Date().toISOString()
+})
+
+// Use in a component
+function MyComponent() {
+ const [time, setTime] = useState('')
+
+ useEffect(() => {
+ getServerTime().then(setTime)
+ }, [])
+
+ return
Server time: {time}
+}
+```
+
+## API Routes
+
+You can create API routes by using the `server` property in your route definitions:
+
+```tsx
+import { createFileRoute } from '@tanstack/react-router'
+import { json } from '@tanstack/react-start'
+
+export const Route = createFileRoute('/api/hello')({
+ server: {
+ handlers: {
+ GET: () => json({ message: 'Hello, World!' }),
+ },
+ },
+})
+```
+
+## Data Fetching
+
+There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
+
+For example:
+
+```tsx
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/people')({
+ loader: async () => {
+ const response = await fetch('https://swapi.dev/api/people')
+ return response.json()
+ },
+ component: PeopleComponent,
+})
+
+function PeopleComponent() {
+ const data = Route.useLoaderData()
+ return (
+
+ {data.results.map((person) => (
+
{person.name}
+ ))}
+
+ )
+}
+```
+
+Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
+
+# Demo files
+
+Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
+
+# Learn More
+
+You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
+
+For TanStack Start specific documentation, visit [TanStack Start](https://tanstack.com/start).
diff --git a/apps/web/components.json b/apps/web/components.json
new file mode 100644
index 00000000..cdcadc69
--- /dev/null
+++ b/apps/web/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/styles.css",
+ "baseColor": "zinc",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "#/components",
+ "utils": "#/lib/utils",
+ "ui": "#/components/ui",
+ "lib": "#/lib",
+ "hooks": "#/hooks"
+ },
+ "iconLibrary": "lucide"
+}
\ No newline at end of file
diff --git a/apps/web/package.json b/apps/web/package.json
new file mode 100644
index 00000000..785ec2ce
--- /dev/null
+++ b/apps/web/package.json
@@ -0,0 +1,69 @@
+{
+ "name": "@opencut/web",
+ "private": true,
+ "type": "module",
+ "imports": {
+ "#/*": "./src/*"
+ },
+ "scripts": {
+ "dev": "vite dev --port 5173",
+ "build": "vite build",
+ "preview": "vite preview",
+ "test": "vitest run",
+ "deploy": "bun run build && wrangler deploy"
+ },
+ "dependencies": {
+ "@base-ui/react": "^1.4.1",
+ "@cloudflare/vite-plugin": "^1.26.0",
+ "@hookform/resolvers": "^5.2.2",
+ "@tailwindcss/vite": "^4.1.18",
+ "@tanstack/react-devtools": "latest",
+ "@tanstack/react-router": "latest",
+ "@tanstack/react-router-devtools": "latest",
+ "@tanstack/react-router-ssr-query": "latest",
+ "@tanstack/react-start": "latest",
+ "@tanstack/router-plugin": "^1.132.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "^1.1.1",
+ "date-fns": "^4.1.0",
+ "embla-carousel-react": "^8.6.0",
+ "input-otp": "^1.4.2",
+ "lucide-react": "^1.14.0",
+ "next-themes": "^0.4.6",
+ "radix-ui": "^1.4.3",
+ "react": "^19.2.0",
+ "react-day-picker": "^10.0.0",
+ "react-dom": "^19.2.0",
+ "react-hook-form": "^7.75.0",
+ "react-resizable-panels": "^4",
+ "recharts": "3.8.0",
+ "sonner": "^2.0.7",
+ "tailwind-merge": "^3.0.2",
+ "tailwindcss": "^4.1.18",
+ "tw-animate-css": "^1.3.6",
+ "vaul": "^1.1.2",
+ "zod": "^4.4.3"
+ },
+ "devDependencies": {
+ "@tailwindcss/typography": "^0.5.16",
+ "@tanstack/devtools-vite": "latest",
+ "@testing-library/dom": "^10.4.1",
+ "@testing-library/react": "^16.3.0",
+ "@types/node": "^22.10.2",
+ "@types/react": "^19.2.0",
+ "@types/react-dom": "^19.2.0",
+ "@vitejs/plugin-react": "^6.0.1",
+ "jsdom": "^28.1.0",
+ "typescript": "^6.0.2",
+ "vite": "^8.0.0",
+ "vitest": "^4.1.5",
+ "wrangler": "^4.70.0"
+ },
+ "pnpm": {
+ "onlyBuiltDependencies": [
+ "esbuild",
+ "lightningcss"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/apps/web/public/favicon.ico b/apps/web/public/favicon.ico
new file mode 100644
index 00000000..a11777cc
Binary files /dev/null and b/apps/web/public/favicon.ico differ
diff --git a/apps/web/public/logo192.png b/apps/web/public/logo192.png
new file mode 100644
index 00000000..fc44b0a3
Binary files /dev/null and b/apps/web/public/logo192.png differ
diff --git a/apps/web/public/logo512.png b/apps/web/public/logo512.png
new file mode 100644
index 00000000..a4e47a65
Binary files /dev/null and b/apps/web/public/logo512.png differ
diff --git a/apps/web/public/manifest.json b/apps/web/public/manifest.json
new file mode 100644
index 00000000..078ef501
--- /dev/null
+++ b/apps/web/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "TanStack App",
+ "name": "Create TanStack App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/apps/web/public/robots.txt b/apps/web/public/robots.txt
new file mode 100644
index 00000000..e9e57dc4
--- /dev/null
+++ b/apps/web/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/apps/web/src/components/ui/accordion.tsx b/apps/web/src/components/ui/accordion.tsx
new file mode 100644
index 00000000..b16de2af
--- /dev/null
+++ b/apps/web/src/components/ui/accordion.tsx
@@ -0,0 +1,64 @@
+import * as React from "react"
+import { ChevronDownIcon } from "lucide-react"
+import { Accordion as AccordionPrimitive } from "radix-ui"
+
+import { cn } from "#/lib/utils.ts"
+
+function Accordion({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function AccordionItem({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AccordionTrigger({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ svg]:rotate-180",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+ )
+}
+
+function AccordionContent({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+