From a5bd364062fd97fe416d01da7f9b84cd5ea00b9b Mon Sep 17 00:00:00 2001
From: SimpleCaci <92838396+SimpleCaci@users.noreply.github.com>
Date: Wed, 15 Jul 2026 17:30:48 -0700
Subject: [PATCH] feat: restore playful field interactions
---
css/coolness.css | 176 ++++++++++++++++++++++++++++++++++++++++++++++
js/coolness.js | 177 +++++++++++++++++++++++++++++++++++++++++++++++
js/main.js | 14 +++-
3 files changed, 366 insertions(+), 1 deletion(-)
create mode 100644 css/coolness.css
create mode 100644 js/coolness.js
diff --git a/css/coolness.css b/css/coolness.css
new file mode 100644
index 0000000..0b77b62
--- /dev/null
+++ b/css/coolness.css
@@ -0,0 +1,176 @@
+/* Progressive visual layer: the portfolio remains complete without this file. */
+body.coolness-ready { --pointer-x: 50vw; --pointer-y: 50vh; }
+body.coolness-ready::after {
+ position: fixed;
+ inset: 0;
+ z-index: -1;
+ background: radial-gradient(circle 32rem at var(--pointer-x) var(--pointer-y), rgba(164, 79, 50, .09), transparent 70%);
+ content: "";
+ pointer-events: none;
+}
+::selection { background: var(--rust); color: var(--paper); }
+
+.field-coordinates {
+ display: inline-flex;
+ min-width: 7.5rem;
+ align-items: center;
+ gap: .45rem;
+ color: var(--muted);
+ font: .58rem var(--mono);
+ letter-spacing: .06em;
+ white-space: nowrap;
+}
+.field-coordinates::before {
+ width: .42rem;
+ height: .42rem;
+ border-radius: 50%;
+ background: var(--rust);
+ box-shadow: 0 0 0 .22rem rgba(164, 79, 50, .14);
+ content: "";
+ animation: signal-pulse 2.4s ease-in-out infinite;
+}
+
+.hero-card { transition: transform 260ms cubic-bezier(.22, 1, .36, 1); transform: perspective(60rem) rotate(1deg) rotateX(var(--field-tilt-y, 0deg)) rotateY(var(--field-tilt-x, 0deg)); }
+.field-map::before { animation: orbit-drift 12s linear infinite; }
+.field-map::after { animation: signal-pulse 2s ease-in-out infinite; }
+.field-map .map-route {
+ position: absolute;
+ inset: 13% 9%;
+ border: 1px solid var(--blue);
+ border-right-color: transparent;
+ border-radius: 46% 54% 58% 42%;
+ opacity: .55;
+ transform: rotate(24deg);
+}
+.field-map .map-route::after {
+ position: absolute;
+ top: 4%;
+ right: 15%;
+ width: .45rem;
+ height: .45rem;
+ border: 2px solid var(--blue);
+ border-radius: 50%;
+ background: var(--paper);
+ content: "";
+}
+.field-map .map-label { position: absolute; right: .55rem; bottom: .45rem; color: var(--muted); font: .53rem var(--mono); letter-spacing: .07em; }
+
+.cool-reveal { opacity: 1; transform: none; }
+.coolness-ready .cool-reveal:not(.is-revealed) { opacity: 0; transform: translateY(1.35rem) rotate(.3deg); }
+.coolness-ready .cool-reveal { transition: opacity 620ms ease, transform 620ms cubic-bezier(.22, 1, .36, 1); }
+.coolness-ready .cool-reveal:nth-child(2n) { transition-delay: 70ms; }
+
+.case-card {
+ transform: perspective(80rem) rotateX(var(--card-tilt-x, 0deg)) rotateY(var(--card-tilt-y, 0deg));
+ transform-style: preserve-3d;
+ transition: border-color 180ms, box-shadow 180ms, transform 220ms cubic-bezier(.22, 1, .36, 1);
+}
+.case-card.is-tilting { border-color: color-mix(in srgb, var(--accent), var(--text) 28%); box-shadow: 0 1.8rem 4.5rem rgba(55, 42, 25, .16); }
+.case-card.is-tilting .case-body { transform: translateZ(1rem); }
+.case-body { transition: transform 220ms cubic-bezier(.22, 1, .36, 1); }
+.case-visual { isolation: isolate; }
+.case-visual::before, .case-visual::after, .case-initials { transition: opacity 180ms; }
+.case-visual.has-scene::before, .case-visual.has-scene::after, .case-visual.has-scene .case-initials { opacity: 0; }
+.project-scene { position: relative; z-index: 2; width: min(92%, 25rem); color: var(--accent); filter: drop-shadow(0 .8rem 1.4rem rgba(49, 40, 27, .12)); }
+.project-scene * { vector-effect: non-scaling-stroke; }
+.scene-line { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
+.scene-faint { fill: none; stroke: currentColor; stroke-width: 1; opacity: .35; }
+.scene-fill { fill: currentColor; opacity: .12; stroke: currentColor; stroke-width: 1.5; }
+.scene-dot { fill: var(--rust); }
+.scene-paper { fill: var(--surface); stroke: currentColor; stroke-width: 1.5; }
+.scene-orbit { transform-origin: center; animation: scene-orbit 9s linear infinite; }
+.scene-dash { stroke-dasharray: 8 8; animation: dash-travel 8s linear infinite; }
+.scene-scan { animation: scan-page 2.8s ease-in-out infinite; }
+.scene-wave { transform-origin: center; animation: wave-breathe 1.9s ease-in-out infinite; }
+.scene-blink { animation: scene-blink 2.4s step-end infinite; }
+.scene-limb { transform-origin: center; animation: pose-shift 4s ease-in-out infinite; }
+
+.cow-companion {
+ position: fixed;
+ right: 1rem;
+ bottom: 1rem;
+ z-index: 40;
+ display: grid;
+ width: 4.25rem;
+ height: 4.25rem;
+ place-items: center;
+ padding: .45rem;
+ border: 1px solid var(--line);
+ border-radius: 50%;
+ background: var(--paper-deep);
+ box-shadow: var(--shadow);
+ cursor: pointer;
+ transition: transform 180ms cubic-bezier(.22, 1, .36, 1);
+}
+.cow-companion:hover { transform: rotate(-5deg) scale(1.07); }
+.cow-companion img { width: 100%; height: 100%; object-fit: contain; filter: grayscale(1) contrast(1.1); mix-blend-mode: multiply; }
+.cow-companion.is-signaling { animation: cow-pop 520ms cubic-bezier(.2, .9, .2, 1); }
+.cow-message {
+ position: fixed;
+ right: 1rem;
+ bottom: 6rem;
+ z-index: 39;
+ max-width: 15rem;
+ padding: .7rem .85rem;
+ border: 1px solid var(--line);
+ border-radius: var(--radius);
+ background: var(--surface);
+ box-shadow: var(--shadow);
+ color: var(--text);
+ font: .64rem/1.45 var(--mono);
+ opacity: 0;
+ transform: translateY(.5rem) rotate(1deg);
+ transition: opacity 180ms, transform 180ms;
+ pointer-events: none;
+}
+.cow-message.is-visible { opacity: 1; transform: translateY(0) rotate(-1deg); }
+
+.cow-trail {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 80;
+ width: 2rem;
+ height: 2rem;
+ opacity: 0;
+ transform: translate3d(-5rem, -5rem, 0) rotate(var(--trail-rotate, 0deg));
+ transition: opacity 140ms;
+ pointer-events: none;
+ will-change: transform;
+}
+.cow-trail.is-active { opacity: .62; }
+.cow-trail img { width: 100%; height: 100%; object-fit: contain; filter: grayscale(1) sepia(.3) contrast(1.15); }
+
+.page-wipe {
+ position: fixed;
+ inset: 0;
+ z-index: 90;
+ display: grid;
+ place-items: center;
+ background: var(--ink);
+ color: var(--paper);
+ clip-path: inset(0 100% 0 0);
+ pointer-events: none;
+ transition: clip-path 260ms cubic-bezier(.75, 0, .25, 1);
+}
+.page-wipe::before { width: 115%; height: .85rem; background: url("../assets/images/rope.png") center 48% / 5rem auto repeat-x; content: ""; filter: sepia(.4); transform: rotate(-2deg); }
+.page-wipe.is-active { clip-path: inset(0 0 0 0); }
+
+@keyframes signal-pulse { 0%, 100% { transform: scale(.8); opacity: .55; } 50% { transform: scale(1.16); opacity: 1; } }
+@keyframes orbit-drift { to { transform: rotate(342deg); } }
+@keyframes scene-orbit { to { transform: rotate(360deg); } }
+@keyframes dash-travel { to { stroke-dashoffset: -64; } }
+@keyframes scan-page { 0%, 100% { transform: translateY(-1.2rem); opacity: .35; } 50% { transform: translateY(1.5rem); opacity: 1; } }
+@keyframes wave-breathe { 0%, 100% { transform: scaleY(.7); } 50% { transform: scaleY(1.15); } }
+@keyframes scene-blink { 0%, 42% { opacity: 1; } 43%, 50% { opacity: .1; } 51%, 100% { opacity: 1; } }
+@keyframes pose-shift { 0%, 100% { transform: rotate(-2deg); } 50% { transform: rotate(4deg); } }
+@keyframes cow-pop { 0% { transform: scale(1) rotate(0); } 40% { transform: scale(1.23) rotate(-12deg); } 72% { transform: scale(.94) rotate(7deg); } 100% { transform: scale(1) rotate(0); } }
+
+@media (max-width: 48rem) { .field-coordinates, .cow-trail { display: none; } .cow-companion { width: 3.5rem; height: 3.5rem; } .cow-message { bottom: 5.2rem; } }
+@media (pointer: coarse) { .cow-trail { display: none; } .case-card { transform: none !important; } }
+@media (prefers-reduced-motion: reduce) {
+ body.coolness-ready::after, .cow-trail, .page-wipe { display: none; }
+ .field-coordinates::before, .field-map::before, .field-map::after, .project-scene *, .cow-companion { animation: none !important; }
+ .coolness-ready .cool-reveal:not(.is-revealed) { opacity: 1; transform: none; }
+ .case-card, .hero-card { transform: none !important; }
+}
diff --git a/js/coolness.js b/js/coolness.js
new file mode 100644
index 0000000..5b2eab7
--- /dev/null
+++ b/js/coolness.js
@@ -0,0 +1,177 @@
+(() => {
+ const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
+ const finePointer = window.matchMedia("(pointer: fine)");
+ const script = document.currentScript;
+ const siteRoot = script ? new URL("../", script.src) : new URL("../", window.location.href);
+ const asset = (path) => new URL(path, siteRoot).href;
+
+ document.body.classList.add("coolness-ready");
+
+ const header = document.querySelector(".site-header");
+ if (header) {
+ const coordinates = document.createElement("span");
+ coordinates.className = "field-coordinates";
+ coordinates.textContent = "POINTER 000 / 000";
+ coordinates.setAttribute("aria-hidden", "true");
+ header.querySelector(".wordmark")?.after(coordinates);
+ document.addEventListener("pointermove", (event) => {
+ document.body.style.setProperty("--pointer-x", `${event.clientX}px`);
+ document.body.style.setProperty("--pointer-y", `${event.clientY}px`);
+ coordinates.textContent = `POINTER ${String(Math.round(event.clientX)).padStart(3, "0")} / ${String(Math.round(event.clientY)).padStart(3, "0")}`;
+ }, { passive: true });
+ }
+
+ const map = document.querySelector(".field-map");
+ if (map) {
+ const route = document.createElement("span");
+ route.className = "map-route";
+ const label = document.createElement("span");
+ label.className = "map-label";
+ label.textContent = "SIGNAL ACQUIRED";
+ map.append(route, label);
+ if (!reducedMotion.matches && finePointer.matches) {
+ map.closest(".hero-card")?.addEventListener("pointermove", (event) => {
+ const card = event.currentTarget;
+ const bounds = card.getBoundingClientRect();
+ card.style.setProperty("--field-tilt-x", `${((event.clientX - bounds.left) / bounds.width - .5) * 4}deg`);
+ card.style.setProperty("--field-tilt-y", `${((event.clientY - bounds.top) / bounds.height - .5) * -4}deg`);
+ });
+ }
+ }
+
+ const sceneMarkup = {
+ "Solar System Missions": ``,
+ "Gesture-Reactive Avatar": ``,
+ "Ambient Dashboard": ``,
+ "HandwritingConverter": ``,
+ "VoxNav": ``,
+ "Yoga Pose Match": ``
+ };
+
+ function hydrateScene(card) {
+ if (card.dataset.sceneReady === "true") return;
+ const title = card.querySelector("h2")?.textContent?.trim();
+ const markup = sceneMarkup[title];
+ const visual = card.querySelector(".case-visual");
+ if (!markup || !visual) return;
+ const documentFragment = new DOMParser().parseFromString(markup, "image/svg+xml").documentElement;
+ visual.append(document.importNode(documentFragment, true));
+ visual.classList.add("has-scene");
+ card.dataset.sceneReady = "true";
+ }
+
+ function enhanceCard(card) {
+ hydrateScene(card);
+ card.classList.add("cool-reveal");
+ reveal(card);
+ if (reducedMotion.matches || !finePointer.matches) return;
+ card.addEventListener("pointermove", (event) => {
+ const bounds = card.getBoundingClientRect();
+ card.style.setProperty("--card-tilt-y", `${((event.clientX - bounds.left) / bounds.width - .5) * 2.6}deg`);
+ card.style.setProperty("--card-tilt-x", `${((event.clientY - bounds.top) / bounds.height - .5) * -2.6}deg`);
+ card.classList.add("is-tilting");
+ });
+ card.addEventListener("pointerleave", () => {
+ card.style.removeProperty("--card-tilt-x");
+ card.style.removeProperty("--card-tilt-y");
+ card.classList.remove("is-tilting");
+ });
+ }
+
+ let observer;
+ function reveal(element) {
+ if (reducedMotion.matches || !("IntersectionObserver" in window)) {
+ element.classList.add("is-revealed");
+ return;
+ }
+ observer ||= new IntersectionObserver((entries) => {
+ entries.forEach((entry) => {
+ if (!entry.isIntersecting) return;
+ entry.target.classList.add("is-revealed");
+ observer.unobserve(entry.target);
+ });
+ }, { threshold: .12, rootMargin: "0px 0px -5%" });
+ observer.observe(element);
+ }
+
+ document.querySelectorAll("main > section, .preview, .featured").forEach((element) => {
+ element.classList.add("cool-reveal");
+ reveal(element);
+ });
+ document.querySelectorAll(".case-card").forEach(enhanceCard);
+ const projectList = document.querySelector("[data-project-list]");
+ if (projectList) {
+ new MutationObserver((mutations) => mutations.forEach((mutation) => mutation.addedNodes.forEach((node) => {
+ if (node instanceof HTMLElement && node.matches(".case-card")) enhanceCard(node);
+ }))).observe(projectList, { childList: true });
+ }
+
+ const companion = document.createElement("button");
+ companion.className = "cow-companion";
+ companion.type = "button";
+ companion.setAttribute("aria-label", "Ask the field assistant for a signal");
+ const cow = document.createElement("img");
+ cow.src = asset("assets/images/cow.png");
+ cow.alt = "";
+ companion.append(cow);
+ const message = document.createElement("div");
+ message.className = "cow-message";
+ message.setAttribute("role", "status");
+ message.setAttribute("aria-live", "polite");
+ const messages = ["Signal acquired. Keep building.", "Cow-approved system boundary.", "Prototype honestly. Iterate loudly.", "Useful beats impressive.", "Moo is a valid debugging strategy."];
+ let messageIndex = -1;
+ let messageTimer;
+ companion.addEventListener("click", () => {
+ messageIndex = (messageIndex + 1) % messages.length;
+ message.textContent = messages[messageIndex];
+ message.classList.add("is-visible");
+ companion.classList.remove("is-signaling");
+ requestAnimationFrame(() => companion.classList.add("is-signaling"));
+ clearTimeout(messageTimer);
+ messageTimer = setTimeout(() => message.classList.remove("is-visible"), 3200);
+ });
+ document.body.append(message, companion);
+
+ if (!reducedMotion.matches && finePointer.matches) {
+ const trail = document.createElement("div");
+ trail.className = "cow-trail";
+ const image = document.createElement("img");
+ image.src = asset("assets/images/cow.png");
+ image.alt = "";
+ trail.append(image);
+ document.body.append(trail);
+ let targetX = -80, targetY = -80, x = targetX, y = targetY, rotation = 0, frame;
+ const tick = () => {
+ x += (targetX - x) * .18;
+ y += (targetY - y) * .18;
+ rotation += ((targetX - x) * .12 - rotation) * .12;
+ trail.style.transform = `translate3d(${x + 15}px, ${y + 18}px, 0) rotate(${rotation}deg)`;
+ if (Math.abs(targetX - x) + Math.abs(targetY - y) > .6) frame = requestAnimationFrame(tick);
+ else frame = null;
+ };
+ document.addEventListener("pointermove", (event) => {
+ targetX = event.clientX;
+ targetY = event.clientY;
+ trail.classList.add("is-active");
+ if (!frame) frame = requestAnimationFrame(tick);
+ }, { passive: true });
+ document.addEventListener("pointerleave", () => trail.classList.remove("is-active"));
+ document.addEventListener("visibilitychange", () => { if (document.hidden && frame) { cancelAnimationFrame(frame); frame = null; } });
+ }
+
+ if (!reducedMotion.matches) {
+ const wipe = document.createElement("div");
+ wipe.className = "page-wipe";
+ wipe.setAttribute("aria-hidden", "true");
+ document.body.append(wipe);
+ document.addEventListener("click", (event) => {
+ const link = event.target.closest("a[href]");
+ if (!link || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || link.target === "_blank" || link.hasAttribute("download")) return;
+ const destination = new URL(link.href, window.location.href);
+ if (destination.origin !== window.location.origin || destination.pathname === window.location.pathname && destination.hash) return;
+ event.preventDefault();
+ wipe.classList.add("is-active");
+ setTimeout(() => { window.location.href = destination.href; }, 260);
+ });
+ }
+})();
diff --git a/js/main.js b/js/main.js
index 6e49e4d..31f5aef 100644
--- a/js/main.js
+++ b/js/main.js
@@ -5,4 +5,16 @@ document.querySelectorAll("[data-current-year]").forEach((element) => {
document.querySelectorAll(".mobile-nav a").forEach((link) => {
link.addEventListener("click", () => link.closest("details")?.removeAttribute("open"));
});
-
+
+const mainScript = document.currentScript;
+if (mainScript) {
+ const style = document.createElement("link");
+ style.rel = "stylesheet";
+ style.href = new URL("../css/coolness.css", mainScript.src).href;
+ document.head.append(style);
+
+ const coolness = document.createElement("script");
+ coolness.src = new URL("coolness.js", mainScript.src).href;
+ coolness.defer = true;
+ document.head.append(coolness);
+}