patx/afterdarklabs

indexerd.html 2

Commit 9bd06c7 · patx · 2026-09-03T01:13:04-04:00

Changeset
9bd06c7d6eee54f8606528940258472e6924af04
Parents
73c995dc80e7992ce7d742b4d6799250e199deb9

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/docs/indexerd.html b/docs/indexerd.html
index 123ee56..b9a801d 100644
--- a/docs/indexerd.html
+++ b/docs/indexerd.html
@@ -74,8 +74,7 @@
                 font-size: 125%;
             }
 
-            nav img[src*="/logo_assets/mark"],
-            footer img[src*="/logo_assets/mark"] {
+            nav img[src*="/logo_assets/mark"] {
                 width: 3rem;
                 height: 3rem;
             }
@@ -202,30 +201,62 @@
         }
 
         .nav-brand-ready .nav-brand {
-            visibility: hidden;
-            opacity: 0;
-            clip-path: inset(20% 100% 20% 0 round 1rem);
-            filter: blur(0.3rem);
+            visibility: visible;
+            opacity: 1;
             pointer-events: none;
-            transform: translate3d(-1.5rem, 0, 0) skewX(-10deg) scaleX(0.45);
-            transition:
-                opacity 160ms ease,
-                transform 280ms ease,
-                filter 220ms ease,
-                clip-path 280ms ease,
-                visibility 0s linear 280ms;
-            will-change: transform, opacity, clip-path, filter;
+            transform: none;
         }
 
         .nav-brand-ready .nav-brand.is-visible {
-            visibility: visible;
-            opacity: 1;
-            clip-path: none;
-            filter: none;
             pointer-events: auto;
-            transform: none;
-            animation: nav-brand-swish-in 720ms cubic-bezier(0.16, 1, 0.3, 1);
-            transition: none;
+        }
+
+        .nav-cta {
+            display: inline-flex;
+            min-height: 3.25rem;
+            align-items: center;
+            justify-content: center;
+            padding: 0.75rem 1.15rem;
+            font-size: clamp(1.05rem, 5vw, 1.3rem);
+            line-height: 1;
+            letter-spacing: -0.035em;
+        }
+
+        .nav-brand-word {
+            display: flex;
+            padding-right: 0.08em;
+            font-size: clamp(2.3rem, 11vw, 2.8rem);
+            line-height: 0.76;
+            letter-spacing: -0.08em;
+        }
+
+        .nav-brand-letter {
+            display: inline-block;
+            opacity: var(--nav-letter-opacity, 1);
+            filter: blur(var(--nav-letter-blur, 0));
+            transform:
+                translate3d(var(--nav-letter-x, 0), var(--nav-letter-y, 0), 0)
+                rotate(var(--nav-letter-rotate, 0))
+                skewX(var(--nav-letter-skew, 0))
+                scale(var(--nav-letter-scale-x, 1), var(--nav-letter-scale-y, 1));
+            transform-origin: center;
+            will-change: opacity, filter, transform;
+        }
+
+        .nav-brand-ready .nav-brand-letter {
+            opacity: var(--nav-letter-opacity, 0);
+        }
+
+        @media (min-width: 1024px) {
+            .nav-brand-word {
+                font-size: clamp(2.3rem, 4vw, 3.3rem);
+            }
+
+            .nav-cta {
+                min-height: 2.75rem;
+                padding: 0.72rem 1.35rem;
+                font-size: clamp(1.2rem, 2vw, 1.6rem);
+            }
         }
 
         .hero-vignette {
@@ -269,33 +300,13 @@
             letter-spacing: -0.075em;
         }
 
