mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-19 05:48:33 +02:00
Add blog and products pages (including bauhaus privacy policy)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { FounderSignature } from "@/components/about/founder-signature";
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { FaMapMarkerAlt, FaPhone, FaBuilding, FaExternalLinkAlt } from "react-icons/fa";
|
||||
import { FaXTwitter, FaTelegram, FaDiscord } from "react-icons/fa6";
|
||||
import type { Metadata } from "next";
|
||||
@@ -22,7 +23,7 @@ export default function AboutPage() {
|
||||
<main className="relative flex flex-col items-center overflow-hidden">
|
||||
<Header />
|
||||
|
||||
<div id="main-content" className="z-10 w-full max-w-6xl mx-auto py-20 px-4">
|
||||
<div id="main-content" className="z-10 w-full max-w-6xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-10 text-center">About Us</h1>
|
||||
|
||||
<div className="section-container">
|
||||
@@ -95,6 +96,7 @@ export default function AboutPage() {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Bauhaus Echo is Live | Such Software Blog",
|
||||
description: "Our first mobile game is available on Android and iOS. A visual memory puzzle inspired by Bauhaus design.",
|
||||
alternates: { canonical: "/blog/bauhaus-echo-launch" },
|
||||
};
|
||||
|
||||
export default function BauhausEchoLaunchPost() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<article id="main-content" className="z-10 w-full max-w-3xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<Link href="/blog" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">
|
||||
← Back to Blog
|
||||
</Link>
|
||||
|
||||
<header className="mb-12">
|
||||
<time className="text-sm text-muted-foreground">February 6, 2026</time>
|
||||
<h1 className="text-3xl md:text-4xl font-bold mt-2">Bauhaus Echo is Live</h1>
|
||||
</header>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none">
|
||||
<p>
|
||||
Bauhaus Echo is now available on Google Play and the App Store. It is a visual memory game
|
||||
where you memorize tile arrangements, watch them shuffle, and reconstruct the original pattern.
|
||||
</p>
|
||||
|
||||
<h2>The Concept</h2>
|
||||
<p>
|
||||
The game shows you a grid of colorful geometric tiles inspired by Bauhaus design. You get a
|
||||
few seconds to memorize their positions. Then the tiles spin, shrink, and scatter to the
|
||||
bottom of the screen. Your job is to drag them back to where they belong.
|
||||
</p>
|
||||
|
||||
<h2>Four Game Modes</h2>
|
||||
<ul>
|
||||
<li><strong>Classic</strong> - The core experience. Memorize and reconstruct.</li>
|
||||
<li><strong>Speed</strong> - Tiles appear one at a time. Place each before the next drops.</li>
|
||||
<li><strong>Endless</strong> - Speed mode that loops forever. See how long you last.</li>
|
||||
<li><strong>Memory</strong> - Traditional card-matching pairs.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Tech Stack</h2>
|
||||
<p>
|
||||
Built with libGDX, a cross-platform Java game framework. The same codebase runs on Android,
|
||||
iOS (via RoboVM), desktop, and web (via GWT). The tile assets are packed into a TextureAtlas
|
||||
for efficient rendering.
|
||||
</p>
|
||||
|
||||
<h2>Download</h2>
|
||||
<p>
|
||||
Get it on{" "}
|
||||
<a href="https://play.google.com/store/apps/details?id=com.suchsoftware.bauhausecho" target="_blank" rel="noopener noreferrer">
|
||||
Google Play
|
||||
</a>{" "}
|
||||
or the{" "}
|
||||
<a href="https://apps.apple.com/app/bauhaus-echo" target="_blank" rel="noopener noreferrer">
|
||||
App Store
|
||||
</a>
|
||||
. Free with ads, or pay once to remove them.
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Blog | Such Software",
|
||||
description: "News, updates, and technical articles from Such Software.",
|
||||
alternates: { canonical: "/blog" },
|
||||
};
|
||||
|
||||
// For now, hardcoded posts. Can migrate to MDX/CMS later.
|
||||
const posts = [
|
||||
{
|
||||
slug: "smirk-wallet-release",
|
||||
title: "Smirk Wallet v0.1.6",
|
||||
date: "2026-02-06",
|
||||
excerpt: "Non-custodial browser extension for social tipping with BTC, LTC, XMR, WOW, and GRIN. Now on GitHub, Chrome Web Store pending.",
|
||||
draft: false,
|
||||
},
|
||||
{
|
||||
slug: "website-relaunch-2026",
|
||||
title: "Website Relaunch",
|
||||
date: "2026-02-06",
|
||||
excerpt: "We rebuilt such.software from the ground up. Here is what we used and why.",
|
||||
draft: false,
|
||||
},
|
||||
{
|
||||
slug: "bauhaus-echo-launch",
|
||||
title: "Bauhaus Echo is Live",
|
||||
date: "2026-02-06",
|
||||
excerpt: "Our first mobile game is available on Android and iOS. A visual memory puzzle inspired by Bauhaus design.",
|
||||
draft: true,
|
||||
},
|
||||
];
|
||||
|
||||
const publishedPosts = posts.filter((p) => !p.draft);
|
||||
|
||||
export default function BlogPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-4xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-6">Blog</h1>
|
||||
<p className="text-xl text-muted-foreground mb-12">
|
||||
News, launches, and technical notes.
|
||||
</p>
|
||||
|
||||
{publishedPosts.length === 0 ? (
|
||||
<p className="text-muted-foreground">No posts yet. Check back soon.</p>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
{publishedPosts.map((post) => (
|
||||
<Link key={post.slug} href={`/blog/${post.slug}`}>
|
||||
<Card className="glass-card hover:border-primary/30 transition-colors cursor-pointer">
|
||||
<time className="text-sm text-muted-foreground">{post.date}</time>
|
||||
<h2 className="text-2xl font-bold mt-1 mb-2">{post.title}</h2>
|
||||
<p className="text-muted-foreground">{post.excerpt}</p>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Smirk Wallet v0.1.6 | Such Software Blog",
|
||||
description: "Announcing Smirk Wallet, a non-custodial browser extension for social tipping with BTC, LTC, XMR, WOW, and GRIN.",
|
||||
alternates: { canonical: "/blog/smirk-wallet-release" },
|
||||
};
|
||||
|
||||
export default function SmirkWalletReleasePost() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<article id="main-content" className="z-10 w-full max-w-3xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<Link href="/blog" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">
|
||||
← Back to Blog
|
||||
</Link>
|
||||
|
||||
<header className="mb-12">
|
||||
<time className="text-sm text-muted-foreground">February 6, 2026</time>
|
||||
<h1 className="text-3xl md:text-4xl font-bold mt-2">Smirk Wallet v0.1.6</h1>
|
||||
</header>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none">
|
||||
<p>
|
||||
Smirk Wallet is a non-custodial browser extension that lets you tip people cryptocurrency
|
||||
by their Telegram or Discord username. We have shipped six releases and the extension is
|
||||
available now on GitHub.
|
||||
</p>
|
||||
|
||||
<h2>What It Does</h2>
|
||||
<p>
|
||||
One 12-word seed phrase gives you addresses for five currencies: Bitcoin, Litecoin, Monero,
|
||||
Wownero, and Grin. Link your Telegram or Discord account on smirk.cash, and anyone running
|
||||
Smirk can send you crypto by username.
|
||||
</p>
|
||||
<p>
|
||||
Tips are encrypted with the recipient's public key. Only they can claim. If a tip goes
|
||||
unclaimed, the sender can claw it back. No funds get lost.
|
||||
</p>
|
||||
|
||||
<h2>Non-Custodial by Design</h2>
|
||||
<p>
|
||||
Your private spend keys are generated locally and never leave your browser. They are
|
||||
encrypted with your password. We store only public keys and view keys (for Monero/Wownero
|
||||
balance scanning). We cannot move your funds.
|
||||
</p>
|
||||
|
||||
<h2>What We Built</h2>
|
||||
<ul>
|
||||
<li>Browser extension (TypeScript, Preact, Vite)</li>
|
||||
<li>Backend API (Rust, Axum, PostgreSQL)</li>
|
||||
<li>Light wallet servers for XMR and WOW</li>
|
||||
<li>Grin wallet with client-side WASM</li>
|
||||
<li>Telegram and Discord bots for tip notifications</li>
|
||||
<li>User dashboard at smirk.cash</li>
|
||||
</ul>
|
||||
|
||||
<h2>Chrome Web Store Status</h2>
|
||||
<p>
|
||||
Our initial submission was rejected for requesting an unused permission (activeTab).
|
||||
We had included it for a planned feature that was not yet implemented. The fix is already
|
||||
in v0.1.6. We have removed the permission and resubmitted.
|
||||
</p>
|
||||
<p>
|
||||
In the meantime, you can install manually from{" "}
|
||||
<a href="https://github.com/Such-Software/smirk-extension/releases" target="_blank" rel="noopener noreferrer">
|
||||
GitHub Releases
|
||||
</a>
|
||||
. Firefox support is also available.
|
||||
</p>
|
||||
|
||||
<h2>Open Source</h2>
|
||||
<p>
|
||||
Everything is open source. The extension, backend, and WASM libraries are all on GitHub:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/Such-Software/smirk-extension" target="_blank" rel="noopener noreferrer">
|
||||
smirk-extension
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Such-Software/monero-oxide" target="_blank" rel="noopener noreferrer">
|
||||
monero-oxide
|
||||
</a>{" "}
|
||||
(Monero WASM)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Get Started</h2>
|
||||
<p>
|
||||
Visit{" "}
|
||||
<a href="https://smirk.cash" target="_blank" rel="noopener noreferrer">
|
||||
smirk.cash
|
||||
</a>{" "}
|
||||
to learn more, or join the community on{" "}
|
||||
<a href="https://t.me/smirkwallet" target="_blank" rel="noopener noreferrer">
|
||||
Telegram
|
||||
</a>{" "}
|
||||
or{" "}
|
||||
<a href="https://discord.gg/7EnsaWTm6C" target="_blank" rel="noopener noreferrer">
|
||||
Discord
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Website Relaunch | Such Software Blog",
|
||||
description: "We rebuilt such.software from the ground up. Here is what we used and why.",
|
||||
alternates: { canonical: "/blog/website-relaunch-2026" },
|
||||
};
|
||||
|
||||
export default function WebsiteRelaunchPost() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<article id="main-content" className="z-10 w-full max-w-3xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<Link href="/blog" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">
|
||||
← Back to Blog
|
||||
</Link>
|
||||
|
||||
<header className="mb-12">
|
||||
<time className="text-sm text-muted-foreground">February 6, 2026</time>
|
||||
<h1 className="text-3xl md:text-4xl font-bold mt-2">Website Relaunch</h1>
|
||||
</header>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none">
|
||||
<p>
|
||||
We rebuilt such.software. New design, new infrastructure, same focus on performance and accessibility.
|
||||
</p>
|
||||
|
||||
<h2>Stack</h2>
|
||||
<ul>
|
||||
<li><strong>Framework:</strong> Next.js 15 (App Router)</li>
|
||||
<li><strong>Language:</strong> TypeScript</li>
|
||||
<li><strong>Styling:</strong> Tailwind CSS with CSS variables for theming</li>
|
||||
<li><strong>Animation:</strong> Framer Motion</li>
|
||||
<li><strong>Monorepo:</strong> Turborepo + pnpm workspaces</li>
|
||||
<li><strong>Testing:</strong> Playwright with axe-core for accessibility</li>
|
||||
<li><strong>Bot Protection:</strong> Cloudflare Turnstile</li>
|
||||
<li><strong>Email:</strong> Resend</li>
|
||||
</ul>
|
||||
|
||||
<h2>Procedural Graphics</h2>
|
||||
<p>
|
||||
The homepage features two custom procedural animations: NebulaField and DataGrid. Both are
|
||||
SVG-based, theme-aware, and respect prefers-reduced-motion. NebulaField renders animated
|
||||
data streams flowing toward a central icon using quadratic Bezier curves. DataGrid draws
|
||||
a pulsing grid with traveling data packets.
|
||||
</p>
|
||||
|
||||
<h2>Accessibility</h2>
|
||||
<p>
|
||||
We run 42 automated accessibility tests across Chromium, Firefox, and WebKit on every build.
|
||||
Tests cover WCAG 2.2 AA compliance, heading hierarchy, color contrast, keyboard navigation,
|
||||
and form accessibility. Lighthouse scores are 99-100 across all pages.
|
||||
</p>
|
||||
|
||||
<h2>Performance</h2>
|
||||
<p>
|
||||
The NebulaField component is lazy loaded with next/dynamic and requestIdleCallback. Canvas
|
||||
rendering is throttled. Fonts are loaded via next/font for zero layout shift. Images use
|
||||
next/image for automatic optimization.
|
||||
</p>
|
||||
|
||||
<h2>Structure</h2>
|
||||
<p>
|
||||
The site now has Products, Services, Blog, and About sections. Products showcases our
|
||||
standalone offerings: Webshops, Custom Websites, Smirk Wallet, and Bauhaus Echo. Services
|
||||
covers our partnership model and consulting work. Mobile users get a bottom navigation bar.
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { ContactForm } from "@/components/contact/contact-form";
|
||||
import { FounderSignature } from "@/components/about/founder-signature";
|
||||
import { ServicesPreview } from "@/components/landing/services-preview";
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { DynamicBackground } from "@/components/landing/dynamic-background";
|
||||
import Link from "next/link";
|
||||
import { Button } from "@repo/ui/components/button";
|
||||
@@ -103,7 +104,7 @@ export default function Home() {
|
||||
<FounderSignature />
|
||||
</div>
|
||||
|
||||
<div id="contact" className="w-full mt-20 mb-20 flex flex-col items-center">
|
||||
<div id="contact" className="w-full mt-20 mb-20 pb-16 md:pb-0 flex flex-col items-center">
|
||||
<div className="w-full max-w-6xl text-left mb-12 section-container border-l-4 border-l-cyan-500">
|
||||
<h2 className="text-3xl font-bold mb-3 tracking-tight text-foreground">Start a Project</h2>
|
||||
<p className="text-muted-foreground text-lg max-w-3xl leading-relaxed">
|
||||
@@ -114,6 +115,7 @@ export default function Home() {
|
||||
<ContactForm />
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
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 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. Memorize, shuffle, reconstruct. Android, iOS, Web, Desktop.",
|
||||
alternates: { canonical: "/products/bauhaus-echo" },
|
||||
};
|
||||
|
||||
const gameModes = [
|
||||
{ name: "Classic", description: "Memorize tile positions, reconstruct after shuffle" },
|
||||
{ name: "Speed", description: "Tiles drop one at a time, place each before the next appears" },
|
||||
{ name: "Endless", description: "Speed mode with tile cycling, play forever" },
|
||||
{ name: "Memory", description: "Traditional card-matching pairs game" },
|
||||
];
|
||||
|
||||
export default function BauhausEchoPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-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-blue-600 dark:text-blue-400">
|
||||
Bauhaus Echo
|
||||
</h1>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none mb-12">
|
||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||
A visual memory puzzle game inspired by Bauhaus design aesthetics.
|
||||
Memorize arrangements of geometric tiles, watch them shuffle, then reconstruct the pattern.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-4 mb-12">
|
||||
<a href="https://play.google.com/store/apps/details?id=com.suchsoftware.bauhausecho" target="_blank" rel="noopener noreferrer">
|
||||
<Button size="lg">Google Play</Button>
|
||||
</a>
|
||||
<a href="https://apps.apple.com/app/bauhaus-echo" target="_blank" rel="noopener noreferrer">
|
||||
<Button variant="outline" size="lg">App Store</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">How It Works</h2>
|
||||
<div className="section-container border-l-4 border-blue-500 mb-12">
|
||||
<ol className="space-y-4 text-muted-foreground">
|
||||
<li><strong>1. Memorization</strong> — Tiles appear in a grid. Study their positions.</li>
|
||||
<li><strong>2. Shuffle</strong> — Tiles spin, shrink, and scatter to the bottom.</li>
|
||||
<li><strong>3. Reconstruction</strong> — Drag tiles back to their original positions.</li>
|
||||
<li><strong>4. Scoring</strong> — Earn points based on accuracy and speed.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Game Modes</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
{gameModes.map((mode) => (
|
||||
<Card key={mode.name} className="glass-card border-blue-500/20">
|
||||
<h3 className="text-xl font-bold mb-2">{mode.name}</h3>
|
||||
<p className="text-muted-foreground">{mode.description}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Features</h2>
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-12">
|
||||
<Card className="glass-card border-blue-500/20">
|
||||
<h3 className="font-bold mb-2">500+ Tile Designs</h3>
|
||||
<p className="text-sm text-muted-foreground">Curated Bauhaus-style geometric art</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-blue-500/20">
|
||||
<h3 className="font-bold mb-2">Multiple Grid Sizes</h3>
|
||||
<p className="text-sm text-muted-foreground">2×2, 3×3, 4×4, 5×5</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-blue-500/20">
|
||||
<h3 className="font-bold mb-2">Cross-Platform</h3>
|
||||
<p className="text-sm text-muted-foreground">Android, iOS, Web, Desktop</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||
<Link href="/products/bauhaus-echo/privacy" className="hover:text-foreground">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<span>•</span>
|
||||
<span>Contact: apps@such.software</span>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Privacy Policy | Bauhaus Echo | Such Software",
|
||||
description: "Privacy policy for Bauhaus Echo mobile game.",
|
||||
alternates: { canonical: "/products/bauhaus-echo/privacy" },
|
||||
};
|
||||
|
||||
export default function BauhausEchoPrivacyPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-3xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<Link href="/products/bauhaus-echo" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">
|
||||
← Back to Bauhaus Echo
|
||||
</Link>
|
||||
|
||||
<h1 className="text-3xl md:text-4xl font-bold mb-4">Privacy Policy for Bauhaus Echo</h1>
|
||||
<p className="text-muted-foreground mb-8">Last Updated: January 31, 2026</p>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none">
|
||||
<p>
|
||||
<strong>Such Software LLC</strong> ("we," "us," or "our") operates the Bauhaus Echo mobile
|
||||
application (the "App"). This page informs you of our policies regarding the collection,
|
||||
use, and disclosure of personal data when you use our App.
|
||||
</p>
|
||||
|
||||
<h2>Information We Collect</h2>
|
||||
|
||||
<h3>Advertising Data</h3>
|
||||
<p>
|
||||
We use <strong>Google AdMob</strong> to display advertisements. AdMob may collect and use
|
||||
data to show you personalized ads. This data may include:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Device identifiers (Advertising ID)</li>
|
||||
<li>IP address</li>
|
||||
<li>General location (country/region)</li>
|
||||
<li>App usage data</li>
|
||||
</ul>
|
||||
<p>
|
||||
For more information on how Google uses data, visit:{" "}
|
||||
<a href="https://policies.google.com/technologies/partner-sites" target="_blank" rel="noopener noreferrer">
|
||||
How Google uses information from sites or apps that use our services
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<strong>iOS Users:</strong> We will ask for your permission via App Tracking Transparency (ATT)
|
||||
before collecting data for personalized advertising. You can deny this request and still use
|
||||
the app with non-personalized ads.
|
||||
</p>
|
||||
|
||||
<h3>In-App Purchases</h3>
|
||||
<p>
|
||||
If you purchase the "Remove Ads" upgrade, your transaction is processed by Google Play (Android)
|
||||
or the Apple App Store (iOS). We do not have access to your payment information. We only receive
|
||||
confirmation of your purchase status.
|
||||
</p>
|
||||
|
||||
<h3>High Scores & Leaderboards</h3>
|
||||
<p>If you submit scores to our online leaderboards, we collect:</p>
|
||||
<ul>
|
||||
<li>Your chosen display name (anonymous by default)</li>
|
||||
<li>Your game scores and statistics</li>
|
||||
<li>A randomly generated anonymous player ID</li>
|
||||
</ul>
|
||||
<p>
|
||||
We do <strong>not</strong> require or collect your real name, email, or any other personal
|
||||
information to use leaderboards.
|
||||
</p>
|
||||
|
||||
<h3>Locally Stored Data</h3>
|
||||
<p>The App stores the following data locally on your device:</p>
|
||||
<ul>
|
||||
<li>Game progress and high scores</li>
|
||||
<li>Settings preferences (sound, music, grid size)</li>
|
||||
<li>Premium purchase status</li>
|
||||
</ul>
|
||||
<p>This data is not transmitted to our servers unless you use the online leaderboard feature.</p>
|
||||
|
||||
<h2>How We Use Your Information</h2>
|
||||
<p>We use collected data to:</p>
|
||||
<ul>
|
||||
<li>Display advertisements (unless you purchase "Remove Ads")</li>
|
||||
<li>Process in-app purchases</li>
|
||||
<li>Maintain online leaderboards</li>
|
||||
<li>Improve the App experience</li>
|
||||
</ul>
|
||||
|
||||
<h2>Third-Party Services</h2>
|
||||
<p>The App uses the following third-party services that may collect information:</p>
|
||||
<ul>
|
||||
<li><strong>Google AdMob</strong> — Advertising</li>
|
||||
<li><strong>Google Play Services</strong> — Android platform and billing</li>
|
||||
<li><strong>Apple StoreKit</strong> — iOS platform and billing</li>
|
||||
</ul>
|
||||
|
||||
<h2>Children's Privacy</h2>
|
||||
<p>
|
||||
The App is intended for general audiences and does not knowingly collect personal information
|
||||
from children under 13. If you believe we have collected data from a child under 13, please
|
||||
contact us immediately.
|
||||
</p>
|
||||
|
||||
<h2>Data Retention</h2>
|
||||
<ul>
|
||||
<li><strong>Advertising data</strong> is managed by Google per their retention policies</li>
|
||||
<li><strong>Leaderboard data</strong> is retained indefinitely but can be deleted upon request</li>
|
||||
<li><strong>Local data</strong> is stored on your device until you uninstall the App</li>
|
||||
</ul>
|
||||
|
||||
<h2>Your Rights</h2>
|
||||
<p>You may:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Opt out of personalized ads</strong> via your device settings (Android: Settings → Google → Ads;
|
||||
iOS: Settings → Privacy → Tracking)
|
||||
</li>
|
||||
<li><strong>Request deletion</strong> of your leaderboard data by contacting us</li>
|
||||
<li><strong>Remove local data</strong> by uninstalling the App</li>
|
||||
</ul>
|
||||
|
||||
<h2>Changes to This Policy</h2>
|
||||
<p>
|
||||
We may update this Privacy Policy from time to time. We will notify you of any changes by
|
||||
posting the new Privacy Policy on this page and updating the "Last Updated" date.
|
||||
</p>
|
||||
|
||||
<h2>Contact Us</h2>
|
||||
<p>If you have any questions about this Privacy Policy, please contact us:</p>
|
||||
<p>
|
||||
<strong>Such Software LLC</strong><br />
|
||||
Email: apps@such.software<br />
|
||||
Website: https://such.software
|
||||
</p>
|
||||
|
||||
<h2>Consent</h2>
|
||||
<p>By using Bauhaus Echo, you consent to this Privacy Policy.</p>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
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 Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Custom Websites | Such Software",
|
||||
description: "Real-time platforms and interactive web experiences. See our work on rcryptocurrency.com and moonplace.io.",
|
||||
alternates: { canonical: "/products/custom-websites" },
|
||||
};
|
||||
|
||||
export default function CustomWebsitesPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-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-purple-600 dark:text-purple-400">
|
||||
Custom Websites
|
||||
</h1>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none mb-12">
|
||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||
Real-time platforms, collaborative tools, and high-end brand experiences.
|
||||
We build performant web applications that handle scale.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Featured Projects</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
<a href="https://rcryptocurrency.com" target="_blank" rel="noopener noreferrer">
|
||||
<Card className="glass-card border-purple-500/20 hover:border-purple-500/50 transition-colors cursor-pointer h-full">
|
||||
<h3 className="text-xl font-bold mb-3">rcryptocurrency.com</h3>
|
||||
<p className="text-muted-foreground mb-4">
|
||||
Governance dashboard for the r/CryptoCurrency subreddit with 10M+ members.
|
||||
Real-time DAO metrics, proposal tracking, and community analytics.
|
||||
</p>
|
||||
<ul className="text-sm text-muted-foreground space-y-1">
|
||||
<li>Real-time data via WebSocket</li>
|
||||
<li>Reddit API integration</li>
|
||||
<li>On-chain governance tracking</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</a>
|
||||
<a href="https://moonplace.io" target="_blank" rel="noopener noreferrer">
|
||||
<Card className="glass-card border-purple-500/20 hover:border-purple-500/50 transition-colors cursor-pointer h-full">
|
||||
<h3 className="text-xl font-bold mb-3">moonplace.io</h3>
|
||||
<p className="text-muted-foreground mb-4">
|
||||
Collaborative pixel art canvas with real-time multiplayer.
|
||||
Thousands of concurrent users painting together.
|
||||
</p>
|
||||
<ul className="text-sm text-muted-foreground space-y-1">
|
||||
<li>WebSocket real-time sync</li>
|
||||
<li>Canvas rendering optimization</li>
|
||||
<li>User authentication and cooldowns</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
<Card className="glass-card border-purple-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Real-time Platforms</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Socket.io and WebRTC integration for collaborative tools, dashboards,
|
||||
and live data visualization.
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-purple-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Brand Motion</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Framer Motion and Three.js powered interfaces that tell your brand's
|
||||
story through motion and depth.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<Link href="/about">
|
||||
<Button size="lg">Start a Project</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Products | Such Software",
|
||||
description: "Tools and applications built by Such Software. Webshops, custom websites, Smirk Wallet, and Bauhaus Echo.",
|
||||
alternates: { canonical: "/products" },
|
||||
openGraph: {
|
||||
title: "Products | Such Software",
|
||||
description: "Tools and applications built by Such Software.",
|
||||
type: "website",
|
||||
},
|
||||
};
|
||||
|
||||
const products = [
|
||||
{
|
||||
slug: "webshops",
|
||||
title: "Webshops",
|
||||
description: "Headless Medusa commerce with revenue-share pricing. No upfront costs.",
|
||||
color: "emerald",
|
||||
},
|
||||
{
|
||||
slug: "custom-websites",
|
||||
title: "Custom Websites",
|
||||
description: "Real-time platforms and interactive experiences. See rcryptocurrency.com and moonplace.io.",
|
||||
color: "purple",
|
||||
},
|
||||
{
|
||||
slug: "smirk-wallet",
|
||||
title: "Smirk Wallet",
|
||||
description: "Non-custodial browser wallet for social tipping. BTC, LTC, XMR, WOW, GRIN.",
|
||||
color: "amber",
|
||||
},
|
||||
{
|
||||
slug: "bauhaus-echo",
|
||||
title: "Bauhaus Echo",
|
||||
description: "Visual memory puzzle game inspired by Bauhaus design. Android, iOS, Web.",
|
||||
color: "blue",
|
||||
},
|
||||
];
|
||||
|
||||
const colorClasses: Record<string, string> = {
|
||||
emerald: "border-emerald-500/30 hover:border-emerald-500/60",
|
||||
purple: "border-purple-500/30 hover:border-purple-500/60",
|
||||
amber: "border-amber-500/30 hover:border-amber-500/60",
|
||||
blue: "border-blue-500/30 hover:border-blue-500/60",
|
||||
};
|
||||
|
||||
const titleColors: Record<string, string> = {
|
||||
emerald: "text-emerald-600 dark:text-emerald-400",
|
||||
purple: "text-purple-600 dark:text-purple-400",
|
||||
amber: "text-amber-600 dark:text-amber-400",
|
||||
blue: "text-blue-600 dark:text-blue-400",
|
||||
};
|
||||
|
||||
export default function ProductsPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-5xl mx-auto py-20 px-4 pb-24 md:pb-20">
|
||||
<div className="text-center mb-16">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 bg-clip-text text-transparent bg-gradient-to-r from-amber-400 to-purple-500">
|
||||
Our Products
|
||||
</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
|
||||
Tools we have built, from commerce infrastructure to consumer apps.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
{products.map((product) => (
|
||||
<Link key={product.slug} href={`/products/${product.slug}`}>
|
||||
<Card className={`glass-card h-full min-h-[200px] ${colorClasses[product.color]} transition-all cursor-pointer`}>
|
||||
<h2 className={`text-2xl font-bold mb-3 ${titleColors[product.color]}`}>
|
||||
{product.title}
|
||||
</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
{product.description}
|
||||
</p>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
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 Link from "next/link";
|
||||
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.",
|
||||
alternates: { canonical: "/products/smirk-wallet" },
|
||||
};
|
||||
|
||||
const currencies = [
|
||||
{ name: "Bitcoin", symbol: "BTC", type: "secp256k1" },
|
||||
{ name: "Litecoin", symbol: "LTC", type: "secp256k1" },
|
||||
{ name: "Monero", symbol: "XMR", type: "ed25519" },
|
||||
{ name: "Wownero", symbol: "WOW", type: "ed25519" },
|
||||
{ name: "Grin", symbol: "GRIN", type: "secp256k1" },
|
||||
];
|
||||
|
||||
export default function SmirkWalletPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-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-amber-600 dark:text-amber-400">
|
||||
Smirk Wallet
|
||||
</h1>
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none mb-12">
|
||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||
Non-custodial multi-currency wallet for social tipping.
|
||||
Tip anyone on Telegram or Discord by username. Your keys never leave your browser.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-4 mb-12">
|
||||
<a href="https://smirk.cash" target="_blank" rel="noopener noreferrer">
|
||||
<Button size="lg">Visit smirk.cash</Button>
|
||||
</a>
|
||||
<a href="https://github.com/Such-Software/smirk-extension/releases" target="_blank" rel="noopener noreferrer">
|
||||
<Button variant="outline" size="lg">Download Extension</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Features</h2>
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
<Card className="glass-card border-amber-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Non-Custodial</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Your private keys are generated locally and encrypted with your password.
|
||||
We never see your spend keys. Your funds are always yours.
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-amber-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Social Tipping</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Link your Telegram or Discord account. Anyone with Smirk can send you
|
||||
crypto by username. No wallet address needed.
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-amber-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Encrypted Tips</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Tips are encrypted with the recipient's public key. Only they can claim.
|
||||
Senders can reclaim unclaimed tips (no lost funds).
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-amber-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">One Seed, Five Chains</h3>
|
||||
<p className="text-muted-foreground">
|
||||
A single 12-word seed phrase derives addresses for all supported currencies.
|
||||
Restore your entire wallet with one backup.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-6">Supported Currencies</h2>
|
||||
<div className="overflow-x-auto mb-12">
|
||||
<table className="w-full text-left">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="py-3 px-4 font-semibold">Currency</th>
|
||||
<th className="py-3 px-4 font-semibold">Symbol</th>
|
||||
<th className="py-3 px-4 font-semibold">Key Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currencies.map((c) => (
|
||||
<tr key={c.symbol} className="border-b border-border/50">
|
||||
<td className="py-3 px-4">{c.name}</td>
|
||||
<td className="py-3 px-4 font-mono">{c.symbol}</td>
|
||||
<td className="py-3 px-4 text-muted-foreground">{c.type}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="section-container border-l-4 border-amber-500 mb-12">
|
||||
<h2 className="text-2xl font-bold mb-4">Why These Coins?</h2>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
BTC is the most widely used. LTC is fast and cheap (MWEB support coming).
|
||||
XMR is the gold standard for privacy. WOW has a great community.
|
||||
GRIN is lightweight, private, and has the fairest emission schedule of any coin.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<a href="https://t.me/smirkwallet" target="_blank" rel="noopener noreferrer">
|
||||
<Button variant="outline">Telegram</Button>
|
||||
</a>
|
||||
<a href="https://discord.gg/7EnsaWTm6C" target="_blank" rel="noopener noreferrer">
|
||||
<Button variant="outline">Discord</Button>
|
||||
</a>
|
||||
<a href="https://github.com/Such-Software/smirk-extension" target="_blank" rel="noopener noreferrer">
|
||||
<Button variant="outline">GitHub</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
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 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.",
|
||||
alternates: { canonical: "/products/webshops" },
|
||||
};
|
||||
|
||||
export default function WebshopsPage() {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col items-center bg-background text-foreground">
|
||||
<Header />
|
||||
<div id="main-content" className="z-10 w-full max-w-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>
|
||||
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-12">
|
||||
<Card className="glass-card border-emerald-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Zero Upfront Cost</h3>
|
||||
<p className="text-muted-foreground">
|
||||
We invest our development time and infrastructure. You focus on your brand and products.
|
||||
We take a small percentage of sales once you are live.
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-emerald-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Own Your Data</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Unlike Shopify, you own your customer data, product catalog, and order history.
|
||||
No platform lock-in, ever.
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-emerald-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Flexible Architecture</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Subscriptions, bundles, B2B pricing, multi-warehouse inventory.
|
||||
Model your exact business logic without platform constraints.
|
||||
</p>
|
||||
</Card>
|
||||
<Card className="glass-card border-emerald-500/20">
|
||||
<h3 className="text-xl font-bold mb-3">Performance First</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Next.js storefronts optimized for Core Web Vitals. 100/100 Lighthouse scores,
|
||||
fast load times, high conversion rates.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="section-container border-l-4 border-emerald-500 mb-12">
|
||||
<h2 className="text-2xl font-bold mb-4">Tech Stack</h2>
|
||||
<ul className="space-y-2 text-muted-foreground">
|
||||
<li><strong>Backend:</strong> Medusa v2 (Node.js, PostgreSQL)</li>
|
||||
<li><strong>Frontend:</strong> Next.js 15, React 18, Tailwind CSS</li>
|
||||
<li><strong>Payments:</strong> Stripe, PayPal, crypto (optional)</li>
|
||||
<li><strong>Hosting:</strong> Vercel, Railway, or your infrastructure</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<Link href="/about">
|
||||
<Button size="lg">Get in Touch</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DataGrid } from "@repo/ui/procedural/data-grid";
|
||||
import { Button } from "@repo/ui/components/button";
|
||||
import { Header } from "@/components/layout/header";
|
||||
import { MobileNav } from "@/components/layout/mobile-nav";
|
||||
import Link from "next/link";
|
||||
import { Card } from "@repo/ui/components/card";
|
||||
import type { Metadata } from "next";
|
||||
@@ -150,7 +151,7 @@ export default function ServicesPage() {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MobileNav />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -20,12 +20,24 @@ export function Header() {
|
||||
<div className="flex items-center gap-4">
|
||||
<SiteLogo />
|
||||
<nav className="hidden md:flex gap-6 text-sm font-medium" aria-label="Main navigation">
|
||||
<Link
|
||||
href="/products"
|
||||
className={pathname.startsWith("/products") ? "text-primary" : "hover:text-primary transition-colors"}
|
||||
>
|
||||
Products
|
||||
</Link>
|
||||
<Link
|
||||
href="/services"
|
||||
className={pathname === "/services" ? "text-primary" : "hover:text-primary transition-colors"}
|
||||
>
|
||||
Services
|
||||
</Link>
|
||||
<Link
|
||||
href="/blog"
|
||||
className={pathname.startsWith("/blog") ? "text-primary" : "hover:text-primary transition-colors"}
|
||||
>
|
||||
Blog
|
||||
</Link>
|
||||
<Link
|
||||
href="/about"
|
||||
className={pathname === "/about" ? "text-primary" : "hover:text-primary transition-colors"}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
'use client';
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { FaHouse, FaBoxesStacked, FaNewspaper, FaCircleInfo, FaGear } from "react-icons/fa6";
|
||||
|
||||
const navItems = [
|
||||
{ href: "/", label: "Home", icon: FaHouse },
|
||||
{ href: "/products", label: "Products", icon: FaBoxesStacked },
|
||||
{ href: "/services", label: "Services", icon: FaGear },
|
||||
{ href: "/blog", label: "Blog", icon: FaNewspaper },
|
||||
{ href: "/about", label: "About", icon: FaCircleInfo },
|
||||
];
|
||||
|
||||
export function MobileNav() {
|
||||
const pathname = usePathname();
|
||||
|
||||
const isActive = (href: string) => {
|
||||
if (href === "/") return pathname === "/";
|
||||
return pathname.startsWith(href);
|
||||
};
|
||||
|
||||
return (
|
||||
<nav
|
||||
className="fixed bottom-0 left-0 right-0 z-50 md:hidden bg-background/95 backdrop-blur-xl border-t border-border/50"
|
||||
aria-label="Mobile navigation"
|
||||
style={{ paddingBottom: "env(safe-area-inset-bottom)" }}
|
||||
>
|
||||
<div className="flex justify-around items-center h-16 px-2">
|
||||
{navItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const active = isActive(item.href);
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`flex flex-col items-center justify-center gap-1 px-3 py-2 rounded-lg transition-colors ${
|
||||
active
|
||||
? "text-primary"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Icon className="h-5 w-5" aria-hidden="true" />
|
||||
<span className="text-[10px] font-medium">{item.label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user