From 2b85354d3a24d58f953179dedcf56176dd4acb66 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 28 Oct 2026 17:08:20 +0000 Subject: [PATCH 1/2] Add About and Contact pages and fix the privacy address /about and /contact are new prerendered pages with AboutPage and ContactPage JSON-LD. The privacy policy pointed at privacy@debatelab.app, a domain that belongs to another product; it now uses privacyEmail from src/config/site.json, and the terms gain a Contact section. The home Organization gets an email and contactPoint, and every footer (React and the generated guides) links About and Contact. Co-Authored-By: Claude Opus 5.5 --- docs/operations.md | 2 +- public/ai-debate-case-generator/index.html | 2 +- public/ai-debate-coach/index.html | 2 +- public/ai-debate-practice/index.html | 2 +- public/ai-rebuttal-generator/index.html | 2 +- public/argument-flowchart/index.html | 2 +- public/best-ai-debate-tools/index.html | 2 +- .../british-parliamentary-debate/index.html | 2 +- public/congress-debate/index.html | 2 +- public/debate-clash-points/index.html | 2 +- public/debate-evidence-finder/index.html | 2 +- public/debate-fallacies/index.html | 2 +- public/debate-judge-adaptation/index.html | 2 +- public/debate-motion-analysis/index.html | 2 +- public/debate-rebuttal-practice/index.html | 2 +- public/debate-speech-structure/index.html | 2 +- public/debate-topics-for-students/index.html | 2 +- public/debateowl-pricing/index.html | 2 +- public/debateowl-vs-chatgpt/index.html | 2 +- public/debateowl-vs-debateclub/index.html | 2 +- public/debateowl-vs-windebate/index.html | 2 +- .../how-to-cross-examine-in-debate/index.html | 2 +- public/how-to-flow-in-debate/index.html | 2 +- public/how-to-practice-rebuttals/index.html | 2 +- .../index.html | 2 +- public/how-to-weigh-in-debate/index.html | 2 +- public/how-to-write-a-debate-case/index.html | 2 +- public/lincoln-douglas-debate/index.html | 2 +- public/llms.txt | 2 + public/online-debate-timer/index.html | 2 +- public/policy-debate/index.html | 2 +- public/public-forum-debate/index.html | 2 +- public/world-schools-debate/index.html | 2 +- scripts/generate_seo_content.py | 4 +- src/App.tsx | 6 ++ src/components/landing/Footer.tsx | 7 ++ src/components/layout/PublicPageShell.tsx | 7 +- src/config/site.json | 2 + src/lib/route-modules.ts | 2 + src/lib/seo/pages.ts | 6 ++ src/locales/en.json | 47 +++++++++++-- src/locales/es.json | 47 +++++++++++-- src/locales/hu.json | 47 +++++++++++-- src/pages/AboutPage.tsx | 66 +++++++++++++++++++ src/pages/ContactPage.tsx | 45 +++++++++++++ src/pages/PrivacyPage.tsx | 3 +- src/pages/TermsPage.tsx | 5 +- src/prerender/entry.tsx | 27 +++++++- 48 files changed, 333 insertions(+), 54 deletions(-) create mode 100644 src/pages/AboutPage.tsx create mode 100644 src/pages/ContactPage.tsx diff --git a/docs/operations.md b/docs/operations.md index ff9709db4..54c739055 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -51,7 +51,7 @@ Vercel builds and serves the frontend from the Git repository. A push to `main` - `noindex` headers for `/app`, `/login`, `/share` and `/embed/debate-timer`. The app shell's robots meta is `noindex, nofollow`, the same policy. Public pages, including the prerendered `/expocit/sources`, are `index, follow`. A route deletes the platform `Access-Control-Allow-Origin: *` header on HTML responses and leaves it on static assets; - Content Security Policy and the other response security headers, including the `Permissions-Policy` that allows the microphone for the app's own origin only. Jam is limited to `https://js.jam.dev` and `https://recorder.jam.dev`; `script-src` does not include `unsafe-eval`. Web Analytics and Speed Insights stay on this origin (`/_vercel/insights` and `/_vercel/speed-insights`), which `'self'` already allows. Every path except `/embed/debate-timer` refuses to be framed (`X-Frame-Options: DENY` and `frame-ancestors 'none'`). `/embed/debate-timer` allows any site to frame it and gets its own CSP, which permits only same-origin scripts and requests. The two rules use complementary sources (`/((?!embed/debate-timer$).*)` and `/embed/debate-timer`) so no path gets both, a new embed needs its path added to both, and `src/test/vercel-deployment-config.test.ts` fails if two matching rules ever set the same header. -After `vite build`, [`scripts/prerender.mjs`](../scripts/prerender.mjs) renders the landing page, `/terms`, `/privacy`, `/expocit/sources`, and the 404 page in Node through [`src/prerender/entry.tsx`](../src/prerender/entry.tsx). It writes them to `dist/index.html`, `dist/terms/index.html`, `dist/privacy/index.html`, `dist/expocit/sources/index.html`, and `dist/404.html`, each with its own title, description, and canonical URL from [`src/lib/seo/pages.ts`](../src/lib/seo/pages.ts); the landing page also carries the Organization, SoftwareApplication (with the trial and Pro offers) and FAQPage JSON-LD. The built shell is kept as `dist/app-shell.html`. On load, the client replaces the prerendered HTML with the same page. A new public SPA page needs an entry in `PAGE_HEADS`, in the prerender entry, and a route kept out of the 404. +After `vite build`, [`scripts/prerender.mjs`](../scripts/prerender.mjs) renders the landing page, `/terms`, `/privacy`, `/about`, `/contact`, `/expocit/sources`, and the 404 page in Node through [`src/prerender/entry.tsx`](../src/prerender/entry.tsx). It writes them to `dist/index.html`, `dist/terms/index.html`, `dist/privacy/index.html`, `dist/about/index.html`, `dist/contact/index.html`, `dist/expocit/sources/index.html`, and `dist/404.html`, each with its own title, description, and canonical URL from [`src/lib/seo/pages.ts`](../src/lib/seo/pages.ts); `/about` and `/contact` carry AboutPage and ContactPage JSON-LD, and the contact and privacy addresses come from `contactEmail` and `privacyEmail` in [`src/config/site.json`](../src/config/site.json); the landing page also carries the Organization (with its `contactPoint`), SoftwareApplication (with the trial and Pro offers) and FAQPage JSON-LD. The built shell is kept as `dist/app-shell.html`. On load, the client replaces the prerendered HTML with the same page. A new public SPA page needs an entry in `PAGE_HEADS`, in the prerender entry, and a route kept out of the 404. The free tools under `/tools` ([`src/pages/tools/`](../src/pages/tools/)) are prerendered the same way, listed in [`src/prerender/tool-pages.tsx`](../src/prerender/tool-pages.tsx) with heads in [`src/lib/seo/tool-pages.ts`](../src/lib/seo/tool-pages.ts). They render their own JSON-LD in the page body, so the dev server and the prerendered HTML carry the same structured data, and their `dateModified` is `TOOL_PAGES_MODIFIED` in that file: bump it with a substantive content change. Their speech times come from [`src/lib/free-tools/formats.ts`](../src/lib/free-tools/formats.ts), which cites a league source per format. diff --git a/public/ai-debate-case-generator/index.html b/public/ai-debate-case-generator/index.html index 440a48a0e..168c24291 100644 --- a/public/ai-debate-case-generator/index.html +++ b/public/ai-debate-case-generator/index.html @@ -56,6 +56,6 @@ How to Flow in Debate Best AI Debate Tools Compared - + diff --git a/public/ai-debate-coach/index.html b/public/ai-debate-coach/index.html index 64da0dca3..0079fb305 100644 --- a/public/ai-debate-coach/index.html +++ b/public/ai-debate-coach/index.html @@ -56,6 +56,6 @@ Congressional Debate Prep with AI How to Write a Debate Case - + diff --git a/public/ai-debate-practice/index.html b/public/ai-debate-practice/index.html index 970d6fe7e..e48cd02a2 100644 --- a/public/ai-debate-practice/index.html +++ b/public/ai-debate-practice/index.html @@ -56,6 +56,6 @@ Public Forum Debate Prep with AI How to Write a Debate Case - + diff --git a/public/ai-rebuttal-generator/index.html b/public/ai-rebuttal-generator/index.html index 166f22789..19125c286 100644 --- a/public/ai-rebuttal-generator/index.html +++ b/public/ai-rebuttal-generator/index.html @@ -56,6 +56,6 @@ Debate Judge Adaptation Guide Best AI Debate Tools Compared - + diff --git a/public/argument-flowchart/index.html b/public/argument-flowchart/index.html index cb09e014e..9b0a1472d 100644 --- a/public/argument-flowchart/index.html +++ b/public/argument-flowchart/index.html @@ -56,6 +56,6 @@ How to Practice Debate Rebuttals Best AI Debate Tools Compared - + diff --git a/public/best-ai-debate-tools/index.html b/public/best-ai-debate-tools/index.html index 13093057b..9108f6bd2 100644 --- a/public/best-ai-debate-tools/index.html +++ b/public/best-ai-debate-tools/index.html @@ -54,6 +54,6 @@ AI Debate Practice for Stronger Cases, Rebuttals, and Speeches Public Forum Debate Prep with AI - + diff --git a/public/british-parliamentary-debate/index.html b/public/british-parliamentary-debate/index.html index 4bc1b4f34..4e6ffbc81 100644 --- a/public/british-parliamentary-debate/index.html +++ b/public/british-parliamentary-debate/index.html @@ -55,6 +55,6 @@ Find Better Debate Evidence Without Losing the Argument How to Write a Debate Case - + diff --git a/public/congress-debate/index.html b/public/congress-debate/index.html index 9b6e923cd..b9f813f38 100644 --- a/public/congress-debate/index.html +++ b/public/congress-debate/index.html @@ -55,6 +55,6 @@ Common Debate Fallacies and How to Attack Them Best AI Debate Tools Compared - + diff --git a/public/debate-clash-points/index.html b/public/debate-clash-points/index.html index d12e99b0e..a6bdfdf2c 100644 --- a/public/debate-clash-points/index.html +++ b/public/debate-clash-points/index.html @@ -56,6 +56,6 @@ AI Debate Practice for Stronger Cases, Rebuttals, and Speeches Public Forum Debate Prep with AI - + diff --git a/public/debate-evidence-finder/index.html b/public/debate-evidence-finder/index.html index b7e8ed1fb..47d01358c 100644 --- a/public/debate-evidence-finder/index.html +++ b/public/debate-evidence-finder/index.html @@ -56,6 +56,6 @@ Policy Debate Prep with AI How to Write a Debate Case - + diff --git a/public/debate-fallacies/index.html b/public/debate-fallacies/index.html index cd4895a32..04994c0a1 100644 --- a/public/debate-fallacies/index.html +++ b/public/debate-fallacies/index.html @@ -56,6 +56,6 @@ Policy Debate Prep with AI Best AI Debate Tools Compared - + diff --git a/public/debate-judge-adaptation/index.html b/public/debate-judge-adaptation/index.html index 6f7fc8e53..0a67622b1 100644 --- a/public/debate-judge-adaptation/index.html +++ b/public/debate-judge-adaptation/index.html @@ -56,6 +56,6 @@ Ask an AI Debate Coach for Round-Ready Prep Public Forum Debate Prep with AI - + diff --git a/public/debate-motion-analysis/index.html b/public/debate-motion-analysis/index.html index a11f35730..be47a20aa 100644 --- a/public/debate-motion-analysis/index.html +++ b/public/debate-motion-analysis/index.html @@ -56,6 +56,6 @@ DebateOwl vs ChatGPT for Debate Prep AI Debate Practice for Stronger Cases, Rebuttals, and Speeches - + diff --git a/public/debate-rebuttal-practice/index.html b/public/debate-rebuttal-practice/index.html index 39c9b6790..b30abe089 100644 --- a/public/debate-rebuttal-practice/index.html +++ b/public/debate-rebuttal-practice/index.html @@ -56,6 +56,6 @@ DebateOwl vs DebateClub Public Forum Debate Prep with AI - + diff --git a/public/debate-speech-structure/index.html b/public/debate-speech-structure/index.html index d0a326aab..3faa7f72d 100644 --- a/public/debate-speech-structure/index.html +++ b/public/debate-speech-structure/index.html @@ -56,6 +56,6 @@ Find Better Debate Evidence Without Losing the Argument Public Forum Debate Prep with AI - + diff --git a/public/debate-topics-for-students/index.html b/public/debate-topics-for-students/index.html index a740fbe35..c3a7012f4 100644 --- a/public/debate-topics-for-students/index.html +++ b/public/debate-topics-for-students/index.html @@ -56,6 +56,6 @@ British Parliamentary Debate Prep with AI Best AI Debate Tools Compared - + diff --git a/public/debateowl-pricing/index.html b/public/debateowl-pricing/index.html index db545b238..a38a93824 100644 --- a/public/debateowl-pricing/index.html +++ b/public/debateowl-pricing/index.html @@ -54,6 +54,6 @@ Debate Speech Structure and Delivery AI Debate Practice for Stronger Cases, Rebuttals, and Speeches - + diff --git a/public/debateowl-vs-chatgpt/index.html b/public/debateowl-vs-chatgpt/index.html index 55b133a89..03b4a9803 100644 --- a/public/debateowl-vs-chatgpt/index.html +++ b/public/debateowl-vs-chatgpt/index.html @@ -54,6 +54,6 @@ Find Better Debate Evidence Without Losing the Argument Public Forum Debate Prep with AI - + diff --git a/public/debateowl-vs-debateclub/index.html b/public/debateowl-vs-debateclub/index.html index c5c2ea373..818079bdc 100644 --- a/public/debateowl-vs-debateclub/index.html +++ b/public/debateowl-vs-debateclub/index.html @@ -54,6 +54,6 @@ Policy Debate Prep with AI How to Write a Debate Case - + diff --git a/public/debateowl-vs-windebate/index.html b/public/debateowl-vs-windebate/index.html index d7ce7f4d0..37bf7a309 100644 --- a/public/debateowl-vs-windebate/index.html +++ b/public/debateowl-vs-windebate/index.html @@ -54,6 +54,6 @@ How to Practice Debate Rebuttals AI Debate Practice for Stronger Cases, Rebuttals, and Speeches - + diff --git a/public/how-to-cross-examine-in-debate/index.html b/public/how-to-cross-examine-in-debate/index.html index 7fb793428..9b78d9a67 100644 --- a/public/how-to-cross-examine-in-debate/index.html +++ b/public/how-to-cross-examine-in-debate/index.html @@ -56,6 +56,6 @@ Lincoln-Douglas Debate Prep with AI Best AI Debate Tools Compared - + diff --git a/public/how-to-flow-in-debate/index.html b/public/how-to-flow-in-debate/index.html index ef598145c..4e4ec44e0 100644 --- a/public/how-to-flow-in-debate/index.html +++ b/public/how-to-flow-in-debate/index.html @@ -56,6 +56,6 @@ Best AI Debate Tools Compared AI Debate Practice for Stronger Cases, Rebuttals, and Speeches - + diff --git a/public/how-to-practice-rebuttals/index.html b/public/how-to-practice-rebuttals/index.html index 895356908..e22a3e9f0 100644 --- a/public/how-to-practice-rebuttals/index.html +++ b/public/how-to-practice-rebuttals/index.html @@ -56,6 +56,6 @@ DebateOwl Pricing and Plans AI Debate Practice for Stronger Cases, Rebuttals, and Speeches - + diff --git a/public/how-to-prepare-for-a-debate-tournament/index.html b/public/how-to-prepare-for-a-debate-tournament/index.html index d1f1772b7..aa44449c8 100644 --- a/public/how-to-prepare-for-a-debate-tournament/index.html +++ b/public/how-to-prepare-for-a-debate-tournament/index.html @@ -56,6 +56,6 @@ Map the Clash With an Argument Flowchart Public Forum Debate Prep with AI - + diff --git a/public/how-to-weigh-in-debate/index.html b/public/how-to-weigh-in-debate/index.html index 2b073d644..6c7268ac2 100644 --- a/public/how-to-weigh-in-debate/index.html +++ b/public/how-to-weigh-in-debate/index.html @@ -56,6 +56,6 @@ Practice Rebuttals Before Your Opponent Finds the Weakness Public Forum Debate Prep with AI - + diff --git a/public/how-to-write-a-debate-case/index.html b/public/how-to-write-a-debate-case/index.html index fdb602219..7559418e7 100644 --- a/public/how-to-write-a-debate-case/index.html +++ b/public/how-to-write-a-debate-case/index.html @@ -56,6 +56,6 @@ Congressional Debate Prep with AI Best AI Debate Tools Compared - + diff --git a/public/lincoln-douglas-debate/index.html b/public/lincoln-douglas-debate/index.html index 5db5709f3..723bca267 100644 --- a/public/lincoln-douglas-debate/index.html +++ b/public/lincoln-douglas-debate/index.html @@ -55,6 +55,6 @@ Best AI Debate Tools Compared AI Debate Practice for Stronger Cases, Rebuttals, and Speeches - + diff --git a/public/llms.txt b/public/llms.txt index 7d4bcce47..f8b15af97 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -19,6 +19,8 @@ DebateOwl runs at https://debateowl.com/. It is a separate product from debatela - [Home](https://debateowl.com/): what DebateOwl is, the 8 prep modes, pricing and FAQ - [Pricing](https://debateowl.com/debateowl-pricing): trial, Pro and what each includes - [Best AI debate tools](https://debateowl.com/best-ai-debate-tools): how to choose an AI debate tool +- [About](https://debateowl.com/about): what DebateOwl is, who makes it and its former name +- [Contact](https://debateowl.com/contact): email hello@debateowl.com for support and feedback, privacy@debateowl.com for privacy and data requests - [Sitemap](https://debateowl.com/sitemap.xml): every public page ## Free tools diff --git a/public/online-debate-timer/index.html b/public/online-debate-timer/index.html index 84a5c49d3..9be94184e 100644 --- a/public/online-debate-timer/index.html +++ b/public/online-debate-timer/index.html @@ -56,6 +56,6 @@ Debate Speech Structure and Delivery Best AI Debate Tools Compared - + diff --git a/public/policy-debate/index.html b/public/policy-debate/index.html index 31227e38b..5d9d43de3 100644 --- a/public/policy-debate/index.html +++ b/public/policy-debate/index.html @@ -55,6 +55,6 @@ AI Debate Practice for Stronger Cases, Rebuttals, and Speeches How to Write a Debate Case - + diff --git a/public/public-forum-debate/index.html b/public/public-forum-debate/index.html index d95895197..00006f420 100644 --- a/public/public-forum-debate/index.html +++ b/public/public-forum-debate/index.html @@ -55,6 +55,6 @@ Common Debate Fallacies and How to Attack Them Best AI Debate Tools Compared - + diff --git a/public/world-schools-debate/index.html b/public/world-schools-debate/index.html index 08211d3dc..e9d9cfc9b 100644 --- a/public/world-schools-debate/index.html +++ b/public/world-schools-debate/index.html @@ -55,6 +55,6 @@ How to Weigh in Debate Best AI Debate Tools Compared - + diff --git a/scripts/generate_seo_content.py b/scripts/generate_seo_content.py index 95d9daf79..57d010043 100644 --- a/scripts/generate_seo_content.py +++ b/scripts/generate_seo_content.py @@ -576,7 +576,7 @@ def spans_of(): {about(page["slug"])}
- + ''' @@ -622,6 +622,8 @@ def llms_txt(): - [Home]({BASE}/): what {PRODUCT} is, the {len(PREP_MODES)} prep modes, pricing and FAQ - [Pricing]({page_url("debateowl-pricing")}): trial, Pro and what each includes - [Best AI debate tools]({page_url("best-ai-debate-tools")}): how to choose an AI debate tool +- [About]({BASE}/about): what {PRODUCT} is, who makes it and its former name +- [Contact]({BASE}/contact): email {SITE["contactEmail"]} for support and feedback, {SITE["privacyEmail"]} for privacy and data requests - [Sitemap]({BASE}/sitemap.xml): every public page ## Free tools diff --git a/src/App.tsx b/src/App.tsx index 980bd95fd..3303cd74a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -52,6 +52,8 @@ function prerenderedModule(loader: RouteLoader<{ default: ComponentType }>) { const LandingPage = prerenderedPage("/"); const TermsPage = prerenderedPage("/terms"); const PrivacyPage = prerenderedPage("/privacy"); +const AboutPage = prerenderedPage("/about"); +const ContactPage = prerenderedPage("/contact"); // The Hungarian and Spanish addresses in src/config/localized-routes.json. const LOCALIZED_PAGES = LOCALIZED_PATHS.map((entry) => ({ ...entry, Page: prerenderedModule(localizedRouteLoaders[entry.path]) })); const HomePage = lazy(routeLoaders["/app"]); @@ -170,6 +172,8 @@ const ROUTE_FALLBACKS: Record = { "/": { variant: "public", label: site.product }, "/terms": { variant: "public", label: "Terms", labelKey: "footer.terms" }, "/privacy": { variant: "public", label: "Privacy", labelKey: "footer.privacy" }, + "/about": { variant: "public", label: "About", labelKey: "footer.about" }, + "/contact": { variant: "public", label: "Contact", labelKey: "footer.contact" }, "/expocit/sources": { variant: "public", label: "Sources", labelKey: "crossfire.sources" }, "/login": { variant: "public", label: "Sign in", labelKey: "login.title" }, "/auth/callback": { variant: "public", label: "Sign in", labelKey: "login.title" }, @@ -449,6 +453,8 @@ function AppRoutes() { ))} } /> } /> + } /> + } /> } /> } /> } /> diff --git a/src/components/landing/Footer.tsx b/src/components/landing/Footer.tsx index 88bb049f9..2c083bba6 100644 --- a/src/components/landing/Footer.tsx +++ b/src/components/landing/Footer.tsx @@ -37,6 +37,13 @@ const Footer = () => { {t('footer.privacy')} + {/* English pages: there is no Hungarian or Spanish About or Contact address. */} + + {t('footer.about')} + + + {t('footer.contact')} +
diff --git a/src/components/layout/PublicPageShell.tsx b/src/components/layout/PublicPageShell.tsx index 5b51bd1a3..76e493fcd 100644 --- a/src/components/layout/PublicPageShell.tsx +++ b/src/components/layout/PublicPageShell.tsx @@ -7,6 +7,7 @@ import { useAuth } from '@/lib/supabase/auth-context'; import { cn } from '@/lib/utils'; import { MAIN_CONTENT_ID, SkipLink } from '@/components/layout/SkipLink'; import { LanguageSwitcher } from '@/components/LanguageSwitcher'; +import Footer from '@/components/landing/Footer'; /** Where "home" is for this visitor: the app when signed in, the landing page otherwise. */ // eslint-disable-next-line react-refresh/only-export-components @@ -17,9 +18,10 @@ export function useHomePath() { /** * Frame for public reading pages (legal, sources, shared items, not found): a way home - * at the top and a readable measure for long text. + * at the top and a readable measure for long text. `footer` adds the site footer, for + * pages that are part of the public site rather than a dead end. */ -export function PublicPageShell({ children, className }: { children: ReactNode; className?: string }) { +export function PublicPageShell({ children, className, footer = false }: { children: ReactNode; className?: string; footer?: boolean }) { const { t } = useTranslation(); const home = useHomePath(); return ( @@ -35,6 +37,7 @@ export function PublicPageShell({ children, className }: { children: ReactNode;
{children}
+ {footer &&