mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-17 05:32:05 +02:00
SEO: keyword-led titles/descriptions, structured data, internal linking
- Rewrite title tags + meta descriptions site-wide to front-load search intent (home, products index, services, apps, support, about, blog, and every product page); keep brand suffix, fix overlong/truncated descriptions, use colons not em dashes. Home keeps the visible 'Precision Engineering' hero; only the <title> changed. De-jargon Webshops away from leading with 'headless'. - Add reusable JSON-LD helper (JsonLd + breadcrumb/softwareApp/article builders). WebSite schema site-wide; BreadcrumbList on all product + blog pages; SoftwareApplication on the apps/games; BlogPosting on posts. - Cross-link the Monero/Wownero cluster (Neroswap, Smirk, Wownerogue, Such Moon Launch) and link products to services. - Data fixes: Vegan IQ corrected to 1,000+ questions with Daily Challenge and Weekly Quiz (page + index); generalize Support page off a single game.
This commit is contained in:
@@ -8,14 +8,14 @@ import { FaXTwitter, FaTelegram, FaDiscord, FaBluesky, FaTiktok, FaYoutube, FaIn
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "About | Such Software",
|
||||
description: "Software studio in Kennett Square, PA. Founded by John Winter Murphy, specializing in accessible web engineering and WCAG 2.2 compliance.",
|
||||
title: "About: A Kennett Square, PA Software Studio | Such Software",
|
||||
description: "Such Software is a Kennett Square, PA studio founded by John Winter Murphy, building accessible web apps, crypto tools, and games with WCAG 2.2 compliance.",
|
||||
alternates: {
|
||||
canonical: "/about",
|
||||
},
|
||||
openGraph: {
|
||||
title: "About | Such Software",
|
||||
description: "Software studio in Kennett Square, PA. Founded by John Winter Murphy, specializing in accessible web engineering and WCAG 2.2 compliance.",
|
||||
title: "About: A Kennett Square, PA Software Studio | Such Software",
|
||||
description: "Such Software is a Kennett Square, PA studio founded by John Winter Murphy, building accessible web apps, crypto tools, and games with WCAG 2.2 compliance.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,12 +7,12 @@ import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Apps | Such Software",
|
||||
description: "Download our apps on App Store and Google Play.",
|
||||
title: "Our Apps: Games & Tools for iOS, Android & Web | Such Software",
|
||||
description: "Download Such Software's apps and games: Bauhaus Echo, Suchoice, Vegan IQ, Such Moon Launch, and more. Free on the App Store, Google Play, and the web.",
|
||||
alternates: { canonical: "/apps" },
|
||||
openGraph: {
|
||||
title: "Such Software Apps",
|
||||
description: "Download our apps on App Store and Google Play.",
|
||||
title: "Our Apps: Games & Tools for iOS, Android & Web | Such Software",
|
||||
description: "Download Such Software's apps and games: Bauhaus Echo, Suchoice, Vegan IQ, Such Moon Launch, and more. Free on the App Store, Google Play, and the web.",
|
||||
type: "website",
|
||||
images: [{ url: "/images/branding/OG_banner_v2_light.png", width: 1200, height: 630, alt: "Such Software" }],
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||
import { getAllSlugs, getPost } from "@/lib/blog";
|
||||
import { JsonLd, articleLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||
|
||||
type Props = { params: Promise<{ slug: string }> };
|
||||
|
||||
@@ -50,6 +51,12 @@ export default async function BlogPost({ params }: Props) {
|
||||
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={articleLd({ title: post.title, description: post.excerpt, slug, date: post.date })} />
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Blog", path: "/blog" },
|
||||
{ name: post.title, path: `/blog/${slug}` },
|
||||
])} />
|
||||
<Header />
|
||||
<article id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<Link href="/blog" className="text-sm text-muted-foreground hover:text-emerald-600 dark:hover:text-emerald-400 mb-8 inline-flex items-center gap-2 transition-colors">
|
||||
|
||||
@@ -6,12 +6,12 @@ import type { Metadata } from "next";
|
||||
import { getAllPosts } from "@/lib/blog";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Blog | Such Software",
|
||||
description: "News, updates, and technical articles from Such Software.",
|
||||
title: "Blog: Build Notes on Crypto, Commerce & Apps | Such Software",
|
||||
description: "Technical write-ups from Such Software on provably-fair games, Monero tooling, custom e-commerce, and the apps we ship.",
|
||||
alternates: { canonical: "/blog" },
|
||||
openGraph: {
|
||||
title: "Blog | Such Software",
|
||||
description: "News, updates, and technical articles from Such Software.",
|
||||
title: "Blog: Build Notes on Crypto, Commerce & Apps | Such Software",
|
||||
description: "Technical write-ups from Such Software on provably-fair games, Monero tooling, custom e-commerce, and the apps we ship.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,14 +9,14 @@ const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://such.software"),
|
||||
title: "Such Software | Precision Engineering for Everyone",
|
||||
description: "E-commerce infrastructure, custom app development, and accessible web applications. Engineered for scale, compliance, and performance.",
|
||||
title: "Custom E-Commerce, App & Crypto Development | Such Software",
|
||||
description: "Such Software is a Kennett Square, PA studio building custom online stores, web apps, mobile games, and Monero & crypto tools. Start a project today.",
|
||||
alternates: {
|
||||
canonical: "/",
|
||||
},
|
||||
openGraph: {
|
||||
title: "Such Software",
|
||||
description: "E-commerce infrastructure, custom app development, and accessible web applications.",
|
||||
title: "Such Software: Custom E-Commerce, Apps & Crypto Development",
|
||||
description: "A Kennett Square, PA software studio building custom online stores, web apps, mobile games, and Monero & crypto tools.",
|
||||
type: "website",
|
||||
siteName: "Such Software",
|
||||
images: [
|
||||
@@ -30,8 +30,8 @@ export const metadata: Metadata = {
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Such Software",
|
||||
description: "E-commerce infrastructure, custom app development, and accessible web applications.",
|
||||
title: "Such Software: Custom E-Commerce, Apps & Crypto Development",
|
||||
description: "A Kennett Square, PA software studio building custom online stores, web apps, mobile games, and Monero & crypto tools.",
|
||||
images: ["/images/branding/OG_banner_v2_light.png"],
|
||||
},
|
||||
icons: {
|
||||
@@ -86,6 +86,14 @@ const structuredData = {
|
||||
],
|
||||
};
|
||||
|
||||
const websiteData = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
name: "Such Software",
|
||||
url: "https://such.software",
|
||||
publisher: { "@type": "Organization", name: "Such Software LLC" },
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
@@ -98,6 +106,10 @@ export default function RootLayout({
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteData) }}
|
||||
/>
|
||||
</head>
|
||||
<body className={inter.className}>
|
||||
<ThemeProvider
|
||||
|
||||
@@ -2,17 +2,18 @@ import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { StoreButtons } from "@/components/store-buttons";
|
||||
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Bauhaus Echo | Such Software",
|
||||
description: "Visual memory puzzle game inspired by Bauhaus design. Five modes including a pressure-free Zen sandbox. Android, iOS, Web, Desktop.",
|
||||
title: "Bauhaus Echo: Visual Memory Puzzle Game | Such Software",
|
||||
description: "A Bauhaus-inspired memory puzzle game with 5 modes and a pressure-free Zen sandbox. Play free on iOS, Android, web, and desktop.",
|
||||
alternates: { canonical: "/products/bauhaus-echo" },
|
||||
openGraph: {
|
||||
title: "Bauhaus Echo | Such Software",
|
||||
description: "Visual memory puzzle game inspired by Bauhaus design. Five modes including a pressure-free Zen sandbox. Android, iOS, Web, Desktop.",
|
||||
title: "Bauhaus Echo: Visual Memory Puzzle Game | Such Software",
|
||||
description: "A Bauhaus-inspired memory puzzle game with 5 modes and a pressure-free Zen sandbox. Play free on iOS, Android, web, and desktop.",
|
||||
type: "website",
|
||||
images: [{ url: "/images/products/bauhaus-echo.png", width: 512, height: 512, alt: "Bauhaus Echo" }],
|
||||
},
|
||||
@@ -29,6 +30,19 @@ const gameModes = [
|
||||
export default function BauhausEchoPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Bauhaus Echo", path: "/products/bauhaus-echo" },
|
||||
])} />
|
||||
<JsonLd data={softwareAppLd({
|
||||
name: "Bauhaus Echo",
|
||||
path: "/products/bauhaus-echo",
|
||||
description: "A Bauhaus-inspired memory puzzle game with 5 modes and a pressure-free Zen sandbox. Play free on iOS, Android, web, and desktop.",
|
||||
category: "GameApplication",
|
||||
operatingSystem: "Android, iOS, Web, Windows, macOS, Linux",
|
||||
price: "0",
|
||||
})} />
|
||||
<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">
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { Button } from "@repo/ui/components/button";
|
||||
import { JsonLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
@@ -19,6 +20,11 @@ export const metadata: Metadata = {
|
||||
export default function CustomWebsitesPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Custom Web Apps", path: "/products/custom-websites" },
|
||||
])} />
|
||||
<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">
|
||||
|
||||
@@ -3,17 +3,18 @@ import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { Button } from "@repo/ui/components/button";
|
||||
import Link from "next/link";
|
||||
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Neroswap — Aggregated DEX & CEX Orderbook Data | Such Software",
|
||||
title: "Neroswap: No-KYC Monero & Bitcoin Price Aggregator | Such Software",
|
||||
description:
|
||||
"Real-time aggregated orderbook data across decentralized and centralized exchanges. Compare Monero, Wownero, and Bitcoin prices across BasicSwap, Retoswap, Eigenwallet, Nonlogs, CexSwap, and AltQuick — no KYC, no custody.",
|
||||
"Compare live Monero, Wownero, and Bitcoin prices across 6 DEX and no-KYC CEX orderbooks in real time. No accounts, no custody, no tracking.",
|
||||
alternates: { canonical: "/products/neroswap" },
|
||||
openGraph: {
|
||||
title: "Neroswap — DEX & CEX Aggregator",
|
||||
title: "Neroswap: No-KYC Monero & Bitcoin Price Aggregator",
|
||||
description:
|
||||
"Compare crypto prices across 6 exchanges in real time. Privacy-focused, self-sovereign, no KYC.",
|
||||
"Compare live Monero, Wownero, and Bitcoin prices across 6 DEX and no-KYC CEX orderbooks in real time. No accounts, no custody, no tracking.",
|
||||
type: "website",
|
||||
url: "https://such.software/products/neroswap",
|
||||
},
|
||||
@@ -95,6 +96,20 @@ const features = [
|
||||
export default function NeroswapPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Neroswap", path: "/products/neroswap" },
|
||||
])} />
|
||||
<JsonLd data={softwareAppLd({
|
||||
name: "Neroswap",
|
||||
path: "/products/neroswap",
|
||||
description: "Compare live Monero, Wownero, and Bitcoin prices across 6 DEX and no-KYC CEX orderbooks in real time. No accounts, no custody, no tracking.",
|
||||
category: "FinanceApplication",
|
||||
operatingSystem: "Web browser",
|
||||
price: "0",
|
||||
offerUrl: "https://neroswap.com",
|
||||
})} />
|
||||
<Header />
|
||||
<div
|
||||
id="main-content"
|
||||
@@ -214,6 +229,13 @@ export default function NeroswapPage() {
|
||||
<Button variant="outline">Read the Blog Post</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground mt-12">
|
||||
Related:{" "}
|
||||
<Link href="/products/smirk-wallet" className="text-yellow-600 dark:text-yellow-400 underline">Smirk Wallet</Link>,{" "}
|
||||
<Link href="/products/wownerogue" className="text-yellow-600 dark:text-yellow-400 underline">Wownerogue</Link>, and our{" "}
|
||||
<Link href="/services" className="text-yellow-600 dark:text-yellow-400 underline">crypto consulting</Link>.
|
||||
</p>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
|
||||
@@ -11,8 +11,8 @@ export const metadata: Metadata = {
|
||||
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",
|
||||
description: "Tools and applications built by Such Software.",
|
||||
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.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
@@ -35,7 +35,7 @@ const products = [
|
||||
{
|
||||
slug: "webshops",
|
||||
title: "Webshops",
|
||||
description: "Headless Medusa commerce with revenue-share pricing. No upfront costs.",
|
||||
description: "Custom online stores on revenue-share pricing. No upfront cost. Built on headless Medusa.",
|
||||
color: "emerald",
|
||||
icon: "/images/products/occupy-wallets.png",
|
||||
},
|
||||
@@ -70,7 +70,7 @@ const products = [
|
||||
{
|
||||
slug: "vegan-iq",
|
||||
title: "Vegan IQ",
|
||||
description: "Test your plant-based knowledge with 2000+ trivia questions across 8 categories. Verified data, swipe gameplay. Android and iOS.",
|
||||
description: "Test your plant-based knowledge with 1,000+ trivia questions across 8 categories, plus a daily challenge and weekly quiz. Android and iOS.",
|
||||
color: "green",
|
||||
icon: "/images/products/vegan-iq.png",
|
||||
},
|
||||
|
||||
@@ -3,15 +3,16 @@ import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { Button } from "@repo/ui/components/button";
|
||||
import Link from "next/link";
|
||||
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Smirk Wallet | Such Software",
|
||||
description: "Non-custodial browser wallet for social tipping. Send BTC, LTC, XMR, WOW, and GRIN by Telegram or Discord username.",
|
||||
title: "Smirk Wallet: Non-Custodial Crypto Tipping by Username | Such Software",
|
||||
description: "Tip BTC, LTC, XMR, WOW, and GRIN on Telegram or Discord by username. A non-custodial browser wallet: your keys never leave your device.",
|
||||
alternates: { canonical: "/products/smirk-wallet" },
|
||||
openGraph: {
|
||||
title: "Smirk Wallet | Such Software",
|
||||
description: "Non-custodial browser wallet for social tipping. Send BTC, LTC, XMR, WOW, and GRIN by Telegram or Discord username.",
|
||||
title: "Smirk Wallet: Non-Custodial Crypto Tipping by Username",
|
||||
description: "Tip BTC, LTC, XMR, WOW, and GRIN on Telegram or Discord by username. A non-custodial browser wallet: your keys never leave your device.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
@@ -27,6 +28,20 @@ const currencies = [
|
||||
export default function SmirkWalletPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Smirk Wallet", path: "/products/smirk-wallet" },
|
||||
])} />
|
||||
<JsonLd data={softwareAppLd({
|
||||
name: "Smirk Wallet",
|
||||
path: "/products/smirk-wallet",
|
||||
description: "Tip BTC, LTC, XMR, WOW, and GRIN on Telegram or Discord by username. A non-custodial browser wallet: your keys never leave your device.",
|
||||
category: "FinanceApplication",
|
||||
operatingSystem: "Chrome, Firefox (browser extension)",
|
||||
price: "0",
|
||||
offerUrl: "https://smirk.cash",
|
||||
})} />
|
||||
<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">
|
||||
@@ -130,6 +145,13 @@ export default function SmirkWalletPage() {
|
||||
<Button variant="outline">GitHub</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground mt-12">
|
||||
Related:{" "}
|
||||
<Link href="/products/neroswap" className="text-amber-600 dark:text-amber-400 hover:underline">Neroswap</Link>,{" "}
|
||||
<Link href="/products/wownerogue" className="text-amber-600 dark:text-amber-400 hover:underline">Wownerogue</Link>, and{" "}
|
||||
<Link href="/products/such-moon-launch" className="text-amber-600 dark:text-amber-400 hover:underline">Such Moon Launch</Link>.
|
||||
</p>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
|
||||
@@ -2,16 +2,17 @@ import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { SuchMoonLaunchStoreButtons } from "@/components/store-buttons";
|
||||
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Such Moon Launch | Such Software",
|
||||
description: "Vry rocket. Much landing. Wow! A pixel-art rocket-flying arcade with 11 levels, 4 weapons, and an endless mode. Wownero-themed. Android, iOS, Web, Desktop.",
|
||||
title: "Such Moon Launch: Pixel Rocket Arcade Game | Such Software",
|
||||
description: "Fly a rocket across the solar system in this Wownero-themed pixel arcade. 11 levels, 4 weapons, tilt-to-steer, endless mode. Free on iOS, Android, web, desktop.",
|
||||
alternates: { canonical: "/products/such-moon-launch" },
|
||||
openGraph: {
|
||||
title: "Such Moon Launch | Such Software",
|
||||
description: "Vry rocket. Much landing. Wow! A pixel-art rocket-flying arcade with 11 levels, 4 weapons, and an endless mode. Wownero-themed.",
|
||||
title: "Such Moon Launch: Pixel Rocket Arcade Game | Such Software",
|
||||
description: "Fly a rocket across the solar system in this Wownero-themed pixel arcade. 11 levels, 4 weapons, tilt-to-steer, endless mode. Free on iOS, Android, web, desktop.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
@@ -41,6 +42,20 @@ const weapons = [
|
||||
export default function SuchMoonLaunchPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Such Moon Launch", path: "/products/such-moon-launch" },
|
||||
])} />
|
||||
<JsonLd data={softwareAppLd({
|
||||
name: "Such Moon Launch",
|
||||
path: "/products/such-moon-launch",
|
||||
description: "Fly a rocket across the solar system in this Wownero-themed pixel arcade. 11 levels, 4 weapons, tilt-to-steer, endless mode. Free on iOS, Android, web, desktop.",
|
||||
category: "GameApplication",
|
||||
operatingSystem: "Android, iOS, Web, Windows, macOS, Linux",
|
||||
price: "0",
|
||||
offerUrl: "https://moonlaunch.such.software",
|
||||
})} />
|
||||
<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">
|
||||
|
||||
@@ -2,17 +2,18 @@ import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { StoreButtons } from "@/components/store-buttons";
|
||||
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Suchoice | Such Software",
|
||||
description: "Six ways to decide: AI photo reader, spin the wheel, coin flip, 3D dice, head-to-head duel, and city explorer. Android and iOS.",
|
||||
title: "Suchoice: Spin the Wheel & AI Decision Maker App | Such Software",
|
||||
description: "Can't decide? Snap a photo and let AI read your options, spin the wheel, flip a coin, or roll 3D dice. The fun decision-maker app for iOS and Android.",
|
||||
alternates: { canonical: "/products/suchoice" },
|
||||
openGraph: {
|
||||
title: "Suchoice | Such Software",
|
||||
description: "Six ways to decide: AI photo reader, spin the wheel, coin flip, 3D dice, head-to-head duel, and city explorer. Android and iOS.",
|
||||
title: "Suchoice: Spin the Wheel & AI Decision Maker App | Such Software",
|
||||
description: "Can't decide? Snap a photo and let AI read your options, spin the wheel, flip a coin, or roll 3D dice. The fun decision-maker app for iOS and Android.",
|
||||
type: "website",
|
||||
images: [{ url: "/images/products/suchoice.png", width: 512, height: 512, alt: "Suchoice" }],
|
||||
},
|
||||
@@ -30,6 +31,19 @@ const features = [
|
||||
export default function SuchoicePage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Suchoice", path: "/products/suchoice" },
|
||||
])} />
|
||||
<JsonLd data={softwareAppLd({
|
||||
name: "Suchoice",
|
||||
path: "/products/suchoice",
|
||||
description: "Can't decide? Snap a photo and let AI read your options, spin the wheel, flip a coin, or roll 3D dice. The fun decision-maker app for iOS and Android.",
|
||||
category: "LifestyleApplication",
|
||||
operatingSystem: "Android, iOS",
|
||||
price: "0",
|
||||
})} />
|
||||
<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">
|
||||
|
||||
@@ -2,25 +2,28 @@ import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { StoreButtons } from "@/components/store-buttons";
|
||||
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Vegan IQ | Such Software",
|
||||
description: "Test your plant-based knowledge with 1000+ trivia questions across 8 categories. Swipe and tap gameplay with verified data. Android and iOS.",
|
||||
title: "Vegan IQ: Plant-Based Trivia Game | Such Software",
|
||||
description: "Test your plant-based knowledge with 1,000+ trivia questions across 8 categories, plus a daily challenge and weekly quiz. Free vegan trivia for iOS and Android.",
|
||||
alternates: { canonical: "/products/vegan-iq" },
|
||||
openGraph: {
|
||||
title: "Vegan IQ | Such Software",
|
||||
description: "Test your plant-based knowledge with 1000+ trivia questions across 8 categories. Swipe and tap gameplay with verified data. Android and iOS.",
|
||||
title: "Vegan IQ: Plant-Based Trivia Game | Such Software",
|
||||
description: "Test your plant-based knowledge with 1,000+ trivia questions across 8 categories, plus a daily challenge and weekly quiz. Free vegan trivia for iOS and Android.",
|
||||
type: "website",
|
||||
images: [{ url: "/images/products/vegan-iq.png", width: 512, height: 512, alt: "Vegan IQ" }],
|
||||
},
|
||||
};
|
||||
|
||||
const features = [
|
||||
{ name: "1000+ Questions", description: "Curated trivia questions backed by verified data from USDA, Poore & Nemecek, NIH, and FAOSTAT. No AI-generated content." },
|
||||
{ name: "1,000+ Questions", description: "Curated trivia questions backed by verified data from USDA, Poore & Nemecek, NIH, and FAOSTAT. No AI-generated content." },
|
||||
{ name: "8 Categories", description: "Nutrition, environment, ingredients, food history, animal welfare, health, cooking, and culture." },
|
||||
{ name: "Daily Challenge", description: "A fresh set of questions every day. Build a streak and keep your knowledge sharp." },
|
||||
{ name: "Weekly Quiz", description: "New themed quiz each week with fresh content to test how far you've come." },
|
||||
{ name: "Swipe & Tap", description: "Swipe right for true, left for false. Tap your answer for multiple choice. Fast, intuitive gameplay." },
|
||||
{ name: "Dark & Light Mode", description: "Play in whichever theme you prefer. Automatic or manual switching." },
|
||||
{ name: "SFX & Haptics", description: "Satisfying sound effects and haptic feedback on correct and incorrect answers." },
|
||||
@@ -30,6 +33,19 @@ const features = [
|
||||
export default function VeganIqPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Vegan IQ", path: "/products/vegan-iq" },
|
||||
])} />
|
||||
<JsonLd data={softwareAppLd({
|
||||
name: "Vegan IQ",
|
||||
path: "/products/vegan-iq",
|
||||
description: "Test your plant-based knowledge with 1,000+ trivia questions across 8 categories, plus a daily challenge and weekly quiz. Free vegan trivia for iOS and Android.",
|
||||
category: "GameApplication",
|
||||
operatingSystem: "Android, iOS",
|
||||
price: "0",
|
||||
})} />
|
||||
<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">
|
||||
@@ -51,7 +67,7 @@ export default function VeganIqPage() {
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none mb-12">
|
||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||
Think you know your vegan facts? Vegan IQ puts your plant-based knowledge to the test with over 1000 trivia questions across 8 categories.
|
||||
Think you know your vegan facts? Vegan IQ puts your plant-based knowledge to the test with over 1,000 trivia questions across 8 categories.
|
||||
Every question is backed by verified data from sources like the USDA, Poore & Nemecek (2018), NIH, and FAOSTAT.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -2,16 +2,17 @@ import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import { Button } from "@repo/ui/components/button";
|
||||
import { JsonLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Webshops | Such Software",
|
||||
description: "Headless Medusa commerce engines with revenue-share pricing. No upfront costs, aligned incentives.",
|
||||
title: "Custom Online Stores & E-Commerce Development | Such Software",
|
||||
description: "Custom online stores with no upfront cost and revenue-share pricing. Own your data, escape template limits, and hit 100/100 Lighthouse. Built on Medusa.",
|
||||
alternates: { canonical: "/products/webshops" },
|
||||
openGraph: {
|
||||
title: "Webshops | Such Software",
|
||||
description: "Headless Medusa commerce engines with revenue-share pricing. No upfront costs, aligned incentives.",
|
||||
title: "Custom Online Stores & E-Commerce Development | Such Software",
|
||||
description: "Custom online stores with no upfront cost and revenue-share pricing. Own your data and escape template limits. Built on Medusa.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
@@ -19,20 +20,27 @@ export const metadata: Metadata = {
|
||||
export default function WebshopsPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Custom Online Stores", path: "/products/webshops" },
|
||||
])} />
|
||||
<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-6 text-emerald-600 dark:text-emerald-400">
|
||||
Webshops
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-2 text-emerald-600 dark:text-emerald-400">
|
||||
Custom Online Stores
|
||||
</h1>
|
||||
<p className="text-base text-muted-foreground mb-6">Webshops built and run on revenue share.</p>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none mb-12">
|
||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||
We build headless commerce platforms on Medusa v2 with a revenue-share model.
|
||||
No massive setup fees, no monthly retainers. We only win when you make sales.
|
||||
We build custom online stores with a revenue-share model: no large setup fee, no monthly
|
||||
retainer. We only win when you make sales. Under the hood it is a headless Medusa v2
|
||||
backend with a Next.js storefront, so you own your data and your business logic.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -81,6 +89,12 @@ export default function WebshopsPage() {
|
||||
<Link href="/about#contact">
|
||||
<Button size="lg">Get in Touch</Button>
|
||||
</Link>
|
||||
<p className="text-sm text-muted-foreground mt-6">
|
||||
See also our{" "}
|
||||
<Link href="/services" className="text-emerald-600 dark:text-emerald-400 hover:underline">development services</Link>
|
||||
{" "}and{" "}
|
||||
<Link href="/products/custom-websites" className="text-emerald-600 dark:text-emerald-400 hover:underline">custom web apps</Link>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
|
||||
@@ -3,6 +3,7 @@ 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 { JsonLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -70,6 +71,11 @@ const steps = [
|
||||
export default function WowneroguePage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<JsonLd data={breadcrumbLd([
|
||||
{ name: "Home", path: "/" },
|
||||
{ name: "Products", path: "/products" },
|
||||
{ name: "Wownerogue", path: "/products/wownerogue" },
|
||||
])} />
|
||||
<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">
|
||||
|
||||
@@ -9,14 +9,14 @@ import { Card } from "@repo/ui/components/card";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Services | Such Software",
|
||||
description: "Medusa v2 development, custom web applications, and cryptocurrency consulting. High-performance headless commerce, real-time platforms, and Web3 integration.",
|
||||
title: "Medusa, Web App & Crypto Development Services | Such Software",
|
||||
description: "Custom e-commerce on Medusa v2, real-time web apps, and Monero/crypto consulting. A Kennett Square, PA studio building for scale, accessibility, and performance.",
|
||||
alternates: {
|
||||
canonical: "/services",
|
||||
},
|
||||
openGraph: {
|
||||
title: "Services | Such Software",
|
||||
description: "Medusa v2 development, custom web applications, and cryptocurrency consulting.",
|
||||
title: "Medusa, Web App & Crypto Development Services | Such Software",
|
||||
description: "Custom e-commerce on Medusa v2, real-time web apps, and Monero/crypto consulting. A Kennett Square, PA studio building for scale, accessibility, and performance.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5,12 +5,12 @@ import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Support | Such Software",
|
||||
description: "Get help with Bauhaus Echo or any Such Software product. Report bugs, request features, or send feedback.",
|
||||
title: "Support & Feedback | Such Software",
|
||||
description: "Get help with any Such Software app or game. Report a bug, request a feature, or send feedback. We read every message.",
|
||||
alternates: { canonical: "/support" },
|
||||
openGraph: {
|
||||
title: "Support | Such Software",
|
||||
description: "Get help with Bauhaus Echo or any Such Software product. Report bugs, request features, or send feedback.",
|
||||
title: "Support & Feedback | Such Software",
|
||||
description: "Get help with any Such Software app or game. Report a bug, request a feature, or send feedback. We read every message.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
@@ -39,13 +39,13 @@ export default function SupportPage() {
|
||||
<div className="section-container border-l-4 border-emerald-500">
|
||||
<h3 className="font-bold mb-2">Feature Requests</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Have an idea to make the game better? We're always looking for ways to improve.
|
||||
Have an idea to make your app or game better? We're always looking for ways to improve.
|
||||
</p>
|
||||
</div>
|
||||
<div className="section-container border-l-4 border-amber-500">
|
||||
<h3 className="font-bold mb-2">General Feedback</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enjoying the game? Hate something? All feedback is welcome and helps us prioritize.
|
||||
Enjoying one of our apps? Hate something? All feedback is welcome and helps us prioritize.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* JSON-LD helpers. `JsonLd` renders a structured-data <script>; the builders return
|
||||
* schema.org objects so pages can stay declarative. Server components only (no client JS).
|
||||
*/
|
||||
const SITE = "https://such.software";
|
||||
|
||||
export function JsonLd({ data }: { data: object }) {
|
||||
return (
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/** Breadcrumb trail for a deep page, e.g. Home › Products › Neroswap. */
|
||||
export function breadcrumbLd(items: { name: string; path: string }[]) {
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
itemListElement: items.map((it, i) => ({
|
||||
"@type": "ListItem",
|
||||
position: i + 1,
|
||||
name: it.name,
|
||||
item: `${SITE}${it.path}`,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
type SoftwareAppOpts = {
|
||||
name: string;
|
||||
path: string;
|
||||
description: string;
|
||||
/** schema.org applicationCategory, e.g. "GameApplication", "FinanceApplication". */
|
||||
category: string;
|
||||
operatingSystem: string;
|
||||
/** Price as a string; "0" for free. */
|
||||
price?: string;
|
||||
/** External play/download URL the offer points at (defaults to the on-site path). */
|
||||
offerUrl?: string;
|
||||
/** Optional store rating, rendered as AggregateRating when provided. */
|
||||
rating?: { value: string; count: string };
|
||||
};
|
||||
|
||||
/** SoftwareApplication node: drives app/game rich results (and star ratings when rated). */
|
||||
export function softwareAppLd(opts: SoftwareAppOpts) {
|
||||
const data: Record<string, unknown> = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
name: opts.name,
|
||||
url: `${SITE}${opts.path}`,
|
||||
description: opts.description,
|
||||
applicationCategory: opts.category,
|
||||
operatingSystem: opts.operatingSystem,
|
||||
offers: {
|
||||
"@type": "Offer",
|
||||
price: opts.price ?? "0",
|
||||
priceCurrency: "USD",
|
||||
url: opts.offerUrl ?? `${SITE}${opts.path}`,
|
||||
},
|
||||
publisher: { "@type": "Organization", name: "Such Software" },
|
||||
};
|
||||
if (opts.rating) {
|
||||
data.aggregateRating = {
|
||||
"@type": "AggregateRating",
|
||||
ratingValue: opts.rating.value,
|
||||
ratingCount: opts.rating.count,
|
||||
};
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/** BlogPosting node for an article page. */
|
||||
export function articleLd(opts: { title: string; description: string; slug: string; date: string }) {
|
||||
const url = `${SITE}/blog/${opts.slug}`;
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
headline: opts.title,
|
||||
description: opts.description,
|
||||
url,
|
||||
mainEntityOfPage: url,
|
||||
datePublished: opts.date,
|
||||
dateModified: opts.date,
|
||||
author: { "@type": "Organization", name: "Such Software" },
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Such Software",
|
||||
logo: { "@type": "ImageObject", url: `${SITE}/images/branding/clean_dark_w_text.svg` },
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user