feat(site): the tagline takes the title, and the em dashes go

The hero h1 is now "Precision Engineering for Everyone". The concrete offer
moves into the supporting line, which keeps the search terms the h1 gives up
and no longer restates the splash's opening sentence.

The title's gradient drifts instead of sweeping: a 300% ramp over 18s with
symmetric easing, so there is no hard turnaround at either end of the travel.

Em dashes are gone from every tsx/ts file (87 lines, 104 occurrences), per
AGENTS.md rule 9. Label lists and titles take a colon; appositives take
commas; independent clauses take a semicolon or a period. A few sentences
were restructured where no single mark read well.

Metadata keeps "Crypto" deliberately: it is a real service line and a real
search term, and it is no longer in the visible headline.
This commit is contained in:
jwinterm
2026-08-17 04:18:18 -04:00
parent 127081bcac
commit 0848fcabde
30 changed files with 98 additions and 94 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ export default function AboutPage() {
<p className="text-xl leading-relaxed">
Such Software is a software studio in Kennett Square, PA, founded by <a href="https://jwinterm.com">John Winter Murphy</a>,
whose background is in semiconductor and nuclear engineering: a Materials Engineering PhD from UT-Dallas and six years at Lawrence Livermore National Laboratory.
We build accessible e-commerce websites, crypto and payment integrations, and original mobile apps and games. We also consult on cryptocurrency and payment software and integration — the client or a licensed third party holds funds; we never take custody — and teach nuclear, radiation, and semiconductor modeling through open-source labs, training, and talks.
We build accessible e-commerce websites, crypto and payment integrations, and original mobile apps and games. We also consult on cryptocurrency and payment software and integration; the client or a licensed third party holds funds, and we never take custody. We teach nuclear, radiation, and semiconductor modeling through open-source labs, training, and talks.
</p>
</div>
+7 -4
View File
@@ -283,19 +283,22 @@
@keyframes nebula-caret { 0%, 50% { border-color: currentColor; } 50.01%, 100% { border-color: transparent; } }
@keyframes nebula-term-out { to { opacity: 0; } }
/* Hero title: an animated gradient sheen. LCP-safe — the text paints immediately with the
/* Hero title: an animated gradient sheen. LCP-safe: the text paints immediately with the
gradient; only the gradient POSITION animates, which never delays the largest paint. The
global reduced-motion rule freezes it to the base gradient. */
global reduced-motion rule freezes it to the base gradient.
The wider 300% ramp over an 18s cycle makes this a slow drift rather than a visible sweep,
and the symmetric cubic-bezier easing removes the hard turnaround the old ease-in-out had
at each end of the travel. */
/* The brand's four-colour system (S green, U magenta, C blue, H teal), one per glyph.
Light theme uses the light palette; .dark swaps in the dark palette, which is the
same hues lifted for contrast on near-black -- from packs/such-software/pack.json. */
.hero-title {
background-image: linear-gradient(110deg, #1a5c38 0%, #b83670 30%, #176a92 55%, #12857e 80%, #1a5c38 100%);
background-size: 220% auto;
background-size: 300% auto;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: hero-sheen 9s ease-in-out infinite;
animation: hero-sheen 18s cubic-bezier(0.37, 0, 0.63, 1) infinite;
}
.dark .hero-title {
background-image: linear-gradient(110deg, #35925c 0%, #e8689e 30%, #4aade0 55%, #3ad4c6 80%, #35925c 100%);
+4 -3
View File
@@ -28,11 +28,12 @@ export default function Home() {
className="hero-title text-4xl sm:text-5xl md:text-7xl font-bold mb-6 pb-2"
style={{ maxWidth: "min(calc(90vw - 120px), 100%)" }}
>
<span className="block">Custom E-commerce,</span>
<span className="block">Apps &amp; Crypto Tools</span>
<span className="block">Precision Engineering</span>
<span className="block">for Everyone</span>
</h1>
<p className="max-w-2xl text-xl text-muted-foreground mb-8">
Websites, games, payments, and consulting too — plus our own mobile apps. A software studio in Kennett Square, PA. Precision engineering for everyone.
Custom e-commerce, apps, games, and crypto payment tools, built to order in
Kennett Square, PA. Plus a shelf of mobile apps and games of our own.
</p>
<div className="flex flex-wrap justify-center gap-4 mb-14">
@@ -1,6 +1,6 @@
import { gameOgImage, OG_SIZE, OG_CONTENT_TYPE } from "@/lib/og";
export const alt = "Alpha Doc — an academic-horror roguelike";
export const alt = "Alpha Doc: an academic-horror roguelike";
export const size = OG_SIZE;
export const contentType = OG_CONTENT_TYPE;
+7 -7
View File
@@ -27,11 +27,11 @@ export const metadata: Metadata = {
};
const faqs = [
{ q: "What is Alpha Doc?", a: "A top-down roguelike set in a physics research facility warped by a containment breach. You play a doctoral candidate who caused the disaster the same night as their thesis defense — now irradiated, you descend through five department wings to face the Committee." },
{ q: "What are the four radiation types?", a: "Alpha, Beta, Gamma, and Neutron, each a distinct playstyle. Neutron is the standout: it's not a weapon but an environmental transmutation tool — you reshape the map itself, destroying cover and creating hazards before a fight even starts." },
{ q: "Is there a morality system?", a: "Yes, but it's never an explicit choice. The aggressive play that wins fights also destroys the evidence of your culpability. Your standing is inferred from the world and reflected in four different endings — you can't quite feel good about winning." },
{ q: "What is Alpha Doc?", a: "A top-down roguelike set in a physics research facility warped by a containment breach. You play a doctoral candidate who caused the disaster the same night as their thesis defense. Now irradiated, you descend through five department wings to face the Committee." },
{ q: "What are the four radiation types?", a: "Alpha, Beta, Gamma, and Neutron, each a distinct playstyle. Neutron is the standout: it's not a weapon but an environmental transmutation tool: you reshape the map itself, destroying cover and creating hazards before a fight even starts." },
{ q: "Is there a morality system?", a: "Yes, but it's never an explicit choice. The aggressive play that wins fights also destroys the evidence of your culpability. Your standing is inferred from the world and reflected in four different endings; you can't quite feel good about winning." },
{ q: "How will it be sold?", a: "Modern shareware: a free, complete first sector (the Grant Office), then a single one-time purchase to unlock the rest. No ads, no microtransactions, no energy timers." },
{ q: "When is it out?", a: "It's in active development — a playable vertical slice exists. Follow Such Software for release news on Steam, itch.io, iOS, and Android." },
{ q: "When is it out?", a: "It's in active development: a playable vertical slice exists. Follow Such Software for release news on Steam, itch.io, iOS, and Android." },
];
const jsonLd = {
@@ -50,10 +50,10 @@ const jsonLd = {
const features = [
{ title: "Four-element combat", body: "Alpha, Beta, Gamma, and Neutron are four real playstyles, not reskins. Each rewards a different read of the room." },
{ title: "Neutron reshapes the map", body: "The Neutron element transmutes the environment — destroy cover, open paths, arm dormant hazards. You change the battlefield before the battle." },
{ title: "Neutron reshapes the map", body: "The Neutron element transmutes the environment: destroy cover, open paths, arm dormant hazards. You change the battlefield before the battle." },
{ title: "Instability as a dial", body: "Push instability for harder-hitting shots at a slower fire rate, or dial it back and let it decay. Tension, not a punishment cliff." },
{ title: "Emergent morality", body: "No morality meter. Burning evidence for power quietly shifts dialogue, companion reactions, and which of four endings you reach." },
{ title: "Academic horror", body: "Your enemies are colleagues with real grievances — the reviewer you ignored, the safety inspector you overruled, the adjuncts caught in the blast." },
{ title: "Academic horror", body: "Your enemies are colleagues with real grievances: the reviewer you ignored, the safety inspector you overruled, the adjuncts caught in the blast." },
{ title: "Respectful premium design", body: "Free first sector, one-time unlock, no ads or timers. The systems are built for a complete experience, not a monetization funnel." },
];
@@ -89,7 +89,7 @@ export default function AlphaDocPage() {
<div className="prose dark:prose-invert max-w-none mb-12">
<p className="text-xl text-muted-foreground leading-relaxed">
It is 2:07 AM. A fifth-year PhD student has just caused a catastrophic accelerator overload,
irradiating the building and everyone in it — and the thesis defense is at 8:00 AM. Descend
irradiating the building and everyone in it, and the thesis defense is at 8:00 AM. Descend
through five warped department wings, master four types of radiation, and decide whether to
bury the evidence or face the Committee. Alpha Doc is a systems-driven roguelike with dark
academic teeth.
@@ -1,6 +1,6 @@
import { gameOgImage, OG_SIZE, OG_CONTENT_TYPE } from "@/lib/og";
export const alt = "Barns & Neutrons — a game about the Chart of Nuclides";
export const alt = "Barns & Neutrons: a game about the Chart of Nuclides";
export const size = OG_SIZE;
export const contentType = OG_CONTENT_TYPE;
@@ -21,16 +21,16 @@ export const metadata: Metadata = {
],
openGraph: {
title: "Barns & Neutrons: A Game About the Chart of Nuclides",
description: "A cozy, honest expedition across the real Chart of Nuclides — the anti-spreadsheet. In development by Such Software.",
description: "A cozy, honest expedition across the real Chart of Nuclides: the anti-spreadsheet. In development by Such Software.",
type: "website",
url: "/products/barns-and-neutrons",
},
};
const faqs = [
{ q: "What is Barns & Neutrons?", a: "A cozy exploration-puzzle game set on the real Chart of Nuclides — the grid of every known isotope. You start on a small island of known nuclides and each step into the dark is a small physics puzzle: capture a neutron, coax a decay. The reward is discovery, as a new nuclide lights up and its Compendium entry unfolds." },
{ q: "What is Barns & Neutrons?", a: "A cozy exploration-puzzle game set on the real Chart of Nuclides, the grid of every known isotope. You start on a small island of known nuclides and each step into the dark is a small physics puzzle: capture a neutron, coax a decay. The reward is discovery, as a new nuclide lights up and its Compendium entry unfolds." },
{ q: "Is it educational?", a: "Yes, but through play, not lectures. Every animation reflects real physics, and discoveries unlock real-world context (Tc-99m in medical imaging, Am-241 in smoke detectors, C-14 in dating) along with half-lives, Q-values, and cornerstone papers. The data is grounded in open sources like ENDF, IAEA, and NIST." },
{ q: "What does “Barns” mean?", a: "A barn is the real unit of nuclear cross-section (10⁻²⁴ cm²) — a measure of how likely a nucleus is to interact with a neutron. The neutron-capture puzzles are literally about tuning energy to hit that cross-section." },
{ q: "What does “Barns” mean?", a: "A barn is the real unit of nuclear cross-section (10⁻²⁴ cm²), a measure of how likely a nucleus is to interact with a neutron. The neutron-capture puzzles are literally about tuning energy to hit that cross-section." },
{ q: "What platforms will it be on?", a: "It is built in Godot for desktop (Windows, macOS, Linux) and mobile (iOS, Android)." },
{ q: "When is it out?", a: "It is in active development. The first playable slice (the Mo-99 → Tc-99m neighborhood) is the current milestone. Follow Such Software for release news." },
];
@@ -51,8 +51,8 @@ const jsonLd = {
const features = [
{ title: "Discovery is the loop", body: "No linear tech tree. You explore a real, enormous map of ~3,300 nuclides. Completionism and pure curiosity pull you deeper, with no timers and no game-overs." },
{ title: "Honest physics", body: "When reality and a cuter lie compete, reality wins — then we make reality beautiful. Neutron scattering is forward-peaked at fast energies because it actually is." },
{ title: "Failure is serendipity", body: "Miss a neutron capture and the nucleus does what it really would — maybe a decay instead. You discover a different nuclide and learn real behavior. No punishment, only discovery." },
{ title: "Honest physics", body: "When reality and a cuter lie compete, reality wins; then we make reality beautiful. Neutron scattering is forward-peaked at fast energies because it actually is." },
{ title: "Failure is serendipity", body: "Miss a neutron capture and the nucleus does what it really would: maybe a decay instead. You discover a different nuclide and learn real behavior. No punishment, only discovery." },
{ title: "The Compendium", body: "Every discovered isotope unlocks a card: real uses, half-lives, Q-values, and a link to a cornerstone paper (Fermi, Chadwick, Hahn & Strassmann). You learn the history, not just the facts." },
{ title: "A 3D tabletop, not a spreadsheet", body: "A tilted, extruded ceramic-tile board with a glowing valley of stability and dramatic discovery vignettes. Cozy, warm, and tactile." },
{ title: "Open core", body: "The physics, data, and code are open; the art, prose, and Compendium illustrations are ours. Facts aren't copyrightable, and we cite them anyway." },
@@ -85,13 +85,13 @@ export default function BarnsAndNeutronsPage() {
</h1>
</div>
</div>
<p className="text-base text-muted-foreground mb-6">A cozy, honest expedition across the real Chart of Nuclides — the anti-spreadsheet.</p>
<p className="text-base text-muted-foreground mb-6">A cozy, honest expedition across the real Chart of Nuclides: the anti-spreadsheet.</p>
<div className="prose dark:prose-invert max-w-none mb-12">
<p className="text-xl text-muted-foreground leading-relaxed">
Barns &amp; Neutrons is a calm puzzle game about the real Chart of Nuclides. You begin on a
tiny lit island of known isotopes, and each step into the dark is a small, elegant physics
puzzle — capture a neutron, coax a decay. The reward is discovery: a new nuclide lights up
puzzle: capture a neutron, coax a decay. The reward is discovery: a new nuclide lights up
and its Compendium entry unfolds. It is our flagship edutainment title, and it shares its
DNA with our founder&rsquo;s open-source{" "}
<a href="https://jwinterm.github.io/geant4-radiation-labs/" target="_blank" rel="noopener noreferrer" className="text-yellow-600 dark:text-yellow-400 hover:underline">GEANT4 radiation labs</a>.
@@ -70,7 +70,7 @@ export default function BauhausEchoPage() {
<p className="text-xl text-muted-foreground leading-relaxed">
A visual memory puzzle game inspired by Bauhaus design aesthetics.
Memorize arrangements of geometric tiles, watch them shuffle, then reconstruct the pattern.
Or unwind in Zen mode — a pressure-free sandbox where you compose with tiles at your own pace.
Or unwind in Zen mode, a pressure-free sandbox where you compose with tiles at your own pace.
</p>
</div>
@@ -82,7 +82,7 @@ export default function BauhausEchoPage() {
]}
extraLinks={[
{ href: "https://bauhaus.such.software", label: "Play on Web" },
{ href: "https://suchsoftware.itch.io/bauhaus-echo", label: "itch.io — $2" },
{ href: "https://suchsoftware.itch.io/bauhaus-echo", label: "itch.io: $2" },
]}
/>
</div>
@@ -90,10 +90,10 @@ export default function BauhausEchoPage() {
<h2 className="text-2xl font-bold mb-6">How It Works</h2>
<div className="section-container border-l-4 border-blue-500 mb-12">
<ol className="space-y-4 text-muted-foreground">
<li><strong>1. Memorization</strong> — Tiles appear in a grid. Study their positions.</li>
<li><strong>2. Shuffle</strong> — Tiles spin, shrink, and scatter to the bottom.</li>
<li><strong>3. Reconstruction</strong> — Drag tiles back to their original positions.</li>
<li><strong>4. Scoring</strong> — Earn points based on accuracy and speed.</li>
<li><strong>1. Memorization</strong>: Tiles appear in a grid. Study their positions.</li>
<li><strong>2. Shuffle</strong>: Tiles spin, shrink, and scatter to the bottom.</li>
<li><strong>3. Reconstruction</strong>: Drag tiles back to their original positions.</li>
<li><strong>4. Scoring</strong>: Earn points based on accuracy and speed.</li>
</ol>
</div>
@@ -82,8 +82,8 @@ export default function BauhausEchoPrivacyPage() {
<h3>In-App Purchases</h3>
<p>If you purchase the &quot;Remove Ads&quot; upgrade, your transaction is processed by:</p>
<ul>
<li><strong>Google Play</strong> (Android) — <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Google Play Privacy Policy</a></li>
<li><strong>Apple App Store</strong> (iOS) — <a href="https://www.apple.com/legal/privacy/" target="_blank" rel="noopener noreferrer">Apple Privacy Policy</a></li>
<li><strong>Google Play</strong> (Android): <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Google Play Privacy Policy</a></li>
<li><strong>Apple App Store</strong> (iOS): <a href="https://www.apple.com/legal/privacy/" target="_blank" rel="noopener noreferrer">Apple Privacy Policy</a></li>
</ul>
<p>
We do not have access to your payment information. We only receive confirmation of your purchase status.
+1 -1
View File
@@ -95,7 +95,7 @@ export default function BloomwordPage() {
Bloomword is a daily word game with a twist you can watch grow. Everyone gets the same
letters each day and races the sunset to find words, and every word you find sprouts new
growth on a living 3D typographic tree. Keep your streak, collect the biomes, and share the
bloom you grew today. It is free on iOS and Android — learn more at{" "}
bloom you grew today. It is free on iOS and Android. Learn more at{" "}
<a href="https://bloomword.earth" target="_blank" rel="noopener noreferrer" className="text-green-600 dark:text-green-400 hover:underline">bloomword.earth</a>.
</p>
</div>
@@ -1,6 +1,6 @@
import { gameOgImage, OG_SIZE, OG_CONTENT_TYPE } from "@/lib/og";
export const alt = "Decay Theory — Erwin's Atomic Adventure";
export const alt = "Decay Theory: Erwin's Atomic Adventure";
export const size = OG_SIZE;
export const contentType = OG_CONTENT_TYPE;
@@ -27,10 +27,10 @@ export const metadata: Metadata = {
};
const faqs = [
{ q: "What is Decay Theory?", a: "A 2D side-scrolling platformer where the entire world is made of individual atoms undergoing radioactive decay in real time. You don't see platforms vanish in bulk — you watch the ground dissolve atom by atom beneath your feet." },
{ q: "Who is Erwin?", a: "Erwin is a quantum-coherent cat — a nod to Schrödinger — whose body is itself a particle system. Health is 'quantum coherence,' shown as particle density: a healthy Erwin is a tight cloud; near death, a ghostly shimmer." },
{ q: "What is Decay Theory?", a: "A 2D side-scrolling platformer where the entire world is made of individual atoms undergoing radioactive decay in real time. You don't see platforms vanish in bulk; you watch the ground dissolve atom by atom beneath your feet." },
{ q: "Who is Erwin?", a: "Erwin is a quantum-coherent cat, a nod to Schrödinger, whose body is itself a particle system. Health is 'quantum coherence,' shown as particle density: a healthy Erwin is a tight cloud; near death, a ghostly shimmer." },
{ q: "Does it actually teach physics?", a: "Yes, through mechanics rather than lectures. Decay modes, half-lives, decay chains, fission, and reactor control are things you feel. Isotopes use real half-lives and color coding." },
{ q: "How is it released?", a: "Episode 1, 'Ground State,' is a free vertical slice — no ads, no IAP. The full game (more zones and powers) unlocks via a single one-time purchase." },
{ q: "How is it released?", a: "Episode 1, 'Ground State,' is a free vertical slice: no ads, no IAP. The full game (more zones and powers) unlocks via a single one-time purchase." },
{ q: "When is it out?", a: "Episode 1 is the current milestone, targeting iOS and Android first, with a desktop (Steam) port later. Follow Such Software for the launch." },
];
@@ -50,8 +50,8 @@ const jsonLd = {
};
const features = [
{ title: "The world decays as you watch", body: "Every tile is thousands of simulated atoms with a real decay probability. Platforms erode into swiss cheese and crumble under your feet — stochastic, beautiful, and deadly." },
{ title: "Quantum powers", body: "Observation collapses a wave function to stabilize ground; Time Rewind undoes a fatal step. Later powers — superposition, entanglement, tunneling — each teach a real concept." },
{ title: "The world decays as you watch", body: "Every tile is thousands of simulated atoms with a real decay probability. Platforms erode into swiss cheese and crumble under your feet: stochastic, beautiful, and deadly." },
{ title: "Quantum powers", body: "Observation collapses a wave function to stabilize ground; Time Rewind undoes a fatal step. Later powers each teach a real concept: superposition, entanglement, tunneling." },
{ title: "Erwin, a living particle system", body: "Your cat is rendered as 2,000+ particles. Coherence is health: take damage and Erwin literally comes apart at the seams." },
{ title: "Real isotope data", body: "Isotopes carry authentic half-lives and decay chains, color-coded by family. The Geiger-counter audio tempo tracks the local decay rate." },
{ title: "Tight, skill-based platforming", body: "Precise tilt-and-touch controls, checkpoints, and speedrun-friendly level design. A real platformer, not a quiz with jumps." },
@@ -91,7 +91,7 @@ export default function DecayTheoryPage() {
<p className="text-xl text-muted-foreground leading-relaxed">
Decay Theory is a hard-sci-fi platformer where the entire world is composed of individual
simulated atoms decaying in real time. You play Erwin, a quantum cat navigating a world that
is literally evaporating at the subatomic level — and you learn real nuclear physics by
is literally evaporating at the subatomic level, and you learn real nuclear physics by
feeling it, not by reading it.
</p>
</div>
@@ -111,7 +111,7 @@ export default function DecayTheoryPage() {
<ul className="space-y-2 text-muted-foreground">
<li><strong>Architecture:</strong> Godot renders; a Rust core runs 100% of the atom simulation.</li>
<li><strong>Performance:</strong> tens of thousands of active, decaying atoms at a smooth frame rate on mid-range phones.</li>
<li><strong>Powers:</strong> quantum mechanics turned into verbs — observation, time rewind, and more.</li>
<li><strong>Powers:</strong> quantum mechanics turned into verbs like observation, time rewind, and more.</li>
<li><strong>Platforms:</strong> iOS and Android first, desktop (Steam) to follow.</li>
</ul>
</div>
+3 -3
View File
@@ -49,7 +49,7 @@ const exchanges = [
{
name: "BasicSwap",
type: "DEX",
desc: "SMSG atomic swaps — BTC, LTC, XMR, PART, WOW, FIRO, DASH & more. Fully decentralized, on-chain settlement.",
desc: "SMSG atomic swaps: BTC, LTC, XMR, PART, WOW, FIRO, DASH & more. Fully decentralized, on-chain settlement.",
},
{
name: "Retoswap",
@@ -93,7 +93,7 @@ const features = [
},
{
title: "Clickable Markets",
desc: "Every ticker row links directly to the market on the source exchange. Find the best price, click, and trade — no extra steps.",
desc: "Every ticker row links directly to the market on the source exchange. Find the best price, click, and trade. No extra steps.",
},
{
title: "Kraken Reference Prices",
@@ -199,7 +199,7 @@ export default function NeroswapPage() {
<p className="text-muted-foreground leading-relaxed mb-4">
Neroswap runs a fleet of Python scrapers on a dedicated server. Every
10 minutes, each scraper pulls the latest orderbook or ticker data
from its exchange — via API, gRPC, or HTML scraping over Tor. The
from its exchange, via API, gRPC, or HTML scraping over Tor. The
results are merged into JSON files and synced to a CDN edge server.
</p>
<p className="text-muted-foreground leading-relaxed">
+1 -1
View File
@@ -152,7 +152,7 @@ export default function ProductsPage() {
Portfolio
</h1>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Products &amp; projects — from commerce backends to consumer games, plus three nuclear-physics games in development.
Products &amp; projects: from commerce backends to consumer games, plus three nuclear-physics games in development.
</p>
</div>
@@ -7,12 +7,12 @@ import Link from "next/link";
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Such Moon Launch — Global Leaderboard | Such Software",
title: "Such Moon Launch: Global Leaderboard | Such Software",
description:
"Live global rankings for Such Moon Launch: fastest landings, biggest Moonrock hauls, and Endless Mode wave records across all 12 levels.",
alternates: { canonical: "/products/such-moon-launch/leaderboard" },
openGraph: {
title: "Such Moon Launch — Global Leaderboard",
title: "Such Moon Launch: Global Leaderboard",
description:
"Live global rankings: fastest landings, biggest Moonrock hauls, and Endless Mode wave records.",
type: "website",
@@ -113,7 +113,7 @@ export default async function LeaderboardPage({
🏆 Leaderboard
</h1>
<p className="text-base text-muted-foreground mb-6">
Live global rankings — <span className="text-foreground font-medium">{levelName}</span>. Updates every minute.
Live global rankings: <span className="text-foreground font-medium">{levelName}</span>. Updates every minute.
</p>
{/* Level selector */}
@@ -167,7 +167,7 @@ export default async function LeaderboardPage({
</Card>
) : scores.length === 0 ? (
<Card className="glass-card border-amber-500/20 p-8 text-center text-muted-foreground">
No scores yet for {levelName}. Be the first —{" "}
No scores yet for {levelName}. Be the first:{" "}
<a href="https://moonlaunch.such.software" className="text-amber-600 dark:text-amber-400 hover:underline">
play on web
</a>
@@ -227,7 +227,7 @@ export default async function LeaderboardPage({
{board === "time"
? "Ranked by fastest completion time."
: board === "score"
? "Ranked by total score — speed + fuel + Moonrocks."
? "Ranked by total score: speed + fuel + Moonrocks."
: "Ranked by highest wave survived."}
</p>
@@ -23,7 +23,7 @@ export const metadata: Metadata = {
};
const levels = [
{ name: "Moon", description: "Tutorial — learn to fly and land" },
{ name: "Moon", description: "Tutorial: learn to fly and land" },
{ name: "Mars", description: "Dodge your first Martians" },
{ name: "Venus", description: "Gamma rays and more enemies" },
{ name: "Io", description: "Asteroids join the chaos" },
@@ -33,7 +33,7 @@ const levels = [
{ name: "Pluto", description: "Tiny target, long journey" },
{ name: "Asteroid Belt", description: "Land on a drifting rock cluster" },
{ name: "Space Station", description: "Rotating target, black holes" },
{ name: "Mothership", description: "Boss fight — land on the enemy ship" },
{ name: "Mothership", description: "Boss fight: land on the enemy ship" },
{ name: "Endless Mode", description: "Procedural waves, infinite challenge" },
];
@@ -76,7 +76,7 @@ export default function SuchMoonLaunchPage() {
<p className="text-xl text-muted-foreground leading-relaxed">
A Wownero-themed pixel-art rocket arcade. Fly across the solar system, dodge Martians,
asteroids, gamma rays, and black holes. Collect crypto, upgrade your ship, and unlock
13 ship skins. 11 story levels plus a procedural endless mode — with tilt-to-steer
13 ship skins. 11 story levels plus a procedural endless mode, with tilt-to-steer
controls on mobile.
</p>
</div>
@@ -95,7 +95,7 @@ export default function SuchMoonLaunchPage() {
<div className="section-container border-l-4 border-amber-500 mb-12 text-muted-foreground space-y-4">
<p>
Such Moon Launch is the greatly expanded re-release of <strong>the very first game I ever wrote</strong>.
The original — <em>Meowlet Moon Launch</em> — was a single-level rocket-landing prototype
The original, <em>Meowlet Moon Launch</em>, was a single-level rocket-landing prototype
published years ago as a test app under our old <strong>Tabby Labs</strong> developer account.
One level, simple physics, no music, no juice. But it shipped, and people played it.
</p>
@@ -104,7 +104,7 @@ export default function SuchMoonLaunchPage() {
a procedural endless mode, 4 weapons, 8 ship upgrades, 13 skins, achievements on Google Play
Games and Apple Game Center, cloud save, a global leaderboard backend, a 3D landing
mini-game on final approach, and proper <strong>tilt-to-steer</strong> controls calibrated
for phones — no drift, no fight. All wrapped in a Wownero-themed pixel-art aesthetic.
for phones: no drift, no fight. All wrapped in a Wownero-themed pixel-art aesthetic.
</p>
<p>
Same rocket, same Moon. Almost everything else is new.
@@ -114,10 +114,10 @@ export default function SuchMoonLaunchPage() {
<h2 className="text-2xl font-bold mb-6">How It Works</h2>
<div className="section-container border-l-4 border-amber-500 mb-12">
<ol className="space-y-4 text-muted-foreground">
<li><strong>1. Launch</strong> — Blast off from Earth with limited fuel.</li>
<li><strong>2. Navigate</strong> — Fly through space, dodging hazards and collecting crypto.</li>
<li><strong>3. Land</strong> — Touch down gently on the target planet. Tilt and speed matter.</li>
<li><strong>4. Upgrade</strong> — Spend Moonrocks on thrust, fuel, weapons, shields, and skins.</li>
<li><strong>1. Launch</strong>: Blast off from Earth with limited fuel.</li>
<li><strong>2. Navigate</strong>: Fly through space, dodging hazards and collecting crypto.</li>
<li><strong>3. Land</strong>: Touch down gently on the target planet. Tilt and speed matter.</li>
<li><strong>4. Upgrade</strong>: Spend Moonrocks on thrust, fuel, weapons, shields, and skins.</li>
</ol>
</div>
@@ -184,12 +184,12 @@ export default function SuchMoonLaunchPage() {
<h2 className="text-2xl font-bold mb-6">Space Hazards</h2>
<div className="section-container border-l-4 border-amber-500 mb-12">
<ul className="space-y-2 text-muted-foreground">
<li><strong>Martians</strong> — enemy ships that chase your rocket</li>
<li><strong>Gamma Rays</strong> — periodic lethal beams across the level</li>
<li><strong>Asteroids</strong> — orbiting and drifting rock hazards</li>
<li><strong>Black Holes</strong> — extreme gravity, instant death at the event horizon</li>
<li><strong>Nebula Zones</strong> — drain fuel and slow you down</li>
<li><strong>Solar Wind</strong> — constant force pushing you off course</li>
<li><strong>Martians</strong>: enemy ships that chase your rocket</li>
<li><strong>Gamma Rays</strong>: periodic lethal beams across the level</li>
<li><strong>Asteroids</strong>: orbiting and drifting rock hazards</li>
<li><strong>Black Holes</strong>: extreme gravity, instant death at the event horizon</li>
<li><strong>Nebula Zones</strong>: drain fuel and slow you down</li>
<li><strong>Solar Wind</strong>: constant force pushing you off course</li>
</ul>
</div>
@@ -103,8 +103,8 @@ export default function SuchMoonLaunchPrivacyPage() {
<h3>In-App Purchases</h3>
<p>If you purchase the &quot;Remove Ads&quot; upgrade, your transaction is processed by:</p>
<ul>
<li><strong>Google Play</strong> (Android) — <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Google Play Privacy Policy</a></li>
<li><strong>Apple App Store</strong> (iOS) — <a href="https://www.apple.com/legal/privacy/" target="_blank" rel="noopener noreferrer">Apple Privacy Policy</a></li>
<li><strong>Google Play</strong> (Android): <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Google Play Privacy Policy</a></li>
<li><strong>Apple App Store</strong> (iOS): <a href="https://www.apple.com/legal/privacy/" target="_blank" rel="noopener noreferrer">Apple Privacy Policy</a></li>
</ul>
<p>
We do not have access to your payment information. We only receive confirmation of your purchase status.
+2 -2
View File
@@ -27,7 +27,7 @@ const features = [
{ name: "Snap & Decide", description: "Point your camera at any menu, ingredient list, label, or set of options. AI reads them and loads them into the wheel." },
{ name: "Spin the Wheel", description: "Add your own options or use the ones AI found. Spin the wheel of destiny and go with it." },
{ name: "Coin Flip", description: "Instant 50/50 decisions with a satisfying animated flip, sound effects, and confetti." },
{ name: "Dice Roll", description: "3D physics dice — d6, d8, 2d6, and d20. Shake your phone or tap to roll. Real tumbling, real numbers." },
{ name: "Dice Roll", description: "3D physics dice: d6, d8, 2d6, and d20. Shake your phone or tap to roll. Real tumbling, real numbers." },
{ name: "Decision Duel", description: "Two players, one screen. Tap near the puck to send it flying toward your opponent's goal. Speed increases every hit." },
{ name: "Adventure Mode", description: "Enter a city, spin for a random activity, and open the winner in Google Maps." },
];
@@ -69,7 +69,7 @@ export default function SuchoicePage() {
<div className="prose dark:prose-invert max-w-none mb-12">
<p className="text-xl text-muted-foreground leading-relaxed">
Stop overthinking every decision. Six ways to decide — snap a photo and let AI read it, spin the wheel, flip a coin, roll 3D physics dice, battle it out in a head-to-head duel, or explore your city with a random activity.
Stop overthinking every decision. Six ways to decide: snap a photo and let AI read it, spin the wheel, flip a coin, roll 3D physics dice, battle it out in a head-to-head duel, or explore your city with a random activity.
</p>
</div>
+4 -4
View File
@@ -100,10 +100,10 @@ export default function VeganIqPage() {
Every question in Vegan IQ is grounded in real data. We do not use LLMs to generate question content. Our sources include:
</p>
<ul className="list-disc pl-6 space-y-2 text-muted-foreground mt-4">
<li><strong>USDA FoodData Central</strong> — nutritional composition of plant-based foods</li>
<li><strong>Poore & Nemecek, Science (2018)</strong> — environmental impact data for food production</li>
<li><strong>National Institutes of Health (NIH)</strong> — health and dietary reference data</li>
<li><strong>FAOSTAT</strong> — global food production and agricultural statistics from the UN FAO</li>
<li><strong>USDA FoodData Central</strong>: nutritional composition of plant-based foods</li>
<li><strong>Poore & Nemecek, Science (2018)</strong>: environmental impact data for food production</li>
<li><strong>National Institutes of Health (NIH)</strong>: health and dietary reference data</li>
<li><strong>FAOSTAT</strong>: global food production and agricultural statistics from the UN FAO</li>
</ul>
</div>
</div>
@@ -1,6 +1,6 @@
import { gameOgImage, OG_SIZE, OG_CONTENT_TYPE } from "@/lib/og";
export const alt = "Such Software — four things, done well";
export const alt = "Such Software: four things, done well";
export const size = OG_SIZE;
export const contentType = OG_CONTENT_TYPE;
+1 -1
View File
@@ -33,7 +33,7 @@ export default function SupportPage() {
<div className="section-container border-l-4 border-blue-500">
<h3 className="font-bold mb-2">Bug Reports</h3>
<p className="text-sm text-muted-foreground">
Something not working right? Let us know what happened, your device, and OS version — we&apos;ll investigate.
Something not working right? Let us know what happened, your device, and OS version. We&apos;ll investigate.
</p>
</div>
<div className="section-container border-l-4 border-emerald-500">
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect } from 'react';
// Self-hosted, cookieless Umami — config via env so nothing is hardcoded.
// Self-hosted, cookieless Umami: config via env so nothing is hardcoded.
// Honors Global Privacy Control + Do-Not-Track. No-op unless both env vars are set.
const HOST = process.env.NEXT_PUBLIC_UMAMI_HOST;
const WEBSITE_ID = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
@@ -56,7 +56,7 @@ export function DynamicBackground({ nebulaPosition, visible = true }: DynamicBac
const effectiveTheme = resolvedTheme || systemTheme || 'dark';
const themeMode = effectiveTheme === 'dark' ? 'dark' : 'light';
// While the splash is active (visible === false) keep the nebula/monitor hidden —
// While the splash is active (visible === false) keep the nebula/monitor hidden;
// the splash provides its own Cherenkov glow. Reveal it once the visitor enters.
if (!showNebula || !visible) {
return <div className="fixed inset-0 z-0 h-screen w-screen bg-background" />;
@@ -121,7 +121,7 @@ export function HomeShell({ children }: { children: ReactNode }) {
const pin = () => window.scrollTo(0, 0);
window.addEventListener("scroll", pin, { passive: true });
// The gesture that triggered the enter keeps emitting MOMENTUM (a trackpad flick
// can trail for ~1.5s — longer than the 800ms wave). Releasing the lock on a fixed
// can trail for ~1.5s, longer than the 800ms wave). Releasing the lock on a fixed
// timer let those trailing ticks scroll the revealed page, so you landed below the
// hero instead of at the very top. Hold the lock until the input goes QUIET
// (250ms with no wheel/touch), capped at 3s so a deliberate scroller is never trapped.
@@ -4,7 +4,7 @@ import Link from "next/link";
import { HoverVignette } from "@/components/showcase/hover-vignette";
// Literal class strings per color so Tailwind's JIT keeps them (no dynamic interpolation).
// The colour tint + border only paint on hover/focus (via the `group/tile` link) — at rest
// The colour tint + border only paint on hover/focus (via the `group/tile` link); at rest
// the .bento-item CSS keeps the card chrome transparent so tiles read as title + render only.
const cardBg: Record<string, string> = {
orange: "group-hover/tile:!bg-orange-50/95 dark:group-hover/tile:!bg-orange-950/95 group-focus-within/tile:!bg-orange-50/95 dark:group-focus-within/tile:!bg-orange-950/95 group-hover/tile:!border-orange-500/20 group-focus-within/tile:!border-orange-500/20",
@@ -51,7 +51,7 @@ type Item = {
href: string;
external?: boolean;
/**
* Optional hover/focus "vignette" — a transparent alpha-WebM 3D render from the
* Optional hover/focus "vignette", a transparent alpha-WebM 3D render from the
* game/app that plays OVER this tile's static art on hover or keyboard focus.
* Purely additive: with no assets present (or on Safari / reduced-motion) the tile
* renders exactly as its static `image`. Asset convention:
@@ -71,11 +71,11 @@ const items: Item[] = [
// 3D smirk coin (our engine + sg_outline inset ink): rests smirk-up, hover tosses it with a
// decelerating end-over-end tumble (the suchoice coin-flip feel), lands $-up, half-flips home.
{ 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, vignette: { webm: "/showcase/vignettes/smirk.webm", poster: "/showcase/vignettes/smirk.png" } },
// Transparent chroma-keyed vignette (the engine can't emit real alpha — BW_FLAT magenta key):
// Transparent chroma-keyed vignette (the engine can't emit real alpha, hence the BW_FLAT magenta key):
// full-bloom tree at rest, hover retracts and regrows it. Palindrome loop, seam 0.0.
{ name: "Bloomword", tagline: "A word game where every answer grows the tree.", color: "green", span: 1, image: "/images/products/bloomword.png", alt: "Bloomword, a word game about growing a tree", href: "/products/bloomword", vignette: { webm: "/showcase/vignettes/bloomword.webm", poster: "/showcase/vignettes/bloomword.png" } },
// Single theme-independent asset: the INSET black silhouette stroke (sg_outline) sits ON the yellow letters, so one
// baked alpha render reads on both light and dark tiles — no per-theme pair needed (a 3D->web pipeline win).
// baked alpha render reads on both light and dark tiles; no per-theme pair needed (a 3D->web pipeline win).
{ name: "Barns & Neutrons", tagline: "Cozy puzzle expedition across the real Chart of Nuclides.", color: "amber", span: 2, image: "/images/products/barns-and-neutrons.svg", alt: "Barns & Neutrons, a game about the Chart of Nuclides", href: "/products/barns-and-neutrons", vignette: { webm: "/showcase/vignettes/barns.webm", poster: "/showcase/vignettes/barns.png" } },
// Cover reel from the vegan-IQ social pipeline: logo panel at rest, card-flips to a real
// quiz question (unanswered -> answered) on hover, flips back for a perfect loop seam.
@@ -83,8 +83,8 @@ const items: Item[] = [
];
function BentoCard({ item, delay }: { item: Item; delay: number }) {
// Feature tiles (a live vignette) drop the static screenshot — the transparent poster is the
// rest state — and grow + lift above their neighbours on hover so the animation is the focus.
// Feature tiles (a live vignette) drop the static screenshot, since the transparent poster is the
// rest state, and grow + lift above their neighbours on hover so the animation is the focus.
const feature = !!item.vignette;
const inner = (
<Card className={`h-full glass-card group min-h-[300px] rounded-[2.5rem] ${cardBg[item.color]} cursor-pointer`}>
@@ -72,7 +72,7 @@ function DemoCard({ demo }: { demo: Demo }) {
aria-label={`Open the ${demo.name} demo storefront in a new tab`}
>
<Card className={`glass-card h-full !p-0 overflow-hidden cursor-pointer transition-colors ${cardBorder[demo.color]}`}>
{/* Real screenshot of the live themed storefront — captured at 1280×800,
{/* Real screenshot of the live themed storefront, captured at 1280×800,
cropped to the hero/nav (the most brand-distinctive region). */}
<div className="h-40 w-full overflow-hidden">
<Image
@@ -16,7 +16,7 @@ type HoverVignetteProps = {
* decode VP9, still play the clip. Pointless for alpha overlays (H.264 carries no alpha). */
mp4?: string;
/** contain (default): transparent overlay letterboxed over the tile art. cover: an opaque reel that
* FILLS the tile (no padding) — the poster is the tile art. */
* FILLS the tile (no padding); the poster is the tile art. */
fit?: 'contain' | 'cover';
className?: string;
};
@@ -38,12 +38,12 @@ type HoverVignetteProps = {
* tile plays while it is substantially on screen (IntersectionObserver ≥ 0.5) and
* resets when it scrolls away. Same deferred download: nothing loads until the tile
* is actually seen.
* - `prefers-reduced-motion`: no listeners, no observer — the video never downloads and
* - `prefers-reduced-motion`: no listeners, no observer; the video never downloads and
* never plays. The poster is the whole experience.
* - Codec gate: play() is skipped unless the browser reports it can decode one of the
* provided sources (VP9 webm, or the optional H.264 mp4 for `cover` reels). Safari
* cannot decode VP9-alpha, so overlay tiles degrade to the transparent poster there
* — never a broken or opaque box.
* cannot decode VP9-alpha, so overlay tiles degrade to the transparent poster there,
* never a broken or opaque box.
* - `pointer-events-none` + `aria-hidden`: decorative; never blocks the tile's link or
* steals focus, and adds no layout (absolutely positioned, inset-0).
*/
@@ -94,7 +94,7 @@ export function HoverVignette({
try {
video.currentTime = 0;
} catch {
/* not yet loaded — nothing to reset */
/* not yet loaded: nothing to reset */
}
};
@@ -22,7 +22,7 @@ type ShowcaseVideoProps = {
* experience for those visitors.
* - `muted` + `playsInline` so mobile autoplay-on-view is permitted (one
* video at a time; this component is for a single hero/section clip, not a
* grid — a grid would gate to one visible at a time).
* grid: a grid would gate to one visible at a time).
*/
export function ShowcaseVideo({ webm, mp4, poster, label, className }: ShowcaseVideoProps) {
const ref = useRef<HTMLVideoElement>(null);
+2 -2
View File
@@ -28,7 +28,7 @@ export function StoreButtons({ links, extraLinks }: StoreButtonsProps) {
return (
<Button key={platform} variant="outline" size="lg" disabled>
<Icon className="h-5 w-5 mr-2" aria-hidden="true" />
{label} — Coming Soon
{label}: Coming Soon
</Button>
);
}
@@ -108,7 +108,7 @@ export function BauhausEchoStoreButtons() {
]}
extraLinks={[
{ href: "https://bauhaus.such.software", label: "Play on Web" },
{ href: "https://suchsoftware.itch.io/bauhaus-echo", label: "itch.io — $2" },
{ href: "https://suchsoftware.itch.io/bauhaus-echo", label: "itch.io: $2" },
]}
/>
);
+1 -1
View File
@@ -104,7 +104,7 @@ const cryptoProof: Proof[] = [
];
// Barns/nuclear/Numchangers are the science + math anchors; Zero-Day Defender, Bloomword, and
// Vegan IQ are the edutainment titles (Bloomword + Vegan IQ are cross-listed from Games & Apps —
// Vegan IQ are the edutainment titles (Bloomword + Vegan IQ are cross-listed from Games & Apps:
// they are genuinely both a game we ship AND a game that teaches).
const educationProof: Proof[] = [
{ title: "Barns & Neutrons", blurb: "A cozy exploration-puzzle game across the real Chart of Nuclides: capture neutrons, coax decays, and learn honest nuclear physics. Our premiere visual work.", img: "/images/products/barns-and-neutrons.svg", href: "/products/barns-and-neutrons", tag: "Coming soon" },