diff --git a/package-lock.json b/package-lock.json index 5a26a2c8..f15ff5ac 100755 --- a/package-lock.json +++ b/package-lock.json @@ -58,6 +58,7 @@ "react": "^18.3.1", "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", + "react-helmet-async": "^3.0.0", "react-hook-form": "^7.53.0", "react-i18next": "^17.0.8", "react-resizable-panels": "^2.1.3", @@ -6001,6 +6002,26 @@ "react": "^18.3.1" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-helmet-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-3.0.0.tgz", + "integrity": "sha512-nA3IEZfXiclgrz4KLxAhqJqIfFDuvzQwlKwpdmzZIuC1KNSghDEIXmyU0TKtbM+NafnkICcwx8CECFrZ/sL/1w==", + "license": "Apache-2.0", + "dependencies": { + "invariant": "^2.2.4", + "react-fast-compare": "^3.2.2", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-hook-form": { "version": "7.53.1", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.1.tgz", @@ -6364,6 +6385,12 @@ "node": ">=10" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index 8aeef11f..addae109 100755 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "react": "^18.3.1", "react-day-picker": "^8.10.1", "react-dom": "^18.3.1", + "react-helmet-async": "^3.0.0", "react-hook-form": "^7.53.0", "react-i18next": "^17.0.8", "react-resizable-panels": "^2.1.3", diff --git a/public/llms.txt b/public/llms.txt new file mode 100644 index 00000000..b2b1f41b --- /dev/null +++ b/public/llms.txt @@ -0,0 +1,41 @@ +# Cohorta + +> AI-powered synthetic focus groups for product research and user insights + +Cohorta is a SaaS platform that lets product teams, UX researchers, and marketers run AI-moderated focus groups with synthetic personas — without recruiting real participants. + +## What Cohorta does + +- **Synthetic persona generation**: Create detailed AI personas from a single research brief. Each persona has demographics, psychographics, OCEAN personality traits, behavioral attributes, and a realistic backstory. +- **AI-moderated focus groups**: Run asynchronous focus group sessions where AI personas discuss your research topics, react to stimuli (mockups, documents, pricing pages), and provide nuanced qualitative feedback. +- **Thematic analysis**: Automatically extract key themes, sentiment, and insights from focus group transcripts. +- **Discussion guide generation**: AI generates a structured discussion guide based on your research objectives. + +## Use cases + +- Product concept testing before building +- Pricing sensitivity research +- UX evaluation of wireframes and prototypes +- Brand perception testing +- Feature prioritisation research +- Market segmentation insights + +## Key facts + +- Results in minutes, not weeks — no participant recruitment +- UK-hosted infrastructure, GDPR-compliant +- Pay-per-project credit system (credits never expire) +- Free trial: 50 credits on signup, no card required +- Supports Azure OpenAI (GPT-5.4) and compatible models + +## Pricing + +- Starter: $49 / 50 credits — ~25 AI personas or 1 focus group run +- Pro: $199 / 220 credits — ~110 AI personas or 5 focus group runs +- Scale: $499 / 600 credits — ~300 AI personas or 15 focus group runs + +## Company + +Made by AImpress Ltd, UK. +Contact: hello@ai-impress.com +Website: https://cohorta.ai-impress.com diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000..ccf7214b --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,11 @@ +User-agent: * +Allow: / +Disallow: /app/ +Disallow: /dashboard +Disallow: /synthetic-users +Disallow: /focus-groups +Disallow: /admin +Disallow: /usage +Disallow: /billing + +Sitemap: https://cohorta.ai-impress.com/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 00000000..6c435dd8 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,33 @@ + + + + https://cohorta.ai-impress.com/ + weekly + 1.0 + + + https://cohorta.ai-impress.com/#pricing + weekly + 0.8 + + + https://cohorta.ai-impress.com/#product + monthly + 0.7 + + + https://cohorta.ai-impress.com/about + monthly + 0.5 + + + https://cohorta.ai-impress.com/privacy + yearly + 0.3 + + + https://cohorta.ai-impress.com/terms + yearly + 0.3 + + diff --git a/src/App.tsx b/src/App.tsx index ef920419..aa442c49 100755 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import { Toaster } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { HelmetProvider } from 'react-helmet-async'; import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; import Index from "./pages/Index"; import NotFound from "./pages/NotFound"; @@ -35,6 +36,7 @@ import "./styles/backButton.css"; const queryClient = new QueryClient(); const App = () => ( + @@ -96,6 +98,7 @@ const App = () => ( + ); export default App; diff --git a/src/components/PageMeta.tsx b/src/components/PageMeta.tsx new file mode 100644 index 00000000..2fd7ee06 --- /dev/null +++ b/src/components/PageMeta.tsx @@ -0,0 +1,37 @@ +import { Helmet } from 'react-helmet-async'; + +interface PageMetaProps { + title?: string; + description?: string; + noindex?: boolean; + ogImage?: string; +} + +const SITE_NAME = 'Cohorta'; +const BASE_URL = 'https://cohorta.ai-impress.com'; +const DEFAULT_DESCRIPTION = + 'Run AI-moderated focus groups with synthetic personas. Get real research insights in minutes, not weeks — no recruits, no scheduling, no bias.'; +const DEFAULT_OG_IMAGE = `${BASE_URL}/og-image.png`; + +export default function PageMeta({ + title, + description = DEFAULT_DESCRIPTION, + noindex = false, + ogImage = DEFAULT_OG_IMAGE, +}: PageMetaProps) { + const fullTitle = title ? `${title} — ${SITE_NAME}` : `${SITE_NAME} — AI-Powered Synthetic Focus Groups`; + + return ( + + {fullTitle} + + {noindex && } + + + + + + + + ); +} diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index ff7f6a20..71807c52 100755 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -1,3 +1,5 @@ +import { Helmet } from 'react-helmet-async'; +import PageMeta from '@/components/PageMeta'; import Hero from '@/components/landing/Hero'; import CompassBg from '@/components/landing/CompassBg'; import StatsBand from '@/components/landing/StatsBand'; @@ -11,9 +13,43 @@ import Pricing from '@/components/landing/Pricing'; import FAQ from '@/components/landing/FAQ'; import FinalCTA from '@/components/landing/FinalCTA'; +const JSON_LD = [ + { + '@context': 'https://schema.org', + '@type': 'Organization', + name: 'AImpress Ltd', + url: 'https://cohorta.ai-impress.com', + logo: 'https://cohorta.ai-impress.com/favicon.svg', + contactPoint: { '@type': 'ContactPoint', email: 'hello@ai-impress.com' }, + }, + { + '@context': 'https://schema.org', + '@type': 'SoftwareApplication', + name: 'Cohorta', + url: 'https://cohorta.ai-impress.com', + applicationCategory: 'BusinessApplication', + operatingSystem: 'Web', + description: + 'AI-powered synthetic focus groups for product research. Generate AI personas and run moderated sessions in minutes.', + offers: [ + { '@type': 'Offer', name: 'Starter', price: '49', priceCurrency: 'USD' }, + { '@type': 'Offer', name: 'Pro', price: '199', priceCurrency: 'USD' }, + { '@type': 'Offer', name: 'Scale', price: '499', priceCurrency: 'USD' }, + ], + }, +]; + export default function Index() { return (
+ + + {JSON_LD.map((schema, i) => ( + + ))} + diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index ef8e39d2..612d8d2b 100755 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import PageMeta from '@/components/PageMeta'; import { useNavigate, useLocation, Link } from 'react-router-dom'; import { z } from 'zod'; import { useForm } from 'react-hook-form'; @@ -158,6 +159,7 @@ export default function Login() { return (
+ {/* Left: form */}
diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx index 728ea272..6b79139d 100644 --- a/src/pages/Register.tsx +++ b/src/pages/Register.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import PageMeta from '@/components/PageMeta'; import { useNavigate, Link, useSearchParams } from 'react-router-dom'; import { z } from 'zod'; import { useForm } from 'react-hook-form'; @@ -206,6 +207,7 @@ export default function Register() { return (
+ {/* Left: form */}