From 375e7e28684b4322ea1da4318cf7f31a2c3773af Mon Sep 17 00:00:00 2001 From: jwinterm Date: Fri, 6 Feb 2026 13:10:15 -0500 Subject: [PATCH] update bauhaus privacy policy and blog styling --- TODO.md | 14 +- apps/web/package.json | 1 + .../src/app/blog/bauhaus-echo-launch/page.tsx | 87 ++++----- .../app/blog/smirk-wallet-release/page.tsx | 17 +- .../app/blog/website-relaunch-2026/page.tsx | 23 ++- apps/web/src/app/globals.css | 68 +++++++ .../products/bauhaus-echo/privacy/page.tsx | 172 ++++++++++++++---- apps/web/tailwind.config.ts | 2 +- pnpm-lock.yaml | 22 +++ 9 files changed, 313 insertions(+), 93 deletions(-) diff --git a/TODO.md b/TODO.md index d296631..ee1883c 100644 --- a/TODO.md +++ b/TODO.md @@ -6,14 +6,13 @@ ## Nice to Have -- [ ] **Blog** - Consider adding blog/articles section (MDX, Contentlayer, or headless CMS) - [ ] **Rate limiting** - Turnstile handles bots; add rate limiting only if abuse observed - [ ] Manual screen reader testing (NVDA, VoiceOver) - [ ] Further canvas performance optimization +- [ ] **Blog migration** - Move to MDX/Contentlayer when post count grows ## Future Projects -- [ ] Puzzle game (mobile app with web demo) - [ ] Decision making app (mobile app with web demo) --- @@ -62,6 +61,17 @@ packages/ ## Completed +### Products & Blog (February 2026) +- [x] **Products section** - Landing page + 4 product pages (Webshops, Custom Websites, Smirk Wallet, Bauhaus Echo) +- [x] **Bauhaus Echo** - Full product page with game modes, features, app store links, privacy policy +- [x] **Smirk Wallet** - Full product page with features, supported currencies, community links +- [x] **Blog infrastructure** - Blog listing with draft system, individual post pages +- [x] **Blog posts** - Website Relaunch (published), Smirk Wallet v0.1.6 (published), Bauhaus Echo Launch (draft) +- [x] **Mobile bottom navigation** - Fixed nav bar for mobile with 5 items (Home, Products, Services, Blog, About) +- [x] **Header updates** - Desktop nav now includes Products and Blog links +- [x] **Portfolio bento grid** - Added Smirk Wallet card (1x2 tall) +- [x] **Footer/About** - Replaced Reddit with Discord, added jwinterm.com link + ### Lighthouse Scores (January 2026) | Page | Performance | Accessibility | SEO | Best Practices | diff --git a/apps/web/package.json b/apps/web/package.json index 041f0ec..466b492 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -28,6 +28,7 @@ "devDependencies": { "@axe-core/playwright": "^4.11.0", "@playwright/test": "^1.57.0", + "@tailwindcss/typography": "^0.5.19", "@types/node": "^20.0.0", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", diff --git a/apps/web/src/app/blog/bauhaus-echo-launch/page.tsx b/apps/web/src/app/blog/bauhaus-echo-launch/page.tsx index 47beb38..2fa8e93 100644 --- a/apps/web/src/app/blog/bauhaus-echo-launch/page.tsx +++ b/apps/web/src/app/blog/bauhaus-echo-launch/page.tsx @@ -13,56 +13,59 @@ export default function BauhausEchoLaunchPost() { return (
-
- +
+ ← Back to Blog -
- -

Bauhaus Echo is Live

-
+
+
+ +

Bauhaus Echo is Live

+

A visual memory puzzle inspired by Bauhaus design

+
-
-

- 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. -

+
+

+ 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. +

-

The Concept

-

- 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. -

+

The Concept

+

+ 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. +

-

Four Game Modes

-
    -
  • Classic - The core experience. Memorize and reconstruct.
  • -
  • Speed - Tiles appear one at a time. Place each before the next drops.
  • -
  • Endless - Speed mode that loops forever. See how long you last.
  • -
  • Memory - Traditional card-matching pairs.
  • -
