Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 118 additions & 34 deletions app/[locale]/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { PageHero } from "@/components/sections/shared/PageHero";
import { CtaBand } from "@/components/sections/shared/CtaBand";
import { ZoomSlideshow } from "@/components/sections/about/ZoomSlideshow";
import { PhotoGrid } from "@/components/sections/about/PhotoGrid";
import { MissionPillars } from "@/components/sections/about/MissionPillars";
import { FounderStory } from "@/components/sections/about/FounderStory";
import { FounderVideo } from "@/components/sections/about/FounderVideo";
import { QuoteBand } from "@/components/sections/about/QuoteBand";
import { WhatWeProvide } from "@/components/sections/about/WhatWeProvide";
import { AiScams } from "@/components/sections/about/AiScams";
import Link from "next/link";
import { getDictionary } from "@/lib/i18n/get-dictionary";
import { getRouteMetadata } from "@/lib/i18n/metadata";
import { localizeHref, type Locale } from "@/lib/i18n/config";
import "../../minimal.css";

const FTC_ALERT =
"https://consumer.ftc.gov/consumer-alerts/2026/09/scammers-are-spoofing-car-dealership-websites-what-you-need-know";
const FTC_REPORT = "https://reportfraud.ftc.gov/";

export async function generateMetadata({
params,
Expand All @@ -29,33 +25,121 @@ export default async function AboutPage({
const { locale } = await params;
const dict = (await getDictionary(locale as Locale)).aboutPage;
const href = (h: string) => localizeHref(locale as Locale, h);
const { hero, zoom, photos, mission, story, video, ai, quote, provide, cta } = dict;
const last = provide.items.length - 1;

return (
<div className="rd rd-page">
<PageHero
className="a-hero"
centered
title={dict.hero.title}
body={dict.hero.body}
>
<div className="scroll-cue" aria-hidden="true">
<span />
<div className="rd rd-page mn">
<div className="mn-col">
<h1>{hero.title}</h1>
<p className="mn-lead">{hero.body}</p>

<h2 id="chapters">{photos.title}</h2>
<p>{photos.body}</p>
<p>{zoom.body}</p>

<h2 id="mission">{mission.title}</h2>
<p>{mission.body}</p>
{mission.items.map((item) => (
<div key={item.title}>
<h3>{item.title}</h3>
<p>{item.body}</p>
</div>
))}

<h2 id="story">{story.title}</h2>
<p>{story.body}</p>
{story.strikes.map((s) => (
<div key={s.n}>
<h3>{s.title}</h3>
<p className="mn-meta">{s.tag}</p>
<p>{s.body}</p>
</div>
))}
<blockquote>
<p>{story.quote}</p>
<p className="mn-meta">{story.quoteCite}</p>
</blockquote>

<h2 id="founder-video">{video.title}</h2>
<p>{video.body}</p>
<video
controls
preload="metadata"
playsInline
poster="/images/about/founder-rana-poster.webp"
aria-label={video.label}
style={{ display: "block", width: "100%", margin: "0 0 12px" }}
>
<source src="/videos/founder-rana.mp4" type="video/mp4" />
</video>
<p className="mn-meta">
{video.name}, {video.role}
</p>

<h2 id="ai">{ai.title}</h2>
<p>{ai.body}</p>
<ol>
{ai.steps.map((s) => (
<li key={s.title}>
<strong>{s.title}</strong> {s.body}
</li>
))}
</ol>
<p className="mn-meta">{ai.source}</p>
<h3>
{ai.saveStat}: {ai.saveTitle}
</h3>
<p>{ai.saveBody}</p>
<p>
<Link href={href("/fraud#law")}>{ai.saveCta}</Link>
</p>
<h3>{ai.tipsTitle}</h3>
<ul>
{ai.tips.map((t) => (
<li key={t}>{t}</li>
))}
</ul>
<div className="mn-links">
<a href={FTC_ALERT} target="_blank" rel="noopener noreferrer">
{ai.ftcCta}
</a>
<a href={FTC_REPORT} target="_blank" rel="noopener noreferrer">
{ai.reportCta}
</a>
</div>

<blockquote style={{ marginTop: 64 }}>
<p>{quote.text}</p>
<p className="mn-meta">{quote.cite}</p>
</blockquote>

<h2 id="provide">{provide.title}</h2>
<p>{provide.body}</p>
{provide.items.map((item, i) => (
<div key={item.title}>
<h3>{item.title}</h3>
<p>
{item.body}
{i === last && (
<>
{" "}
<Link href={href("/research")}>{provide.projectsLink}</Link>
</>
)}
</p>
</div>
))}

<h2>{cta.title}</h2>
<p>{cta.body}</p>
<div className="mn-links">
<Link className="mn-btn" href={href("/join")}>
{cta.primary}
</Link>
<Link href={href("/team")}>{cta.secondary}</Link>
</div>
</PageHero>
<ZoomSlideshow dict={dict.zoom} />
<PhotoGrid dict={dict.photos} />
<MissionPillars dict={dict.mission} />
<FounderStory dict={dict.story} />
<FounderVideo dict={dict.video} />
<AiScams dict={dict.ai} rightsHref={href("/fraud#law")} />
<QuoteBand text={dict.quote.text} cite={dict.quote.cite} />
<WhatWeProvide dict={dict.provide} projectsHref={href("/research")} />
<CtaBand
title={dict.cta.title}
body={dict.cta.body}
primary={{ label: dict.cta.primary, href: href("/join") }}
secondary={{ label: dict.cta.secondary, href: href("/team") }}
/>
</div>
</div>
);
}
91 changes: 35 additions & 56 deletions app/[locale]/join/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Link from "next/link";
import { PageHero } from "@/components/sections/shared/PageHero";
import { ApplyForm } from "@/components/sections/join/ApplyForm";
import { ContactForm } from "@/components/sections/join/ContactForm";
import { getDictionary } from "@/lib/i18n/get-dictionary";
import type { Locale } from "@/lib/i18n/config";
import "../../minimal.css";

// Minimum weekly hours per role, same order as joinPage.roles.items.
const ROLE_HOURS = [5, 5, 5, 3, 3, 3];
Expand All @@ -17,69 +17,48 @@ export default async function JoinPage({
const d = (await getDictionary(locale as Locale)).joinPage;

return (
<div className="rd rd-page">
<PageHero title={d.hero.title} body={d.hero.body}>
<div className="ctas">
<Link className="btn btn-solid" href="#apply">
{d.hero.ctaPrimary}
</Link>
<Link className="btn btn-line" href="#chapters">
{d.hero.ctaSecondary}
</Link>
<div className="rd rd-page mn">
<div className="mn-col">
<h1>{d.hero.title}</h1>
<p className="mn-lead">{d.hero.body}</p>
<div className="mn-links">
<Link href="#apply">{d.hero.ctaPrimary}</Link>
<Link href="#chapters">{d.hero.ctaSecondary}</Link>
</div>
</PageHero>

<section className="pt0" id="chapters">
<div className="wrap">
<div className="intro">
<h2 className="h2">{d.chapters.title}</h2>
<h2 id="chapters">{d.chapters.title}</h2>
{d.chapters.items.map((c) => (
<div key={c.name}>
<h3>{c.name}</h3>
<p className="mn-meta">{c.school}</p>
<p>{c.focus}</p>
<p>
<strong>{d.chapters.leadsLabel}</strong> {c.leads}
</p>
</div>
<div className="chapters">
{d.chapters.items.map((c) => (
<div className="card chapter" key={c.name}>
<h3>{c.name}</h3>
<div className="u">{c.school}</div>
<p className="d">{c.focus}</p>
<div className="leads">
<strong>{d.chapters.leadsLabel}</strong> {c.leads}
</div>
</div>
))}
</div>
</div>
</section>
))}

<section className="pt0">
<div className="wrap">
<div className="intro">
<h2 className="h2">{d.roles.title}</h2>
<p className="lede">{d.roles.body}</p>
</div>
<ul className="roles">
{d.roles.items.map((r, i) => (
<li key={r}>
<b>{r}</b>
{d.roles.hours.replace("{n}", String(ROLE_HOURS[i]))}
</li>
))}
</ul>
</div>
</section>
<h2>{d.roles.title}</h2>
<p>{d.roles.body}</p>
<ul>
{d.roles.items.map((r, i) => (
<li key={r}>
<strong>{r}</strong> {d.roles.hours.replace("{n}", String(ROLE_HOURS[i]))}
</li>
))}
</ul>

<section id="apply">
<div className="wrap apply-wrap">
<h2 className="h2">{d.form.title}</h2>
<section id="apply">
<h2>{d.form.title}</h2>
<ApplyForm dict={d} />
</div>
</section>
</section>

<section className="pt0" id="contact">
<div className="wrap apply-wrap">
<h2 className="h2">{d.contact.title}</h2>
<p className="lede mt18">{d.contact.body}</p>
<section id="contact">
<h2>{d.contact.title}</h2>
<p>{d.contact.body}</p>
<ContactForm dict={d.contact} />
</div>
</section>
</section>
</div>
</div>
);
}
Loading