feat: add About page with detailed game mechanics and links
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m51s
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m51s
This commit is contained in:
@@ -0,0 +1,214 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { BookOpen, TrendingUp, TrendingDown, Coins, Shuffle, RotateCcw, Building2, ExternalLink, Github } from 'lucide-react'
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: 'About — HashEx',
|
||||||
|
description: 'How HashEx works: rules, features, and quirks of the hashtag stock market.',
|
||||||
|
}
|
||||||
|
|
||||||
|
function Section({ title, icon: Icon, children }: { title: string; icon: React.ElementType; children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<section className="space-y-3">
|
||||||
|
<div className="flex items-center gap-2 border-b border-surface-border pb-2">
|
||||||
|
<Icon className="h-5 w-5 text-indigo-400 shrink-0" />
|
||||||
|
<h2 className="text-lg font-semibold">{title}</h2>
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Rule({ label, children }: { label: string; children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-3 text-sm">
|
||||||
|
<span className="text-indigo-400 font-medium shrink-0 w-32">{label}</span>
|
||||||
|
<span className="text-slate-300">{children}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AboutPage() {
|
||||||
|
return (
|
||||||
|
<div className="max-w-2xl mx-auto space-y-10 py-4">
|
||||||
|
{/* Header */}
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<BookOpen className="h-7 w-7 text-indigo-400" />
|
||||||
|
<h1 className="text-3xl font-bold">About HashEx</h1>
|
||||||
|
</div>
|
||||||
|
<p className="text-slate-400">
|
||||||
|
HashEx is a stock-market simulation game where the "stocks" are Mastodon hashtags.
|
||||||
|
Prices update automatically based on real post activity. Start with{' '}
|
||||||
|
<span className="text-white font-medium">$2,000</span> and see how much you can grow it.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Getting started */}
|
||||||
|
<Section title="Getting Started" icon={TrendingUp}>
|
||||||
|
<div className="space-y-2 text-sm text-slate-300">
|
||||||
|
<p>
|
||||||
|
Every new account starts with <span className="text-white font-medium">$2,000</span> in play money and{' '}
|
||||||
|
<span className="text-white font-medium">1 research point</span>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Use research points to unlock hashtags. Each point lets you search for a tag on Mastodon — if it has
|
||||||
|
activity, it gets added to the exchange with a live price. You earn more points each day based on your
|
||||||
|
account balance.
|
||||||
|
</p>
|
||||||
|
<div className="bg-surface-card border border-surface-border rounded-lg p-3 space-y-1 mt-2">
|
||||||
|
<p className="text-xs text-slate-500 uppercase tracking-wider mb-1">Daily research points</p>
|
||||||
|
<div className="grid grid-cols-2 gap-x-4 text-xs">
|
||||||
|
<span className="text-slate-400">Balance under $10k</span><span>1 pt / day</span>
|
||||||
|
<span className="text-slate-400">$10k+</span><span>2 pts / day</span>
|
||||||
|
<span className="text-slate-400">$100k+</span><span>3 pts / day</span>
|
||||||
|
<span className="text-slate-400">$1M+</span><span>5 pts / day</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Pricing */}
|
||||||
|
<Section title="How Prices Work" icon={Coins}>
|
||||||
|
<div className="space-y-2 text-sm text-slate-300">
|
||||||
|
<p>
|
||||||
|
Every hashtag has a price calculated from its post rate on Mastodon:
|
||||||
|
</p>
|
||||||
|
<div className="bg-surface-card border border-surface-border rounded-lg px-4 py-3 font-mono text-center text-indigo-300">
|
||||||
|
price = max($0.25, posts_per_hour × $0.25)
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Prices update on a regular cycle. A hashtag that goes completely quiet for long enough will be
|
||||||
|
automatically <span className="text-orange-400">deactivated</span> — you'll get a warning on the
|
||||||
|
home page if any of your positions are at risk. Research it again to reactivate it.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Trade types */}
|
||||||
|
<Section title="Trade Types" icon={TrendingUp}>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<Rule label="Buy Long">
|
||||||
|
Bet the price goes <span className="text-emerald-400">up</span>. You buy shares and profit when the
|
||||||
|
price rises above your average buy price.
|
||||||
|
</Rule>
|
||||||
|
<Rule label="Sell Long">
|
||||||
|
Close or reduce a long position. Profit = (current price − avg buy price) × shares.
|
||||||
|
</Rule>
|
||||||
|
<Rule label="Buy Short">
|
||||||
|
Bet the price goes <span className="text-red-400">down</span>. You put up collateral and profit when
|
||||||
|
the price falls below your entry.
|
||||||
|
</Rule>
|
||||||
|
<Rule label="Sell Short">
|
||||||
|
Close a short. Profit = (avg entry − current price) × shares. If the price rose above your entry you
|
||||||
|
take a loss — and your balance <span className="text-red-400">can go negative</span>.
|
||||||
|
</Rule>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-500 mt-2">
|
||||||
|
All trades are validated server-side. You cannot buy more than your balance, sell more shares than you hold,
|
||||||
|
or trade a hashtag you haven't researched.
|
||||||
|
</p>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Short selling specifics */}
|
||||||
|
<Section title="Short Selling — Quirks & Risks" icon={TrendingDown}>
|
||||||
|
<div className="space-y-2 text-sm text-slate-300">
|
||||||
|
<p>
|
||||||
|
Shorts use a <span className="text-white font-medium">collateral model</span>. When you buy short, the
|
||||||
|
cost is <code className="text-xs bg-surface-card px-1 py-0.5 rounded">price × shares</code>. When you
|
||||||
|
close, you receive back <code className="text-xs bg-surface-card px-1 py-0.5 rounded">(2 × entry − current) × shares</code>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This means losses are <span className="text-red-400">uncapped</span>. If the price doubles, your
|
||||||
|
payout is zero. If it triples, your balance goes negative.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
A <span className="text-red-400">negative balance</span> isn't game-over — you can still trade, but
|
||||||
|
your total portfolio value will show in red. You can reset your account at any time from your profile page.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Hedge Funds */}
|
||||||
|
<Section title="Hedge Funds" icon={Building2}>
|
||||||
|
<div className="space-y-2 text-sm text-slate-300">
|
||||||
|
<p>
|
||||||
|
Admins can create <span className="text-white font-medium">Hedge Funds</span> — shared pools of capital
|
||||||
|
that multiple players can manage together.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
As a fund manager you trade on behalf of the fund by appending{' '}
|
||||||
|
<code className="text-xs bg-surface-card px-1 py-0.5 rounded">?fund=[slug]</code> to any hashtag page
|
||||||
|
(there are quick links on the fund page). A banner confirms you're in Fund Mode.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Outside investors can buy and sell <span className="text-white font-medium">fund shares</span> from the
|
||||||
|
fund's page. The NAV (net asset value) per share is calculated live from the fund's total
|
||||||
|
portfolio. Fund investments show up in your Holdings and Trade History.
|
||||||
|
</p>
|
||||||
|
<p className="text-slate-500 text-xs">
|
||||||
|
Fund shares are stored to 6 decimal places. Fund accounts cannot sign in directly and do not earn
|
||||||
|
research points or play the lottery.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Account reset */}
|
||||||
|
<Section title="Account Reset" icon={RotateCcw}>
|
||||||
|
<div className="space-y-2 text-sm text-slate-300">
|
||||||
|
<p>
|
||||||
|
You can reset your account from your profile page at any time. All positions are closed and your
|
||||||
|
balance returns to $2,000.
|
||||||
|
</p>
|
||||||
|
<Rule label="Keep history">
|
||||||
|
Your trade log is preserved. A{' '}
|
||||||
|
<span className="text-purple-400">Donation</span> entry is recorded if you were in profit, or a{' '}
|
||||||
|
<span className="text-red-400">Bankruptcy</span> if you were in the red, followed by an{' '}
|
||||||
|
<span className="text-emerald-400">Account Open</span>.
|
||||||
|
</Rule>
|
||||||
|
<Rule label="Erase history">
|
||||||
|
All trade records are deleted along with the reset.
|
||||||
|
</Rule>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Lucky Dip */}
|
||||||
|
<Section title="Lucky Dip" icon={Shuffle}>
|
||||||
|
<p className="text-sm text-slate-300">
|
||||||
|
Once per day you can open the Lucky Dip lottery. Pick a box — most are empty, but a few hold cash prizes.
|
||||||
|
Winnings are added directly to your balance.
|
||||||
|
</p>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* Links */}
|
||||||
|
<section className="bg-surface-card border border-surface-border rounded-xl p-5 space-y-3">
|
||||||
|
<h2 className="text-sm font-semibold text-slate-300 uppercase tracking-wider">Links</h2>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<a
|
||||||
|
href="https://mastodon.nervesocket.com/@ThaMunsta"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-2 text-sm text-indigo-400 hover:text-indigo-300 transition-colors"
|
||||||
|
>
|
||||||
|
<ExternalLink className="h-4 w-4 shrink-0" />
|
||||||
|
@ThaMunsta on Mastodon — questions, feedback, bug reports
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://git.dev.nervesocket.com/ThaMunsta/hashex"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-2 text-sm text-indigo-400 hover:text-indigo-300 transition-colors"
|
||||||
|
>
|
||||||
|
<Github className="h-4 w-4 shrink-0" />
|
||||||
|
Source code — contribute or run your own instance
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="text-center">
|
||||||
|
<Link href="/" className="text-sm text-indigo-400 hover:text-indigo-300">
|
||||||
|
← Back to the exchange
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<!-- dark indigo rounded background -->
|
||||||
|
<rect width="32" height="32" rx="7" fill="#1e1b4b"/>
|
||||||
|
<!-- # symbol — slightly angled vertical bars, two horizontal bars -->
|
||||||
|
<!-- left vertical bar (slants slightly: top-right to bottom-left) -->
|
||||||
|
<line x1="12" y1="6" x2="10" y2="26" stroke="#a5b4fc" stroke-width="2.8" stroke-linecap="round"/>
|
||||||
|
<!-- right vertical bar -->
|
||||||
|
<line x1="20" y1="6" x2="18" y2="26" stroke="#a5b4fc" stroke-width="2.8" stroke-linecap="round"/>
|
||||||
|
<!-- upper horizontal bar -->
|
||||||
|
<line x1="6" y1="13" x2="26" y2="13" stroke="#a5b4fc" stroke-width="2.8" stroke-linecap="round"/>
|
||||||
|
<!-- lower horizontal bar -->
|
||||||
|
<line x1="5" y1="20" x2="25" y2="20" stroke="#a5b4fc" stroke-width="2.8" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 809 B |
+4
-1
@@ -102,7 +102,10 @@ export default async function HomePage() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="text-slate-400 max-w-xl mx-auto">
|
<p className="text-slate-400 max-w-xl mx-auto">
|
||||||
Trade hashtags like stocks. Prices are driven by real-time activity on Mastodon.
|
Trade hashtags like stocks. Prices are driven by real-time activity on Mastodon.
|
||||||
Research a tag to unlock it, then buy long or short.
|
Research a tag to unlock it, then buy long or short.{' '}
|
||||||
|
<Link href="/about" className="text-indigo-400 hover:text-indigo-300 underline underline-offset-2">
|
||||||
|
Learn more →
|
||||||
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
<div className="flex justify-center gap-4 mt-6">
|
<div className="flex justify-center gap-4 mt-6">
|
||||||
{session ? (
|
{session ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user