-        @keyframes nav-brand-swish-in {
-            0% {
-                opacity: 0;
-                clip-path: polygon(0 43%, 0 43%, 0 57%, 0 57%);
-                filter: blur(0.45rem);
-                transform: translate3d(-3rem, 0.25rem, 0) skewX(-22deg) scale(0.3, 1.28);
-            }
-            38% {
-                opacity: 1;
-                clip-path: polygon(0 -7%, 104% 6%, 97% 108%, -2% 91%);
-                filter: blur(0);
-                transform: translate3d(0.7rem, -0.08rem, 0) skewX(8deg) scale(1.08, 0.88);
-            }
-            58% {
-                clip-path: polygon(0 5%, 98% -4%, 103% 95%, 2% 103%);
-                transform: translate3d(-0.3rem, 0.04rem, 0) skewX(-5deg) scale(0.96, 1.08);
-            }
-            78% {
-                clip-path: polygon(0 -1%, 101% 2%, 99% 101%, 0 98%);
-                transform: translate3d(0.12rem, 0, 0) skewX(2deg) scale(1.02, 0.98);
-            }
-            100% {
-                opacity: 1;
-                clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
-                filter: blur(0);
-                transform: none;
-            }
+        .hero-letter {
+            display: inline-block;
+            opacity: var(--hero-letter-opacity, 1);
+            filter: blur(var(--hero-letter-blur, 0));
+            transform: scale(var(--hero-letter-scale, 1));
+            transform-origin: center;
+            will-change: opacity, filter, transform;
         }
 
         @keyframes neon-letter-flicker {
@@ -331,11 +342,14 @@
         .work-intro-stage {
             position: sticky;
             top: var(--site-nav-height);
+            z-index: 3;
             display: flex;
             box-sizing: border-box;
             align-items: flex-start;
             overflow: hidden;
             padding: clamp(0.75rem, 1.5vw, 1.25rem) clamp(1.5rem, 5vw, 4rem);
+            transform: translate3d(0, var(--work-intro-shift, 0), 0);
+            will-change: transform;
         }
 
         .work-heading {
@@ -361,16 +375,48 @@
             display: inline-block;
         }
 
+        .work-heading-email {
+            color: inherit;
+            font-weight: 500;
+            text-decoration: none;
+            transition: font-weight 160ms ease;
+        }
+
+        .work-heading-email:hover,
+        .work-heading-email:focus-visible {
+            font-weight: 700;
+        }
+
+        .work-heading-email:focus-visible {
+            outline: 0.055em solid #000;
+            outline-offset: 0.075em;
+        }
+
+        .work-intro--closing {
+            height: auto;
+            min-height: 0;
+        }
+
+        .work-intro--closing .work-intro-stage {
+            position: relative;
+            top: auto;
+            transform: none;
+            will-change: auto;
+        }
+
         .work-intro.is-scroll-ready .work-heading-word {
             opacity: 0;
             filter: blur(0.18em);
             transform: translate3d(0, 0.42em, 0) rotate(1.5deg);
             transform-origin: left bottom;
+            will-change: opacity, filter, transform;
+        }
+
+        .work-intro.is-scroll-ready.is-motion-ready .work-heading-word {
             transition:
                 opacity 220ms ease,
                 filter 360ms ease,
                 transform 420ms cubic-bezier(0.16, 1, 0.3, 1);
-            will-change: opacity, filter, transform;
         }
 
         .work-intro.is-scroll-ready .work-heading-word.is-revealed {
@@ -380,10 +426,10 @@
         }
 
         @media (prefers-reduced-motion: reduce) {
-            .nav-brand-ready .nav-brand,
-            .nav-brand-ready .nav-brand.is-visible {
-                animation: none;
-                transition: none;
+            .nav-brand-letter {
+                filter: none;
+                transform: none;
+                will-change: auto;
             }
 
             .neon-letter,
@@ -391,6 +437,13 @@
                 animation: none;
             }
 
+            .hero-letter {
+                opacity: 1;
+                filter: none;
+                transform: none;
+                will-change: auto;
+            }
+
             .work-intro {
                 height: auto;
             }
@@ -398,6 +451,8 @@
             .work-intro-stage {
                 position: relative;
                 top: auto;
+                transform: none;
+                will-change: auto;
             }
 
             .work-intro.is-scroll-ready .work-heading-word {
@@ -410,7 +465,7 @@
 
         .work-stage {
             box-sizing: border-box;
-            padding: clamp(0.75rem, 1.5vw, 1.5rem) clamp(1rem, 2vw, 2rem) clamp(5rem, 9vw, 8rem);
+            padding: clamp(0.75rem, 1.5vw, 1.5rem) clamp(1rem, 2vw, 2rem);
         }
 
         .work-stage-content {
@@ -437,14 +492,14 @@
             border-radius: 0;
         }
 
-        #work-scroller > article > a {
+        #work-scroller > article > .portfolio-media {
             display: block;
             width: 100%;
             height: 100%;
             border-radius: 0;
         }
 
-        #work-scroller > article > a > img {
+        #work-scroller > article > .portfolio-media > img {
             width: 100%;
             height: 100%;
             aspect-ratio: auto;
@@ -452,7 +507,7 @@
             transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
         }
 
-        #work-scroller > article > div {
+        #work-scroller > article > .portfolio-details {
             position: absolute;
             inset: 0;
             z-index: 2;
@@ -474,17 +529,16 @@
             backdrop-filter: blur(0.35rem);
         }
 
-        #work-scroller > article:hover > div,
-        #work-scroller > article:focus-within > div,
-        #work-scroller > article.is-description-visible > div {
+        #work-scroller > article:focus-within > .portfolio-details,
+        #work-scroller > article.is-description-visible > .portfolio-details {
             opacity: 1;
             clip-path: inset(0);
+            pointer-events: auto;
             transform: none;
         }
 
