feat(site): the tagline takes the title, and the em dashes go

The hero h1 is now "Precision Engineering for Everyone". The concrete offer
moves into the supporting line, which keeps the search terms the h1 gives up
and no longer restates the splash's opening sentence.

The title's gradient drifts instead of sweeping: a 300% ramp over 18s with
symmetric easing, so there is no hard turnaround at either end of the travel.

Em dashes are gone from every tsx/ts file (87 lines, 104 occurrences), per
AGENTS.md rule 9. Label lists and titles take a colon; appositives take
commas; independent clauses take a semicolon or a period. A few sentences
were restructured where no single mark read well.

Metadata keeps "Crypto" deliberately: it is a real service line and a real
search term, and it is no longer in the visible headline.
This commit is contained in:
jwinterm
2026-08-17 04:18:18 -04:00
parent 127081bcac
commit 0848fcabde
30 changed files with 98 additions and 94 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { useEffect } from 'react';
// Self-hosted, cookieless Umami — config via env so nothing is hardcoded.
// Self-hosted, cookieless Umami: config via env so nothing is hardcoded.
// Honors Global Privacy Control + Do-Not-Track. No-op unless both env vars are set.
const HOST = process.env.NEXT_PUBLIC_UMAMI_HOST;
const WEBSITE_ID = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
@@ -56,7 +56,7 @@ export function DynamicBackground({ nebulaPosition, visible = true }: DynamicBac
const effectiveTheme = resolvedTheme || systemTheme || 'dark';
const themeMode = effectiveTheme === 'dark' ? 'dark' : 'light';
// While the splash is active (visible === false) keep the nebula/monitor hidden —
// While the splash is active (visible === false) keep the nebula/monitor hidden;
// the splash provides its own Cherenkov glow. Reveal it once the visitor enters.
if (!showNebula || !visible) {
return <div className="fixed inset-0 z-0 h-screen w-screen bg-background" />;
@@ -121,7 +121,7 @@ export function HomeShell({ children }: { children: ReactNode }) {
const pin = () => window.scrollTo(0, 0);
window.addEventListener("scroll", pin, { passive: true });
// The gesture that triggered the enter keeps emitting MOMENTUM (a trackpad flick
// can trail for ~1.5s — longer than the 800ms wave). Releasing the lock on a fixed
// can trail for ~1.5s, longer than the 800ms wave). Releasing the lock on a fixed
// timer let those trailing ticks scroll the revealed page, so you landed below the
// hero instead of at the very top. Hold the lock until the input goes QUIET
// (250ms with no wheel/touch), capped at 3s so a deliberate scroller is never trapped.
@@ -4,7 +4,7 @@ import Link from "next/link";
import { HoverVignette } from "@/components/showcase/hover-vignette";
// Literal class strings per color so Tailwind's JIT keeps them (no dynamic interpolation).
// The colour tint + border only paint on hover/focus (via the `group/tile` link) — at rest
// The colour tint + border only paint on hover/focus (via the `group/tile` link); at rest
// the .bento-item CSS keeps the card chrome transparent so tiles read as title + render only.
const cardBg: Record<string, string> = {
orange: "group-hover/tile:!bg-orange-50/95 dark:group-hover/tile:!bg-orange-950/95 group-focus-within/tile:!bg-orange-50/95 dark:group-focus-within/tile:!bg-orange-950/95 group-hover/tile:!border-orange-500/20 group-focus-within/tile:!border-orange-500/20",
@@ -51,7 +51,7 @@ type Item = {
href: string;
external?: boolean;
/**
* Optional hover/focus "vignette" — a transparent alpha-WebM 3D render from the
* Optional hover/focus "vignette", a transparent alpha-WebM 3D render from the
* game/app that plays OVER this tile's static art on hover or keyboard focus.
* Purely additive: with no assets present (or on Safari / reduced-motion) the tile
* renders exactly as its static `image`. Asset convention:
@@ -71,11 +71,11 @@ const items: Item[] = [
// 3D smirk coin (our engine + sg_outline inset ink): rests smirk-up, hover tosses it with a
// decelerating end-over-end tumble (the suchoice coin-flip feel), lands $-up, half-flips home.
{ name: "Smirk Wallet", tagline: "Non-custodial browser wallet with social tipping.", color: "amber", span: 1, image: "/images/products/smirk-wallet.png", alt: "Smirk Wallet browser extension", href: "https://smirk.cash", external: true, vignette: { webm: "/showcase/vignettes/smirk.webm", poster: "/showcase/vignettes/smirk.png" } },
// Transparent chroma-keyed vignette (the engine can't emit real alpha — BW_FLAT magenta key):
// Transparent chroma-keyed vignette (the engine can't emit real alpha, hence the BW_FLAT magenta key):
// full-bloom tree at rest, hover retracts and regrows it. Palindrome loop, seam 0.0.
{ name: "Bloomword", tagline: "A word game where every answer grows the tree.", color: "green", span: 1, image: "/images/products/bloomword.png", alt: "Bloomword, a word game about growing a tree", href: "/products/bloomword", vignette: { webm: "/showcase/vignettes/bloomword.webm", poster: "/showcase/vignettes/bloomword.png" } },
// Single theme-independent asset: the INSET black silhouette stroke (sg_outline) sits ON the yellow letters, so one
// baked alpha render reads on both light and dark tiles — no per-theme pair needed (a 3D->web pipeline win).
// baked alpha render reads on both light and dark tiles; no per-theme pair needed (a 3D->web pipeline win).
{ name: "Barns & Neutrons", tagline: "Cozy puzzle expedition across the real Chart of Nuclides.", color: "amber", span: 2, image: "/images/products/barns-and-neutrons.svg", alt: "Barns & Neutrons, a game about the Chart of Nuclides", href: "/products/barns-and-neutrons", vignette: { webm: "/showcase/vignettes/barns.webm", poster: "/showcase/vignettes/barns.png" } },
// Cover reel from the vegan-IQ social pipeline: logo panel at rest, card-flips to a real
// quiz question (unanswered -> answered) on hover, flips back for a perfect loop seam.
@@ -83,8 +83,8 @@ const items: Item[] = [
];
function BentoCard({ item, delay }: { item: Item; delay: number }) {
// Feature tiles (a live vignette) drop the static screenshot — the transparent poster is the
// rest state — and grow + lift above their neighbours on hover so the animation is the focus.
// Feature tiles (a live vignette) drop the static screenshot, since the transparent poster is the
// rest state, and grow + lift above their neighbours on hover so the animation is the focus.
const feature = !!item.vignette;
const inner = (
<Card className={`h-full glass-card group min-h-[300px] rounded-[2.5rem] ${cardBg[item.color]} cursor-pointer`}>
@@ -72,7 +72,7 @@ function DemoCard({ demo }: { demo: Demo }) {
aria-label={`Open the ${demo.name} demo storefront in a new tab`}
>
<Card className={`glass-card h-full !p-0 overflow-hidden cursor-pointer transition-colors ${cardBorder[demo.color]}`}>
{/* Real screenshot of the live themed storefront — captured at 1280×800,
{/* Real screenshot of the live themed storefront, captured at 1280×800,
cropped to the hero/nav (the most brand-distinctive region). */}
<div className="h-40 w-full overflow-hidden">
<Image
@@ -16,7 +16,7 @@ type HoverVignetteProps = {
* decode VP9, still play the clip. Pointless for alpha overlays (H.264 carries no alpha). */
mp4?: string;
/** contain (default): transparent overlay letterboxed over the tile art. cover: an opaque reel that
* FILLS the tile (no padding) — the poster is the tile art. */
* FILLS the tile (no padding); the poster is the tile art. */
fit?: 'contain' | 'cover';
className?: string;
};
@@ -38,12 +38,12 @@ type HoverVignetteProps = {
* tile plays while it is substantially on screen (IntersectionObserver ≥ 0.5) and
* resets when it scrolls away. Same deferred download: nothing loads until the tile
* is actually seen.
* - `prefers-reduced-motion`: no listeners, no observer — the video never downloads and
* - `prefers-reduced-motion`: no listeners, no observer; the video never downloads and
* never plays. The poster is the whole experience.
* - Codec gate: play() is skipped unless the browser reports it can decode one of the
* provided sources (VP9 webm, or the optional H.264 mp4 for `cover` reels). Safari
* cannot decode VP9-alpha, so overlay tiles degrade to the transparent poster there
* — never a broken or opaque box.
* cannot decode VP9-alpha, so overlay tiles degrade to the transparent poster there,
* never a broken or opaque box.
* - `pointer-events-none` + `aria-hidden`: decorative; never blocks the tile's link or
* steals focus, and adds no layout (absolutely positioned, inset-0).
*/
@@ -94,7 +94,7 @@ export function HoverVignette({
try {
video.currentTime = 0;
} catch {
/* not yet loaded — nothing to reset */
/* not yet loaded: nothing to reset */
}
};
@@ -22,7 +22,7 @@ type ShowcaseVideoProps = {
* experience for those visitors.
* - `muted` + `playsInline` so mobile autoplay-on-view is permitted (one
* video at a time; this component is for a single hero/section clip, not a
* grid — a grid would gate to one visible at a time).
* grid: a grid would gate to one visible at a time).
*/
export function ShowcaseVideo({ webm, mp4, poster, label, className }: ShowcaseVideoProps) {
const ref = useRef<HTMLVideoElement>(null);
+2 -2
View File
@@ -28,7 +28,7 @@ export function StoreButtons({ links, extraLinks }: StoreButtonsProps) {
return (
<Button key={platform} variant="outline" size="lg" disabled>
<Icon className="h-5 w-5 mr-2" aria-hidden="true" />
{label} — Coming Soon
{label}: Coming Soon
</Button>
);
}
@@ -108,7 +108,7 @@ export function BauhausEchoStoreButtons() {
]}
extraLinks={[
{ href: "https://bauhaus.such.software", label: "Play on Web" },
{ href: "https://suchsoftware.itch.io/bauhaus-echo", label: "itch.io — $2" },
{ href: "https://suchsoftware.itch.io/bauhaus-echo", label: "itch.io: $2" },
]}
/>
);