191 lines
6.5 KiB
JavaScript
191 lines
6.5 KiB
JavaScript
// Who it's for — three audience cards, each with a Reddit-style customer quote
|
|
// and Vibn's answer.
|
|
|
|
const AUDIENCE = [
|
|
{
|
|
label: "Small business owners",
|
|
icon: "shop",
|
|
headline: "Stop renting. Build the tool that actually fits.",
|
|
quote: "I'm paying $312/month for software that does 60% of what I need and zero of the rest.",
|
|
source: "u/coffeeshop_owner · r/smallbusiness",
|
|
answer: "Replace the whole stack with one tool that matches your workflow — bookings, customers, invoicing, all in one place. Owned by you.",
|
|
},
|
|
{
|
|
label: "Freelancers & local builders",
|
|
icon: "spark",
|
|
headline: "Become the craftsman who builds for businesses in your town.",
|
|
quote: "My client wants a quote tool. I can mock the frontend in a day. The backend? Two weeks I don't have.",
|
|
source: "u/agency_of_one · r/freelance",
|
|
answer: "Deliver the whole thing — login, data, hosting, polish — in the same chat where you built the screens. Bill for the system, not the hours.",
|
|
},
|
|
{
|
|
label: "Quiet entrepreneurs",
|
|
icon: "spark2",
|
|
headline: "Build a business without ever picking up the phone.",
|
|
quote: "I want to build my thing, ship my thing, and find my customers — without doing sales calls or talking to a developer.",
|
|
source: "u/asynchronous_human · r/indiehackers",
|
|
answer: "No deploys. No GitHub. No cold outreach. The thing you described is online, with logins, marketing on autopilot — ready for the right people to find it.",
|
|
},
|
|
];
|
|
|
|
function Audience() {
|
|
return (
|
|
<section className="section audience">
|
|
<style>{`
|
|
.audience-head { text-align: center; max-width: 820px; margin: 0 auto 56px; }
|
|
.audience-title {
|
|
font-size: clamp(36px, 4.8vw, 64px);
|
|
font-weight: 500; letter-spacing: -0.025em; line-height: 1.02;
|
|
text-wrap: balance;
|
|
}
|
|
.audience-sub {
|
|
margin-top: 20px;
|
|
color: var(--fg-mute);
|
|
font-size: 17px;
|
|
}
|
|
|
|
.audience-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 18px;
|
|
}
|
|
@media (max-width: 1000px) { .audience-grid { grid-template-columns: 1fr; } }
|
|
|
|
.a-card {
|
|
position: relative;
|
|
padding: 28px 26px 26px;
|
|
border-radius: 18px;
|
|
background: linear-gradient(180deg, oklch(0.20 0.009 60 / 0.55), oklch(0.17 0.008 60 / 0.55));
|
|
border: 1px solid var(--hairline);
|
|
display: flex; flex-direction: column;
|
|
min-height: 380px;
|
|
overflow: hidden;
|
|
}
|
|
.a-card::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0; left: 24px; right: 24px;
|
|
height: 1px;
|
|
background: linear-gradient(90deg, transparent, var(--accent), transparent);
|
|
opacity: .6;
|
|
}
|
|
|
|
.a-icon {
|
|
width: 40px; height: 40px;
|
|
border-radius: 10px;
|
|
display: grid; place-items: center;
|
|
background: oklch(0.22 0.011 60);
|
|
border: 1px solid var(--hairline);
|
|
color: var(--accent);
|
|
margin-bottom: 18px;
|
|
}
|
|
.a-label {
|
|
font-size: 19px; font-weight: 500;
|
|
letter-spacing: -0.015em;
|
|
color: var(--fg);
|
|
}
|
|
.a-headline {
|
|
margin: 8px 0 0;
|
|
color: var(--accent);
|
|
font-size: 15px;
|
|
line-height: 1.4;
|
|
letter-spacing: -0.005em;
|
|
font-weight: 500;
|
|
text-wrap: balance;
|
|
}
|
|
|
|
.a-quote {
|
|
margin: 18px 0 0;
|
|
padding: 16px 18px;
|
|
background: oklch(0.16 0.008 60 / 0.55);
|
|
border-left: 2px solid var(--accent);
|
|
border-radius: 4px 10px 10px 4px;
|
|
font-style: italic;
|
|
color: var(--fg-dim);
|
|
font-size: 14.5px;
|
|
line-height: 1.5;
|
|
position: relative;
|
|
}
|
|
.a-source {
|
|
margin-top: 8px;
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--fg-faint);
|
|
letter-spacing: 0.02em;
|
|
}
|
|
|
|
.a-answer {
|
|
margin-top: auto;
|
|
padding-top: 22px;
|
|
font-size: 15px;
|
|
color: var(--fg);
|
|
line-height: 1.5;
|
|
display: flex; gap: 10px; align-items: flex-start;
|
|
}
|
|
.a-answer .label {
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
letter-spacing: 0.12em;
|
|
text-transform: uppercase;
|
|
color: var(--accent);
|
|
padding: 3px 7px;
|
|
background: oklch(0.74 0.175 35 / 0.12);
|
|
border: 1px solid oklch(0.74 0.175 35 / 0.4);
|
|
border-radius: 4px;
|
|
margin-top: 1px;
|
|
flex-shrink: 0;
|
|
}
|
|
`}</style>
|
|
|
|
<div className="wrap">
|
|
<div className="audience-head">
|
|
<Eyebrow>Who Vibn is for</Eyebrow>
|
|
<h2 className="audience-title" style={{ marginTop: 18 }}>
|
|
People who have an idea — not a stack.
|
|
</h2>
|
|
<p className="audience-sub">
|
|
Three people who feel the same thing — different ways to fix it.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="audience-grid">
|
|
{AUDIENCE.map((a) => (
|
|
<div className="a-card" key={a.label}>
|
|
<div className="a-icon"><AudienceIcon name={a.icon} /></div>
|
|
<div className="a-label">{a.label}</div>
|
|
<p className="a-headline">{a.headline}</p>
|
|
|
|
<div className="a-quote">
|
|
"{a.quote}"
|
|
<div className="a-source">— {a.source}</div>
|
|
</div>
|
|
|
|
<div className="a-answer">
|
|
<span className="label">Vibn</span>
|
|
<span>{a.answer}</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function AudienceIcon({ name }) {
|
|
const p = { width: 20, height: 20, viewBox: "0 0 20 20", fill: "none",
|
|
stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" };
|
|
if (name === "shop") return (
|
|
<svg {...p}><path d="M3.5 6.5h13l-1 9.5h-11l-1-9.5Z"/><path d="M7 6.5V5a3 3 0 0 1 6 0v1.5"/></svg>
|
|
);
|
|
if (name === "spark") return (
|
|
<svg {...p}><path d="M10 3v4M10 13v4M3 10h4M13 10h4M5.3 5.3l2.8 2.8M11.9 11.9l2.8 2.8M14.7 5.3l-2.8 2.8M8.1 11.9l-2.8 2.8"/></svg>
|
|
);
|
|
if (name === "spark2") return (
|
|
<svg {...p}><path d="M10 2.5v3M10 14.5v3M2.5 10h3M14.5 10h3"/><circle cx="10" cy="10" r="3"/></svg>
|
|
);
|
|
return null;
|
|
}
|
|
|
|
Object.assign(window, { Audience });
|