-        #work-scroller > article:hover > a > img,
-        #work-scroller > article:focus-within > a > img,
-        #work-scroller > article.is-description-visible > a > img {
+        #work-scroller > article:focus-within > .portfolio-media > img,
+        #work-scroller > article.is-description-visible > .portfolio-media > img {
             transform: scale(1.025);
         }
 
@@ -493,7 +547,7 @@
             outline-offset: -2px;
         }
 
-        #work-scroller > article > div > p {
+        #work-scroller > article > .portfolio-details > p {
             width: 100%;
             margin: 0;
             color: #000;
@@ -513,6 +567,25 @@
             font-weight: 700;
         }
 
+        .portfolio-live-link {
+            display: inline-block;
+            color: #000;
+            font-weight: 500;
+            text-decoration: none;
+            transition: font-weight 160ms ease;
+            white-space: nowrap;
+        }
+
+        .portfolio-live-link:hover,
+        .portfolio-live-link:focus-visible {
+            font-weight: 700;
+        }
+
+        .portfolio-live-link:focus-visible {
+            outline: 0.09em solid #000;
+            outline-offset: 0.08em;
+        }
+
         .portfolio-scroll-ready #work-scroller > article {
             opacity: var(--card-opacity, 0);
             filter: blur(var(--card-blur, 0.4rem));
@@ -532,6 +605,29 @@
 
         }
 
+        @media (min-width: 768px) and (hover: hover) {
+            #work-scroller > article:hover > .portfolio-details {
+                opacity: 1;
+                clip-path: inset(0);
+                pointer-events: auto;
+                transform: none;
+            }
+
+            #work-scroller > article:hover > .portfolio-media > img {
+                transform: scale(1.025);
+            }
+        }
+
+        @media (max-width: 767px) {
+            #work-scroller > article {
+                cursor: pointer;
+            }
+
+            #work-scroller > article.is-description-visible {
+                cursor: default;
+            }
+        }
+
         @media (prefers-reduced-motion: reduce) {
             .portfolio-scroll-ready #work-scroller > article {
                 opacity: 1;
@@ -539,8 +635,8 @@
                 transform: none;
             }
 
-            #work-scroller > article > a > img,
-            #work-scroller > article > div {
+            #work-scroller > article > .portfolio-media > img,
+            #work-scroller > article > .portfolio-details {
                 transition: none;
             }
         }
@@ -610,18 +706,18 @@
         <div class="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:py-5">
             <div class="flex items-center justify-between gap-3 lg:hidden">
                 <a href="#hero" class="nav-brand flex min-w-0 items-center rounded-2xl p-1.5 pr-3 text-left text-white transition-colors hover:bg-white/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00]" aria-label="Go to top">
-                    <span class="logo-font block text-2xl font-semibold tracking-tighter">ERD</span>
+                    <span class="nav-brand-word logo-font font-semibold" aria-hidden="true"><span class="nav-brand-letter">E</span><span class="nav-brand-letter">R</span><span class="nav-brand-letter">D</span></span>
                 </a>
-                <a href="mailto:[email protected]" class="neon-ink shrink-0 rounded-2xl bg-[#fff] px-4 py-3 text-sm font-semibold transition-colors hover:bg-white">Let's Talk</a>
+                <a href="mailto:[email protected]" class="nav-cta neon-ink shrink-0 rounded-2xl bg-[#fff] font-semibold transition-colors hover:bg-white">Let's Talk</a>
             </div>
 
             <div class="hidden items-center justify-between lg:flex">
                 <a href="#hero" class="nav-brand flex items-center gap-3" aria-label="Go to top">
                     <div>
-                        <span class="logo-font block text-2xl font-semibold tracking-tighter">ERD</span>
+                        <span class="nav-brand-word logo-font font-semibold" aria-hidden="true"><span class="nav-brand-letter">E</span><span class="nav-brand-letter">R</span><span class="nav-brand-letter">D</span></span>
                     </div>
                 </a>
-                <a href="mailto:[email protected]" class="neon-ink rounded-2xl bg-[#fff] px-6 py-3 font-semibold transition-colors hover:bg-white">Let's Talk</a>
+                <a href="mailto:[email protected]" class="nav-cta neon-ink rounded-2xl bg-[#fff] font-semibold transition-colors hover:bg-white">Let's Talk</a>
             </div>
         </div>
     </nav>
@@ -632,9 +728,7 @@
 
             <div class="hero-content mx-auto grid w-full max-w-7xl justify-items-center gap-10 px-6 py-32 text-center">
                 <div>
-                    <h1 class="heading text-7xl font-bold leading-none tracking-tighter md:text-8xl lg:text-7xl xl:text-8xl">
-                        ERD
-                    </h1>
+                    <h1 class="heading text-7xl font-bold leading-none tracking-tighter md:text-8xl lg:text-7xl xl:text-8xl" aria-label="ERD"><span class="hero-letter" aria-hidden="true">E</span><span class="hero-letter" aria-hidden="true">R</span><span class="hero-letter" aria-hidden="true">D</span></h1>
                 </div>
             </div>
 
