feat(seo): react-helmet-async, JSON-LD, robots.txt, sitemap, llms.txt

- HelmetProvider wraps App; PageMeta component for per-page title/description/OG/noindex
- Index.tsx: Organization + SoftwareApplication JSON-LD structured data
- Login, Register: noindex meta
- public/robots.txt: Allow /, Disallow app routes, Sitemap pointer
- public/sitemap.xml: static sitemap for landing sections
- public/llms.txt: Cohorta description for ChatGPT Search / Perplexity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-24 14:29:42 +01:00
parent 31d25a9293
commit 14f63a3e0c
10 changed files with 193 additions and 0 deletions

27
package-lock.json generated
View file

@ -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",

View file

@ -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",

41
public/llms.txt Normal file
View file

@ -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

11
public/robots.txt Normal file
View file

@ -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

33
public/sitemap.xml Normal file
View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://cohorta.ai-impress.com/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://cohorta.ai-impress.com/#pricing</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://cohorta.ai-impress.com/#product</loc>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://cohorta.ai-impress.com/about</loc>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://cohorta.ai-impress.com/privacy</loc>
<changefreq>yearly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://cohorta.ai-impress.com/terms</loc>
<changefreq>yearly</changefreq>
<priority>0.3</priority>
</url>
</urlset>

View file

@ -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 = () => (
<HelmetProvider>
<QueryClientProvider client={queryClient}>
<BrowserRouter basename={import.meta.env.BASE_URL}>
<AuthProvider>
@ -96,6 +98,7 @@ const App = () => (
</AuthProvider>
</BrowserRouter>
</QueryClientProvider>
</HelmetProvider>
);
export default App;

View file

@ -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 (
<Helmet>
<title>{fullTitle}</title>
<meta name="description" content={description} />
{noindex && <meta name="robots" content="noindex, nofollow" />}
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImage} />
</Helmet>
);
}

View file

@ -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 (
<div className="bg-background overflow-hidden relative">
<PageMeta />
<Helmet>
{JSON_LD.map((schema, i) => (
<script key={i} type="application/ld+json">
{JSON.stringify(schema)}
</script>
))}
</Helmet>
<CompassBg />
<Hero />
<StatsBand />

View file

@ -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 (
<div className="flex min-h-screen overflow-hidden bg-background">
<PageMeta title="Log in" noindex />
{/* Left: form */}
<div className="flex-1 flex items-center justify-center px-6 py-12">

View file

@ -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 (
<div className="flex min-h-screen overflow-hidden bg-background">
<PageMeta title="Create account" noindex />
{/* Left: form */}
<div className="flex-1 flex items-center justify-center px-6 py-12">