diff --git a/frontend/src/app/(auth)/forgot-password/page.tsx b/frontend/src/app/(auth)/forgot-password/page.tsx
index 68ece78..add9c28 100644
--- a/frontend/src/app/(auth)/forgot-password/page.tsx
+++ b/frontend/src/app/(auth)/forgot-password/page.tsx
@@ -2,14 +2,15 @@ import { Suspense } from 'react';
import type { Metadata } from 'next';
import Loader from '~/components/loader';
import { ForgotPasswordForm } from './form';
+import { buildMetadata } from '~/lib/metadata';
-export const metadata: Metadata = {
+export const metadata: Metadata = buildMetadata({
title: 'Reset Password',
description:
'Reset your Retailytics password to regain access to your retail intelligence dashboards and store data.',
- alternates: { canonical: '/forgot-password' },
- robots: { index: false, follow: true },
-};
+ path: '/forgot-password',
+ index: false,
+});
export default async function ForgotPasswordPage() {
return (
diff --git a/frontend/src/app/(auth)/login/page.tsx b/frontend/src/app/(auth)/login/page.tsx
index 289d8bd..b077150 100644
--- a/frontend/src/app/(auth)/login/page.tsx
+++ b/frontend/src/app/(auth)/login/page.tsx
@@ -4,14 +4,15 @@ import type { Metadata } from 'next';
import { cache, Suspense } from 'react';
import Loader from '~/components/loader';
import { redirect } from 'next/navigation';
+import { buildMetadata } from '~/lib/metadata';
-export const metadata: Metadata = {
+export const metadata: Metadata = buildMetadata({
title: 'Sign In',
description:
'Sign in to your Retailytics account to access store enumeration dashboards, field data, and market intelligence reports.',
- alternates: { canonical: '/login' },
- robots: { index: false, follow: true },
-};
+ path: '/login',
+ index: false,
+});
const getSession = cache(() => auth());
diff --git a/frontend/src/app/(auth)/register/page.tsx b/frontend/src/app/(auth)/register/page.tsx
index 41b8d1d..61a3387 100644
--- a/frontend/src/app/(auth)/register/page.tsx
+++ b/frontend/src/app/(auth)/register/page.tsx
@@ -5,14 +5,15 @@ import { RegisterForm } from './form';
import { cache, Suspense } from 'react';
import Loader from '~/components/loader';
import { redirect } from 'next/navigation';
+import { buildMetadata } from '~/lib/metadata';
-export const metadata: Metadata = {
+export const metadata: Metadata = buildMetadata({
title: 'Create Account',
description:
'Create your Retailytics account to start collecting store data, validating field submissions, and turning local data into market intelligence.',
- alternates: { canonical: '/register' },
- robots: { index: false, follow: true },
-};
+ path: '/register',
+ index: false,
+});
const getSession = cache(() => auth());
diff --git a/frontend/src/app/contact/page.tsx b/frontend/src/app/contact/page.tsx
new file mode 100644
index 0000000..316f367
--- /dev/null
+++ b/frontend/src/app/contact/page.tsx
@@ -0,0 +1,55 @@
+import type { Metadata } from 'next';
+import { siteConfig } from '~/lib/site';
+import SitePage from '~/components/site-page';
+import { buildMetadata } from '~/lib/metadata';
+import { TrackedMailto } from '~/components/analytics/tracked-mailto';
+
+export const metadata: Metadata = buildMetadata({
+ title: 'Contact',
+ description:
+ 'Get in touch with the Retailytics team at Ajared Research Inc. for demos, partnerships, support, and questions about retail intelligence.',
+ path: '/contact',
+});
+
+export default function ContactPage() {
+ return (
+
+
+ Retailytics is built and operated by {siteConfig.legalName}. The fastest
+ way to reach us is by email, and we aim to respond within two business
+ days.
+
+
+
Email
+
+
+ {siteConfig.contactEmail}
+
+
+
+
Sales & demos
+
+ Want to see Retailytics in action for your market? Email us with a short
+ note about your team and the regions you cover, and we'll set up a
+ walkthrough.
+
+ }>
{children}
diff --git a/frontend/src/app/llms.txt/route.ts b/frontend/src/app/llms.txt/route.ts
new file mode 100644
index 0000000..fbe9dda
--- /dev/null
+++ b/frontend/src/app/llms.txt/route.ts
@@ -0,0 +1,40 @@
+import { absoluteUrl, siteConfig } from '~/lib/site';
+
+const features = [
+ 'Assign enumerators to specific geographic areas',
+ 'Capture detailed retail store data in the field',
+ 'Real-time submission with quality-controlled validation',
+ 'Market analytics and reporting',
+ 'Location and market intelligence',
+];
+
+export function GET() {
+ const body = `# ${siteConfig.name}
+
+> ${siteConfig.description}
+
+${siteConfig.name} is a retail intelligence platform built by ${siteConfig.legalName} — it helps teams assign enumerators to geographic areas, capture detailed retail store data in the field, validate submissions through quality control, and analyse the results to answer market questions.
+
+## Core pages
+- [Home](${absoluteUrl('/')}): Product overview, how it works, and FAQ.
+- [How it works](${absoluteUrl('/#how-it-works')}): The four-step enumeration-to-insight workflow.
+- [FAQ](${absoluteUrl('/#faq')}): Common questions about the platform and the data it collects.
+- [Contact](${absoluteUrl('/contact')}): Reach the team for demos, partnerships, and support.
+
+## Company
+${siteConfig.name} is a product of ${siteConfig.legalName} (${siteConfig.social.parent}, ${siteConfig.social.parentCa}), an AI research and product studio that builds information systems and data products. Contact: ${siteConfig.contactEmail}.
+
+## Key features
+${features.map((feature) => `- ${feature}`).join('\n')}
+
+## Legal
+- [Privacy Policy](${absoluteUrl('/privacy')})
+- [Terms of Service](${absoluteUrl('/terms')})
+`;
+
+ return new Response(body, {
+ headers: {
+ 'Content-Type': 'text/plain; charset=utf-8',
+ },
+ });
+}
diff --git a/frontend/src/app/opengraph-image.tsx b/frontend/src/app/opengraph-image.tsx
new file mode 100644
index 0000000..e44994a
--- /dev/null
+++ b/frontend/src/app/opengraph-image.tsx
@@ -0,0 +1,72 @@
+import { ImageResponse } from 'next/og';
+import { siteConfig } from '~/lib/site';
+
+export const alt = `${siteConfig.name} — ${siteConfig.tagline}`;
+export const size = { width: 1200, height: 630 };
+export const contentType = 'image/png';
+
+export default function OpengraphImage() {
+ return new ImageResponse(
+
+
+ {siteConfig.name}
+
+
+
+
+ {siteConfig.tagline}
+
+
+ Store enumeration, field data collection, and market analysis in one
+ platform.
+
+
+
+
+ {siteConfig.url.replace(/^https?:\/\//, '')}
+ by {siteConfig.legalName}
+
+
,
+ { ...size },
+ );
+}
diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
index 5578466..73e0795 100644
--- a/frontend/src/app/page.tsx
+++ b/frontend/src/app/page.tsx
@@ -1,20 +1,18 @@
import CTA from '~/components/cta';
import FAQ from '~/components/faq';
import Hero from '~/components/hero';
-import type { Metadata } from 'next';
-import { siteConfig } from '~/lib/site';
import Footer from '~/components/footer';
+import type { Metadata } from 'next';
+import { buildMetadata } from '~/lib/metadata';
import HowItWorks from '~/components/how-it-works';
+import { EngagementTracker } from '~/components/analytics/engagement-tracker';
-export const metadata: Metadata = {
- title: { absolute: `${siteConfig.name} — ${siteConfig.tagline}` },
- description: siteConfig.description,
- alternates: { canonical: '/' },
-};
+export const metadata: Metadata = buildMetadata({ path: '/' });
export default function Home() {
return (
+
diff --git a/frontend/src/app/privacy/page.tsx b/frontend/src/app/privacy/page.tsx
new file mode 100644
index 0000000..7072950
--- /dev/null
+++ b/frontend/src/app/privacy/page.tsx
@@ -0,0 +1,64 @@
+import type { Metadata } from 'next';
+import { siteConfig } from '~/lib/site';
+import SitePage from '~/components/site-page';
+import { buildMetadata } from '~/lib/metadata';
+
+export const metadata: Metadata = buildMetadata({
+ title: 'Privacy Policy',
+ description: `How ${siteConfig.legalName} collects, uses, and protects data on Retailytics.`,
+ path: '/privacy',
+ index: false,
+});
+
+export default function PrivacyPage() {
+ return (
+
+
+ This Privacy Policy explains how {siteConfig.legalName}
+ ("we", "us") handles information in connection with
+ the Retailytics platform. By using Retailytics you agree to the
+ practices described here.
+
+
+
Information we collect
+
+ We collect account details you provide (such as name and email),
+ operational data submitted through the platform (such as store and
+ location records captured by enumerators), and standard technical data
+ such as device and usage information.
+
+
+
How we use information
+
+ We use information to operate and improve the platform, validate and
+ analyse submitted data, secure accounts, and communicate with you about
+ the service.
+
+
+
Sharing
+
+ We do not sell personal information. We share data only with service
+ providers who help us operate Retailytics, or where required by law.
+
+
+
Data retention & security
+
+ We retain data for as long as needed to provide the service and meet
+ legal obligations, and we apply appropriate safeguards to protect it.
+
+
+ );
+}
diff --git a/frontend/src/app/robots.ts b/frontend/src/app/robots.ts
new file mode 100644
index 0000000..71dca96
--- /dev/null
+++ b/frontend/src/app/robots.ts
@@ -0,0 +1,14 @@
+import type { MetadataRoute } from 'next';
+import { siteConfig } from '~/lib/site';
+
+export default function robots(): MetadataRoute.Robots {
+ return {
+ rules: {
+ userAgent: '*',
+ allow: '/',
+ disallow: ['/api/', '/admin', '/user'],
+ },
+ sitemap: `${siteConfig.url}/sitemap.xml`,
+ host: siteConfig.url,
+ };
+}
diff --git a/frontend/src/app/sitemap.ts b/frontend/src/app/sitemap.ts
new file mode 100644
index 0000000..873df31
--- /dev/null
+++ b/frontend/src/app/sitemap.ts
@@ -0,0 +1,13 @@
+import type { MetadataRoute } from 'next';
+import { absoluteUrl, publicRoutes } from '~/lib/site';
+
+export default function sitemap(): MetadataRoute.Sitemap {
+ const lastModified = new Date();
+
+ return publicRoutes.map((route) => ({
+ url: absoluteUrl(route.path),
+ lastModified,
+ changeFrequency: route.changeFrequency,
+ priority: route.priority,
+ }));
+}
diff --git a/frontend/src/app/terms/page.tsx b/frontend/src/app/terms/page.tsx
new file mode 100644
index 0000000..d0739b2
--- /dev/null
+++ b/frontend/src/app/terms/page.tsx
@@ -0,0 +1,63 @@
+import type { Metadata } from 'next';
+import { siteConfig } from '~/lib/site';
+import SitePage from '~/components/site-page';
+import { buildMetadata } from '~/lib/metadata';
+
+export const metadata: Metadata = buildMetadata({
+ title: 'Terms of Service',
+ description: `The terms governing use of the Retailytics platform from ${siteConfig.legalName}.`,
+ path: '/terms',
+ index: false,
+});
+
+export default function TermsPage() {
+ return (
+
+
+ These Terms of Service ("Terms") govern your access to and use
+ of the Retailytics platform provided by {siteConfig.legalName}
+ ("we", "us"). By creating an account or using the
+ service, you agree to these Terms.
+
+
+
Use of the service
+
+ You may use Retailytics only in compliance with these Terms and
+ applicable law. You are responsible for activity under your account and
+ for keeping your credentials secure.
+
+
+
Data and content
+
+ You retain rights to the data you submit. You grant us the rights needed
+ to host, process, and analyse that data to provide the service.
+
+
+
Acceptable use
+
+ You agree not to misuse the service, attempt to disrupt it, or use it to
+ collect data unlawfully or without proper authorisation.
+
+
+
Availability & disclaimer
+
+ The service is provided on an "as is" basis. We do not warrant
+ that it will be uninterrupted or error-free, and we are not liable for
+ indirect or consequential damages to the extent permitted by law.
+