# CC-1 Brief — Apps/www Next.js Port + DS Retrofit + Production Bootstrap **Date**: 2026-04-25 (autored late evening 2026-04-24 dok Marko spava) **Status**: Scope B ratified by Marko ("c da slazem se"), full port + bootstrap delegated **Authorized by**: Marko Marković ("i sve sam guraj sad, sam odlucuj") **PM**: claude-opus-4-7 (Cowork) **Scope**: Migrate apps/www from Vite SPA to Next.js 14+ App Router, retrofit DS spec (Stage 1+2+3+light), bootstrap production infrastructure (auth, API routes, Stripe, analytics, i18n, SEO) --- ## §0 Pre-flight context **Existing apps/www state** (auditован 2026-04-24): Stack: - Vite 6.3.5 + React 19.1.0 + TypeScript 5.9.3 - No Tailwind, no UI library — plain CSS sa custom properties u `src/styles/globals.css` - Lucide-react za ikonice - Vitest za testing - npm scripts: `dev` / `build` / `preview` / `test` / `test:watch` Komponente (sve u `src/components/`): - `Navbar` — top nav - `Hero` — h1 "AI Agents That Remember", 2 download CTAs (Windows + macOS GitHub releases), bee-orchestrator hero illustration, honey glow background - `Features` — TBD audit - `CrownJewels` — TBD audit - `HowItWorks` — TBD audit - `Pricing` — **FUNCTIONAL Stripe checkout integration** sa 3 tiera (FREE/PRO $19/TEAMS $49), API endpoint `https://cloud.waggle-os.ai/api/stripe/create-checkout-session`, "Most Popular" badge na PRO - `Enterprise` — TBD audit - `BetaSignup` — TBD audit - `BrandPersonasCard` — sa Vitest test coverage - `Footer` — TBD audit Stilski pristup: **inline styles sa CSS var refs** (e.g., `style={{ color: 'var(--honey-500)' }}`). CSS vars u `globals.css` već usklađeni sa DS Stage 1 dark tokens (hive-50 do hive-950, honey-300/400/500/600, status-ai/healthy, shadow-honey/elevated). **NEMA `[data-theme="light"]` block** — light mode treba dodati per DS Stage 4. Public assets (`public/brand/`): - 13 bee personas u light + dark variants (.png) - 16 app icons u light + dark variants (.jpeg) - hex-texture-light.png + hex-texture-dark.png - logo.jpeg + logo-light.jpeg - Backup folders sa older bee assets (mogu biti deleted post-port) Test setup: - `__tests__/setup.ts` - `__tests__/BrandPersonasCard.test.tsx` - `vitest.config.ts` Routing: **single-page** (App.tsx renderuje sve komponente sequential, no React Router). Section IDs: hero, features, crown-jewels, how-it-works, pricing, enterprise, beta-signup, footer. --- ## §1 Migration target — Next.js 14 App Router ### §1.1 Stack target ```json { "name": "waggle-www", "private": true, "version": "0.3.0", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "test": "vitest run", "test:watch": "vitest" }, "dependencies": { "next": "^15.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", "lucide-react": "^0.577.0" }, "devDependencies": { "@types/node": "^22.x", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "typescript": "^5.9.3", "vitest": "^2.x", "@vitejs/plugin-react": "^4.7.0", "happy-dom": "^15.x" } } ``` Ne koristiti Tailwind — postojeći CSS vars patternd radi i mapira na DS. Retain. ### §1.2 Folder structure target ``` apps/www/ ├── app/ # Next.js App Router │ ├── layout.tsx # Root layout sa │ ├── page.tsx # Landing home (port App.tsx → server component sa client island-ima) │ ├── globals.css # Import postojeći styles/globals.css + dodati [data-theme="light"] block │ ├── pricing/ │ │ └── page.tsx # Standalone pricing page za direct linking │ ├── api/ │ │ ├── stripe/ │ │ │ └── checkout/ │ │ │ └── route.ts # POST /api/stripe/checkout — replace external cloud.waggle-os.ai │ │ ├── waitlist/ │ │ │ └── route.ts # POST /api/waitlist — beta signup endpoint │ │ └── analytics/ │ │ └── route.ts # POST /api/analytics — anonymous event collection │ ├── (legal)/ │ │ ├── privacy/page.tsx │ │ ├── terms/page.tsx │ │ └── cookies/page.tsx │ ├── opengraph-image.png # OG fallback image │ ├── twitter-image.png # Twitter card │ ├── icon.png # Favicon │ └── apple-icon.png ├── components/ # All client components (with "use client" header) │ ├── Navbar.tsx │ ├── Hero.tsx # Server component (static content) │ ├── Features.tsx # Server component │ ├── CrownJewels.tsx │ ├── HowItWorks.tsx │ ├── Pricing.tsx # CLIENT (useState za loading, useCallback za checkout) │ ├── Enterprise.tsx │ ├── BetaSignup.tsx # CLIENT (form state) │ ├── BrandPersonasCard.tsx # Server (static render) │ ├── Footer.tsx │ ├── ThemeToggle.tsx # NEW — Auto/Light/Dark toggle, sets data-theme on │ └── CookieBanner.tsx # NEW — GDPR consent ├── lib/ │ ├── stripe.ts # Server-side Stripe client init │ ├── analytics.ts # Client-side event tracking │ ├── i18n.ts # Locale detection + helpers │ └── theme.ts # data-theme persistence (localStorage waggle.theme) ├── data/ │ └── personas.ts # Move from src/data ├── public/ │ ├── brand/ # Same as before — assets kept │ └── robots.txt # NEW ├── messages/ # i18n locale strings (English first) │ └── en.json ├── middleware.ts # i18n routing + locale detection ├── next.config.mjs # Next.js config ├── tsconfig.json # Updated paths ├── vitest.config.ts # Adjust for Next.js └── package.json ``` ### §1.3 Per-component port plan **Server components** (no "use client", static render): - `Hero` — replace `` with `next/image` for optimization. Add LCP priority hint. - `Features` — verify static; if has hover state, mark as client. - `CrownJewels` — same pattern. - `HowItWorks` — same. - `Enterprise` — same. - `BrandPersonasCard` — uses static personas data; server component. - `Footer` — static. **Client components** (need "use client" directive): - `Navbar` — likely has scroll-aware state, mobile menu toggle. - `Pricing` — uses `useRef`, `useEffect` (IntersectionObserver), `useState` (loading), `useCallback` (Stripe checkout). All client-side. Update fetch URL from `https://cloud.waggle-os.ai/api/stripe/create-checkout-session` to **internal** `/api/stripe/checkout`. - `BetaSignup` — form state, submission loading, success/error UI. Update endpoint to internal `/api/waitlist`. - `ThemeToggle` (NEW) — `useState` + `useEffect` za localStorage sync + `document.documentElement.setAttribute('data-theme', mode)`. - `CookieBanner` (NEW) — consent state. ### §1.4 Path mapping (asset references) - `` → `` - All `public/brand/*` references stay relative to public root. - CSS `url("data:image/svg+xml,...")` honeycomb pattern stays inline u globals.css. --- ## §2 DS retrofit — light/dark mode + Stage 4 tokens ### §2.1 globals.css augmentation Postojeći `:root` block sadrži dark tokens. Dodaj `[data-theme="light"]` block sa Stage 4 light tokens: ```css [data-theme="light"] { --hive-950: #fafaf7; /* swap top */ --hive-900: #f0ede5; --hive-850: #e8e3d6; --hive-800: #d8d2c1; --hive-700: #b8b0a0; --hive-600: #8a8073; --hive-500: #6b6359; --hive-400: #4a443e; --hive-300: #2c2724; --hive-200: #1a1815; --hive-100: #0e0c0a; --hive-50: #1a1815; /* swap bottom — used as fg */ --honey-600: #8e6912; /* darker for AAA on light */ --honey-500: #b8821f; /* recalibrated AAA on cream */ --honey-400: #c4a418; --honey-300: #e0c869; --honey-glow: rgba(184, 130, 31, 0.15); --honey-pulse: rgba(184, 130, 31, 0.08); --status-ai: #6b46c1; --status-healthy: #059669; --shadow-honey: 0 0 24px rgba(184, 130, 31, 0.18), 0 0 4px rgba(184, 130, 31, 0.10); --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04); } ``` ### §2.2 Theme toggle component `components/ThemeToggle.tsx`: ```tsx "use client"; import { useState, useEffect } from 'react'; import { Sun, Moon, Monitor } from 'lucide-react'; type Mode = 'auto' | 'light' | 'dark'; export default function ThemeToggle() { const [mode, setMode] = useState('auto'); useEffect(() => { const stored = localStorage.getItem('waggle.theme') as Mode | null; setMode(stored ?? 'auto'); }, []); useEffect(() => { const apply = (m: Mode) => { const effective = m === 'auto' ? (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark') : m; document.documentElement.setAttribute('data-theme', effective); }; apply(mode); localStorage.setItem('waggle.theme', mode); if (mode === 'auto') { const mq = window.matchMedia('(prefers-color-scheme: light)'); const handler = () => apply(mode); mq.addEventListener('change', handler); return () => mq.removeEventListener('change', handler); } }, [mode]); return (
); } ``` Place `` u Navbar desno (visible on all pages). ### §2.3 Asset variant resolution Bee personas, app icons, hex texture imaju light + dark variants. Dodati helper: ```tsx import { useTheme } from '@/lib/theme'; const theme = useTheme(); const beeAsset = `/brand/bee-orchestrator-${theme}.png`; ``` Za Hero specifically: switch bee asset on theme change (orchestrator-dark vs orchestrator-light). --- ## §3 Bootstrap items — production infrastructure ### §3.1 API routes #### `/api/stripe/checkout` (POST) - Replace external `https://cloud.waggle-os.ai/api/stripe/create-checkout-session` - Server-side init Stripe client sa `STRIPE_SECRET_KEY` env var - Body: `{ tier: 'PRO' | 'TEAMS', billingPeriod: 'monthly' | 'annual' }` - Returns: `{ url: string }` (Stripe Checkout Session URL) - Error: `{ message: string }` 400/500 - Use Stripe Price IDs from env (`STRIPE_PRICE_PRO_MONTHLY`, `STRIPE_PRICE_TEAMS_MONTHLY`, etc.) #### `/api/waitlist` (POST) - Body: `{ email: string, persona?: string, source?: string }` - Validate email format - Store: forward to Resend / Supabase / Postmark — choose one (Marko: pick what's cheapest, suggest Resend for now) - Returns: `{ success: boolean }` - Rate limit: 5 requests/min per IP #### `/api/analytics` (POST) - Body: `{ event: string, properties: Record, anonymous_id: string }` - Anonymous events only (no PII unless user opted in via cookie consent) - Forward to PostHog or Plausible — Marko pick (PostHog je in tools list, prefer) - Returns: `{ accepted: boolean }` ### §3.2 Cookie consent + GDPR `components/CookieBanner.tsx`: - Show on first visit - 3 options: "Accept all", "Necessary only", "Customize" - Store consent: `localStorage.setItem('waggle.consent', JSON.stringify({...}))` - Categories: necessary (always on), analytics (opt-in), marketing (opt-in) - Render bottom-fixed sa backdrop blur na dark / soft drop shadow na light ### §3.3 Legal pages `app/(legal)/privacy/page.tsx`, `terms/page.tsx`, `cookies/page.tsx`: - Markdown-driven content (lib/markdown.ts helper) - Static generation (export const dynamic = 'force-static') - Linked from Footer + CookieBanner Privacy text seed: koristi `pm-toolkit:privacy-policy` skill output kao starting draft, manual review pre publish-a. ### §3.4 SEO + metadata `app/layout.tsx`: ```tsx export const metadata: Metadata = { metadataBase: new URL('https://waggle-os.ai'), title: { default: 'Waggle — AI Agents That Remember', template: '%s · Waggle' }, description: 'A workspace where AI agents remember your context, connect to your tools, and improve with every interaction. Desktop-native. Privacy-first. Local-first cognitive layer with bitemporal memory.', keywords: ['AI memory', 'cognitive layer', 'local-first AI', 'EU AI Act', 'GDPR AI', 'bitemporal knowledge graph', 'MCP protocol', 'agent harness'], authors: [{ name: 'Egzakta Group' }], openGraph: { type: 'website', siteName: 'Waggle', images: [{ url: '/opengraph-image.png', width: 1200, height: 630 }], }, twitter: { card: 'summary_large_image', images: ['/twitter-image.png'], creator: '@waggle_os', }, robots: { index: true, follow: true }, alternates: { canonical: '/', languages: { 'en': '/' }, // expand on i18n rollout }, }; ``` `public/robots.txt`: ``` User-agent: * Allow: / Disallow: /api/ Sitemap: https://waggle-os.ai/sitemap.xml ``` `app/sitemap.ts`: ```tsx import { MetadataRoute } from 'next'; export default function sitemap(): MetadataRoute.Sitemap { return [ { url: 'https://waggle-os.ai', lastModified: new Date(), priority: 1.0 }, { url: 'https://waggle-os.ai/pricing', lastModified: new Date(), priority: 0.8 }, { url: 'https://waggle-os.ai/privacy', lastModified: new Date(), priority: 0.3 }, { url: 'https://waggle-os.ai/terms', lastModified: new Date(), priority: 0.3 }, ]; } ``` ### §3.5 i18n scaffolding (English first) Install `next-intl` (recommended for App Router): ``` pnpm add next-intl ``` Locale routing: `/[locale]/...` ali za MVP samo English live, infrastruktura ready za sledeće lokale (DE, FR, ES) bez code rewrite. `middleware.ts`: ```ts import createMiddleware from 'next-intl/middleware'; export default createMiddleware({ locales: ['en'], defaultLocale: 'en', }); export const config = { matcher: ['/((?!api|_next|.*\\..*).*)'] }; ``` `messages/en.json` — extract user-facing strings iz komponenti (Hero h1, CTA labels, pricing tier names, BetaSignup form copy). Per `feedback_i18n_landing_policy.md`: engleski first, locale-ready infra predefinisana. ### §3.6 Build + deploy **Recommended host**: **Vercel** (zero-config Next.js, native preview deploys, edge runtime za API routes, integrated analytics) Alternative: **Cloudflare Pages** (cheaper za high traffic, edge-first) Env vars u Vercel dashboard: - `STRIPE_SECRET_KEY` - `STRIPE_PRICE_PRO_MONTHLY` - `STRIPE_PRICE_TEAMS_MONTHLY` - `STRIPE_WEBHOOK_SECRET` - `RESEND_API_KEY` (ili Postmark/Supabase ekvivalent) - `POSTHOG_PROJECT_KEY` - `POSTHOG_HOST` - `NEXT_PUBLIC_POSTHOG_KEY` (client-side init) `next.config.mjs`: ```js /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, images: { formats: ['image/avif', 'image/webp'], }, experimental: { optimizePackageImports: ['lucide-react'], }, }; export default nextConfig; ``` --- ## §4 Wireframe v1.1 LOCKED gap analysis Per `project_landing_wireframe_v11_locked_2026_04_22` memory, wireframe v1.1 specifies sections: | Wireframe section | Existing component? | Action | |---|---|---| | Hero with KPI counter | Hero.tsx (no KPI counter yet) | **Augment** — add KPI counter sa "X memories preserved", "Y agents running", real-time API call ili static placeholder | | Why-now hook | NOT PRESENT | **Add** new section between Hero and Features — narrative hook ("AI without memory is groundhog day") | | Three-product columns | NOT PRESENT (Features may overlap) | **Add or refactor** — "hive-mind (OSS) / Waggle (consumer) / KVARK (enterprise)" 3-column architecture overview | | How-it-works | HowItWorks.tsx | Verify content matches v1.1 spec | | Pricing 3-tier | Pricing.tsx | ✓ Functional, **integrate with internal API route** | | Personas grid 13 bees | BrandPersonasCard.tsx (single card) | **Augment** — add full 13-grid section pre BetaSignup | | FAQ | NOT PRESENT | **Add** new FAQ section (5-7 questions: pricing, privacy, EU AI Act, MCP, OSS) | | Founder note | NOT PRESENT | **Add** "From the team" section with Marko's photo + short narrative | | CTA | Hero CTAs + Pricing CTAs + BetaSignup | Sufficient | | Footer | Footer.tsx | Verify links: Privacy, Terms, Cookies, Twitter, GitHub, Discord (TBD) | --- ## §5 Test migration Existing `__tests__/BrandPersonasCard.test.tsx` + `__tests__/setup.ts` keep but adapt: - Vitest config update (Next.js compatibility, may need `vitest-environment-vitest-pure` or `happy-dom`) - Add component tests for: ThemeToggle, CookieBanner, Pricing checkout flow (mock Stripe), BetaSignup submission - Add API route integration tests sa MSW (Mock Service Worker) --- ## §6 Sequential execution plan (12 commits suggested) Commit 1: `[port] init Next.js 15 scaffold, retain Vite parallel for rollback` Commit 2: `[port] migrate components to Next.js with use client where needed` Commit 3: `[port] move src/styles → app/globals.css + light mode tokens block` Commit 4: `[port] ThemeToggle component + integrate u Navbar` Commit 5: `[port] API routes — stripe checkout, waitlist, analytics` Commit 6: `[port] middleware.ts + i18n scaffolding (en only live)` Commit 7: `[port] legal pages (privacy/terms/cookies) + CookieBanner` Commit 8: `[port] SEO metadata + sitemap + robots + OG/Twitter images` Commit 9: `[port] add wireframe v1.1 missing sections (WhyNow + ThreeProducts + PersonasGrid + FAQ + FounderNote)` Commit 10: `[port] update tests + add new component coverage` Commit 11: `[port] build verification + Vercel deploy config` Commit 12: `[port] retire Vite scaffolding (delete vite.config.ts, src/main.tsx etc.) — POST-VERIFICATION` Commit 1-11: incremental, ne bocе vam working state. Commit 12: cleanup, tek posle visual + functional QA na deployed staging URL. --- ## §7 Halt criteria + budget - **Budget**: $0 (no API calls during port — codegen only). Stripe + Resend + PostHog incur $0 setup, pay-per-use after launch. - **Wall-clock**: 8-12h CC-1 work realistic za scratch port + bootstrap. Marko može da paste-uje brief u CC-1 kad SOTA padne, paralelno sa Gate D exit + claim-narrative work. - **Halt triggers**: - Vite cleanup (Commit 12) — only after staging deploy verified by Marko visual review - Live env vars (Stripe Production keys) — Marko populates u Vercel dashboard, not in code - Domain DNS cutover (waggle-os.ai → Vercel) — Marko handles - **Rollback**: Commits 1-11 keep Vite scaffolding present, can `git revert` and rebuild Vite if Next.js port has unforeseen issue. Commit 12 is one-way. --- ## §8 PM signals to watch - "use client" overuse (every component) — sign of confused server/client split, code review pass needed - CSS regression na light mode toggle — visual diff vs DS Stage 4 spec - Stripe checkout flow break — manual test sa test card 4242 4242 4242 4242 pre prod - SEO meta validation — `next build` output check za canonical URLs, OG images - Lighthouse score baseline — target ≥90 Performance, ≥95 Accessibility, ≥100 Best Practices, ≥95 SEO --- ## §9 Authorized by PM Marko Marković, 2026-04-24 evening, full execution delegated ("i sve sam guraj sad, sam odlucuj, ja odoh da spavam citam ujutro sta si sve uradio"). CC-1 može da počne kad benchmark završi i SOTA narrative ratifikuje, ili paralelno ako ima dovoljno context-a (Vite stack je read-only audit, port je clean greenfield Next.js). Brief je deterministic — sve odluke arhitekture su uzete u brief-u, no clarifying questions needed. --- ## §10 Companion deliverables (paralelno sa ovim brief-om) PM (claude-opus-4-7) overnight produces: 1. `briefs/2026-04-25-launch-comms-templates.md` — multi-asset suite (technical blog post, LinkedIn, Twitter, hive-mind announcement, waitlist email, press kit) 2. `briefs/e2e-persona-tests/2026-04-25-e2e-persona-test-matrix.md` — 9-archetype × scenarios test matrix (3 monetization tier × 3 user proficiency) 3. `decisions/2026-04-25-overnight-pm-execution-log.md` — what was completed overnight, decision rationale, open items za Marko ujutru Sve čekaju Marka u 2026-04-25 ujutru za review.