From c62dcd16ccf4b0758f6ff7e0bc1b6691a4b1fc8e Mon Sep 17 00:00:00 2001 From: Oladapo <263417777+DANTE-1903@users.noreply.github.com> Date: Sun, 18 Oct 2026 13:52:28 +0000 Subject: [PATCH 1/3] chore(cleanup): remove unused components, dead helpers, and orphaned dependencies Merge after #402 and #404. Both remove code, which leaves more files unused. Context -------- Much of components/ was copied from the main Boundless app and the old bounties app, and nothing here imports it. Unused code makes the repo harder to read, sends contributors to the wrong examples, and keeps dependencies in package.json for nothing. Scope ------ - Delete the files below, then repeat until nothing is left that only deleted files used (e.g. components/ui/command.tsx, components/marketing/faq-data.ts, lib/crop-image.ts). - Remove the dependencies that end up unused and regenerate package-lock.json with npm install. - Remove images in public/ that only the deleted files referenced. Removed components ------------------- - components/marketing/: article-card, brand-kit-cta, cta-pill, cta-ticker, cta-band(-kept), escrow-flow, faq (+faq-accordion, faq-data), four-steps, funding-paths, funding-programs, get-started-modal, intro-section, personas, prize-pool, team-section, testimonials, trust-bar, youtube-embed, explore-path, glow, pillar-card, placeholder - components/ui/: avatar-group, command, country-select, dialog, entity-row, form, glow-banner, hover-card, image-cropper, input, option-card, select, snapshot-card, sonner, sparks-icon, success-dialog, tabs, textarea, token-amount, label, popover Removed helpers and dependencies --------------------------------- - lib/crop-image.ts (only used by deleted image-cropper) - Dependencies unused after the above: react-hook-form, react-easy-crop, sonner, cmdk Removed public assets ---------------------- - brand/Boundless-mark-card.svg, brand/boundless-mark.svg, brand/usdc.png, currency/sparks.svg, onboarding/success-badge.svg, social/linkedin.svg, team/team1.svg, team/team2.svg, team/team3.svg, team/team4.svg - (plus a handful of other public images that are unreferenced after this cleanup) Standing rules -------------- - If something here turns out to be needed later, port it from the main app again; that is the repo's rule in README.md. Co-Authored-By: Buffy --- components/icons/credit-bold.tsx | 44 ----- components/marketing/article-card.tsx | 6 - components/marketing/brand-kit-cta.tsx | 47 ----- components/marketing/cta-pill.tsx | 56 ------ components/marketing/cta-ticker.tsx | 81 -------- components/marketing/escrow-flow.tsx | 134 ------------- components/marketing/faq-accordion.tsx | 77 -------- components/marketing/faq-data.ts | 85 -------- components/marketing/faq.tsx | 37 ---- components/marketing/four-steps.tsx | 11 -- components/marketing/funding-paths.tsx | 93 --------- components/marketing/funding-programs.tsx | 219 --------------------- components/marketing/get-started-modal.tsx | 94 --------- components/marketing/intro-section.tsx | 54 ----- components/marketing/personas.tsx | 120 ----------- components/marketing/prize-pool.tsx | 108 ---------- components/marketing/team-section.tsx | 94 --------- components/marketing/testimonials.tsx | 180 ----------------- components/marketing/trust-bar.tsx | 20 -- components/marketing/youtube-embed.tsx | 44 ----- components/ui/avatar-group.tsx | 137 ------------- components/ui/command.tsx | 91 --------- components/ui/country-select.tsx | 119 ----------- components/ui/dialog.tsx | 160 --------------- components/ui/entity-row.tsx | 111 ----------- components/ui/form.tsx | 168 ---------------- components/ui/glow-banner.tsx | 60 ------ components/ui/hover-card.tsx | 44 ----- components/ui/image-cropper.tsx | 194 ------------------ components/ui/input.tsx | 175 ---------------- components/ui/option-card.tsx | 70 ------- components/ui/select.tsx | 183 ----------------- components/ui/snapshot-card.tsx | 82 -------- components/ui/sonner.tsx | 40 ---- components/ui/sparks-icon.tsx | 19 -- components/ui/success-dialog.tsx | 152 -------------- components/ui/tabs.tsx | 55 ------ components/ui/textarea.tsx | 69 ------- components/ui/token-amount.tsx | 75 ------- lib/crop-image.ts | 89 --------- package-lock.json | 66 ------- package.json | 4 - public/brand/Boundless-mark-card.svg | 66 ------- public/brand/boundless-mark.svg | 4 - public/brand/usdc.png | Bin 12143 -> 0 bytes public/currency/sparks.svg | 15 -- public/onboarding/success-badge.svg | 9 - public/social/linkedin.svg | 5 - public/team/team1.svg | 9 - public/team/team2.svg | 9 - public/team/team3.svg | 9 - public/team/team4.svg | 9 - 52 files changed, 3902 deletions(-) delete mode 100644 components/icons/credit-bold.tsx delete mode 100644 components/marketing/article-card.tsx delete mode 100644 components/marketing/brand-kit-cta.tsx delete mode 100644 components/marketing/cta-pill.tsx delete mode 100644 components/marketing/cta-ticker.tsx delete mode 100644 components/marketing/escrow-flow.tsx delete mode 100644 components/marketing/faq-accordion.tsx delete mode 100644 components/marketing/faq-data.ts delete mode 100644 components/marketing/faq.tsx delete mode 100644 components/marketing/four-steps.tsx delete mode 100644 components/marketing/funding-paths.tsx delete mode 100644 components/marketing/funding-programs.tsx delete mode 100644 components/marketing/get-started-modal.tsx delete mode 100644 components/marketing/intro-section.tsx delete mode 100644 components/marketing/personas.tsx delete mode 100644 components/marketing/prize-pool.tsx delete mode 100644 components/marketing/team-section.tsx delete mode 100644 components/marketing/testimonials.tsx delete mode 100644 components/marketing/trust-bar.tsx delete mode 100644 components/marketing/youtube-embed.tsx delete mode 100644 components/ui/avatar-group.tsx delete mode 100644 components/ui/command.tsx delete mode 100644 components/ui/country-select.tsx delete mode 100644 components/ui/dialog.tsx delete mode 100644 components/ui/entity-row.tsx delete mode 100644 components/ui/form.tsx delete mode 100644 components/ui/glow-banner.tsx delete mode 100644 components/ui/hover-card.tsx delete mode 100644 components/ui/image-cropper.tsx delete mode 100644 components/ui/input.tsx delete mode 100644 components/ui/option-card.tsx delete mode 100644 components/ui/select.tsx delete mode 100644 components/ui/snapshot-card.tsx delete mode 100644 components/ui/sonner.tsx delete mode 100644 components/ui/sparks-icon.tsx delete mode 100644 components/ui/success-dialog.tsx delete mode 100644 components/ui/tabs.tsx delete mode 100644 components/ui/textarea.tsx delete mode 100644 components/ui/token-amount.tsx delete mode 100644 lib/crop-image.ts delete mode 100644 public/brand/Boundless-mark-card.svg delete mode 100644 public/brand/boundless-mark.svg delete mode 100644 public/brand/usdc.png delete mode 100644 public/currency/sparks.svg delete mode 100644 public/onboarding/success-badge.svg delete mode 100644 public/social/linkedin.svg delete mode 100644 public/team/team1.svg delete mode 100644 public/team/team2.svg delete mode 100644 public/team/team3.svg delete mode 100644 public/team/team4.svg diff --git a/components/icons/credit-bold.tsx b/components/icons/credit-bold.tsx deleted file mode 100644 index 23476ea0..00000000 --- a/components/icons/credit-bold.tsx +++ /dev/null @@ -1,44 +0,0 @@ -// Generated by scripts/generate-icons.mjs from svg/bold/credit.svg. Do not edit. -import type { SVGProps } from 'react'; - -export function CreditBoldIcon(props: SVGProps) { - return ( - - - - - - - - - - - - - - ); -} diff --git a/components/marketing/article-card.tsx b/components/marketing/article-card.tsx deleted file mode 100644 index e43c611f..00000000 --- a/components/marketing/article-card.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { Placeholder } from './placeholder'; - -/** Blog article card. Plain scaffold pending design. */ -export function ArticleCard() { - return ; -} diff --git a/components/marketing/brand-kit-cta.tsx b/components/marketing/brand-kit-cta.tsx deleted file mode 100644 index cf3dfc0f..00000000 --- a/components/marketing/brand-kit-cta.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Download } from 'lucide-react'; -import Image from 'next/image'; - -import { cn } from '@/lib/utils'; - -import { MarketingButton } from './marketing-button'; -import { Reveal } from './reveal'; -import { Section } from './section'; - -/** Brand kit call to action: logo + heading on the left, download button right. */ -export function BrandKitCta({ className }: { className?: string }) { - return ( -
- -
-
- -
- -
-

