diff --git a/apps/web/public/images/products/hash-bags.png b/apps/web/public/images/products/hash-bags.png new file mode 100644 index 0000000..b971970 Binary files /dev/null and b/apps/web/public/images/products/hash-bags.png differ diff --git a/apps/web/src/app/apps/page.tsx b/apps/web/src/app/apps/page.tsx index 76ddc56..0af9008 100644 --- a/apps/web/src/app/apps/page.tsx +++ b/apps/web/src/app/apps/page.tsx @@ -19,6 +19,18 @@ export const metadata: Metadata = { }; const apps = [ + { + name: "Hash Bags", + tagline: "Non-custodial multi-chain wallet. Wownero-first.", + icon: "/images/products/hash-bags.png", + color: "text-emerald-600 dark:text-emerald-400", + borderColor: "border-emerald-500/20", + href: "/products/hash-bags", + links: [ + { platform: "google" as const, href: "https://play.google.com/store/apps/details?id=com.suchsoftware.hashwallet" }, + { platform: "apple" as const, href: null }, + ], + }, { name: "Bloomword", tagline: "Grow your vocab: a daily word game with a living 3D tree", diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index b71bc59..f4b32a1 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -327,3 +327,40 @@ html[data-splash="skip"] #splash-overlay { display: none !important; } + +/* Splash handover burst: the four pillar cells disperse as the mark hands over to the page. + The blobs sit where the monogram's four cells were (a 2x2 grid about the centre), so this + reads as the logo coming apart rather than as a generic flash. + + ONLY transform and opacity animate, both compositor properties. This deliberately avoids + animating filter/background: the matrix animation pegged a core doing exactly that, because + a filter change re-renders the whole filter graph every frame. Soft edges come from a static + radial-gradient that is painted once and then only transformed. */ +@keyframes splash-burst { + from { transform: translate(-50%, -50%) scale(0.35); opacity: var(--burst-peak); } + to { transform: translate(-50%, -50%) scale(3.1); opacity: 0; } +} +.splash-burst-blob { + position: absolute; + width: 34vmin; + height: 34vmin; + border-radius: 9999px; + opacity: 0; + will-change: transform, opacity; + animation: splash-burst 420ms cubic-bezier(0.16, 0.84, 0.44, 1) forwards; + /* Half-transparent by construction: the gradient's own alpha never reaches 1, and + --burst-peak holds the whole layer under half opacity. */ + --burst-peak: 0.42; +} +.dark .splash-burst-blob { --burst-peak: 0.5; } +.splash-burst-s { background: radial-gradient(circle, rgb(26 92 56 / 0.55), transparent 70%); } +.splash-burst-u { background: radial-gradient(circle, rgb(184 54 112 / 0.55), transparent 70%); } +.splash-burst-c { background: radial-gradient(circle, rgb(23 106 146 / 0.55), transparent 70%); } +.splash-burst-h { background: radial-gradient(circle, rgb(18 133 126 / 0.55), transparent 70%); } +.dark .splash-burst-s { background: radial-gradient(circle, rgb(53 146 92 / 0.55), transparent 70%); } +.dark .splash-burst-u { background: radial-gradient(circle, rgb(232 104 158 / 0.55), transparent 70%); } +.dark .splash-burst-c { background: radial-gradient(circle, rgb(74 173 224 / 0.55), transparent 70%); } +.dark .splash-burst-h { background: radial-gradient(circle, rgb(58 212 198 / 0.55), transparent 70%); } +@media (prefers-reduced-motion: reduce) { + .splash-burst-blob { display: none; } +} diff --git a/apps/web/src/app/products/hash-bags/page.tsx b/apps/web/src/app/products/hash-bags/page.tsx new file mode 100644 index 0000000..efeba21 --- /dev/null +++ b/apps/web/src/app/products/hash-bags/page.tsx @@ -0,0 +1,142 @@ +import { Header } from "@/components/layout/header"; +import { MobileNav } from "@/components/layout/mobile-nav"; +import { Card } from "@repo/ui/components/card"; +import { StoreButtons } from "@/components/store-buttons"; +import { JsonLd, breadcrumbLd, softwareAppLd } from "@/components/seo/json-ld"; +import Image from "next/image"; +import Link from "next/link"; +import type { Metadata } from "next"; + +const DESCRIPTION = + "Open-source, non-custodial wallet for Monero, Wownero, Bitcoin, and nine more chains. Wownero-first, with Ledger, Trezor, and BitBox support. Free on Android; iOS and desktop coming soon."; + +export const metadata: Metadata = { + title: "Hash Bags: Non-Custodial Multi-Chain Crypto Wallet | Such Software", + description: DESCRIPTION, + alternates: { canonical: "/products/hash-bags" }, + openGraph: { + title: "Hash Bags: Non-Custodial Multi-Chain Crypto Wallet | Such Software", + description: DESCRIPTION, + type: "website", + images: ["/images/branding/OG_banner_v2_light.png"], + }, + twitter: { + card: "summary_large_image", + images: ["/images/branding/OG_banner_v2_light.png"], + }, +}; + +const chains = [ + { name: "Monero", ticker: "XMR" }, + { name: "Wownero", ticker: "WOW" }, + { name: "Bitcoin", ticker: "BTC" }, + { name: "Litecoin", ticker: "LTC, incl. MWEB" }, + { name: "Bitcoin Cash", ticker: "BCH" }, + { name: "Dogecoin", ticker: "DOGE" }, + { name: "Nano", ticker: "XNO" }, + { name: "Ethereum", ticker: "ETH + ERC-20s" }, + { name: "Polygon", ticker: "POL" }, + { name: "Base", ticker: "BASE" }, + { name: "Arbitrum", ticker: "ARB" }, + { name: "BNB Smart Chain", ticker: "BSC" }, +]; + +export default function HashBagsPage() { + return ( +
+ + +
+
+ + ← Back to Products + + +
+ Hash Bags icon +

+ Hash Bags +

+
+ +
+

+ A wallet for your coins. Hash Bags is an open-source, non-custodial, multi-chain + crypto wallet: your keys and your funds stay on your device, and we never hold + either. Free on Android today, with iOS and desktop on the way. +

+
+ +
+ +

+ Live on Google Play. The iOS build is in review, and macOS, Linux, and Windows + follow after that. +

+
+ +
+

Why it exists

+
+

+ Cake Wallet ended Wownero support in early 2026. Hash Bags is a fork that keeps + Wownero a first-class mobile experience, maintained by the project's founder, + while shipping a slimmer and more focused multi-coin wallet without the chains and + integrations we would rather not maintain or recommend. +

+
+
+ +
+

Twelve chains

+
+ {chains.map((c) => ( + +
{c.name}
+
{c.ticker}
+
+ ))} +
+
+ +
+

Hardware wallets

+

+ Ledger, Trezor, and BitBox are supported, so the keys can stay off the phone + entirely if that is how you prefer to hold them. +

+
+ +
+ + ← All products + +
+
+ +
+ ); +} diff --git a/apps/web/src/app/products/page.tsx b/apps/web/src/app/products/page.tsx index df48767..eafa345 100644 --- a/apps/web/src/app/products/page.tsx +++ b/apps/web/src/app/products/page.tsx @@ -31,6 +31,14 @@ const products: Product[] = [ icon: "/images/products/custom-websites.svg", releaseDate: "2025-01-01", }, + { + slug: "hash-bags", + title: "Hash Bags", + description: "Open-source, non-custodial wallet for Monero, Wownero, Bitcoin, and nine more chains. Wownero-first, with Ledger, Trezor, and BitBox support. Android now; iOS and desktop coming.", + color: "emerald", + icon: "/images/products/hash-bags.png", + releaseDate: "2026-08-17", + }, { slug: "smirk-wallet", title: "Smirk Wallet", diff --git a/apps/web/src/app/sitemap.ts b/apps/web/src/app/sitemap.ts index 2caf56f..c42d583 100644 --- a/apps/web/src/app/sitemap.ts +++ b/apps/web/src/app/sitemap.ts @@ -17,6 +17,7 @@ export default function sitemap(): MetadataRoute.Sitemap { { url: `${base}/products/neroswap`, lastModified: updated, changeFrequency: "monthly", priority: 0.8 }, { url: `${base}/products/webshops`, lastModified: updated, changeFrequency: "monthly", priority: 0.8 }, { url: `${base}/products/custom-websites`, lastModified: updated, changeFrequency: "monthly", priority: 0.8 }, + { url: `${base}/products/hash-bags`, lastModified: updated, changeFrequency: "weekly", priority: 0.8 }, { url: `${base}/products/smirk-wallet`, lastModified: updated, changeFrequency: "monthly", priority: 0.8 }, { url: `${base}/products/bauhaus-echo`, lastModified: updated, changeFrequency: "monthly", priority: 0.8 }, { url: `${base}/products/vegan-iq`, lastModified: updated, changeFrequency: "monthly", priority: 0.8 }, diff --git a/apps/web/src/components/landing/hero-splash.tsx b/apps/web/src/components/landing/hero-splash.tsx index 1bf68bf..afa99e7 100644 --- a/apps/web/src/components/landing/hero-splash.tsx +++ b/apps/web/src/components/landing/hero-splash.tsx @@ -12,6 +12,16 @@ const SPLASH_MS = 1337; // gate stacked on the first. const FADE_MS = 260; +// The four monogram cells, in the 2x2 grid the mark ends on: S and C in the left column, +// U and H in the right. The burst throws one blob out of each, in that letter's own colour, +// so the handover reads as the logo dispersing rather than as an unrelated flash. +const BURST_CELLS = [ + { key: "s", left: "44%", top: "44%" }, + { key: "u", left: "56%", top: "44%" }, + { key: "c", left: "44%", top: "56%" }, + { key: "h", left: "56%", top: "56%" }, +] as const; + /** * First-load splash: the pillar signature. * @@ -77,30 +87,55 @@ export function HeroSplash({ onEnter, leaving }: { onEnter: () => void; leaving? }; }, [leaving]); + const fade = { transitionDuration: `${FADE_MS}ms`, opacity: fadeOut ? 0 : 1 }; + return (