diff --git a/packages/website/MOCK_SETUP.md b/packages/website/MOCK_SETUP.md
new file mode 100644
index 000000000..ad2e7e6da
--- /dev/null
+++ b/packages/website/MOCK_SETUP.md
@@ -0,0 +1,220 @@
+# Visual Edit API Mock Infrastructure
+
+## Overview
+
+The Visual Edit API mock infrastructure enables local development and testing without requiring external API dependencies. It provides pattern-matched, predictable responses for common DOM manipulation requests.
+
+## Setup
+
+### Environment Configuration
+
+Mock mode is controlled by the `USE_MOCKS` environment variable.
+
+### Running in Mock Mode
+
+```bash
+# Using the provided script
+nr dev:mock
+
+# Or manually
+USE_MOCKS=true nr dev
+```
+
+### Running in Production Mode
+
+```bash
+# Normal development (uses real Cerebras API)
+nr dev
+
+# Build (always uses real API)
+nr build
+```
+
+## Architecture
+
+### Core Components
+
+1. **Environment Config** (`lib/env.ts`)
+ - Centralized environment variable management
+ - Dynamic endpoint selection based on `USE_MOCKS`
+
+2. **Mock State Manager** (`lib/mock-state/visual-edit-mock.ts`)
+ - Singleton pattern for global state management
+ - Pattern-matching engine for DOM manipulation requests
+ - Request counting for testing
+
+3. **Mock API Endpoints**
+ - `/api/mock/visual-edit` - Main code generation endpoint
+ - `/api/mock/check-visual-edit` - Health check endpoint
+ - Both return 403 if `USE_MOCKS=false`
+
+4. **Client Integration** (`instrumentation-client.ts`)
+ - Dynamic endpoint selection
+ - Window-scoped `USE_MOCKS` flag
+
+## Supported Mock Patterns
+
+The mock state manager recognizes these patterns and generates appropriate JavaScript:
+
+| Pattern Keywords | Example Prompt | Generated Code |
+|-----------------|----------------|----------------|
+| `change text`, `update text`, `set text` | "Change text to 'Hello World'" | `element.textContent = "Hello World";` |
+| `add class`, `toggle class` | "Add class 'active'" | `element.classList.add("active");` |
+| `remove class`, `delete class` | "Remove class 'hidden'" | `element.classList.remove("hidden");` |
+| `change color`, `set color` | "Change color to #ff0000" | `element.style.color = "#ff0000";` |
+| `background` | "Set background to blue" | `element.style.backgroundColor = "blue";` |
+| `hide`, `remove` | "Hide this element" | `element.style.display = "none";` |
+| `show`, `reveal`, `display` | "Show the element" | `element.style.display = "block";` |
+| `fade`, `opacity` | "Fade to 0.5 opacity" | `element.style.opacity = "0.5";` |
+| `animate`, `transition` | "Animate this element" | `element.style.transition = "all 0.3s ease";`
`element.style.transform = "scale(1.05)";` |
+| `border` | "Add red border" | `element.style.border = "2px solid red";` |
+| `width`, `height`, `size` | "Set width to 200px" | `element.style.width = "200px";` |
+
+### Fallback Behavior
+
+If no pattern matches, the mock returns:
+```javascript
+element.style.outline = "2px solid #3b82f6";
+```
+
+## API Contract
+
+### POST /api/mock/visual-edit
+
+**Request:**
+```json
+{
+ "messages": [
+ {
+ "role": "user",
+ "content": "Change the text to 'Hello World'"
+ }
+ ]
+}
+```
+
+**Response:**
+```javascript
+element.textContent = "Hello World";
+```
+
+**Headers:**
+- `Content-Type: text/javascript`
+- `Cache-Control: no-cache, no-store, must-revalidate`
+
+**Constraints:**
+- Maximum 15,000 characters per message
+- 300-500ms simulated delay for realism
+- Returns 403 if `USE_MOCKS=false`
+
+### GET /api/mock/check-visual-edit
+
+**Response:**
+```json
+{
+ "healthy": true
+}
+```
+
+## Testing
+
+### Manual Test Checklist
+
+Run these tests with `USE_MOCKS=true`:
+
+- [ ] **Environment Toggle**
+ - [ ] Verify `USE_MOCKS=false` returns 403 from mock endpoints
+ - [ ] Verify `USE_MOCKS=true` enables mock endpoints
+
+- [ ] **Code Generation**
+ - [ ] Text change: "Change text to 'Test'"
+ - [ ] Add class: "Add class 'active'"
+ - [ ] Remove class: "Remove class 'hidden'"
+ - [ ] Color change: "Change color to red"
+ - [ ] Background: "Set background to blue"
+ - [ ] Hide element: "Hide this"
+ - [ ] Show element: "Show this"
+ - [ ] Opacity: "Set opacity to 0.5"
+ - [ ] Animation: "Animate this element"
+ - [ ] Border: "Add green border"
+ - [ ] Size: "Set width to 300px"
+
+- [ ] **Integration**
+ - [ ] No external API calls made in mock mode
+ - [ ] Generated code executes without errors
+ - [ ] Visual changes apply correctly to DOM
+
+- [ ] **Error Handling**
+ - [ ] Message exceeding 15,000 characters returns 400
+ - [ ] Invalid JSON returns 500
+ - [ ] CORS headers present on all responses
+
+### Programmatic Testing
+
+```typescript
+import { visualEditMockState } from "./lib/mock-state/visual-edit-mock"
+
+// Reset state between tests
+visualEditMockState.reset()
+
+// Generate code
+const code = visualEditMockState.generateCode("Change text to 'Test'")
+console.log(code) // element.textContent = "Test";
+
+// Check request count
+console.log(visualEditMockState.getRequestCount()) // 1
+```
+
+### Mock Reset Utility
+
+The mock state manager provides a `reset()` method for test isolation:
+
+```typescript
+// Reset request count and state
+visualEditMockState.reset()
+```
+
+## Limitations
+
+- Mock responses are pattern-matched, not AI-generated
+- Complex multi-step manipulations return single-step code
+- No natural language understanding beyond keyword matching
+- No context awareness across multiple requests
+
+## Production Behavior
+
+When `USE_MOCKS=false` (production):
+- All requests route to `/api/visual-edit` (real Cerebras API)
+- Mock endpoints return 403
+- No mock state tracking
+- Full AI-powered code generation
+
+## Troubleshooting
+
+### Mock endpoints return 403
+- Verify `USE_MOCKS=true` environment variable
+- Check that script is injected in `app/layout.tsx`
+- Confirm window.USE_MOCKS is set in browser console
+
+### Generated code doesn't match pattern
+- Review supported patterns table
+- Check that prompt includes recognized keywords
+- Fallback behavior applies for unrecognized patterns
+
+### No visual changes occur
+- Verify React Grab is initialized
+- Check browser console for JavaScript errors
+- Confirm element selection is correct
+
+## Future Enhancements
+
+Potential improvements to the mock infrastructure:
+
+- [ ] Enhanced pattern matching with regex support
+- [ ] Multi-step code generation for complex requests
+- [ ] Mock analytics and usage tracking
+- [ ] UI for inspecting mock state
+- [ ] Configurable delay ranges
+- [ ] Pattern customization via config file
+- [ ] Request/response logging
+- [ ] Mock data persistence
\ No newline at end of file
diff --git a/packages/website/app/api/mock/check-visual-edit/route.ts b/packages/website/app/api/mock/check-visual-edit/route.ts
new file mode 100644
index 000000000..d9cd669d1
--- /dev/null
+++ b/packages/website/app/api/mock/check-visual-edit/route.ts
@@ -0,0 +1,26 @@
+import { NextResponse } from "next/server"
+import { env } from "../../../../lib/env"
+
+const getCorsHeaders = () => ({
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
+ "Access-Control-Allow-Headers": "Content-Type",
+})
+
+export const OPTIONS = async () => {
+ return NextResponse.json({}, { headers: getCorsHeaders() })
+}
+
+export const GET = async () => {
+ if (!env.useMocks) {
+ return NextResponse.json(
+ { error: "Mock endpoints are disabled. Set USE_MOCKS=true to enable." },
+ { status: 403, headers: getCorsHeaders() }
+ )
+ }
+
+ return NextResponse.json(
+ { healthy: true },
+ { headers: getCorsHeaders() }
+ )
+}
\ No newline at end of file
diff --git a/packages/website/app/api/mock/visual-edit/route.ts b/packages/website/app/api/mock/visual-edit/route.ts
new file mode 100644
index 000000000..04879aca7
--- /dev/null
+++ b/packages/website/app/api/mock/visual-edit/route.ts
@@ -0,0 +1,59 @@
+import { NextRequest, NextResponse } from "next/server"
+import { env } from "../../../../lib/env"
+import { visualEditMockState } from "../../../../lib/mock-state/visual-edit-mock"
+
+const MAX_INPUT_CHARACTERS = 15000
+
+const getCorsHeaders = () => ({
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
+})
+
+export const OPTIONS = async () => {
+ return NextResponse.json({}, { headers: getCorsHeaders() })
+}
+
+export const POST = async (request: NextRequest) => {
+ if (!env.useMocks) {
+ return NextResponse.json(
+ { error: "Mock endpoints are disabled. Set USE_MOCKS=true to enable." },
+ { status: 403, headers: getCorsHeaders() }
+ )
+ }
+
+ try {
+ const body = await request.json()
+ const messages = body.messages || []
+
+ for (const message of messages) {
+ if (message.content.length > MAX_INPUT_CHARACTERS) {
+ return NextResponse.json(
+ { error: `Message exceeds maximum length of ${MAX_INPUT_CHARACTERS} characters` },
+ { status: 400, headers: getCorsHeaders() }
+ )
+ }
+ }
+
+ const lastMessage = messages[messages.length - 1]
+ const prompt = lastMessage?.content || ""
+
+ await new Promise((resolve) => setTimeout(resolve, 300 + Math.random() * 200))
+
+ const generatedCode = visualEditMockState.generateCode(prompt)
+
+ return new NextResponse(generatedCode, {
+ headers: {
+ ...getCorsHeaders(),
+ "Content-Type": "text/javascript",
+ "Cache-Control": "no-cache, no-store, must-revalidate",
+ },
+ })
+ } catch (error) {
+ console.error("Mock visual-edit error:", error)
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500, headers: getCorsHeaders() }
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/website/app/layout.tsx b/packages/website/app/layout.tsx
index 4c3c7b75a..93c999f1f 100644
--- a/packages/website/app/layout.tsx
+++ b/packages/website/app/layout.tsx
@@ -56,6 +56,11 @@ const RootLayout = ({