mirror of
https://github.com/logos-co/logos-web.git
synced 2026-08-27 12:11:14 +00:00
Increase small text to 12px (#114)
* fix: increase small text to 12px * fix: use initiative CTA label * refactor: use Tailwind text-xs utility * fix: make homepage banner dismissible * fix: update Discord links and banner text * fix: refresh Discord invite links
This commit is contained in:
+2
-2
@@ -18,7 +18,7 @@ const createListingDoc = (
|
||||
description: 'Funded work.',
|
||||
breadcrumbLabel: 'RFPs',
|
||||
submitCtaLabel: 'Submit',
|
||||
submitCtaHref: 'https://discord.gg/logosnetwork',
|
||||
submitCtaHref: 'https://discord.gg/Ykv4eZyHUJ',
|
||||
submitCtaExternal: true,
|
||||
defaultView: 'grid',
|
||||
pageSize: 12,
|
||||
@@ -26,7 +26,7 @@ const createListingDoc = (
|
||||
nextLabel: 'Next',
|
||||
bottomCtaTitle: 'Have a build idea?',
|
||||
bottomCtaLabel: 'Submit',
|
||||
bottomCtaHref: 'https://discord.gg/logosnetwork',
|
||||
bottomCtaHref: 'https://discord.gg/Ykv4eZyHUJ',
|
||||
bottomCtaExternal: true,
|
||||
...overrides,
|
||||
})
|
||||
|
||||
@@ -51,7 +51,7 @@ export function BookAction({
|
||||
className="inline-flex min-w-[150px] items-center justify-between gap-4 rounded-xl bg-brand-off-white px-4 py-3 text-brand-dark-green transition-colors hover:bg-gray-01 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-off-white"
|
||||
>
|
||||
<span className="flex flex-col">
|
||||
<span className="font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
<span className="font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{label}
|
||||
</span>
|
||||
<span className="font-mono text-[9px] leading-[1.35] opacity-60">
|
||||
|
||||
@@ -28,12 +28,8 @@ function ColorSwatch({
|
||||
<div
|
||||
className={`absolute right-[9px] bottom-[9px] left-[9px] overflow-hidden ${textColor === 'white' ? 'text-white' : 'text-black'}`}
|
||||
>
|
||||
<p className="truncate text-[10px] leading-[1.2] md:text-[16px]">
|
||||
{name}
|
||||
</p>
|
||||
<p className="truncate text-[10px] leading-[1.2] md:text-[16px]">
|
||||
{hex}
|
||||
</p>
|
||||
<p className="truncate text-xs leading-[1.2] md:text-[16px]">{name}</p>
|
||||
<p className="truncate text-xs leading-[1.2] md:text-[16px]">{hex}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -52,7 +52,7 @@ export function Buttons() {
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 items-start gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
Primary
|
||||
</p>
|
||||
<Button variant="primary" href={EXTERNAL_URLS.docs}>
|
||||
@@ -60,7 +60,7 @@ export function Buttons() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
Secondary
|
||||
</p>
|
||||
<Button variant="secondary" href={EXTERNAL_URLS.docs}>
|
||||
@@ -68,7 +68,7 @@ export function Buttons() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
Tertiary
|
||||
</p>
|
||||
<Button variant="tertiary" href={EXTERNAL_URLS.docs}>
|
||||
@@ -76,7 +76,7 @@ export function Buttons() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
Link
|
||||
</p>
|
||||
<Button variant="link" href={EXTERNAL_URLS.docs}>
|
||||
@@ -248,7 +248,7 @@ export function ViewToggles() {
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 items-start gap-8 sm:grid-cols-2">
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
Grid active (default on /rfps)
|
||||
</p>
|
||||
<ViewToggle
|
||||
@@ -258,7 +258,7 @@ export function ViewToggles() {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
List active (default on /ideas)
|
||||
</p>
|
||||
<ViewToggle
|
||||
@@ -282,7 +282,7 @@ export function Paginations() {
|
||||
</h2>
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
3 pages · current 1
|
||||
</p>
|
||||
<Pagination
|
||||
@@ -292,7 +292,7 @@ export function Paginations() {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
5 pages · current 3
|
||||
</p>
|
||||
<Pagination
|
||||
@@ -302,7 +302,7 @@ export function Paginations() {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase opacity-50">
|
||||
10 pages · current 5 · ellipsis collapse
|
||||
</p>
|
||||
<Pagination
|
||||
|
||||
@@ -211,7 +211,7 @@ function MetaBlock({
|
||||
|
||||
function SameAsDesktopTag() {
|
||||
return (
|
||||
<span className="inline-flex h-[24px] items-center justify-center rounded-full bg-brand-yellow px-2.5 text-[10px] font-medium leading-[1.3] text-black uppercase">
|
||||
<span className="inline-flex h-[24px] items-center justify-center rounded-full bg-brand-yellow px-2.5 text-xs font-medium leading-[1.3] text-black uppercase">
|
||||
Same as desktop
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -24,10 +24,7 @@ export function Community({
|
||||
return (
|
||||
<section className="border-t border-brand-dark-green/10 pt-6 pb-25">
|
||||
<ContentWidth className="flex w-full flex-col gap-10">
|
||||
<SectionHeading
|
||||
number={data.number}
|
||||
heading={data.heading}
|
||||
/>
|
||||
<SectionHeading number={data.number} heading={data.heading} />
|
||||
<div className="flex flex-col">
|
||||
{communityLinks.map((item, index) => (
|
||||
<a
|
||||
@@ -43,11 +40,9 @@ export function Community({
|
||||
<span className="font-sans font-medium">
|
||||
{String(index + 1).padStart(2, '0')}
|
||||
</span>
|
||||
<span className="font-display">
|
||||
{data.items[item.key]}
|
||||
</span>
|
||||
<span className="font-display">{data.items[item.key]}</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1 border-b border-brand-dark-green/50 pb-0.5 font-mono text-[10px] leading-[1.35] font-semibold whitespace-nowrap uppercase">
|
||||
<span className="inline-flex items-center gap-1 border-b border-brand-dark-green/50 pb-0.5 font-mono text-xs leading-[1.35] font-semibold whitespace-nowrap uppercase">
|
||||
{data.cta}
|
||||
<ButtonArrowIcon />
|
||||
</span>
|
||||
|
||||
@@ -18,7 +18,7 @@ export function Hero({ heading, intro }: HeroProps) {
|
||||
{heading}
|
||||
</h1>
|
||||
</LambdaLockup>
|
||||
<p className="absolute top-[126px] left-3 max-w-[342px] whitespace-pre-line font-mono text-[10px] leading-[1.3] md:top-[90px] md:left-[calc(50%+6px)] md:max-w-[345px]">
|
||||
<p className="absolute top-[126px] left-3 max-w-[342px] whitespace-pre-line font-mono text-xs leading-[1.3] md:top-[90px] md:left-[calc(50%+6px)] md:max-w-[345px]">
|
||||
{intro}
|
||||
</p>
|
||||
</ContentWidth>
|
||||
|
||||
@@ -199,7 +199,7 @@ describe('LambdaPrizePage – support section driven from data', () => {
|
||||
}),
|
||||
expect.objectContaining({
|
||||
label: 'Connect with Logos',
|
||||
href: 'https://discord.gg/logosnetwork',
|
||||
href: 'https://discord.gg/Ykv4eZyHUJ',
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
@@ -42,7 +42,7 @@ export function AboutProgramme({
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mx-auto w-full max-w-[345px] text-center font-sans text-[12px] leading-[1.2] font-medium text-brand-dark-green">
|
||||
<p className="mx-auto w-full max-w-[345px] text-center font-sans text-xs leading-[1.2] font-medium text-brand-dark-green">
|
||||
{copy.body}
|
||||
</p>
|
||||
<div className="flex flex-col gap-3 text-brand-dark-green">
|
||||
|
||||
@@ -21,7 +21,7 @@ export function TertiaryCta({
|
||||
<Link
|
||||
href={href}
|
||||
{...(isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
|
||||
className={`inline-flex cursor-pointer items-center gap-1 border-b pb-0.5 font-mono text-[10px] font-semibold uppercase leading-[1.35] ${
|
||||
className={`inline-flex cursor-pointer items-center gap-1 border-b pb-0.5 font-mono text-xs font-semibold uppercase leading-[1.35] ${
|
||||
tone === 'light'
|
||||
? 'border-brand-off-white/50 text-brand-off-white'
|
||||
: 'border-brand-dark-green/50 text-brand-dark-green'
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { LambdaPrizePageCopy } from './types'
|
||||
|
||||
function SupportLabel({ children }: { children: string }) {
|
||||
return (
|
||||
<span className="inline-flex items-center border-b border-brand-dark-green/20 pb-0.5 font-mono text-[10px] leading-[1.35] font-semibold text-brand-dark-green/70 uppercase">
|
||||
<span className="inline-flex items-center border-b border-brand-dark-green/20 pb-0.5 font-mono text-xs leading-[1.35] font-semibold text-brand-dark-green/70 uppercase">
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -172,7 +172,7 @@ function EventPill({
|
||||
interactive?: boolean
|
||||
}) {
|
||||
const baseClassName =
|
||||
'block rounded-lg border border-brand-dark-green/50 p-2 font-mono text-[10px] font-medium uppercase leading-[1.3]'
|
||||
'block rounded-lg border border-brand-dark-green/50 p-2 font-mono text-xs font-medium uppercase leading-[1.3]'
|
||||
const content = <span className="line-clamp-2">{event.calendarTitle}</span>
|
||||
|
||||
return interactive && event.link ? (
|
||||
@@ -231,7 +231,7 @@ function MonthSelect({
|
||||
aria-label={label}
|
||||
value={value}
|
||||
onChange={(event) => onChange(Number(event.target.value))}
|
||||
className="h-full w-full cursor-pointer appearance-none rounded-full border border-transparent bg-brand-off-white py-0 pl-6 pr-10 font-mono text-[10px] font-semibold uppercase leading-[1.35] text-brand-dark-green"
|
||||
className="h-full w-full cursor-pointer appearance-none rounded-full border border-transparent bg-brand-off-white py-0 pl-6 pr-10 font-mono text-xs font-semibold uppercase leading-[1.35] text-brand-dark-green"
|
||||
>
|
||||
{MONTHS.map((month, index) => (
|
||||
<option key={month} value={index}>
|
||||
@@ -263,7 +263,7 @@ function YearSelect({
|
||||
aria-label={label}
|
||||
value={value}
|
||||
onChange={(event) => onChange(Number(event.target.value))}
|
||||
className="h-full w-full cursor-pointer appearance-none rounded-full border border-transparent bg-brand-off-white py-0 pl-6 pr-10 font-mono text-[10px] font-semibold uppercase leading-[1.35] text-brand-dark-green"
|
||||
className="h-full w-full cursor-pointer appearance-none rounded-full border border-transparent bg-brand-off-white py-0 pl-6 pr-10 font-mono text-xs font-semibold uppercase leading-[1.35] text-brand-dark-green"
|
||||
>
|
||||
{years.map((year) => (
|
||||
<option key={year} value={year}>
|
||||
@@ -302,7 +302,7 @@ function NavButtons({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToday}
|
||||
className="flex h-[31px] w-[54px] cursor-pointer items-center justify-center rounded-full border border-brand-dark-green/30 bg-brand-off-white font-mono text-[10px] font-semibold uppercase leading-[1.35]"
|
||||
className="flex h-[31px] w-[54px] cursor-pointer items-center justify-center rounded-full border border-brand-dark-green/30 bg-brand-off-white font-mono text-xs font-semibold uppercase leading-[1.35]"
|
||||
>
|
||||
{todayLabel}
|
||||
</button>
|
||||
@@ -513,7 +513,7 @@ export function EventsCalendar({
|
||||
{WEEKDAYS.map((day) => (
|
||||
<span
|
||||
key={day}
|
||||
className="flex items-center justify-center font-mono text-[10px] font-semibold uppercase leading-[1.35]"
|
||||
className="flex items-center justify-center font-mono text-xs font-semibold uppercase leading-[1.35]"
|
||||
>
|
||||
{day}
|
||||
</span>
|
||||
|
||||
@@ -35,7 +35,7 @@ function Dot() {
|
||||
|
||||
function UnderlineLabel({ children }: { children: string }) {
|
||||
return (
|
||||
<span className="font-mono text-[10px] font-semibold uppercase leading-[1.35] text-brand-dark-green underline decoration-brand-dark-green/50 underline-offset-[3px]">
|
||||
<span className="font-mono text-xs font-semibold uppercase leading-[1.35] text-brand-dark-green underline decoration-brand-dark-green/50 underline-offset-[3px]">
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@ export default async function ManifestoPage({
|
||||
const data = findSection<ManifestoCopySection>(
|
||||
page.sections,
|
||||
'manifestoCopy',
|
||||
'manifesto.copy',
|
||||
'manifesto.copy'
|
||||
)
|
||||
|
||||
const moreLinks = data.more.map((label, index) => ({
|
||||
@@ -63,7 +63,7 @@ export default async function ManifestoPage({
|
||||
<span className="block">{data.headingLine2}</span>
|
||||
</span>
|
||||
</h1>
|
||||
<p className="font-mono-body mx-auto mt-6 w-[102px] text-center text-[10px] leading-[1.3] tracking-[0px] desktop:absolute desktop:top-[426px] desktop:left-1/2 desktop:mt-0 desktop:-translate-x-1/2">
|
||||
<p className="font-mono-body mx-auto mt-6 w-[102px] text-center text-xs leading-[1.3] tracking-[0px] desktop:absolute desktop:top-[426px] desktop:left-1/2 desktop:mt-0 desktop:-translate-x-1/2">
|
||||
{data.author.map((line) => (
|
||||
<span key={line} className="block">
|
||||
{line}
|
||||
@@ -118,7 +118,7 @@ export default async function ManifestoPage({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<aside className="font-mono-body mt-12 text-[10px] leading-[1.3] tracking-[0px] desktop:absolute desktop:top-[2995px] desktop:left-0 desktop:mt-0 desktop:h-[88px] desktop:w-[306px] desktop:text-left">
|
||||
<aside className="font-mono-body mt-12 text-xs leading-[1.3] tracking-[0px] desktop:absolute desktop:top-[2995px] desktop:left-0 desktop:mt-0 desktop:h-[88px] desktop:w-[306px] desktop:text-left">
|
||||
<h2 className="font-bold">{data.moreHeading}</h2>
|
||||
<div className="mt-3 space-y-3 desktop:mt-[12px] desktop:space-y-[12px]">
|
||||
{moreLinks.map((item) => (
|
||||
|
||||
@@ -91,7 +91,7 @@ export function BlogHero({
|
||||
{ href: '#podcasts', label: copy.navPodcasts },
|
||||
].map((item) => {
|
||||
const className =
|
||||
'inline-flex cursor-pointer items-center gap-1 font-mono text-[10px] font-semibold uppercase leading-[1.35] transition-opacity hover:opacity-70'
|
||||
'inline-flex cursor-pointer items-center gap-1 font-mono text-xs font-semibold uppercase leading-[1.35] transition-opacity hover:opacity-70'
|
||||
|
||||
return (
|
||||
<a key={item.href} href={item.href} className={className}>
|
||||
@@ -102,7 +102,7 @@ export function BlogHero({
|
||||
})}
|
||||
<a
|
||||
href={copy.navBroadcastHref}
|
||||
className="inline-flex cursor-pointer items-center gap-1 font-mono text-[10px] font-semibold uppercase leading-[1.35] transition-opacity hover:opacity-70"
|
||||
className="inline-flex cursor-pointer items-center gap-1 font-mono text-xs font-semibold uppercase leading-[1.35] transition-opacity hover:opacity-70"
|
||||
>
|
||||
<ArrowIcon direction="down" />
|
||||
{copy.navBroadcast}
|
||||
@@ -196,7 +196,7 @@ export function GallerySection({ articles }: { articles: BlogArticleRow[] }) {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-baseline justify-between gap-6">
|
||||
<p className="max-w-[170px] font-sans text-[12px] font-medium leading-[1.2] tracking-normal md:text-[14px]">
|
||||
<p className="max-w-[170px] font-sans text-xs font-medium leading-[1.2] tracking-normal md:text-[14px]">
|
||||
{article.title}
|
||||
</p>
|
||||
<div className="text-mono-s shrink-0 text-brand-dark-green md:text-right">
|
||||
@@ -299,7 +299,7 @@ export function BroadcastSection({
|
||||
<div className="absolute left-3 top-3 flex w-[calc(100%-24px)] flex-col gap-6 desktop:h-[380px] desktop:w-[453px] desktop:justify-between desktop:gap-0">
|
||||
<div className="flex items-center gap-25.5">
|
||||
<LogosMark size={6} className="shrink-0 text-brand-off-white" />
|
||||
<p className="font-mono text-[10px] font-medium uppercase leading-[1.3] text-brand-off-white">
|
||||
<p className="font-mono text-xs font-medium uppercase leading-[1.3] text-brand-off-white">
|
||||
{copy.media}
|
||||
</p>
|
||||
</div>
|
||||
@@ -308,7 +308,7 @@ export function BroadcastSection({
|
||||
<h2 className="w-[185px] font-sans text-[24px] leading-[1.1] tracking-[-0.01em] text-brand-off-white">
|
||||
{copy.broadcastHeading}
|
||||
</h2>
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-brand-off-white desktop:w-full">
|
||||
<p className="font-mono text-xs leading-[1.3] text-brand-off-white desktop:w-full">
|
||||
{copy.broadcastDescription}
|
||||
</p>
|
||||
</div>
|
||||
@@ -325,7 +325,7 @@ export function BroadcastSection({
|
||||
</div>
|
||||
|
||||
<div className="absolute left-3 right-3 bottom-3 top-[292px] overflow-hidden rounded-[100px] bg-accent-tan text-brand-dark-green transition-colors group-hover:bg-brand-yellow desktop:left-auto desktop:right-3 desktop:bottom-auto desktop:top-3 desktop:h-[382px] desktop:w-[702px]">
|
||||
<span className="absolute left-1/2 top-1/2 inline-flex -translate-x-1/2 -translate-y-1/2 items-center gap-1 font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
<span className="absolute left-1/2 top-1/2 inline-flex -translate-x-1/2 -translate-y-1/2 items-center gap-1 font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{copy.broadcastCta}
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
|
||||
@@ -59,7 +59,7 @@ export function TextLink({
|
||||
href={href}
|
||||
aria-label={label}
|
||||
className={cn(
|
||||
'cursor-pointer font-mono text-[10px] font-semibold leading-[1.35] uppercase underline underline-offset-[3px] transition-opacity hover:opacity-70',
|
||||
'cursor-pointer font-mono text-xs font-semibold leading-[1.35] uppercase underline underline-offset-[3px] transition-opacity hover:opacity-70',
|
||||
tone === 'light'
|
||||
? 'text-brand-off-white decoration-brand-off-white/50'
|
||||
: 'text-brand-dark-green decoration-brand-dark-green/50',
|
||||
@@ -73,7 +73,7 @@ export function TextLink({
|
||||
|
||||
export function UnderlineLabel({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<span className="font-mono text-[10px] font-semibold uppercase leading-[1.35] text-brand-dark-green underline decoration-brand-dark-green/50 underline-offset-[3px]">
|
||||
<span className="font-mono text-xs font-semibold uppercase leading-[1.35] text-brand-dark-green underline decoration-brand-dark-green/50 underline-offset-[3px]">
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
@@ -151,7 +151,7 @@ export function SectionCta({ href, label }: { href: string; label: string }) {
|
||||
const className =
|
||||
'flex h-[84px] w-full cursor-pointer items-center justify-center rounded-xl border border-brand-dark-green text-brand-dark-green transition-colors hover:bg-brand-yellow'
|
||||
const content = (
|
||||
<span className="inline-flex items-center gap-1 font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
<span className="inline-flex items-center gap-1 font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{label}
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
|
||||
@@ -63,7 +63,7 @@ function PodcastHero({
|
||||
<div className="absolute left-3 top-3 flex h-[268px] w-[calc(100%-24px)] max-w-[345px] min-w-0 flex-col justify-between text-brand-off-white xl:h-[380px] xl:w-[453px] xl:max-w-[453px]">
|
||||
<div className="flex items-center gap-25.5">
|
||||
<LogosMark size={6} className="shrink-0" />
|
||||
<span className="font-mono text-[10px] font-medium uppercase leading-[1.3]">
|
||||
<span className="font-mono text-xs font-medium uppercase leading-[1.3]">
|
||||
{copy.media}
|
||||
</span>
|
||||
</div>
|
||||
@@ -86,7 +86,7 @@ function PodcastHero({
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute left-3 top-[397px] flex h-[290px] w-[calc(100%-24px)] items-center justify-center rounded-[100px] bg-accent-tan text-brand-dark-green xl:hidden">
|
||||
<span className="inline-flex items-center gap-1 font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
<span className="inline-flex items-center gap-1 font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{copy.seeAllEpisodes}
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
|
||||
@@ -52,7 +52,7 @@ function HeroAnchorLink({
|
||||
className="inline-flex cursor-pointer items-center justify-center text-brand-dark-green"
|
||||
>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<span className="border-b border-brand-dark-green/50 pb-0.5 font-mono text-[10px] leading-[1.35] font-semibold whitespace-nowrap uppercase">
|
||||
<span className="border-b border-brand-dark-green/50 pb-0.5 font-mono text-xs leading-[1.35] font-semibold whitespace-nowrap uppercase">
|
||||
{children}
|
||||
</span>
|
||||
<ButtonArrowIcon />
|
||||
@@ -76,7 +76,7 @@ export default async function NodeProgramPage({
|
||||
const data = findSection<NodeProgrammeCopySection>(
|
||||
page.sections,
|
||||
'nodeProgrammeCopy',
|
||||
'nodeProgramme.copy',
|
||||
'nodeProgramme.copy'
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -31,7 +31,7 @@ function PodcastIntro({ copy }: { copy: PodcastIntroCopy }) {
|
||||
<span className="inline-flex size-3.75 shrink-0 rotate-180 items-center justify-center">
|
||||
<ButtonArrowIcon />
|
||||
</span>
|
||||
<span className="font-mono text-[10px] leading-[1.3] font-medium uppercase">
|
||||
<span className="font-mono text-xs leading-[1.3] font-medium uppercase">
|
||||
{copy.backToMedia}
|
||||
</span>
|
||||
</Link>
|
||||
@@ -80,7 +80,7 @@ export default async function LogosPodcastPage({
|
||||
const data = findSection<PodcastCopySection>(
|
||||
page.sections,
|
||||
'podcastCopy',
|
||||
'podcast.copy',
|
||||
'podcast.copy'
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--display: var(--next-font-display), Georgia, serif;
|
||||
--mono: var(--next-font-mono), monospace;
|
||||
--public-sans:
|
||||
var(--next-font-sans), "Public Sans", ui-sans-serif, system-ui, sans-serif;
|
||||
var(--next-font-sans), 'Public Sans', ui-sans-serif, system-ui, sans-serif;
|
||||
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
@@ -22,13 +22,13 @@
|
||||
}
|
||||
|
||||
.page::before {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -767px;
|
||||
left: 50%;
|
||||
width: 2565px;
|
||||
height: 3206px;
|
||||
background: url("/campaigns/ukdebt/page-bg.webp") center / cover no-repeat;
|
||||
background: url('/campaigns/ukdebt/page-bg.webp') center / cover no-repeat;
|
||||
filter: blur(157px);
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
@@ -36,10 +36,10 @@
|
||||
}
|
||||
|
||||
.page::after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: url("/campaigns/ukdebt/paper.webp") center / cover no-repeat;
|
||||
background: url('/campaigns/ukdebt/paper.webp') center / cover no-repeat;
|
||||
mix-blend-mode: multiply;
|
||||
opacity: 0.2;
|
||||
pointer-events: none;
|
||||
@@ -68,12 +68,12 @@
|
||||
.ctaSection::after,
|
||||
.sourcesStrip::after,
|
||||
.campaignFooter::after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
border-radius: inherit;
|
||||
background: url("/campaigns/ukdebt/paper.webp") center / cover no-repeat;
|
||||
background: url('/campaigns/ukdebt/paper.webp') center / cover no-repeat;
|
||||
mix-blend-mode: multiply;
|
||||
opacity: 0.42;
|
||||
pointer-events: none;
|
||||
@@ -102,14 +102,14 @@
|
||||
}
|
||||
|
||||
.hero::after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
z-index: 1;
|
||||
width: 100vw;
|
||||
background: url("/campaigns/ukdebt/paper-wrinkles.webp") center / cover
|
||||
background: url('/campaigns/ukdebt/paper-wrinkles.webp') center / cover
|
||||
no-repeat;
|
||||
opacity: 0.38;
|
||||
pointer-events: none;
|
||||
@@ -154,7 +154,7 @@
|
||||
}
|
||||
|
||||
.heroFigure::before {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 273px;
|
||||
left: 0;
|
||||
@@ -478,7 +478,7 @@
|
||||
z-index: 0;
|
||||
background:
|
||||
linear-gradient(0deg, rgba(21, 37, 33, 0.72), rgba(21, 37, 33, 0.18)),
|
||||
url("/campaigns/ukdebt/card-bg.webp") center / cover;
|
||||
url('/campaigns/ukdebt/card-bg.webp') center / cover;
|
||||
filter: blur(45px);
|
||||
opacity: 0.85;
|
||||
transform: scale(1.08);
|
||||
@@ -613,7 +613,7 @@
|
||||
padding: 0;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(15, 17, 16, 0.55), rgba(15, 17, 16, 0.7)),
|
||||
url("/campaigns/ukdebt/spend-bg.webp") center / cover no-repeat;
|
||||
url('/campaigns/ukdebt/spend-bg.webp') center / cover no-repeat;
|
||||
}
|
||||
|
||||
.spendPanel .panelBackdrop {
|
||||
@@ -931,7 +931,7 @@
|
||||
|
||||
.historyPanel::before,
|
||||
.historyPanel::after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -942,7 +942,7 @@
|
||||
left: 50%;
|
||||
z-index: -1;
|
||||
width: 100vw;
|
||||
background-image: url("/campaigns/ukdebt/paper.webp");
|
||||
background-image: url('/campaigns/ukdebt/paper.webp');
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
@@ -970,7 +970,7 @@
|
||||
|
||||
.historyColumns::before,
|
||||
.historyColumns::after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -12px;
|
||||
right: -12px;
|
||||
@@ -1233,21 +1233,21 @@
|
||||
z-index: 0;
|
||||
width: 2226px;
|
||||
height: 2783px;
|
||||
background: url("/campaigns/ukdebt/page-bg.webp") center / cover no-repeat;
|
||||
background: url('/campaigns/ukdebt/page-bg.webp') center / cover no-repeat;
|
||||
filter: blur(242.883px);
|
||||
opacity: 1;
|
||||
transform: translateX(-974px);
|
||||
}
|
||||
|
||||
.ctaSection::before {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 0;
|
||||
width: 3682px;
|
||||
height: 4602.5px;
|
||||
background: url("/campaigns/ukdebt/cta-bg.webp") center / cover no-repeat;
|
||||
background: url('/campaigns/ukdebt/cta-bg.webp') center / cover no-repeat;
|
||||
filter: blur(204.775px);
|
||||
transform: translate(calc(-50% + 639.5px), calc(-50% - 40.5px)) rotate(90deg);
|
||||
}
|
||||
@@ -1310,7 +1310,7 @@
|
||||
}
|
||||
|
||||
.ctaButtonSecondary::after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@@ -1344,7 +1344,7 @@
|
||||
border: 1px solid rgba(255, 255, 255, 0.65);
|
||||
border-radius: 4px;
|
||||
font: inherit;
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@@ -1378,7 +1378,7 @@
|
||||
grid-column: 1 / -1;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
@@ -1503,7 +1503,7 @@
|
||||
.footerNav strong,
|
||||
.footerBuilt {
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
color: inherit;
|
||||
}
|
||||
@@ -2287,7 +2287,7 @@
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.9);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(15, 17, 16, 0.55), rgba(15, 17, 16, 0.7)),
|
||||
url("/campaigns/ukdebt/spend-bg.webp") center / cover no-repeat;
|
||||
url('/campaigns/ukdebt/spend-bg.webp') center / cover no-repeat;
|
||||
}
|
||||
|
||||
.spendPanel .panelBackdrop {
|
||||
@@ -2369,7 +2369,7 @@
|
||||
|
||||
.spendBubble span {
|
||||
margin-top: 3px;
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.zeroSection {
|
||||
@@ -2741,7 +2741,7 @@
|
||||
.ctaSignup input,
|
||||
.ctaSignup button {
|
||||
height: 28px;
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ctaSignup input {
|
||||
|
||||
@@ -22,7 +22,10 @@ import L from 'leaflet'
|
||||
import MarkerClusterGroup from 'react-leaflet-cluster'
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
|
||||
import type { ActiveCircleMarker, ActiveCircleUpcomingEvent } from '@/lib/active-circles'
|
||||
import type {
|
||||
ActiveCircleMarker,
|
||||
ActiveCircleUpcomingEvent,
|
||||
} from '@/lib/active-circles'
|
||||
import { submitNewsletterSignup } from '@/lib/newsletter-signup'
|
||||
|
||||
const DEFAULT_CENTER: [number, number] = [20, 0]
|
||||
@@ -56,7 +59,7 @@ const yellowMarkerIcon = L.divIcon({
|
||||
function clusterIconCreator(cluster: { getChildCount: () => number }) {
|
||||
const count = cluster.getChildCount()
|
||||
return L.divIcon({
|
||||
html: `<div style="display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:9999px;background-color:#ffd328;color:#152521;font-size:10px;font-weight:500;">${count}</div>`,
|
||||
html: `<div style="display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:9999px;background-color:#ffd328;color:#152521;font-size:12px;font-weight:500;">${count}</div>`,
|
||||
className: 'logos-circle-cluster',
|
||||
iconSize: L.point(32, 32, true),
|
||||
})
|
||||
@@ -119,8 +122,12 @@ function EventPopupContent({
|
||||
{event.name}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span className="text-mono-s block text-brand-dark-green">{date}</span>
|
||||
<span className="text-mono-s block text-brand-dark-green">{time}</span>
|
||||
<span className="text-mono-s block text-brand-dark-green">
|
||||
{date}
|
||||
</span>
|
||||
<span className="text-mono-s block text-brand-dark-green">
|
||||
{time}
|
||||
</span>
|
||||
{location ? (
|
||||
<span className="text-mono-s block truncate text-gray-05">
|
||||
{location}
|
||||
@@ -133,7 +140,12 @@ function EventPopupContent({
|
||||
|
||||
if (event.eventUrl) {
|
||||
return (
|
||||
<a href={event.eventUrl} target="_blank" rel="noopener noreferrer" className={cardClass}>
|
||||
<a
|
||||
href={event.eventUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cardClass}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
)
|
||||
@@ -145,7 +157,9 @@ function EventPopupContent({
|
||||
function NoEventPopupContent({ marker }: { marker: ActiveCircleMarker }) {
|
||||
const t = useTranslations('circlesMap')
|
||||
const [email, setEmail] = useState('')
|
||||
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle')
|
||||
const [status, setStatus] = useState<
|
||||
'idle' | 'loading' | 'success' | 'error'
|
||||
>('idle')
|
||||
const [errorMsg, setErrorMsg] = useState('')
|
||||
|
||||
const handleSubmit = async (e: FormEvent) => {
|
||||
@@ -171,7 +185,9 @@ function NoEventPopupContent({ marker }: { marker: ActiveCircleMarker }) {
|
||||
<div className="pr-6 font-sans text-[18px] leading-[1.15]">
|
||||
{t('subscribedHeading')}
|
||||
</div>
|
||||
<div className="text-mono-s mt-1 text-gray-05">{t('subscribedBody')}</div>
|
||||
<div className="text-mono-s mt-1 text-gray-05">
|
||||
{t('subscribedBody')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -322,7 +338,9 @@ function TouchGestureHandling({ hint }: { hint: string }) {
|
||||
if (event.touches.length < 2) map.dragging.disable()
|
||||
}
|
||||
|
||||
container.addEventListener('touchstart', handleTouchStart, { passive: true })
|
||||
container.addEventListener('touchstart', handleTouchStart, {
|
||||
passive: true,
|
||||
})
|
||||
container.addEventListener('touchmove', handleTouchMove, { passive: true })
|
||||
container.addEventListener('touchend', handleTouchEnd, { passive: true })
|
||||
container.addEventListener('touchcancel', handleTouchEnd, { passive: true })
|
||||
@@ -397,10 +415,13 @@ function DesktopScrollZoomGate() {
|
||||
(entries) => {
|
||||
if (!entries.some((entry) => entry.isIntersecting)) return
|
||||
setShowHint(true)
|
||||
hintTimeout = setTimeout(() => setShowHint(false), SCROLL_ZOOM_HINT_DURATION_MS)
|
||||
hintTimeout = setTimeout(
|
||||
() => setShowHint(false),
|
||||
SCROLL_ZOOM_HINT_DURATION_MS
|
||||
)
|
||||
observer.disconnect()
|
||||
},
|
||||
{ threshold: 0.35 },
|
||||
{ threshold: 0.35 }
|
||||
)
|
||||
observer.observe(map.getContainer())
|
||||
|
||||
@@ -508,7 +529,8 @@ export default function CirclesWorldMap({
|
||||
minWidth={0}
|
||||
maxWidth={480}
|
||||
closeButton
|
||||
autoPan>
|
||||
autoPan
|
||||
>
|
||||
{upcomingEvent ? (
|
||||
<EventPopupContent event={upcomingEvent} locale={locale} />
|
||||
) : (
|
||||
|
||||
@@ -30,7 +30,7 @@ function HeroBackLink({ eyebrow }: { eyebrow: string }) {
|
||||
<span className="inline-flex size-3.75 shrink-0 rotate-180 items-center justify-center">
|
||||
<ButtonArrowIcon />
|
||||
</span>
|
||||
<span className="font-mono text-[10px] leading-[1.3] font-medium uppercase">
|
||||
<span className="font-mono text-xs leading-[1.3] font-medium uppercase">
|
||||
{eyebrow}
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
@@ -22,7 +22,7 @@ export function JourneySection({
|
||||
href={link.href}
|
||||
className="flex h-[71px] cursor-pointer flex-col items-start justify-between rounded-xl bg-brand-dark-green p-3 text-brand-off-white md:h-[83px]"
|
||||
>
|
||||
<span className="font-mono text-[10px] font-semibold leading-[1.35]">
|
||||
<span className="font-mono text-xs font-semibold leading-[1.35]">
|
||||
{(index + 1).toString().padStart(2, '0')}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1 text-subhead-sans">
|
||||
|
||||
@@ -55,7 +55,7 @@ function ImageOverlayPanel({ panel }: { panel: ActionPanel }) {
|
||||
{panel.title}
|
||||
</h3>
|
||||
{panel.description ? (
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-brand-off-white max-w-[219px] md:max-w-[380px]">
|
||||
<p className="font-mono text-xs leading-[1.3] text-brand-off-white max-w-[219px] md:max-w-[380px]">
|
||||
{panel.description}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -83,7 +83,7 @@ function OfficeHoursPanel({ data }: { data: OfficeHours }) {
|
||||
<h3 className="font-sans text-[18px] font-normal leading-[1.15] tracking-[-0.18px] text-brand-dark-green">
|
||||
{data.title}
|
||||
</h3>
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-brand-dark-green max-w-[380px]">
|
||||
<p className="font-mono text-xs leading-[1.3] text-brand-dark-green max-w-[380px]">
|
||||
{data.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -75,7 +75,7 @@ export function BuildersHubDetailLayout({
|
||||
<span className="inline-flex size-3.75 shrink-0 rotate-180 items-center justify-center">
|
||||
<ButtonArrowIcon />
|
||||
</span>
|
||||
<span className="font-mono text-[10px] font-medium leading-[1.3] uppercase">
|
||||
<span className="font-mono text-xs font-medium leading-[1.3] uppercase">
|
||||
{backLabel}
|
||||
</span>
|
||||
</Link>
|
||||
@@ -86,7 +86,7 @@ export function BuildersHubDetailLayout({
|
||||
<div className="flex items-center gap-3">
|
||||
<LogosMark size={28} className="text-brand-dark-green shrink-0" />
|
||||
{eyebrow ? (
|
||||
<span className="font-mono text-[10px] font-medium leading-[1.3] text-brand-dark-green/70 uppercase">
|
||||
<span className="font-mono text-xs font-medium leading-[1.3] text-brand-dark-green/70 uppercase">
|
||||
{eyebrow}
|
||||
</span>
|
||||
) : null}
|
||||
@@ -105,7 +105,7 @@ export function BuildersHubDetailLayout({
|
||||
<section className="border-t border-brand-dark-green/10">
|
||||
<div className="mx-auto max-w-360 px-3 py-12 grid gap-12 md:grid-cols-3">
|
||||
<div className="md:col-span-2">
|
||||
<h2 className="font-mono text-[10px] font-medium leading-[1.3] text-brand-dark-green/70 uppercase mb-4">
|
||||
<h2 className="font-mono text-xs font-medium leading-[1.3] text-brand-dark-green/70 uppercase mb-4">
|
||||
About
|
||||
</h2>
|
||||
<div className="font-sans text-[16px] leading-[1.5] text-brand-dark-green space-y-4 max-w-[60ch]">
|
||||
@@ -134,13 +134,13 @@ export function BuildersHubDetailLayout({
|
||||
|
||||
{meta && meta.length > 0 ? (
|
||||
<aside className="md:col-span-1">
|
||||
<h2 className="font-mono text-[10px] font-medium leading-[1.3] text-brand-dark-green/70 uppercase mb-4">
|
||||
<h2 className="font-mono text-xs font-medium leading-[1.3] text-brand-dark-green/70 uppercase mb-4">
|
||||
Details
|
||||
</h2>
|
||||
<dl className="space-y-4">
|
||||
{meta.map((item, i) => (
|
||||
<div key={i}>
|
||||
<dt className="font-mono text-[10px] leading-[1.3] text-brand-dark-green/60 uppercase">
|
||||
<dt className="font-mono text-xs leading-[1.3] text-brand-dark-green/60 uppercase">
|
||||
{item.label}
|
||||
</dt>
|
||||
<dd className="font-sans text-[14px] leading-[1.4] text-brand-dark-green mt-1">
|
||||
|
||||
@@ -35,7 +35,7 @@ export function BuildersHubHero({ hero }: Props) {
|
||||
|
||||
{/* Eyebrow — desktop center / mobile right */}
|
||||
{hero.description ? (
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-black absolute top-6 left-[203px] w-[178px] md:left-[50%] md:translate-x-[6px] md:w-[226px]">
|
||||
<p className="font-mono text-xs leading-[1.3] text-black absolute top-6 left-[203px] w-[178px] md:left-[50%] md:translate-x-[6px] md:w-[226px]">
|
||||
{hero.description}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
@@ -61,14 +61,14 @@ export function BuildersHubListingHeader({
|
||||
{backHref ? (
|
||||
<Link
|
||||
href={backHref}
|
||||
className="absolute top-[-20px] left-0 inline-flex items-center gap-2 font-mono text-[10px] leading-[1.3] text-brand-dark-green uppercase hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-dark-green"
|
||||
className="absolute top-[-20px] left-0 inline-flex items-center gap-2 font-mono text-xs leading-[1.3] text-brand-dark-green uppercase hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-dark-green"
|
||||
>
|
||||
<span aria-hidden="true">←</span>
|
||||
<span>{backLabel}</span>
|
||||
</Link>
|
||||
) : null}
|
||||
{!backHref && eyebrow ? (
|
||||
<p className="absolute top-0 left-1/2 -translate-x-1/2 font-mono text-[10px] leading-[1.3] text-brand-dark-green uppercase">
|
||||
<p className="absolute top-0 left-1/2 -translate-x-1/2 font-mono text-xs leading-[1.3] text-brand-dark-green uppercase">
|
||||
{eyebrow}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -83,7 +83,7 @@ export function BuildersHubListingHeader({
|
||||
|
||||
<div className="hidden md:col-span-3 md:col-start-7 md:block">
|
||||
{description ? (
|
||||
<p className="max-w-[342px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="max-w-[342px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{description}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -105,7 +105,7 @@ export function BuildersHubListingHeader({
|
||||
|
||||
{mobileCopy ? (
|
||||
<p
|
||||
className={`${mobileDescriptionMarginClass} font-mono text-[10px] leading-[1.3] text-brand-dark-green md:hidden`}
|
||||
className={`${mobileDescriptionMarginClass} font-mono text-xs leading-[1.3] text-brand-dark-green md:hidden`}
|
||||
>
|
||||
{mobileCopy}
|
||||
</p>
|
||||
|
||||
@@ -61,7 +61,7 @@ function OverviewRow({ index, link }: { index: number; link: OverviewLink }) {
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{link.description}
|
||||
</p>
|
||||
</div>
|
||||
@@ -79,7 +79,7 @@ function OverviewRow({ index, link }: { index: number; link: OverviewLink }) {
|
||||
</div>
|
||||
|
||||
{/* Description — middle column (start col 7) */}
|
||||
<p className="absolute top-3 left-[50%] translate-x-[6px] w-[345px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="absolute top-3 left-[50%] translate-x-[6px] w-[345px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{link.description}
|
||||
</p>
|
||||
|
||||
@@ -99,7 +99,7 @@ function OverviewRow({ index, link }: { index: number; link: OverviewLink }) {
|
||||
function OverviewCtaLabel({ children }: { children: string }) {
|
||||
return (
|
||||
<span className="inline-flex items-center justify-center text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
{children}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -68,7 +68,7 @@ function ResourceRow({
|
||||
<span className="ml-3 font-display">{resource.title}</span>
|
||||
</p>
|
||||
<span className="absolute top-3 right-3 inline-flex items-center justify-center text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
{resource.ctaLabel}
|
||||
</span>
|
||||
</span>
|
||||
@@ -84,11 +84,11 @@ function ResourceRow({
|
||||
{resource.title}
|
||||
</span>
|
||||
</div>
|
||||
<p className="absolute top-3 left-[50%] translate-x-[6px] w-[312px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="absolute top-3 left-[50%] translate-x-[6px] w-[312px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{resource.description}
|
||||
</p>
|
||||
<span className="absolute top-3 left-[83.33%] translate-x-[2px] inline-flex items-center justify-center text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
{resource.ctaLabel}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -48,7 +48,7 @@ export function IdeaCard({ idea }: Props) {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<p className="absolute left-4 bottom-4 w-[186px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="absolute left-4 bottom-4 w-[186px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{blurb}{' '}
|
||||
<span className="opacity-70">/ Idea by @{idea.submitter.handle}</span>
|
||||
</p>
|
||||
|
||||
@@ -45,21 +45,21 @@ export function IdeaRow({ index, idea }: Props) {
|
||||
href={detailHref}
|
||||
className="inline-flex cursor-pointer items-center justify-center justify-self-end border border-brand-dark-green/50 px-3 py-2 text-brand-dark-green focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-brand-dark-green"
|
||||
>
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap">
|
||||
{ctaLabel}
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="ml-1 font-mono text-[10px] leading-none"
|
||||
className="ml-1 font-mono text-xs leading-none"
|
||||
>
|
||||
→
|
||||
</span>
|
||||
</Link>
|
||||
<p className="min-w-0 font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="min-w-0 font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{mobileDescription}
|
||||
</p>
|
||||
{rewardLines.length > 0 ? (
|
||||
<p className="w-[83px] justify-self-end font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="w-[83px] justify-self-end font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{rewardLines.map((line) => (
|
||||
<span key={line} className="block">
|
||||
{line}
|
||||
@@ -82,13 +82,13 @@ export function IdeaRow({ index, idea }: Props) {
|
||||
{idea.title}
|
||||
</Link>
|
||||
</div>
|
||||
<p className="min-w-0 flex-1 font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="min-w-0 flex-1 font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
<span className="block truncate">{description}</span>
|
||||
<span className="block truncate">{submitter}</span>
|
||||
</p>
|
||||
<div className="flex shrink-0 items-start gap-3">
|
||||
{rewardLines.length > 0 ? (
|
||||
<span className="w-[107px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<span className="w-[107px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{rewardLines.map((line) => (
|
||||
<span key={line} className="block">
|
||||
{line}
|
||||
@@ -102,7 +102,7 @@ export function IdeaRow({ index, idea }: Props) {
|
||||
href={detailHref}
|
||||
className="inline-flex cursor-pointer items-center justify-center text-brand-dark-green focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-brand-dark-green"
|
||||
>
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
{ctaLabel}
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
@@ -26,7 +26,7 @@ export function RelatedLinksList({
|
||||
if (items.length === 0) return null
|
||||
return (
|
||||
<>
|
||||
<h2 className="font-mono text-[10px] font-medium leading-[1.3] text-brand-dark-green/70 uppercase mb-4">
|
||||
<h2 className="font-mono text-xs font-medium leading-[1.3] text-brand-dark-green/70 uppercase mb-4">
|
||||
{heading}
|
||||
</h2>
|
||||
<ul className="space-y-2">
|
||||
|
||||
@@ -31,7 +31,7 @@ export function RfpCard({ rfp }: Props) {
|
||||
{rfp.title}
|
||||
</h3>
|
||||
<span className="mt-[14.2px] inline-flex items-center justify-center text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
Learn More
|
||||
</span>
|
||||
</span>
|
||||
@@ -54,7 +54,7 @@ export function RfpCard({ rfp }: Props) {
|
||||
) : null}
|
||||
|
||||
{/* Description */}
|
||||
<p className="absolute left-4 bottom-4 w-[186px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="absolute left-4 bottom-4 w-[186px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{blurb}
|
||||
</p>
|
||||
</Link>
|
||||
|
||||
@@ -35,15 +35,15 @@ export function RfpListRow({ index, rfp }: Props) {
|
||||
<span className="ml-3 font-display">{rfp.title}</span>
|
||||
</p>
|
||||
<span className="inline-flex items-center justify-center justify-self-end border border-brand-dark-green/50 px-3 py-2 text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap">
|
||||
Learn More
|
||||
</span>
|
||||
</span>
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{blurb}
|
||||
</p>
|
||||
{rewardLines.length > 0 ? (
|
||||
<p className="w-[83px] justify-self-end font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="w-[83px] justify-self-end font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{rewardLines.map((line) => (
|
||||
<span key={line} className="block">
|
||||
{line}
|
||||
@@ -65,12 +65,12 @@ export function RfpListRow({ index, rfp }: Props) {
|
||||
{rfp.title}
|
||||
</span>
|
||||
</div>
|
||||
<p className="min-w-0 flex-1 truncate font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="min-w-0 flex-1 truncate font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{blurb}
|
||||
</p>
|
||||
<div className="flex shrink-0 items-start gap-3">
|
||||
{rewardLines.length > 0 ? (
|
||||
<span className="w-[107px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<span className="w-[107px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{rewardLines.map((line) => (
|
||||
<span key={line} className="block">
|
||||
{line}
|
||||
@@ -81,7 +81,7 @@ export function RfpListRow({ index, rfp }: Props) {
|
||||
<span className="w-[107px]" />
|
||||
)}
|
||||
<span className="inline-flex items-center justify-center text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap border-b border-brand-dark-green/50 pb-0.5">
|
||||
Learn More
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function BuildersHubSectionHeader({
|
||||
{/* Desktop layout — absolute, matches Figma x-coords */}
|
||||
<div className="absolute inset-0 hidden md:block">
|
||||
{eyebrow ? (
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-black absolute top-[3px] left-[50%] translate-x-[6px] w-[226px]">
|
||||
<p className="font-mono text-xs leading-[1.3] text-black absolute top-[3px] left-[50%] translate-x-[6px] w-[226px]">
|
||||
{eyebrow}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -53,7 +53,7 @@ export function BuildersHubSectionHeader({
|
||||
{/* Mobile layout — eyebrow + CTA stacked on the right side */}
|
||||
<div className="absolute top-0 right-3 flex flex-col items-start gap-6 md:hidden w-[178px] text-right">
|
||||
{eyebrow ? (
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-black w-full">
|
||||
<p className="font-mono text-xs leading-[1.3] text-black w-full">
|
||||
{eyebrow}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
@@ -44,7 +44,7 @@ function InitiativeCard({ initiative }: { initiative: CircleInitiative }) {
|
||||
</div>
|
||||
|
||||
<span className="absolute right-3 top-3 inline-flex items-center gap-1 rounded-xl bg-brand-off-white px-3 py-2 text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
<span className="font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{initiative.ctaLabel}
|
||||
</span>
|
||||
<ArrowIcon />
|
||||
|
||||
@@ -65,66 +65,66 @@ function DetailHero({
|
||||
return (
|
||||
<section className="relative h-auto min-h-[291px] bg-brand-off-white px-3 pt-10 md:h-[334px] md:pb-0">
|
||||
<ContentWidth className="relative h-full">
|
||||
<SmartLink
|
||||
href={circle.detailBackLink.href}
|
||||
className="absolute left-3 top-[-20px] inline-flex items-center gap-1 px-0 py-2 text-brand-dark-green"
|
||||
>
|
||||
<ArrowIcon direction="left" />
|
||||
<span className="font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
{circle.detailBackLink.label}
|
||||
</span>
|
||||
</SmartLink>
|
||||
<SmartLink
|
||||
href={circle.detailBackLink.href}
|
||||
className="absolute left-3 top-[-20px] inline-flex items-center gap-1 px-0 py-2 text-brand-dark-green"
|
||||
>
|
||||
<ArrowIcon direction="left" />
|
||||
<span className="font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{circle.detailBackLink.label}
|
||||
</span>
|
||||
</SmartLink>
|
||||
|
||||
<div className="flex items-center gap-3 md:absolute md:left-3 md:top-10">
|
||||
<CircleLogo size="large" />
|
||||
<h1 className="font-display text-[30px] leading-none text-brand-dark-green md:text-[36px]">
|
||||
{circle.name}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 grid gap-10 md:absolute md:left-[calc(50%+6px)] md:top-10 md:mt-0 md:w-[345px] md:gap-7">
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="text-mono-s max-w-[369px] text-brand-dark-green md:max-w-[342px]">
|
||||
{circle.description}
|
||||
</p>
|
||||
|
||||
<Button
|
||||
href={circle.joinUrl}
|
||||
variant="secondary"
|
||||
className="md:hidden"
|
||||
{...(isExternalHref(circle.joinUrl)
|
||||
? { target: '_blank', rel: 'noopener noreferrer' }
|
||||
: {})}
|
||||
>
|
||||
{settings.detailJoinCtaLabel}
|
||||
</Button>
|
||||
<div className="flex items-center gap-3 md:absolute md:left-3 md:top-10">
|
||||
<CircleLogo size="large" />
|
||||
<h1 className="font-display text-[30px] leading-none text-brand-dark-green md:text-[36px]">
|
||||
{circle.name}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<dl className="text-mono-s w-full text-brand-dark-green">
|
||||
{stats.map(([label, value]) => (
|
||||
<div
|
||||
key={label}
|
||||
className="grid grid-cols-[83px_minmax(0,1fr)] gap-3 border-t border-brand-dark-green/10 py-1.5 md:grid-cols-[107px_minmax(0,1fr)]"
|
||||
>
|
||||
<dt className="font-mono text-[10px] font-medium uppercase leading-[1.3]">
|
||||
{label}
|
||||
</dt>
|
||||
<dd className="min-w-0 break-words">{value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
<div className="mt-10 grid gap-10 md:absolute md:left-[calc(50%+6px)] md:top-10 md:mt-0 md:w-[345px] md:gap-7">
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
<p className="text-mono-s max-w-[369px] text-brand-dark-green md:max-w-[342px]">
|
||||
{circle.description}
|
||||
</p>
|
||||
|
||||
<Button
|
||||
href={circle.joinUrl}
|
||||
variant="secondary"
|
||||
className="hidden md:absolute md:left-[calc(83.333%+2px)] md:top-10 md:mt-0 md:inline-flex"
|
||||
{...(isExternalHref(circle.joinUrl)
|
||||
? { target: '_blank', rel: 'noopener noreferrer' }
|
||||
: {})}
|
||||
>
|
||||
{settings.detailJoinCtaLabel}
|
||||
</Button>
|
||||
<Button
|
||||
href={circle.joinUrl}
|
||||
variant="secondary"
|
||||
className="md:hidden"
|
||||
{...(isExternalHref(circle.joinUrl)
|
||||
? { target: '_blank', rel: 'noopener noreferrer' }
|
||||
: {})}
|
||||
>
|
||||
{settings.detailJoinCtaLabel}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<dl className="text-mono-s w-full text-brand-dark-green">
|
||||
{stats.map(([label, value]) => (
|
||||
<div
|
||||
key={label}
|
||||
className="grid grid-cols-[83px_minmax(0,1fr)] gap-3 border-t border-brand-dark-green/10 py-1.5 md:grid-cols-[107px_minmax(0,1fr)]"
|
||||
>
|
||||
<dt className="font-mono text-xs font-medium uppercase leading-[1.3]">
|
||||
{label}
|
||||
</dt>
|
||||
<dd className="min-w-0 break-words">{value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
href={circle.joinUrl}
|
||||
variant="secondary"
|
||||
className="hidden md:absolute md:left-[calc(83.333%+2px)] md:top-10 md:mt-0 md:inline-flex"
|
||||
{...(isExternalHref(circle.joinUrl)
|
||||
? { target: '_blank', rel: 'noopener noreferrer' }
|
||||
: {})}
|
||||
>
|
||||
{settings.detailJoinCtaLabel}
|
||||
</Button>
|
||||
</ContentWidth>
|
||||
</section>
|
||||
)
|
||||
@@ -230,7 +230,7 @@ function SectionShell({
|
||||
<ContentWidth>
|
||||
<div className="flex flex-col gap-3 px-3">
|
||||
<div className="flex justify-start md:justify-end">
|
||||
<h2 className="font-mono text-[10px] font-medium uppercase leading-[1.3] text-brand-dark-green">
|
||||
<h2 className="font-mono text-xs font-medium uppercase leading-[1.3] text-brand-dark-green">
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
@@ -299,7 +299,7 @@ function InitiativeCard({ initiative }: { initiative: CircleInitiative }) {
|
||||
</div>
|
||||
|
||||
<span className="absolute right-3 top-3 inline-flex items-center gap-1 rounded-xl bg-brand-off-white px-3 py-2 text-brand-dark-green">
|
||||
<span className="font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
<span className="font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{initiative.ctaLabel}
|
||||
</span>
|
||||
<ArrowIcon />
|
||||
|
||||
@@ -46,7 +46,7 @@ export function AfformPageIntro({ text }: Props) {
|
||||
.filter(Boolean)
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-[40em] space-y-4 text-balance font-mono text-[10px] leading-[1.3]">
|
||||
<div className="mx-auto max-w-[40em] space-y-4 text-balance font-mono text-xs leading-[1.3]">
|
||||
{paragraphs.map((paragraph, index) => (
|
||||
<p key={index}>{renderInlineLinks(paragraph)}</p>
|
||||
))}
|
||||
|
||||
@@ -15,7 +15,10 @@ import {
|
||||
} from 'react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { buildFormSchema, MAX_TEXT_LENGTH } from '@/lib/civicrm/contactFormSchema'
|
||||
import {
|
||||
buildFormSchema,
|
||||
MAX_TEXT_LENGTH,
|
||||
} from '@/lib/civicrm/contactFormSchema'
|
||||
import { HEAR_ABOUT_FORM_KEY } from '@/lib/civicrm/hear-about-field'
|
||||
import type {
|
||||
AfformConfig,
|
||||
@@ -267,7 +270,9 @@ export function ConnectFormSection({
|
||||
if (!el) return
|
||||
const rect = el.getBoundingClientRect()
|
||||
const top =
|
||||
window.scrollY + rect.top - Math.max(0, (window.innerHeight - rect.height) / 2)
|
||||
window.scrollY +
|
||||
rect.top -
|
||||
Math.max(0, (window.innerHeight - rect.height) / 2)
|
||||
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' })
|
||||
el.focus({ preventScroll: true })
|
||||
}
|
||||
@@ -353,7 +358,7 @@ export function ConnectFormSection({
|
||||
<h3 className="mb-4 font-sans text-[24px] leading-[1.1] tracking-[-0.24px] text-brand-dark-green">
|
||||
{confirmationMessage}
|
||||
</h3>
|
||||
<p className="mx-auto max-w-[40em] text-balance font-mono text-[10px] leading-[1.3] text-brand-dark-green/80">
|
||||
<p className="mx-auto max-w-[40em] text-balance font-mono text-xs leading-[1.3] text-brand-dark-green/80">
|
||||
{t('successBody')}
|
||||
</p>
|
||||
</motion.div>
|
||||
@@ -475,7 +480,7 @@ export function ConnectFormSection({
|
||||
type="button"
|
||||
onClick={handleRemoveChatRow(index)}
|
||||
disabled={loadingState}
|
||||
className="shrink-0 self-center font-mono text-[10px] leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
className="shrink-0 self-center font-mono text-xs leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
aria-label={t('removeChatRow', { index: index + 1 })}
|
||||
>
|
||||
{t('remove')}
|
||||
@@ -484,7 +489,7 @@ export function ConnectFormSection({
|
||||
</div>
|
||||
))}
|
||||
{hasError('chat') ? (
|
||||
<p className="font-mono text-[10px] font-semibold text-red-600">
|
||||
<p className="font-mono text-xs font-semibold text-red-600">
|
||||
{t('chatNameRequired')}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -492,7 +497,7 @@ export function ConnectFormSection({
|
||||
type="button"
|
||||
onClick={handleAddChatRow}
|
||||
disabled={loadingState}
|
||||
className="text-left font-mono text-[10px] leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
className="text-left font-mono text-xs leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
aria-label={t('addChatRow')}
|
||||
>
|
||||
{t('addAnother')}
|
||||
@@ -537,7 +542,7 @@ export function ConnectFormSection({
|
||||
const isChecked = value === true
|
||||
return (
|
||||
<div key={field.formKey} className="w-full">
|
||||
<label className="inline-flex cursor-pointer items-start font-mono text-[10px] leading-[1.3]">
|
||||
<label className="inline-flex cursor-pointer items-start font-mono text-xs leading-[1.3]">
|
||||
<input
|
||||
className="sr-only"
|
||||
type="checkbox"
|
||||
@@ -590,7 +595,7 @@ export function ConnectFormSection({
|
||||
{!isOverLimit &&
|
||||
hasFieldError &&
|
||||
REQUIRED_FIELDS.has(field.formKey) ? (
|
||||
<p className="mt-2 font-mono text-[10px] font-semibold text-red-600">
|
||||
<p className="mt-2 font-mono text-xs font-semibold text-red-600">
|
||||
{t('fieldRequired', { field: label.toLowerCase() })}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -606,8 +611,7 @@ export function ConnectFormSection({
|
||||
values = [values[0], '']
|
||||
}
|
||||
const canAddRow =
|
||||
field.formKey !== 'website' ||
|
||||
values.length < MAX_WEBSITE_ROWS
|
||||
field.formKey !== 'website' || values.length < MAX_WEBSITE_ROWS
|
||||
return (
|
||||
<div key={field.formKey} className="w-full space-y-3">
|
||||
<FieldLabel>{label}</FieldLabel>
|
||||
@@ -627,7 +631,7 @@ export function ConnectFormSection({
|
||||
type="button"
|
||||
onClick={handleRemoveRepeatable(field.formKey, index)}
|
||||
disabled={loadingState}
|
||||
className="shrink-0 self-center font-mono text-[10px] leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
className="shrink-0 self-center font-mono text-xs leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
aria-label={t('removeFieldRow', {
|
||||
field: label,
|
||||
index: index + 1,
|
||||
@@ -643,7 +647,7 @@ export function ConnectFormSection({
|
||||
type="button"
|
||||
onClick={handleAddRepeatable(field.formKey)}
|
||||
disabled={loadingState}
|
||||
className="text-left font-mono text-[10px] leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
className="text-left font-mono text-xs leading-[1.3] text-brand-dark-green/70 hover:text-brand-dark-green"
|
||||
aria-label={t('addFieldRow', { field: label })}
|
||||
>
|
||||
{t('addAnother')}
|
||||
@@ -677,7 +681,7 @@ export function ConnectFormSection({
|
||||
{!isOverLimit &&
|
||||
hasFieldError &&
|
||||
REQUIRED_FIELDS.has(field.formKey) ? (
|
||||
<p className="mt-2 font-mono text-[10px] font-semibold text-red-600">
|
||||
<p className="mt-2 font-mono text-xs font-semibold text-red-600">
|
||||
{field.formKey === 'email'
|
||||
? t('emailInvalid')
|
||||
: t('fieldRequired', { field: label.toLowerCase() })}
|
||||
@@ -691,7 +695,7 @@ export function ConnectFormSection({
|
||||
<motion.p
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="font-mono text-[10px] font-semibold text-red-600"
|
||||
className="font-mono text-xs font-semibold text-red-600"
|
||||
>
|
||||
{serverError}
|
||||
</motion.p>
|
||||
@@ -717,7 +721,7 @@ export function ConnectFormSection({
|
||||
<div className="flex flex-col gap-6 pt-4 md:flex-row md:items-end md:justify-between">
|
||||
{pagePrivacy ? (
|
||||
<div className="max-w-[50em]">
|
||||
<p className="text-balance font-mono text-[10px] leading-[1.3] text-brand-dark-green/80">
|
||||
<p className="text-balance font-mono text-xs leading-[1.3] text-brand-dark-green/80">
|
||||
{pagePrivacyLink && pagePrivacy.includes('Privacy Policy') ? (
|
||||
<>
|
||||
{pagePrivacy.split('Privacy Policy')[0]}
|
||||
|
||||
@@ -16,7 +16,7 @@ export function FieldError({ visible, children }: Props) {
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="mt-2 font-mono text-[10px] leading-[1.3] font-semibold text-red-600"
|
||||
className="mt-2 font-mono text-xs leading-[1.3] font-semibold text-red-600"
|
||||
>
|
||||
{children}
|
||||
</motion.p>
|
||||
|
||||
@@ -14,7 +14,7 @@ export function FieldLabel({ htmlFor, children, required, className }: Props) {
|
||||
<label
|
||||
htmlFor={htmlFor}
|
||||
className={cn(
|
||||
'mb-1.5 block font-mono text-[10px] leading-[1.3] text-brand-dark-green',
|
||||
'mb-1.5 block font-mono text-xs leading-[1.3] text-brand-dark-green',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -32,19 +32,20 @@ export function TextInput({
|
||||
const isOverLimit = count > maxLength
|
||||
return (
|
||||
<div className={cn('w-full', wrapperClassName)}>
|
||||
{label ? (
|
||||
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
||||
) : null}
|
||||
{label ? <FieldLabel htmlFor={id}>{label}</FieldLabel> : null}
|
||||
<input
|
||||
id={id}
|
||||
value={value}
|
||||
aria-invalid={error || isOverLimit || undefined}
|
||||
className={cn(inputClassName, (error || isOverLimit) && 'border-red-600')}
|
||||
className={cn(
|
||||
inputClassName,
|
||||
(error || isOverLimit) && 'border-red-600'
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{isOverLimit ? (
|
||||
<div
|
||||
className="mt-1 flex items-center gap-2 font-mono text-[10px] leading-[1.3] font-semibold text-red-600"
|
||||
className="mt-1 flex items-center gap-2 font-mono text-xs leading-[1.3] font-semibold text-red-600"
|
||||
aria-live="polite"
|
||||
>
|
||||
{overLimitMessage ? <p>{overLimitMessage}</p> : null}
|
||||
|
||||
@@ -27,9 +27,7 @@ export function TextareaInput({
|
||||
const isOverLimit = count > maxLength
|
||||
return (
|
||||
<div className="w-full">
|
||||
{label ? (
|
||||
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
||||
) : null}
|
||||
{label ? <FieldLabel htmlFor={id}>{label}</FieldLabel> : null}
|
||||
<textarea
|
||||
id={id}
|
||||
value={value}
|
||||
@@ -41,7 +39,7 @@ export function TextareaInput({
|
||||
{...props}
|
||||
/>
|
||||
<div
|
||||
className="mt-1 flex items-center gap-2 font-mono text-[10px] leading-[1.3]"
|
||||
className="mt-1 flex items-center gap-2 font-mono text-xs leading-[1.3]"
|
||||
aria-live="polite"
|
||||
>
|
||||
{isOverLimit && overLimitMessage ? (
|
||||
|
||||
@@ -99,15 +99,15 @@ function CardBody({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full max-w-[339px] items-baseline gap-10">
|
||||
<p className="min-h-[51px] w-[169.5px] shrink-0 break-words font-sans text-[12px] leading-[1.2] font-medium text-brand-dark-green desktop:text-body-sans desktop:font-normal">
|
||||
<p className="min-h-[51px] w-[169.5px] shrink-0 break-words font-sans text-xs leading-[1.2] font-medium text-brand-dark-green desktop:text-body-sans desktop:font-normal">
|
||||
{title}
|
||||
</p>
|
||||
<div className="shrink-0">
|
||||
<p className="font-mono-body text-[10px] leading-[1.3] font-normal text-brand-dark-green desktop:text-eyebrow">
|
||||
<p className="font-mono-body text-xs leading-[1.3] font-normal text-brand-dark-green desktop:text-eyebrow">
|
||||
{date}
|
||||
</p>
|
||||
{author ? (
|
||||
<p className="font-mono-body text-[10px] leading-[1.3] font-normal text-brand-dark-green desktop:text-eyebrow">
|
||||
<p className="font-mono-body text-xs leading-[1.3] font-normal text-brand-dark-green desktop:text-eyebrow">
|
||||
{author}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -144,7 +144,7 @@ export default function BlogSection({ data, articles }: Props) {
|
||||
{(data.label || data.eyebrow || data.cta) && (
|
||||
<div className="contents lg:flex lg:items-center lg:justify-between">
|
||||
{data.label ? (
|
||||
<p className="font-mono-body absolute top-6 right-[169px] left-3 text-[10px] leading-[1.3] font-normal text-brand-dark-green lg:static lg:right-auto lg:w-auto lg:text-mono-s">
|
||||
<p className="font-mono-body absolute top-6 right-[169px] left-3 text-xs leading-[1.3] font-normal text-brand-dark-green lg:static lg:right-auto lg:w-auto lg:text-mono-s">
|
||||
{data.label}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
@@ -122,7 +122,7 @@ export default function CivilSocietyAccordion({
|
||||
<p className="font-sans text-[14px] leading-[1.2]">
|
||||
{item.body}
|
||||
</p>
|
||||
<div className="flex flex-col gap-4 font-mono text-[10px] leading-[1.3] lg:gap-[20px]">
|
||||
<div className="flex flex-col gap-4 font-mono text-xs leading-[1.3] lg:gap-[20px]">
|
||||
{item.facts.map((fact, index) => (
|
||||
<p key={fact}>
|
||||
<FactText
|
||||
|
||||
@@ -50,7 +50,7 @@ function PathCardView({ card }: { card: PathCard }) {
|
||||
|
||||
<span className="inline-flex shrink-0 items-center justify-center rounded-xl bg-brand-off-white px-3 py-2 text-brand-dark-green backdrop-blur-[5px] transition-colors duration-300 ease-out group-hover/path-card:bg-accent-steel-teal group-focus-visible/path-card:bg-accent-steel-teal lg:px-[clamp(8px,0.8vw,12px)] desktop:px-3">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold whitespace-nowrap uppercase lg:text-[clamp(8px,0.7vw,10px)] desktop:text-[10px]">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold whitespace-nowrap uppercase">
|
||||
{card.cta}
|
||||
</span>
|
||||
<ButtonArrowIcon />
|
||||
@@ -69,7 +69,9 @@ interface FeatureCardsSectionProps {
|
||||
data: HomeChoosePathSection
|
||||
}
|
||||
|
||||
export default function FeatureCardsSection({ data }: FeatureCardsSectionProps) {
|
||||
export default function FeatureCardsSection({
|
||||
data,
|
||||
}: FeatureCardsSectionProps) {
|
||||
const cards: PathCard[] = [
|
||||
{
|
||||
key: 'build',
|
||||
@@ -111,7 +113,7 @@ export default function FeatureCardsSection({ data }: FeatureCardsSectionProps)
|
||||
{data.title}
|
||||
</SectionHeadingReveal>
|
||||
|
||||
<div className="font-mono-body mx-auto flex w-[351.5px] max-w-[calc(100vw-24px)] flex-col gap-[13px] text-[10px] leading-[1.3] text-brand-dark-green lg:mx-0 lg:w-[226px] lg:gap-4 lg:text-mono-s">
|
||||
<div className="font-mono-body mx-auto flex w-[351.5px] max-w-[calc(100vw-24px)] flex-col gap-[13px] text-xs leading-[1.3] text-brand-dark-green lg:mx-0 lg:w-[226px] lg:gap-4 lg:text-mono-s">
|
||||
<p>{data.kicker}</p>
|
||||
<p>{data.body}</p>
|
||||
</div>
|
||||
|
||||
@@ -13,10 +13,13 @@ import type { SocialProofStats } from '@/lib/social-proof-stats'
|
||||
function StatCardView({ card }: { card: HomeStatCard }) {
|
||||
return (
|
||||
<article className="hidden h-full w-full min-w-0 rounded-[20px] border border-brand-dark-green bg-brand-off-white text-brand-dark-green md:grid md:grid-cols-1">
|
||||
<div aria-hidden className="col-start-1 row-start-1 aspect-square w-full" />
|
||||
<div
|
||||
aria-hidden
|
||||
className="col-start-1 row-start-1 aspect-square w-full"
|
||||
/>
|
||||
<div className="col-start-1 row-start-1 flex min-w-0 flex-col gap-4 p-4 xl:gap-[23px] xl:p-5">
|
||||
<span className="inline-flex w-fit items-center rounded-[4px] border border-brand-dark-green px-[11px] py-1.5">
|
||||
<span className="font-sans text-[12px] leading-[1.15] tracking-[-0.01em]">
|
||||
<span className="font-sans text-xs leading-[1.15] tracking-[-0.01em]">
|
||||
{card.label}
|
||||
</span>
|
||||
</span>
|
||||
@@ -47,7 +50,7 @@ function MobileStatRowView({
|
||||
<p className="w-full font-sans text-[18px] leading-[1.15] font-normal tracking-[-0.18px] [text-box-edge:cap_alphabetic] [text-box-trim:trim-both]">
|
||||
{card.value} {card.label}
|
||||
</p>
|
||||
<p className="font-mono-body w-full text-[10px] leading-[1.3] font-normal tracking-normal [text-box-edge:cap_alphabetic] [text-box-trim:trim-both]">
|
||||
<p className="font-mono-body w-full text-xs leading-[1.3] font-normal tracking-normal [text-box-edge:cap_alphabetic] [text-box-trim:trim-both]">
|
||||
{card.body}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function StartBuildingSection({
|
||||
<h2 className="font-display text-[30px] leading-none tracking-[-0.03em] lg:text-[36px]">
|
||||
{data.title}
|
||||
</h2>
|
||||
<p className="font-sans text-[12px] leading-[1.2] lg:w-[254px]">
|
||||
<p className="font-sans text-xs leading-[1.2] lg:w-[254px]">
|
||||
{data.body}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function UseCasesSection({
|
||||
{data.headline}
|
||||
</SectionHeadingReveal>
|
||||
</div>
|
||||
<SectionHeadingReveal className="font-mono-body mt-[80px] text-center text-[10px] leading-[1.3] tracking-normal whitespace-nowrap lg:mt-0 lg:hidden">
|
||||
<SectionHeadingReveal className="font-mono-body mt-[80px] text-center text-xs leading-[1.3] tracking-normal whitespace-nowrap lg:mt-0 lg:hidden">
|
||||
{data.eyebrow}
|
||||
</SectionHeadingReveal>
|
||||
<p className="text-mono-s hidden w-full lg:block">
|
||||
@@ -80,7 +80,7 @@ export default function UseCasesSection({
|
||||
}
|
||||
</h3>
|
||||
<div className="absolute right-[10px] bottom-[11px] left-[15px] flex items-end justify-between gap-3 lg:static">
|
||||
<p className="font-mono-body w-[186px] text-[10px] leading-[1.3] lg:max-w-[186px]">
|
||||
<p className="font-mono-body w-[186px] text-xs leading-[1.3] lg:max-w-[186px]">
|
||||
{
|
||||
data[
|
||||
card.key as 'secure' | 'money' | 'archives' | 'donations'
|
||||
|
||||
@@ -203,10 +203,10 @@ describe('MovementPageView – content-driven copy', () => {
|
||||
initiatives: [],
|
||||
resources: [],
|
||||
locale: 'en',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expect(html).toContain(
|
||||
'Logos is a movement of builders, activists, and community organisers rebuilding civil society.',
|
||||
'Logos is a movement of builders, activists, and community organisers rebuilding civil society.'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -220,7 +220,7 @@ describe('MovementPageView – content-driven copy', () => {
|
||||
initiatives: [],
|
||||
resources: [],
|
||||
locale: 'en',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expect(html).toContain('Join an Activist Circle')
|
||||
})
|
||||
@@ -235,7 +235,7 @@ describe('MovementPageView – content-driven copy', () => {
|
||||
initiatives: [],
|
||||
resources: [],
|
||||
locale: 'en',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expect(html).toContain('Become an Activist Builder')
|
||||
})
|
||||
@@ -250,11 +250,47 @@ describe('MovementPageView – content-driven copy', () => {
|
||||
initiatives: [],
|
||||
resources: [],
|
||||
locale: 'en',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expect(html).toContain('Activism in Action')
|
||||
})
|
||||
|
||||
it('renders the initiative CTA label from content', () => {
|
||||
const html = renderToStaticMarkup(
|
||||
createElement(MovementPageView, {
|
||||
data: fixture,
|
||||
circlesSettings: circlesSettings as never,
|
||||
mapMarkers: [],
|
||||
upcomingEvents: [],
|
||||
initiatives: [
|
||||
{
|
||||
schemaVersion: 1,
|
||||
slug: 'digital-escape-egress',
|
||||
status: 'published',
|
||||
circleSlug: 'london',
|
||||
href: 'https://example.com/proposal',
|
||||
image: {
|
||||
src: '/initiative.webp',
|
||||
alt: '',
|
||||
width: 720,
|
||||
height: 480,
|
||||
},
|
||||
featured: false,
|
||||
language: 'en',
|
||||
locationLabel: 'London',
|
||||
title: 'Digital IDs knowledge hub',
|
||||
description: 'An initiative description.',
|
||||
ctaLabel: 'Read the proposal',
|
||||
},
|
||||
],
|
||||
resources: [],
|
||||
locale: 'en',
|
||||
})
|
||||
)
|
||||
|
||||
expect(html).toContain('Read the proposal')
|
||||
})
|
||||
|
||||
it('renders find.title from data prop', () => {
|
||||
const html = renderToStaticMarkup(
|
||||
createElement(MovementPageView, {
|
||||
@@ -265,7 +301,7 @@ describe('MovementPageView – content-driven copy', () => {
|
||||
initiatives: [],
|
||||
resources: [],
|
||||
locale: 'en',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expect(html).toContain('Find a Circle near you.')
|
||||
})
|
||||
|
||||
@@ -26,8 +26,8 @@ function IssueCard({ initiative }: { initiative: CircleInitiative }) {
|
||||
aria-hidden="true"
|
||||
className="absolute right-3 top-3 inline-flex items-center gap-1 rounded-xl bg-brand-off-white px-3 py-2 text-brand-dark-green pointer-events-none"
|
||||
>
|
||||
<span className="font-mono text-[10px] font-semibold uppercase leading-[1.35]">
|
||||
View issue
|
||||
<span className="font-mono text-xs font-semibold uppercase leading-[1.35]">
|
||||
{initiative.ctaLabel}
|
||||
</span>
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
|
||||
@@ -33,7 +33,7 @@ const ctaToneClass: Record<CtaTone, string> = {
|
||||
|
||||
export function Cta({ href, label, tone = 'primary', className }: CtaProps) {
|
||||
const isExternal = href.startsWith('http')
|
||||
const classes = `inline-flex cursor-pointer items-center justify-center gap-1 font-mono text-[10px] font-semibold leading-[1.35] uppercase backdrop-blur-[5px] transition-opacity hover:opacity-70 ${ctaToneClass[tone]} ${className ?? ''}`
|
||||
const classes = `inline-flex cursor-pointer items-center justify-center gap-1 font-mono text-xs font-semibold leading-[1.35] uppercase backdrop-blur-[5px] transition-opacity hover:opacity-70 ${ctaToneClass[tone]} ${className ?? ''}`
|
||||
const content = (
|
||||
<>
|
||||
<span
|
||||
@@ -135,7 +135,7 @@ export function SectionHeader({
|
||||
<p
|
||||
className={cn(
|
||||
'text-mono-s max-w-[456px] md:col-span-3 md:col-start-7',
|
||||
descriptionClassName,
|
||||
descriptionClassName
|
||||
)}
|
||||
>
|
||||
{description}
|
||||
|
||||
@@ -12,7 +12,10 @@ export function isExternalHref(href: string): boolean {
|
||||
return /^https?:\/\//.test(href)
|
||||
}
|
||||
|
||||
export function TextLink({ href, children }: LinkItem & { children: ReactNode }) {
|
||||
export function TextLink({
|
||||
href,
|
||||
children,
|
||||
}: LinkItem & { children: ReactNode }) {
|
||||
if (isExternalHref(href)) {
|
||||
return (
|
||||
<a
|
||||
@@ -35,7 +38,7 @@ export function TextLink({ href, children }: LinkItem & { children: ReactNode })
|
||||
|
||||
export function CtaLink({ label, href }: LinkItem) {
|
||||
const className =
|
||||
'inline-flex cursor-pointer items-center gap-1 rounded-[4px] font-mono text-[10px] font-semibold uppercase leading-[1.35] text-brand-dark-green'
|
||||
'inline-flex cursor-pointer items-center gap-1 rounded-[4px] font-mono text-xs font-semibold uppercase leading-[1.35] text-brand-dark-green'
|
||||
const content = (
|
||||
<>
|
||||
<span>{label}</span>
|
||||
@@ -65,7 +68,7 @@ export function CtaLink({ label, href }: LinkItem) {
|
||||
|
||||
export function LinkButton({ label, href }: LinkItem) {
|
||||
const className =
|
||||
'inline-flex cursor-pointer items-center gap-1 border-b border-brand-dark-green/50 pb-0.5 font-mono text-[10px] font-semibold uppercase leading-[1.35] text-brand-dark-green'
|
||||
'inline-flex cursor-pointer items-center gap-1 border-b border-brand-dark-green/50 pb-0.5 font-mono text-xs font-semibold uppercase leading-[1.35] text-brand-dark-green'
|
||||
const content = (
|
||||
<>
|
||||
<span>{label}</span>
|
||||
|
||||
@@ -81,7 +81,7 @@ export function DeveloperProgramsSection({
|
||||
}`}
|
||||
>
|
||||
<h4 className="w-[249px] text-h4-sans">{rfp.title}</h4>
|
||||
<span className="absolute top-[83px] left-4 font-mono text-[10px] font-semibold uppercase underline underline-offset-[3px]">
|
||||
<span className="absolute top-[83px] left-4 font-mono text-xs font-semibold uppercase underline underline-offset-[3px]">
|
||||
{rfp.ctaLabel ?? data.rfpsTitle}
|
||||
</span>
|
||||
<p className="absolute bottom-4 left-4 w-[186px] text-mono-s">
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Link } from '@/i18n/navigation'
|
||||
*
|
||||
* Layout matches Figma exactly:
|
||||
* - w-[226px], py-20, gap-1 (4px in Figma)
|
||||
* - text-mono-s (Fira Mono Regular 10px) for inactive items
|
||||
* - text-mono-s (Fira Mono Regular 12px) for inactive items
|
||||
* - dot (h-2 w-3 rounded-full) + text-eyebrow for the active item
|
||||
* - sticky top-0 from xl upward (matches the gap-122 desktop frame)
|
||||
*/
|
||||
|
||||
@@ -65,7 +65,9 @@ function OverviewMediaPanelActions({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`flex items-baseline gap-1.5${className ? ` ${className}` : ''}`}>
|
||||
<div
|
||||
className={`flex items-baseline gap-1.5${className ? ` ${className}` : ''}`}
|
||||
>
|
||||
{cta ? (
|
||||
<Button
|
||||
{...resolveBasecampInstallCtaLinkProps(cta)}
|
||||
@@ -124,7 +126,9 @@ export function OverviewMediaPanel({
|
||||
const footerMarker = footerLabel ?? (isCompact ? eyebrow : undefined)
|
||||
|
||||
const copyBody = (
|
||||
<div className={`flex w-full min-w-0 flex-col gap-3 break-words${copyBodyClassName ? ` ${copyBodyClassName}` : ''}`}>
|
||||
<div
|
||||
className={`flex w-full min-w-0 flex-col gap-3 break-words${copyBodyClassName ? ` ${copyBodyClassName}` : ''}`}
|
||||
>
|
||||
<h2 className="text-h4-sans md:w-[377px]">
|
||||
{hasMobileTitle ? (
|
||||
<>
|
||||
@@ -136,7 +140,7 @@ export function OverviewMediaPanel({
|
||||
)}
|
||||
</h2>
|
||||
{mobileBody !== undefined && mobileBody.length > 0 ? (
|
||||
<div className="flex max-w-full flex-col gap-3 text-[12px] leading-[1.2] font-medium text-brand-dark-green md:hidden">
|
||||
<div className="flex max-w-full flex-col gap-3 text-xs leading-[1.2] font-medium text-brand-dark-green md:hidden">
|
||||
{mobileBody.map((paragraph) => (
|
||||
<p key={paragraph}>{paragraph}</p>
|
||||
))}
|
||||
@@ -144,7 +148,7 @@ export function OverviewMediaPanel({
|
||||
) : null}
|
||||
{body.length > 0 ? (
|
||||
<div
|
||||
className={`max-w-full flex-col gap-3 text-[12px] leading-[1.2] font-medium text-brand-dark-green md:w-[485px] ${
|
||||
className={`max-w-full flex-col gap-3 text-xs leading-[1.2] font-medium text-brand-dark-green md:w-[485px] ${
|
||||
hasMobileBody ? 'hidden md:flex' : 'flex'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { TechDetailHero } from '@acid-info/logos-ui'
|
||||
import type { HeroSection } from '@repo/content/schemas'
|
||||
|
||||
import {
|
||||
Button,
|
||||
ButtonArrowIcon,
|
||||
type ButtonVariant,
|
||||
} from '@/components/ui'
|
||||
import { Button, ButtonArrowIcon, type ButtonVariant } from '@/components/ui'
|
||||
import { Link } from '@/i18n/navigation'
|
||||
import { resolveBasecampInstallCtaLinkProps } from '@/lib/basecamp-release-links'
|
||||
|
||||
@@ -53,7 +49,7 @@ export default function TechStackDetailHero({
|
||||
<span className="inline-flex size-3.75 shrink-0 rotate-180 items-center justify-center">
|
||||
<ButtonArrowIcon />
|
||||
</span>
|
||||
<span className="font-mono text-[10px] leading-[1.3] font-medium uppercase">
|
||||
<span className="font-mono text-xs leading-[1.3] font-medium uppercase">
|
||||
{data.eyebrow}
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
@@ -38,7 +38,7 @@ function StackItemCta({
|
||||
className={`inline-flex items-center justify-center rounded-xl bg-brand-dark-green px-3 py-2 text-brand-off-white backdrop-blur-[5px] ${className}`}
|
||||
>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold whitespace-nowrap uppercase">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold whitespace-nowrap uppercase">
|
||||
{children}
|
||||
</span>
|
||||
{icon}
|
||||
@@ -234,7 +234,7 @@ function HoverStackItem({
|
||||
key={detail.title}
|
||||
className="flex h-[50px] items-center justify-center rounded-[18px] border border-brand-dark-green/50 px-2 text-brand-dark-green"
|
||||
>
|
||||
<p className="text-center font-mono text-[10px] leading-[1.3] font-medium uppercase">
|
||||
<p className="text-center font-mono text-xs leading-[1.3] font-medium uppercase">
|
||||
{detail.title}
|
||||
</p>
|
||||
</div>
|
||||
@@ -248,10 +248,10 @@ function HoverStackItem({
|
||||
key={detail.title}
|
||||
className="rounded-[18px] border border-brand-dark-green/50 px-4 py-3 text-left text-brand-dark-green"
|
||||
>
|
||||
<p className="font-mono text-[10px] leading-[1.25] font-semibold tracking-[0.08em] uppercase">
|
||||
<p className="font-mono text-xs leading-[1.25] font-semibold tracking-[0.08em] uppercase">
|
||||
{detail.title}
|
||||
</p>
|
||||
<p className="mt-1.5 font-mono text-[10px] leading-[1.25]">
|
||||
<p className="mt-1.5 font-mono text-xs leading-[1.25]">
|
||||
{detail.body}
|
||||
</p>
|
||||
</div>
|
||||
@@ -267,12 +267,7 @@ function HoverStackItem({
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
target={target}
|
||||
rel={rel}
|
||||
className={containerClassName}
|
||||
>
|
||||
<Link href={href} target={target} rel={rel} className={containerClassName}>
|
||||
{content}
|
||||
</Link>
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function TechOverviewModular({ data }: Props) {
|
||||
</h2>
|
||||
|
||||
{data.body && data.body.length > 0 ? (
|
||||
<div className="mt-15 flex w-full flex-col gap-3 text-left text-[12px] leading-[1.2] font-medium text-brand-dark-green lg:mt-10">
|
||||
<div className="mt-15 flex w-full flex-col gap-3 text-left text-xs leading-[1.2] font-medium text-brand-dark-green lg:mt-10">
|
||||
{data.body.map((paragraph) => (
|
||||
<p key={paragraph}>{paragraph}</p>
|
||||
))}
|
||||
|
||||
@@ -5,6 +5,7 @@ import Image from 'next/image'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
import type { HomepageHighlight } from '@repo/content/schemas'
|
||||
import { XIcon } from '@acid-info/logos-ui'
|
||||
|
||||
import { IconMask } from '@/components/icons/icon-mask'
|
||||
import { ExternalLink } from '@/components/ui/external-link'
|
||||
@@ -40,36 +41,53 @@ function HighlightLink({
|
||||
|
||||
export function HomepageHighlightCard({
|
||||
data,
|
||||
onDismiss,
|
||||
}: {
|
||||
data: HomepageHighlight
|
||||
onDismiss: () => void
|
||||
}) {
|
||||
return (
|
||||
<HighlightLink
|
||||
href={data.cta.href}
|
||||
<div
|
||||
className={clsx(
|
||||
'group absolute z-10 flex cursor-pointer rounded-[6px] transition-opacity hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
|
||||
'top-2 left-3 w-[calc(100vw-24px)] max-w-none items-center gap-3 bg-gray-01 py-0.5 pr-1 pl-0.5 text-brand-dark-green focus-visible:outline-brand-dark-green',
|
||||
'md:left-[119px] md:w-[calc(50vw-137px)] md:max-w-[345px] md:items-start md:gap-2 md:bg-gray-06 md:p-1 md:text-brand-off-white md:focus-visible:outline-brand-off-white'
|
||||
'absolute top-2 left-3 z-10 w-[calc(100vw-24px)] max-w-none rounded-[6px] bg-gray-01 text-brand-dark-green',
|
||||
'md:left-[119px] md:w-[calc(50vw-137px)] md:max-w-[345px] md:bg-gray-06 md:text-brand-off-white'
|
||||
)}
|
||||
>
|
||||
<span className="relative h-[65px] w-[65px] shrink-0 overflow-hidden rounded-[4px] md:h-11 md:w-[37px]">
|
||||
<Image
|
||||
src={data.image.src}
|
||||
alt={data.image.alt}
|
||||
fill
|
||||
sizes="(max-width: 767px) 65px, 37px"
|
||||
className="object-cover"
|
||||
/>
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-1 flex-col items-start justify-center gap-[3px] py-1 md:justify-start md:py-0">
|
||||
<span className="text-mono-s block w-full text-brand-dark-green md:text-brand-off-white">
|
||||
{data.body}
|
||||
<HighlightLink
|
||||
href={data.cta.href}
|
||||
className={clsx(
|
||||
'group flex cursor-pointer items-center gap-3 rounded-[6px] py-0.5 pr-7 pl-0.5 transition-opacity hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-dark-green',
|
||||
'md:items-start md:gap-2 md:p-1 md:focus-visible:outline-brand-off-white'
|
||||
)}
|
||||
>
|
||||
<span className="relative h-[65px] w-[65px] shrink-0 overflow-hidden rounded-[4px] md:h-11 md:w-[37px]">
|
||||
<Image
|
||||
src={data.image.src}
|
||||
alt={data.image.alt}
|
||||
fill
|
||||
sizes="(max-width: 767px) 65px, 37px"
|
||||
className="object-cover"
|
||||
/>
|
||||
</span>
|
||||
<span className="flex items-center gap-1 font-mono text-[10px] leading-[1.35] font-semibold text-brand-dark-green uppercase md:text-brand-off-white">
|
||||
{data.cta.label}
|
||||
<ArrowIcon />
|
||||
<span className="flex min-w-0 flex-1 flex-col items-start justify-center gap-[3px] py-1 md:justify-start md:py-0">
|
||||
<span className="block w-full font-mono-body text-[10px] leading-[1.3] text-brand-dark-green md:text-brand-off-white">
|
||||
{data.body}
|
||||
</span>
|
||||
<span className="flex items-center gap-1 font-mono text-[10px] leading-[1.35] font-semibold text-brand-dark-green uppercase md:text-brand-off-white">
|
||||
{data.cta.label}
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</HighlightLink>
|
||||
</HighlightLink>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={onDismiss}
|
||||
aria-label={data.dismissAriaLabel}
|
||||
className="absolute top-1 right-1 z-10 inline-flex size-5 cursor-pointer items-center justify-center rounded-full border border-current transition-opacity hover:opacity-70 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
|
||||
>
|
||||
<XIcon size={12} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,10 +57,14 @@ export default function SiteHeaderClient({
|
||||
null
|
||||
)
|
||||
const [hasPassedHero, setHasPassedHero] = useState(false)
|
||||
const [isHomepageHighlightDismissed, setIsHomepageHighlightDismissed] =
|
||||
useState(false)
|
||||
const pathname = usePathname()
|
||||
const normalizedPathname = pathname.replace(/\/$/, '') || ROUTES.home
|
||||
const showHomepageHighlight =
|
||||
normalizedPathname === ROUTES.home && homepageHighlight?.enabled === true
|
||||
normalizedPathname === ROUTES.home &&
|
||||
homepageHighlight?.enabled === true &&
|
||||
!isHomepageHighlightDismissed
|
||||
|
||||
const usesHeroHeaderTone =
|
||||
normalizedPathname === ROUTES.home ||
|
||||
@@ -138,7 +142,10 @@ export default function SiteHeaderClient({
|
||||
)}
|
||||
>
|
||||
{showHomepageHighlight ? (
|
||||
<HomepageHighlightCard data={homepageHighlight} />
|
||||
<HomepageHighlightCard
|
||||
data={homepageHighlight}
|
||||
onDismiss={() => setIsHomepageHighlightDismissed(true)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<a
|
||||
@@ -188,7 +195,10 @@ export default function SiteHeaderClient({
|
||||
)}
|
||||
>
|
||||
{showHomepageHighlight ? (
|
||||
<HomepageHighlightCard data={homepageHighlight} />
|
||||
<HomepageHighlightCard
|
||||
data={homepageHighlight}
|
||||
onDismiss={() => setIsHomepageHighlightDismissed(true)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<a
|
||||
|
||||
@@ -93,7 +93,7 @@ export const EXTERNAL_URLS = {
|
||||
// Social
|
||||
twitter: 'https://twitter.com/logos_network',
|
||||
twitterDevs: 'https://x.com/logos_devs',
|
||||
discord: 'https://discord.gg/logosnetwork',
|
||||
discord: 'https://discord.gg/Ykv4eZyHUJ',
|
||||
youtube: 'https://youtube.com/@logos_network',
|
||||
github: 'https://github.com/logos-co',
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ const repoPressArticlePaths = [
|
||||
'packages/content/src/schemas/press.ts',
|
||||
].map((path) => join(repoRoot, path))
|
||||
const jobsHref = 'https://free.technology/jobs'
|
||||
const discordHref = 'https://discord.gg/logosnetwork'
|
||||
const discordHref = 'https://discord.gg/Ykv4eZyHUJ'
|
||||
const onboardingCalendarHref = 'https://cal.com/team/logos-onboarding/intro'
|
||||
const logosDocsHref = 'https://docs.logos.co/'
|
||||
const communityIdeasHref = 'https://github.com/logos-co/ideas'
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"featured": false,
|
||||
"order": 6,
|
||||
"submittedAt": "2026-03-04T00:00:00Z",
|
||||
"discussionUrl": "https://discord.gg/logosnetwork"
|
||||
"discussionUrl": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"featured": false,
|
||||
"order": 7,
|
||||
"submittedAt": "2026-03-12T00:00:00Z",
|
||||
"discussionUrl": "https://discord.gg/logosnetwork"
|
||||
"discussionUrl": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
"featured": false,
|
||||
"order": 2,
|
||||
"submittedAt": "2026-01-22T00:00:00Z",
|
||||
"discussionUrl": "https://discord.gg/logosnetwork"
|
||||
"discussionUrl": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
"featured": false,
|
||||
"order": 5,
|
||||
"submittedAt": "2026-02-26T00:00:00Z",
|
||||
"discussionUrl": "https://discord.gg/logosnetwork"
|
||||
"discussionUrl": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"featured": false,
|
||||
"order": 3,
|
||||
"submittedAt": "2026-02-09T00:00:00Z",
|
||||
"discussionUrl": "https://discord.gg/logosnetwork"
|
||||
"discussionUrl": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"featured": false,
|
||||
"order": 4,
|
||||
"submittedAt": "2026-02-18T00:00:00Z",
|
||||
"discussionUrl": "https://discord.gg/logosnetwork"
|
||||
"discussionUrl": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"featured": true,
|
||||
"order": 1,
|
||||
"submittedAt": "2026-01-04T00:00:00Z",
|
||||
"discussionUrl": "https://discord.gg/logosnetwork"
|
||||
"discussionUrl": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"breadcrumbLabel": "Ideas",
|
||||
"submitCta": {
|
||||
"label": "Submit an idea",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
},
|
||||
"defaultView": "list",
|
||||
@@ -20,7 +20,7 @@
|
||||
"title": "We want your ideas.",
|
||||
"cta": {
|
||||
"label": "Submit an idea",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"breadcrumbLabel": "RFPs",
|
||||
"submitCta": {
|
||||
"label": "Submit an RFP",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
},
|
||||
"defaultView": "grid",
|
||||
@@ -20,7 +20,7 @@
|
||||
"title": "Have a build idea worth funding?",
|
||||
"cta": {
|
||||
"label": "Submit an RFP",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"slug": "attack-resistant-public-registries",
|
||||
"status": "published",
|
||||
"reward": { "amount": 3000, "currency": "USDC", "xp": 1200 },
|
||||
"applyUrl": "https://discord.gg/logosnetwork",
|
||||
"applyUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"tags": ["registry", "censorship-resistance", "consensus"],
|
||||
"image": {
|
||||
"src": "/cms/builders-hub/rfps/attack-resistant-public-registries/cover.webp",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"slug": "build-a-dex",
|
||||
"status": "published",
|
||||
"reward": { "amount": 5000, "currency": "USDC", "xp": 1500 },
|
||||
"applyUrl": "https://discord.gg/logosnetwork",
|
||||
"applyUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"tags": ["defi", "trading", "ux"],
|
||||
"image": {
|
||||
"src": "/cms/builders-hub/rfps/build-a-dex/cover.webp",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"slug": "community-governance-tooling",
|
||||
"status": "published",
|
||||
"reward": { "amount": 1800, "currency": "USDC", "xp": 600 },
|
||||
"applyUrl": "https://discord.gg/logosnetwork",
|
||||
"applyUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"tags": ["governance", "circles", "voting"],
|
||||
"image": {
|
||||
"src": "/cms/builders-hub/rfps/community-governance-tooling/cover.webp",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"slug": "decentralised-archives",
|
||||
"status": "published",
|
||||
"reward": { "amount": 2000, "currency": "USDC", "xp": 800 },
|
||||
"applyUrl": "https://discord.gg/logosnetwork",
|
||||
"applyUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"tags": ["archives", "preservation", "codex"],
|
||||
"image": {
|
||||
"src": "/cms/builders-hub/rfps/decentralised-archives/cover.webp",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"slug": "integrate-fileverse",
|
||||
"status": "published",
|
||||
"reward": { "amount": 1500, "currency": "USDC", "xp": 500 },
|
||||
"applyUrl": "https://discord.gg/logosnetwork",
|
||||
"applyUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"tags": ["files", "collaboration", "integration"],
|
||||
"image": {
|
||||
"src": "/cms/builders-hub/rfps/integrate-fileverse/cover.webp",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"slug": "private-financial-networks",
|
||||
"status": "published",
|
||||
"reward": { "amount": 4000, "currency": "USDC", "xp": 1500 },
|
||||
"applyUrl": "https://discord.gg/logosnetwork",
|
||||
"applyUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"tags": ["payments", "privacy", "circles"],
|
||||
"image": {
|
||||
"src": "/cms/builders-hub/rfps/private-financial-networks/cover.webp",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"slug": "secure-decentralised-frontends",
|
||||
"status": "published",
|
||||
"reward": { "amount": 2500, "currency": "USDC", "xp": 1000 },
|
||||
"applyUrl": "https://discord.gg/logosnetwork",
|
||||
"applyUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"tags": ["frontend", "privacy", "infrastructure"],
|
||||
"image": {
|
||||
"src": "/cms/builders-hub/rfps/secure-decentralised-frontends/cover.webp",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"secondaryCtas": [
|
||||
{
|
||||
"label": "Join Discord",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
},
|
||||
{
|
||||
@@ -174,7 +174,7 @@
|
||||
],
|
||||
"cta": {
|
||||
"label": "Add to calendar",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
}
|
||||
},
|
||||
@@ -208,7 +208,7 @@
|
||||
"description": "Chat with us and get real time support.",
|
||||
"cta": {
|
||||
"label": "Join",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
}
|
||||
},
|
||||
@@ -274,7 +274,7 @@
|
||||
"seeAllCta": { "label": "Explore RFPs", "href": "/builders-hub/rfps" },
|
||||
"submitCta": {
|
||||
"label": "Submit an RFP",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
},
|
||||
"displayCount": 8,
|
||||
@@ -301,7 +301,7 @@
|
||||
"seeAllCta": { "label": "See all ideas", "href": "/builders-hub/ideas" },
|
||||
"submitCta": {
|
||||
"label": "Submit an idea",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
},
|
||||
"displayCount": 7
|
||||
@@ -357,7 +357,7 @@
|
||||
"description": "Talk to Logos core contributors.",
|
||||
"cta": {
|
||||
"label": "Join",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"timezone": "America/Sao_Paulo",
|
||||
"memberCount": 38,
|
||||
"discordChannel": "circle-florianopolis",
|
||||
"discordUrl": "https://discord.gg/logosnetwork",
|
||||
"discordUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"forumUrl": "https://forum.logos.co/",
|
||||
"joinUrl": "/circles#resources",
|
||||
"image": {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"timezone": "Europe/London",
|
||||
"memberCount": 112,
|
||||
"discordChannel": "circle-london",
|
||||
"discordUrl": "https://discord.gg/logosnetwork",
|
||||
"discordUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"forumUrl": "https://forum.logos.co/",
|
||||
"joinUrl": "/circles#resources",
|
||||
"image": {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"timezone": "Europe/Lisbon",
|
||||
"memberCount": 47,
|
||||
"discordChannel": "circle-porto",
|
||||
"discordUrl": "https://discord.gg/logosnetwork",
|
||||
"discordUrl": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"forumUrl": "https://forum.logos.co/",
|
||||
"joinUrl": "/circles#resources",
|
||||
"image": {
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
"label": "Connect with Logos",
|
||||
"body": "Join the community channels for implementation support and feedback.",
|
||||
"action": "Connect",
|
||||
"href": "https://discord.gg/logosnetwork"
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
{
|
||||
"label": "Discord",
|
||||
"href": "https://discord.gg/logosnetwork",
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"external": true
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"homepageHighlight": {
|
||||
"enabled": true,
|
||||
"body": "Testnet v0.2 is live for experimentation. Introducing improvements to the entire stack.",
|
||||
"dismissAriaLabel": "Dismiss announcement",
|
||||
"cta": {
|
||||
"label": "Learn More",
|
||||
"href": "https://blog.logos.co/article/testnet-v02-live"
|
||||
@@ -71,7 +72,7 @@
|
||||
{ "label": "Events", "href": "https://luma.com/logosevents" },
|
||||
{
|
||||
"label": "Join Community",
|
||||
"href": "https://discord.gg/logosnetwork"
|
||||
"href": "https://discord.gg/Ykv4eZyHUJ"
|
||||
},
|
||||
{ "label": "Install Basecamp", "href": "/basecamp" },
|
||||
{ "label": "Work with us", "href": "https://free.technology/jobs" }
|
||||
@@ -127,7 +128,7 @@
|
||||
{
|
||||
"label": "Socials",
|
||||
"links": [
|
||||
{ "label": "Discord", "href": "https://discord.gg/logosnetwork" },
|
||||
{ "label": "Discord", "href": "https://discord.gg/Ykv4eZyHUJ" },
|
||||
{ "label": "X", "href": "https://twitter.com/logos_network" },
|
||||
{
|
||||
"label": "YouTube",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
],
|
||||
"social": {
|
||||
"twitter": "https://twitter.com/logos_network",
|
||||
"discord": "https://discord.gg/logosnetwork",
|
||||
"discord": "https://discord.gg/Ykv4eZyHUJ",
|
||||
"youtube": "https://youtube.com/@logos_network",
|
||||
"github": "https://github.com/logos-co"
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export type NavLink = z.infer<typeof navLinkSchema>
|
||||
export const homepageHighlightSchema = z.object({
|
||||
enabled: z.boolean(),
|
||||
body: z.string().min(1),
|
||||
dismissAriaLabel: z.string().min(1),
|
||||
cta: navLinkSchema,
|
||||
image: mediaRefSchema,
|
||||
})
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
@utility text-eyebrow {
|
||||
font-family: var(--font-mono);
|
||||
font-weight: 500; /* Fira Code Medium */
|
||||
font-size: 0.625rem; /* 10px */
|
||||
font-size: 0.75rem; /* 12px */
|
||||
line-height: 1.3;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
@@ -162,7 +162,7 @@
|
||||
@utility text-mono-s {
|
||||
font-family: var(--font-mono-body);
|
||||
font-weight: 400; /* Fira Mono Regular */
|
||||
font-size: 0.625rem; /* 10px */
|
||||
font-size: 0.75rem; /* 12px */
|
||||
line-height: 1.3;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export function Button(props: ButtonProps) {
|
||||
)
|
||||
|
||||
const isLink = variant === 'link'
|
||||
const labelClass = `font-mono text-[10px] leading-[1.35] font-semibold uppercase whitespace-nowrap ${
|
||||
const labelClass = `font-mono text-xs leading-[1.35] font-semibold uppercase whitespace-nowrap ${
|
||||
isLink ? 'border-b border-brand-dark-green/50 pb-0.5' : ''
|
||||
}`
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ export function Card({
|
||||
</div>
|
||||
{description && (
|
||||
<p
|
||||
className={`max-w-[222px] text-center font-mono text-[10px] leading-[1.3] text-brand-dark-green transition-opacity duration-200 ${revealClass}`}
|
||||
className={`max-w-[222px] text-center font-mono text-xs leading-[1.3] text-brand-dark-green transition-opacity duration-200 ${revealClass}`}
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
@@ -149,7 +149,7 @@ function CardCTA({
|
||||
const className =
|
||||
'absolute top-3 right-3 z-10 inline-flex items-center rounded-xl bg-brand-dark-green px-3 py-2 backdrop-blur-[5px]'
|
||||
const content = (
|
||||
<span className="font-mono text-[10px] leading-[1.35] font-semibold text-brand-off-white uppercase">
|
||||
<span className="font-mono text-xs leading-[1.35] font-semibold text-brand-off-white uppercase">
|
||||
{label}
|
||||
</span>
|
||||
)
|
||||
@@ -194,10 +194,10 @@ export function CardInfo({
|
||||
: 'flex flex-col gap-0.5'
|
||||
}
|
||||
>
|
||||
<p className="font-mono text-[10px] leading-[1.3] font-medium text-brand-dark-green uppercase">
|
||||
<p className="font-mono text-xs leading-[1.3] font-medium text-brand-dark-green uppercase">
|
||||
{label}
|
||||
</p>
|
||||
<p className="font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -240,7 +240,7 @@ function ActionCard({
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-end justify-between gap-4">
|
||||
<p className="max-w-[326px] font-sans text-[12px] font-medium leading-[1.2]">
|
||||
<p className="max-w-[326px] font-sans text-xs font-medium leading-[1.2]">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
@@ -278,7 +278,7 @@ function MenuCard({
|
||||
<p className="mr-[54px] max-w-[148px] font-sans text-[14px] leading-[1.2]">
|
||||
{label}
|
||||
</p>
|
||||
<p className="font-sans text-[12px] font-medium leading-[1.2] text-brand-off-white/50">
|
||||
<p className="font-sans text-xs font-medium leading-[1.2] text-brand-off-white/50">
|
||||
{description}
|
||||
</p>
|
||||
{imageNodes.length > 0 && (
|
||||
|
||||
@@ -32,7 +32,7 @@ export function Table({
|
||||
</div>
|
||||
<div className="flex flex-1 items-start gap-6">
|
||||
{subtitle && (
|
||||
<p className="max-w-[226px] font-mono text-[10px] leading-[1.3] text-brand-dark-green">
|
||||
<p className="max-w-[226px] font-mono text-xs leading-[1.3] text-brand-dark-green">
|
||||
{subtitle}
|
||||
</p>
|
||||
)}
|
||||
@@ -85,12 +85,12 @@ export function TableRow({
|
||||
</div>
|
||||
|
||||
{/* Description — 464/1440 */}
|
||||
<div className="hidden flex-[464] font-mono text-[10px] leading-[1.3] text-brand-dark-green md:block [&>p]:leading-[1.3]">
|
||||
<div className="hidden flex-[464] font-mono text-xs leading-[1.3] text-brand-dark-green md:block [&>p]:leading-[1.3]">
|
||||
{description}
|
||||
</div>
|
||||
|
||||
{/* Reward — fixed 107px */}
|
||||
<div className="hidden w-[107px] shrink-0 font-mono text-[10px] leading-[1.3] text-brand-dark-green md:block [&>p]:leading-[1.3]">
|
||||
<div className="hidden w-[107px] shrink-0 font-mono text-xs leading-[1.3] text-brand-dark-green md:block [&>p]:leading-[1.3]">
|
||||
{reward}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user