- Brand Kit -

-

- Get the latest
- Boundless Brand kit -

-
-
- - - - Download All - - - -
-
- ); -} diff --git a/components/marketing/cta-pill.tsx b/components/marketing/cta-pill.tsx deleted file mode 100644 index e2bbc452..00000000 --- a/components/marketing/cta-pill.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { LucideIcon } from 'lucide-react'; - -import { cn } from '@/lib/utils'; - -type CtaPillProps = { - label: string; - icon: LucideIcon; - iconRight?: boolean; - active?: boolean; - className?: string; -}; - -const SHADOW_ACTIVE = - 'shadow-[0px_5px_5px_0px_rgba(0,0,0,0.05),0px_15px_15px_0px_rgba(0,0,0,0.05),0px_30px_30px_0px_rgba(0,0,0,0.1),inset_0px_0px_100px_0px_rgba(212,255,240,0.04)]'; -const SHADOW_IDLE = - 'shadow-[0px_5px_10px_0px_rgba(0,0,0,0.05),0px_15px_30px_0px_rgba(0,0,0,0.05),0px_30px_60px_0px_rgba(0,0,0,0.1),inset_0px_0px_100px_0px_rgba(212,255,240,0.04)]'; - -/** Pill-shaped product CTA. Enlarges and turns solid green when active. */ -export function CtaPill({ - label, - icon: Icon, - iconRight, - active, - className, -}: CtaPillProps) { - const icon = ( - - ); - return ( -
- {!iconRight && icon} - - {label} - - {iconRight && icon} -
- ); -} diff --git a/components/marketing/cta-ticker.tsx b/components/marketing/cta-ticker.tsx deleted file mode 100644 index b6755b62..00000000 --- a/components/marketing/cta-ticker.tsx +++ /dev/null @@ -1,81 +0,0 @@ -'use client'; - -import type { LucideIcon } from 'lucide-react'; -import { ArrowUpRight, Pencil, Plus, Rocket, User } from 'lucide-react'; -import { useEffect, useState } from 'react'; - -import { cn } from '@/lib/utils'; - -import { CtaPill } from './cta-pill'; - -type Cta = { label: string; icon: LucideIcon; iconRight?: boolean }; - -const CTAS: Cta[] = [ - { label: 'Post', icon: Pencil }, - { label: 'Launch', icon: Rocket }, - { label: 'Create', icon: Plus }, - { label: 'Enter', icon: ArrowUpRight, iconRight: true }, - { label: 'Join', icon: User }, -]; - -const ROW = 47; // pitch: 39px pill + 8px gap -const TRACK = [...CTAS, ...CTAS, ...CTAS]; // copies for a seamless loop -const START = CTAS.length + 2; // centers "Create" first, matching the design -const RESET_AT = CTAS.length * 2; // snap back one cycle once we reach the copy -const STEP_MS = 1800; -const SLIDE_MS = 600; - -/** Vertical CTA picker that scrolls action pills through a highlighted center slot. */ -export function CtaTicker({ className }: { className?: string }) { - const [step, setStep] = useState(START); - const [sliding, setSliding] = useState(true); - - useEffect(() => { - if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; - const id = setInterval(() => setStep(s => s + 1), STEP_MS); - return () => clearInterval(id); - }, []); - - // After sliding into the duplicate copy, jump back a cycle without animating. - useEffect(() => { - if (step < RESET_AT) return; - const id = setTimeout(() => { - setSliding(false); - setStep(s => s - CTAS.length); - }, SLIDE_MS); - return () => clearTimeout(id); - }, [step]); - - useEffect(() => { - if (sliding) return; - const id = requestAnimationFrame(() => setSliding(true)); - return () => cancelAnimationFrame(id); - }, [sliding]); - - return ( -
-
- {TRACK.map((cta, index) => ( -
- -
- ))} -
-
- ); -} diff --git a/components/marketing/escrow-flow.tsx b/components/marketing/escrow-flow.tsx deleted file mode 100644 index 6ddf7cea..00000000 --- a/components/marketing/escrow-flow.tsx +++ /dev/null @@ -1,134 +0,0 @@ -'use client'; - -import { Check, LoaderCircle, Lock } from 'lucide-react'; -import { useReducedMotion } from 'motion/react'; -import { useEffect, useState } from 'react'; - -import { cn } from '@/lib/utils'; - -import { Glow } from './glow'; - -type Step = { title: string; description: string }; - -const STEPS: Step[] = [ - { - title: 'Match', - description: 'Connect with the right contributors or participants.', - }, - { - title: 'Secure', - description: 'Lock funding into escrow before work starts.', - }, -]; - -// Four phases walk the escrow rail through its lifecycle, then loop: -// 0 both pending, 1 Match confirmed, 2 Secure in progress, 3 Secure locked. -const PHASES = 4; -const STEP_MS = 1400; - -/** Pending circle: a dark disc with a faint ring, matching the figma default. */ -function PendingDot() { - return ( - - - - ); -} - -/** Filled circle holding a status icon (check, spinner or lock). */ -function StatusDot({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ); -} - -/** - * "Track Progress" illustration: a glassy escrow card whose Match/Secure rail - * animates through its lifecycle (pending dot, green check, securing spinner, - * locked) with the connector filling green, looping. Honors reduced motion by - * resting on the final secured state. - */ -export function EscrowFlow({ className }: { className?: string }) { - const reduceMotion = useReducedMotion(); - const [step, setStep] = useState(0); - - useEffect(() => { - if (reduceMotion) return; - const id = setInterval(() => setStep(s => (s + 1) % PHASES), STEP_MS); - return () => clearInterval(id); - }, [reduceMotion]); - - // Reduced motion rests on the final secured state instead of looping. - const phase = reduceMotion ? PHASES - 1 : step; - const matchDone = phase >= 1; - const lineFilled = phase >= 1; - - return ( -
- {/* Border glow concentrated at the centre of the left edge (only the - border line glows here), fading to the figma's faint white border. */} -
-
- {/* Green patch near the centre of the right edge (inside, not on the - border). */} - - -
- {/* Indicator rail: Match dot, connector, Secure dot. */} -
- {matchDone ? ( - - - - ) : ( - - )} - - - - - - {phase >= 3 ? ( - - - - ) : phase === 2 ? ( - - - - ) : ( - - )} -
- -
- {STEPS.map(step => ( -
-

- {step.title} -

-

{step.description}

-
- ))} -
-
-
-
- - {/* Top glow: overlays the card from its top edge and bursts out above, - centred and kept transparent so it reads as a soft burst. */} - -
- ); -} diff --git a/components/marketing/faq-accordion.tsx b/components/marketing/faq-accordion.tsx deleted file mode 100644 index 88685bb8..00000000 --- a/components/marketing/faq-accordion.tsx +++ /dev/null @@ -1,77 +0,0 @@ -'use client'; - -import { Minus, Plus } from 'lucide-react'; -import { useState } from 'react'; - -import { cn } from '@/lib/utils'; - -import type { FaqItem } from './faq-data'; - -interface FaqAccordionProps { - items: FaqItem[]; - /** Indexes open on first render (others collapse). Defaults to none. */ - defaultOpen?: number[]; - className?: string; -} - -/** Expandable FAQ list: each row toggles its answer; multiple can stay open. */ -export function FaqAccordion({ - items, - defaultOpen = [], - className, -}: FaqAccordionProps) { - const [open, setOpen] = useState(defaultOpen); - - const toggle = (index: number) => - setOpen(current => - current.includes(index) - ? current.filter(i => i !== index) - : [...current, index] - ); - - return ( -
- {items.map((item, index) => { - const isOpen = open.includes(index); - - return ( -
- -
- ); - })} -
- ); -} diff --git a/components/marketing/faq-data.ts b/components/marketing/faq-data.ts deleted file mode 100644 index 42262999..00000000 --- a/components/marketing/faq-data.ts +++ /dev/null @@ -1,85 +0,0 @@ -export interface FaqItem { - question: string; - answer: string; -} - -/** When the FAQ content was last reviewed; shown under the page title. */ -export const FAQ_LAST_UPDATED = 'Last Updated June 2026'; - -export const FAQ_ITEMS: FaqItem[] = [ - { - question: 'What types of opportunities are available?', - answer: - 'Hackathons, grants, bounties, crowdfunding campaigns, and community initiatives.', - }, - { - question: 'Can I launch my own program?', - answer: - 'Yes. Organizations and communities can create funding programs, challenges, and campaigns.', - }, - { - question: 'Do I need cryptocurrency to use Boundless?', - answer: - 'No. Boundless is designed to help people discover and participate in opportunities regardless of their technical background.', - }, - { - question: 'Who can use Boundless?', - answer: - 'Developers, designers, researchers, founders, creators, students, nonprofits, and communities.', - }, - { - question: 'What is Boundless?', - answer: - 'Boundless is a platform where people can complete work, join hackathons, raise funds, and apply for grants—all from one account.', - }, - { - question: 'How do I get paid?', - answer: - 'Payments are released directly to your Boundless wallet once work or milestones are approved.', - }, - { - question: 'What kind of work can I find on Boundless?', - answer: - 'You can find opportunities in development, design, writing, research, marketing, community management, and more.', - }, - { - question: 'How does escrow work?', - answer: - 'Funds are locked before work begins and released only when agreed requirements are completed.', - }, - { - question: 'Can teams use Boundless?', - answer: - 'Yes. Teams and organizations can post opportunities, run hackathons, launch campaigns, and manage contributors.', - }, - { - question: "What happens if there's a dispute?", - answer: - 'A review process helps resolve disagreements fairly, using submitted work and project requirements as evidence.', - }, - { - question: 'How does reputation work?', - answer: - 'Your activity and completed work contribute to a reputation score that helps showcase your track record.', - }, - { - question: 'Can I use Boundless from any country?', - answer: - 'Boundless is designed for a global audience, though some features may vary depending on local regulations and availability.', - }, - { - question: 'Is there a fee to join?', - answer: - 'Creating an account and exploring the platform is free. Any applicable fees are clearly shown before transactions are made.', - }, - { - question: 'How do I apply for a grant?', - answer: - 'Browse available grant programs, review the requirements, and submit your proposal through the platform.', - }, - { - question: 'What makes Boundless different?', - answer: - 'Boundless combines opportunities, funding, accountability, and reputation into one platform, making it easier to discover, fund, and complete meaningful work.', - }, -]; diff --git a/components/marketing/faq.tsx b/components/marketing/faq.tsx deleted file mode 100644 index 82924189..00000000 --- a/components/marketing/faq.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { ArrowRight } from 'lucide-react'; -import Link from 'next/link'; - -import { FaqAccordion } from './faq-accordion'; -import { FAQ_ITEMS } from './faq-data'; -import { Section } from './section'; - -// The home section previews the first few questions; the rest live on /faq. -const HOME_FAQS = FAQ_ITEMS.slice(0, 4); - -/** Home "Frequently Asked Questions (FAQs)": heading beside a preview accordion. */ -export function Faq() { - return ( -
-
-

