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";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "About | Such Software",
|
title: "About: A Kennett Square, PA Software Studio | Such Software",
|
||||||
description: "Software studio in Kennett Square, PA. Founded by John Winter Murphy, specializing in accessible web engineering and WCAG 2.2 compliance.",
|
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: {
|
alternates: {
|
||||||
canonical: "/about",
|
canonical: "/about",
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "About | Such Software",
|
title: "About: A Kennett Square, PA Software Studio | Such Software",
|
||||||
description: "Software studio in Kennett Square, PA. Founded by John Winter Murphy, specializing in accessible web engineering and WCAG 2.2 compliance.",
|
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",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import Link from "next/link";
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Apps | Such Software",
|
title: "Our Apps: Games & Tools for iOS, Android & Web | Such Software",
|
||||||
description: "Download our apps on App Store and Google Play.",
|
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" },
|
alternates: { canonical: "/apps" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Such Software Apps",
|
title: "Our Apps: Games & Tools for iOS, Android & Web | Such Software",
|
||||||
description: "Download our apps on App Store and Google Play.",
|
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",
|
type: "website",
|
||||||
images: [{ url: "/images/branding/OG_banner_v2_light.png", width: 1200, height: 630, alt: "Such Software" }],
|
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 { notFound } from "next/navigation";
|
||||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||||
import { getAllSlugs, getPost } from "@/lib/blog";
|
import { getAllSlugs, getPost } from "@/lib/blog";
|
||||||
|
import { JsonLd, articleLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||||
|
|
||||||
type Props = { params: Promise<{ slug: string }> };
|
type Props = { params: Promise<{ slug: string }> };
|
||||||
|
|
||||||
@@ -50,6 +51,12 @@ export default async function BlogPost({ params }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<article id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<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";
|
import { getAllPosts } from "@/lib/blog";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Blog | Such Software",
|
title: "Blog: Build Notes on Crypto, Commerce & Apps | Such Software",
|
||||||
description: "News, updates, and technical articles from 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" },
|
alternates: { canonical: "/blog" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Blog | Such Software",
|
title: "Blog: Build Notes on Crypto, Commerce & Apps | Such Software",
|
||||||
description: "News, updates, and technical articles from 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",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ const inter = Inter({ subsets: ["latin"] });
|
|||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
metadataBase: new URL("https://such.software"),
|
metadataBase: new URL("https://such.software"),
|
||||||
title: "Such Software | Precision Engineering for Everyone",
|
title: "Custom E-Commerce, App & Crypto Development | Such Software",
|
||||||
description: "E-commerce infrastructure, custom app development, and accessible web applications. Engineered for scale, compliance, and performance.",
|
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: {
|
alternates: {
|
||||||
canonical: "/",
|
canonical: "/",
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Such Software",
|
title: "Such Software: Custom E-Commerce, Apps & Crypto Development",
|
||||||
description: "E-commerce infrastructure, custom app development, and accessible web applications.",
|
description: "A Kennett Square, PA software studio building custom online stores, web apps, mobile games, and Monero & crypto tools.",
|
||||||
type: "website",
|
type: "website",
|
||||||
siteName: "Such Software",
|
siteName: "Such Software",
|
||||||
images: [
|
images: [
|
||||||
@@ -30,8 +30,8 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: "summary_large_image",
|
card: "summary_large_image",
|
||||||
title: "Such Software",
|
title: "Such Software: Custom E-Commerce, Apps & Crypto Development",
|
||||||
description: "E-commerce infrastructure, custom app development, and accessible web applications.",
|
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"],
|
images: ["/images/branding/OG_banner_v2_light.png"],
|
||||||
},
|
},
|
||||||
icons: {
|
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({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
@@ -98,6 +106,10 @@ export default function RootLayout({
|
|||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
|
||||||
/>
|
/>
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteData) }}
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body className={inter.className}>
|
<body className={inter.className}>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
|
|||||||
@@ -2,17 +2,18 @@ import { Header } from "@/components/layout/header";
|
|||||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||||
import { Card } from "@repo/ui/components/card";
|
import { Card } from "@repo/ui/components/card";
|
||||||
import { StoreButtons } from "@/components/store-buttons";
|
import { StoreButtons } from "@/components/store-buttons";
|
||||||
|
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Bauhaus Echo | Such Software",
|
title: "Bauhaus Echo: Visual Memory Puzzle Game | Such Software",
|
||||||
description: "Visual memory puzzle game inspired by Bauhaus design. Five modes including a pressure-free Zen sandbox. Android, iOS, Web, Desktop.",
|
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" },
|
alternates: { canonical: "/products/bauhaus-echo" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Bauhaus Echo | Such Software",
|
title: "Bauhaus Echo: Visual Memory Puzzle Game | Such Software",
|
||||||
description: "Visual memory puzzle game inspired by Bauhaus design. Five modes including a pressure-free Zen sandbox. Android, iOS, Web, Desktop.",
|
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",
|
type: "website",
|
||||||
images: [{ url: "/images/products/bauhaus-echo.png", width: 512, height: 512, alt: "Bauhaus Echo" }],
|
images: [{ url: "/images/products/bauhaus-echo.png", width: 512, height: 512, alt: "Bauhaus Echo" }],
|
||||||
},
|
},
|
||||||
@@ -29,6 +30,19 @@ const gameModes = [
|
|||||||
export default function BauhausEchoPage() {
|
export default function BauhausEchoPage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<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 { MobileNav } from "@/components/layout/mobile-nav";
|
||||||
import { Card } from "@repo/ui/components/card";
|
import { Card } from "@repo/ui/components/card";
|
||||||
import { Button } from "@repo/ui/components/button";
|
import { Button } from "@repo/ui/components/button";
|
||||||
|
import { JsonLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
@@ -19,6 +20,11 @@ export const metadata: Metadata = {
|
|||||||
export default function CustomWebsitesPage() {
|
export default function CustomWebsitesPage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<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 { Card } from "@repo/ui/components/card";
|
||||||
import { Button } from "@repo/ui/components/button";
|
import { Button } from "@repo/ui/components/button";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Neroswap — Aggregated DEX & CEX Orderbook Data | Such Software",
|
title: "Neroswap: No-KYC Monero & Bitcoin Price Aggregator | Such Software",
|
||||||
description:
|
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" },
|
alternates: { canonical: "/products/neroswap" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Neroswap — DEX & CEX Aggregator",
|
title: "Neroswap: No-KYC Monero & Bitcoin Price Aggregator",
|
||||||
description:
|
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",
|
type: "website",
|
||||||
url: "https://such.software/products/neroswap",
|
url: "https://such.software/products/neroswap",
|
||||||
},
|
},
|
||||||
@@ -95,6 +96,20 @@ const features = [
|
|||||||
export default function NeroswapPage() {
|
export default function NeroswapPage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div
|
<div
|
||||||
id="main-content"
|
id="main-content"
|
||||||
@@ -214,6 +229,13 @@ export default function NeroswapPage() {
|
|||||||
<Button variant="outline">Read the Blog Post</Button>
|
<Button variant="outline">Read the Blog Post</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<MobileNav />
|
<MobileNav />
|
||||||
</main>
|
</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.",
|
description: "Explore Such Software's products: Wownerogue Monero roguelike, Neroswap crypto aggregator, Smirk tipping wallet, headless Webshops, and our mobile games.",
|
||||||
alternates: { canonical: "/products" },
|
alternates: { canonical: "/products" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Products | Such Software",
|
title: "Our Products & Apps: Crypto Tools, Games & Commerce | Such Software",
|
||||||
description: "Tools and applications built by 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",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -35,7 +35,7 @@ const products = [
|
|||||||
{
|
{
|
||||||
slug: "webshops",
|
slug: "webshops",
|
||||||
title: "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",
|
color: "emerald",
|
||||||
icon: "/images/products/occupy-wallets.png",
|
icon: "/images/products/occupy-wallets.png",
|
||||||
},
|
},
|
||||||
@@ -70,7 +70,7 @@ const products = [
|
|||||||
{
|
{
|
||||||
slug: "vegan-iq",
|
slug: "vegan-iq",
|
||||||
title: "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",
|
color: "green",
|
||||||
icon: "/images/products/vegan-iq.png",
|
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 { Card } from "@repo/ui/components/card";
|
||||||
import { Button } from "@repo/ui/components/button";
|
import { Button } from "@repo/ui/components/button";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Smirk Wallet | Such Software",
|
title: "Smirk Wallet: Non-Custodial Crypto Tipping by Username | Such Software",
|
||||||
description: "Non-custodial browser wallet for social tipping. Send BTC, LTC, XMR, WOW, and GRIN by Telegram or Discord 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.",
|
||||||
alternates: { canonical: "/products/smirk-wallet" },
|
alternates: { canonical: "/products/smirk-wallet" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Smirk Wallet | Such Software",
|
title: "Smirk Wallet: Non-Custodial Crypto Tipping by Username",
|
||||||
description: "Non-custodial browser wallet for social tipping. Send BTC, LTC, XMR, WOW, and GRIN by Telegram or Discord 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",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -27,6 +28,20 @@ const currencies = [
|
|||||||
export default function SmirkWalletPage() {
|
export default function SmirkWalletPage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<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>
|
<Button variant="outline">GitHub</Button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<MobileNav />
|
<MobileNav />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ import { Header } from "@/components/layout/header";
|
|||||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||||
import { Card } from "@repo/ui/components/card";
|
import { Card } from "@repo/ui/components/card";
|
||||||
import { SuchMoonLaunchStoreButtons } from "@/components/store-buttons";
|
import { SuchMoonLaunchStoreButtons } from "@/components/store-buttons";
|
||||||
|
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Such Moon Launch | Such Software",
|
title: "Such Moon Launch: Pixel Rocket Arcade Game | 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.",
|
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" },
|
alternates: { canonical: "/products/such-moon-launch" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Such Moon Launch | Such Software",
|
title: "Such Moon Launch: Pixel Rocket Arcade Game | 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.",
|
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",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -41,6 +42,20 @@ const weapons = [
|
|||||||
export default function SuchMoonLaunchPage() {
|
export default function SuchMoonLaunchPage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<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 { MobileNav } from "@/components/layout/mobile-nav";
|
||||||
import { Card } from "@repo/ui/components/card";
|
import { Card } from "@repo/ui/components/card";
|
||||||
import { StoreButtons } from "@/components/store-buttons";
|
import { StoreButtons } from "@/components/store-buttons";
|
||||||
|
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Suchoice | Such Software",
|
title: "Suchoice: Spin the Wheel & AI Decision Maker App | 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.",
|
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" },
|
alternates: { canonical: "/products/suchoice" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Suchoice | Such Software",
|
title: "Suchoice: Spin the Wheel & AI Decision Maker App | 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.",
|
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",
|
type: "website",
|
||||||
images: [{ url: "/images/products/suchoice.png", width: 512, height: 512, alt: "Suchoice" }],
|
images: [{ url: "/images/products/suchoice.png", width: 512, height: 512, alt: "Suchoice" }],
|
||||||
},
|
},
|
||||||
@@ -30,6 +31,19 @@ const features = [
|
|||||||
export default function SuchoicePage() {
|
export default function SuchoicePage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<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 { MobileNav } from "@/components/layout/mobile-nav";
|
||||||
import { Card } from "@repo/ui/components/card";
|
import { Card } from "@repo/ui/components/card";
|
||||||
import { StoreButtons } from "@/components/store-buttons";
|
import { StoreButtons } from "@/components/store-buttons";
|
||||||
|
import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Vegan IQ | Such Software",
|
title: "Vegan IQ: Plant-Based Trivia Game | 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.",
|
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" },
|
alternates: { canonical: "/products/vegan-iq" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Vegan IQ | Such Software",
|
title: "Vegan IQ: Plant-Based Trivia Game | 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.",
|
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",
|
type: "website",
|
||||||
images: [{ url: "/images/products/vegan-iq.png", width: 512, height: 512, alt: "Vegan IQ" }],
|
images: [{ url: "/images/products/vegan-iq.png", width: 512, height: 512, alt: "Vegan IQ" }],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const features = [
|
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: "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: "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: "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." },
|
{ 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() {
|
export default function VeganIqPage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<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">
|
<div className="prose dark:prose-invert max-w-none mb-12">
|
||||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
<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.
|
Every question is backed by verified data from sources like the USDA, Poore & Nemecek (2018), NIH, and FAOSTAT.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ import { Header } from "@/components/layout/header";
|
|||||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||||
import { Card } from "@repo/ui/components/card";
|
import { Card } from "@repo/ui/components/card";
|
||||||
import { Button } from "@repo/ui/components/button";
|
import { Button } from "@repo/ui/components/button";
|
||||||
|
import { JsonLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Webshops | Such Software",
|
title: "Custom Online Stores & E-Commerce Development | Such Software",
|
||||||
description: "Headless Medusa commerce engines with revenue-share pricing. No upfront costs, aligned incentives.",
|
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" },
|
alternates: { canonical: "/products/webshops" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Webshops | Such Software",
|
title: "Custom Online Stores & E-Commerce Development | Such Software",
|
||||||
description: "Headless Medusa commerce engines with revenue-share pricing. No upfront costs, aligned incentives.",
|
description: "Custom online stores with no upfront cost and revenue-share pricing. Own your data and escape template limits. Built on Medusa.",
|
||||||
type: "website",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -19,20 +20,27 @@ export const metadata: Metadata = {
|
|||||||
export default function WebshopsPage() {
|
export default function WebshopsPage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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 />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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">
|
<Link href="/products" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">
|
||||||
← Back to Products
|
← Back to Products
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<h1 className="text-4xl md:text-5xl font-bold mb-6 text-emerald-600 dark:text-emerald-400">
|
<h1 className="text-4xl md:text-5xl font-bold mb-2 text-emerald-600 dark:text-emerald-400">
|
||||||
Webshops
|
Custom Online Stores
|
||||||
</h1>
|
</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">
|
<div className="prose dark:prose-invert max-w-none mb-12">
|
||||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||||
We build headless commerce platforms on Medusa v2 with a revenue-share model.
|
We build custom online stores with a revenue-share model: no large setup fee, no monthly
|
||||||
No massive setup fees, no monthly retainers. We only win when you make sales.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -81,6 +89,12 @@ export default function WebshopsPage() {
|
|||||||
<Link href="/about#contact">
|
<Link href="/about#contact">
|
||||||
<Button size="lg">Get in Touch</Button>
|
<Button size="lg">Get in Touch</Button>
|
||||||
</Link>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
<MobileNav />
|
<MobileNav />
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { MobileNav } from "@/components/layout/mobile-nav";
|
|||||||
import { Card } from "@repo/ui/components/card";
|
import { Card } from "@repo/ui/components/card";
|
||||||
import { WownerogueLinks } from "@/components/store-buttons";
|
import { WownerogueLinks } from "@/components/store-buttons";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { JsonLd, breadcrumbLd } from "@/components/seo/json-ld";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -70,6 +71,11 @@ const steps = [
|
|||||||
export default function WowneroguePage() {
|
export default function WowneroguePage() {
|
||||||
return (
|
return (
|
||||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
<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) }} />
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
||||||
<Header />
|
<Header />
|
||||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
<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";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Services | Such Software",
|
title: "Medusa, Web App & Crypto Development Services | Such Software",
|
||||||
description: "Medusa v2 development, custom web applications, and cryptocurrency consulting. High-performance headless commerce, real-time platforms, and Web3 integration.",
|
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: {
|
alternates: {
|
||||||
canonical: "/services",
|
canonical: "/services",
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Services | Such Software",
|
title: "Medusa, Web App & Crypto Development Services | Such Software",
|
||||||
description: "Medusa v2 development, custom web applications, and cryptocurrency consulting.",
|
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",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import Link from "next/link";
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Support | Such Software",
|
title: "Support & Feedback | Such Software",
|
||||||
description: "Get help with Bauhaus Echo or any Such Software product. Report bugs, request features, or send feedback.",
|
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" },
|
alternates: { canonical: "/support" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Support | Such Software",
|
title: "Support & Feedback | Such Software",
|
||||||
description: "Get help with Bauhaus Echo or any Such Software product. Report bugs, request features, or send feedback.",
|
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",
|
type: "website",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -39,13 +39,13 @@ export default function SupportPage() {
|
|||||||
<div className="section-container border-l-4 border-emerald-500">
|
<div className="section-container border-l-4 border-emerald-500">
|
||||||
<h3 className="font-bold mb-2">Feature Requests</h3>
|
<h3 className="font-bold mb-2">Feature Requests</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="section-container border-l-4 border-amber-500">
|
<div className="section-container border-l-4 border-amber-500">
|
||||||
<h3 className="font-bold mb-2">General Feedback</h3>
|
<h3 className="font-bold mb-2">General Feedback</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</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