+

Four Game Modes

+
    +
  • Classic — The core experience. Memorize and reconstruct.
  • +
  • Speed — Tiles appear one at a time. Place each before the next drops.
  • +
  • Endless — Speed mode that loops forever. See how long you last.
  • +
  • Memory — Traditional card-matching pairs.
  • +
-

Tech Stack

-

- 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. -

+

Tech Stack

+

+ 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. +

-

Download

-

- Get it on{" "} - - Google Play - {" "} - or the{" "} - - App Store - - . Free with ads, or pay once to remove them. -

+

Download

+

+ Get it on{" "} + + Google Play + {" "} + or the{" "} + + App Store + + . Free with ads, or pay once to remove them. +

+
diff --git a/apps/web/src/app/blog/smirk-wallet-release/page.tsx b/apps/web/src/app/blog/smirk-wallet-release/page.tsx index 2e17c1e..bf245aa 100644 --- a/apps/web/src/app/blog/smirk-wallet-release/page.tsx +++ b/apps/web/src/app/blog/smirk-wallet-release/page.tsx @@ -13,17 +13,19 @@ export default function SmirkWalletReleasePost() { return (
-
- +
+ ← Back to Blog -
- -

Smirk Wallet v0.1.6

-
+
+
+ +

Smirk Wallet v0.1.6

+

Non-custodial social tipping for Telegram and Discord

+
-
+

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 @@ -107,6 +109,7 @@ export default function SmirkWalletReleasePost() { .

+
diff --git a/apps/web/src/app/blog/website-relaunch-2026/page.tsx b/apps/web/src/app/blog/website-relaunch-2026/page.tsx index d0634d9..72dfd85 100644 --- a/apps/web/src/app/blog/website-relaunch-2026/page.tsx +++ b/apps/web/src/app/blog/website-relaunch-2026/page.tsx @@ -13,17 +13,19 @@ export default function WebsiteRelaunchPost() { return (
-
- +
+ ← Back to Blog -
- -

Website Relaunch

-
+
+
+ +

Website Relaunch

+

New design, new infrastructure, same focus on quality

+
-
+

We rebuilt such.software. New design, new infrastructure, same focus on performance and accessibility.

@@ -42,10 +44,10 @@ export default function WebsiteRelaunchPost() {

Procedural Graphics

- The homepage features two custom procedural animations: NebulaField and DataGrid. Both are + The site 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. + data streams flowing toward a central icon using quadratic Bezier curves on the homepage. + DataGrid draws a pulsing grid with traveling data packets on the Services page.

Accessibility

@@ -69,6 +71,7 @@ export default function WebsiteRelaunchPost() { covers our partnership model and consulting work. Mobile users get a bottom navigation bar.

+
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index da800a2..ac00e75 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -99,6 +99,74 @@ .glass-card:hover { border-color: hsl(var(--primary) / 0.3); } + + /* Typography customization for blog posts */ + .prose { + --tw-prose-body: hsl(var(--foreground)); + --tw-prose-headings: hsl(var(--foreground)); + --tw-prose-lead: hsl(var(--muted-foreground)); + --tw-prose-links: hsl(var(--primary)); + --tw-prose-bold: hsl(var(--foreground)); + --tw-prose-counters: hsl(var(--muted-foreground)); + --tw-prose-bullets: hsl(var(--muted-foreground)); + --tw-prose-hr: hsl(var(--border)); + --tw-prose-quotes: hsl(var(--foreground)); + --tw-prose-quote-borders: hsl(var(--border)); + --tw-prose-captions: hsl(var(--muted-foreground)); + --tw-prose-code: hsl(var(--foreground)); + --tw-prose-pre-code: hsl(var(--foreground)); + --tw-prose-pre-bg: hsl(var(--muted)); + --tw-prose-th-borders: hsl(var(--border)); + --tw-prose-td-borders: hsl(var(--border)); + } + + .prose h2 { + @apply text-xl font-semibold mt-10 mb-4 pb-2 border-b border-border; + } + + .prose h3 { + @apply text-lg font-medium mt-6 mb-3; + } + + .prose p { + @apply leading-relaxed mb-4; + } + + .prose ul { + @apply my-4 ml-4 space-y-2; + } + + .prose li { + @apply leading-relaxed; + } + + .prose a { + @apply text-emerald-600 dark:text-emerald-400 underline underline-offset-2 hover:text-emerald-700 dark:hover:text-emerald-300 transition-colors; + } + + .prose strong { + @apply font-semibold; + } + + .prose hr { + @apply my-8 border-border; + } + + .prose table { + @apply w-full my-6 text-sm; + } + + .prose thead th { + @apply text-left font-semibold p-3 bg-muted/50 border-b border-border; + } + + .prose tbody td { + @apply p-3 border-b border-border; + } + + .prose tbody tr:hover { + @apply bg-muted/30; + } } /* Respect users who prefer reduced motion */ diff --git a/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx b/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx index ab83af7..fdce11f 100644 --- a/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx +++ b/apps/web/src/app/products/bauhaus-echo/privacy/page.tsx @@ -5,7 +5,7 @@ import type { Metadata } from "next"; export const metadata: Metadata = { title: "Privacy Policy | Bauhaus Echo | Such Software", - description: "Privacy policy for Bauhaus Echo mobile game.", + description: "Privacy policy for Bauhaus Echo mobile game. GDPR and CPRA compliant.", alternates: { canonical: "/products/bauhaus-echo/privacy" }, }; @@ -13,20 +13,46 @@ export default function BauhausEchoPrivacyPage() { return (
-
- +
+ ← Back to Bauhaus Echo -

Privacy Policy for Bauhaus Echo

-

Last Updated: January 31, 2026

+
+
+

Privacy Policy for Bauhaus Echo

+

Last Updated: February 6, 2026

+
-
-

- Such Software LLC ("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. +

+

+ Such Software LLC ("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. +

+ +
+ +

Your Privacy Choices (GDPR/CPRA)

+

+ We respect your privacy. When you first open the App, you may see a consent + dialog depending on your location:

+
    +
  • EU/UK Users (GDPR): You will be asked to consent before we show personalized ads. You can accept, decline, or manage your preferences.
  • +
  • California/US Users (CPRA): You will have the option to opt out of the sale or sharing of your personal information.
  • +
+

Your choices:

+
    +
  • If you decline consent, we will show non-personalized ads instead
  • +
  • If you accept, ads may be personalized based on your interests
  • +
  • You can change your choice at any time in the App settings
  • +
+

+ Premium Users: If you purchase "Remove Ads," no advertising data is collected and no consent is required. +

+ +

Information We Collect

@@ -54,22 +80,13 @@ export default function BauhausEchoPrivacyPage() {

In-App Purchases

-

- 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. -

- -

High Scores & Leaderboards

-

If you submit scores to our online leaderboards, we collect:

+

If you purchase the "Remove Ads" upgrade, your transaction is processed by:

    -
  • Your chosen display name (anonymous by default)
  • -
  • Your game scores and statistics
  • -
  • A randomly generated anonymous player ID
  • +
  • Google Play (Android) — Google Play Privacy Policy
  • +
  • Apple App Store (iOS) — Apple Privacy Policy

- We do not require or collect your real name, email, or any other personal - information to use leaderboards. + We do not have access to your payment information. We only receive confirmation of your purchase status.

Locally Stored Data

@@ -81,6 +98,8 @@ export default function BauhausEchoPrivacyPage() {

This data is not transmitted to our servers unless you use the online leaderboard feature.

+
+

How We Use Your Information

We use collected data to:

    @@ -90,13 +109,64 @@ export default function BauhausEchoPrivacyPage() {
  • Improve the App experience
+
+

Third-Party Services

The App uses the following third-party services that may collect information:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ServicePurposePrivacy Policy
Google AdMobAdvertisingLink
Google UMP SDKConsent managementLink
Google Play ServicesAndroid platform, billingLink
Apple StoreKitiOS platform, billingLink
+
+ +

Google AdMob Data Collection

+

When ads are shown, Google AdMob may collect:

    -
  • Google AdMob — Advertising
  • -
  • Google Play Services — Android platform and billing
  • -
  • Apple StoreKit — iOS platform and billing
  • +
  • Device advertising identifier
  • +
  • IP address (for approximate location)
  • +
  • Device information (model, OS version)
  • +
  • App usage information
+

This data is used to:

+
    +
  • Show relevant advertisements
  • +
  • Measure ad performance
  • +
  • Prevent fraud
  • +
+

+ If you decline consent (EU/UK) or opt out (US), AdMob will show non-personalized ads and limit data collection. +

+ +

Children's Privacy

@@ -105,6 +175,8 @@ export default function BauhausEchoPrivacyPage() { contact us immediately.

+
+

Data Retention

  • Advertising data is managed by Google per their retention policies
  • @@ -112,33 +184,71 @@ export default function BauhausEchoPrivacyPage() {
  • Local data is stored on your device until you uninstall the App
+
+

Your Rights

You may:

    +
  • Control your consent via the in-app consent dialog (shown on first launch)
  • +
  • Change your privacy choices at any time via App settings (if available in your region)
  • - Opt out of personalized ads via your device settings (Android: Settings → Google → Ads; - iOS: Settings → Privacy → Tracking) + Opt out of personalized ads via your device settings: +
      +
    • Android: Settings → Google → Ads → Opt out of Ads Personalization
    • +
    • iOS: Settings → Privacy → Tracking → Disable tracking for Bauhaus Echo
    • +
  • Request deletion of your leaderboard data by contacting us
  • Remove local data by uninstalling the App
+

For EU/UK Residents (GDPR)

+

You have the right to:

+
    +
  • Access your personal data
  • +
  • Rectify inaccurate data
  • +
  • Request deletion of your data
  • +
  • Object to processing
  • +
  • Data portability
  • +
+

To exercise these rights, contact us at the email below.

+ +

For California Residents (CPRA)

+

You have the right to:

+
    +
  • Know what personal information is collected
  • +
  • Delete your personal information
  • +
  • Opt out of the sale or sharing of personal information
  • +
  • Non-discrimination for exercising your rights
  • +
+

To opt out, use the consent dialog shown in the App or contact us.

+ +
+

Changes to This Policy

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.

+
+

Contact Us

If you have any questions about this Privacy Policy, please contact us:

Such Software LLC
Email: apps@such.software
- Website: https://such.software + Website:{" "} + + https://bauhaus.such.software +

-

Consent

-

By using Bauhaus Echo, you consent to this Privacy Policy.

+
+ +

Consent

+

By using Bauhaus Echo, you consent to this Privacy Policy.

+
diff --git a/apps/web/tailwind.config.ts b/apps/web/tailwind.config.ts index 91f68db..381f068 100644 --- a/apps/web/tailwind.config.ts +++ b/apps/web/tailwind.config.ts @@ -78,6 +78,6 @@ const config: Config = { }, }, }, - plugins: [require("tailwindcss-animate")], + plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")], }; export default config; \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2da6fa..47754b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,6 +70,9 @@ importers: '@playwright/test': specifier: ^1.57.0 version: 1.57.0 + '@tailwindcss/typography': + specifier: ^0.5.19 + version: 0.5.19(tailwindcss@3.4.19) '@types/node': specifier: ^20.0.0 version: 20.19.30 @@ -512,6 +515,11 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -1823,6 +1831,10 @@ packages: peerDependencies: postcss: ^8.2.14 + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} @@ -2557,6 +2569,11 @@ snapshots: dependencies: tslib: 2.8.1 + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.19 + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -4061,6 +4078,11 @@ snapshots: postcss: 8.5.6 postcss-selector-parser: 6.1.2 + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0