@@ -668,110 +762,117 @@
                 <div class="work-stage-content">
                 <div id="work-scroller" class="portfolio-grid">
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://gardenfreshwholesale.com" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Garden Fresh Wholesale website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/gardenfresh.webp?v=2" width="1200" height="702" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Garden Fresh Wholesale ecommerce web app project by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description">We built <span class="portfolio-name">Garden Fresh Wholesale</span>'s business website and local SEO strategy that increased customer acquisition by 48% since launch.</span>
+                                <span class="portfolio-description">We built <span class="portfolio-name">Garden Fresh Wholesale</span>'s business website and local SEO strategy that increased customer acquisition by 48% since launch. <a class="portfolio-live-link" href="https://gardenfreshwholesale.com" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://miadruck.com" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Mia Druck website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/miadruck.webp?v=3" width="1200" height="681" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Mia Druck real estate website project by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description"><span class="portfolio-name">Mia Druck</span> needed a real estate website with heavy local SEO and a custom API for the most up-to-date listings and sales. We delivered this custom site in under a week.</span>
+                                <span class="portfolio-description"><span class="portfolio-name">Mia Druck</span> needed a real estate website with heavy local SEO and a custom API for the most up-to-date listings and sales. We delivered this custom site in under a week. <a class="portfolio-live-link" href="https://miadruck.com" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://coralvibefl.com" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Coral Vibe website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/coralvibe.webp?v=1" width="1200" height="678" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Shopify store for Coral Vibe Florida project by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description"><span class="portfolio-name">Coral Vibe</span> hired us to design their Shopify store and provide support for ecommerce operations, product management, and social media marketing. Since launch, store visits resulting in sales have increased by 86%.</span>
+                                <span class="portfolio-description"><span class="portfolio-name">Coral Vibe</span> hired us to design their Shopify store and provide support for ecommerce operations, product management, and social media marketing. Since launch, store visits resulting in sales have increased by 86%. <a class="portfolio-live-link" href="https://coralvibefl.com" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://frontlineboats.com" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Frontline Boat Services website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/frontlineboats.webp?v=1" width="1200" height="700" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Frontline Boat Services marine services website project by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description">For <span class="portfolio-name">Frontline Boat Services</span>, we built a highly focused business website with local SEO, custom payment links for customers, and an owner-managed photo gallery.</span>
+                                <span class="portfolio-description">For <span class="portfolio-name">Frontline Boat Services</span>, we built a highly focused business website with local SEO, custom payment links for customers, and an owner-managed photo gallery. <a class="portfolio-live-link" href="https://frontlineboats.com" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://voldservices.com" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Vold Services website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/voldservices.webp" width="1200" height="702" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Vold Services exterior cleaning website with a scroll-animated hero, designed by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description"><span class="portfolio-name">Vold Services</span> hired us to create their business website with a scroll-animated hero, local SEO/GEO and an owner-managed photo gallery.</span>
+                                <span class="portfolio-description"><span class="portfolio-name">Vold Services</span> hired us to create their business website with a scroll-animated hero, local SEO/GEO and an owner-managed photo gallery. <a class="portfolio-live-link" href="https://voldservices.com" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://vegy.app/" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Vegy website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/vegy.webp?v=1" width="1200" height="702" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Vegy wholesale ordering platform project by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description">We have partnered with <span class="portfolio-name">Vegy</span> since 2023 to create a white-label web app for wholesale ordering with AI and email integrations, real-time updates, and advanced analytics. The app processed over $2 million in orders in the first year.</span>
+                                <span class="portfolio-description">We have partnered with <span class="portfolio-name">Vegy</span> since 2023 to create a white-label web app for wholesale ordering with AI and email integrations, real-time updates, and advanced analytics. The app processed over $2 million in orders in the first year. <a class="portfolio-live-link" href="https://vegy.app/" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://prima.vegy.app/" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Prima Produce website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/prima.webp?v=1" width="1200" height="702" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Prima Produce business website project by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description">Our partner <span class="portfolio-name">Prima Produce</span> hired us to build their business website with custom online ordering and cohesive branding. Working with their existing logo, we created a complete online experience for Prima's customers.</span>
+                                <span class="portfolio-description">Our partner <span class="portfolio-name">Prima Produce</span> hired us to build their business website with custom online ordering and cohesive branding. Working with their existing logo, we created a complete online experience for Prima's customers. <a class="portfolio-live-link" href="https://prima.vegy.app/" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                     <article class="w-[82vw] shrink-0 snap-start overflow-hidden rounded-3xl bg-zinc-950 sm:w-[28rem] md:w-[38rem]">
-                        <a href="https://invoiceotter.com" target="_blank" rel="noopener noreferrer" class="block w-full overflow-hidden rounded-t-3xl text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-[#ccff00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#17212b]" aria-label="Visit Invoice Otter website">
+                        <div class="portfolio-media">
                             <img src="https://afterdarklabs.io/invoiceotter.webp?v=1" width="1200" height="673" loading="lazy" decoding="async" class="aspect-video w-full object-cover" alt="Invoice Otter SaaS project by After Dark Labs">
