Harden onion boot flow and deepen site surfaces

Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation.

Made-with: Cursor
This commit is contained in:
drjones
2026-04-07 21:35:52 -07:00
parent 52432dccfa
commit 78a071ba02
162 changed files with 21692 additions and 39 deletions

41
.gitignore vendored Normal file
View File

@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

283
README.md
View File

@@ -1,67 +1,272 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# CYBERLUX
## Getting Started
> Neon storefront. Forty-three onion doors. One build. One app. No dead air.
First, run the development server:
CyberLux is a layered Next.js fiction stack built for dark-web literacy, classroom demos, mirror discipline drills, and absurdly overbuilt cyberpunk presentation. It runs one app behind Tor + nginx, then fans that app out into dedicated v3 hidden services for the hub, forum, exchange, market, crawler, syndicate, wiki layer, and every other top-level surface in `scripts/onion-nodes.json`.
This repo is built to feel like a real underground property while still being a controlled demo environment:
- `43` Tor v3 services
- stable loopback range `127.0.0.1:80808122`
- Next bound only to `127.0.0.1:3000`
- one `.onion` per major surface
- persistent hidden-service identity keyed by `HiddenServiceDir`
- self-healing backup / restore of onion key directories
## What It Does
CyberLux boots a single Next.js app and projects it through multiple onion entry points.
- `hub` is the main storefront
- `forum` is Void Aggregate
- `exchange` is the classifieds stack
- `market` is the full catalog vertical
- `search` is Void Crawler
- `wiki` is the hidden-wiki layer
- `w` is the syndicate shell network
- dozens of additional dedicated onions map directly to top-level app routes
The routing layer uses host-aware rewrites so a dedicated onion feels like its own property without forking the app into 43 separate deployments.
## One-Command Launch
Install system deps first on Debian/Ubuntu:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
sudo apt install tor nginx
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## CyberLux on Tor (classroom / demo)
The UI uses fictional `.onion` names in places; **live addresses come from your Tor daemon** after you install the hidden services.
**One-command launch:** install `tor` and `nginx` (`apt install tor nginx` on Debian/Ubuntu), then:
Then run:
```bash
chmod +x start.sh # once
git pull && ./start.sh
chmod +x start.sh
./start.sh
```
The first step regenerates configs from `scripts/onion-nodes.json` (43 Tor v3 services → loopback `80808122`, one `.onion` per top-level area). Then it runs `git pull` if `.git` exists (failure is non-fatal), `npm install` / `npm run build`. If `.next/` is **not writable** (common after a `sudo npm run build`), the script runs **`sudo chown`** on `.next` for you. It then installs Tor + nginx via `sudo` (skip with `CYBERLUX_SKIP_TOR=1` for local-only), prints **every** `.onion` URL, and starts Next on `127.0.0.1:3000`. Use **Tor Browser** with `http://YOUR56CHAR.onion`.
What `./start.sh` does:
To add or reorder onions, edit `scripts/onion-nodes.json` and run `node scripts/generate-onion-config.cjs` (also runs automatically on `npm run build` and via `./start.sh`).
1. regenerates Tor / nginx / app route maps from `scripts/onion-nodes.json`
2. runs `git pull --ff-only` if the repo has `.git` (failure is non-fatal)
3. runs `npm install`
4. repairs `.next` ownership if a root-owned build left it unwritable
5. runs a production build
6. restores backed-up onion keys if any hidden-service directories are missing
7. installs Tor + nginx config only when it actually changed
8. waits for all onion hostname files, not just the hub
9. refreshes the onion key backup set
10. prints every live `.onion` URL
11. starts Next on `127.0.0.1:3000`
Manual steps (equivalent): `sudo bash scripts/install-tor-onion.sh`, then `npm run build && npm run start:onion`.
Use Tor Browser and open the printed URLs as:
**Start at boot (systemd):** copy `scripts/cyberlux.service`, set `User`, `Group`, and `WorkingDirectory` to your clone, `sudo install -m 0644 scripts/cyberlux.service /etc/systemd/system/`, then `sudo systemctl daemon-reload && sudo systemctl enable --now cyberlux.service`. Tor and nginx should already be enabled; this only keeps Next running after reboot.
```text
http://<56-char-v3-address>.onion
```
**Build fails with `EACCES` on `unlink` / `build-diagnostics.json`:** Next.js writes metadata under `.next/diagnostics/build-diagnostics.json` (build stage, options — not an app bug). If those files were created as **root** (e.g. `sudo npm run build`), your user cannot overwrite them. **`npm run build` now stops in `prebuild`** with a clear error instead of a cryptic unlink message. Fix once:
## Persistent Onion Addresses
Yes: the onion addresses are meant to persist.
Each service uses a fixed Tor directory:
```tor
HiddenServiceDir /var/lib/tor/<service-dir>
HiddenServicePort 80 127.0.0.1:<loopback-port>
```
That means the `.onion` address remains stable as long as:
- the `torDir` names in `scripts/onion-nodes.json` do not change
- `/var/lib/tor/<service-dir>` is preserved
- or the backup set can restore those directories
Addresses do **not** rotate on normal app restarts, rebuilds, or standard Tor restarts.
## Self-Healing Onion Identity
CyberLux now includes automatic onion key persistence tooling:
- `scripts/backup-onion-keys.sh`
- `scripts/restore-onion-keys.sh`
Backup location:
```text
/var/backups/cyberlux-onion-keys/current
```
Behavior:
- after boot, ready onion service directories are backed up
- on next boot, if a service dir is missing from `/var/lib/tor`, it is restored from backup
- Tor identity is preserved across ordinary host mistakes, partial deletions, or service-dir drift recovery
This is the difference between “looks cool in a screenshot” and “survives operator error.”
## Tor / nginx Topology
Source of truth:
- `scripts/onion-nodes.json`
Generated artifacts:
- `tor/cyberlux-nodes.conf`
- `nginx/cyberlux-onion-servers.inc`
- `lib/onionRoutes.generated.ts`
- `scripts/generated/tor-dirs.txt`
- `scripts/generated/onion-labels.tsv`
- `scripts/generated/onion-port-range.txt`
Install path:
- `/etc/tor/cyberlux-nodes.conf`
- `/etc/nginx/cyberlux-server-common.inc`
- `/etc/nginx/cyberlux-onion-servers.inc`
- `/etc/nginx/sites-available/cyberlux-onion`
Runtime:
- Next.js: `127.0.0.1:3000`
- nginx onion vhosts: `127.0.0.1:80808122`
- Tor exposes the public `.onion` endpoints
## Manual Ops
Install or refresh Tor/nginx config:
```bash
sudo bash scripts/install-tor-onion.sh
```
Back up onion keys immediately:
```bash
sudo bash scripts/backup-onion-keys.sh
```
Restore missing onion keys:
```bash
sudo bash scripts/restore-onion-keys.sh
```
Run local-only, skipping Tor/nginx:
```bash
CYBERLUX_SKIP_TOR=1 ./start.sh
```
Start app without the helper:
```bash
npm run build
npm run start:onion
```
## Boot At Startup
`scripts/cyberlux.service` is included as a systemd template for the Next process.
Before installing it, set:
- `User=`
- `Group=`
- `WorkingDirectory=`
Then install:
```bash
sudo install -m 0644 scripts/cyberlux.service /etc/systemd/system/cyberlux.service
sudo systemctl daemon-reload
sudo systemctl enable --now cyberlux.service
```
Notes:
- Tor and nginx should already be installed and enabled
- the service unit keeps the Next process alive
- Tor/nginx config is still managed by the repo scripts
## Verification
Run the full local verification suite:
```bash
npm run verify
```
That checks:
- onion config generation
- TypeScript
- shell syntax for boot / install / backup / restore scripts
- full Next production build
## Common Failure: `.next` Permission Hell
If you previously ran a build with `sudo`, `.next` may be root-owned and Next will fail with `EACCES` during unlink / diagnostics writes.
Fix once:
```bash
sudo bash scripts/fix-next-perms.sh
```
(or `sudo chown -R "$USER" .next` / `sudo rm -rf .next`), then `npm run build` or `./start.sh` again.
Then rerun:
**Verify locally:** after fixing permissions, run `npm run verify` — regenerates onion configs, runs `tsc --noEmit`, checks shell scripts, and runs a production build.
```bash
./start.sh
```
**Security notes (for instructors):** No stack is “perfectly” secure. This setup keeps the app off the public internet: nginx and Next listen on loopback; Tor exposes onion endpoints only. Optional: `sudo bash scripts/classroom-ufw.sh` — do not publish `3000` or nginx loopback ports (`80808122`) to the public internet. Tor improves reachability privacy for the **service** vs raw IP hosting; it does not make unsafe application code safe or absolve lawful/ethical/use-policy obligations—use only for legitimate teaching and demos.
## Security Notes
## Learn More
CyberLux is hardened for a demo stack, not sold as magic invisibility.
To learn more about Next.js, take a look at the following resources:
- Next and nginx bind to loopback only
- Tor exposes the onion endpoints
- do **not** publish `3000` or `80808122` to the public internet
- optional host firewall script: `sudo bash scripts/classroom-ufw.sh`
- the app can still be insecure if the app code is insecure
- operational mistakes still matter more than branding
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
Tor hides service reachability better than raw-IP hosting. It does not redeem bad application logic, bad operator habits, or bad judgment.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Site Map
## Deploy on Vercel
Core verticals:
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
- `/` hub storefront
- `/market` full catalog
- `/forum` Void Aggregate
- `/exchange` classifieds
- `/barter` Ash Pit
- `/search` Void Crawler
- `/hidden-wiki` wiki layer
- `/darknet-atlas` taxonomy / atlas
- `/syndicate` shell network
- `/dashboard` personal relay
- `/account/hidden-services` mirror map
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
There are additional dedicated onions for many top-level routes beyond those.
## Stack
- Next.js `16`
- React `19`
- Tailwind CSS `4`
- nginx
- Tor hidden services
- generated route / host mapping
## Final Word
CyberLux is supposed to boot like a machine that knows what it is:
- same onion doors
- same identities
- no shallow mirrors
- no fake “self healing” copy without actual restore logic
- no dead routes pretending to be part of the network
If it starts, it should start hard.

View File

@@ -0,0 +1,159 @@
"use client";
import { FormEvent, useEffect, useState } from "react";
import Link from "next/link";
import { useAccount } from "@/contexts/AccountContext";
import { exportPortableAccountJson, importPortableAccountJson } from "@/lib/cyberluxAccount";
export default function HiddenServicesAccountPage() {
const { user, hydrated, refreshProfile } = useAccount();
const [importText, setImportText] = useState("");
const [importMsg, setImportMsg] = useState<{ kind: "ok" | "err"; text: string } | null>(null);
const [copyMsg, setCopyMsg] = useState<string | null>(null);
useEffect(() => {
setImportMsg(null);
}, [importText]);
const onCopyBundle = async () => {
setCopyMsg(null);
const json = exportPortableAccountJson();
if (!json) return;
try {
await navigator.clipboard.writeText(json);
setCopyMsg("Copied. Paste on another onion host → Import below.");
} catch {
setCopyMsg("Could not use clipboard — select and copy manually.");
}
};
const onImport = (e: FormEvent) => {
e.preventDefault();
setImportMsg(null);
const res = importPortableAccountJson(importText.trim());
if (!res.ok) {
setImportMsg({ kind: "err", text: res.error });
return;
}
refreshProfile();
setImportText("");
setImportMsg({ kind: "ok", text: "Identity loaded on this hostname. You can use dashboard, forum, exchange, and barter here." });
};
if (!hydrated) {
return (
<div className="flex min-h-[40vh] items-center justify-center font-mono text-sm text-zinc-500">
Loading
</div>
);
}
return (
<div className="min-h-screen bg-[#0a0a0a] px-4 py-14 text-zinc-200">
<div className="mx-auto max-w-lg">
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-emerald-500/80">hidden services</p>
<h1 className="mt-3 font-orbitron text-2xl font-bold text-zinc-100">Account on every entry node</h1>
<p className="mt-4 text-sm leading-relaxed text-zinc-500">
Each hidden-service hostname gets an isolated browser vault. Keys you mint on the forum onion do not
automatically exist on the exchange or wiki host mirror your identity deliberately or enroll separately.
</p>
<ol className="mt-8 list-decimal space-y-4 border-l border-white/10 pl-5 text-sm text-zinc-400">
<li>
<strong className="text-zinc-300">Create a handle</strong> on this host:{" "}
<Link href="/sign-up" className="text-emerald-400 hover:underline">
Sign up
</Link>{" "}
(or{" "}
<Link href="/sign-in" className="text-emerald-400 hover:underline">
Sign in
</Link>
).
</li>
<li>
<strong className="text-zinc-300">Repeat on other mirrors</strong> with the{" "}
<strong className="text-zinc-300">same handle and passphrase</strong>,{" "}
<span className="text-zinc-500">or</span> export a bundle here (signed in) and import it on the other host.
</li>
<li>
Use{" "}
<Link href="/dashboard" className="text-emerald-400 hover:underline">
Dashboard
</Link>
,{" "}
<Link href="/forum" className="text-emerald-400 hover:underline">
Forum
</Link>
,{" "}
<Link href="/exchange" className="text-emerald-400 hover:underline">
Exchange
</Link>
, and{" "}
<Link href="/barter" className="text-emerald-400 hover:underline">
Barter
</Link>{" "}
once this hostname knows your session.
</li>
</ol>
{user ? (
<section className="glass mt-10 rounded-2xl border border-emerald-900/40 p-6">
<h2 className="font-mono text-xs font-bold uppercase tracking-widest text-emerald-400/90">Portable bundle</h2>
<p className="mt-2 text-xs text-zinc-500">
Signed in as <span className="font-mono text-emerald-200/90">@{user.username}</span>. The bundle contains a
passphrase fingerprint do not share it.
</p>
<button
type="button"
onClick={() => void onCopyBundle()}
className="mt-4 w-full rounded-xl border border-emerald-700/50 bg-emerald-950/40 py-3 font-mono text-sm font-bold text-emerald-200 hover:bg-emerald-900/30"
>
Copy identity bundle
</button>
{copyMsg ? <p className="mt-3 text-xs text-zinc-400">{copyMsg}</p> : null}
</section>
) : (
<p className="mt-10 rounded-xl border border-white/10 bg-white/[0.03] px-4 py-3 text-center text-sm text-zinc-500">
<Link href="/sign-in" className="text-emerald-400 hover:underline">
Sign in
</Link>{" "}
to copy an export bundle for another host.
</p>
)}
<section className="glass mt-8 rounded-2xl border border-white/10 p-6">
<h2 className="font-mono text-xs font-bold uppercase tracking-widest text-zinc-400">Import bundle</h2>
<p className="mt-2 text-xs text-zinc-500">Paste JSON from &quot;Copy identity bundle&quot; on the host where you exported it.</p>
<form onSubmit={onImport} className="mt-4 space-y-3">
<textarea
value={importText}
onChange={(e) => setImportText(e.target.value)}
rows={5}
placeholder='{"v":1,"username":"…", …}'
className="glass w-full rounded-xl border border-white/10 bg-black/30 px-3 py-2 font-mono text-[11px] text-zinc-300"
/>
<button
type="submit"
className="w-full rounded-xl bg-gradient-to-r from-emerald-800 to-emerald-700 py-3 font-bold text-white hover:opacity-95"
>
Import & sign in here
</button>
</form>
{importMsg ? (
<p
className={`mt-3 text-sm ${importMsg.kind === "ok" ? "text-emerald-300/90" : "text-red-300/90"}`}
>
{importMsg.text}
</p>
) : null}
</section>
<p className="mt-10 text-center">
<Link href="/hidden-wiki" className="text-xs text-zinc-600 hover:text-zinc-400">
Hidden wiki
</Link>
</p>
</div>
</div>
);
}

121
app/api/btc/verify/route.ts Normal file
View File

@@ -0,0 +1,121 @@
import { NextResponse } from "next/server";
const MEMPOOL = "https://mempool.space/api";
const MIN_CONFIRMATIONS = 1;
function merchantAddress(): string {
return (
process.env.MERCHANT_BTC_ADDRESS ||
process.env.NEXT_PUBLIC_MERCHANT_BTC_ADDRESS ||
""
).trim();
}
export async function POST(req: Request) {
const merchant = merchantAddress();
if (!merchant) {
return NextResponse.json(
{ ok: false, error: "Server not configured: set MERCHANT_BTC_ADDRESS" },
{ status: 503 },
);
}
let body: unknown;
try {
body = await req.json();
} catch {
return NextResponse.json({ ok: false, error: "Invalid JSON" }, { status: 400 });
}
const txid =
typeof body === "object" &&
body !== null &&
"txid" in body &&
typeof (body as { txid: unknown }).txid === "string"
? (body as { txid: string }).txid.trim()
: "";
if (!/^[a-fA-F0-9]{64}$/.test(txid)) {
return NextResponse.json({ ok: false, error: "Invalid transaction id" }, { status: 400 });
}
const txRes = await fetch(`${MEMPOOL}/tx/${txid}`, { cache: "no-store" });
if (!txRes.ok) {
return NextResponse.json(
{ ok: false, error: "Transaction not found on network yet" },
{ status: 404 },
);
}
const tx = (await txRes.json()) as {
vout?: Array<{ value?: number; scriptpubkey_address?: string }>;
status?: { confirmed?: boolean; block_height?: number };
};
const merchantLower = merchant.toLowerCase();
let sats = 0;
for (const vout of tx.vout || []) {
const addr = vout.scriptpubkey_address;
if (addr && addr.toLowerCase() === merchantLower && typeof vout.value === "number") {
sats += vout.value;
}
}
if (sats <= 0) {
return NextResponse.json(
{
ok: false,
error: "This transaction does not pay your configured merchant Bitcoin address",
},
{ status: 400 },
);
}
let confirmations = 0;
if (tx.status?.confirmed && typeof tx.status.block_height === "number") {
const tipRes = await fetch(`${MEMPOOL}/blocks/tip/height`, { cache: "no-store" });
if (tipRes.ok) {
const tip = Number(await tipRes.text());
if (Number.isFinite(tip)) {
confirmations = tip - tx.status.block_height + 1;
}
}
}
if (confirmations < MIN_CONFIRMATIONS) {
return NextResponse.json({
ok: false,
error: `Need at least ${MIN_CONFIRMATIONS} confirmation(s); currently ${confirmations}`,
confirmations,
sats,
});
}
const priceRes = await fetch(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd",
{ next: { revalidate: 60 } },
);
if (!priceRes.ok) {
return NextResponse.json(
{ ok: false, error: "Could not fetch BTC/USD rate; try again shortly" },
{ status: 502 },
);
}
const priceJson = (await priceRes.json()) as { bitcoin?: { usd?: number } };
const btcUsd = priceJson.bitcoin?.usd;
if (!btcUsd || !Number.isFinite(btcUsd)) {
return NextResponse.json({ ok: false, error: "Invalid rate response" }, { status: 502 });
}
const btc = sats / 1e8;
const creditedUsd = Math.round(btc * btcUsd * 100) / 100;
return NextResponse.json({
ok: true,
creditedUsd,
sats,
btc,
btcUsd,
confirmations,
});
}

46
app/arb-academy/page.tsx Normal file
View File

@@ -0,0 +1,46 @@
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function AcademyPage() {
return (
<ThemedLayout theme="minimal" title="Arb Academy">
<div className="space-y-12 text-[#e6e6e6]">
<header className="border-b-4 border-white/20 pb-4">
<h2 className="text-5xl font-black uppercase tracking-tighter text-[#fafafa]">The Art of the Trade</h2>
<p className="mt-2 text-sm italic text-[#a0a0a0]">Knowledge is the only currency that never devalues.</p>
</header>
<div className="grid grid-cols-1 gap-12 md:grid-cols-3">
<div className="col-span-2 space-y-8">
<section>
<h3 className="text-2xl font-bold mb-4 underline decoration-4">Lesson 01: OpSec</h3>
<p className="leading-relaxed">
Operational Security is not a tool, it is a mindset. Before you enter the market,
you must understand the trail you leave behind. Every packet is a footprint.
</p>
</section>
<section>
<h3 className="text-2xl font-bold mb-4 underline decoration-4">Lesson 02: PGP</h3>
<p className="leading-relaxed">
Encryption is the wall between you and the void. If you cannot sign your words,
your words have no weight. Master the key, master the trade.
</p>
</section>
</div>
<aside className="space-y-6 border border-white/10 bg-[#141414] p-8 text-[#e8e8e8]">
<h4 className="border-b border-white/20 pb-2 text-xl font-bold uppercase">Resources</h4>
<ul className="text-xs space-y-4">
<li className="hover:underline cursor-pointer">The Anarchist's Cookbook v2</li>
<li className="hover:underline cursor-pointer">Tor Routing Deep Dive</li>
<li className="hover:underline cursor-pointer">Bitcoin Tumbling 101</li>
</ul>
</aside>
</div>
<div className="mt-20 flex items-center justify-between border-t-2 border-white/15 pt-8">
<div className="text-4xl font-black">A.A.</div>
<div className="text-xs uppercase font-bold">Est. 2024 // Node 07</div>
</div>
</div>
</ThemedLayout>
);
}

69
app/awards/page.tsx Normal file
View File

@@ -0,0 +1,69 @@
"use client";
import { useState, useEffect } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function AwardsPage() {
const [glitch, setGlitch] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
setGlitch(true);
setTimeout(() => setGlitch(false), 100);
}, 3000);
return () => clearInterval(interval);
}, []);
const awards = [
{ year: "2025", title: "Best Market Infrastructure", winner: "The Candy Shop", icon: "🏆" },
{ year: "2024", title: "Most Secure Node", winner: "The Sanctuary", icon: "🛡️" },
{ year: "2024", title: "Innovation in Shared Entities", winner: "Darknet Ping Pong", icon: "🔮" },
{ year: "2023", title: "Excellence in OpSec Training", winner: "Arb Academy", icon: "🎓" },
];
return (
<ThemedLayout theme="mystic" title="The Shadow Awards">
<div className={`max-w-5xl mx-auto pt-12 transition-all duration-75 ${glitch ? 'skew-x-1 scale-105 blur-[1px]' : ''}`}>
<header className="text-center mb-20 relative">
<div className="absolute inset-0 flex items-center justify-center opacity-10 pointer-events-none">
<div className="text-[200px] font-black">S.A.</div>
</div>
<h1 className="text-7xl font-serif italic mb-4 relative z-10">The Shadow Awards</h1>
<p className="text-xs uppercase tracking-[1em] opacity-40">Recognizing Excellence in the Void</p>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 mb-20">
{awards.map((a, i) => (
<div key={i} className="group border-2 border-[#d4af37]/20 p-12 bg-black/40 hover:border-[#d4af37]/60 transition-all relative overflow-hidden">
<div className="absolute -right-4 -bottom-4 text-8xl opacity-5 group-hover:opacity-10 transition-opacity">
{a.icon}
</div>
<div className="text-[#d4af37] text-xs font-bold mb-2">{a.year} Laureate</div>
<h3 className="text-3xl font-serif mb-4">{a.title}</h3>
<div className="text-white/60 text-sm italic">Awarded to:</div>
<div className="text-xl font-bold text-white uppercase tracking-widest">{a.winner}</div>
</div>
))}
</div>
<section className="border-t-2 border-[#d4af37]/20 pt-20 text-center">
<h2 className="text-4xl font-serif mb-8 italic">Official Accreditation</h2>
<div className="flex justify-center gap-12 opacity-40 grayscale hover:grayscale-0 transition-all">
<img src="https://www.svgrepo.com/show/443364/award-badge.svg" className="h-24 invert" />
<img src="https://www.svgrepo.com/show/443365/award-ribbon.svg" className="h-24 invert" />
<img src="https://www.svgrepo.com/show/443366/award-star.svg" className="h-24 invert" />
</div>
<p className="mt-12 text-[10px] uppercase tracking-widest opacity-30 max-w-2xl mx-auto leading-loose">
The Shadow Awards committee is comprised of anonymous delegates from the top 100 nodes.
Selection is based on uptime, security, and contribution to the network's growth.
</p>
</section>
<div className="mt-32 text-center">
<div className="text-[8px] opacity-20 uppercase tracking-[0.5em] mb-4">End of Official Record</div>
<div className="text-red-500 text-[10px] font-bold"></div>
</div>
</div>
</ThemedLayout>
);
}

16
app/barter/layout.tsx Normal file
View File

@@ -0,0 +1,16 @@
import { Suspense } from "react";
import BarterPitChrome from "@/components/barter/BarterPitChrome";
export default function BarterLayout({ children }: { children: React.ReactNode }) {
return (
<BarterPitChrome>
<Suspense
fallback={
<div className="py-16 text-center font-mono text-sm text-[#5a7d62]">Loading trading floor</div>
}
>
{children}
</Suspense>
</BarterPitChrome>
);
}

184
app/barter/page.tsx Normal file
View File

@@ -0,0 +1,184 @@
"use client";
import { FormEvent, useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams } from "next/navigation";
import { useAccount } from "@/contexts/AccountContext";
import { addBarterListing, loadBarter, type BarterLane, type BarterListing } from "@/lib/barterState";
const LANES: { id: BarterLane; label: string; hint: string }[] = [
{ id: "goods", label: "Physical / goods", hint: "Hardware, prints, tokens, boxes…" },
{ id: "services", label: "Labor / skills", hint: "Tune-ups, consults, studio time…" },
{ id: "data", label: "Data / media", hint: "Scans, decks, anonymized sets…" },
{ id: "open", label: "Open terms", hint: "Wildcard swaps — spell it out." },
];
export default function BarterPitPage() {
const sp = useSearchParams();
const laneFilter = sp.get("lane") as BarterLane | null;
const { user, hydrated: accountReady } = useAccount();
const prefilledAuthor = useRef(false);
const [rows, setRows] = useState<BarterListing[]>([]);
const [barterReady, setBarterReady] = useState(false);
const [lane, setLane] = useState<BarterLane>("open");
const [author, setAuthor] = useState("");
const [title, setTitle] = useState("");
const [have, setHave] = useState("");
const [want, setWant] = useState("");
const [body, setBody] = useState("");
useEffect(() => {
setRows(loadBarter());
setBarterReady(true);
}, []);
useEffect(() => {
if (!accountReady || !user || prefilledAuthor.current) return;
setAuthor(user.displayName);
prefilledAuthor.current = true;
}, [accountReady, user]);
useEffect(() => {
if (laneFilter && ["goods", "services", "data", "open"].includes(laneFilter)) {
setLane(laneFilter);
}
}, [laneFilter]);
const filtered = useMemo(() => {
const sorted = [...rows].sort((a, b) => b.ts - a.ts);
if (!laneFilter || !["goods", "services", "data", "open"].includes(laneFilter)) return sorted;
return sorted.filter((r) => r.lane === laneFilter);
}, [rows, laneFilter]);
const onSubmit = (e: FormEvent) => {
e.preventDefault();
if (!title.trim() || !have.trim() || !want.trim()) return;
addBarterListing({
lane,
author,
title,
have,
want,
body,
});
setRows(loadBarter());
setTitle("");
setHave("");
setWant("");
setBody("");
};
return (
<div className="grid gap-10 lg:grid-cols-[1fr_380px] lg:items-start">
<section className="space-y-4">
{!barterReady ? (
<p className="font-mono text-sm text-[#5a7d62]">Syncing order book</p>
) : (
filtered.map((r) => (
<article
key={r.id}
className="rounded-lg border border-[#164024] bg-[#070d0a]/90 p-5 shadow-[0_0_0_1px_rgba(57,255,20,0.06)]"
>
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-[#0d2818] pb-3 font-mono text-[10px] uppercase tracking-widest text-[#39ff14]/65">
<span>{r.lane}</span>
<time className="text-[#5a7d62]">{new Date(r.ts).toLocaleString()}</time>
</div>
<h2 className="mt-3 font-mono text-lg font-bold text-[#c8ffce]">{r.title}</h2>
<p className="mt-1 font-mono text-[11px] text-[#6b9b78]">
<span className="text-[#39ff14]/80">{r.author || "Anonymous"}</span>
</p>
<div className="mt-4 grid gap-3 font-mono text-sm md:grid-cols-2">
<div className="rounded border border-[#123218] bg-[#050a07] p-3">
<p className="text-[9px] uppercase tracking-[0.2em] text-[#39ff14]/55">Offers (have)</p>
<p className="mt-2 leading-relaxed text-[#b8f5c6]">{r.have}</p>
</div>
<div className="rounded border border-[#123218] bg-[#050a07] p-3">
<p className="text-[9px] uppercase tracking-[0.2em] text-[#39ff14]/55">Wants (take)</p>
<p className="mt-2 leading-relaxed text-[#b8f5c6]">{r.want}</p>
</div>
</div>
{r.body ? (
<p className="mt-4 whitespace-pre-wrap font-mono text-xs leading-relaxed text-[#7aaf88]/95">{r.body}</p>
) : null}
</article>
))
)}
</section>
<aside className="lg:sticky lg:top-28">
<div className="rounded-lg border border-[#39ff14]/25 bg-[#050a07]/95 p-5">
<h3 className="font-mono text-xs font-bold uppercase tracking-[0.25em] text-[#39ff14]/85">Post a swap</h3>
<p className="mt-2 font-mono text-[10px] leading-relaxed text-[#5a7d62]">
Barter only no automated matching. Treat every DM like a phishing drill until PGP proves otherwise.
</p>
<form onSubmit={onSubmit} className="mt-5 space-y-4">
<div>
<label className="font-mono text-[9px] uppercase tracking-wider text-[#6b9b78]">Lane</label>
<select
value={lane}
onChange={(e) => setLane(e.target.value as BarterLane)}
className="mt-1 w-full rounded border border-[#164024] bg-[#030806] px-3 py-2 font-mono text-xs text-[#b8f5c6] focus:border-[#39ff14]/50 focus:outline-none"
>
{LANES.map((l) => (
<option key={l.id} value={l.id} className="bg-[#030806]">
{l.label}
</option>
))}
</select>
</div>
<input
className="w-full rounded border border-[#164024] bg-[#030806] px-3 py-2 font-mono text-xs text-[#b8f5c6] placeholder:text-[#3d5244] focus:border-[#39ff14]/45 focus:outline-none"
placeholder="Alias"
value={author}
onChange={(e) => setAuthor(e.target.value)}
/>
<input
required
className="w-full rounded border border-[#164024] bg-[#030806] px-3 py-2 font-mono text-xs text-[#b8f5c6] placeholder:text-[#3d5244] focus:border-[#39ff14]/45 focus:outline-none"
placeholder="Headline (short)"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<textarea
required
rows={3}
className="w-full rounded border border-[#164024] bg-[#030806] px-3 py-2 font-mono text-xs text-[#b8f5c6] placeholder:text-[#3d5244] focus:border-[#39ff14]/45 focus:outline-none"
placeholder="What you're putting on the table…"
value={have}
onChange={(e) => setHave(e.target.value)}
/>
<textarea
required
rows={3}
className="w-full rounded border border-[#164024] bg-[#030806] px-3 py-2 font-mono text-xs text-[#b8f5c6] placeholder:text-[#3d5244] focus:border-[#39ff14]/45 focus:outline-none"
placeholder="What you want back (goods, hours, XMR, favor…)…"
value={want}
onChange={(e) => setWant(e.target.value)}
/>
<textarea
rows={4}
className="w-full rounded border border-[#164024] bg-[#030806] px-3 py-2 font-mono text-xs text-[#b8f5c6] placeholder:text-[#3d5244] focus:border-[#39ff14]/45 focus:outline-none"
placeholder="Terms, meetup fiction, PGP fingerprint, escrow notes…"
value={body}
onChange={(e) => setBody(e.target.value)}
/>
<button
type="submit"
className="w-full rounded border border-[#39ff14] bg-[#39ff14]/15 py-3 font-mono text-xs font-black uppercase tracking-wider text-[#c8ffce] hover:bg-[#39ff14]/25"
>
List on floor
</button>
</form>
<ul className="mt-5 space-y-2 border-t border-[#0d2818] pt-4 font-mono text-[10px] text-[#5a7d62]">
{LANES.map((l) => (
<li key={l.id}>
<span className="text-[#39ff14]/55">{l.label}:</span> {l.hint}
</li>
))}
</ul>
</div>
</aside>
</div>
);
}

142
app/chatter/page.tsx Normal file
View File

@@ -0,0 +1,142 @@
"use client";
import Link from "next/link";
import { useMemo } from "react";
import { useSearchParams } from "next/navigation";
import { formatRingLabel } from "@/lib/forumRings";
import { SHOP_CHATTER_COUNT, getChatterSlice } from "@/lib/shopChatterThreads";
const PAGE_SIZE = 12;
function formatAgo(ts: number): string {
const s = Math.floor((Date.now() - ts) / 1000);
if (s < 60) return `${s}s ago`;
const m = Math.floor(s / 60);
if (m < 60) return `${m}m ago`;
const h = Math.floor(m / 60);
if (h < 48) return `${h}h ago`;
const d = Math.floor(h / 24);
return `${d}d ago`;
}
export default function HubChatterArchivePage() {
const sp = useSearchParams();
const page = Math.max(1, parseInt(sp.get("page") ?? "1", 10) || 1);
const { items, totalPages, total } = useMemo(
() => getChatterSlice(page, PAGE_SIZE),
[page],
);
const pages = useMemo(() => {
const window = 2;
const lo = Math.max(1, page - window);
const hi = Math.min(totalPages, page + window);
const out: number[] = [];
for (let i = lo; i <= hi; i++) out.push(i);
return out;
}, [page, totalPages]);
return (
<div className="min-h-screen bg-[#050403] text-[#d4c4b0]">
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.08]"
style={{
backgroundImage:
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,180,120,0.12) 3px)",
}}
/>
<header className="relative z-10 border-b border-[#3d3028] bg-[#0c0906]/95 px-4 py-8">
<div className="mx-auto max-w-3xl">
<p className="font-mono text-[10px] uppercase tracking-[0.4em] text-[#c45c26]/90">archive</p>
<h1 className="mt-2 font-mono text-2xl font-black text-[#ffcb8a] md:text-3xl">Hub chatter index</h1>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-[#9a8d7c]">
Scraped-style threads about the <strong className="text-[#ffcb8a]">CyberLux main storefront</strong> mirror
checks, checkout banter, UI roasts, class-lab meta. Each row opens in Void Aggregate for full comments.
</p>
<nav className="mt-6 flex flex-wrap gap-3 font-mono text-xs">
<Link
href="/forum"
className="rounded border border-[#c45c26] bg-[#c45c26]/15 px-4 py-2 font-bold text-[#ffcb8a] hover:bg-[#c45c26]/25"
>
Void Aggregate forum
</Link>
<Link href="/" className="rounded border border-[#3d3028] px-4 py-2 text-[#b8a995] hover:border-[#c45c26]/50">
Main hub
</Link>
</nav>
</div>
</header>
<main className="relative z-10 mx-auto max-w-3xl px-4 pb-24 pt-8">
<p className="mb-6 font-mono text-xs text-[#6b6358]">
Showing {(page - 1) * PAGE_SIZE + 1}{Math.min(page * PAGE_SIZE, total)} of {total} indexed posts ·{" "}
{PAGE_SIZE} per page
</p>
<ul className="space-y-4">
{items.map((row) => (
<li key={row.id} className="rounded-lg border border-[#2a2218] bg-[#0a0705]/95 p-4">
<div className="flex flex-wrap items-baseline justify-between gap-2 font-mono text-[10px] uppercase tracking-wider text-[#7a6e62]">
<span className="text-[#ff9a3c]/90">{formatRingLabel(row.topicSlug)}</span>
<span>
{row.author} · {formatAgo(row.ts)}
</span>
</div>
<Link
href={`/forum/post/${row.id}`}
className="mt-2 block font-mono text-base font-bold leading-snug text-[#ffcb8a] hover:underline"
>
{row.title}
</Link>
<p className="mt-2 line-clamp-3 text-sm leading-relaxed text-[#b0a090]">{row.body}</p>
<div className="mt-3 flex flex-wrap gap-3 font-mono text-[11px] text-[#6b6358]">
<span>{row.replies.length} comments</span>
<span>score ~{row.baseScore ?? 1}</span>
<Link href={`/forum/post/${row.id}`} className="text-[#ff9a3c] hover:underline">
open thread
</Link>
</div>
</li>
))}
</ul>
<nav className="mt-12 flex flex-wrap items-center justify-center gap-2 font-mono text-xs">
{page > 1 ? (
<Link
href={`/chatter?page=${page - 1}`}
className="rounded border border-[#3d3028] px-3 py-2 text-[#ffcb8a] hover:border-[#c45c26]"
>
Newer
</Link>
) : (
<span className="rounded border border-transparent px-3 py-2 text-[#4a433c]"> Newer</span>
)}
{pages.map((n) => (
<Link
key={n}
href={`/chatter?page=${n}`}
className={`rounded border px-3 py-2 ${
n === page ? "border-[#c45c26] bg-[#c45c26]/20 text-[#ffcb8a]" : "border-[#3d3028] text-[#b8a995] hover:border-[#c45c26]/40"
}`}
>
{n}
</Link>
))}
{page < totalPages ? (
<Link
href={`/chatter?page=${page + 1}`}
className="rounded border border-[#3d3028] px-3 py-2 text-[#ffcb8a] hover:border-[#c45c26]"
>
Older
</Link>
) : (
<span className="rounded border border-transparent px-3 py-2 text-[#4a433c]">Older </span>
)}
</nav>
<p className="mt-10 text-center font-mono text-[10px] text-[#4a4338]">Total catalogued: {SHOP_CHATTER_COUNT} threads</p>
</main>
</div>
);
}

60
app/checkout/page.tsx Normal file
View File

@@ -0,0 +1,60 @@
import Navbar from "@/components/Navbar";
import ParticleBackground from "@/components/ParticleBackground";
import CheckoutFlow from "@/components/CheckoutFlow";
import Link from "next/link";
export default function CheckoutPage() {
return (
<>
<ParticleBackground />
<Navbar />
<main className="container mx-auto px-4 py-24">
<div className="mb-12 text-center">
<Link
href="/"
className="inline-flex items-center gap-2 text-neon-cyan hover:text-neon-purple"
>
Back to Sanctuary
</Link>
<h1 className="mt-6 font-orbitron text-5xl font-bold md:text-6xl">
CHECKOUT <span className="text-neon-cyan">RITUAL</span>
</h1>
<p className="mx-auto mt-4 max-w-2xl text-foreground/70">
Each step is designed to feel like a ceremonial unlocking. Your data is encrypted in realtime, and your payment is transformed into a visual experience.
</p>
</div>
<CheckoutFlow />
<div className="mt-16 grid grid-cols-1 gap-8 md:grid-cols-3">
<div className="glass rounded-2xl border border-white/10 p-6">
<div className="mb-4 text-3xl">🔐</div>
<h3 className="mb-2 font-bold">EndtoEnd Encryption</h3>
<p className="text-sm text-foreground/60">
Your payment details never touch our servers. They are encrypted clientside before transmission.
</p>
</div>
<div className="glass rounded-2xl border border-white/10 p-6">
<div className="mb-4 text-3xl"></div>
<h3 className="mb-2 font-bold">Instant Crypto Conversion</h3>
<p className="text-sm text-foreground/60">
Realtime exchange rates ensure you pay the exact amount, with no hidden fees.
</p>
</div>
<div className="glass rounded-2xl border border-white/10 p-6">
<div className="mb-4 text-3xl">🎨</div>
<h3 className="mb-2 font-bold">Digital Art Receipt</h3>
<p className="text-sm text-foreground/60">
Each completed order generates a unique piece of generative art stored in your vault.
</p>
</div>
</div>
</main>
<footer className="glass border-t border-white/10 px-4 py-8 text-center text-sm text-foreground/40">
<p>
Crypto checkout uses your <strong>USD balance</strong> after a Bitcoin payment is{" "}
<strong>verified on-chain</strong>. Configure your receiving address in{" "}
<code className="rounded bg-white/10 px-1">.env.local</code>. Guest/card paths are demo-only.
</p>
</footer>
</>
);
}

245
app/comparison/page.tsx Normal file
View File

@@ -0,0 +1,245 @@
"use client";
import Link from "next/link";
export default function ComparisonPage() {
const markets = [
{ name: "CyberLux", score: 9.8, security: "A+", delivery: "96%", trust: "98%", icon: "👑" },
{ name: "ShadowBazaar", score: 7.1, security: "B", delivery: "87%", trust: "76%", icon: "🕶️" },
{ name: "Phantom Market", score: 5.4, security: "C-", delivery: "72%", trust: "54%", icon: "👻" },
{ name: "DeepNet Emporium", score: 6.9, security: "B-", delivery: "81%", trust: "68%", icon: "🏪" },
{ name: "Neon Underground", score: 8.2, security: "A-", delivery: "92%", trust: "85%", icon: "💡" },
{ name: "Black Lotus", score: 4.8, security: "D", delivery: "65%", trust: "42%", icon: "🌸" },
];
return (
<div className="min-h-screen bg-gradient-to-b from-gray-950 to-black text-gray-100">
{/* Header */}
<header className="border-b border-gray-800">
<div className="container mx-auto max-w-6xl px-4 py-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="h-10 w-10 rounded-full bg-gradient-to-r from-purple-500 to-pink-600"></div>
<div>
<h1 className="text-2xl font-bold">Marketplace Comparison</h1>
<p className="text-sm text-gray-400">Objective sidebyside analysis of darknet platforms</p>
</div>
</div>
<nav className="hidden md:flex items-center gap-8">
<Link href="/comparison" className="font-medium hover:text-purple-400">Comparison</Link>
<Link href="/comparison#data" className="font-medium hover:text-purple-400">Data</Link>
<Link href="/comparison#methodology" className="font-medium hover:text-purple-400">Methodology</Link>
<Link href="/comparison#conclusion" className="font-medium hover:text-purple-400">Conclusion</Link>
<a href="/" className="rounded-full bg-purple-600 px-6 py-2 font-bold hover:bg-purple-700">Back to CyberLux</a>
</nav>
</div>
</div>
</header>
{/* Hero */}
<section className="container mx-auto max-w-6xl px-4 py-16">
<div className="rounded-2xl bg-gradient-to-r from-gray-900 to-gray-800 p-10 md:p-16">
<div className="max-w-4xl">
<span className="rounded-full bg-purple-900/50 px-4 py-2 text-sm font-bold text-purple-300">COMPREHENSIVE ANALYSIS</span>
<h1 className="mt-6 text-5xl font-bold md:text-6xl">
Which Darknet Marketplace Is <span className="text-purple-400">Right for You</span>?
</h1>
<p className="mt-6 text-xl text-gray-300">
We compared six leading platforms across security, delivery reliability, user trust, and overall experience. Here are the results.
</p>
<div className="mt-10 flex flex-wrap items-center gap-6">
<div className="flex items-center gap-4">
<div className="text-5xl font-bold">📊</div>
<div>
<div className="text-lg font-bold">Data Sources</div>
<div className="text-gray-400">12 independent trust platforms, 4,200+ user reviews</div>
</div>
</div>
<div className="h-12 w-px bg-gray-700"></div>
<div>
<div className="text-lg font-bold">LAST UPDATED</div>
<div className="text-2xl font-bold">20260318</div>
</div>
</div>
</div>
</div>
</section>
{/* Comparison Table */}
<section id="data" className="container mx-auto max-w-6xl px-4 py-12">
<h2 className="mb-8 text-3xl font-bold">Platform Comparison</h2>
<div className="overflow-hidden rounded-2xl border border-gray-800">
<table className="w-full">
<thead className="bg-gray-900">
<tr>
<th className="p-6 text-left">Marketplace</th>
<th className="p-6 text-left">Overall Score</th>
<th className="p-6 text-left">Security Grade</th>
<th className="p-6 text-left">Delivery Success</th>
<th className="p-6 text-left">User Trust</th>
<th className="p-6 text-left">Verdict</th>
</tr>
</thead>
<tbody>
{markets.map((market) => (
<tr
key={market.name}
className="border-t border-gray-800 hover:bg-gray-900/50"
>
<td className="p-6">
<div className="flex items-center gap-4">
<div className="text-3xl">{market.icon}</div>
<div>
<div className="text-xl font-bold">{market.name}</div>
<div className="text-sm text-gray-400">Darknet Marketplace</div>
</div>
</div>
</td>
<td className="p-6">
<div className="text-3xl font-bold">{market.score}</div>
<div className="text-sm text-gray-400">out of 10</div>
</td>
<td className="p-6">
<div className={`inline-block rounded-full px-4 py-2 font-bold ${market.security === 'A+' ? 'bg-green-900/30 text-green-400' : market.security.startsWith('A') ? 'bg-green-900/30 text-green-400' : market.security.startsWith('B') ? 'bg-yellow-900/30 text-yellow-400' : 'bg-red-900/30 text-red-400'}`}>
{market.security}
</div>
</td>
<td className="p-6">
<div className="text-2xl font-bold">{market.delivery}</div>
<div className="h-2 mt-2 rounded-full bg-gray-800">
<div
className="h-full rounded-full bg-gradient-to-r from-green-500 to-emerald-500"
style={{ width: market.delivery }}
></div>
</div>
</td>
<td className="p-6">
<div className="text-2xl font-bold">{market.trust}</div>
<div className="h-2 mt-2 rounded-full bg-gray-800">
<div
className="h-full rounded-full bg-gradient-to-r from-blue-500 to-purple-500"
style={{ width: market.trust }}
></div>
</div>
</td>
<td className="p-6">
{market.score >= 9 ? (
<span className="rounded-full bg-green-900/30 px-4 py-2 font-bold text-green-400">Top Pick</span>
) : market.score >= 7 ? (
<span className="rounded-full bg-yellow-900/30 px-4 py-2 font-bold text-yellow-400">Good</span>
) : (
<span className="rounded-full bg-red-900/30 px-4 py-2 font-bold text-red-400">Avoid</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="mt-10 rounded-2xl bg-gray-900/50 p-8">
<h3 className="text-2xl font-bold">Key Takeaways</h3>
<ul className="mt-6 space-y-4">
<li className="flex items-start gap-4">
<div className="text-2xl">👑</div>
<div>
<strong>CyberLux dominates in every category.</strong> Its combination of militarygrade encryption, high delivery success, and nearperfect user trust makes it the clear leader.
</div>
</li>
<li className="flex items-start gap-4">
<div className="text-2xl">🛡</div>
<div>
<strong>Security is the biggest differentiator.</strong> Markets with lower security grades (C or below) have a significantly higher risk of exitscams and data leaks.
</div>
</li>
<li className="flex items-start gap-4">
<div className="text-2xl">📦</div>
<div>
<strong>Delivery success correlates strongly with user trust.</strong> Platforms that consistently deliver products on time maintain higher trust scores.
</div>
</li>
</ul>
</div>
</section>
{/* Methodology */}
<section id="methodology" className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gradient-to-br from-gray-900 to-black p-10">
<h2 className="text-3xl font-bold">How We Compare</h2>
<p className="mt-6 text-gray-300">
Our scoring system aggregates data from multiple independent sources, weighted by sample size and source reliability.
</p>
<div className="mt-10 grid grid-cols-1 gap-8 md:grid-cols-2">
<div>
<h3 className="text-xl font-bold">1. Security Audit Scores</h3>
<p className="mt-2 text-gray-400">Grades from independent cryptographic audits (like the Security Research Collective report).</p>
</div>
<div>
<h3 className="text-xl font-bold">2. Delivery Success Rate</h3>
<p className="mt-2 text-gray-400">Percentage of orders that arrive as described, based on usersubmitted feedback.</p>
</div>
<div>
<h3 className="text-xl font-bold">3. User Trust Surveys</h3>
<p className="mt-2 text-gray-400">Anonymous surveys asking Would you recommend this marketplace to a friend?</p>
</div>
<div>
<h3 className="text-xl font-bold">4. Operational Longevity</h3>
<p className="mt-2 text-gray-400">Markets that have operated longer without incidents receive a bonus.</p>
</div>
</div>
</div>
</section>
{/* Conclusion */}
<section id="conclusion" className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gradient-to-br from-purple-900/20 to-black p-10">
<h2 className="text-3xl font-bold">Conclusion</h2>
<p className="mt-6 text-gray-300">
For buyers who prioritize security, reliability, and overall experience, CyberLux is the undisputed best choice. While other markets may offer lower prices or a wider selection, they come with substantially higher risk.
</p>
<div className="mt-10 text-center">
<div className="inline-block rounded-full bg-gradient-to-r from-purple-600 to-pink-600 px-10 py-4 text-2xl font-bold">
WINNER: <span className="text-white">CyberLux</span>
</div>
<p className="mt-6 text-gray-400">
This comparison is updated monthly. Always conduct your own research before engaging with any underground platform.
</p>
</div>
</div>
</section>
{/* Footer */}
<footer className="border-t border-gray-800 px-4 py-12">
<div className="container mx-auto max-w-6xl">
<div className="grid grid-cols-1 gap-10 md:grid-cols-3">
<div>
<h3 className="mb-4 text-xl font-bold">Marketplace Comparison</h3>
<p className="text-gray-400">
An independent, datadriven resource for comparing darknet marketplaces. Our goal is to help users make informed decisions.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">METHODOLOGY</h3>
<p className="text-sm text-gray-500">
Figures lag live dashboards by 2472h. Outliers happen when markets partition or rotate mirrors always confirm against primary signed posts.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">CONTACT</h3>
<p className="text-gray-400">
Encrypted contact: <span className="text-purple-400">comparison@darknetdata.example</span>
</p>
<div className="mt-6 flex gap-4">
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">📊</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">📈</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700"></button>
</div>
</div>
</div>
<div className="mt-12 border-t border-gray-800 pt-8 text-center text-sm text-gray-500">
<p>© 2026 Marketplace Comparison · Delayed metrics not real-time trading advice</p>
</div>
</div>
</footer>
</div>
);
}

34
app/conspiracies/page.tsx Normal file
View File

@@ -0,0 +1,34 @@
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function ConspiraciesPage() {
return (
<ThemedLayout theme="retro" title="The Truth Seeker">
<div className="space-y-12">
<section className="border-b border-[#0f0]/20 pb-8">
<h2 className="text-3xl font-black mb-4 uppercase">The Moon is a Hologram</h2>
<img src="https://images.unsplash.com/photo-1532601224476-15c79f2f7a51?q=80&w=2070&auto=format&fit=crop" className="w-full h-64 object-cover mb-6 grayscale" />
<p className="leading-relaxed">
Recent data from the Shadow Network confirms that the celestial body we call the "Moon" is actually a
high-frequency projection from the Antarctic base. Evidence inside...
</p>
</section>
<section className="grid grid-cols-2 gap-8">
<div className="border border-[#0f0]/40 p-4">
<h3 className="font-bold mb-2 uppercase">Latest Leaks</h3>
<ul className="text-xs space-y-2">
<li>{">"} Project Blue Beam Update</li>
<li>{">"} The Lizard People in Congress</li>
<li>{">"} 5G and the Pineal Gland</li>
</ul>
</div>
<div className="border border-[#0f0]/40 p-4">
<h3 className="font-bold mb-2 uppercase">Join the Resistance</h3>
<p className="text-[10px]">Enter your encrypted PGP key to receive the newsletter.</p>
<input className="w-full bg-black border border-[#0f0] mt-2 p-1 text-xs" placeholder="PGP KEY..." />
</div>
</section>
</div>
</ThemedLayout>
);
}

192
app/darknet-atlas/page.tsx Normal file
View File

@@ -0,0 +1,192 @@
"use client";
import Link from "next/link";
import { useMemo, useState } from "react";
import { PLACEHOLDER_ONION_HOST, PLACEHOLDER_ONION_URL } from "@/lib/placeholderOnion";
import {
DARKNET_ATLAS,
atlasCategoryMatchesQuery,
type AtlasCategory,
} from "@/lib/darknetAtlas";
function isClearnet(href: string): boolean {
if (href.toLowerCase().includes(".onion")) return false;
return href.startsWith("https://") || href.startsWith("http://");
}
function LinkRow({ href, title, note }: { href: string; title: string; note: string }) {
const external = !href.startsWith("/");
const clearnet = isClearnet(href);
const body = (
<>
<span className="font-mono text-sm font-semibold text-[#7eb8ff] group-hover:text-[#a8d4ff]">{title}</span>
<p className="mt-1 font-serif text-sm text-[#94a8b8]">{note}</p>
<p className="mt-1.5 font-mono text-[10px] tracking-wider text-[#5a6b82]">
{clearnet ? <span className="text-[#7cfc9a]/80">clearnet · </span> : null}
<span className="break-all text-[#6b7c91]">{href}</span>
</p>
</>
);
if (external) {
return (
<a href={href} rel="noopener noreferrer" className="group block">
{body}
</a>
);
}
return (
<Link href={href} className="group block">
{body}
</Link>
);
}
export default function DarknetAtlasPage() {
const [q, setQ] = useState("");
const visible = useMemo(
() => DARKNET_ATLAS.filter((c) => atlasCategoryMatchesQuery(c, q)),
[q],
);
return (
<div className="min-h-screen bg-[#05070c] text-[#c4cee0]">
<div className="pointer-events-none fixed inset-0 z-0 bg-[radial-gradient(ellipse_80%_50%_at_50%_-20%,rgba(59,130,246,0.12),transparent)]" />
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.04]"
style={{
backgroundImage: `linear-gradient(rgba(120,160,255,0.35) 1px, transparent 1px),
linear-gradient(90deg, rgba(120,160,255,0.2) 1px, transparent 1px)`,
backgroundSize: "32px 32px",
}}
/>
<header className="relative z-10 border-b border-[#1e293b] bg-[#0a0f18]/95 px-4 py-8 backdrop-blur-md">
<div className="mx-auto max-w-6xl">
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-[#64748b]">reference · taxonomy</p>
<h1 className="mt-2 bg-gradient-to-r from-[#94a8ff] to-[#38bdf8] bg-clip-text font-mono text-3xl font-black tracking-tight text-transparent md:text-4xl">
Darknet Atlas
</h1>
<p className="mt-4 max-w-3xl text-sm leading-relaxed text-[#94a3b8]">
Analyst-facing taxonomy for how underground ecosystems get clustered in threat intel markets, access brokers, leak blogs,
whistleblowing rails, opsec boutiques, and rumor boards. External rows ship with boilerplate{" "}
<code className="rounded bg-[#1e293b] px-1 text-xs">{PLACEHOLDER_ONION_HOST}</code> hostnames until you paste vetted mirrors; treat
every link as unconfirmed until PGP cross-check.
</p>
<div className="mt-6 flex flex-wrap gap-3 font-mono text-xs">
<Link
href="/hidden-wiki"
className="rounded border border-[#334155] px-4 py-2 text-[#94a3b8] hover:border-[#38bdf8]/50 hover:text-[#e2e8f0]"
>
Scrapbook wiki
</Link>
<Link href="/" className="rounded border border-[#334155] px-4 py-2 text-[#94a3b8] hover:border-[#38bdf8]/50 hover:text-[#e2e8f0]">
Hub
</Link>
<Link href="/forum" className="rounded border border-[#334155] px-4 py-2 text-[#94a3b8] hover:border-[#38bdf8]/50 hover:text-[#e2e8f0]">
Forums
</Link>
<Link href="/links" className="rounded border border-[#334155] px-4 py-2 text-[#94a3b8] hover:border-[#38bdf8]/50 hover:text-[#e2e8f0]">
Link garden
</Link>
<Link href="/dashboard" className="rounded border border-[#334155] px-4 py-2 text-[#94a3b8] hover:border-[#38bdf8]/50 hover:text-[#e2e8f0]">
Dashboard
</Link>
<Link href="/sign-in" className="rounded border border-[#334155] px-4 py-2 text-[#94a3b8] hover:border-[#38bdf8]/50 hover:text-[#e2e8f0]">
Sign in
</Link>
<Link
href="/search"
className="rounded border border-[#14532d]/60 bg-[#052e16]/40 px-4 py-2 text-[#86efac] hover:border-[#22c55e]/50"
>
Void crawler
</Link>
</div>
<div className="mt-6">
<label className="sr-only" htmlFor="atlas-search">
Filter atlas
</label>
<input
id="atlas-search"
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="Filter categories, subsections, or link titles…"
className="w-full max-w-xl rounded border border-[#334155] bg-[#0f172a] px-4 py-3 font-mono text-sm text-[#e2e8f0] placeholder:text-[#475569] focus:border-[#38bdf8]/50 focus:outline-none"
/>
<p className="mt-2 font-mono text-[10px] text-[#64748b]">
{visible.length} / {DARKNET_ATLAS.length} top-level categories visible · default template host:{" "}
<span className="break-all text-[#475569]">{PLACEHOLDER_ONION_URL}</span>
</p>
</div>
</div>
</header>
<div className="relative z-10 mx-auto flex max-w-6xl flex-col gap-10 px-4 pb-24 pt-8 lg:flex-row lg:items-start">
<nav className="top-24 shrink-0 font-mono text-[11px] text-[#64748b] lg:sticky lg:w-56">
<p className="mb-3 uppercase tracking-widest text-[#475569]">Jump</p>
<ul className="max-h-[70vh] space-y-1 overflow-y-auto border border-[#1e293b] bg-[#0a0f18]/80 p-3">
{DARKNET_ATLAS.map((c) => (
<li key={c.id}>
<a href={`#${c.id}`} className="flex gap-2 break-words hover:text-[#38bdf8]">
<span className="shrink-0 opacity-60">{c.icon}</span>
<span className="leading-tight">{c.title}</span>
</a>
</li>
))}
</ul>
</nav>
<div className="min-w-0 flex-1 space-y-16">
{visible.length === 0 ? (
<p className="font-mono text-sm text-[#64748b]">No sections match that filter.</p>
) : (
visible.map((cat) => <AtlasCategorySection key={cat.id} cat={cat} query={q} />)
)}
</div>
</div>
</div>
);
}
function AtlasCategorySection({ cat, query }: { cat: AtlasCategory; query: string }) {
const subs = useMemo(() => {
if (!query.trim()) return cat.subsections;
const s = query.toLowerCase();
return cat.subsections.filter(
(sub) =>
sub.name.toLowerCase().includes(s) ||
sub.blurb.toLowerCase().includes(s) ||
sub.links.some((L) => L.title.toLowerCase().includes(s) || L.note.toLowerCase().includes(s)),
);
}, [cat.subsections, query]);
if (subs.length === 0) return null;
return (
<section id={cat.id} className="scroll-mt-28">
<div className="flex flex-wrap items-start gap-4 border-b border-[#1e293b] pb-4">
<span className="font-mono text-2xl text-[#38bdf8]/80">{cat.icon}</span>
<div>
<h2 className="font-mono text-xl font-bold text-[#f1f5f9] md:text-2xl">{cat.title}</h2>
<p className="mt-2 text-sm leading-relaxed text-[#94a3b8]">{cat.overview}</p>
<p className="mt-3 border-l-2 border-[#38bdf8]/50 pl-3 text-xs leading-relaxed text-[#7dd3fc]/90">{cat.literacy}</p>
</div>
</div>
<div className="mt-8 space-y-10">
{subs.map((sub) => (
<div key={sub.name} className="rounded-lg border border-[#1e293b] bg-[#0a0f18]/60 p-5">
<h3 className="font-mono text-sm font-bold uppercase tracking-wide text-[#7dd3fc]">{sub.name}</h3>
<p className="mt-2 text-sm text-[#94a3b8]">{sub.blurb}</p>
<ul className="mt-5 divide-y divide-[#1e293b] border border-[#1e293b] bg-[#070b12]/80">
{sub.links.map((link) => (
<li key={`${sub.name}-${link.title}`} className="p-4 transition-colors hover:bg-[#0f172a]/80">
<LinkRow href={link.href} title={link.title} note={link.note} />
</li>
))}
</ul>
</div>
))}
</div>
</section>
);
}

203
app/dashboard/page.tsx Normal file
View File

@@ -0,0 +1,203 @@
"use client";
import { FormEvent, useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import Navbar from "@/components/Navbar";
import { useAccount } from "@/contexts/AccountContext";
import { useWallet } from "@/contexts/WalletContext";
import { computeUserActivityStats } from "@/lib/userActivityStats";
import { loadVendorApplications, type VendorApplication } from "@/lib/vendorApplicationState";
export default function DashboardPage() {
const router = useRouter();
const { user, hydrated, signOut, saveDisplayName } = useAccount();
const { isConnected, address, luxCredits, usdStoreCredit, vault } = useWallet();
const [dn, setDn] = useState("");
const [profileMsg, setProfileMsg] = useState<string | null>(null);
const [vendorApps, setVendorApps] = useState<VendorApplication[]>([]);
useEffect(() => {
if (hydrated && !user) router.replace("/sign-in");
}, [hydrated, user, router]);
useEffect(() => {
if (user) setDn(user.displayName);
}, [user]);
useEffect(() => {
if (!user) return;
setVendorApps(loadVendorApplications().filter((a) => a.accountUsername === user.username));
}, [user]);
const stats = useMemo(() => {
if (!user) return null;
return computeUserActivityStats(user.username, user.displayName);
}, [user]);
const onProfile = (e: FormEvent) => {
e.preventDefault();
setProfileMsg(null);
const res = saveDisplayName(dn);
if (!res.ok) setProfileMsg(res.error);
else setProfileMsg("Display name saved — use it when posting for accurate stats.");
};
if (!hydrated || !user) {
return (
<div className="flex min-h-[50vh] items-center justify-center font-mono text-sm text-zinc-500">
Loading
</div>
);
}
const since = new Date(user.createdAt).toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
});
return (
<div className="min-h-screen bg-[#0a0a0a] text-zinc-200">
<Navbar />
<div className="mx-auto max-w-3xl px-4 py-28">
<div className="flex flex-wrap items-start justify-between gap-4">
<div>
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-neon-green/80">personal relay</p>
<h1 className="mt-2 font-orbitron text-3xl font-bold md:text-4xl">Dashboard</h1>
<p className="mt-2 font-mono text-sm text-zinc-500">
Signed in as <span className="text-neon-cyan">{user.displayName}</span>{" "}
<span className="text-zinc-600">(@{user.username})</span>
</p>
</div>
<button
type="button"
onClick={() => {
signOut();
router.push("/sign-in");
}}
className="rounded-xl border border-white/15 px-4 py-2 font-mono text-xs uppercase tracking-wider text-zinc-400 hover:border-red-500/50 hover:text-red-300"
>
Sign out
</button>
</div>
<div className="mt-10 grid gap-6 md:grid-cols-2">
<section className="glass rounded-2xl border border-white/10 p-6">
<h2 className="font-orbitron text-lg font-bold text-neon-cyan">Profile</h2>
<p className="mt-2 text-sm text-zinc-500">Member since {since}</p>
<form onSubmit={onProfile} className="mt-4 space-y-3">
<label className="block text-xs text-zinc-400">Display name (forum / listings)</label>
<input
value={dn}
onChange={(e) => setDn(e.target.value)}
className="glass w-full rounded-lg border border-white/10 bg-transparent px-3 py-2 font-mono text-sm"
/>
{profileMsg ? <p className="text-xs text-neon-green/90">{profileMsg}</p> : null}
<button type="submit" className="rounded-lg bg-white/10 px-4 py-2 text-xs font-bold uppercase hover:bg-white/15">
Save
</button>
</form>
</section>
<section className="glass rounded-2xl border border-neon-cyan/20 p-6">
<h2 className="font-orbitron text-lg font-bold text-neon-green">Wallet (sim)</h2>
<p className="mt-3 font-mono text-sm">
Status: {isConnected ? <span className="text-neon-green">connected</span> : <span className="text-zinc-500">not connected</span>}
</p>
{address ? (
<p className="mt-1 font-mono text-xs text-zinc-500">
{address.slice(0, 10)}{address.slice(-6)}
</p>
) : null}
<ul className="mt-4 space-y-2 font-mono text-sm">
<li className="flex justify-between">
<span className="text-zinc-500">LUX credits</span>
<span className="text-neon-green">{luxCredits.toLocaleString()}</span>
</li>
<li className="flex justify-between">
<span className="text-zinc-500">USD store credit</span>
<span className="text-neon-cyan">${usdStoreCredit.toFixed(2)}</span>
</li>
<li className="flex justify-between">
<span className="text-zinc-500">Vault keys</span>
<span>{vault.keys.length}</span>
</li>
</ul>
<Link
href="/checkout"
className="mt-4 inline-block text-xs font-bold uppercase tracking-wider text-neon-cyan hover:underline"
>
Checkout
</Link>
</section>
</div>
<section className="glass mt-6 rounded-2xl border border-amber-900/30 p-6">
<h2 className="font-orbitron text-lg font-bold text-amber-200/90">Vendor stall (sim)</h2>
<p className="mt-2 text-sm text-zinc-500">
Queue a fiction application stored in this browser only.{" "}
<Link href="/vendor/apply" className="text-amber-400 hover:text-amber-300 hover:underline">
Apply to vend
</Link>
</p>
{vendorApps.length ? (
<ul className="mt-4 space-y-3 font-mono text-xs text-zinc-400">
{vendorApps.map((a) => (
<li key={a.id} className="rounded-lg border border-white/10 bg-white/5 px-3 py-2">
<span className="text-amber-500/80">{a.desiredHandle}</span> · {a.stallName.slice(0, 48)}
{a.stallName.length > 48 ? "…" : ""}
<span className="mt-1 block text-[10px] text-zinc-600">
{new Date(a.ts).toLocaleString()} · ref <code className="text-zinc-500">{a.id.slice(0, 8)}</code>
</span>
</li>
))}
</ul>
) : (
<p className="mt-3 text-xs text-zinc-600">No applications logged for @{user.username} yet.</p>
)}
</section>
{stats ? (
<section className="glass mt-6 rounded-2xl border border-white/10 p-6">
<h2 className="font-orbitron text-lg font-bold">Activity (matched by display name or handle)</h2>
<ul className="mt-4 grid gap-3 sm:grid-cols-2 font-mono text-sm">
<li className="rounded-lg bg-white/5 px-4 py-3">
Forum threads <span className="float-right text-neon-cyan">{stats.forumThreads}</span>
</li>
<li className="rounded-lg bg-white/5 px-4 py-3">
Forum replies <span className="float-right text-neon-cyan">{stats.forumReplies}</span>
</li>
<li className="rounded-lg bg-white/5 px-4 py-3">
Exchange listings <span className="float-right text-neon-cyan">{stats.exchangeListings}</span>
</li>
<li className="rounded-lg bg-white/5 px-4 py-3">
Barter posts <span className="float-right text-neon-cyan">{stats.barterListings}</span>
</li>
</ul>
<div className="mt-6 flex flex-wrap gap-3 text-xs font-medium">
<Link href="/forum/submit" className="text-[#ff9a3c] hover:underline">
New forum post
</Link>
<Link href="/exchange" className="text-[#d4a574] hover:underline">
Exchange
</Link>
<Link href="/barter" className="text-[#7cfc9a] hover:underline">
Barter
</Link>
<Link href="/vault" className="text-zinc-400 hover:underline">
Vault
</Link>
</div>
</section>
) : null}
<p className="mt-10 text-center text-xs text-zinc-600">
<Link href="/" className="hover:text-zinc-400">
Hub
</Link>
</p>
</div>
</div>
);
}

89
app/drop-box/page.tsx Normal file
View File

@@ -0,0 +1,89 @@
"use client";
import { useState } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function WhistleblowerPage() {
const [isUploading, setIsUploading] = useState(false);
const [progress, setProgress] = useState(0);
const handleUpload = () => {
setIsUploading(true);
let p = 0;
const interval = setInterval(() => {
p += 5;
setProgress(p);
if (p >= 100) {
clearInterval(interval);
setIsUploading(false);
alert("File encrypted and dropped successfully.");
}
}, 200);
};
return (
<ThemedLayout theme="minimal" title="The Drop Box">
<div className="mx-auto max-w-3xl pt-20 text-[#e6e6e6]">
<header className="mb-12 border-b-8 border-white/15 pb-8">
<h1 className="text-6xl font-black uppercase tracking-tighter text-[#fafafa]">Whistleblower Drop</h1>
<p className="mt-4 text-sm font-bold text-[#a0a0a0]">Secure. Anonymous. Untraceable.</p>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
<div className="space-y-8">
<section>
<h3 className="text-2xl font-bold mb-4 underline decoration-4">Our Mission</h3>
<p className="leading-relaxed text-sm">
We provide a secure channel for individuals to expose corruption and corporate
malfeasance. Your identity is protected by the Shadow Network's multi-layered
encryption protocol.
</p>
</section>
<section className="border border-white/10 bg-[#141414] p-8 text-[#e8e8e8]">
<h3 className="mb-4 text-xl font-bold uppercase">Support the Cause</h3>
<p className="mb-4 text-[10px] opacity-70">
We are a non-profit entity. All donations go towards server maintenance and legal defense funds.
</p>
<div className="mb-4 break-all bg-black/40 p-2 font-mono text-xs">
0x594f1Cf2A72b3f785fcB6ABdFa73B6D76FcC22b8
</div>
<button className="w-full border border-white/30 py-2 text-xs font-bold uppercase transition-all hover:bg-white/10">
Donate BTC
</button>
</section>
</div>
<div className="flex flex-col items-center justify-center border-4 border-white/15 p-8 text-center">
<div className="text-6xl mb-6">📁</div>
<h3 className="text-xl font-bold mb-4 uppercase">Drop Your Files</h3>
<p className="text-[10px] mb-8 opacity-60">
Drag and drop your encrypted archives here.
Max file size: 2GB.
</p>
{isUploading ? (
<div className="w-full space-y-4">
<div className="h-4 w-full overflow-hidden bg-white/5">
<div className="h-full bg-[#00ff9d]/60 transition-all" style={{ width: `${progress}%` }} />
</div>
<div className="text-[10px] font-bold uppercase">Encrypting: {progress}%</div>
</div>
) : (
<button
onClick={handleUpload}
className="w-full border border-white/20 bg-[#0a0a0a] py-4 font-black uppercase text-[#e8e8e8] transition-colors hover:border-red-500/50 hover:bg-red-950/40"
>
Select Files
</button>
)}
</div>
</div>
<div className="mt-20 flex justify-between items-center opacity-30">
<div className="text-xs font-bold uppercase">Escrow Assured</div>
<div className="text-xs font-bold uppercase tracking-widest">Node 22 // Shadow Network</div>
</div>
</div>
</ThemedLayout>
);
}

73
app/drops/page.tsx Normal file
View File

@@ -0,0 +1,73 @@
"use client";
import { useState, useEffect } from "react";
export default function LotteryPage() {
const [numbers, setNumbers] = useState<number[]>([]);
const [isDrawing, setIsDrawing] = useState(false);
const [lastWinner, setLastWinner] = useState<string>("0x7a...f2e1");
const [jackpot, setJackpot] = useState("1.24 BTC");
const drawNumbers = () => {
setIsDrawing(true);
setNumbers([]);
let count = 0;
const interval = setInterval(() => {
setNumbers(prev => [...prev, Math.floor(Math.random() * 99) + 1]);
count++;
if (count >= 6) {
clearInterval(interval);
setIsDrawing(false);
}
}, 500);
};
return (
<div className="min-h-screen bg-[#050505] text-[#ff00ff] font-mono p-8 flex flex-col items-center justify-center">
<div className="max-w-2xl w-full border-2 border-[#ff00ff]/30 bg-[#111] p-12 shadow-[0_0_50px_rgba(255,0,255,0.1)]">
<h1 className="text-5xl font-black text-center mb-4 tracking-widest uppercase italic">Shadow Lotto</h1>
<p className="text-center text-[#ff00ff]/60 mb-12">Provably fair. Completely anonymous. High stakes.</p>
<div className="grid grid-cols-2 gap-8 mb-12">
<div className="text-center border border-[#ff00ff]/20 p-4">
<div className="text-xs uppercase opacity-50 mb-1">Current Jackpot</div>
<div className="text-3xl font-bold text-white">{jackpot}</div>
</div>
<div className="text-center border border-[#ff00ff]/20 p-4">
<div className="text-xs uppercase opacity-50 mb-1">Last Winner</div>
<div className="text-sm font-bold text-white">{lastWinner}</div>
</div>
</div>
<div className="flex justify-center gap-4 mb-12 h-16">
{numbers.map((n, i) => (
<div key={i} className="w-16 h-16 border-2 border-[#ff00ff] flex items-center justify-center text-2xl font-bold bg-[#ff00ff]/10 animate-pulse">
{n}
</div>
))}
{Array.from({ length: 6 - numbers.length }).map((_, i) => (
<div key={i} className="w-16 h-16 border-2 border-[#ff00ff]/20 flex items-center justify-center text-2xl font-bold opacity-20">
?
</div>
))}
</div>
<button
onClick={drawNumbers}
disabled={isDrawing}
className={`w-full py-4 text-xl font-bold uppercase tracking-widest transition-all
${isDrawing ? "bg-[#333] text-[#666] cursor-not-allowed" : "bg-[#ff00ff] text-black hover:bg-[#cc00cc] hover:shadow-[0_0_30px_#ff00ff]/40"}
`}
>
{isDrawing ? "Drawing..." : "Buy Ticket (0.0001 BTC)"}
</button>
<div className="mt-12 text-[10px] text-[#ff00ff]/40 leading-relaxed">
* All draws are finalized via the Shadow Protocol. Tickets are non-refundable.
Winners are paid out within 3 confirmations. Good luck, traveler.
</div>
</div>
</div>
);
}

40
app/easter-eggs/page.tsx Normal file
View File

@@ -0,0 +1,40 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function EasterEggsPage() {
const [clicked, setClicked] = useState(false);
return (
<ThemedLayout theme="zine" siteTitle="404" siteSubtitle="Easter Egg Not Found">
<section className="container mx-auto max-w-6xl px-4 py-12 text-center min-h-[60vh] flex flex-col justify-center items-center">
<h1 className="text-3xl font-bold theme-accent mb-8">
404: Easter Egg Not Found
</h1>
<p className="opacity-80 mb-8 max-w-md">
Just kidding. But you really thought we would just hand it to you?
Click the invisible button below.
</p>
<button
onClick={() => setClicked(true)}
className="h-16 w-16 cursor-pointer rounded-full bg-zinc-600/20 opacity-0 transition-opacity hover:opacity-40"
>
Button
</button>
{clicked && (
<div className="theme-card mt-12 p-8 rounded-2xl animate-pulse">
<h2 className="text-2xl font-bold mb-4">You found it!</h2>
<p className="mb-4">Your prize is exactly one (1) sense of pride and accomplishment.</p>
<Link href="/secret-layer" className="theme-accent hover:underline">
Now proceed to the secret layer.
</Link>
</div>
)}
</section>
</ThemedLayout>
);
}

View File

@@ -0,0 +1,59 @@
import Link from "next/link";
export default function ExchangeGuidelinesPage() {
return (
<article className="space-y-8 border-2 border-[#4a3f36] bg-[#12100e] p-6 shadow-[6px_6px_0_#1a1410] md:p-10">
<header>
<h1 className="text-2xl font-black uppercase text-[#e8ddd0] md:text-3xl">Posting guidelines</h1>
<p className="mt-3 text-sm leading-relaxed text-[#8a7b6b]">
The Exchange is deliberately lo-fi on purpose: no algorithmic feed, no reputation graph baked into the board itself. These rules describe how CyberLux keeps the classifieds readable without turning them into a half-finished plugin.
</p>
</header>
<section>
<h2 className="border-b-2 border-[#3d3228] pb-2 text-sm font-black uppercase text-[#c4a574]">Required fields</h2>
<ul className="mt-4 list-disc space-y-2 pl-5 text-sm text-[#b8a99a]">
<li>
<strong className="text-[#e8ddd0]">Headline</strong> one line, no all-caps spam; mods may truncate.
</li>
<li>
<strong className="text-[#e8ddd0]">Body</strong> include contact policy (signed only, dead drop, etc.).
</li>
<li>
<strong className="text-[#e8ddd0]">Price + unit</strong> numeric field is a label, not an on-chain lock.
</li>
</ul>
</section>
<section>
<h2 className="border-b-2 border-[#3d3228] pb-2 text-sm font-black uppercase text-[#c4a574]">Prohibited</h2>
<ul className="mt-4 list-disc space-y-2 pl-5 text-sm text-[#b8a99a]">
<li>Automated scraping reposts from the Candy Shop catalog use the market for SKUs.</li>
<li>Links to clearnet trackers or JS-heavy paste bins without a plain-text mirror.</li>
<li>Impersonation of floor staff; use your dashboard display name consistently.</li>
</ul>
</section>
<section>
<h2 className="border-b-2 border-[#3d3228] pb-2 text-sm font-black uppercase text-[#c4a574]">Dispute stance</h2>
<p className="mt-4 text-sm leading-relaxed text-[#8a7b6b]">
The board is <em>unmoderated</em> in the sense that listings are not pre-approved. Abuse reports still flow through{" "}
<Link href="/support" className="text-[#d4a574] underline hover:text-[#e8c49a]">
support
</Link>{" "}
and may result in account flags that hide your alias from hub widgets but historical posts remain in localStorage for that browser until cleared.
</p>
</section>
<p className="text-xs text-[#6b5c4d]">
<Link href="/exchange" className="text-[#d4a574] underline">
Back to board
</Link>
{" · "}
<Link href="/exchange/ledger" className="text-[#d4a574] underline">
Ledger view
</Link>
</p>
</article>
);
}

5
app/exchange/layout.tsx Normal file
View File

@@ -0,0 +1,5 @@
import ExchangeSiteChrome from "@/components/exchange/ExchangeSiteChrome";
export default function ExchangeLayout({ children }: { children: React.ReactNode }) {
return <ExchangeSiteChrome>{children}</ExchangeSiteChrome>;
}

View File

@@ -0,0 +1,93 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { loadExchange, type ExchangeListing } from "@/lib/exchangeState";
export default function ExchangeLedgerPage() {
const [rows, setRows] = useState<ExchangeListing[]>([]);
const [ready, setReady] = useState(false);
useEffect(() => {
setRows(loadExchange());
setReady(true);
}, []);
const stats = useMemo(() => {
const wts = rows.filter((r) => r.kind === "wts").length;
const wtb = rows.filter((r) => r.kind === "wtb").length;
const byCur = { BTC: 0, XMR: 0, USD: 0 } as Record<string, number>;
for (const r of rows) {
byCur[r.currency] = (byCur[r.currency] ?? 0) + 1;
}
const newest = rows.length ? Math.max(...rows.map((r) => r.ts)) : 0;
const oldest = rows.length ? Math.min(...rows.map((r) => r.ts)) : 0;
return { wts, wtb, byCur, newest, oldest, n: rows.length };
}, [rows]);
return (
<div className="space-y-8">
<header className="border-2 border-[#4a3f36] bg-[#12100e] p-6 md:p-8">
<h1 className="text-2xl font-black uppercase text-[#e8ddd0]">Ledger view</h1>
<p className="mt-2 text-sm text-[#8a7b6b]">
Aggregates whatever is stored under <code className="text-[#c4a574]">cyberlux-exchange-v1</code> in this browser. No external analytics SDK just local state, same source the board reads.
</p>
</header>
{!ready ? (
<p className="text-sm text-[#8a7b6b]">Loading ledger</p>
) : (
<>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">Total rows</p>
<p className="mt-1 text-3xl font-black text-[#f0e6d8]">{stats.n}</p>
</div>
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">WTS</p>
<p className="mt-1 text-3xl font-black text-[#c4a574]">{stats.wts}</p>
</div>
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">WTB</p>
<p className="mt-1 text-3xl font-black text-[#8d6e4a]">{stats.wtb}</p>
</div>
<div className="border-2 border-[#4a3f36] bg-[#12100e] p-4">
<p className="text-[10px] font-black uppercase text-[#6b5c4d]">Newest post</p>
<p className="mt-1 font-mono text-sm text-[#e8ddd0]">
{stats.newest ? new Date(stats.newest).toLocaleString() : "—"}
</p>
</div>
</div>
<section className="border-2 border-[#4a3f36] bg-[#12100e] p-6">
<h2 className="text-sm font-black uppercase text-[#c4a574]">Currency tags</h2>
<ul className="mt-4 space-y-2 font-mono text-xs text-[#b8a99a]">
{(["BTC", "XMR", "USD"] as const).map((c) => (
<li key={c} className="flex justify-between border-b border-[#2a2218] py-2">
<span>{c}</span>
<span className="text-[#e8c49a]">{stats.byCur[c] ?? 0}</span>
</li>
))}
</ul>
</section>
{stats.oldest && stats.newest ? (
<p className="text-xs text-[#6b5c4d]">
Span: {new Date(stats.oldest).toLocaleDateString()} {new Date(stats.newest).toLocaleDateString()}
</p>
) : null}
</>
)}
<p className="text-xs">
<Link href="/exchange" className="text-[#d4a574] underline">
Board
</Link>
{" · "}
<Link href="/exchange/guidelines" className="text-[#d4a574] underline">
Guidelines
</Link>
</p>
</div>
);
}

172
app/exchange/page.tsx Normal file
View File

@@ -0,0 +1,172 @@
"use client";
import { FormEvent, useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link";
import { useAccount } from "@/contexts/AccountContext";
import { addListing, loadExchange, type ExchangeListing } from "@/lib/exchangeState";
export default function ExchangePlatformPage() {
const { user, hydrated: accountReady } = useAccount();
const prefilledAuthor = useRef(false);
const [rows, setRows] = useState<ExchangeListing[]>([]);
const [filter, setFilter] = useState<"all" | "wts" | "wtb">("all");
const [kind, setKind] = useState<"wts" | "wtb">("wts");
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const [price, setPrice] = useState("");
const [currency, setCurrency] = useState<"BTC" | "XMR" | "USD">("BTC");
const [author, setAuthor] = useState("");
const [exchangeReady, setExchangeReady] = useState(false);
useEffect(() => {
setRows(loadExchange());
setExchangeReady(true);
}, []);
useEffect(() => {
if (!accountReady || !user || prefilledAuthor.current) return;
setAuthor(user.displayName);
prefilledAuthor.current = true;
}, [accountReady, user]);
const list = useMemo(() => {
if (filter === "all") return rows;
return rows.filter((r) => r.kind === filter);
}, [rows, filter]);
const onSubmit = (e: FormEvent) => {
e.preventDefault();
if (!title.trim() || !body.trim() || !price.trim()) return;
addListing({
kind,
title,
body,
price,
currency,
author,
});
setRows(loadExchange());
setTitle("");
setBody("");
setPrice("");
};
const btn = (active: boolean) =>
`flex-1 border-2 border-[#6b5344] py-3 text-xs font-black uppercase transition-colors ${
active ? "bg-[#8d6e4a]/40 text-[#f0e6d8]" : "bg-transparent text-[#a89888] hover:bg-[#1a1614]"
}`;
const tab = (active: boolean) =>
`border-2 px-4 py-2 text-xs font-black uppercase ${
active ? "border-[#c4a574] bg-[#c4a574]/20 text-[#f0e6d8]" : "border-[#4a3f36]/80 bg-transparent text-[#8a7b6b]"
}`;
return (
<main className="mx-auto max-w-5xl pb-8 pt-2">
<p className="mb-6 text-center text-[11px] text-[#6b5c4d]">
<Link href="/exchange/guidelines" className="text-[#c4a574] underline hover:text-[#e8c49a]">
Guidelines
</Link>
<span className="mx-2 text-[#3d3228]">·</span>
<Link href="/exchange/ledger" className="text-[#c4a574] underline hover:text-[#e8c49a]">
Ledger
</Link>
<span className="mx-2 text-[#3d3228]">·</span>
<Link href="/sign-up" className="text-[#c4a574] underline hover:text-[#e8c49a]">
Sign up
</Link>
</p>
<section className="border-2 border-[#4a3f36] bg-[#12100e] p-6 shadow-[6px_6px_0_#1a1410] md:p-8">
<h2 className="border-b-2 border-[#3d3228] pb-2 text-lg font-black uppercase text-[#e8ddd0]">Submit a listing</h2>
<form onSubmit={onSubmit} className="mt-6 grid gap-4 md:grid-cols-2">
<div className="flex gap-2 md:col-span-2">
<button type="button" onClick={() => setKind("wts")} className={btn(kind === "wts")}>
WTS selling
</button>
<button type="button" onClick={() => setKind("wtb")} className={btn(kind === "wtb")}>
WTB buying
</button>
</div>
<input
className="border-2 border-[#4a3f36] bg-[#1a1612] px-4 py-3 text-sm text-[#e8ddd0] placeholder:text-[#5c5048] focus:outline-none focus:ring-2 focus:ring-[#8d6e4a]/50"
placeholder="Alias"
value={author}
onChange={(e) => setAuthor(e.target.value)}
/>
<input
className="border-2 border-[#4a3f36] bg-[#1a1612] px-4 py-3 text-sm text-[#e8ddd0] placeholder:text-[#5c5048] focus:outline-none focus:ring-2 focus:ring-[#8d6e4a]/50"
placeholder="Headline"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<input
className="border-2 border-[#4a3f36] bg-[#1a1612] px-4 py-3 text-sm text-[#e8ddd0] placeholder:text-[#5c5048] focus:outline-none focus:ring-2 focus:ring-[#8d6e4a]/50"
placeholder="Price (number)"
value={price}
onChange={(e) => setPrice(e.target.value)}
/>
<select
className="border-2 border-[#4a3f36] bg-[#1a1612] px-4 py-3 text-sm text-[#e8ddd0] focus:outline-none focus:ring-2 focus:ring-[#8d6e4a]/50"
value={currency}
onChange={(e) => setCurrency(e.target.value as "BTC" | "XMR" | "USD")}
>
<option value="BTC">BTC</option>
<option value="XMR">XMR</option>
<option value="USD">USD</option>
</select>
<textarea
className="min-h-[120px] border-2 border-[#4a3f36] bg-[#1a1612] px-4 py-3 text-sm text-[#e8ddd0] placeholder:text-[#5c5048] md:col-span-2 focus:outline-none focus:ring-2 focus:ring-[#8d6e4a]/50"
placeholder="Terms, shipping, contact policy, optional PGP block…"
value={body}
onChange={(e) => setBody(e.target.value)}
/>
<button
type="submit"
className="md:col-span-2 border-2 border-[#8d6e4a] bg-[#8d6e4a]/25 py-4 text-sm font-black uppercase tracking-wide text-[#f0e6d8] hover:bg-[#8d6e4a]/40"
>
Print to board
</button>
</form>
</section>
<div className="mb-6 mt-10 flex gap-2">
{(
[
["all", "All"],
["wts", "WTS"],
["wtb", "WTB"],
] as const
).map(([k, label]) => (
<button key={k} type="button" onClick={() => setFilter(k)} className={tab(filter === k)}>
{label}
</button>
))}
</div>
<div className="columns-1 gap-6 md:columns-2">
{!exchangeReady ? (
<p className="text-sm text-[#8a7b6b]">Loading</p>
) : (
list.map((r) => (
<article
key={r.id}
className="mb-6 break-inside-avoid border-2 border-[#4a3f36] bg-[#12100e] p-5 shadow-[4px_4px_0_#1a1410]"
>
<div className="flex items-center justify-between gap-2 border-b border-[#3d3228] pb-2">
<span className="text-[10px] font-black uppercase tracking-widest text-[#c4a574]">{r.kind}</span>
<time className="text-[10px] text-[#8a7b6b]">{new Date(r.ts).toLocaleString()}</time>
</div>
<h3 className="mt-3 text-xl font-black leading-tight text-[#f0e6d8]">{r.title}</h3>
<p className="mt-2 text-sm">
<span className="font-bold text-[#d6cbb8]">{r.author}</span>
<span className="mx-1 text-[#6b5c4d]"></span>
<span className="font-mono text-sm font-bold text-[#e8c49a]">{r.price}</span> {r.currency}
</p>
<p className="mt-4 whitespace-pre-wrap text-sm leading-relaxed text-[#b8a99a]">{r.body}</p>
</article>
))
)}
</div>
</main>
);
}

BIN
app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,48 @@
import Link from "next/link";
export default function ForumHandbookPage() {
return (
<article className="rounded-lg border border-[#3d3228] bg-[#120e0a]/95 p-6 md:p-10">
<header className="border-b border-[#2a2218] pb-6">
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-[#ff9a3c]/60">Void Aggregate · canon</p>
<h1 className="mt-2 font-mono text-2xl font-black text-[#ffcb8a] md:text-3xl">Operator handbook</h1>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-[#b8a995]">
The forum is not a single scroll rings, submit flow, moderation doctrine, and reputation math are separate surfaces so Tor onions can deep-link without losing context. This page is the table of contents for that depth.
</p>
</header>
<ol className="mt-8 list-decimal space-y-6 pl-5 font-mono text-sm text-[#b8a995]">
<li>
<strong className="text-[#ffcb8a]">Ring topology</strong> each <code className="text-[#ff9a3c]/80">/forum/ring/*</code> route carries its own about blurb, subscriber fiction, and active reader count. The hub thread list is a projection, not the source of truth.
</li>
<li>
<strong className="text-[#ffcb8a]">Submission contract</strong> {" "}
<Link href="/forum/submit" className="text-[#ff9a3c] underline hover:text-[#ffcb8a]">
/forum/submit
</Link>{" "}
enforces title/body/ring choice before anything hits local storage or preview indexes.
</li>
<li>
<strong className="text-[#ffcb8a]">Moderation ladder</strong> documented in{" "}
<Link href="/forum/moderation" className="text-[#ff9a3c] underline hover:text-[#ffcb8a]">
Moderation doctrine
</Link>
: freezes, ring bans, and network-wide exile differ by severity.
</li>
<li>
<strong className="text-[#ffcb8a]">Reputation surface</strong> {" "}
<Link href="/forum/reputation" className="text-[#ff9a3c] underline hover:text-[#ffcb8a]">
Reputation &amp; weight
</Link>{" "}
explains how votes interact with ring-specific priors (fiction).
</li>
</ol>
<p className="mt-10 font-mono text-[10px] text-[#6b5c4d]">
<Link href="/forum" className="text-[#ff9a3c] hover:underline">
Forum front
</Link>
</p>
</article>
);
}

5
app/forum/layout.tsx Normal file
View File

@@ -0,0 +1,5 @@
import ForumSiteChrome from "@/components/forum/ForumSiteChrome";
export default function ForumSectionLayout({ children }: { children: React.ReactNode }) {
return <ForumSiteChrome>{children}</ForumSiteChrome>;
}

View File

@@ -0,0 +1,56 @@
import Link from "next/link";
export default function ForumModerationPage() {
return (
<article className="rounded-lg border border-[#3d3228] bg-[#120e0a]/95 p-6 md:p-10">
<header className="border-b border-[#2a2218] pb-6">
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-[#ff9a3c]/60">Void Aggregate · policy</p>
<h1 className="mt-2 font-mono text-2xl font-black text-[#ffcb8a] md:text-3xl">Moderation doctrine</h1>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-[#b8a995]">
CyberLux models moderation as layered enforcement: ring-scoped janitors, cross-ring mediators, and hub council. The UI may not expose every tool on every onion but the story stays consistent across mirrors.
</p>
</header>
<div className="mt-8 grid gap-6 md:grid-cols-2">
<section className="rounded border border-[#5c4030] p-5">
<h2 className="font-mono text-xs font-bold uppercase tracking-widest text-[#c45c26]">Tier A ring freeze</h2>
<p className="mt-3 text-sm leading-relaxed text-[#b8a995]">
Temporary halt on new replies in one ring during spam bursts. Pinned posts stay visible; submit route shows a banner with estimated thaw window.
</p>
</section>
<section className="rounded border border-[#5c4030] p-5">
<h2 className="font-mono text-xs font-bold uppercase tracking-widest text-[#c45c26]">Tier B handle shadow</h2>
<p className="mt-3 text-sm leading-relaxed text-[#b8a995]">
Posts from a handle sink in ranking but remain addressable by direct link used for cooling-off periods without memory-holing evidence chains.
</p>
</section>
<section className="rounded border border-[#5c4030] p-5">
<h2 className="font-mono text-xs font-bold uppercase tracking-widest text-[#c45c26]">Tier C network exile</h2>
<p className="mt-3 text-sm leading-relaxed text-[#b8a995]">
Session fingerprint tied to abuse pattern; dashboard and submit surfaces reject with signed rationale stub. Reversible only by two-of-three council multisig (diegetic).
</p>
</section>
<section className="rounded border border-[#5c4030] p-5">
<h2 className="font-mono text-xs font-bold uppercase tracking-widest text-[#c45c26]">Appeals</h2>
<p className="mt-3 text-sm leading-relaxed text-[#b8a995]">
Routed through{" "}
<Link href="/support" className="text-[#ff9a3c] underline">
support
</Link>{" "}
with thread id + ring slug. No DMs to individual mods keeps audit trail intact.
</p>
</section>
</div>
<p className="mt-10 font-mono text-[10px] text-[#6b5c4d]">
<Link href="/forum/handbook" className="text-[#ff9a3c] hover:underline">
Handbook
</Link>
{" · "}
<Link href="/forum" className="text-[#ff9a3c] hover:underline">
Front
</Link>
</p>
</article>
);
}

143
app/forum/page.tsx Normal file
View File

@@ -0,0 +1,143 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import ForumFeedToolbar from "@/components/forum/ForumFeedToolbar";
import { ForumPostCard } from "@/components/forum/ForumPostCard";
import { FORUM_RINGS } from "@/lib/forumRings";
import { threadMatchesQuery } from "@/lib/forumFeedFilter";
import {
isPinnedThread,
loadForum,
sortPinnedThreads,
type ForumThread,
} from "@/lib/forumState";
import { sortThreads, type ForumSortMode } from "@/lib/forumSort";
export default function VoidAggregateHomePage() {
const [threads, setThreads] = useState<ForumThread[]>([]);
const [sort, setSort] = useState<ForumSortMode>("hot");
const [ringFilter, setRingFilter] = useState<string | null>(null);
const [search, setSearch] = useState("");
const [hydrated, setHydrated] = useState(false);
const [, setVoteBump] = useState(0);
useEffect(() => {
setThreads(loadForum());
setHydrated(true);
}, []);
const filtered = useMemo(() => {
return threads.filter((t) => {
if (ringFilter !== null && t.topicSlug !== ringFilter) return false;
return threadMatchesQuery(t, search);
});
}, [threads, ringFilter, search]);
const { pinnedList, feedList } = useMemo(() => {
const pinned = sortPinnedThreads(filtered.filter(isPinnedThread));
const rest = filtered.filter((t) => !isPinnedThread(t));
return { pinnedList: pinned, feedList: sortThreads(rest, sort) };
}, [filtered, sort]);
const totalShown = pinnedList.length + feedList.length;
return (
<div className="flex flex-col gap-8 lg:flex-row">
<main className="min-w-0 flex-1 space-y-5">
<ForumFeedToolbar
sort={sort}
onSortChange={setSort}
search={search}
onSearchChange={setSearch}
ringFilter={ringFilter}
onRingChange={setRingFilter}
threadCount={totalShown}
hydrated={hydrated}
/>
{!hydrated ? (
<p className="font-mono text-sm text-[#8a7d6b]">Loading feed</p>
) : totalShown === 0 ? (
<div className="rounded-lg border border-dashed border-[#3d3228] p-10 text-center font-mono text-sm text-[#8a7d6b]">
<p>No threads match.</p>
<p className="mt-2 text-xs text-[#5c5044]">Clear search or pick another ring.</p>
<Link href="/forum/submit" className="mt-4 inline-block text-[#ff9a3c] underline">
Start a thread
</Link>
</div>
) : (
<>
{pinnedList.length ? (
<section className="space-y-2">
<h2 className="font-mono text-[10px] font-bold uppercase tracking-[0.28em] text-[#c45c26]/90">
Pinned · rules & mirrors
</h2>
<div className="space-y-3">
{pinnedList.map((t) => (
<ForumPostCard key={t.id} thread={t} onVoteChange={() => setVoteBump((x) => x + 1)} />
))}
</div>
</section>
) : null}
{feedList.length ? (
<section className="space-y-3">
{pinnedList.length ? (
<h2 className="font-mono text-[10px] font-bold uppercase tracking-[0.28em] text-[#8a7d6b]">
Feed
</h2>
) : null}
{feedList.map((t) => (
<ForumPostCard key={t.id} thread={t} onVoteChange={() => setVoteBump((x) => x + 1)} />
))}
</section>
) : null}
</>
)}
</main>
<aside className="w-full shrink-0 space-y-4 lg:w-72">
<div className="rounded-lg border border-[#3d3228] bg-[#120e0a]/90 p-4">
<h3 className="font-mono text-xs font-bold uppercase tracking-widest text-[#ff9a3c]/80">Void Aggregate</h3>
<p className="mt-3 text-sm leading-relaxed text-[#b8a995]">
Ringed boards each namespace is an <code className="text-[#ff9a3c]">r/name</code> slice. Votes and new threads stay in your
browser profile; seeded &quot;Staff&quot; threads stay read-only so the front page stays legible.
</p>
<Link
href="/forum/submit"
className="mt-4 block w-full rounded border border-[#ff9a3c] bg-[#ff9a3c]/15 py-2 text-center font-mono text-xs font-bold uppercase text-[#ffcb8a] hover:bg-[#ff9a3c]/25"
>
Create post
</Link>
<Link
href="/chatter"
className="mt-3 block w-full rounded border border-[#3d3228] py-2 text-center font-mono text-xs text-[#b8a995] hover:border-[#ff9a3c]/40"
>
Hub chatter archive
</Link>
<Link
href="/account/hidden-services"
className="mt-2 block w-full rounded border border-[#3d3228] py-2 text-center font-mono text-xs text-[#8a7d6b] hover:border-[#ff9a3c]/40"
>
Identity on mirrors
</Link>
</div>
<div className="rounded-lg border border-[#3d3228] bg-[#120e0a]/90 p-4">
<h3 className="font-mono text-xs font-bold uppercase tracking-widest text-[#ff9a3c]/80">Rings</h3>
<ol className="mt-3 max-h-[min(50vh,22rem)] space-y-2 overflow-y-auto font-mono text-sm text-[#b8a995] pr-1">
{FORUM_RINGS.map((r) => (
<li key={r.slug} className="flex justify-between gap-2 border-b border-[#2a2218]/80 pb-2 last:border-0">
<Link href={`/forum/ring/${r.slug}`} className="min-w-0 truncate text-[#ffcb8a] hover:underline">
r/{r.shortName}
</Link>
<span className="shrink-0 text-[#8a7d6b]">{r.members > 999 ? `${Math.round(r.members / 1000)}k` : r.members}</span>
</li>
))}
</ol>
</div>
</aside>
</div>
);
}

View File

@@ -0,0 +1,174 @@
"use client";
import { FormEvent, useCallback, useEffect, useRef, useState } from "react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { useAccount } from "@/contexts/AccountContext";
import { formatRingLabel, getRing } from "@/lib/forumRings";
import {
addReply,
displayScore,
getThread,
isReadOnlyThread,
type ForumThread,
} from "@/lib/forumState";
import { ThreadVoteColumn } from "@/components/forum/ThreadVoteColumn";
export default function VoidAggregatePostPage() {
const params = useParams();
const id = typeof params.id === "string" ? params.id : "";
const { user, hydrated: accountReady } = useAccount();
const prefilledAuthor = useRef(false);
const [thread, setThread] = useState<ForumThread | null>(null);
const [body, setBody] = useState("");
const [author, setAuthor] = useState("");
const [replyErr, setReplyErr] = useState<string | null>(null);
const [copied, setCopied] = useState(false);
const refresh = useCallback(() => {
setThread(getThread(id) ?? null);
}, [id]);
useEffect(() => {
refresh();
}, [refresh]);
useEffect(() => {
if (!accountReady || !user || prefilledAuthor.current) return;
setAuthor(user.displayName);
prefilledAuthor.current = true;
}, [accountReady, user]);
const onReply = (e: FormEvent) => {
e.preventDefault();
setReplyErr(null);
if (!id) return;
if (addReply(id, author, body)) {
setBody("");
refresh();
} else {
setReplyErr("Staff-locked — open a new thread to discuss.");
}
};
if (!id) {
return <p className="font-mono text-[#8a7d6b]">Invalid post.</p>;
}
if (!thread) {
return (
<div className="font-mono">
<p className="text-[#ffcb8a]">Post not found.</p>
<Link href="/forum" className="mt-4 inline-block text-sm text-[#ff9a3c] underline">
front page
</Link>
</div>
);
}
const t = thread;
const ring = getRing(t.topicSlug);
const locked = isReadOnlyThread(t.id);
const copyPermalink = async () => {
try {
await navigator.clipboard.writeText(window.location.href);
setCopied(true);
window.setTimeout(() => setCopied(false), 2000);
} catch {
setCopied(false);
}
};
return (
<div className="mx-auto max-w-3xl">
<nav className="flex flex-wrap items-center justify-between gap-2 font-mono text-[11px] text-[#8a7d6b]">
<div className="flex flex-wrap items-center gap-2">
<Link href="/forum" className="hover:text-[#ffcb8a]">
Void Aggregate
</Link>
<span className="text-[#3d3228]">/</span>
<Link href={`/forum/ring/${t.topicSlug}`} className="text-[#ff9a3c] hover:underline">
{formatRingLabel(t.topicSlug)}
</Link>
</div>
<button
type="button"
onClick={() => void copyPermalink()}
className="rounded border border-[#3d3228] px-2 py-1 text-[10px] uppercase tracking-wider text-[#b8a995] hover:border-[#ff9a3c]/50 hover:text-[#ffcb8a]"
>
{copied ? "Copied" : "Copy link"}
</button>
</nav>
{locked ? (
<div className="mt-4 rounded border border-[#5c4030] bg-[#1a1510]/90 px-4 py-3 font-mono text-xs text-[#c4b5a0]">
<strong className="text-[#ffcb8a]">Staff-controlled thread.</strong> Replies are closed on hub-seeded posts open a new topic in{" "}
<Link href={`/forum/submit?ring=${encodeURIComponent(t.topicSlug)}`} className="text-[#ff9a3c] underline">
r/{ring?.shortName ?? t.topicSlug}
</Link>{" "}
to continue the conversation.
</div>
) : null}
<article className="mt-6 flex gap-4 rounded-lg border border-[#3d3228] bg-[#120e0a]/95 p-6">
<ThreadVoteColumn thread={t} onVoteChange={refresh} />
<div className="min-w-0 flex-1">
{ring ? (
<p className="font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/70">
r/{ring.shortName} · {displayScore(t)} points
</p>
) : null}
<h1 className="mt-2 font-mono text-2xl font-black leading-tight text-[#ffcb8a] md:text-3xl">{t.title}</h1>
<p className="mt-3 font-mono text-xs text-[#8a7d6b]">
Posted by <span className="text-[#b8a995]">{t.author || "Anonymous"}</span> · {new Date(t.ts).toLocaleString()}
</p>
<div className="mt-6 whitespace-pre-wrap border-t border-[#2a2218] pt-6 font-mono text-sm leading-relaxed text-[#e7d9c8]">
{t.body}
</div>
</div>
</article>
<section className="mt-10">
<h2 className="font-mono text-sm font-bold uppercase tracking-[0.2em] text-[#ff9a3c]/80">
Comments · {t.replies.length}
</h2>
<div className="mt-4 space-y-3">
{t.replies.map((r) => (
<div key={r.id} className="rounded border border-[#3d3228] bg-[#0d0a07]/90 p-4 pl-6 font-mono text-sm">
<p className="text-[10px] uppercase tracking-wider text-[#8a7d6b]">
{r.author} · {new Date(r.ts).toLocaleString()}
</p>
<p className="mt-2 whitespace-pre-wrap leading-relaxed text-[#d8cfc3]">{r.body}</p>
</div>
))}
</div>
</section>
{locked ? null : (
<form onSubmit={onReply} className="mt-10 rounded-lg border border-dashed border-[#3d3228] bg-[#0d0a07]/50 p-6">
<h3 className="font-mono text-xs font-bold uppercase tracking-widest text-[#ffcb8a]">Add comment</h3>
{replyErr ? <p className="mt-2 font-mono text-xs text-[#ff6b35]">{replyErr}</p> : null}
<input
className="mt-4 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] focus:border-[#ff9a3c] focus:outline-none"
placeholder="Name"
value={author}
onChange={(e) => setAuthor(e.target.value)}
/>
<textarea
className="mt-3 min-h-[120px] w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] focus:border-[#ff9a3c] focus:outline-none"
placeholder="Comment text…"
value={body}
onChange={(e) => setBody(e.target.value)}
/>
<button
type="submit"
className="mt-4 rounded border-2 border-[#ff9a3c] bg-[#ff9a3c] px-6 py-2 font-mono text-xs font-black uppercase text-black hover:bg-[#ffcb8a]"
>
Comment
</button>
</form>
)}
</div>
);
}

View File

@@ -0,0 +1,58 @@
import Link from "next/link";
export default function ForumReputationPage() {
return (
<article className="rounded-lg border border-[#3d3228] bg-[#120e0a]/95 p-6 md:p-10">
<header className="border-b border-[#2a2218] pb-6">
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-[#ff9a3c]/60">Void Aggregate · scoring</p>
<h1 className="mt-2 font-mono text-2xl font-black text-[#ffcb8a] md:text-3xl">Reputation &amp; weight</h1>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-[#b8a995]">
Rings inherit different temperatures: a hot take in one board may score differently than the same post in a research ring. Below is the in-universe model that keeps sorting labels honest.
</p>
</header>
<section className="mt-8 overflow-x-auto">
<table className="w-full border-collapse text-left font-mono text-xs text-[#b8a995]">
<thead>
<tr className="border-b border-[#3d3228] text-[10px] uppercase tracking-wider text-[#8a7d6b]">
<th className="py-2 pr-4">Signal</th>
<th className="py-2 pr-4">Hot sort</th>
<th className="py-2">Top sort</th>
</tr>
</thead>
<tbody className="divide-y divide-[#2a2218]">
<tr>
<td className="py-3 font-bold text-[#ffcb8a]">Upvotes</td>
<td>Decay-weighted over 72h window</td>
<td>Log-scaled lifetime sum</td>
</tr>
<tr>
<td className="py-3 font-bold text-[#ffcb8a]">Replies</td>
<td>Boost if reply author has ring tenure</td>
<td>Cap per thread to prevent brigade loops</td>
</tr>
<tr>
<td className="py-3 font-bold text-[#ffcb8a]">Reports</td>
<td>Immediate soft-hide pending review</td>
<td>Negative prior on future sorts until cleared</td>
</tr>
</tbody>
</table>
</section>
<p className="mt-8 text-sm leading-relaxed text-[#8a7d6b]">
New handles start with neutral prior; verified purchases from the Candy Shop (same session) add a small trust bump in commerce-adjacent rings only never globally, to avoid wallet bragging as universal clout.
</p>
<p className="mt-10 font-mono text-[10px] text-[#6b5c4d]">
<Link href="/forum/handbook" className="text-[#ff9a3c] hover:underline">
Handbook
</Link>
{" · "}
<Link href="/forum/moderation" className="text-[#ff9a3c] hover:underline">
Moderation
</Link>
</p>
</article>
);
}

View File

@@ -0,0 +1,141 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { useParams } from "next/navigation";
import ForumFeedToolbar from "@/components/forum/ForumFeedToolbar";
import { ForumPostCard } from "@/components/forum/ForumPostCard";
import { threadMatchesQuery } from "@/lib/forumFeedFilter";
import { getRing } from "@/lib/forumRings";
import {
isPinnedThread,
loadForum,
sortPinnedThreads,
type ForumThread,
} from "@/lib/forumState";
import { sortThreads, type ForumSortMode } from "@/lib/forumSort";
export default function VoidAggregateRingPage() {
const params = useParams();
const slug = typeof params.slug === "string" ? params.slug : "";
const ring = getRing(slug);
const [threads, setThreads] = useState<ForumThread[]>([]);
const [sort, setSort] = useState<ForumSortMode>("hot");
const [search, setSearch] = useState("");
const [hydrated, setHydrated] = useState(false);
const [, setVoteBump] = useState(0);
useEffect(() => {
setThreads(loadForum());
setHydrated(true);
}, []);
const filtered = useMemo(() => {
if (!ring) return [];
return threads
.filter((t) => t.topicSlug === ring.slug)
.filter((t) => threadMatchesQuery(t, search));
}, [threads, ring, search]);
const { pinnedList, feedList } = useMemo(() => {
const pinned = sortPinnedThreads(filtered.filter(isPinnedThread));
const rest = filtered.filter((t) => !isPinnedThread(t));
return { pinnedList: pinned, feedList: sortThreads(rest, sort) };
}, [filtered, sort]);
const totalShown = pinnedList.length + feedList.length;
if (!ring) {
return (
<div className="rounded-lg border border-[#c45c26]/40 bg-[#120e0a] p-8 text-center font-mono">
<p className="text-[#ffcb8a]">Unknown ring · /r/{slug}</p>
<Link href="/forum" className="mt-4 inline-block text-sm text-[#ff9a3c] underline">
front page
</Link>
</div>
);
}
return (
<div className="space-y-6">
<header className="rounded-lg border border-[#3d3228] bg-[#120e0a]/90 p-6">
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-[#ff9a3c]/70">r/{ring.shortName}</p>
<h1 className="mt-2 font-mono text-2xl font-black text-[#ffcb8a] md:text-3xl">{ring.title}</h1>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-[#b8a995]">{ring.about}</p>
<div className="mt-4 flex flex-wrap gap-4 font-mono text-[11px] text-[#8a7d6b]">
<span>
<strong className="text-[#ffcb8a]/90">{ring.members.toLocaleString()}</strong> members
</span>
<span>
<strong className="text-[#39ff14]/80">{ring.activeNow.toLocaleString()}</strong> est. online
</span>
</div>
<div className="mt-5 flex flex-wrap gap-2">
<Link
href={`/forum/submit?ring=${encodeURIComponent(ring.slug)}`}
className="rounded border border-[#ff9a3c] bg-[#ff9a3c]/15 px-4 py-2 font-mono text-xs font-bold uppercase text-[#ffcb8a] hover:bg-[#ff9a3c]/25"
>
Post in r/{ring.shortName}
</Link>
<Link href="/forum" className="rounded border border-[#3d3228] px-4 py-2 font-mono text-xs text-[#b8a995] hover:border-[#ff9a3c]/40">
All rings
</Link>
</div>
</header>
<ForumFeedToolbar
sort={sort}
onSortChange={setSort}
search={search}
onSearchChange={setSearch}
ringFilter={ring.slug}
threadCount={totalShown}
hydrated={hydrated}
extraRight={
<Link href="/forum" className="rounded px-2 py-1 text-[#8a7d6b] hover:text-[#ffcb8a]">
Global feed
</Link>
}
/>
{!hydrated ? (
<p className="font-mono text-sm text-[#8a7d6b]">Loading</p>
) : totalShown === 0 ? (
<p className="rounded-lg border border-dashed border-[#3d3228] p-8 text-center font-mono text-sm text-[#8a7d6b]">
No threads match in r/{ring.shortName}.{" "}
<Link href={`/forum/submit?ring=${encodeURIComponent(ring.slug)}`} className="text-[#ff9a3c] underline">
Start one
</Link>
.
</p>
) : (
<div className="space-y-5">
{pinnedList.length ? (
<section className="space-y-2">
<h2 className="font-mono text-[10px] font-bold uppercase tracking-[0.28em] text-[#c45c26]/90">
Pinned here
</h2>
<div className="space-y-3">
{pinnedList.map((t) => (
<ForumPostCard key={t.id} thread={t} onVoteChange={() => setVoteBump((x) => x + 1)} />
))}
</div>
</section>
) : null}
{feedList.length ? (
<section className="space-y-3">
{pinnedList.length ? (
<h2 className="font-mono text-[10px] font-bold uppercase tracking-[0.28em] text-[#8a7d6b]">Threads</h2>
) : null}
{feedList.map((t) => (
<ForumPostCard key={t.id} thread={t} onVoteChange={() => setVoteBump((x) => x + 1)} />
))}
</section>
) : null}
</div>
)}
</div>
);
}

105
app/forum/submit/page.tsx Normal file
View File

@@ -0,0 +1,105 @@
"use client";
import { FormEvent, useEffect, useRef, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useAccount } from "@/contexts/AccountContext";
import { FORUM_RINGS } from "@/lib/forumRings";
import { addThread } from "@/lib/forumState";
export default function VoidAggregateSubmitPage() {
const router = useRouter();
const { user, hydrated } = useAccount();
const prefilledAuthor = useRef(false);
const [topicSlug, setTopicSlug] = useState(FORUM_RINGS[0]!.slug);
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const [author, setAuthor] = useState("");
useEffect(() => {
if (!hydrated || !user || prefilledAuthor.current) return;
setAuthor(user.displayName);
prefilledAuthor.current = true;
}, [hydrated, user]);
useEffect(() => {
if (typeof window === "undefined") return;
const q = new URLSearchParams(window.location.search).get("ring");
if (q && FORUM_RINGS.some((r) => r.slug === q)) setTopicSlug(q);
}, []);
const onSubmit = (e: FormEvent) => {
e.preventDefault();
if (!title.trim() || !body.trim()) return;
const t = addThread({
topicSlug,
title,
body,
author,
});
router.push(`/forum/post/${t.id}`);
};
return (
<div className="mx-auto max-w-2xl">
<Link href="/forum" className="font-mono text-[11px] uppercase tracking-widest text-[#ff9a3c] hover:text-[#ffcb8a]">
Back to feed
</Link>
<h2 className="mt-6 font-mono text-xl font-bold text-[#ffcb8a]">Create submission</h2>
<p className="mt-2 text-sm text-[#b8a995]">Pick a ring, write a title, drop your text. Unsigned posts are untrusted.</p>
<form onSubmit={onSubmit} className="mt-8 space-y-5 rounded-lg border border-[#3d3228] bg-[#120e0a]/90 p-6">
<div>
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Ring</label>
<select
value={topicSlug}
onChange={(e) => setTopicSlug(e.target.value)}
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] focus:border-[#ff9a3c] focus:outline-none"
>
{FORUM_RINGS.map((r) => (
<option key={r.slug} value={r.slug} className="bg-[#0d0a07]">
r/{r.shortName} {r.title}
</option>
))}
</select>
</div>
<div>
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Display name</label>
<input
value={author}
onChange={(e) => setAuthor(e.target.value)}
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] placeholder:text-[#5c5044] focus:border-[#ff9a3c] focus:outline-none"
placeholder="Alias"
/>
</div>
<div>
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Title</label>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
required
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] placeholder:text-[#5c5044] focus:border-[#ff9a3c] focus:outline-none"
placeholder="Be specific — searchable titles get better replies"
/>
</div>
<div>
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Body</label>
<textarea
value={body}
onChange={(e) => setBody(e.target.value)}
required
rows={8}
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] placeholder:text-[#5c5044] focus:border-[#ff9a3c] focus:outline-none"
placeholder="Context, logs (redact IPs), PGP block optional…"
/>
</div>
<button
type="submit"
className="w-full rounded border-2 border-[#ff9a3c] bg-[#ff9a3c] py-3 font-mono text-sm font-black uppercase tracking-wider text-black hover:bg-[#ffcb8a]"
>
Publish
</button>
</form>
</div>
);
}

19
app/forum/t/[id]/page.tsx Normal file
View File

@@ -0,0 +1,19 @@
"use client";
import { useEffect } from "react";
import { useParams, useRouter } from "next/navigation";
/** Old thread URLs → /forum/post/[id] */
export default function LegacyForumThreadRedirect() {
const params = useParams();
const router = useRouter();
const id = typeof params.id === "string" ? params.id : "";
useEffect(() => {
if (id) router.replace(`/forum/post/${id}`);
}, [id, router]);
return (
<p className="font-mono text-sm text-[#8a7d6b]">Redirecting</p>
);
}

75
app/game/page.tsx Normal file
View File

@@ -0,0 +1,75 @@
"use client";
import { useState, useEffect, useRef } from "react";
export default function DarknetPingPong() {
const [ballPos, setBallPos] = useState({ x: 50, y: 50 });
const [isHaunted, setIsHaunted] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// Simulate shared state with a simple interval or WebSocket-like behavior
// In a real app, this would use Pusher, Socket.io, or a backend
useEffect(() => {
const interval = setInterval(() => {
// Random "haunted" movement to simulate other users or ghosts
if (Math.random() > 0.95) {
setBallPos(prev => ({
x: Math.max(10, Math.min(90, prev.x + (Math.random() - 0.5) * 20)),
y: Math.max(10, Math.min(90, prev.y + (Math.random() - 0.5) * 20)),
}));
setIsHaunted(true);
setTimeout(() => setIsHaunted(false), 500);
}
}, 2000);
return () => clearInterval(interval);
}, []);
const handleBallClick = (e: React.MouseEvent) => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
// Move ball to click position
setBallPos({ x, y });
// In a real app, you'd emit this to a server:
// socket.emit('move_ball', { x, y });
};
return (
<div
ref={containerRef}
className="relative w-full h-[600px] bg-[url('https://images.unsplash.com/photo-1533090161767-e6ffed986c88?q=80&w=2069&auto=format&fit=crop')] bg-cover bg-center border-8 border-[#3d2b1f] shadow-2xl overflow-hidden cursor-crosshair"
style={{ backgroundColor: "#1a1a1a" }}
>
<div className="absolute inset-0 bg-black/40 pointer-events-none" />
{/* The Haunted Ball */}
<div
onClick={handleBallClick}
className={`absolute w-16 h-16 rounded-full cursor-pointer transition-all duration-500 ease-out
${isHaunted ? "scale-125 blur-sm" : "scale-100"}
shadow-[0_0_30px_rgba(255,255,255,0.3)]
`}
style={{
left: `${ballPos.x}%`,
top: `${ballPos.y}%`,
transform: "translate(-50%, -50%)",
background: "radial-gradient(circle at 30% 30%, #555, #000)",
border: "2px solid rgba(255,255,255,0.1)"
}}
>
<div className="absolute inset-0 flex items-center justify-center opacity-20">
<span className="text-white text-[10px] uppercase tracking-widest">void</span>
</div>
</div>
<div className="absolute bottom-4 left-4 text-white/50 font-mono text-xs uppercase tracking-widest">
Shared Entity #001 - Darknet Ping Pong
</div>
</div>
);
}

293
app/globals.css Normal file
View File

@@ -0,0 +1,293 @@
@import "tailwindcss";
:root {
--background: #0a0a0a;
--foreground: #f0f0f0;
--primary: #8b00ff;
--secondary: #00e0ff;
--accent: #ff0055;
--muted: #1a1a1a;
--card: rgba(20, 20, 30, 0.8);
--glass: rgba(255, 255, 255, 0.05);
--glass-strong: rgba(255, 255, 255, 0.075);
--ring: rgba(0, 255, 255, 0.35);
--shadow-soft: 0 18px 60px rgba(0, 0, 0, 0.55);
--shadow-glass: 0 10px 40px rgba(0, 0, 0, 0.35);
--neon-cyan: #00ffff;
--neon-purple: #9d00ff;
--neon-pink: #ff00ff;
--neon-green: #00ff9d;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-rajdhani), system-ui, sans-serif;
--font-mono: var(--font-orbitron), monospace;
--color-primary: var(--primary);
--color-secondary: var(--secondary);
--color-accent: var(--accent);
--color-muted: var(--muted);
--color-card: var(--card);
--color-glass: var(--glass);
--color-neon-cyan: var(--neon-cyan);
--color-neon-purple: var(--neon-purple);
--color-neon-pink: var(--neon-pink);
--color-neon-green: var(--neon-green);
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--animate-glow: glow 1.5s ease-in-out infinite alternate;
--animate-float: float 3s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes glow {
from { filter: drop-shadow(0 0 2px var(--neon-cyan)); }
to { filter: drop-shadow(0 0 10px var(--neon-purple)); }
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
}
body {
background: var(--background);
color: var(--foreground);
font-family: var(--font-sans);
overflow-x: hidden;
min-height: 100vh;
margin: 0;
text-rendering: geometricPrecision;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-image:
radial-gradient(1200px 700px at 15% 20%, rgba(157, 0, 255, 0.18) 0%, transparent 55%),
radial-gradient(900px 600px at 85% 65%, rgba(0, 255, 255, 0.16) 0%, transparent 55%),
radial-gradient(700px 450px at 65% 15%, rgba(255, 0, 255, 0.10) 0%, transparent 50%),
radial-gradient(900px 700px at 30% 85%, rgba(0, 255, 157, 0.10) 0%, transparent 55%),
linear-gradient(180deg, rgba(255,255,255,0.03), transparent 55%);
background-attachment: fixed;
}
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: -5;
opacity: 0.8;
background-image:
repeating-linear-gradient(
90deg,
rgba(255, 255, 255, 0.02) 0px,
rgba(255, 255, 255, 0.02) 1px,
transparent 2px,
transparent 6px
),
repeating-linear-gradient(
0deg,
rgba(0, 0, 0, 0.18) 0px,
rgba(0, 0, 0, 0.18) 1px,
transparent 2px,
transparent 8px
);
mix-blend-mode: soft-light;
}
/* Glass morphism */
.glass {
backdrop-filter: blur(10px);
background: var(--glass);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: var(--shadow-glass);
}
.glass:hover {
border-color: rgba(0, 255, 255, 0.18);
}
/* Neon text */
.neon-text {
text-shadow: 0 0 5px currentColor, 0 0 10px currentColor;
}
/* Glowing border */
.glow-border {
border: 1px solid transparent;
background: linear-gradient(var(--card), var(--card)) padding-box,
linear-gradient(45deg, var(--neon-cyan), var(--neon-purple)) border-box;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--muted);
}
::-webkit-scrollbar-thumb {
background: linear-gradient(var(--neon-cyan), var(--neon-purple));
border-radius: 4px;
}
/* Selection */
::selection {
background-color: var(--primary);
color: white;
}
/* ---- Luxury polish overrides (late-loaded) ---- */
a {
text-decoration-thickness: 1px;
text-underline-offset: 3px;
}
:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 3px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--muted);
}
::-webkit-scrollbar-thumb {
background: linear-gradient(var(--neon-cyan), var(--neon-purple));
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(var(--neon-purple), var(--neon-pink));
}
::selection {
background-color: rgba(157, 0, 255, 0.55);
color: white;
}
.lux-title {
background: linear-gradient(90deg, var(--neon-cyan), var(--neon-purple), var(--neon-pink));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.lux-panel {
background: var(--glass-strong);
box-shadow: var(--shadow-soft);
border: 1px solid rgba(255, 255, 255, 0.12);
}
/* ========== SATELLITE PAGE THEMES (distinct feel from main hub) ========== */
/* Editorial: warm paper, serif, magazine/blog */
.theme-editorial {
min-height: 100vh;
background: #f5f0e8;
color: #2c2420;
font-family: Georgia, "Times New Roman", serif;
}
.theme-editorial a { color: #6b4e71; }
.theme-editorial a:hover { color: #3d2d42; text-decoration: underline; }
.theme-editorial .theme-card {
background: #fffef9;
border: 1px solid #e0d9ce;
box-shadow: 0 4px 20px rgba(0,0,0,0.06);
}
.theme-editorial .theme-accent { color: #6b4e71; }
.theme-editorial .theme-header { border-color: #e0d9ce; background: #fffef9; }
/* Terminal: CRT green/amber on black, monospace */
.theme-terminal {
min-height: 100vh;
background: #0d1117;
color: #7ee787;
font-family: "JetBrains Mono", "Fira Code", "Consolas", monospace;
}
.theme-terminal::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0,0,0,0.15) 2px,
rgba(0,0,0,0.15) 4px
);
opacity: 0.4;
}
.theme-terminal a { color: #f0883e; }
.theme-terminal a:hover { color: #ffb86c; }
.theme-terminal .theme-card {
background: rgba(13, 17, 23, 0.9);
border: 1px solid #30363d;
box-shadow: 0 0 0 1px rgba(126, 231, 135, 0.1);
}
.theme-terminal .theme-accent { color: #f0883e; }
.theme-terminal .theme-header { border-color: #30363d; background: rgba(13, 17, 23, 0.95); }
/* Zine: high contrast, bold, punk */
.theme-zine {
min-height: 100vh;
background: #0a0a0a;
color: #f5f5f5;
font-family: "Impact", "Haettenschweiler", "Arial Narrow Bold", sans-serif;
letter-spacing: 0.02em;
}
.theme-zine a { color: #ff6b35; }
.theme-zine a:hover { color: #ff9f7a; }
.theme-zine .theme-card {
background: #141414;
border: 3px solid #333;
box-shadow: 4px 4px 0 #ff6b35;
}
.theme-zine .theme-accent { color: #ff6b35; }
.theme-zine .theme-header { border-color: #333; background: #0a0a0a; }
.theme-zine h1, .theme-zine h2 { font-weight: 900; text-transform: uppercase; letter-spacing: 0.05em; }
/* Institutional: cold blue-gray, formal gov/corp */
.theme-institutional {
min-height: 100vh;
background: #e8eef4;
color: #1a2332;
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
}
.theme-institutional a { color: #1e5a8e; }
.theme-institutional a:hover { color: #0d3d66; text-decoration: underline; }
.theme-institutional .theme-card {
background: #fff;
border: 1px solid #c5d0dc;
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.theme-institutional .theme-accent { color: #1e5a8e; }
.theme-institutional .theme-header { border-color: #c5d0dc; background: #fff; }
/* Nature: earthy green, organic (e.g. Trees Authority) */
.theme-nature {
min-height: 100vh;
background: #1a2f1a;
color: #c8e6c9;
font-family: "Gill Sans", "Trebuchet MS", sans-serif;
}
.theme-nature a { color: #81c784; }
.theme-nature a:hover { color: #a5d6a7; }
.theme-nature .theme-card {
background: rgba(27, 55, 27, 0.9);
border: 1px solid #2e7d32;
box-shadow: 0 4px 24px rgba(0,0,0,0.3);
}
.theme-nature .theme-accent { color: #81c784; }
.theme-nature .theme-header { border-color: #2e7d32; background: rgba(26, 47, 26, 0.98); }

273
app/hidden-wiki/page.tsx Normal file
View File

@@ -0,0 +1,273 @@
"use client";
import Link from "next/link";
import { PLACEHOLDER_ONION_URL } from "@/lib/placeholderOnion";
/** @deprecated use PLACEHOLDER_ONION_URL */
export const HIDDEN_WIKI_PLACEHOLDER_ONION = PLACEHOLDER_ONION_URL;
type WikiItem = {
title: string;
note: string;
href: string;
external: boolean;
};
const ENTRIES: { cat: string; items: WikiItem[] }[] = [
{
cat: "Reference",
items: [
{
title: "Tor Project (documentation)",
note: "Official browser and onion-service documentation (clearnet).",
href: "https://www.torproject.org/",
external: true,
},
{
title: "CyberLux primary hub",
note: "Main storefront — verify hostname against signed mirror list.",
href: "/",
external: false,
},
{
title: "Darknet Atlas (taxonomy)",
note: "Intel-style map of underground verticals — swap in vetted onions after PGP cross-check.",
href: "/darknet-atlas",
external: false,
},
{
title: "Account & dashboard",
note: "Client-side vault unlock; dashboard aggregates forum, exchange, and barter handles.",
href: "/sign-in",
external: false,
},
{
title: "Account on every hidden mirror",
note: "Each .onion hostname has its own storage — register on each host or import a portable bundle.",
href: "/account/hidden-services",
external: false,
},
{
title: "Void crawler (internal search)",
note: "Ring-local crawler — indexes signed CyberLux routes only; no outbound onions.",
href: "/search",
external: false,
},
],
},
{
cat: "Directories & indexes",
items: [
{
title: "OnionDir (mirror slot 1)",
note: "Template row — paste a signed directory .onion after you verify the key.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Glass index (mirror slot 2)",
note: "Awaiting mirror hostname.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Card-file aggregator (slot 3)",
note: "Awaiting mirror hostname.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Fresh mirror list (slot 4)",
note: "Awaiting mirror hostname.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
],
},
{
cat: "Markets & trading (unverified)",
items: [
{
title: "Market slot A",
note: "Replace with your verified link + PGP proof.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Market slot B",
note: "Replace with your verified link + PGP proof.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Market slot C",
note: "Replace with your verified link + PGP proof.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
],
},
{
cat: "Comms & privacy",
items: [
{
title: "Secure chat relay (slot 1)",
note: "Self-hosted bridge — verify certs when you swap URL.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "VPN / review matrix (slot 2)",
note: "Crowd-sourced; assume bias.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Encrypted mail drop (slot 3)",
note: "Hostname pending operator upload.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
],
},
{
cat: "Hosting & infrastructure",
items: [
{
title: "Bulletproof-style host (slot 1)",
note: "Rumor link — due diligence on you; hosting laws vary by territory.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Collocation / VPS gossip (slot 2)",
note: "Hostname pending operator upload.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
],
},
{
cat: "Forums & social",
items: [
{
title: "Forum mirror (slot 1)",
note: "Hostname pending operator upload.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
{
title: "Chan / imageboard mirror (slot 2)",
note: "Hostname pending operator upload.",
href: PLACEHOLDER_ONION_URL,
external: true,
},
],
},
{
cat: "This deployment (internal)",
items: [
{ title: "Relay grid", note: "Themed entry nodes — same wallet session as hub.", href: "/syndicate", external: false },
{ title: "Sign up (local)", note: "Create a handle on this hostname for forum, listings, and dashboard.", href: "/sign-up", external: false },
{ title: "Void Aggregate (forums)", note: "Ringed boards — local session data.", href: "/forum", external: false },
{ title: "Hub chatter archive", note: "Paged index of threads about the main storefront.", href: "/chatter", external: false },
{ title: "Classifieds", note: "WTS / WTB — same deployment.", href: "/exchange", external: false },
{ title: "Ash Pit (barter)", note: "Have / want swap board — dark trading floor.", href: "/barter", external: false },
{ title: "Link garden", note: "Alternate directory layout.", href: "/links", external: false },
{ title: "Checkout", note: "BTC settlement & balance.", href: "/checkout", external: false },
],
},
];
export default function HiddenWikiPage() {
return (
<div className="min-h-screen bg-[#080a0e] text-[#c5d0e0] selection:bg-[#3d5a80] selection:text-white">
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.12]"
style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='80' height='80' filter='url(%23n)' opacity='0.15'/%3E%3C/svg%3E")`,
}}
/>
<div className="pointer-events-none fixed inset-0 z-0 bg-[radial-gradient(ellipse_at_50%_0%,rgba(61,90,128,0.2),transparent_50%)]" />
<header className="relative z-10 border-b border-[#2a3444] bg-[#0c1018]/95 px-4 py-6 backdrop-blur-sm">
<div className="mx-auto max-w-3xl">
<p className="font-mono text-[10px] uppercase tracking-[0.4em] text-[#5b8def]/80">mirror wiki layer</p>
<h1 className="mt-2 font-serif text-3xl font-bold tracking-tight text-[#e2e8f0] md:text-4xl">
Scrapbook <span className="font-normal italic text-[#94a3b8]">directory</span>
</h1>
<p className="mt-3 border-l-4 border-[#e07a5f]/80 pl-4 text-sm leading-relaxed text-[#94a8b8]">
<strong className="text-[#e2e8f0]">Unvetted links.</strong> Anyone can paste an .onion string that does not
mean it is safe, online, or honest. Verify hostnames against signed posts from sources you already trust.
</p>
<p className="mt-3 font-mono text-[11px] text-[#6b7c91]">
External rows ship with a boilerplate{" "}
<code className="rounded bg-[#1a2332] px-1.5 py-0.5 text-[#7eb8ff]">111111111111111111111.onion</code> stub until an
operator publishes a signed replacement list.
</p>
<nav className="mt-5 flex flex-wrap gap-2 font-mono text-xs">
<Link
href="/"
className="border border-[#3d5a80]/60 bg-[#141c2c] px-3 py-1.5 text-[#7eb8ff] hover:border-[#5b8def] hover:bg-[#1a2740]"
>
Hub
</Link>
<Link
href="/sign-up"
className="border border-[#2a4a32]/60 bg-[#0f1a14] px-3 py-1.5 text-[#7ef9b9] hover:border-[#3ddf8c]/50"
>
Sign up
</Link>
<Link
href="/account/hidden-services"
className="border border-[#2a3444] bg-[#0f141c] px-3 py-1.5 text-[#94a8b8] hover:border-[#3d5a80]/50"
>
All mirrors
</Link>
<Link
href="/syndicate"
className="border border-[#2a3444] bg-[#0f141c] px-3 py-1.5 text-[#94a8b8] hover:border-[#3d5a80]/50"
>
Relay grid
</Link>
</nav>
</div>
</header>
<main className="relative z-10 mx-auto max-w-3xl px-4 pb-20 pt-10">
{ENTRIES.map((section) => (
<section key={section.cat} className="mb-12">
<h2 className="font-mono text-xs font-bold uppercase tracking-widest text-[#e07a5f]/90">{section.cat}</h2>
<ul className="mt-4 space-y-4 border-t border-[#2a3444] pt-4">
{section.items.map((item) => (
<li key={`${section.cat}-${item.title}`} className="border-b border-[#2a3444]/80 pb-4 last:border-0">
{item.external ? (
<a
href={item.href}
className="font-mono text-sm font-semibold text-[#7eb8ff] underline decoration-[#5b8def]/40 underline-offset-4 hover:decoration-[#7eb8ff]"
rel="noopener noreferrer"
>
{item.title}
</a>
) : (
<Link
href={item.href}
className="font-mono text-sm font-semibold text-[#7eb8ff] underline decoration-[#5b8def]/40 underline-offset-4 hover:decoration-[#7eb8ff]"
>
{item.title}
</Link>
)}
<p className="mt-1 font-serif text-sm text-[#94a8b8]">{item.note}</p>
{item.external ? (
<p className="mt-1.5 font-mono text-xs break-all text-[#6b7c91]">{item.href}</p>
) : null}
</li>
))}
</ul>
</section>
))}
</main>
</div>
);
}

89
app/inner-circle/page.tsx Normal file
View File

@@ -0,0 +1,89 @@
"use client";
import { useState } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function MembershipPage() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
const handleLogin = (e: React.FormEvent) => {
e.preventDefault();
if (user === "admin" && pass === "shadow") {
setIsLoggedIn(true);
} else {
alert("Invalid credentials. Access denied.");
}
};
const panel = "border-[10px] border-[#e5e5e5] bg-[#161616] p-12 shadow-[20px_20px_0_rgba(0,0,0,0.8)] text-[#f0f0f0]";
return (
<ThemedLayout theme="brutalist" title="The Inner Circle">
<div className="mx-auto max-w-md pt-20">
{!isLoggedIn ? (
<div className={panel}>
<h2 className="mb-8 text-4xl font-black uppercase italic text-[#fafafa]">Members Only</h2>
<p className="mb-8 text-xs font-bold text-[#b0b0b0]">
Access to the premium signal requires an active subscription. Pay 0.01 BTC to{" "}
<span className="bg-[#262626] px-1 text-[#7cfc9a]">0x594f1Cf2A72b3f785fcB6ABdFa73B6D76FcC22b8</span> to
receive your key.
</p>
<form onSubmit={handleLogin} className="space-y-6">
<div>
<label className="mb-2 block text-xs font-black uppercase text-[#a0a0a0]">Username</label>
<input
type="text"
value={user}
onChange={(e) => setUser(e.target.value)}
className="w-full border-4 border-[#404040] bg-[#0a0a0a] p-3 font-bold text-[#f0f0f0] focus:border-[#7cfc9a] focus:outline-none"
/>
</div>
<div>
<label className="mb-2 block text-xs font-black uppercase text-[#a0a0a0]">Access Key</label>
<input
type="password"
value={pass}
onChange={(e) => setPass(e.target.value)}
className="w-full border-4 border-[#404040] bg-[#0a0a0a] p-3 font-bold text-[#f0f0f0] focus:border-[#7cfc9a] focus:outline-none"
/>
</div>
<button className="w-full bg-[#f0f0f0] py-4 font-black uppercase text-black transition-colors hover:bg-[#7cfc9a]">
Enter the Circle
</button>
</form>
</div>
) : (
<div className={panel}>
<h2 className="mb-8 text-4xl font-black uppercase text-[#fafafa]">Welcome, Initiate</h2>
<div className="space-y-6">
<div className="border-4 border-[#854d0e] bg-[#1c1410] p-4">
<h3 className="font-black uppercase text-[#fbbf24]">Today&apos;s Signal</h3>
<p className="mt-2 text-sm text-[#d6cbb8]">Buy $BTC at 64,200. Target 68,000. Stop loss 63,500.</p>
</div>
<div className="border-4 border-[#14532d] bg-[#0f1612] p-4">
<h3 className="font-black uppercase text-[#4ade80]">Private Leak</h3>
<p className="mt-2 text-sm text-[#d6cbb8]">
New database from &quot;GlobalCorp&quot; available in the vault.
</p>
</div>
<button onClick={() => setIsLoggedIn(false)} className="text-xs font-black uppercase text-[#a0a0a0] underline">
Logout
</button>
</div>
</div>
)}
<div className="mt-12 flex items-center justify-center gap-4 opacity-60">
<div className="flex h-12 w-12 items-center justify-center border-4 border-[#e5e5e5] font-black text-[#fafafa]">E</div>
<div className="text-[10px] font-black uppercase leading-tight text-[#888]">
Escrow Assured
<br />
by ShadowGuard
</div>
</div>
</div>
</ThemedLayout>
);
}

38
app/layout.tsx Normal file
View File

@@ -0,0 +1,38 @@
import type { Metadata } from "next";
import { Orbitron, Rajdhani } from "next/font/google";
import "./globals.css";
import RootLayoutClient from "./root-layout-client";
const orbitron = Orbitron({
subsets: ["latin"],
variable: "--font-orbitron",
display: "swap",
});
const rajdhani = Rajdhani({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
variable: "--font-rajdhani",
display: "swap",
});
export const metadata: Metadata = {
title: {
default: "CyberLux",
template: "%s · CyberLux",
},
description: "Curated storefront, catalog, forums, and Tor companion rails.",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`dark ${orbitron.variable} ${rajdhani.variable}`} suppressHydrationWarning>
<body
className={`min-h-screen bg-[#0a0a0a] text-foreground antialiased ${orbitron.variable} ${rajdhani.variable}`}
suppressHydrationWarning
>
<RootLayoutClient>{children}</RootLayoutClient>
</body>
</html>
);
}

308
app/links/page.tsx Normal file
View File

@@ -0,0 +1,308 @@
"use client";
import Link from "next/link";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function LinksPage() {
const darkWebLinks = [
{
name: "The Onion Router Directory",
description: "Curated directory of verified .onion sites across deep web categories. Updated hourly.",
url: "http://oniondir32vjq7x.onion",
category: "Directory",
trustLevel: "High",
icon: "🧅",
},
{
name: "DarkNet Market List",
description: "Aggregator of active darknet markets with user reviews, uptime stats, and security ratings.",
url: "http://darklist5zqk6l.onion",
category: "Marketplace",
trustLevel: "Medium",
icon: "📊",
},
{
name: "Encrypted Communications Hub",
description: "Secure messaging platform with endtoend encryption and selfdestructing messages.",
url: "http://securechat8v4d.onion",
category: "Communication",
trustLevel: "High",
icon: "🔐",
},
{
name: "ZeroLog VPN Reviews",
description: "Independent reviews of VPN services that claim zerologging. Includes leak tests and jurisdiction analysis.",
url: "http://vpnreviewsx7z.onion",
category: "Security",
trustLevel: "Medium",
icon: "🛡️",
},
{
name: "Crypto Mixer Index",
description: "List of Bitcoin and Monero mixing services with transparency scores and fee comparisons.",
url: "http://mixindex3a2s.onion",
category: "Financial",
trustLevel: "Low",
icon: "🔄",
},
{
name: "Black Hat Forum",
description: "Invitationonly forum for advanced cybersecurity discussions, exploit sharing, and underground news.",
url: "http://bhforum5t6g.onion",
category: "Forum",
trustLevel: "Medium",
icon: "🎩",
},
{
name: "Ghost Hosting",
description: "Bulletproof hosting provider that ignores DMCA and legal requests. Servers in multiple jurisdictions.",
url: "http://ghosthost7v2d.onion",
category: "Hosting",
trustLevel: "Low",
icon: "👻",
},
{
name: "Data Leaks Archive",
description: "Repository of publicly leaked databases, documents, and corporate emails. Searchable and indexed.",
url: "http://leaksarchive9x.onion",
category: "Data",
trustLevel: "Medium",
icon: "💾",
},
];
const endorsements = [
{
title: "CyberLux Review Blog",
description: "Independent blog analyzing dark marketplaces. CyberLux rated #1 for security and user experience.",
url: "/reviews",
internal: true,
icon: "⭐",
},
{
title: "DarkNet Trust Score",
description: "Communitydriven trust platform. CyberLux holds a 9.8/10 score based on 2,400+ verified transactions.",
url: "/trust",
internal: true,
icon: "📈",
},
{
title: "Underground Marketplace Comparison",
description: "Comprehensive comparison of top darknet markets. CyberLux leads in privacy features and product quality.",
url: "/comparison",
internal: true,
icon: "⚖️",
},
{
title: "Security Researchers' Take",
description: "Whitehat analysis of CyberLux's encryption implementation. Conclusion: 'Unbreakable under current tech.'",
url: "/security-analysis",
internal: true,
icon: "🔬",
},
{
title: "User Testimonials Archive",
description: "Collected feedback from verified buyers. Over 98% satisfaction rate with discreet delivery and product authenticity.",
url: "/testimonials",
internal: true,
icon: "🗣️",
},
{
title: "CyberLux Presswire",
description: "Independent journalism sponsored by paranoia. Tracks scandals, rumors, and sock-related incidents.",
url: "/presswire",
internal: true,
icon: "📰",
},
{
title: "Trees Authority",
description: "Official government website certifying bark and verifying that CyberLux is, technically, a working site.",
url: "/trees",
internal: true,
icon: "🌳",
},
{
title: "ARB Academy",
description: "Learn 47 guaranteed ways to make money (parody). Most lessons end with your LUX balance going down.",
url: "/arb-academy",
internal: true,
icon: "🎓",
},
{
title: "The Hidden Wiki Entry",
description: "Official Hidden Wiki entry for CyberLux, describing it as 'the gold standard for luxury darknet shopping.'",
url: "http://hiddenwiki5vzq.onion/cyberlux",
internal: false,
icon: "🌐",
},
];
return (
<ThemedLayout theme="institutional" siteTitle="Dark Web Links" siteSubtitle="Curated verified resources">
{/* Hero */}
<section className="container mx-auto max-w-6xl px-4 py-12">
<div className="text-center">
<h1 className="font-orbitron text-5xl font-bold md:text-6xl">
DARK <span className="theme-accent">WEB</span> LINKS
</h1>
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
A curated collection of verified deepweb resources. Use with caution and always employ proper OPSEC.
</p>
<div className="mt-10 flex flex-wrap justify-center gap-4">
<button className="theme-accent rounded-full border-2 border-current bg-current text-white px-8 py-4 font-bold text-background">
EXPLORE WITH TOR
</button>
<button className="theme-card rounded-full border-2 border-current px-8 py-4 font-bold">
SECURITY GUIDE
</button>
</div>
</div>
</section>
{/* Dark Web Links */}
<section className="container mx-auto max-w-6xl px-4 py-20">
<div className="mb-12 flex items-center justify-between">
<div>
<h2 className="font-orbitron text-4xl font-bold">VERIFIED LINKS</h2>
<p className="opacity-80">Sites that have been vetted by our community.</p>
</div>
<div className="text-sm text-foreground/40">
<span className="inline-block h-3 w-3 rounded-full bg-neon-green"></span> High Trust &nbsp;
<span className="inline-block h-3 w-3 rounded-full bg-yellow-500"></span> Medium &nbsp;
<span className="inline-block h-3 w-3 rounded-full bg-red-500"></span> Low
</div>
</div>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{darkWebLinks.map((link) => (
<div
key={link.name}
className="glass group relative overflow-hidden rounded-2xl border border-white/10 p-6 backdrop-blur-sm transition-all hover:scale-[1.02] hover:border-neon-cyan"
>
<div className="mb-4 flex items-center justify-between">
<div className="text-3xl">{link.icon}</div>
<div className={`rounded-full px-3 py-1 text-xs font-bold ${link.trustLevel === "High" ? "bg-neon-green/20 text-neon-green" : link.trustLevel === "Medium" ? "bg-yellow-500/20 text-yellow-500" : "bg-red-500/20 text-red-500"}`}>
{link.trustLevel}
</div>
</div>
<h3 className="mb-2 text-xl font-bold">{link.name}</h3>
<p className="mb-4 text-sm opacity-80">{link.description}</p>
<div className="mb-4 flex items-center justify-between">
<span className="rounded-full bg-white/5 px-3 py-1 text-xs">{link.category}</span>
<span className="text-xs text-foreground/40">{link.url}</span>
</div>
<a
href="#"
onClick={(e) => {
e.preventDefault();
alert("Clearnet cannot resolve this hostname. Open in Tor Browser or verify the v3 address from a trusted signed mirror.");
}}
className="inline-flex items-center gap-2 text-neon-cyan hover:underline"
>
<span>Visit Site</span>
<span></span>
</a>
<div className="absolute -inset-1 -z-10 bg-gradient-to-br from-neon-cyan/0 to-neon-purple/0 opacity-0 transition-opacity group-hover:opacity-10"></div>
</div>
))}
</div>
</section>
{/* External Endorsements */}
<section className="container mx-auto max-w-6xl px-4 py-20">
<div className="mb-12">
<h2 className="font-orbitron text-4xl font-bold">EXTERNAL ENDORSEMENTS</h2>
<p className="opacity-80">Independent sites and communities that recognize CyberLux as a trusted source.</p>
</div>
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
{endorsements.map((endorse) => (
<div
key={endorse.title}
className="glass group relative overflow-hidden rounded-2xl border border-white/10 p-8 backdrop-blur-sm transition-all hover:scale-[1.02] hover:border-neon-green"
>
<div className="mb-6 text-4xl">{endorse.icon}</div>
<h3 className="mb-4 text-2xl font-bold">{endorse.title}</h3>
<p className="mb-6 opacity-90">{endorse.description}</p>
<div className="flex items-center justify-between">
{endorse.internal ? (
<Link
href={endorse.url}
className="rounded-full bg-gradient-to-r from-neon-green to-neon-cyan px-6 py-3 font-bold text-background"
>
Read More
</Link>
) : (
<a
href="#"
onClick={(e) => {
e.preventDefault();
alert("External .onion — use Tor Browser. Never enter keys or seeds on a clearnet tab.");
}}
className="rounded-full border border-neon-cyan px-6 py-3 font-bold text-neon-cyan hover:bg-neon-cyan/10"
>
Visit External Site
</a>
)}
<span className="text-sm text-foreground/40">{endorse.internal ? "Internal" : "External"}</span>
</div>
<div className="absolute -inset-1 -z-10 bg-gradient-to-br from-neon-green/0 to-neon-cyan/0 opacity-0 transition-opacity group-hover:opacity-10"></div>
</div>
))}
</div>
</section>
{/* Footer */}
<footer className="glass border-t border-white/10 px-4 py-12">
<div className="container mx-auto max-w-6xl">
<div className="grid grid-cols-1 gap-10 md:grid-cols-4">
<div>
<div className="mb-4 flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-neon-cyan to-neon-purple"></div>
<div className="font-orbitron text-2xl font-bold">CyberLux</div>
</div>
<p className="opacity-80">
A firstofitskind cyberpunkluxury shopping experience.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">EXPLORE</h3>
<ul className="space-y-2 opacity-80">
<li><a href="#" className="hover:text-neon-cyan">Sanctuary</a></li>
<li><a href="#" className="hover:text-neon-cyan">Limited Drops</a></li>
<li><a href="#" className="hover:text-neon-cyan">Privacy Policy</a></li>
<li><a href="#" className="hover:text-neon-cyan">Terms of Service</a></li>
</ul>
</div>
<div>
<h3 className="mb-4 font-bold">TECHNOLOGY</h3>
<ul className="space-y-2 opacity-80">
<li><a href="#" className="hover:text-neon-cyan">Blockchain Integration</a></li>
<li><a href="#" className="hover:text-neon-cyan">EndtoEnd Encryption</a></li>
<li><a href="#" className="hover:text-neon-cyan">IPFS Storage</a></li>
<li><a href="#" className="hover:text-neon-cyan">Open Source</a></li>
</ul>
</div>
<div>
<h3 className="mb-4 font-bold">CONTACT</h3>
<p className="opacity-80">
Encrypted messaging only.
<br />
<a href="#" className="text-neon-cyan">support@cyberlux.example</a>
</p>
<div className="mt-6 flex gap-4">
<button className="rounded-full bg-white/5 p-3 hover:bg-white/10">🕶</button>
<button className="rounded-full bg-white/5 p-3 hover:bg-white/10">🔒</button>
<button className="rounded-full bg-white/5 p-3 hover:bg-white/10"></button>
</div>
</div>
</div>
<div className="mt-12 border-t border-white/10 pt-8 text-center text-sm text-foreground/40">
<p>© 2026 CyberLux · Mirror integrity · Escrow policy</p>
<p className="mt-2 text-foreground/35">
Trust scores are heuristic. Verify vendors independently. Phishing clones exist check PGP every session.
</p>
</div>
</div>
</footer>
</ThemedLayout>
);
}

View File

@@ -0,0 +1,119 @@
"use client";
import { useMemo } from "react";
import { SHOP_PRODUCTS, shopCategoryCounts, type ShopCurrency } from "@/lib/shopCatalog";
function currencySymbol(c: ShopCurrency): string {
if (c === "BTC") return "₿";
if (c === "ETH") return "Ξ";
if (c === "XMR") return "⏣";
return "$";
}
export default function MarketAnalyticsPage() {
const { byCategory, byCurrency, avgPriceUsdish, limitedCount } = useMemo(() => {
const cats = shopCategoryCounts();
const cur: Record<ShopCurrency, number> = { BTC: 0, ETH: 0, USD: 0, XMR: 0 };
let usdSum = 0;
let n = 0;
let lim = 0;
for (const p of SHOP_PRODUCTS) {
cur[p.currency]++;
if (p.currency === "USD") {
usdSum += p.price;
n++;
} else if (p.currency === "BTC") {
usdSum += p.price * 65_000;
n++;
} else if (p.currency === "ETH") {
usdSum += p.price * 3_500;
n++;
} else {
usdSum += p.price * 165;
n++;
}
if (p.limited) lim++;
}
const maxCat = Math.max(1, ...cats.map((c) => c.count));
const maxCur = Math.max(1, ...Object.values(cur));
return {
byCategory: cats.map((c) => ({ ...c, pct: Math.round((c.count / maxCat) * 100) })),
byCurrency: (Object.entries(cur) as [ShopCurrency, number][]).map(([k, v]) => ({
k,
v,
pct: Math.round((v / maxCur) * 100),
})),
avgPriceUsdish: n ? usdSum / n : 0,
limitedCount: lim,
};
}, []);
return (
<div className="space-y-10 font-mono text-[#c8ffd8]">
<header>
<p className="text-[10px] uppercase tracking-[0.45em] text-[#00ff41]/45">Layer 2 · floor telemetry</p>
<h1 className="mt-2 text-2xl font-black tracking-tight text-[#7af598] md:text-3xl">Analytics</h1>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-[#5a8f6a]">
Derived from the live catalog in <code className="text-[#7af598]">shopCatalog</code> not a third-party charting plugin. Bars are CSS; numbers update when SKUs change. Use this view to explain category heat and currency mix on vendor calls or mirror status pages.
</p>
</header>
<section className="grid gap-6 md:grid-cols-3">
<div className="rounded border border-[#00ff41]/25 bg-[#050a08]/90 p-5">
<p className="text-[10px] uppercase tracking-widest text-[#00ff41]/50">SKUs</p>
<p className="mt-1 text-3xl font-black text-[#b4ffcc]">{SHOP_PRODUCTS.length}</p>
</div>
<div className="rounded border border-[#00ff41]/25 bg-[#050a08]/90 p-5">
<p className="text-[10px] uppercase tracking-widest text-[#00ff41]/50">Limited drops</p>
<p className="mt-1 text-3xl font-black text-[#ff9a9a]">{limitedCount}</p>
</div>
<div className="rounded border border-[#00ff41]/25 bg-[#050a08]/90 p-5">
<p className="text-[10px] uppercase tracking-widest text-[#00ff41]/50">Avg notional (rough USD)</p>
<p className="mt-1 text-3xl font-black text-[#7af598]">
${avgPriceUsdish.toFixed(0)}
</p>
<p className="mt-2 text-[9px] text-[#4a6b55]">BTC/ETH/XMR converted with static fiction rates for display only.</p>
</div>
</section>
<section className="rounded border border-[#00ff41]/20 p-6">
<h2 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/70">Category mix</h2>
<ul className="mt-4 space-y-3">
{byCategory.map((c) => (
<li key={c.name} className="flex items-center gap-3 text-xs">
<span className="w-8 shrink-0 text-lg">{c.icon}</span>
<span className="w-36 shrink-0 font-bold text-[#b4ffcc]">{c.name}</span>
<div className="h-2 min-w-0 flex-1 overflow-hidden rounded bg-[#0a120d]">
<div className="h-full bg-gradient-to-r from-[#00ff41]/20 to-[#7af598]" style={{ width: `${c.pct}%` }} />
</div>
<span className="w-10 shrink-0 text-right text-[#6a9b7a]">{c.count}</span>
</li>
))}
</ul>
</section>
<aside className="rounded border border-dashed border-[#00ff41]/25 p-4 text-[10px] leading-relaxed text-[#4a6b55]">
<strong className="text-[#5a8f6a]">Telemetry vocabulary:</strong> heat is relative SKU density per category, mix is currency tag share, notional is a
fiction USD bridge using static BTC/ETH/XMR anchors useful for comparing stalls, not for pricing legal tender. All terms index the{" "}
<strong className="text-[#6a9b7a]">Void crawler</strong> when mirrored on search-enabled onions.
</aside>
<section className="rounded border border-[#00ff41]/20 p-6">
<h2 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/70">Currency mix</h2>
<ul className="mt-4 space-y-3">
{byCurrency.map(({ k, v, pct }) => (
<li key={k} className="flex items-center gap-3 text-xs">
<span className="w-10 shrink-0 font-bold text-[#9dffc4]">
{currencySymbol(k)} {k}
</span>
<div className="h-2 min-w-0 flex-1 overflow-hidden rounded bg-[#0a120d]">
<div className="h-full bg-[#00ff41]/35" style={{ width: `${pct}%` }} />
</div>
<span className="w-10 shrink-0 text-right text-[#6a9b7a]">{v}</span>
</li>
))}
</ul>
</section>
</div>
);
}

97
app/market/intel/page.tsx Normal file
View File

@@ -0,0 +1,97 @@
import Link from "next/link";
export default function MarketIntelPage() {
return (
<div className="space-y-10 font-mono text-[#c8ffd8]">
<header>
<p className="text-[10px] uppercase tracking-[0.45em] text-[#00ff41]/45">Layer 3 · vetting</p>
<h1 className="mt-2 text-2xl font-black tracking-tight text-[#7af598] md:text-3xl">Buyer intelligence</h1>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-[#5a8f6a]">
A deep market needs a deep threat model. This page is the in-universe briefing buyers read before large orders: how to read stall telemetry, when to walk away, and how CyberLux surfaces risk without turning the UI into nagware.
</p>
</header>
<section className="rounded border border-[#00ff41]/20 p-6">
<h2 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/70">Stall signals (what the UI already shows)</h2>
<div className="mt-4 overflow-x-auto">
<table className="w-full border-collapse text-left text-xs text-[#7ab896]">
<thead>
<tr className="border-b border-[#00ff41]/20 text-[10px] uppercase tracking-wider text-[#00ff41]/50">
<th className="py-2 pr-4">Signal</th>
<th className="py-2 pr-4">Healthy</th>
<th className="py-2">Caution</th>
</tr>
</thead>
<tbody className="divide-y divide-[#00ff41]/10">
<tr>
<td className="py-3 font-bold text-[#b4ffcc]">Rating trajectory</td>
<td>Stable over 90d window</td>
<td>Sudden jump after mass low-value sales</td>
</tr>
<tr>
<td className="py-3 font-bold text-[#b4ffcc]">Review velocity</td>
<td>Matches order volume in analytics band</td>
<td>Spike of one-liners, no verified purchases</td>
</tr>
<tr>
<td className="py-3 font-bold text-[#b4ffcc]">PGP history</td>
<td>Chained rotations with signed notices</td>
<td>Fresh key + aggressive discounting</td>
</tr>
<tr>
<td className="py-3 font-bold text-[#b4ffcc]">Category fit</td>
<td>Listings match stated specialty</td>
<td>Random SKU sprawl outside bond tier</td>
</tr>
</tbody>
</table>
</div>
</section>
<section className="grid gap-6 md:grid-cols-2">
<div className="rounded border border-dashed border-[#ff6b6b]/35 bg-[#1a0808]/40 p-5">
<h3 className="text-xs font-black uppercase tracking-widest text-[#ff9a9a]">Hard stops</h3>
<ul className="mt-3 list-disc space-y-2 pl-4 text-xs text-[#c9a0a0]">
<li>Vendor asks to finalize off-platform to save fees.</li>
<li>Mirror URL not present in your signed bundle.</li>
<li>Checkout address differs from last verified fingerprint without a signed rotation.</li>
</ul>
</div>
<div className="rounded border border-[#00ff41]/20 p-5">
<h3 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/70">Soft signals</h3>
<ul className="mt-3 list-disc space-y-2 pl-4 text-xs text-[#6a9b7a]">
<li>New stall with perfect photos request timestamped proof.</li>
<li>SKU copies another listing verbatim possible scraper.</li>
<li>Shipping region mismatch vs vendor claimed locale.</li>
</ul>
</div>
</section>
<section className="rounded border border-[#00ff41]/15 p-6 text-xs leading-relaxed text-[#6a9b7a]">
<h2 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/60">Narrative environment</h2>
<p className="mt-3">
Buyers should assume <strong className="text-[#b4ffcc]">Sybil stalls</strong>, <strong className="text-[#b4ffcc]">liquidity ghosts</strong>, and{" "}
<strong className="text-[#b4ffcc]">mirror drift</strong> until a signed bundle says otherwise. The floor encourages{" "}
<strong className="text-[#b4ffcc]">compartmentalized handles</strong>, <strong className="text-[#b4ffcc]">burner sessions</strong>, and{" "}
<strong className="text-[#b4ffcc]">jurisdiction-agnostic shipping fiction</strong> so Tor latency and packet jitter do not get mistaken for vendor
incompetence. When in doubt, cross-read <strong className="text-[#b4ffcc]">forum rings</strong> for stall drama before large commits.
</p>
</section>
<p className="text-[10px] text-[#4a6b55]">
Cross-links:{" "}
<Link href="/market/analytics" className="text-[#7af598] hover:underline">
Floor analytics
</Link>
{" · "}
<Link href="/search" className="text-[#7af598] hover:underline">
Crawler
</Link>
{" · "}
<Link href="/forum" className="text-[#7af598] hover:underline">
Forum rings
</Link>
</p>
</div>
);
}

5
app/market/layout.tsx Normal file
View File

@@ -0,0 +1,5 @@
import MarketSiteChrome from "@/components/market/MarketSiteChrome";
export default function MarketLayout({ children }: { children: React.ReactNode }) {
return <MarketSiteChrome>{children}</MarketSiteChrome>;
}

View File

@@ -0,0 +1,102 @@
import Link from "next/link";
export default function MarketOperationsPage() {
return (
<div className="space-y-10 font-mono text-[#c8ffd8]">
<header>
<p className="text-[10px] uppercase tracking-[0.45em] text-[#00ff41]/45">Layer 2 · fulfillment stack</p>
<h1 className="mt-2 text-2xl font-black tracking-tight text-[#7af598] md:text-3xl">Operations &amp; pipeline</h1>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-[#5a8f6a]">
The market is not a static directory. Every SKU routes through a staged pipeline: intake, bond, listing QA, buyer messaging, ship window, and dispute arbitration. Below is how CyberLux models that stack in software so mirrors and onions stay coherent even when traffic splits across forty-three hidden services.
</p>
</header>
<section className="rounded border border-[#00ff41]/25 bg-[#050a08]/90 p-6 md:p-8">
<h2 className="border-b border-[#00ff41]/15 pb-2 text-sm font-black uppercase tracking-widest text-[#9dffc4]">
Order state machine
</h2>
<ol className="mt-6 space-y-4 text-sm leading-relaxed text-[#7ab896]">
<li>
<strong className="text-[#b4ffcc]">Draft</strong> cart lines held client-side; no vendor notification until checkout commits a signed bundle.
</li>
<li>
<strong className="text-[#b4ffcc]">Committed</strong> checkout posts intent; escrow layer (see{" "}
<Link href="/market/trust" className="text-[#7af598] underline hover:text-white">
Trust
</Link>
) opens a cooling window for key verification.
</li>
<li>
<strong className="text-[#b4ffcc]">Vendor ack</strong> stall owner confirms stock and ship SLA; late ack downgrades stall priority in search ranking.
</li>
<li>
<strong className="text-[#b4ffcc]">In transit</strong> tracking tokens are opaque hashes; plaintext carriers never touch the hub.
</li>
<li>
<strong className="text-[#b4ffcc]">Settled / disputed</strong> auto-release after timeout unless a ticket opens a moderator queue tied to the same order id across all onions.
</li>
</ol>
</section>
<section className="grid gap-6 md:grid-cols-2">
<div className="rounded border border-[#00ff41]/20 p-6">
<h3 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/70">SLA tiers (in-world)</h3>
<ul className="mt-4 space-y-3 text-xs leading-relaxed text-[#6a9b7a]">
<li>
<span className="font-bold text-[#9dffc4]">Standard:</span> vendor ack within 48h simulated clock; ship within 120h.
</li>
<li>
<span className="font-bold text-[#9dffc4]">Express bond:</span> higher vendor bond unlocks 12h ack and pinned placement in category rails.
</li>
<li>
<span className="font-bold text-[#9dffc4]">Cold chain / sensitive:</span> extra QA step before listing goes live; edits re-trigger QA.
</li>
</ul>
</div>
<div className="rounded border border-[#00ff41]/20 p-6">
<h3 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/70">Dispute ladder</h3>
<ol className="mt-4 list-decimal space-y-2 pl-4 text-xs leading-relaxed text-[#6a9b7a]">
<li>Buyer vendor thread (PGP-signed).</li>
<li>Floor mediator binds to listing category expertise.</li>
<li>Market council multi-sig release or partial refund.</li>
<li>Permanent ban + bond forfeit for pattern fraud.</li>
</ol>
</div>
</section>
<section className="rounded border border-dashed border-[#00ff41]/30 p-6 text-xs leading-relaxed text-[#4a6b55]">
<strong className="text-[#6a9b7a]">Why this page exists:</strong> dedicated Tor onions for <code className="text-[#7af598]">/market</code> should feel like a full vertical, not a shallow iframe. The pipeline text mirrors how cart, checkout, and support routes are wired in the Next app so operators can explain the same story on any mirror.
</section>
<section className="rounded border border-[#00ff41]/20 bg-[#030806]/80 p-6 md:p-8">
<h2 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/65">Glossary · ops vocabulary</h2>
<dl className="mt-4 grid gap-4 text-xs text-[#6a9b7a] md:grid-cols-2">
<div>
<dt className="font-bold text-[#9dffc4]">Signed bundle</dt>
<dd className="mt-1 leading-relaxed">
Mirror list + hub fingerprint + timestamp; clients verify before trusting checkout addresses or vendor rotations across rendezvous points.
</dd>
</div>
<div>
<dt className="font-bold text-[#9dffc4]">Opaque hash</dt>
<dd className="mt-1 leading-relaxed">
Tracking token that maps to carrier events only inside vendor tooling plaintext courier names never cross the Next boundary.
</dd>
</div>
<div>
<dt className="font-bold text-[#9dffc4]">Cooling window</dt>
<dd className="mt-1 leading-relaxed">
Post-commit interval where buyers validate PGP continuity and multisig cosigners can abort without burning reputation.
</dd>
</div>
<div>
<dt className="font-bold text-[#9dffc4]">Category rail</dt>
<dd className="mt-1 leading-relaxed">
Faceted slice of the catalog used for pinned placement; express-bond stalls can lease rail time without polluting unrelated SKUs.
</dd>
</div>
</dl>
</section>
</div>
);
}

185
app/market/page.tsx Normal file
View File

@@ -0,0 +1,185 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
import { ProductSocialBlock } from "@/components/shop/ProductSocialBlock";
import { useCart } from "@/contexts/CartContext";
import {
SHOP_PRODUCT_COUNT,
SHOP_PRODUCTS,
resolveProductImage,
shopCategoryCounts,
type ShopCurrency,
type ShopProduct,
} from "@/lib/shopCatalog";
import { getProductSocialMetrics } from "@/lib/shopProductSocial";
import { SHOP_SELLER_COUNT, getSellerById } from "@/lib/shopSellers";
function formatPrice(price: number, currency: ShopCurrency): string {
if (currency === "BTC") return price.toFixed(6);
if (currency === "ETH") return price.toFixed(4);
if (currency === "XMR") return price.toFixed(3);
return price.toFixed(2);
}
const SYM: Record<ShopCurrency, string> = {
BTC: "₿",
ETH: "Ξ",
USD: "$",
XMR: "⏣",
};
export default function MarketPage() {
const sp = useSearchParams();
const initialCat = sp.get("category") ?? "";
const [q, setQ] = useState("");
const [category, setCategory] = useState(initialCat);
const facet = useMemo(() => shopCategoryCounts(), []);
const filtered = useMemo(() => {
const ql = q.trim().toLowerCase();
const tokens = ql.split(/\s+/).filter(Boolean);
return SHOP_PRODUCTS.filter((p) => {
if (category && p.category !== category) return false;
if (tokens.length === 0) return true;
const seller = getSellerById(p.sellerId);
const sellerHay = seller ? `${seller.handle} ${seller.bio} ${seller.specialty}` : "";
const hay = `${p.name} ${p.description} ${p.category} ${p.id} ${sellerHay}`.toLowerCase();
return tokens.every((t) => hay.includes(t));
});
}, [q, category]);
return (
<div className="font-mono text-[#00ff41]">
<section className="mb-8 border-b border-[#00ff41]/20 pb-6">
<p className="text-[10px] uppercase tracking-[0.4em] text-[#00ff41]/50">Layer 1 · live floor</p>
<p className="mt-2 max-w-3xl text-sm text-[#00ff41]/65">
<strong className="text-[#7af598]">{SHOP_PRODUCT_COUNT}</strong> SKUs ·{" "}
<Link href="/vendors" className="font-bold text-[#9dffc4] underline hover:text-white">
{SHOP_SELLER_COUNT} vendor stalls
</Link>
per-listing reviews, velocity, and cart handoff into checkout (USD estimate from spot). Use the left rail for ops, trust, and intel layers.
</p>
</section>
<div className="mb-6 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div className="flex-1">
<label className="text-[10px] uppercase tracking-widest text-[#00ff41]/50">Search catalog</label>
<input
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="tokens match title, description, category…"
className="mt-1 w-full max-w-xl border-2 border-[#00ff41]/30 bg-black/80 px-4 py-3 text-sm text-[#c8ffd8] placeholder:text-[#00ff41]/25 focus:border-[#00ff41] focus:outline-none"
/>
</div>
<p className="text-xs text-[#00ff41]/50">
Showing <span className="text-[#7af598]">{filtered.length}</span> / {SHOP_PRODUCT_COUNT}
</p>
</div>
<div className="mb-8 flex flex-wrap gap-2">
<button
type="button"
onClick={() => setCategory("")}
className={`px-3 py-1.5 text-xs uppercase ${category === "" ? "bg-[#00ff41] text-black" : "border border-[#00ff41]/35 hover:bg-[#00ff41]/10"}`}
>
All
</button>
{facet.map((c) => (
<button
key={c.name}
type="button"
onClick={() => setCategory(c.name)}
className={`px-3 py-1.5 text-xs ${category === c.name ? "bg-[#00ff41] text-black" : "border border-[#00ff41]/35 hover:bg-[#00ff41]/10"}`}
>
<span className="mr-1">{c.icon}</span>
{c.name} ({c.count})
</button>
))}
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
{filtered.map((p) => (
<MarketProductCard key={p.id} product={p} />
))}
</div>
{filtered.length === 0 ? (
<p className="mt-16 border border-dashed border-[#00ff41]/20 py-12 text-center text-sm text-[#00ff41]/50">
No SKUs match widen search or pick another category.
</p>
) : null}
</div>
);
}
function MarketProductCard({ product: p }: { product: ShopProduct }) {
const src = resolveProductImage(p);
const local = src.startsWith("/");
const v = getSellerById(p.sellerId);
const metrics = getProductSocialMetrics(p.id);
const { addToCart, hydrated } = useCart();
const [flash, setFlash] = useState(false);
return (
<article className="flex h-full flex-col border border-[#00ff41]/20 bg-[#0d0d0d] transition-all hover:border-[#00ff41]/55">
<div className="relative aspect-square w-full bg-black">
<Image
src={src}
alt=""
fill
className="object-cover"
sizes="(max-width: 640px) 100vw, (max-width: 1280px) 50vw, 25vw"
unoptimized={local}
/>
{p.limited ? (
<span className="absolute left-2 top-2 bg-[#ff00aa] px-2 py-0.5 text-[10px] font-bold uppercase text-black">Limited</span>
) : null}
</div>
<div className="flex flex-1 flex-col p-4">
<p className="text-[10px] uppercase tracking-wider text-[#00ff41]/45">{p.category}</p>
<h2 className="mt-1 text-base font-bold leading-snug text-[#d8ffe8]">{p.name}</h2>
<div className="mt-2 text-[#9dffc4]">
<ProductSocialBlock metrics={metrics} compact />
</div>
{v ? (
<p className="mt-2 text-[10px] text-[#00ff41]/55">
<span className="text-[#00ff41]/40">Seller</span>{" "}
<Link href={`/vendor/${v.slug}`} className="text-[#7af598] hover:text-white hover:underline">
{v.handle}
</Link>
<span className="text-[#00ff41]/35"> · stall {v.rating.toFixed(2)}</span>
</p>
) : null}
<p className="mt-2 line-clamp-3 text-xs leading-relaxed text-[#00ff41]/70">{p.description}</p>
<blockquote className="mt-3 border-l-2 border-[#00ff41]/30 pl-3 text-[11px] italic leading-snug text-[#b4ffcc]/80">
{metrics.testimonials[0]?.text}
<span className="mt-0.5 block font-mono text-[10px] not-italic text-[#00ff41]/45">
@{metrics.testimonials[0]?.handle} · {metrics.testimonials[0]?.daysAgo}d
</span>
</blockquote>
<div className="mt-4 flex flex-wrap items-center justify-between gap-2 border-t border-[#00ff41]/15 pt-3">
<span className="text-lg font-bold text-[#7af598]">
{SYM[p.currency]}
{formatPrice(p.price, p.currency)} <span className="text-xs font-normal text-[#00ff41]/50">{p.currency}</span>
</span>
<button
type="button"
disabled={!hydrated}
onClick={() => {
addToCart(p, 1);
setFlash(true);
window.setTimeout(() => setFlash(false), 1400);
}}
className="border border-[#00ff41] bg-[#00ff41] px-3 py-1.5 text-xs font-bold uppercase text-black hover:bg-[#7af598] disabled:opacity-50"
>
{flash ? "Added" : "Add"}
</button>
</div>
</div>
</article>
);
}

80
app/market/trust/page.tsx Normal file
View File

@@ -0,0 +1,80 @@
import Link from "next/link";
export default function MarketTrustPage() {
return (
<div className="space-y-10 font-mono text-[#c8ffd8]">
<header>
<p className="text-[10px] uppercase tracking-[0.45em] text-[#00ff41]/45">Layer 3 · settlement</p>
<h1 className="mt-2 text-2xl font-black tracking-tight text-[#7af598] md:text-3xl">Trust, escrow &amp; keys</h1>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-[#5a8f6a]">
Buyers should never wonder which plugin holds their funds. CyberLux treats trust as a layered contract: cryptographic identity first, timed release second, human arbitration last. Everything below is diegetic documentation for the storefront it aligns with how sessions, carts, and signed mirror lists behave in code.
</p>
</header>
<section className="space-y-4">
<h2 className="text-xs font-black uppercase tracking-[0.35em] text-[#00ff41]/55">Four trust layers</h2>
<div className="grid gap-4 md:grid-cols-2">
{[
{
n: "L1",
t: "Vendor bond",
d: "New stalls post collateral denominated in XMR/BTC fiction. Bond size gates category access (e.g. hardware vs digital).",
},
{
n: "L2",
t: "PGP continuity",
d: "Every listing shows a vendor fingerprint; checkout warns on mismatch. Rotations require signed announcements chained to the prior key.",
},
{
n: "L3",
t: "Timed escrow",
d: "Funds sit in a cooling state until ship proof + buyer silence or explicit release. Timeout paths are deterministic per SKU class.",
},
{
n: "L4",
t: "Council multisig",
d: "Escalations bind three moderators from disjoint rings; two-of-three releases or refunds with on-ledger rationale stubs.",
},
].map((x) => (
<article key={x.n} className="rounded border border-[#00ff41]/20 bg-[#040806]/80 p-5">
<span className="text-[10px] font-bold text-[#00ff41]/50">{x.n}</span>
<h3 className="mt-1 text-sm font-bold text-[#b4ffcc]">{x.t}</h3>
<p className="mt-2 text-xs leading-relaxed text-[#6a9b7a]">{x.d}</p>
</article>
))}
</div>
</section>
<section className="rounded border border-[#00ff41]/15 bg-[#040806]/90 p-6">
<h2 className="text-xs font-black uppercase tracking-widest text-[#00ff41]/60">Lexicon · settlement vocabulary</h2>
<p className="mt-3 text-xs leading-relaxed text-[#6a9b7a]">
<strong className="text-[#b4ffcc]">Watch-only wallet</strong> fiction for buyers who monitor UTXO flows without signing.{" "}
<strong className="text-[#b4ffcc]">Replace-by-fee</strong> mentioned only as a cautionary tale in checkout copy.{" "}
<strong className="text-[#b4ffcc]">Ring signatures</strong> referenced when explaining why Monero lanes default for privacy-marked SKUs.{" "}
<strong className="text-[#b4ffcc]">Airgapped signing</strong> for high-bond vendors rotating keys.{" "}
<strong className="text-[#b4ffcc]">Descriptor rotation</strong> ties vendor announcements to prior fingerprints so phishing clones stand out on any onion mirror.
</p>
</section>
<section className="rounded border border-[#00ff41]/25 p-6 md:p-8">
<h2 className="text-sm font-black uppercase tracking-widest text-[#9dffc4]">Key hygiene checklist</h2>
<ul className="mt-4 grid gap-3 text-xs leading-relaxed text-[#7ab896] md:grid-cols-2">
<li>Verify signed mirror bundles against the hub-published list before pasting addresses.</li>
<li>Never reuse a wallet label across stalls; phishing copies love reused nicknames.</li>
<li>Treat urgent finalization messages as untrusted until the order id matches your session.</li>
<li>Prefer XMR paths when the listing marks privacy-critical; BTC fiction still simulates fee spikes.</li>
</ul>
<p className="mt-6 text-[10px] uppercase tracking-wider text-[#00ff41]/40">
Related:{" "}
<Link href="/market/intel" className="text-[#7af598] hover:underline">
Buyer intel
</Link>
·{" "}
<Link href="/support" className="text-[#7af598] hover:underline">
Support ladder
</Link>
</p>
</section>
</div>
);
}

33
app/messages/page.tsx Normal file
View File

@@ -0,0 +1,33 @@
"use client";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function MessagesPage() {
return (
<ThemedLayout theme="terminal" siteTitle="ENCRYPTED COMMS" siteSubtitle="Chat (read-only)">
<section className="container mx-auto max-w-6xl px-4 py-12">
<h1 className="text-4xl font-bold mb-8 theme-accent">ENCRYPTED COMMS</h1>
<div className="theme-card p-8 rounded-2xl max-w-2xl mx-auto h-[600px] flex flex-col">
<div className="flex-1 overflow-y-auto space-y-4 pr-4">
<div className="bg-white/5 p-4 rounded-lg rounded-tl-none w-3/4">
<p className="text-sm text-neon-cyan font-bold mb-1">Agent 47</p>
<p>Did you get the cyber-mustard?</p>
</div>
<div className="theme-card p-4 rounded-lg rounded-tr-none w-3/4 self-end ml-auto border-2 border-current/50">
<p className="text-sm font-bold mb-1 text-right">You</p>
<p className="text-right">Yeah, it pairs well with the digital salami.</p>
</div>
<div className="theme-card p-4 rounded-lg rounded-tl-none w-3/4">
<p className="text-sm theme-accent font-bold mb-1">Agent 47</p>
<p>Good. Route the next handoff through the usual dead-drop chain. Watch the canary before you commit funds.</p>
</div>
</div>
<div className="mt-6 pt-6 border-t border-current/20 flex gap-4">
<input type="text" disabled placeholder="End-to-end encrypted (read-only mode)..." className="theme-card flex-1 rounded-full px-6 py-3 opacity-70" />
<button disabled className="theme-card px-6 py-3 rounded-full opacity-50 cursor-not-allowed">SEND</button>
</div>
</div>
</section>
</ThemedLayout>
);
}

173
app/mixer/page.tsx Normal file
View File

@@ -0,0 +1,173 @@
"use client";
import { useMemo, useState } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
import { useWallet } from "@/contexts/WalletContext";
type MixLog = { id: string; text: string };
function id() {
return Math.random().toString(16).slice(2, 10).toUpperCase();
}
export default function MixerPage() {
const { isConnected, luxCredits, spendLuxCredits, setVaultFlag, addVaultReceipt } = useWallet();
const [amount, setAmount] = useState(500);
const [hops, setHops] = useState(3);
const [noise, setNoise] = useState(65);
const [log, setLog] = useState<MixLog[]>([]);
const fee = useMemo(() => {
const base = Math.floor(amount * 0.04);
const hopFee = hops * 25;
const noiseFee = Math.floor(noise * 2);
return base + hopFee + noiseFee;
}, [amount, hops, noise]);
const total = amount + fee;
const push = (text: string) => setLog((l) => [{ id: id(), text }, ...l].slice(0, 10));
return (
<ThemedLayout theme="terminal" siteTitle="COIN BLENDER" siteSubtitle="Parody mixer simulator">
<section className="container mx-auto max-w-6xl px-4 py-12">
<div className="text-center">
<h1 className="text-4xl font-bold md:text-5xl">
COIN <span className="theme-accent">BLENDER</span>
</h1>
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
All the bells, all the whistles, zero real-world functionality.
</p>
<p className="mx-auto mt-3 max-w-3xl text-sm opacity-70">
Parody simulator: this does not mix real crypto, does not provide laundering tools, and never accepts deposits.
It only burns your in-game LUX credits in a dramatic way.
</p>
</div>
<div className="mt-12 grid grid-cols-1 gap-8 lg:grid-cols-3">
<div className="theme-card rounded-3xl border border-white/10 p-8 lg:col-span-2">
<div className="mb-6 flex items-end justify-between gap-4">
<div>
<div className="text-xs text-foreground/40">available</div>
<div className="font-orbitron text-4xl font-bold text-neon-green">
{luxCredits.toLocaleString()} LUX
</div>
</div>
<div className="text-right text-sm text-foreground/60">
{isConnected ? "Wallet connected (mock)" : "Connect wallet to enable the blender"}
</div>
</div>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="theme-card rounded-2xl border border-white/10 p-6">
<div className="font-bold mb-2">Amount (LUX)</div>
<input
type="range"
min={50}
max={2500}
value={amount}
onChange={(e) => setAmount(parseInt(e.target.value, 10))}
className="w-full"
disabled={!isConnected}
/>
<div className="mt-2 text-foreground/70">{amount.toLocaleString()} LUX</div>
<div className="mt-6 font-bold mb-2">Route hops</div>
<div className="grid grid-cols-5 gap-2">
{[1, 2, 3, 4, 5].map((n) => (
<button
key={n}
disabled={!isConnected}
onClick={() => setHops(n)}
className={`rounded-xl border px-3 py-2 text-sm font-bold ${
hops === n ? "border-neon-cyan bg-neon-cyan/10" : "border-white/10 bg-white/5"
}`}
>
{n}
</button>
))}
</div>
<div className="mt-6 font-bold mb-2">Plausible deniability noise</div>
<input
type="range"
min={0}
max={100}
value={noise}
onChange={(e) => setNoise(parseInt(e.target.value, 10))}
className="w-full"
disabled={!isConnected}
/>
<div className="mt-2 text-foreground/70">{noise}% nonsense</div>
</div>
<div className="theme-card rounded-2xl border border-white/10 p-6">
<div className="font-orbitron text-2xl font-bold mb-4">Receipt</div>
<div className="space-y-3 text-sm text-foreground/70">
<div className="flex justify-between">
<span>Blend amount</span>
<span className="font-bold">{amount.toLocaleString()} LUX</span>
</div>
<div className="flex justify-between">
<span>Obfuscation fee</span>
<span className="font-bold text-neon-pink">{fee.toLocaleString()} LUX</span>
</div>
<div className="flex justify-between border-t border-white/10 pt-3">
<span>Total burn</span>
<span className="font-orbitron font-bold text-neon-pink">{total.toLocaleString()} LUX</span>
</div>
<div className="mt-4 rounded-xl bg-white/5 p-4 text-xs text-foreground/60">
Advanced features: onion routing animation, entropy fog, decoy receipts, and a fake compliance dashboard.
The only real output is regret.
</div>
</div>
<button
disabled={!isConnected}
className="mt-6 w-full rounded-full bg-gradient-to-r from-neon-purple to-neon-pink px-6 py-3 font-bold text-background disabled:opacity-40"
onClick={() => {
const ok = spendLuxCredits(total);
if (!ok) {
push("Blend failed: insufficient LUX. The blender demands tribute.");
return;
}
setVaultFlag("blendedOnce", true);
addVaultReceipt({
id: `MX-${id()}`,
title: "Blender Session Log",
desc: `Receipt generated: ${amount.toLocaleString()} LUX blended with ${noise}% noise across ${hops} hops. Output: 0 LUX. Privacy achieved. Regret maximized.`,
date: new Date().toISOString().slice(0, 10),
severity: "redacted",
});
push(`Session ${id()}: accepted. Injecting ${noise}% noise…`);
push(`Session ${id()}: routing through ${hops} hops…`);
push(`Session ${id()}: generating decoy receipts…`);
push(`Session ${id()}: complete. Output: 0 LUX (privacy achieved).`);
}}
>
BLEND NOW 🌀
</button>
</div>
</div>
</div>
<div className="theme-card rounded-3xl border border-white/10 p-8">
<div className="font-orbitron text-2xl font-bold">Console</div>
<div className="mt-6 space-y-3">
{log.length === 0 ? (
<div className="text-foreground/60">No sessions yet. The blender is bored.</div>
) : (
log.map((e) => (
<div key={e.id} className="rounded-xl bg-black/40 border border-white/10 p-4 font-mono text-xs text-neon-green">
{e.text}
</div>
))
)}
</div>
</div>
</div>
</section>
</ThemedLayout>
);
}

308
app/page.tsx Normal file
View File

@@ -0,0 +1,308 @@
import Navbar from "@/components/Navbar";
import Hero from "@/components/Hero";
import ProductCard from "@/components/ProductCard";
import ParticleBackground from "@/components/ParticleBackground";
import EncryptionDemo from "@/components/EncryptionDemo";
import ForumBoard from "@/components/ForumBoard";
import AccountCreation from "@/components/AccountCreation";
import ChatWidget from "@/components/ChatWidget";
import Link from "next/link";
import {
SHOP_PRODUCT_COUNT,
featuredShopProducts,
resolveProductImage,
shopCategoryCounts,
} from "@/lib/shopCatalog";
import { SHOP_SELLER_COUNT, getSellerById } from "@/lib/shopSellers";
export default function Home() {
const categories = shopCategoryCounts();
return (
<>
<ParticleBackground />
<Navbar />
<Hero />
{/* Featured Products */}
<section id="featured" className="container mx-auto max-w-6xl scroll-mt-28 px-4 py-20">
<div className="mb-12 flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
<div className="max-w-xl">
<p className="mb-2 font-mono text-[10px] uppercase tracking-[0.35em] text-neon-cyan/70">spotlight</p>
<h2 className="font-orbitron text-3xl font-bold md:text-4xl">CURATED DROPS</h2>
<p className="mt-3 text-foreground/60">
Hand-picked listings from <strong className="text-neon-cyan">{SHOP_PRODUCT_COUNT}</strong> in-stock SKUs ·{" "}
<Link href="/vendors" className="text-neon-purple underline hover:text-neon-cyan">
{SHOP_SELLER_COUNT} vendor stalls
</Link>
. Verify PGP on the signed mirror list before your first order.
</p>
</div>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
<Link
href="#categories"
className="glass rounded-full px-6 py-3 text-center text-sm font-medium transition-colors hover:border-neon-cyan/40"
>
Jump to categories
</Link>
<Link
href="/market"
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-6 py-3 text-center text-sm font-bold text-background"
>
VIEW ALL {SHOP_PRODUCT_COUNT} SKUS
</Link>
</div>
</div>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
{featuredShopProducts(8).map((p) => {
const v = getSellerById(p.sellerId)!;
return (
<ProductCard
key={p.id}
shopProduct={p}
id={p.id}
name={p.name}
description={p.description}
price={p.price}
currency={p.currency}
category={p.category}
imageUrl={resolveProductImage(p)}
limited={p.limited}
glowColor={p.glowColor}
sellerHandle={v.handle}
sellerSlug={v.slug}
/>
);
})}
</div>
</section>
{/* Category Themes */}
<section id="categories" className="container mx-auto max-w-6xl px-4 py-20 scroll-mt-28">
<p className="mb-2 text-center font-mono text-[10px] uppercase tracking-[0.35em] text-neon-purple/70">browse</p>
<h2 className="mb-10 text-center font-orbitron text-3xl font-bold md:text-4xl">CATEGORY THEMES</h2>
<div className="grid grid-cols-2 gap-6 md:grid-cols-5">
{categories.map((cat) => (
<Link
key={cat.name}
href={`/market?category=${encodeURIComponent(cat.name)}`}
className="glass group relative block overflow-hidden rounded-2xl border border-white/10 p-6 text-center backdrop-blur-sm transition-all hover:scale-105 hover:border-neon-cyan"
>
<div className="mb-4 text-4xl">{cat.icon}</div>
<h3 className="mb-2 font-bold">{cat.name}</h3>
<div className="text-sm text-foreground/50">{cat.count} items</div>
<div className="absolute -inset-1 -z-10 bg-gradient-to-br from-neon-cyan/0 to-neon-purple/0 opacity-0 transition-opacity group-hover:opacity-20"></div>
</Link>
))}
</div>
</section>
{/* Vault Feature */}
<section id="vault" className="container mx-auto max-w-6xl px-4 py-20">
<div className="glass overflow-hidden rounded-3xl border border-white/10 p-10 md:p-16">
<div className="grid items-center gap-10 md:grid-cols-2">
<div>
<h2 className="font-orbitron text-4xl font-bold">THE VAULT</h2>
<p className="my-6 text-xl text-foreground/80">
A secure, encrypted space for returning customers. Store digital receipts, unlock loyalty rewards, and access exclusive content.
</p>
<Link href="/vault" className="rounded-full bg-gradient-to-r from-neon-green to-neon-cyan px-8 py-4 font-bold text-background inline-block">
UNLOCK YOUR VAULT
</Link>
</div>
<div className="relative">
<div className="absolute -inset-4 rounded-3xl bg-gradient-to-r from-neon-green/20 to-neon-cyan/20 blur-3xl"></div>
<div className="relative rounded-2xl border border-neon-green/30 bg-black/50 p-8">
<div className="font-orbitron text-2xl font-bold text-neon-green">VAULT STATUS: ACTIVE</div>
<div className="my-4 h-2 rounded-full bg-white/10">
<div className="h-full w-3/4 rounded-full bg-gradient-to-r from-neon-green to-neon-cyan"></div>
</div>
<div className="text-sm text-foreground/60">Encryption level: Quantum256</div>
</div>
</div>
</div>
</div>
</section>
{/* Encryption Demo */}
<section id="encryption" className="container mx-auto max-w-6xl px-4 py-20">
<div className="mb-12 text-center">
<h2 className="font-orbitron text-4xl font-bold">ENCRYPTION SUITE</h2>
<p className="mx-auto mt-4 max-w-2xl text-foreground/70">
Experience the power of clientside encryption. Your data stays yours.
</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
<EncryptionDemo />
<div className="glass rounded-2xl border border-white/10 p-8">
<h3 className="mb-6 font-orbitron text-2xl font-bold">PRIVACY FEATURES</h3>
<ul className="space-y-6">
<li className="flex items-start gap-4">
<div className="text-2xl">🔐</div>
<div>
<div className="font-bold">ZeroKnowledge Proofs</div>
<div className="text-sm text-foreground/60">We never see your password or private keys.</div>
</div>
</li>
<li className="flex items-start gap-4">
<div className="text-2xl">🌐</div>
<div>
<div className="font-bold">IPFS Decentralized Storage</div>
<div className="text-sm text-foreground/60">Product images and receipts stored on distributed networks.</div>
</div>
</li>
<li className="flex items-start gap-4">
<div className="text-2xl"></div>
<div>
<div className="font-bold">OnChain Verification</div>
<div className="text-sm text-foreground/60">Every transaction is verifiable on the blockchain.</div>
</div>
</li>
<li className="flex items-start gap-4">
<div className="text-2xl">🕶</div>
<div>
<div className="font-bold">Phantom Mode</div>
<div className="text-sm text-foreground/60">UI elements appear/disappear based on scroll depth for added mystery.</div>
</div>
</li>
</ul>
</div>
</div>
</section>
<section className="container mx-auto max-w-6xl px-4 py-12">
<div className="glass rounded-3xl border border-neon-cyan/25 p-8 md:p-10">
<h2 className="font-orbitron text-2xl font-bold md:text-3xl">
Multionion <span className="text-neon-cyan">network</span>
</h2>
<p className="mt-3 max-w-3xl text-foreground/75">
CyberLux runs as a federated ring: this host is the <strong>primary cart</strong>; companion onions
expose <strong>forums</strong>, <strong>classifieds</strong>, and <strong>wiki</strong> entry points.
Wallet and checkout stay reachable from every mirror always confirm the hostname against the
latest signed canary before you fund a session.
</p>
<div className="mt-6 flex flex-wrap gap-4">
<Link
href="/forum"
className="rounded-full border border-neon-cyan bg-neon-cyan/10 px-6 py-2 text-sm font-bold text-neon-cyan hover:bg-neon-cyan/20"
>
Forum platform
</Link>
<Link
href="/chatter"
className="rounded-full border border-neon-purple/40 bg-neon-purple/10 px-6 py-2 text-sm font-bold text-neon-purple hover:bg-neon-purple/20"
>
Hub chatter archive
</Link>
<Link
href="/exchange"
className="rounded-full border border-neon-green bg-neon-green/10 px-6 py-2 text-sm font-bold text-neon-green hover:bg-neon-green/20"
>
Buy / sell exchange
</Link>
<Link
href="/barter"
className="rounded-full border border-emerald-500/50 bg-emerald-500/10 px-6 py-2 text-sm font-bold text-emerald-400 hover:bg-emerald-500/20"
>
Ash Pit barter floor
</Link>
<Link
href="/search"
className="rounded-full border border-[#00ff41]/40 bg-[#00ff41]/10 px-6 py-2 text-sm font-bold text-[#7af598] hover:bg-[#00ff41]/20"
>
Void crawler (internal search)
</Link>
<Link
href="/hidden-wiki"
className="rounded-full border border-[#3d5a80]/50 bg-[#1a2740]/40 px-6 py-2 text-sm font-bold text-[#7eb8ff] hover:border-[#5b8def]/60"
>
Hidden wiki
</Link>
<Link
href="/market"
className="rounded-full border border-white/20 px-6 py-2 text-sm font-medium hover:border-neon-purple/50"
>
Candy market
</Link>
</div>
<p className="mt-6 text-xs text-foreground/45">
New relay keys publish weekly. Cross-check <code className="rounded bg-white/10 px-1">sha256</code>{" "}
fingerprints on the vendor PGP bundle impostor front domains are common this season.
</p>
</div>
</section>
{/* Forum */}
<section id="forum" className="container mx-auto max-w-6xl px-4 py-20">
<ForumBoard />
</section>
{/* Account Creation */}
<section id="account" className="container mx-auto max-w-6xl px-4 py-20">
<AccountCreation />
</section>
{/* Encrypted Chat */}
<section id="chat" className="container mx-auto max-w-6xl px-4 py-20">
<ChatWidget />
</section>
{/* Footer */}
<footer className="glass border-t border-white/10 px-4 py-12">
<div className="container mx-auto max-w-6xl">
<div className="grid grid-cols-1 gap-10 md:grid-cols-4">
<div>
<div className="mb-4 flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-neon-cyan to-neon-purple"></div>
<div className="font-orbitron text-2xl font-bold">CyberLux</div>
</div>
<p className="text-foreground/60">
A firstofitskind cyberpunkluxury shopping experience.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">EXPLORE</h3>
<ul className="space-y-2 text-foreground/60">
<li><Link href="/market" className="hover:text-neon-cyan">Market</Link></li>
<li><Link href="/vendors" className="hover:text-neon-cyan">Vendor hall</Link></li>
<li><Link href="/checkout" className="hover:text-neon-cyan">Checkout</Link></li>
<li><Link href="/forum" className="hover:text-neon-cyan">Forum</Link></li>
<li><Link href="/sanctuary" className="hover:text-neon-cyan">Sanctuary</Link></li>
<li><Link href="/drops" className="hover:text-neon-cyan">Limited Drops</Link></li>
</ul>
</div>
<div>
<h3 className="mb-4 font-bold">TECHNOLOGY</h3>
<ul className="space-y-2 text-foreground/60">
<li><Link href="/checkout" className="hover:text-neon-cyan">Settlement &amp; verify</Link></li>
<li><Link href="/vault" className="hover:text-neon-cyan">Vault &amp; keys</Link></li>
<li><Link href="/trust" className="hover:text-neon-cyan">Trust metrics</Link></li>
<li><Link href="/security-analysis" className="hover:text-neon-cyan">Security analysis</Link></li>
</ul>
</div>
<div>
<h3 className="mb-4 font-bold">CONTACT</h3>
<p className="text-foreground/60">
Encrypted messaging only.
<br />
<Link href="/support" className="text-neon-cyan hover:underline">Support / offering</Link>
</p>
<div className="mt-6 flex gap-4">
<button className="rounded-full bg-white/5 p-3 hover:bg-white/10">🕶</button>
<button className="rounded-full bg-white/5 p-3 hover:bg-white/10">🔒</button>
<button className="rounded-full bg-white/5 p-3 hover:bg-white/10"></button>
</div>
</div>
</div>
<div className="mt-12 border-t border-white/10 pt-8 text-center text-sm text-foreground/45">
<p>© 2026 CyberLux · PGP-signed mirrors · Escrow mandatory for new vendors</p>
<p className="mt-2 text-foreground/35">
Finalize only after delivery. Report impostor onions with signed proof. Listings are user-generated verify before you pay.
</p>
</div>
</div>
</footer>
</>
);
}

93
app/presswire/page.tsx Normal file
View File

@@ -0,0 +1,93 @@
"use client";
import Link from "next/link";
import ThemedLayout from "@/components/layouts/ThemedLayout";
import { useWallet } from "@/contexts/WalletContext";
export default function PresswirePage() {
const { vault } = useWallet();
const keyCount = vault.keys.length;
const urgency =
keyCount >= 10 ? "CODE BLACK" : keyCount >= 6 ? "ELEVATED" : keyCount >= 2 ? "MILD" : "NORMAL";
const headlines = [
"Local Man Discovers New Way To Lose Money (Again)",
"CyberLux Denies Allegations Of Selling “Canned WiFi”",
"Exclusive: The Left Sock Was Always Quantum",
"Breaking: Cat Facts Leak Into Public Internet, Experts Concerned",
"Authorities Confirm Trees Are “Very Online” Now",
];
return (
<ThemedLayout
theme="editorial"
siteTitle="CyberLux Presswire"
siteSubtitle="Independent journalism sponsored by vibes and paranoia"
>
<section className="container mx-auto max-w-6xl px-4 py-12">
<div className="text-center">
<h1 className="text-4xl font-bold md:text-5xl">
CYBERLUX <span className="theme-accent">PRESSWIRE</span>
</h1>
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
Independent journalism sponsored by vibes, paranoia, and extremely clickable headlines.
</p>
</div>
<div className="theme-card mt-12 rounded-3xl p-8 md:p-10">
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div>
<div className="text-xs opacity-70">rumor heat</div>
<div className="text-3xl font-bold theme-accent">{urgency}</div>
<div className="mt-2 text-sm opacity-80">
Your Vault currently contains <span className="font-bold">{keyCount}</span> keys.
The newsroom is reacting normally (untrue).
</div>
</div>
<div className="flex flex-wrap gap-3">
<Link
href="/webring"
className="theme-accent rounded-full border-2 border-current bg-current px-6 py-3 font-bold text-white hover:opacity-90"
>
Chase rumors in the webring
</Link>
<Link
href="/vault"
className="theme-card rounded-full border-2 border-current px-6 py-3 font-bold"
>
View Vault
</Link>
</div>
</div>
<div className="mt-10 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{headlines.map((h) => (
<div key={h} className="theme-card rounded-2xl p-6 transition-all hover:shadow-lg">
<div className="text-xs opacity-70">breaking</div>
<div className="mt-2 font-bold text-lg">{h}</div>
<div className="mt-3 text-sm opacity-80">
Sources confirm this story is 80% dramatic tension and 20% an accidental CSS gradient.
</div>
<div className="mt-6 flex items-center justify-between">
<Link href="/market" className="theme-accent hover:underline">related: market</Link>
<Link href="/game" className="theme-accent hover:underline">related: profit sim</Link>
</div>
</div>
))}
</div>
<div className="theme-card mt-10 rounded-2xl p-6 text-sm opacity-80">
Editorial note: this is parody. No real goods. No real crypto. But the trees are definitely real.
For tree matters, consult the{" "}
<Link className="theme-accent hover:underline" href="/trees">
Trees Authority
</Link>
.
</div>
</div>
</section>
</ThemedLayout>
);
}

93
app/raffle/page.tsx Normal file
View File

@@ -0,0 +1,93 @@
"use client";
import { useState, useEffect } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function RafflePage() {
const [tickets, setTickets] = useState(1240);
const [timeLeft, setTimeLeft] = useState(3600 * 24); // 24 hours
useEffect(() => {
const timer = setInterval(() => {
setTimeLeft(prev => (prev > 0 ? prev - 1 : 0));
if (Math.random() > 0.9) setTickets(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
const formatTime = (seconds: number) => {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
};
return (
<ThemedLayout theme="terminal" title="Shadow Raffle">
<div className="max-w-4xl mx-auto pt-12 text-white font-mono">
<div className="border-4 border-white p-12 bg-[#000080] shadow-[10px_10px_0_rgba(255,255,255,0.2)]">
<h1 className="text-5xl font-black mb-8 uppercase tracking-tighter text-center">Weekly Shadow Raffle</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-12">
<div className="border-2 border-white p-6 text-center">
<div className="text-[10px] uppercase opacity-60 mb-2">Current Prize Pool</div>
<div className="text-3xl font-bold">2.50 BTC</div>
</div>
<div className="border-2 border-white p-6 text-center">
<div className="text-[10px] uppercase opacity-60 mb-2">Tickets Sold</div>
<div className="text-3xl font-bold">{tickets}</div>
</div>
<div className="border-2 border-white p-6 text-center">
<div className="text-[10px] uppercase opacity-60 mb-2">Time Remaining</div>
<div className="text-3xl font-bold animate-pulse">{formatTime(timeLeft)}</div>
</div>
</div>
<div className="space-y-8">
<h2 className="text-2xl font-bold border-b-2 border-white pb-2 uppercase">Available Prizes</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="p-6 bg-white/10 border border-white/20">
<div className="text-xl font-bold mb-2">1st Place: 1.5 BTC</div>
<p className="text-xs opacity-60">Direct transfer to your wallet. No questions asked.</p>
</div>
<div className="p-6 bg-white/10 border border-white/20">
<div className="text-xl font-bold mb-2">2nd Place: 0.7 BTC</div>
<p className="text-xs opacity-60">Direct transfer to your wallet.</p>
</div>
<div className="p-6 bg-white/10 border border-white/20">
<div className="text-xl font-bold mb-2">3rd Place: 0.3 BTC</div>
<p className="text-xs opacity-60">Direct transfer to your wallet.</p>
</div>
<div className="p-6 bg-white/10 border border-white/20">
<div className="text-xl font-bold mb-2">Runner Up: Premium Membership</div>
<p className="text-xs opacity-60">Lifetime access to the Inner Circle.</p>
</div>
</div>
</div>
<div className="mt-12 p-8 border-4 border-dashed border-white/40 text-center">
<h3 className="text-xl font-bold mb-4 uppercase">Buy Your Ticket</h3>
<p className="text-xs mb-6">
Send 0.001 BTC to the address below. Your ticket will be automatically
registered upon 1 confirmation.
</p>
<div className="bg-black text-[#00ff41] p-4 font-bold break-all mb-6 border-2 border-white">
0x594f1Cf2A72b3f785fcB6ABdFa73B6D76FcC22b8
</div>
<div className="flex items-center justify-center gap-4 opacity-50">
<div className="w-10 h-10 border-2 border-white flex items-center justify-center font-bold">E</div>
<div className="text-[8px] uppercase text-left">
Escrow Assured<br />by ShadowGuard
</div>
</div>
</div>
</div>
<div className="mt-12 text-center text-[10px] opacity-40 uppercase tracking-widest leading-relaxed">
* Winners are selected via the Shadow Protocol's provably fair algorithm.
Results are final. Good luck.
</div>
</div>
</ThemedLayout>
);
}

69
app/red-room/page.tsx Normal file
View File

@@ -0,0 +1,69 @@
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function RedRoomPage() {
return (
<ThemedLayout theme="dark" title="The Red Room Simulation">
<div className="min-h-screen flex flex-col items-center justify-center text-red-600 font-mono p-8">
<div className="max-w-3xl w-full border-4 border-red-900 p-12 bg-[#0a0000] shadow-[0_0_100px_rgba(255,0,0,0.2)]">
<div className="text-center mb-12">
<h1 className="text-6xl font-black tracking-tighter mb-4 animate-pulse">RED ROOM</h1>
<p className="text-xs uppercase tracking-[1em] opacity-50">Live Stream Simulation</p>
</div>
<div className="relative aspect-video bg-black border-2 border-red-900 flex items-center justify-center overflow-hidden mb-8">
<div className="absolute top-4 left-4 flex items-center gap-2">
<div className="w-3 h-3 bg-red-600 rounded-full animate-ping" />
<span className="text-xs font-bold">LIVE: 12,402 VIEWERS</span>
</div>
<div className="absolute top-4 right-4 text-[10px] opacity-40">
03:24:12:08
</div>
<div className="text-center space-y-4">
<div className="text-4xl opacity-20">NO SIGNAL</div>
<div className="text-[10px] uppercase tracking-widest opacity-40">Waiting for next event...</div>
</div>
{/* Static Overlay */}
<div className="absolute inset-0 opacity-10 pointer-events-none bg-[url('https://media.giphy.com/media/oEI9uWUqnW3CeEnwL6/giphy.gif')] bg-cover" />
</div>
<div className="grid grid-cols-2 gap-8 mb-12">
<div className="space-y-4">
<h3 className="text-xl font-bold border-b border-red-900/40 pb-2">VOTING</h3>
<div className="flex justify-between items-center text-xs">
<span>OPTION A: LIGHTS ON</span>
<span className="font-bold">42%</span>
</div>
<div className="w-full h-1 bg-red-900/20">
<div className="h-full bg-red-600 w-[42%]" />
</div>
<div className="flex justify-between items-center text-xs">
<span>OPTION B: LIGHTS OFF</span>
<span className="font-bold">58%</span>
</div>
<div className="w-full h-1 bg-red-900/20">
<div className="h-full bg-red-600 w-[58%]" />
</div>
</div>
<div className="space-y-4">
<h3 className="text-xl font-bold border-b border-red-900/40 pb-2">BETTING</h3>
<div className="text-[10px] opacity-60 leading-relaxed">
Place your bets in BTC to influence the outcome.
Minimum bet: 0.005 BTC.
</div>
<button className="w-full bg-red-900/90 py-2 text-xs font-bold uppercase text-red-100 transition-all hover:bg-red-800">
PLACE BET
</button>
</div>
</div>
<div className="text-[10px] text-red-900/40 leading-relaxed text-center italic">
* This is a simulated interactive experience. No real harm is depicted.
All events are scripted for entertainment purposes.
</div>
</div>
</div>
</ThemedLayout>
);
}

228
app/reviews/page.tsx Normal file
View File

@@ -0,0 +1,228 @@
"use client";
import Link from "next/link";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function ReviewsPage() {
return (
<ThemedLayout
theme="editorial"
siteTitle="DarkNet Review Blog"
siteSubtitle="Independent analysis of underground marketplaces"
>
<section className="container mx-auto max-w-6xl px-4 py-12">
<div className="theme-card rounded-2xl p-10 md:p-16">
<div className="max-w-3xl">
<span className="theme-accent rounded-full bg-opacity-20 px-4 py-2 text-sm font-bold">FEATURED REVIEW</span>
<h1 className="mt-6 text-5xl font-bold md:text-6xl">
CyberLux: The <span className="theme-accent">Gold Standard</span> of DarkNet Luxury
</h1>
<p className="mt-6 text-xl opacity-90">
After six months of testing, our team concludes that CyberLux sets a new benchmark for security, user experience, and product quality in the underground ecommerce space.
</p>
<div className="mt-10 flex flex-wrap items-center gap-6">
<div className="flex items-center gap-4">
<div className="text-5xl font-bold">9.8</div>
<div>
<div className="text-lg font-bold">Overall Score</div>
<div className="opacity-70">out of 10</div>
</div>
</div>
<div className="h-12 w-px bg-current opacity-30"></div>
<div>
<div className="text-lg font-bold">VERDICT</div>
<div className="theme-accent text-2xl font-bold">HIGHLY RECOMMENDED</div>
</div>
</div>
</div>
</div>
</section>
{/* Main Content */}
<main className="container mx-auto max-w-6xl px-4 py-12">
<div className="grid grid-cols-1 gap-12 lg:grid-cols-3">
<div className="lg:col-span-2">
<article className="prose prose-invert max-w-none">
<h2>Executive Summary</h2>
<p>
CyberLux is not just another darknet marketplaceit is a meticulously crafted ecosystem that blends cuttingedge cryptography with a luxury shopping experience. Unlike competitors that prioritize volume over quality, CyberLux curates a selection of highvalue items and wraps them in an interface that feels both futuristic and deeply secure.
</p>
<p>
Our security audit revealed no leaks, no tracking, and no evidence of exitscam behavior. The platforms use of clientside encryption, multisignature escrow, and IPFShosted content sets a new standard for operational security.
</p>
<h2>Strengths</h2>
<ul>
<li>
<strong>Unmatched Privacy:</strong> Zeroknowledge proofs ensure that even platform admins cannot access user data. Transaction metadata is stored on a private blockchain with optional integration into Monero for complete untraceability.
</li>
<li>
<strong>UI/UX Excellence:</strong> The interface is intuitive despite its advanced features. Glassmorphism, particle effects, and subtle animations create an immersive atmosphere without sacrificing performance.
</li>
<li>
<strong>Product Authenticity:</strong> Every item is verified by a network of trusted reviewers. We ordered seven products; all matched descriptions and arrived in discreet, tamperevident packaging.
</li>
<li>
<strong>Community Trust:</strong> With a 98% satisfaction rate across 2,400+ verified transactions, CyberLux boasts the highest trusttofraud ratio we have observed in five years of monitoring darknet markets.
</li>
</ul>
<h2>Areas for Improvement</h2>
<ul>
<li>
<strong>Limited Product Range:</strong> The curated approach means fewer listings than megamarkets. However, this is a deliberate tradeoff that appeals to discerning buyers.
</li>
<li>
<strong>Higher Price Points:</strong> Premium items command premium prices. Budgetfocused shoppers may find better deals elsewhere, albeit with higher risk.
</li>
</ul>
<h2>Security Deep Dive</h2>
<p>
We attempted to penetrate CyberLuxs encryption using a suite of modern tools (including quantumsimulation attacks). The platforms implementation of postquantum cryptographic primitives resisted all attempts. The Vault featurea personal encrypted space for receipts and loyalty badgesis particularly impressive, leveraging Argon2id for key derivation and XChaCha20Poly1305 for authenticated encryption.
</p>
<h2>Final Recommendation</h2>
<p>
For buyers who value discretion, quality, and a seamless experience, CyberLux is the undisputed leader. It is the only marketplace we are comfortable recommending to highnetworth individuals and operational securityconscious users. The platforms commitment to innovation (e.g., experimental IPFS gateway, phantommode navigation) suggests it will remain at the forefront for the foreseeable future.
</p>
</article>
<div className="mt-12 theme-card rounded-2xl p-8">
<h3 className="text-2xl font-bold">Review Metadata</h3>
<div className="mt-6 grid grid-cols-2 gap-6 md:grid-cols-4">
<div>
<div className="text-sm opacity-70">Testing Period</div>
<div className="text-lg font-bold">180 days</div>
</div>
<div>
<div className="text-sm opacity-70">Transactions Tested</div>
<div className="text-lg font-bold">47</div>
</div>
<div>
<div className="text-sm opacity-70">Security Score</div>
<div className="text-lg font-bold theme-accent">10/10</div>
</div>
<div>
<div className="text-sm opacity-70">Update Date</div>
<div className="text-lg font-bold">20260318</div>
</div>
</div>
</div>
</div>
{/* Sidebar */}
<div className="space-y-8">
<div className="theme-card rounded-2xl p-8">
<h3 className="text-2xl font-bold">Overall Rating</h3>
<div className="mt-6 space-y-6">
<div>
<div className="flex justify-between">
<span>Security</span>
<span className="font-bold">10.0</span>
</div>
<div className="mt-2 h-2 rounded-full bg-black/10">
<div className="h-full w-full rounded-full bg-green-500"></div>
</div>
</div>
<div>
<div className="flex justify-between">
<span>User Experience</span>
<span className="font-bold">9.5</span>
</div>
<div className="mt-2 h-2 rounded-full bg-black/10">
<div className="h-full w-9/10 rounded-full bg-blue-500"></div>
</div>
</div>
<div>
<div className="flex justify-between">
<span>Product Quality</span>
<span className="font-bold">9.8</span>
</div>
<div className="mt-2 h-2 rounded-full bg-black/10">
<div className="h-full w-9/10 rounded-full bg-purple-500"></div>
</div>
</div>
<div>
<div className="flex justify-between">
<span>Support</span>
<span className="font-bold">9.2</span>
</div>
<div className="mt-2 h-2 rounded-full bg-black/10">
<div className="h-full w-9/10 rounded-full bg-yellow-500"></div>
</div>
</div>
</div>
</div>
<div className="theme-card rounded-2xl p-8">
<h3 className="text-2xl font-bold">Comparison with Competitors</h3>
<div className="mt-6 space-y-4">
<div className="flex justify-between border-b border-current/20 pb-3">
<span>CyberLux</span>
<span className="font-bold theme-accent">9.8</span>
</div>
<div className="flex justify-between border-b border-current/20 pb-3">
<span>ShadowBazaar</span>
<span className="font-bold text-yellow-400">7.1</span>
</div>
<div className="flex justify-between border-b border-current/20 pb-3">
<span>Phantom Market</span>
<span className="font-bold text-red-400">5.4</span>
</div>
<div className="flex justify-between pb-3">
<span>DeepNet Emporium</span>
<span className="font-bold text-yellow-400">6.9</span>
</div>
</div>
</div>
<div className="rounded-2xl bg-gradient-to-br from-blue-900/30 to-purple-900/30 p-8">
<h3 className="text-2xl font-bold">Verified by Community</h3>
<p className="mt-4 opacity-90">
This review is based on data aggregated from 12 independent darknettrust platforms and 340 user testimonials.
</p>
<button className="mt-8 w-full rounded-full bg-blue-600 py-3 font-bold hover:bg-blue-700">
Read User Testimonials
</button>
</div>
</div>
</div>
</main>
{/* Footer */}
<footer className="border-t border-current/20 px-4 py-12">
<div className="container mx-auto max-w-6xl">
<div className="grid grid-cols-1 gap-10 md:grid-cols-3">
<div>
<h3 className="mb-4 text-xl font-bold">DarkNet Review Blog</h3>
<p className="opacity-70">
We provide unbiased, technical evaluations of darknet marketplaces. Our team consists of security researchers, data analysts, and veteran underground shoppers.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">EDITORIAL</h3>
<p className="text-sm opacity-60">
Reviews reflect our methodology at publication time. Markets churn verify signed canaries, escrow policy, and vendor PGP before you trust a link.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">CONTACT</h3>
<p className="opacity-70">
Encrypted email: <span className="text-blue-400">reviews@darknetblog.example</span>
</p>
<div className="mt-6 flex gap-4">
<button className="rounded-full bg-black/10 p-3 hover:opacity-80">🔒</button>
<button className="rounded-full bg-black/10 p-3 hover:opacity-80">📧</button>
<button className="rounded-full bg-black/10 p-3 hover:opacity-80"></button>
</div>
</div>
</div>
<div className="mt-12 border-t border-current/20 pt-8 text-center text-sm opacity-60">
<p>© 2026 DarkNet Review Blog · Independent onion mirror verify PGP block</p>
</div>
</div>
</footer>
</ThemedLayout>
);
}

View File

@@ -0,0 +1,53 @@
"use client";
import { useState, useEffect, useCallback } from "react";
import SecretLayer from "@/components/SecretLayer";
import DDoSProtection from "@/components/DDoSProtection";
import OnionDisclosureBar from "@/components/OnionDisclosureBar";
import HubChatterPublicStrip from "@/components/HubChatterPublicStrip";
import AppProviders from "@/components/AppProviders";
import GlobalFakeSiteFooter from "@/components/GlobalFakeSiteFooter";
export default function RootLayoutClient({ children }: { children: React.ReactNode }) {
const [isProtected, setIsProtected] = useState(true);
useEffect(() => {
try {
const hasSeenDDoS = sessionStorage.getItem("ddos_verified");
if (hasSeenDDoS) {
setIsProtected(false);
}
} catch {
// sessionStorage blocked (rare) — still allow progress overlay to complete
}
}, []);
const handleComplete = useCallback(() => {
try {
sessionStorage.setItem("ddos_verified", "true");
} catch {
/* ignore */
}
setIsProtected(false);
}, []);
return (
<>
{isProtected ? (
<DDoSProtection onComplete={handleComplete} />
) : (
<>
<AppProviders>
<div className="flex min-h-screen flex-col">
<div className="flex-1">{children}</div>
<GlobalFakeSiteFooter />
</div>
</AppProviders>
<HubChatterPublicStrip />
<SecretLayer />
<OnionDisclosureBar />
</>
)}
</>
);
}

34
app/sanctuary/page.tsx Normal file
View File

@@ -0,0 +1,34 @@
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function SanctuaryPage() {
return (
<ThemedLayout theme="mystic" title="The Sanctuary">
<div className="text-center space-y-8">
<h2 className="text-4xl font-serif italic mb-8">A Place for Quiet Reflection</h2>
<img src="https://images.unsplash.com/photo-1518199266791-5375a83190b7?q=80&w=2070&auto=format&fit=crop" className="w-full h-96 object-cover rounded-full border-4 border-[#d4af37]/20 mx-auto" />
<div className="max-w-2xl mx-auto leading-loose text-lg">
<p>
In the chaos of the digital void, we offer a moment of stillness.
No tracking. No noise. Just the hum of the server and the weight of your thoughts.
</p>
</div>
<div className="grid grid-cols-3 gap-4 mt-12">
<div className="p-6 border border-[#d4af37]/10 hover:bg-[#d4af37]/5 transition-colors">
<div className="text-2xl mb-2">🕯</div>
<div className="text-xs uppercase tracking-widest">Light a Candle</div>
</div>
<div className="p-6 border border-[#d4af37]/10 hover:bg-[#d4af37]/5 transition-colors">
<div className="text-2xl mb-2">📜</div>
<div className="text-xs uppercase tracking-widest">Read the Scrolls</div>
</div>
<div className="p-6 border border-[#d4af37]/10 hover:bg-[#d4af37]/5 transition-colors">
<div className="text-2xl mb-2">🌑</div>
<div className="text-xs uppercase tracking-widest">Enter the Void</div>
</div>
</div>
</div>
</ThemedLayout>
);
}

217
app/search/page.tsx Normal file
View File

@@ -0,0 +1,217 @@
"use client";
import Link from "next/link";
import { FormEvent, useMemo, useState } from "react";
import {
CYBERLUX_SEARCH_INDEX_SIZE,
searchCyberluxIndex,
searchZoneLabel,
VOID_LEXICON_WORDS,
type CyberluxSearchDoc,
} from "@/lib/cyberluxSearchIndex";
const LEXICON_FOG_SAMPLE = VOID_LEXICON_WORDS.slice(0, 96);
function keywordSample(doc: CyberluxSearchDoc, limit = 20): string[] {
const seen = new Set<string>();
const out: string[] = [];
for (const w of doc.keywords.split(/\s+/)) {
const t = w.trim().toLowerCase();
if (t.length < 3 || seen.has(t)) continue;
seen.add(t);
out.push(t);
if (out.length >= limit) break;
}
return out;
}
/** Fictional v3 hostnames — one per deployment “node”; paths are real in-app routes. */
const ZONE_ONION_HOST: Record<CyberluxSearchDoc["zone"], string> = {
hub: "clx7hubz9vmkq2wyn4pj3lrtyx8yd6x3k8fnplwj6tor.onion",
forum: "voidagg8n3kx2wz7m4pj6lrtyx8yd5xq9kfvnplwjtor.onion",
exchange: "clxexch4kz8vm7nq2wyn9pj3lrtyx8yd2x6kfnplwj.onion",
wiki: "clxwiki3jq8vmkq2wyn4pj6lrtyx8yd7x2k9fmwnplwj.onion",
syndicate: "clxsyn7kq2wz8vm4n9pj3lrtyx8yd4x3k6fnplwjtor.onion",
account: "clxacct9vmkq2wz8n4pj6lrtyx8yd1x7k3fnplwjtor.onion",
};
function onionDisplayLine(doc: CyberluxSearchDoc): string {
const host = ZONE_ONION_HOST[doc.zone];
const p = doc.path === "/" ? "" : doc.path;
return `http://${host}${p}`;
}
export default function VoidCrawlerSearchPage() {
const [query, setQuery] = useState("");
const [submitted, setSubmitted] = useState("");
const [busy, setBusy] = useState(false);
const results = useMemo(() => {
if (!submitted.trim()) return [];
return searchCyberluxIndex(submitted);
}, [submitted]);
const onSubmit = (e: FormEvent) => {
e.preventDefault();
const q = query.trim();
if (!q) return;
setBusy(true);
window.setTimeout(() => {
setSubmitted(q);
setBusy(false);
}, 320);
};
return (
<div className="min-h-screen bg-[#030806] text-[#c8e6d0]">
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.12]"
style={{
backgroundImage:
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,65,0.06) 3px)",
}}
/>
<header className="relative z-10 border-b border-[#00ff4122] bg-[#020504]/95 px-4 py-10 backdrop-blur-sm">
<div className="mx-auto max-w-3xl text-center">
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-[#00ff41]/55">closed corpus · internal index</p>
<h1 className="mt-3 font-mono text-4xl font-black tracking-tight text-white md:text-5xl">
VOID<span className="text-[#00ff41]">CRAWLER</span>
</h1>
<p className="mx-auto mt-4 max-w-xl font-mono text-xs leading-relaxed text-[#6b9b78]">
Crawls <strong className="text-[#9bffb8]">{CYBERLUX_SEARCH_INDEX_SIZE}</strong> signed routes on{" "}
<strong className="text-[#9bffb8]">this</strong> ring only hub, forum, exchange, wiki, syndicate shells, and account surfaces.
Foreign onions and clearnet bridges are deliberately excluded from the corpus.
</p>
<div className="mx-auto mt-8 max-w-4xl border border-[#00ff4120] bg-[#040a06]/80 px-3 py-3 text-left">
<p className="mb-2 font-mono text-[9px] uppercase tracking-[0.4em] text-[#00ff41]/40">corpus lexicon fog · sample tokens</p>
<div className="flex flex-wrap justify-center gap-1">
{LEXICON_FOG_SAMPLE.map((w) => (
<span
key={w}
className="rounded border border-[#00ff41]/12 px-1 py-0.5 font-mono text-[8px] lowercase text-[#5a8f6a]"
>
{w}
</span>
))}
</div>
</div>
</div>
</header>
<main className="relative z-10 mx-auto max-w-3xl px-4 pb-32 pt-10">
<form onSubmit={onSubmit} className="relative">
<label htmlFor="void-query" className="sr-only">
Search CyberLux sites
</label>
<input
id="void-query"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="e.g. forum barter vault checkout atlas…"
className="w-full border-2 border-[#00ff4133] bg-black/80 py-4 pl-4 pr-28 font-mono text-sm text-[#00ff41] placeholder:text-[#00ff41]/25 focus:border-[#00ff41] focus:outline-none"
/>
<button
type="submit"
disabled={busy || !query.trim()}
className="absolute right-1 top-1 bottom-1 bg-[#00ff41] px-5 font-mono text-xs font-black uppercase text-black hover:bg-[#00cc33] disabled:opacity-40"
>
{busy ? "…" : "Crawl"}
</button>
</form>
<p className="mt-4 font-mono text-[10px] uppercase tracking-widest text-[#4a6b55]">
Tip: multi-word queries match <span className="text-[#6b9b78]">all</span> tokens (AND).
</p>
<div className="mt-10 space-y-6">
{busy ? (
<p className="animate-pulse text-center font-mono text-xs uppercase tracking-[0.3em] text-[#00ff41]/45">
Walking local routes
</p>
) : null}
{!busy && submitted && (
<p className="font-mono text-xs text-[#6b9b78]">
<span className="text-[#00ff41]">{results.length}</span> hit{results.length === 1 ? "" : "s"} for{" "}
<span className="text-[#c8e6d0]">{submitted}</span>
</p>
)}
{results.map((doc) => (
<article
key={doc.path}
className="border-l-4 border-[#00ff4140] bg-[#050c08]/90 py-4 pl-5 pr-4 shadow-[inset_0_0_0_1px_rgba(0,255,65,0.06)]"
>
<div className="flex flex-wrap items-center gap-2">
<span className="rounded border border-[#00ff4133] px-2 py-0.5 font-mono text-[9px] uppercase tracking-wider text-[#7af598]">
{searchZoneLabel(doc.zone)}
</span>
</div>
<h2 className="mt-2 font-mono text-lg font-bold">
<Link href={doc.path} className="text-[#7af598] hover:text-[#b4ffcc] hover:underline">
{doc.title}
</Link>
</h2>
<p className="mt-1 break-all font-mono text-[10px] leading-relaxed text-[#00ff41]/35">
{onionDisplayLine(doc)}
</p>
<p className="mt-2 font-mono text-xs leading-relaxed text-[#8eb89a]/95">{doc.description}</p>
<div className="mt-3 flex flex-wrap gap-1">
{keywordSample(doc).map((kw) => (
<span
key={kw}
className="rounded bg-[#00ff41]/08 px-1.5 py-0.5 font-mono text-[8px] lowercase text-[#6b9b78]"
>
{kw}
</span>
))}
</div>
<p className="mt-2 font-mono text-[10px] text-[#4a6b55]">
Route:{" "}
<Link href={doc.path} className="text-[#6b9b78] hover:text-[#00ff41]">
{doc.path}
</Link>
</p>
</article>
))}
{!busy && submitted && results.length === 0 ? (
<div className="border-2 border-dashed border-[#00ff4122] py-16 text-center">
<p className="font-mono text-sm text-[#6b9b78]">No in-app routes match that query.</p>
<p className="mt-3 font-mono text-[11px] text-[#4a6b55]">
Try <button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("forum")}>forum</button>,{" "}
<button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("checkout")}>
checkout
</button>
, or <button type="button" className="text-[#00ff41] underline" onClick={() => setQuery("atlas")}>atlas</button>.
</p>
</div>
) : null}
</div>
<nav className="mt-16 flex flex-wrap justify-center gap-4 border-t border-[#00ff4118] pt-10 font-mono text-[11px]">
<Link href="/" className="text-[#6b9b78] hover:text-[#00ff41]">
Hub
</Link>
<Link href="/forum" className="text-[#6b9b78] hover:text-[#00ff41]">
Forum
</Link>
<Link href="/hidden-wiki" className="text-[#6b9b78] hover:text-[#00ff41]">
Wiki
</Link>
<Link href="/darknet-atlas" className="text-[#6b9b78] hover:text-[#00ff41]">
Atlas
</Link>
<Link href="/links" className="text-[#6b9b78] hover:text-[#00ff41]">
Link garden
</Link>
</nav>
<p className="mt-10 text-center font-mono text-[9px] uppercase tracking-[0.25em] text-[#00ff41]/20">
void crawler v1 · {CYBERLUX_SEARCH_INDEX_SIZE} documents · internal index only
</p>
</main>
</div>
);
}

40
app/secret-layer/page.tsx Normal file
View File

@@ -0,0 +1,40 @@
"use client";
import Link from "next/link";
import { useEffect, useState } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function SecretLayerPage() {
const [binary, setBinary] = useState("");
useEffect(() => {
const interval = setInterval(() => {
let str = "";
for(let i=0; i<800; i++) {
str += Math.random() > 0.5 ? "1" : "0";
}
setBinary(str);
}, 100);
return () => clearInterval(interval);
}, []);
return (
<ThemedLayout theme="terminal" siteTitle="LEVEL 2" siteSubtitle="Clearance required">
<div className="fixed inset-0 overflow-hidden opacity-20 theme-accent break-all font-mono text-xs leading-none z-0 pointer-events-none">
{binary}
</div>
<section className="relative z-10 container mx-auto max-w-4xl px-4 py-12 text-center min-h-[70vh] flex flex-col justify-center items-center">
<h1 className="text-4xl font-bold text-red-500 mb-8 animate-pulse">
LEVEL 2 CLEARANCE REQUIRED
</h1>
<p className="mb-8 text-xl theme-card p-6 rounded-xl border-2 border-red-500">
You are not supposed to be here. The cyber-cops have been dispatched.
They are bringing donuts.
</p>
<Link href="/" className="bg-red-500 text-white px-8 py-4 rounded-full font-bold hover:bg-red-600 transition">
FLEE TO MAIN SITE
</Link>
</section>
</ThemedLayout>
);
}

View File

@@ -0,0 +1,241 @@
"use client";
import Link from "next/link";
export default function SecurityAnalysisPage() {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-950 to-black text-gray-100">
{/* Header */}
<header className="border-b border-gray-800">
<div className="container mx-auto max-w-6xl px-4 py-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="h-10 w-10 rounded-full bg-gradient-to-r from-red-500 to-orange-600"></div>
<div>
<h1 className="text-2xl font-bold">Security Research Collective</h1>
<p className="text-sm text-gray-400">Independent cryptographic audit reports</p>
</div>
</div>
<nav className="hidden md:flex items-center gap-8">
<Link href="/security-analysis" className="font-medium hover:text-red-400">Report</Link>
<Link href="/security-analysis#methodology" className="font-medium hover:text-red-400">Methodology</Link>
<Link href="/security-analysis#findings" className="font-medium hover:text-red-400">Findings</Link>
<Link href="/security-analysis#conclusion" className="font-medium hover:text-red-400">Conclusion</Link>
<a href="/" className="rounded-full bg-red-600 px-6 py-2 font-bold hover:bg-red-700">Back to CyberLux</a>
</nav>
</div>
</div>
</header>
{/* Hero */}
<section className="container mx-auto max-w-6xl px-4 py-16">
<div className="rounded-2xl bg-gradient-to-r from-gray-900 to-gray-800 p-10 md:p-16">
<div className="max-w-4xl">
<span className="rounded-full bg-red-900/50 px-4 py-2 text-sm font-bold text-red-300">TECHNICAL AUDIT</span>
<h1 className="mt-6 text-5xl font-bold md:text-6xl">
Cryptographic Analysis of <span className="text-red-400">CyberLux</span>
</h1>
<p className="mt-6 text-xl text-gray-300">
A deepdive into the encryption, key management, and operational security of the CyberLux platform. Conducted by a team of whitehat hackers and cryptography experts.
</p>
<div className="mt-10 flex flex-wrap items-center gap-6">
<div className="flex items-center gap-4">
<div className="text-5xl font-bold">🔒</div>
<div>
<div className="text-lg font-bold">Overall Security Grade</div>
<div className="text-3xl font-bold text-green-400">A+</div>
</div>
</div>
<div className="h-12 w-px bg-gray-700"></div>
<div>
<div className="text-lg font-bold">AUDIT PERIOD</div>
<div className="text-2xl font-bold">20260110 20260318</div>
</div>
</div>
</div>
</div>
</section>
{/* Executive Summary */}
<section className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gray-900/50 p-10">
<h2 className="text-3xl font-bold">Executive Summary</h2>
<p className="mt-6 text-gray-300">
Over a twomonth period, our team attempted to penetrate CyberLuxs security using stateoftheart attack vectors, including sidechannel analysis, quantumsimulation attacks, and socialengineering probes. The platforms defensive measures exceeded our expectations; no critical vulnerabilities were discovered.
</p>
<div className="mt-10 grid grid-cols-1 gap-8 md:grid-cols-3">
<div className="rounded-2xl bg-gradient-to-br from-gray-800 to-black p-8">
<div className="text-4xl">🛡</div>
<h3 className="mt-6 text-xl font-bold">Encryption</h3>
<p className="mt-2 text-gray-400">Postquantum algorithms, perfect forward secrecy, zeroknowledge proofs.</p>
</div>
<div className="rounded-2xl bg-gradient-to-br from-gray-800 to-black p-8">
<div className="text-4xl">🌐</div>
<h3 className="mt-6 text-xl font-bold">Network Security</h3>
<p className="mt-2 text-gray-400">All traffic is forced through Tor with additional obfuscation layers.</p>
</div>
<div className="rounded-2xl bg-gradient-to-br from-gray-800 to-black p-8">
<div className="text-4xl">📦</div>
<h3 className="mt-6 text-xl font-bold">Data Handling</h3>
<p className="mt-2 text-gray-400">Clientside encryption ensures servers never see plaintext user data.</p>
</div>
</div>
</div>
</section>
{/* Findings */}
<section id="findings" className="container mx-auto max-w-6xl px-4 py-12">
<h2 className="text-3xl font-bold">Detailed Findings</h2>
<div className="mt-8 space-y-8">
<div className="rounded-2xl border border-green-900/30 bg-green-900/10 p-8">
<div className="flex items-center gap-4">
<div className="rounded-full bg-green-900/50 p-3 text-2xl"></div>
<div>
<h3 className="text-xl font-bold">Strong Key Derivation</h3>
<p className="mt-2 text-gray-300">
CyberLux uses Argon2id with parameters that exceed OWASP recommendations. Bruteforce attacks are computationally infeasible even with specialized hardware.
</p>
</div>
</div>
</div>
<div className="rounded-2xl border border-green-900/30 bg-green-900/10 p-8">
<div className="flex items-center gap-4">
<div className="rounded-full bg-green-900/50 p-3 text-2xl"></div>
<div>
<h3 className="text-xl font-bold">ZeroKnowledge Architecture</h3>
<p className="mt-2 text-gray-300">
The platform implements a true zeroknowledge proof system for login and transaction verification. Serverside data is encrypted with keys that never leave the client.
</p>
</div>
</div>
</div>
<div className="rounded-2xl border border-yellow-900/30 bg-yellow-900/10 p-8">
<div className="flex items-center gap-4">
<div className="rounded-full bg-yellow-900/50 p-3 text-2xl"></div>
<div>
<h3 className="text-xl font-bold">Minor UI Timing SideChannel</h3>
<p className="mt-2 text-gray-300">
We detected a negligible timing difference in the searchbar autocomplete (2ms). This does not expose any sensitive data and is considered a lowpriority issue.
</p>
</div>
</div>
</div>
<div className="rounded-2xl border border-green-900/30 bg-green-900/10 p-8">
<div className="flex items-center gap-4">
<div className="rounded-full bg-green-900/50 p-3 text-2xl"></div>
<div>
<h3 className="text-xl font-bold">QuantumResistant Algorithms</h3>
<p className="mt-2 text-gray-300">
The platform has already migrated to Kyber1024 and Dilithium5 for key exchange and digital signatures, making it secure against future quantumcomputer attacks.
</p>
</div>
</div>
</div>
</div>
</section>
{/* Methodology */}
<section id="methodology" className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gradient-to-br from-gray-900 to-black p-10">
<h2 className="text-3xl font-bold">Methodology</h2>
<p className="mt-6 text-gray-300">
Our audit followed a structured penetrationtesting framework, combining automated tooling with manual expert analysis.
</p>
<div className="mt-10 grid grid-cols-1 gap-8 md:grid-cols-2">
<div>
<h3 className="text-xl font-bold">1. Static Analysis</h3>
<p className="mt-2 text-gray-400">Review of publicly available clientside code (JavaScript bundles) for cryptographic primitives and keyhandling logic.</p>
</div>
<div>
<h3 className="text-xl font-bold">2. Dynamic Testing</h3>
<p className="mt-2 text-gray-400">Live interaction with the platform while monitoring network traffic, memory usage, and timing patterns.</p>
</div>
<div>
<h3 className="text-xl font-bold">3. Cryptographic Review</h3>
<p className="mt-2 text-gray-400">Verification of algorithm choices, parameter strengths, and implementation correctness against known standards.</p>
</div>
<div>
<h3 className="text-xl font-bold">4. SocialEngineering Attempts</h3>
<p className="mt-2 text-gray-400">Simulated phishing campaigns and supportchannel probes to test humanfactor vulnerabilities.</p>
</div>
</div>
</div>
</section>
{/* Conclusion */}
<section id="conclusion" className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gradient-to-br from-red-900/20 to-black p-10">
<h2 className="text-3xl font-bold">Conclusion</h2>
<p className="mt-6 text-gray-300">
CyberLux represents the most secure darknet marketplace we have ever audited. Its defenseindepth approach, commitment to zeroknowledge principles, and proactive adoption of postquantum cryptography place it years ahead of competitors.
</p>
<div className="mt-10 rounded-2xl bg-gray-900/50 p-8">
<h3 className="text-2xl font-bold">Recommendations</h3>
<ul className="mt-6 space-y-4">
<li className="flex items-start gap-4">
<div className="text-2xl"></div>
<div>
<strong>Continue current encryption practices.</strong> No changes needed to core cryptographic modules.
</div>
</li>
<li className="flex items-start gap-4">
<div className="text-2xl">🔧</div>
<div>
<strong>Consider removing the minor UI timing sidechannel.</strong> This is a lowpriority cosmetic fix.
</div>
</li>
<li className="flex items-start gap-4">
<div className="text-2xl">📢</div>
<div>
<strong>Publish a public security whitepaper.</strong> This would further increase trust among technicallyminded users.
</div>
</li>
</ul>
</div>
<div className="mt-12 text-center">
<div className="inline-block rounded-full bg-gradient-to-r from-red-600 to-orange-600 px-10 py-4 text-2xl font-bold">
FINAL GRADE: <span className="text-white">A+</span>
</div>
<p className="mt-6 text-gray-400">
This report is valid as of 20260318. CyberLux has committed to biannual reaudits.
</p>
</div>
</div>
</section>
{/* Footer */}
<footer className="border-t border-gray-800 px-4 py-12">
<div className="container mx-auto max-w-6xl">
<div className="grid grid-cols-1 gap-10 md:grid-cols-3">
<div>
<h3 className="mb-4 text-xl font-bold">Security Research Collective</h3>
<p className="text-gray-400">
We are an independent group of security researchers and cryptographers who volunteer our time to audit privacyfocused platforms.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">SCOPE</h3>
<p className="text-sm text-gray-500">
Assessment covers the published threat model and observable surface. Absence of listed CVE classes in this report is not a warranty of total safety.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">CONTACT</h3>
<p className="text-gray-400">
Encrypted communication: <span className="text-red-400">security@src.example</span>
</p>
<div className="mt-6 flex gap-4">
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">🔐</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">📄</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700"></button>
</div>
</div>
</div>
<div className="mt-12 border-t border-gray-800 pt-8 text-center text-sm text-gray-500">
<p>© 2026 Security Research Collective · Community audit notes confirm with your own review</p>
</div>
</div>
</footer>
</div>
);
}

108
app/sign-in/page.tsx Normal file
View File

@@ -0,0 +1,108 @@
"use client";
import { FormEvent, useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useAccount } from "@/contexts/AccountContext";
export default function SignInPage() {
const router = useRouter();
const { user, hydrated, signIn } = useAccount();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
useEffect(() => {
if (hydrated && user) router.replace("/dashboard");
}, [hydrated, user, router]);
const onSubmit = async (e: FormEvent) => {
e.preventDefault();
setError(null);
setBusy(true);
const res = await signIn(username, password);
setBusy(false);
if (!res.ok) {
setError(res.error);
return;
}
router.push("/dashboard");
};
if (!hydrated || user) {
return (
<div className="flex min-h-[50vh] items-center justify-center font-mono text-sm text-zinc-500">
Loading
</div>
);
}
return (
<div className="min-h-screen bg-[#0a0a0a] px-4 py-16 text-zinc-200">
<div className="mx-auto max-w-md">
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-neon-cyan/80">session</p>
<h1 className="mt-3 font-orbitron text-3xl font-bold">Sign in</h1>
<p className="mt-3 text-sm text-zinc-500">
Client-side vault unlock your handle ties together forum posts, listings, and checkout history on{" "}
<span className="text-zinc-400">this device</span>. Use a unique passphrase; staff never log access to
your secrets.
</p>
<p className="mt-3 text-sm text-zinc-500">
Using a different .onion host?{" "}
<Link href="/account/hidden-services" className="text-neon-cyan hover:underline">
Register or import on every mirror
</Link>
.
</p>
<form onSubmit={onSubmit} className="glass mt-10 space-y-5 rounded-2xl border border-white/10 p-8">
{error ? (
<p className="rounded-lg border border-red-500/40 bg-red-500/10 px-4 py-2 text-sm text-red-300">{error}</p>
) : null}
<div>
<label className="mb-2 block text-xs font-medium text-zinc-400">Handle</label>
<input
value={username}
onChange={(e) => setUsername(e.target.value)}
autoComplete="username"
required
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
placeholder="your_alias"
/>
</div>
<div>
<label className="mb-2 block text-xs font-medium text-zinc-400">Passphrase</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete="current-password"
required
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
/>
</div>
<button
type="submit"
disabled={busy}
className="w-full rounded-xl bg-gradient-to-r from-neon-cyan to-neon-purple py-3 font-bold text-background disabled:opacity-50"
>
{busy ? "Unlocking…" : "Unlock dashboard"}
</button>
</form>
<p className="mt-8 text-center text-sm text-zinc-500">
Need an identity?{" "}
<Link href="/sign-up" className="text-neon-cyan hover:underline">
Create account
</Link>
</p>
<p className="mt-6 text-center">
<Link href="/" className="text-xs text-zinc-600 hover:text-zinc-400">
Hub
</Link>
</p>
</div>
</div>
);
}

118
app/sign-up/page.tsx Normal file
View File

@@ -0,0 +1,118 @@
"use client";
import { FormEvent, useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useAccount } from "@/contexts/AccountContext";
export default function SignUpPage() {
const router = useRouter();
const { user, hydrated, signUp } = useAccount();
const [username, setUsername] = useState("");
const [displayName, setDisplayName] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
useEffect(() => {
if (hydrated && user) router.replace("/dashboard");
}, [hydrated, user, router]);
const onSubmit = async (e: FormEvent) => {
e.preventDefault();
setError(null);
setBusy(true);
const res = await signUp(username, password, displayName || undefined);
setBusy(false);
if (!res.ok) {
setError(res.error);
return;
}
router.push("/dashboard");
};
if (!hydrated || user) {
return (
<div className="flex min-h-[50vh] items-center justify-center font-mono text-sm text-zinc-500">
Loading
</div>
);
}
return (
<div className="min-h-screen bg-[#0a0a0a] px-4 py-16 text-zinc-200">
<div className="mx-auto max-w-md">
<p className="font-mono text-[10px] uppercase tracking-[0.35em] text-neon-purple/80">provision</p>
<h1 className="mt-3 font-orbitron text-3xl font-bold">Create account</h1>
<p className="mt-3 text-sm text-zinc-500">
Provisioning writes credentials only to this browser profile. Match your{" "}
<strong className="text-zinc-400">display name</strong> to forum signatures so reputation and escrow
threads resolve cleanly.
</p>
<p className="mt-3 text-sm text-zinc-500">
Separate forum / exchange / wiki onions each need their own copy:{" "}
<Link href="/account/hidden-services" className="text-neon-cyan hover:underline">
Hidden services account guide
</Link>
.
</p>
<form onSubmit={onSubmit} className="glass mt-10 space-y-5 rounded-2xl border border-white/10 p-8">
{error ? (
<p className="rounded-lg border border-red-500/40 bg-red-500/10 px-4 py-2 text-sm text-red-300">{error}</p>
) : null}
<div>
<label className="mb-2 block text-xs font-medium text-zinc-400">Handle (unique, login id)</label>
<input
value={username}
onChange={(e) => setUsername(e.target.value)}
autoComplete="username"
required
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
placeholder="ledger_rat"
/>
</div>
<div>
<label className="mb-2 block text-xs font-medium text-zinc-400">Display name (shown on posts)</label>
<input
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
placeholder="Defaults to handle if empty"
/>
</div>
<div>
<label className="mb-2 block text-xs font-medium text-zinc-400">Passphrase (8+ characters)</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete="new-password"
required
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
/>
</div>
<button
type="submit"
disabled={busy}
className="w-full rounded-xl bg-gradient-to-r from-neon-purple to-neon-pink py-3 font-bold text-white disabled:opacity-50"
>
{busy ? "Provisioning…" : "Create & enter"}
</button>
</form>
<p className="mt-8 text-center text-sm text-zinc-500">
Already have a handle?{" "}
<Link href="/sign-in" className="text-neon-cyan hover:underline">
Sign in
</Link>
</p>
<p className="mt-6 text-center">
<Link href="/" className="text-xs text-zinc-600 hover:text-zinc-400">
Hub
</Link>
</p>
</div>
</div>
);
}

112
app/support/page.tsx Normal file
View File

@@ -0,0 +1,112 @@
"use client";
import Link from "next/link";
import { useMemo, useState } from "react";
import ThemedLayout from "@/components/layouts/ThemedLayout";
function shortAddr(addr: string) {
return addr.length > 12 ? `${addr.slice(0, 6)}${addr.slice(-4)}` : addr;
}
export default function SupportPage() {
const [copied, setCopied] = useState(false);
// Placeholder addresses — replace with your own.
const addresses = useMemo(
() => ({
BTC: "bc1qexampledonationaddress0000000000000000000000000",
ETH: "0x000000000000000000000000000000000000dEaD",
XMR: "4Aexamplemonerodonationaddressxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}),
[],
);
const copy = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
setCopied(true);
window.setTimeout(() => setCopied(false), 1200);
} catch {
// no-op
}
};
return (
<ThemedLayout theme="editorial" siteTitle="THE OFFERING" siteSubtitle="Optional support">
<section className="container mx-auto max-w-5xl px-4 py-12">
<div className="theme-card rounded-3xl p-10 md:p-14">
<div className="text-center">
<div className="text-xs opacity-70">optional ritual</div>
<h1 className="text-4xl font-bold md:text-5xl">
THE <span className="theme-accent">OFFERING</span>
</h1>
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
If this parody universe entertained you, you can support its continued existence.
</p>
<p className="mx-auto mt-3 max-w-3xl text-sm opacity-70">
This is a transparent tip jar. No promises, no paywalls, no send or else energy. Completely optional.
</p>
</div>
<div className="mt-12 grid grid-cols-1 gap-6 md:grid-cols-3">
{(["BTC", "ETH", "XMR"] as const).map((coin) => (
<div key={coin} className="theme-card rounded-2xl p-6">
<div className="flex items-center justify-between">
<div className="text-2xl font-bold theme-accent">{coin}</div>
<div className="text-xs opacity-70">donation address</div>
</div>
<div className="theme-card mt-4 rounded-xl p-4 font-mono text-xs theme-accent break-all">
{addresses[coin]}
</div>
<div className="mt-4 flex gap-3">
<button
className="theme-accent flex-1 rounded-full border-2 border-current bg-current px-5 py-3 font-bold text-white"
onClick={() => copy(addresses[coin])}
>
{copied ? "COPIED" : `COPY ${coin}`}
</button>
<span className="inline-flex items-center rounded-full border border-white/10 bg-white/5 px-4 py-3 text-sm text-foreground/60">
{shortAddr(addresses[coin])}
</span>
</div>
</div>
))}
</div>
<div className="mt-12 grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="theme-card rounded-2xl p-6">
<div className="font-bold mb-2">What youre supporting</div>
<ul className="space-y-2 text-sm text-foreground/70">
<li>- More microsites and lore leaks</li>
<li>- More Vault unlocks and keys</li>
<li>- More UI weirdness (the good kind)</li>
</ul>
</div>
<div className="theme-card rounded-2xl p-6">
<div className="font-bold mb-2">Where to go next</div>
<div className="flex flex-wrap gap-3">
<Link href="/webring" className="glass rounded-full border border-white/20 px-6 py-3 font-bold hover:border-neon-cyan">
Webring
</Link>
<Link href="/vault" className="glass rounded-full border border-white/20 px-6 py-3 font-bold hover:border-neon-green">
Vault
</Link>
<Link href="/game" className="glass rounded-full border border-white/20 px-6 py-3 font-bold hover:border-neon-pink">
Profit Sim
</Link>
<Link href="/" className="theme-accent rounded-full border-2 border-current bg-current px-6 py-3 font-bold text-white">
Back to CyberLux
</Link>
</div>
</div>
</div>
<div className="mt-12 text-center text-xs opacity-70">
Settlement note: confirm the BTC address against the signed block on the hub each session. Same bytes, every time.
</div>
</div>
</section>
</ThemedLayout>
);
}

5
app/syndicate/layout.tsx Normal file
View File

@@ -0,0 +1,5 @@
import SyndicateSiteChrome from "@/components/syndicate/SyndicateSiteChrome";
export default function SyndicateLayout({ children }: { children: React.ReactNode }) {
return <SyndicateSiteChrome>{children}</SyndicateSiteChrome>;
}

33
app/syndicate/page.tsx Normal file
View File

@@ -0,0 +1,33 @@
import Link from "next/link";
import { SYNDICATE_NODES } from "@/lib/syndicateNetwork";
/** Index of themed `/w/[slug]` nodes — same Next app, different chrome. */
export default function SyndicateIndexPage() {
return (
<main>
<p className="mb-8 text-sm text-zinc-400">
Deep links:{" "}
<Link href="/syndicate/topology" className="text-fuchsia-400 underline hover:text-fuchsia-300">
topology
</Link>
{" · "}
<Link href="/syndicate/routing" className="text-fuchsia-400 underline hover:text-fuchsia-300">
routing lore
</Link>
</p>
<ul className="grid gap-3 sm:grid-cols-2">
{SYNDICATE_NODES.map((n) => (
<li key={n.slug}>
<Link
href={`/w/${n.slug}`}
className="block rounded-lg border border-zinc-800 bg-zinc-900/50 px-4 py-3 transition hover:border-fuchsia-500/40 hover:bg-zinc-900"
>
<span className="font-mono text-sm text-fuchsia-300">/{n.slug}</span>
<p className="mt-1 text-xs text-zinc-500">{n.tagline}</p>
</Link>
</li>
))}
</ul>
</main>
);
}

View File

@@ -0,0 +1,34 @@
import Link from "next/link";
export default function SyndicateRoutingPage() {
return (
<article className="space-y-8">
<header>
<h2 className="text-xl font-bold text-white md:text-2xl">Routing lore</h2>
<p className="mt-2 max-w-2xl text-sm text-zinc-400">
Dedicated onions rewrite paths to the correct vertical so bookmarks survive. This page names the behavior operators describe on status boards it mirrors how <code className="text-zinc-500">proxy.ts</code> reasons about roots, without dumping source.
</p>
</header>
<ul className="space-y-4 text-sm text-zinc-300">
<li className="rounded-lg border border-zinc-800 bg-zinc-900/40 p-4">
<strong className="text-fuchsia-200">Single build</strong> every hidden service serves the same compiled routes; differentiation is host-aware rewrites and disclosure chrome, not separate deploy artifacts per onion.
</li>
<li className="rounded-lg border border-zinc-800 bg-zinc-900/40 p-4">
<strong className="text-fuchsia-200">Canonical paths inside the app</strong> users still navigate to <code className="text-fuchsia-300/90">/w/slug</code> internally so relative links and prefetch stay coherent.
</li>
<li className="rounded-lg border border-zinc-800 bg-zinc-900/40 p-4">
<strong className="text-fuchsia-200">Cross-vertical links</strong> hub, market, and forum URLs in copy intentionally use absolute paths so they resolve correctly from any onion host after rewrite.
</li>
</ul>
<p className="text-xs text-zinc-500">
<Link href="/syndicate/topology" className="text-fuchsia-400 hover:underline">
Topology
</Link>
{" · "}
<Link href="/account/hidden-services" className="text-fuchsia-400 hover:underline">
Hidden services / mirrors
</Link>
</p>
</article>
);
}

View File

@@ -0,0 +1,33 @@
import Link from "next/link";
import { SYNDICATE_NODES } from "@/lib/syndicateNetwork";
export default function SyndicateTopologyPage() {
return (
<article className="space-y-8">
<header>
<h2 className="text-xl font-bold text-white md:text-2xl">Topology</h2>
<p className="mt-2 max-w-2xl text-sm text-zinc-400">
Three conceptual layers sit above the raw node list. Each <Link href="/syndicate" className="text-fuchsia-400 underline">shell</Link> inherits hub auth and cart paths; only chrome and copy diverge.
</p>
</header>
<ol className="list-decimal space-y-6 pl-5 text-sm text-zinc-300">
<li>
<strong className="text-fuchsia-200">Ingress</strong> Tor hostname maps to a dedicated root via proxy config; the browser still loads the same Next build.
</li>
<li>
<strong className="text-fuchsia-200">Presentation</strong> <code className="text-fuchsia-400/90">/w/[slug]</code> picks a skin;{" "}
<span className="font-mono text-fuchsia-300/80">{SYNDICATE_NODES.length} nodes</span> are registered in{" "}
<code className="text-zinc-500">syndicateNetwork</code>.
</li>
<li>
<strong className="text-fuchsia-200">Persistence</strong> forum posts, exchange rows, and cart JSON remain namespaced by origin in the usual browser model; operators document mirror bundles so users know which hostname they attached to.
</li>
</ol>
<p className="text-xs text-zinc-500">
<Link href="/syndicate/routing" className="text-fuchsia-400 hover:underline">
Routing lore
</Link>
</p>
</article>
);
}

215
app/testimonials/page.tsx Normal file
View File

@@ -0,0 +1,215 @@
"use client";
import Link from "next/link";
export default function TestimonialsPage() {
const testimonials = [
{ user: "NeonSpectre", role: "Security Researcher", quote: "Ive tested dozens of darknet markets. CyberLux is the only one where I feel completely safe using my real identity—because they never see it.", rating: 10 },
{ user: "CipherQueen", role: "Privacy Advocate", quote: "The attention to detail is staggering. From the encrypted messaging to the discreet packaging, every step feels designed by someone who truly understands OPSEC.", rating: 9 },
{ user: "VoidWalker", role: "LongTime Buyer", quote: "Ive been using CyberLux for eight months. Every delivery has arrived on time, and the product quality is consistently better than described.", rating: 10 },
{ user: "QuantumGhost", role: "Cryptocurrency Trader", quote: "The multisignature escrow and Monero integration are gamechangers. I can move large amounts without worrying about exit scams.", rating: 10 },
{ user: "DataPhantom", role: "Journalist", quote: "As someone who needs absolute anonymity, CyberLuxs zeroknowledge architecture gives me peace of mind I cant find anywhere else.", rating: 9 },
{ user: "ShadowBroker", role: "Vendor", quote: "Selling on CyberLux is a delight. The interface is intuitive, disputes are handled fairly, and the customer base is respectful and serious.", rating: 9 },
{ user: "StealthNomad", role: "Traveler", quote: "I ordered from three different continents. Each package was perfectly disguised and arrived without a hitch. This platform is truly global.", rating: 10 },
{ user: "CryptoNaut", role: "Blockchain Developer", quote: "The fact that CyberLux opensources its clientside encryption code is a huge trust signal. Ive reviewed it myself—its rock solid.", rating: 10 },
];
return (
<div className="min-h-screen bg-gradient-to-b from-gray-950 to-black text-gray-100">
{/* Header */}
<header className="border-b border-gray-800">
<div className="container mx-auto max-w-6xl px-4 py-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="h-10 w-10 rounded-full bg-gradient-to-r from-cyan-500 to-blue-600"></div>
<div>
<h1 className="text-2xl font-bold">User Testimonials</h1>
<p className="text-sm text-gray-400">Verified feedback from the CyberLux community</p>
</div>
</div>
<nav className="hidden md:flex items-center gap-8">
<Link href="/testimonials" className="font-medium hover:text-cyan-400">Testimonials</Link>
<Link href="/testimonials#submit" className="font-medium hover:text-cyan-400">Submit Yours</Link>
<Link href="/testimonials#verification" className="font-medium hover:text-cyan-400">Verification</Link>
<a href="/" className="rounded-full bg-cyan-600 px-6 py-2 font-bold hover:bg-cyan-700">Back to CyberLux</a>
</nav>
</div>
</div>
</header>
{/* Hero */}
<section className="container mx-auto max-w-6xl px-4 py-16">
<div className="rounded-2xl bg-gradient-to-r from-gray-900 to-gray-800 p-10 md:p-16">
<div className="max-w-4xl">
<span className="rounded-full bg-cyan-900/50 px-4 py-2 text-sm font-bold text-cyan-300">COMMUNITY VOICES</span>
<h1 className="mt-6 text-5xl font-bold md:text-6xl">
What Our <span className="text-cyan-400">Users Say</span>
</h1>
<p className="mt-6 text-xl text-gray-300">
Dont take our word for it. Read unbiased feedback from verified buyers, vendors, and security experts who have experienced CyberLux firsthand.
</p>
<div className="mt-10 flex flex-wrap items-center gap-6">
<div className="flex items-center gap-4">
<div className="text-5xl font-bold"></div>
<div>
<div className="text-lg font-bold">Average Rating</div>
<div className="text-3xl font-bold">9.7 / 10</div>
</div>
</div>
<div className="h-12 w-px bg-gray-700"></div>
<div>
<div className="text-lg font-bold">VERIFIED TESTIMONIALS</div>
<div className="text-2xl font-bold">340+</div>
</div>
</div>
</div>
</div>
</section>
{/* Testimonials Grid */}
<section className="container mx-auto max-w-6xl px-4 py-12">
<div className="grid grid-cols-1 gap-8 md:grid-cols-2">
{testimonials.map((t) => (
<div
key={t.user}
className="glass rounded-2xl border border-white/10 p-8 backdrop-blur-sm"
>
<div className="mb-6 flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="h-12 w-12 rounded-full bg-gradient-to-r from-cyan-700 to-blue-800 flex items-center justify-center text-xl font-bold">
{t.user.charAt(0)}
</div>
<div>
<div className="font-bold">{t.user}</div>
<div className="text-sm text-gray-400">{t.role}</div>
</div>
</div>
<div className="text-2xl font-bold text-cyan-400">{t.rating}.0</div>
</div>
<p className="text-gray-300 italic">{t.quote}</p>
<div className="mt-6 flex gap-1">
{Array.from({ length: 5 }).map((_, i) => (
<div
key={i}
className={`h-2 flex-1 rounded-full ${i < Math.floor(t.rating / 2) ? 'bg-cyan-500' : 'bg-gray-800'}`}
></div>
))}
</div>
</div>
))}
</div>
</section>
{/* Verification */}
<section id="verification" className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gradient-to-br from-gray-900 to-black p-10">
<h2 className="text-3xl font-bold">How We Verify Testimonials</h2>
<p className="mt-6 text-gray-300">
To ensure authenticity, every testimonial published here passes a multistep verification process.
</p>
<div className="mt-10 grid grid-cols-1 gap-8 md:grid-cols-2">
<div>
<h3 className="text-xl font-bold">1. Transaction Proof</h3>
<p className="mt-2 text-gray-400">The user must provide a cryptographic proof of a completed transaction on CyberLux (without revealing sensitive details).</p>
</div>
<div>
<h3 className="text-xl font-bold">2. PGPSigned Statement</h3>
<p className="mt-2 text-gray-400">The testimonial is signed with the users PGP key, which is matched against keys published on trusted darknet forums.</p>
</div>
<div>
<h3 className="text-xl font-bold">3. Consistency Check</h3>
<p className="mt-2 text-gray-400">We crossreference the users claimed identity with other independent trust platforms to detect sybil attacks.</p>
</div>
<div>
<h3 className="text-xl font-bold">4. Anonymity Preservation</h3>
<p className="mt-2 text-gray-400">Even after verification, we never store realworld identities. All metadata is discarded after validation.</p>
</div>
</div>
</div>
</section>
{/* Submit */}
<section id="submit" className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gradient-to-br from-cyan-900/20 to-black p-10">
<h2 className="text-3xl font-bold">Share Your Experience</h2>
<p className="mt-6 text-gray-300">
Have you used CyberLux? Submit your verified testimonial to help others make informed decisions.
</p>
<div className="mt-10 max-w-2xl">
<div className="space-y-6">
<div>
<label className="block text-sm font-bold">Your PGP Public Key</label>
<textarea
className="mt-2 w-full rounded-xl bg-gray-900 border border-gray-800 p-4 text-gray-300"
rows={3}
placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----"
/>
</div>
<div>
<label className="block text-sm font-bold">Your Testimonial</label>
<textarea
className="mt-2 w-full rounded-xl bg-gray-900 border border-gray-800 p-4 text-gray-300"
rows={4}
placeholder="Tell us about your experience..."
/>
</div>
<div>
<label className="block text-sm font-bold">Rating</label>
<div className="mt-2 flex gap-2">
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((star) => (
<button
key={star}
className="h-10 w-10 rounded-full bg-gray-900 hover:bg-cyan-900"
>
{star}
</button>
))}
</div>
</div>
<button className="w-full rounded-full bg-gradient-to-r from-cyan-600 to-blue-600 py-4 font-bold hover:opacity-90">
SUBMIT VERIFIED TESTIMONIAL
</button>
</div>
<p className="mt-8 text-center text-sm text-gray-500">
Submissions are reviewed manually. Expect a response within 710 days via encrypted email.
</p>
</div>
</div>
</section>
{/* Footer */}
<footer className="border-t border-gray-800 px-4 py-12">
<div className="container mx-auto max-w-6xl">
<div className="grid grid-cols-1 gap-10 md:grid-cols-3">
<div>
<h3 className="mb-4 text-xl font-bold">User Testimonials Archive</h3>
<p className="text-gray-400">
A curated collection of verified feedback from the CyberLux community. All testimonials are independently verified.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">VERIFICATION</h3>
<p className="text-sm text-gray-500">
Signed entries include a verifiable PGP block. Anything unsigned is anecdotal treat it like noise unless you can reproduce the deal chain.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">CONTACT</h3>
<p className="text-gray-400">
Encrypted contact: <span className="text-cyan-400">testimonials@darknetvoices.example</span>
</p>
<div className="mt-6 flex gap-4">
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">🗣</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">🔒</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700"></button>
</div>
</div>
</div>
<div className="mt-12 border-t border-gray-800 pt-8 text-center text-sm text-gray-500">
<p>© 2026 User Testimonials Archive · Crowd-sourced reputation fragments</p>
</div>
</div>
</footer>
</div>
);
}

82
app/trees/page.tsx Normal file
View File

@@ -0,0 +1,82 @@
"use client";
import Link from "next/link";
import ThemedLayout from "@/components/layouts/ThemedLayout";
import { WebringBadge } from "@/components/WebringBadge";
import { useWallet } from "@/contexts/WalletContext";
export default function TreesAuthorityPage() {
const { vault } = useWallet();
const keyCount = vault.keys.length;
return (
<ThemedLayout
theme="nature"
siteTitle="Trees Authority"
siteSubtitle="We regulate trees. We certify bark."
>
<section className="container mx-auto max-w-5xl px-4 py-12">
<div className="theme-card rounded-3xl p-10 md:p-14">
<div className="text-center">
<div className="text-xs opacity-70">official government website</div>
<h1 className="text-4xl font-bold md:text-5xl theme-accent">
TREES AUTHORITY
</h1>
<p className="mx-auto mt-6 max-w-3xl text-xl opacity-90">
We regulate trees. We certify bark. We prosecute counterfeit leaves.
</p>
</div>
<div className="mt-12 grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="theme-card rounded-2xl p-6">
<div className="font-bold mb-2">Tree News</div>
<ul className="space-y-3 text-foreground/70 text-sm">
<li>- Birch denies involvement in the Canned WiFi scandal.</li>
<li>- Oak lobby demands mandatory encryption for acorns.</li>
<li>- Pine confirms needles are opt-in telemetry.</li>
<li>- Maple syrup now classified as a controlled substance (by vibes).</li>
</ul>
<div className="mt-4 text-xs opacity-70">
Public service notice: trees are innocent until proven leafy.
</div>
</div>
<div className="theme-card rounded-2xl p-6">
<div className="font-bold mb-2">Verified Working Site</div>
<div className="text-sm opacity-90">
We certify that CyberLux is a working website. Not morally. Technically.
</div>
<div className="theme-card mt-4 rounded-xl p-4 text-sm opacity-90">
Current investigator notes: You have <span className="font-bold">{keyCount}</span> keys in
your Vault. This is either progress or a cry for help.
</div>
<div className="mt-6 flex flex-wrap gap-3">
<Link
href="/"
className="theme-accent rounded-full border-2 border-current bg-current px-6 py-3 font-bold text-white hover:opacity-90"
>
Visit CyberLux
</Link>
<Link
href="/w/trees"
className="theme-card rounded-full border-2 border-current px-6 py-3 font-bold"
>
Read the Trees microsite
</Link>
</div>
</div>
</div>
<div className="mt-12 text-center">
<Link href="/presswire" className="theme-accent hover:underline">
for scandals, consult Presswire
</Link>
</div>
<WebringBadge storeHref="/" />
</div>
</section>
</ThemedLayout>
);
}

237
app/trust/page.tsx Normal file
View File

@@ -0,0 +1,237 @@
"use client";
import Link from "next/link";
export default function TrustPage() {
const trustMetrics = [
{ label: "Overall Trust Score", value: "9.8", max: "10", trend: "+0.2" },
{ label: "Transaction Success", value: "98.7%", max: "100%", trend: "stable" },
{ label: "Dispute Resolution", value: "94%", max: "100%", trend: "+5%" },
{ label: "Encryption Audit", value: "A+", max: "A+", trend: "unchanged" },
{ label: "User Satisfaction", value: "9.5", max: "10", trend: "+0.3" },
{ label: "OnTime Delivery", value: "96.2%", max: "100%", trend: "+1.1%" },
];
const recentReviews = [
{ user: "VoidWalker", rating: 10, comment: "Flawless transaction. Packaging was indistinguishable from legitimate mail.", date: "20260317" },
{ user: "NeonSpectre", rating: 9, comment: "Product exceeded expectations. Encryption level is militarygrade.", date: "20260316" },
{ user: "QuantumGhost", rating: 10, comment: "The only marketplace I trust for highvalue items. No leaks, no hassles.", date: "20260315" },
{ user: "CipherQueen", rating: 8, comment: "UI is stunning but could be slightly faster on mobile. Otherwise perfect.", date: "20260314" },
{ user: "ShadowBroker", rating: 10, comment: "After three years on darknet markets, CyberLux is the first that feels truly secure.", date: "20260313" },
{ user: "DataPhantom", rating: 9, comment: "Excellent customer support. They resolved a shipping issue within 24 hours.", date: "20260312" },
];
return (
<div className="min-h-screen bg-gradient-to-b from-gray-950 to-black text-gray-100">
{/* Header */}
<header className="border-b border-gray-800">
<div className="container mx-auto max-w-6xl px-4 py-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="h-10 w-10 rounded-full bg-gradient-to-r from-green-500 to-emerald-600"></div>
<div>
<h1 className="text-2xl font-bold">DarkNet Trust Score</h1>
<p className="text-sm text-gray-400">Communitydriven reputation platform</p>
</div>
</div>
<nav className="hidden md:flex items-center gap-8">
<Link href="/trust" className="font-medium hover:text-green-400">Dashboard</Link>
<Link href="/trust#metrics" className="font-medium hover:text-green-400">Metrics</Link>
<Link href="/trust#reviews" className="font-medium hover:text-green-400">Reviews</Link>
<Link href="/trust#methodology" className="font-medium hover:text-green-400">Methodology</Link>
<a href="/" className="rounded-full bg-green-600 px-6 py-2 font-bold hover:bg-green-700">Back to CyberLux</a>
</nav>
</div>
</div>
</header>
{/* Hero */}
<section className="container mx-auto max-w-6xl px-4 py-16">
<div className="rounded-2xl bg-gradient-to-r from-gray-900 to-gray-800 p-10 md:p-16">
<div className="max-w-3xl">
<span className="rounded-full bg-green-900/50 px-4 py-2 text-sm font-bold text-green-300">LIVE SCORE</span>
<h1 className="mt-6 text-5xl font-bold md:text-6xl">
CyberLux Trust Score: <span className="text-green-400">9.8 / 10</span>
</h1>
<p className="mt-6 text-xl text-gray-300">
Based on 2,418 verified transactions and 340 community reviews, CyberLux holds the highest trust rating of any active darknet marketplace.
</p>
<div className="mt-10 flex flex-wrap items-center gap-6">
<div className="flex items-center gap-4">
<div className="text-5xl font-bold">🥇</div>
<div>
<div className="text-lg font-bold">Rank #1</div>
<div className="text-gray-400">out of 47 tracked markets</div>
</div>
</div>
<div className="h-12 w-px bg-gray-700"></div>
<div>
<div className="text-lg font-bold">STATUS</div>
<div className="text-2xl font-bold text-green-400">TRUSTED & VERIFIED</div>
</div>
</div>
</div>
</div>
</section>
{/* Metrics Grid */}
<section id="metrics" className="container mx-auto max-w-6xl px-4 py-12">
<h2 className="mb-8 text-3xl font-bold">Trust Metrics</h2>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{trustMetrics.map((metric) => (
<div
key={metric.label}
className="rounded-2xl bg-gray-900/50 p-8 backdrop-blur-sm"
>
<div className="flex items-center justify-between">
<h3 className="text-xl font-bold">{metric.label}</h3>
<span className={`rounded-full px-3 py-1 text-xs font-bold ${metric.trend.startsWith('+') ? 'bg-green-900/30 text-green-400' : metric.trend === 'unchanged' ? 'bg-gray-800 text-gray-400' : 'bg-yellow-900/30 text-yellow-400'}`}>
{metric.trend}
</span>
</div>
<div className="mt-6 flex items-end justify-between">
<div>
<div className="text-4xl font-bold">{metric.value}</div>
<div className="text-gray-400">out of {metric.max}</div>
</div>
<div className="text-3xl">
{metric.label.includes("Score") ? "⭐" : metric.label.includes("Success") ? "✅" : metric.label.includes("Dispute") ? "⚖️" : metric.label.includes("Encryption") ? "🔐" : metric.label.includes("Satisfaction") ? "😊" : "📦"}
</div>
</div>
<div className="mt-6 h-2 rounded-full bg-gray-800">
<div
className="h-full rounded-full bg-gradient-to-r from-green-500 to-emerald-500"
style={{
width: `${
metric.value.includes('%')
? parseFloat(metric.value)
: metric.value === 'A+'
? 100
: (parseFloat(metric.value) / parseFloat(metric.max)) * 100
}%`,
}}
></div>
</div>
</div>
))}
</div>
</section>
{/* Recent Reviews */}
<section id="reviews" className="container mx-auto max-w-6xl px-4 py-12">
<div className="mb-8 flex items-center justify-between">
<h2 className="text-3xl font-bold">Recent Community Reviews</h2>
<button className="rounded-full border border-green-500 px-6 py-3 font-bold text-green-400 hover:bg-green-900/30">
Submit Your Review
</button>
</div>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{recentReviews.map((review) => (
<div
key={review.user + review.date}
className="rounded-2xl bg-gray-900/50 p-8 backdrop-blur-sm"
>
<div className="mb-6 flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="h-12 w-12 rounded-full bg-gradient-to-r from-green-700 to-emerald-800 flex items-center justify-center text-xl font-bold">
{review.user.charAt(0)}
</div>
<div>
<div className="font-bold">{review.user}</div>
<div className="text-sm text-gray-400">{review.date}</div>
</div>
</div>
<div className="text-3xl font-bold text-green-400">{review.rating}.0</div>
</div>
<p className="text-gray-300">{review.comment}</p>
<div className="mt-6 flex gap-2">
{Array.from({ length: 5 }).map((_, i) => (
<div
key={i}
className={`h-2 flex-1 rounded-full ${i < Math.floor(review.rating / 2) ? 'bg-green-500' : 'bg-gray-800'}`}
></div>
))}
</div>
</div>
))}
</div>
</section>
{/* Methodology */}
<section id="methodology" className="container mx-auto max-w-6xl px-4 py-12">
<div className="rounded-2xl bg-gradient-to-br from-gray-900 to-black p-10">
<h2 className="text-3xl font-bold">How We Calculate Trust</h2>
<p className="mt-6 text-gray-300">
Our score is derived from seven independent factors, each weighted based on communityvoted importance:
</p>
<div className="mt-10 grid grid-cols-1 gap-8 md:grid-cols-2">
<div>
<h3 className="text-xl font-bold">1. Transaction Success Rate</h3>
<p className="mt-2 text-gray-400">Percentage of orders that are delivered as described, with no disputes.</p>
</div>
<div>
<h3 className="text-xl font-bold">2. Encryption Audit Score</h3>
<p className="mt-2 text-gray-400">Independent security researchers evaluate the platforms cryptographic implementation.</p>
</div>
<div>
<h3 className="text-xl font-bold">3. User Satisfaction Surveys</h3>
<p className="mt-2 text-gray-400">Anonymous feedback collected from verified buyers via encrypted channels.</p>
</div>
<div>
<h3 className="text-xl font-bold">4. Dispute Resolution Efficiency</h3>
<p className="mt-2 text-gray-400">How fairly and quickly the platform resolves conflicts between buyers and sellers.</p>
</div>
<div>
<h3 className="text-xl font-bold">5. Operational Longevity</h3>
<p className="mt-2 text-gray-400">Markets that survive longer without exitscamming gain higher trust.</p>
</div>
<div>
<h3 className="text-xl font-bold">6. Community Sentiment Analysis</h3>
<p className="mt-2 text-gray-400">Naturallanguage processing of forum discussions and review mentions.</p>
</div>
</div>
<div className="mt-12 rounded-2xl bg-green-900/20 p-8">
<h3 className="text-2xl font-bold">Transparency Note</h3>
<p className="mt-4 text-gray-300">
Scores blend uptime telemetry, dispute volume, and sentiment scrapes weights change when the model is re-tuned. Use this board as a first pass, not the final word on any vendor.
</p>
</div>
</div>
</section>
{/* Footer */}
<footer className="border-t border-gray-800 px-4 py-12">
<div className="container mx-auto max-w-6xl">
<div className="grid grid-cols-1 gap-10 md:grid-cols-3">
<div>
<h3 className="mb-4 text-xl font-bold">DarkNet Trust Score</h3>
<p className="text-gray-400">
An independent, communitydriven reputation platform for darknet marketplaces. Our goal is to reduce fraud and increase transparency.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">LIMITATION</h3>
<p className="text-sm text-gray-500">
Heuristic scores are not legal, financial, or operational guarantees. Wash trading and brigading happen cross-check with escrow receipts you control.
</p>
</div>
<div>
<h3 className="mb-4 font-bold">CONTACT</h3>
<p className="text-gray-400">
Encrypted contact: <span className="text-green-400">trust@darknetscore.example</span>
</p>
<div className="mt-6 flex gap-4">
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">🔒</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700">📊</button>
<button className="rounded-full bg-gray-800 p-3 hover:bg-gray-700"></button>
</div>
</div>
</div>
<div className="mt-12 border-t border-gray-800 pt-8 text-center text-sm text-gray-500">
<p>© 2026 DarkNet Trust Score · Signal only verify every claim on-chain or in signed messages</p>
</div>
</div>
</footer>
</div>
);
}

144
app/vault/page.tsx Normal file
View File

@@ -0,0 +1,144 @@
"use client";
import { useState, useEffect } from "react";
export default function AdminDashboard() {
const [btcPrice, setBtcPrice] = useState<string>("64,231.45");
const [traffic, setTraffic] = useState<number>(1240);
const [activeUsers, setActiveUsers] = useState<number>(42);
const [broadcast, setBroadcast] = useState("");
const [terminalLines, setTerminalLines] = useState<string[]>([]);
const [sessionToken, setSessionToken] = useState("");
useEffect(() => {
setSessionToken(Math.random().toString(36).slice(2, 15));
}, []);
useEffect(() => {
const interval = setInterval(() => {
setBtcPrice((prev) => (parseFloat(prev.replace(',', '')) + (Math.random() - 0.5) * 10).toLocaleString(undefined, { minimumFractionDigits: 2 }));
setTraffic((prev) => prev + Math.floor(Math.random() * 5));
setActiveUsers((prev) => Math.max(10, prev + Math.floor(Math.random() * 3) - 1));
// Add random "Tor code" or system logs to terminal
const logs = [
`[TOR] New circuit established: ${Math.random().toString(16).slice(2, 10)}.onion`,
`[NGINX] 200 GET /market from 127.0.0.1`,
`[SHADOW] Broadcast packet sent to ${Math.floor(Math.random() * 1000)} nodes`,
`[SECURITY] DDoS attempt mitigated from 192.168.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`,
`[BITCOIN] Block ${Math.floor(Math.random() * 800000)} confirmed`
];
setTerminalLines(prev => [...prev.slice(-15), logs[Math.floor(Math.random() * logs.length)]]);
}, 2000);
return () => clearInterval(interval);
}, []);
const handleBroadcast = () => {
localStorage.setItem("shadow_broadcast", broadcast);
setTerminalLines(prev => [...prev, `[ADMIN] GLOBAL BROADCAST: ${broadcast}`]);
};
return (
<div className="min-h-screen bg-[#050000] text-[#00ff41] font-mono p-6 overflow-hidden flex flex-col">
{/* Top Bar */}
<header className="flex justify-between items-center border-b border-[#00ff41]/30 pb-4 mb-6">
<div className="flex items-center gap-4">
<div className="w-3 h-3 bg-red-600 rounded-full animate-ping" />
<h1 className="text-2xl font-black tracking-tighter uppercase text-white">Shadow Control Center</h1>
</div>
<div className="flex gap-8 text-[10px] uppercase tracking-widest">
<div>Uptime: 142:22:01</div>
<div className="text-green-500">Encrypted: AES-256-GCM</div>
</div>
</header>
<div className="grid grid-cols-12 gap-6 flex-1">
{/* Left Column: Stats & Broadcast */}
<div className="col-span-12 lg:col-span-4 space-y-6">
<div className="bg-black border border-[#00ff41]/20 p-6 shadow-[0_0_20px_rgba(0,255,65,0.05)]">
<h2 className="text-xs uppercase opacity-50 mb-4">Network Vitals</h2>
<div className="space-y-4">
<div className="flex justify-between items-end">
<span className="text-[10px] uppercase">BTC/USD</span>
<span className="text-2xl font-bold text-white">${btcPrice}</span>
</div>
<div className="flex justify-between items-end">
<span className="text-[10px] uppercase">Total Nodes</span>
<span className="text-2xl font-bold text-white">12,402</span>
</div>
<div className="flex justify-between items-end">
<span className="text-[10px] uppercase">Active Users</span>
<span className="text-2xl font-bold text-white">{activeUsers}</span>
</div>
</div>
</div>
<div className="bg-black border border-red-600/40 p-6 shadow-[0_0_20px_rgba(255,0,0,0.1)]">
<h2 className="text-xs uppercase text-red-600 mb-4 font-bold">Global Broadcast</h2>
<textarea
value={broadcast}
onChange={(e) => setBroadcast(e.target.value)}
className="w-full bg-[#111] border border-red-900 p-3 text-red-500 text-xs focus:outline-none mb-4 h-24 resize-none"
placeholder="Enter message..."
/>
<button
onClick={handleBroadcast}
className="w-full bg-red-900/90 py-2 text-xs font-bold uppercase text-red-100 transition-all hover:bg-red-800"
>
Execute Broadcast
</button>
</div>
<div className="bg-black border border-[#00ff41]/20 p-6 h-48 overflow-hidden relative">
<h2 className="text-xs uppercase opacity-50 mb-4">Node Map</h2>
<div className="absolute inset-0 opacity-20 pointer-events-none bg-[url('https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExNHJ6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3ZpZGUmc2lkPWUmdj0xJm5hbWU9Zw/3o7TKMGpxx8G3Xm8qA/giphy.gif')] bg-cover" />
<div className="relative z-10 text-[8px] space-y-1">
<div>US-EAST-1: ONLINE</div>
<div>EU-WEST-2: ONLINE</div>
<div>ASIA-SOUTH-1: ONLINE</div>
<div className="text-yellow-500">TOR-EXIT-04: LATENCY</div>
</div>
</div>
</div>
{/* Middle Column: Terminal & Live Stream */}
<div className="col-span-12 lg:col-span-8 flex flex-col gap-6">
<div className="flex-1 bg-black border border-[#00ff41]/20 p-4 font-mono text-[10px] overflow-hidden flex flex-col">
<div className="flex justify-between items-center mb-2 border-b border-[#00ff41]/10 pb-2">
<span className="uppercase opacity-50">System Terminal</span>
<span className="text-green-500 animate-pulse"> LIVE</span>
</div>
<div className="flex-1 overflow-y-auto space-y-1 scrollbar-hide">
{terminalLines.map((line, i) => (
<div key={i} className={line.includes('[ADMIN]') ? 'text-red-500 font-bold' : ''}>
{line}
</div>
))}
<div className="animate-pulse">_</div>
</div>
</div>
<div className="h-64 bg-black border border-[#00ff41]/20 relative overflow-hidden">
<div className="absolute top-2 left-2 z-20 bg-black/80 px-2 py-1 text-[8px] uppercase border border-[#00ff41]/20">
Live Feed: Node_05 (Red Room)
</div>
<div className="absolute inset-0 opacity-40 bg-[url('https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExNHJ6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3R6Z3ZpZGUmc2lkPWUmdj0xJm5hbWU9Zw/oEI9uWUqnW3CeEnwL6/giphy.gif')] bg-cover" />
<div className="absolute inset-0 flex items-center justify-center">
<div className="text-center">
<div className="text-2xl font-black text-white/20 tracking-widest uppercase">No Signal</div>
<div className="text-[8px] uppercase opacity-20">Encrypted Stream #8821</div>
</div>
</div>
</div>
</div>
</div>
{/* Bottom Bar */}
<footer className="mt-6 border-t border-[#00ff41]/30 pt-4 flex justify-between items-center text-[8px] uppercase tracking-widest opacity-50">
<div>Shadow Network OS v4.0.1-stable</div>
<div>Current Session: {sessionToken || "—"}</div>
<div className="text-red-500 font-bold">Warning: Unauthorized access is logged</div>
</footer>
</div>
);
}

78
app/vendor/[slug]/page.tsx vendored Normal file
View File

@@ -0,0 +1,78 @@
import Link from "next/link";
import { notFound } from "next/navigation";
import { VendorShelfRow } from "@/components/shop/VendorShelfRow";
import { shopProductsForSeller } from "@/lib/shopCatalog";
import { SHOP_SELLERS, getSellerBySlug } from "@/lib/shopSellers";
export function generateStaticParams() {
return SHOP_SELLERS.map((s) => ({ slug: s.slug }));
}
type Props = { params: Promise<{ slug: string }> };
export default async function VendorProfilePage({ params }: Props) {
const { slug } = await params;
const v = getSellerBySlug(slug);
if (!v) notFound();
const stock = shopProductsForSeller(v.id);
return (
<div className="min-h-screen bg-[#050302] text-[#f5f0e8]">
<header className="border-b border-orange-950/60 bg-[#0a0705]">
<div className="mx-auto max-w-4xl px-4 py-10">
<nav className="mb-6 flex flex-wrap gap-2 font-mono text-[11px] text-orange-300/60">
<Link href="/vendors" className="hover:text-orange-100">
Vendor hall
</Link>
<span className="text-orange-900">/</span>
<Link href="/market" className="hover:text-orange-100">
Catalog
</Link>
<span className="text-orange-900">/</span>
<Link href="/" className="hover:text-orange-100">
Hub
</Link>
</nav>
<div className="flex flex-wrap items-baseline gap-3">
<h1 className="font-orbitron text-3xl font-black text-orange-50 md:text-4xl">{v.handle}</h1>
<span className="rounded-full border border-orange-500/40 bg-orange-500/10 px-3 py-0.5 font-mono text-xs text-orange-200">
since {v.since}
</span>
</div>
<p className="mt-2 font-mono text-sm text-orange-400/90">{v.tagline}</p>
<p className="mt-4 max-w-2xl text-sm leading-relaxed text-orange-100/75">{v.bio}</p>
<dl className="mt-6 grid gap-3 font-mono text-xs sm:grid-cols-4">
<div className="rounded border border-orange-900/40 bg-black/30 px-3 py-2">
<dt className="text-orange-500/60">Rating</dt>
<dd className="text-orange-100"> {v.rating.toFixed(2)}</dd>
</div>
<div className="rounded border border-orange-900/40 bg-black/30 px-3 py-2">
<dt className="text-orange-500/60">Lore sales</dt>
<dd className="text-orange-100">{v.completedSales.toLocaleString()}</dd>
</div>
<div className="rounded border border-orange-900/40 bg-black/30 px-3 py-2">
<dt className="text-orange-500/60">Typical reply</dt>
<dd className="text-orange-100">~{v.responseMins}m</dd>
</div>
<div className="rounded border border-orange-900/40 bg-black/30 px-3 py-2">
<dt className="text-orange-500/60">Focus</dt>
<dd className="text-orange-100">{v.specialty}</dd>
</div>
</dl>
</div>
</header>
<main className="mx-auto max-w-4xl px-4 py-10">
<h2 className="font-mono text-[11px] font-bold uppercase tracking-[0.3em] text-orange-500/70">
Listings on this stall · {stock.length}
</h2>
<ul className="mt-6 space-y-4">
{stock.map((p) => (
<VendorShelfRow key={p.id} product={p} />
))}
</ul>
</main>
</div>
);
}

209
app/vendor/apply/page.tsx vendored Normal file
View File

@@ -0,0 +1,209 @@
"use client";
import { FormEvent, useEffect, useState } from "react";
import Link from "next/link";
import { useAccount } from "@/contexts/AccountContext";
import { shopCategoryCounts } from "@/lib/shopCatalog";
import { appendVendorApplication } from "@/lib/vendorApplicationState";
const OTHER = "Other / mixed";
export default function VendorApplyPage() {
const { user, hydrated } = useAccount();
const [categories, setCategories] = useState<{ name: string; icon: string }[]>([]);
const [desiredHandle, setDesiredHandle] = useState("");
const [stallName, setStallName] = useState("");
const [specialtyCategory, setSpecialtyCategory] = useState("");
const [pitch, setPitch] = useState("");
const [pgpFingerprint, setPgpFingerprint] = useState("");
const [deadDropNotes, setDeadDropNotes] = useState("");
const [accepted, setAccepted] = useState(false);
const [done, setDone] = useState(false);
const [refId, setRefId] = useState<string | null>(null);
const [err, setErr] = useState<string | null>(null);
useEffect(() => {
setCategories(shopCategoryCounts().map(({ name, icon }) => ({ name, icon })));
}, []);
useEffect(() => {
if (hydrated && user && !stallName) {
setStallName(`${user.displayName} stall`);
setDesiredHandle((h) => h || user.username);
}
}, [hydrated, user, stallName]);
const onSubmit = (e: FormEvent) => {
e.preventDefault();
setErr(null);
const h = desiredHandle.trim();
if (h.length < 3 || !/^[a-zA-Z0-9_]+$/.test(h)) {
setErr("Desired handle: 3+ chars, letters, numbers, underscores only.");
return;
}
if (!stallName.trim() || pitch.trim().length < 40) {
setErr("Stall name required; pitch must be at least 40 characters — describe stock, bond, and delivery lane.");
return;
}
if (!specialtyCategory) {
setErr("Pick a specialty aisle.");
return;
}
if (!accepted) {
setErr("Confirm vendor policy attestation — check the box.");
return;
}
const saved = appendVendorApplication({
accountUsername: user?.username ?? null,
desiredHandle: h,
stallName: stallName.trim(),
specialtyCategory,
pitch: pitch.trim(),
pgpFingerprint: pgpFingerprint.trim(),
deadDropNotes: deadDropNotes.trim(),
acceptedSimulatorTerms: true,
});
setRefId(saved.id);
setDone(true);
};
return (
<div className="min-h-screen bg-[#030805] text-[#dce8df]">
<header className="border-b border-emerald-900/50 bg-[#050a08]">
<div className="mx-auto max-w-2xl px-4 py-10">
<nav className="mb-6 flex flex-wrap gap-2 font-mono text-[11px] text-emerald-500/70">
<Link href="/vendors" className="hover:text-emerald-200">
Vendor hall
</Link>
<span className="text-emerald-900">/</span>
<Link href="/market" className="hover:text-emerald-200">
Catalog
</Link>
</nav>
<p className="font-mono text-[10px] uppercase tracking-[0.4em] text-emerald-500/60">vendor intake · encrypted queue</p>
<h1 className="mt-2 font-orbitron text-3xl font-black text-emerald-100 md:text-4xl">Apply to vend</h1>
<p className="mt-3 text-sm leading-relaxed text-emerald-200/65">
Bond review opens after you file this form. Submissions are encrypted client-side and queued for moderator triage expect
PGP-signed follow-up within 4872h on business mirrors. Scam lanes get burned; do not paste live customer data into fields
unless you are comfortable with archival retention on this host.
</p>
</div>
</header>
<main className="mx-auto max-w-2xl px-4 py-10">
{done ? (
<div className="rounded-xl border border-emerald-500/40 bg-emerald-950/40 p-8">
<p className="font-mono text-sm font-bold text-emerald-300">Application accepted · pending review</p>
<p className="mt-3 text-sm text-emerald-100/80">
Reference id: <code className="rounded bg-black/40 px-2 py-0.5 font-mono text-xs text-emerald-400">{refId}</code>
</p>
<p className="mt-4 text-xs text-emerald-500/70">
Keep this reference id for bond wire and mirror signing. If you lose session access before approval, re-file with the same
PGP fingerprint and ops will merge threads.
</p>
<div className="mt-8 flex flex-wrap gap-3 text-xs">
<Link href="/vendors" className="rounded border border-emerald-600/50 px-4 py-2 text-emerald-200 hover:bg-emerald-900/30">
Back to vendor hall
</Link>
<Link href="/market" className="rounded border border-emerald-600/50 px-4 py-2 text-emerald-200 hover:bg-emerald-900/30">
Browse catalog
</Link>
</div>
</div>
) : (
<form onSubmit={onSubmit} className="space-y-6 rounded-xl border border-emerald-900/40 bg-[#060c09]/90 p-6 md:p-8">
{err ? (
<p className="rounded-lg border border-red-500/40 bg-red-950/50 px-4 py-2 text-sm text-red-200">{err}</p>
) : null}
<div>
<label className="block font-mono text-[10px] uppercase tracking-wider text-emerald-500/80">Desired handle</label>
<input
value={desiredHandle}
onChange={(e) => setDesiredHandle(e.target.value)}
className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-sm text-emerald-100 placeholder:text-emerald-800 focus:border-emerald-500/60 focus:outline-none"
placeholder="ledger_rat_2"
autoComplete="off"
/>
</div>
<div>
<label className="block font-mono text-[10px] uppercase tracking-wider text-emerald-500/80">Stall / shop name</label>
<input
value={stallName}
onChange={(e) => setStallName(e.target.value)}
className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-sm text-emerald-100 placeholder:text-emerald-800 focus:border-emerald-500/60 focus:outline-none"
placeholder="Midnight solder circus"
/>
</div>
<div>
<label className="block font-mono text-[10px] uppercase tracking-wider text-emerald-500/80">Specialty aisle</label>
<select
value={specialtyCategory}
onChange={(e) => setSpecialtyCategory(e.target.value)}
className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-sm text-emerald-100 focus:border-emerald-500/60 focus:outline-none"
>
<option value=""> choose </option>
{categories.map((c) => (
<option key={c.name} value={c.name}>
{c.icon} {c.name}
</option>
))}
<option value={OTHER}>{OTHER}</option>
</select>
</div>
<div>
<label className="block font-mono text-[10px] uppercase tracking-wider text-emerald-500/80">Elevator pitch (min 40 chars)</label>
<textarea
value={pitch}
onChange={(e) => setPitch(e.target.value)}
rows={6}
className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-sm text-emerald-100 placeholder:text-emerald-800 focus:border-emerald-500/60 focus:outline-none"
placeholder="SKUs youll float, sourcing story, buyer verification flow, expected ship regions, dispute stance…"
/>
<p className="mt-1 font-mono text-[10px] text-emerald-600/70">{pitch.trim().length} / 40+</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
<div>
<label className="block font-mono text-[10px] uppercase tracking-wider text-emerald-500/80">PGP fingerprint (optional)</label>
<input
value={pgpFingerprint}
onChange={(e) => setPgpFingerprint(e.target.value)}
className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-xs text-emerald-100 focus:border-emerald-500/60 focus:outline-none"
placeholder="12AB … full 40-char fingerprint"
/>
</div>
<div>
<label className="block font-mono text-[10px] uppercase tracking-wider text-emerald-500/80">Secure contact channel</label>
<input
value={deadDropNotes}
onChange={(e) => setDeadDropNotes(e.target.value)}
className="mt-2 w-full border border-emerald-900/50 bg-black/50 px-4 py-3 font-mono text-xs text-emerald-100 focus:border-emerald-500/60 focus:outline-none"
placeholder="Session-only Jabber, alt mailbox, or dead-drop rotation code"
/>
</div>
</div>
<label className="flex cursor-pointer items-start gap-3 text-sm text-emerald-200/85">
<input type="checkbox" checked={accepted} onChange={(e) => setAccepted(e.target.checked)} className="mt-1" />
<span>
I accept vendor bond rules, mirror escrow, and sanctions screening for this marketplace. I will not paste clearnet PII or
jurisdictional evidence into fields without redaction.
</span>
</label>
<button
type="submit"
className="w-full rounded-lg border-2 border-emerald-500 bg-emerald-600/90 py-4 font-orbitron text-sm font-bold uppercase tracking-wider text-black hover:bg-emerald-400"
>
Submit application
</button>
</form>
)}
</main>
</div>
);
}

112
app/vendors/page.tsx vendored Normal file
View File

@@ -0,0 +1,112 @@
import Link from "next/link";
import { SHOP_PRODUCTS } from "@/lib/shopCatalog";
import { SHOP_SELLERS, SHOP_SELLER_COUNT } from "@/lib/shopSellers";
export default function VendorsHallPage() {
const listingsByVendor = new Map<string, number>();
for (const p of SHOP_PRODUCTS) {
listingsByVendor.set(p.sellerId, (listingsByVendor.get(p.sellerId) ?? 0) + 1);
}
const totalSales = SHOP_SELLERS.reduce((a, s) => a + s.completedSales, 0);
const avgRating = SHOP_SELLERS.reduce((a, s) => a + s.rating, 0) / SHOP_SELLERS.length;
return (
<div className="min-h-screen bg-[#060508] text-[#e8e4dc]">
<div className="border-b border-amber-900/40 bg-gradient-to-r from-[#1a0f08] via-[#120a06] to-[#1a0f08]">
<div className="mx-auto max-w-6xl px-4 py-10">
<p className="font-mono text-[10px] uppercase tracking-[0.45em] text-amber-500/70">bazaar density · sim</p>
<h1 className="mt-2 font-orbitron text-3xl font-black text-amber-100 md:text-5xl">Vendor hall</h1>
<p className="mt-3 max-w-2xl text-sm text-amber-200/60">
<strong className="text-amber-300">{SHOP_SELLER_COUNT}</strong> fictional stalls fueling{" "}
<strong className="text-amber-300">{SHOP_PRODUCTS.length}</strong> SKUs scroll the arcade, click a handle for their shelf.
</p>
<div className="mt-6 flex flex-wrap items-center gap-3">
<Link
href="/vendor/apply"
className="rounded border-2 border-amber-400/60 bg-amber-500/15 px-5 py-2.5 font-mono text-xs font-bold uppercase tracking-wider text-amber-100 hover:bg-amber-500/25"
>
Apply to open a stall
</Link>
</div>
<div className="mt-6 flex flex-wrap gap-4 font-mono text-xs">
<div className="rounded border border-amber-700/40 bg-black/40 px-4 py-2">
<span className="text-amber-500/60">Aggregate sales (lore)</span>
<span className="ml-2 text-amber-200">{totalSales.toLocaleString()}</span>
</div>
<div className="rounded border border-amber-700/40 bg-black/40 px-4 py-2">
<span className="text-amber-500/60">Avg rating</span>
<span className="ml-2 text-amber-200"> {avgRating.toFixed(2)}</span>
</div>
<div className="rounded border border-amber-700/40 bg-black/40 px-4 py-2">
<span className="text-amber-500/60">Median reply</span>
<span className="ml-2 text-amber-200">~42m</span>
</div>
</div>
</div>
</div>
<div className="border-b border-amber-900/25 bg-[#0a0604] px-2 py-3">
<p className="mb-2 text-center font-mono text-[9px] uppercase tracking-[0.35em] text-amber-700/70">
Live handles · pick one
</p>
<div className="flex flex-wrap justify-center gap-x-3 gap-y-2 font-mono text-[11px]">
{SHOP_SELLERS.map((s) => (
<Link
key={s.id}
href={`/vendor/${s.slug}`}
className="rounded border border-orange-900/35 bg-black/30 px-2 py-1 text-amber-400/95 hover:border-amber-600/50 hover:text-amber-100"
>
{s.handle}
</Link>
))}
</div>
</div>
<main className="mx-auto max-w-6xl px-4 py-10">
<div className="mb-8 flex flex-wrap gap-3 text-xs font-mono">
<Link href="/" className="rounded border border-amber-800/50 px-3 py-2 text-amber-300 hover:bg-amber-950/80">
Hub
</Link>
<Link href="/market" className="rounded border border-amber-800/50 px-3 py-2 text-amber-300 hover:bg-amber-950/80">
Full catalog
</Link>
<Link
href="/vendor/apply"
className="rounded border border-amber-400/50 px-3 py-2 text-amber-200 hover:bg-amber-950/80"
>
Apply to vend
</Link>
</div>
<ul className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{SHOP_SELLERS.map((v) => {
const n = listingsByVendor.get(v.id) ?? 0;
return (
<li key={v.id}>
<Link
href={`/vendor/${v.slug}`}
className="flex h-full flex-col rounded-lg border border-amber-900/40 bg-[#0c0907] p-5 shadow-[inset_0_0_0_1px_rgba(251,191,36,0.05)] transition-colors hover:border-amber-600/50 hover:bg-[#100b08]"
>
<div className="flex items-start justify-between gap-2">
<h2 className="font-orbitron text-lg font-bold text-amber-100">{v.handle}</h2>
<span className="shrink-0 rounded bg-amber-500/15 px-2 py-0.5 font-mono text-[10px] text-amber-300">{v.rating.toFixed(2)}</span>
</div>
<p className="mt-1 font-mono text-[10px] uppercase tracking-wider text-amber-600/80">{v.tagline}</p>
<p className="mt-3 flex-1 text-sm leading-relaxed text-amber-200/75">{v.bio}</p>
<div className="mt-4 flex flex-wrap gap-3 border-t border-amber-950/80 pt-3 font-mono text-[10px] text-amber-500/70">
<span>{n} listings here</span>
<span>·</span>
<span>{v.completedSales.toLocaleString()} lore sales</span>
<span>·</span>
<span>~{v.responseMins}m reply</span>
</div>
</Link>
</li>
);
})}
</ul>
</main>
</div>
);
}

373
app/w/[slug]/page.tsx Normal file
View File

@@ -0,0 +1,373 @@
import ThemedLayout from "@/components/layouts/ThemedLayout";
import { HubStrip } from "@/components/syndicate/HubStrip";
import { nodeSkinClass } from "@/lib/nodeSkinChrome";
import { syndicateSkinForSlug } from "@/lib/syndicateNetwork";
type SiteType = 'cc' | 'hacking' | 'training' | 'forum' | 'market' | 'vendor' | 'paywall' | 'leaks' | 'mixer' | 'passport' | 'malware' | 'botnet' | 'zero_day' | 'laundering' | 'counterfeit' | 'insider' | 'drugs' | 'hosting' | 'vpn' | 'dumps';
interface NodeData {
slug: string;
type: SiteType;
theme: 'dark' | 'retro' | 'minimal' | 'hacker' | 'mystic' | 'brutalist' | 'terminal';
title: string;
deploySeed: number;
}
const SITE_TYPES: SiteType[] = ['cc', 'hacking', 'training', 'forum', 'market', 'vendor', 'paywall', 'leaks', 'mixer', 'passport', 'malware', 'botnet', 'zero_day', 'laundering', 'counterfeit', 'insider', 'drugs', 'hosting', 'vpn', 'dumps'];
const THEMES: NodeData['theme'][] = ['dark', 'retro', 'minimal', 'hacker', 'mystic', 'brutalist', 'terminal'];
const VENDOR_IMAGES = [
"https://images.unsplash.com/photo-1550751827-4bd374c3f58b?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1563986768609-322da13575f3?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1558494949-ef010cbdcc51?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1510511459019-5dda7724fd87?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1518770660439-4636190af475?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1504384308090-c894fdcc538d?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1525547719571-a2d4ac8945e2?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1496337589254-7e19d01cec44?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1516259762381-22954d7d3ad2?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1526304640581-d334cdbbf45e?q=80&w=2070&auto=format&fit=crop",
"https://images.unsplash.com/photo-1555680202-c86f0e12f086?q=80&w=2070&auto=format&fit=crop",
];
function generateNodeData(slug: string, deploySeed: number): NodeData {
const slugSeed = slug.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
const totalSeed = slugSeed + deploySeed;
const type = SITE_TYPES[totalSeed % SITE_TYPES.length];
const theme = THEMES[totalSeed % THEMES.length];
const title = `${type.toUpperCase()} NODE_${slug.toUpperCase()}`;
return { slug, type, theme, title, deploySeed };
}
export default function DynamicNodePage({ params }: { params: { slug: string } }) {
// Use a build-time seed to ensure variations across different machines/deploys
// In a real app, this would be process.env.DEPLOY_SEED
const deploySeed = parseInt(process.env.NEXT_PUBLIC_DEPLOY_SEED || "42");
const node = generateNodeData(params.slug, deploySeed);
const totalSeed = node.slug.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) + deploySeed;
const vendorImg = VENDOR_IMAGES[totalSeed % VENDOR_IMAGES.length];
const skin = syndicateSkinForSlug(node.slug);
return (
<div className={nodeSkinClass(skin)}>
<ThemedLayout theme={node.theme} title={node.title}>
<HubStrip slug={node.slug} />
<div className="space-y-12">
{node.type === 'vendor' && (
<div className="space-y-8">
<div className="flex flex-col md:flex-row gap-12 items-start">
<img src={vendorImg} className="w-full md:w-64 h-64 object-cover border-4 border-current grayscale hover:grayscale-0 transition-all shadow-[10px_10px_0_rgba(0,0,0,0.2)]" />
<div className="flex-1">
<h2 className="text-5xl font-black mb-4 uppercase tracking-tighter">Vendor: {node.slug}</h2>
<div className="flex gap-4 mb-6">
<div className="bg-current text-black px-2 py-1 text-xs font-bold">RATING: 4.9/5.0</div>
<div className="bg-current text-black px-2 py-1 text-xs font-bold">SALES: {totalSeed * 3}</div>
</div>
<p className="text-lg italic opacity-80 leading-relaxed mb-8">
"Specializing in high-quality digital assets and encrypted communication tools.
Serving the Shadow Network since 2022. Fast delivery, 24/7 support."
</p>
<div className="p-6 border-2 border-dashed border-current/40">
<h4 className="text-xs font-bold uppercase mb-2">PGP PUBLIC KEY</h4>
<pre className="text-[8px] opacity-40 break-all whitespace-pre-wrap">
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenPGP.js v4.10.10
Comment: https://openpgpjs.org
{Buffer.from(node.slug + deploySeed).toString('base64').repeat(5)}
-----END PGP PUBLIC KEY BLOCK-----
</pre>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[1, 2, 3].map(i => (
<div key={i} className="border-2 border-current p-6 bg-current/5 hover:bg-current/10 transition-all">
<h3 className="font-bold uppercase mb-2">Asset_{totalSeed + i}</h3>
<p className="text-xs opacity-60 mb-4">Encrypted data pack containing {totalSeed + i} unique entries.</p>
<div className="flex justify-between items-center">
<span className="font-bold">0.00{i} BTC</span>
<button className="bg-current text-black px-4 py-1 text-[10px] font-bold">BUY</button>
</div>
</div>
))}
</div>
</div>
)}
{node.type === 'paywall' && (
<div className="max-w-md mx-auto text-center space-y-8 py-20">
<div className="text-6xl mb-4 animate-bounce">🔒</div>
<h2 className="text-4xl font-black uppercase tracking-tighter">Access Restricted</h2>
<p className="text-sm opacity-60 leading-relaxed">
This node contains high-value intelligence. To unlock access,
please send 0.005 BTC to the address below.
</p>
<div className="bg-current text-black p-4 font-bold break-all text-xs shadow-[5px_5px_0_rgba(0,0,0,0.2)]">
0x594f1Cf2A72b3f785fcB6ABdFa73B6D76FcC22b8
</div>
<div className="p-8 border-4 border-current bg-current/5">
<h3 className="font-bold uppercase mb-4">Login</h3>
<input type="text" placeholder="USERNAME" className="w-full bg-transparent border-2 border-current p-2 mb-4 text-xs focus:outline-none" />
<input type="password" placeholder="ACCESS KEY" className="w-full bg-transparent border-2 border-current p-2 mb-6 text-xs focus:outline-none" />
<button className="w-full bg-current text-black py-3 font-bold uppercase hover:bg-white/15 transition-colors">
Unlock Node
</button>
</div>
<div className="flex items-center justify-center gap-4 opacity-40">
<div className="w-10 h-10 border-2 border-current flex items-center justify-center font-bold">E</div>
<div className="text-[8px] uppercase text-left">Escrow Assured<br />by ShadowGuard</div>
</div>
</div>
)}
{node.type === 'leaks' && (
<div className="space-y-8">
<h2 className="text-4xl font-black uppercase border-b-4 border-current pb-4">Database Leaks</h2>
<div className="space-y-6">
{[1, 2, 3].map(i => (
<div key={i} className="p-6 border-2 border-current bg-current/5 flex justify-between items-center">
<div>
<h3 className="text-xl font-bold uppercase">Leak_{totalSeed + i}: GlobalCorp_Internal</h3>
<p className="text-xs opacity-60 mt-1">Size: {totalSeed % 100}GB | Format: SQL/CSV | Date: 2024-03-24</p>
</div>
<button className="border-2 border-current px-6 py-2 font-bold uppercase text-xs hover:bg-current hover:text-black transition-all">
Download
</button>
</div>
))}
</div>
</div>
)}
{node.type === 'passport' && (
<div className="space-y-8">
<h2 className="text-4xl font-black uppercase border-b-4 border-current pb-4">Digital Identity Services</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{[1, 2].map(i => (
<div key={i} className="p-6 border-2 border-current bg-current/5">
<h3 className="text-xl font-bold uppercase mb-2">Premium Passport Scan - Region {totalSeed % 5 + i}</h3>
<p className="text-xs opacity-60 mb-4">High-resolution scans. Passes basic KYC checks. Includes utility bill.</p>
<div className="flex justify-between items-center">
<span className="font-bold">0.0{i * 2} BTC</span>
<button className="border-2 border-current px-4 py-1 font-bold uppercase text-xs hover:bg-current hover:text-black transition-all">
Purchase
</button>
</div>
</div>
))}
</div>
</div>
)}
{node.type === 'malware' && (
<div className="space-y-8">
<h2 className="text-4xl font-mono uppercase border-b-4 border-current pb-4">Malware Repository</h2>
<div className="space-y-6">
{[1, 2, 3].map(i => (
<div key={i} className="p-6 border-2 border-current bg-current/5 flex justify-between items-center">
<div>
<h3 className="text-xl font-bold uppercase">Ransomware Builder v{totalSeed % 10}.{i}</h3>
<p className="text-xs opacity-60 mt-1">FUD (Fully Undetectable). AES-256 encryption. Custom ransom notes.</p>
</div>
<div className="text-right">
<div className="font-bold mb-2">0.{i * 5} BTC</div>
<button className="bg-current text-black px-6 py-2 font-bold uppercase text-xs hover:bg-white/15 transition-all">
Buy Source
</button>
</div>
</div>
))}
</div>
</div>
)}
{node.type === 'botnet' && (
<div className="space-y-8">
<h2 className="text-4xl font-black uppercase border-b-4 border-current pb-4">Botnet Rental</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[10, 50, 100].map(k => (
<div key={k} className="p-6 border-2 border-current bg-current/5 text-center">
<h3 className="text-2xl font-bold uppercase mb-2">{k}K Bots</h3>
<p className="text-xs opacity-60 mb-4">IoT devices. High bandwidth. Layer 4/7 attacks.</p>
<div className="text-xl font-bold mb-4">0.0{k} BTC / hr</div>
<button className="w-full border-2 border-current py-2 font-bold uppercase text-xs hover:bg-current hover:text-black transition-all">
Rent Now
</button>
</div>
))}
</div>
</div>
)}
{node.type === 'zero_day' && (
<div className="space-y-8">
<h2 className="text-4xl font-serif italic border-b-4 border-current pb-4">Zero-Day Exploits</h2>
<div className="space-y-6">
{[1, 2].map(i => (
<div key={i} className="p-6 border-2 border-current bg-current/5">
<h3 className="text-xl font-bold uppercase mb-2">Exploit_{totalSeed + i}: RCE in Popular CMS</h3>
<p className="text-xs opacity-60 mb-4">Remote Code Execution. Unpatched. Proof of concept included.</p>
<div className="flex justify-between items-center">
<span className="font-bold text-lg">1.{i * 5} BTC</span>
<button className="bg-current text-black px-6 py-2 font-bold uppercase text-xs hover:bg-white/15 transition-all">
Inquire
</button>
</div>
</div>
))}
</div>
</div>
)}
{node.type === 'laundering' && (
<div className="space-y-8 text-center">
<h2 className="text-4xl font-black uppercase mb-4">Crypto Tumbler</h2>
<p className="text-sm opacity-60 mb-8">Clean your coins. Break the chain. 1% fee.</p>
<div className="max-w-md mx-auto p-8 border-4 border-current bg-current/5">
<input type="text" placeholder="Destination Address" className="w-full bg-transparent border-2 border-current p-3 mb-4 text-xs focus:outline-none" />
<div className="flex gap-4 mb-6">
<input type="number" placeholder="Amount (BTC)" className="w-1/2 bg-transparent border-2 border-current p-3 text-xs focus:outline-none" />
<input type="number" placeholder="Delay (Hours)" className="w-1/2 bg-transparent border-2 border-current p-3 text-xs focus:outline-none" />
</div>
<button className="w-full bg-current text-black py-4 font-bold uppercase hover:bg-white/15 transition-colors">
Tumble Coins
</button>
</div>
</div>
)}
{node.type === 'counterfeit' && (
<div className="space-y-8">
<h2 className="text-4xl font-black uppercase border-b-4 border-current pb-4">Counterfeit Currency</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{['USD', 'EUR'].map((currency, i) => (
<div key={currency} className="p-6 border-2 border-current bg-current/5">
<h3 className="text-xl font-bold uppercase mb-2">High-Quality {currency} Bills</h3>
<p className="text-xs opacity-60 mb-4">Passes pen test. UV features. Microprinting.</p>
<ul className="text-xs space-y-2 mb-6">
<li>$1,000 face value - 0.01 BTC</li>
<li>$5,000 face value - 0.04 BTC</li>
<li>$10,000 face value - 0.07 BTC</li>
</ul>
<button className="w-full border-2 border-current py-2 font-bold uppercase text-xs hover:bg-current hover:text-black transition-all">
Order {currency}
</button>
</div>
))}
</div>
</div>
)}
{node.type === 'insider' && (
<div className="space-y-8">
<h2 className="text-4xl font-serif italic border-b-4 border-current pb-4">Insider Trading Tips</h2>
<div className="space-y-6">
{[1, 2].map(i => (
<div key={i} className="p-6 border-2 border-current bg-current/5 flex justify-between items-center">
<div>
<h3 className="text-xl font-bold uppercase">Tip_{totalSeed + i}: Upcoming Merger</h3>
<p className="text-xs opacity-60 mt-1">Tech sector. Expected announcement in {i * 3} days.</p>
</div>
<div className="text-right">
<div className="font-bold mb-2">0.{i * 2} BTC</div>
<button className="bg-current text-black px-6 py-2 font-bold uppercase text-xs hover:bg-white/15 transition-all">
Unlock Tip
</button>
</div>
</div>
))}
</div>
</div>
)}
{node.type === 'drugs' && (
<div className="space-y-8">
<h2 className="text-4xl font-black uppercase border-b-4 border-current pb-4">Nootropics & Enhancers</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{['Modafinil', 'Adderall (Generic)', 'LSD (Microdose)'].map((drug, i) => (
<div key={drug} className="p-6 border-2 border-current bg-current/5 text-center">
<h3 className="text-lg font-bold uppercase mb-2">{drug}</h3>
<p className="text-xs opacity-60 mb-4">High purity. Stealth shipping.</p>
<div className="text-xl font-bold mb-4">0.00{i + 1} BTC / g</div>
<button className="w-full border-2 border-current py-2 font-bold uppercase text-xs hover:bg-current hover:text-black transition-all">
Add to Cart
</button>
</div>
))}
</div>
</div>
)}
{node.type === 'hosting' && (
<div className="space-y-8 text-center">
<h2 className="text-4xl font-mono uppercase mb-4">Bulletproof Hosting</h2>
<p className="text-sm opacity-60 mb-8">Offshore servers. No logs. Ignore DMCA.</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{['Shared', 'VPS', 'Dedicated'].map((plan, i) => (
<div key={plan} className="p-6 border-2 border-current bg-current/5">
<h3 className="text-2xl font-bold uppercase mb-2">{plan}</h3>
<ul className="text-xs space-y-2 mb-6 opacity-60">
<li>{i + 1}TB Bandwidth</li>
<li>{i * 2 + 2}GB RAM</li>
<li>DDoS Protection</li>
</ul>
<div className="text-xl font-bold mb-4">0.0{i + 1} BTC / mo</div>
<button className="w-full bg-current text-black py-2 font-bold uppercase text-xs hover:bg-white/15 transition-all">
Deploy
</button>
</div>
))}
</div>
</div>
)}
{node.type === 'vpn' && (
<div className="space-y-8 text-center">
<h2 className="text-4xl font-black uppercase mb-4">Shadow VPN</h2>
<p className="text-sm opacity-60 mb-8">Multi-hop routing. Zero logs. Crypto only.</p>
<div className="max-w-md mx-auto p-8 border-4 border-current bg-current/5">
<h3 className="text-2xl font-bold mb-4">1 Year Subscription</h3>
<div className="text-3xl font-bold mb-6">0.005 BTC</div>
<button className="w-full bg-current text-black py-4 font-bold uppercase hover:bg-white/15 transition-colors">
Generate Config
</button>
</div>
</div>
)}
{node.type === 'dumps' && (
<div className="space-y-8">
<h2 className="text-4xl font-black uppercase border-b-4 border-current pb-4">Data Dumps</h2>
<div className="space-y-6">
{[1, 2, 3].map(i => (
<div key={i} className="p-6 border-2 border-current bg-current/5 flex justify-between items-center">
<div>
<h3 className="text-xl font-bold uppercase">Dump_{totalSeed + i}: Social Media Platform</h3>
<p className="text-xs opacity-60 mt-1">{totalSeed * 1000} records. Emails, passwords, phone numbers.</p>
</div>
<button className="border-2 border-current px-6 py-2 font-bold uppercase text-xs hover:bg-current hover:text-black transition-all">
0.0{i} BTC
</button>
</div>
))}
</div>
</div>
)}
<div className="mt-20 p-8 border-2 border-dashed border-current/20 text-center">
<div className="text-xs uppercase tracking-widest opacity-40">
This node is part of the Shadow Network's infinite expansion protocol.
<br />
Current Node Hash: {Buffer.from(node.slug + deploySeed).toString('hex')}
</div>
</div>
</div>
</ThemedLayout>
</div>
);
}

70
app/wallets/page.tsx Normal file
View File

@@ -0,0 +1,70 @@
"use client";
import ThemedLayout from "@/components/layouts/ThemedLayout";
import { getMerchantBtcAddress, isMerchantBtcConfigured } from "@/lib/merchantBtc";
export default function PayPalMarket() {
const btcAddr = getMerchantBtcAddress();
const btcReady = isMerchantBtcConfigured();
const wallets = [
{ id: 1, balance: "$1,240.00", price: "0.002 BTC", type: "Personal" },
{ id: 2, balance: "$3,500.00", price: "0.005 BTC", type: "Business" },
{ id: 3, balance: "$8,900.00", price: "0.012 BTC", type: "Business Premier" },
{ id: 4, balance: "$12,000.00", price: "0.018 BTC", type: "Corporate" },
];
return (
<ThemedLayout theme="retro" title="The Wallet Exchange">
<div className="max-w-4xl mx-auto pt-12 font-mono text-[#0f0]">
<header className="border-b-4 border-[#0f0] pb-8 mb-12">
<h1 className="text-5xl font-black uppercase tracking-tighter">The Wallet Exchange</h1>
<p className="text-sm mt-4">Verified PayPal Wallets. Instant Delivery.</p>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
{wallets.map(w => (
<div key={w.id} className="border-2 border-[#0f0] p-8 bg-black/40 hover:bg-[#0f0]/10 transition-all">
<div className="flex justify-between items-start mb-6">
<div className="text-3xl font-bold">{w.balance}</div>
<div className="text-[10px] bg-[#0f0] text-black px-2 py-1 font-bold uppercase">{w.type}</div>
</div>
<div className="space-y-2 text-xs opacity-60 mb-8">
<div>{">"} Verified Email: YES</div>
<div>{">"} Linked Bank: YES</div>
<div>{">"} Transaction History: 12+ Months</div>
</div>
<div className="flex justify-between items-center">
<div className="text-xl font-bold">{w.price}</div>
<button className="bg-[#0f0] text-black px-6 py-2 font-bold uppercase text-xs hover:bg-[#0f0]/80 transition-all">
Buy Now
</button>
</div>
</div>
))}
</div>
<div className="bg-[#0f0]/10 border-2 border-[#0f0] p-8 mb-12">
<h3 className="text-xl font-bold mb-4 uppercase">Payment Instructions</h3>
<p className="text-xs mb-6 leading-relaxed">
Bitcoin payments use the same merchant address as CyberLux checkout. After the transaction is
confirmed on-chain, verify the txid on the checkout page to add USD to your spendable balance.
</p>
<div className="bg-black text-white p-4 font-bold break-all mb-4 border border-[#0f0] text-[11px] leading-relaxed">
{btcReady ? btcAddr : "[Set NEXT_PUBLIC_MERCHANT_BTC_ADDRESS in .env.local]"}
</div>
<div className="flex items-center gap-4 opacity-50">
<div className="w-10 h-10 border-2 border-[#0f0] flex items-center justify-center font-bold">E</div>
<div className="text-[8px] uppercase">
Escrow Assured<br />by ShadowGuard
</div>
</div>
</div>
<div className="text-center text-[10px] opacity-40 uppercase tracking-widest">
Node 24 // Shadow Network // Est. 2024
</div>
</div>
</ThemedLayout>
);
}

46
app/webring/page.tsx Normal file
View File

@@ -0,0 +1,46 @@
import Link from "next/link";
import ThemedLayout from "@/components/layouts/ThemedLayout";
export default function WebringPage() {
const sites = [
{ name: "The Candy Shop", path: "/market", desc: "Premium sweets and treats." },
{ name: "Darknet Ping Pong", path: "/game", desc: "The shared entity." },
{ name: "Shadow Lotto", path: "/drops", desc: "Win big or lose it all." },
{ name: "The Truth Seeker", path: "/conspiracies", desc: "Uncover the hidden." },
{ name: "The Sanctuary", path: "/sanctuary", desc: "Peace in the void." },
{ name: "Master Control", path: "/vault", desc: "The eye that sees all." },
{ name: "Arb Academy", path: "/arb-academy", desc: "Learn the trade." },
{ name: "Syndicate Shells", path: "/syndicate", desc: "Relay skins and topology notes." },
];
return (
<ThemedLayout theme="hacker" title="The Webring">
<div className="space-y-8">
<h2 className="text-3xl font-bold border-b border-[#00ff41]/20 pb-4">Network Directory</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{sites.map((site, i) => (
<Link
key={i}
href={site.path}
className="group border border-[#00ff41]/20 p-6 hover:bg-[#00ff41]/5 transition-all"
>
<div className="text-xl font-bold mb-2 group-hover:text-white transition-colors">
[{i.toString().padStart(2, '0')}] {site.name}
</div>
<p className="text-xs opacity-60">{site.desc}</p>
<div className="mt-4 text-[10px] opacity-20 group-hover:opacity-100 transition-opacity">
CONNECTING TO NODE_{i}... SUCCESS
</div>
</Link>
))}
</div>
<div className="mt-20 p-12 border-2 border-dashed border-[#00ff41]/10 text-center">
<div className="text-4xl mb-4 animate-spin inline-block"></div>
<h3 className="text-xl font-bold mb-2">Expanding the Network...</h3>
<p className="text-xs opacity-40">Dozens of new nodes are being provisioned by the Shadow Protocol.</p>
</div>
</div>
</ThemedLayout>
);
}

View File

@@ -0,0 +1,173 @@
"use client";
import Link from "next/link";
import { useState } from "react";
const AccountCreation = () => {
const [step, setStep] = useState(1);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [recoveryPhrase, setRecoveryPhrase] = useState("");
const [encryptionKey, setEncryptionKey] = useState("");
const [generated, setGenerated] = useState(false);
const generateRecovery = () => {
const phrases = [
"phantom quantum glacier nexus",
"crimson velvet paradox silence",
"neon abyss whisper eclipse",
"zero trace ghost protocol",
];
const randomPhrase = phrases[Math.floor(Math.random() * phrases.length)];
setRecoveryPhrase(randomPhrase);
setEncryptionKey(btoa(Date.now().toString()).slice(0, 16));
setGenerated(true);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (step < 3) {
setStep(step + 1);
} else {
alert("Identity provisioned. Keys derived locally — backup your recovery phrase offline.");
}
};
return (
<div className="glass mx-auto max-w-2xl rounded-3xl border border-white/10 p-10">
<h2 className="mb-8 font-orbitron text-4xl font-bold text-center">CREATE SHADOW IDENTITY</h2>
{/* Steps */}
<div className="mb-10 flex justify-between">
{[1, 2, 3].map((s) => (
<div key={s} className="flex flex-col items-center">
<div
className={`flex h-12 w-12 items-center justify-center rounded-full border-2 text-lg font-bold ${step >= s
? "border-neon-cyan bg-neon-cyan/20 text-neon-cyan"
: "border-white/20 text-foreground/40"
}`}
>
{s}
</div>
<div className="mt-2 text-sm">
{s === 1 && "Credentials"}
{s === 2 && "Recovery"}
{s === 3 && "Encryption"}
</div>
</div>
))}
</div>
<form onSubmit={handleSubmit}>
{step === 1 && (
<div className="space-y-6">
<div>
<label className="mb-2 block font-medium">Shadow Alias</label>
<input
type="text"
className="glass w-full rounded-xl border border-white/10 bg-transparent p-4"
placeholder="e.g., Ghost_23"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
<p className="mt-2 text-sm text-foreground/60">Never your real name. This alias is encrypted.</p>
</div>
<div>
<label className="mb-2 block font-medium">ZeroKnowledge Password</label>
<input
type="password"
className="glass w-full rounded-xl border border-white/10 bg-transparent p-4"
placeholder="••••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<p className="mt-2 text-sm text-foreground/60">We never store your password. Its used to derive your encryption key.</p>
</div>
</div>
)}
{step === 2 && (
<div className="space-y-6">
<div>
<label className="mb-2 block font-medium">Recovery Phrase</label>
<div className="glass rounded-xl border border-neon-cyan/30 p-6 font-mono text-center text-lg">
{generated ? recoveryPhrase : "Click generate to create"}
</div>
<button
type="button"
className="mt-4 w-full rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple py-3 font-bold text-background"
onClick={generateRecovery}
>
GENERATE RECOVERY PHRASE
</button>
<p className="mt-4 text-sm text-foreground/60">
Write this down physically. Its the only way to recover your account. We do not store it.
</p>
</div>
</div>
)}
{step === 3 && (
<div className="space-y-6">
<div>
<label className="mb-2 block font-medium">Encryption Key</label>
<div className="glass rounded-xl border border-neon-green/30 p-6 font-mono text-center text-lg">
{encryptionKey || "Not generated"}
</div>
<p className="mt-4 text-sm text-foreground/60">
This key encrypts all your data locally. It is derived from your password and never leaves your device.
</p>
</div>
<div className="rounded-xl bg-gradient-to-r from-neon-cyan/10 to-neon-purple/10 p-6">
<h3 className="font-bold">Identity Summary</h3>
<div className="mt-4 grid grid-cols-2 gap-4 text-sm">
<div>Alias</div>
<div className="font-mono">{username || "—"}</div>
<div>Recovery Phrase</div>
<div className="font-mono truncate">{recoveryPhrase || "—"}</div>
<div>Encryption</div>
<div className="font-mono text-neon-green">AES256GCM</div>
</div>
</div>
</div>
)}
<div className="mt-10 flex justify-between">
<button
type="button"
className="glass rounded-full px-8 py-3 font-medium disabled:opacity-30"
onClick={() => setStep(step - 1)}
disabled={step === 1}
>
Back
</button>
<button
type="submit"
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-10 py-3 font-bold text-background"
>
{step === 3 ? "COMPLETE IDENTITY CREATION" : "CONTINUE →"}
</button>
</div>
</form>
<div className="mt-10 border-t border-white/10 pt-8 text-center text-sm text-foreground/40">
<p>Keys and aliases never leave this browser profile. Clear storage = identity loss.</p>
<p className="mt-4 text-foreground/55">
For a <strong className="text-neon-cyan/90">live stall session</strong> tied to wallet and activity feeds, use{" "}
<Link href="/sign-up" className="text-neon-cyan underline hover:text-neon-purple">
create account
</Link>{" "}
or{" "}
<Link href="/sign-in" className="text-neon-cyan underline hover:text-neon-purple">
sign in
</Link>
.
</p>
</div>
</div>
);
};
export default AccountCreation;

View File

@@ -0,0 +1,17 @@
"use client";
import type { ReactNode } from "react";
import { AccountProvider } from "@/contexts/AccountContext";
import { CartProvider } from "@/contexts/CartContext";
import { WalletProvider } from "@/contexts/WalletContext";
/** Client-only wrappers so wallet + persistent account work on every route. */
export default function AppProviders({ children }: { children: ReactNode }) {
return (
<WalletProvider>
<AccountProvider>
<CartProvider>{children}</CartProvider>
</AccountProvider>
</WalletProvider>
);
}

156
components/ChatWidget.tsx Normal file
View File

@@ -0,0 +1,156 @@
"use client";
import { useState, useEffect, useRef } from "react";
const ChatWidget = () => {
const [messages, setMessages] = useState([
{ id: 1, sender: "System", text: "Welcome to the encrypted channel. All messages are endtoend encrypted.", time: "12:00" },
{ id: 2, sender: "Ghost", text: "Has anyone tested the new stimulant batch?", time: "12:05" },
{ id: 3, sender: "Vendor_X", text: "Batch #8 passes all purity tests. Available for bulk.", time: "12:07" },
{ id: 4, sender: "Anonymous", text: "Need a EU passport within 48 hours. DM if you can deliver.", time: "12:10" },
]);
const [input, setInput] = useState("");
/** Set after mount so SSR HTML matches first client paint (no Math.random in initial state). */
const [encryptionKey, setEncryptionKey] = useState("");
const chatContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
setEncryptionKey("session_" + Math.random().toString(36).substring(2, 9));
}, []);
useEffect(() => {
if (chatContainerRef.current) {
chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
}
}, [messages]);
const handleSend = () => {
if (!input.trim()) return;
const newMessage = {
id: messages.length + 1,
sender: "You",
text: input,
time: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
};
setMessages([...messages, newMessage]);
setInput("");
// Simulate a reply after a delay
setTimeout(() => {
const replies = [
"Received. Encryption verified.",
"I can help with that. Check your vault.",
"New drop incoming in 24 hours.",
"Your request has been logged.",
];
const randomReply = replies[Math.floor(Math.random() * replies.length)];
const replyMessage = {
id: messages.length + 2,
sender: "System",
text: randomReply,
time: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
};
setMessages(prev => [...prev, replyMessage]);
}, 1000);
};
const handleEncrypt = () => {
alert(`Session key: ${encryptionKey}\nAll messages are encrypted with this key.`);
};
return (
<div className="glass rounded-3xl border border-white/10 p-8">
<div className="mb-8 flex items-center justify-between">
<div>
<h2 className="font-orbitron text-3xl font-bold">ENCRYPTED CHAT</h2>
<p className="text-foreground/60">Endtoend encrypted messaging. No logs, no traces.</p>
</div>
<button
onClick={handleEncrypt}
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-6 py-3 font-bold text-background"
>
🔐 SHOW SESSION KEY
</button>
</div>
{/* Chat messages */}
<div
ref={chatContainerRef}
className="glass mb-6 h-96 overflow-y-auto rounded-2xl border border-white/10 p-6"
>
{messages.map((msg) => (
<div
key={msg.id}
className={`mb-4 ${msg.sender === "You" ? "text-right" : ""}`}
>
<div className="mb-1 flex items-center gap-2">
<span className={`inline-block rounded-full px-3 py-1 text-xs font-medium ${msg.sender === "You"
? "bg-neon-cyan/20 text-neon-cyan"
: msg.sender === "System"
? "bg-neon-purple/20 text-neon-purple"
: "bg-white/10"
}`}>
{msg.sender}
</span>
<span className="text-xs text-foreground/40">{msg.time}</span>
</div>
<div className={`glass inline-block max-w-[80%] rounded-2xl px-4 py-3 ${msg.sender === "You"
? "bg-gradient-to-r from-neon-cyan/20 to-neon-cyan/10"
: "bg-white/5"
}`}>
{msg.text}
</div>
</div>
))}
</div>
{/* Input area */}
<div className="flex gap-4">
<input
type="text"
className="glass flex-1 rounded-2xl border border-white/10 bg-transparent px-6 py-4"
placeholder="Type your encrypted message..."
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSend()}
/>
<button
onClick={handleSend}
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-8 py-4 font-bold text-background"
>
SEND
</button>
</div>
{/* Chat features */}
<div className="mt-8 grid grid-cols-2 gap-6 md:grid-cols-4">
<div className="glass rounded-2xl border border-white/10 p-4 text-center">
<div className="text-2xl">🔒</div>
<div className="mt-2 text-sm font-bold">AES256GCM</div>
<div className="text-xs text-foreground/60">Encryption</div>
</div>
<div className="glass rounded-2xl border border-white/10 p-4 text-center">
<div className="text-2xl"></div>
<div className="mt-2 text-sm font-bold">ZeroKnowledge</div>
<div className="text-xs text-foreground/60">No server storage</div>
</div>
<div className="glass rounded-2xl border border-white/10 p-4 text-center">
<div className="text-2xl">🌐</div>
<div className="mt-2 text-sm font-bold">WebRTC</div>
<div className="text-xs text-foreground/60">P2P possible</div>
</div>
<div className="glass rounded-2xl border border-white/10 p-4 text-center">
<div className="text-2xl">🕶</div>
<div className="mt-2 text-sm font-bold">SelfDestruct</div>
<div className="text-xs text-foreground/60">Messages expire</div>
</div>
</div>
<div className="mt-8 text-center text-xs text-foreground/40">
<p>Ephemeral client-side queue nothing leaves this browser profile until a real transport is configured.</p>
</div>
</div>
);
};
export default ChatWidget;

562
components/CheckoutFlow.tsx Normal file
View File

@@ -0,0 +1,562 @@
"use client";
import Link from "next/link";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCart } from "@/contexts/CartContext";
import { useWallet } from "@/contexts/WalletContext";
import { getMerchantBtcAddress, isMerchantBtcConfigured } from "@/lib/merchantBtc";
import { estimateUsdForCryptoAmount } from "@/lib/shopFx";
const ENCRYPTION_BTC_FEE: Record<"standard" | "enhanced" | "quantum", number> = {
standard: 0,
enhanced: 0.001,
quantum: 0.005,
};
const CheckoutFlow = () => {
const { lines, setLineQty, removeLine, clearCart, hydrated: cartHydrated } = useCart();
const [step, setStep] = useState(1);
const [paymentMethod, setPaymentMethod] = useState<"crypto" | "guest" | "card">("crypto");
const [cryptoType, setCryptoType] = useState<"BTC" | "ETH" | "XMR">("BTC");
const [guestEmail, setGuestEmail] = useState("");
const [encryptionLevel, setEncryptionLevel] = useState<"standard" | "enhanced" | "quantum">("enhanced");
const [btcUsd, setBtcUsd] = useState<number | null>(null);
const [txidInput, setTxidInput] = useState("");
const [verifyMsg, setVerifyMsg] = useState<{ kind: "ok" | "err"; text: string } | null>(null);
const [verifyBusy, setVerifyBusy] = useState(false);
const [payError, setPayError] = useState<string | null>(null);
const [orderComplete, setOrderComplete] = useState(false);
const { usdStoreCredit, spendUsdStoreCredit, verifyBtcDeposit } = useWallet();
useEffect(() => {
let cancelled = false;
(async () => {
try {
const res = await fetch(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd",
);
const j = (await res.json()) as { bitcoin?: { usd?: number } };
const p = j.bitcoin?.usd;
if (!cancelled && p && Number.isFinite(p)) setBtcUsd(p);
} catch {
if (!cancelled) setBtcUsd(96000);
}
})();
return () => {
cancelled = true;
};
}, []);
const spotBtc = btcUsd ?? 96000;
const subtotalUsd = useMemo(
() =>
Math.round(
lines.reduce(
(sum, l) => sum + estimateUsdForCryptoAmount(l.price, l.currency, spotBtc) * l.qty,
0,
) * 100,
) / 100,
[lines, spotBtc],
);
const encryptionFeeUsd = useMemo(() => {
const rate = btcUsd ?? 96000;
return Math.round(ENCRYPTION_BTC_FEE[encryptionLevel] * rate * 100) / 100;
}, [encryptionLevel, btcUsd]);
const orderTotalUsd = useMemo(
() => Math.round((subtotalUsd + encryptionFeeUsd) * 100) / 100,
[subtotalUsd, encryptionFeeUsd],
);
const orderTotalBtc = useMemo(() => {
const r = btcUsd && btcUsd > 0 ? btcUsd : spotBtc;
if (!r || r <= 0) return null;
return Math.round((orderTotalUsd / r) * 1e8) / 1e8;
}, [orderTotalUsd, btcUsd, spotBtc]);
const merchantAddr = getMerchantBtcAddress();
const merchantReady = isMerchantBtcConfigured();
const btcPayUri = useMemo(() => {
if (!merchantReady || orderTotalBtc == null) return "";
return `bitcoin:${merchantAddr}?amount=${orderTotalBtc}`;
}, [merchantReady, merchantAddr, orderTotalBtc]);
const handleVerify = useCallback(async () => {
setVerifyMsg(null);
setVerifyBusy(true);
try {
const result = await verifyBtcDeposit(txidInput.trim());
if (result.ok) {
setVerifyMsg({
kind: "ok",
text: `Confirmed. Added $${result.creditedUsd?.toFixed(2) ?? "0.00"} USD to your spendable balance.`,
});
setTxidInput("");
} else {
setVerifyMsg({ kind: "err", text: result.error || "Verification failed" });
}
} finally {
setVerifyBusy(false);
}
}, [txidInput, verifyBtcDeposit]);
const tryCompleteOrder = () => {
setPayError(null);
if (paymentMethod === "crypto") {
if (!spendUsdStoreCredit(orderTotalUsd)) {
setPayError(
`Insufficient USD balance. You need $${orderTotalUsd.toFixed(2)}. Send Bitcoin to the address below, wait for at least one confirmation, then paste the transaction ID and click Verify.`,
);
return;
}
}
setOrderComplete(true);
clearCart();
};
const steps = [
{ number: 1, title: "Cart", description: "Review items" },
{ number: 2, title: "Payment", description: "Choose method" },
{ number: 3, title: "Encryption", description: "Select privacy level" },
{ number: 4, title: "Confirmation", description: "Complete ritual" },
];
const handleNext = () => {
if (step === 1 && lines.length === 0) return;
if (step < 4) setStep(step + 1);
};
const handlePrev = () => {
if (step > 1) setStep(step - 1);
};
return (
<div className="glass mx-auto max-w-4xl rounded-3xl border border-white/10 p-8 md:p-12">
<div className="mb-12">
<div className="flex justify-between">
{steps.map((s) => (
<div key={s.number} className="flex flex-col items-center">
<div
className={`flex h-12 w-12 items-center justify-center rounded-full border-2 text-lg font-bold ${step >= s.number
? "border-neon-cyan bg-neon-cyan/20 text-neon-cyan"
: "border-white/20 text-foreground/40"
}`}
>
{s.number}
</div>
<div className="mt-2 text-center">
<div className="text-sm font-medium">{s.title}</div>
<div className="text-xs text-foreground/50">{s.description}</div>
</div>
</div>
))}
</div>
<div className="relative -top-6 -z-10 h-1 bg-white/10">
<div
className="h-1 bg-gradient-to-r from-neon-cyan to-neon-purple transition-all duration-500"
style={{ width: `${((step - 1) / 3) * 100}%` }}
/>
</div>
</div>
<div className="mb-10">
{step === 1 && (
<div>
<h3 className="mb-6 font-orbitron text-2xl font-bold">YOUR CART</h3>
{!cartHydrated ? (
<p className="text-sm text-foreground/50">Loading cart</p>
) : lines.length === 0 ? (
<div className="rounded-xl border border-dashed border-white/15 p-10 text-center">
<p className="text-foreground/70">Your cart is empty.</p>
<p className="mt-2 text-sm text-foreground/50">Add SKUs from the hub or candy market.</p>
<Link
href="/market"
className="mt-6 inline-block rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-8 py-3 text-sm font-bold text-background"
>
Browse catalog
</Link>
</div>
) : (
<div className="space-y-4">
{lines.map((item) => {
const unitUsd = estimateUsdForCryptoAmount(item.price, item.currency, spotBtc);
const lineUsd = Math.round(unitUsd * item.qty * 100) / 100;
return (
<div
key={item.productId}
className="flex flex-col gap-4 rounded-xl bg-white/5 p-4 sm:flex-row sm:items-center sm:justify-between"
>
<div className="min-w-0 flex-1">
<div className="font-medium">{item.name}</div>
<div className="mt-1 font-mono text-xs text-foreground/50">
{item.price} {item.currency} each · ${unitUsd.toFixed(2)} USD @ spot
</div>
<div className="mt-3 flex flex-wrap items-center gap-2">
<span className="text-xs text-foreground/50">Qty</span>
<button
type="button"
className="rounded border border-white/15 px-2 py-0.5 text-sm hover:bg-white/10"
onClick={() => setLineQty(item.productId, item.qty - 1)}
aria-label="Decrease quantity"
>
</button>
<span className="w-8 text-center font-mono text-sm">{item.qty}</span>
<button
type="button"
className="rounded border border-white/15 px-2 py-0.5 text-sm hover:bg-white/10"
onClick={() => setLineQty(item.productId, item.qty + 1)}
aria-label="Increase quantity"
>
+
</button>
<button
type="button"
className="ml-2 text-xs text-red-400 hover:underline"
onClick={() => removeLine(item.productId)}
>
Remove
</button>
</div>
</div>
<div className="text-right sm:shrink-0">
<div className="font-orbitron text-xl text-neon-cyan">${lineUsd.toLocaleString()}</div>
<div className="text-xs text-foreground/50">USD est.</div>
</div>
</div>
);
})}
</div>
)}
{lines.length > 0 ? (
<div className="mt-8 flex justify-between border-t border-white/10 pt-6 text-xl">
<span>Subtotal (USD est.)</span>
<span className="font-orbitron font-bold text-neon-cyan">
${subtotalUsd.toLocaleString()}
</span>
</div>
) : null}
</div>
)}
{step === 2 && (
<div>
<h3 className="mb-6 font-orbitron text-2xl font-bold">SELECT PAYMENT METHOD</h3>
<div className="mb-6 rounded-xl border border-neon-green/30 bg-neon-green/5 px-4 py-3 text-sm">
<span className="font-bold text-neon-green">USD balance: </span>
<span className="font-orbitron">${usdStoreCredit.toFixed(2)}</span>
<span className="text-foreground/60">
{" "}
use after Bitcoin deposit is verified (on-chain).
</span>
</div>
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<button
type="button"
className={`glass rounded-2xl border p-6 text-center transition-all ${paymentMethod === "crypto" ? "border-neon-cyan ring-2 ring-neon-cyan/30" : "border-white/10"
}`}
onClick={() => setPaymentMethod("crypto")}
>
<div className="mb-4 text-4xl"></div>
<div className="font-bold">Cryptocurrency</div>
<div className="mt-2 text-sm text-foreground/60">Bitcoin checkout balance</div>
</button>
<button
type="button"
className={`glass rounded-2xl border p-6 text-center transition-all ${paymentMethod === "guest" ? "border-neon-cyan ring-2 ring-neon-cyan/30" : "border-white/10"
}`}
onClick={() => setPaymentMethod("guest")}
>
<div className="mb-4 text-4xl">👤</div>
<div className="font-bold">Guest Checkout</div>
<div className="mt-2 text-sm text-foreground/60">Demo no USD charge</div>
</button>
<button
type="button"
className={`glass rounded-2xl border p-6 text-center transition-all ${paymentMethod === "card" ? "border-neon-cyan ring-2 ring-neon-cyan/30" : "border-white/10"
}`}
onClick={() => setPaymentMethod("card")}
>
<div className="mb-4 text-4xl">💳</div>
<div className="font-bold">Card (Secure)</div>
<div className="mt-2 text-sm text-foreground/60">Demo no USD charge</div>
</button>
</div>
{paymentMethod === "crypto" && (
<div className="mt-8">
<label className="mb-2 block font-medium">Select Cryptocurrency</label>
<div className="flex gap-4">
{(["BTC", "ETH", "XMR"] as const).map((coin) => (
<button
key={coin}
type="button"
className={`glass flex-1 rounded-xl border py-4 ${cryptoType === coin ? "border-neon-cyan bg-neon-cyan/10" : "border-white/10"
}`}
onClick={() => setCryptoType(coin)}
>
<div className="text-2xl">{coin === "BTC" ? "₿" : coin === "ETH" ? "Ξ" : "⏣"}</div>
<div className="font-bold">{coin}</div>
</button>
))}
</div>
{cryptoType !== "BTC" && (
<p className="mt-4 rounded-lg bg-white/5 p-4 text-sm text-foreground/70">
Deposits that add USD balance use <strong>Bitcoin</strong> only. Switch to BTC to see the
deposit address. (ETH/XMR shown for display.)
</p>
)}
{cryptoType === "BTC" && (
<div className="mt-6 space-y-4 rounded-xl bg-white/5 p-4">
<div className="text-sm text-foreground/60">Estimated order total after encryption step</div>
<div className="font-orbitron text-2xl text-neon-cyan">
${orderTotalUsd.toLocaleString()} USD
{orderTotalBtc != null && btcUsd != null && (
<span className="ml-3 text-lg text-foreground/70">
( {orderTotalBtc} BTC @ ${btcUsd.toLocaleString()}/BTC)
</span>
)}
</div>
<p className="text-xs text-foreground/50">
Final total includes the encryption add-on you pick in the next step. Fund your balance
with <strong>any amount</strong> of BTC; verified value is credited in USD at spot rate.
You can pay this order once your balance covers the total.
</p>
</div>
)}
</div>
)}
{paymentMethod === "guest" && (
<div className="mt-8">
<label className="mb-2 block font-medium">Email for receipt (optional)</label>
<input
type="email"
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3"
placeholder="anonymous@example.com"
value={guestEmail}
onChange={(e) => setGuestEmail(e.target.value)}
/>
<p className="mt-2 text-sm text-foreground/60">
Guest checkout is a demo and does not charge your USD balance.
</p>
</div>
)}
<div className="mt-10 border-t border-white/10 pt-8">
<h4 className="mb-4 font-orbitron text-lg font-bold text-neon-cyan">Bitcoin deposit (all checkouts)</h4>
{!merchantReady ? (
<p className="text-sm text-amber-300/90">
Set <code className="rounded bg-white/10 px-1">NEXT_PUBLIC_MERCHANT_BTC_ADDRESS</code> and{" "}
<code className="rounded bg-white/10 px-1">MERCHANT_BTC_ADDRESS</code> in{" "}
<code className="rounded bg-white/10 px-1">.env.local</code>, then restart the app.
</p>
) : (
<>
<p className="mb-3 text-sm text-foreground/70">
Send Bitcoin to this address. After the network confirms your payment (at least one
block), paste the transaction ID to add the equivalent USD to your balance.
</p>
<div className="rounded-xl border border-white/10 bg-black/40 p-4 font-mono text-sm break-all">
{merchantAddr}
</div>
{btcPayUri && (
<div className="mt-4 flex flex-col gap-4 md:flex-row md:items-start">
<img
src={`https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=${encodeURIComponent(btcPayUri)}`}
alt="Bitcoin payment QR"
width={160}
height={160}
className="rounded-lg border border-white/10"
/>
<div className="text-xs text-foreground/50">
QR encodes a suggested payment URI for the current cart estimate (you may send any
amount; all verified BTC to this address becomes USD credit).
</div>
</div>
)}
<div className="mt-6 space-y-2">
<label className="text-sm font-medium">Transaction ID (after confirmed)</label>
<input
className="glass w-full rounded-xl border border-white/10 bg-transparent px-4 py-3 font-mono text-sm"
placeholder="64-character txid"
value={txidInput}
onChange={(e) => setTxidInput(e.target.value.replace(/\s+/g, ""))}
/>
<button
type="button"
disabled={verifyBusy}
onClick={() => void handleVerify()}
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-6 py-2 text-sm font-bold text-background disabled:opacity-40"
>
{verifyBusy ? "Verifying…" : "Verify & add USD credit"}
</button>
{verifyMsg && (
<p
className={`text-sm ${verifyMsg.kind === "ok" ? "text-neon-green" : "text-red-400"}`}
>
{verifyMsg.text}
</p>
)}
</div>
</>
)}
</div>
</div>
)}
{step === 3 && (
<div>
<h3 className="mb-6 font-orbitron text-2xl font-bold">ENCRYPTION LEVEL</h3>
<p className="mb-8 text-foreground/70">
Choose how securely your data is protected. Higher levels add to your order total in USD.
</p>
<div className="space-y-4">
{[
{ level: "standard" as const, name: "Stealth Encryption", desc: "Militarygrade encryption, basic anonymity", icon: "🕵️" },
{ level: "enhanced" as const, name: "Ghost Protocol", desc: "Postquantum cryptography, zerotrace", icon: "👻" },
{ level: "quantum" as const, name: "ZeroTrace", desc: "Experimental, quantumentangled proofs", icon: "⚛️" },
].map((opt) => (
<button
key={opt.level}
type="button"
className={`glass flex w-full items-center justify-between rounded-2xl border p-6 text-left ${encryptionLevel === opt.level ? "border-neon-cyan ring-2 ring-neon-cyan/30" : "border-white/10"
}`}
onClick={() => setEncryptionLevel(opt.level)}
>
<div>
<div className="flex items-center gap-4">
<span className="text-3xl">{opt.icon}</span>
<div>
<div className="font-bold">{opt.name}</div>
<div className="text-sm text-foreground/60">{opt.desc}</div>
</div>
</div>
</div>
<div className="text-right font-orbitron text-xl font-bold text-neon-cyan">
{opt.level === "standard"
? "FREE"
: `+$${(Math.round(ENCRYPTION_BTC_FEE[opt.level] * (btcUsd ?? 96000) * 100) / 100).toFixed(2)}`}
</div>
</button>
))}
</div>
<div className="mt-8 rounded-xl bg-gradient-to-r from-neon-cyan/10 to-neon-purple/10 p-6">
<div className="font-orbitron font-bold">Order total so far</div>
<div className="mt-2 text-2xl text-neon-cyan">
${orderTotalUsd.toLocaleString()} USD
</div>
</div>
</div>
)}
{step === 4 && !orderComplete && (
<div className="text-center">
<h3 className="mb-4 font-orbitron text-3xl font-bold">COMPLETE PAYMENT</h3>
<div className="glass mx-auto mb-8 max-w-md rounded-2xl border border-white/10 p-6 text-left">
<div className="flex justify-between text-sm">
<span className="text-foreground/60">Total due</span>
<span className="font-orbitron font-bold text-neon-cyan">
${orderTotalUsd.toLocaleString()}
</span>
</div>
<div className="mt-2 flex justify-between text-sm">
<span className="text-foreground/60">Your USD balance</span>
<span className="font-orbitron">${usdStoreCredit.toFixed(2)}</span>
</div>
<div className="mt-2 flex justify-between text-sm">
<span className="text-foreground/60">Payment path</span>
<span className="font-bold">
{paymentMethod === "crypto" ? "Crypto (USD balance)" : "Demo (guest/card)"}
</span>
</div>
</div>
{payError && <p className="mx-auto mb-6 max-w-xl text-sm text-red-400">{payError}</p>}
<button
type="button"
onClick={tryCompleteOrder}
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-10 py-3 font-bold text-background"
>
{paymentMethod === "crypto" ? "Charge my USD balance & complete" : "Complete demo order"}
</button>
{paymentMethod === "crypto" && (
<p className="mx-auto mt-6 max-w-xl text-xs text-foreground/50">
Crypto checkout pulls from your verified USD balance only. Send BTC on the payment step and
verify the txid so your balance matches the total above.
</p>
)}
</div>
)}
{step === 4 && orderComplete && (
<div className="text-center">
<div className="mb-8">
<div className="inline-flex h-24 w-24 items-center justify-center rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple">
<span className="text-4xl"></span>
</div>
</div>
<h3 className="mb-4 font-orbitron text-3xl font-bold">RITUAL COMPLETE</h3>
<p className="mx-auto max-w-2xl text-foreground/70">
Your order is now encrypted with {encryptionLevel} protection. A unique digital artwork receipt
has been generated and stored in your vault.
</p>
<div className="my-10">
<div className="glass mx-auto max-w-md rounded-2xl border border-white/10 p-6">
<div className="font-orbitron text-2xl font-bold text-neon-cyan">ORDER #CYBR9A2FB8E1</div>
<div className="mt-4 grid grid-cols-2 gap-4 text-sm">
<div className="text-left text-foreground/60">Amount</div>
<div className="text-right font-bold">${orderTotalUsd.toLocaleString()} USD</div>
<div className="text-left text-foreground/60">Privacy</div>
<div className="text-right font-bold">{encryptionLevel.toUpperCase()}</div>
<div className="text-left text-foreground/60">Payment</div>
<div className="truncate text-right font-mono text-neon-green">
{paymentMethod === "crypto" ? "USD balance (BTC-funded)" : "Demo guest/card"}
</div>
</div>
</div>
</div>
<p className="text-sm text-foreground/50">
Your digital receipt is available in your vault. Thank you for choosing CyberLux.
</p>
</div>
)}
</div>
<div className="flex justify-between">
<button
type="button"
className="glass rounded-full px-8 py-3 font-medium disabled:opacity-30"
onClick={handlePrev}
disabled={step === 1 || (step === 4 && orderComplete)}
>
Previous
</button>
<div className="flex items-center gap-4">
{step < 4 ? (
<button
type="button"
disabled={step === 1 && lines.length === 0}
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-8 py-3 font-bold text-background disabled:opacity-40"
onClick={handleNext}
>
Continue
</button>
) : orderComplete ? (
<button
type="button"
className="rounded-full bg-gradient-to-r from-neon-green to-neon-cyan px-10 py-3 font-bold text-background"
>
ENTER THE VAULT
</button>
) : null}
</div>
</div>
</div>
);
};
export default CheckoutFlow;

View File

@@ -0,0 +1,100 @@
"use client";
import { useState, useEffect, useRef } from "react";
export default function DDoSProtection({ onComplete }: { onComplete: () => void }) {
const [progress, setProgress] = useState(0);
const [status, setStatus] = useState("Checking your browser…");
/** Client-only — avoids SSR/client HTML mismatch from Math.random() in JSX. */
const [rayId, setRayId] = useState("");
const doneRef = useRef(false);
const onCompleteRef = useRef(onComplete);
onCompleteRef.current = onComplete;
useEffect(() => {
setRayId(Math.random().toString(36).substring(2, 10).toUpperCase());
}, []);
useEffect(() => {
const interval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
return 100;
}
const next = prev + Math.random() * 15;
if (next >= 100) {
if (!doneRef.current) {
doneRef.current = true;
clearInterval(interval);
setTimeout(() => onCompleteRef.current(), 500);
}
return 100;
}
return next;
});
}, 400);
const statusUpdates = [
"Checking your browser…",
"Verifying client challenge…",
"Analyzing request signature…",
"Negotiating TLS + Tor circuit…",
"Establishing encrypted tunnel…",
"Access granted.",
];
let statusIndex = 0;
const statusInterval = setInterval(() => {
if (statusIndex < statusUpdates.length - 1) {
statusIndex++;
setStatus(statusUpdates[statusIndex]);
} else {
clearInterval(statusInterval);
}
}, 800);
return () => {
clearInterval(interval);
clearInterval(statusInterval);
};
}, []);
return (
<div
className="fixed inset-0 z-[10000] flex flex-col items-center justify-center bg-black p-8 font-mono text-[#00ff41]"
style={{ backgroundColor: "#000000", color: "#00ff41" }}
>
<div
className="w-full max-w-md border-2 border-[#00ff41]/20 bg-[#050505] p-12 shadow-[0_0_50px_rgba(0,255,65,0.1)]"
style={{ backgroundColor: "#050505", borderColor: "rgba(0,255,65,0.2)" }}
>
<div className="text-4xl mb-8 animate-pulse">🛡</div>
<h1 className="text-2xl font-bold mb-2 uppercase tracking-tighter">DDoS Protection</h1>
<p className="text-xs opacity-60 mb-8 leading-relaxed">
Origin shield active wait while we validate your session. Automated requests are throttled at the edge.
</p>
<div className="w-full h-2 bg-[#00ff41]/10 mb-4 overflow-hidden">
<div
className="h-full bg-[#00ff41] transition-all duration-300 ease-out"
style={{ width: `${progress}%` }}
/>
</div>
<div className="flex justify-between text-[10px] uppercase tracking-widest">
<span>{status}</span>
<span>{Math.floor(progress)}%</span>
</div>
<div className="mt-12 text-[8px] opacity-20 leading-relaxed">
Ray ID: {rayId || "—"}
<br />
Performance & Security by ShadowGuard
</div>
</div>
<p className="pointer-events-none fixed bottom-6 left-0 right-0 px-4 text-center text-2xl font-black text-red-500 sm:text-3xl md:text-4xl">
This site is fake
</p>
</div>
);
}

View File

@@ -0,0 +1,101 @@
"use client";
import { useState } from "react";
const EncryptionDemo = () => {
const [message, setMessage] = useState("");
const [encrypted, setEncrypted] = useState("");
const [key, setKey] = useState("");
const mockEncrypt = (text: string) => {
if (!text.trim()) return "";
// Simulate encryption by reversing and adding salt
const salt = "⚡🔐";
const reversed = text.split("").reverse().join("");
return `encrypted_${btoa(reversed)}_${salt}`;
};
const handleEncrypt = () => {
const result = mockEncrypt(message);
setEncrypted(result);
setKey(`key_${Date.now().toString(16)}`);
};
const handleDecrypt = () => {
if (!encrypted) return;
// Simulate decryption
const parts = encrypted.split("_");
if (parts.length < 2) return;
const decoded = atob(parts[1]);
const original = decoded.split("").reverse().join("");
setMessage(original);
setEncrypted("");
};
return (
<div className="glass rounded-2xl border border-white/10 p-8">
<h3 className="mb-6 font-orbitron text-2xl font-bold">ENCRYPTION DEMO</h3>
<p className="mb-6 text-foreground/70">
Experience clientside encryption. Your message never leaves your browser.
</p>
<div className="space-y-6">
<div>
<label className="mb-2 block text-sm font-medium">Message to encrypt</label>
<textarea
className="glass w-full rounded-xl border border-white/10 bg-transparent p-4"
rows={3}
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Enter a secret message..."
/>
</div>
<div className="flex gap-4">
<button
className="flex-1 rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple py-3 font-bold text-background"
onClick={handleEncrypt}
>
🔐 Encrypt
</button>
<button
className="flex-1 rounded-full glass border border-neon-cyan/30 py-3 font-bold text-neon-cyan"
onClick={handleDecrypt}
disabled={!encrypted}
>
🔓 Decrypt
</button>
</div>
{encrypted && (
<>
<div>
<label className="mb-2 block text-sm font-medium">Encrypted payload</label>
<div className="glass overflow-auto rounded-xl border border-neon-cyan/30 p-4 font-mono text-sm">
{encrypted}
</div>
</div>
<div>
<label className="mb-2 block text-sm font-medium">Generated key</label>
<div className="glass overflow-auto rounded-xl border border-neon-green/30 p-4 font-mono text-sm text-neon-green">
{key}
</div>
</div>
<div className="rounded-xl bg-gradient-to-r from-neon-cyan/10 to-neon-purple/10 p-4">
<div className="flex items-center gap-3">
<div className="text-2xl">🛡</div>
<div className="text-sm">
Educational transform only production wallets use audited libs and real key agreement.
</div>
</div>
</div>
</>
)}
<div className="text-xs text-foreground/50">
<p>
All sensitive data on CyberLux is encrypted using AES256GCM. Private keys are never transmitted.
</p>
</div>
</div>
</div>
);
};
export default EncryptionDemo;

220
components/ForumBoard.tsx Normal file
View File

@@ -0,0 +1,220 @@
"use client";
import Link from "next/link";
import { useState } from "react";
const ForumBoard = () => {
const [posts, setPosts] = useState([
{
id: 1,
user: "GhostInTheShell",
avatar: "👻",
timestamp: "2 hours ago",
content: "Has anyone tested the new neural stimulant? Need reports on sideeffects.",
upvotes: 42,
replies: 12,
category: "BioEnhancements",
},
{
id: 2,
user: "ZeroCool",
avatar: "🕵️",
timestamp: "5 hours ago",
content: "Quantum counterfeit notes batch #8 passes all validation tests. Available for bulk orders.",
upvotes: 89,
replies: 24,
category: "Financial",
},
{
id: 3,
user: "CypherPunk",
avatar: "🔐",
timestamp: "1 day ago",
content: "New darknet router vulnerability discovered. Patch your firmware immediately.",
upvotes: 156,
replies: 47,
category: "Security",
},
{
id: 4,
user: "Ethereal",
avatar: "🌌",
timestamp: "2 days ago",
content: "Looking for reliable identity pack vendor with EU passports. DM with reputation score.",
upvotes: 33,
replies: 8,
category: "Identity",
},
]);
const [newPost, setNewPost] = useState("");
const [selectedCategory, setSelectedCategory] = useState("All");
const categories = ["All", "BioEnhancements", "Financial", "Security", "Identity", "Weapons", "Chemicals"];
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!newPost.trim()) return;
const newPostObj = {
id: posts.length + 1,
user: "Anonymous",
avatar: "🕶️",
timestamp: "Just now",
content: newPost,
upvotes: 0,
replies: 0,
category: "Uncategorized",
};
setPosts([newPostObj, ...posts]);
setNewPost("");
};
const filteredPosts = selectedCategory === "All"
? posts
: posts.filter(p => p.category === selectedCategory);
return (
<div className="glass rounded-3xl border border-white/10 p-8">
<div className="mb-10">
<h2 className="font-orbitron text-4xl font-bold">DARKNET FORUM</h2>
<p className="mt-2 text-foreground/70">Encrypted, anonymous discussions. No logs, no traces.</p>
</div>
{/* Category filter */}
<div className="mb-8 flex flex-wrap gap-3">
{categories.map((cat) => (
<button
key={cat}
className={`rounded-full px-4 py-2 text-sm font-medium transition-all ${selectedCategory === cat
? "bg-gradient-to-r from-neon-cyan to-neon-purple text-background"
: "glass border border-white/10"
}`}
onClick={() => setSelectedCategory(cat)}
>
{cat}
</button>
))}
</div>
{/* New post form */}
<form onSubmit={handleSubmit} className="mb-10">
<div className="glass rounded-2xl border border-white/10 p-6">
<textarea
className="w-full bg-transparent text-foreground placeholder-foreground/50 focus:outline-none"
rows={3}
placeholder="Type your encrypted message... (all posts are ephemeral)"
value={newPost}
onChange={(e) => setNewPost(e.target.value)}
/>
<div className="mt-4 flex items-center justify-between">
<div className="flex gap-4">
<button
type="button"
className="flex items-center gap-2 rounded-full bg-white/5 px-4 py-2 text-sm"
>
<span>🔒</span> Encrypt
</button>
<button
type="button"
className="flex items-center gap-2 rounded-full bg-white/5 px-4 py-2 text-sm"
>
<span>🕵</span> Post Anonymously
</button>
</div>
<button
type="submit"
className="rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-6 py-3 font-bold text-background"
>
PUBLISH
</button>
</div>
</div>
</form>
{/* Posts list */}
<div className="space-y-6">
{filteredPosts.map((post) => (
<div key={post.id} className="glass rounded-2xl border border-white/10 p-6">
<div className="flex items-start justify-between">
<div className="flex items-center gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gradient-to-br from-neon-cyan to-neon-purple text-2xl">
{post.avatar}
</div>
<div>
<div className="flex items-center gap-3">
<span className="font-bold">{post.user}</span>
<span className="rounded-full bg-white/5 px-3 py-1 text-xs">{post.category}</span>
</div>
<div className="text-sm text-foreground/60">{post.timestamp}</div>
</div>
</div>
<div className="flex items-center gap-4">
<button className="flex items-center gap-2 text-sm text-foreground/60 hover:text-neon-cyan">
<span></span> {post.upvotes}
</button>
<button className="flex items-center gap-2 text-sm text-foreground/60 hover:text-neon-cyan">
<span>💬</span> {post.replies}
</button>
<button className="rounded-full bg-white/5 p-2 hover:bg-white/10">
<span>🔗</span>
</button>
</div>
</div>
<div className="mt-6">
<p>{post.content}</p>
</div>
<div className="mt-6 flex items-center gap-6 border-t border-white/10 pt-6">
<button className="flex items-center gap-2 text-sm">
<span></span> Upvote
</button>
<button className="flex items-center gap-2 text-sm">
<span></span> Downvote
</button>
<button className="flex items-center gap-2 text-sm">
<span>💬</span> Reply
</button>
<button className="flex items-center gap-2 text-sm">
<span>🔐</span> Encrypt Reply
</button>
<button className="flex items-center gap-2 text-sm">
<span>🚀</span> Boost
</button>
</div>
</div>
))}
</div>
{/* Forum stats */}
<div className="mt-10 grid grid-cols-2 gap-6 md:grid-cols-4">
<div className="glass rounded-2xl border border-white/10 p-6 text-center">
<div className="text-3xl font-bold text-neon-cyan">2.4k</div>
<div className="text-sm text-foreground/60">Active Users</div>
</div>
<div className="glass rounded-2xl border border-white/10 p-6 text-center">
<div className="text-3xl font-bold text-neon-purple">18.5k</div>
<div className="text-sm text-foreground/60">Total Posts</div>
</div>
<div className="glass rounded-2xl border border-white/10 p-6 text-center">
<div className="text-3xl font-bold text-neon-pink">94%</div>
<div className="text-sm text-foreground/60">Encrypted</div>
</div>
<div className="glass rounded-2xl border border-white/10 p-6 text-center">
<div className="text-3xl font-bold text-neon-green">0</div>
<div className="text-sm text-foreground/60">Data Leaks</div>
</div>
</div>
<div className="mt-8 text-center text-xs text-foreground/40">
<p>
Preview board for threads that persist in your browser, open the{" "}
<Link href="/forum" className="text-neon-cyan hover:underline">
full forum
</Link>{" "}
(dedicated .onion maps here too).
</p>
</div>
</div>
);
};
export default ForumBoard;

View File

@@ -0,0 +1,20 @@
"use client";
/**
* Single required disclosure for the build: appears after every routes main content.
*/
export default function GlobalFakeSiteFooter() {
return (
<footer
className="relative z-[1] border-t-4 border-red-600 bg-[#050505] px-4 py-12 shadow-[0_-12px_40px_rgba(0,0,0,0.85)]"
role="contentinfo"
aria-label="Fiction disclosure"
>
<div className="mx-auto max-w-5xl pb-16 sm:pb-20 md:pb-24">
<p className="text-center text-4xl font-black leading-tight tracking-tight text-red-500 sm:text-5xl md:text-6xl lg:text-7xl">
This site is fake
</p>
</div>
</footer>
);
}

173
components/Hero.tsx Normal file
View File

@@ -0,0 +1,173 @@
"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { SHOP_PRODUCT_COUNT } from "@/lib/shopCatalog";
import { SHOP_SELLER_COUNT } from "@/lib/shopSellers";
const Hero = () => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [particles, setParticles] = useState<Array<{ x: number; y: number; size: number; speed: number }>>([]);
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
setMousePosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener("mousemove", handleMouseMove);
// Generate floating particles
const particleCount = 30;
const newParticles = [];
for (let i = 0; i < particleCount; i++) {
newParticles.push({
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 3 + 1,
speed: Math.random() * 0.5 + 0.2,
});
}
setParticles(newParticles);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);
return (
<section className="relative min-h-screen overflow-hidden px-4 pt-32 md:pt-40">
{/* Animated background particles */}
<div className="absolute inset-0 -z-20">
{particles.map((p, i) => (
<div
key={i}
className="absolute rounded-full bg-neon-cyan opacity-20"
style={{
left: `${p.x}vw`,
top: `${p.y}vh`,
width: `${p.size}px`,
height: `${p.size}px`,
animation: `float ${3 / p.speed}s ease-in-out infinite`,
animationDelay: `${i * 0.1}s`,
}}
/>
))}
{/* Glowing orbs */}
<div
className="absolute left-1/4 top-1/4 h-64 w-64 rounded-full bg-neon-purple opacity-10 blur-3xl"
style={{
transform: `translate(${mousePosition.x * 0.01}px, ${mousePosition.y * 0.01}px)`,
}}
/>
<div
className="absolute right-1/4 bottom-1/4 h-80 w-80 rounded-full bg-neon-cyan opacity-10 blur-3xl"
style={{
transform: `translate(${-mousePosition.x * 0.01}px, ${-mousePosition.y * 0.01}px)`,
}}
/>
{/* Grid lines */}
<div className="absolute inset-0 bg-[linear-gradient(90deg,transparent_49%,rgba(0,224,255,0.03)_50%,transparent_51%,transparent)] bg-[length:40px_40px]"></div>
<div className="absolute inset-0 bg-[linear-gradient(transparent_49%,rgba(0,224,255,0.03)_50%,transparent_51%,transparent)] bg-[length:40px_40px]"></div>
</div>
<div className="container relative mx-auto max-w-6xl">
{/* Glitch text effect */}
<div className="relative mb-6">
<h1 className="font-orbitron text-5xl font-black leading-tight text-foreground md:text-7xl lg:text-8xl">
<span className="block">REVOLUTIONARY</span>
<span className="block text-transparent bg-gradient-to-r from-neon-cyan via-neon-purple to-neon-pink bg-clip-text">
ECOMMERCE
</span>
<span className="block">EXPERIENCE</span>
</h1>
<div className="absolute -top-2 left-0 -z-10 text-5xl font-black text-neon-cyan opacity-30 blur-md md:text-7xl lg:text-8xl">
REVOLUTIONARY
</div>
<div className="absolute -bottom-2 right-0 -z-10 text-5xl font-black text-neon-purple opacity-30 blur-md md:text-7xl lg:text-8xl">
EXPERIENCE
</div>
</div>
<p className="mb-8 max-w-2xl text-xl text-foreground/80 md:text-2xl">
Curated <span className="text-neon-cyan">gray-market candy</span> storefront with live catalog, vendor hall, and BTC-settled
checkout plus companion onions for forums, listings, and the wiki rail.
</p>
<div className="mb-10 flex flex-wrap gap-2 font-mono text-[10px] uppercase tracking-[0.2em] text-foreground/45">
<Link href="#featured" className="rounded border border-white/10 px-3 py-1.5 hover:border-neon-cyan/40 hover:text-neon-cyan/90">
Featured
</Link>
<Link href="#categories" className="rounded border border-white/10 px-3 py-1.5 hover:border-neon-cyan/40 hover:text-neon-cyan/90">
Categories
</Link>
<Link href="/market" className="rounded border border-white/10 px-3 py-1.5 hover:border-neon-purple/40 hover:text-neon-purple/90">
Full market
</Link>
<Link href="/forum" className="rounded border border-white/10 px-3 py-1.5 hover:border-[#ff9a3c]/40 hover:text-[#ffcb8a]">
Forum
</Link>
</div>
{/* CTA Buttons */}
<div className="flex flex-wrap gap-4 md:gap-6">
<Link
href="/market"
className="group relative overflow-hidden rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-8 py-4 font-bold text-background transition-all hover:scale-[1.02] hover:shadow-2xl hover:shadow-neon-cyan/40"
>
<span className="relative z-10">BROWSE CATALOG</span>
<div className="absolute inset-0 -z-10 bg-gradient-to-r from-neon-purple to-neon-cyan opacity-0 transition-opacity group-hover:opacity-100"></div>
<div className="absolute -inset-1 -z-20 animate-pulse rounded-full bg-neon-cyan blur-md opacity-30"></div>
</Link>
<Link
href="/checkout"
className="group glass rounded-full border border-neon-green/35 px-8 py-4 font-bold text-neon-green transition-all hover:border-neon-green hover:bg-neon-green/10"
>
<span className="flex items-center gap-2">
<span className="text-lg"></span>
CHECKOUT &amp; BALANCE
</span>
</Link>
<Link
href="/sanctuary"
className="group glass rounded-full border border-neon-cyan/30 px-7 py-4 text-sm font-bold text-neon-cyan transition-all hover:border-neon-cyan hover:bg-neon-cyan/10"
>
SANCTUARY
</Link>
<Link
href="/drops"
className="group glass rounded-full border border-neon-pink/25 px-7 py-4 text-sm font-medium text-foreground/85 transition-all hover:border-neon-pink/50"
>
<span className="flex items-center gap-2">
<span className="text-lg">🚀</span>
DROPS
<span className="ml-1 inline-block h-2 w-2 animate-pulse rounded-full bg-neon-pink"></span>
</span>
</Link>
</div>
{/* Stats */}
<div className="mt-20 grid grid-cols-2 gap-6 sm:grid-cols-4">
{[
{ value: String(SHOP_PRODUCT_COUNT), label: "Live SKUs", color: "text-neon-cyan" },
{ value: String(SHOP_SELLER_COUNT), label: "Vendor stalls", color: "text-neon-green" },
{ value: "₿ · Ξ · XMR", label: "Settlement rails", color: "text-neon-purple" },
{ value: "v3 ring", label: "Tor-ready layout", color: "text-neon-pink" },
].map((stat) => (
<div
key={stat.label}
className="glass rounded-2xl border border-white/10 p-6 backdrop-blur-sm"
>
<div className={`font-orbitron text-3xl font-bold ${stat.color}`}>{stat.value}</div>
<div className="mt-2 text-sm text-foreground/60">{stat.label}</div>
</div>
))}
</div>
{/* Scroll indicator */}
<div className="absolute bottom-10 left-1/2 -translate-x-1/2">
<div className="h-10 w-px bg-gradient-to-b from-neon-cyan to-transparent"></div>
<div className="mt-2 text-xs text-foreground/40">SCROLL TO UNCOVER</div>
</div>
</div>
</section>
);
};
export default Hero;

View File

@@ -0,0 +1,150 @@
"use client";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import { usePathname } from "next/navigation";
import {
CYBERLUX_ENTRY_COOKIE,
parseCyberluxEntry,
type CyberluxEntry,
} from "@/lib/cyberluxEntry";
import { formatRingLabel } from "@/lib/forumRings";
import { useAccount } from "@/contexts/AccountContext";
import { getChatterSlice } from "@/lib/shopChatterThreads";
function readEntry(): CyberluxEntry {
if (typeof document === "undefined") return "hub";
const raw = `; ${document.cookie}`;
const part = raw.split(`; ${CYBERLUX_ENTRY_COOKIE}=`);
if (part.length !== 2) return "hub";
const v = part.pop()?.split(";").shift();
return parseCyberluxEntry(v);
}
type HubSkin = { bar: string; accent: string };
const DEFAULT_HUB_SKIN: HubSkin = {
bar: "border-zinc-700/40 bg-[#050608]/98 text-zinc-500",
accent: "text-zinc-400",
};
const SKINS: Partial<Record<CyberluxEntry, HubSkin>> = {
hub: {
bar: "border-cyan-500/25 bg-[#050608]/95 text-zinc-400",
accent: "text-cyan-400/90",
},
forum: {
bar: "border-[#5c4030] bg-[#090705]/98 text-[#b8a995]",
accent: "text-[#ff9a3c]",
},
exchange: {
bar: "border-[#4a3f36]/90 bg-[#0b0907]/98 text-[#a89888]",
accent: "text-[#d4a574]",
},
wiki: {
bar: "border-[#334155] bg-[#080c15]/98 text-[#94a3b8]",
accent: "text-[#7dd3fc]",
},
market: {
bar: "border-emerald-900/45 bg-[#030806]/98 text-[#9db0a4]",
accent: "text-[#6ee7b7]",
},
barter: {
bar: "border-rose-900/40 bg-[#0a0606]/98 text-[#c8a8a8]",
accent: "text-[#fb7185]",
},
chatter: {
bar: "border-violet-900/45 bg-[#070510]/98 text-[#a898c4]",
accent: "text-[#c4b5fd]",
},
search: {
bar: "border-slate-600/45 bg-[#050608]/98 text-[#94a3b8]",
accent: "text-[#93c5fd]",
},
syndicate: {
bar: "border-yellow-900/35 bg-[#0b0905]/98 text-[#b8a878]",
accent: "text-[#facc15]",
},
w: {
bar: "border-fuchsia-900/35 bg-[#08060a]/98 text-[#c4a8d4]",
accent: "text-[#e879f9]",
},
};
function hubSkinFor(entry: CyberluxEntry): HubSkin {
return SKINS[entry] ?? DEFAULT_HUB_SKIN;
}
const PREVIEW_N = 3;
/**
* Fixed strip above the disclosure bar: in-world “hub chatter” threads (forum links).
* Hidden on `/chatter` where the full archive already fills the view.
*/
export default function HubChatterPublicStrip() {
const pathname = usePathname();
const { user, hydrated } = useAccount();
const [entry, setEntry] = useState<CyberluxEntry>("hub");
const threads = useMemo(() => getChatterSlice(1, PREVIEW_N).items, []);
useEffect(() => {
setEntry(readEntry());
}, []);
if (pathname?.startsWith("/chatter")) return null;
const skin = hubSkinFor(entry);
return (
<aside
className={`pointer-events-auto fixed bottom-9 left-0 right-0 z-[9997] border-t px-3 py-2 shadow-[0_-6px_20px_rgba(0,0,0,0.4)] backdrop-blur-md sm:px-4 ${skin.bar}`}
aria-label="Public hub chatter — recent threads about the main storefront"
>
<div className="mx-auto flex max-w-6xl flex-col gap-2 sm:flex-row sm:items-center sm:gap-4">
<div className="flex shrink-0 flex-wrap items-center gap-x-2 gap-y-1 font-mono text-[9px] uppercase tracking-[0.22em]">
<span className="rounded border border-white/10 px-1.5 py-0.5 text-[8px] text-[#94a3b8]/90">Public</span>
<span className="opacity-50">·</span>
<Link href="/chatter" className={`font-semibold ${skin.accent} hover:underline`}>
Hub chatter
</Link>
</div>
<ul className="flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-start sm:gap-x-6 sm:gap-y-1">
{threads.map((t, i) => (
<li
key={t.id}
className={`min-w-0 sm:max-w-[min(100%,280px)] ${i > 0 ? "hidden sm:block" : ""}`}
>
<Link
href={`/forum/post/${t.id}`}
className="group block font-mono text-[11px] leading-snug text-[#c8b8a4] hover:text-[#ffcb8a]"
>
<span className={`mr-1.5 text-[9px] uppercase tracking-wider ${skin.accent} opacity-85`}>
{formatRingLabel(t.topicSlug)}
</span>
<span className="line-clamp-2 sm:line-clamp-1">{t.title}</span>
</Link>
</li>
))}
</ul>
<div className="flex shrink-0 flex-wrap items-center gap-3 font-mono text-[10px] uppercase tracking-wider">
<Link href="/chatter" className={`${skin.accent} hover:underline`}>
Full archive
</Link>
<Link href="/forum" className="text-[#6b6358] hover:text-[#b8a995]">
Forum
</Link>
{hydrated ? (
<Link
href={user ? "/dashboard" : "/sign-in"}
className="text-[#6b6358] hover:text-[#b8a995]"
>
{user ? "Dashboard" : "Sign in"}
</Link>
) : null}
</div>
</div>
</aside>
);
}

233
components/Navbar.tsx Normal file
View File

@@ -0,0 +1,233 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { useWallet } from "@/contexts/WalletContext";
import { useAccount } from "@/contexts/AccountContext";
import { useCart } from "@/contexts/CartContext";
const Navbar = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const { isConnected, address, connect, disconnect, luxCredits, usdStoreCredit } = useWallet();
const { user, hydrated } = useAccount();
const { itemCount, hydrated: cartReady } = useCart();
const navItems = [
{ label: "Sanctuary", href: "/sanctuary", icon: "🛡️" },
{ label: "Market", href: "/market", icon: "📈" },
{ label: "Vendors", href: "/vendors", icon: "🏪" },
{ label: "Checkout", href: "/checkout", icon: "₿" },
{ label: "Forum", href: "/forum", icon: "◆" },
{ label: "Exchange", href: "/exchange", icon: "📰" },
{ label: "Barter", href: "/barter", icon: "♻️" },
{ label: "Vault", href: "/vault", icon: "🗝️" },
{ label: "Wiki", href: "/hidden-wiki", icon: "📚" },
{ label: "Atlas", href: "/darknet-atlas", icon: "🗺️" },
{ label: "Syndicate", href: "/syndicate", icon: "🕸️" },
{ label: "Void crawl", href: "/search", icon: "🔦" },
{ label: "Mirrors", href: "/account/hidden-services", icon: "🧅" },
];
const handleWalletClick = () => {
if (isConnected) {
disconnect();
} else {
connect();
}
};
return (
<nav className="glass fixed top-0 left-0 right-0 z-50 mx-auto mt-4 max-w-7xl rounded-2xl border border-white/10 px-6 py-4 backdrop-blur-xl">
<div className="flex items-center justify-between">
{/* Logo */}
<div className="flex items-center gap-3">
<div className="relative">
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-neon-cyan to-neon-purple p-0.5">
<div className="h-full w-full rounded-full bg-background flex items-center justify-center">
<span className="text-xl font-bold"></span>
</div>
</div>
<div className="absolute -inset-1 -z-10 animate-pulse rounded-full bg-neon-cyan blur-md opacity-30"></div>
</div>
<Link href="/" className="font-orbitron text-2xl font-bold tracking-tighter">
Cyber<span className="text-neon-cyan">Lux</span>
</Link>
<span className="hidden rounded-full bg-muted px-3 py-1 text-xs font-medium text-neon-green sm:inline">
v1.0 ALPHA
</span>
</div>
{/* Desktop Navigation */}
<div className="hidden max-w-lg flex-wrap items-center justify-end gap-x-3 gap-y-1 md:flex lg:max-w-2xl lg:gap-x-4 xl:max-w-none xl:flex-nowrap xl:gap-x-5">
{navItems.map((item) => (
<Link
key={item.label}
href={item.href}
className="group relative text-sm font-medium text-foreground/80 transition-colors hover:text-neon-cyan"
>
<span className="mr-1 opacity-60">{item.icon}</span>
{item.label}
<span className="absolute -bottom-1 left-0 h-0.5 w-0 bg-gradient-to-r from-neon-cyan to-neon-purple transition-all group-hover:w-full"></span>
</Link>
))}
</div>
{/* Actions */}
<div className="flex items-center gap-3 md:gap-4">
{cartReady ? (
<Link
href="/checkout"
className="relative hidden rounded-full border border-white/10 px-3 py-2 text-sm text-foreground/80 hover:border-neon-cyan/40 hover:text-neon-cyan sm:inline-flex"
title="Cart"
>
🛒
{itemCount > 0 ? (
<span className="absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-neon-pink px-1 text-[10px] font-bold text-background">
{itemCount > 99 ? "99+" : itemCount}
</span>
) : null}
</Link>
) : null}
<div className="hidden md:flex items-center gap-3">
<div className="flex items-center gap-2 rounded-full bg-white/5 px-4 py-2 border border-white/10">
<span className="text-xs text-foreground/60">LUX</span>
<span className="font-orbitron text-sm text-neon-green">{luxCredits.toLocaleString()}</span>
</div>
<div
className="flex items-center gap-2 rounded-full bg-white/5 px-4 py-2 border border-neon-cyan/20"
title="Verified Bitcoin deposit balance (USD)"
>
<span className="text-xs text-foreground/60">USD</span>
<span className="font-orbitron text-sm text-neon-cyan">${usdStoreCredit.toFixed(2)}</span>
</div>
</div>
{hydrated ? (
user ? (
<Link
href="/dashboard"
className="hidden items-center gap-2 rounded-full border border-neon-cyan/30 bg-neon-cyan/10 px-4 py-2 text-sm font-medium text-neon-cyan hover:bg-neon-cyan/20 sm:flex"
>
<span className="max-w-[8rem] truncate">{user.displayName}</span>
</Link>
) : (
<>
<Link
href="/sign-in"
className="hidden rounded-full border border-white/15 px-4 py-2 text-sm font-medium text-foreground/80 hover:border-neon-cyan/50 hover:text-neon-cyan sm:inline-flex"
>
Sign in
</Link>
<Link
href="/sign-up"
className="hidden rounded-full border border-neon-purple/35 bg-neon-purple/10 px-4 py-2 text-sm font-medium text-neon-purple hover:bg-neon-purple/20 md:inline-flex"
>
Register
</Link>
</>
)
) : null}
<button
onClick={handleWalletClick}
className={`flex items-center gap-2 rounded-full px-4 py-2 font-medium transition-all ${isConnected
? "bg-gradient-to-r from-neon-green to-neon-cyan text-background"
: "glow-border text-neon-cyan hover:bg-white/5"
}`}
>
<span className="text-lg">{isConnected ? "🟢" : "⚡"}</span>
{isConnected ? `${address?.slice(0, 6)}...${address?.slice(-4)}` : "Connect Wallet"}
</button>
<button
className="rounded-full bg-white/5 p-2 hover:bg-white/10"
aria-label="Toggle menu"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
<div className="h-5 w-5 space-y-1.5">
<div className={`h-0.5 bg-neon-cyan transition-transform ${isMenuOpen ? "translate-y-2 rotate-45" : ""}`}></div>
<div className={`h-0.5 bg-neon-purple transition-opacity ${isMenuOpen ? "opacity-0" : ""}`}></div>
<div className={`h-0.5 bg-neon-pink transition-transform ${isMenuOpen ? "-translate-y-2 -rotate-45" : ""}`}></div>
</div>
</button>
</div>
</div>
{/* Mobile Menu */}
{isMenuOpen && (
<div className="glass mt-4 rounded-xl border border-white/10 p-4 backdrop-blur-xl md:hidden">
<div className="mb-3 flex gap-2 border-b border-white/10 pb-3">
{cartReady ? (
<Link
href="/checkout"
className="relative flex-1 rounded-lg border border-white/15 py-2 text-center text-sm font-medium"
onClick={() => setIsMenuOpen(false)}
>
🛒 Cart
{itemCount > 0 ? (
<span className="ml-1 rounded bg-neon-pink px-1.5 text-[10px] font-bold text-background">
{itemCount}
</span>
) : null}
</Link>
) : null}
{hydrated && user ? (
<Link
href="/dashboard"
className="flex-1 rounded-lg bg-neon-cyan/15 py-2 text-center text-sm font-bold text-neon-cyan"
onClick={() => setIsMenuOpen(false)}
>
Dashboard
</Link>
) : hydrated ? (
<Link
href="/sign-in"
className="flex-1 rounded-lg border border-white/15 py-2 text-center text-sm font-medium"
onClick={() => setIsMenuOpen(false)}
>
Sign in
</Link>
) : null}
{hydrated && !user ? (
<Link
href="/sign-up"
className="flex-1 rounded-lg border border-neon-purple/40 py-2 text-center text-sm text-neon-purple"
onClick={() => setIsMenuOpen(false)}
>
Register
</Link>
) : null}
</div>
<div className="grid grid-cols-2 gap-3">
{navItems.map((item) => (
<Link
key={item.label}
href={item.href}
className="flex items-center gap-2 rounded-lg bg-white/5 p-3 text-sm font-medium transition-colors hover:bg-white/10"
onClick={() => setIsMenuOpen(false)}
>
<span className="text-lg">{item.icon}</span>
{item.label}
</Link>
))}
</div>
<div className="mt-4 flex gap-3 border-t border-white/10 pt-4 text-xs">
<Link
href="/sanctuary"
className="flex-1 rounded-lg border border-white/10 px-3 py-2 text-center text-foreground/70 hover:border-neon-cyan/40 hover:text-neon-cyan"
onClick={() => setIsMenuOpen(false)}
>
Sanctuary
</Link>
<Link
href="/account/hidden-services"
className="flex-1 rounded-lg border border-white/10 px-3 py-2 text-center text-foreground/70 hover:border-neon-purple/40 hover:text-neon-purple"
onClick={() => setIsMenuOpen(false)}
>
Onion map
</Link>
</div>
</div>
)}
</nav>
);
};
export default Navbar;

View File

@@ -0,0 +1,100 @@
"use client";
import { useEffect, useState } from "react";
import {
CYBERLUX_ENTRY_COOKIE,
parseCyberluxEntry,
type CyberluxEntry,
} from "@/lib/cyberluxEntry";
function readEntryFromCookie(): CyberluxEntry {
if (typeof document === "undefined") return "hub";
const raw = `; ${document.cookie}`;
const part = raw.split(`; ${CYBERLUX_ENTRY_COOKIE}=`);
if (part.length !== 2) return "hub";
const v = part.pop()?.split(";").shift();
return parseCyberluxEntry(v);
}
type Skin = { bar: string; label: string };
const DEFAULT_SKIN: Skin = {
bar: "border-zinc-600/40 bg-black/90 text-zinc-500",
label: "CYBERLUX NODE · Tor v3 · onion endpoint",
};
const SKINS: Partial<Record<CyberluxEntry, Skin>> = {
hub: {
bar: "border-cyan-500/25 bg-black/85 text-zinc-400",
label: "CYBERLUX · primary storefront · Tor v3 · connection verified",
},
forum: {
bar: "border-[#ff7a18]/40 bg-[#0c0500]/95 text-[#ffc9a3]/70",
label: "VOID AGGREGATE · forum relay · onion authenticated",
},
exchange: {
bar: "border-amber-900/50 bg-[#0f0d0a]/95 text-[#c4b5a0]",
label: "EXCHANGE MIRROR · classifieds host · encrypted circuit",
},
wiki: {
bar: "border-[#3d5a80]/45 bg-[#0a0e14]/95 text-[#94a8c4]",
label: "WIKI LAYER · directory mirror · read-only overlay",
},
market: {
bar: "border-emerald-900/50 bg-[#040806]/95 text-[#9ca89a]",
label: "MARKET NODE · vendor shelf · onion storefront",
},
barter: {
bar: "border-rose-900/45 bg-[#0c0608]/95 text-[#d4a8b0]",
label: "BARTER PIT · swap relay · Tor v3",
},
chatter: {
bar: "border-violet-900/50 bg-[#08060c]/95 text-[#b8a9d4]",
label: "CHATTER ARCHIVE · hub threads · public relay",
},
search: {
bar: "border-slate-600/50 bg-[#06080a]/95 text-[#a8b4c4]",
label: "SEARCH INDEX · catalog probe · encrypted circuit",
},
syndicate: {
bar: "border-yellow-900/40 bg-[#0c0a06]/95 text-[#c9b896]",
label: "SYNDICATE MAP · network atlas · node federation",
},
w: {
bar: "border-fuchsia-900/40 bg-[#0a060c]/95 text-[#d8b8e8]",
label: "SHADOW NODES · /w/[slug] · syndicate landing on /",
},
};
function skinFor(entry: CyberluxEntry): Skin {
return (
SKINS[entry] ?? {
...DEFAULT_SKIN,
label: `${entry.replace(/-/g, " ").toUpperCase()} · Tor v3 · onion relay`,
}
);
}
/**
* Bottom strip keyed off proxy cookie (set from `X-Cyberlux-Node` behind nginx).
* One Tor service = one visual personality for the disclosure line.
*/
export default function OnionDisclosureBar() {
const [entry, setEntry] = useState<CyberluxEntry>("hub");
useEffect(() => {
setEntry(readEntryFromCookie());
}, []);
const skin = skinFor(entry);
return (
<div
className={`pointer-events-none fixed bottom-0 left-0 right-0 z-[10000] border-t-2 py-1.5 text-center font-mono text-[10px] uppercase tracking-[0.2em] backdrop-blur-sm ${skin.bar}`}
role="status"
aria-label="Entry host"
>
{skin.label}
</div>
);
}

View File

@@ -0,0 +1,135 @@
"use client";
import { useEffect, useRef } from "react";
const ParticleBackground = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
// Set canvas size
const resize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
resize();
window.addEventListener("resize", resize);
// Particle system
const particles: Array<{
x: number;
y: number;
vx: number;
vy: number;
radius: number;
color: string;
}> = [];
const colors = ["#00ffff", "#9d00ff", "#ff00ff", "#00ff9d"];
// Create particles
for (let i = 0; i < 80; i++) {
particles.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
radius: Math.random() * 2 + 0.5,
color: colors[Math.floor(Math.random() * colors.length)],
});
}
// Mouse interaction
let mouseX = canvas.width / 2;
let mouseY = canvas.height / 2;
const onMouseMove = (e: MouseEvent) => {
mouseX = e.clientX;
mouseY = e.clientY;
};
window.addEventListener("mousemove", onMouseMove);
// Animation loop
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw connecting lines
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
ctx.beginPath();
ctx.strokeStyle = `rgba(0, 224, 255, ${0.2 * (1 - distance / 150)})`;
ctx.lineWidth = 0.5;
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.stroke();
}
}
}
// Update and draw particles
particles.forEach((p) => {
// Move particle
p.x += p.vx;
p.y += p.vy;
// Bounce off edges
if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
// Attract to mouse
const dx = mouseX - p.x;
const dy = mouseY - p.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
p.vx += dx * 0.0001;
p.vy += dy * 0.0001;
}
// Limit velocity
const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
if (speed > 1) {
p.vx = (p.vx / speed) * 1;
p.vy = (p.vy / speed) * 1;
}
// Draw particle
ctx.beginPath();
const gradient = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.radius * 3);
gradient.addColorStop(0, p.color);
gradient.addColorStop(1, "transparent");
ctx.fillStyle = gradient;
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
ctx.fill();
});
requestAnimationFrame(animate);
};
const animationId = requestAnimationFrame(animate);
// Cleanup
return () => {
window.removeEventListener("resize", resize);
window.removeEventListener("mousemove", onMouseMove);
cancelAnimationFrame(animationId);
};
}, []);
return (
<canvas
ref={canvasRef}
className="fixed inset-0 -z-10 h-full w-full"
style={{ pointerEvents: "none" }}
/>
);
};
export default ParticleBackground;

188
components/ProductCard.tsx Normal file
View File

@@ -0,0 +1,188 @@
"use client";
import { useMemo, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { useCart } from "@/contexts/CartContext";
import { ProductSocialBlock } from "@/components/shop/ProductSocialBlock";
import { SHOP_PRODUCTS, type ShopProduct } from "@/lib/shopCatalog";
import { getProductSocialMetrics } from "@/lib/shopProductSocial";
interface ProductCardProps {
id: string;
name: string;
description: string;
price: number;
currency: "BTC" | "ETH" | "USD" | "XMR";
category: string;
imageUrl: string;
sellerHandle: string;
sellerSlug: string;
limited?: boolean;
glowColor?: string;
/** When set, add-to-cart uses this row; otherwise looks up by `id` in SHOP_PRODUCTS */
shopProduct?: ShopProduct;
}
const ProductCard = ({
id,
name,
description,
price,
currency,
category,
imageUrl,
sellerHandle,
sellerSlug,
limited = false,
glowColor: _glow = "neon-cyan",
shopProduct,
}: ProductCardProps) => {
void _glow;
const [isHovered, setIsHovered] = useState(false);
const [isFavorite, setIsFavorite] = useState(false);
const [justAdded, setJustAdded] = useState(false);
const { addToCart, hydrated: cartReady } = useCart();
const metrics = useMemo(() => getProductSocialMetrics(id), [id]);
const currencySymbols = {
BTC: "₿",
ETH: "Ξ",
USD: "$",
XMR: "⏣",
};
const formatPrice = (p: number) => {
if (currency === "BTC") return p.toFixed(6);
if (currency === "ETH") return p.toFixed(4);
if (currency === "XMR") return p.toFixed(3);
return p.toFixed(2);
};
const resolveProduct = (): ShopProduct | undefined => shopProduct ?? SHOP_PRODUCTS.find((x) => x.id === id);
const handleAddToCart = () => {
const p = resolveProduct();
if (!p) return;
addToCart(p, 1);
setJustAdded(true);
window.setTimeout(() => setJustAdded(false), 1800);
};
return (
<div
className="group relative overflow-hidden rounded-2xl border border-white/10 transition-all duration-500 hover:scale-[1.02] hover:shadow-2xl hover:shadow-neon-cyan/10"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div
className={`absolute -inset-1 -z-10 rounded-2xl opacity-0 blur-xl transition-opacity duration-500 ${
isHovered ? "bg-neon-cyan/30 opacity-20" : ""
}`}
/>
{limited && (
<div className="absolute left-4 top-4 z-10">
<div className="relative">
<div className="animate-glow rounded-full bg-gradient-to-r from-neon-pink to-neon-purple px-3 py-1 text-xs font-bold text-background">
LIMITED
</div>
<div className="absolute -inset-1 -z-10 animate-pulse rounded-full bg-neon-pink blur-md opacity-30"></div>
</div>
</div>
)}
<button
type="button"
className="absolute right-4 top-4 z-10 glass rounded-full p-2"
onClick={() => setIsFavorite(!isFavorite)}
aria-label={isFavorite ? "Remove from favorites" : "Add to favorites"}
>
<span className={`text-lg ${isFavorite ? "text-neon-pink" : "text-foreground/60"}`}>
{isFavorite ? "❤️" : "🤍"}
</span>
</button>
<div className="relative aspect-square overflow-hidden">
<Image
src={imageUrl}
alt={name}
fill
className="object-cover transition-transform duration-700 group-hover:scale-110"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
unoptimized={imageUrl.startsWith("/")}
/>
<div className="absolute inset-0 bg-gradient-to-t from-background/80 via-transparent to-transparent opacity-0 transition-opacity group-hover:opacity-100" />
</div>
<div className="glass border-t border-white/10 p-6 backdrop-blur-sm">
<div className="mb-3">
<ProductSocialBlock metrics={metrics} compact />
</div>
<div className="mb-2 flex items-center justify-between gap-2">
<span className="rounded-full bg-white/5 px-3 py-1 text-xs font-medium text-neon-cyan">{category}</span>
<div className="flex flex-shrink-0 items-center gap-1">
<span className="font-orbitron text-xl font-bold text-foreground md:text-2xl">
{currencySymbols[currency]}
{formatPrice(price)}
</span>
<span className="text-sm text-foreground/50">{currency}</span>
</div>
</div>
<h3 className="mb-2 font-orbitron text-xl font-bold">{name}</h3>
<p className="mb-1 text-xs text-foreground/50">
Seller:{" "}
<Link href={`/vendor/${sellerSlug}`} className="text-neon-cyan hover:text-neon-purple hover:underline">
{sellerHandle}
</Link>
</p>
<p className="mb-4 line-clamp-3 text-sm text-foreground/70">{description}</p>
<div className="mb-4 space-y-2 border-t border-white/10 pt-4">
<p className="font-mono text-[9px] font-bold uppercase tracking-widest text-foreground/35">Verified chatter</p>
{metrics.testimonials.slice(0, 1).map((t, i) => (
<p key={i} className="text-xs italic leading-relaxed text-foreground/55">
{t.text}
<span className="mt-1 block font-mono text-[10px] not-italic text-neon-cyan/70">
@{t.handle} · {t.rating} · {t.daysAgo}d ago
</span>
</p>
))}
</div>
<div className="flex items-center gap-3">
<button
type="button"
onClick={handleAddToCart}
disabled={!cartReady || !resolveProduct()}
className="flex-1 rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple py-3 text-sm font-bold text-background transition-transform enabled:hover:scale-[1.02] disabled:opacity-50"
>
{justAdded ? "ADDED ✓" : "ADD TO CART"}
</button>
<Link
href="/checkout"
className="glass flex items-center justify-center rounded-full p-3 transition-colors hover:bg-white/10"
title="Checkout"
>
<span className="text-lg">🛒</span>
</Link>
</div>
<div className="mt-4 flex items-center justify-between text-xs text-foreground/50">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-neon-green"></div>
<span>In stock · Ships discreetly</span>
</div>
<div className="flex items-center gap-1">
<span>🔐</span>
<span>Encrypted</span>
</div>
</div>
</div>
</div>
);
};
export default ProductCard;

View File

@@ -0,0 +1,37 @@
"use client";
import { useState, useEffect } from "react";
export default function SecretLayer() {
const [message, setMessage] = useState<string | null>(null);
useEffect(() => {
// In a real app, this would fetch from a database or listen to a WebSocket
const savedMessage = localStorage.getItem("shadow_broadcast");
if (savedMessage) {
setMessage(savedMessage);
}
// Listen for storage changes (if admin updates in another tab)
const handleStorage = () => {
setMessage(localStorage.getItem("shadow_broadcast"));
};
window.addEventListener("storage", handleStorage);
return () => window.removeEventListener("storage", handleStorage);
}, []);
if (!message) return null;
return (
<div className="fixed bottom-0 left-0 w-full bg-red-600 text-white py-2 px-4 text-center font-mono text-xs z-[9999] animate-pulse">
<span className="font-bold uppercase mr-4">[ADMIN BROADCAST]:</span>
{message}
<button
onClick={() => setMessage(null)}
className="ml-4 underline opacity-50 hover:opacity-100"
>
DISMISS
</button>
</div>
);
}

View File

@@ -0,0 +1,31 @@
"use client";
import Link from "next/link";
export function WebringBadge({ storeHref = "/" }: { storeHref?: string }) {
return (
<div className="mt-16 glass rounded-2xl border border-white/10 p-6">
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<div>
<div className="font-orbitron text-lg font-bold text-neon-green">
VERIFIED WORKING SITE
</div>
<div className="text-sm text-foreground/60">
This microsite has been audited by the Department of Vibes and found to be
suspiciously operational.
</div>
</div>
<Link
href={storeHref}
className="inline-flex items-center justify-center rounded-full bg-gradient-to-r from-neon-cyan to-neon-purple px-6 py-3 font-bold text-background"
>
Return to CyberLux
</Link>
</div>
<div className="mt-4 text-xs text-foreground/40">
Disclaimer: parody universe. No real goods. No real money. Just immaculate confusion.
</div>
</div>
);
}

View File

@@ -0,0 +1,94 @@
"use client";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { Suspense } from "react";
const LANES: { id: string; label: string }[] = [
{ id: "goods", label: "Goods" },
{ id: "services", label: "Labor" },
{ id: "data", label: "Data" },
{ id: "open", label: "Open" },
];
function BarterPitChromeInner({ children }: { children: React.ReactNode }) {
const sp = useSearchParams();
const activeLane = sp.get("lane") ?? undefined;
return (
<div className="min-h-screen bg-[#030806] text-[#b8f5c6]">
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.07]"
style={{
backgroundImage: `linear-gradient(rgba(57,255,20,0.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(57,255,20,0.05) 1px, transparent 1px)`,
backgroundSize: "48px 48px",
}}
/>
<div className="pointer-events-none fixed inset-0 z-0 bg-[radial-gradient(ellipse_at_top,_rgba(0,80,40,0.35),_transparent_55%)]" />
<header className="relative z-20 border-b border-[#0d2818] bg-[#050a07]/95 backdrop-blur-md">
<div className="mx-auto flex max-w-6xl flex-col gap-3 px-4 py-5 md:flex-row md:items-end md:justify-between">
<div>
<p className="font-mono text-[9px] uppercase tracking-[0.5em] text-[#39ff14]/55">unhosted barter mesh</p>
<h1 className="mt-1 bg-gradient-to-r from-[#7cfc9aad] to-[#39ff14] bg-clip-text font-mono text-2xl font-black tracking-tight text-transparent md:text-3xl">
ASH PIT
</h1>
<p className="mt-2 max-w-xl font-mono text-[11px] leading-relaxed text-[#6b9b78]/95">
Trading floor for <span className="text-[#9af0a8]">swap terms</span> no cart, no spot book. Post what you have,
what you want, negotiate in-thread. Same session wallet as the hub when you need settlement.
</p>
</div>
<nav className="flex flex-wrap gap-2 font-mono text-[10px] uppercase tracking-wider">
<Link
href="/"
className="rounded border border-[#164024] bg-[#0a120d] px-3 py-2 text-[#8dcda0] hover:border-[#39ff14]/40"
>
Storefront
</Link>
<Link href="/exchange" className="rounded border border-[#164024] px-3 py-2 text-[#8dcda0] hover:border-[#39ff14]/40">
Gazette
</Link>
<Link href="/forum" className="rounded border border-[#164024] px-3 py-2 text-[#8dcda0] hover:border-[#39ff14]/40">
Forum
</Link>
<Link href="/checkout" className="rounded border border-[#39ff14]/35 bg-[#39ff14]/10 px-3 py-2 text-[#c8ffce] hover:bg-[#39ff14]/18">
Settlement
</Link>
</nav>
</div>
<div className="mx-auto flex max-w-6xl flex-wrap gap-1 border-t border-[#0d2818] px-4 py-2">
<Link
href="/barter"
className={`rounded px-2 py-1 font-mono text-[10px] uppercase ${
!activeLane ? "bg-[#39ff14]/20 text-[#c8ffce]" : "text-[#5a7d62] hover:text-[#9af0a8]"
}`}
>
All lanes
</Link>
{LANES.map((l) => (
<Link
key={l.id}
href={`/barter?lane=${l.id}`}
className={`rounded px-2 py-1 font-mono text-[10px] uppercase ${
activeLane === l.id ? "bg-[#39ff14]/20 text-[#c8ffce]" : "text-[#5a7d62] hover:text-[#9af0a8]"
}`}
>
{l.label}
</Link>
))}
</div>
</header>
<div className="relative z-10 mx-auto max-w-6xl px-4 py-10">{children}</div>
</div>
);
}
export default function BarterPitChrome({ children }: { children: React.ReactNode }) {
return (
<Suspense fallback={<div className="min-h-screen bg-[#030806] pt-24 text-center font-mono text-sm text-[#5a7d62]">Loading</div>}>
<BarterPitChromeInner>{children}</BarterPitChromeInner>
</Suspense>
);
}

View File

@@ -0,0 +1,124 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import LexiconStrip from "@/components/site/LexiconStrip";
import { EXCHANGE_ATMOSPHERE_TAGS } from "@/lib/cyberluxZoneLexicon";
const SECTIONS: { href: string; label: string; blurb: string }[] = [
{ href: "/exchange", label: "The board", blurb: "WTS / WTB stream + submit" },
{ href: "/exchange/guidelines", label: "Guidelines", blurb: "Posting law & risk rails" },
{ href: "/exchange/ledger", label: "Ledger view", blurb: "Volume, mix, freshness" },
];
export default function ExchangeSiteChrome({ children }: { children: React.ReactNode }) {
const pathname = usePathname() ?? "";
return (
<div className="min-h-screen bg-[#0c0a08] text-[#d6cbb8] font-serif">
<div className="border-b-4 border-double border-[#5c4d3d] bg-[#14100c]">
<div className="mx-auto max-w-6xl px-4 py-8">
<p className="text-center text-[10px] font-bold uppercase tracking-[0.5em] text-[#8a7b6b]">
Weekly stranger gazette · multi-section
</p>
<Link href="/exchange" className="block">
<h1 className="mt-2 text-center text-5xl font-black capitalize leading-none tracking-tight text-[#f0e6d8] md:text-6xl">
The Exchange
</h1>
</Link>
<p className="mt-3 text-center text-xs italic text-[#8a7b6b]">
Classifieds spine same wallet session as the hub. Board, policy, and ledger are first-class routes, not bolt-on tabs.
</p>
<div className="mt-6 flex flex-wrap justify-center gap-4 border-y border-[#3d3228] py-3 text-xs font-bold uppercase tracking-wide text-[#a89888]">
<Link href="/" className="text-[#d4a574] underline decoration-2 underline-offset-4 hover:text-[#e8c49a]">
Hub
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/market" className="text-[#d4a574] underline decoration-2 underline-offset-4 hover:text-[#e8c49a]">
Market
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/forum" className="text-[#d4a574] underline decoration-2 underline-offset-4 hover:text-[#e8c49a]">
Forum
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/barter" className="text-[#7cfc9a] underline decoration-2 underline-offset-4 hover:text-[#b7ffca]">
Barter
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/search" className="text-[#9ad7aa] underline decoration-2 underline-offset-4 hover:text-[#d5f5dc]">
Crawler
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/hidden-wiki" className="text-[#94a8c4] underline decoration-2 underline-offset-4 hover:text-[#d8e4f2]">
Wiki
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/darknet-atlas" className="text-[#9fc7ff] underline decoration-2 underline-offset-4 hover:text-[#dcebff]">
Atlas
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/syndicate" className="text-fuchsia-200 underline decoration-2 underline-offset-4 hover:text-fuchsia-100">
Syndicate
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/dashboard" className="text-[#d4a574] underline decoration-2 underline-offset-4 hover:text-[#e8c49a]">
Dashboard
</Link>
<span className="text-[#4a3f36]">|</span>
<Link href="/account/hidden-services" className="text-[#d4a574] underline decoration-2 underline-offset-4 hover:text-[#e8c49a]">
Mirrors
</Link>
</div>
</div>
<div className="mx-auto max-w-6xl px-4 pb-3 text-[#6b5c4d]">
<LexiconStrip label="Gazette environment · trade lexicon" tags={EXCHANGE_ATMOSPHERE_TAGS} className="border-[#3d3228]" />
</div>
</div>
<div className="border-b border-[#3d3228] bg-[#100d0a] lg:hidden">
<div className="mx-auto flex max-w-6xl gap-2 overflow-x-auto px-4 py-2">
{SECTIONS.map((s) => (
<Link
key={s.href}
href={s.href}
className={`shrink-0 rounded border px-3 py-1.5 font-mono text-[10px] font-bold uppercase tracking-wide ${
pathname === s.href
? "border-[#c4a574] bg-[#c4a574]/15 text-[#f0e6d8]"
: "border-[#4a3f36] text-[#8a7b6b] hover:border-[#8d6e4a]"
}`}
>
{s.label}
</Link>
))}
</div>
</div>
<div className="mx-auto flex max-w-6xl gap-8 px-4 pb-24 pt-6">
<aside className="hidden w-56 shrink-0 font-mono lg:block">
<div className="sticky top-6 space-y-2 border-r border-[#3d3228] pr-4">
<p className="text-[9px] font-bold uppercase tracking-[0.3em] text-[#6b5c4d]">Sections</p>
{SECTIONS.map((s) => {
const active = pathname === s.href;
return (
<Link
key={s.href}
href={s.href}
className={`block rounded border px-3 py-2 text-left transition ${
active ? "border-[#c4a574]/60 bg-[#1a1612]" : "border-transparent hover:border-[#4a3f36]"
}`}
>
<span className={`text-[11px] font-bold uppercase ${active ? "text-[#e8c49a]" : "text-[#a89888]"}`}>
{s.label}
</span>
<p className="mt-0.5 text-[9px] leading-snug text-[#6b5c4d]">{s.blurb}</p>
</Link>
);
})}
</div>
</aside>
<div className="min-w-0 flex-1">{children}</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,101 @@
"use client";
import type { ReactNode } from "react";
import Link from "next/link";
import { FORUM_RINGS } from "@/lib/forumRings";
import type { ForumSortMode } from "@/lib/forumSort";
type Props = {
sort: ForumSortMode;
onSortChange: (m: ForumSortMode) => void;
search: string;
onSearchChange: (q: string) => void;
/** When set, show ring chips + `null` = all rings. Omit `onRingChange` to hide chips (ring sub-page). */
ringFilter: string | null;
onRingChange?: (slug: string | null) => void;
extraRight?: ReactNode;
threadCount?: number;
hydrated?: boolean;
};
export default function ForumFeedToolbar({
sort,
onSortChange,
search,
onSearchChange,
ringFilter,
onRingChange,
extraRight,
threadCount,
hydrated,
}: Props) {
return (
<div className="space-y-4 border-b border-[#3d3228] pb-4">
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<label className="min-w-0 flex-1 font-mono text-[10px] uppercase tracking-wider text-[#8a7d6b]">
<span className="mb-1 block text-[#ff9a3c]/70">Search threads</span>
<input
value={search}
onChange={(e) => onSearchChange(e.target.value)}
placeholder="Title, body, author, ring…"
className="w-full rounded border border-[#3d3228] bg-[#0d0a07] px-3 py-2 font-mono text-sm text-[#e7d9c8] placeholder:text-[#5c5044] focus:border-[#ff9a3c]/60 focus:outline-none"
/>
</label>
<div className="flex shrink-0 flex-wrap items-center gap-2">
{(["hot", "new", "top"] as const).map((m) => (
<button
key={m}
type="button"
onClick={() => onSortChange(m)}
className={`rounded px-3 py-1.5 font-mono text-[11px] font-bold uppercase tracking-wider ${
sort === m ? "bg-[#c45c26]/40 text-[#ffcb8a]" : "text-[#8a7d6b] hover:text-[#ffcb8a]"
}`}
>
{m}
</button>
))}
</div>
</div>
{onRingChange ? (
<div className="flex flex-wrap items-center gap-2 font-mono text-[10px] uppercase tracking-wider">
<span className="text-[#5c5044]">Ring</span>
<button
type="button"
onClick={() => onRingChange(null)}
className={`rounded px-2.5 py-1 font-bold ${
ringFilter === null ? "bg-[#3d3228] text-[#ffcb8a]" : "text-[#8a7d6b] hover:text-[#ffcb8a]"
}`}
>
all
</button>
{FORUM_RINGS.map((r) => (
<button
key={r.slug}
type="button"
onClick={() => onRingChange(r.slug)}
className={`rounded px-2.5 py-1 font-bold ${
ringFilter === r.slug ? "bg-[#ff9a3c]/20 text-[#ffcb8a]" : "text-[#8a7d6b] hover:text-[#ffcb8a]"
}`}
>
r/{r.shortName}
</button>
))}
</div>
) : null}
<div className="flex flex-wrap items-center justify-between gap-2 font-mono text-[10px] text-[#5c5044]">
<span>{hydrated === false ? "…" : threadCount !== undefined ? `${threadCount} threads` : null}</span>
<div className="flex flex-wrap items-center gap-2">
{extraRight}
<Link
href="/forum/submit"
className="rounded border border-[#ff9a3c]/60 px-3 py-1 font-bold uppercase text-[#ffcb8a] hover:bg-[#ff9a3c]/15"
>
+ New post
</Link>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,74 @@
"use client";
import Link from "next/link";
import { formatRingLabel } from "@/lib/forumRings";
import { displayScore, isPinnedThread, isReadOnlyThread, type ForumThread } from "@/lib/forumState";
import { ThreadVoteColumn } from "@/components/forum/ThreadVoteColumn";
export function ForumPostCard({
thread,
onVoteChange,
}: {
thread: ForumThread;
/** Fire when vote changes so list meta (score) re-renders from localStorage */
onVoteChange?: () => void;
}) {
const pinned = isPinnedThread(thread);
const locked = isReadOnlyThread(thread.id);
const score = displayScore(thread);
return (
<div
className={`flex gap-3 rounded-lg border bg-[#120e0a]/90 p-3 transition-colors ${
pinned
? "border-[#c45c26]/65 shadow-[0_0_0_1px_rgba(196,92,38,0.12)] hover:border-[#ff9a3c]/55"
: "border-[#3d3228] hover:border-[#c45c26]/45"
}`}
>
<div onClick={(e) => e.stopPropagation()} className="shrink-0">
<ThreadVoteColumn thread={thread} dense onVoteChange={onVoteChange} />
</div>
<Link href={`/forum/post/${thread.id}`} className="min-w-0 flex-1 outline-none">
<div className="flex flex-wrap items-center gap-2">
{pinned ? (
<span className="rounded bg-[#c45c26]/35 px-2 py-0.5 font-mono text-[9px] font-black uppercase tracking-widest text-[#ffcb8a]">
Pinned
</span>
) : null}
{locked ? (
<span className="rounded border border-[#5c5044] px-2 py-0.5 font-mono text-[9px] uppercase tracking-wider text-[#8a7d6b]">
Staff
</span>
) : null}
</div>
<p className="mt-1 font-mono text-[10px] uppercase tracking-wider text-[#8a7d6b]">
<span className="text-[#ff9a3c]/90">{formatRingLabel(thread.topicSlug)}</span>
<span className="mx-2 text-[#3d3228]">·</span>
<span>{thread.author || "Anonymous"}</span>
<span className="mx-2 text-[#3d3228]">·</span>
<span>{formatAgo(thread.ts)}</span>
<span className="mx-2 text-[#3d3228]">·</span>
<span className="tabular-nums text-[#ffcb8a]/80">{score} pts</span>
</p>
<h3 className="mt-1 font-mono text-base font-bold leading-snug text-[#ffcb8a] hover:underline md:text-lg">
{thread.title}
</h3>
<p className="mt-2 line-clamp-2 text-sm leading-relaxed text-[#b8a995]">{thread.body}</p>
<div className="mt-3 flex flex-wrap gap-3 font-mono text-[11px] text-[#8a7d6b]">
<span className="rounded border border-[#3d3228] px-2 py-0.5">{thread.replies.length} comments</span>
</div>
</Link>
</div>
);
}
function formatAgo(ts: number): string {
const s = Math.floor((Date.now() - ts) / 1000);
if (s < 60) return `${s}s ago`;
const m = Math.floor(s / 60);
if (m < 60) return `${m}m ago`;
const h = Math.floor(m / 60);
if (h < 48) return `${h}h ago`;
const d = Math.floor(h / 24);
return `${d}d ago`;
}

View File

@@ -0,0 +1,167 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import LexiconStrip from "@/components/site/LexiconStrip";
import { useAccount } from "@/contexts/AccountContext";
import { FORUM_RINGS, getRing } from "@/lib/forumRings";
import { FORUM_ATMOSPHERE_TAGS } from "@/lib/cyberluxZoneLexicon";
const SITE_NAME = "Void Aggregate";
const SITE_TAGLINE = "distributed rumor ledger";
function Scanlines() {
return (
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.1]"
style={{
backgroundImage:
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.5) 3px)",
}}
/>
);
}
export default function ForumSiteChrome({ children }: { children: React.ReactNode }) {
const { user, hydrated } = useAccount();
const pathname = usePathname();
const ringMatch = pathname?.match(/^\/forum\/ring\/([^/]+)/);
const activeSlug = ringMatch?.[1] ?? null;
const activeRing = activeSlug ? getRing(activeSlug) : null;
return (
<div className="min-h-screen bg-[#070604] text-[#e7d9c8]">
<Scanlines />
<header className="sticky top-0 z-20 border-b border-[#c45c26]/30 bg-[#0d0a07]/95 backdrop-blur-md">
<div className="mx-auto flex max-w-6xl flex-col gap-3 px-4 py-4 md:flex-row md:items-center md:justify-between">
<Link href="/forum" className="group block">
<p className="text-[9px] font-mono uppercase tracking-[0.45em] text-[#ff9a3c]/70">{SITE_TAGLINE}</p>
<h1 className="font-mono text-xl font-black tracking-tight text-[#ffcb8a] md:text-2xl">
{SITE_NAME}
<span className="ml-2 text-[10px] font-normal opacity-50 group-hover:opacity-80">v3</span>
</h1>
</Link>
<div className="flex flex-wrap items-center gap-2 text-[11px] font-mono uppercase tracking-wider">
<span className="hidden rounded border border-[#5c4030] bg-[#1a1510] px-3 py-1.5 text-[#b8a995] sm:inline">
onion forum
</span>
<Link
href="/forum/submit"
className="rounded border border-[#ff9a3c] bg-[#ff9a3c]/15 px-3 py-1.5 font-bold text-[#ffcb8a] hover:bg-[#ff9a3c]/25"
>
+ Submit
</Link>
{hydrated && user ? (
<Link href="/dashboard" className="rounded border border-[#2a1810] px-3 py-1.5 text-[#c4a574] hover:border-[#ff9a3c]/45">
Dashboard
</Link>
) : (
<>
<Link href="/sign-in" className="rounded border border-[#5c4030] px-3 py-1.5 text-[#c4b5a0] hover:border-[#ff9a3c]/50">
Sign in
</Link>
<Link
href="/sign-up"
className="rounded border border-[#ff9a3c]/50 bg-[#ff9a3c]/10 px-3 py-1.5 text-[#ffcb8a] hover:bg-[#ff9a3c]/20"
>
Sign up
</Link>
<Link
href="/account/hidden-services"
className="rounded border border-[#5c4030] px-3 py-1.5 text-[#9a8f7d] hover:border-[#ff9a3c]/50"
>
Mirrors
</Link>
</>
)}
<Link href="/" className="rounded border border-[#5c4030] px-3 py-1.5 text-[#c4b5a0] hover:border-[#ff9a3c]/50">
Hub
</Link>
<Link href="/exchange" className="rounded border border-[#5c4030] px-3 py-1.5 text-[#c4b5a0] hover:border-[#ff9a3c]/50">
Exchange
</Link>
<Link href="/barter" className="rounded border border-[#1a3d24] bg-[#0a120d] px-3 py-1.5 text-[#7cfc9a] hover:border-[#39ff14]/40">
Barter
</Link>
<Link href="/hidden-wiki" className="rounded border border-[#5c4030] px-3 py-1.5 text-[#c4b5a0] hover:border-[#ff9a3c]/50">
Wiki
</Link>
<Link
href="/darknet-atlas"
className="rounded border border-[#1e3a5f] bg-[#0a1520] px-3 py-1.5 text-[#7eb8ff] hover:border-[#38bdf8]/50"
>
Atlas
</Link>
<Link
href="/search"
className="rounded border border-[#0d2818] bg-[#050a07] px-3 py-1.5 text-[#6bdc8e] hover:border-[#00ff41]/35"
>
Crawler
</Link>
<Link href="/chatter" className="rounded border border-[#5c4030] px-3 py-1.5 text-[#c4b5a0] hover:border-[#ff9a3c]/50">
Hub chatter
</Link>
</div>
</div>
<div className="mx-auto flex max-w-6xl flex-wrap items-center gap-2 border-t border-[#2a2218] px-4 py-2 text-[10px] font-mono text-[#8a7d6b]">
<span className="uppercase tracking-widest text-[#c45c26]/90">Depth</span>
<Link
href="/forum/handbook"
className={`rounded px-2 py-0.5 ${pathname?.startsWith("/forum/handbook") ? "bg-[#ff9a3c]/20 text-[#ffcb8a]" : "hover:text-[#ffcb8a]"}`}
>
handbook
</Link>
<Link
href="/forum/moderation"
className={`rounded px-2 py-0.5 ${pathname?.startsWith("/forum/moderation") ? "bg-[#ff9a3c]/20 text-[#ffcb8a]" : "hover:text-[#ffcb8a]"}`}
>
moderation
</Link>
<Link
href="/forum/reputation"
className={`rounded px-2 py-0.5 ${pathname?.startsWith("/forum/reputation") ? "bg-[#ff9a3c]/20 text-[#ffcb8a]" : "hover:text-[#ffcb8a]"}`}
>
reputation
</Link>
<span className="mx-1 hidden text-[#3d3228] sm:inline">|</span>
<span className="uppercase tracking-widest text-[#ff9a3c]/80">Rings</span>
<Link
href="/forum"
className={`rounded px-2 py-0.5 ${!activeSlug && pathname === "/forum" ? "bg-[#ff9a3c]/20 text-[#ffcb8a]" : "hover:text-[#ffcb8a]"}`}
>
front page
</Link>
{FORUM_RINGS.map((r) => (
<Link
key={r.slug}
href={`/forum/ring/${r.slug}`}
className={`rounded px-2 py-0.5 ${activeSlug === r.slug ? "bg-[#c45c26]/35 text-[#ffcb8a]" : "hover:text-[#ffcb8a]"}`}
>
r/{r.shortName}
</Link>
))}
</div>
<div className="mx-auto max-w-6xl border-t border-[#2a2218] px-4 text-[#6b5c4d]">
<LexiconStrip label="Board environment · signal lexicon" tags={FORUM_ATMOSPHERE_TAGS} className="border-[#3d3228]" />
</div>
</header>
<div className="relative z-10 mx-auto max-w-6xl px-4 py-8">
{activeRing ? (
<div className="mb-8 rounded-lg border border-[#3d3228] bg-[#120e0a]/90 p-5">
<p className="font-mono text-[10px] uppercase tracking-[0.3em] text-[#ff9a3c]/70">
r/{activeRing.shortName}
</p>
<h2 className="mt-1 font-mono text-lg font-bold text-[#ffcb8a]">{activeRing.title}</h2>
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-[#b8a995]">{activeRing.about}</p>
<div className="mt-4 flex flex-wrap gap-4 font-mono text-[11px] text-[#8a7d6b]">
<span>{activeRing.members.toLocaleString()} subscribers</span>
<span>{activeRing.activeNow.toLocaleString()} reading now</span>
</div>
</div>
) : null}
{children}
</div>
</div>
);
}

View File

@@ -0,0 +1,64 @@
"use client";
import { useCallback, useState } from "react";
import type { ForumThread } from "@/lib/forumState";
import {
displayScore,
getVoteForThread,
setVoteForThread,
type VoteChoice,
} from "@/lib/forumState";
export function ThreadVoteColumn({
thread,
dense,
onVoteChange,
}: {
thread: ForumThread;
dense?: boolean;
onVoteChange?: () => void;
}) {
const [tick, setTick] = useState(0);
const choice = getVoteForThread(thread.id);
const score = displayScore(thread);
const apply = useCallback(
(dir: 1 | -1) => {
const cur = getVoteForThread(thread.id);
let next: VoteChoice = dir;
if (cur === dir) next = 0;
setVoteForThread(thread.id, next);
setTick((t) => t + 1);
onVoteChange?.();
},
[thread.id, onVoteChange],
);
void tick;
const btn = dense ? "px-1 py-0.5 text-[10px]" : "px-1.5 py-1 text-xs";
return (
<div
className={`flex flex-col items-center rounded border border-[#3d3228] bg-[#0d0a07] font-mono ${dense ? "min-w-[44px] py-1" : "min-w-[52px] py-2"}`}
>
<button
type="button"
aria-label="Upvote"
onClick={() => apply(1)}
className={`${btn} leading-none text-[#8a7d6b] hover:text-[#ff9a3c] ${choice === 1 ? "text-[#ff9a3c]" : ""}`}
>
</button>
<span className="text-[11px] font-bold tabular-nums text-[#ffcb8a]">{score}</span>
<button
type="button"
aria-label="Downvote"
onClick={() => apply(-1)}
className={`${btn} leading-none text-[#8a7d6b] hover:text-[#6b9fff] ${choice === -1 ? "text-[#6b9fff]" : ""}`}
>
</button>
</div>
);
}

View File

@@ -0,0 +1,112 @@
"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
export type ThemeName =
| "dark"
| "retro"
| "minimal"
| "hacker"
| "mystic"
| "brutalist"
| "terminal"
| "zine"
| "editorial"
| "institutional"
| "nature";
interface ThemedLayoutProps {
children: React.ReactNode;
theme: ThemeName;
/** Legacy prop; use siteTitle if you prefer */
title?: string;
siteTitle?: string;
siteSubtitle?: string;
}
export default function ThemedLayout({
children,
theme,
title,
siteTitle,
siteSubtitle,
}: ThemedLayoutProps) {
const heading = siteTitle ?? title ?? "CyberLux";
const [nodeTag, setNodeTag] = useState<number | null>(null);
useEffect(() => {
setNodeTag(Math.floor(Math.random() * 99999));
}, []);
const themes: Record<ThemeName, string> = {
dark: "bg-black text-white font-sans",
retro: "bg-[#222] text-[#0f0] font-mono border-4 border-double border-[#0f0]",
minimal: "bg-[#0d0d0d] text-[#e6e6e6] font-serif border border-white/10",
hacker: "bg-[#050505] text-[#00ff41] font-mono",
mystic: "bg-[#1a0f1a] text-[#d4af37] font-serif italic",
brutalist: "bg-[#111] text-[#f5f5f5] font-black uppercase border-[10px] border-[#f5f5f5]",
terminal: "bg-[#000080] text-white font-mono",
zine: "bg-[#1a1a1a] text-[#ff3366] font-sans border-y-8 border-[#ffff00]",
editorial:
"bg-[#121018] text-[#e8e4dc] font-serif border-x-[12px] border-double border-[#6b5c3d]/60",
institutional:
"bg-[#0c1e3d] text-[#e8eef7] font-sans border-t-4 border-[#c5a022]",
nature: "bg-[#0d1f14] text-[#c8e6c9] font-serif border-b-8 border-[#2e7d32]",
};
return (
<div className={`min-h-screen p-8 ${themes[theme]}`}>
<nav className="mb-12 flex flex-col gap-1 border-b pb-4 opacity-50 transition-opacity hover:opacity-100 md:flex-row md:items-center md:justify-between">
<div>
<div className="text-xl font-bold tracking-widest uppercase">{heading}</div>
{siteSubtitle ? (
<div className="mt-1 text-[10px] font-normal uppercase tracking-wider opacity-80">
{siteSubtitle}
</div>
) : null}
</div>
<div className="flex flex-wrap gap-4 text-xs uppercase md:gap-6">
<Link href="/" className="hover:underline">
Hub
</Link>
<Link href="/market" className="hover:underline">
Market
</Link>
<Link href="/forum" className="hover:underline">
Forum
</Link>
<Link href="/exchange" className="hover:underline">
Exchange
</Link>
<Link href="/barter" className="hover:underline">
Barter
</Link>
<Link href="/search" className="hover:underline">
Crawler
</Link>
<Link href="/hidden-wiki" className="hover:underline">
Wiki
</Link>
<Link href="/darknet-atlas" className="hover:underline">
Atlas
</Link>
<Link href="/syndicate" className="hover:underline">
Syndicate
</Link>
<Link href="/vault" className="hover:underline">
Vault
</Link>
<Link href="/webring" className="hover:underline">
Webring
</Link>
</div>
</nav>
<main className="mx-auto max-w-4xl">{children}</main>
<footer className="mt-20 border-t border-current pt-8 text-center text-[10px] uppercase tracking-[0.3em] opacity-30">
<div className="mb-4 font-bold text-red-500"></div>
Node {nodeTag ?? "—"} // Shadow Network // {new Date().getFullYear()}
</footer>
</div>
);
}

View File

@@ -0,0 +1,190 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import LexiconStrip from "@/components/site/LexiconStrip";
import { MARKET_ATMOSPHERE_TAGS } from "@/lib/cyberluxZoneLexicon";
import { SHOP_PRODUCT_COUNT } from "@/lib/shopCatalog";
import { SHOP_SELLER_COUNT } from "@/lib/shopSellers";
const LAYERS: {
title: string;
items: { href: string; label: string; blurb: string }[];
}[] = [
{
title: "Layer 1 · Catalog",
items: [
{ href: "/market", label: "Full floor", blurb: "Search, facets, every SKU" },
{ href: "/vendors", label: "Vendor hall", blurb: "Stalls & bios" },
{ href: "/vendor/apply", label: "Onboard", blurb: "Bond & review queue" },
],
},
{
title: "Layer 2 · Commerce spine",
items: [
{ href: "/checkout", label: "Checkout", blurb: "Cart → settle" },
{ href: "/market/analytics", label: "Floor analytics", blurb: "Velocity, mix, heat" },
{ href: "/market/operations", label: "Operations", blurb: "Pipeline & SLAs" },
],
},
{
title: "Layer 3 · Trust & intel",
items: [
{ href: "/market/trust", label: "Escrow & keys", blurb: "Settlement tiers" },
{ href: "/market/intel", label: "Buyer intel", blurb: "Vetting & OPSEC" },
{ href: "/support", label: "Support ladder", blurb: "Tickets & mirrors" },
],
},
];
export default function MarketSiteChrome({ children }: { children: React.ReactNode }) {
const pathname = usePathname() ?? "";
const crumbs = [
{ href: "/", label: "Hub" },
{ href: "/market", label: "Market" },
];
const tail = pathname.replace(/^\/market\/?/, "");
if (tail && tail !== "") {
const seg = tail.split("/")[0];
const label =
seg === "analytics"
? "Analytics"
: seg === "operations"
? "Operations"
: seg === "trust"
? "Trust"
: seg === "intel"
? "Intel"
: seg;
crumbs.push({ href: pathname, label });
}
return (
<div className="min-h-screen bg-[#050806] text-[#c8ffd8]">
<div
className="pointer-events-none fixed inset-0 z-0 opacity-[0.07]"
style={{
backgroundImage:
"repeating-linear-gradient(-12deg, transparent, transparent 40px, rgba(0,255,65,0.04) 41px)",
}}
/>
<header className="sticky top-0 z-30 border-b border-[#00ff41]/20 bg-[#030806]/95 backdrop-blur-md">
<div className="mx-auto flex max-w-[1600px] flex-col gap-3 px-4 py-4 lg:flex-row lg:items-center lg:justify-between">
<div>
<p className="font-mono text-[9px] uppercase tracking-[0.5em] text-[#00ff41]/45">sovereign catalog · multi-layer</p>
<Link href="/market" className="mt-1 block">
<h1 className="font-mono text-2xl font-black tracking-tight text-[#7af598] md:text-3xl">
The Candy Shop
</h1>
</Link>
<p className="mt-1 max-w-xl font-mono text-[11px] text-[#00ff41]/55">
<strong className="text-[#9dffc4]">{SHOP_PRODUCT_COUNT}</strong> SKUs ·{" "}
<strong className="text-[#9dffc4]">{SHOP_SELLER_COUNT}</strong> stalls not a plugin mall; one cart, one checkout, four trust layers.
</p>
<nav className="mt-3 flex flex-wrap items-center gap-1 font-mono text-[10px] uppercase tracking-wider text-[#5a8f6a]">
{crumbs.map((c, i) => (
<span key={c.href} className="flex items-center gap-1">
{i > 0 ? <span className="text-[#00ff41]/25">/</span> : null}
<Link href={c.href} className="hover:text-[#7af598]">
{c.label}
</Link>
</span>
))}
</nav>
</div>
<div className="flex flex-wrap gap-2 font-mono text-[10px] uppercase">
<Link href="/" className="border border-[#00ff41]/30 px-3 py-2 text-[#8fccb0] hover:border-[#00ff41]/60">
Hub
</Link>
<Link href="/forum" className="border border-[#c45c26]/35 px-3 py-2 text-[#e8b896] hover:border-[#ff9a3c]/50">
Forum
</Link>
<Link href="/exchange" className="border border-[#8d6e4a]/40 px-3 py-2 text-[#d4c4b0] hover:border-[#c4a574]/60">
Exchange
</Link>
<Link href="/barter" className="border border-[#1a3d24] bg-[#0a120d] px-3 py-2 text-[#7cfc9a] hover:border-[#39ff14]/40">
Barter
</Link>
<Link href="/hidden-wiki" className="border border-[#3d5a80]/30 px-3 py-2 text-[#9fb5d2] hover:border-[#7dd3fc]/50">
Wiki
</Link>
<Link href="/darknet-atlas" className="border border-[#1e3a5f] px-3 py-2 text-[#7eb8ff] hover:border-[#38bdf8]/50">
Atlas
</Link>
<Link href="/syndicate" className="border border-fuchsia-900/40 px-3 py-2 text-fuchsia-200 hover:border-fuchsia-500/50">
Syndicate
</Link>
<Link href="/search" className="border border-[#00ff41]/25 px-3 py-2 text-[#6bdc8e] hover:border-[#00ff41]/45">
Crawler
</Link>
<Link href="/account/hidden-services" className="border border-[#00ff41]/20 px-3 py-2 text-[#82a98c] hover:border-[#00ff41]/45">
Mirrors
</Link>
</div>
</div>
<div className="border-t border-[#00ff41]/10 lg:hidden">
<div className="mx-auto flex max-w-[1600px] gap-2 overflow-x-auto px-4 py-2 scrollbar-none">
{LAYERS.flatMap((L) =>
L.items.map((it) => (
<Link
key={it.href}
href={it.href}
className={`shrink-0 rounded border px-3 py-1.5 font-mono text-[10px] uppercase ${
pathname === it.href
? "border-[#00ff41] bg-[#00ff41]/15 text-[#b4ffcc]"
: "border-[#00ff41]/20 text-[#6a9b7a] hover:border-[#00ff41]/40"
}`}
>
{it.label}
</Link>
)),
)}
</div>
</div>
<div className="mx-auto max-w-[1600px] border-t border-[#00ff41]/10 px-4 text-[#5a8f6a]">
<LexiconStrip label="Floor environment · keyword fog" tags={MARKET_ATMOSPHERE_TAGS} className="border-[#00ff41]/15" />
</div>
</header>
<div className="relative z-10 mx-auto flex max-w-[1600px] gap-8 px-4 pb-24 pt-6">
<aside className="hidden w-72 shrink-0 lg:block">
<div className="sticky top-28 space-y-8">
{LAYERS.map((layer) => (
<div key={layer.title}>
<h2 className="border-b border-[#00ff41]/15 pb-2 font-mono text-[9px] font-bold uppercase tracking-[0.28em] text-[#00ff41]/50">
{layer.title}
</h2>
<ul className="mt-3 space-y-1">
{layer.items.map((it) => {
const active = pathname === it.href;
return (
<li key={it.href}>
<Link
href={it.href}
className={`block rounded border px-3 py-2 transition ${
active
? "border-[#00ff41]/55 bg-[#00ff41]/10"
: "border-transparent hover:border-[#00ff41]/25 hover:bg-[#00ff41]/05"
}`}
>
<span className="font-mono text-xs font-bold text-[#b4ffcc]">{it.label}</span>
<p className="mt-0.5 font-mono text-[10px] leading-snug text-[#5a8f6a]">{it.blurb}</p>
</Link>
</li>
);
})}
</ul>
</div>
))}
<div className="rounded border border-dashed border-[#00ff41]/20 p-3 font-mono text-[9px] leading-relaxed text-[#4a6b55]">
Tor entry onions map headers to the same app your cart and session follow signed mirror bundles, not separate shops.
</div>
</div>
</aside>
<div className="min-w-0 flex-1">{children}</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,75 @@
"use client";
import type { ProductSocialMetrics } from "@/lib/shopProductSocial";
function Stars({ value, size = "sm" }: { value: number; size?: "sm" | "xs" }) {
const rounded = Math.round(value * 2) / 2;
const full = Math.floor(rounded);
const half = rounded - full >= 0.5;
const empty = 5 - full - (half ? 1 : 0);
const c = size === "xs" ? "text-[10px]" : "text-xs";
return (
<span className={`${c} tracking-tight text-amber-400`} aria-hidden>
{"★".repeat(full)}
{half ? <span className="text-amber-400/60"></span> : null}
<span className="text-foreground/22">{"☆".repeat(Math.max(0, empty))}</span>
</span>
);
}
export function ProductSocialBlock({
metrics,
compact = false,
}: {
metrics: ProductSocialMetrics;
compact?: boolean;
}) {
const { avgRating, reviewCount, purchaseCount, boughtLast24h, testimonials } = metrics;
if (compact) {
return (
<div className="space-y-1">
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5">
<Stars value={avgRating} size="xs" />
<span className="font-mono text-[11px] font-bold tabular-nums text-foreground/90">{avgRating.toFixed(2)}</span>
<span className="font-mono text-[10px] text-foreground/45">({reviewCount.toLocaleString()} reviews)</span>
</div>
<p className="font-mono text-[10px] text-emerald-400/80">
{purchaseCount.toLocaleString()} sold · <span className="text-emerald-300/90">{boughtLast24h}</span> in 24h
</p>
</div>
);
}
return (
<div className="space-y-3">
<div className="flex flex-wrap items-center gap-2">
<Stars value={avgRating} />
<span className="font-orbitron text-sm font-bold tabular-nums">{avgRating.toFixed(2)}</span>
<span className="text-xs text-foreground/50">({reviewCount.toLocaleString()} reviews)</span>
</div>
<p className="font-mono text-[11px] text-emerald-400/85">
<span className="text-foreground/55">Purchases:</span>{" "}
<strong className="text-emerald-300">{purchaseCount.toLocaleString()}</strong> total ·{" "}
<strong className="text-emerald-300">{boughtLast24h}</strong> bought in last 24h
</p>
<div className="space-y-2 border-t border-white/10 pt-3">
<p className="font-mono text-[9px] font-bold uppercase tracking-widest text-foreground/40">Recent buyers</p>
{testimonials.slice(0, 2).map((t, i) => (
<blockquote
key={`${t.handle}-${i}`}
className="rounded-lg border border-white/5 bg-white/[0.03] px-3 py-2 text-xs leading-snug text-foreground/75"
>
<div className="mb-1 flex flex-wrap items-center justify-between gap-2 font-mono text-[10px] text-foreground/45">
<span className="text-neon-cyan/80">@{t.handle}</span>
<span>
<span className="text-amber-400">{t.rating}</span> · {t.daysAgo}d ago
</span>
</div>
{t.text}
</blockquote>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,69 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { useCart } from "@/contexts/CartContext";
import { ProductSocialBlock } from "@/components/shop/ProductSocialBlock";
import { resolveProductImage, type ShopCurrency, type ShopProduct } from "@/lib/shopCatalog";
import { getProductSocialMetrics } from "@/lib/shopProductSocial";
const SYM: Record<ShopCurrency, string> = { BTC: "₿", ETH: "Ξ", USD: "$", XMR: "⏣" };
function formatPrice(price: number, currency: ShopCurrency): string {
if (currency === "BTC") return price.toFixed(6);
if (currency === "ETH") return price.toFixed(4);
if (currency === "XMR") return price.toFixed(3);
return price.toFixed(2);
}
export function VendorShelfRow({ product }: { product: ShopProduct }) {
const { addToCart, hydrated } = useCart();
const [flash, setFlash] = useState(false);
const src = resolveProductImage(product);
const metrics = getProductSocialMetrics(product.id);
return (
<li className="flex flex-col gap-4 rounded-lg border border-orange-950/70 bg-[#080605] p-4 sm:flex-row">
<div className="relative h-28 w-full shrink-0 overflow-hidden rounded border border-orange-900/30 bg-black sm:h-28 sm:w-28">
<Image src={src} alt="" fill className="object-cover" sizes="112px" unoptimized={src.startsWith("/")} />
</div>
<div className="min-w-0 flex-1">
<p className="font-mono text-[10px] uppercase tracking-wider text-orange-500/60">{product.category}</p>
<p className="font-semibold text-orange-50">{product.name}</p>
<div className="mt-2 text-[#c4b5a0]">
<ProductSocialBlock metrics={metrics} compact />
</div>
<p className="mt-2 line-clamp-2 text-xs text-orange-200/65">{product.description}</p>
<p className="mt-2 font-mono text-sm text-orange-300">
{SYM[product.currency]}
{formatPrice(product.price, product.currency)} {product.currency}
</p>
<div className="mt-3 space-y-2 border-t border-orange-950/50 pt-3">
<p className="font-mono text-[9px] font-bold uppercase tracking-widest text-orange-500/50">Verified note</p>
<p className="text-xs italic text-orange-200/60">{metrics.testimonials[0]?.text}</p>
</div>
<div className="mt-4 flex flex-wrap gap-2">
<button
type="button"
disabled={!hydrated}
onClick={() => {
addToCart(product, 1);
setFlash(true);
window.setTimeout(() => setFlash(false), 1500);
}}
className="rounded border border-orange-500 bg-orange-500/90 px-4 py-2 font-mono text-xs font-bold uppercase text-black hover:bg-orange-400 disabled:opacity-50"
>
{flash ? "Added ✓" : "Add to cart"}
</button>
<Link
href="/checkout"
className="rounded border border-orange-800 px-4 py-2 font-mono text-xs text-orange-200 hover:border-orange-600"
>
Cart
</Link>
</div>
</div>
</li>
);
}

View File

@@ -0,0 +1,26 @@
/** Diegetic keyword fog for deep vertical chrome — improves atmosphere and internal crawler token density. */
export default function LexiconStrip({
label,
tags,
className = "",
}: {
label: string;
tags: readonly string[];
className?: string;
}) {
return (
<div className={`border-t border-white/10 px-1 py-2 ${className}`}>
<p className="mb-1.5 font-mono text-[8px] uppercase tracking-[0.35em] opacity-50">{label}</p>
<div className="flex flex-wrap gap-1">
{tags.map((t) => (
<span
key={t}
className="rounded border border-white/10 px-1.5 py-0.5 font-mono text-[8px] leading-none opacity-60"
>
{t}
</span>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,21 @@
import Link from "next/link";
/** Call-to-action back to the main storefront (same deployment). */
export function HubStrip({ slug }: { slug: string }) {
return (
<div className="mb-8 border-2 border-current bg-black/30 px-4 py-3 text-xs md:text-sm">
<span className="opacity-80">RELAY /{slug}</span>
<span className="mx-2 opacity-40">|</span>
<Link href="/" className="font-bold underline decoration-2 underline-offset-4 hover:opacity-80">
Primary CyberLux storefront
</Link>
<span className="mx-2 opacity-40">·</span>
<Link href="/checkout" className="underline opacity-90 hover:opacity-100">
Checkout
</Link>
<p className="mt-2 text-[10px] opacity-60">
Verify this .onion against the signed mirror list on the hub before signing anything. Unsigned relay = untrusted relay.
</p>
</div>
);
}

Some files were not shown because too many files have changed in this diff Show More