patx/afterdarklabs
indexerd 3
Commit efd28f5 · patx · 2026-09-03T02:03:58-04:00
Comments
No comments yet.
Diff
diff --git a/docs/index3.html b/docs/index3.html
deleted file mode 100644
index 9cd69df..0000000
--- a/docs/index3.html
+++ /dev/null
@@ -1,574 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <title>South Florida Website Design & Development | After Dark Labs</title>
- <meta name="description" content="After Dark Labs is an independent South Florida design and development studio building custom websites, Shopify stores, SaaS products, and business tools.">
- <meta name="robots" content="index,follow">
- <link rel="canonical" href="https://afterdarklabs.io/">
- <meta name="theme-color" content="#111a22">
-
- <meta property="og:title" content="After Dark Labs — Websites and tools that stand out">
- <meta property="og:description" content="Custom websites, Shopify stores, SaaS products, and business tools from an independent South Florida studio.">
- <meta property="og:image" content="https://afterdarklabs.io/twitter-card.png?v=3">
- <meta property="og:type" content="website">
- <meta property="og:url" content="https://afterdarklabs.io/">
- <meta name="twitter:card" content="summary_large_image">
- <meta name="twitter:title" content="After Dark Labs — Websites and tools that stand out">
- <meta name="twitter:description" content="Custom websites, Shopify stores, SaaS products, and business tools from an independent South Florida studio.">
- <meta name="twitter:image" content="https://afterdarklabs.io/twitter-card.png?v=3">
-
- <link rel="icon" href="https://afterdarklabs.io/logo_assets/favicon.ico">
- <link rel="icon" type="image/svg+xml" href="https://afterdarklabs.io/logo_assets/badge.svg">
- <link rel="apple-touch-icon" sizes="180x180" href="https://afterdarklabs.io/logo_assets/apple-touch-icon.png">
-
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&family=Syne:wght@600;700;800&display=swap" rel="stylesheet">
-
- <style>
- :root {
- --ink: #f7f8f8;
- --muted: #9ca6ad;
- --dim: #6f7a82;
- --page: #111a22;
- --page-rgb: 17, 26, 34;
- --surface: #16232d;
- --surface-2: #1a2934;
- --line: rgba(255,255,255,.10);
- --line-soft: rgba(255,255,255,.065);
- --neon: #ccff00;
- --max: 1240px;
- --ease: cubic-bezier(.22,1,.36,1);
- }
-
- * { box-sizing: border-box; }
- html { scroll-behavior: smooth; background: var(--page); }
- body {
- margin: 0;
- color: var(--ink);
- background:
- radial-gradient(circle at 78% 5%, rgba(204,255,0,.055), transparent 24rem),
- var(--page);
- font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
- -webkit-font-smoothing: antialiased;
- overflow-x: hidden;
- }
- body::before {
- content: "";
- position: fixed;
- inset: 0;
- pointer-events: none;
- z-index: 999;
- opacity: .14;
- background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.07'/%3E%3C/svg%3E");
- mix-blend-mode: soft-light;
- }
- a { color: inherit; }
- img { display: block; width: 100%; }
- button, a { -webkit-tap-highlight-color: transparent; }
- ::selection { color: #101600; background: var(--neon); }
-
- .wrap { width: min(calc(100% - 40px), var(--max)); margin-inline: auto; }
- .display { font-family: "Space Grotesk", sans-serif; letter-spacing: -.055em; }
- .logo-font { font-family: Syne, sans-serif; }
- .eyebrow {
- display: inline-flex;
- align-items: center;
- gap: .65rem;
- color: var(--muted);
- font-size: .74rem;
- font-weight: 600;
- letter-spacing: .16em;
- text-transform: uppercase;
- }
- .eyebrow::before { content: ""; width: 1.35rem; height: 1px; background: var(--neon); }
-
- .skip-link {
- position: fixed; left: 1rem; top: -5rem; z-index: 2000;
- background: var(--neon); color: #0c1200; padding: .75rem 1rem; border-radius: 999px;
- font-weight: 700; text-decoration: none;
- }
- .skip-link:focus { top: 1rem; }
-
- nav {
- position: fixed;
- inset: 0 0 auto;
- z-index: 100;
- border-bottom: 1px solid transparent;
- transition: background-color .25s ease, border-color .25s ease, backdrop-filter .25s ease;
- }
- nav.scrolled {
- background: rgba(var(--page-rgb), .82);
- border-color: var(--line-soft);
- backdrop-filter: blur(16px);
- }
- .nav-inner { min-height: 78px; display: flex; align-items: center; justify-content: space-between; gap: 2rem; }
- .brand { display: inline-flex; align-items: center; gap: .75rem; text-decoration: none; }
- .brand img { width: 42px; height: 42px; object-fit: contain; }
- .brand-copy { line-height: .9; }
- .brand-copy strong { display: block; font-size: 1.03rem; letter-spacing: -.04em; }
- .brand-copy span { display: block; color: var(--neon); font-size: .58rem; letter-spacing: .28em; margin-top: .28rem; }
- .nav-links { display: flex; align-items: center; gap: 1.8rem; }
- .nav-links > a:not(.button) {
- position: relative; color: #c8d0d5; font-size: .84rem; font-weight: 600; text-decoration: none;
- }
- .nav-links > a:not(.button)::after {
- content: ""; position: absolute; left: 0; right: 100%; bottom: -.35rem; height: 1px; background: var(--neon);
- transition: right .25s var(--ease);
- }
- .nav-links > a:not(.button):hover::after, .nav-links > a:not(.button):focus-visible::after { right: 0; }
-
- .button {
- display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
- min-height: 48px; padding: .82rem 1.3rem; border: 1px solid transparent; border-radius: 999px;
- background: var(--neon); color: #0f1500; font-weight: 700; font-size: .86rem; text-decoration: none;
- transition: transform .2s var(--ease), background-color .2s ease, box-shadow .2s ease;
- }
- .button:hover { transform: translateY(-2px); background: #fff; box-shadow: 0 0 30px rgba(204,255,0,.12); }
- .button.secondary { background: transparent; color: var(--ink); border-color: rgba(255,255,255,.18); }
- .button.secondary:hover { background: rgba(255,255,255,.06); box-shadow: none; }
- .arrow { width: 1em; height: 1em; transition: transform .25s var(--ease); }
- a:hover .arrow { transform: translate(3px,-3px); }
-
- /* HERO */
- .hero {
- min-height: 100svh;
- display: flex;
- align-items: center;
- padding: 150px 0 72px;
- border-bottom: 1px solid var(--line-soft);
- position: relative;
- isolation: isolate;
- }
- .hero::after {
- content: "";
- position: absolute;
- width: 28rem; height: 28rem; right: -8rem; top: 25%;
- border-radius: 50%;
- background: radial-gradient(circle, rgba(204,255,0,.10), rgba(204,255,0,.025) 38%, transparent 68%);
- filter: blur(10px);
- z-index: -1;
- }
- .hero-grid { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(240px,.45fr); gap: clamp(3rem,7vw,7rem); align-items: end; }
- .hero h1 {
- max-width: 980px;
- margin: 1.6rem 0 0;
- font-size: clamp(4.45rem, 9.45vw, 9.4rem);
- line-height: .84;
- font-weight: 700;
- }
- .hero h1 .line { display: block; }
- .standout { display: inline-flex; position: relative; isolation: isolate; color: var(--neon); white-space: nowrap; }
- .standout::before {
- content: ""; position: absolute; inset: -.18em -.18em -.12em;
- background: radial-gradient(ellipse at 47% 58%, rgba(204,255,0,.32), rgba(204,255,0,.10) 45%, transparent 74%);
- filter: blur(.13em); z-index: -2; opacity: .82;
- }
- .neon-letter { display: inline-block; color: var(--neon); -webkit-text-stroke: .006em rgba(240,255,170,.6); filter: drop-shadow(0 0 .035em rgba(204,255,0,.6)) drop-shadow(0 0 .13em rgba(204,255,0,.22)); }
- .neon-gap { display: inline-block; width: .20em; }
- .neon-letter.flicker { animation: flicker var(--duration,13s) steps(1,end) infinite; animation-delay: var(--delay,0s); }
- @keyframes flicker {
- 0%, 5%, 5.7%, 38%, 38.7%, 100% { opacity: 1; filter: drop-shadow(0 0 .035em rgba(204,255,0,.62)) drop-shadow(0 0 .13em rgba(204,255,0,.22)); }
- 5.15%, 38.15% { opacity: .07; filter: none; }
- 5.43%, 38.43% { opacity: .37; filter: drop-shadow(0 0 .02em rgba(204,255,0,.25)); }
- }
- .hero-side { padding-bottom: .55rem; }
- .hero-side p { margin: 0 0 1.75rem; color: #c6cdd2; font-size: clamp(1.05rem,1.5vw,1.25rem); line-height: 1.55; }
- .hero-actions { display: flex; flex-wrap: wrap; gap: .7rem; }
- .hero-meta {
- margin-top: 4rem; padding-top: 1.2rem; border-top: 1px solid var(--line);
- display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 1.2rem;
- color: var(--muted); font-size: .78rem; line-height: 1.5;
- }
- .hero-meta strong { display: block; color: var(--ink); font-size: .82rem; margin-bottom: .15rem; }
-
- /* WORK */
- .section { padding: clamp(5.5rem,9vw,9rem) 0; border-bottom: 1px solid var(--line-soft); }
- .section-head { display: flex; align-items: end; justify-content: space-between; gap: 2rem; margin-bottom: 3.2rem; }
- .section-head h2 { margin: .65rem 0 0; font-size: clamp(3rem,5.7vw,5.8rem); line-height: .93; font-weight: 600; }
- .section-head p { max-width: 500px; margin: 0; color: var(--muted); line-height: 1.65; }
-
- .projects { display: grid; gap: 6.5rem; }
- .project {
- display: grid;
- grid-template-columns: minmax(0,1.55fr) minmax(260px,.45fr);
- gap: clamp(2rem,5vw,5rem);
- align-items: end;
- }
- .project:nth-child(even) { grid-template-columns: minmax(260px,.45fr) minmax(0,1.55fr); }
- .project:nth-child(even) .project-media { order: 2; }
- .project:nth-child(even) .project-copy { order: 1; }
- .project-media {
- position: relative; overflow: hidden; border-radius: 26px;
- background: var(--surface); border: 1px solid var(--line-soft);
- aspect-ratio: 16 / 10;
- transform: translateZ(0);
- }
- .project-media::after {
- content: ""; position: absolute; inset: 0; border: 1px solid rgba(255,255,255,.05); border-radius: inherit; pointer-events: none;
- }
- .project-media img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--ease), filter .5s ease; }
- .project:hover .project-media img { transform: scale(1.025); }
- .project-index { color: var(--neon); font-size: .71rem; letter-spacing: .14em; font-weight: 700; }
- .project-copy h3 { margin: .75rem 0 .8rem; font: 600 clamp(2rem,4vw,3.75rem)/.98 "Space Grotesk", sans-serif; letter-spacing: -.05em; }
- .project-copy p { margin: 0; color: var(--muted); font-size: .98rem; line-height: 1.7; }
- .project-tags { display: flex; flex-wrap: wrap; gap: .4rem; margin: 1.25rem 0 1.7rem; }
- .tag { padding: .42rem .68rem; border: 1px solid var(--line); border-radius: 999px; color: #c7ced3; font-size: .69rem; }
- .text-link { display: inline-flex; align-items: center; gap: .45rem; color: var(--ink); font-size: .83rem; font-weight: 700; text-decoration: none; }
- .text-link span { border-bottom: 1px solid rgba(255,255,255,.32); padding-bottom: .16rem; }
-
- .lab-strip {
- margin-top: 7rem;
- display: grid;
- grid-template-columns: .7fr 1.3fr;
- gap: 3rem;
- border-top: 1px solid var(--line);
- padding-top: 2.25rem;
- }
- .lab-strip h3 { margin: 0; font: 600 clamp(2rem,3.4vw,3.25rem)/1 "Space Grotesk",sans-serif; letter-spacing: -.05em; }
- .lab-list { border-top: 1px solid var(--line); }
- .lab-item {
- display: grid; grid-template-columns: 1fr auto; gap: 1rem; align-items: center;
- padding: 1.1rem 0; border-bottom: 1px solid var(--line); text-decoration: none;
- }
- .lab-item strong { display: block; margin-bottom: .25rem; font-size: .95rem; }
- .lab-item span { color: var(--muted); font-size: .78rem; }
- .lab-item .go { color: var(--neon); font-size: 1.15rem; transition: transform .2s var(--ease); }
- .lab-item:hover .go { transform: translate(4px,-4px); }
-
- /* CAPABILITIES */
- .cap-grid { display: grid; grid-template-columns: .8fr 1.2fr; gap: clamp(3rem,8vw,8rem); }
- .cap-intro h2 { margin: .75rem 0 1.5rem; font-size: clamp(3rem,5.4vw,5.4rem); line-height: .95; font-weight: 600; }
- .cap-intro p { margin: 0; max-width: 460px; color: var(--muted); line-height: 1.7; }
- .cap-list { border-top: 1px solid var(--line); }
- .cap-row {
- display: grid; grid-template-columns: 2.4rem .72fr 1.28fr; gap: 1.3rem;
- padding: 1.55rem 0; border-bottom: 1px solid var(--line); align-items: start;
- }
- .cap-row .num { color: var(--neon); font-size: .72rem; padding-top: .28rem; }
- .cap-row h3 { margin: 0; font-size: 1rem; font-weight: 650; }
- .cap-row p { margin: 0; color: var(--muted); line-height: 1.55; font-size: .88rem; }
- .price-note { margin-top: 1.7rem; color: var(--dim); font-size: .77rem; line-height: 1.6; }
- .price-note strong { color: #cbd2d6; font-weight: 600; }
-
- /* STUDIO */
- .studio { position: relative; overflow: hidden; }
- .studio::before {
- content: "ADL";
- position: absolute; right: -1vw; bottom: -4.2vw;
- font: 800 min(31vw,440px)/.7 Syne,sans-serif; letter-spacing: -.09em;
- color: rgba(255,255,255,.025); pointer-events: none;
- }
- .studio-grid { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(3rem,8vw,8rem); align-items: end; position: relative; z-index: 1; }
- .studio h2 { margin: .75rem 0 1.5rem; font-size: clamp(3.3rem,6vw,6.1rem); line-height: .92; font-weight: 600; max-width: 800px; }
- .studio-copy { color: #c4cbd0; font-size: clamp(1.05rem,1.5vw,1.25rem); line-height: 1.7; max-width: 800px; }
- .studio-facts { display: grid; gap: 0; border-top: 1px solid var(--line); }
- .fact { display: flex; justify-content: space-between; gap: 1.5rem; padding: 1rem 0; border-bottom: 1px solid var(--line); font-size: .8rem; }
- .fact span:first-child { color: var(--muted); }
- .fact span:last-child { text-align: right; color: #d6dcdf; }
-
- /* CTA */
- .cta { padding: clamp(6rem,12vw,11rem) 0; }
- .cta-inner { display: grid; grid-template-columns: 1.5fr .5fr; gap: 3rem; align-items: end; }
- .cta h2 { margin: 0; max-width: 970px; font-size: clamp(4rem,8.2vw,8rem); line-height: .85; font-weight: 650; }
- .cta h2 span { color: var(--neon); }
- .cta-side p { color: var(--muted); line-height: 1.65; margin: 0 0 1.5rem; }
-
- footer { border-top: 1px solid var(--line-soft); padding: 2.2rem 0 2.5rem; }
- .footer-grid { display: flex; align-items: center; justify-content: space-between; gap: 2rem; color: var(--dim); font-size: .76rem; }
- .footer-links { display: flex; flex-wrap: wrap; gap: 1rem; }
- .footer-links a { color: #a8b1b7; text-decoration: none; }
- .footer-links a:hover { color: var(--neon); }
-
- .reveal { opacity: 0; transform: translateY(26px); transition: opacity .75s var(--ease), transform .75s var(--ease); }
- .reveal.in { opacity: 1; transform: none; }
-
- @media (max-width: 900px) {
- .nav-links > a:not(.button) { display: none; }
- .hero-grid, .project, .project:nth-child(even), .cap-grid, .studio-grid, .cta-inner, .lab-strip { grid-template-columns: 1fr; }
- .project:nth-child(even) .project-media, .project:nth-child(even) .project-copy { order: initial; }
- .hero { padding-top: 125px; }
- .hero-side { max-width: 620px; }
- .hero-meta { margin-top: 2.6rem; }
- .project { gap: 1.7rem; }
- .projects { gap: 5rem; }
- .project-copy { max-width: 620px; }
- .cap-grid { gap: 3rem; }
- .studio-grid { gap: 3rem; }
- .cta-side { max-width: 500px; }
- }
-
- @media (max-width: 620px) {
- .wrap { width: min(calc(100% - 28px), var(--max)); }
- .nav-inner { min-height: 68px; }
- .brand img { width: 38px; height: 38px; }
- .brand-copy strong { font-size: .94rem; }
- .nav-links { gap: .6rem; }
- .nav-links .button { min-height: 42px; padding: .7rem .95rem; font-size: .76rem; }
- .hero { min-height: auto; padding: 128px 0 70px; }
- .hero h1 { font-size: clamp(4rem,18.6vw,6.3rem); }
- .hero-meta { grid-template-columns: 1fr; gap: .7rem; }
- .hero-meta div { padding-bottom: .75rem; border-bottom: 1px solid var(--line-soft); }
- .section-head { display: block; }
- .section-head p { margin-top: 1.25rem; }
- .project-media { border-radius: 18px; aspect-ratio: 4/3; }
- .cap-row { grid-template-columns: 2rem 1fr; }
- .cap-row p { grid-column: 2; margin-top: -.55rem; }
- .footer-grid { align-items: flex-start; flex-direction: column; gap: 1rem; }
- }
-
- @media (prefers-reduced-motion: reduce) {
- html { scroll-behavior: auto; }
- *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
- .reveal { opacity: 1; transform: none; }
- }
- </style>
-
- <script type="application/ld+json">
- {
- "@context":"https://schema.org",
- "@type":"Organization",
- "name":"After Dark Labs",
- "url":"https://afterdarklabs.io/",
- "logo":"https://afterdarklabs.io/logo_assets/icon-512.png",
- "description":"Independent South Florida design and development studio building custom websites, Shopify stores, SaaS products, and business tools.",
- "areaServed":"United States"
- }
- </script>
-</head>
-<body>
- <a class="skip-link" href="#main">Skip to content</a>
-
- <nav id="nav" aria-label="Primary navigation">
- <div class="wrap nav-inner">
- <a class="brand" href="#top" aria-label="After Dark Labs home">
- <img src="https://afterdarklabs.io/logo_assets/mark.svg" alt="" width="42" height="42">
- <span class="brand-copy logo-font">
- <strong>After Dark</strong>
- <span>LABS</span>
- </span>
- </a>
- <div class="nav-links">
- <a href="#work">Work</a>
- <a href="#capabilities">Capabilities</a>
- <a href="#studio">Studio</a>
- <a class="button" href="https://cal.com/afterdarklabs/meet" target="_blank" rel="noopener noreferrer">Book a consultation</a>
- </div>
- </div>
- </nav>
-
- <main id="main">
- <section class="hero" id="top">
- <div class="wrap">
- <div class="hero-grid">
- <div>
- <div class="eyebrow">Independent design + development studio</div>
- <h1 class="display">
- <span class="line">Websites and tools</span>
- <span class="line">that help businesses</span>
- <span class="line standout" aria-hidden="true">
- <span class="neon-letter flicker" style="--delay:-1.1s;--duration:11s">s</span><span class="neon-letter">t</span><span class="neon-letter flicker" style="--delay:-5.6s;--duration:13s">a</span><span class="neon-letter">n</span><span class="neon-letter flicker" style="--delay:-8.4s;--duration:15s">d</span><span class="neon-gap"></span><span class="neon-letter">o</span><span class="neon-letter flicker" style="--delay:-3.3s;--duration:12s">u</span><span class="neon-letter">t</span>
- </span>
- <span class="line" style="position:absolute;clip:rect(0,0,0,0);width:1px;height:1px;overflow:hidden">stand out</span>
- </h1>
- </div>
-
- <aside class="hero-side">
- <p>Custom websites, Shopify stores, SaaS products and internal tools—designed and built without templates, page builders or agency bloat.</p>
- <div class="hero-actions">
- <a class="button" href="https://cal.com/afterdarklabs/meet" target="_blank" rel="noopener noreferrer">Book a consultation
- <svg class="arrow" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M5 15 15 5M7 5h8v8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
- </a>
- <a class="button secondary" href="#work">View selected work</a>
- </div>
- </aside>
- </div>
-
- <div class="hero-meta" aria-label="Studio facts">
- <div><strong>South Florida</strong>Lighthouse Point, FL</div>
- <div><strong>Design + development</strong>One studio, one point of contact</div>
- <div><strong>Built custom</strong>Fast, lean and made for the business</div>
- </div>
- </div>
- </section>
-
- <section class="section" id="work">
- <div class="wrap">
- <header class="section-head reveal">
- <div>
- <div class="eyebrow">Selected work</div>
- <h2 class="display">The work does<br>the talking.</h2>
- </div>
- <p>A mix of service businesses, e-commerce, SaaS and internal software. Different problems, same standard: clear thinking and clean execution.</p>
- </header>
-
- <div class="projects">
- <article class="project reveal">
- <a class="project-media" href="https://voldservices.com" target="_blank" rel="noopener noreferrer" aria-label="Visit Vold Services website">
- <img src="https://afterdarklabs.io/voldservices.webp" alt="Vold Services website by After Dark Labs" loading="lazy" width="1200" height="702">
- </a>
- <div class="project-copy">
- <div class="project-index">01 / VOLD SERVICES</div>
- <h3>Making exterior cleaning feel premium.</h3>
- <p>A conversion-focused service site built around a scroll-driven hero, clear offer hierarchy and the kind of polish that makes a local business look established before the first call.</p>
- <div class="project-tags"><span class="tag">Web design</span><span class="tag">Development</span><span class="tag">Motion</span></div>
- <a class="text-link" href="https://voldservices.com" target="_blank" rel="noopener noreferrer"><span>Visit live site</span><svg class="arrow" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M5 15 15 5M7 5h8v8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></a>
- </div>
- </article>
-
- <article class="project reveal">
- <a class="project-media" href="https://frontlineboats.com" target="_blank" rel="noopener noreferrer" aria-label="Visit Frontline Boat Services website">
- <img src="https://afterdarklabs.io/frontlineboats.webp?v=1" alt="Frontline Boat Services website by After Dark Labs" loading="lazy" width="1200" height="700">
- </a>
- <div class="project-copy">
- <div class="project-index">02 / FRONTLINE BOAT SERVICES</div>
- <h3>A marine brand with some horsepower.</h3>
- <p>A sharp, high-contrast site for a South Florida marine shop—built to organize a broad service offering, communicate technical expertise and perform in local search.</p>
- <div class="project-tags"><span class="tag">Brand direction</span><span class="tag">Web design</span><span class="tag">Local SEO</span></div>
- <a class="text-link" href="https://frontlineboats.com" target="_blank" rel="noopener noreferrer"><span>Visit live site</span><svg class="arrow" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M5 15 15 5M7 5h8v8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></a>
- </div>
- </article>
-
- <article class="project reveal">
- <a class="project-media" href="https://vegy.app/" target="_blank" rel="noopener noreferrer" aria-label="Visit Vegy website">
- <img src="https://afterdarklabs.io/vegy.webp?v=1" alt="Vegy wholesale ordering platform by After Dark Labs" loading="lazy" width="1200" height="702">
- </a>
- <div class="project-copy">
- <div class="project-index">03 / VEGY</div>
- <h3>Wholesale ordering, rebuilt from the workflow up.</h3>
- <p>A white-label ordering platform for produce wholesalers with role-based experiences, live order updates, operational tools and accounting integrations—software designed around how the business actually runs.</p>
- <div class="project-tags"><span class="tag">Product design</span><span class="tag">Web app</span><span class="tag">Backend systems</span></div>
- <a class="text-link" href="https://vegy.app/" target="_blank" rel="noopener noreferrer"><span>Visit product site</span><svg class="arrow" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M5 15 15 5M7 5h8v8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></a>
- </div>
- </article>
-
- <article class="project reveal">
- <a class="project-media" href="https://coralvibefl.com" target="_blank" rel="noopener noreferrer" aria-label="Visit Coral Vibe website">
- <img src="https://afterdarklabs.io/coralvibe.webp?v=1" alt="Coral Vibe Shopify store by After Dark Labs" loading="lazy" width="1200" height="678">
- </a>
- <div class="project-copy">
- <div class="project-index">04 / CORAL VIBE</div>
- <h3>E-commerce that lets the product be the color.</h3>
- <p>A custom Shopify storefront for a coral retailer, balancing dense product information with a clean visual system that keeps browsing, discovery and purchase simple.</p>
- <div class="project-tags"><span class="tag">Shopify</span><span class="tag">E-commerce</span><span class="tag">UX</span></div>
- <a class="text-link" href="https://coralvibefl.com" target="_blank" rel="noopener noreferrer"><span>Visit live store</span><svg class="arrow" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M5 15 15 5M7 5h8v8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></a>
- </div>
- </article>
-
- <article class="project reveal">
- <a class="project-media" href="https://invoiceotter.com" target="_blank" rel="noopener noreferrer" aria-label="Visit Invoice Otter website">
- <img src="https://afterdarklabs.io/invoiceotter.webp?v=1" alt="Invoice Otter SaaS product by After Dark Labs" loading="lazy" width="1200" height="673">
- </a>
- <div class="project-copy">
- <div class="project-index">05 / INVOICE OTTER</div>
- <h3>A small SaaS product with a clear job.</h3>
- <p>A focused subscription product with a simple interface, straightforward onboarding and just enough personality to make invoicing feel less like admin.</p>
- <div class="project-tags"><span class="tag">SaaS</span><span class="tag">Product UI</span><span class="tag">Subscriptions</span></div>
- <a class="text-link" href="https://invoiceotter.com" target="_blank" rel="noopener noreferrer"><span>Visit product</span><svg class="arrow" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M5 15 15 5M7 5h8v8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></a>
- </div>
- </article>
- </div>
-
- <div class="lab-strip reveal">
- <div>
- <div class="eyebrow">From the lab</div>
- <h3>We build our own things, too.</h3>
- </div>
- <div class="lab-list">
- <a class="lab-item" href="https://makeitpdf.org/" target="_blank" rel="noopener noreferrer"><span><strong>MakeItPDF</strong><span>Privacy-first browser utility</span></span><span class="go">↗</span></a>
- <a class="lab-item" href="https://harrisonerd.com/micropie" target="_blank" rel="noopener noreferrer"><span><strong>MicroPie</strong><span>Open-source Python / ASGI framework</span></span><span class="go">↗</span></a>
- <a class="lab-item" href="https://gitman.io" target="_blank" rel="noopener noreferrer"><span><strong>GitMan.io</strong><span>Developer platform and Git hosting</span></span><span class="go">↗</span></a>
- </div>
- </div>
- </div>
- </section>
-
- <section class="section" id="capabilities">
- <div class="wrap cap-grid">
- <div class="cap-intro reveal">
- <div class="eyebrow">Capabilities</div>
- <h2 class="display">Built around the business. Not a template.</h2>
- <p>Strategy, interface design and engineering stay connected from the first idea to launch, so the final product feels intentional instead of assembled.</p>
- </div>
-
- <div class="reveal">
- <div class="cap-list">
- <div class="cap-row"><span class="num">01</span><h3>Websites & commerce</h3><p>Custom business websites, redesigns, Shopify storefronts and subscription experiences.</p></div>
- <div class="cap-row"><span class="num">02</span><h3>Apps & platforms</h3><p>SaaS MVPs, customer portals, internal tools, APIs and workflow software.</p></div>
- <div class="cap-row"><span class="num">03</span><h3>Brand & growth</h3><p>Visual identity, messaging, local search foundations, lead capture and conversion-minded UX.</p></div>
- <div class="cap-row"><span class="num">04</span><h3>Infrastructure & AI</h3><p>Hosting, domains, business email, third-party integrations, LLM features and intelligent automation.</p></div>
- </div>
- <p class="price-note"><strong>Typical business websites: $500–$2,000 one time.</strong> Larger e-commerce, apps and custom platforms are quoted by scope. Hosting is included for most service-business websites.</p>
- </div>
- </div>
- </section>
-
- <section class="section studio" id="studio">
- <div class="wrap studio-grid">
- <div class="reveal">
- <div class="eyebrow">The studio</div>
- <h2 class="display">Small by design.<br>Technical by default.</h2>
- <div class="studio-copy">After Dark Labs is an independent studio in Lighthouse Point, Florida. You work directly with the person designing and building the project—no account-manager relay race, no theme marketplace, no layers of agency overhead.</div>
- </div>
- <div class="studio-facts reveal">
- <div class="fact"><span>Based in</span><span>Lighthouse Point, Florida</span></div>
- <div class="fact"><span>Works with</span><span>Local businesses + product teams</span></div>
- <div class="fact"><span>Build philosophy</span><span>Custom, fast, maintainable</span></div>
- <div class="fact"><span>Engagement</span><span>Direct collaboration</span></div>
- </div>
- </div>
- </section>
-
- <section class="cta">
- <div class="wrap cta-inner reveal">
- <h2 class="display">Have something worth <span>building?</span></h2>
- <div class="cta-side">
- <p>Tell us what you're working on. We'll talk through the problem, scope and the simplest way to make it real.</p>
- <a class="button" href="https://cal.com/afterdarklabs/meet" target="_blank" rel="noopener noreferrer">Book a consultation
- <svg class="arrow" viewBox="0 0 20 20" fill="none" aria-hidden="true"><path d="M5 15 15 5M7 5h8v8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
- </a>
- </div>
- </div>
- </section>
- </main>
-
- <footer>
- <div class="wrap footer-grid">
- <div>© 2026 After Dark Labs · Lighthouse Point, FL</div>
- <div class="footer-links">
- <a href="mailto:[email protected]">Email</a>
- <a href="#work">Work</a>
- <a href="#capabilities">Capabilities</a>
- <a href="https://harrisonerd.com/" target="_blank" rel="noopener noreferrer">Harrison Erd ↗</a>
- </div>
- </div>
- </footer>
-
- <script>
- (() => {
- const nav = document.getElementById('nav');
- const setNav = () => nav.classList.toggle('scrolled', window.scrollY > 18);
- setNav();
- window.addEventListener('scroll', setNav, { passive: true });
-
- const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
- const revealEls = [...document.querySelectorAll('.reveal')];
- if (reduce || !('IntersectionObserver' in window)) {
- revealEls.forEach(el => el.classList.add('in'));
- } else {
- const observer = new IntersectionObserver((entries) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- entry.target.classList.add('in');
- observer.unobserve(entry.target);
- }
- });
- }, { threshold: .12, rootMargin: '0px 0px -4% 0px' });
- revealEls.forEach(el => observer.observe(el));
- }
- })();
- </script>
-</body>
-</html>
\ No newline at end of file
diff --git a/docs/indexerd.html b/docs/indexerd.html
index b9a801d..274172b 100644
--- a/docs/indexerd.html
+++ b/docs/indexerd.html
@@ -339,17 +339,29 @@
background: var(--page-bg);
}
+ .work-intro:not(.work-intro--closing) {
+ height: auto;
+ }
+
.work-intro-stage {
position: sticky;
top: var(--site-nav-height);
z-index: 3;
display: flex;
box-sizing: border-box;
- align-items: flex-start;
+ height: calc(100svh - var(--site-nav-height));
+ align-items: center;
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-intro:not(.work-intro--closing) .work-intro-stage {
+ position: relative;
+ top: auto;
+ height: auto;
+ align-items: flex-start;
+ padding-top: 0;
+ padding-bottom: 25px;
}
.work-heading {
@@ -400,8 +412,10 @@
.work-intro--closing .work-intro-stage {
position: relative;
top: auto;
- transform: none;
- will-change: auto;
+ height: auto;
+ align-items: flex-start;
+ padding-top: 48px;
+ padding-bottom: 56px;
}
.work-intro.is-scroll-ready .work-heading-word {
@@ -451,6 +465,7 @@
.work-intro-stage {
position: relative;
top: auto;
+ height: auto;
transform: none;
will-change: auto;
}
@@ -465,7 +480,7 @@
.work-stage {
box-sizing: border-box;
- padding: clamp(0.75rem, 1.5vw, 1.5rem) clamp(1rem, 2vw, 2rem);
+ padding: clamp(0rem, 0.5vw, 0.5rem) clamp(1rem, 2vw, 2rem);
}
.work-stage-content {
@@ -849,21 +864,12 @@
<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>
+ <span class="work-heading-word">move</span>
+ <span class="work-heading-word">your</span>
+ <span class="work-heading-word">business</span>
+ <span class="work-heading-word">forward</span>
+ <span class="work-heading-word">→</span>
<a class="work-heading-word work-heading-email" href="mailto:[email protected]">[email protected]</a>
</span>
</h2>
@@ -1021,20 +1027,16 @@
})();
(function () {
- const intro = document.querySelector('.work-intro');
- const stage = intro?.querySelector('.work-intro-stage');
+ const intro = document.querySelector('.work-intro:not(.work-intro--closing)');
const heading = intro?.querySelector('.work-heading');
- const heroHeading = document.querySelector('.hero-content h1');
+ const firstCard = document.querySelector('#work-scroller > article');
const words = Array.from(intro?.querySelectorAll('.work-heading-word') || []);
- if (!intro || !stage || !heading || !heroHeading || !words.length) return;
+ if (!intro || !heading || !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;
@@ -1059,7 +1061,6 @@
updateFrame = 0;
if (reduceMotion.matches) {
- stage.style.removeProperty('--work-intro-shift');
words.forEach(word => word.classList.add('is-revealed'));
revealedWords = words.length;
return;
@@ -1068,8 +1069,6 @@
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;
@@ -1084,21 +1083,19 @@
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;
const viewportHeight = window.visualViewport?.height || window.innerHeight;
- const stickyTravel = Math.max(0, intro.offsetHeight - stage.offsetHeight);
- const revealEnd = introTop - navHeight + stickyTravel;
-
- revealStart = introTop - Math.max(1, viewportHeight - navHeight);
+ const headingCenter = getDocumentTop(heading) + heading.offsetHeight / 2;
+ const visualCenter = navHeight + (viewportHeight - navHeight) * 0.58;
+ const cardRevealStart = firstCard
+ ? getDocumentTop(firstCard) - viewportHeight * 0.94
+ : headingCenter - visualCenter + viewportHeight * 0.32;
+ revealStart = headingCenter - visualCenter;
+ const revealEnd = Math.max(
+ cardRevealStart + viewportHeight * 0.06,
+ revealStart + viewportHeight * 0.24
+ );
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();