diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 8cab8cbf..a2851a4e 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -1,6 +1,8 @@ import type { Metadata } from "next" import Link from "next/link" import { FiArrowUpRight } from "react-icons/fi" +import { ClosingCta } from "@/components/layout/closing-cta" +import { CONTAINER, SECTION_TITLE } from "@/components/layout/page" import { AssociationBodies } from "@/components/structure/association-bodies" import { BoardGallery } from "@/components/structure/board-gallery" import { LegalStatus } from "@/components/structure/legal-status" @@ -15,14 +17,13 @@ export const metadata: Metadata = { "Un’organizzazione pensata per durare: scopri l’organigramma, il Direttivo, gli organi associativi e i ruoli di PoliNetwork APS.", } -const containerClass = "mx-auto flex w-full max-w-330 flex-col gap-10 px-6 md:gap-14" -const titleClass = - "typo-headline-medium sm:typo-display-medium text-balance bg-linear-to-b from-text-primary to-text-secondary bg-clip-text text-center text-transparent" +const containerClass = `${CONTAINER} flex flex-col gap-10 md:gap-14` +const titleClass = `${SECTION_TITLE} text-center` export default function StructurePage() { return (
-
+
@@ -49,21 +50,19 @@ export default function StructurePage() {
-
-

- Tutto ciò che c’è da sapere su PoliNetwork. -

-

- Scopri chi siamo, cosa facciamo e come entrare a far parte della nostra realtà. -

- -
-
+ + + Unisciti a noi + + + + } + /> +
diff --git a/src/app/chi-siamo/page.tsx b/src/app/chi-siamo/page.tsx index 0fdae581..d96a6022 100644 --- a/src/app/chi-siamo/page.tsx +++ b/src/app/chi-siamo/page.tsx @@ -14,7 +14,7 @@ export const metadata: Metadata = { export default function ChiSiamoPage() { return ( -
+
diff --git a/src/app/projects/[id]/page.tsx b/src/app/projects/[id]/page.tsx index 3e023664..8d1c623e 100644 --- a/src/app/projects/[id]/page.tsx +++ b/src/app/projects/[id]/page.tsx @@ -5,6 +5,7 @@ import { notFound } from "next/navigation" import { FiArrowLeft, FiArrowUpRight, FiUploadCloud } from "react-icons/fi" import { Glass } from "@/components/glass" import { GradientIcon } from "@/components/gradient-icon" +import { SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" import { getProjectResources, resourceDetails } from "@/components/projects/resources" import { Button } from "@/components/ui/button" import { Card, CardContent, CardTitle } from "@/components/ui/card" @@ -66,12 +67,8 @@ export default async function ProjectPage({ params }: Props) { {project.category in categoryLabels && (

{categoryLabels[project.category]}

)} -

- {project.title} -

-

- {project.descriptionIt} -

+

{project.title}

+

{project.descriptionIt}

@@ -82,13 +79,10 @@ export default async function ProjectPage({ params }: Props) { className="mx-auto flex w-full max-w-330 flex-col gap-10 px-6 md:gap-14" >
-

+

Come usarlo

-

+

{resources.length > 1 ? "Scegli la versione più adatta a te." : "Apri il progetto e segui le istruzioni per iniziare."} @@ -143,10 +137,8 @@ export default async function ProjectPage({ params }: Props) {

-

- Scopri gli altri progetti -

-

+

Scopri gli altri progetti

+

Strumenti e app realizzati dagli studenti del Politecnico, per gli studenti del Politecnico.

-
-
+ } + />
) } diff --git a/src/components/chi-siamo/answer-section.tsx b/src/components/chi-siamo/answer-section.tsx index 0e718fc6..45ffdb98 100644 --- a/src/components/chi-siamo/answer-section.tsx +++ b/src/components/chi-siamo/answer-section.tsx @@ -1,5 +1,6 @@ import { FiAward, FiBookOpen, FiUsers } from "react-icons/fi" import { CardIcon } from "@/components/card-icon" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" const cards = [ { @@ -23,12 +24,10 @@ const cards = [ export function AnswerSection() { return ( -
+
-

- La nostra risposta -

-

+

La nostra risposta

+

Costruiamo e manteniamo la rete di comunicazione studentesca del Politecnico di Milano.

@@ -40,7 +39,7 @@ export function AnswerSection() { {...card} size="compact" align="start" - className="h-full p-0 [&_h3]:text-balance" + className="h-full p-0 [&_h3]:text-balance [&_p]:max-w-none" /> ))} diff --git a/src/components/chi-siamo/history-section.tsx b/src/components/chi-siamo/history-section.tsx index 4a2fbf46..0ccd9923 100644 --- a/src/components/chi-siamo/history-section.tsx +++ b/src/components/chi-siamo/history-section.tsx @@ -1,20 +1,17 @@ import Link from "next/link" -import { FiArrowUpRight, FiClock, FiUsers } from "react-icons/fi" -import { Glass } from "@/components/glass" -import { GradientIcon } from "@/components/gradient-icon" +import { FiArrowUpRight } from "react-icons/fi" +import { CONTAINER, SECTION_TITLE } from "@/components/layout/page" import { Button } from "@/components/ui/button" const blocks = [ { title: "Da dove veniamo", description: "Dal primo gruppo Telegram, nel 2016, a una rete costruita dagli studenti, per gli studenti.", - icon: FiClock, cta: "La nostra storia", href: "/storia", }, { title: "Come siamo organizzati", - icon: FiUsers, description: "Direttivo, team operativi, admin e soci. Guarda come funziona la macchina dall'interno.", cta: "La nostra struttura", href: "/about", @@ -23,21 +20,18 @@ const blocks = [ export function HistorySection() { return ( -
+
{blocks.map((block) => ( - - -

- {block.title} -

-

{block.description}

+
+

{block.title}

+

{block.description}

- +
))}
) diff --git a/src/components/chi-siamo/numbers-section.tsx b/src/components/chi-siamo/numbers-section.tsx index 947591b0..4baa82a4 100644 --- a/src/components/chi-siamo/numbers-section.tsx +++ b/src/components/chi-siamo/numbers-section.tsx @@ -1,6 +1,7 @@ import { FiBook, FiBookOpen, FiHeadphones, FiSend, FiUser, FiUsers } from "react-icons/fi" import { GradientIcon } from "@/components/gradient-icon" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" +import { Card } from "@/components/ui/card" const stats = [ { value: "500+", label: "gruppi Telegram e WhatsApp attivi", icon: FiSend }, @@ -13,35 +14,30 @@ const stats = [ export function NumbersSection() { return ( -
+
-

- I numeri -

-

+

I numeri

+

Un network in continua crescita, costruito dagli studenti, per gli studenti.

-
+
    {stats.map((stat) => ( - - - - {stat.value} - - - - {stat.label} - +
  • + + +
    +

    {stat.value}

    +

    {stat.label}

    +
    +
    +
  • ))} -
+ -

+

Dal 2016 al fianco degli studenti del PoliMi

diff --git a/src/components/chi-siamo/path-section.tsx b/src/components/chi-siamo/path-section.tsx index 2b835347..263f4880 100644 --- a/src/components/chi-siamo/path-section.tsx +++ b/src/components/chi-siamo/path-section.tsx @@ -1,6 +1,7 @@ import { FiBook, FiClipboard, FiFileText, FiGift, FiUser, FiUserCheck } from "react-icons/fi" import { Glass } from "@/components/glass" import { GradientIcon } from "@/components/gradient-icon" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" import { cn } from "@/lib/utils" const steps = [ @@ -18,10 +19,8 @@ const steps = [ export function PathSection() { return ( -
-

- Il tuo percorso, sempre al tuo fianco -

+
+

Il tuo percorso, sempre al tuo fianco

    {steps.map((step, index) => ( @@ -46,7 +45,7 @@ export function PathSection() { ))}
-

+

Dalla prima simulazione del TOL fino alla laurea, ogni studente ha sempre{" "} una risposta a portata di mano e{" "} qualcuno a cui chiederla. diff --git a/src/components/chi-siamo/problem-section.tsx b/src/components/chi-siamo/problem-section.tsx index d2bb57df..f1cd07e8 100644 --- a/src/components/chi-siamo/problem-section.tsx +++ b/src/components/chi-siamo/problem-section.tsx @@ -1,5 +1,6 @@ import { FiEdit, FiFileText, FiUsers } from "react-icons/fi" -import { Card, CardAction, CardContent } from "@/components/ui/card" +import { GradientIcon } from "@/components/gradient-icon" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" const questions = [ { text: "Dove trovo il gruppo del mio corso?", icon: FiUsers }, @@ -9,23 +10,22 @@ const questions = [ export function ProblemSection() { return ( -

+
-

- Il problema -

-

- Il PoliMi è complesso. Le informazioni non dovrebbero esserlo. -

+

Il problema

+

Il PoliMi è complesso. Le informazioni non dovrebbero esserlo.

-
+
    {questions.map((question) => ( - - {question.text} - - +
  • + + {question.text} +
  • ))} -
+
) } diff --git a/src/components/chi-siamo/support-section.tsx b/src/components/chi-siamo/support-section.tsx index 8f3493ca..506a90fd 100644 --- a/src/components/chi-siamo/support-section.tsx +++ b/src/components/chi-siamo/support-section.tsx @@ -1,31 +1,25 @@ -import { FiHeart } from "react-icons/fi" -import { Glass } from "@/components/glass" -import { GradientIcon } from "@/components/gradient-icon" +import { CONTAINER, SECTION_TITLE } from "@/components/layout/page" export function SupportSection() { return ( -
- -
- -

Sostieni PoliNetwork

-

- Se vuoi supportare PoliNetwork economicamente, puoi destinarci il 5x1000 nella dichiarazione dei redditi. Si - tratta di una quota IRPEF che verseresti comunque, e che puoi scegliere di indirizzare a noi senza alcun - costo aggiuntivo. -

-
- -

5x1000

-

Il tuo sostegno può fare la differenza

-

- Nella sezione dedicata agli Enti del Terzo Settore, indica il nostro codice fiscale: -

-

- 97927490157 -

-
-
+
+
+

Sostieni PoliNetwork

+

+ Se vuoi supportare PoliNetwork economicamente, puoi destinarci il 5x1000 nella dichiarazione dei redditi. Si + tratta di una quota IRPEF che verseresti comunque, e che puoi scegliere di indirizzare a noi senza alcun costo + aggiuntivo. +

+
+
+

+ Il tuo 5x1000 può fare la differenza +

+

+ Nella sezione dedicata agli Enti del Terzo Settore, indica il nostro codice fiscale: +

+

97927490157

+
) } diff --git a/src/components/chi-siamo/vision-section.tsx b/src/components/chi-siamo/vision-section.tsx index ec6ba6d9..b95392ff 100644 --- a/src/components/chi-siamo/vision-section.tsx +++ b/src/components/chi-siamo/vision-section.tsx @@ -1,29 +1,17 @@ -import Link from "next/link" -import { FiArrowDown, FiArrowUpRight } from "react-icons/fi" -import { Button } from "@/components/ui/button" +import { CONTAINER, SECTION_LEAD } from "@/components/layout/page" +import { cn } from "@/lib/utils" export function VisionSection() { return ( -
+
+

La nostra visione

Un Politecnico in cui nessuno studente resti indietro per mancanza di{" "} informazioni.

-

+

Rendiamo le informazioni sul PoliMi più accessibili, comprensibili e facili da trovare.

-
- - -
) } diff --git a/src/components/home/materials.tsx b/src/components/home/materials.tsx index 199c96f5..fff6659b 100644 --- a/src/components/home/materials.tsx +++ b/src/components/home/materials.tsx @@ -2,7 +2,9 @@ import Link from "next/link" import { Fragment } from "react" import { FiArrowUpRight, FiBook, FiBookOpen, FiClipboard, FiFileText, FiUploadCloud } from "react-icons/fi" import { CardIcon } from "@/components/card-icon" +import { CONTAINER } from "@/components/layout/page" import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils" import { CardCaption } from "../card-caption" const MATERIALS_URL = "https://polinetwork.org/it/materials/view/" @@ -33,7 +35,12 @@ const archiveTypes = [ export function Materials() { return ( -
+

Materials

diff --git a/src/components/home/projects.tsx b/src/components/home/projects.tsx index 4d67233f..d0078106 100644 --- a/src/components/home/projects.tsx +++ b/src/components/home/projects.tsx @@ -1,7 +1,9 @@ import Link from "next/link" import { FiArrowUpRight } from "react-icons/fi" import { CardCaption } from "@/components/card-caption" +import { CONTAINER } from "@/components/layout/page" import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils" import { Carousel, CarouselContent, CarouselDots, CarouselItem } from "../ui/carousel" const featuredCards = [ @@ -47,7 +49,12 @@ function ProjectCard({ className, ...card }: (typeof featuredCards)[number] & { export function Projects() { return ( -
+

15+ Projects

diff --git a/src/components/join/admin.tsx b/src/components/join/admin.tsx index a41f66c4..cf27b697 100644 --- a/src/components/join/admin.tsx +++ b/src/components/join/admin.tsx @@ -3,6 +3,8 @@ import connectorFirst from "@/assets/icons/join/connector-first.svg" import connectorSecond from "@/assets/icons/join/connector-second.svg" import { InfoCard } from "@/components/collabora/info-card" import { Highlight, SectionTitle } from "@/components/collabora/section" +import { SECTION_LEAD } from "@/components/layout/page" +import { cn } from "@/lib/utils" import { JoinCards } from "./cards" import { adminCards, adminSteps } from "./constants" @@ -14,7 +16,7 @@ export function AdminSection() {
Diventa Admin -
+

Un admin è la presenza di PoliNetwork nei gruppi ogni giorno.

Non servono competenze specifiche ma solamente{" "} diff --git a/src/components/join/introduction.tsx b/src/components/join/introduction.tsx index c464bcec..e49057a4 100644 --- a/src/components/join/introduction.tsx +++ b/src/components/join/introduction.tsx @@ -3,6 +3,8 @@ import { FiMessageCircle, FiUserCheck, FiUsers } from "react-icons/fi" import networkLogo from "@/assets/images/join/network-logo.png" import { CardIcon } from "@/components/card-icon" import { Highlight, SectionTitle } from "@/components/collabora/section" +import { SECTION_LEAD } from "@/components/layout/page" +import { cn } from "@/lib/utils" const paths = [ { title: "Diventa Admin", icon: FiMessageCircle, href: "#admin" }, @@ -15,7 +17,7 @@ export function Introduction() {

Cosa significa far parte di PoliNetwork -
+

PoliNetwork è gestita interamente da studenti del Politecnico di Milano.

diff --git a/src/components/join/membership.tsx b/src/components/join/membership.tsx index 0d7a82ee..ecec0b21 100644 --- a/src/components/join/membership.tsx +++ b/src/components/join/membership.tsx @@ -2,6 +2,7 @@ import Image from "next/image" import externalLink from "@/assets/icons/join/external-link.svg" import membersPhoto from "@/assets/images/join/members.webp" import { Highlight, SectionTitle } from "@/components/collabora/section" +import { SECTION_LEAD } from "@/components/layout/page" import { PhotoShapes } from "@/components/shapes" import { cn } from "@/lib/utils" import { membershipSteps } from "./constants" @@ -17,7 +18,7 @@ export function MembershipSection() { Diventa Socio Diventa Socio di PoliNetwork -
+

Il tesseramento è il modo per fare parte di PoliNetwork in modo formale, con{" "} diritto di voto nelle Assemblee dei Soci, di candidarsi alle elezioni del direttivo e{" "} diff --git a/src/components/join/teams.tsx b/src/components/join/teams.tsx index 82dbb24f..964b9659 100644 --- a/src/components/join/teams.tsx +++ b/src/components/join/teams.tsx @@ -1,7 +1,9 @@ import Link from "next/link" import { FiArrowUpRight } from "react-icons/fi" import { Highlight, SectionTitle } from "@/components/collabora/section" +import { SECTION_LEAD } from "@/components/layout/page" import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils" import { JoinCards } from "./cards" import { teamCards } from "./constants" @@ -13,7 +15,7 @@ export function TeamsSection() { >

Entra in un Team -
+

Entrare in un team significa avere un ruolo operativo con deliverable reali, non essere osservatori di un progetto altrui. diff --git a/src/components/layout/TextOnlySection.tsx b/src/components/layout/TextOnlySection.tsx index da6cbbd8..0cd50606 100644 --- a/src/components/layout/TextOnlySection.tsx +++ b/src/components/layout/TextOnlySection.tsx @@ -60,7 +60,7 @@ export default function TextOnlyLayout({ return (

diff --git a/src/components/layout/closing-cta.tsx b/src/components/layout/closing-cta.tsx new file mode 100644 index 00000000..34ca9cbe --- /dev/null +++ b/src/components/layout/closing-cta.tsx @@ -0,0 +1,14 @@ +import type { ReactNode } from "react" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" +import { cn } from "@/lib/utils" + +/** The centred call to action that closes a page: title, a short lead and one button. */ +export function ClosingCta({ title, lead, action }: { title: ReactNode; lead: ReactNode; action: ReactNode }) { + return ( +
+

{title}

+

{lead}

+ {action} +
+ ) +} diff --git a/src/components/layout/page.ts b/src/components/layout/page.ts new file mode 100644 index 00000000..1070c10c --- /dev/null +++ b/src/components/layout/page.ts @@ -0,0 +1,8 @@ +/** Content column of the redesigned pages: Figma's ~1272px column with the homepage's 24/40/64px gutters. */ +export const CONTAINER = "mx-auto w-full max-w-350 px-6 sm:px-10 lg:px-16" + +/** Section title in the grey gradient. Closing statements (`ClosingCta`) switch it to blue. */ +export const SECTION_TITLE = + "typo-headline-medium sm:typo-display-medium text-balance bg-linear-to-b from-text-primary to-text-secondary bg-clip-text text-transparent" + +export const SECTION_LEAD = "typo-body-large sm:typo-headline-extrasmall text-pretty" diff --git a/src/components/storia/boards.tsx b/src/components/storia/boards.tsx index 1beca4d5..f3b6bc17 100644 --- a/src/components/storia/boards.tsx +++ b/src/components/storia/boards.tsx @@ -1,21 +1,17 @@ "use client" -import Image from "next/image" -import { Glass } from "@/components/glass" +import { CONTAINER, SECTION_TITLE } from "@/components/layout/page" +import { BoardMemberCard } from "@/components/structure/board-member-card" import TabsNavigation from "@/components/tabs" import { Tabs } from "@/components/tabs/tabs" import { TabsContent } from "@/components/tabs/tabs-content" -import { Card, CardContent } from "@/components/ui/card" -import { cn } from "@/lib/utils" import { boardMembers, boardTerms } from "./constants" export function Boards() { return ( -
+

I nostri direttivi

-

- Ogni mandato, un nuovo capitolo -

+

Ogni mandato, un nuovo capitolo

({ value: term, label: term }))} /> {boardTerms.map((term) => ( @@ -25,39 +21,26 @@ export function Boards() {
{boardMembers.map((member) => ( - - {member.name} -
- -

{member.name}

- {member.role &&

{member.role}

} -
- + member={member} + sizes="(max-width: 639px) 45vw, (max-width: 1279px) 30vw, 224px" + className="sm:w-[calc((100%-48px)/3)] xl:w-[calc((100%-72px)/4)] min-[360px]:w-[calc((100%-24px)/2)]" + /> ))}

Il Direttivo viene eletto ogni due anni dall’Assemblea dei Soci tra i soci dell’associazione.

- +

{term}

Il Direttivo

Si concentra sul rafforzamento della struttura, sull’evoluzione dei servizi e sullo sviluppo di nuovi progetti per la community.

- +
) : (
diff --git a/src/components/storia/constants.ts b/src/components/storia/constants.ts index 78b1325d..dae96aad 100644 --- a/src/components/storia/constants.ts +++ b/src/components/storia/constants.ts @@ -1,3 +1,5 @@ +import { FiUser, FiUserCheck, FiUsers } from "react-icons/fi" +import telegram from "@/assets/icons/telegram-gradient.svg" import ale from "@/assets/images/storia/ale-he.jpg" import alessandro from "@/assets/images/storia/alessandro-gabbini.jpg" import elia from "@/assets/images/storia/elia-falzoni.jpg" @@ -10,19 +12,27 @@ import tommaso from "@/assets/images/storia/tommaso-morganti.jpg" export const milestones = [ { year: "2016", + href: "#capitolo-1", + icon: FiUser, description: "Nasce il primo gruppo per rispondere a una necessità semplice: trovare informazioni e parlare con i compagni di corso.", }, { year: "2017", + href: "#capitolo-1", + icon: telegram.src, description: "I gruppi si moltiplicano su Telegram e WhatsApp e iniziamo a coprire tutti i corsi di laurea.", }, { year: "2022", + href: "#capitolo-5", + icon: FiUserCheck, description: "PoliNetwork diventa un’APS-ETS, con un direttivo eletto e una struttura ancora più democratica.", }, { year: "OGGI", + href: "#direttivi", + icon: FiUsers, description: "Oltre 200 admin volontari e una community che accompagna ogni giorno migliaia di studenti del Politecnico di Milano.", }, diff --git a/src/components/storia/continuation.tsx b/src/components/storia/continuation.tsx index 1b417174..a4cd0d18 100644 --- a/src/components/storia/continuation.tsx +++ b/src/components/storia/continuation.tsx @@ -1,53 +1,57 @@ import Link from "next/link" import { FiArrowUpRight } from "react-icons/fi" -import { Glass } from "@/components/glass" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" +const years = ["2016", "2022", "2026", "?"] + +// Figma: text on the left, the years on a line on the right. No panel or gradient behind it, and the "?" is a +// quiet open slot rather than a filled pill that looks like a button. export function Continuation() { return ( -
- -
-

- La storia continua. -

-

- Ogni anno qualcuno arriva, qualcuno si laurea e qualcun altro raccoglie il testimone. La missione rimane la - stessa: rendere la vita universitaria più semplice, accessibile e umana. -

- -
-
-
    - {["2016", "2022", "2026", "?"].map((year) => ( -
  1. - - {year} - -
  2. - ))} -
-

Nuove persone, nuove idee, nuovi progetti.

-

- Il prossimo capitolo lo scriviamo insieme. -

-
-
+
+
+

+ La storia continua. +

+

+ Ogni anno qualcuno arriva, qualcuno si laurea e qualcun altro raccoglie il testimone. Nuove persone, nuove + idee, nuovi progetti. La missione rimane la stessa: rendere la vita universitaria più semplice, accessibile e + umana. +

+
+ +
+
    + {years.map((year, index) => ( +
  1. 0 && "flex-1")}> + {index > 0 && } + + {year} + +
  2. + ))} +
+

+ Il prossimo capitolo lo scriviamo insieme. +

+ +
) } diff --git a/src/components/storia/hero.tsx b/src/components/storia/hero.tsx index 6c260137..b13235ee 100644 --- a/src/components/storia/hero.tsx +++ b/src/components/storia/hero.tsx @@ -1,76 +1,30 @@ -import Image from "next/image" import Link from "next/link" -import { FiArrowDown, FiUsers } from "react-icons/fi" -import { Glass } from "@/components/glass" -import { GradientIcon } from "@/components/gradient-icon" +import { FiArrowDown } from "react-icons/fi" +import { CONTAINER } from "@/components/layout/page" import { Button } from "@/components/ui/button" -import { Card, CardContent } from "@/components/ui/card" import { Hero } from "@/components/ui/hero" -import { cn } from "@/lib/utils" -import { boardMembers } from "./constants" - -const portraits = [ - { member: boardMembers[1], position: "top-12 left-0 -rotate-6" }, - { - member: boardMembers[0], - position: "-translate-x-1/2 top-0 left-1/2 z-10", - }, - { member: boardMembers[3], position: "top-12 right-0 rotate-6 sm:top-16" }, -] +// Figma: a centred XL title and nothing else. The board portraits appear further down, under "I nostri direttivi". export function HistoryHero() { return ( -
-
- -
- - -
-
-
-
- {portraits.map(({ member, position }) => ( - - {member.name} -
- - {member.role} - - - ))} -
- - -
-

200+

-

admin volontari, oggi

-
-
+
+ +
+ +
) diff --git a/src/components/storia/organization.tsx b/src/components/storia/organization.tsx index 7780ee4c..969b1fd7 100644 --- a/src/components/storia/organization.tsx +++ b/src/components/storia/organization.tsx @@ -1,187 +1,18 @@ import Link from "next/link" import { FiArrowDown } from "react-icons/fi" -import { - LuBookUser, - LuCodeXml, - LuCrown, - LuGlobe, - LuHandshake, - LuPencilRuler, - LuShield, - LuShieldCheck, - LuUserCog, - LuUsersRound, -} from "react-icons/lu" -import { CardIcon } from "@/components/card-icon" -import { GradientIcon } from "@/components/gradient-icon" +import { CONTAINER, SECTION_LEAD } from "@/components/layout/page" +import { OrganizationDiagram } from "@/components/structure/organization-chart" import { Button } from "@/components/ui/button" -import { Card, CardTitle } from "@/components/ui/card" import { cn } from "@/lib/utils" -const teamLeads = [ - { title: "Team lead HR", icon: LuBookUser }, - { title: "Team lead IT", icon: LuCodeXml }, - { title: "Team lead Design & Marketing", icon: LuPencilRuler }, - { title: "Team lead International", icon: LuGlobe }, - { title: "Team lead Eventi & Partnership", icon: LuHandshake }, -] - -const nodeClassName = "[&_h3]:text-text-primary" -const mainNodeClassName = "[&_h3]:typo-title-large w-fit px-4 [&_h3]:whitespace-nowrap" - -type BranchPosition = "first" | "middle" | "last" | "only" - -/** Horizontal rail and arrow above one child of a branch: the rail runs from the first child's center to the last's. */ -function BranchArrow({ position }: { position: BranchPosition }) { - return ( -
- {position !== "only" && ( - - )} - -
- ) -} - -function branchPosition(index: number, count: number): BranchPosition { - if (count === 1) return "only" - if (index === 0) return "first" - return index === count - 1 ? "last" : "middle" -} - -function Stem() { - return -} - -function DesktopChart() { - return ( -
- - -
- {/* Rail from the center of the admin column (1/12) to the center of the Team growth columns (7/12) */} -
- - - -
-
- - - -
-
- - -
    - {teamLeads.map((lead, index) => ( -
  • - -
    - -
    - -
  • - ))} -
-
- - - - Membri dei team - - -
-
-
-
- ) -} - -function MobileChart() { - return ( -
- - -
-
- - - -
-
- - - -
-
- - -
    - {teamLeads.map((lead) => ( -
  • - - - - {lead.title} - - -
  • - ))} -
- - -
-
-
- ) -} - export function Organization() { return ( -
+

Come è organizzata PoliNetwork

-

+

Direttivo, team operativi, admin e soci. Guarda come funziona la macchina dall’interno.

- + {/* From xl the diagram rises beside the intro: its narrow top (the Direttivo node) fits next to the text */} +
) } diff --git a/src/components/storia/projects.tsx b/src/components/storia/projects.tsx index ee42bc09..aae29a9e 100644 --- a/src/components/storia/projects.tsx +++ b/src/components/storia/projects.tsx @@ -4,9 +4,10 @@ import { FiAirplay, FiArrowUpRight, FiBarChart, FiBook, FiCheck, FiHeadphones } import telegram from "@/assets/icons/telegram-gradient.svg" import projectsImage from "@/assets/images/storia/progetti.png" import { CardIcon } from "@/components/card-icon" -import { Glass } from "@/components/glass" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" import TextOnlyLayout from "@/components/layout/TextOnlySection" import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils" const projects = [ { @@ -35,15 +36,13 @@ const projects = [ export function HistoryProjects() { return ( -
+
-

- Le cose che abbiamo costruito. -

-

+

Le cose che abbiamo costruito.

+

Nel corso degli anni PoliNetwork ha sviluppato e mantenuto un ecosistema di{" "} strumenti gratuiti e open source pensati per rispondere ai bisogni concreti degli studenti. @@ -51,22 +50,23 @@ export function HistoryProjects() { } classNames={{ - section: "gap-12 px-0 md:px-0 xl:flex-row xl:items-center [&>div>h2]:typo-title-large", - textDiv: "min-w-0 xl:w-1/2 xl:items-start", - title: "text-blue-secondary xl:text-start", - descriptionDiv: "xl:text-start", + section: "gap-12 xl:flex-row xl:items-center [&>div>h2]:typo-title-large", + textDiv: "min-w-0 items-start xl:w-1/2", + title: "text-start text-blue-secondary", + descriptionDiv: "text-start", contentDiv: "min-w-0 xl:w-1/2", }} > Il sito PoliNetwork su computer e smartphone -

-
+ {/* Figma: the six tools on the left, the note about where they come from beside them, without a panel */} +
+
{projects.map((project) => ( ))}
- -
-

- Molti dei progetti di PoliNetwork sono nati da idee individuali: studenti che hanno - creato autonomamente uno strumento utile e che, nel tempo, sono entrati a far parte del network. -

-

Da un’idea di uno studente a uno strumento per migliaia di studenti.

-
- -
+
) diff --git a/src/components/storia/thanks.tsx b/src/components/storia/thanks.tsx index faf9b32e..e7ea6bfe 100644 --- a/src/components/storia/thanks.tsx +++ b/src/components/storia/thanks.tsx @@ -2,12 +2,13 @@ import { useState } from "react" import { FiSearch } from "react-icons/fi" -import { Glass } from "@/components/glass" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" import TextOnlyLayout from "@/components/layout/TextOnlySection" import TabsNavigation from "@/components/tabs" import { Tabs } from "@/components/tabs/tabs" import { TabsContent } from "@/components/tabs/tabs-content" import { Input } from "@/components/ui/input" +import { cn } from "@/lib/utils" import { contributors } from "./constants" const categories = ["Tutti", "Direttivi", "Admin", "Collaboratori"] @@ -21,28 +22,24 @@ export function Thanks() { title="Un grazie speciale" description={ <> -

- Centinaia di persone, una sola comunità. -

-

+

Centinaia di persone, una sola comunità.

+

PoliNetwork esiste grazie a tutte le persone che, in modi diversi, hanno dedicato tempo, energie e idee alla sua crescita. Alcune hanno coordinato un corso, altre sviluppato uno strumento, organizzato un evento, moderato un gruppo o semplicemente aiutato qualcuno nel momento giusto.

-

- Questa è la nostra memoria collettiva. -

+

Questa è la nostra memoria collettiva.

} classNames={{ - section: "mx-auto max-w-330 gap-12 px-6 md:px-6 xl:flex-row [&>div>h2]:typo-title-large", - textDiv: "xl:w-1/2 xl:items-start xl:text-start", - title: "text-blue-secondary xl:text-start", - descriptionDiv: "xl:text-start", + section: `${CONTAINER} gap-12 xl:flex-row xl:gap-16 [&>div>h2]:typo-title-large`, + textDiv: "items-start text-start xl:w-1/2", + title: "text-start text-blue-secondary", + descriptionDiv: "text-start", contentDiv: "min-w-0 xl:w-1/2", }} > - +
} @@ -80,7 +77,7 @@ export function Thanks() { ) })} - +
) } diff --git a/src/components/storia/timeline.tsx b/src/components/storia/timeline.tsx index 63b93047..872aeb08 100644 --- a/src/components/storia/timeline.tsx +++ b/src/components/storia/timeline.tsx @@ -1,50 +1,69 @@ +import Image from "next/image" import Link from "next/link" -import { - FiArrowDown, - FiCheckCircle, - FiClipboard, - FiClock, - FiLayers, - FiUser, - FiUserCheck, - FiUsers, -} from "react-icons/fi" +import type { ReactNode } from "react" +import { FiArrowDown, FiArrowRight, FiCheck, FiClipboard, FiClock, FiUser, FiUserCheck, FiUsers } from "react-icons/fi" import telegram from "@/assets/icons/telegram-gradient.svg" -import { CardCaption } from "@/components/card-caption" -import { CardIcon } from "@/components/card-icon" -import { Glass } from "@/components/glass" +import { GradientIcon, type GradientIconType } from "@/components/gradient-icon" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils" import { chapters, milestones } from "./constants" -const milestoneLinks = ["#capitolo-1", "#capitolo-1", "#capitolo-5", "#direttivi"] - -const milestoneIcons = [FiUser, telegram.src, FiUserCheck, FiUsers] +/** The small icon tile Figma uses for timeline nodes and diagram steps. */ +function IconTile({ icon, className }: { icon: GradientIconType | string; className?: string }) { + return ( + + {typeof icon === "string" ? ( + + ) : ( + + )} + + ) +} export function Timeline() { return ( -
-

+
+

Da un semplice gruppo a una rete costruita dagli studenti,{" "} per gli studenti.

-
    - {milestones.map((milestone, index) => ( -
  1. - + + {/* Nodes on a line: vertical below lg, horizontal from lg (Figma) */} +
      + {milestones.map((milestone) => ( +
    1. + + + {milestone.year} + +

      + {milestone.description} +

    2. ))}
    +
    -

    +

    Dieci anni di crescita, persone e progetti. La nostra storia{" "} continua, insieme a te.

    @@ -58,122 +77,124 @@ export function Timeline() { ) } -const chapterVisuals = [ -
    - {[ - { icon: telegram.src, title: "Il primo gruppo" }, - { icon: FiUser, title: "I primi admin e volontari" }, - { icon: FiUserCheck, title: "Test su Informatica" }, - { icon: FiUsers, title: "Tutti i corsi" }, - ].map((item) => ( - - ))} -
    , -
    - {[ - { icon: FiClipboard, title: "Regole interne" }, - { icon: FiUserCheck, title: "Comunicazioni selezionate" }, - { icon: FiUsers, title: "Una community indipendente" }, - ].map((item) => ( - - ))} -
    , -
    +function Arrow({ className }: { className?: string }) { + return +} + +/** A left-to-right sequence of steps, joined by arrows. */ +function Steps({ items }: { items: { label: string; icon?: GradientIconType | string; value?: string }[] }) { + return ( +
      + {items.map((item, index) => ( +
    1. + {index > 0 && } +
      + {item.icon && } + {item.value && {item.value}} + {item.label} +
      +
    2. + ))} +
    + ) +} + +/** Two columns read as "before → after". */ +function BeforeAfter({ before, after }: { before: { title: string; items: string[] }; after: typeof before }) { + return ( +
    + {[before, after].map((column, index) => ( +
    +

    {column.title}

    +
      + {column.items.map((item) => ( +
    • {item}
    • + ))} +
    +
    + ))} + +
    + ) +} + +const chapterVisuals: ReactNode[] = [ + , + , + , + , + , +
      {[ - { title: "5", caption: "persone", icon: FiUser }, - { title: "~25", caption: "rappresentanti", icon: FiUsers }, - { title: "4", caption: "composizioni", icon: FiLayers }, + { title: "APS-ETS", status: "Riconosciuto dallo Stato", done: true }, + { title: "RUNTS", status: "Iscritta come ETS", done: true }, + { title: "Albo PoliMi", status: "Ancora da raggiungere", done: false }, ].map((item) => ( - +
    • + {item.done ? ( + + ) : ( + + )} + {item.title} + {item.status} +
    • ))} -
    , -
    - - -
    , -
    - - -
    , -
    - - - -
    , + , ] export function Chapters() { return ( -
    -
      +
      +
        {chapters.map((chapter, index) => (
      1. -
        - - {String(index + 1).padStart(2, "0")} - -

        - {chapter.title} -

        -

        - {chapter.subtitle} -

        -

        - {chapter.description} -

        + + {String(index + 1).padStart(2, "0")} + +
        +

        {chapter.title}

        +

        {chapter.subtitle}

        +

        {chapter.description}

        +
        +
        + {chapterVisuals[index]}
        -
        {chapterVisuals[index]}
      2. ))}
      diff --git a/src/components/structure/association-bodies.tsx b/src/components/structure/association-bodies.tsx index 4249de00..a808421e 100644 --- a/src/components/structure/association-bodies.tsx +++ b/src/components/structure/association-bodies.tsx @@ -4,7 +4,6 @@ import { Accordion } from "@/components/accordion-association/accordion" import { AccordionContent } from "@/components/accordion-association/accordion-content" import { AccordionItem } from "@/components/accordion-association/accordion-item" import { AccordionTrigger } from "@/components/accordion-association/accordion-trigger" -import { GradientIcon } from "@/components/gradient-icon" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { associationBodies } from "./constants" @@ -26,9 +25,10 @@ export function AssociationBodies() {
      {associationBodies.map((body) => ( - - - {body.title} + + + {body.title} + {body.description} diff --git a/src/components/structure/board-gallery.tsx b/src/components/structure/board-gallery.tsx index d28bd40a..6193ba05 100644 --- a/src/components/structure/board-gallery.tsx +++ b/src/components/structure/board-gallery.tsx @@ -1,34 +1,22 @@ -import Image from "next/image" import { boardMembers } from "@/components/storia/constants" -import { Card, CardContent } from "@/components/ui/card" +import { BoardMemberCard } from "@/components/structure/board-member-card" import { Carousel, CarouselContent, CarouselDots, CarouselItem } from "@/components/ui/carousel" -import { cn } from "@/lib/utils" export function BoardGallery() { return ( - + {boardMembers.map((member) => ( - - - {member.name} -
      - -

      {member.name}

      - {member.role &&

      {member.role}

      } -
      - + + ))} diff --git a/src/components/structure/board-member-card.tsx b/src/components/structure/board-member-card.tsx new file mode 100644 index 00000000..3d96b274 --- /dev/null +++ b/src/components/structure/board-member-card.tsx @@ -0,0 +1,34 @@ +import Image from "next/image" +import type { boardMembers } from "@/components/storia/constants" +import { Card, CardContent } from "@/components/ui/card" +import { cn } from "@/lib/utils" + +/** A Direttivo member's portrait, with name and role over a gradient. */ +export function BoardMemberCard({ + member, + sizes, + className, +}: { + member: (typeof boardMembers)[number] + sizes: string + className?: string +}) { + return ( + + {member.name} +
      + +

      {member.name}

      + {member.role &&

      {member.role}

      } +
      + + ) +} diff --git a/src/components/structure/constants.ts b/src/components/structure/constants.ts index 716ce349..9750ef89 100644 --- a/src/components/structure/constants.ts +++ b/src/components/structure/constants.ts @@ -1,42 +1,27 @@ import { FiBriefcase, FiCompass, FiFlag, FiUsers } from "react-icons/fi" -import { - LuBookUser, - LuCodeXml, - LuCrown, - LuGlobe, - LuHandshake, - LuPencilRuler, - LuScale, - LuShield, - LuUserCog, - LuUsersRound, -} from "react-icons/lu" +import { LuBookUser, LuCodeXml, LuGlobe, LuHandshake, LuPencilRuler, LuShield, LuUsersRound } from "react-icons/lu" export const associationBodies = [ { id: "assemblea", - icon: FiUsers, title: "Assemblea dei Soci", description: "È l’organo sovrano di PoliNetwork. Si riunisce almeno una volta l’anno per approvare il bilancio consuntivo e, ogni due anni, per eleggere il Consiglio Direttivo e il Collegio dei Probiviri. Approva eventuali modifiche allo Statuto. Ogni socio ha diritto di voto e può candidarsi direttamente a entrambi gli organi.", }, { id: "consiglio", - icon: LuCrown, title: "Consiglio Direttivo", description: "È l’organo esecutivo e di garanzia dell’associazione. Vigila sulle attività di PoliNetwork per preservare i valori dello Statuto, gestisce le finanze e i rapporti istituzionali con partner esterni, emana i regolamenti interni. Approva o respinge le linee d’azione proposte dai singoli team e le strategie delineate dal Team Growth, senza gestire direttamente le attività quotidiane dei team.", }, { id: "probiviri", - icon: LuScale, title: "Collegio dei Probiviri", description: "È l’organo di garanzia interna, indipendente dal Consiglio Direttivo. Interviene in caso di controversie tra membri o organi dell’associazione, contestazioni sulle elezioni, e vigila sul rispetto dello Statuto e dei regolamenti quando è in gioco l’imparzialità del Direttivo stesso. Viene eletto dall’Assemblea dei Soci con lo stesso mandato biennale del Direttivo, in modo indipendente da esso.", }, { id: "growth", - icon: LuUserCog, title: "Team Growth", description: "È il livello di coordinamento tra i cinque team operativi: riunisce i Capi Dipartimento e alcuni membri del Direttivo per allineare le priorità comuni, le nomine, il recruiting e le decisioni che coinvolgono più team contemporaneamente. Non è un team a sé e non si aprono candidature dirette: si entra in Team Growth diventando Capo Dipartimento di uno dei cinque team.", diff --git a/src/components/structure/legal-status.tsx b/src/components/structure/legal-status.tsx index 02df69a9..5987707f 100644 --- a/src/components/structure/legal-status.tsx +++ b/src/components/structure/legal-status.tsx @@ -1,15 +1,15 @@ import { FiArrowUpRight } from "react-icons/fi" import { Glass } from "@/components/glass" import { GradientIcon } from "@/components/gradient-icon" +import { SECTION_TITLE } from "@/components/layout/page" import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils" import { legalFacts, regulationsHref, statuteHref } from "./constants" export function LegalStatus() { return ( -

      - Forma giuridica -

      +

      Forma giuridica

      {legalFacts.map((fact) => (
      @@ -24,7 +24,7 @@ export function LegalStatus() { ))}
      -

      +

      Consulta Statuto e Regolamenti

      diff --git a/src/components/structure/organization-chart.tsx b/src/components/structure/organization-chart.tsx index 917b6a05..6a26b8af 100644 --- a/src/components/structure/organization-chart.tsx +++ b/src/components/structure/organization-chart.tsx @@ -1,42 +1,64 @@ -import Image from "next/image" -import { FiArrowDown, FiUsers } from "react-icons/fi" +import type { IconType } from "react-icons" +import { FiArrowDown, FiArrowLeft, FiUsers } from "react-icons/fi" import { LuCrown, LuShield, LuShieldCheck, LuUserCog, LuUsersRound } from "react-icons/lu" -import arrow from "@/assets/icons/structure/arrow.svg" -import branch from "@/assets/icons/structure/branch.svg" -import { CardIcon } from "@/components/card-icon" import { Glass } from "@/components/glass" import { GradientIcon } from "@/components/gradient-icon" -import { Card, CardTitle } from "@/components/ui/card" +import { CONTAINER } from "@/components/layout/page" import { cn } from "@/lib/utils" import { teamLeads } from "./constants" -function DownArrow() { +const teamNames = new Intl.ListFormat("it").format(teamLeads.map((lead) => lead.title.replace("Team lead ", ""))) +const CHART_LABEL = `Il Direttivo coordina i Capi admin e il Team growth. I Capi admin coordinano gli Admin. Il Team growth coordina i team lead ${teamNames}, con i rispettivi membri.` + +// Nodes are not links, so they have no hover state +const tileClassName = "rounded-rectangles border-white/50 bg-background-blur text-text-primary" + +/** `fill` stretches the node to its column instead of sizing it by its label. */ +function Node({ icon, label, fill = false }: { icon: IconType; label: string; fill?: boolean }) { return ( -
      - -
      + + + {label} + ) } -const nodeClassName = "[&_h3]:text-text-primary" -// The admin column is one sixth of the chart: a smaller title and padding keep "Capi admin" on one line -const adminNodeClassName = "[&_h3]:typo-headline-small [&>*]:px-4 [&_h3]:whitespace-nowrap" +function LeadNode({ icon, label }: { icon: IconType; label: string }) { + return ( + + + {label} + + ) +} + +function Arrow({ className }: { className?: string }) { + return +} /** Horizontal rail and arrow above one child of a branch: the rail runs from the first child's center to the last's. */ function BranchArrow({ position }: { position: "first" | "middle" | "last" | "only" }) { return ( -
      +
      {position !== "only" && ( )} - +
      ) } @@ -48,58 +70,45 @@ function branchPosition(index: number, count: number) { } function Stem() { - return + return } function DesktopChart() { return ( -
      - +
      + -
      - {/* Rail from the center of the admin column (1/12) to the center of the Team Growth columns (7/12) */} -
      - - - +
      + {/* Rail from the center of the admin column (1/12) to the center of the Team growth columns (7/12) */} +
      + + +
      -
      -
      - - - -
      +
      + + +
      -
      - +
      + -
        +
          {teamLeads.map((lead, index) => (
        • - +
        • ))}
        -
        - +
        + - - Membri Team - - + Membri Team +
      @@ -107,92 +116,94 @@ function DesktopChart() { ) } -export function OrganizationChart() { +/** Figma mobile "Sezione organigramma": two columns, team leads hanging off a vertical rail, Membri Team balancing the admin column. */ +function MobileChart() { return ( -
      - -
      - -
      -
      - -
      -
      - -
      +
      + + +
      +
      + + +
      -
      -
      - - - +
      + + + +
      + + {/* Arrow from the team leads' rail, across the column gap */} + + + +
      -
      - - -
        - {teamLeads.map((lead) => ( -
      • - - - - {lead.title} - - -
      • - ))} -
      - - +
      +
      + + {/* Down from the node, then left to the rail the team leads hang from */} +
      + +
      +
        + {teamLeads.map((lead) => ( +
      • + + + + {lead.title} + +
      • + ))} +
      - -
      - -

      Soci

      -
      -
      -

      Soci

      -

      - Essere socio è una condizione trasversale, compatibile con qualsiasi ruolo all’interno di PoliNetwork. Ogni - persona può ricoprire più ruoli contemporaneamente. -

      -
      -
      +
      + ) +} + +/** The organisation diagram, shared by /about and /storia. */ +export function OrganizationDiagram({ id, className }: { id?: string; className?: string }) { + return ( + + ) +} + +export function OrganizationChart() { + return ( +
      +
      + + + +
      +
      +

      + + Soci +

      + + Ruolo trasversale + +
      +

      + Essere socio è una condizione trasversale, compatibile con qualsiasi ruolo all’interno di PoliNetwork. + Ogni persona può ricoprire più ruoli contemporaneamente. +

      +
      +
      +
      ) } diff --git a/src/components/structure/participation.tsx b/src/components/structure/participation.tsx index e5331189..c8648b21 100644 --- a/src/components/structure/participation.tsx +++ b/src/components/structure/participation.tsx @@ -12,7 +12,10 @@ export function Participation() { - + {path.title} diff --git a/src/components/team/constants.ts b/src/components/team/constants.ts index 29f2622c..383c6ed5 100644 --- a/src/components/team/constants.ts +++ b/src/components/team/constants.ts @@ -21,10 +21,9 @@ export type Team = { title: string description: string roles: Pick[] - reverse?: boolean + /** Figma alternates the sections: text on the right for HR, title and text side by side for Events. */ } -// Copy and specialisms from the desktop Team frame (5546:10874). export const teams: Team[] = [ { id: "it", @@ -62,7 +61,6 @@ export const teams: Team[] = [ { title: "Community & Engagement", icon: FiAward }, { title: "Crescita del team", icon: FiCornerRightUp }, ], - reverse: true, }, { id: "events-partnership", diff --git a/src/components/team/team-section.tsx b/src/components/team/team-section.tsx index 3ccb9421..ff200180 100644 --- a/src/components/team/team-section.tsx +++ b/src/components/team/team-section.tsx @@ -1,49 +1,66 @@ +import Image from "next/image" import { FiUsers } from "react-icons/fi" -import { CardIcon } from "@/components/card-icon" +import { GradientIcon } from "@/components/gradient-icon" +import { CONTAINER, SECTION_LEAD, SECTION_TITLE } from "@/components/layout/page" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" import { applicationHref, type Team } from "./constants" -export function TeamSection({ team }: { team: Team }) { +/** The specialisms are not links: a plain list, not tiles. */ +function RoleList({ team }: { team: Team }) { + return ( +
        + {team.roles.map(({ title, icon }) => ( +
      • + {typeof icon === "string" ? ( + + ) : ( + + )} + {title} +
      • + ))} +
      + ) +} + +function JoinButton({ team }: { team: Team }) { + return ( + + ) +} + +/** `reverse` puts the specialisms on the left: the page alternates sides from one team to the next. */ +export function TeamSection({ team, reverse = false }: { team: Team; reverse?: boolean }) { + const title = ( +

      + {team.title} +

      + ) + const description =

      {team.description}

      + return (
      -
      -
      -

      - {team.title} -

      -

      {team.description}

      - +
      + {title} + {description} +
      +
      -
        - {team.roles.map((role) => ( -
      • - -
      • - ))} -
      +
      ) }