Add ToDo-Karten product page, fluid design tokens, and Navbar/Hero fixes

New /todo-cards page (Hero, How-it-works, Focus, Testimonials, Pricing)
built with the fluid clamp() token system (app/lib/fluid.ts) and
scroll-reveal animations (app/components/Reveal.tsx), matching styling
consistency with /challenge's testimonial section.

Navbar: page-aware active state and anchor-link navigation from any
route (not just "/"), fixed logo click to actually navigate instead of
silently rewriting the URL, fluid nav text size, custom hash-scroll
handling for cross-page anchor links.

Hero: responsive breakpoint fix for the text/image split at Tablet
widths, entrance animation for the brand's orange dot, removed a red
dot artifact from hero.png.

Also includes prior uncommitted work on About/Blog/Newsletter/Footer
and the cart lib (app/lib/cart.ts).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-19 11:34:52 +00:00
parent 35670126ca
commit e4f0c66d7b
42 changed files with 1635 additions and 363 deletions
+56
View File
@@ -0,0 +1,56 @@
"use client";
import { useEffect, useState } from "react";
const CART_KEY = "ep_cart";
const CART_EVENT = "ep-cart-updated";
type CartItem = { id: string; qty: number };
function readCart(): CartItem[] {
if (typeof window === "undefined") return [];
try {
const raw = window.localStorage.getItem(CART_KEY);
return raw ? JSON.parse(raw) : [];
} catch {
return [];
}
}
function writeCart(items: CartItem[]) {
window.localStorage.setItem(CART_KEY, JSON.stringify(items));
window.dispatchEvent(new Event(CART_EVENT));
}
export function getCartCount(): number {
return readCart().reduce((sum, item) => sum + item.qty, 0);
}
export function addToCart(id: string, qty = 1) {
const items = readCart();
const existing = items.find((i) => i.id === id);
if (existing) existing.qty += qty;
else items.push({ id, qty });
writeCart(items);
}
// Reactive cart item count — 0 until the client hydrates and reads
// localStorage, then stays in sync across tabs ("storage") and same-tab
// updates (the CART_EVENT dispatched by addToCart, which "storage" alone
// doesn't fire for the tab that made the change).
export function useCartCount(): number {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(getCartCount());
const onUpdate = () => setCount(getCartCount());
window.addEventListener(CART_EVENT, onUpdate);
window.addEventListener("storage", onUpdate);
return () => {
window.removeEventListener(CART_EVENT, onUpdate);
window.removeEventListener("storage", onUpdate);
};
}, []);
return count;
}