-
+
{title}
{image && }
diff --git a/src/components/Governance/Delegates/index.tsx b/src/components/Governance/Delegates/index.tsx
new file mode 100644
index 000000000..00216592f
--- /dev/null
+++ b/src/components/Governance/Delegates/index.tsx
@@ -0,0 +1,87 @@
+import { Avatar, Container, Grid, IconButton, SvgIcon, Typography } from '@mui/material'
+import React from 'react'
+import XIcon from '@/public/images/x-icon.svg'
+import DiscordIcon from '@/public/images/discord-icon.svg'
+import css from './styles.module.css'
+import type { BaseBlock } from '@/components/Home/types'
+import { shortenAddress } from '@/lib/shortenAddress'
+import HeaderCTA from '@/components/common/HeaderCTA'
+import layoutCss from '@/components/common/styles.module.css'
+import delegatesData from '@/content/delegates.json'
+import { getDelegateImage } from '@/lib/getDelegateImage'
+
+type Delegate =
+ | {
+ name: string
+ address: string
+ ens: string | null
+ image: string
+ reason: string
+ contribution: string
+ } & {
+ twitter?: string
+ discord?: string
+ }
+
+const DelegateCard = (props: Delegate) => (
+
+
+
+
+
+
+
+
+ {props.name}
+
+
+
+ {props.ens || shortenAddress(props.address)}
+
+
+
+
+
+ {props.reason}
+
+
+
+ {props.twitter && (
+
+
+
+ )}
+ {props.discord && (
+
+
+
+ )}
+
+
+)
+
+const Delegates = (props: BaseBlock & { footer: { text: string; highlight: string } }) => {
+ return (
+
+
+
+
+ {delegatesData.map((item, index) => (
+
+
+
+ ))}
+
+
+
+
+
+ {props.footer.text}
+ {props.footer.highlight}
+
+
+
+ )
+}
+
+export default Delegates
diff --git a/src/components/Governance/Delegates/styles.module.css b/src/components/Governance/Delegates/styles.module.css
new file mode 100644
index 000000000..073f230cd
--- /dev/null
+++ b/src/components/Governance/Delegates/styles.module.css
@@ -0,0 +1,110 @@
+.card {
+ border: 1px solid var(--mui-palette-border-light);
+ border-radius: 12px;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ position: relative;
+ transition: 0.3s;
+ height: 206px;
+}
+
+.cardHeader {
+ display: flex;
+ gap: 16px;
+ margin-bottom: 16px;
+}
+
+.avatar {
+ width: 48px;
+ height: 48px;
+ margin: auto;
+}
+
+.name {
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+}
+
+.description {
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+}
+
+.socials {
+ position: absolute;
+ bottom: 24px;
+ left: 24px;
+ display: flex;
+ gap: 16px;
+}
+
+.socials a {
+ padding: 0;
+}
+
+.socials svg {
+ width: 24px;
+ height: 24px;
+}
+
+.gradient {
+ position: absolute;
+ height: 15%;
+ bottom: 0;
+ left: 16px;
+ right: 0px;
+ background: linear-gradient(180deg, rgba(18, 19, 18, 0) 0%, var(--mui-palette-background-default) 100%);
+ z-index: 1;
+}
+
+.footer {
+ margin: 0 auto;
+}
+
+/* Footer highlight */
+.footer :global span {
+ color: var(--mui-palette-text-primary);
+ text-decoration: underline;
+ text-underline-offset: 8px;
+}
+
+.cardGrid :global(.MuiGrid-item):nth-of-type(9) .socials {
+ display: none;
+}
+
+@media (min-width: 600px) {
+ .card {
+ height: 228px;
+ }
+}
+
+@media (min-width: 900px) {
+ .gradient {
+ left: 30px;
+ height: 33%;
+ }
+
+ .cardGrid :global(.MuiGrid-item):nth-of-type(n + 7) .socials {
+ display: none;
+ }
+
+ .footer {
+ margin: 68px auto 0;
+ }
+
+ .socials {
+ margin-top: 24px;
+ }
+}
+
+@media (min-width: 1630px) {
+ .gradient {
+ left: 50px;
+ }
+}
diff --git a/src/components/Governance/Intro/index.tsx b/src/components/Governance/Intro/index.tsx
new file mode 100644
index 000000000..96cb7e47f
--- /dev/null
+++ b/src/components/Governance/Intro/index.tsx
@@ -0,0 +1,27 @@
+import { Container, Divider, Grid, Typography } from '@mui/material'
+import type { BaseBlock } from '@/components/Home/types'
+import css from './styles.module.css'
+import ButtonsWrapper from '@/components/common/ButtonsWrapper'
+
+const Intro = ({ title, buttons, image }: BaseBlock) => {
+ return (
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Intro
diff --git a/src/components/Governance/Intro/styles.module.css b/src/components/Governance/Intro/styles.module.css
new file mode 100644
index 000000000..c24d93562
--- /dev/null
+++ b/src/components/Governance/Intro/styles.module.css
@@ -0,0 +1,62 @@
+.container {
+ margin-top: 0px;
+ margin-bottom: 75px;
+ justify-content: space-between;
+ flex-direction: column-reverse;
+}
+
+.imageContainer {
+ position: relative;
+ overflow: hidden;
+ height: 400px;
+}
+
+.imageWrapper {
+ margin-top: 40px;
+ transform: scale(1.8);
+}
+
+@media (min-width: 600px) {
+ .container {
+ margin-top: 100px;
+ margin-bottom: 180px;
+ }
+
+ .imageWrapper {
+ margin-top: 0;
+ }
+}
+
+@media (min-width: 900px) {
+ .container {
+ flex-direction: row;
+ }
+
+ .imageContainer {
+ height: 500px;
+ }
+
+ .imageWrapper {
+ transform: scale(1);
+ position: absolute;
+ z-index: -1;
+ width: 270%;
+ left: -85%;
+ }
+}
+
+@media (min-width: 1200px) {
+ .imageWrapper {
+ width: 240%;
+ left: -70%;
+ top: -10%;
+ }
+}
+
+@media (min-width: 1630px) {
+ .imageWrapper {
+ width: 180%;
+ left: -50%;
+ top: -20%;
+ }
+}
diff --git a/src/components/Governance/OurMission/index.tsx b/src/components/Governance/OurMission/index.tsx
new file mode 100644
index 000000000..f4e6c8633
--- /dev/null
+++ b/src/components/Governance/OurMission/index.tsx
@@ -0,0 +1,45 @@
+import type { ReactNode } from 'react'
+import type { BaseBlock } from '@/components/Home/types'
+import HeaderCTA from '@/components/common/HeaderCTA'
+import { Container, Grid, Typography } from '@mui/material'
+import layoutCss from '@/components/common/styles.module.css'
+import css from './styles.module.css'
+
+const GridItemBigTitle = ({ title, text, children }: Partial & { children?: ReactNode }) => {
+ return (
+
+
+
+ {title}
+
+ {text}
+
+ {children ? {children}
: null}
+
+ )
+}
+
+const OurMission = (props: BaseBlock) => {
+ return (
+
+
+
+
+ {props.items?.map((item, index) => (
+
+ {item.image ? : null}
+ {item.link ? (
+
+
+ {item.link.title}
+
+
+ ) : null}
+
+ ))}
+
+
+ )
+}
+
+export default OurMission
diff --git a/src/components/Governance/OurMission/styles.module.css b/src/components/Governance/OurMission/styles.module.css
new file mode 100644
index 000000000..dc77ea58f
--- /dev/null
+++ b/src/components/Governance/OurMission/styles.module.css
@@ -0,0 +1,53 @@
+.gradientEmphasis b {
+ background: linear-gradient(90deg, #00c9ff 0%, #92fe9d 100%);
+ -webkit-background-clip: text;
+ color: transparent;
+}
+
+.card {
+ border: 1px solid var(--mui-palette-border-light);
+ transition: 0.3s;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ position: relative;
+}
+
+.card:first-of-type {
+ border-top-left-radius: 12px;
+ border-top-right-radius: 12px;
+}
+
+.card:last-of-type {
+ border-bottom-left-radius: 12px;
+ border-bottom-right-radius: 12px;
+}
+
+.childrenWrapper {
+ margin-top: 40px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.tagline {
+ color: var(--mui-palette-text-primary);
+ text-decoration: underline;
+ text-underline-offset: 8px;
+}
+
+@media (min-width: 600px) {
+ .card:first-of-type {
+ border-top-right-radius: 0px;
+ border-top-left-radius: 12px;
+ border-bottom-left-radius: 12px;
+ margin-right: -1px;
+ }
+
+ .card:last-of-type {
+ border-bottom-left-radius: 0px;
+ border-top-right-radius: 12px;
+ border-bottom-right-radius: 12px;
+ }
+}
diff --git a/src/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement.tsx b/src/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement.tsx
new file mode 100644
index 000000000..46a7e22ad
--- /dev/null
+++ b/src/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement.tsx
@@ -0,0 +1,26 @@
+import Image from 'next/image'
+import FrameImage from '@/public/images/Governance/Parallaxes/DaoStats/background.svg'
+import ProposalsImage from '@/public/images/Governance/Parallaxes/DaoStats/proposals.png'
+import DelegatesImage from '@/public/images/Governance/Parallaxes/DaoStats/delegates.png'
+import DelegatorsImage from '@/public/images/Governance/Parallaxes/DaoStats/delegators.png'
+import ParallaxWrapper from '@/components/common/ParallaxWrapper'
+import css from './styles.module.css'
+
+const ParallaxDaoStatsElement = () => {
+ return (
+
+ )
+}
+
+export default ParallaxDaoStatsElement
diff --git a/src/components/Governance/ParallaxDaoStats/index.tsx b/src/components/Governance/ParallaxDaoStats/index.tsx
new file mode 100644
index 000000000..ea189a2de
--- /dev/null
+++ b/src/components/Governance/ParallaxDaoStats/index.tsx
@@ -0,0 +1,12 @@
+import ParallaxDaoStatsElement from '@/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement'
+import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText'
+
+const ParallaxDaoStats = (props: ParallaxTextProps) => {
+ return (
+
+
+
+ )
+}
+
+export default ParallaxDaoStats
diff --git a/src/components/Governance/ParallaxDaoStats/styles.module.css b/src/components/Governance/ParallaxDaoStats/styles.module.css
new file mode 100644
index 000000000..7201cb00b
--- /dev/null
+++ b/src/components/Governance/ParallaxDaoStats/styles.module.css
@@ -0,0 +1,60 @@
+.parallaxWrapper {
+ position: relative;
+ text-align: center;
+ margin: 0 auto;
+ width: 342px;
+}
+
+.baseImage {
+ width: 100%;
+ height: 290px;
+}
+
+.proposals {
+ position: absolute;
+ bottom: 242px;
+ left: 60px;
+ width: 144px;
+}
+
+.delegates {
+ position: absolute;
+ bottom: 150px;
+ left: 190px;
+ width: 162px;
+}
+
+.delegators {
+ position: absolute;
+ bottom: 0px;
+ left: 126px;
+ width: 162px;
+}
+
+@media (min-width: 600px) {
+ .parallaxWrapper {
+ width: 570px;
+ }
+
+ .baseImage {
+ height: 404px;
+ }
+
+ .proposals {
+ bottom: 333px;
+ left: 75px;
+ width: 226px;
+ }
+
+ .delegates {
+ bottom: 207px;
+ left: 275px;
+ width: 227px;
+ }
+
+ .delegators {
+ bottom: 27px;
+ left: 160px;
+ width: 226px;
+ }
+}
diff --git a/src/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement.tsx b/src/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement.tsx
new file mode 100644
index 000000000..de55d6f0e
--- /dev/null
+++ b/src/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement.tsx
@@ -0,0 +1,34 @@
+import Image from 'next/image'
+import FrameImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/background.svg'
+import LeftLineImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/leftLine.svg'
+import RightLineImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/rightLine.svg'
+import StepZeroImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/0.png'
+import StepOneImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/1.png'
+import StepTwoImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/2.png'
+import ParallaxWrapper from '@/components/common/ParallaxWrapper'
+import css from './styles.module.css'
+
+const ParallaxGovernanceProcessElement = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ParallaxGovernanceProcessElement
diff --git a/src/components/Governance/ParallaxGovernanceProcess/index.tsx b/src/components/Governance/ParallaxGovernanceProcess/index.tsx
new file mode 100644
index 000000000..549c59a4b
--- /dev/null
+++ b/src/components/Governance/ParallaxGovernanceProcess/index.tsx
@@ -0,0 +1,12 @@
+import ParallaxGovernanceProcessElement from '@/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement'
+import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText'
+
+const ParallaxGovernanceProcess = (props: ParallaxTextProps) => {
+ return (
+
+
+
+ )
+}
+
+export default ParallaxGovernanceProcess
diff --git a/src/components/Governance/ParallaxGovernanceProcess/styles.module.css b/src/components/Governance/ParallaxGovernanceProcess/styles.module.css
new file mode 100644
index 000000000..6d265602f
--- /dev/null
+++ b/src/components/Governance/ParallaxGovernanceProcess/styles.module.css
@@ -0,0 +1,88 @@
+.parallaxWrapper {
+ position: relative;
+ text-align: center;
+ margin: 0 auto;
+ width: 342px;
+}
+
+.baseImage {
+ width: 100%;
+ height: 240px;
+}
+
+.leftLine {
+ position: absolute;
+ width: 77px;
+ bottom: 83px;
+ left: 77px;
+}
+
+.rightLine {
+ position: absolute;
+ width: 92px;
+ bottom: 60px;
+ left: 170px;
+}
+
+.stepZero {
+ position: absolute;
+ bottom: 149px;
+ left: 36px;
+ width: 41px;
+}
+
+.stepOne {
+ position: absolute;
+ bottom: 72px;
+ left: 130px;
+ width: 41px;
+}
+
+.stepTwo {
+ position: absolute;
+ bottom: 164px;
+ left: 262px;
+ width: 41px;
+}
+
+@media (min-width: 600px) {
+ .parallaxWrapper {
+ width: 570px;
+ }
+
+ .baseImage {
+ height: 404px;
+ }
+
+ .leftLine {
+ position: absolute;
+ width: 128px;
+ bottom: 180px;
+ left: 104px;
+ }
+
+ .rightLine {
+ position: absolute;
+ width: 154px;
+ bottom: 150px;
+ left: 270px;
+ }
+
+ .stepZero {
+ bottom: 253px;
+ left: 36px;
+ width: 68px;
+ }
+
+ .stepOne {
+ bottom: 114px;
+ left: 205px;
+ width: 68px;
+ }
+
+ .stepTwo {
+ bottom: 276px;
+ left: 423px;
+ width: 68px;
+ }
+}
diff --git a/src/components/Governance/Proposals/Proposals.tsx b/src/components/Governance/Proposals/Proposals.tsx
new file mode 100644
index 000000000..23cd0cbea
--- /dev/null
+++ b/src/components/Governance/Proposals/Proposals.tsx
@@ -0,0 +1,76 @@
+import { Chip, Container, Stack, Typography } from '@mui/material'
+import type { BaseBlock } from '@/components/Home/types'
+import HeaderCTA from '@/components/common/HeaderCTA'
+import { parseSnapshotTitle } from '@/lib/parseSnapshotTitle'
+import { useSafeSnapshot } from '@/hooks/useSafeSnapshot'
+import layoutCss from '@/components/common/styles.module.css'
+import css from './styles.module.css'
+
+const PROPOSAL_LINK_BASE_URL = 'https://snapshot.org/#/safe.eth/proposal/'
+
+type SnapshotProposal = {
+ id: string
+ title: string
+ state: 'active' | 'closed' | 'pending'
+ author?: string
+}
+
+const colors: Record = {
+ pending: '#FF8061',
+ active: '#00B460',
+ closed: '#52BFDC',
+}
+
+const ProposalRow = ({ title, state, href }: Omit & { href: string }) => {
+ const [sepNumber, description] = parseSnapshotTitle(title)
+
+ return (
+
+
+ {`SEP ${sepNumber}`}
+ {description}
+
+
+ {state}
+
+ }
+ className={css.proposalChip}
+ variant="outlined"
+ sx={{
+ borderColor: colors[state],
+ backgroundColor: colors[state],
+ }}
+ />
+
+ )
+}
+
+const Proposals = (props: BaseBlock) => {
+ const { data: proposals } = useSafeSnapshot()
+
+ return (
+
+
+
+
+ {proposals?.map((proposal) => (
+
+ ))}
+
+
+ )
+}
+
+export default Proposals
diff --git a/src/components/Governance/Proposals/index.tsx b/src/components/Governance/Proposals/index.tsx
new file mode 100644
index 000000000..ea0ea2397
--- /dev/null
+++ b/src/components/Governance/Proposals/index.tsx
@@ -0,0 +1,5 @@
+import dynamic from 'next/dynamic'
+
+const Proposals = dynamic(() => import('./Proposals'))
+
+export default Proposals
diff --git a/src/components/Governance/Proposals/styles.module.css b/src/components/Governance/Proposals/styles.module.css
new file mode 100644
index 000000000..cdc885c74
--- /dev/null
+++ b/src/components/Governance/Proposals/styles.module.css
@@ -0,0 +1,27 @@
+.proposalRow {
+ border: 1px solid var(--mui-palette-border-light);
+ border-radius: 12px;
+ padding: 24px;
+
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ transition: 0.3s;
+}
+
+.proposalRow:hover {
+ border-color: var(--mui-palette-primary-main);
+}
+
+.proposalChip {
+ margin: auto 0;
+ width: fit-content;
+}
+
+@media (min-width: 600px) {
+ .proposalRow {
+ flex-direction: row;
+ justify-content: space-between;
+ }
+}
diff --git a/src/components/Governance/ResourceHub/index.tsx b/src/components/Governance/ResourceHub/index.tsx
new file mode 100644
index 000000000..4b6fc6c41
--- /dev/null
+++ b/src/components/Governance/ResourceHub/index.tsx
@@ -0,0 +1,22 @@
+import type { BaseBlock } from '@/components/Home/types'
+import HeaderCTA from '@/components/common/HeaderCTA'
+import { GridItem } from '@/components/common/UspBlock'
+import { Container, Grid } from '@mui/material'
+import css from './styles.module.css'
+import layoutCss from '@/components/common/styles.module.css'
+
+const ResourceHub = ({ items, ...props }: BaseBlock) => {
+ return (
+
+
+
+
+ {items?.map((item, index) => (
+
+ ))}
+
+
+ )
+}
+
+export default ResourceHub
diff --git a/src/components/Governance/ResourceHub/styles.module.css b/src/components/Governance/ResourceHub/styles.module.css
new file mode 100644
index 000000000..bb38c709d
--- /dev/null
+++ b/src/components/Governance/ResourceHub/styles.module.css
@@ -0,0 +1,5 @@
+.gridContainer {
+ border: 1px solid var(--mui-palette-border-light);
+ border-radius: 12px;
+ overflow: hidden;
+}
diff --git a/src/components/Governance/index.tsx b/src/components/Governance/index.tsx
new file mode 100644
index 000000000..2f7a60696
--- /dev/null
+++ b/src/components/Governance/index.tsx
@@ -0,0 +1,6 @@
+import PageContent from '@/components/common/PageContent'
+import governanceContent from '@/content/governance.json'
+
+export const Governance = () => {
+ return
+}
diff --git a/src/components/Home/ContractParallax/index.tsx b/src/components/Home/ContractParallax/index.tsx
index aae41dea8..8ab33c11f 100644
--- a/src/components/Home/ContractParallax/index.tsx
+++ b/src/components/Home/ContractParallax/index.tsx
@@ -1,6 +1,6 @@
import type { ReactElement } from 'react'
import ContractParallaxElement from './ContractParallaxElement'
-import ParallaxText, { type ParallaxTextProps } from '@/components/Wallet/ParallaxText'
+import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText'
import layoutCss from '@/components/common/styles.module.css'
const ContractParallax = (props: ParallaxTextProps): ReactElement => {
diff --git a/src/components/Home/TitleLogos/index.tsx b/src/components/Home/TitleLogos/index.tsx
deleted file mode 100644
index 9e02a8d07..000000000
--- a/src/components/Home/TitleLogos/index.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Container, Grid, Typography } from '@mui/material'
-import type { ReactElement } from 'react'
-
-import layoutCss from '@/components/common/styles.module.css'
-import type { BaseBlock } from '@/components/Home/types'
-
-const TitleLogos = ({ title, items }: BaseBlock): ReactElement => {
- return (
-
-
-
- {title}
-
-
-
- {items &&
- items.map((item, index) => (
-
-
-
- ))}
-
-
-
-
- )
-}
-
-export default TitleLogos
diff --git a/src/components/Home/TitleTextLinkedCardGrid/index.tsx b/src/components/Home/TitleTextLinkedCardGrid/index.tsx
index 5084fc60e..54c90b76f 100644
--- a/src/components/Home/TitleTextLinkedCardGrid/index.tsx
+++ b/src/components/Home/TitleTextLinkedCardGrid/index.tsx
@@ -38,9 +38,7 @@ const TitleTextLinkedCardGrid = ({ title, text, caption, items }: BaseBlock): Re
{item.caption}
-
- {item.title}
-
+
{item.title}
diff --git a/src/components/Wallet/Intro/index.tsx b/src/components/Wallet/Intro/index.tsx
index fd4b1d0b1..5982af59a 100644
--- a/src/components/Wallet/Intro/index.tsx
+++ b/src/components/Wallet/Intro/index.tsx
@@ -7,7 +7,7 @@ import type { BaseBlock } from '@/components/Home/types'
import css from './styles.module.css'
import HeaderParticles from '@/public/images/header_particles.svg'
-const Intro = ({ image, title, buttons }: BaseBlock & { gif: BaseBlock['image'] }): ReactElement => {
+const Intro = ({ image, title, buttons }: BaseBlock): ReactElement => {
return (