- apps/web: redesigned landing (Hero/Scanner/HowItWorks/Features/Footer), honest live-vs-coming-soon badges, same-origin /api/scan, ember theme. - ecosystem.config.cjs: runnable — pyre-api/worker via `node --import tsx`, pyre-web via `next start`, fork mode, env wired. pm2 web+api verified online (api /health 200, scan 200, web 200). - infra/nginx/feedthepyre.com.conf: app at / (proxy :3000), API at /api (proxy :4000, prefix preserved), dev tracker at /status (static). - scripts/deploy-web.sh: sudo cutover (install vhost, nginx -t, reload, certbot --nginx --keep-until-expiring). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69 lines
2.1 KiB
TypeScript
69 lines
2.1 KiB
TypeScript
/**
|
|
* Feature / status cards. HONEST about what is live vs coming soon — coming-soon
|
|
* items carry a clear badge and must not imply they work today.
|
|
* Server component (static content, no hooks).
|
|
*/
|
|
type Status = "live" | "partial" | "soon";
|
|
|
|
const FEATURES: ReadonlyArray<{
|
|
title: string;
|
|
body: string;
|
|
status: Status;
|
|
statusLabel: string;
|
|
}> = [
|
|
{
|
|
title: "Scan & classify",
|
|
body: "Read your SPL token accounts and conservatively classify every one. Unknown means skip.",
|
|
status: "live",
|
|
statusLabel: "Live",
|
|
},
|
|
{
|
|
title: "Reclaim ATA rent",
|
|
body: "The scan already estimates recoverable rent from empty accounts. One-click close to send that SOL back to you is next.",
|
|
status: "partial",
|
|
statusLabel: "Scan live · close soon",
|
|
},
|
|
{
|
|
title: "Burn dead tokens",
|
|
body: "Burn worthless leftover balances to zero, then close the emptied accounts to recover their rent.",
|
|
status: "soon",
|
|
statusLabel: "Phase 3",
|
|
},
|
|
{
|
|
title: "Prometheus AI meme rebirth",
|
|
body: "Turn burned remnants into an AI-generated meme Spawn for human-reviewed launch. Entertainment, not a promise of value.",
|
|
status: "soon",
|
|
statusLabel: "Coming soon",
|
|
},
|
|
];
|
|
|
|
function badgeClass(status: Status): string {
|
|
if (status === "live") return "badge badge--live";
|
|
if (status === "partial") return "badge badge--partial";
|
|
return "badge badge--soon";
|
|
}
|
|
|
|
export function Features() {
|
|
return (
|
|
<section className="features" aria-labelledby="features-heading">
|
|
<h2 className="section-heading" id="features-heading">
|
|
What's live, what's coming
|
|
</h2>
|
|
<div className="features__grid">
|
|
{FEATURES.map(({ title, body, status, statusLabel }) => (
|
|
<article
|
|
key={title}
|
|
className={`feature-card feature-card--${status}`}
|
|
>
|
|
<div className="feature-card__top">
|
|
<h3 className="feature-card__title">{title}</h3>
|
|
<span className={badgeClass(status)}>{statusLabel}</span>
|
|
</div>
|
|
<p className="feature-card__body">{body}</p>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|