- Frequently Asked - - Questions (FAQs) - -

- -
- - - - See More FAQs - - -
-
-
- ); -} diff --git a/components/marketing/four-steps.tsx b/components/marketing/four-steps.tsx deleted file mode 100644 index e2b667a2..00000000 --- a/components/marketing/four-steps.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Placeholder } from './placeholder'; -import { Section } from './section'; - -/** "How it works" four-step flow. Plain scaffold pending design. */ -export function FourSteps() { - return ( -
- -
- ); -} diff --git a/components/marketing/funding-paths.tsx b/components/marketing/funding-paths.tsx deleted file mode 100644 index c13969af..00000000 --- a/components/marketing/funding-paths.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Play } from 'lucide-react'; -import { type ReactNode } from 'react'; - -import { CtaTicker } from './cta-ticker'; -import { EscrowFlow } from './escrow-flow'; -import { ExplorePath } from './explore-path'; -import { Glow } from './glow'; -import { PrizePool } from './prize-pool'; -import { Section } from './section'; - -type Step = { - title: string; - description: string; - illustration: ReactNode; -}; - -const STEPS: Step[] = [ - { - title: 'Discover Opportunities', - description: - 'Find grants, hackathons, bounties and campaigns aligned with your goals.', - illustration: , - }, - { - title: 'Apply or Participate', - description: 'Submit proposals, join teams, or contribute your skills.', - illustration: ( -
- {/* Tight green glow around the highlighted (centre) action. */} - - {/* Arrows pointing at the centre slot. */} - - - -
- ), - }, - { - title: 'Earn Funding or Rewards', - description: - 'Receive support, prizes or payments for successful contributions.', - illustration: , - }, - { - title: 'Track Progress', - description: - 'Stay informed with transparent updates and milestone tracking.', - illustration: , - }, -]; - -/** "Work flows through Boundless in 4 steps": four animated workflow columns. */ -export function FundingPaths() { - return ( -
-

- Work flows through{' '} - Boundless in 4 steps -

- -
- {STEPS.map(step => ( -
-
-

- {step.title} -

-

- {step.description} -

-
- -
- {step.illustration} -
-
- ))} -
-
- ); -} diff --git a/components/marketing/funding-programs.tsx b/components/marketing/funding-programs.tsx deleted file mode 100644 index 5c0a588b..00000000 --- a/components/marketing/funding-programs.tsx +++ /dev/null @@ -1,219 +0,0 @@ -'use client'; - -import { ArrowRight, ArrowUpRight } from 'lucide-react'; -import { useMotionValueEvent, useScroll } from 'motion/react'; -import Link from 'next/link'; -import { Fragment, useRef, useState } from 'react'; - -import { cn } from '@/lib/utils'; - -import { Section } from './section'; - -type Program = { name: string; description: string }; - -const PROGRAMS: Program[] = [ - { - name: 'Bounties', - description: 'Complete tasks and earn rewards for your contributions.', - }, - { - name: 'Hackathons', - description: 'Compete, collaborate, and bring new ideas to life.', - }, - { - name: 'Crowdfunding', - description: - 'Raise support directly from communities that believe in your vision.', - }, - { - name: 'Grants', - description: 'Secure funding for impactful projects and initiatives.', - }, -]; - -const GRADIENT = - 'bg-[linear-gradient(180deg,rgba(13,17,17,0)_48.68%,rgba(46,237,170,0.08)_100%)]'; - -/** Blank placeholder for a program's dashboard screenshot. */ -function DashboardCard({ className }: { className?: string }) { - return ( -
- ); -} - -/** Title, description and arrow shown beneath a dashboard card. */ -function Caption({ program }: { program: Program }) { - return ( -
-
-

- {program.name} -

-

- {program.description} -

-
- -
- ); -} - -/** Heading, copy and the "Get Started" link shared by the desktop panel. */ -function Intro() { - return ( -
-

- Every Funding Path. - One Platform. -

-

- Boundless unifies multiple ways to launch, support and fund projects in - a single accountable system. -

- - Get Started - - -
- ); -} - -/** - * Desktop "Every Funding Path. One Platform.": the section pins while the user - * scrolls, advancing through each program (Bounties, Hackathons, Crowdfunding, - * Grants) before the page continues. The selector is also clickable, scrolling - * to that program's segment. - */ -function DesktopPrograms() { - const ref = useRef(null); - const [active, setActive] = useState(0); - - const { scrollYProgress } = useScroll({ - target: ref, - offset: ['start start', 'end end'], - }); - - useMotionValueEvent(scrollYProgress, 'change', value => { - const index = Math.min( - PROGRAMS.length - 1, - Math.max(0, Math.floor(value * PROGRAMS.length)) - ); - setActive(index); - }); - - // Scroll the window so the clicked program sits in the middle of its segment. - function handleSelect(index: number) { - const el = ref.current; - if (!el) return; - const top = el.getBoundingClientRect().top + window.scrollY; - const distance = el.offsetHeight - window.innerHeight; - const target = top + ((index + 0.5) / PROGRAMS.length) * distance; - const reduce = window.matchMedia( - '(prefers-reduced-motion: reduce)' - ).matches; - window.scrollTo({ top: target, behavior: reduce ? 'auto' : 'smooth' }); - } - - return ( - - ); -} - -/** Mobile "Every Funding Path. One Platform.": every program stacked. */ -function MobilePrograms() { - return ( -
- - -
- {PROGRAMS.map((program, index) => ( - -
- - -
- {index < PROGRAMS.length - 1 ? ( - - ) : null} -
- ))} -
-
- ); -} - -/** - * "Every Funding Path. One Platform.": a program selector beside a - * dashboard-screenshot card. On desktop the section pins and scroll advances - * through each program; on mobile every program stacks vertically. - */ -export function FundingPrograms() { - return ( - <> - - - - ); -} diff --git a/components/marketing/get-started-modal.tsx b/components/marketing/get-started-modal.tsx deleted file mode 100644 index b9ae4d68..00000000 --- a/components/marketing/get-started-modal.tsx +++ /dev/null @@ -1,94 +0,0 @@ -'use client'; - -import { XIcon } from 'lucide-react'; -import Image from 'next/image'; -import type { ReactNode } from 'react'; - -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogTitle, - DialogTrigger, -} from '@/components/ui/dialog'; - -interface GetStartedModalProps { - /** Controlled open state. Omit to let the trigger manage it. */ - open?: boolean; - onOpenChange?: (open: boolean) => void; - /** Element that opens the modal. Skip it for fully controlled usage. */ - trigger?: ReactNode; - /** Fired when the primary action is pressed. */ - onGetStarted?: () => void; -} - -/** - * Promotional get-started modal: the Boundless mark, a headline, supporting - * copy and the primary / dismiss actions. Built on the Dialog and Button - * primitives so it inherits the design system focus and overlay behaviour. - */ -export function GetStartedModal({ - open, - onOpenChange, - trigger, - onGetStarted, -}: GetStartedModalProps) { - return ( - - {trigger ? {trigger} : null} - - - - - -
- -
- - Ready to Turn Skills Into Rewards? - - - Access bounties, hackathons, grants, and crowdfunding - opportunities, all in one ecosystem. - -
-
- -
- - - - -
-
-
- ); -} diff --git a/components/marketing/intro-section.tsx b/components/marketing/intro-section.tsx deleted file mode 100644 index ca3c35f0..00000000 --- a/components/marketing/intro-section.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { type ReactNode } from 'react'; - -import { cn } from '@/lib/utils'; - -import { Reveal } from './reveal'; -import { Section } from './section'; - -// Mint glow at the top, fading fully to dark so it merges into the section below. -const GLOW = - 'linear-gradient(180deg, rgba(46, 237, 170, 0.08) 0%, rgba(13, 17, 17, 0.00) 100%), #0D1111'; - -interface IntroSectionProps { - eyebrow: ReactNode; - title: ReactNode; - children: ReactNode; - eyebrowClassName?: string; -} - -/** Two-column intro: eyebrow + heading on the left, supporting copy on the right. */ -export function IntroSection({ - eyebrow, - title, - children, - eyebrowClassName, -}: IntroSectionProps) { - return ( -
-
-
- -

