manan77709 commited on
Commit
73e9550
·
1 Parent(s): 428cb2b

added header footer font changed about section contact section founder section and stuffs

Browse files
frontend-react/src/components/FontLoader.jsx ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect } from "react"
2
+
3
+ const FONTS = [
4
+ "https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400&family=Instrument+Serif:ital@0;1&display=swap",
5
+ ]
6
+
7
+ export default function FontLoader() {
8
+ useEffect(() => {
9
+ FONTS.forEach((href) => {
10
+ if (document.querySelector(`link[href="${href}"]`)) return
11
+ const link = document.createElement("link")
12
+ link.rel = "stylesheet"
13
+ link.href = href
14
+ document.head.appendChild(link)
15
+ })
16
+ }, [])
17
+
18
+ return null
19
+ }
frontend-react/src/components/Footer.jsx ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ExternalLink } from "lucide-react"
2
+ import GithubIcon from "./GithubIcon"
3
+ import { FOUNDER, LINKS } from "../constants"
4
+
5
+ export default function Footer() {
6
+ return (
7
+ <footer className="site-footer">
8
+ <div className="site-footer__inner">
9
+ <div className="site-footer__brand">
10
+ <span className="site-logo__mark site-logo__mark--sm">CV</span>
11
+ <div>
12
+ <p className="site-footer__name">ClearVoice</p>
13
+ <p className="site-footer__tagline">Evidence-backed health claim verification</p>
14
+ </div>
15
+ </div>
16
+
17
+ <div className="site-footer__links">
18
+ <a href={LINKS.github} target="_blank" rel="noopener noreferrer" className="btn btn-ghost btn-sm">
19
+ <GithubIcon size={15} />
20
+ Repository
21
+ </a>
22
+ <a href={LINKS.profile} target="_blank" rel="noopener noreferrer" className="btn btn-ghost btn-sm">
23
+ {FOUNDER.name}
24
+ </a>
25
+ <a href={LINKS.demo} target="_blank" rel="noopener noreferrer" className="btn btn-ghost btn-sm">
26
+ Demo
27
+ <ExternalLink size={13} />
28
+ </a>
29
+ </div>
30
+
31
+ <p className="site-footer__copy">
32
+ Built by {FOUNDER.name} · PubMed-powered · {new Date().getFullYear()}
33
+ </p>
34
+ </div>
35
+ </footer>
36
+ )
37
+ }
frontend-react/src/components/GithubIcon.jsx ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export default function GithubIcon({ size = 16, className }) {
2
+ return (
3
+ <svg
4
+ width={size}
5
+ height={size}
6
+ viewBox="0 0 24 24"
7
+ fill="currentColor"
8
+ className={className}
9
+ aria-hidden="true"
10
+ >
11
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
12
+ </svg>
13
+ )
14
+ }
frontend-react/src/components/Header.jsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ExternalLink, Menu, X } from "lucide-react"
2
+ import GithubIcon from "./GithubIcon"
3
+ import { useState } from "react"
4
+ import { LINKS, NAV_ITEMS } from "../constants"
5
+
6
+ export default function Header({ onNavigate }) {
7
+ const [menuOpen, setMenuOpen] = useState(false)
8
+
9
+ function handleNav(id) {
10
+ onNavigate(id)
11
+ setMenuOpen(false)
12
+ }
13
+
14
+ return (
15
+ <header className="site-header">
16
+ <div className="site-header__inner">
17
+ <button type="button" className="site-logo" onClick={() => handleNav("verify")}>
18
+ <span className="site-logo__mark">CV</span>
19
+ <span className="site-logo__text">
20
+ <strong>ClearVoice</strong>
21
+ <small>Medical fact-checker</small>
22
+ </span>
23
+ </button>
24
+
25
+ <nav className={`site-nav ${menuOpen ? "site-nav--open" : ""}`}>
26
+ {NAV_ITEMS.map(({ id, label }) => (
27
+ <button key={id} type="button" className="btn btn-ghost btn-sm" onClick={() => handleNav(id)}>
28
+ {label}
29
+ </button>
30
+ ))}
31
+ <a href={LINKS.github} target="_blank" rel="noopener noreferrer" className="btn btn-ghost btn-sm">
32
+ <GithubIcon size={15} />
33
+ GitHub
34
+ </a>
35
+ <a href={LINKS.api} target="_blank" rel="noopener noreferrer" className="btn btn-outline btn-sm">
36
+ API
37
+ <ExternalLink size={13} />
38
+ </a>
39
+ <a href={LINKS.demo} target="_blank" rel="noopener noreferrer" className="btn btn-primary btn-sm">
40
+ Live demo
41
+ </a>
42
+ </nav>
43
+
44
+ <button
45
+ type="button"
46
+ className="site-menu-toggle"
47
+ aria-label={menuOpen ? "Close menu" : "Open menu"}
48
+ onClick={() => setMenuOpen((v) => !v)}
49
+ >
50
+ {menuOpen ? <X size={20} /> : <Menu size={20} />}
51
+ </button>
52
+ </div>
53
+ </header>
54
+ )
55
+ }
frontend-react/src/components/ResultsPanel.jsx ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ ChevronDown,
3
+ ChevronUp,
4
+ ExternalLink,
5
+ Zap,
6
+ Layers,
7
+ FileText,
8
+ Lightbulb,
9
+ BookOpen,
10
+ } from "lucide-react"
11
+ import VerdictBadge from "./VerdictBadge"
12
+
13
+ function StanceDot({ stance }) {
14
+ const cls =
15
+ stance === "SUPPORTS"
16
+ ? "stance-dot--supports"
17
+ : stance === "CONTRADICTS"
18
+ ? "stance-dot--contradicts"
19
+ : "stance-dot--neutral"
20
+ return <span className={`stance-dot ${cls}`} aria-hidden="true" />
21
+ }
22
+
23
+ export default function ResultsPanel({ result, expandedPapers, onTogglePaper }) {
24
+ if (!result) return null
25
+
26
+ return (
27
+ <div className="results">
28
+ <div className="results-card">
29
+ <div className="results-card__header">
30
+ <VerdictBadge verdict={result.verdict} />
31
+ <div className="results-stats">
32
+ <div className="results-stat">
33
+ <span className="results-stat__value">{Math.round(result.confidence * 100)}%</span>
34
+ <span className="results-stat__label">Confidence</span>
35
+ </div>
36
+ <div className="results-stat">
37
+ <span className="results-stat__value">{result.evidence_strength || "—"}</span>
38
+ <span className="results-stat__label">Evidence</span>
39
+ </div>
40
+ </div>
41
+ </div>
42
+
43
+ {result.cached && (
44
+ <p className="results-cache">
45
+ <Zap size={13} />
46
+ Served from cache
47
+ </p>
48
+ )}
49
+
50
+ {result.decomposition?.is_complex && (
51
+ <div className="results-block results-block--accent">
52
+ <div className="results-block__heading">
53
+ <Layers size={15} />
54
+ Complex claim — {result.decomposition.sub_claims.length} sub-claims analyzed
55
+ </div>
56
+ <ul className="results-list">
57
+ {result.decomposition.sub_claims.map((sc, i) => (
58
+ <li key={i}>{sc}</li>
59
+ ))}
60
+ </ul>
61
+ </div>
62
+ )}
63
+
64
+ {result.plain_english && (
65
+ <div className="results-block">
66
+ <div className="results-block__heading">
67
+ <FileText size={15} />
68
+ In plain English
69
+ </div>
70
+ <p className="results-block__text">{result.plain_english}</p>
71
+ </div>
72
+ )}
73
+
74
+ {result.takeaway && (
75
+ <div className="results-takeaway">
76
+ <Lightbulb size={15} />
77
+ <p><strong>Takeaway.</strong> {result.takeaway}</p>
78
+ </div>
79
+ )}
80
+ </div>
81
+
82
+ <details className="results-card results-details">
83
+ <summary className="results-details__summary">
84
+ <BookOpen size={15} />
85
+ Technical explanation
86
+ </summary>
87
+ <p className="results-block__text">{result.explanation}</p>
88
+
89
+ {result.citations?.length > 0 && (
90
+ <div className="citations">
91
+ <p className="citations__title">Citations</p>
92
+ {result.citations.map((c, i) => (
93
+ <div key={i} className="citation-row">
94
+ {c.pmid && /^\d+$/.test(c.pmid) ? (
95
+ <a
96
+ href={`https://pubmed.ncbi.nlm.nih.gov/${c.pmid}/`}
97
+ target="_blank"
98
+ rel="noopener noreferrer"
99
+ className="citation-link"
100
+ >
101
+ {c.title}
102
+ <ExternalLink size={12} />
103
+ </a>
104
+ ) : (
105
+ <span className="citation-text">{c.title}</span>
106
+ )}
107
+ </div>
108
+ ))}
109
+ </div>
110
+ )}
111
+ </details>
112
+
113
+ {result.judge && result.judge.overall_quality !== "UNKNOWN" && (
114
+ <div className="results-card">
115
+ <div className="results-card__row">
116
+ <div className="results-block__heading">Evidence quality</div>
117
+ <span className={`quality-badge quality-badge--${result.judge.overall_quality.toLowerCase()}`}>
118
+ {result.judge.overall_quality}
119
+ </span>
120
+ </div>
121
+ <p className="results-block__text results-block__text--muted">
122
+ {result.judge.quality_explanation}
123
+ </p>
124
+
125
+ <div className="papers-list">
126
+ {result.judge.papers.map((jp, i) => {
127
+ const paper = result.papers?.[i]
128
+ return (
129
+ <div key={i} className="paper-item">
130
+ <button
131
+ type="button"
132
+ className="paper-item__toggle"
133
+ onClick={() => onTogglePaper(i)}
134
+ >
135
+ <div className="paper-item__title">
136
+ <StanceDot stance={jp.stance} />
137
+ <span>{paper?.title} ({paper?.year})</span>
138
+ </div>
139
+ {expandedPapers[i] ? (
140
+ <ChevronUp size={16} className="paper-item__chevron" />
141
+ ) : (
142
+ <ChevronDown size={16} className="paper-item__chevron" />
143
+ )}
144
+ </button>
145
+
146
+ {expandedPapers[i] && paper && (
147
+ <div className="paper-item__body">
148
+ <div className="paper-metrics">
149
+ <div className="paper-metric">
150
+ <span>Study type</span>
151
+ <strong>{jp.study_type}</strong>
152
+ </div>
153
+ <div className="paper-metric">
154
+ <span>Quality</span>
155
+ <strong>{jp.quality_score}/5</strong>
156
+ </div>
157
+ <div className="paper-metric">
158
+ <span>Similarity</span>
159
+ <strong>{paper.similarity}</strong>
160
+ </div>
161
+ </div>
162
+ <p className="paper-item__summary">{jp.one_line_summary}</p>
163
+ <p className="paper-item__journal">{paper.journal}</p>
164
+ <a
165
+ href={`https://pubmed.ncbi.nlm.nih.gov/${paper.pmid}/`}
166
+ target="_blank"
167
+ rel="noopener noreferrer"
168
+ className="citation-link citation-link--sm"
169
+ >
170
+ View on PubMed
171
+ <ExternalLink size={10} />
172
+ </a>
173
+ </div>
174
+ )}
175
+ </div>
176
+ )
177
+ })}
178
+ </div>
179
+ </div>
180
+ )}
181
+ </div>
182
+ )
183
+ }
frontend-react/src/components/VerdictBadge.jsx ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { CheckCircle, XCircle, AlertTriangle, HelpCircle } from "lucide-react"
2
+
3
+ const VERDICT_CONFIG = {
4
+ TRUE: {
5
+ Icon: CheckCircle,
6
+ label: "True",
7
+ className: "verdict--success",
8
+ },
9
+ FALSE: {
10
+ Icon: XCircle,
11
+ label: "False",
12
+ className: "verdict--danger",
13
+ },
14
+ MISLEADING: {
15
+ Icon: AlertTriangle,
16
+ label: "Misleading",
17
+ className: "verdict--warning",
18
+ },
19
+ "INSUFFICIENT EVIDENCE": {
20
+ Icon: HelpCircle,
21
+ label: "Insufficient evidence",
22
+ className: "verdict--neutral",
23
+ },
24
+ ERROR: {
25
+ Icon: XCircle,
26
+ label: "Error",
27
+ className: "verdict--neutral",
28
+ },
29
+ }
30
+
31
+ export default function VerdictBadge({ verdict }) {
32
+ const config = VERDICT_CONFIG[verdict] || VERDICT_CONFIG.ERROR
33
+ const { Icon, label, className } = config
34
+
35
+ return (
36
+ <div className={`verdict-badge ${className}`}>
37
+ <Icon size={18} strokeWidth={2} />
38
+ <span>{label}</span>
39
+ </div>
40
+ )
41
+ }
frontend-react/src/components/VerifyForm.jsx ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Loader2, ArrowRight, RotateCcw } from "lucide-react"
2
+ import { SAMPLE_CLAIMS } from "../constants"
3
+
4
+ export default function VerifyForm({ claim, loading, onClaimChange, onVerify, onClear }) {
5
+ return (
6
+ <section id="verify" className="hero-section">
7
+ <div className="hero-section__content">
8
+ <p className="hero-section__eyebrow">PubMed-backed verification</p>
9
+ <h1 className="hero-section__title">
10
+ Check health claims against real research
11
+ </h1>
12
+ <p className="hero-section__desc">
13
+ Enter any health claim. ClearVoice searches live peer-reviewed studies,
14
+ scores evidence quality, and returns a transparent verdict.
15
+ </p>
16
+ </div>
17
+
18
+ <div className="verify-card">
19
+ <label htmlFor="claim-input" className="verify-card__label">
20
+ Health claim
21
+ </label>
22
+ <textarea
23
+ id="claim-input"
24
+ value={claim}
25
+ onChange={(e) => onClaimChange(e.target.value)}
26
+ placeholder="e.g. Vitamin C prevents colds..."
27
+ className="verify-card__input"
28
+ rows={3}
29
+ onKeyDown={(e) => {
30
+ if (e.key === "Enter" && !e.shiftKey) {
31
+ e.preventDefault()
32
+ onVerify()
33
+ }
34
+ }}
35
+ />
36
+
37
+ <div className="sample-claims">
38
+ <span className="sample-claims__label">Try an example</span>
39
+ <div className="sample-claims__list">
40
+ {SAMPLE_CLAIMS.map((sample) => (
41
+ <button
42
+ key={sample}
43
+ type="button"
44
+ className="btn btn-chip"
45
+ disabled={loading}
46
+ onClick={() => onClaimChange(sample)}
47
+ >
48
+ {sample}
49
+ </button>
50
+ ))}
51
+ </div>
52
+ </div>
53
+
54
+ <div className="verify-card__actions">
55
+ <p className="verify-card__hint">Enter to submit · Shift+Enter for new line</p>
56
+ <div className="verify-card__buttons">
57
+ {(claim.trim() || loading) && (
58
+ <button
59
+ type="button"
60
+ className="btn btn-ghost"
61
+ disabled={loading}
62
+ onClick={onClear}
63
+ >
64
+ <RotateCcw size={16} />
65
+ Clear
66
+ </button>
67
+ )}
68
+ <button
69
+ type="button"
70
+ className="btn btn-primary"
71
+ disabled={loading || !claim.trim()}
72
+ onClick={onVerify}
73
+ >
74
+ {loading ? (
75
+ <>
76
+ <Loader2 size={16} className="spin" />
77
+ Analyzing
78
+ </>
79
+ ) : (
80
+ <>
81
+ Verify claim
82
+ <ArrowRight size={16} />
83
+ </>
84
+ )}
85
+ </button>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </section>
90
+ )
91
+ }
frontend-react/src/sections/AboutSection.jsx ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Search, BookOpen, Scale, MessageSquare } from "lucide-react"
2
+
3
+ const STEPS = [
4
+ {
5
+ icon: Search,
6
+ title: "Live PubMed search",
7
+ text: "Your claim is searched against real-time peer-reviewed literature — not a stale offline database.",
8
+ },
9
+ {
10
+ icon: Scale,
11
+ title: "Multi-agent evaluation",
12
+ text: "Retrieval, quality judging, and contradiction detection work together to weigh the evidence.",
13
+ },
14
+ {
15
+ icon: MessageSquare,
16
+ title: "Transparent verdict",
17
+ text: "You get a clear verdict, confidence score, study breakdown, and plain-English explanation.",
18
+ },
19
+ ]
20
+
21
+ export default function AboutSection() {
22
+ return (
23
+ <section id="about" className="section">
24
+ <div className="section__header">
25
+ <p className="section__eyebrow">How it works</p>
26
+ <h2 className="section__title">Science-first verification</h2>
27
+ <p className="section__desc">
28
+ ClearVoice evaluates health claims using live medical research. Every response cites
29
+ actual studies with quality scores and stance analysis.
30
+ </p>
31
+ </div>
32
+
33
+ <div className="steps-grid">
34
+ {STEPS.map(({ icon: Icon, title, text }) => (
35
+ <article key={title} className="step-card">
36
+ <div className="step-card__icon">
37
+ <Icon size={20} strokeWidth={1.5} />
38
+ </div>
39
+ <h3>{title}</h3>
40
+ <p>{text}</p>
41
+ </article>
42
+ ))}
43
+ </div>
44
+
45
+ <div className="feature-strip">
46
+ <div className="feature-strip__item">
47
+ <BookOpen size={16} />
48
+ <span>PubMedBERT semantic search</span>
49
+ </div>
50
+ <div className="feature-strip__item">
51
+ <Scale size={16} />
52
+ <span>Evidence quality scoring</span>
53
+ </div>
54
+ <div className="feature-strip__item">
55
+ <MessageSquare size={16} />
56
+ <span>Plain-English summaries</span>
57
+ </div>
58
+ </div>
59
+ </section>
60
+ )
61
+ }
frontend-react/src/sections/ContactSection.jsx ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ExternalLink, Mail, Zap } from "lucide-react"
2
+ import GithubIcon from "../components/GithubIcon"
3
+ import { FOUNDER, LINKS } from "../constants"
4
+
5
+ const CONTACTS = [
6
+ {
7
+ icon: GithubIcon,
8
+ label: "GitHub",
9
+ value: "MananBabbar07/ClearVoice",
10
+ href: LINKS.github,
11
+ desc: "Report issues, contribute, or explore the source code.",
12
+ },
13
+ {
14
+ icon: Zap,
15
+ label: "API",
16
+ value: "Hugging Face Spaces",
17
+ href: LINKS.api,
18
+ desc: "Direct access to the verification endpoint for integrations.",
19
+ },
20
+ {
21
+ icon: ExternalLink,
22
+ label: "Live demo",
23
+ value: "clear-voice-five.vercel.app",
24
+ href: LINKS.demo,
25
+ desc: "Try the deployed version of ClearVoice in your browser.",
26
+ },
27
+ {
28
+ icon: Mail,
29
+ label: "Reach out",
30
+ value: FOUNDER.name,
31
+ href: FOUNDER.github,
32
+ desc: "Open a GitHub issue or connect via profile for questions and feedback.",
33
+ },
34
+ ]
35
+
36
+ export default function ContactSection() {
37
+ return (
38
+ <section id="contact" className="section section--last">
39
+ <div className="section__header">
40
+ <p className="section__eyebrow">Contact</p>
41
+ <h2 className="section__title">Get in touch</h2>
42
+ <p className="section__desc">
43
+ Questions, feedback, or collaboration — pick whichever channel works best for you.
44
+ </p>
45
+ </div>
46
+
47
+ <div className="contact-grid">
48
+ {CONTACTS.map(({ icon: Icon, label, value, href, desc }) => (
49
+ <a key={label} href={href} target="_blank" rel="noopener noreferrer" className="contact-card">
50
+ <div className="contact-card__icon">
51
+ <Icon size={18} strokeWidth={1.5} />
52
+ </div>
53
+ <p className="contact-card__label">{label}</p>
54
+ <p className="contact-card__value">{value}</p>
55
+ <p className="contact-card__desc">{desc}</p>
56
+ </a>
57
+ ))}
58
+ </div>
59
+ </section>
60
+ )
61
+ }
frontend-react/src/sections/FoundersSection.jsx ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ExternalLink } from "lucide-react"
2
+ import GithubIcon from "../components/GithubIcon"
3
+ import { FOUNDER, LINKS } from "../constants"
4
+
5
+ export default function FoundersSection() {
6
+ return (
7
+ <section id="founders" className="section">
8
+ <div className="section__header">
9
+ <p className="section__eyebrow">Founder</p>
10
+ <h2 className="section__title">Built with intent</h2>
11
+ <p className="section__desc">
12
+ ClearVoice started as a project to fight medical misinformation with open, traceable evidence.
13
+ </p>
14
+ </div>
15
+
16
+ <article className="founder-card">
17
+ <div className="founder-card__avatar">{FOUNDER.name.split(" ").map((n) => n[0]).join("")}</div>
18
+ <div className="founder-card__body">
19
+ <div className="founder-card__top">
20
+ <div>
21
+ <h3>{FOUNDER.name}</h3>
22
+ <p className="founder-card__role">{FOUNDER.role}</p>
23
+ </div>
24
+ <div className="founder-card__actions">
25
+ <a href={FOUNDER.github} target="_blank" rel="noopener noreferrer" className="btn btn-outline btn-sm">
26
+ <GithubIcon size={15} />
27
+ GitHub
28
+ </a>
29
+ <a href={LINKS.github} target="_blank" rel="noopener noreferrer" className="btn btn-primary btn-sm">
30
+ View project
31
+ <ExternalLink size={13} />
32
+ </a>
33
+ </div>
34
+ </div>
35
+ <p className="founder-card__bio">{FOUNDER.bio}</p>
36
+ </div>
37
+ </article>
38
+ </section>
39
+ )
40
+ }