feat: binary micro-interactions across the page

Section headlines are written out behind a column of digits, mono labels
resolve from binary, the portrait assembles itself from bits, section indexes
rattle and keep one bit alive, cards get corner marks, the header shows the
scroll position as an eight-bit number, pressed links and buttons shed a few
digits, and the work stage leans towards the pointer. All effects respect
reduced motion and leave the content readable without JavaScript.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 00:14:22 +02:00
co-authored by Claude Opus 5.5
parent 123ab8e354
commit dc4a01cbe4
17 changed files with 556 additions and 33 deletions
@@ -2,7 +2,7 @@
import * as React from "react";
import Image from "next/image";
import { motion } from "framer-motion";
import { motion, useMotionValue, useSpring } from "framer-motion";
export type WorkItem = {
readonly name: string;
@@ -88,10 +88,26 @@ const WorkStage: React.FC<{ item: WorkItem }> = ({ item }) => {
const active = inView && !still;
// The frames lean a few degrees towards the pointer.
const tiltX = useSpring(useMotionValue(0), { stiffness: 120, damping: 18 });
const tiltY = useSpring(useMotionValue(0), { stiffness: 120, damping: 18 });
const onPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
if (still || event.pointerType !== "mouse") return;
const box = event.currentTarget.getBoundingClientRect();
tiltY.set(((event.clientX - box.left) / box.width - 0.5) * 7);
tiltX.set(-((event.clientY - box.top) / box.height - 0.5) * 5);
};
const onPointerLeave = () => {
tiltX.set(0);
tiltY.set(0);
};
return (
<div
ref={ref}
className="relative overflow-hidden rounded-3xl bg-slate-900 px-5 pb-0 pt-8 md:px-12 md:pt-14"
onPointerMove={onPointerMove}
onPointerLeave={onPointerLeave}
className="relative overflow-hidden rounded-3xl bg-slate-900 px-5 pb-0 pt-8 [perspective:1400px] md:px-12 md:pt-14"
>
<div
className="pointer-events-none absolute inset-0 opacity-60"
@@ -102,7 +118,14 @@ const WorkStage: React.FC<{ item: WorkItem }> = ({ item }) => {
aria-hidden="true"
/>
<div className="relative mx-auto max-w-4xl">
<motion.div
className="relative mx-auto max-w-4xl"
style={{
rotateX: tiltX,
rotateY: tiltY,
transformStyle: "preserve-3d",
}}
>
{/* Desktop */}
<motion.div
initial={{ opacity: 0, y: 40 }}
@@ -150,7 +173,7 @@ const WorkStage: React.FC<{ item: WorkItem }> = ({ item }) => {
/>
</div>
</motion.div>
</div>
</motion.div>
</div>
);
};