- {eyebrow} -

-

- {title} -

-
- - - {children} - -
-
-
- ); -} diff --git a/components/marketing/personas.tsx b/components/marketing/personas.tsx deleted file mode 100644 index 07716efb..00000000 --- a/components/marketing/personas.tsx +++ /dev/null @@ -1,120 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; - -import { cn } from '@/lib/utils'; - -import { Section } from './section'; - -type Persona = { title: string; description: string }; - -const PERSONAS: Persona[] = [ - { - title: 'Designers', - description: - 'Contribute creative work, collaborate with teams and get rewarded.', - }, - { - title: 'Developers', - description: - 'Turn ideas into products and earn through challenges and opportunities.', - }, - { - title: 'Communities', - description: 'Launch programs that engage and support contributors.', - }, - { - title: 'Startups & Founders', - description: - 'Access funding, visibility and opportunities to accelerate growth.', - }, -]; - -const STEP_MS = 3200; - -/** - * "Built for Builders, Creators & Communities": four audience personas as an - * auto-advancing stepper. The active persona's indicator lights up and its - * progress bar fills before moving to the next. - */ -export function Personas() { - const [active, setActive] = useState(0); - - useEffect(() => { - if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; - const id = setInterval( - () => setActive(current => (current + 1) % PERSONAS.length), - STEP_MS - ); - return () => clearInterval(id); - }, []); - - return ( -
-
-

- Built for{' '} - - Builders, Creators & Communities - -

- -
- {PERSONAS.map((persona, index) => { - const isActive = index === active; - - return ( -
- {/* Oversized faded index, clipped (desktop only). */} -
- - {String(index + 1).padStart(2, '0')} - -
- - {/* Progress: indicator + fill bar. */} -
- - - - - - -
- - {/* Step copy. */} -
-

- {persona.title} -

-

- {persona.description} -

-
-
- ); - })} -
-
-
- ); -} diff --git a/components/marketing/prize-pool.tsx b/components/marketing/prize-pool.tsx deleted file mode 100644 index 17f75396..00000000 --- a/components/marketing/prize-pool.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import Image from 'next/image'; - -import { cn } from '@/lib/utils'; - -type PrizeTier = { - place: string; - amount: string; -}; - -const DEFAULT_TIERS: PrizeTier[] = [ - { place: '1st Place', amount: '120,000' }, - { place: '2nd Place', amount: '80,000' }, - { place: '3rd Place', amount: '50,000' }, - { place: '4th Place', amount: '30,000' }, - { place: '5th Place', amount: '20,000' }, -]; - -type PrizePoolProps = { - total?: string; - currency?: string; - label?: string; - tiers?: PrizeTier[]; - className?: string; -}; - -function PrizeRow({ - place, - amount, - currency, -}: PrizeTier & { currency: string }) { - return ( -
- {place} -
- {amount} - {currency} -
- {/* Dot centered on the 2px timeline (34px padding + 1px to its center, - minus half the dot). */} - -
- ); -} - -function TiersBlock({ - tiers, - currency, - ariaHidden, -}: { - tiers: PrizeTier[]; - currency: string; - ariaHidden?: boolean; -}) { - return ( -
- {tiers.map((tier, index) => ( - - ))} -
- ); -} - -/** Total prize pool: a fixed header above an infinitely scrolling tier list. */ -export function PrizePool({ - total = '300,000', - currency = 'USDC', - label = 'Total Prize Pool', - tiers = DEFAULT_TIERS, - className, -}: PrizePoolProps) { - return ( -
- {/* Fixed header */} -
- -
- - {label} - -
- {total} - {currency} -
-
-
- - {/* Only the tiers scroll, in two copies for a seamless loop. */} -
-
- - -
-
-
- ); -} diff --git a/components/marketing/team-section.tsx b/components/marketing/team-section.tsx deleted file mode 100644 index 542a0078..00000000 --- a/components/marketing/team-section.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import Image from 'next/image'; -import { type ReactNode } from 'react'; - -import { Reveal } from './reveal'; -import { Section } from './section'; - -// Mint glow at the top, fading fully to dark so it merges into the section below. -const GLOW = - 'linear-gradient(180deg, rgba(46, 237, 170, 0.08) 0%, rgba(13, 17, 17, 0.00) 100%), #0D1111'; - -interface TeamMember { - name: string; - role: string; - /** Portrait path in `/public`. */ - image: string; - linkedin?: string; -} - -interface TeamSectionProps { - title: ReactNode; - members: TeamMember[]; -} - -/** Team grid: heading above portrait cards with name, role and a LinkedIn link. */ -export function TeamSection({ title, members }: TeamSectionProps) { - return ( -
-
-
- -