-                        </a>
-                        <div class="p-6">
+                        </div>
+                        <div class="portfolio-details p-6">
                             <p class="text-sm font-medium text-white">
-                                <span class="portfolio-description">We built the mobile app, web interface, and the APIs for <span class="portfolio-name">Invoice Otter</span>'s contractor invoicing and estimating solutions with subscriptions, integrated payments, a custom AI chatbot, and per-user customization.</span>
+                                <span class="portfolio-description">We built the mobile app, web interface, and the APIs for <span class="portfolio-name">Invoice Otter</span>'s contractor invoicing and estimating solutions with subscriptions, integrated payments, a custom AI chatbot, and per-user customization. <a class="portfolio-live-link" href="https://invoiceotter.com" target="_blank" rel="noopener noreferrer">See live site</a></span>
                             </p>
                         </div>
                     </article>
                 </div>
                 </div>
             </div>
+
+            <div class="work-intro work-intro--closing" aria-labelledby="closing-heading">
+                <div class="work-intro-stage">
+                    <h2 id="closing-heading" class="work-heading">
+                        <span class="work-heading-words">
+                            <span class="work-heading-word">Have</span>
+                            <span class="work-heading-word">a</span>
+                            <span class="work-heading-word">big</span>
+                            <span class="work-heading-word">idea?</span>
+                            <span class="work-heading-word">Let’s</span>
+                            <span class="work-heading-word">make</span>
+                            <span class="work-heading-word">it</span>
+                            <span class="work-heading-word">impossible</span>
+                            <span class="work-heading-word">to</span>
+                            <span class="work-heading-word">ignore.</span>
+                            <span class="work-heading-word">Tell</span>
+                            <span class="work-heading-word">us</span>
+                            <span class="work-heading-word">what’s</span>
+                            <span class="work-heading-word">next</span>
+                            <span class="work-heading-word">→</span>
+                            <a class="work-heading-word work-heading-email" href="mailto:[email protected]">[email protected]</a>
+                        </span>
+                    </h2>
+                </div>
+            </div>
         </section>
 
     </main>
 
