Rebuild the About page from the current marketing frames, and leave the unapproved sentences off the page.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilalloandCursor committed 2026-11-03 16:59:36 -07:00
1 parent 3dee3a587b
commit 2101676429
56 files changed
+1515 -238

No files matched your search

@@ -0,0 +1,26 @@
"use client";
/**
* Figma: "Section / Centered Header" (24214:25892)
*/
import { memo } from "react";
import { useMessages } from "../../../contexts/MessagesContext";
import CenteredHeaderView from "./CenteredHeader.view";
import type { CenteredHeaderSegment } from "./CenteredHeader.types";
const CenteredHeaderContainer = memo(() => {
const copy = useMessages().pages.about.centeredHeader;
return (
<CenteredHeaderView
accessibleName={copy.accessibleName}
subhead={copy.subhead}
credit={copy.credit}
segments={copy.segments as CenteredHeaderSegment[]}
/>
);
});
CenteredHeaderContainer.displayName = "CenteredHeader";
export default CenteredHeaderContainer;
@@ -0,0 +1,12 @@
export type CenteredHeaderIcon = "arrow" | "ring";
export type CenteredHeaderSegment =
| { type: "word"; text: string }
| { type: "icon"; icon: CenteredHeaderIcon };
export interface CenteredHeaderViewProps {
accessibleName: string;
segments: CenteredHeaderSegment[];
subhead: string;
credit: string;
}
@@ -0,0 +1,103 @@
"use client";
import { memo, type ReactNode } from "react";
import { getAssetPath } from "../../../../lib/assetUtils";
import type {
CenteredHeaderIcon,
CenteredHeaderSegment,
CenteredHeaderViewProps,
} from "./CenteredHeader.types";
const ICON_SRC: Record<CenteredHeaderIcon, string> = {
arrow: "assets/shapes/about-header-arrow.svg",
ring: "assets/shapes/about-header-ring.svg",
};
const ICON_CLASS: Record<CenteredHeaderIcon, string> = {
arrow: "size-10 md:size-[54px] lg:size-20",
ring: "size-8 md:size-[54px] lg:size-20",
};
/** Poster lines at 1024+, matching the desktop frame. Narrower widths wrap on their own. */
const DESKTOP_LINES = [[0], [1], [2, 3], [4, 5], [6, 7, 8]] as const;
function SegmentView({ segment }: { segment: CenteredHeaderSegment }) {
if (segment.type === "word") {
return (
<span className="font-bricolage-grotesque text-display-m whitespace-nowrap text-[var(--color-content-default-primary)] md:text-display-xl lg:text-display-xxl">
{segment.text}
</span>
);
}
return (
<span
className={`inline-flex shrink-0 items-center justify-center ${ICON_CLASS[segment.icon]}`}
>
{/* eslint-disable-next-line @next/next/no-img-element -- decorative Figma mark */}
<img
src={getAssetPath(ICON_SRC[segment.icon])}
alt=""
width={80}
height={80}
className="size-full"
/>
</span>
);
}
function CenteredHeaderView({
accessibleName,
segments,
subhead,
credit,
}: CenteredHeaderViewProps) {
const desktopLines: ReactNode = DESKTOP_LINES.map((line) => (
<div key={line.join("-")} className="flex items-center justify-center gap-4">
{line.map((index) => {
const segment = segments[index];
return segment ? <SegmentView key={index} segment={segment} /> : null;
})}
</div>
));
return (
<section
data-figma-node="24214:25892"
className="flex flex-col items-center gap-[var(--space-600)] overflow-hidden bg-black px-[var(--space-600)] py-[var(--space-3000)] md:px-[var(--space-1800)]"
aria-labelledby="about-centered-header"
>
<h1 id="about-centered-header" className="sr-only">
{accessibleName}
</h1>
<div
aria-hidden
className="flex w-full flex-wrap items-center justify-center gap-x-3 gap-y-2 lg:hidden"
>
{segments.map((segment, index) => (
<SegmentView key={index} segment={segment} />
))}
</div>
<div
aria-hidden
className="hidden w-full max-w-[32rem] flex-col items-center gap-4 lg:flex"
>
{desktopLines}
</div>
<p className="max-w-[720px] text-center font-inter text-paragraph-m text-[var(--color-content-default-secondary)] md:text-paragraph-xl lg:font-bricolage-grotesque lg:text-display-s">
{subhead}
</p>
<div
className="h-px w-[295px] bg-[var(--color-border-default-secondary)]"
aria-hidden
/>
<p className="max-w-[720px] text-center font-inter text-label-xs text-[var(--color-content-default-tertiary)] md:text-label-s">
{credit}
</p>
</section>
);
}
CenteredHeaderView.displayName = "CenteredHeaderView";
export default memo(CenteredHeaderView);
@@ -0,0 +1,6 @@
export { default } from "./CenteredHeader.container";
export type {
CenteredHeaderIcon,
CenteredHeaderSegment,
CenteredHeaderViewProps,
} from "./CenteredHeader.types";
@@ -0,0 +1,18 @@
"use client";
/**
* Figma: "Section / Credits" (24240:1547)
*/
import { memo } from "react";
import { useMessages } from "../../../contexts/MessagesContext";
import CreditsView from "./Credits.view";
const CreditsContainer = memo(() => {
const copy = useMessages().pages.about.credits;
return <CreditsView heading={copy.heading} rows={copy.rows} />;
});
CreditsContainer.displayName = "Credits";
export default CreditsContainer;
@@ -0,0 +1,9 @@
export type CreditRow = {
role: string;
name: string;
};
export interface CreditsViewProps {
heading: string;
rows: CreditRow[];
}
@@ -0,0 +1,42 @@
"use client";
import { memo } from "react";
import type { CreditsViewProps } from "./Credits.types";
function CreditsView({ heading, rows }: CreditsViewProps) {
return (
<section
data-figma-node="24240:1547"
className="flex flex-col items-center bg-black px-[var(--space-400)] py-16 md:px-16 lg:px-20 lg:py-[var(--space-3000)]"
aria-labelledby="about-credits-heading"
>
<div className="flex w-full max-w-[800px] flex-col items-center gap-12">
<h2
id="about-credits-heading"
className="text-center font-bricolage-grotesque text-heading-l text-[var(--color-content-default-primary)]"
>
{heading}
</h2>
<dl className="m-0 flex w-full flex-col gap-5">
{rows.map((row) => (
<div
key={row.role}
className="flex flex-col gap-1 border-b border-[var(--color-border-default-tertiary)] pb-4"
>
<dt className="font-inter text-label-caps-xxs text-[var(--color-content-default-tertiary)] uppercase">
{row.role}
</dt>
<dd className="m-0 font-inter text-paragraph-s text-[var(--color-content-default-primary)]">
{row.name}
</dd>
</div>
))}
</dl>
</div>
</section>
);
}
CreditsView.displayName = "CreditsView";
export default memo(CreditsView);
@@ -0,0 +1,2 @@
export { default } from "./Credits.container";
export type { CreditRow, CreditsViewProps } from "./Credits.types";
@@ -0,0 +1,29 @@
"use client";
/**
* Figma: "Section / Four Questions" (24230:852)
*/
import { memo } from "react";
import { useMessages } from "../../../contexts/MessagesContext";
import FourQuestionsView from "./FourQuestions.view";
import type { FourQuestionCard } from "./FourQuestions.types";
const FourQuestionsContainer = memo(() => {
const copy = useMessages().pages.about.fourQuestions;
return (
<FourQuestionsView
heading={copy.heading}
cards={copy.cards as FourQuestionCard[]}
wishHeading={copy.wishHeading}
wishParagraphs={copy.wishParagraphs}
statsHeading={copy.statsHeading}
statsSource={copy.statsSource}
stats={copy.stats}
/>
);
});
FourQuestionsContainer.displayName = "FourQuestions";
export default FourQuestionsContainer;
@@ -0,0 +1,29 @@
export const FOUR_QUESTION_TONE_OPTIONS = [
"yellow",
"teal",
"lavender",
"red",
] as const;
export type FourQuestionTone = (typeof FOUR_QUESTION_TONE_OPTIONS)[number];
export type FourQuestionCard = {
tone: FourQuestionTone;
question: string;
line: string;
};
export type FourQuestionStat = {
value: string;
line: string;
};
export interface FourQuestionsViewProps {
heading: string;
cards: FourQuestionCard[];
wishHeading: string;
wishParagraphs: string[];
statsHeading: string;
statsSource: string;
stats: FourQuestionStat[];
}
@@ -0,0 +1,98 @@
"use client";
import { memo } from "react";
import type {
FourQuestionTone,
FourQuestionsViewProps,
} from "./FourQuestions.types";
const TONE_CLASS: Record<FourQuestionTone, string> = {
yellow: "bg-[var(--color-surface-invert-brand-secondary)]",
teal: "bg-[var(--color-surface-invert-brand-teal)]",
lavender: "bg-[var(--color-surface-invert-brand-lavender)]",
red: "bg-[var(--color-surface-invert-brand-red)]",
};
function FourQuestionsView({
heading,
cards,
wishHeading,
wishParagraphs,
statsHeading,
statsSource,
stats,
}: FourQuestionsViewProps) {
return (
<section
data-figma-node="24230:852"
className="flex flex-col gap-11 border-b border-[var(--color-border-default-secondary)] bg-black px-[var(--space-400)] py-16 lg:gap-14 lg:px-[var(--space-2400)] lg:py-20"
aria-labelledby="about-four-questions-heading"
>
<h2
id="about-four-questions-heading"
className="max-w-[768px] font-bricolage-grotesque text-heading-l text-[var(--color-content-default-primary)] lg:text-heading-xxl"
>
{heading}
</h2>
<ul className="m-0 grid list-none grid-cols-1 gap-4 p-0 md:grid-cols-2 md:gap-6">
{cards.map((card) => (
<li
key={card.question}
className={`flex flex-col gap-3 rounded-[var(--radius-300)] p-6 lg:gap-4 lg:rounded-[var(--radius-500)] lg:p-8 ${TONE_CLASS[card.tone]}`}
>
<h3 className="font-bricolage-grotesque text-display-xs text-[var(--color-content-invert-primary)]">
{card.question}
</h3>
<p className="font-inter text-paragraph-s text-[var(--color-content-invert-tertiary)] lg:text-paragraph-m">
{card.line}
</p>
</li>
))}
</ul>
<div className="flex max-w-[736px] flex-col gap-6">
<h2 className="font-bricolage-grotesque text-heading-l text-[var(--color-content-default-primary)] lg:text-heading-xxl">
{wishHeading}
</h2>
<div className="flex flex-col gap-[1lh]">
{wishParagraphs.map((paragraph) => (
<p
key={paragraph}
className="font-inter text-paragraph-m text-[var(--color-content-default-secondary)] lg:text-paragraph-l"
>
{paragraph}
</p>
))}
</div>
</div>
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2 lg:gap-3">
<h3 className="font-bricolage-grotesque text-display-s text-[var(--color-content-default-primary)]">
{statsHeading}
</h3>
<p className="font-inter text-paragraph-s text-[var(--color-content-default-tertiary)] lg:text-paragraph-m">
{statsSource}
</p>
</div>
<dl className="m-0 flex flex-col pt-0 lg:flex-row lg:gap-16 lg:pt-10">
{stats.map((stat) => (
<div
key={stat.value}
className="flex flex-col gap-2.5 border-t border-[var(--color-border-default-secondary)] py-6 lg:flex-1 lg:gap-4 lg:border-t-0 lg:py-0"
>
<dt className="font-bricolage-grotesque text-display-s text-[var(--color-content-default-primary)] lg:text-display-xl">
{stat.value}
</dt>
<dd className="m-0 font-inter text-paragraph-s text-[var(--color-content-default-secondary)] lg:text-paragraph-m">
{stat.line}
</dd>
</div>
))}
</dl>
</div>
</section>
);
}
FourQuestionsView.displayName = "FourQuestionsView";
export default memo(FourQuestionsView);
@@ -0,0 +1,8 @@
export { default } from "./FourQuestions.container";
export type {
FourQuestionCard,
FourQuestionStat,
FourQuestionTone,
FourQuestionsViewProps,
} from "./FourQuestions.types";
export { FOUR_QUESTION_TONE_OPTIONS } from "./FourQuestions.types";
@@ -0,0 +1,21 @@
"use client";
/**
* Figma: "Section / History" (24220:30635)
*/
import { memo } from "react";
import { useMessages } from "../../../contexts/MessagesContext";
import HistoryView from "./History.view";
import type { HistoryItem } from "./History.types";
const HistoryContainer = memo(() => {
const copy = useMessages().pages.about.history;
return (
<HistoryView heading={copy.heading} items={copy.items as HistoryItem[]} />
);
});
HistoryContainer.displayName = "History";
export default HistoryContainer;
@@ -0,0 +1,24 @@
export type HistoryAccent = "brand" | "positive";
export type HistoryRun = {
text: string;
emphasis?: boolean;
};
export type HistoryItem =
| {
kind: "entry";
date: string;
accent: HistoryAccent;
body: HistoryRun[];
}
| {
kind: "callout";
title: string;
body: string;
};
export interface HistoryViewProps {
heading: string;
items: HistoryItem[];
}
@@ -0,0 +1,82 @@
"use client";
import { memo } from "react";
import type { HistoryAccent, HistoryItem, HistoryViewProps } from "./History.types";
const ACCENT_CLASS: Record<HistoryAccent, string> = {
brand: "bg-[var(--color-content-default-brand-primary)]",
positive: "bg-[var(--color-surface-default-positive-primary)]",
};
const DATE_CLASS =
"font-bricolage-grotesque text-display-xxs text-[var(--color-content-default-primary)]";
const BODY_CLASS =
"font-inter text-paragraph-m text-[var(--color-content-default-secondary)]";
function DateMark({ date, accent }: { date: string; accent: HistoryAccent }) {
return (
<div className="flex w-full flex-col gap-1 md:w-[140px] md:shrink-0">
<p className={`${DATE_CLASS} whitespace-nowrap`}>{date}</p>
<span className={`h-0.5 w-8 ${ACCENT_CLASS[accent]}`} aria-hidden />
</div>
);
}
function HistoryItemView({ item }: { item: HistoryItem }) {
if (item.kind === "callout") {
return (
<aside className="flex flex-col gap-5 border-l-[3px] border-[var(--color-content-default-brand-primary)] pl-6">
<p className={DATE_CLASS}>{item.title}</p>
<p className={BODY_CLASS}>{item.body}</p>
</aside>
);
}
return (
<div
className={`flex flex-col gap-4 md:flex-row md:gap-8 ${
item.accent === "positive" ? "pt-6" : ""
}`}
>
<DateMark date={item.date} accent={item.accent} />
<p className={`min-w-0 flex-1 ${BODY_CLASS}`}>
{item.body.map((run, index) =>
run.emphasis ? (
<em key={index} className="italic">
{run.text}
</em>
) : (
<span key={index}>{run.text}</span>
),
)}
</p>
</div>
);
}
function HistoryView({ heading, items }: HistoryViewProps) {
return (
<section
data-figma-node="24220:30635"
className="flex flex-col items-center gap-10 bg-black px-[var(--space-400)] py-12 md:gap-16 md:px-[var(--space-1800)] md:py-[88px]"
aria-labelledby="about-history-heading"
>
<h2
id="about-history-heading"
className="w-full max-w-[800px] text-center font-bricolage-grotesque text-heading-l text-[var(--color-content-default-primary)]"
>
{heading}
</h2>
<div className="flex w-full max-w-[800px] flex-col gap-8 md:gap-10">
{items.map((item, index) => (
<HistoryItemView key={item.kind === "entry" ? item.date : index} item={item} />
))}
</div>
</section>
);
}
HistoryView.displayName = "HistoryView";
export default memo(HistoryView);
@@ -0,0 +1,2 @@
export { default } from "./History.container";
export type { HistoryItem, HistoryViewProps } from "./History.types";
@@ -0,0 +1,37 @@
"use client";
/**
* Figma: "Section / Narrative Block" (24237:1241)
*/
import { memo, useCallback, useState } from "react";
import { useMessages } from "../../../contexts/MessagesContext";
import AskOrganizerInquiryModal from "../../modals/AskOrganizerInquiry";
import NarrativeBlockView from "./NarrativeBlock.view";
const NarrativeBlockContainer = memo(() => {
const copy = useMessages().pages.about.narrativeBlock;
const [inquiryOpen, setInquiryOpen] = useState(false);
const openInquiry = useCallback(() => setInquiryOpen(true), []);
const closeInquiry = useCallback(() => setInquiryOpen(false), []);
return (
<>
<NarrativeBlockView
heading={copy.heading}
paragraphs={copy.paragraphs}
addHeading={copy.addHeading}
addLine={copy.addLine}
shareExperiences={copy.shareExperiences}
helpBuild={copy.helpBuild}
helpBuildHref={copy.helpBuildHref}
onShareExperiences={openInquiry}
/>
<AskOrganizerInquiryModal isOpen={inquiryOpen} onClose={closeInquiry} />
</>
);
});
NarrativeBlockContainer.displayName = "NarrativeBlock";
export default NarrativeBlockContainer;
@@ -0,0 +1,10 @@
export interface NarrativeBlockViewProps {
heading: string;
paragraphs: string[];
addHeading: string;
addLine: string;
shareExperiences: string;
helpBuild: string;
helpBuildHref: string;
onShareExperiences: () => void;
}
@@ -0,0 +1,77 @@
"use client";
import { memo } from "react";
import Button from "../../buttons/Button";
import type { NarrativeBlockViewProps } from "./NarrativeBlock.types";
function NarrativeBlockView({
heading,
paragraphs,
addHeading,
addLine,
shareExperiences,
helpBuild,
helpBuildHref,
onShareExperiences,
}: NarrativeBlockViewProps) {
return (
<section
data-figma-node="24237:1241"
className="flex flex-col items-center gap-10 border-b border-[var(--color-border-default-secondary)] bg-black px-[var(--space-400)] py-16 lg:gap-14 lg:px-[var(--space-2400)] lg:py-[var(--space-3000)]"
aria-labelledby="about-narrative-heading"
>
<div className="flex w-full max-w-[800px] flex-col gap-8 lg:gap-10">
<h2
id="about-narrative-heading"
className="font-bricolage-grotesque text-heading-l text-[var(--color-content-default-primary)] lg:text-heading-xxl"
>
{heading}
</h2>
<div className="flex flex-col gap-8">
{paragraphs.map((paragraph) => (
<p
key={paragraph}
className="font-inter text-paragraph-l text-[var(--color-content-default-secondary)] lg:text-paragraph-xl"
>
{paragraph}
</p>
))}
</div>
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<h3 className="font-bricolage-grotesque text-display-xs text-[var(--color-content-default-primary)]">
{addHeading}
</h3>
<p className="font-inter text-paragraph-s text-[var(--color-content-default-secondary)]">
{addLine}
</p>
</div>
<div className="flex flex-wrap gap-4">
<Button
buttonType="filled"
palette="default"
size="medium"
onClick={onShareExperiences}
>
{shareExperiences}
</Button>
<Button
buttonType="filled"
palette="default"
size="medium"
href={helpBuildHref}
target="_blank"
rel="noopener noreferrer"
>
{helpBuild}
</Button>
</div>
</div>
</div>
</section>
);
}
NarrativeBlockView.displayName = "NarrativeBlockView";
export default memo(NarrativeBlockView);
@@ -0,0 +1,2 @@
export { default } from "./NarrativeBlock.container";
export type { NarrativeBlockViewProps } from "./NarrativeBlock.types";
@@ -0,0 +1,18 @@
"use client";
/**
* Figma: "Section / Participant Selection" (24227:30762)
*/
import { memo } from "react";
import { useMessages } from "../../../contexts/MessagesContext";
import ParticipantSelectionView from "./ParticipantSelection.view";
const ParticipantSelectionContainer = memo(() => {
const copy = useMessages().pages.about.participantSelection;
return <ParticipantSelectionView {...copy} />;
});
ParticipantSelectionContainer.displayName = "ParticipantSelection";
export default ParticipantSelectionContainer;
@@ -0,0 +1,6 @@
export interface ParticipantSelectionViewProps {
heading: string;
body: string;
groups: string[];
footnote: string;
}
@@ -0,0 +1,89 @@
"use client";
import { memo } from "react";
import { getAssetPath } from "../../../../lib/assetUtils";
import type { ParticipantSelectionViewProps } from "./ParticipantSelection.types";
function ParticipantArt() {
return (
<div
className="relative mx-auto aspect-[540/555] w-full max-w-[420px] pr-12 lg:mx-0 lg:w-[540px] lg:max-w-none lg:shrink-0 lg:pr-0"
aria-hidden
>
{/* eslint-disable @next/next/no-img-element -- decorative Figma shapes */}
<img
src={getAssetPath("assets/shapes/about-participant-circles.svg")}
alt=""
width={282}
height={282}
className="absolute top-0 left-0 h-auto w-[52.2%]"
/>
<img
src={getAssetPath("assets/shapes/about-participant-rings.svg")}
alt=""
width={334}
height={334}
className="absolute top-[6.3%] left-[38.15%] h-auto w-[61.85%]"
/>
<img
src={getAssetPath("assets/shapes/about-participant-clover.svg")}
alt=""
width={200}
height={200}
className="absolute top-[57.4%] left-[25.7%] h-auto w-[37%] rotate-[60deg]"
/>
{/* eslint-enable @next/next/no-img-element */}
</div>
);
}
function ParticipantSelectionView({
heading,
body,
groups,
footnote,
}: ParticipantSelectionViewProps) {
return (
<section
data-figma-node="24227:30762"
className="flex flex-col gap-4 border-b border-[var(--color-border-default-secondary)] bg-black px-[var(--space-400)] py-20 lg:flex-row lg:items-center lg:justify-center lg:gap-8 lg:px-[var(--space-2400)]"
aria-labelledby="about-participant-heading"
>
<ParticipantArt />
<div className="flex w-full min-w-0 flex-col gap-12 lg:max-w-[928px] lg:flex-1">
<div className="flex flex-col gap-4">
<h2
id="about-participant-heading"
className="font-bricolage-grotesque text-display-s text-[var(--color-content-default-primary)] lg:text-heading-xxl"
>
{heading}
</h2>
<p className="font-inter text-paragraph-s text-[var(--color-content-default-tertiary)] lg:text-paragraph-l">
{body}
</p>
</div>
<div>
<ul className="m-0 list-none p-0">
{groups.map((group, index) => (
<li
key={group}
className={`border-b border-[var(--color-border-default-secondary)] py-4 font-inter text-paragraph-xs text-[var(--color-content-default-primary)] lg:text-paragraph-m ${
index === 0 ? "border-t" : ""
}`}
>
{group}
</li>
))}
</ul>
<p className="pt-6 font-inter text-paragraph-xxs text-[var(--color-content-default-tertiary)] lg:text-paragraph-s">
{footnote}
</p>
</div>
</div>
</section>
);
}
ParticipantSelectionView.displayName = "ParticipantSelectionView";
export default memo(ParticipantSelectionView);
@@ -0,0 +1,2 @@
export { default } from "./ParticipantSelection.container";
export type { ParticipantSelectionViewProps } from "./ParticipantSelection.types";
@@ -13,6 +13,7 @@ const QuoteBlockContainer = memo<QuoteBlockProps>(
className = "",
quote = "The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized.",
quoteSecondary,
statementStacked = false,
author = "Jo Freeman",
source = "The Tyranny of Structurelessness",
avatarSrc = getAssetPath(ASSETS.QUOTE_AVATAR),
@@ -151,6 +152,7 @@ const QuoteBlockContainer = memo<QuoteBlockProps>(
className={className}
quote={quote}
quoteSecondary={quoteSecondary}
statementStacked={statementStacked}
author={author}
source={source}
quoteId={quoteId}
@@ -11,6 +11,11 @@ export interface QuoteBlockProps {
quote?: string;
/** Second paragraph for **`statement`** (Section/Quote); merged into one `<p>` from **`lg`**. */
quoteSecondary?: string;
/**
* Keep the two statement paragraphs stacked at every breakpoint.
* Default joins them into one paragraph from `lg`.
*/
statementStacked?: boolean;
author?: string;
source?: string;
avatarSrc?: string;
@@ -46,6 +51,7 @@ export interface QuoteBlockViewProps {
className: string;
quote: string;
quoteSecondary?: string;
statementStacked: boolean;
author: string;
source?: string;
quoteId: string;
@@ -11,6 +11,7 @@ function QuoteBlockView({
className,
quote,
quoteSecondary,
statementStacked,
author,
source,
quoteId,
@@ -41,13 +42,25 @@ function QuoteBlockView({
<div className="relative z-10 flex w-full min-w-0 shrink-0 flex-col items-center text-center">
<p
id={quoteId}
className={`${statementTextClass} mb-0 flex w-full min-w-0 flex-col gap-9 text-center md:gap-[length:var(--text-xx-large-heading--line-height)] lg:block lg:gap-0`}
className={
statementStacked
? `${statementTextClass} mb-0 flex w-full min-w-0 flex-col gap-[1lh] text-center`
: `${statementTextClass} mb-0 flex w-full min-w-0 flex-col gap-9 text-center md:gap-[length:var(--text-xx-large-heading--line-height)] lg:block lg:gap-0`
}
>
<span className="block lg:inline">{quote}</span>
<span className={statementStacked ? "block" : "block lg:inline"}>
{quote}
</span>
{quoteSecondary ? (
<>
<span className="hidden lg:inline">{" "}</span>
<span className="block lg:inline">{quoteSecondary}</span>
{statementStacked ? (
" "
) : (
<span className="hidden lg:inline">{" "}</span>
)}
<span className={statementStacked ? "block" : "block lg:inline"}>
{quoteSecondary}
</span>
</>
) : null}
</p>
@@ -0,0 +1,18 @@
"use client";
/**
* Figma: "Section / Rebuild Process" (24237:1085)
*/
import { memo } from "react";
import { useMessages } from "../../../contexts/MessagesContext";
import RebuildProcessView from "./RebuildProcess.view";
const RebuildProcessContainer = memo(() => {
const copy = useMessages().pages.about.rebuildProcess;
return <RebuildProcessView {...copy} />;
});
RebuildProcessContainer.displayName = "RebuildProcess";
export default RebuildProcessContainer;
@@ -0,0 +1,4 @@
export interface RebuildProcessViewProps {
heading: string;
body: string;
}
@@ -0,0 +1,28 @@
"use client";
import { memo } from "react";
import type { RebuildProcessViewProps } from "./RebuildProcess.types";
function RebuildProcessView({ heading, body }: RebuildProcessViewProps) {
return (
<section
data-figma-node="24237:1085"
className="flex flex-col gap-6 border-b border-white/15 bg-black px-[var(--space-400)] py-16 lg:flex-row lg:items-start lg:justify-center lg:gap-12 lg:px-[var(--space-2400)] lg:py-[var(--space-3000)]"
aria-labelledby="about-rebuild-heading"
>
<h2
id="about-rebuild-heading"
className="font-bricolage-grotesque text-heading-l text-[var(--color-content-default-primary)] lg:w-[287px] lg:shrink-0"
>
{heading}
</h2>
<p className="max-w-[800px] font-inter text-paragraph-l text-[var(--color-content-default-tertiary)] lg:flex-1 lg:text-paragraph-xl">
{body}
</p>
</section>
);
}
RebuildProcessView.displayName = "RebuildProcessView";
export default memo(RebuildProcessView);
@@ -0,0 +1,2 @@
export { default } from "./RebuildProcess.container";
export type { RebuildProcessViewProps } from "./RebuildProcess.types";
@@ -0,0 +1,67 @@
"use client";
/**
* Figma: "Section / Your Turn" (24239:1397)
*/
import { memo, useCallback, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useMessages } from "../../../contexts/MessagesContext";
import { fetchAuthSession } from "../../../../lib/create/api";
import { prepareFreshCreateFlowEntrySync } from "../../../(app)/create/utils/prepareFreshCreateFlowEntry";
import AskOrganizerInquiryModal from "../../modals/AskOrganizerInquiry";
import YourTurnView from "./YourTurn.view";
const YourTurnContainer = memo(() => {
const router = useRouter();
const copy = useMessages().pages.about.yourTurn;
const [signedIn, setSignedIn] = useState(false);
const [inquiryOpen, setInquiryOpen] = useState(false);
useEffect(() => {
let cancelled = false;
void fetchAuthSession()
.then(({ user }) => {
if (!cancelled) setSignedIn(Boolean(user));
})
.catch(() => {
/* guest fallback */
});
return () => {
cancelled = true;
};
}, []);
const onCreateRule = useCallback(() => {
prepareFreshCreateFlowEntrySync({ signedIn });
router.push("/create/informational");
}, [router, signedIn]);
return (
<>
<YourTurnView
heading={copy.heading}
body={copy.body}
createRule={copy.createRule}
facilitator={copy.facilitator}
facilitatorHref={copy.facilitatorHref}
questionsLead={copy.questionsLead}
faq={copy.faq}
faqHref={copy.faqHref}
or={copy.or}
askOrganizer={copy.askOrganizer}
illustrationCaption={copy.illustrationCaption}
onCreateRule={onCreateRule}
onAskOrganizer={() => setInquiryOpen(true)}
/>
<AskOrganizerInquiryModal
isOpen={inquiryOpen}
onClose={() => setInquiryOpen(false)}
/>
</>
);
});
YourTurnContainer.displayName = "YourTurn";
export default YourTurnContainer;
@@ -0,0 +1,15 @@
export interface YourTurnViewProps {
heading: string;
body: string;
createRule: string;
facilitator: string;
facilitatorHref: string;
questionsLead: string;
faq: string;
faqHref: string;
or: string;
askOrganizer: string;
illustrationCaption: string;
onCreateRule: () => void;
onAskOrganizer: () => void;
}
@@ -0,0 +1,102 @@
"use client";
import { memo } from "react";
import Button from "../../buttons/Button";
import Link from "../../navigation/Link";
import type { YourTurnViewProps } from "./YourTurn.types";
const COLOR_BARS = [
"bg-[var(--color-lavender-lavender200)]",
"bg-[var(--color-surface-invert-brand-teal)]",
"bg-[var(--color-surface-invert-brand-secondary)]",
"bg-[var(--color-surface-invert-brand-red)]",
] as const;
function YourTurnView({
heading,
body,
createRule,
facilitator,
facilitatorHref,
questionsLead,
faq,
faqHref,
or,
askOrganizer,
illustrationCaption,
onCreateRule,
onAskOrganizer,
}: YourTurnViewProps) {
return (
<section
data-figma-node="24239:1397"
className="border-b border-[var(--color-border-default-secondary)] bg-[var(--color-surface-default-tertiary)] px-[var(--space-600)] py-12 lg:px-[var(--space-2400)] lg:py-[var(--space-3000)]"
aria-labelledby="about-your-turn-heading"
>
<div className="mx-auto flex w-full max-w-[1920px] flex-col gap-10 lg:flex-row lg:items-start lg:gap-12">
<div className="flex w-full flex-col gap-8 lg:max-w-[472px] lg:shrink-0 lg:gap-12">
<div className="flex flex-col gap-5 lg:gap-6">
<div className="flex h-1 w-[184px] gap-1.5 lg:h-3 lg:w-[400px]" aria-hidden>
{COLOR_BARS.map((bar) => (
<span key={bar} className={`h-full flex-1 rounded-sm ${bar}`} />
))}
</div>
<h2
id="about-your-turn-heading"
className="font-bricolage-grotesque text-heading-l text-[var(--color-content-default-primary)]"
>
{heading}
</h2>
<p className="font-inter text-paragraph-m text-[var(--color-content-default-secondary)]">
{body}
</p>
</div>
<div className="flex flex-col gap-3 md:flex-row md:gap-4">
<Button
buttonType="filled"
palette="default"
size="large"
className="w-full justify-center md:w-auto"
onClick={onCreateRule}
>
{createRule}
</Button>
<Button
buttonType="outline"
palette="default"
size="large"
className="w-full justify-center md:w-auto"
href={facilitatorHref}
>
{facilitator}
</Button>
</div>
<p className="font-inter text-paragraph-s text-[var(--color-content-default-secondary)]">
{questionsLead}{" "}
<Link href={faqHref} variant="paragraph" theme="dark" type="primary">
{faq}
</Link>{" "}
{or}{" "}
<Link
variant="paragraph"
theme="dark"
type="primary"
onClick={onAskOrganizer}
>
{askOrganizer}
</Link>
</p>
</div>
<div className="flex h-[246px] w-full flex-col items-start justify-end rounded-[var(--radius-300)] border border-dashed border-[var(--color-border-default-tertiary)] bg-[var(--color-surface-default-secondary)] p-6 lg:h-[340px] lg:flex-1">
<p className="font-inter text-paragraph-xxs text-[var(--color-content-default-tertiary)]">
{illustrationCaption}
</p>
</div>
</div>
</section>
);
}
YourTurnView.displayName = "YourTurnView";
export default memo(YourTurnView);
@@ -0,0 +1,2 @@
export { default } from "./YourTurn.container";
export type { YourTurnViewProps } from "./YourTurn.types";