{title}

-
- -
    - {members.map((member, i) => ( - -
    -
    - {member.name} -
    -
    - -
    -
    -

    - {member.name} -

    -

    - {member.role} -

    -
    - - {member.linkedin ? ( - - - - ) : null} -
    -
    - ))} -
-
-
-
- ); -} diff --git a/components/marketing/testimonials.tsx b/components/marketing/testimonials.tsx deleted file mode 100644 index ca5328ce..00000000 --- a/components/marketing/testimonials.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { ArrowUpRight } from 'lucide-react'; -import Image from 'next/image'; - -import { SocialGlyph } from '@/components/layout/brand-icons'; -import { cn } from '@/lib/utils'; - -type Testimonial = { - name: string; - handle: string; - avatar: string; - quote: string; -}; - -// Two rows that scroll in opposite directions. Avatars reuse the team photos. -const ROW_ONE: Testimonial[] = [ - { - name: 'Alex Carter', - handle: '@alexcarterx', - avatar: '/team/team1.svg', - quote: - 'I have used multiple bounty platforms before, but @boundless_fi was the first one that actually enforced delivery and payment through escrow instead of blind trust.', - }, - { - name: 'Daniel Kim', - handle: '@danielbuilds', - avatar: '/team/team2.svg', - quote: - 'Winning a hackathon on @boundless_fi did not feel like the end of the journey. The milestone continuation model helped our team keep building after the event.', - }, - { - name: 'Maya Thompson', - handle: '@mayathompson', - avatar: '/team/team3.svg', - quote: - 'The reputation layer on @boundless_fi makes each milestone meaningful. Your work history actually compounds into something you own.', - }, -]; - -const ROW_TWO: Testimonial[] = [ - { - name: 'James Okoro', - handle: '@jamesbuilds', - avatar: '/team/team4.svg', - quote: - 'What sold me on @boundless_fi was how simple the experience is for the team, while the blockchain infrastructure stays underneath.', - }, - { - name: 'Marcus Reid', - handle: '@marcusreidx', - avatar: '/team/team2.svg', - quote: - 'Most platforms only help you find work. @boundless_fi feels like infrastructure for building long-term reputation in Web3.', - }, - { - name: 'Chloe Bennett', - handle: '@chloebennett', - avatar: '/team/team1.svg', - quote: - 'The milestone tracking UI on @boundless_fi gave our community full visibility into project progress and fund releases.', - }, -]; - -function Card({ name, handle, avatar, quote }: Testimonial) { - return ( -
- {/* Border accent lines (exact Figma specs), over a faint base rim. - Top: a white gradient line across the top edge, fading at both ends. */} - - {/* Bottom: a shorter white gradient line set toward the left. */} - - {/* Green accent on the left edge (Figma: 53px line, gradient fading at - both ends, #4BDD74 at ~0.6 in the middle). */} - -
- {name} -
- {name} - {handle} -
- -
-