-    <footer class="border-t border-white/10 bg-black py-16">
-        <div class="mx-auto max-w-7xl px-6 text-center">
-            <a href="#hero" class="mb-6 inline-flex items-center justify-center gap-3">
-                <span class="min-w-0 text-left">
-                    <span class="logo-font block text-3xl font-semibold tracking-tighter">ERD</span>
-                </span>
-            </a>
-            <p style="color: #000;">
-                <a href="https://share.google/inOLBEb4neFhKNkWb" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-[#ccff00]">&starf;&starf;&starf;&starf;&starf; on Google</a>
-                <br>
-                Let's work together &rarr; <a href="mailto:[email protected]" class="transition-colors hover:text-[#ccff00]" style="margin-top:50px;">[email protected]</a>
-                <br><br>
-                © 2026 <a href="https://harrisonerd.com/" target="_blank" rel="noopener noreferrer" class="transition-colors hover:text-[#ccff00]">Harrison Erd</a>
-            </p>
-
-        </div>
-    </footer>
-
     <script>
         (function () {
             const hero = document.getElementById('hero');
@@ -817,57 +918,158 @@
 
         (function () {
             const hero = document.getElementById('hero');
+            const heading = hero?.querySelector('.hero-content h1');
+            const letters = Array.from(heading?.querySelectorAll('.hero-letter') || []);
             const nav = document.querySelector('nav');
             const brands = Array.from(document.querySelectorAll('.nav-brand'));
-            if (!hero || !nav || !brands.length) return;
+            const navLetterGroups = brands.map(brand => Array.from(brand.querySelectorAll('.nav-brand-letter')));
+            if (!hero || !heading || !letters.length || !nav || !brands.length) return;
 
+            const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
+            let heroTop = 0;
+            let fadeDistance = 1;
+            let headingSize = 1;
             let updateFrame = 0;
-            let brandIsVisible = null;
+            let resizeFrame = 0;
 
-            function updateNavBrand() {
+            function clamp(value, minimum = 0, maximum = 1) {
+                return Math.min(maximum, Math.max(minimum, value));
+            }
+
+            function updateHeroLetters() {
                 updateFrame = 0;
-                const revealBrand = hero.getBoundingClientRect().bottom <= nav.getBoundingClientRect().bottom + 1;
-                if (revealBrand === brandIsVisible) return;
 
-                brandIsVisible = revealBrand;
-                brands.forEach(brand => brand.classList.toggle('is-visible', revealBrand));
+                if (reduceMotion.matches) {
+                    const revealBrand = hero.getBoundingClientRect().bottom <= nav.getBoundingClientRect().bottom + 1;
+                    letters.forEach(letter => {
+                        letter.style.removeProperty('--hero-letter-opacity');
+                        letter.style.removeProperty('--hero-letter-blur');
+                        letter.style.removeProperty('--hero-letter-scale');
+                    });
+                    navLetterGroups.forEach(group => group.forEach(letter => {
+                        letter.style.setProperty('--nav-letter-opacity', revealBrand ? '1' : '0');
+                        letter.style.removeProperty('--nav-letter-blur');
+                        letter.style.removeProperty('--nav-letter-x');
+                        letter.style.removeProperty('--nav-letter-y');
+                        letter.style.removeProperty('--nav-letter-rotate');
+                        letter.style.removeProperty('--nav-letter-skew');
+                        letter.style.removeProperty('--nav-letter-scale-x');
+                        letter.style.removeProperty('--nav-letter-scale-y');
+                    }));
+                    brands.forEach(brand => brand.classList.toggle('is-visible', revealBrand));
+                    return;
+                }
+
+                const progress = clamp((window.scrollY - heroTop) / fadeDistance);
+                let visibleNavLetters = 0;
+
+                letters.forEach((letter, index) => {
+                    const start = 0.05 + index * 0.23;
+                    const localProgress = clamp((progress - start) / 0.3);
+                    const eased = localProgress * localProgress * (3 - 2 * localProgress);
+                    const remaining = 1 - eased;
+                    const wobble = Math.sin(localProgress * Math.PI * 3 + index * 0.65) * remaining;
+
+                    letter.style.setProperty('--hero-letter-opacity', (1 - eased).toFixed(4));
+                    letter.style.setProperty('--hero-letter-blur', `${(eased * headingSize * 0.018).toFixed(2)}px`);
+                    letter.style.setProperty('--hero-letter-scale', (1 - eased * 0.055).toFixed(4));
+
+                    navLetterGroups.forEach(group => {
+                        const navLetter = group[index];
+                        if (!navLetter) return;
+
+                        navLetter.style.setProperty('--nav-letter-opacity', eased.toFixed(4));
+                        navLetter.style.setProperty('--nav-letter-blur', `${(remaining * 0.32).toFixed(3)}rem`);
+                        navLetter.style.setProperty('--nav-letter-x', `${(-1.35 * remaining + wobble * 0.42).toFixed(3)}rem`);
+                        navLetter.style.setProperty('--nav-letter-y', `${(wobble * 0.18).toFixed(3)}rem`);
+                        navLetter.style.setProperty('--nav-letter-rotate', `${(-12 * remaining + wobble * 7).toFixed(2)}deg`);
+                        navLetter.style.setProperty('--nav-letter-skew', `${(-18 * remaining + wobble * 9).toFixed(2)}deg`);
+                        navLetter.style.setProperty('--nav-letter-scale-x', `${(0.38 + eased * 0.62 + wobble * 0.08).toFixed(4)}`);
+                        navLetter.style.setProperty('--nav-letter-scale-y', `${(1.28 - eased * 0.28 - wobble * 0.06).toFixed(4)}`);
+                    });
+
+                    if (eased > 0.02) visibleNavLetters += 1;
+                });
+
+                brands.forEach(brand => brand.classList.toggle('is-visible', visibleNavLetters > 0));
             }
 
-            function scheduleNavBrandUpdate() {
-                if (!updateFrame) updateFrame = window.requestAnimationFrame(updateNavBrand);
+            function measureHeroLetters() {
+                resizeFrame = 0;
+                heroTop = hero.getBoundingClientRect().top + window.scrollY;
+                fadeDistance = Math.max(1, hero.offsetHeight - (nav?.getBoundingClientRect().height || 0));
+                headingSize = parseFloat(window.getComputedStyle(heading).fontSize) || 1;
+                updateHeroLetters();
             }
 
+            function scheduleHeroLetterUpdate() {
+                if (!updateFrame) updateFrame = window.requestAnimationFrame(updateHeroLetters);
+            }
+
+            function scheduleHeroLetterMeasure() {
+                if (!resizeFrame) resizeFrame = window.requestAnimationFrame(measureHeroLetters);
+            }
+
+            window.addEventListener('scroll', scheduleHeroLetterUpdate, { passive: true });
+            window.addEventListener('resize', scheduleHeroLetterMeasure);
+            window.visualViewport?.addEventListener('resize', scheduleHeroLetterMeasure);
+            window.addEventListener('pageshow', scheduleHeroLetterMeasure);
+            reduceMotion.addEventListener('change', scheduleHeroLetterMeasure);
+            document.fonts?.ready.then(scheduleHeroLetterMeasure);
             document.documentElement.classList.add('nav-brand-ready');
-            window.addEventListener('scroll', scheduleNavBrandUpdate, { passive: true });
-            window.addEventListener('resize', scheduleNavBrandUpdate);
-            window.addEventListener('pageshow', scheduleNavBrandUpdate);
-            updateNavBrand();
+            measureHeroLetters();
         })();
 
         (function () {
             const intro = document.querySelector('.work-intro');
             const stage = intro?.querySelector('.work-intro-stage');
+            const heading = intro?.querySelector('.work-heading');
+            const heroHeading = document.querySelector('.hero-content h1');
             const words = Array.from(intro?.querySelectorAll('.work-heading-word') || []);
-            if (!intro || !stage || !words.length) return;
+            if (!intro || !stage || !heading || !heroHeading || !words.length) return;
 
             const nav = document.querySelector('nav');
             const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
             let revealStart = 0;
             let revealDistance = 1;
+            let shiftStart = 0;
+            let shiftEnd = 1;
+            let introStartShift = 0;
             let revealedWords = -1;
             let updateFrame = 0;
             let resizeFrame = 0;
 
+            function clamp(value, minimum = 0, maximum = 1) {
+                return Math.min(maximum, Math.max(minimum, value));
+            }
+
+            function getDocumentTop(element) {
+                let top = 0;
+                let current = element;
+
+                while (current) {
+                    top += current.offsetTop;
+                    current = current.offsetParent;
+                }
+
+                return top;
+            }
+
             function updateWorkIntro() {
                 updateFrame = 0;
 
                 if (reduceMotion.matches) {
+                    stage.style.removeProperty('--work-intro-shift');
                     words.forEach(word => word.classList.add('is-revealed'));
                     revealedWords = words.length;
                     return;
                 }
 
-                const progress = Math.min(1, Math.max(0, (window.scrollY - revealStart) / revealDistance));
+                const progress = window.scrollY <= 1
+                    ? 0
+                    : clamp((window.scrollY - revealStart) / revealDistance);
+                const shiftProgress = clamp((window.scrollY - shiftStart) / Math.max(1, shiftEnd - shiftStart));
+                stage.style.setProperty('--work-intro-shift', `${(introStartShift * (1 - shiftProgress)).toFixed(2)}px`);
                 const nextRevealedWords = Math.ceil(progress * words.length);
                 if (nextRevealedWords === revealedWords) return;
 
@@ -880,6 +1082,7 @@
             function measureWorkIntro() {
                 resizeFrame = 0;
                 intro.classList.toggle('is-scroll-ready', !reduceMotion.matches);
+                intro.classList.remove('is-motion-ready');
 
                 const introTop = intro.getBoundingClientRect().top + window.scrollY;
                 const navHeight = nav?.getBoundingClientRect().height || 0;
@@ -889,8 +1092,21 @@
 
                 revealStart = introTop - Math.max(1, viewportHeight - navHeight);
                 revealDistance = Math.max(1, revealEnd - revealStart);
+                shiftStart = revealStart;
+                shiftEnd = introTop - navHeight;
+
+                const heroCenter = getDocumentTop(heroHeading) + heroHeading.offsetHeight / 2;
+                const introCenter = getDocumentTop(heading) + heading.offsetHeight / 2;
+                const availableShiftDistance = Math.max(1, shiftEnd - shiftStart) * 0.96;
+                introStartShift = Math.max(heroCenter - introCenter, -availableShiftDistance);
                 revealedWords = -1;
                 updateWorkIntro();
+
+                if (!reduceMotion.matches) {
+                    window.requestAnimationFrame(() => {
+                        window.requestAnimationFrame(() => intro.classList.add('is-motion-ready'));
+                    });
+                }
             }
 
             function scheduleWorkIntroUpdate() {
@@ -915,7 +1131,7 @@
             if (!cards.length) return;
 
             const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
-            const touchLayout = window.matchMedia('(hover: none), (pointer: coarse)');
+            const mobileLayout = window.matchMedia('(max-width: 767px)');
             let cardTops = [];
             let updateFrame = 0;
             let resizeFrame = 0;
@@ -938,7 +1154,7 @@
 
             function fitDescriptions() {
                 cards.forEach(card => {
-                    const panel = card.querySelector(':scope > div');
+                    const panel = card.querySelector(':scope > .portfolio-details');
                     const copy = panel?.querySelector('p');
                     if (!panel || !copy) return;
 
@@ -1014,22 +1230,30 @@
                 if (!resizeFrame) resizeFrame = window.requestAnimationFrame(measureCards);
             }
 
+            function closeDescriptions(exceptCard = null) {
+                cards.forEach(card => {
+                    if (card !== exceptCard) card.classList.remove('is-description-visible');
+                });
+            }
+
             cards.forEach(card => {
-                const link = card.querySelector(':scope > a');
-                link?.addEventListener('click', event => {
-                    if (!touchLayout.matches || card.classList.contains('is-description-visible')) return;
+                card.addEventListener('click', event => {
+                    if (!mobileLayout.matches || event.target.closest('.portfolio-live-link')) return;
 
-                    event.preventDefault();
-                    cards.forEach(otherCard => otherCard.classList.remove('is-description-visible'));
+                    closeDescriptions(card);
                     card.classList.add('is-description-visible');
                 });
             });
 
             document.addEventListener('pointerdown', event => {
-                if (!touchLayout.matches || event.target.closest('#work-scroller > article')) return;
-                cards.forEach(card => card.classList.remove('is-description-visible'));
+                if (!mobileLayout.matches || event.target.closest('#work-scroller > article')) return;
+                closeDescriptions();
+                const activeCard = document.activeElement?.closest?.('#work-scroller > article');
+                if (activeCard) document.activeElement.blur();
             }, { passive: true });
 
+            mobileLayout.addEventListener('change', () => closeDescriptions());
+
             section.classList.add('portfolio-scroll-ready');
             window.addEventListener('scroll', scheduleCardUpdate, { passive: true });
             window.addEventListener('resize', scheduleCardMeasure);
@@ -1040,6 +1264,79 @@
             measureCards();
         })();
 
+        (function () {
+            const closingStatement = document.querySelector('.work-intro--closing');
+            const words = Array.from(closingStatement?.querySelectorAll('.work-heading-word') || []);
+            if (!closingStatement || !words.length) return;
+
+            const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
+            let revealStart = 0;
+            let revealDistance = 1;
+            let revealedWords = -1;
+            let updateFrame = 0;
+            let resizeFrame = 0;
+
+            function clamp(value, minimum = 0, maximum = 1) {
+                return Math.min(maximum, Math.max(minimum, value));
+            }
+
+            function updateClosingIntro() {
+                updateFrame = 0;
+
+                if (reduceMotion.matches) {
+                    words.forEach(word => word.classList.add('is-revealed'));
+                    revealedWords = words.length;
+                    return;
+                }
+
+                const progress = clamp((window.scrollY - revealStart) / revealDistance);
+                const nextRevealedWords = Math.ceil(progress * words.length);
+                if (nextRevealedWords === revealedWords) return;
+
+                revealedWords = nextRevealedWords;
+                words.forEach((word, index) => {
+                    word.classList.toggle('is-revealed', index < revealedWords);
+                });
+            }
+
+            function measureClosingIntro() {
+                resizeFrame = 0;
+                closingStatement.classList.toggle('is-scroll-ready', !reduceMotion.matches);
+                closingStatement.classList.remove('is-motion-ready');
+
+                const statementTop = closingStatement.getBoundingClientRect().top + window.scrollY;
+                const viewportHeight = window.visualViewport?.height || window.innerHeight;
+                const statementHeight = closingStatement.offsetHeight;
+                const maximumScroll = Math.max(0, document.documentElement.scrollHeight - viewportHeight);
+                revealStart = Math.min(maximumScroll, statementTop - viewportHeight + statementHeight * 0.05);
+                revealDistance = Math.max(1, maximumScroll - revealStart);
+                revealedWords = -1;
+                updateClosingIntro();
+
+                if (!reduceMotion.matches) {
+                    window.requestAnimationFrame(() => {
+                        window.requestAnimationFrame(() => closingStatement.classList.add('is-motion-ready'));
+                    });
+                }
+            }
+
+            function scheduleClosingIntroUpdate() {
+                if (!updateFrame) updateFrame = window.requestAnimationFrame(updateClosingIntro);
+            }
+
+            function scheduleClosingIntroMeasure() {
+                if (!resizeFrame) resizeFrame = window.requestAnimationFrame(measureClosingIntro);
+            }
+
+            window.addEventListener('scroll', scheduleClosingIntroUpdate, { passive: true });
+            window.addEventListener('resize', scheduleClosingIntroMeasure);
+            window.visualViewport?.addEventListener('resize', scheduleClosingIntroMeasure);
+            window.addEventListener('pageshow', scheduleClosingIntroMeasure);
+            reduceMotion.addEventListener('change', scheduleClosingIntroMeasure);
+            document.fonts?.ready.then(scheduleClosingIntroMeasure);
+            measureClosingIntro();
+        })();
+
     </script>
 </body>
 </html>