mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-17 05:32:05 +02:00
feat(site): one voice, brand gradients, a lighter splash gate, denser portfolio
- Voice unified on the title-tag register: the homepage h1 now says what we sell (Custom E-commerce, Apps & Crypto Tools); 'Precision engineering for everyone' is demoted to the supporting line. The structured-data and manifest descriptions now match the OG description instead of carrying their own registers. - The off-palette cyan/blue gradients are gone: .hero-title and the new .brand-gradient-text use the four-colour system (light palette on light, dark palette on dark, per the pack). Splash cover text, services and portfolio headings, the hero aura, and button shadows all follow. - The splash now dismisses once 40% scrolled past instead of requiring the whole screen to leave the viewport (it was already once-per-session and reduced-motion-skipping). - Portfolio: heading 'Portfolio', subtitle 'Products & projects', meta title to match; grid grows to three columns on xl in a max-w-6xl container. - anim3_matrix slimmed: single-layer glow, unfiltered crackle and orbs, and the border cycles stroke colour instead of animating filter:hue-rotate -- the filter animation forced a full filter re-render per frame and pegged a core on the page hosting it.
This commit is contained in:
@@ -286,14 +286,29 @@
|
||||
/* Hero title: an animated gradient sheen. LCP-safe — the text paints immediately with the
|
||||
gradient; only the gradient POSITION animates, which never delays the largest paint. The
|
||||
global reduced-motion rule freezes it to the base gradient. */
|
||||
/* The brand's four-colour system (S green, U magenta, C blue, H teal), one per glyph.
|
||||
Light theme uses the light palette; .dark swaps in the dark palette, which is the
|
||||
same hues lifted for contrast on near-black -- from packs/such-software/pack.json. */
|
||||
.hero-title {
|
||||
background-image: linear-gradient(110deg, #22d3ee 0%, #3b82f6 30%, #7dd3fc 50%, #3b82f6 70%, #22d3ee 100%);
|
||||
background-image: linear-gradient(110deg, #1a5c38 0%, #b83670 30%, #176a92 55%, #12857e 80%, #1a5c38 100%);
|
||||
background-size: 220% auto;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
animation: hero-sheen 9s ease-in-out infinite;
|
||||
}
|
||||
.dark .hero-title {
|
||||
background-image: linear-gradient(110deg, #35925c 0%, #e8689e 30%, #4aade0 55%, #3ad4c6 80%, #35925c 100%);
|
||||
}
|
||||
.brand-gradient-text {
|
||||
background-image: linear-gradient(90deg, #1a5c38, #b83670, #176a92, #12857e);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
.dark .brand-gradient-text {
|
||||
background-image: linear-gradient(90deg, #35925c, #e8689e, #4aade0, #3ad4c6);
|
||||
}
|
||||
@keyframes hero-sheen {
|
||||
0%, 100% { background-position: 0% center; }
|
||||
50% { background-position: 100% center; }
|
||||
|
||||
@@ -59,7 +59,7 @@ const structuredData = {
|
||||
name: "Such Software LLC",
|
||||
url: "https://such.software",
|
||||
logo: "https://such.software/images/branding/icon-512.png",
|
||||
description: "E-commerce infrastructure, custom app development, and accessible web applications. Engineered for scale, compliance, and performance.",
|
||||
description: "A Kennett Square, PA software studio building custom online stores, web apps, mobile games, and Monero & crypto tools.",
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
streetAddress: "110 E State St, Suite 300",
|
||||
|
||||
@@ -21,23 +21,23 @@ export default function Home() {
|
||||
{/* Decorative aura behind the title (aria-hidden, LCP-safe, motion-safe). */}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="hero-aura pointer-events-none absolute left-1/2 top-24 sm:top-16 -z-10 h-64 w-[46rem] max-w-[92vw] rounded-full bg-cyan-400/25 blur-3xl"
|
||||
className="hero-aura pointer-events-none absolute left-1/2 top-24 sm:top-16 -z-10 h-64 w-[46rem] max-w-[92vw] rounded-full bg-[#12857e]/25 blur-3xl"
|
||||
/>
|
||||
<h1
|
||||
id="hero-title"
|
||||
className="hero-title text-4xl sm:text-5xl md:text-7xl font-bold mb-6 pb-2"
|
||||
style={{ maxWidth: "min(calc(90vw - 120px), 100%)" }}
|
||||
>
|
||||
<span className="block">Precision Engineering</span>
|
||||
<span className="block">for Everyone</span>
|
||||
<span className="block">Custom E-commerce,</span>
|
||||
<span className="block">Apps & Crypto Tools</span>
|
||||
</h1>
|
||||
<p className="max-w-2xl text-xl text-muted-foreground mb-8">
|
||||
Custom e-commerce, websites, apps, and consulting. Plus our own mobile apps and games. A software studio in Kennett Square, PA.
|
||||
Websites, games, payments, and consulting too — plus our own mobile apps. A software studio in Kennett Square, PA. Precision engineering for everyone.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-4 mb-14">
|
||||
<Link href="/products">
|
||||
<Button size="lg" className="rounded-full shadow-lg shadow-cyan-500/25 hover:shadow-cyan-400/40 hover:scale-[1.03] transition-all">View Portfolio</Button>
|
||||
<Button size="lg" className="rounded-full shadow-lg shadow-[#12857e]/25 hover:shadow-[#3ad4c6]/40 hover:scale-[1.03] transition-all">View Portfolio</Button>
|
||||
</Link>
|
||||
<Link href="#contact">
|
||||
<Button variant="outline" size="lg" className="rounded-full hover:scale-[1.03] transition-all">Start a Project</Button>
|
||||
|
||||
@@ -4,11 +4,11 @@ import { ProductsGrid, type Product } from "@/components/portfolio/products-grid
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Products: Crypto Tools, Games & Commerce | Such Software",
|
||||
title: "Portfolio: Products & Projects | Such Software",
|
||||
description: "Explore Such Software's products: Wownerogue Monero roguelike, Neroswap crypto aggregator, Smirk wallet, headless Webshops, our mobile games, and nuclear games in development.",
|
||||
alternates: { canonical: "/products" },
|
||||
openGraph: {
|
||||
title: "Products: Crypto Tools, Games & Commerce | Such Software",
|
||||
title: "Portfolio: Products & Projects | Such Software",
|
||||
description: "Explore Such Software's products: Wownerogue Monero roguelike, Neroswap crypto aggregator, Smirk wallet, headless Webshops, our mobile games, and nuclear games in development.",
|
||||
type: "website",
|
||||
},
|
||||
@@ -146,13 +146,13 @@ 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 id="main-content" className="z-10 w-full max-w-6xl 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">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 brand-gradient-text">
|
||||
Portfolio
|
||||
</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
|
||||
The tools and apps we have shipped, from commerce backends to consumer games, plus three nuclear-physics games in development.
|
||||
Products & projects — from commerce backends to consumer games, plus three nuclear-physics games in development.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function ServicesPage() {
|
||||
<div id="main-content" className="z-10 w-full max-w-5xl mx-auto py-20 px-4">
|
||||
{/* Page header */}
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 pb-3 bg-clip-text text-transparent bg-gradient-to-r from-emerald-400 to-blue-500">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6 pb-3 brand-gradient-text">
|
||||
What we build
|
||||
</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
|
||||
|
||||
@@ -196,7 +196,7 @@ export function HeroSplash({ onEnter, sectionRef, leaving }: { onEnter: () => vo
|
||||
|
||||
{/* Cover text hidden on mobile (logo + corner icons carry it there); fades out on enter */}
|
||||
<p
|
||||
className={`mt-10 hidden sm:block bg-gradient-to-r from-cyan-400 to-blue-600 bg-clip-text pb-1 text-3xl font-bold text-transparent sm:text-4xl md:text-5xl transition-opacity duration-700 motion-safe:animate-in motion-safe:fade-in ${entering ? "opacity-0" : "opacity-100"}`}
|
||||
className={`mt-10 hidden sm:block brand-gradient-text pb-1 text-3xl font-bold text-transparent sm:text-4xl md:text-5xl transition-opacity duration-700 motion-safe:animate-in motion-safe:fade-in ${entering ? "opacity-0" : "opacity-100"}`}
|
||||
style={{ animationDuration: "700ms", animationDelay: "120ms", animationFillMode: "backwards" }}
|
||||
>
|
||||
Precision Engineering for Everyone
|
||||
|
||||
@@ -49,16 +49,18 @@ export function HomeShell({ children }: { children: ReactNode }) {
|
||||
if (entered) return;
|
||||
const el = splashRef.current;
|
||||
if (!el) return;
|
||||
// Enter once 40% of the splash has scrolled by: requiring the whole screen to
|
||||
// leave the viewport made the reveal feel like a toll booth.
|
||||
const io = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (!entry.isIntersecting) {
|
||||
if (entry.intersectionRatio < 0.6) {
|
||||
restoreScroll.current = Math.max(0, window.scrollY - el.offsetHeight);
|
||||
sessionStorage.setItem("such:entered", "1");
|
||||
setEntered(true);
|
||||
setSplashUp(false); // scrolled past: just unmount, no overlay fade needed
|
||||
}
|
||||
},
|
||||
{ threshold: 0 },
|
||||
{ threshold: [0, 0.6] },
|
||||
);
|
||||
io.observe(el);
|
||||
return () => io.disconnect();
|
||||
|
||||
@@ -133,16 +133,16 @@ export function ProductsGrid({ products }: { products: Product[] }) {
|
||||
id="product-sort"
|
||||
value={sort}
|
||||
onChange={(e) => setSort(e.target.value as SortKey)}
|
||||
className="rounded-lg border border-border bg-background px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-cyan-400/50"
|
||||
className="rounded-lg border border-border bg-background px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-[#3ad4c6]/50"
|
||||
>
|
||||
<option value="release">Release date (newest)</option>
|
||||
<option value="name">Name (A–Z)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
<div className="grid md:grid-cols-2 xl:grid-cols-3 gap-8">
|
||||
{sorted.map((product, i) => (
|
||||
<ScrollReveal key={product.slug} delay={(i % 2) * 0.08}>
|
||||
<ScrollReveal key={product.slug} delay={(i % 3) * 0.08}>
|
||||
<Link href={`/products/${product.slug}`} className="block h-full">
|
||||
<div className="h-full cursor-pointer">
|
||||
<ProductCardInner product={product} />
|
||||
|
||||
Reference in New Issue
Block a user