{quote}

-
- ); -} - -function Row({ - items, - direction, -}: { - items: Testimonial[]; - direction: 'left' | 'right'; -}) { - // Duplicate the row so the -50% loop lands on an identical copy. - const loop = [...items, ...items]; - return ( -
- {loop.map((item, index) => ( - - ))} -
- ); -} - -/** - * "Trusted by Builders Worldwide": two horizontally scrolling rows of social - * testimonials. Hidden on mobile per the design. - */ -export function Testimonials() { - return ( -
-
-
-
-
-

- Trusted by{' '} - Builders Worldwide -

-

- Hear how communities, founders and contributors are creating - impact through Boundless. -

-
- - {/* Liquid-glass CTA, scaled from a 282px reference to this ~44px - pill. The lit border runs only along the top-left -> bottom-right - diagonal (where light enters and exits): a 135deg gradient ring, - bright at those two corners and transparent at the top-right and - bottom-left, rendered via a masked overlay. A top-left radial - highlight sets the light direction and diagonal inset bevels form - the refractive lens edge. The fill stays dark/translucent over the - 10px backdrop blur, with only a faint inner frost. */} - - - - Follow Boundless - - -
- {/* Marquee breaks out of the centered container to span the full - width; the enclosing section is `overflow-hidden`, so the 100vw - breakout is clipped and the soft edge fade lands at the true edges. */} -
- - -
-
-
-
- ); -} diff --git a/components/marketing/trust-bar.tsx b/components/marketing/trust-bar.tsx deleted file mode 100644 index cd02bf0e..00000000 --- a/components/marketing/trust-bar.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { PartnerLogos } from './partner-logos'; -import { Section } from './section'; - -/** - * Ecosystem trust strip: a label over an auto-scrolling partner-logo marquee. - * The teal-at-top gradient continues the hero's bottom glow so the seam merges. - */ -export function TrustBar() { - return ( -
-

- Trusted By Communities, Ecosystems & Builders -

- -
- ); -} diff --git a/components/marketing/youtube-embed.tsx b/components/marketing/youtube-embed.tsx deleted file mode 100644 index 2ce5a96d..00000000 --- a/components/marketing/youtube-embed.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client'; - -import { Play } from 'lucide-react'; -import { useState } from 'react'; - -interface YouTubeEmbedProps { - id: string; - title: string; -} - -/** Lazy YouTube player: shows the poster + play button, loading the iframe on click. */ -export function YouTubeEmbed({ id, title }: YouTubeEmbedProps) { - const [playing, setPlaying] = useState(false); - - if (playing) { - return ( -