Files
pyre/apps/web/src/components/Features.tsx
RogueWave d159ad5196 feat(web+infra): polished front page, app at /, tracker at /status
- 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>
2026-05-31 03:24:58 +00:00

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&apos;s live, what&apos;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>
);
}