mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-17 05:32:05 +02:00
Add Wownerogue product page + blog post; remove r/CryptoCurrency and MoonPlace
- New /products/wownerogue page (SEO metadata + VideoGame JSON-LD) and wownerogue-launch blog post covering the fairness, block-timing, and payout design. - Drop the retired r/CryptoCurrency and MoonPlace references from the portfolio grid, products index, and custom-websites page; feature Wownerogue + Neroswap instead. - Add WownerogueLinks component (live instances + GitHub source) and wire it into MDX. - Remove now-orphaned r-cryptocurrency and moonplace images; add product page to sitemap.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "Wownerogue: A Provably-Fair Roguelike on Block Time"
|
||||
date: "2026-06-01"
|
||||
excerpt: "A browser roguelike where the clock is the blockchain. How the commit-reveal fairness, random block-timing deadline, and Monero payout batching actually work."
|
||||
draft: false
|
||||
---
|
||||
|
||||
**Wownerogue** is a multiplayer roguelike that runs in the browser. You drop into a dungeon, and you have until the next block to reach the exit. A monster is hunting you. There is a treasure bag worth grabbing on the way out. That is the whole game, and the catch is in the clock: the deadline is a new block on the Monero or Wownero chain, so it is genuinely random.
|
||||
|
||||
It runs in two places. [play.wowne.ro](https://play.wowne.ro) is the Wownero instance: free to play, with paid entry feeding a separate leaderboard, and no crypto payouts. [monerogue.app](https://monerogue.app) is the Monero stagenet instance, where escaping pays out stagenet XMR from a house wallet. Stagenet coins are worthless, which makes it a safe place to run the full payment-and-payout loop in the open.
|
||||
|
||||
<WownerogueLinks />
|
||||
|
||||
## The clock is the point
|
||||
|
||||
Most timed games give you a countdown. Wownerogue gives you a block. Monero and Wownero target roughly two-minute block times, but proof-of-work timing is a Poisson process: the next block might land in fifteen seconds or in five minutes, and nothing on the server can predict which. Your run starts when a block arrives, and a later block ends it.
|
||||
|
||||
I had one early instinct to "fix" this by adding a minimum fairness floor so nobody got a fifteen-second round. That was wrong, and I tore it out. The random deadline is not a bug to smooth over: it is the entire reason the game is interesting. The only timing guard left is a two-second grace at the very start, so a block landing in the same instant your run begins cannot kill you before you have moved.
|
||||
|
||||
## Provably fair, the boring honest way
|
||||
|
||||
A game that holds money has to prove it is not cooking the board after you pay. Wownerogue uses a standard commit-reveal:
|
||||
|
||||
1. Before your run, the server generates a 256-bit seed and shows you `SHA-256(seed)`: the commitment. You can copy it.
|
||||
2. The seed drives a deterministic RNG that lays out the whole dungeon: walls, the exit, the treasure, and the monster's behaviour.
|
||||
3. When the run ends, the server reveals the seed.
|
||||
4. You hash the revealed seed and confirm it equals the commitment you were shown up front.
|
||||
|
||||
Because the commitment is published before you move, the server cannot reroll a dungeon it does not like once the round is live. Feed the seed into the verification page and it regenerates the exact board you played, then prints a layout fingerprint so you can compare. No trust required, just a hash.
|
||||
|
||||
## Two instances, one codebase
|
||||
|
||||
The split between the two sites is config, not forks. The same server runs both; environment variables decide the currency, the network, whether payouts are on, and the branding. Mainnet Wownero runs free-to-play with payouts switched off. Stagenet Monero runs with payouts on. That meant being careful that a single flag actually gated the money path end to end, and not just part of it: an early version of the payout toggle disabled credits payouts but not direct ones, which is exactly the kind of gap you do not want in code that moves funds.
|
||||
|
||||
## Paying out without tripping over Monero
|
||||
|
||||
The interesting backend problem is payouts. When a Monero wallet spends, the change output (and the spent output) get locked for about ten blocks. Fire payouts naively and you will try to spend funds that are still locked, and the transfer fails.
|
||||
|
||||
So payouts batch, and the batcher checks unlocked balance before it sends. If the wallet does not have enough unlocked balance to cover the batch right now, it does not fail the payouts: it defers them back to pending and retries on the next block tick and the next interval. Insufficient-funds errors are treated as safe to retry, because they happen before anything is broadcast. Anything ambiguous (an error that might mean the transaction already went out) gets parked for manual review instead of being auto-retried, so a flaky RPC response can never cause a double payout.
|
||||
|
||||
## Keeping the free board honest
|
||||
|
||||
Free play is the obvious abuse target: spin up sockets, farm the leaderboard. Scores are computed server-side from real game state, so you cannot just post a number. The rate limiter already capped new connections and game starts per IP, but nothing stopped one IP from holding a pile of sockets open, so I added a per-IP concurrent-socket cap. Not bulletproof against a determined botnet, but it raises the cost of the cheap attacks well past worthwhile.
|
||||
|
||||
## Stack
|
||||
|
||||
Node.js and Socket.io for real-time game state, chat, and live spectating. PostgreSQL for everything durable, with money handled in integer atomic units and idempotent, transactional writes. Monero and Wownero wallet-RPC for payments and payouts. The client is plain JavaScript with a tile renderer; no framework, because the game is a grid and a render loop.
|
||||
|
||||
The whole thing is MIT-licensed and public: [github.com/Such-Software/wownerogue](https://github.com/Such-Software/wownerogue). If you want to see exactly how the fairness check, the block-timing loop, or the payout batching work, the answer is in there rather than in this post.
|
||||
|
||||
## Play it
|
||||
|
||||
<WownerogueLinks />
|
||||
|
||||
Queue up, escape before the block lands, and check your seed afterward. If you escape with the bag on the Monero instance, the stagenet XMR is real (and worthless, which is the point).
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 258 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 509 KiB |
@@ -6,6 +6,7 @@ import {
|
||||
SuchoiceStoreButtons,
|
||||
BauhausEchoStoreButtons,
|
||||
SuchMoonLaunchStoreButtons,
|
||||
WownerogueLinks,
|
||||
} from "@/components/store-buttons";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
@@ -71,6 +72,7 @@ export default async function BlogPost({ params }: Props) {
|
||||
SuchoiceStoreButtons,
|
||||
BauhausEchoStoreButtons,
|
||||
SuchMoonLaunchStoreButtons,
|
||||
WownerogueLinks,
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,12 @@ import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Custom Websites | Such Software",
|
||||
description: "Real-time platforms and interactive web experiences. See our work on rcryptocurrency.com and moonplace.io.",
|
||||
title: "Custom Web Apps & Real-Time Platforms | Such Software",
|
||||
description: "We build fast, accessible real-time web apps: multiplayer games, live dashboards, and crypto platforms with WebSocket sync. Next.js, Socket.io, WebRTC.",
|
||||
alternates: { canonical: "/products/custom-websites" },
|
||||
openGraph: {
|
||||
title: "Custom Websites | Such Software",
|
||||
description: "Real-time platforms and interactive web experiences. See our work on rcryptocurrency.com and moonplace.io.",
|
||||
title: "Custom Web Apps & Real-Time Platforms | Such Software",
|
||||
description: "Fast, accessible real-time web apps: multiplayer games, live dashboards, and crypto platforms with WebSocket sync. Next.js, Socket.io, WebRTC.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
@@ -38,31 +38,32 @@ export default function CustomWebsitesPage() {
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Featured Projects</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
<a href="https://rcryptocurrency.com" target="_blank" rel="noopener noreferrer">
|
||||
<Link href="/products/wownerogue">
|
||||
<Card className="glass-card border-purple-500/20 hover:border-purple-500/50 transition-colors cursor-pointer h-full">
|
||||
<h3 className="text-xl font-bold mb-3">rcryptocurrency.com</h3>
|
||||
<h3 className="text-xl font-bold mb-3">Wownerogue</h3>
|
||||
<p className="text-muted-foreground mb-4">
|
||||
Governance dashboard for the r/CryptoCurrency subreddit with 10M+ members.
|
||||
Real-time DAO metrics, proposal tracking, and community analytics.
|
||||
A provably-fair multiplayer roguelike that runs entirely in the browser, with
|
||||
game rounds synced to Monero and Wownero block times. Live spectating, a shared
|
||||
chat lobby, and on-chain payouts.
|
||||
</p>
|
||||
<ul className="text-sm text-muted-foreground space-y-1">
|
||||
<li>Real-time data via WebSocket</li>
|
||||
<li>Reddit API integration</li>
|
||||
<li>On-chain governance tracking</li>
|
||||
<li>Socket.io real-time game state and spectating</li>
|
||||
<li>SHA-256 commit-reveal for provable fairness</li>
|
||||
<li>Wallet-RPC payment and payout pipeline</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</a>
|
||||
<a href="https://moonplace.io" target="_blank" rel="noopener noreferrer">
|
||||
</Link>
|
||||
<a href="https://neroswap.com" target="_blank" rel="noopener noreferrer">
|
||||
<Card className="glass-card border-purple-500/20 hover:border-purple-500/50 transition-colors cursor-pointer h-full">
|
||||
<h3 className="text-xl font-bold mb-3">moonplace.io</h3>
|
||||
<h3 className="text-xl font-bold mb-3">Neroswap</h3>
|
||||
<p className="text-muted-foreground mb-4">
|
||||
Collaborative pixel art canvas with real-time multiplayer.
|
||||
Thousands of concurrent users painting together.
|
||||
A live orderbook aggregator that pulls prices from six DEX and no-KYC CEX venues
|
||||
and compares Monero, Wownero, and Bitcoin markets in real time.
|
||||
</p>
|
||||
<ul className="text-sm text-muted-foreground space-y-1">
|
||||
<li>WebSocket real-time sync</li>
|
||||
<li>Canvas rendering optimization</li>
|
||||
<li>User authentication and cooldowns</li>
|
||||
<li>Real-time price feeds via WebSocket</li>
|
||||
<li>Multi-exchange orderbook normalization</li>
|
||||
<li>No accounts, no custody, no tracking</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</a>
|
||||
|
||||
@@ -7,8 +7,8 @@ import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Products | Such Software",
|
||||
description: "Tools and applications built by Such Software. Neroswap, Webshops, Custom Websites, Smirk Wallet, Bauhaus Echo, Suchoice, Vegan IQ, and Such Moon Launch.",
|
||||
title: "Our Products & Apps: Crypto Tools, Games & Commerce | Such Software",
|
||||
description: "Explore Such Software's products: Wownerogue Monero roguelike, Neroswap crypto aggregator, Smirk tipping wallet, headless Webshops, and our mobile games.",
|
||||
alternates: { canonical: "/products" },
|
||||
openGraph: {
|
||||
title: "Products | Such Software",
|
||||
@@ -18,6 +18,13 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
const products = [
|
||||
{
|
||||
slug: "wownerogue",
|
||||
title: "Wownerogue",
|
||||
description: "A provably-fair multiplayer roguelike synced to Monero and Wownero block times. Play free in the browser, escape the dungeon, earn on-chain payouts.",
|
||||
color: "orange",
|
||||
icon: "/images/products/privacy-labs.png",
|
||||
},
|
||||
{
|
||||
slug: "neroswap",
|
||||
title: "Neroswap",
|
||||
@@ -35,9 +42,9 @@ const products = [
|
||||
{
|
||||
slug: "custom-websites",
|
||||
title: "Custom Websites",
|
||||
description: "Real-time platforms and interactive experiences. See rcryptocurrency.com and moonplace.io.",
|
||||
description: "Real-time web apps, multiplayer games, and live dashboards. Built on Next.js, Socket.io, and WebRTC.",
|
||||
color: "purple",
|
||||
icon: "/images/products/r-cryptocurrency.png",
|
||||
icon: "/images/products/neroswap.png",
|
||||
},
|
||||
{
|
||||
slug: "smirk-wallet",
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { WownerogueLinks } from "@/components/store-buttons";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Wownerogue: Provably-Fair Monero Multiplayer Roguelike | Such Software",
|
||||
description: "Play Wownerogue: a provably-fair, blockchain-synced multiplayer roguelike. Every run is verifiable, every round ends on a new block. Free in-browser, no download.",
|
||||
alternates: { canonical: "/products/wownerogue" },
|
||||
keywords: [
|
||||
"provably fair game",
|
||||
"monero game",
|
||||
"wownero game",
|
||||
"crypto roguelike",
|
||||
"blockchain multiplayer game",
|
||||
"browser roguelike",
|
||||
"monerogue",
|
||||
"wownerogue",
|
||||
],
|
||||
openGraph: {
|
||||
title: "Wownerogue: Provably-Fair Monero Multiplayer Roguelike",
|
||||
description: "A provably-fair, blockchain-synced multiplayer roguelike. Escape the dungeon before the next block. Free in-browser at play.wowne.ro.",
|
||||
type: "website",
|
||||
url: "/products/wownerogue",
|
||||
},
|
||||
};
|
||||
|
||||
// VideoGame structured data: the highest-value rich result for a game query.
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "VideoGame",
|
||||
name: "Wownerogue",
|
||||
alternateName: "Monerogue",
|
||||
url: "https://such.software/products/wownerogue",
|
||||
description:
|
||||
"A provably-fair, blockchain-synced multiplayer roguelike. Escape the dungeon before the next block, avoid the monster, grab the treasure, and earn on-chain payouts.",
|
||||
applicationCategory: "Game",
|
||||
genre: ["Roguelike", "Multiplayer"],
|
||||
gamePlatform: "Web browser",
|
||||
operatingSystem: "Any (web browser)",
|
||||
playMode: "MultiPlayer",
|
||||
author: { "@type": "Organization", name: "Such Software" },
|
||||
publisher: { "@type": "Organization", name: "Such Software" },
|
||||
offers: {
|
||||
"@type": "Offer",
|
||||
price: "0",
|
||||
priceCurrency: "USD",
|
||||
url: "https://play.wowne.ro",
|
||||
},
|
||||
};
|
||||
|
||||
const features = [
|
||||
{ title: "Provably Fair", body: "Each round commits to a SHA-256 hash before you play and reveals the seed after. That seed deterministically generates the entire dungeon, so you can verify nothing was changed once the round began." },
|
||||
{ title: "Synced to Block Time", body: "Rounds start and end on new Monero or Wownero blocks. Because block timing is random, your escape deadline is genuinely unpredictable: that is the whole game." },
|
||||
{ title: "Live Spectating", body: "Watch any in-progress run in real time from the lobby, with a global feed announcing escapes as they happen." },
|
||||
{ title: "Two Leaderboards", body: "Free runs feed the Pleb board; paid runs feed the Hall of Champions. Rankings are time-windowed (day, week, all-time)." },
|
||||
{ title: "On-Chain Payouts", body: "On the payout instance, escaping pays out real funds from a house wallet over wallet-RPC, batched and verified against the chain." },
|
||||
{ title: "Open Source", body: "The full server and client are MIT-licensed and public. Read exactly how the fairness, payments, and payouts work." },
|
||||
];
|
||||
|
||||
const steps = [
|
||||
{ n: "1", title: "Queue", body: "Join the queue. Your run starts when the next block lands." },
|
||||
{ n: "2", title: "Escape", body: "Move with WASD or arrow keys. Reach the exit before the next block, or the monster, ends your run." },
|
||||
{ n: "3", title: "Grab the bag", body: "Collecting the treasure bag is optional but pays a higher multiplier on escape." },
|
||||
{ n: "4", title: "Verify", body: "After the run, the seed is revealed. Check it against the pre-game commitment on the verification page." },
|
||||
];
|
||||
|
||||
export default function WowneroguePage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<Link href="/products" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">
|
||||
← Back to Products
|
||||
</Link>
|
||||
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-2 text-orange-600 dark:text-orange-400">
|
||||
Wownerogue
|
||||
</h1>
|
||||
<p className="text-base text-muted-foreground mb-6">A provably-fair roguelike, synced to the blockchain.</p>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none mb-8">
|
||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||
Wownerogue is a multiplayer dungeon-crawler that runs entirely in your browser. Every
|
||||
round is provably fair and synced to Monero or Wownero block times: you have until the
|
||||
next block to escape the dungeon, avoid the monster, and grab the treasure. Random block
|
||||
timing means your deadline is never the same twice.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-12">
|
||||
<WownerogueLinks />
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Two Worlds</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
<Card className="glass-card border-orange-500/20">
|
||||
<h3 className="text-xl font-bold mb-2">play.wowne.ro</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
The Wownero mainnet instance: free to play, with paid entry for the Hall of Champions.
|
||||
No crypto payouts, all the tension. The friendly place to learn the game.
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-orange-500/20">
|
||||
<h3 className="text-xl font-bold mb-2">monerogue.app</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
The Monero stagenet instance, where escaping pays out stagenet XMR from a house wallet.
|
||||
Stagenet coins have no real value, so it is a safe sandbox for the full payout loop.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">How a Run Works</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4 mb-12">
|
||||
{steps.map((s) => (
|
||||
<Card key={s.n} className="glass-card border-orange-500/20">
|
||||
<h3 className="font-bold mb-1">{s.n}. {s.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">{s.body}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Features</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
{features.map((f) => (
|
||||
<Card key={f.title} className="glass-card border-orange-500/20">
|
||||
<h3 className="text-lg font-bold mb-2">{f.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">{f.body}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">How “Provably Fair” Works</h2>
|
||||
<div className="section-container border-l-4 border-orange-500 mb-12 text-muted-foreground space-y-4">
|
||||
<p>
|
||||
Before your run begins, the server publishes a SHA-256 hash, the commitment, of a secret
|
||||
random seed. You can record it. When the run ends, the server reveals the seed itself.
|
||||
Anyone can hash the revealed seed and confirm it matches the commitment they were shown
|
||||
earlier, which proves the seed was fixed before you started moving.
|
||||
</p>
|
||||
<p>
|
||||
That same seed deterministically generates the dungeon layout, the treasure and exit
|
||||
positions, and the monster’s behaviour. Feed the seed back in and you regenerate
|
||||
the exact dungeon you played. There is no way to re-roll a bad board after the fact, and
|
||||
no hidden house edge in the level itself. The verification page does this check for you.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Under the Hood</h2>
|
||||
<div className="section-container border-l-4 border-orange-500 mb-12">
|
||||
<ul className="space-y-2 text-muted-foreground">
|
||||
<li><strong>Real-time:</strong> Node.js and Socket.io drive game state, chat, and spectating.</li>
|
||||
<li><strong>Fairness:</strong> SHA-256 commit-reveal with a seeded RNG for deterministic dungeons.</li>
|
||||
<li><strong>Payments:</strong> Monero and Wownero wallet-RPC for entry payments and batched payouts.</li>
|
||||
<li><strong>Storage:</strong> PostgreSQL with atomic, idempotent money handling in integer units.</li>
|
||||
<li><strong>Open source:</strong> MIT-licensed. The whole thing is on <a href="https://github.com/Such-Software/wownerogue" target="_blank" rel="noopener noreferrer" className="text-orange-600 dark:text-orange-400 hover:underline">GitHub</a>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground mb-12">
|
||||
Want the build notes? Read{" "}
|
||||
<Link href="/blog/wownerogue-launch" className="text-orange-600 dark:text-orange-400 hover:underline">
|
||||
the launch write-up
|
||||
</Link>
|
||||
{" "}for how the fairness, block-timing, and payout logic came together.
|
||||
</p>
|
||||
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground flex-wrap">
|
||||
<a href="https://github.com/Such-Software/wownerogue" target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors">
|
||||
Source on GitHub
|
||||
</a>
|
||||
<span>•</span>
|
||||
<Link href="/support" className="hover:text-foreground transition-colors">
|
||||
Support & Feedback
|
||||
</Link>
|
||||
<span>•</span>
|
||||
<span>Contact: apps@such.software</span>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{ url: base, lastModified: new Date(), changeFrequency: "weekly", priority: 1.0 },
|
||||
{ url: `${base}/products`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.9 },
|
||||
{ url: `${base}/products/wownerogue`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 },
|
||||
{ url: `${base}/products/neroswap`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 },
|
||||
{ url: `${base}/products/webshops`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 },
|
||||
{ url: `${base}/products/custom-websites`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.8 },
|
||||
|
||||
@@ -5,67 +5,46 @@ import Link from "next/link";
|
||||
export const PortfolioGrid = () => {
|
||||
return (
|
||||
<section className="grid grid-cols-1 md:grid-cols-4 auto-rows-[300px] gap-6 w-full max-w-7xl mx-auto p-4 z-10 relative">
|
||||
{/* Row 1: r/CryptoCurrency (2x1) */}
|
||||
<Link href="https://rcryptocurrency.com" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-2" style={{ animationDelay: "0ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] !bg-slate-950/90 cursor-pointer">
|
||||
<div className="absolute inset-0 z-20 bg-gradient-to-t from-black/95 via-black/40 to-transparent p-8 flex flex-col justify-end items-center text-center pointer-events-none">
|
||||
<h3 className="text-3xl font-bold text-white shadow-black drop-shadow-xl tracking-tight">r/CryptoCurrency</h3>
|
||||
<p className="text-gray-200 shadow-black drop-shadow-md max-w-md mt-2 font-medium text-base leading-relaxed">
|
||||
Governance dashboard for 10M+ members. Real-time DAO metrics.
|
||||
</p>
|
||||
{/* Row 1: Wownerogue (2x1) */}
|
||||
<Link href="/products/wownerogue" className="bento-item md:col-span-2" style={{ animationDelay: "0ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] !bg-orange-50/95 dark:!bg-orange-950/95 !border-orange-500/20 cursor-pointer">
|
||||
<div className="absolute inset-x-0 top-0 z-20 p-6 bg-gradient-to-b from-background/95 to-transparent pointer-events-none">
|
||||
<h3 className="text-xl font-bold text-orange-700 dark:text-orange-400">Wownerogue</h3>
|
||||
<p className="text-sm mt-1 text-foreground font-semibold">Provably-fair multiplayer roguelike, synced to Monero and Wownero blocks.</p>
|
||||
</div>
|
||||
<div className="absolute inset-6 bottom-24 rounded-2xl overflow-hidden z-10 bg-slate-800/30">
|
||||
<Image
|
||||
src="/images/products/r-cryptocurrency.png"
|
||||
alt="Screenshot of r/CryptoCurrency subreddit homepage showing over 10 million subscribers, community links, and a cartoon moon mascot"
|
||||
fill
|
||||
className="object-contain p-4 opacity-80 transition-transform duration-1000 group-hover:scale-105"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
/>
|
||||
<div className="absolute inset-x-4 bottom-4 top-24 rounded-lg overflow-hidden z-10 bg-black/5 dark:bg-white/5 border border-orange-500/10 shadow-inner">
|
||||
<Image
|
||||
src="/images/products/privacy-labs.png"
|
||||
alt="Screenshot of the Wownerogue and Monerogue roguelike dungeon-crawler game"
|
||||
fill
|
||||
className="object-contain p-2 opacity-60 transition-transform duration-1000 group-hover:scale-105"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
{/* Row 1: MoonPlace (1x1) */}
|
||||
<Link href="https://moonplace.io" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-1" style={{ animationDelay: "75ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] !bg-indigo-50/95 dark:!bg-indigo-950/95 !border-indigo-500/20 cursor-pointer">
|
||||
<div className="absolute inset-x-0 top-0 z-20 p-6 bg-gradient-to-b from-background/95 to-transparent pointer-events-none">
|
||||
<h3 className="text-xl font-bold text-indigo-700 dark:text-indigo-400">MoonPlace</h3>
|
||||
<p className="text-sm mt-1 text-foreground font-semibold">Collaborative pixel canvas engine.</p>
|
||||
</div>
|
||||
<div className="absolute inset-x-4 bottom-4 top-24 rounded-lg overflow-hidden z-10 bg-black/5 dark:bg-white/5 border border-indigo-500/10 shadow-inner">
|
||||
<Image
|
||||
src="/images/products/moonplace.png"
|
||||
alt="Screenshot of MoonPlace collaborative pixel art canvas showing colorful community-created artwork"
|
||||
fill
|
||||
className="object-contain p-2 opacity-50 transition-transform duration-1000 group-hover:scale-110"
|
||||
sizes="(max-width: 768px) 100vw, 25vw"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
{/* Row 1-2: Smirk Wallet (1x2 tall, far right) */}
|
||||
<Link href="https://smirk.cash" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-1 md:row-span-2" style={{ animationDelay: "150ms" }}>
|
||||
{/* Row 1: Smirk Wallet (2x1) */}
|
||||
<Link href="https://smirk.cash" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-2" style={{ animationDelay: "75ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] !bg-amber-50/95 dark:!bg-amber-950/95 !border-amber-500/20 cursor-pointer">
|
||||
<div className="absolute inset-x-0 top-0 z-20 p-6 bg-gradient-to-b from-background/95 to-transparent pointer-events-none">
|
||||
<div className="absolute inset-x-0 top-0 z-20 p-6 bg-gradient-to-b from-background/95 to-transparent pointer-events-none">
|
||||
<h3 className="text-xl font-bold text-amber-700 dark:text-amber-400">Smirk Wallet</h3>
|
||||
<p className="text-sm mt-1 text-foreground font-semibold">Non-custodial browser wallet with encrypted social tipping.</p>
|
||||
</div>
|
||||
<div className="absolute inset-x-4 bottom-4 top-28 rounded-lg overflow-hidden z-10 bg-black/5 dark:bg-white/5 border border-amber-500/10 shadow-inner flex items-center justify-center">
|
||||
<Image
|
||||
src="/images/products/smirk-wallet.png"
|
||||
alt="Screenshot of Smirk Wallet browser extension showing multi-currency support for BTC, LTC, XMR, WOW, and GRIN"
|
||||
fill
|
||||
className="object-contain p-4 opacity-60 transition-transform duration-1000 group-hover:scale-110"
|
||||
sizes="(max-width: 768px) 100vw, 25vw"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute inset-x-4 bottom-4 top-24 rounded-lg overflow-hidden z-10 bg-black/5 dark:bg-white/5 border border-amber-500/10 shadow-inner flex items-center justify-center">
|
||||
<Image
|
||||
src="/images/products/smirk-wallet.png"
|
||||
alt="Screenshot of Smirk Wallet browser extension showing multi-currency support for BTC, LTC, XMR, WOW, and GRIN"
|
||||
fill
|
||||
className="object-contain p-4 opacity-60 transition-transform duration-1000 group-hover:scale-110"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
{/* Row 2: Neroswap (2x1) */}
|
||||
<Link href="https://neroswap.com" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-2" style={{ animationDelay: "225ms" }}>
|
||||
<Link href="https://neroswap.com" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-2" style={{ animationDelay: "150ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] !bg-yellow-50/95 dark:!bg-yellow-950/95 !border-yellow-500/20 cursor-pointer">
|
||||
<div className="absolute inset-x-0 top-0 z-20 p-6 bg-gradient-to-b from-background/95 to-transparent pointer-events-none">
|
||||
<h3 className="text-xl font-bold text-yellow-700 dark:text-yellow-400">Neroswap</h3>
|
||||
@@ -84,7 +63,7 @@ export const PortfolioGrid = () => {
|
||||
</Link>
|
||||
|
||||
{/* Row 2: Bauhaus Echo (1x1) */}
|
||||
<Link href="/products/bauhaus-echo" className="bento-item md:col-span-1" style={{ animationDelay: "300ms" }}>
|
||||
<Link href="/products/bauhaus-echo" className="bento-item md:col-span-1" style={{ animationDelay: "225ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] !bg-blue-50/95 dark:!bg-blue-950/95 !border-blue-500/20 cursor-pointer">
|
||||
<div className="absolute inset-x-0 top-0 z-20 p-6 bg-gradient-to-b from-background/95 to-transparent pointer-events-none">
|
||||
<h3 className="text-xl font-bold text-blue-700 dark:text-blue-400">Bauhaus Echo</h3>
|
||||
@@ -102,46 +81,24 @@ export const PortfolioGrid = () => {
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
{/* Row 3: Occupy Wallets (2x1) */}
|
||||
<Link href="https://occupywallets.art" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-2" style={{ animationDelay: "375ms" }}>
|
||||
{/* Row 2: Occupy Wallets (1x1) */}
|
||||
<Link href="https://occupywallets.art" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-1" style={{ animationDelay: "300ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] cursor-pointer">
|
||||
<div className="absolute inset-0 z-20 p-8 flex flex-col justify-center items-center text-center pointer-events-none">
|
||||
<div className="absolute inset-0 z-20 p-6 flex flex-col justify-center items-center text-center pointer-events-none">
|
||||
<h3 className="text-2xl font-bold text-zinc-900 dark:text-white drop-shadow-sm bg-white/60 dark:bg-black/60 backdrop-blur-3xl rounded-xl px-6 py-2">Occupy Wallets</h3>
|
||||
<p className="text-zinc-900 dark:text-zinc-200 font-bold drop-shadow-sm bg-white/60 dark:bg-black/60 backdrop-blur-3xl rounded-xl px-6 py-2 mt-4">High-fidelity gallery for fiscal graffiti.</p>
|
||||
<p className="text-zinc-900 dark:text-zinc-200 font-bold drop-shadow-sm bg-white/60 dark:bg-black/60 backdrop-blur-3xl rounded-xl px-4 py-2 mt-4 text-sm">High-fidelity gallery for fiscal graffiti.</p>
|
||||
</div>
|
||||
<div className="absolute inset-4 rounded-xl overflow-hidden z-10 bg-zinc-200/50 dark:bg-zinc-800/50">
|
||||
<Image
|
||||
src="/images/products/occupy-wallets.png"
|
||||
alt="Screenshot of Occupy Wallets gallery displaying illustrated artwork with political and economic commentary"
|
||||
fill
|
||||
className="object-contain p-4 opacity-60 transition-transform duration-1000 group-hover:scale-105"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
/>
|
||||
<Image
|
||||
src="/images/products/occupy-wallets.png"
|
||||
alt="Screenshot of Occupy Wallets gallery displaying illustrated artwork with political and economic commentary"
|
||||
fill
|
||||
className="object-contain p-4 opacity-60 transition-transform duration-1000 group-hover:scale-105"
|
||||
sizes="(max-width: 768px) 100vw, 25vw"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
{/* Row 3: Privacy Labs (2x1) */}
|
||||
<Link href="https://play.wowne.ro" target="_blank" rel="noopener noreferrer" className="bento-item md:col-span-2" style={{ animationDelay: "450ms" }}>
|
||||
<Card className="h-full glass-card group min-h-[300px] rounded-[2.5rem] !bg-orange-50/95 dark:!bg-orange-950/95 !border-orange-500/20 cursor-pointer">
|
||||
<div className="absolute inset-x-0 top-0 z-20 p-6 bg-gradient-to-b from-background/95 to-transparent pointer-events-none">
|
||||
<h3 className="text-xl font-bold text-orange-700 dark:text-orange-400">Privacy Labs</h3>
|
||||
<ul className="mt-2 space-y-1 text-sm text-foreground font-semibold">
|
||||
<li className="flex items-center gap-2">Monerogue / Wownerogue</li>
|
||||
<li className="flex items-center gap-2"><span className="text-xs opacity-60">(Blockchain Gaming)</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="absolute inset-x-4 bottom-4 top-24 rounded-lg overflow-hidden z-10 bg-black/5 dark:bg-white/5 border border-orange-500/10 shadow-inner">
|
||||
<Image
|
||||
src="/images/products/privacy-labs.png"
|
||||
alt="Screenshots of Monerogue roguelike game and Wowne.ro WebAssembly wallet interface"
|
||||
fill
|
||||
className="object-contain p-2 opacity-50 transition-transform duration-1000 group-hover:scale-110"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -88,6 +88,20 @@ export function SuchMoonLaunchStoreButtons() {
|
||||
);
|
||||
}
|
||||
|
||||
export function WownerogueLinks() {
|
||||
// Wownerogue is a browser game, not a store app: link the two live instances + source.
|
||||
return (
|
||||
<StoreButtons
|
||||
links={[]}
|
||||
extraLinks={[
|
||||
{ href: "https://play.wowne.ro", label: "Play Free (Wownero)" },
|
||||
{ href: "https://monerogue.app", label: "Monero Stagenet" },
|
||||
{ href: "https://github.com/Such-Software/wownerogue", label: "Source on GitHub" },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function BauhausEchoStoreButtons() {
|
||||
return (
|
||||
<StoreButtons
|
||||
|
||||
Reference in New Issue
Block a user