mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-20 05:58:32 +02:00
Tweak copy and add links
This commit is contained in:
@@ -19,19 +19,6 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
const apps = [
|
||||
{
|
||||
name: "Such Moon Launch",
|
||||
tagline: "Vry rocket. Much landing. Wow!",
|
||||
icon: "/images/products/such-moon-launch.png",
|
||||
color: "text-amber-600 dark:text-amber-400",
|
||||
borderColor: "border-amber-500/20",
|
||||
href: "/products/such-moon-launch",
|
||||
links: [
|
||||
// null = render disabled "Coming Soon" button; flip to real URLs once live.
|
||||
{ platform: "google" as const, href: null },
|
||||
{ platform: "apple" as const, href: null },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Vegan IQ",
|
||||
tagline: "Plant-based trivia backed by real data",
|
||||
@@ -68,20 +55,33 @@ const apps = [
|
||||
{ platform: "apple" as const, href: "https://apps.apple.com/us/app/suchoice/id6759626658" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Such Moon Launch",
|
||||
tagline: "Vry rocket. Much landing. Wow!",
|
||||
icon: "/images/products/such-moon-launch.png",
|
||||
color: "text-amber-600 dark:text-amber-400",
|
||||
borderColor: "border-amber-500/20",
|
||||
href: "/products/such-moon-launch",
|
||||
links: [
|
||||
// Google Play live; Apple in review (null renders a disabled "Coming Soon").
|
||||
{ platform: "google" as const, href: "https://play.google.com/store/apps/details?id=com.suchsoftware.suchmoonlaunch" },
|
||||
{ platform: "apple" as const, href: null },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function LinksPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-lg mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<div id="main-content" className="z-10 w-full max-w-3xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<h1 className="text-3xl font-bold mb-2 text-center">Such Software</h1>
|
||||
<p className="text-muted-foreground text-center mb-10">Our apps</p>
|
||||
|
||||
<div className="space-y-8">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
{apps.map((app, i) => (
|
||||
<ScrollReveal key={app.name} delay={i * 0.08}>
|
||||
<div className={`rounded-xl border ${app.borderColor} bg-card p-6`}>
|
||||
<ScrollReveal key={app.name} delay={(i % 2) * 0.08}>
|
||||
<div className={`h-full rounded-xl border ${app.borderColor} bg-card p-6`}>
|
||||
<div className="flex items-center gap-4 mb-3">
|
||||
<Image
|
||||
src={app.icon}
|
||||
|
||||
@@ -18,20 +18,6 @@ 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",
|
||||
description: "Aggregated DEX & CEX orderbook data. Compare Monero, Wownero, and Bitcoin prices across 6 exchanges in real time.",
|
||||
color: "yellow",
|
||||
icon: "/images/products/neroswap.png",
|
||||
},
|
||||
{
|
||||
slug: "webshops",
|
||||
title: "Webshops",
|
||||
@@ -81,6 +67,20 @@ const products = [
|
||||
color: "amber",
|
||||
icon: "/images/products/such-moon-launch.png",
|
||||
},
|
||||
{
|
||||
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",
|
||||
description: "Aggregated DEX & CEX orderbook data. Compare Monero, Wownero, and Bitcoin prices across 6 exchanges in real time.",
|
||||
color: "yellow",
|
||||
icon: "/images/products/neroswap.png",
|
||||
},
|
||||
];
|
||||
|
||||
const colorClasses: Record<string, string> = {
|
||||
|
||||
@@ -117,20 +117,24 @@ export default function WowneroguePage() {
|
||||
|
||||
<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>
|
||||
<a href="https://play.wowne.ro" target="_blank" rel="noopener noreferrer">
|
||||
<Card className="glass-card border-orange-500/20 hover:border-orange-500/50 transition-colors cursor-pointer h-full">
|
||||
<h3 className="text-xl font-bold mb-2 text-orange-600 dark:text-orange-400">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>
|
||||
</a>
|
||||
<a href="https://monerogue.app" target="_blank" rel="noopener noreferrer">
|
||||
<Card className="glass-card border-orange-500/20 hover:border-orange-500/50 transition-colors cursor-pointer h-full">
|
||||
<h3 className="text-xl font-bold mb-2 text-orange-600 dark:text-orange-400">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>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">How a Run Works</h2>
|
||||
|
||||
@@ -44,7 +44,7 @@ export const FounderSignature = () => {
|
||||
He previously co-founded <span className="text-foreground font-semibold">Patrn LLC</span> (art101.io), a series of art-history NFT drops, and <span className="text-foreground font-semibold">Tabby Labs</span>, which was developing a cryptocurrency wallet with an interactive world inside.
|
||||
</p>
|
||||
<p className="text-muted-foreground leading-relaxed mt-4">
|
||||
He most recently taught engineering at <span className="text-foreground font-semibold">Cecil College</span> and senior-level materials engineering (microfabrication, nanomaterials) at <span className="text-foreground font-semibold">Frostburg State University</span>. Active in cryptocurrency since 2014; <a href="https://github.com/jwinterm" target="_blank" rel="noopener noreferrer" className="text-foreground font-semibold underline decoration-primary/50 hover:text-primary transition-colors">GitHub</a> for the receipts.
|
||||
He most recently taught engineering at <span className="text-foreground font-semibold">Cecil College</span> and senior-level materials engineering (microfabrication, nanomaterials) at <span className="text-foreground font-semibold">Frostburg State University</span>. Active in cryptocurrency since 2014, with a public track record on <a href="https://github.com/jwinterm" target="_blank" rel="noopener noreferrer" className="text-foreground font-semibold underline decoration-primary/50 hover:text-primary transition-colors">GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,103 +2,97 @@ import { Card } from "@repo/ui/components/card";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
// Literal class strings per color so Tailwind's JIT keeps them (no dynamic interpolation).
|
||||
const cardBg: Record<string, string> = {
|
||||
orange: "!bg-orange-50/95 dark:!bg-orange-950/95 !border-orange-500/20",
|
||||
yellow: "!bg-yellow-50/95 dark:!bg-yellow-950/95 !border-yellow-500/20",
|
||||
amber: "!bg-amber-50/95 dark:!bg-amber-950/95 !border-amber-500/20",
|
||||
emerald: "!bg-emerald-50/95 dark:!bg-emerald-950/95 !border-emerald-500/20",
|
||||
blue: "!bg-blue-50/95 dark:!bg-blue-950/95 !border-blue-500/20",
|
||||
green: "!bg-green-50/95 dark:!bg-green-950/95 !border-green-500/20",
|
||||
};
|
||||
const titleColor: Record<string, string> = {
|
||||
orange: "text-orange-700 dark:text-orange-400",
|
||||
yellow: "text-yellow-700 dark:text-yellow-400",
|
||||
amber: "text-amber-700 dark:text-amber-400",
|
||||
emerald: "text-emerald-700 dark:text-emerald-400",
|
||||
blue: "text-blue-700 dark:text-blue-400",
|
||||
green: "text-green-700 dark:text-green-400",
|
||||
};
|
||||
const imgBorder: Record<string, string> = {
|
||||
orange: "border-orange-500/10",
|
||||
yellow: "border-yellow-500/10",
|
||||
amber: "border-amber-500/10",
|
||||
emerald: "border-emerald-500/10",
|
||||
blue: "border-blue-500/10",
|
||||
green: "border-green-500/10",
|
||||
};
|
||||
const spanClass: Record<number, string> = {
|
||||
1: "md:col-span-1",
|
||||
2: "md:col-span-2",
|
||||
};
|
||||
|
||||
type Item = {
|
||||
name: string;
|
||||
tagline: string;
|
||||
color: keyof typeof titleColor;
|
||||
span: 1 | 2;
|
||||
image: string;
|
||||
alt: string;
|
||||
href: string;
|
||||
external?: boolean;
|
||||
};
|
||||
|
||||
// Order matters: spans 2,1,1,2,2,1 tile a 3-column grid with no gaps while alternating the
|
||||
// wide side row to row for a balanced bento look.
|
||||
const items: Item[] = [
|
||||
{ name: "Wownerogue", tagline: "Provably-fair roguelike, synced to crypto block times.", color: "orange", span: 2, image: "/images/products/privacy-labs.png", alt: "Screenshot of the Wownerogue roguelike dungeon-crawler", href: "/products/wownerogue" },
|
||||
{ name: "Neroswap", tagline: "DEX & CEX orderbook aggregator.", color: "yellow", span: 1, image: "/images/products/neroswap.png", alt: "Screenshot of the Neroswap orderbook aggregator", href: "https://neroswap.com", external: true },
|
||||
{ name: "Smirk Wallet", tagline: "Non-custodial browser wallet with social tipping.", color: "amber", span: 1, image: "/images/products/smirk-wallet.png", alt: "Smirk Wallet browser extension", href: "https://smirk.cash", external: true },
|
||||
{ name: "Occupy Wallets", tagline: "Custom e-commerce storefront we build and host for an art brand.", color: "emerald", span: 2, image: "/images/products/occupy-wallets.png", alt: "Occupy Wallets online store and gallery", href: "https://occupywallets.art", external: true },
|
||||
{ name: "Bauhaus Echo", tagline: "Visual memory puzzle game.", color: "blue", span: 2, image: "/images/products/bauhaus-echo.png", alt: "Bauhaus Echo visual memory puzzle game", href: "/products/bauhaus-echo" },
|
||||
{ name: "Vegan IQ", tagline: "Plant-based trivia game.", color: "green", span: 1, image: "/images/products/vegan-iq.png", alt: "Vegan IQ plant-based trivia game", href: "/products/vegan-iq" },
|
||||
];
|
||||
|
||||
function BentoCard({ item, delay }: { item: Item; delay: number }) {
|
||||
const inner = (
|
||||
<Card className={`h-full glass-card group min-h-[300px] rounded-[2.5rem] ${cardBg[item.color]} 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 ${titleColor[item.color]}`}>{item.name}</h3>
|
||||
<p className="text-sm mt-1 text-foreground font-semibold">{item.tagline}</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 ${imgBorder[item.color]} shadow-inner`}>
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.alt}
|
||||
fill
|
||||
className="object-contain p-2 opacity-60 transition-transform duration-1000 group-hover:scale-105"
|
||||
sizes={item.span === 2 ? "(max-width: 768px) 100vw, 66vw" : "(max-width: 768px) 100vw, 33vw"}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const className = `bento-item ${spanClass[item.span]}`;
|
||||
const style = { animationDelay: `${delay}ms` };
|
||||
|
||||
return item.external ? (
|
||||
<a href={item.href} target="_blank" rel="noopener noreferrer" className={className} style={style}>
|
||||
{inner}
|
||||
</a>
|
||||
) : (
|
||||
<Link href={item.href} className={className} style={style}>
|
||||
{inner}
|
||||
</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: 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-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: 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">
|
||||
<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-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: "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>
|
||||
<p className="text-sm mt-1 text-foreground font-semibold">DEX & CEX orderbook aggregator.</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-yellow-500/10 shadow-inner">
|
||||
<Image
|
||||
src="/images/products/neroswap.png"
|
||||
alt="Screenshot of Neroswap DEX and CEX orderbook aggregator interface"
|
||||
fill
|
||||
className="object-cover opacity-50 transition-transform duration-1000 group-hover:scale-105"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
{/* Row 2: Bauhaus Echo (1x1) */}
|
||||
<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>
|
||||
<p className="text-sm mt-1 text-foreground font-semibold">Visual memory puzzle game.</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-blue-500/10 shadow-inner">
|
||||
<Image
|
||||
src="/images/products/bauhaus-echo.png"
|
||||
alt="Screenshot of Bauhaus Echo visual memory puzzle game with geometric tiles"
|
||||
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 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-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-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, 25vw"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
<section className="grid grid-cols-1 md:grid-cols-3 auto-rows-[300px] gap-6 w-full max-w-7xl mx-auto p-4 z-10 relative">
|
||||
{items.map((item, i) => (
|
||||
<BentoCard key={item.name} item={item} delay={i * 75} />
|
||||
))}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -72,13 +72,11 @@ export function SuchoiceStoreButtons() {
|
||||
}
|
||||
|
||||
export function SuchMoonLaunchStoreButtons() {
|
||||
// Both stores submitted 2026-05-18; pending review. Flip null → real URLs
|
||||
// once the app is live (Apple assigns the appstore.com /id<NUM> at first
|
||||
// approval; Google URL is predictable as id=com.suchsoftware.suchmoonlaunch).
|
||||
// Google Play is live; Apple still in review (flip null → real URL once approved).
|
||||
return (
|
||||
<StoreButtons
|
||||
links={[
|
||||
{ platform: "google", href: null },
|
||||
{ platform: "google", href: "https://play.google.com/store/apps/details?id=com.suchsoftware.suchmoonlaunch" },
|
||||
{ platform: "apple", href: null },
|
||||
]}
|
||||
extraLinks={[
|
||||
|
||||
Reference in New Issue
Block a user